mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-03 21:21:32 +00:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
b7dd2b8d89 | |||
bf8a8379db | |||
e0bbeb5d64 | |||
4b0f3782bd | |||
3adf9b5e88 | |||
f9d4508149 | |||
54dc9cbdaa | |||
ea9624955c | |||
960578efc4 | |||
d7affe6c8c | |||
a04a812f28 | |||
9f6383b4c5 | |||
7f5a8c7a6e | |||
b1e7a05440 | |||
998df55711 | |||
3138c3e3e5 | |||
d12fee305c | |||
66aef77b05 |
20
.github/workflows/check.yml
vendored
20
.github/workflows/check.yml
vendored
@ -12,11 +12,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: harden runner
|
- name: harden runner
|
||||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
- name: checkout repository
|
- name: checkout repository
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: install stable rust toolchain with rustfmt
|
- name: install stable rust toolchain with rustfmt
|
||||||
@ -33,11 +33,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: harden runner
|
- name: harden runner
|
||||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
- name: checkout repository
|
- name: checkout repository
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: shellcheck
|
- name: shellcheck
|
||||||
@ -55,11 +55,11 @@ jobs:
|
|||||||
name: full build linux-${{ matrix.arch }}
|
name: full build linux-${{ matrix.arch }}
|
||||||
steps:
|
steps:
|
||||||
- name: harden runner
|
- name: harden runner
|
||||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
- name: checkout repository
|
- name: checkout repository
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: install stable rust toolchain
|
- name: install stable rust toolchain
|
||||||
@ -83,11 +83,11 @@ jobs:
|
|||||||
name: full test linux-${{ matrix.arch }}
|
name: full test linux-${{ matrix.arch }}
|
||||||
steps:
|
steps:
|
||||||
- name: harden runner
|
- name: harden runner
|
||||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
- name: checkout repository
|
- name: checkout repository
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: install stable rust toolchain
|
- name: install stable rust toolchain
|
||||||
@ -110,11 +110,11 @@ jobs:
|
|||||||
name: full clippy linux-${{ matrix.arch }}
|
name: full clippy linux-${{ matrix.arch }}
|
||||||
steps:
|
steps:
|
||||||
- name: harden runner
|
- name: harden runner
|
||||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
- name: checkout repository
|
- name: checkout repository
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: install stable rust toolchain with clippy
|
- name: install stable rust toolchain with clippy
|
||||||
|
6
.github/workflows/release-plz.yml
vendored
6
.github/workflows/release-plz.yml
vendored
@ -15,7 +15,7 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- name: harden runner
|
- name: harden runner
|
||||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
- name: generate cultivator token
|
- name: generate cultivator token
|
||||||
@ -25,7 +25,7 @@ jobs:
|
|||||||
app-id: "${{ secrets.EDERA_CULTIVATION_APP_ID }}"
|
app-id: "${{ secrets.EDERA_CULTIVATION_APP_ID }}"
|
||||||
private-key: "${{ secrets.EDERA_CULTIVATION_APP_PRIVATE_KEY }}"
|
private-key: "${{ secrets.EDERA_CULTIVATION_APP_PRIVATE_KEY }}"
|
||||||
- name: checkout repository
|
- name: checkout repository
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@ -37,7 +37,7 @@ jobs:
|
|||||||
- name: install linux dependencies
|
- name: install linux dependencies
|
||||||
run: ./hack/ci/install-linux-deps.sh
|
run: ./hack/ci/install-linux-deps.sh
|
||||||
- name: release-plz
|
- name: release-plz
|
||||||
uses: MarcoIeni/release-plz-action@e28810957ef1fedfa89b5e9692e750ce45f62a67 # v0.5.65
|
uses: MarcoIeni/release-plz-action@db75300cf27adcd986d6f0cf4a72a4ffcc11dae5 # v0.5.86
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: "${{ steps.generate-token.outputs.token }}"
|
GITHUB_TOKEN: "${{ steps.generate-token.outputs.token }}"
|
||||||
CARGO_REGISTRY_TOKEN: "${{ secrets.KRATA_RELEASE_CARGO_TOKEN }}"
|
CARGO_REGISTRY_TOKEN: "${{ secrets.KRATA_RELEASE_CARGO_TOKEN }}"
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
|
/.idea
|
||||||
|
/.vscode
|
||||||
|
21
CHANGELOG.md
21
CHANGELOG.md
@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.0.24](https://github.com/edera-dev/krata/compare/v0.0.23...v0.0.24) - 2024-12-14
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- *(xen)* update xenclient and xenplatform to the latest structure (#433)
|
||||||
|
- *(xencall)* improve asynchronous support (#430)
|
||||||
|
- *(evtchn)* harden evtchn handling and improve api (#431)
|
||||||
|
- *(xenstore)* multi-watch and maybe-commit support (#429)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- *(xenclient)* examples should use supported platform
|
||||||
|
- *(xenclient)* boot example should use unsupported platform on aarch64
|
||||||
|
- *(xenplatform)* e820 sanitize should now produce valid mappings
|
||||||
|
- *(xenplatform)* use cfg attributes for returning supported platforms
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
- *(deps)* upgrade dependencies and clean code (#432)
|
||||||
|
- update Cargo.toml dependencies
|
||||||
|
|
||||||
## [0.0.23](https://github.com/edera-dev/krata/compare/v0.0.22...v0.0.23) - 2024-09-17
|
## [0.0.23](https://github.com/edera-dev/krata/compare/v0.0.22...v0.0.23) - 2024-09-17
|
||||||
|
|
||||||
### Other
|
### Other
|
||||||
|
255
Cargo.lock
generated
255
Cargo.lock
generated
@ -1,22 +1,16 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
version = "0.22.0"
|
version = "0.24.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
|
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gimli",
|
"gimli",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "adler"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "adler2"
|
name = "adler2"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@ -34,9 +28,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.15"
|
version = "0.6.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
|
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"anstyle-parse",
|
"anstyle-parse",
|
||||||
@ -49,70 +43,76 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle"
|
name = "anstyle"
|
||||||
version = "1.0.8"
|
version = "1.0.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-parse"
|
name = "anstyle-parse"
|
||||||
version = "0.2.5"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
|
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"utf8parse",
|
"utf8parse",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-query"
|
name = "anstyle-query"
|
||||||
version = "1.1.1"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
|
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-wincon"
|
name = "anstyle-wincon"
|
||||||
version = "3.0.4"
|
version = "3.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
|
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"windows-sys",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.82"
|
version = "0.1.83"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1"
|
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.74",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.3.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.73"
|
version = "0.3.74"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
|
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr2line",
|
"addr2line",
|
||||||
"cc",
|
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"miniz_oxide 0.7.4",
|
"miniz_oxide",
|
||||||
"object",
|
"object",
|
||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit-vec"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.6.0"
|
version = "2.6.0"
|
||||||
@ -127,35 +127,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.7.1"
|
version = "1.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "c2rust-bitfields"
|
|
||||||
version = "0.18.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b43c3f07ab0ef604fa6f595aa46ec2f8a22172c975e186f6f5bf9829a3b72c41"
|
|
||||||
dependencies = [
|
|
||||||
"c2rust-bitfields-derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "c2rust-bitfields-derive"
|
|
||||||
version = "0.18.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d3cbc102e2597c9744c8bd8c15915d554300601c91a079430d309816b0912545"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.10"
|
version = "1.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292"
|
checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf"
|
||||||
|
dependencies = [
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@ -171,9 +154,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.2"
|
version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
@ -221,12 +204,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.33"
|
version = "1.0.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253"
|
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"miniz_oxide 0.8.0",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -242,21 +225,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.29.0"
|
version = "0.31.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.5"
|
version = "0.15.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hermit-abi"
|
|
||||||
version = "0.3.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "humantime"
|
name = "humantime"
|
||||||
@ -266,9 +243,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.5.0"
|
version = "2.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
|
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
@ -282,7 +259,7 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "krata-xencall"
|
name = "krata-xencall"
|
||||||
version = "0.0.23"
|
version = "0.0.24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"libc",
|
"libc",
|
||||||
@ -290,20 +267,19 @@ dependencies = [
|
|||||||
"nix",
|
"nix",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"uuid",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "krata-xenclient"
|
name = "krata-xenclient"
|
||||||
version = "0.0.23"
|
version = "0.0.24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"bit-vec",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"krata-xencall",
|
"krata-xencall",
|
||||||
"krata-xenplatform",
|
"krata-xenplatform",
|
||||||
"krata-xenstore",
|
"krata-xenstore",
|
||||||
"libc",
|
|
||||||
"log",
|
"log",
|
||||||
"regex",
|
"regex",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@ -313,7 +289,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "krata-xenevtchn"
|
name = "krata-xenevtchn"
|
||||||
version = "0.0.23"
|
version = "0.0.24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"libc",
|
"libc",
|
||||||
@ -325,7 +301,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "krata-xengnt"
|
name = "krata-xengnt"
|
||||||
version = "0.0.23"
|
version = "0.0.24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"nix",
|
"nix",
|
||||||
@ -334,14 +310,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "krata-xenplatform"
|
name = "krata-xenplatform"
|
||||||
version = "0.0.23"
|
version = "0.0.24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"c2rust-bitfields",
|
|
||||||
"elf",
|
"elf",
|
||||||
"env_logger",
|
|
||||||
"flate2",
|
"flate2",
|
||||||
"indexmap",
|
|
||||||
"krata-xencall",
|
"krata-xencall",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
@ -357,7 +330,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "krata-xenstore"
|
name = "krata-xenstore"
|
||||||
version = "0.0.23"
|
version = "0.0.24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
@ -369,9 +342,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.158"
|
version = "0.2.168"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
@ -406,15 +379,6 @@ version = "2.7.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "miniz_oxide"
|
|
||||||
version = "0.7.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
|
|
||||||
dependencies = [
|
|
||||||
"adler",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@ -426,14 +390,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.0.2"
|
version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
"windows-sys",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -450,9 +413,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.36.3"
|
version = "0.36.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9"
|
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@ -482,48 +445,48 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.14"
|
version = "0.2.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.86"
|
version = "1.0.92"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.36"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.3"
|
version = "0.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
|
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.6"
|
version = "1.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -533,9 +496,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.7"
|
version = "0.4.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -544,9 +507,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.4"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
@ -560,6 +523,12 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
@ -583,30 +552,19 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.5.7"
|
version = "0.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
|
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "2.0.90"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "2.0.74"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -615,29 +573,29 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.63"
|
version = "2.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.63"
|
version = "2.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.74",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.40.0"
|
version = "1.42.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
|
checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -648,7 +606,7 @@ dependencies = [
|
|||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"windows-sys",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -659,14 +617,14 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.74",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.12"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
@ -676,9 +634,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.10.0"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
|
checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
@ -698,6 +656,15 @@ dependencies = [
|
|||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
20
Cargo.toml
20
Cargo.toml
@ -10,38 +10,34 @@ members = [
|
|||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.0.23"
|
version = "0.0.24"
|
||||||
homepage = "https://krata.dev"
|
homepage = "https://krata.dev"
|
||||||
license = "GPL-2.0-or-later"
|
license = "GPL-2.0-or-later"
|
||||||
repository = "https://github.com/edera-dev/krata"
|
repository = "https://github.com/edera-dev/krata"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
async-trait = "0.1.82"
|
async-trait = "0.1.83"
|
||||||
|
bit-vec = "0.8.0"
|
||||||
byteorder = "1"
|
byteorder = "1"
|
||||||
c2rust-bitfields = "0.18.0"
|
|
||||||
elf = "0.7.4"
|
elf = "0.7.4"
|
||||||
env_logger = "0.11.5"
|
env_logger = "0.11.5"
|
||||||
flate2 = "1.0"
|
flate2 = "1.0"
|
||||||
indexmap = "2.5.0"
|
indexmap = "2.6.0"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
memchr = "2"
|
memchr = "2"
|
||||||
nix = "0.29.0"
|
nix = "0.29.0"
|
||||||
regex = "1.10.6"
|
regex = "1.11.1"
|
||||||
slice-copy = "0.3.0"
|
slice-copy = "0.3.0"
|
||||||
thiserror = "1.0"
|
thiserror = "2.0.7"
|
||||||
xz2 = "0.1"
|
xz2 = "0.1"
|
||||||
|
|
||||||
[workspace.dependencies.serde]
|
|
||||||
version = "1.0.209"
|
|
||||||
features = ["derive"]
|
|
||||||
|
|
||||||
[workspace.dependencies.tokio]
|
[workspace.dependencies.tokio]
|
||||||
version = "1.40.0"
|
version = "1.41.1"
|
||||||
features = ["full"]
|
features = ["full"]
|
||||||
|
|
||||||
[workspace.dependencies.uuid]
|
[workspace.dependencies.uuid]
|
||||||
version = "1.10.0"
|
version = "1.11.0"
|
||||||
features = ["v4"]
|
features = ["v4"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
@ -14,7 +14,6 @@ log = { workspace = true }
|
|||||||
nix = { workspace = true, features = ["ioctl"] }
|
nix = { workspace = true, features = ["ioctl"] }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
uuid = { workspace = true }
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "xencall"
|
name = "xencall"
|
||||||
@ -22,6 +21,10 @@ name = "xencall"
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
env_logger = { workspace = true }
|
env_logger = { workspace = true }
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "xencall-cpu-topology"
|
||||||
|
path = "examples/cpu_topology.rs"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "xencall-domain-info"
|
name = "xencall-domain-info"
|
||||||
path = "examples/domain_info.rs"
|
path = "examples/domain_info.rs"
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use xencall::error::Result;
|
use xencall::error::Result;
|
||||||
use xencall::sys::CpuId;
|
|
||||||
use xencall::XenCall;
|
use xencall::XenCall;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@ -11,9 +10,5 @@ async fn main() -> Result<()> {
|
|||||||
println!("{:?}", physinfo);
|
println!("{:?}", physinfo);
|
||||||
let topology = call.cpu_topology().await?;
|
let topology = call.cpu_topology().await?;
|
||||||
println!("{:?}", topology);
|
println!("{:?}", topology);
|
||||||
call.set_cpufreq_gov(CpuId::All, "performance").await?;
|
|
||||||
call.set_cpufreq_gov(CpuId::Single(0), "performance")
|
|
||||||
.await?;
|
|
||||||
call.set_turbo_mode(CpuId::All, true).await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
@ -7,8 +7,6 @@ async fn main() -> Result<()> {
|
|||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
let call = XenCall::open(0)?;
|
let call = XenCall::open(0)?;
|
||||||
let physinfo = call.phys_info().await?;
|
|
||||||
println!("{:?}", physinfo);
|
|
||||||
let topology = call.cpu_topology().await?;
|
let topology = call.cpu_topology().await?;
|
||||||
println!("{:?}", topology);
|
println!("{:?}", topology);
|
||||||
call.set_cpufreq_gov(CpuId::All, "performance").await?;
|
call.set_cpufreq_gov(CpuId::All, "performance").await?;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
|
use tokio::task::JoinError;
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("version of xen is not supported")]
|
#[error("version of xen is not supported")]
|
||||||
@ -16,6 +18,8 @@ pub enum Error {
|
|||||||
MmapBatchFailed(nix::errno::Errno),
|
MmapBatchFailed(nix::errno::Errno),
|
||||||
#[error("specified value is too long")]
|
#[error("specified value is too long")]
|
||||||
ValueTooLong,
|
ValueTooLong,
|
||||||
|
#[error("failed to join async task: {0}")]
|
||||||
|
JoinError(JoinError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
@ -10,10 +10,11 @@ use crate::sys::{
|
|||||||
PciAssignDevice, XenCapabilitiesInfo, DOMCTL_DEV_PCI, HYPERVISOR_DOMCTL,
|
PciAssignDevice, XenCapabilitiesInfo, DOMCTL_DEV_PCI, HYPERVISOR_DOMCTL,
|
||||||
HYPERVISOR_EVENT_CHANNEL_OP, HYPERVISOR_HVM_OP, HYPERVISOR_MEMORY_OP, HYPERVISOR_MMUEXT_OP,
|
HYPERVISOR_EVENT_CHANNEL_OP, HYPERVISOR_HVM_OP, HYPERVISOR_MEMORY_OP, HYPERVISOR_MMUEXT_OP,
|
||||||
HYPERVISOR_MULTICALL, HYPERVISOR_XEN_VERSION, XENVER_CAPABILITIES, XEN_DOMCTL_ASSIGN_DEVICE,
|
HYPERVISOR_MULTICALL, HYPERVISOR_XEN_VERSION, XENVER_CAPABILITIES, XEN_DOMCTL_ASSIGN_DEVICE,
|
||||||
XEN_DOMCTL_CREATEDOMAIN, XEN_DOMCTL_DESTROYDOMAIN, XEN_DOMCTL_GETDOMAININFO,
|
XEN_DOMCTL_CREATEDOMAIN, XEN_DOMCTL_CREATE_DOMAIN2_INTERFACE_THRESHOLD,
|
||||||
XEN_DOMCTL_GETHVMCONTEXT, XEN_DOMCTL_GETPAGEFRAMEINFO3, XEN_DOMCTL_HYPERCALL_INIT,
|
XEN_DOMCTL_DESTROYDOMAIN, XEN_DOMCTL_GETDOMAININFO, XEN_DOMCTL_GETHVMCONTEXT,
|
||||||
XEN_DOMCTL_IOMEM_PERMISSION, XEN_DOMCTL_IOPORT_PERMISSION, XEN_DOMCTL_IRQ_PERMISSION,
|
XEN_DOMCTL_GETPAGEFRAMEINFO3, XEN_DOMCTL_HYPERCALL_INIT, XEN_DOMCTL_IOMEM_PERMISSION,
|
||||||
XEN_DOMCTL_MAX_MEM, XEN_DOMCTL_MAX_VCPUS, XEN_DOMCTL_PAUSEDOMAIN, XEN_DOMCTL_SETHVMCONTEXT,
|
XEN_DOMCTL_IOPORT_PERMISSION, XEN_DOMCTL_IRQ_PERMISSION, XEN_DOMCTL_MAX_MEM,
|
||||||
|
XEN_DOMCTL_MAX_VCPUS, XEN_DOMCTL_PAUSEDOMAIN, XEN_DOMCTL_SETHVMCONTEXT,
|
||||||
XEN_DOMCTL_SETVCPUCONTEXT, XEN_DOMCTL_SET_ADDRESS_SIZE, XEN_DOMCTL_SET_PAGING_MEMPOOL_SIZE,
|
XEN_DOMCTL_SETVCPUCONTEXT, XEN_DOMCTL_SET_ADDRESS_SIZE, XEN_DOMCTL_SET_PAGING_MEMPOOL_SIZE,
|
||||||
XEN_DOMCTL_UNPAUSEDOMAIN, XEN_MEM_ADD_TO_PHYSMAP, XEN_MEM_CLAIM_PAGES, XEN_MEM_MEMORY_MAP,
|
XEN_DOMCTL_UNPAUSEDOMAIN, XEN_MEM_ADD_TO_PHYSMAP, XEN_MEM_CLAIM_PAGES, XEN_MEM_MEMORY_MAP,
|
||||||
XEN_MEM_POPULATE_PHYSMAP,
|
XEN_MEM_POPULATE_PHYSMAP,
|
||||||
@ -25,15 +26,15 @@ use std::ffi::{c_long, c_uint, c_ulong, c_void};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use sys::{
|
use sys::{
|
||||||
CpuId, E820Entry, ForeignMemoryMap, PhysdevMapPirq, Sysctl, SysctlCputopo, SysctlCputopoinfo,
|
CpuId, E820Entry, ForeignMemoryMap, PhysdevMapPirq, SetDomainHandle, Sysctl, SysctlCputopo,
|
||||||
SysctlPhysinfo, SysctlPmOp, SysctlPmOpValue, SysctlReadconsole, SysctlSetCpuFreqGov,
|
SysctlCputopoinfo, SysctlPhysinfo, SysctlPmOp, SysctlPmOpValue, SysctlReadconsole,
|
||||||
SysctlValue, VcpuGuestContextAny, HYPERVISOR_PHYSDEV_OP, HYPERVISOR_SYSCTL, PHYSDEVOP_MAP_PIRQ,
|
SysctlSetCpuFreqGov, SysctlValue, VcpuGuestContextAny, HYPERVISOR_PHYSDEV_OP,
|
||||||
XEN_DOMCTL_MAX_INTERFACE_VERSION, XEN_DOMCTL_MIN_INTERFACE_VERSION, XEN_MEM_SET_MEMORY_MAP,
|
HYPERVISOR_SYSCTL, PHYSDEVOP_MAP_PIRQ, XEN_DOMCTL_MAX_INTERFACE_VERSION,
|
||||||
|
XEN_DOMCTL_MIN_INTERFACE_VERSION, XEN_DOMCTL_SETDOMAINHANDLE, XEN_MEM_SET_MEMORY_MAP,
|
||||||
XEN_SYSCTL_CPUTOPOINFO, XEN_SYSCTL_MAX_INTERFACE_VERSION, XEN_SYSCTL_MIN_INTERFACE_VERSION,
|
XEN_SYSCTL_CPUTOPOINFO, XEN_SYSCTL_MAX_INTERFACE_VERSION, XEN_SYSCTL_MIN_INTERFACE_VERSION,
|
||||||
XEN_SYSCTL_PHYSINFO, XEN_SYSCTL_PM_OP, XEN_SYSCTL_PM_OP_DISABLE_TURBO,
|
XEN_SYSCTL_PHYSINFO, XEN_SYSCTL_PM_OP, XEN_SYSCTL_PM_OP_DISABLE_TURBO,
|
||||||
XEN_SYSCTL_PM_OP_ENABLE_TURBO, XEN_SYSCTL_PM_OP_SET_CPUFREQ_GOV, XEN_SYSCTL_READCONSOLE,
|
XEN_SYSCTL_PM_OP_ENABLE_TURBO, XEN_SYSCTL_PM_OP_SET_CPUFREQ_GOV, XEN_SYSCTL_READCONSOLE,
|
||||||
};
|
};
|
||||||
use tokio::sync::Semaphore;
|
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
@ -44,7 +45,6 @@ use std::slice;
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct XenCall {
|
pub struct XenCall {
|
||||||
pub handle: Arc<File>,
|
pub handle: Arc<File>,
|
||||||
semaphore: Arc<Semaphore>,
|
|
||||||
domctl_interface_version: u32,
|
domctl_interface_version: u32,
|
||||||
sysctl_interface_version: u32,
|
sysctl_interface_version: u32,
|
||||||
}
|
}
|
||||||
@ -60,7 +60,6 @@ impl XenCall {
|
|||||||
let sysctl_interface_version = XenCall::detect_sysctl_interface_version(&handle)?;
|
let sysctl_interface_version = XenCall::detect_sysctl_interface_version(&handle)?;
|
||||||
Ok(XenCall {
|
Ok(XenCall {
|
||||||
handle: Arc::new(handle),
|
handle: Arc::new(handle),
|
||||||
semaphore: Arc::new(Semaphore::new(1)),
|
|
||||||
domctl_interface_version,
|
domctl_interface_version,
|
||||||
sysctl_interface_version,
|
sysctl_interface_version,
|
||||||
})
|
})
|
||||||
@ -117,7 +116,6 @@ impl XenCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn mmap(&self, addr: u64, len: u64) -> Option<u64> {
|
pub async fn mmap(&self, addr: u64, len: u64) -> Option<u64> {
|
||||||
let _permit = self.semaphore.acquire().await.ok()?;
|
|
||||||
trace!(
|
trace!(
|
||||||
"call fd={} mmap addr={:#x} len={}",
|
"call fd={} mmap addr={:#x} len={}",
|
||||||
self.handle.as_raw_fd(),
|
self.handle.as_raw_fd(),
|
||||||
@ -125,14 +123,20 @@ impl XenCall {
|
|||||||
len
|
len
|
||||||
);
|
);
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = mmap(
|
let handle = self.handle.clone();
|
||||||
addr as *mut c_void,
|
let ptr = tokio::task::spawn_blocking(move || {
|
||||||
len as usize,
|
mmap(
|
||||||
PROT_READ | PROT_WRITE,
|
addr as *mut c_void,
|
||||||
MAP_SHARED,
|
len as usize,
|
||||||
self.handle.as_raw_fd(),
|
PROT_READ | PROT_WRITE,
|
||||||
0,
|
MAP_SHARED,
|
||||||
);
|
handle.as_raw_fd(),
|
||||||
|
0,
|
||||||
|
) as u64
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(Error::JoinError)
|
||||||
|
.ok()? as *mut c_void;
|
||||||
if ptr == MAP_FAILED {
|
if ptr == MAP_FAILED {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@ -149,18 +153,22 @@ impl XenCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn hypercall(&self, op: c_ulong, arg: [c_ulong; 5]) -> Result<c_long> {
|
pub async fn hypercall(&self, op: c_ulong, arg: [c_ulong; 5]) -> Result<c_long> {
|
||||||
let _permit = self.semaphore.acquire().await?;
|
|
||||||
trace!(
|
trace!(
|
||||||
"call fd={} hypercall op={:#x} arg={:?}",
|
"call fd={} hypercall op={:#x} arg={:?}",
|
||||||
self.handle.as_raw_fd(),
|
self.handle.as_raw_fd(),
|
||||||
op,
|
op,
|
||||||
arg
|
arg
|
||||||
);
|
);
|
||||||
unsafe {
|
|
||||||
|
let handle = self.handle.clone();
|
||||||
|
tokio::task::spawn_blocking(move || unsafe {
|
||||||
let mut call = Hypercall { op, arg };
|
let mut call = Hypercall { op, arg };
|
||||||
let result = sys::hypercall(self.handle.as_raw_fd(), &mut call)?;
|
sys::hypercall(handle.as_raw_fd(), &mut call)
|
||||||
Ok(result as c_long)
|
.map(|x| x as c_long)
|
||||||
}
|
.map_err(|e| e.into())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(Error::JoinError)?
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn hypercall0(&self, op: c_ulong) -> Result<c_long> {
|
pub async fn hypercall0(&self, op: c_ulong) -> Result<c_long> {
|
||||||
@ -232,18 +240,21 @@ impl XenCall {
|
|||||||
num: u64,
|
num: u64,
|
||||||
addr: u64,
|
addr: u64,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let _permit = self.semaphore.acquire().await?;
|
let handle = self.handle.clone();
|
||||||
let mut resource = MmapResource {
|
tokio::task::spawn_blocking(move || {
|
||||||
dom: domid as u16,
|
let mut resource = MmapResource {
|
||||||
typ,
|
dom: domid as u16,
|
||||||
id,
|
typ,
|
||||||
idx,
|
id,
|
||||||
num,
|
idx,
|
||||||
addr,
|
num,
|
||||||
};
|
addr,
|
||||||
unsafe {
|
};
|
||||||
sys::mmap_resource(self.handle.as_raw_fd(), &mut resource)?;
|
|
||||||
}
|
unsafe { sys::mmap_resource(handle.as_raw_fd(), &mut resource) }
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(Error::JoinError)??;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,7 +265,6 @@ impl XenCall {
|
|||||||
addr: u64,
|
addr: u64,
|
||||||
mfns: Vec<u64>,
|
mfns: Vec<u64>,
|
||||||
) -> Result<c_long> {
|
) -> Result<c_long> {
|
||||||
let _permit = self.semaphore.acquire().await?;
|
|
||||||
trace!(
|
trace!(
|
||||||
"call fd={} mmap_batch domid={} num={} addr={:#x} mfns={:?}",
|
"call fd={} mmap_batch domid={} num={} addr={:#x} mfns={:?}",
|
||||||
self.handle.as_raw_fd(),
|
self.handle.as_raw_fd(),
|
||||||
@ -320,7 +330,7 @@ impl XenCall {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let count = result.unwrap();
|
let count = result?;
|
||||||
if count <= 0 {
|
if count <= 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -328,7 +338,7 @@ impl XenCall {
|
|||||||
|
|
||||||
return Ok(paged as c_long);
|
return Ok(paged as c_long);
|
||||||
}
|
}
|
||||||
Ok(result.unwrap() as c_long)
|
Ok(result? as c_long)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,6 +376,28 @@ impl XenCall {
|
|||||||
Ok(alloc_unbound.port)
|
Ok(alloc_unbound.port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn set_domain_handle(&self, domid: u32, domain_handle: [u8; 16]) -> Result<()> {
|
||||||
|
trace!(
|
||||||
|
"domctl fd={} set_domain_handle domid={} handle={:?}",
|
||||||
|
self.handle.as_raw_fd(),
|
||||||
|
domid,
|
||||||
|
domain_handle,
|
||||||
|
);
|
||||||
|
let mut domctl = DomCtl {
|
||||||
|
cmd: XEN_DOMCTL_SETDOMAINHANDLE,
|
||||||
|
interface_version: self.domctl_interface_version,
|
||||||
|
domid,
|
||||||
|
value: DomCtlValue {
|
||||||
|
set_domain_handle: SetDomainHandle {
|
||||||
|
handle: domain_handle,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_domain_info(&self, domid: u32) -> Result<GetDomainInfo> {
|
pub async fn get_domain_info(&self, domid: u32) -> Result<GetDomainInfo> {
|
||||||
trace!(
|
trace!(
|
||||||
"domctl fd={} get_domain_info domid={}",
|
"domctl fd={} get_domain_info domid={}",
|
||||||
@ -395,7 +427,14 @@ impl XenCall {
|
|||||||
cmd: XEN_DOMCTL_CREATEDOMAIN,
|
cmd: XEN_DOMCTL_CREATEDOMAIN,
|
||||||
interface_version: self.domctl_interface_version,
|
interface_version: self.domctl_interface_version,
|
||||||
domid: 0,
|
domid: 0,
|
||||||
value: DomCtlValue { create_domain },
|
value: if self.domctl_interface_version >= XEN_DOMCTL_CREATE_DOMAIN2_INTERFACE_THRESHOLD
|
||||||
|
{
|
||||||
|
DomCtlValue {
|
||||||
|
create_domain2: create_domain.to_cd_2(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DomCtlValue { create_domain }
|
||||||
|
},
|
||||||
};
|
};
|
||||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)
|
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)
|
||||||
.await?;
|
.await?;
|
||||||
@ -587,6 +626,11 @@ impl XenCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_memory_map(&self, max_entries: u32) -> Result<Vec<E820Entry>> {
|
pub async fn get_memory_map(&self, max_entries: u32) -> Result<Vec<E820Entry>> {
|
||||||
|
trace!(
|
||||||
|
"fd={} get_memory_map max_entries={}",
|
||||||
|
self.handle.as_raw_fd(),
|
||||||
|
max_entries,
|
||||||
|
);
|
||||||
let mut memory_map = MemoryMap {
|
let mut memory_map = MemoryMap {
|
||||||
count: max_entries,
|
count: max_entries,
|
||||||
buffer: 0,
|
buffer: 0,
|
||||||
|
@ -205,6 +205,16 @@ pub const XEN_DOMCTL_GDBSX_PAUSEVCPU: u32 = 1001;
|
|||||||
pub const XEN_DOMCTL_GDBSX_UNPAUSEVCPU: u32 = 1002;
|
pub const XEN_DOMCTL_GDBSX_UNPAUSEVCPU: u32 = 1002;
|
||||||
pub const XEN_DOMCTL_GDBSX_DOMSTATUS: u32 = 1003;
|
pub const XEN_DOMCTL_GDBSX_DOMSTATUS: u32 = 1003;
|
||||||
|
|
||||||
|
pub const XEN_DOMINF_DYING: u32 = 1u32 << 0;
|
||||||
|
pub const XEN_DOMINF_HVM_GUEST: u32 = 1u32 << 1;
|
||||||
|
pub const XEN_DOMINF_SHUTDOWN: u32 = 1u32 << 2;
|
||||||
|
pub const XEN_DOMINF_PAUSED: u32 = 1u32 << 3;
|
||||||
|
pub const XEN_DOMINF_BLOCKED: u32 = 1u32 << 4;
|
||||||
|
pub const XEN_DOMINF_RUNNING: u32 = 1u32 << 5;
|
||||||
|
pub const XEN_DOMINF_DEBUGGED: u32 = 1u32 << 6;
|
||||||
|
pub const XEN_DOMINF_XS_DOMAIN: u32 = 1u32 << 7;
|
||||||
|
pub const XEN_DOMINF_HAP: u32 = 1u32 << 8;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct DomCtl {
|
pub struct DomCtl {
|
||||||
@ -231,6 +241,7 @@ pub struct AddressSize {
|
|||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub union DomCtlValue {
|
pub union DomCtlValue {
|
||||||
pub create_domain: CreateDomain,
|
pub create_domain: CreateDomain,
|
||||||
|
pub create_domain2: CreateDomain2,
|
||||||
pub get_domain_info: GetDomainInfo,
|
pub get_domain_info: GetDomainInfo,
|
||||||
pub max_mem: MaxMem,
|
pub max_mem: MaxMem,
|
||||||
pub max_cpus: MaxVcpus,
|
pub max_cpus: MaxVcpus,
|
||||||
@ -244,6 +255,7 @@ pub union DomCtlValue {
|
|||||||
pub assign_device: AssignDevice,
|
pub assign_device: AssignDevice,
|
||||||
pub hvm_context: HvmContext,
|
pub hvm_context: HvmContext,
|
||||||
pub paging_mempool: PagingMempool,
|
pub paging_mempool: PagingMempool,
|
||||||
|
pub set_domain_handle: SetDomainHandle,
|
||||||
pub pad: [u8; 128],
|
pub pad: [u8; 128],
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,6 +276,24 @@ pub struct CreateDomain {
|
|||||||
pub arch_domain_config: ArchDomainConfig,
|
pub arch_domain_config: ArchDomainConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct CreateDomain2 {
|
||||||
|
pub ssidref: u32,
|
||||||
|
pub handle: [u8; 16],
|
||||||
|
pub flags: u32,
|
||||||
|
pub iommu_opts: u32,
|
||||||
|
pub max_vcpus: u32,
|
||||||
|
pub max_evtchn_port: u32,
|
||||||
|
pub max_grant_frames: i32,
|
||||||
|
pub max_maptrack_frames: i32,
|
||||||
|
pub grant_opts: u32,
|
||||||
|
pub altp2m_opts: u32,
|
||||||
|
pub vmtrace_size: u32,
|
||||||
|
pub cpupool_id: u32,
|
||||||
|
pub arch_domain_config: ArchDomainConfig,
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for CreateDomain {
|
impl Default for CreateDomain {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
CreateDomain {
|
CreateDomain {
|
||||||
@ -283,6 +313,32 @@ impl Default for CreateDomain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CreateDomain {
|
||||||
|
pub fn to_cd_2(self) -> CreateDomain2 {
|
||||||
|
CreateDomain2 {
|
||||||
|
ssidref: self.ssidref,
|
||||||
|
handle: self.handle,
|
||||||
|
flags: self.flags,
|
||||||
|
iommu_opts: self.iommu_opts,
|
||||||
|
max_vcpus: self.max_vcpus,
|
||||||
|
max_evtchn_port: self.max_evtchn_port,
|
||||||
|
max_grant_frames: self.max_grant_frames,
|
||||||
|
max_maptrack_frames: self.max_maptrack_frames,
|
||||||
|
grant_opts: self.grant_opts,
|
||||||
|
altp2m_opts: 0,
|
||||||
|
vmtrace_size: self.vmtrace_size,
|
||||||
|
cpupool_id: self.cpupool_id,
|
||||||
|
arch_domain_config: self.arch_domain_config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
pub struct SetDomainHandle {
|
||||||
|
pub handle: [u8; 16],
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
pub struct GetDomainInfo {
|
pub struct GetDomainInfo {
|
||||||
@ -377,7 +433,8 @@ pub struct HypercallInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const XEN_DOMCTL_MIN_INTERFACE_VERSION: u32 = 0x00000015;
|
pub const XEN_DOMCTL_MIN_INTERFACE_VERSION: u32 = 0x00000015;
|
||||||
pub const XEN_DOMCTL_MAX_INTERFACE_VERSION: u32 = 0x00000016;
|
pub const XEN_DOMCTL_MAX_INTERFACE_VERSION: u32 = 0x00000017;
|
||||||
|
pub const XEN_DOMCTL_CREATE_DOMAIN2_INTERFACE_THRESHOLD: u32 = 0x00000017;
|
||||||
|
|
||||||
pub const SECINITSID_DOMU: u32 = 12;
|
pub const SECINITSID_DOMU: u32 = 12;
|
||||||
|
|
||||||
|
@ -10,12 +10,12 @@ resolver = "2"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
|
bit-vec = { workspace = true }
|
||||||
indexmap = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
libc = { workspace = true }
|
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
krata-xencall = { path = "../xencall", version = "^0.0.23" }
|
krata-xencall = { path = "../xencall", version = "^0.0.24" }
|
||||||
krata-xenplatform = { path = "../xenplatform", version = "^0.0.23" }
|
krata-xenplatform = { path = "../xenplatform", version = "^0.0.24" }
|
||||||
krata-xenstore = { path = "../xenstore", version = "^0.0.23" }
|
krata-xenstore = { path = "../xenstore", version = "^0.0.24" }
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
use std::{env, process};
|
use std::{env, process};
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use xenclient::error::Result;
|
use xenclient::error::Result;
|
||||||
use xenclient::{DomainConfig, XenClient};
|
use xenclient::tx::channel::ChannelDeviceConfig;
|
||||||
use xenplatform::domain::BaseDomainConfig;
|
use xenclient::{config::DomainConfig, XenClient};
|
||||||
|
use xenplatform::domain::{
|
||||||
#[cfg(target_arch = "x86_64")]
|
KernelFormat, PlatformDomainConfig, PlatformKernelConfig, PlatformOptions,
|
||||||
type RuntimePlatform = xenplatform::x86pv::X86PvPlatform;
|
PlatformResourcesConfig,
|
||||||
|
};
|
||||||
#[cfg(not(target_arch = "x86_64"))]
|
use xenplatform::RuntimePlatformType;
|
||||||
type RuntimePlatform = xenplatform::unsupported::UnsupportedPlatform;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
@ -22,32 +22,31 @@ async fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
let kernel_image_path = args.get(1).expect("argument not specified");
|
let kernel_image_path = args.get(1).expect("argument not specified");
|
||||||
let initrd_path = args.get(2).expect("argument not specified");
|
let initrd_path = args.get(2).expect("argument not specified");
|
||||||
let client = XenClient::new(0, RuntimePlatform::new()).await?;
|
let client = XenClient::new().await?;
|
||||||
let config = DomainConfig {
|
|
||||||
base: BaseDomainConfig {
|
let mut config = DomainConfig::new();
|
||||||
uuid: Uuid::new_v4(),
|
config.platform(PlatformDomainConfig {
|
||||||
max_vcpus: 1,
|
uuid: Uuid::new_v4(),
|
||||||
target_vcpus: 1,
|
platform: RuntimePlatformType::supported(),
|
||||||
max_mem_mb: 512,
|
kernel: PlatformKernelConfig {
|
||||||
target_mem_mb: 512,
|
data: Arc::new(fs::read(&kernel_image_path).await?),
|
||||||
enable_iommu: true,
|
format: KernelFormat::ElfCompressed,
|
||||||
kernel: fs::read(&kernel_image_path).await?,
|
|
||||||
initrd: fs::read(&initrd_path).await?,
|
|
||||||
cmdline: "earlyprintk=xen earlycon=xen console=hvc0 init=/init".to_string(),
|
cmdline: "earlyprintk=xen earlycon=xen console=hvc0 init=/init".to_string(),
|
||||||
owner_domid: 0,
|
initrd: Some(Arc::new(fs::read(&initrd_path).await?)),
|
||||||
},
|
},
|
||||||
backend_domid: 0,
|
resources: PlatformResourcesConfig {
|
||||||
name: "xenclient-test".to_string(),
|
max_vcpus: 1,
|
||||||
swap_console_backend: None,
|
assigned_vcpus: 1,
|
||||||
disks: vec![],
|
max_memory_mb: 512,
|
||||||
channels: vec![],
|
assigned_memory_mb: 512,
|
||||||
vifs: vec![],
|
},
|
||||||
pcis: vec![],
|
options: PlatformOptions { iommu: true },
|
||||||
filesystems: vec![],
|
});
|
||||||
extra_keys: vec![],
|
config.name("xenclient-test");
|
||||||
extra_rw_paths: vec![],
|
let mut channel = ChannelDeviceConfig::new();
|
||||||
};
|
channel.default_console().backend_initialized();
|
||||||
let created = client.create(&config).await?;
|
config.add_channel(channel);
|
||||||
println!("created domain {}", created.domid);
|
let created = client.create(config).await?;
|
||||||
|
println!("created domain {}", created.platform.domid);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
66
crates/xen/xenclient/examples/boot_speed.rs
Normal file
66
crates/xen/xenclient/examples/boot_speed.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use std::{env, process};
|
||||||
|
use tokio::fs;
|
||||||
|
use uuid::Uuid;
|
||||||
|
use xenclient::config::{DomainConfig, DomainResult};
|
||||||
|
use xenclient::error::Result;
|
||||||
|
use xenclient::tx::channel::ChannelDeviceConfig;
|
||||||
|
use xenclient::XenClient;
|
||||||
|
use xenplatform::domain::{
|
||||||
|
KernelFormat, PlatformDomainConfig, PlatformKernelConfig, PlatformOptions,
|
||||||
|
PlatformResourcesConfig,
|
||||||
|
};
|
||||||
|
use xenplatform::elfloader::ElfImageLoader;
|
||||||
|
use xenplatform::RuntimePlatformType;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
if args.len() != 2 {
|
||||||
|
println!("usage: boot-speed <kernel-image>");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
let kernel_path = args.get(1).expect("argument not specified");
|
||||||
|
let kernel = Arc::new(fs::read(kernel_path).await?);
|
||||||
|
let kernel = ElfImageLoader::load(kernel)?.into_elf_bytes();
|
||||||
|
let client = XenClient::new().await?;
|
||||||
|
|
||||||
|
for i in 0..5u32 {
|
||||||
|
let start = std::time::Instant::now();
|
||||||
|
let domain = create_domain(&client, kernel.clone(), i).await?;
|
||||||
|
let end = std::time::Instant::now();
|
||||||
|
let duration = end - start;
|
||||||
|
println!("boot setup time: {:?}", duration);
|
||||||
|
client.destroy(domain.platform.domid).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_domain(client: &XenClient, kernel: Arc<Vec<u8>>, i: u32) -> Result<DomainResult> {
|
||||||
|
let mut config = DomainConfig::new();
|
||||||
|
config.platform(PlatformDomainConfig {
|
||||||
|
uuid: Uuid::new_v4(),
|
||||||
|
platform: RuntimePlatformType::supported(),
|
||||||
|
kernel: PlatformKernelConfig {
|
||||||
|
data: kernel,
|
||||||
|
format: KernelFormat::ElfUncompressed,
|
||||||
|
cmdline: "earlyprintk=xen earlycon=xen console=hvc0 init=/init".to_string(),
|
||||||
|
initrd: None,
|
||||||
|
},
|
||||||
|
resources: PlatformResourcesConfig {
|
||||||
|
max_vcpus: 1,
|
||||||
|
assigned_vcpus: 1,
|
||||||
|
max_memory_mb: 512,
|
||||||
|
assigned_memory_mb: 512,
|
||||||
|
},
|
||||||
|
options: PlatformOptions { iommu: true },
|
||||||
|
});
|
||||||
|
config.name(format!("xenboot-{}", i));
|
||||||
|
config.start(false);
|
||||||
|
let mut channel = ChannelDeviceConfig::new();
|
||||||
|
channel.default_console().backend_initialized();
|
||||||
|
config.add_channel(channel);
|
||||||
|
client.create(config).await
|
||||||
|
}
|
185
crates/xen/xenclient/src/config.rs
Normal file
185
crates/xen/xenclient/src/config.rs
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use xencall::XenCall;
|
||||||
|
pub use xenplatform::domain::PlatformDomainConfig;
|
||||||
|
use xenplatform::domain::PlatformDomainInfo;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::Result,
|
||||||
|
tx::{
|
||||||
|
channel::ChannelDeviceConfig,
|
||||||
|
fs9p::Fs9pDeviceConfig,
|
||||||
|
pci::PciRootDeviceConfig,
|
||||||
|
vbd::VbdDeviceConfig,
|
||||||
|
vif::VifDeviceConfig,
|
||||||
|
{BlockDeviceResult, DeviceResult},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct DomainConfig {
|
||||||
|
platform: Option<PlatformDomainConfig>,
|
||||||
|
name: Option<String>,
|
||||||
|
backend_domid: u32,
|
||||||
|
channels: Vec<ChannelDeviceConfig>,
|
||||||
|
vifs: Vec<VifDeviceConfig>,
|
||||||
|
vbds: Vec<VbdDeviceConfig>,
|
||||||
|
fs9ps: Vec<Fs9pDeviceConfig>,
|
||||||
|
pci: Option<PciRootDeviceConfig>,
|
||||||
|
extra_keys: HashMap<String, String>,
|
||||||
|
extra_rw_paths: Vec<String>,
|
||||||
|
start: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DomainConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DomainConfig {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
platform: None,
|
||||||
|
name: None,
|
||||||
|
backend_domid: 0,
|
||||||
|
channels: Vec::new(),
|
||||||
|
vifs: Vec::new(),
|
||||||
|
vbds: Vec::new(),
|
||||||
|
fs9ps: Vec::new(),
|
||||||
|
pci: None,
|
||||||
|
extra_keys: HashMap::new(),
|
||||||
|
extra_rw_paths: Vec::new(),
|
||||||
|
start: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn platform(&mut self, platform: PlatformDomainConfig) -> &mut Self {
|
||||||
|
self.platform = Some(platform);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_platform(&self) -> &Option<PlatformDomainConfig> {
|
||||||
|
&self.platform
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&mut self, name: impl AsRef<str>) -> &mut Self {
|
||||||
|
self.name = Some(name.as_ref().to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_name(&self) -> &Option<String> {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn backend_domid(&mut self, backend_domid: u32) -> &mut Self {
|
||||||
|
self.backend_domid = backend_domid;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_backend_domid(&self) -> u32 {
|
||||||
|
self.backend_domid
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_channel(&mut self, channel: ChannelDeviceConfig) -> &mut Self {
|
||||||
|
self.channels.push(channel);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_channels(&self) -> &Vec<ChannelDeviceConfig> {
|
||||||
|
&self.channels
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_vif(&mut self, vif: VifDeviceConfig) -> &mut Self {
|
||||||
|
self.vifs.push(vif);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_vifs(&self) -> &Vec<VifDeviceConfig> {
|
||||||
|
&self.vifs
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_vbd(&mut self, vbd: VbdDeviceConfig) -> &mut Self {
|
||||||
|
self.vbds.push(vbd);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_vbds(&self) -> &Vec<VbdDeviceConfig> {
|
||||||
|
&self.vbds
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_fs9p(&mut self, fs9p: Fs9pDeviceConfig) -> &mut Self {
|
||||||
|
self.fs9ps.push(fs9p);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_fs9ps(&self) -> &Vec<Fs9pDeviceConfig> {
|
||||||
|
&self.fs9ps
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pci(&mut self, pci: PciRootDeviceConfig) -> &mut Self {
|
||||||
|
self.pci = Some(pci);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_pci(&self) -> &Option<PciRootDeviceConfig> {
|
||||||
|
&self.pci
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_extra_key(&mut self, key: impl AsRef<str>, value: impl ToString) -> &mut Self {
|
||||||
|
self.extra_keys
|
||||||
|
.insert(key.as_ref().to_string(), value.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_extra_keys(&self) -> &HashMap<String, String> {
|
||||||
|
&self.extra_keys
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_rw_path(&mut self, path: impl AsRef<str>) -> &mut Self {
|
||||||
|
self.extra_rw_paths.push(path.as_ref().to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_rw_paths(&self) -> &Vec<String> {
|
||||||
|
&self.extra_rw_paths
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(&mut self, start: bool) -> &mut Self {
|
||||||
|
self.start = start;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_start(&self) -> bool {
|
||||||
|
self.start
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn done(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn prepare(
|
||||||
|
&mut self,
|
||||||
|
domid: u32,
|
||||||
|
call: &XenCall,
|
||||||
|
platform: &PlatformDomainInfo,
|
||||||
|
) -> Result<()> {
|
||||||
|
if let Some(pci) = self.pci.as_mut() {
|
||||||
|
pci.prepare(domid, call).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for channel in &mut self.channels {
|
||||||
|
channel.prepare(platform).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DomainResult {
|
||||||
|
pub platform: PlatformDomainInfo,
|
||||||
|
pub channels: Vec<DeviceResult>,
|
||||||
|
pub vifs: Vec<DeviceResult>,
|
||||||
|
pub vbds: Vec<BlockDeviceResult>,
|
||||||
|
pub fs9ps: Vec<DeviceResult>,
|
||||||
|
pub pci: Option<DeviceResult>,
|
||||||
|
}
|
79
crates/xen/xenclient/src/devalloc.rs
Normal file
79
crates/xen/xenclient/src/devalloc.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
use bit_vec::BitVec;
|
||||||
|
|
||||||
|
const DEVICE_COUNT: usize = 4096;
|
||||||
|
const BYTE_COUNT: usize = DEVICE_COUNT / 8;
|
||||||
|
|
||||||
|
pub struct DeviceIdAllocator {
|
||||||
|
states: BitVec,
|
||||||
|
cursor: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DeviceIdAllocator {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeviceIdAllocator {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
states: BitVec::from_elem(DEVICE_COUNT, false),
|
||||||
|
cursor: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize(bytes: &[u8]) -> Option<Self> {
|
||||||
|
if bytes.len() != BYTE_COUNT + 4 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cursor = bytes[0] as u32
|
||||||
|
| ((bytes[1] as u32) << 8)
|
||||||
|
| ((bytes[2] as u32) << 16)
|
||||||
|
| ((bytes[3] as u32) << 24);
|
||||||
|
let slice = &bytes[4..BYTE_COUNT + 4];
|
||||||
|
if slice.len() != BYTE_COUNT {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let states = BitVec::from_bytes(slice);
|
||||||
|
|
||||||
|
Some(Self { states, cursor })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocate(&mut self) -> Option<u32> {
|
||||||
|
let start = self.cursor;
|
||||||
|
loop {
|
||||||
|
let id = self.cursor;
|
||||||
|
let value = self.states.get(self.cursor as usize)?;
|
||||||
|
|
||||||
|
self.cursor = (self.cursor + 1) % DEVICE_COUNT as u32;
|
||||||
|
|
||||||
|
if !value {
|
||||||
|
self.states.set(id as usize, true);
|
||||||
|
return Some(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.cursor == start {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(&mut self, id: u32) {
|
||||||
|
self.states.set(id as usize, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count_free(&mut self) -> u32 {
|
||||||
|
self.states.count_zeros() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize(&mut self) -> Vec<u8> {
|
||||||
|
let mut bytes = Vec::with_capacity(BYTE_COUNT + 4);
|
||||||
|
bytes.push((self.cursor & 0xff) as u8);
|
||||||
|
bytes.push(((self.cursor >> 8) & 0xff) as u8);
|
||||||
|
bytes.push(((self.cursor >> 16) & 0xff) as u8);
|
||||||
|
bytes.push(((self.cursor >> 24) & 0xff) as u8);
|
||||||
|
bytes.extend_from_slice(&self.states.to_bytes());
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
}
|
131
crates/xen/xenclient/src/devstate.rs
Normal file
131
crates/xen/xenclient/src/devstate.rs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use tokio::{
|
||||||
|
select,
|
||||||
|
time::{sleep, timeout},
|
||||||
|
};
|
||||||
|
use xenstore::{XsdClient, XsdInterface};
|
||||||
|
|
||||||
|
use crate::error::{Error, Result};
|
||||||
|
|
||||||
|
pub struct DeviceLocator {
|
||||||
|
pub frontend_domid: u32,
|
||||||
|
pub backend_domid: u32,
|
||||||
|
pub frontend_type: String,
|
||||||
|
pub backend_type: String,
|
||||||
|
pub device_id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeviceLocator {
|
||||||
|
pub fn new(
|
||||||
|
frontend_domid: u32,
|
||||||
|
backend_domid: u32,
|
||||||
|
frontend_type: String,
|
||||||
|
backend_type: String,
|
||||||
|
device_id: u64,
|
||||||
|
) -> Self {
|
||||||
|
DeviceLocator {
|
||||||
|
frontend_domid,
|
||||||
|
backend_domid,
|
||||||
|
frontend_type,
|
||||||
|
backend_type,
|
||||||
|
device_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn frontend_state_path(&self) -> String {
|
||||||
|
format!(
|
||||||
|
"/local/domain/{}/device/{}/{}/state",
|
||||||
|
self.frontend_domid, self.frontend_type, self.device_id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn backend_state_path(&self) -> String {
|
||||||
|
format!(
|
||||||
|
"/local/domain/{}/backend/{}/{}/{}/state",
|
||||||
|
self.backend_domid, self.backend_type, self.frontend_domid, self.device_id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DeviceStateWaiter {
|
||||||
|
devices: Vec<DeviceLocator>,
|
||||||
|
xsd: XsdClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeviceStateWaiter {
|
||||||
|
pub fn new(xsd: XsdClient) -> Self {
|
||||||
|
DeviceStateWaiter {
|
||||||
|
devices: vec![],
|
||||||
|
xsd,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_device(&mut self, device: DeviceLocator) -> &mut DeviceStateWaiter {
|
||||||
|
self.devices.push(device);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn check_states(xsd: &XsdClient, state_paths: &[String], desired: u32) -> Result<bool> {
|
||||||
|
let mut ready = 0;
|
||||||
|
for state_path in state_paths {
|
||||||
|
let Some(state_text) = xsd.read_string(state_path).await? else {
|
||||||
|
return Err(Error::DevStateWaitError(format!(
|
||||||
|
"state path '{}' did not exist",
|
||||||
|
state_path
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(state_value) = state_text.parse::<u32>().ok() else {
|
||||||
|
return Err(Error::DevStateWaitError(format!(
|
||||||
|
"state path '{}' did not have a valid value",
|
||||||
|
state_path
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
|
||||||
|
if state_value > desired {
|
||||||
|
return Err(Error::DevStateWaitError(format!(
|
||||||
|
"state path '{}' had a state of {} which is greater than {}",
|
||||||
|
state_path, state_value, desired
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if state_value == desired {
|
||||||
|
ready += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(ready == state_paths.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_wait(self, desired: u32) -> Result<()> {
|
||||||
|
let mut watch = self.xsd.create_multi_watch().await?;
|
||||||
|
let mut state_paths = Vec::new();
|
||||||
|
for device in self.devices {
|
||||||
|
let state_path = device.backend_state_path();
|
||||||
|
self.xsd.bind_watch_id(watch.id, &state_path).await?;
|
||||||
|
state_paths.push(state_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if DeviceStateWaiter::check_states(&self.xsd, &state_paths, desired).await? {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
select! {
|
||||||
|
_update = watch.receiver.recv() => {},
|
||||||
|
_timeout = sleep(Duration::from_millis(250)) => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn wait(self, desired: u32, deadline: Duration) -> Result<()> {
|
||||||
|
if let Some(err) = timeout(deadline, self.do_wait(desired)).await.err() {
|
||||||
|
return Err(Error::DevStateWaitError(format!(
|
||||||
|
"took too long for devices to be ready: {}",
|
||||||
|
err
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -34,6 +34,8 @@ pub enum Error {
|
|||||||
RegexError(#[from] regex::Error),
|
RegexError(#[from] regex::Error),
|
||||||
#[error("error: {0}")]
|
#[error("error: {0}")]
|
||||||
GenericError(String),
|
GenericError(String),
|
||||||
|
#[error("parameter missing: {0}")]
|
||||||
|
ParameterMissing(&'static str),
|
||||||
#[error("failed to parse int: {0}")]
|
#[error("failed to parse int: {0}")]
|
||||||
ParseIntError(#[from] std::num::ParseIntError),
|
ParseIntError(#[from] std::num::ParseIntError),
|
||||||
#[error("invalid pci bdf string")]
|
#[error("invalid pci bdf string")]
|
||||||
@ -42,6 +44,12 @@ pub enum Error {
|
|||||||
PciDeviceNotAssignable(PciBdf),
|
PciDeviceNotAssignable(PciBdf),
|
||||||
#[error("xen platform error: {0}")]
|
#[error("xen platform error: {0}")]
|
||||||
XenPlatform(#[from] xenplatform::error::Error),
|
XenPlatform(#[from] xenplatform::error::Error),
|
||||||
|
#[error("invalid block index")]
|
||||||
|
InvalidBlockIdx,
|
||||||
|
#[error("device state wait error: {0}")]
|
||||||
|
DevStateWaitError(String),
|
||||||
|
#[error("device ids exhausted")]
|
||||||
|
DevIdExhausted,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use config::{DomainConfig, DomainResult};
|
||||||
|
use error::{Error, Result};
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use pci::PciBdf;
|
|
||||||
use tokio::time::timeout;
|
use tokio::time::timeout;
|
||||||
use tx::ClientTransaction;
|
use tx::{DeviceConfig, XenTransaction};
|
||||||
use xenplatform::boot::BootSetupPlatform;
|
use xenplatform::domain::{PlatformDomainInfo, PlatformDomainManager};
|
||||||
use xenplatform::domain::{BaseDomainConfig, BaseDomainManager, CreatedDomain};
|
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
@ -15,109 +14,26 @@ use std::time::Duration;
|
|||||||
use xencall::XenCall;
|
use xencall::XenCall;
|
||||||
use xenstore::{XsdClient, XsdInterface};
|
use xenstore::{XsdClient, XsdInterface};
|
||||||
|
|
||||||
|
pub mod config;
|
||||||
|
pub mod devalloc;
|
||||||
|
pub mod devstate;
|
||||||
pub mod pci;
|
pub mod pci;
|
||||||
pub mod tx;
|
pub mod tx;
|
||||||
|
pub mod util;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct XenClient<P: BootSetupPlatform> {
|
pub struct XenClient {
|
||||||
pub store: XsdClient,
|
pub store: XsdClient,
|
||||||
pub call: XenCall,
|
pub call: XenCall,
|
||||||
domain_manager: Arc<BaseDomainManager<P>>,
|
domain_manager: Arc<PlatformDomainManager>,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct BlockDeviceRef {
|
|
||||||
pub path: String,
|
|
||||||
pub major: u32,
|
|
||||||
pub minor: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct DomainDisk {
|
|
||||||
pub vdev: String,
|
|
||||||
pub block: BlockDeviceRef,
|
|
||||||
pub writable: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct DomainFilesystem {
|
|
||||||
pub path: String,
|
|
||||||
pub tag: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct DomainNetworkInterface {
|
|
||||||
pub mac: String,
|
|
||||||
pub mtu: u32,
|
|
||||||
pub bridge: Option<String>,
|
|
||||||
pub script: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct DomainChannel {
|
|
||||||
pub typ: String,
|
|
||||||
pub initialized: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct DomainEventChannel {
|
|
||||||
pub name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
|
||||||
pub enum DomainPciRdmReservePolicy {
|
|
||||||
Invalid,
|
|
||||||
#[default]
|
|
||||||
Strict,
|
|
||||||
Relaxed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DomainPciRdmReservePolicy {
|
|
||||||
pub fn to_option_str(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
DomainPciRdmReservePolicy::Invalid => "-1",
|
|
||||||
DomainPciRdmReservePolicy::Strict => "0",
|
|
||||||
DomainPciRdmReservePolicy::Relaxed => "1",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct DomainPciDevice {
|
|
||||||
pub bdf: PciBdf,
|
|
||||||
pub permissive: bool,
|
|
||||||
pub msi_translate: bool,
|
|
||||||
pub power_management: bool,
|
|
||||||
pub rdm_reserve_policy: DomainPciRdmReservePolicy,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct DomainConfig {
|
|
||||||
pub base: BaseDomainConfig,
|
|
||||||
pub backend_domid: u32,
|
|
||||||
pub name: String,
|
|
||||||
pub disks: Vec<DomainDisk>,
|
|
||||||
pub swap_console_backend: Option<String>,
|
|
||||||
pub channels: Vec<DomainChannel>,
|
|
||||||
pub vifs: Vec<DomainNetworkInterface>,
|
|
||||||
pub filesystems: Vec<DomainFilesystem>,
|
|
||||||
pub pcis: Vec<DomainPciDevice>,
|
|
||||||
pub extra_keys: Vec<(String, String)>,
|
|
||||||
pub extra_rw_paths: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct CreatedChannel {
|
|
||||||
pub ring_ref: u64,
|
|
||||||
pub evtchn: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
impl<P: BootSetupPlatform> XenClient<P> {
|
impl XenClient {
|
||||||
pub async fn new(current_domid: u32, platform: P) -> Result<XenClient<P>> {
|
pub async fn new() -> Result<XenClient> {
|
||||||
let store = XsdClient::open().await?;
|
let store = XsdClient::open().await?;
|
||||||
let call: XenCall = XenCall::open(current_domid)?;
|
let call: XenCall = XenCall::open(0)?;
|
||||||
let domain_manager = BaseDomainManager::new(call.clone(), platform).await?;
|
let domain_manager = PlatformDomainManager::new(call.clone()).await?;
|
||||||
Ok(XenClient {
|
Ok(XenClient {
|
||||||
store,
|
store,
|
||||||
call,
|
call,
|
||||||
@ -125,29 +41,47 @@ impl<P: BootSetupPlatform> XenClient<P> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create(&self, config: &DomainConfig) -> Result<CreatedDomain> {
|
pub async fn create(&self, config: DomainConfig) -> Result<DomainResult> {
|
||||||
let created = self.domain_manager.create(config.base.clone()).await?;
|
let platform = config
|
||||||
match self.init(created.domid, config, &created).await {
|
.get_platform()
|
||||||
Ok(_) => Ok(created),
|
.as_ref()
|
||||||
|
.ok_or_else(|| Error::ParameterMissing("platform"))?
|
||||||
|
.clone();
|
||||||
|
let platform = self.domain_manager.create(platform).await?;
|
||||||
|
match self.init(platform.domid, config, &platform).await {
|
||||||
|
Ok(result) => Ok(result),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
// ignore since destroying a domain is best-effort when an error occurs
|
// ignore since destroying a domain is best-effort when an error occurs
|
||||||
let _ = self.domain_manager.destroy(created.domid).await;
|
let _ = self.domain_manager.destroy(platform.domid).await;
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn transaction(&self, domid: u32, backend_domid: u32) -> Result<ClientTransaction> {
|
pub async fn transaction(&self, domid: u32, backend_domid: u32) -> Result<XenTransaction> {
|
||||||
ClientTransaction::new(&self.store, domid, backend_domid).await
|
XenTransaction::new(&self.store, domid, backend_domid).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn init(&self, domid: u32, config: &DomainConfig, created: &CreatedDomain) -> Result<()> {
|
async fn init(
|
||||||
|
&self,
|
||||||
|
domid: u32,
|
||||||
|
mut config: DomainConfig,
|
||||||
|
created: &PlatformDomainInfo,
|
||||||
|
) -> Result<DomainResult> {
|
||||||
trace!("xenclient init domid={} domain={:?}", domid, created);
|
trace!("xenclient init domid={} domain={:?}", domid, created);
|
||||||
let transaction = self.transaction(domid, config.backend_domid).await?;
|
let platform_config = config
|
||||||
transaction
|
.get_platform()
|
||||||
.add_domain_declaration(&config.name, &config.base, created)
|
.as_ref()
|
||||||
.await?;
|
.ok_or_else(|| Error::ParameterMissing("platform"))?;
|
||||||
transaction.commit().await?;
|
loop {
|
||||||
|
let transaction = self.transaction(domid, config.get_backend_domid()).await?;
|
||||||
|
transaction
|
||||||
|
.add_domain_declaration(config.get_name().clone(), platform_config, created)
|
||||||
|
.await?;
|
||||||
|
if transaction.maybe_commit().await? {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if !self
|
if !self
|
||||||
.store
|
.store
|
||||||
.introduce_domain(domid, created.store_mfn, created.store_evtchn)
|
.introduce_domain(domid, created.store_mfn, created.store_evtchn)
|
||||||
@ -155,57 +89,69 @@ impl<P: BootSetupPlatform> XenClient<P> {
|
|||||||
{
|
{
|
||||||
return Err(Error::IntroduceDomainFailed);
|
return Err(Error::IntroduceDomainFailed);
|
||||||
}
|
}
|
||||||
let transaction = self.transaction(domid, config.backend_domid).await?;
|
config.prepare(domid, &self.call, created).await?;
|
||||||
transaction
|
let mut channels;
|
||||||
.add_channel_device(
|
let mut vifs;
|
||||||
created,
|
let mut vbds;
|
||||||
0,
|
let mut fs9ps;
|
||||||
&DomainChannel {
|
let mut pci_result;
|
||||||
typ: config
|
loop {
|
||||||
.swap_console_backend
|
let transaction = self.transaction(domid, config.get_backend_domid()).await?;
|
||||||
.clone()
|
|
||||||
.unwrap_or("xenconsoled".to_string())
|
|
||||||
.to_string(),
|
|
||||||
initialized: true,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
for (index, channel) in config.channels.iter().enumerate() {
|
channels = Vec::new();
|
||||||
transaction
|
for channel in config.get_channels() {
|
||||||
.add_channel_device(created, index + 1, channel)
|
let result = channel.add_to_transaction(&transaction).await?;
|
||||||
.await?;
|
channels.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
vifs = Vec::new();
|
||||||
|
for vif in config.get_vifs() {
|
||||||
|
let result = vif.add_to_transaction(&transaction).await?;
|
||||||
|
vifs.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
vbds = Vec::new();
|
||||||
|
for vbd in config.get_vbds() {
|
||||||
|
let result = vbd.add_to_transaction(&transaction).await?;
|
||||||
|
vbds.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs9ps = Vec::new();
|
||||||
|
for fs9p in config.get_fs9ps() {
|
||||||
|
let result = fs9p.add_to_transaction(&transaction).await?;
|
||||||
|
fs9ps.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
pci_result = None;
|
||||||
|
if let Some(pci) = config.get_pci().as_ref() {
|
||||||
|
pci_result = Some(pci.add_to_transaction(&transaction).await?);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (key, value) in config.get_extra_keys() {
|
||||||
|
transaction.write(key, value, None).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for rw_path in config.get_rw_paths() {
|
||||||
|
transaction.add_rw_path(rw_path).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if transaction.maybe_commit().await? {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (index, disk) in config.disks.iter().enumerate() {
|
if config.get_start() {
|
||||||
transaction.add_vbd_device(index, disk).await?;
|
self.call.unpause_domain(domid).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (index, filesystem) in config.filesystems.iter().enumerate() {
|
Ok(DomainResult {
|
||||||
transaction.add_9pfs_device(index, filesystem).await?;
|
platform: created.clone(),
|
||||||
}
|
channels,
|
||||||
|
vifs,
|
||||||
for (index, vif) in config.vifs.iter().enumerate() {
|
vbds,
|
||||||
transaction.add_vif_device(index, vif).await?;
|
fs9ps,
|
||||||
}
|
pci: pci_result,
|
||||||
|
})
|
||||||
for (index, pci) in config.pcis.iter().enumerate() {
|
|
||||||
transaction
|
|
||||||
.add_pci_device(&self.call, index, config.pcis.len(), pci)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (key, value) in &config.extra_keys {
|
|
||||||
transaction.write_key(key, value).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
for key in &config.extra_rw_paths {
|
|
||||||
transaction.add_rw_path(key).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
transaction.commit().await?;
|
|
||||||
self.call.unpause_domain(domid).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn destroy(&self, domid: u32) -> Result<()> {
|
pub async fn destroy(&self, domid: u32) -> Result<()> {
|
||||||
@ -251,39 +197,7 @@ impl<P: BootSetupPlatform> XenClient<P> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for backend in &backend_paths {
|
for backend in &backend_paths {
|
||||||
let state_path = format!("{}/state", backend);
|
self.destroy_backend(backend).await?;
|
||||||
let mut watch = self.store.create_watch(&state_path).await?;
|
|
||||||
let online_path = format!("{}/online", backend);
|
|
||||||
let tx = self.store.transaction().await?;
|
|
||||||
let state = tx.read_string(&state_path).await?.unwrap_or(String::new());
|
|
||||||
if state.is_empty() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tx.write_string(&online_path, "0").await?;
|
|
||||||
if !state.is_empty() && u32::from_str(&state).unwrap_or(0) != 6 {
|
|
||||||
tx.write_string(&state_path, "5").await?;
|
|
||||||
}
|
|
||||||
self.store.bind_watch(&watch).await?;
|
|
||||||
tx.commit().await?;
|
|
||||||
|
|
||||||
let mut count: u32 = 0;
|
|
||||||
loop {
|
|
||||||
if count >= 3 {
|
|
||||||
debug!("unable to safely destroy backend: {}", backend);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let _ = timeout(Duration::from_secs(1), watch.receiver.recv()).await;
|
|
||||||
let state = self
|
|
||||||
.store
|
|
||||||
.read_string(&state_path)
|
|
||||||
.await?
|
|
||||||
.unwrap_or_else(|| "6".to_string());
|
|
||||||
let state = i64::from_str(&state).unwrap_or(-1);
|
|
||||||
if state == 6 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let tx = self.store.transaction().await?;
|
let tx = self.store.transaction().await?;
|
||||||
@ -305,4 +219,72 @@ impl<P: BootSetupPlatform> XenClient<P> {
|
|||||||
tx.commit().await?;
|
tx.commit().await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn destroy_backend(&self, backend: &str) -> Result<()> {
|
||||||
|
let state_path = format!("{}/state", backend);
|
||||||
|
let mut watch = self.store.create_watch(&state_path).await?;
|
||||||
|
let online_path = format!("{}/online", backend);
|
||||||
|
let tx = self.store.transaction().await?;
|
||||||
|
let state = tx.read_string(&state_path).await?.unwrap_or(String::new());
|
||||||
|
if state.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tx.write_string(&online_path, "0").await?;
|
||||||
|
if !state.is_empty() && u32::from_str(&state).unwrap_or(0) != 6 {
|
||||||
|
tx.write_string(&state_path, "5").await?;
|
||||||
|
}
|
||||||
|
self.store.bind_watch(&watch).await?;
|
||||||
|
tx.commit().await?;
|
||||||
|
|
||||||
|
let mut count: u32 = 0;
|
||||||
|
loop {
|
||||||
|
if count >= 3 {
|
||||||
|
debug!("unable to safely destroy backend: {}", backend);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let _ = timeout(Duration::from_secs(1), watch.receiver.recv()).await;
|
||||||
|
let state = self
|
||||||
|
.store
|
||||||
|
.read_string(&state_path)
|
||||||
|
.await?
|
||||||
|
.unwrap_or_else(|| "6".to_string());
|
||||||
|
let state = i64::from_str(&state).unwrap_or(-1);
|
||||||
|
if state == 6 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
self.store.rm(backend).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn destroy_device(
|
||||||
|
&self,
|
||||||
|
category: &str,
|
||||||
|
domid: u32,
|
||||||
|
devid: u64,
|
||||||
|
blkid: Option<u32>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let dom_path = self.store.get_domain_path(domid).await?;
|
||||||
|
let device_path = format!("{}/device/{}/{}", dom_path, category, devid);
|
||||||
|
if let Some(backend_path) = self
|
||||||
|
.store
|
||||||
|
.read_string(format!("{}/backend", device_path).as_str())
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
self.destroy_backend(&backend_path).await?;
|
||||||
|
}
|
||||||
|
self.destroy_backend(&device_path).await?;
|
||||||
|
loop {
|
||||||
|
let tx = self.transaction(domid, 0).await?;
|
||||||
|
tx.release_devid(devid).await?;
|
||||||
|
if let Some(blkid) = blkid {
|
||||||
|
tx.release_blkid(blkid).await?;
|
||||||
|
}
|
||||||
|
if tx.maybe_commit().await? {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,582 +0,0 @@
|
|||||||
use indexmap::IndexMap;
|
|
||||||
use xencall::{sys::DOMCTL_DEV_RDM_RELAXED, XenCall};
|
|
||||||
use xenplatform::{
|
|
||||||
domain::{BaseDomainConfig, CreatedDomain},
|
|
||||||
sys::XEN_PAGE_SHIFT,
|
|
||||||
};
|
|
||||||
use xenstore::{
|
|
||||||
XsPermission, XsdClient, XsdInterface, XsdTransaction, XS_PERM_NONE, XS_PERM_READ,
|
|
||||||
XS_PERM_READ_WRITE,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
error::{Error, Result},
|
|
||||||
pci::XenPciBackend,
|
|
||||||
DomainChannel, DomainDisk, DomainFilesystem, DomainNetworkInterface, DomainPciDevice,
|
|
||||||
DomainPciRdmReservePolicy,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct ClientTransaction {
|
|
||||||
tx: XsdTransaction,
|
|
||||||
abort: bool,
|
|
||||||
domid: u32,
|
|
||||||
dom_path: String,
|
|
||||||
backend_domid: u32,
|
|
||||||
backend_dom_path: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClientTransaction {
|
|
||||||
pub async fn new(store: &XsdClient, domid: u32, backend_domid: u32) -> Result<Self> {
|
|
||||||
let backend_dom_path = store.get_domain_path(0).await?;
|
|
||||||
let dom_path = store.get_domain_path(domid).await?;
|
|
||||||
Ok(ClientTransaction {
|
|
||||||
tx: store.transaction().await?,
|
|
||||||
abort: true,
|
|
||||||
domid,
|
|
||||||
dom_path,
|
|
||||||
backend_domid,
|
|
||||||
backend_dom_path,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add_domain_declaration(
|
|
||||||
&self,
|
|
||||||
name: impl AsRef<str>,
|
|
||||||
base: &BaseDomainConfig,
|
|
||||||
created: &CreatedDomain,
|
|
||||||
) -> Result<()> {
|
|
||||||
let vm_path = format!("/vm/{}", base.uuid);
|
|
||||||
let ro_perm = &[
|
|
||||||
XsPermission {
|
|
||||||
id: 0,
|
|
||||||
perms: XS_PERM_NONE,
|
|
||||||
},
|
|
||||||
XsPermission {
|
|
||||||
id: self.domid,
|
|
||||||
perms: XS_PERM_READ,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
let no_perm = &[XsPermission {
|
|
||||||
id: 0,
|
|
||||||
perms: XS_PERM_NONE,
|
|
||||||
}];
|
|
||||||
|
|
||||||
let rw_perm = &[XsPermission {
|
|
||||||
id: self.domid,
|
|
||||||
perms: XS_PERM_READ_WRITE,
|
|
||||||
}];
|
|
||||||
|
|
||||||
self.tx.rm(&self.dom_path).await?;
|
|
||||||
self.tx.mknod(&self.dom_path, ro_perm).await?;
|
|
||||||
|
|
||||||
self.tx.rm(&vm_path).await?;
|
|
||||||
self.tx.mknod(&vm_path, ro_perm).await?;
|
|
||||||
|
|
||||||
self.tx.mknod(&vm_path, no_perm).await?;
|
|
||||||
self.tx
|
|
||||||
.mknod(format!("{}/device", vm_path).as_str(), no_perm)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
self.tx
|
|
||||||
.write_string(format!("{}/vm", self.dom_path).as_str(), &vm_path)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
self.tx
|
|
||||||
.mknod(format!("{}/cpu", self.dom_path).as_str(), ro_perm)
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.mknod(format!("{}/memory", self.dom_path).as_str(), ro_perm)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
self.tx
|
|
||||||
.mknod(format!("{}/control", self.dom_path).as_str(), ro_perm)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
self.tx
|
|
||||||
.mknod(
|
|
||||||
format!("{}/control/shutdown", self.dom_path).as_str(),
|
|
||||||
rw_perm,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.mknod(
|
|
||||||
format!("{}/control/feature-poweroff", self.dom_path).as_str(),
|
|
||||||
rw_perm,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.mknod(
|
|
||||||
format!("{}/control/feature-reboot", self.dom_path).as_str(),
|
|
||||||
rw_perm,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.mknod(
|
|
||||||
format!("{}/control/feature-suspend", self.dom_path).as_str(),
|
|
||||||
rw_perm,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.mknod(format!("{}/control/sysrq", self.dom_path).as_str(), rw_perm)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
self.tx
|
|
||||||
.mknod(format!("{}/data", self.dom_path).as_str(), rw_perm)
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.mknod(format!("{}/drivers", self.dom_path).as_str(), rw_perm)
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.mknod(format!("{}/feature", self.dom_path).as_str(), rw_perm)
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.mknod(format!("{}/attr", self.dom_path).as_str(), rw_perm)
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.mknod(format!("{}/error", self.dom_path).as_str(), rw_perm)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
self.tx
|
|
||||||
.write_string(format!("{}/uuid", vm_path).as_str(), &base.uuid.to_string())
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.write_string(format!("{}/name", self.dom_path).as_str(), name.as_ref())
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.write_string(format!("{}/name", vm_path).as_str(), name.as_ref())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
self.tx
|
|
||||||
.write_string(format!("{}/image/os_type", vm_path).as_str(), "linux")
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.write_string(format!("{}/image/cmdline", vm_path).as_str(), &base.cmdline)
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.write_string(
|
|
||||||
format!("{}/memory/static-max", self.dom_path).as_str(),
|
|
||||||
&(base.max_mem_mb * 1024).to_string(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.write_string(
|
|
||||||
format!("{}/memory/target", self.dom_path).as_str(),
|
|
||||||
&(base.target_mem_mb * 1024).to_string(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.write_string(format!("{}/memory/videoram", self.dom_path).as_str(), "0")
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.write_string(
|
|
||||||
format!("{}/domid", self.dom_path).as_str(),
|
|
||||||
&created.domid.to_string(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.write_string(format!("{}/type", self.dom_path).as_str(), "PV")
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.write_string(
|
|
||||||
format!("{}/store/port", self.dom_path).as_str(),
|
|
||||||
&created.store_evtchn.to_string(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.write_string(
|
|
||||||
format!("{}/store/ring-ref", self.dom_path).as_str(),
|
|
||||||
&created.store_mfn.to_string(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
for i in 0..base.max_vcpus {
|
|
||||||
let path = format!("{}/cpu/{}", self.dom_path, i);
|
|
||||||
self.tx.mkdir(&path).await?;
|
|
||||||
self.tx.set_perms(&path, ro_perm).await?;
|
|
||||||
let path = format!("{}/cpu/{}/availability", self.dom_path, i);
|
|
||||||
self.tx
|
|
||||||
.write_string(
|
|
||||||
&path,
|
|
||||||
if i < base.target_vcpus {
|
|
||||||
"online"
|
|
||||||
} else {
|
|
||||||
"offline"
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
self.tx.set_perms(&path, ro_perm).await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn write_key(&self, key: impl AsRef<str>, value: impl AsRef<str>) -> Result<()> {
|
|
||||||
self.tx
|
|
||||||
.write_string(
|
|
||||||
&format!("{}/{}", self.dom_path, key.as_ref()),
|
|
||||||
value.as_ref(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add_rw_path(&self, key: impl AsRef<str>) -> Result<()> {
|
|
||||||
let rw_perm = &[XsPermission {
|
|
||||||
id: self.domid,
|
|
||||||
perms: XS_PERM_READ_WRITE,
|
|
||||||
}];
|
|
||||||
|
|
||||||
self.tx
|
|
||||||
.mknod(&format!("{}/{}", self.dom_path, key.as_ref()), rw_perm)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add_device(
|
|
||||||
&self,
|
|
||||||
typ: impl AsRef<str>,
|
|
||||||
id: u64,
|
|
||||||
frontend_items: Vec<(&str, String)>,
|
|
||||||
backend_items: Vec<(&str, String)>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let console_zero = typ.as_ref() == "console" && id == 0;
|
|
||||||
|
|
||||||
let frontend_path = if console_zero {
|
|
||||||
format!("{}/console", self.dom_path)
|
|
||||||
} else {
|
|
||||||
format!("{}/device/{}/{}", self.dom_path, typ.as_ref(), id)
|
|
||||||
};
|
|
||||||
let backend_path = format!(
|
|
||||||
"{}/backend/{}/{}/{}",
|
|
||||||
self.backend_dom_path,
|
|
||||||
typ.as_ref(),
|
|
||||||
self.domid,
|
|
||||||
id
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut backend_items: Vec<(&str, String)> = backend_items.clone();
|
|
||||||
let mut frontend_items: Vec<(&str, String)> = frontend_items.clone();
|
|
||||||
backend_items.push(("frontend", frontend_path.clone()));
|
|
||||||
frontend_items.push(("backend", backend_path.clone()));
|
|
||||||
let frontend_perms = &[
|
|
||||||
XsPermission {
|
|
||||||
id: self.domid,
|
|
||||||
perms: XS_PERM_NONE,
|
|
||||||
},
|
|
||||||
XsPermission {
|
|
||||||
id: self.backend_domid,
|
|
||||||
perms: XS_PERM_READ,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
let backend_perms = &[
|
|
||||||
XsPermission {
|
|
||||||
id: self.backend_domid,
|
|
||||||
perms: XS_PERM_NONE,
|
|
||||||
},
|
|
||||||
XsPermission {
|
|
||||||
id: self.domid,
|
|
||||||
perms: XS_PERM_READ,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
self.tx.mknod(&frontend_path, frontend_perms).await?;
|
|
||||||
for (p, value) in &frontend_items {
|
|
||||||
let path = format!("{}/{}", frontend_path, *p);
|
|
||||||
self.tx.write_string(&path, value).await?;
|
|
||||||
if !console_zero {
|
|
||||||
self.tx.set_perms(&path, frontend_perms).await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.tx.mknod(&backend_path, backend_perms).await?;
|
|
||||||
for (p, value) in &backend_items {
|
|
||||||
let path = format!("{}/{}", backend_path, *p);
|
|
||||||
self.tx.write_string(&path, value).await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add_vbd_device(&self, index: usize, disk: &DomainDisk) -> Result<()> {
|
|
||||||
let id = (202 << 8) | (index << 4) as u64;
|
|
||||||
let backend_items: Vec<(&str, String)> = vec![
|
|
||||||
("frontend-id", self.domid.to_string()),
|
|
||||||
("online", "1".to_string()),
|
|
||||||
("removable", "0".to_string()),
|
|
||||||
("bootable", "1".to_string()),
|
|
||||||
("state", "1".to_string()),
|
|
||||||
("dev", disk.vdev.to_string()),
|
|
||||||
("type", "phy".to_string()),
|
|
||||||
("mode", if disk.writable { "w" } else { "r" }.to_string()),
|
|
||||||
("device-type", "disk".to_string()),
|
|
||||||
("discard-enable", "0".to_string()),
|
|
||||||
("specification", "xen".to_string()),
|
|
||||||
("physical-device-path", disk.block.path.to_string()),
|
|
||||||
(
|
|
||||||
"physical-device",
|
|
||||||
format!("{:02x}:{:02x}", disk.block.major, disk.block.minor),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
let frontend_items: Vec<(&str, String)> = vec![
|
|
||||||
("backend-id", self.backend_domid.to_string()),
|
|
||||||
("state", "1".to_string()),
|
|
||||||
("virtual-device", id.to_string()),
|
|
||||||
("device-type", "disk".to_string()),
|
|
||||||
("trusted", "1".to_string()),
|
|
||||||
("protocol", "x86_64-abi".to_string()),
|
|
||||||
];
|
|
||||||
|
|
||||||
self.add_device("vbd", id, frontend_items, backend_items)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add_vif_device(&self, index: usize, vif: &DomainNetworkInterface) -> Result<()> {
|
|
||||||
let id = 20 + index as u64;
|
|
||||||
let mut backend_items: Vec<(&str, String)> = vec![
|
|
||||||
("frontend-id", self.domid.to_string()),
|
|
||||||
("online", "1".to_string()),
|
|
||||||
("state", "1".to_string()),
|
|
||||||
("mac", vif.mac.to_string()),
|
|
||||||
("mtu", vif.mtu.to_string()),
|
|
||||||
("type", "vif".to_string()),
|
|
||||||
("handle", id.to_string()),
|
|
||||||
];
|
|
||||||
|
|
||||||
if vif.bridge.is_some() {
|
|
||||||
backend_items.extend_from_slice(&[("bridge", vif.bridge.clone().unwrap())]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if vif.script.is_some() {
|
|
||||||
backend_items.extend_from_slice(&[
|
|
||||||
("script", vif.script.clone().unwrap()),
|
|
||||||
("hotplug-status", "".to_string()),
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
backend_items.extend_from_slice(&[
|
|
||||||
("script", "".to_string()),
|
|
||||||
("hotplug-status", "connected".to_string()),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let frontend_items: Vec<(&str, String)> = vec![
|
|
||||||
("backend-id", self.backend_domid.to_string()),
|
|
||||||
("state", "1".to_string()),
|
|
||||||
("mac", vif.mac.to_string()),
|
|
||||||
("trusted", "1".to_string()),
|
|
||||||
("mtu", vif.mtu.to_string()),
|
|
||||||
];
|
|
||||||
|
|
||||||
self.add_device("vif", id, frontend_items, backend_items)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add_9pfs_device(&self, index: usize, filesystem: &DomainFilesystem) -> Result<()> {
|
|
||||||
let id = 90 + index as u64;
|
|
||||||
let backend_items: Vec<(&str, String)> = vec![
|
|
||||||
("frontend-id", self.domid.to_string()),
|
|
||||||
("online", "1".to_string()),
|
|
||||||
("state", "1".to_string()),
|
|
||||||
("path", filesystem.path.to_string()),
|
|
||||||
("security-model", "none".to_string()),
|
|
||||||
];
|
|
||||||
|
|
||||||
let frontend_items: Vec<(&str, String)> = vec![
|
|
||||||
("backend-id", self.backend_domid.to_string()),
|
|
||||||
("state", "1".to_string()),
|
|
||||||
("tag", filesystem.tag.to_string()),
|
|
||||||
];
|
|
||||||
|
|
||||||
self.add_device("9pfs", id, frontend_items, backend_items)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add_channel_device(
|
|
||||||
&self,
|
|
||||||
domain: &CreatedDomain,
|
|
||||||
index: usize,
|
|
||||||
channel: &DomainChannel,
|
|
||||||
) -> Result<()> {
|
|
||||||
let port = domain.console_evtchn;
|
|
||||||
let ring = domain.console_mfn;
|
|
||||||
|
|
||||||
let mut backend_items = vec![
|
|
||||||
("frontend-id", self.domid.to_string()),
|
|
||||||
("online", "1".to_string()),
|
|
||||||
("protocol", "vt100".to_string()),
|
|
||||||
];
|
|
||||||
|
|
||||||
let mut frontend_items = vec![
|
|
||||||
("backend-id", self.backend_domid.to_string()),
|
|
||||||
("limit", "1048576".to_string()),
|
|
||||||
("output", "pty".to_string()),
|
|
||||||
("tty", "".to_string()),
|
|
||||||
];
|
|
||||||
|
|
||||||
frontend_items.push(("type", channel.typ.clone()));
|
|
||||||
backend_items.push(("type", channel.typ.clone()));
|
|
||||||
|
|
||||||
if index == 0 {
|
|
||||||
if channel.typ != "xenconsoled" {
|
|
||||||
frontend_items.push(("state", "1".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
frontend_items
|
|
||||||
.extend_from_slice(&[("port", port.to_string()), ("ring-ref", ring.to_string())]);
|
|
||||||
} else {
|
|
||||||
frontend_items.extend_from_slice(&[
|
|
||||||
("state", "1".to_string()),
|
|
||||||
("protocol", "vt100".to_string()),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if channel.initialized {
|
|
||||||
backend_items.push(("state", "4".to_string()));
|
|
||||||
} else {
|
|
||||||
backend_items.push(("state", "1".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.add_device("console", index as u64, frontend_items, backend_items)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add_pci_device(
|
|
||||||
&self,
|
|
||||||
call: &XenCall,
|
|
||||||
index: usize,
|
|
||||||
device_count: usize,
|
|
||||||
device: &DomainPciDevice,
|
|
||||||
) -> Result<()> {
|
|
||||||
let backend = XenPciBackend::new();
|
|
||||||
if !backend.is_assigned(&device.bdf).await? {
|
|
||||||
return Err(Error::PciDeviceNotAssignable(device.bdf));
|
|
||||||
}
|
|
||||||
let resources = backend.read_resources(&device.bdf).await?;
|
|
||||||
for resource in resources {
|
|
||||||
if resource.is_bar_io() {
|
|
||||||
call.ioport_permission(
|
|
||||||
self.domid,
|
|
||||||
resource.start as u32,
|
|
||||||
resource.size() as u32,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
} else {
|
|
||||||
call.iomem_permission(
|
|
||||||
self.domid,
|
|
||||||
resource.start >> XEN_PAGE_SHIFT,
|
|
||||||
(resource.size() + (XEN_PAGE_SHIFT - 1)) >> XEN_PAGE_SHIFT,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(irq) = backend.read_irq(&device.bdf).await? {
|
|
||||||
let irq = call.map_pirq(self.domid, irq as isize, None).await?;
|
|
||||||
call.irq_permission(self.domid, irq, true).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
backend.reset(&device.bdf).await?;
|
|
||||||
|
|
||||||
call.assign_device(
|
|
||||||
self.domid,
|
|
||||||
device.bdf.encode(),
|
|
||||||
if device.rdm_reserve_policy == DomainPciRdmReservePolicy::Relaxed {
|
|
||||||
DOMCTL_DEV_RDM_RELAXED
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if device.permissive {
|
|
||||||
backend.enable_permissive(&device.bdf).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let id = 60;
|
|
||||||
|
|
||||||
if index == 0 {
|
|
||||||
let backend_items: Vec<(&str, String)> = vec![
|
|
||||||
("frontend-id", self.domid.to_string()),
|
|
||||||
("online", "1".to_string()),
|
|
||||||
("state", "1".to_string()),
|
|
||||||
("num_devs", device_count.to_string()),
|
|
||||||
];
|
|
||||||
|
|
||||||
let frontend_items: Vec<(&str, String)> = vec![
|
|
||||||
("backend-id", self.backend_domid.to_string()),
|
|
||||||
("state", "1".to_string()),
|
|
||||||
];
|
|
||||||
|
|
||||||
self.add_device("pci", id, frontend_items, backend_items)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let backend_path = format!(
|
|
||||||
"{}/backend/{}/{}/{}",
|
|
||||||
self.backend_dom_path, "pci", self.domid, id
|
|
||||||
);
|
|
||||||
|
|
||||||
self.tx
|
|
||||||
.write_string(
|
|
||||||
format!("{}/key-{}", backend_path, index),
|
|
||||||
&device.bdf.to_string(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
self.tx
|
|
||||||
.write_string(
|
|
||||||
format!("{}/dev-{}", backend_path, index),
|
|
||||||
&device.bdf.to_string(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if let Some(vdefn) = device.bdf.vdefn {
|
|
||||||
self.tx
|
|
||||||
.write_string(
|
|
||||||
format!("{}/vdefn-{}", backend_path, index),
|
|
||||||
&format!("{:#x}", vdefn),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut options = IndexMap::new();
|
|
||||||
options.insert("permissive", if device.permissive { "1" } else { "0" });
|
|
||||||
options.insert("rdm_policy", device.rdm_reserve_policy.to_option_str());
|
|
||||||
options.insert("msitranslate", if device.msi_translate { "1" } else { "0" });
|
|
||||||
options.insert(
|
|
||||||
"power_mgmt",
|
|
||||||
if device.power_management { "1" } else { "0" },
|
|
||||||
);
|
|
||||||
let options = options
|
|
||||||
.into_iter()
|
|
||||||
.map(|(key, value)| format!("{}={}", key, value))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(",");
|
|
||||||
|
|
||||||
self.tx
|
|
||||||
.write_string(format!("{}/opts-{}", backend_path, index), &options)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn commit(mut self) -> Result<()> {
|
|
||||||
self.abort = false;
|
|
||||||
self.tx.commit().await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for ClientTransaction {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if !self.abort {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let tx = self.tx.clone();
|
|
||||||
tokio::task::spawn(async move {
|
|
||||||
let _ = tx.abort().await;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
89
crates/xen/xenclient/src/tx/channel.rs
Normal file
89
crates/xen/xenclient/src/tx/channel.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
use xenplatform::domain::PlatformDomainInfo;
|
||||||
|
|
||||||
|
use super::{DeviceConfig, DeviceDescription, DeviceResult, XenTransaction};
|
||||||
|
use crate::error::{Error, Result};
|
||||||
|
|
||||||
|
pub struct ChannelDeviceConfig {
|
||||||
|
backend_type: String,
|
||||||
|
default_console: bool,
|
||||||
|
default_console_options: Option<(u32, u64)>,
|
||||||
|
backend_initialized: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ChannelDeviceConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChannelDeviceConfig {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
backend_type: "console".to_string(),
|
||||||
|
default_console: false,
|
||||||
|
default_console_options: None,
|
||||||
|
backend_initialized: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn backend_type(&mut self, backend_type: impl AsRef<str>) -> &mut Self {
|
||||||
|
self.backend_type = backend_type.as_ref().to_string();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default_console(&mut self) -> &mut Self {
|
||||||
|
self.default_console = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn backend_initialized(&mut self) -> &mut Self {
|
||||||
|
self.backend_initialized = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn done(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn prepare(&mut self, platform: &PlatformDomainInfo) -> Result<()> {
|
||||||
|
if self.default_console {
|
||||||
|
self.default_console_options = Some((platform.console_evtchn, platform.console_mfn));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl DeviceConfig for ChannelDeviceConfig {
|
||||||
|
type Result = DeviceResult;
|
||||||
|
|
||||||
|
async fn add_to_transaction(&self, tx: &XenTransaction) -> Result<DeviceResult> {
|
||||||
|
let id = tx.assign_next_devid().await?;
|
||||||
|
let mut device = DeviceDescription::new("console", &self.backend_type);
|
||||||
|
device
|
||||||
|
.add_backend_bool("online", true)
|
||||||
|
.add_backend_item("protocol", "vt100")
|
||||||
|
.add_backend_item("type", &self.backend_type)
|
||||||
|
.add_backend_item("state", if self.backend_initialized { 4 } else { 1 });
|
||||||
|
|
||||||
|
if self.default_console {
|
||||||
|
device.special_frontend_path("console");
|
||||||
|
let (port, ring_ref) = self
|
||||||
|
.default_console_options
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| Error::ParameterMissing("default_console_options"))?;
|
||||||
|
device
|
||||||
|
.add_frontend_item("port", port)
|
||||||
|
.add_frontend_item("ring-ref", ring_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
device
|
||||||
|
.add_frontend_item("limit", 1048576)
|
||||||
|
.add_frontend_item("output", "pty")
|
||||||
|
.add_frontend_item("tty", "")
|
||||||
|
.add_frontend_item("type", &self.backend_type)
|
||||||
|
.add_frontend_item("state", 1);
|
||||||
|
tx.add_device(id, device).await?;
|
||||||
|
Ok(DeviceResult { id })
|
||||||
|
}
|
||||||
|
}
|
78
crates/xen/xenclient/src/tx/fs9p.rs
Normal file
78
crates/xen/xenclient/src/tx/fs9p.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use super::{DeviceConfig, DeviceDescription, DeviceResult, XenTransaction};
|
||||||
|
use crate::error::{Error, Result};
|
||||||
|
|
||||||
|
pub struct Fs9pDeviceConfig {
|
||||||
|
backend_type: String,
|
||||||
|
security_model: String,
|
||||||
|
path: Option<String>,
|
||||||
|
tag: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Fs9pDeviceConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fs9pDeviceConfig {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
backend_type: "9pfs".to_string(),
|
||||||
|
security_model: "none".to_string(),
|
||||||
|
path: None,
|
||||||
|
tag: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn backend_type(&mut self, backend_type: impl AsRef<str>) -> &mut Self {
|
||||||
|
self.backend_type = backend_type.as_ref().to_string();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn security_model(&mut self, security_model: impl AsRef<str>) -> &mut Self {
|
||||||
|
self.security_model = security_model.as_ref().to_string();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path(&mut self, path: impl AsRef<str>) -> &mut Self {
|
||||||
|
self.path = Some(path.as_ref().to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tag(&mut self, tag: impl AsRef<str>) -> &mut Self {
|
||||||
|
self.tag = Some(tag.as_ref().to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn done(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl DeviceConfig for Fs9pDeviceConfig {
|
||||||
|
type Result = DeviceResult;
|
||||||
|
|
||||||
|
async fn add_to_transaction(&self, tx: &XenTransaction) -> Result<DeviceResult> {
|
||||||
|
let id = tx.assign_next_devid().await?;
|
||||||
|
let path = self
|
||||||
|
.path
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| Error::ParameterMissing("path"))?;
|
||||||
|
let tag = self
|
||||||
|
.tag
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| Error::ParameterMissing("tag"))?;
|
||||||
|
let mut device = DeviceDescription::new("9pfs", &self.backend_type);
|
||||||
|
device
|
||||||
|
.add_backend_bool("online", true)
|
||||||
|
.add_backend_item("state", 1)
|
||||||
|
.add_backend_item("path", path)
|
||||||
|
.add_backend_item("security_model", &self.security_model);
|
||||||
|
device
|
||||||
|
.add_frontend_item("state", 1)
|
||||||
|
.add_frontend_item("tag", tag);
|
||||||
|
tx.add_device(id, device).await?;
|
||||||
|
Ok(DeviceResult { id })
|
||||||
|
}
|
||||||
|
}
|
425
crates/xen/xenclient/src/tx/mod.rs
Normal file
425
crates/xen/xenclient/src/tx/mod.rs
Normal file
@ -0,0 +1,425 @@
|
|||||||
|
pub mod channel;
|
||||||
|
pub mod fs9p;
|
||||||
|
pub mod pci;
|
||||||
|
pub mod vbd;
|
||||||
|
pub mod vif;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
devalloc::DeviceIdAllocator,
|
||||||
|
error::{Error, Result},
|
||||||
|
};
|
||||||
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use xenplatform::domain::{PlatformDomainConfig, PlatformDomainInfo};
|
||||||
|
use xenstore::{
|
||||||
|
XsPermission, XsdClient, XsdInterface, XsdTransaction, XS_PERM_NONE, XS_PERM_READ,
|
||||||
|
XS_PERM_READ_WRITE,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct XenTransaction {
|
||||||
|
frontend_domid: u32,
|
||||||
|
frontend_dom_path: String,
|
||||||
|
backend_domid: u32,
|
||||||
|
backend_dom_path: String,
|
||||||
|
blkalloc: Arc<Mutex<DeviceIdAllocator>>,
|
||||||
|
devalloc: Arc<Mutex<DeviceIdAllocator>>,
|
||||||
|
tx: XsdTransaction,
|
||||||
|
abort: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XenTransaction {
|
||||||
|
pub async fn new(store: &XsdClient, frontend_domid: u32, backend_domid: u32) -> Result<Self> {
|
||||||
|
let frontend_dom_path = store.get_domain_path(frontend_domid).await?;
|
||||||
|
let backend_dom_path = store.get_domain_path(backend_domid).await?;
|
||||||
|
let tx = store.transaction().await?;
|
||||||
|
|
||||||
|
let devalloc = XenTransaction::load_id_allocator(&tx, "devid", &frontend_dom_path).await?;
|
||||||
|
let blkalloc = XenTransaction::load_id_allocator(&tx, "blkid", &frontend_dom_path).await?;
|
||||||
|
|
||||||
|
Ok(XenTransaction {
|
||||||
|
frontend_domid,
|
||||||
|
frontend_dom_path,
|
||||||
|
backend_domid,
|
||||||
|
backend_dom_path,
|
||||||
|
tx,
|
||||||
|
devalloc: Arc::new(Mutex::new(devalloc)),
|
||||||
|
blkalloc: Arc::new(Mutex::new(blkalloc)),
|
||||||
|
abort: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn load_id_allocator(
|
||||||
|
tx: &XsdTransaction,
|
||||||
|
allocator_type: &str,
|
||||||
|
frontend_dom_path: &str,
|
||||||
|
) -> Result<DeviceIdAllocator> {
|
||||||
|
let state = tx
|
||||||
|
.read(format!(
|
||||||
|
"{}/{}-alloc-state",
|
||||||
|
frontend_dom_path, allocator_type
|
||||||
|
))
|
||||||
|
.await?;
|
||||||
|
let allocator = state
|
||||||
|
.and_then(|state| DeviceIdAllocator::deserialize(&state))
|
||||||
|
.unwrap_or_else(DeviceIdAllocator::new);
|
||||||
|
Ok(allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn assign_next_devid(&self) -> Result<u64> {
|
||||||
|
self.devalloc
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.allocate()
|
||||||
|
.ok_or(Error::DevIdExhausted)
|
||||||
|
.map(|x| x as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn assign_next_blkidx(&self) -> Result<u32> {
|
||||||
|
self.blkalloc
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.allocate()
|
||||||
|
.ok_or(Error::DevIdExhausted)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn release_devid(&self, devid: u64) -> Result<()> {
|
||||||
|
self.devalloc.lock().await.release(devid as u32);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn release_blkid(&self, blkid: u32) -> Result<()> {
|
||||||
|
self.blkalloc.lock().await.release(blkid);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn write(
|
||||||
|
&self,
|
||||||
|
key: impl AsRef<str>,
|
||||||
|
value: impl AsRef<str>,
|
||||||
|
perms: Option<&[XsPermission]>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let path = format!("{}/{}", self.frontend_dom_path, key.as_ref());
|
||||||
|
if let Some(perms) = perms {
|
||||||
|
self.tx.mknod(&path, perms).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// empty string is written by mknod, if perms is set we can skip it.
|
||||||
|
if perms.is_none() || perms.is_some() && !value.as_ref().is_empty() {
|
||||||
|
self.tx.write_string(path, value.as_ref()).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_domain_declaration(
|
||||||
|
&self,
|
||||||
|
name: Option<impl AsRef<str>>,
|
||||||
|
platform: &PlatformDomainConfig,
|
||||||
|
created: &PlatformDomainInfo,
|
||||||
|
) -> Result<()> {
|
||||||
|
let vm_path = format!("/vm/{}", platform.uuid);
|
||||||
|
let ro_perm = &[
|
||||||
|
XsPermission {
|
||||||
|
id: 0,
|
||||||
|
perms: XS_PERM_NONE,
|
||||||
|
},
|
||||||
|
XsPermission {
|
||||||
|
id: self.frontend_domid,
|
||||||
|
perms: XS_PERM_READ,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let no_perm = &[XsPermission {
|
||||||
|
id: 0,
|
||||||
|
perms: XS_PERM_NONE,
|
||||||
|
}];
|
||||||
|
|
||||||
|
let rw_perm = &[XsPermission {
|
||||||
|
id: self.frontend_domid,
|
||||||
|
perms: XS_PERM_READ_WRITE,
|
||||||
|
}];
|
||||||
|
|
||||||
|
self.tx.rm(&self.frontend_dom_path).await?;
|
||||||
|
self.tx.mknod(&self.frontend_dom_path, ro_perm).await?;
|
||||||
|
|
||||||
|
self.tx.rm(&vm_path).await?;
|
||||||
|
self.tx.mknod(&vm_path, no_perm).await?;
|
||||||
|
self.tx
|
||||||
|
.write_string(format!("{}/uuid", vm_path), &platform.uuid.to_string())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
self.write("vm", &vm_path, None).await?;
|
||||||
|
self.write("cpu", "", Some(ro_perm)).await?;
|
||||||
|
self.write("memory", "", Some(ro_perm)).await?;
|
||||||
|
self.write("control", "", Some(ro_perm)).await?;
|
||||||
|
self.write("control/shutdown", "", Some(rw_perm)).await?;
|
||||||
|
self.write("control/feature-poweroff", "", Some(rw_perm))
|
||||||
|
.await?;
|
||||||
|
self.write("control/feature-reboot", "", Some(rw_perm))
|
||||||
|
.await?;
|
||||||
|
self.write("control/feature-suspend", "", Some(rw_perm))
|
||||||
|
.await?;
|
||||||
|
self.write("control/sysrq", "", Some(rw_perm)).await?;
|
||||||
|
self.write("data", "", Some(rw_perm)).await?;
|
||||||
|
self.write("drivers", "", Some(rw_perm)).await?;
|
||||||
|
self.write("feature", "", Some(rw_perm)).await?;
|
||||||
|
self.write("attr", "", Some(rw_perm)).await?;
|
||||||
|
self.write("error", "", Some(rw_perm)).await?;
|
||||||
|
self.write("uuid", platform.uuid.to_string(), Some(ro_perm))
|
||||||
|
.await?;
|
||||||
|
if let Some(name) = name {
|
||||||
|
self.write("name", name.as_ref(), Some(ro_perm)).await?;
|
||||||
|
}
|
||||||
|
self.write(
|
||||||
|
"memory/static-max",
|
||||||
|
(platform.resources.max_memory_mb * 1024).to_string(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
self.write(
|
||||||
|
"memory/target",
|
||||||
|
(platform.resources.assigned_memory_mb * 1024).to_string(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
self.write("memory/videoram", "0", None).await?;
|
||||||
|
self.write("domid", self.frontend_domid.to_string(), None)
|
||||||
|
.await?;
|
||||||
|
self.write("type", "PV", None).await?;
|
||||||
|
self.write("store/port", created.store_evtchn.to_string(), None)
|
||||||
|
.await?;
|
||||||
|
self.write("store/ring-ref", created.store_mfn.to_string(), None)
|
||||||
|
.await?;
|
||||||
|
for i in 0..platform.resources.max_vcpus {
|
||||||
|
let path = format!("{}/cpu/{}", self.frontend_dom_path, i);
|
||||||
|
self.tx.mkdir(&path).await?;
|
||||||
|
self.tx.set_perms(&path, ro_perm).await?;
|
||||||
|
let path = format!("{}/cpu/{}/availability", self.frontend_dom_path, i);
|
||||||
|
self.tx
|
||||||
|
.write_string(
|
||||||
|
&path,
|
||||||
|
if i < platform.resources.assigned_vcpus {
|
||||||
|
"online"
|
||||||
|
} else {
|
||||||
|
"offline"
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
self.tx.set_perms(&path, ro_perm).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_device(&self, id: u64, device: DeviceDescription) -> Result<()> {
|
||||||
|
let frontend_path = if let Some(ref special_frontend_path) = device.special_frontend_path {
|
||||||
|
format!("{}/{}", self.frontend_dom_path, special_frontend_path)
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"{}/device/{}/{}",
|
||||||
|
self.frontend_dom_path, device.frontend_type, id
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let backend_path = format!(
|
||||||
|
"{}/backend/{}/{}/{}",
|
||||||
|
self.backend_dom_path, device.backend_type, self.frontend_domid, id
|
||||||
|
);
|
||||||
|
|
||||||
|
let frontend_perms = &[
|
||||||
|
XsPermission {
|
||||||
|
id: self.frontend_domid,
|
||||||
|
perms: XS_PERM_READ_WRITE,
|
||||||
|
},
|
||||||
|
XsPermission {
|
||||||
|
id: self.backend_domid,
|
||||||
|
perms: XS_PERM_READ,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let backend_perms = &[
|
||||||
|
XsPermission {
|
||||||
|
id: self.backend_domid,
|
||||||
|
perms: XS_PERM_READ_WRITE,
|
||||||
|
},
|
||||||
|
XsPermission {
|
||||||
|
id: self.frontend_domid,
|
||||||
|
perms: XS_PERM_READ,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
self.tx.mknod(&frontend_path, frontend_perms).await?;
|
||||||
|
self.tx.mknod(&backend_path, backend_perms).await?;
|
||||||
|
|
||||||
|
for (key, value) in &device.backend_items {
|
||||||
|
let path = format!("{}/{}", backend_path, key);
|
||||||
|
self.tx.write_string(&path, value).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tx
|
||||||
|
.write_string(format!("{}/frontend", backend_path), &frontend_path)
|
||||||
|
.await?;
|
||||||
|
self.tx
|
||||||
|
.write_string(
|
||||||
|
format!("{}/frontend-id", backend_path),
|
||||||
|
&self.frontend_domid.to_string(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
for (key, value) in &device.frontend_items {
|
||||||
|
let path = format!("{}/{}", frontend_path, key);
|
||||||
|
self.tx.write_string(&path, value).await?;
|
||||||
|
if device.special_frontend_path.is_none() {
|
||||||
|
self.tx.set_perms(&path, frontend_perms).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.tx
|
||||||
|
.write_string(format!("{}/backend", frontend_path), &backend_path)
|
||||||
|
.await?;
|
||||||
|
self.tx
|
||||||
|
.write_string(
|
||||||
|
format!("{}/backend-id", frontend_path),
|
||||||
|
&self.backend_domid.to_string(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_rw_path(&self, key: impl AsRef<str>) -> Result<()> {
|
||||||
|
let rw_perm = &[XsPermission {
|
||||||
|
id: self.frontend_domid,
|
||||||
|
perms: XS_PERM_READ_WRITE,
|
||||||
|
}];
|
||||||
|
|
||||||
|
self.tx
|
||||||
|
.mknod(
|
||||||
|
&format!("{}/{}", self.frontend_dom_path, key.as_ref()),
|
||||||
|
rw_perm,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn before_commit(&self) -> Result<()> {
|
||||||
|
let devid_allocator_state = self.devalloc.lock().await.serialize();
|
||||||
|
let blkid_allocator_state = self.blkalloc.lock().await.serialize();
|
||||||
|
self.tx
|
||||||
|
.write(
|
||||||
|
format!("{}/devid-alloc-state", self.frontend_dom_path),
|
||||||
|
devid_allocator_state,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
self.tx
|
||||||
|
.write(
|
||||||
|
format!("{}/blkid-alloc-state", self.frontend_dom_path),
|
||||||
|
blkid_allocator_state,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn maybe_commit(mut self) -> Result<bool> {
|
||||||
|
self.abort = false;
|
||||||
|
self.before_commit().await?;
|
||||||
|
Ok(self.tx.maybe_commit().await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn commit(mut self) -> Result<()> {
|
||||||
|
self.abort = false;
|
||||||
|
self.before_commit().await?;
|
||||||
|
self.tx.commit().await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for XenTransaction {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if !self.abort {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let tx = self.tx.clone();
|
||||||
|
tokio::task::spawn(async move {
|
||||||
|
let _ = tx.abort().await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DeviceDescription {
|
||||||
|
frontend_type: String,
|
||||||
|
backend_type: String,
|
||||||
|
special_frontend_path: Option<String>,
|
||||||
|
frontend_items: HashMap<String, String>,
|
||||||
|
backend_items: HashMap<String, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeviceDescription {
|
||||||
|
pub fn new(frontend_type: impl AsRef<str>, backend_type: impl AsRef<str>) -> Self {
|
||||||
|
Self {
|
||||||
|
frontend_type: frontend_type.as_ref().to_string(),
|
||||||
|
backend_type: backend_type.as_ref().to_string(),
|
||||||
|
special_frontend_path: None,
|
||||||
|
frontend_items: HashMap::new(),
|
||||||
|
backend_items: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn special_frontend_path(&mut self, path: impl AsRef<str>) -> &mut Self {
|
||||||
|
self.special_frontend_path = Some(path.as_ref().to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_frontend_item(&mut self, key: impl AsRef<str>, value: impl ToString) -> &mut Self {
|
||||||
|
self.frontend_items
|
||||||
|
.insert(key.as_ref().to_string(), value.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_backend_item(&mut self, key: impl AsRef<str>, value: impl ToString) -> &mut Self {
|
||||||
|
self.backend_items
|
||||||
|
.insert(key.as_ref().to_string(), value.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_frontend_bool(&mut self, key: impl AsRef<str>, value: bool) -> &mut Self {
|
||||||
|
self.add_frontend_item(key, if value { "1" } else { "0" })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_backend_bool(&mut self, key: impl AsRef<str>, value: bool) -> &mut Self {
|
||||||
|
self.add_backend_item(key, if value { "1" } else { "0" })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn done(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct DeviceResult {
|
||||||
|
pub id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct BlockDeviceResult {
|
||||||
|
pub id: u64,
|
||||||
|
pub idx: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait DeviceConfig {
|
||||||
|
type Result;
|
||||||
|
|
||||||
|
async fn add_to_transaction(&self, tx: &XenTransaction) -> Result<Self::Result>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct BlockDeviceRef {
|
||||||
|
pub path: String,
|
||||||
|
pub major: u32,
|
||||||
|
pub minor: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockDeviceRef {
|
||||||
|
pub fn new(path: impl AsRef<str>, major: u32, minor: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
path: path.as_ref().to_string(),
|
||||||
|
major,
|
||||||
|
minor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
194
crates/xen/xenclient/src/tx/pci.rs
Normal file
194
crates/xen/xenclient/src/tx/pci.rs
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
use super::{DeviceConfig, DeviceDescription, DeviceResult, XenTransaction};
|
||||||
|
use crate::{
|
||||||
|
error::{Error, Result},
|
||||||
|
pci::{PciBdf, XenPciBackend},
|
||||||
|
};
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
use xencall::{sys::DOMCTL_DEV_RDM_RELAXED, XenCall};
|
||||||
|
use xenplatform::sys::XEN_PAGE_SHIFT;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
pub enum PciRdmReservePolicy {
|
||||||
|
Invalid,
|
||||||
|
#[default]
|
||||||
|
Strict,
|
||||||
|
Relaxed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PciRdmReservePolicy {
|
||||||
|
pub fn to_option_str(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
PciRdmReservePolicy::Invalid => "-1",
|
||||||
|
PciRdmReservePolicy::Strict => "0",
|
||||||
|
PciRdmReservePolicy::Relaxed => "1",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PciDeviceConfig {
|
||||||
|
bdf: PciBdf,
|
||||||
|
rdm_reserve_policy: PciRdmReservePolicy,
|
||||||
|
permissive: bool,
|
||||||
|
msi_translate: bool,
|
||||||
|
power_management: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PciRootDeviceConfig {
|
||||||
|
backend_type: String,
|
||||||
|
devices: Vec<PciDeviceConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PciDeviceConfig {
|
||||||
|
pub fn new(bdf: PciBdf) -> Self {
|
||||||
|
Self {
|
||||||
|
bdf,
|
||||||
|
rdm_reserve_policy: PciRdmReservePolicy::Strict,
|
||||||
|
permissive: false,
|
||||||
|
msi_translate: false,
|
||||||
|
power_management: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rdm_reserve_policy(&mut self, rdm_reserve_policy: PciRdmReservePolicy) -> &mut Self {
|
||||||
|
self.rdm_reserve_policy = rdm_reserve_policy;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn permissive(&mut self, permissive: bool) -> &mut Self {
|
||||||
|
self.permissive = permissive;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn msi_translate(&mut self, msi_translate: bool) -> &mut Self {
|
||||||
|
self.msi_translate = msi_translate;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn power_management(&mut self, power_management: bool) -> &mut Self {
|
||||||
|
self.power_management = power_management;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn done(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PciRootDeviceConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PciRootDeviceConfig {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
backend_type: "pci".to_string(),
|
||||||
|
devices: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn backend_type(&mut self, backend_type: impl AsRef<str>) -> &mut Self {
|
||||||
|
self.backend_type = backend_type.as_ref().to_string();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_device(&mut self, device: PciDeviceConfig) -> &mut Self {
|
||||||
|
self.devices.push(device);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn prepare(&self, domid: u32, call: &XenCall) -> Result<()> {
|
||||||
|
for device in &self.devices {
|
||||||
|
let backend = XenPciBackend::new();
|
||||||
|
if !backend.is_assigned(&device.bdf).await? {
|
||||||
|
return Err(Error::PciDeviceNotAssignable(device.bdf));
|
||||||
|
}
|
||||||
|
let resources = backend.read_resources(&device.bdf).await?;
|
||||||
|
for resource in resources {
|
||||||
|
if resource.is_bar_io() {
|
||||||
|
call.ioport_permission(
|
||||||
|
domid,
|
||||||
|
resource.start as u32,
|
||||||
|
resource.size() as u32,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
} else {
|
||||||
|
call.iomem_permission(
|
||||||
|
domid,
|
||||||
|
resource.start >> XEN_PAGE_SHIFT,
|
||||||
|
(resource.size() + (XEN_PAGE_SHIFT - 1)) >> XEN_PAGE_SHIFT,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(irq) = backend.read_irq(&device.bdf).await? {
|
||||||
|
let irq = call.map_pirq(domid, irq as isize, None).await?;
|
||||||
|
call.irq_permission(domid, irq, true).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
backend.reset(&device.bdf).await?;
|
||||||
|
|
||||||
|
call.assign_device(
|
||||||
|
domid,
|
||||||
|
device.bdf.encode(),
|
||||||
|
if device.rdm_reserve_policy == PciRdmReservePolicy::Relaxed {
|
||||||
|
DOMCTL_DEV_RDM_RELAXED
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if device.permissive {
|
||||||
|
backend.enable_permissive(&device.bdf).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn done(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl DeviceConfig for PciRootDeviceConfig {
|
||||||
|
type Result = DeviceResult;
|
||||||
|
|
||||||
|
async fn add_to_transaction(&self, tx: &XenTransaction) -> Result<DeviceResult> {
|
||||||
|
let id = tx.assign_next_devid().await?;
|
||||||
|
let mut device = DeviceDescription::new("pci", &self.backend_type);
|
||||||
|
device
|
||||||
|
.add_backend_bool("online", true)
|
||||||
|
.add_backend_item("state", 1)
|
||||||
|
.add_backend_item("num_devs", self.devices.len());
|
||||||
|
|
||||||
|
for (index, pci) in self.devices.iter().enumerate() {
|
||||||
|
let mut options = IndexMap::new();
|
||||||
|
options.insert("permissive", if pci.permissive { "1" } else { "0" });
|
||||||
|
options.insert("rdm_policy", pci.rdm_reserve_policy.to_option_str());
|
||||||
|
options.insert("msitranslate", if pci.msi_translate { "1" } else { "0" });
|
||||||
|
let options = options
|
||||||
|
.into_iter()
|
||||||
|
.map(|(key, value)| format!("{}={}", key, value))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(",");
|
||||||
|
device
|
||||||
|
.add_backend_item(format!("key-{}", index), pci.bdf.to_string())
|
||||||
|
.add_backend_item(format!("dev-{}", index), pci.bdf.to_string())
|
||||||
|
.add_backend_item(format!("opts-{}", index), options);
|
||||||
|
|
||||||
|
if let Some(vdefn) = pci.bdf.vdefn {
|
||||||
|
device.add_backend_item(format!("vdefn-{}", index), format!("{:#x}", vdefn));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device.add_frontend_item("state", 1);
|
||||||
|
tx.add_device(id, device).await?;
|
||||||
|
Ok(DeviceResult { id })
|
||||||
|
}
|
||||||
|
}
|
131
crates/xen/xenclient/src/tx/vbd.rs
Normal file
131
crates/xen/xenclient/src/tx/vbd.rs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
use super::{BlockDeviceRef, BlockDeviceResult, DeviceConfig, DeviceDescription, XenTransaction};
|
||||||
|
use crate::{
|
||||||
|
error::{Error, Result},
|
||||||
|
util::vbd_blkidx_to_disk_name,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct VbdDeviceConfig {
|
||||||
|
backend_type: String,
|
||||||
|
removable: bool,
|
||||||
|
bootable: bool,
|
||||||
|
writable: bool,
|
||||||
|
discard: bool,
|
||||||
|
trusted: bool,
|
||||||
|
block_device: Option<BlockDeviceRef>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VbdDeviceConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VbdDeviceConfig {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
backend_type: "vbd".to_string(),
|
||||||
|
removable: false,
|
||||||
|
bootable: true,
|
||||||
|
writable: false,
|
||||||
|
discard: false,
|
||||||
|
trusted: true,
|
||||||
|
block_device: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn backend_type(&mut self, backend_type: impl AsRef<str>) -> &mut Self {
|
||||||
|
self.backend_type = backend_type.as_ref().to_string();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn removable(&mut self, removable: bool) -> &mut Self {
|
||||||
|
self.removable = removable;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bootable(&mut self, bootable: bool) -> &mut Self {
|
||||||
|
self.bootable = bootable;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writable(&mut self, writable: bool) -> &mut Self {
|
||||||
|
self.writable = writable;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn discard(&mut self, discard: bool) -> &mut Self {
|
||||||
|
self.discard = discard;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trusted(&mut self, trusted: bool) -> &mut Self {
|
||||||
|
self.trusted = trusted;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block_device(&mut self, block_device: BlockDeviceRef) -> &mut Self {
|
||||||
|
self.block_device = Some(block_device);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn done(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl DeviceConfig for VbdDeviceConfig {
|
||||||
|
type Result = BlockDeviceResult;
|
||||||
|
|
||||||
|
async fn add_to_transaction(&self, tx: &XenTransaction) -> Result<BlockDeviceResult> {
|
||||||
|
let id = tx.assign_next_devid().await?;
|
||||||
|
let idx = tx.assign_next_blkidx().await?;
|
||||||
|
let vdev = vbd_blkidx_to_disk_name(idx)?;
|
||||||
|
let block_device = self
|
||||||
|
.block_device
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| Error::ParameterMissing("block device"))?;
|
||||||
|
|
||||||
|
let mut device = DeviceDescription::new("vbd", &self.backend_type);
|
||||||
|
device
|
||||||
|
.add_backend_item("online", 1)
|
||||||
|
.add_backend_bool("removable", self.removable)
|
||||||
|
.add_backend_bool("bootable", self.bootable)
|
||||||
|
.add_backend_item("type", "phy")
|
||||||
|
.add_backend_item("device-type", "disk")
|
||||||
|
.add_backend_item("discard-enable", self.discard)
|
||||||
|
.add_backend_item("specification", "xen")
|
||||||
|
.add_backend_item("physical-device-path", &block_device.path)
|
||||||
|
.add_backend_item("mode", if self.writable { "w" } else { "r" })
|
||||||
|
.add_backend_item(
|
||||||
|
"physical-device",
|
||||||
|
format!("{:02x}:{:02x}", block_device.major, block_device.minor),
|
||||||
|
)
|
||||||
|
.add_backend_item("dev", &vdev)
|
||||||
|
.add_backend_item("state", 1);
|
||||||
|
|
||||||
|
// we should use standard virtual-device support for first few block devices.
|
||||||
|
// the kernel warns when you use ext for indexes 5 or less, due to
|
||||||
|
// potential id overlapping.
|
||||||
|
let (vdev, vd_key) = if idx <= 5 {
|
||||||
|
// shift by 4 as partition count is 16
|
||||||
|
((202 << 8) | (idx as u64 * 16u64), "virtual-device")
|
||||||
|
} else {
|
||||||
|
// this is silly but 256 is the number of partitions
|
||||||
|
// multiply the index by that to get the actual id
|
||||||
|
((1u64 << 28u64) + (idx as u64) * 256, "virtual-device-ext")
|
||||||
|
};
|
||||||
|
|
||||||
|
device
|
||||||
|
.add_frontend_item(vd_key, vdev)
|
||||||
|
.add_frontend_item("state", 1)
|
||||||
|
.add_frontend_item("device-type", "disk")
|
||||||
|
.add_frontend_bool("trusted", self.trusted)
|
||||||
|
.add_frontend_item("protocol", "x86_64-abi")
|
||||||
|
.add_frontend_item("x-index", idx);
|
||||||
|
|
||||||
|
tx.add_device(id, device).await?;
|
||||||
|
|
||||||
|
Ok(BlockDeviceResult { id, idx })
|
||||||
|
}
|
||||||
|
}
|
112
crates/xen/xenclient/src/tx/vif.rs
Normal file
112
crates/xen/xenclient/src/tx/vif.rs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
use super::{DeviceConfig, DeviceDescription, DeviceResult, XenTransaction};
|
||||||
|
use crate::error::{Error, Result};
|
||||||
|
|
||||||
|
pub struct VifDeviceConfig {
|
||||||
|
backend_type: String,
|
||||||
|
mac: Option<String>,
|
||||||
|
mtu: Option<u32>,
|
||||||
|
script: Option<String>,
|
||||||
|
bridge: Option<String>,
|
||||||
|
trusted: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VifDeviceConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VifDeviceConfig {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
backend_type: "vif".to_string(),
|
||||||
|
mac: None,
|
||||||
|
mtu: None,
|
||||||
|
script: None,
|
||||||
|
bridge: None,
|
||||||
|
trusted: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn backend_type(&mut self, backend_type: impl AsRef<str>) -> &mut Self {
|
||||||
|
self.backend_type = backend_type.as_ref().to_string();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mac(&mut self, mac: impl AsRef<str>) -> &mut Self {
|
||||||
|
self.mac = Some(mac.as_ref().to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mtu(&mut self, mtu: u32) -> &mut Self {
|
||||||
|
self.mtu = Some(mtu);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn script(&mut self, script: impl AsRef<str>) -> &mut Self {
|
||||||
|
self.script = Some(script.as_ref().to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bridge(&mut self, bridge: impl AsRef<str>) -> &mut Self {
|
||||||
|
self.bridge = Some(bridge.as_ref().to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trusted(&mut self, trusted: bool) -> &mut Self {
|
||||||
|
self.trusted = trusted;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn done(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl DeviceConfig for VifDeviceConfig {
|
||||||
|
type Result = DeviceResult;
|
||||||
|
|
||||||
|
async fn add_to_transaction(&self, tx: &XenTransaction) -> Result<DeviceResult> {
|
||||||
|
let id = tx.assign_next_devid().await?;
|
||||||
|
let mac = self
|
||||||
|
.mac
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| Error::ParameterMissing("mac address"))?;
|
||||||
|
let mtu = self
|
||||||
|
.mtu
|
||||||
|
.ok_or_else(|| Error::ParameterMissing("mtu"))?
|
||||||
|
.to_string();
|
||||||
|
let mut device = DeviceDescription::new("vif", &self.backend_type);
|
||||||
|
device
|
||||||
|
.add_backend_item("online", 1)
|
||||||
|
.add_backend_item("state", 1)
|
||||||
|
.add_backend_item("mac", mac)
|
||||||
|
.add_backend_item("mtu", &mtu)
|
||||||
|
.add_backend_item("type", "vif")
|
||||||
|
.add_backend_item("handle", id);
|
||||||
|
|
||||||
|
if let Some(bridge) = self.bridge.as_ref() {
|
||||||
|
device.add_backend_item("bridge", bridge);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(script) = self.script.as_ref() {
|
||||||
|
device
|
||||||
|
.add_backend_item("script", script)
|
||||||
|
.add_backend_item("hotplug-status", "");
|
||||||
|
} else {
|
||||||
|
device
|
||||||
|
.add_backend_item("script", "")
|
||||||
|
.add_backend_item("hotplug-status", "connected");
|
||||||
|
}
|
||||||
|
|
||||||
|
device
|
||||||
|
.add_frontend_item("state", 1)
|
||||||
|
.add_frontend_item("mac", mac)
|
||||||
|
.add_frontend_item("mtu", &mtu)
|
||||||
|
.add_frontend_bool("trusted", self.trusted);
|
||||||
|
|
||||||
|
tx.add_device(id, device.done()).await?;
|
||||||
|
Ok(DeviceResult { id })
|
||||||
|
}
|
||||||
|
}
|
21
crates/xen/xenclient/src/util.rs
Normal file
21
crates/xen/xenclient/src/util.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use crate::error::{Error, Result};
|
||||||
|
|
||||||
|
pub fn vbd_blkidx_to_disk_name(blkid: u32) -> Result<String> {
|
||||||
|
let mut name = "xvd".to_string();
|
||||||
|
let mut suffix = String::new();
|
||||||
|
let mut n = blkid;
|
||||||
|
loop {
|
||||||
|
let c = (n % 26) as u8;
|
||||||
|
let c = b'a' + c;
|
||||||
|
let c = char::from_u32(c as u32).ok_or(Error::InvalidBlockIdx)?;
|
||||||
|
suffix.push(c);
|
||||||
|
if n >= 26 {
|
||||||
|
n = (n / 26) - 1;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
name.push_str(&suffix.chars().rev().collect::<String>());
|
||||||
|
Ok(name)
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "krata-xenevtchn"
|
name = "krata-xenevtchn"
|
||||||
description = "An implementation of Xen evtchn for krata"
|
description = "An implementation of Xen evtchn for krata."
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
homepage.workspace = true
|
homepage.workspace = true
|
||||||
|
@ -12,6 +12,8 @@ pub enum Error {
|
|||||||
LockAcquireFailed,
|
LockAcquireFailed,
|
||||||
#[error("event port already in use")]
|
#[error("event port already in use")]
|
||||||
PortInUse,
|
PortInUse,
|
||||||
|
#[error("failed to join blocking task")]
|
||||||
|
BlockingTaskJoin,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
@ -3,7 +3,10 @@ pub mod raw;
|
|||||||
pub mod sys;
|
pub mod sys;
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::sys::{BindInterdomain, BindUnboundPort, BindVirq, Notify, UnbindPort};
|
use crate::sys::{
|
||||||
|
BindInterdomainRequest, BindUnboundPortRequest, BindVirqRequest, NotifyRequest,
|
||||||
|
UnbindPortRequest,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::raw::EVENT_CHANNEL_DEVICE;
|
use crate::raw::EVENT_CHANNEL_DEVICE;
|
||||||
use byteorder::{LittleEndian, ReadBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt};
|
||||||
@ -16,12 +19,9 @@ use std::os::raw::c_void;
|
|||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::fs::{File, OpenOptions};
|
use tokio::fs::{File, OpenOptions};
|
||||||
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
use tokio::sync::{Mutex, Notify};
|
||||||
use tokio::sync::{Mutex, RwLock};
|
|
||||||
|
|
||||||
const CHANNEL_QUEUE_LEN: usize = 30;
|
type WakeMap = Arc<Mutex<HashMap<u32, Arc<Notify>>>>;
|
||||||
|
|
||||||
type WakeMap = Arc<RwLock<HashMap<u32, Sender<u32>>>>;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct EventChannelService {
|
pub struct EventChannelService {
|
||||||
@ -32,7 +32,7 @@ pub struct EventChannelService {
|
|||||||
|
|
||||||
pub struct BoundEventChannel {
|
pub struct BoundEventChannel {
|
||||||
pub local_port: u32,
|
pub local_port: u32,
|
||||||
pub receiver: Receiver<u32>,
|
pub receiver: Arc<Notify>,
|
||||||
pub service: EventChannelService,
|
pub service: EventChannelService,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ impl EventChannelService {
|
|||||||
.write(true)
|
.write(true)
|
||||||
.open(EVENT_CHANNEL_DEVICE)
|
.open(EVENT_CHANNEL_DEVICE)
|
||||||
.await?;
|
.await?;
|
||||||
let wakes = Arc::new(RwLock::new(HashMap::new()));
|
let wakes = Arc::new(Mutex::new(HashMap::new()));
|
||||||
let flag = Arc::new(AtomicBool::new(false));
|
let flag = Arc::new(AtomicBool::new(false));
|
||||||
let processor = EventChannelProcessor {
|
let processor = EventChannelProcessor {
|
||||||
flag: flag.clone(),
|
flag: flag.clone(),
|
||||||
@ -77,43 +77,52 @@ impl EventChannelService {
|
|||||||
|
|
||||||
pub async fn bind_virq(&self, virq: u32) -> Result<u32> {
|
pub async fn bind_virq(&self, virq: u32) -> Result<u32> {
|
||||||
let handle = self.handle.lock().await;
|
let handle = self.handle.lock().await;
|
||||||
unsafe {
|
let fd = handle.as_raw_fd();
|
||||||
let mut request = BindVirq { virq };
|
let mut request = BindVirqRequest { virq };
|
||||||
Ok(sys::bind_virq(handle.as_raw_fd(), &mut request)? as u32)
|
let result =
|
||||||
}
|
tokio::task::spawn_blocking(move || unsafe { sys::bind_virq(fd, &mut request) })
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::BlockingTaskJoin)?? as u32;
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn bind_interdomain(&self, domid: u32, port: u32) -> Result<u32> {
|
pub async fn bind_interdomain(&self, domid: u32, port: u32) -> Result<u32> {
|
||||||
let handle = self.handle.lock().await;
|
let handle = self.handle.lock().await;
|
||||||
unsafe {
|
let fd = handle.as_raw_fd();
|
||||||
let mut request = BindInterdomain {
|
let mut request = BindInterdomainRequest {
|
||||||
remote_domain: domid,
|
remote_domain: domid,
|
||||||
remote_port: port,
|
remote_port: port,
|
||||||
};
|
};
|
||||||
Ok(sys::bind_interdomain(handle.as_raw_fd(), &mut request)? as u32)
|
let result =
|
||||||
}
|
tokio::task::spawn_blocking(move || unsafe { sys::bind_interdomain(fd, &mut request) })
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::BlockingTaskJoin)?? as u32;
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn bind_unbound_port(&self, domid: u32) -> Result<u32> {
|
pub async fn bind_unbound_port(&self, domid: u32) -> Result<u32> {
|
||||||
let handle = self.handle.lock().await;
|
let handle = self.handle.lock().await;
|
||||||
unsafe {
|
let fd = handle.as_raw_fd();
|
||||||
let mut request = BindUnboundPort {
|
let mut request = BindUnboundPortRequest {
|
||||||
remote_domain: domid,
|
remote_domain: domid,
|
||||||
};
|
};
|
||||||
Ok(sys::bind_unbound_port(handle.as_raw_fd(), &mut request)? as u32)
|
let result = tokio::task::spawn_blocking(move || unsafe {
|
||||||
}
|
sys::bind_unbound_port(fd, &mut request)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::BlockingTaskJoin)?? as u32;
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn unmask(&self, port: u32) -> Result<()> {
|
pub async fn unmask(&self, port: u32) -> Result<()> {
|
||||||
let handle = self.handle.lock().await;
|
let handle = self.handle.lock().await;
|
||||||
let mut port = port;
|
let mut port = port;
|
||||||
let result = unsafe {
|
let fd = handle.as_raw_fd();
|
||||||
libc::write(
|
let result = tokio::task::spawn_blocking(move || unsafe {
|
||||||
handle.as_raw_fd(),
|
libc::write(fd, &mut port as *mut u32 as *mut c_void, size_of::<u32>())
|
||||||
&mut port as *mut u32 as *mut c_void,
|
})
|
||||||
size_of::<u32>(),
|
.await
|
||||||
)
|
.map_err(|_| Error::BlockingTaskJoin)?;
|
||||||
};
|
|
||||||
if result != size_of::<u32>() as isize {
|
if result != size_of::<u32>() as isize {
|
||||||
return Err(Error::Io(std::io::Error::from_raw_os_error(result as i32)));
|
return Err(Error::Io(std::io::Error::from_raw_os_error(result as i32)));
|
||||||
}
|
}
|
||||||
@ -122,25 +131,32 @@ impl EventChannelService {
|
|||||||
|
|
||||||
pub async fn unbind(&self, port: u32) -> Result<u32> {
|
pub async fn unbind(&self, port: u32) -> Result<u32> {
|
||||||
let handle = self.handle.lock().await;
|
let handle = self.handle.lock().await;
|
||||||
unsafe {
|
let mut request = UnbindPortRequest { port };
|
||||||
let mut request = UnbindPort { port };
|
let fd = handle.as_raw_fd();
|
||||||
let result = sys::unbind(handle.as_raw_fd(), &mut request)? as u32;
|
let result = tokio::task::spawn_blocking(move || unsafe { sys::unbind(fd, &mut request) })
|
||||||
self.wakes.write().await.remove(&port);
|
.await
|
||||||
Ok(result)
|
.map_err(|_| Error::BlockingTaskJoin)?? as u32;
|
||||||
}
|
self.wakes.lock().await.remove(&port);
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn notify(&self, port: u32) -> Result<u32> {
|
pub async fn notify(&self, port: u32) -> Result<u32> {
|
||||||
let handle = self.handle.lock().await;
|
let handle = self.handle.lock().await;
|
||||||
unsafe {
|
let mut request = NotifyRequest { port };
|
||||||
let mut request = Notify { port };
|
let fd = handle.as_raw_fd();
|
||||||
Ok(sys::notify(handle.as_raw_fd(), &mut request)? as u32)
|
let result = tokio::task::spawn_blocking(move || unsafe { sys::notify(fd, &mut request) })
|
||||||
}
|
.await
|
||||||
|
.map_err(|_| Error::BlockingTaskJoin)?? as u32;
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn reset(&self) -> Result<u32> {
|
pub async fn reset(&self) -> Result<u32> {
|
||||||
let handle = self.handle.lock().await;
|
let handle = self.handle.lock().await;
|
||||||
unsafe { Ok(sys::reset(handle.as_raw_fd())? as u32) }
|
let fd = handle.as_raw_fd();
|
||||||
|
let result = tokio::task::spawn_blocking(move || unsafe { sys::reset(fd) })
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::BlockingTaskJoin)?? as u32;
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn bind(&self, domid: u32, port: u32) -> Result<BoundEventChannel> {
|
pub async fn bind(&self, domid: u32, port: u32) -> Result<BoundEventChannel> {
|
||||||
@ -154,17 +170,15 @@ impl EventChannelService {
|
|||||||
Ok(bound)
|
Ok(bound)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn subscribe(&self, port: u32) -> Result<Receiver<u32>> {
|
pub async fn subscribe(&self, port: u32) -> Result<Arc<Notify>> {
|
||||||
let mut wakes = self.wakes.write().await;
|
let mut wakes = self.wakes.lock().await;
|
||||||
let receiver = match wakes.entry(port) {
|
let receiver = match wakes.entry(port) {
|
||||||
Entry::Occupied(_) => {
|
Entry::Occupied(entry) => entry.get().clone(),
|
||||||
return Err(Error::PortInUse);
|
|
||||||
}
|
|
||||||
|
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
let (sender, receiver) = channel::<u32>(CHANNEL_QUEUE_LEN);
|
let notify = Arc::new(Notify::new());
|
||||||
entry.insert(sender);
|
entry.insert(notify.clone());
|
||||||
receiver
|
notify
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(receiver)
|
Ok(receiver)
|
||||||
@ -194,9 +208,16 @@ impl EventChannelProcessor {
|
|||||||
pub fn process(&mut self) -> Result<()> {
|
pub fn process(&mut self) -> Result<()> {
|
||||||
loop {
|
loop {
|
||||||
let port = self.handle.read_u32::<LittleEndian>()?;
|
let port = self.handle.read_u32::<LittleEndian>()?;
|
||||||
if let Some(wake) = self.wakes.blocking_read().get(&port) {
|
let receiver = match self.wakes.blocking_lock().entry(port) {
|
||||||
let _ = wake.try_send(port);
|
Entry::Occupied(entry) => entry.get().clone(),
|
||||||
}
|
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
let notify = Arc::new(Notify::new());
|
||||||
|
entry.insert(notify.clone());
|
||||||
|
notify
|
||||||
|
}
|
||||||
|
};
|
||||||
|
receiver.notify_one();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,13 +32,13 @@ impl RawEventChannelService {
|
|||||||
|
|
||||||
pub fn bind_virq(&self, virq: u32) -> Result<u32> {
|
pub fn bind_virq(&self, virq: u32) -> Result<u32> {
|
||||||
let handle = self.handle.lock().map_err(|_| Error::LockAcquireFailed)?;
|
let handle = self.handle.lock().map_err(|_| Error::LockAcquireFailed)?;
|
||||||
let mut request = sys::BindVirq { virq };
|
let mut request = sys::BindVirqRequest { virq };
|
||||||
Ok(unsafe { sys::bind_virq(handle.as_raw_fd(), &mut request)? as u32 })
|
Ok(unsafe { sys::bind_virq(handle.as_raw_fd(), &mut request)? as u32 })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind_interdomain(&self, domid: u32, port: u32) -> Result<u32> {
|
pub fn bind_interdomain(&self, domid: u32, port: u32) -> Result<u32> {
|
||||||
let handle = self.handle.lock().map_err(|_| Error::LockAcquireFailed)?;
|
let handle = self.handle.lock().map_err(|_| Error::LockAcquireFailed)?;
|
||||||
let mut request = sys::BindInterdomain {
|
let mut request = sys::BindInterdomainRequest {
|
||||||
remote_domain: domid,
|
remote_domain: domid,
|
||||||
remote_port: port,
|
remote_port: port,
|
||||||
};
|
};
|
||||||
@ -47,7 +47,7 @@ impl RawEventChannelService {
|
|||||||
|
|
||||||
pub fn bind_unbound_port(&self, domid: u32) -> Result<u32> {
|
pub fn bind_unbound_port(&self, domid: u32) -> Result<u32> {
|
||||||
let handle = self.handle.lock().map_err(|_| Error::LockAcquireFailed)?;
|
let handle = self.handle.lock().map_err(|_| Error::LockAcquireFailed)?;
|
||||||
let mut request = sys::BindUnboundPort {
|
let mut request = sys::BindUnboundPortRequest {
|
||||||
remote_domain: domid,
|
remote_domain: domid,
|
||||||
};
|
};
|
||||||
Ok(unsafe { sys::bind_unbound_port(handle.as_raw_fd(), &mut request)? as u32 })
|
Ok(unsafe { sys::bind_unbound_port(handle.as_raw_fd(), &mut request)? as u32 })
|
||||||
@ -55,13 +55,13 @@ impl RawEventChannelService {
|
|||||||
|
|
||||||
pub fn unbind(&self, port: u32) -> Result<u32> {
|
pub fn unbind(&self, port: u32) -> Result<u32> {
|
||||||
let handle = self.handle.lock().map_err(|_| Error::LockAcquireFailed)?;
|
let handle = self.handle.lock().map_err(|_| Error::LockAcquireFailed)?;
|
||||||
let mut request = sys::UnbindPort { port };
|
let mut request = sys::UnbindPortRequest { port };
|
||||||
Ok(unsafe { sys::unbind(handle.as_raw_fd(), &mut request)? as u32 })
|
Ok(unsafe { sys::unbind(handle.as_raw_fd(), &mut request)? as u32 })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn notify(&self, port: u32) -> Result<u32> {
|
pub fn notify(&self, port: u32) -> Result<u32> {
|
||||||
let handle = self.handle.lock().map_err(|_| Error::LockAcquireFailed)?;
|
let handle = self.handle.lock().map_err(|_| Error::LockAcquireFailed)?;
|
||||||
let mut request = sys::Notify { port };
|
let mut request = sys::NotifyRequest { port };
|
||||||
Ok(unsafe { sys::notify(handle.as_raw_fd(), &mut request)? as u32 })
|
Ok(unsafe { sys::notify(handle.as_raw_fd(), &mut request)? as u32 })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,34 +2,34 @@ use nix::{ioctl_none, ioctl_readwrite_bad};
|
|||||||
use std::ffi::c_uint;
|
use std::ffi::c_uint;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct BindVirq {
|
pub struct BindVirqRequest {
|
||||||
pub virq: c_uint,
|
pub virq: c_uint,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct BindInterdomain {
|
pub struct BindInterdomainRequest {
|
||||||
pub remote_domain: c_uint,
|
pub remote_domain: c_uint,
|
||||||
pub remote_port: c_uint,
|
pub remote_port: c_uint,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct BindUnboundPort {
|
pub struct BindUnboundPortRequest {
|
||||||
pub remote_domain: c_uint,
|
pub remote_domain: c_uint,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct UnbindPort {
|
pub struct UnbindPortRequest {
|
||||||
pub port: c_uint,
|
pub port: c_uint,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Notify {
|
pub struct NotifyRequest {
|
||||||
pub port: c_uint,
|
pub port: c_uint,
|
||||||
}
|
}
|
||||||
|
|
||||||
ioctl_readwrite_bad!(bind_virq, 0x44500, BindVirq);
|
ioctl_readwrite_bad!(bind_virq, 0x44500, BindVirqRequest);
|
||||||
ioctl_readwrite_bad!(bind_interdomain, 0x84501, BindInterdomain);
|
ioctl_readwrite_bad!(bind_interdomain, 0x84501, BindInterdomainRequest);
|
||||||
ioctl_readwrite_bad!(bind_unbound_port, 0x44503, BindUnboundPort);
|
ioctl_readwrite_bad!(bind_unbound_port, 0x44503, BindUnboundPortRequest);
|
||||||
ioctl_readwrite_bad!(unbind, 0x44502, UnbindPort);
|
ioctl_readwrite_bad!(unbind, 0x44502, UnbindPortRequest);
|
||||||
ioctl_readwrite_bad!(notify, 0x44504, Notify);
|
ioctl_readwrite_bad!(notify, 0x44504, NotifyRequest);
|
||||||
ioctl_none!(reset, 0x4505, 5);
|
ioctl_none!(reset, 0x4505, 5);
|
||||||
|
@ -10,13 +10,11 @@ resolver = "2"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
c2rust-bitfields = { workspace = true }
|
|
||||||
elf = { workspace = true }
|
elf = { workspace = true }
|
||||||
flate2 = { workspace = true }
|
flate2 = { workspace = true }
|
||||||
indexmap = { workspace = true }
|
|
||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
krata-xencall = { path = "../xencall", version = "^0.0.23" }
|
krata-xencall = { path = "../xencall", version = "^0.0.24" }
|
||||||
memchr = { workspace = true }
|
memchr = { workspace = true }
|
||||||
nix = { workspace = true }
|
nix = { workspace = true }
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
@ -27,7 +25,6 @@ uuid = { workspace = true }
|
|||||||
xz2 = { workspace = true }
|
xz2 = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
env_logger = { workspace = true }
|
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
@ -8,16 +8,9 @@ use crate::{
|
|||||||
error::{Error, Result},
|
error::{Error, Result},
|
||||||
mem::PhysicalPages,
|
mem::PhysicalPages,
|
||||||
sys::XEN_PAGE_SHIFT,
|
sys::XEN_PAGE_SHIFT,
|
||||||
|
ImageLoader, PlatformKernelConfig, PlatformResourcesConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct BootSetup<I: BootImageLoader, P: BootSetupPlatform> {
|
|
||||||
pub call: XenCall,
|
|
||||||
pub domid: u32,
|
|
||||||
pub platform: P,
|
|
||||||
pub image_loader: I,
|
|
||||||
pub dtb: Option<Vec<u8>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct DomainSegment {
|
pub struct DomainSegment {
|
||||||
pub vstart: u64,
|
pub vstart: u64,
|
||||||
@ -42,7 +35,7 @@ pub struct BootDomain {
|
|||||||
pub phys: PhysicalPages,
|
pub phys: PhysicalPages,
|
||||||
pub store_evtchn: u32,
|
pub store_evtchn: u32,
|
||||||
pub store_mfn: u64,
|
pub store_mfn: u64,
|
||||||
pub initrd_segment: DomainSegment,
|
pub initrd_segment: Option<DomainSegment>,
|
||||||
pub console_evtchn: u32,
|
pub console_evtchn: u32,
|
||||||
pub console_mfn: u64,
|
pub console_mfn: u64,
|
||||||
pub cmdline: String,
|
pub cmdline: String,
|
||||||
@ -63,7 +56,7 @@ impl BootDomain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let local_page_size: u32 = (1i64 << XEN_PAGE_SHIFT) as u32;
|
let local_page_size: u32 = (1i64 << XEN_PAGE_SHIFT) as u32;
|
||||||
let pages = (size + local_page_size as u64 - 1) / local_page_size as u64;
|
let pages = size.div_ceil(local_page_size as u64);
|
||||||
let start = self.virt_alloc_end;
|
let start = self.virt_alloc_end;
|
||||||
|
|
||||||
let mut segment = DomainSegment {
|
let mut segment = DomainSegment {
|
||||||
@ -142,129 +135,8 @@ impl BootDomain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: BootImageLoader, P: BootSetupPlatform> BootSetup<I, P> {
|
|
||||||
pub fn new(
|
|
||||||
call: XenCall,
|
|
||||||
domid: u32,
|
|
||||||
platform: P,
|
|
||||||
image_loader: I,
|
|
||||||
dtb: Option<Vec<u8>>,
|
|
||||||
) -> BootSetup<I, P> {
|
|
||||||
BootSetup {
|
|
||||||
call,
|
|
||||||
domid,
|
|
||||||
platform,
|
|
||||||
image_loader,
|
|
||||||
dtb,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn initialize(
|
|
||||||
&mut self,
|
|
||||||
initrd: &[u8],
|
|
||||||
target_mem_mb: u64,
|
|
||||||
max_mem_mb: u64,
|
|
||||||
max_vcpus: u32,
|
|
||||||
cmdline: &str,
|
|
||||||
) -> Result<BootDomain> {
|
|
||||||
let target_pages = target_mem_mb << (20 - self.platform.page_shift());
|
|
||||||
let total_pages = max_mem_mb << (20 - self.platform.page_shift());
|
|
||||||
let image_info = self.image_loader.parse(self.platform.hvm()).await?;
|
|
||||||
let mut domain = BootDomain {
|
|
||||||
domid: self.domid,
|
|
||||||
call: self.call.clone(),
|
|
||||||
virt_alloc_end: 0,
|
|
||||||
virt_pgtab_end: 0,
|
|
||||||
pfn_alloc_end: 0,
|
|
||||||
total_pages,
|
|
||||||
target_pages,
|
|
||||||
page_size: self.platform.page_size(),
|
|
||||||
image_info,
|
|
||||||
console_evtchn: 0,
|
|
||||||
console_mfn: 0,
|
|
||||||
max_vcpus,
|
|
||||||
phys: PhysicalPages::new(self.call.clone(), self.domid, self.platform.page_shift()),
|
|
||||||
initrd_segment: DomainSegment::default(),
|
|
||||||
store_evtchn: 0,
|
|
||||||
store_mfn: 0,
|
|
||||||
cmdline: cmdline.to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.platform.initialize_early(&mut domain).await?;
|
|
||||||
|
|
||||||
let mut initrd_segment = if !domain.image_info.unmapped_initrd {
|
|
||||||
Some(domain.alloc_module(initrd).await?)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut kernel_segment = if self.platform.needs_early_kernel() {
|
|
||||||
Some(self.load_kernel_segment(&mut domain).await?)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
self.platform.initialize_memory(&mut domain).await?;
|
|
||||||
domain.virt_alloc_end = domain.image_info.virt_base;
|
|
||||||
|
|
||||||
if kernel_segment.is_none() {
|
|
||||||
kernel_segment = Some(self.load_kernel_segment(&mut domain).await?);
|
|
||||||
}
|
|
||||||
|
|
||||||
if domain.image_info.unmapped_initrd {
|
|
||||||
initrd_segment = Some(domain.alloc_module(initrd).await?);
|
|
||||||
}
|
|
||||||
|
|
||||||
domain.initrd_segment =
|
|
||||||
initrd_segment.ok_or(Error::MemorySetupFailed("initrd_segment missing"))?;
|
|
||||||
|
|
||||||
self.platform.alloc_magic_pages(&mut domain).await?;
|
|
||||||
|
|
||||||
domain.store_evtchn = self.call.evtchn_alloc_unbound(self.domid, 0).await?;
|
|
||||||
|
|
||||||
let _kernel_segment =
|
|
||||||
kernel_segment.ok_or(Error::MemorySetupFailed("kernel_segment missing"))?;
|
|
||||||
|
|
||||||
Ok(domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn boot(&mut self, domain: &mut BootDomain) -> Result<()> {
|
|
||||||
let domain_info = self.call.get_domain_info(self.domid).await?;
|
|
||||||
let shared_info_frame = domain_info.shared_info_frame;
|
|
||||||
self.platform.setup_page_tables(domain).await?;
|
|
||||||
self.platform
|
|
||||||
.setup_start_info(domain, shared_info_frame)
|
|
||||||
.await?;
|
|
||||||
self.platform.setup_hypercall_page(domain).await?;
|
|
||||||
self.platform.bootlate(domain).await?;
|
|
||||||
self.platform
|
|
||||||
.setup_shared_info(domain, shared_info_frame)
|
|
||||||
.await?;
|
|
||||||
self.platform.vcpu(domain).await?;
|
|
||||||
domain.phys.unmap_all()?;
|
|
||||||
self.platform.gnttab_seed(domain).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn load_kernel_segment(&mut self, domain: &mut BootDomain) -> Result<DomainSegment> {
|
|
||||||
let kernel_segment = domain
|
|
||||||
.alloc_segment(
|
|
||||||
domain.image_info.virt_kstart,
|
|
||||||
domain.image_info.virt_kend - domain.image_info.virt_kstart,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
let kernel_segment_ptr = kernel_segment.addr as *mut u8;
|
|
||||||
let kernel_segment_slice =
|
|
||||||
unsafe { slice::from_raw_parts_mut(kernel_segment_ptr, kernel_segment.size as usize) };
|
|
||||||
self.image_loader
|
|
||||||
.load(&domain.image_info, kernel_segment_slice)
|
|
||||||
.await?;
|
|
||||||
Ok(kernel_segment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait BootSetupPlatform: Clone {
|
pub trait BootSetupPlatform {
|
||||||
fn create_domain(&self, enable_iommu: bool) -> CreateDomain;
|
fn create_domain(&self, enable_iommu: bool) -> CreateDomain;
|
||||||
fn page_size(&self) -> u64;
|
fn page_size(&self) -> u64;
|
||||||
fn page_shift(&self) -> u64;
|
fn page_shift(&self) -> u64;
|
||||||
@ -304,6 +176,135 @@ pub trait BootSetupPlatform: Clone {
|
|||||||
async fn vcpu(&mut self, domain: &mut BootDomain) -> Result<()>;
|
async fn vcpu(&mut self, domain: &mut BootDomain) -> Result<()>;
|
||||||
|
|
||||||
async fn setup_hypercall_page(&mut self, domain: &mut BootDomain) -> Result<()>;
|
async fn setup_hypercall_page(&mut self, domain: &mut BootDomain) -> Result<()>;
|
||||||
|
|
||||||
|
async fn initialize_internal(
|
||||||
|
&mut self,
|
||||||
|
domid: u32,
|
||||||
|
call: XenCall,
|
||||||
|
image_loader: &ImageLoader,
|
||||||
|
domain: &mut BootDomain,
|
||||||
|
kernel: &PlatformKernelConfig,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.initialize_early(domain).await?;
|
||||||
|
|
||||||
|
let mut initrd_segment = if !domain.image_info.unmapped_initrd && kernel.initrd.is_some() {
|
||||||
|
Some(domain.alloc_module(kernel.initrd.as_ref().unwrap()).await?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut kernel_segment = if self.needs_early_kernel() {
|
||||||
|
Some(self.load_kernel_segment(image_loader, domain).await?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
self.initialize_memory(domain).await?;
|
||||||
|
domain.virt_alloc_end = domain.image_info.virt_base;
|
||||||
|
|
||||||
|
if kernel_segment.is_none() {
|
||||||
|
kernel_segment = Some(self.load_kernel_segment(image_loader, domain).await?);
|
||||||
|
}
|
||||||
|
|
||||||
|
if domain.image_info.unmapped_initrd && kernel.initrd.is_some() {
|
||||||
|
initrd_segment = Some(domain.alloc_module(kernel.initrd.as_ref().unwrap()).await?);
|
||||||
|
}
|
||||||
|
|
||||||
|
domain.initrd_segment = initrd_segment;
|
||||||
|
self.alloc_magic_pages(domain).await?;
|
||||||
|
domain.store_evtchn = call.evtchn_alloc_unbound(domid, 0).await?;
|
||||||
|
let _kernel_segment =
|
||||||
|
kernel_segment.ok_or(Error::MemorySetupFailed("kernel_segment missing"))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
async fn initialize(
|
||||||
|
&mut self,
|
||||||
|
domid: u32,
|
||||||
|
call: XenCall,
|
||||||
|
image_loader: &ImageLoader,
|
||||||
|
kernel: &PlatformKernelConfig,
|
||||||
|
resources: &PlatformResourcesConfig,
|
||||||
|
) -> Result<BootDomain> {
|
||||||
|
let target_pages = resources.assigned_memory_mb << (20 - self.page_shift());
|
||||||
|
let total_pages = resources.max_memory_mb << (20 - self.page_shift());
|
||||||
|
let image_info = image_loader.parse(self.hvm()).await?;
|
||||||
|
let mut domain = BootDomain {
|
||||||
|
domid,
|
||||||
|
call: call.clone(),
|
||||||
|
virt_alloc_end: 0,
|
||||||
|
virt_pgtab_end: 0,
|
||||||
|
pfn_alloc_end: 0,
|
||||||
|
total_pages,
|
||||||
|
target_pages,
|
||||||
|
page_size: self.page_size(),
|
||||||
|
image_info,
|
||||||
|
console_evtchn: 0,
|
||||||
|
console_mfn: 0,
|
||||||
|
max_vcpus: resources.max_vcpus,
|
||||||
|
phys: PhysicalPages::new(call.clone(), domid, self.page_shift()),
|
||||||
|
initrd_segment: None,
|
||||||
|
store_evtchn: 0,
|
||||||
|
store_mfn: 0,
|
||||||
|
cmdline: kernel.cmdline.clone(),
|
||||||
|
};
|
||||||
|
match self
|
||||||
|
.initialize_internal(domid, call, image_loader, &mut domain, kernel)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => Ok(domain),
|
||||||
|
Err(error) => {
|
||||||
|
domain.phys.unmap_all()?;
|
||||||
|
Err(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn boot_internal(
|
||||||
|
&mut self,
|
||||||
|
call: XenCall,
|
||||||
|
domid: u32,
|
||||||
|
domain: &mut BootDomain,
|
||||||
|
) -> Result<()> {
|
||||||
|
let domain_info = call.get_domain_info(domid).await?;
|
||||||
|
let shared_info_frame = domain_info.shared_info_frame;
|
||||||
|
self.setup_page_tables(domain).await?;
|
||||||
|
self.setup_start_info(domain, shared_info_frame).await?;
|
||||||
|
self.setup_hypercall_page(domain).await?;
|
||||||
|
self.bootlate(domain).await?;
|
||||||
|
self.setup_shared_info(domain, shared_info_frame).await?;
|
||||||
|
self.vcpu(domain).await?;
|
||||||
|
self.gnttab_seed(domain).await?;
|
||||||
|
domain.phys.unmap_all()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn boot(&mut self, domid: u32, call: XenCall, domain: &mut BootDomain) -> Result<()> {
|
||||||
|
let result = self.boot_internal(call, domid, domain).await;
|
||||||
|
domain.phys.unmap_all()?;
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn load_kernel_segment(
|
||||||
|
&mut self,
|
||||||
|
image_loader: &ImageLoader,
|
||||||
|
domain: &mut BootDomain,
|
||||||
|
) -> Result<DomainSegment> {
|
||||||
|
let kernel_segment = domain
|
||||||
|
.alloc_segment(
|
||||||
|
domain.image_info.virt_kstart,
|
||||||
|
domain.image_info.virt_kend - domain.image_info.virt_kstart,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let kernel_segment_ptr = kernel_segment.addr as *mut u8;
|
||||||
|
let kernel_segment_slice =
|
||||||
|
unsafe { slice::from_raw_parts_mut(kernel_segment_ptr, kernel_segment.size as usize) };
|
||||||
|
image_loader
|
||||||
|
.load(&domain.image_info, kernel_segment_slice)
|
||||||
|
.await?;
|
||||||
|
Ok(kernel_segment)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
boot::{BootSetup, BootSetupPlatform},
|
boot::BootDomain, elfloader::ElfImageLoader, error::Error, ImageLoader, RuntimePlatform,
|
||||||
elfloader::ElfImageLoader,
|
RuntimePlatformType,
|
||||||
};
|
};
|
||||||
|
use log::warn;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use xencall::XenCall;
|
use xencall::XenCall;
|
||||||
|
|
||||||
@ -11,42 +12,92 @@ use crate::error::Result;
|
|||||||
|
|
||||||
pub const XEN_EXTRA_MEMORY_KB: u64 = 2048;
|
pub const XEN_EXTRA_MEMORY_KB: u64 = 2048;
|
||||||
|
|
||||||
pub struct BaseDomainManager<P: BootSetupPlatform> {
|
pub struct PlatformDomainManager {
|
||||||
call: XenCall,
|
call: XenCall,
|
||||||
pub platform: Arc<P>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: BootSetupPlatform> BaseDomainManager<P> {
|
impl PlatformDomainManager {
|
||||||
pub async fn new(call: XenCall, platform: P) -> Result<BaseDomainManager<P>> {
|
pub async fn new(call: XenCall) -> Result<PlatformDomainManager> {
|
||||||
Ok(BaseDomainManager {
|
Ok(PlatformDomainManager { call })
|
||||||
call,
|
|
||||||
platform: Arc::new(platform),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create(&self, config: BaseDomainConfig) -> Result<CreatedDomain> {
|
fn max_memory_kb(resources: &PlatformResourcesConfig) -> u64 {
|
||||||
let mut domain = self.platform.create_domain(config.enable_iommu);
|
(resources.max_memory_mb * 1024) + XEN_EXTRA_MEMORY_KB
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_base_domain(
|
||||||
|
&self,
|
||||||
|
config: &PlatformDomainConfig,
|
||||||
|
platform: &RuntimePlatform,
|
||||||
|
) -> Result<u32> {
|
||||||
|
let mut domain = platform.create_domain(config.options.iommu);
|
||||||
domain.handle = config.uuid.into_bytes();
|
domain.handle = config.uuid.into_bytes();
|
||||||
domain.max_vcpus = config.max_vcpus;
|
domain.max_vcpus = config.resources.max_vcpus;
|
||||||
let domid = self.call.create_domain(domain).await?;
|
let domid = self.call.create_domain(domain).await?;
|
||||||
self.call.set_max_vcpus(domid, config.max_vcpus).await?;
|
Ok(domid)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn configure_domain_resources(
|
||||||
|
&self,
|
||||||
|
domid: u32,
|
||||||
|
config: &PlatformDomainConfig,
|
||||||
|
) -> Result<()> {
|
||||||
self.call
|
self.call
|
||||||
.set_max_mem(domid, (config.max_mem_mb * 1024) + XEN_EXTRA_MEMORY_KB)
|
.set_max_vcpus(domid, config.resources.max_vcpus)
|
||||||
.await?;
|
.await?;
|
||||||
let loader = ElfImageLoader::load_file_kernel(&config.kernel)?;
|
self.call
|
||||||
let platform = (*self.platform).clone();
|
.set_max_mem(
|
||||||
let mut boot = BootSetup::new(self.call.clone(), domid, platform, loader, None);
|
domid,
|
||||||
let mut domain = boot
|
PlatformDomainManager::max_memory_kb(&config.resources),
|
||||||
.initialize(
|
|
||||||
&config.initrd,
|
|
||||||
config.target_mem_mb,
|
|
||||||
config.max_mem_mb,
|
|
||||||
config.max_vcpus,
|
|
||||||
&config.cmdline,
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
boot.boot(&mut domain).await?;
|
Ok(())
|
||||||
Ok(CreatedDomain {
|
}
|
||||||
|
|
||||||
|
async fn create_internal(
|
||||||
|
&self,
|
||||||
|
domid: u32,
|
||||||
|
config: &PlatformDomainConfig,
|
||||||
|
mut platform: RuntimePlatform,
|
||||||
|
) -> Result<BootDomain> {
|
||||||
|
self.configure_domain_resources(domid, config).await?;
|
||||||
|
let kernel = config.kernel.clone();
|
||||||
|
let loader = tokio::task::spawn_blocking(move || match kernel.format {
|
||||||
|
KernelFormat::ElfCompressed => ElfImageLoader::load(kernel.data),
|
||||||
|
KernelFormat::ElfUncompressed => Ok(ElfImageLoader::new(kernel.data)),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(Error::AsyncJoinError)??;
|
||||||
|
let loader = ImageLoader::Elf(loader);
|
||||||
|
let mut domain = platform
|
||||||
|
.initialize(
|
||||||
|
domid,
|
||||||
|
self.call.clone(),
|
||||||
|
&loader,
|
||||||
|
&config.kernel,
|
||||||
|
&config.resources,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
platform.boot(domid, self.call.clone(), &mut domain).await?;
|
||||||
|
Ok(domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create(&self, config: PlatformDomainConfig) -> Result<PlatformDomainInfo> {
|
||||||
|
let platform = config.platform.create();
|
||||||
|
let domid = self.create_base_domain(&config, &platform).await?;
|
||||||
|
let domain = match self.create_internal(domid, &config, platform).await {
|
||||||
|
Ok(domain) => domain,
|
||||||
|
Err(error) => {
|
||||||
|
if let Err(destroy_fail) = self.call.destroy_domain(domid).await {
|
||||||
|
warn!(
|
||||||
|
"failed to destroy failed domain {}: {}",
|
||||||
|
domid, destroy_fail
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(PlatformDomainInfo {
|
||||||
domid,
|
domid,
|
||||||
store_evtchn: domain.store_evtchn,
|
store_evtchn: domain.store_evtchn,
|
||||||
store_mfn: domain.store_mfn,
|
store_mfn: domain.store_mfn,
|
||||||
@ -62,21 +113,43 @@ impl<P: BootSetupPlatform> BaseDomainManager<P> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct BaseDomainConfig {
|
pub struct PlatformDomainConfig {
|
||||||
pub uuid: Uuid,
|
pub uuid: Uuid,
|
||||||
pub owner_domid: u32,
|
pub platform: RuntimePlatformType,
|
||||||
pub max_vcpus: u32,
|
pub resources: PlatformResourcesConfig,
|
||||||
pub target_vcpus: u32,
|
pub kernel: PlatformKernelConfig,
|
||||||
pub max_mem_mb: u64,
|
pub options: PlatformOptions,
|
||||||
pub target_mem_mb: u64,
|
|
||||||
pub kernel: Vec<u8>,
|
|
||||||
pub initrd: Vec<u8>,
|
|
||||||
pub cmdline: String,
|
|
||||||
pub enable_iommu: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CreatedDomain {
|
pub struct PlatformKernelConfig {
|
||||||
|
pub data: Arc<Vec<u8>>,
|
||||||
|
pub format: KernelFormat,
|
||||||
|
pub initrd: Option<Arc<Vec<u8>>>,
|
||||||
|
pub cmdline: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PlatformResourcesConfig {
|
||||||
|
pub max_vcpus: u32,
|
||||||
|
pub assigned_vcpus: u32,
|
||||||
|
pub max_memory_mb: u64,
|
||||||
|
pub assigned_memory_mb: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PlatformOptions {
|
||||||
|
pub iommu: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum KernelFormat {
|
||||||
|
ElfUncompressed,
|
||||||
|
ElfCompressed,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PlatformDomainInfo {
|
||||||
pub domid: u32,
|
pub domid: u32,
|
||||||
pub store_evtchn: u32,
|
pub store_evtchn: u32,
|
||||||
pub store_mfn: u64,
|
pub store_mfn: u64,
|
||||||
|
@ -19,6 +19,12 @@ use std::mem::size_of;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use xz2::bufread::XzDecoder;
|
use xz2::bufread::XzDecoder;
|
||||||
|
|
||||||
|
const ELF_MAGIC: &[u8] = &[
|
||||||
|
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
];
|
||||||
|
const GZIP_MAGIC: &[u8] = &[0x1f, 0x8b];
|
||||||
|
const XZ_MAGIC: &[u8] = &[0xfd, 0x37, 0x7a, 0x58];
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ElfImageLoader {
|
pub struct ElfImageLoader {
|
||||||
data: Arc<Vec<u8>>,
|
data: Arc<Vec<u8>>,
|
||||||
@ -60,22 +66,40 @@ fn xen_note_value_as_u64(endian: AnyEndian, value: &[u8]) -> Option<u64> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ElfImageLoader {
|
impl ElfImageLoader {
|
||||||
pub fn new(data: Vec<u8>) -> ElfImageLoader {
|
pub fn new(data: Arc<Vec<u8>>) -> ElfImageLoader {
|
||||||
ElfImageLoader {
|
ElfImageLoader { data }
|
||||||
data: Arc::new(data),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_gz(data: &[u8]) -> Result<ElfImageLoader> {
|
pub fn load_gz(data: &[u8]) -> Result<ElfImageLoader> {
|
||||||
let buff = BufReader::new(data);
|
let buff = BufReader::new(data);
|
||||||
let image = ElfImageLoader::read_one_stream(&mut GzDecoder::new(buff))?;
|
let image = ElfImageLoader::read_one_stream(&mut GzDecoder::new(buff))?;
|
||||||
Ok(ElfImageLoader::new(image))
|
Ok(ElfImageLoader::new(Arc::new(image)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_xz(data: &[u8]) -> Result<ElfImageLoader> {
|
pub fn load_xz(data: &[u8]) -> Result<ElfImageLoader> {
|
||||||
let buff = BufReader::new(data);
|
let buff = BufReader::new(data);
|
||||||
let image = ElfImageLoader::read_one_stream(&mut XzDecoder::new(buff))?;
|
let image = ElfImageLoader::read_one_stream(&mut XzDecoder::new(buff))?;
|
||||||
Ok(ElfImageLoader::new(image))
|
Ok(ElfImageLoader::new(Arc::new(image)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(data: Arc<Vec<u8>>) -> Result<ElfImageLoader> {
|
||||||
|
if data.len() >= 16 && find_iter(&data[0..15], ELF_MAGIC).next().is_some() {
|
||||||
|
return Ok(ElfImageLoader::new(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
for start in find_iter(&data, GZIP_MAGIC) {
|
||||||
|
if let Ok(elf) = ElfImageLoader::load_gz(&data[start..]) {
|
||||||
|
return Ok(elf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for start in find_iter(&data, XZ_MAGIC) {
|
||||||
|
if let Ok(elf) = ElfImageLoader::load_xz(&data[start..]) {
|
||||||
|
return Ok(elf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(Error::ElfCompressionUnknown)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_one_stream(read: &mut dyn Read) -> Result<Vec<u8>> {
|
fn read_one_stream(read: &mut dyn Read) -> Result<Vec<u8>> {
|
||||||
@ -101,36 +125,11 @@ impl ElfImageLoader {
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_file_gz(path: &str) -> Result<ElfImageLoader> {
|
|
||||||
let file = std::fs::read(path)?;
|
|
||||||
ElfImageLoader::load_gz(file.as_slice())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_file_xz(path: &str) -> Result<ElfImageLoader> {
|
|
||||||
let file = std::fs::read(path)?;
|
|
||||||
ElfImageLoader::load_xz(file.as_slice())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_file_kernel(data: &[u8]) -> Result<ElfImageLoader> {
|
|
||||||
for start in find_iter(data, &[0x1f, 0x8b]) {
|
|
||||||
if let Ok(elf) = ElfImageLoader::load_gz(&data[start..]) {
|
|
||||||
return Ok(elf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for start in find_iter(data, &[0xfd, 0x37, 0x7a, 0x58]) {
|
|
||||||
if let Ok(elf) = ElfImageLoader::load_xz(&data[start..]) {
|
|
||||||
return Ok(elf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(Error::ElfCompressionUnknown)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_sync(&self, hvm: bool) -> Result<BootImageInfo> {
|
fn parse_sync(&self, hvm: bool) -> Result<BootImageInfo> {
|
||||||
let elf = ElfBytes::<AnyEndian>::minimal_parse(self.data.as_slice())?;
|
let elf = ElfBytes::<AnyEndian>::minimal_parse(self.data.as_slice())?;
|
||||||
let headers = elf.section_headers().ok_or(Error::ElfInvalidImage)?;
|
let headers = elf
|
||||||
let mut linux_notes: HashMap<u64, Vec<u8>> = HashMap::new();
|
.section_headers()
|
||||||
|
.ok_or(Error::ElfInvalidImage("section headers missing"))?;
|
||||||
let mut xen_notes: HashMap<u64, ElfNoteValue> = HashMap::new();
|
let mut xen_notes: HashMap<u64, ElfNoteValue> = HashMap::new();
|
||||||
|
|
||||||
for header in headers {
|
for header in headers {
|
||||||
@ -140,62 +139,55 @@ impl ElfImageLoader {
|
|||||||
|
|
||||||
let notes = elf.section_data_as_notes(&header)?;
|
let notes = elf.section_data_as_notes(&header)?;
|
||||||
for note in notes {
|
for note in notes {
|
||||||
if let Note::Unknown(note) = note {
|
let Note::Unknown(note) = note else {
|
||||||
if note.name == "Linux" {
|
continue;
|
||||||
linux_notes.insert(note.n_type, note.desc.to_vec());
|
};
|
||||||
}
|
|
||||||
|
|
||||||
if note.name == "Xen" {
|
if note.name == "Xen" {
|
||||||
for typ in XEN_ELFNOTE_TYPES {
|
for typ in XEN_ELFNOTE_TYPES {
|
||||||
if typ.id != note.n_type {
|
if typ.id != note.n_type {
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
let value = if !typ.is_string {
|
|
||||||
xen_note_value_as_u64(elf.ehdr.endianness, note.desc).unwrap_or(0)
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
xen_notes.insert(typ.id, ElfNoteValue { value });
|
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
|
let value = if !typ.is_string {
|
||||||
|
xen_note_value_as_u64(elf.ehdr.endianness, note.desc).unwrap_or(0)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
xen_notes.insert(typ.id, ElfNoteValue { value });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if linux_notes.is_empty() {
|
|
||||||
return Err(Error::ElfInvalidImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if xen_notes.is_empty() {
|
if xen_notes.is_empty() {
|
||||||
return Err(Error::ElfXenSupportMissing);
|
return Err(Error::ElfXenSupportMissing);
|
||||||
}
|
}
|
||||||
|
|
||||||
let paddr_offset = xen_notes
|
let paddr_offset = xen_notes
|
||||||
.get(&XEN_ELFNOTE_PADDR_OFFSET)
|
.get(&XEN_ELFNOTE_PADDR_OFFSET)
|
||||||
.ok_or(Error::ElfInvalidImage)?
|
.ok_or(Error::ElfXenNoteMissing("PADDR_OFFSET"))?
|
||||||
.value;
|
.value;
|
||||||
let virt_base = xen_notes
|
let virt_base = xen_notes
|
||||||
.get(&XEN_ELFNOTE_VIRT_BASE)
|
.get(&XEN_ELFNOTE_VIRT_BASE)
|
||||||
.ok_or(Error::ElfInvalidImage)?
|
.ok_or(Error::ElfXenNoteMissing("VIRT_BASE"))?
|
||||||
.value;
|
.value;
|
||||||
let entry = xen_notes
|
let entry = xen_notes
|
||||||
.get(&XEN_ELFNOTE_ENTRY)
|
.get(&XEN_ELFNOTE_ENTRY)
|
||||||
.ok_or(Error::ElfInvalidImage)?
|
.ok_or(Error::ElfXenNoteMissing("ENTRY"))?
|
||||||
.value;
|
.value;
|
||||||
let virt_hypercall = xen_notes
|
let virt_hypercall = xen_notes
|
||||||
.get(&XEN_ELFNOTE_HYPERCALL_PAGE)
|
.get(&XEN_ELFNOTE_HYPERCALL_PAGE)
|
||||||
.ok_or(Error::ElfInvalidImage)?
|
.ok_or(Error::ElfXenNoteMissing("HYPERCALL_PAGE"))?
|
||||||
.value;
|
.value;
|
||||||
let init_p2m = xen_notes
|
let init_p2m = xen_notes
|
||||||
.get(&XEN_ELFNOTE_INIT_P2M)
|
.get(&XEN_ELFNOTE_INIT_P2M)
|
||||||
.ok_or(Error::ElfInvalidImage)?
|
.ok_or(Error::ElfXenNoteMissing("INIT_P2M"))?
|
||||||
.value;
|
.value;
|
||||||
let mod_start_pfn = xen_notes
|
let mod_start_pfn = xen_notes
|
||||||
.get(&XEN_ELFNOTE_MOD_START_PFN)
|
.get(&XEN_ELFNOTE_MOD_START_PFN)
|
||||||
.ok_or(Error::ElfInvalidImage)?
|
.ok_or(Error::ElfXenNoteMissing("MOD_START_PFN"))?
|
||||||
.value;
|
.value;
|
||||||
|
|
||||||
let phys32_entry = xen_notes.get(&XEN_ELFNOTE_PHYS32_ENTRY).map(|x| x.value);
|
let phys32_entry = xen_notes.get(&XEN_ELFNOTE_PHYS32_ENTRY).map(|x| x.value);
|
||||||
@ -203,7 +195,9 @@ impl ElfImageLoader {
|
|||||||
let mut start: u64 = u64::MAX;
|
let mut start: u64 = u64::MAX;
|
||||||
let mut end: u64 = 0;
|
let mut end: u64 = 0;
|
||||||
|
|
||||||
let segments = elf.segments().ok_or(Error::ElfInvalidImage)?;
|
let segments = elf
|
||||||
|
.segments()
|
||||||
|
.ok_or(Error::ElfInvalidImage("segments missing"))?;
|
||||||
|
|
||||||
for header in segments {
|
for header in segments {
|
||||||
if (header.p_type != PT_LOAD) || (header.p_flags & (PF_R | PF_W | PF_X)) == 0 {
|
if (header.p_type != PT_LOAD) || (header.p_flags & (PF_R | PF_W | PF_X)) == 0 {
|
||||||
@ -221,7 +215,9 @@ impl ElfImageLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if paddr_offset != u64::MAX && virt_base == u64::MAX {
|
if paddr_offset != u64::MAX && virt_base == u64::MAX {
|
||||||
return Err(Error::ElfInvalidImage);
|
return Err(Error::ElfInvalidImage(
|
||||||
|
"paddr_offset specified, but virt_base is not specified",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let virt_offset = virt_base - paddr_offset;
|
let virt_offset = virt_base - paddr_offset;
|
||||||
@ -247,8 +243,13 @@ impl ElfImageLoader {
|
|||||||
};
|
};
|
||||||
Ok(image_info)
|
Ok(image_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_elf_bytes(self) -> Arc<Vec<u8>> {
|
||||||
|
self.data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct ElfNoteValue {
|
struct ElfNoteValue {
|
||||||
value: u64,
|
value: u64,
|
||||||
}
|
}
|
||||||
@ -262,7 +263,9 @@ impl BootImageLoader for ElfImageLoader {
|
|||||||
|
|
||||||
async fn load(&self, image_info: &BootImageInfo, dst: &mut [u8]) -> Result<()> {
|
async fn load(&self, image_info: &BootImageInfo, dst: &mut [u8]) -> Result<()> {
|
||||||
let elf = ElfBytes::<AnyEndian>::minimal_parse(self.data.as_slice())?;
|
let elf = ElfBytes::<AnyEndian>::minimal_parse(self.data.as_slice())?;
|
||||||
let segments = elf.segments().ok_or(Error::ElfInvalidImage)?;
|
let segments = elf
|
||||||
|
.segments()
|
||||||
|
.ok_or(Error::ElfInvalidImage("segments missing"))?;
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"load dst={:#x} segments={}",
|
"load dst={:#x} segments={}",
|
||||||
|
@ -28,10 +28,14 @@ pub enum Error {
|
|||||||
PopulatePhysmapFailed(usize, usize, usize),
|
PopulatePhysmapFailed(usize, usize, usize),
|
||||||
#[error("unknown elf compression method")]
|
#[error("unknown elf compression method")]
|
||||||
ElfCompressionUnknown,
|
ElfCompressionUnknown,
|
||||||
#[error("expected elf image format not found")]
|
#[error("elf image format invalid: {0}")]
|
||||||
ElfInvalidImage,
|
ElfInvalidImage(&'static str),
|
||||||
|
#[error("elf linux image not found")]
|
||||||
|
ElfNotLinux,
|
||||||
#[error("provided elf image does not contain xen support")]
|
#[error("provided elf image does not contain xen support")]
|
||||||
ElfXenSupportMissing,
|
ElfXenSupportMissing,
|
||||||
|
#[error("provided elf image does not contain xen note {0}")]
|
||||||
|
ElfXenNoteMissing(&'static str),
|
||||||
#[error("regex error: {0}")]
|
#[error("regex error: {0}")]
|
||||||
RegexError(#[from] regex::Error),
|
RegexError(#[from] regex::Error),
|
||||||
#[error("error: {0}")]
|
#[error("error: {0}")]
|
||||||
|
@ -4,9 +4,111 @@ pub mod error;
|
|||||||
pub mod mem;
|
pub mod mem;
|
||||||
pub mod sys;
|
pub mod sys;
|
||||||
|
|
||||||
|
use boot::{BootDomain, BootImageInfo, BootImageLoader, BootSetupPlatform};
|
||||||
|
use domain::{PlatformKernelConfig, PlatformResourcesConfig};
|
||||||
|
use elfloader::ElfImageLoader;
|
||||||
|
use error::Result;
|
||||||
|
use unsupported::UnsupportedPlatform;
|
||||||
|
use xencall::{sys::CreateDomain, XenCall};
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
|
||||||
pub mod domain;
|
pub mod domain;
|
||||||
pub mod unsupported;
|
pub mod unsupported;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub mod x86pv;
|
pub mod x86pv;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum ImageLoader {
|
||||||
|
Elf(ElfImageLoader),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageLoader {
|
||||||
|
async fn parse(&self, hvm: bool) -> Result<BootImageInfo> {
|
||||||
|
match self {
|
||||||
|
ImageLoader::Elf(elf) => elf.parse(hvm).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn load(&self, image_info: &BootImageInfo, dst: &mut [u8]) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
ImageLoader::Elf(elf) => elf.load(image_info, dst).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
|
pub enum RuntimePlatformType {
|
||||||
|
Unsupported,
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
Pv,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RuntimePlatformType {
|
||||||
|
pub fn create(&self) -> RuntimePlatform {
|
||||||
|
match self {
|
||||||
|
RuntimePlatformType::Unsupported => {
|
||||||
|
RuntimePlatform::Unsupported(UnsupportedPlatform::new())
|
||||||
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
RuntimePlatformType::Pv => RuntimePlatform::Pv(x86pv::X86PvPlatform::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn supported() -> RuntimePlatformType {
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
return RuntimePlatformType::Pv;
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
return RuntimePlatformType::Unsupported;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
pub enum RuntimePlatform {
|
||||||
|
Unsupported(UnsupportedPlatform),
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
Pv(x86pv::X86PvPlatform),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RuntimePlatform {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub async fn initialize(
|
||||||
|
&mut self,
|
||||||
|
domid: u32,
|
||||||
|
call: XenCall,
|
||||||
|
image_loader: &ImageLoader,
|
||||||
|
kernel: &PlatformKernelConfig,
|
||||||
|
resources: &PlatformResourcesConfig,
|
||||||
|
) -> Result<BootDomain> {
|
||||||
|
match self {
|
||||||
|
RuntimePlatform::Unsupported(unsupported) => {
|
||||||
|
unsupported
|
||||||
|
.initialize(domid, call, image_loader, kernel, resources)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
RuntimePlatform::Pv(pv) => {
|
||||||
|
pv.initialize(domid, call, image_loader, kernel, resources)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn boot(&mut self, domid: u32, call: XenCall, domain: &mut BootDomain) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
RuntimePlatform::Unsupported(unsupported) => {
|
||||||
|
unsupported.boot(domid, call, domain).await
|
||||||
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
RuntimePlatform::Pv(pv) => pv.boot(domid, call, domain).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_domain(&self, enable_iommu: bool) -> CreateDomain {
|
||||||
|
match self {
|
||||||
|
RuntimePlatform::Unsupported(unsupported) => unsupported.create_domain(enable_iommu),
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
RuntimePlatform::Pv(pv) => pv.create_domain(enable_iommu),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -44,19 +44,11 @@ impl BootSetupPlatform for UnsupportedPlatform {
|
|||||||
panic!("unsupported platform")
|
panic!("unsupported platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn alloc_p2m_segment(&mut self, _: &mut BootDomain) -> Result<Option<DomainSegment>> {
|
|
||||||
panic!("unsupported platform")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn alloc_page_tables(&mut self, _: &mut BootDomain) -> Result<Option<DomainSegment>> {
|
async fn alloc_page_tables(&mut self, _: &mut BootDomain) -> Result<Option<DomainSegment>> {
|
||||||
panic!("unsupported platform")
|
panic!("unsupported platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn setup_page_tables(&mut self, _: &mut BootDomain) -> Result<()> {
|
async fn alloc_p2m_segment(&mut self, _: &mut BootDomain) -> Result<Option<DomainSegment>> {
|
||||||
panic!("unsupported platform")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn setup_hypercall_page(&mut self, _: &mut BootDomain) -> Result<()> {
|
|
||||||
panic!("unsupported platform")
|
panic!("unsupported platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +56,10 @@ impl BootSetupPlatform for UnsupportedPlatform {
|
|||||||
panic!("unsupported platform")
|
panic!("unsupported platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn setup_page_tables(&mut self, _: &mut BootDomain) -> Result<()> {
|
||||||
|
panic!("unsupported platform")
|
||||||
|
}
|
||||||
|
|
||||||
async fn setup_shared_info(&mut self, _: &mut BootDomain, _: u64) -> Result<()> {
|
async fn setup_shared_info(&mut self, _: &mut BootDomain, _: u64) -> Result<()> {
|
||||||
panic!("unsupported platform")
|
panic!("unsupported platform")
|
||||||
}
|
}
|
||||||
@ -76,11 +72,15 @@ impl BootSetupPlatform for UnsupportedPlatform {
|
|||||||
panic!("unsupported platform")
|
panic!("unsupported platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn gnttab_seed(&mut self, _: &mut BootDomain) -> Result<()> {
|
||||||
|
panic!("unsupported platform")
|
||||||
|
}
|
||||||
|
|
||||||
async fn vcpu(&mut self, _: &mut BootDomain) -> Result<()> {
|
async fn vcpu(&mut self, _: &mut BootDomain) -> Result<()> {
|
||||||
panic!("unsupported platform")
|
panic!("unsupported platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn gnttab_seed(&mut self, _: &mut BootDomain) -> Result<()> {
|
async fn setup_hypercall_page(&mut self, _: &mut BootDomain) -> Result<()> {
|
||||||
panic!("unsupported platform")
|
panic!("unsupported platform")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@ use log::{debug, trace};
|
|||||||
use nix::errno::Errno;
|
use nix::errno::Errno;
|
||||||
use slice_copy::copy;
|
use slice_copy::copy;
|
||||||
use xencall::sys::{
|
use xencall::sys::{
|
||||||
x8664VcpuGuestContext, CreateDomain, E820Entry, VcpuGuestContextAny, E820_MAX, E820_RAM,
|
x8664VcpuGuestContext, CreateDomain, VcpuGuestContextAny, MMUEXT_PIN_L4_TABLE,
|
||||||
E820_UNUSABLE, MMUEXT_PIN_L4_TABLE, XEN_DOMCTL_CDF_IOMMU,
|
XEN_DOMCTL_CDF_IOMMU,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -282,154 +282,6 @@ impl X86PvPlatform {
|
|||||||
self.table.mappings[m] = map;
|
self.table.mappings[m] = map;
|
||||||
Ok(m)
|
Ok(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn e820_sanitize(
|
|
||||||
&self,
|
|
||||||
mut source: Vec<E820Entry>,
|
|
||||||
map_limit_kb: u64,
|
|
||||||
balloon_kb: u64,
|
|
||||||
) -> Result<Vec<E820Entry>> {
|
|
||||||
let mut e820 = vec![E820Entry::default(); E820_MAX as usize];
|
|
||||||
|
|
||||||
for entry in &mut source {
|
|
||||||
if entry.addr > 0x100000 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// entries under 1MB should be removed.
|
|
||||||
entry.typ = 0;
|
|
||||||
entry.size = 0;
|
|
||||||
entry.addr = u64::MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut lowest = u64::MAX;
|
|
||||||
let mut highest = 0;
|
|
||||||
|
|
||||||
for entry in &source {
|
|
||||||
if entry.typ == E820_RAM || entry.typ == E820_UNUSABLE || entry.typ == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
lowest = if entry.addr < lowest {
|
|
||||||
entry.addr
|
|
||||||
} else {
|
|
||||||
lowest
|
|
||||||
};
|
|
||||||
|
|
||||||
highest = if entry.addr + entry.size > highest {
|
|
||||||
entry.addr + entry.size
|
|
||||||
} else {
|
|
||||||
highest
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let start_kb = if lowest > 1024 { lowest >> 10 } else { 0 };
|
|
||||||
|
|
||||||
let mut idx: usize = 0;
|
|
||||||
|
|
||||||
e820[idx].addr = 0;
|
|
||||||
e820[idx].size = map_limit_kb << 10;
|
|
||||||
e820[idx].typ = E820_RAM;
|
|
||||||
|
|
||||||
let mut delta_kb = 0u64;
|
|
||||||
|
|
||||||
if start_kb > 0 && map_limit_kb > start_kb {
|
|
||||||
delta_kb = map_limit_kb - start_kb;
|
|
||||||
if delta_kb > 0 {
|
|
||||||
e820[idx].size -= delta_kb << 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let ram_end = source[0].addr + source[0].size;
|
|
||||||
idx += 1;
|
|
||||||
|
|
||||||
for src in &mut source {
|
|
||||||
let end = src.addr + src.size;
|
|
||||||
if src.typ == E820_UNUSABLE || end < ram_end {
|
|
||||||
src.typ = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if src.typ != E820_RAM {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if src.addr >= (1 << 32) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if src.addr < ram_end {
|
|
||||||
let delta = ram_end - src.addr;
|
|
||||||
src.typ = E820_UNUSABLE;
|
|
||||||
|
|
||||||
if src.size < delta {
|
|
||||||
src.typ = 0;
|
|
||||||
} else {
|
|
||||||
src.size -= delta;
|
|
||||||
src.addr = ram_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if src.addr + src.size != end {
|
|
||||||
src.typ = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if end > ram_end {
|
|
||||||
src.typ = E820_UNUSABLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if lowest > ram_end {
|
|
||||||
let mut add_unusable = true;
|
|
||||||
|
|
||||||
for src in &mut source {
|
|
||||||
if !add_unusable {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if src.typ != E820_UNUSABLE {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ram_end != src.addr {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if lowest != src.addr + src.size {
|
|
||||||
src.size = lowest - src.addr;
|
|
||||||
}
|
|
||||||
add_unusable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if add_unusable {
|
|
||||||
e820[1].typ = E820_UNUSABLE;
|
|
||||||
e820[1].addr = ram_end;
|
|
||||||
e820[1].size = lowest - ram_end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for src in &source {
|
|
||||||
if src.typ == E820_RAM || src.typ == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
e820[idx].typ = src.typ;
|
|
||||||
e820[idx].addr = src.addr;
|
|
||||||
e820[idx].size = src.size;
|
|
||||||
idx += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if balloon_kb > 0 || delta_kb > 0 {
|
|
||||||
e820[idx].typ = E820_RAM;
|
|
||||||
e820[idx].addr = if (1u64 << 32u64) > highest {
|
|
||||||
1u64 << 32u64
|
|
||||||
} else {
|
|
||||||
highest
|
|
||||||
};
|
|
||||||
e820[idx].size = (delta_kb << 10) + (balloon_kb << 10);
|
|
||||||
}
|
|
||||||
Ok(e820)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
@ -576,31 +428,6 @@ impl BootSetupPlatform for X86PvPlatform {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn alloc_p2m_segment(
|
|
||||||
&mut self,
|
|
||||||
domain: &mut BootDomain,
|
|
||||||
) -> Result<Option<DomainSegment>> {
|
|
||||||
let mut p2m_alloc_size =
|
|
||||||
((domain.phys.p2m_size() * 8) + X86_PAGE_SIZE - 1) & !(X86_PAGE_SIZE - 1);
|
|
||||||
let from = domain.image_info.virt_p2m_base;
|
|
||||||
let to = from + p2m_alloc_size - 1;
|
|
||||||
let m = self.count_page_tables(domain, from, to, domain.pfn_alloc_end)?;
|
|
||||||
|
|
||||||
let pgtables: usize;
|
|
||||||
{
|
|
||||||
let map = &mut self.table.mappings[m];
|
|
||||||
map.area.pfn = domain.pfn_alloc_end;
|
|
||||||
for lvl_idx in 0..4 {
|
|
||||||
map.levels[lvl_idx].pfn += p2m_alloc_size >> X86_PAGE_SHIFT;
|
|
||||||
}
|
|
||||||
pgtables = map.area.pgtables;
|
|
||||||
}
|
|
||||||
self.table.mappings_count += 1;
|
|
||||||
p2m_alloc_size += (pgtables << X86_PAGE_SHIFT) as u64;
|
|
||||||
let p2m_segment = domain.alloc_segment(0, p2m_alloc_size).await?;
|
|
||||||
Ok(Some(p2m_segment))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn alloc_page_tables(
|
async fn alloc_page_tables(
|
||||||
&mut self,
|
&mut self,
|
||||||
domain: &mut BootDomain,
|
domain: &mut BootDomain,
|
||||||
@ -635,6 +462,61 @@ impl BootSetupPlatform for X86PvPlatform {
|
|||||||
Ok(Some(segment))
|
Ok(Some(segment))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn alloc_p2m_segment(
|
||||||
|
&mut self,
|
||||||
|
domain: &mut BootDomain,
|
||||||
|
) -> Result<Option<DomainSegment>> {
|
||||||
|
let mut p2m_alloc_size =
|
||||||
|
((domain.phys.p2m_size() * 8) + X86_PAGE_SIZE - 1) & !(X86_PAGE_SIZE - 1);
|
||||||
|
let from = domain.image_info.virt_p2m_base;
|
||||||
|
let to = from + p2m_alloc_size - 1;
|
||||||
|
let m = self.count_page_tables(domain, from, to, domain.pfn_alloc_end)?;
|
||||||
|
|
||||||
|
let pgtables: usize;
|
||||||
|
{
|
||||||
|
let map = &mut self.table.mappings[m];
|
||||||
|
map.area.pfn = domain.pfn_alloc_end;
|
||||||
|
for lvl_idx in 0..4 {
|
||||||
|
map.levels[lvl_idx].pfn += p2m_alloc_size >> X86_PAGE_SHIFT;
|
||||||
|
}
|
||||||
|
pgtables = map.area.pgtables;
|
||||||
|
}
|
||||||
|
self.table.mappings_count += 1;
|
||||||
|
p2m_alloc_size += (pgtables << X86_PAGE_SHIFT) as u64;
|
||||||
|
let p2m_segment = domain.alloc_segment(0, p2m_alloc_size).await?;
|
||||||
|
Ok(Some(p2m_segment))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn alloc_magic_pages(&mut self, domain: &mut BootDomain) -> Result<()> {
|
||||||
|
if domain.image_info.virt_p2m_base >= domain.image_info.virt_base
|
||||||
|
|| (domain.image_info.virt_p2m_base & ((1 << self.page_shift()) - 1)) != 0
|
||||||
|
{
|
||||||
|
self.p2m_segment = self.alloc_p2m_segment(domain).await?;
|
||||||
|
}
|
||||||
|
self.start_info_segment = Some(domain.alloc_page()?);
|
||||||
|
self.xenstore_segment = Some(domain.alloc_page()?);
|
||||||
|
domain.store_mfn = domain.phys.p2m[self.xenstore_segment.as_ref().unwrap().pfn as usize];
|
||||||
|
let evtchn = domain.call.evtchn_alloc_unbound(domain.domid, 0).await?;
|
||||||
|
let page = domain.alloc_page()?;
|
||||||
|
domain.console_evtchn = evtchn;
|
||||||
|
domain.console_mfn = domain.phys.p2m[page.pfn as usize];
|
||||||
|
self.page_table_segment = self.alloc_page_tables(domain).await?;
|
||||||
|
self.boot_stack_segment = Some(domain.alloc_page()?);
|
||||||
|
|
||||||
|
if domain.virt_pgtab_end > 0 {
|
||||||
|
domain.alloc_padding_pages(domain.virt_pgtab_end)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.p2m_segment.is_none() {
|
||||||
|
if let Some(mut p2m_segment) = self.alloc_p2m_segment(domain).await? {
|
||||||
|
p2m_segment.vstart = domain.image_info.virt_p2m_base;
|
||||||
|
self.p2m_segment = Some(p2m_segment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn setup_page_tables(&mut self, domain: &mut BootDomain) -> Result<()> {
|
async fn setup_page_tables(&mut self, domain: &mut BootDomain) -> Result<()> {
|
||||||
let p2m_segment = self
|
let p2m_segment = self
|
||||||
.p2m_segment
|
.p2m_segment
|
||||||
@ -696,47 +578,6 @@ impl BootSetupPlatform for X86PvPlatform {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn setup_hypercall_page(&mut self, domain: &mut BootDomain) -> Result<()> {
|
|
||||||
if domain.image_info.virt_hypercall == u64::MAX {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let pfn =
|
|
||||||
(domain.image_info.virt_hypercall - domain.image_info.virt_base) >> self.page_shift();
|
|
||||||
let mfn = domain.phys.p2m[pfn as usize];
|
|
||||||
domain.call.hypercall_init(domain.domid, mfn).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn alloc_magic_pages(&mut self, domain: &mut BootDomain) -> Result<()> {
|
|
||||||
if domain.image_info.virt_p2m_base >= domain.image_info.virt_base
|
|
||||||
|| (domain.image_info.virt_p2m_base & ((1 << self.page_shift()) - 1)) != 0
|
|
||||||
{
|
|
||||||
self.p2m_segment = self.alloc_p2m_segment(domain).await?;
|
|
||||||
}
|
|
||||||
self.start_info_segment = Some(domain.alloc_page()?);
|
|
||||||
self.xenstore_segment = Some(domain.alloc_page()?);
|
|
||||||
domain.store_mfn = domain.phys.p2m[self.xenstore_segment.as_ref().unwrap().pfn as usize];
|
|
||||||
let evtchn = domain.call.evtchn_alloc_unbound(domain.domid, 0).await?;
|
|
||||||
let page = domain.alloc_page()?;
|
|
||||||
domain.console_evtchn = evtchn;
|
|
||||||
domain.console_mfn = domain.phys.p2m[page.pfn as usize];
|
|
||||||
self.page_table_segment = self.alloc_page_tables(domain).await?;
|
|
||||||
self.boot_stack_segment = Some(domain.alloc_page()?);
|
|
||||||
|
|
||||||
if domain.virt_pgtab_end > 0 {
|
|
||||||
domain.alloc_padding_pages(domain.virt_pgtab_end)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.p2m_segment.is_none() {
|
|
||||||
if let Some(mut p2m_segment) = self.alloc_p2m_segment(domain).await? {
|
|
||||||
p2m_segment.vstart = domain.image_info.virt_p2m_base;
|
|
||||||
self.p2m_segment = Some(p2m_segment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn setup_shared_info(
|
async fn setup_shared_info(
|
||||||
&mut self,
|
&mut self,
|
||||||
domain: &mut BootDomain,
|
domain: &mut BootDomain,
|
||||||
@ -803,8 +644,10 @@ impl BootSetupPlatform for X86PvPlatform {
|
|||||||
(*info).store_mfn = domain.phys.p2m[xenstore_segment.pfn as usize];
|
(*info).store_mfn = domain.phys.p2m[xenstore_segment.pfn as usize];
|
||||||
(*info).console.mfn = domain.console_mfn;
|
(*info).console.mfn = domain.console_mfn;
|
||||||
(*info).console.evtchn = domain.console_evtchn;
|
(*info).console.evtchn = domain.console_evtchn;
|
||||||
(*info).mod_start = domain.initrd_segment.vstart;
|
if let Some(ref initrd_segment) = domain.initrd_segment {
|
||||||
(*info).mod_len = domain.initrd_segment.size;
|
(*info).mod_start = initrd_segment.vstart;
|
||||||
|
(*info).mod_len = initrd_segment.size;
|
||||||
|
}
|
||||||
for (i, c) in domain.cmdline.chars().enumerate() {
|
for (i, c) in domain.cmdline.chars().enumerate() {
|
||||||
(*info).cmdline[i] = c as c_char;
|
(*info).cmdline[i] = c as c_char;
|
||||||
}
|
}
|
||||||
@ -828,12 +671,6 @@ impl BootSetupPlatform for X86PvPlatform {
|
|||||||
domain.phys.unmap(pg_pfn)?;
|
domain.phys.unmap(pg_pfn)?;
|
||||||
domain.phys.unmap(p2m_segment.pfn)?;
|
domain.phys.unmap(p2m_segment.pfn)?;
|
||||||
|
|
||||||
let map = domain.call.get_memory_map(E820_MAX).await?;
|
|
||||||
let mem_mb = domain.total_pages >> (20 - self.page_shift());
|
|
||||||
let mem_kb = mem_mb * 1024;
|
|
||||||
let e820 = self.e820_sanitize(map, mem_kb, 0)?;
|
|
||||||
domain.call.set_memory_map(domain.domid, e820).await?;
|
|
||||||
|
|
||||||
domain
|
domain
|
||||||
.call
|
.call
|
||||||
.mmuext(domain.domid, MMUEXT_PIN_L4_TABLE, pg_mfn, 0)
|
.mmuext(domain.domid, MMUEXT_PIN_L4_TABLE, pg_mfn, 0)
|
||||||
@ -841,6 +678,39 @@ impl BootSetupPlatform for X86PvPlatform {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn gnttab_seed(&mut self, domain: &mut BootDomain) -> Result<()> {
|
||||||
|
let xenstore_segment = self
|
||||||
|
.xenstore_segment
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(Error::MemorySetupFailed("xenstore_segment missing"))?;
|
||||||
|
|
||||||
|
let console_gfn = domain.console_mfn as usize;
|
||||||
|
let xenstore_gfn = domain.phys.p2m[xenstore_segment.pfn as usize];
|
||||||
|
let addr = domain
|
||||||
|
.call
|
||||||
|
.mmap(0, 1 << XEN_PAGE_SHIFT)
|
||||||
|
.await
|
||||||
|
.ok_or(Error::MmapFailed)?;
|
||||||
|
domain
|
||||||
|
.call
|
||||||
|
.map_resource(domain.domid, 1, 0, 0, 1, addr)
|
||||||
|
.await?;
|
||||||
|
let entries = unsafe { slice::from_raw_parts_mut(addr as *mut GrantEntry, 2) };
|
||||||
|
entries[0].flags = 1 << 0;
|
||||||
|
entries[0].domid = 0;
|
||||||
|
entries[0].frame = console_gfn as u32;
|
||||||
|
entries[1].flags = 1 << 0;
|
||||||
|
entries[1].domid = 0;
|
||||||
|
entries[1].frame = xenstore_gfn as u32;
|
||||||
|
unsafe {
|
||||||
|
let result = munmap(addr as *mut c_void, 1 << XEN_PAGE_SHIFT);
|
||||||
|
if result != 0 {
|
||||||
|
return Err(Error::UnmapFailed(Errno::from_raw(result)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn vcpu(&mut self, domain: &mut BootDomain) -> Result<()> {
|
async fn vcpu(&mut self, domain: &mut BootDomain) -> Result<()> {
|
||||||
let page_table_segment = self
|
let page_table_segment = self
|
||||||
.page_table_segment
|
.page_table_segment
|
||||||
@ -885,36 +755,14 @@ impl BootSetupPlatform for X86PvPlatform {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn gnttab_seed(&mut self, domain: &mut BootDomain) -> Result<()> {
|
async fn setup_hypercall_page(&mut self, domain: &mut BootDomain) -> Result<()> {
|
||||||
let xenstore_segment = self
|
if domain.image_info.virt_hypercall == u64::MAX {
|
||||||
.xenstore_segment
|
return Ok(());
|
||||||
.as_ref()
|
|
||||||
.ok_or(Error::MemorySetupFailed("xenstore_segment missing"))?;
|
|
||||||
|
|
||||||
let console_gfn = domain.console_mfn as usize;
|
|
||||||
let xenstore_gfn = domain.phys.p2m[xenstore_segment.pfn as usize];
|
|
||||||
let addr = domain
|
|
||||||
.call
|
|
||||||
.mmap(0, 1 << XEN_PAGE_SHIFT)
|
|
||||||
.await
|
|
||||||
.ok_or(Error::MmapFailed)?;
|
|
||||||
domain
|
|
||||||
.call
|
|
||||||
.map_resource(domain.domid, 1, 0, 0, 1, addr)
|
|
||||||
.await?;
|
|
||||||
let entries = unsafe { slice::from_raw_parts_mut(addr as *mut GrantEntry, 2) };
|
|
||||||
entries[0].flags = 1 << 0;
|
|
||||||
entries[0].domid = 0;
|
|
||||||
entries[0].frame = console_gfn as u32;
|
|
||||||
entries[1].flags = 1 << 0;
|
|
||||||
entries[1].domid = 0;
|
|
||||||
entries[1].frame = xenstore_gfn as u32;
|
|
||||||
unsafe {
|
|
||||||
let result = munmap(addr as *mut c_void, 1 << XEN_PAGE_SHIFT);
|
|
||||||
if result != 0 {
|
|
||||||
return Err(Error::UnmapFailed(Errno::from_raw(result)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let pfn =
|
||||||
|
(domain.image_info.virt_hypercall - domain.image_info.virt_base) >> self.page_shift();
|
||||||
|
let mfn = domain.phys.p2m[pfn as usize];
|
||||||
|
domain.call.hypercall_init(domain.domid, mfn).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,12 @@ async fn list_recursive(client: &XsdClient, path: &str) -> Result<()> {
|
|||||||
let children = client.list(path).await?;
|
let children = client.list(path).await?;
|
||||||
for child in children {
|
for child in children {
|
||||||
let full = format!("{}/{}", if path == "/" { "" } else { path }, child);
|
let full = format!("{}/{}", if path == "/" { "" } else { path }, child);
|
||||||
let value = client
|
let value = client.read(full.as_str()).await?.expect("expected value");
|
||||||
.read_string(full.as_str())
|
let stringified = match String::from_utf8(value) {
|
||||||
.await?
|
Ok(string) => format!("\"{}\"", string),
|
||||||
.expect("expected value");
|
Err(error) => format!("{:?}", error.into_bytes()),
|
||||||
println!("{} = {:?}", full, value,);
|
};
|
||||||
|
println!("{} = {}", full, stringified);
|
||||||
pending.push(full);
|
pending.push(full);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,13 @@ impl Error {
|
|||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_again_response(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Error::ResponseError(message) => message == "EAGAIN",
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
@ -55,6 +55,27 @@ impl Drop for XsdWatchHandle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct XsdMultiWatchHandle {
|
||||||
|
pub paths: Vec<String>,
|
||||||
|
pub id: u32,
|
||||||
|
unwatch_sender: Sender<(u32, String)>,
|
||||||
|
pub receiver: Receiver<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XsdMultiWatchHandle {
|
||||||
|
pub fn add_path(&mut self, path: impl AsRef<str>) {
|
||||||
|
self.paths.push(path.as_ref().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for XsdMultiWatchHandle {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
for path in &self.paths {
|
||||||
|
let _ = self.unwatch_sender.try_send((self.id, path.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(async_fn_in_trait)]
|
#[allow(async_fn_in_trait)]
|
||||||
pub trait XsdInterface {
|
pub trait XsdInterface {
|
||||||
async fn list<P: AsRef<str>>(&self, path: P) -> Result<Vec<String>>;
|
async fn list<P: AsRef<str>>(&self, path: P) -> Result<Vec<String>>;
|
||||||
@ -141,7 +162,7 @@ impl XsdClient {
|
|||||||
}
|
}
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
result.unwrap().parse_bool()
|
result?.parse_bool()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_perms<P: AsRef<str>>(
|
async fn set_perms<P: AsRef<str>>(
|
||||||
@ -197,6 +218,16 @@ impl XsdClient {
|
|||||||
response.parse_bool()
|
response.parse_bool()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn create_multi_watch(&self) -> Result<XsdMultiWatchHandle> {
|
||||||
|
let (id, receiver, unwatch_sender) = self.socket.add_watch().await?;
|
||||||
|
Ok(XsdMultiWatchHandle {
|
||||||
|
paths: vec![],
|
||||||
|
id,
|
||||||
|
receiver,
|
||||||
|
unwatch_sender,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn create_watch<P: AsRef<str>>(&self, path: P) -> Result<XsdWatchHandle> {
|
pub async fn create_watch<P: AsRef<str>>(&self, path: P) -> Result<XsdWatchHandle> {
|
||||||
let (id, receiver, unwatch_sender) = self.socket.add_watch().await?;
|
let (id, receiver, unwatch_sender) = self.socket.add_watch().await?;
|
||||||
Ok(XsdWatchHandle {
|
Ok(XsdWatchHandle {
|
||||||
@ -319,6 +350,20 @@ impl XsdTransaction {
|
|||||||
.parse_bool()
|
.parse_bool()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn maybe_commit(&self) -> Result<bool> {
|
||||||
|
match self.end(false).await {
|
||||||
|
Ok(result) => Ok(result),
|
||||||
|
|
||||||
|
Err(error) => {
|
||||||
|
if error.is_again_response() {
|
||||||
|
Ok(false)
|
||||||
|
} else {
|
||||||
|
Err(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn commit(&self) -> Result<bool> {
|
pub async fn commit(&self) -> Result<bool> {
|
||||||
self.end(false).await
|
self.end(false).await
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user