mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
273 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed9e54d6c7 | ||
|
|
282a1001ef | ||
|
|
0d682e185f | ||
|
|
8ec5a248cd | ||
|
|
763ea7da42 | ||
|
|
2856074d12 | ||
|
|
485900eeb4 | ||
|
|
6988da79d2 | ||
|
|
80f7d62e1c | ||
|
|
4c5c7fb77c | ||
|
|
1b8b2fc1af | ||
|
|
7a911db4fa | ||
|
|
8f3462b3e3 | ||
|
|
1f70d16c12 | ||
|
|
25f815e7d1 | ||
|
|
76bde33fc4 | ||
|
|
9591e225e6 | ||
|
|
df5b803a63 | ||
|
|
8c38992143 | ||
|
|
068ea9a4db | ||
|
|
b1752994d5 | ||
|
|
a7f3620564 | ||
|
|
54c484397e | ||
|
|
f9b36a3412 | ||
|
|
1793c1cb39 | ||
|
|
b67755be17 | ||
|
|
4a2539cb92 | ||
|
|
f39b08f21f | ||
|
|
6a1e637a46 | ||
|
|
041acf2a65 | ||
|
|
2ee7c663c2 | ||
|
|
b865b059aa | ||
|
|
1012530bdd | ||
|
|
5b60e5372b | ||
|
|
dc540a7549 | ||
|
|
6c492d8512 | ||
|
|
944d09208e | ||
|
|
c591a96ecb | ||
|
|
eb0ce4b4d6 | ||
|
|
c59b5e18a8 | ||
|
|
98f949d35d | ||
|
|
0b182be65e | ||
|
|
fbec4a070d | ||
|
|
5ba3b80e83 | ||
|
|
1fda830961 | ||
|
|
8c22c03e1e | ||
|
|
51fdc48817 | ||
|
|
999bc2baca | ||
|
|
072af95609 | ||
|
|
6fda567ab8 | ||
|
|
cef51a9de0 | ||
|
|
5810e3bee8 | ||
|
|
6d452fad52 | ||
|
|
561e6a5b6e | ||
|
|
3d9c9d720f | ||
|
|
dde2eb531b | ||
|
|
2cc21d00e2 | ||
|
|
4e31206403 | ||
|
|
00c1b4a95b | ||
|
|
f42c6d567d | ||
|
|
ac0aa8aa5e | ||
|
|
8a7f3425d8 | ||
|
|
c5fdc47918 | ||
|
|
6bc46ba9c1 | ||
|
|
3c26e9b741 | ||
|
|
226a21811e | ||
|
|
79cf3952f3 | ||
|
|
e45e3df64d | ||
|
|
9f89e0a4e0 | ||
|
|
19957d4fa4 | ||
|
|
ea58d2f68a | ||
|
|
71a7d5d85e | ||
|
|
531621474f | ||
|
|
46b790b582 | ||
|
|
9990439829 | ||
|
|
d295de4cd5 | ||
|
|
868d1f6902 | ||
|
|
62f1b6bc61 | ||
|
|
2afd2f9f52 | ||
|
|
a85d27bd52 | ||
|
|
53aecf9341 | ||
|
|
57e9b80123 | ||
|
|
964985bb5c | ||
|
|
cb18edb535 | ||
|
|
05e0daa411 | ||
|
|
a62b611659 | ||
|
|
dc75519372 | ||
|
|
d9ee5c2640 | ||
|
|
ec4a55b860 | ||
|
|
3df8c6fd3b | ||
|
|
474125db12 | ||
|
|
a3bb459bff | ||
|
|
3d947fafe5 | ||
|
|
7d9a704474 | ||
|
|
47f9ee418d | ||
|
|
38c25ea5df | ||
|
|
21da11cee7 | ||
|
|
bb444f3e7c | ||
|
|
3b0f6247b7 | ||
|
|
9b4940f154 | ||
|
|
4bf01fa366 | ||
|
|
4052a89268 | ||
|
|
73efcc1f73 | ||
|
|
b3b252a5d1 | ||
|
|
65f42c1f62 | ||
|
|
bc3a276b43 | ||
|
|
3abc1e1e51 | ||
|
|
147e89f413 | ||
|
|
1327350b4e | ||
|
|
9d31856c08 | ||
|
|
9f457887df | ||
|
|
4269fb1366 | ||
|
|
d889bf625d | ||
|
|
00b4357da3 | ||
|
|
579d0d56e3 | ||
|
|
64ac6ae1a3 | ||
|
|
8ca7318046 | ||
|
|
ec16e14b2f | ||
|
|
af08f6bca5 | ||
|
|
1867ee3ddd | ||
|
|
4e0b41ce77 | ||
|
|
b731c8863f | ||
|
|
8d86cffc87 | ||
|
|
1a09381d1e | ||
|
|
103494f441 | ||
|
|
1125fd2667 | ||
|
|
e5155bab62 | ||
|
|
eb056d374a | ||
|
|
487458c9f8 | ||
|
|
4d74525bdc | ||
|
|
9524a8ffe8 | ||
|
|
fa0565234c | ||
|
|
755eb80c49 | ||
|
|
ac3ab19443 | ||
|
|
8585d1a355 | ||
|
|
5f539e429a | ||
|
|
6835de0f8c | ||
|
|
bf662522dd | ||
|
|
575221cba9 | ||
|
|
9b7af90b4c | ||
|
|
b4ed9f867e | ||
|
|
af958c9448 | ||
|
|
a4859ffe85 | ||
|
|
87f57f7c1e | ||
|
|
a423bf13ad | ||
|
|
7199119bb2 | ||
|
|
5b54dc60aa | ||
|
|
ecb66b5e94 | ||
|
|
cd7f36a713 | ||
|
|
f7b6d99c2d | ||
|
|
e1a70f676a | ||
|
|
bec47ebcc9 | ||
|
|
ddc9004471 | ||
|
|
0c9b4fefc0 | ||
|
|
df10dab952 | ||
|
|
57ee561c39 | ||
|
|
a330c531b0 | ||
|
|
e4211272ad | ||
|
|
f4e2198767 | ||
|
|
f8f0b40e94 | ||
|
|
87c0791d53 | ||
|
|
c46a9592c0 | ||
|
|
561791ed22 | ||
|
|
857edc71a9 | ||
|
|
0e29379bcf | ||
|
|
23352c4945 | ||
|
|
160528b6f5 | ||
|
|
00200296ec | ||
|
|
c612886150 | ||
|
|
a749182777 | ||
|
|
2e8eb96ef6 | ||
|
|
686e6d5082 | ||
|
|
b1a87cb698 | ||
|
|
869e96ed10 | ||
|
|
bcaa299c55 | ||
|
|
454964cc90 | ||
|
|
2afd0e626a | ||
|
|
e885c35a54 | ||
|
|
ff7773044c | ||
|
|
5aaa647639 | ||
|
|
7b99bd9496 | ||
|
|
bb19c3d2b7 | ||
|
|
aa66526ea0 | ||
|
|
c3ce5aa5b1 | ||
|
|
ad33f7c5e6 | ||
|
|
9fc3379e8d | ||
|
|
d56263706d | ||
|
|
4a00371fe8 | ||
|
|
ae7f74d0a8 | ||
|
|
cb6a14dec9 | ||
|
|
e931c40e96 | ||
|
|
a96a5d7816 | ||
|
|
5e2c70b87f | ||
|
|
eca6609dbb | ||
|
|
c5e966c972 | ||
|
|
0856ebbd42 | ||
|
|
422bf89d4d | ||
|
|
500d91311f | ||
|
|
b5270e0b45 | ||
|
|
f84ddedb1a | ||
|
|
b5d10eb28b | ||
|
|
de14db7320 | ||
|
|
269b78ca0f | ||
|
|
5f98710471 | ||
|
|
613c60fc6f | ||
|
|
3f62a172b2 | ||
|
|
423c54eafe | ||
|
|
e1a63822fa | ||
|
|
3083da154e | ||
|
|
5c20869dd5 | ||
|
|
ec27e0ee82 | ||
|
|
0bb509e5d2 | ||
|
|
a161e56bef | ||
|
|
777ba6d577 | ||
|
|
b0258c7b13 | ||
|
|
55c9562c4d | ||
|
|
7419a021cd | ||
|
|
cc9908efe2 | ||
|
|
474e55724f | ||
|
|
b440cb7d23 | ||
|
|
f21fc1dcb6 | ||
|
|
4d2cc3778f | ||
|
|
563919fc4a | ||
|
|
05e5106b9b | ||
|
|
d461daa7fa | ||
|
|
58dbf07453 | ||
|
|
c8d9a43072 | ||
|
|
6a8c0fc887 | ||
|
|
08135fa085 | ||
|
|
45fbf840db | ||
|
|
9fdfdf25fb | ||
|
|
f8f2f09a19 | ||
|
|
edf6dda705 | ||
|
|
f1ab86fecb | ||
|
|
d2b194c4f1 | ||
|
|
75797827d5 | ||
|
|
8b7781e267 | ||
|
|
d20f647b32 | ||
|
|
2de7121680 | ||
|
|
709958be34 | ||
|
|
4201bf4011 | ||
|
|
beb4f86b82 | ||
|
|
72d552e5e1 | ||
|
|
dd592e86fd | ||
|
|
f9f0a60dcf | ||
|
|
9810ed4496 | ||
|
|
02866fccb0 | ||
|
|
a455917db5 | ||
|
|
4a1eab11b4 | ||
|
|
f0ad7fc74f | ||
|
|
2d8635f04a | ||
|
|
c445513976 | ||
|
|
fc2cc53d51 | ||
|
|
0e8769d76a | ||
|
|
47b5581c50 | ||
|
|
3710932248 | ||
|
|
ddad5f3510 | ||
|
|
28683b8471 | ||
|
|
e1cf889dbd | ||
|
|
5894b5370c | ||
|
|
fabc940b65 | ||
|
|
6b9c5d21c4 | ||
|
|
c140fd6220 | ||
|
|
7063c41811 | ||
|
|
92f9866e2f | ||
|
|
23935ad96b | ||
|
|
335fd465fe | ||
|
|
b8e5c2ecca | ||
|
|
a99c33ccfa | ||
|
|
b70f2c5385 | ||
|
|
84bbaf06d1 | ||
|
|
121729a3b0 | ||
|
|
e300c3dcf7 |
14
.github/workflows/build.yaml
vendored
14
.github/workflows/build.yaml
vendored
@@ -29,14 +29,9 @@ jobs:
|
||||
- name: Install Rust toolchain (${{ matrix.rust }})
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
|
||||
- name: Debug Check (default features)
|
||||
run: |
|
||||
git rev-list origin/main..$GITHUB_SHA | xargs -t -I % sh -c 'git checkout %; cargo check --tests --all --target=${{ matrix.target }}'
|
||||
git checkout $GITHUB_SHA
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
|
||||
- name: Build (default features)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor -- -D warnings
|
||||
@@ -59,6 +54,9 @@ jobs:
|
||||
- name: Build (common + mshv)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "common,mshv" -- -D warnings
|
||||
|
||||
- name: Build (common + mshv + kvm)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "common,mshv,kvm" -- -D warnings
|
||||
|
||||
- name: Release Build (default features)
|
||||
run: cargo build --locked --all --release --target=${{ matrix.target }}
|
||||
|
||||
|
||||
14
.github/workflows/quality-aarch64.yaml
vendored
14
.github/workflows/quality-aarch64.yaml
vendored
@@ -25,10 +25,16 @@ jobs:
|
||||
- name: Install Rust toolchain (${{ matrix.rust }})
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Debug Check (default features)
|
||||
run: |
|
||||
git rev-list origin/main..$GITHUB_SHA | xargs -t -I % sh -c 'git checkout %; cargo check --tests --all --target=${{ matrix.target }}'
|
||||
git checkout $GITHUB_SHA
|
||||
|
||||
- name: Formatting (rustfmt)
|
||||
run: cargo fmt -- --check
|
||||
|
||||
|
||||
8
.github/workflows/quality.yaml
vendored
8
.github/workflows/quality.yaml
vendored
@@ -30,6 +30,11 @@ jobs:
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Debug Check (default features)
|
||||
run: |
|
||||
git rev-list origin/main..$GITHUB_SHA | xargs -t -I % sh -c 'git checkout %; cargo check --tests --all --target=${{ matrix.target }}'
|
||||
git checkout $GITHUB_SHA
|
||||
|
||||
- name: Formatting (rustfmt)
|
||||
run: cargo fmt -- --check
|
||||
|
||||
@@ -51,5 +56,8 @@ jobs:
|
||||
- name: Clippy (common + mshv)
|
||||
run: cargo clippy --locked --all --all-targets --no-default-features --tests --features "common,mshv" -- -D warnings
|
||||
|
||||
- name: Clippy (common + mshv + kvm)
|
||||
run: cargo clippy --locked --all --all-targets --no-default-features --tests --features "common,mshv,kvm" -- -D warnings
|
||||
|
||||
- name: Check build did not modify any files
|
||||
run: test -z "$(git status --porcelain)"
|
||||
|
||||
@@ -17,6 +17,23 @@ We follow the [Rust Style](https://github.com/rust-dev-tools/fmt-rfcs/blob/maste
|
||||
convention and enforce it through the Continuous Integration (CI) process calling into `rustfmt`
|
||||
for each submitted Pull Request (PR).
|
||||
|
||||
## Basic Checks
|
||||
|
||||
Please consider creating the following hook as `.git/hooks/pre-commit` in order
|
||||
to ensure basic correctness of your code. You can extend this further if you
|
||||
have specific features that you regularly develop against.
|
||||
|
||||
```sh
|
||||
#!/bin/sh
|
||||
|
||||
cargo fmt -- --check || exit 1
|
||||
cargo check --locked --all --all-targets --tests || exit 1
|
||||
cargo clippy --locked --all --all-targets --tests -- -D warnings || exit 1
|
||||
```
|
||||
|
||||
You will need to `chmod +x .git/hooks/pre-commit` to have it run on every
|
||||
commit you make.
|
||||
|
||||
## Certificate of Origin
|
||||
|
||||
In order to get a clear contribution chain of trust we use the [signed-off-by language](https://01.org/community/signed-process)
|
||||
|
||||
204
Cargo.lock
generated
204
Cargo.lock
generated
@@ -20,9 +20,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.58"
|
||||
version = "1.0.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
|
||||
checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -33,15 +33,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.5.0"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f"
|
||||
checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164"
|
||||
|
||||
[[package]]
|
||||
name = "arch"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"acpi_tables",
|
||||
"anyhow",
|
||||
"byteorder",
|
||||
"fdt",
|
||||
@@ -51,6 +50,7 @@ dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"uuid",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"vm-fdt",
|
||||
@@ -130,9 +130,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.2.8"
|
||||
version = "3.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83"
|
||||
checksum = "29e724a68d9319343bb3328c9cc2dfde263f4b3142ee1059a9980580171c954b"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
@@ -156,7 +156,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "25.0.0"
|
||||
version = "26.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -196,6 +196,41 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55626594feae15d266d52440b26ff77de0e22230cf0c113abe619084c1ddc910"
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "devices"
|
||||
version = "0.1.0"
|
||||
@@ -205,7 +240,6 @@ dependencies = [
|
||||
"arch",
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"epoll",
|
||||
"hypervisor",
|
||||
"libc",
|
||||
"log",
|
||||
@@ -271,9 +305,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fdt"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b643857cf70949306b81d7e92cb9d47add673868edac9863c4a49c42feaf3f1e"
|
||||
checksum = "964f5becd44d069dca0beea2b4bc05639ae7bf3b3f5369c295aff360bb57cca2"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "gdbstub"
|
||||
@@ -318,9 +358,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.1"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
@@ -342,8 +382,8 @@ name = "hypervisor"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"byteorder",
|
||||
"env_logger",
|
||||
"epoll",
|
||||
"iced-x86",
|
||||
"kvm-bindings",
|
||||
"kvm-ioctls",
|
||||
@@ -352,8 +392,9 @@ dependencies = [
|
||||
"mshv-bindings",
|
||||
"mshv-ioctls",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"thiserror",
|
||||
"vfio-ioctls",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
@@ -368,6 +409,12 @@ dependencies = [
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.1"
|
||||
@@ -389,9 +436,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.5.2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d75829ed9377bab6c90039fe47b9d84caceb4b5063266142e21bcce6550cda8"
|
||||
checksum = "427496d698429d86ff947537dff68facb83fe753b7abea4fab4ac1c4b7a4b94b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -408,9 +455,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
||||
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
|
||||
|
||||
[[package]]
|
||||
name = "kvm-bindings"
|
||||
@@ -440,9 +487,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.126"
|
||||
version = "0.2.131"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||
checksum = "04c3b4822ccebfa39c02fc03d1534441b22ead323fa0f48bb7ddd8e6ba076a40"
|
||||
|
||||
[[package]]
|
||||
name = "libssh2-sys"
|
||||
@@ -522,7 +569,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mshv-bindings"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#7ac1b80bff7220171c9a626b23b51620ca774468"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#a4c9916124d771fb9d0ccd97423ffb72876baa83"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"serde",
|
||||
@@ -534,7 +581,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mshv-ioctls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#7ac1b80bff7220171c9a626b23b51620ca774468"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#a4c9916124d771fb9d0ccd97423ffb72876baa83"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mshv-bindings",
|
||||
@@ -563,6 +610,7 @@ dependencies = [
|
||||
"rate_limiter",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"virtio-bindings",
|
||||
@@ -589,9 +637,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.13.0"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
||||
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
@@ -604,9 +652,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.74"
|
||||
version = "0.9.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1"
|
||||
checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
@@ -622,9 +670,9 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.1.0"
|
||||
version = "6.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
|
||||
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
@@ -653,9 +701,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.7"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc"
|
||||
checksum = "9423e2b32f7a043629287a536f21951e8c6a82482d0acb1eeebfc90bc2225b22"
|
||||
|
||||
[[package]]
|
||||
name = "pci"
|
||||
@@ -790,9 +838,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.40"
|
||||
version = "1.0.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
|
||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -810,9 +858,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.20"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -828,9 +876,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.13"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@@ -865,9 +913,9 @@ checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||
|
||||
[[package]]
|
||||
name = "remain"
|
||||
version = "0.2.3"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c35270ea384ac1762895831cc8acb96f171468e52cec82ed9186f9416209fa4"
|
||||
checksum = "06df529c0d271b27ac4a2c260f5ce2914b60bd44702cecec7b9f271adbdde23b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -885,9 +933,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.10"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
|
||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
@@ -906,24 +954,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.12"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
|
||||
checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.138"
|
||||
version = "1.0.143"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47"
|
||||
checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.138"
|
||||
version = "1.0.143"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c"
|
||||
checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -932,15 +980,37 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.82"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
|
||||
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89df7a26519371a3cce44fbb914c2819c84d9b897890987fa3ab096491cc0ea8"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_with_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de337f322382fcdfbb21a014f7c224ee041a23785651db67b9827403178f698f"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.14"
|
||||
@@ -992,9 +1062,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.98"
|
||||
version = "1.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
|
||||
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1056,18 +1126,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.31"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
||||
checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.31"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
||||
checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1076,9 +1146,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.1"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
|
||||
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
@@ -1140,7 +1210,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio-ioctls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#38272d3f06bbc0b4b3cfa3f5da07fd1c1c89e4d1"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#c52e88ac551183bd3784745a7143db45abdb05db"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
@@ -1159,7 +1229,6 @@ dependencies = [
|
||||
name = "vfio_user"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
@@ -1182,7 +1251,6 @@ dependencies = [
|
||||
"remain",
|
||||
"thiserror",
|
||||
"uuid",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1199,9 +1267,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vhost-user-backend"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3eeb6723bbee20dbc2db95c575941922fb96e77eb41786ecd1c8c03348709c4c"
|
||||
checksum = "558ac5ca9569fb03f518b2bdd17606809ffdc894b619b92d30df6c40c33d15f3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
@@ -1227,6 +1295,7 @@ dependencies = [
|
||||
"vhost",
|
||||
"vhost-user-backend",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
@@ -1291,11 +1360,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "519c0a333c871650269cba303bc108075d52a0c0d64f9b91fae61829b53725af"
|
||||
checksum = "b4f59652909f276e6edd8bf36e9f106480b2202f5f046717b3de14f1b4072a28"
|
||||
dependencies = [
|
||||
"log",
|
||||
"virtio-bindings",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
@@ -1316,7 +1386,6 @@ dependencies = [
|
||||
"anyhow",
|
||||
"hypervisor",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"vfio-ioctls",
|
||||
"vm-memory",
|
||||
@@ -1357,7 +1426,6 @@ name = "vm-virtio"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
]
|
||||
@@ -1411,9 +1479,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vmm-sys-util"
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "733537bded03aaa93543f785ae997727b30d1d9f4a03b7861d23290474242e11"
|
||||
checksum = "08604d7be03eb26e33b3cee3ed4aef2bf550b305d1cca60e84da5d28d3790b62"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
|
||||
20
Cargo.toml
20
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cloud-hypervisor"
|
||||
version = "25.0.0"
|
||||
version = "26.0.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
default-run = "cloud-hypervisor"
|
||||
@@ -16,25 +16,25 @@ rust-version = "1.60"
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.58"
|
||||
anyhow = "1.0.62"
|
||||
api_client = { path = "api_client" }
|
||||
clap = { version = "3.2.8", features = ["wrap_help","cargo"] }
|
||||
clap = { version = "3.2.17", features = ["wrap_help","cargo"] }
|
||||
epoll = "4.3.1"
|
||||
event_monitor = { path = "event_monitor" }
|
||||
hypervisor = { path = "hypervisor" }
|
||||
libc = "0.2.126"
|
||||
libc = "0.2.131"
|
||||
log = { version = "0.4.17", features = ["std"] }
|
||||
option_parser = { path = "option_parser" }
|
||||
seccompiler = "0.2.0"
|
||||
serde_json = "1.0.82"
|
||||
serde_json = "1.0.83"
|
||||
signal-hook = "0.3.14"
|
||||
thiserror = "1.0.31"
|
||||
thiserror = "1.0.32"
|
||||
vmm = { path = "vmm" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
vmm-sys-util = "0.10.0"
|
||||
vm-memory = "0.8.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.2.8", features = ["cargo"] }
|
||||
clap = { version = "3.2.17", features = ["cargo"] }
|
||||
|
||||
# List of patched crates
|
||||
[patch.crates-io]
|
||||
@@ -45,8 +45,8 @@ versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_deri
|
||||
[dev-dependencies]
|
||||
dirs = "4.0.0"
|
||||
net_util = { path = "net_util" }
|
||||
once_cell = "1.13.0"
|
||||
serde_json = "1.0.82"
|
||||
once_cell = "1.13.1"
|
||||
serde_json = "1.0.83"
|
||||
test_infra = { path = "test_infra" }
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
|
||||
61
Jenkinsfile
vendored
61
Jenkinsfile
vendored
@@ -23,10 +23,10 @@ pipeline{
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Check for fuzzer cargo files only changes') {
|
||||
stage ('Check for fuzzer files only changes') {
|
||||
when {
|
||||
expression {
|
||||
return fuzzCargoFileOnly()
|
||||
return fuzzFileOnly()
|
||||
}
|
||||
}
|
||||
steps {
|
||||
@@ -56,7 +56,7 @@ pipeline{
|
||||
stage ('Build') {
|
||||
parallel {
|
||||
stage ('Worker build') {
|
||||
agent { node { label 'focal' } }
|
||||
agent { node { label 'jammy' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
@@ -103,6 +103,9 @@ pipeline{
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
environment {
|
||||
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
@@ -123,6 +126,42 @@ pipeline{
|
||||
sh "scripts/dev_cli.sh tests --integration --libc musl"
|
||||
}
|
||||
}
|
||||
stage ('Install azure-cli') {
|
||||
steps {
|
||||
installAzureCli("bionic", "arm64")
|
||||
}
|
||||
}
|
||||
stage ('Download Windows image') {
|
||||
steps {
|
||||
sh '''#!/bin/bash -x
|
||||
IMG_BASENAME=windows-11-iot-enterprise-aarch64.raw
|
||||
IMG_PATH=$HOME/workloads/$IMG_BASENAME
|
||||
IMG_GZ_PATH=$HOME/workloads/$IMG_BASENAME.gz
|
||||
IMG_GZ_BLOB_NAME=windows-11-iot-enterprise-aarch64-9-min.raw.gz
|
||||
cp "scripts/$IMG_BASENAME.sha1" "$HOME/workloads/"
|
||||
pushd "$HOME/workloads"
|
||||
if sha1sum "$IMG_BASENAME.sha1" --check; then
|
||||
exit
|
||||
fi
|
||||
popd
|
||||
mkdir -p "$HOME/workloads"
|
||||
az storage blob download \
|
||||
--container-name private-images \
|
||||
--file "$IMG_GZ_PATH" \
|
||||
--name "$IMG_GZ_BLOB_NAME" \
|
||||
--connection-string "$AZURE_CONNECTION_STRING"
|
||||
gzip -d $IMG_GZ_PATH
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage ('Run Windows guest integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-windows --libc musl"
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
@@ -132,7 +171,7 @@ pipeline{
|
||||
}
|
||||
}
|
||||
stage ('Worker build (musl)') {
|
||||
agent { node { label 'focal' } }
|
||||
agent { node { label 'jammy' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
@@ -249,7 +288,7 @@ pipeline{
|
||||
}
|
||||
}
|
||||
stage ('Worker build - Windows guest') {
|
||||
agent { node { label 'focal' } }
|
||||
agent { node { label 'jammy' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
@@ -267,7 +306,7 @@ pipeline{
|
||||
}
|
||||
stage ('Install azure-cli') {
|
||||
steps {
|
||||
installAzureCli()
|
||||
installAzureCli("jammy", "amd64")
|
||||
}
|
||||
}
|
||||
stage ('Download assets') {
|
||||
@@ -295,7 +334,7 @@ pipeline{
|
||||
}
|
||||
}
|
||||
stage ('Worker build - Live Migration') {
|
||||
agent { node { label 'focal-small' } }
|
||||
agent { node { label 'jammy' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
@@ -397,10 +436,10 @@ def cancelPreviousBuilds() {
|
||||
}
|
||||
}
|
||||
|
||||
def installAzureCli() {
|
||||
def installAzureCli(distro, arch) {
|
||||
sh "sudo apt install -y ca-certificates curl apt-transport-https lsb-release gnupg"
|
||||
sh "curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null"
|
||||
sh "echo \"deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ focal main\" | sudo tee /etc/apt/sources.list.d/azure-cli.list"
|
||||
sh "echo \"deb [arch=${arch}] https://packages.microsoft.com/repos/azure-cli/ ${distro} main\" | sudo tee /etc/apt/sources.list.d/azure-cli.list"
|
||||
sh "sudo apt update"
|
||||
sh "sudo apt install -y azure-cli"
|
||||
}
|
||||
@@ -416,13 +455,13 @@ def boolean docsFileOnly() {
|
||||
) != 0
|
||||
}
|
||||
|
||||
def boolean fuzzCargoFileOnly() {
|
||||
def boolean fuzzFileOnly() {
|
||||
if (env.CHANGE_TARGET == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return sh(
|
||||
returnStatus: true,
|
||||
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v -E 'fuzz\\/Cargo.(toml|lock)'"
|
||||
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v -E 'fuzz/'"
|
||||
) != 0
|
||||
}
|
||||
|
||||
@@ -88,8 +88,8 @@ Hypervisor. Here, all the steps are based on Ubuntu, for other Linux
|
||||
distributions please replace the package manager and package name.
|
||||
|
||||
```shell
|
||||
# Install build-essential, git, and qemu-img
|
||||
$ sudo apt install git build-essential qemu-img
|
||||
# Install build-essential, git, and qemu-utils
|
||||
$ sudo apt install git build-essential qemu-utils
|
||||
# Install rust tool chain
|
||||
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
# If you want to build statically linked binary please add musl target
|
||||
@@ -166,7 +166,7 @@ cloud image. Here we will use a Ubuntu image:
|
||||
$ pushd $CLOUDH
|
||||
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
|
||||
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-amd64.img focal-server-cloudimg-amd64.raw
|
||||
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.4.0/hypervisor-fw
|
||||
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.4.1/hypervisor-fw
|
||||
$ popd
|
||||
```
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
//
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GenericAddress {
|
||||
pub address_space_id: u8,
|
||||
pub register_bit_width: u8,
|
||||
|
||||
@@ -5,4 +5,4 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
vmm-sys-util = "0.9.0"
|
||||
vmm-sys-util = "0.10.0"
|
||||
|
||||
@@ -101,6 +101,10 @@ fn parse_http_response(socket: &mut dyn Read) -> Result<Option<String>, Error> {
|
||||
loop {
|
||||
let mut bytes = vec![0; 256];
|
||||
let count = socket.read(&mut bytes).map_err(Error::Socket)?;
|
||||
// If the return value is 0, the peer has performed an orderly shutdown.
|
||||
if count == 0 {
|
||||
break;
|
||||
}
|
||||
res.push_str(std::str::from_utf8(&bytes[0..count]).unwrap());
|
||||
|
||||
// End of headers
|
||||
@@ -127,7 +131,7 @@ fn parse_http_response(socket: &mut dyn Read) -> Result<Option<String>, Error> {
|
||||
}
|
||||
}
|
||||
}
|
||||
let body_string = content_length.and(Some(String::from(&res[body_offset.unwrap()..])));
|
||||
let body_string = content_length.and(body_offset.map(|o| String::from(&res[o..])));
|
||||
let status_code = get_status_code(&res)?;
|
||||
|
||||
if status_code.is_server_error() {
|
||||
|
||||
@@ -9,21 +9,21 @@ default = []
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { path = "../acpi_tables" }
|
||||
anyhow = "1.0.58"
|
||||
anyhow = "1.0.62"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.126"
|
||||
libc = "0.2.131"
|
||||
linux-loader = { version = "0.4.0", features = ["elf", "bzimage", "pe"] }
|
||||
log = "0.4.17"
|
||||
serde = { version = "1.0.138", features = ["rc", "derive"] }
|
||||
thiserror = "1.0.31"
|
||||
serde = { version = "1.0.143", features = ["rc", "derive"] }
|
||||
thiserror = "1.0.32"
|
||||
uuid = "1.1.2"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-bitmap"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vmm-sys-util = { version = "0.9.0", features = ["with-serde"] }
|
||||
vmm-sys-util = { version = "0.10.0", features = ["with-serde"] }
|
||||
|
||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||
fdt_parser = { version = "0.1.3", package = 'fdt'}
|
||||
fdt_parser = { version = "0.1.4", package = "fdt" }
|
||||
vm-fdt = { git = "https://github.com/rust-vmm/vm-fdt", branch = "main" }
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
pub mod fdt;
|
||||
/// Layout for this aarch64 system.
|
||||
pub mod layout;
|
||||
/// Module for system registers definition
|
||||
pub mod regs;
|
||||
/// Module for loading UEFI binary.
|
||||
pub mod uefi;
|
||||
|
||||
@@ -73,7 +75,9 @@ pub fn configure_vcpu(
|
||||
.map_err(Error::RegsConfiguration)?;
|
||||
}
|
||||
|
||||
let mpidr = vcpu.read_mpidr().map_err(Error::VcpuRegMpidr)?;
|
||||
let mpidr = vcpu
|
||||
.get_sys_reg(regs::MPIDR_EL1)
|
||||
.map_err(Error::VcpuRegMpidr)?;
|
||||
Ok(mpidr)
|
||||
}
|
||||
|
||||
|
||||
41
arch/src/aarch64/regs.rs
Normal file
41
arch/src/aarch64/regs.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
///
|
||||
/// AArch64 system register encoding:
|
||||
/// See https://developer.arm.com/documentation/ddi0487 (chapter D12)
|
||||
///
|
||||
/// 31 22 21 20 19 18 16 15 12 11 8 7 5 4 0
|
||||
/// +----------+---+-----+-----+-----+-----+-----+----+
|
||||
/// |1101010100| L | op0 | op1 | CRn | CRm | op2 | Rt |
|
||||
/// +----------+---+-----+-----+-----+-----+-----+----+
|
||||
///
|
||||
/// Notes:
|
||||
/// - L and Rt are reserved as implementation defined fields, ignored.
|
||||
///
|
||||
const SYSREG_HEAD: u32 = 0b1101010100u32 << 22;
|
||||
const SYSREG_OP0_SHIFT: u32 = 19;
|
||||
const SYSREG_OP0_MASK: u32 = 0b11u32 << 19;
|
||||
const SYSREG_OP1_SHIFT: u32 = 16;
|
||||
const SYSREG_OP1_MASK: u32 = 0b111u32 << 16;
|
||||
const SYSREG_CRN_SHIFT: u32 = 12;
|
||||
const SYSREG_CRN_MASK: u32 = 0b1111u32 << 12;
|
||||
const SYSREG_CRM_SHIFT: u32 = 8;
|
||||
const SYSREG_CRM_MASK: u32 = 0b1111u32 << 8;
|
||||
const SYSREG_OP2_SHIFT: u32 = 5;
|
||||
const SYSREG_OP2_MASK: u32 = 0b111u32 << 5;
|
||||
|
||||
/// Define the ID of system registers
|
||||
#[macro_export]
|
||||
macro_rules! arm64_sys_reg {
|
||||
($name: tt, $op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => {
|
||||
pub const $name: u32 = SYSREG_HEAD
|
||||
| ((($op0 as u32) << SYSREG_OP0_SHIFT) & SYSREG_OP0_MASK as u32)
|
||||
| ((($op1 as u32) << SYSREG_OP1_SHIFT) & SYSREG_OP1_MASK as u32)
|
||||
| ((($crn as u32) << SYSREG_CRN_SHIFT) & SYSREG_CRN_MASK as u32)
|
||||
| ((($crm as u32) << SYSREG_CRM_SHIFT) & SYSREG_CRM_MASK as u32)
|
||||
| ((($op2 as u32) << SYSREG_OP2_SHIFT) & SYSREG_OP2_MASK as u32);
|
||||
};
|
||||
}
|
||||
|
||||
arm64_sys_reg!(MPIDR_EL1, 3, 0, 0, 0, 5);
|
||||
@@ -5,10 +5,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE-BSD-3-Clause file.
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use hypervisor::x86_64::LapicState;
|
||||
use std::io::Cursor;
|
||||
use std::mem;
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -20,32 +16,6 @@ pub const APIC_LVT1: usize = 0x360;
|
||||
pub const APIC_MODE_NMI: u32 = 0x4;
|
||||
pub const APIC_MODE_EXTINT: u32 = 0x7;
|
||||
|
||||
pub fn get_klapic_reg(klapic: &LapicState, reg_offset: usize) -> u32 {
|
||||
let sliceu8 = unsafe {
|
||||
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
|
||||
// Cursors are only readable on arrays of u8, not i8(c_char).
|
||||
mem::transmute::<&[i8], &[u8]>(&klapic.regs[reg_offset..])
|
||||
};
|
||||
let mut reader = Cursor::new(sliceu8);
|
||||
// Following call can't fail if the offsets defined above are correct.
|
||||
reader
|
||||
.read_u32::<LittleEndian>()
|
||||
.expect("Failed to read klapic register")
|
||||
}
|
||||
|
||||
pub fn set_klapic_reg(klapic: &mut LapicState, reg_offset: usize, value: u32) {
|
||||
let sliceu8 = unsafe {
|
||||
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
|
||||
// Cursors are only readable on arrays of u8, not i8(c_char).
|
||||
mem::transmute::<&mut [i8], &mut [u8]>(&mut klapic.regs[reg_offset..])
|
||||
};
|
||||
let mut writer = Cursor::new(sliceu8);
|
||||
// Following call can't fail if the offsets defined above are correct.
|
||||
writer
|
||||
.write_u32::<LittleEndian>(value)
|
||||
.expect("Failed to write klapic register")
|
||||
}
|
||||
|
||||
pub fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 {
|
||||
((reg) & !0x700) | ((mode) << 8)
|
||||
}
|
||||
@@ -57,42 +27,13 @@ pub fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 {
|
||||
pub fn set_lint(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
|
||||
let mut klapic = vcpu.get_lapic()?;
|
||||
|
||||
let lvt_lint0 = get_klapic_reg(&klapic, APIC_LVT0);
|
||||
set_klapic_reg(
|
||||
&mut klapic,
|
||||
let lvt_lint0 = klapic.get_klapic_reg(APIC_LVT0);
|
||||
klapic.set_klapic_reg(
|
||||
APIC_LVT0,
|
||||
set_apic_delivery_mode(lvt_lint0, APIC_MODE_EXTINT),
|
||||
);
|
||||
let lvt_lint1 = get_klapic_reg(&klapic, APIC_LVT1);
|
||||
set_klapic_reg(
|
||||
&mut klapic,
|
||||
APIC_LVT1,
|
||||
set_apic_delivery_mode(lvt_lint1, APIC_MODE_NMI),
|
||||
);
|
||||
let lvt_lint1 = klapic.get_klapic_reg(APIC_LVT1);
|
||||
klapic.set_klapic_reg(APIC_LVT1, set_apic_delivery_mode(lvt_lint1, APIC_MODE_NMI));
|
||||
|
||||
vcpu.set_lapic(&klapic)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const KVM_APIC_REG_SIZE: usize = 0x400;
|
||||
|
||||
#[test]
|
||||
fn test_set_and_get_klapic_reg() {
|
||||
let reg_offset = 0x340;
|
||||
let mut klapic = LapicState::default();
|
||||
set_klapic_reg(&mut klapic, reg_offset, 3);
|
||||
let value = get_klapic_reg(&klapic, reg_offset);
|
||||
assert_eq!(value, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_set_and_get_klapic_out_of_bounds() {
|
||||
let reg_offset = KVM_APIC_REG_SIZE + 10;
|
||||
let mut klapic = LapicState::default();
|
||||
set_klapic_reg(&mut klapic, reg_offset, 3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ pub mod regs;
|
||||
use crate::GuestMemoryMmap;
|
||||
use crate::InitramfsConfig;
|
||||
use crate::RegionType;
|
||||
use hypervisor::x86_64::{CpuId, CpuIdEntry, CPUID_FLAG_VALID_INDEX};
|
||||
use hypervisor::arch::x86::{CpuIdEntry, CPUID_FLAG_VALID_INDEX};
|
||||
use hypervisor::HypervisorError;
|
||||
use linux_loader::loader::bootparam::boot_params;
|
||||
use linux_loader::loader::elf::start_info::{
|
||||
@@ -202,7 +202,7 @@ impl From<Error> for super::Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code, clippy::upper_case_acronyms)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum CpuidReg {
|
||||
EAX,
|
||||
@@ -223,16 +223,14 @@ pub struct CpuidPatch {
|
||||
|
||||
impl CpuidPatch {
|
||||
pub fn set_cpuid_reg(
|
||||
cpuid: &mut CpuId,
|
||||
cpuid: &mut Vec<CpuIdEntry>,
|
||||
function: u32,
|
||||
index: Option<u32>,
|
||||
reg: CpuidReg,
|
||||
value: u32,
|
||||
) {
|
||||
let entries = cpuid.as_mut_slice();
|
||||
|
||||
let mut entry_found = false;
|
||||
for entry in entries.iter_mut() {
|
||||
for entry in cpuid.iter_mut() {
|
||||
if entry.function == function && (index == None || index.unwrap() == entry.index) {
|
||||
entry_found = true;
|
||||
match reg {
|
||||
@@ -279,16 +277,12 @@ impl CpuidPatch {
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = cpuid.push(entry) {
|
||||
error!("Failed adding new CPUID entry: {:?}", e);
|
||||
}
|
||||
cpuid.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn patch_cpuid(cpuid: &mut CpuId, patches: Vec<CpuidPatch>) {
|
||||
let entries = cpuid.as_mut_slice();
|
||||
|
||||
for entry in entries.iter_mut() {
|
||||
pub fn patch_cpuid(cpuid: &mut [CpuIdEntry], patches: Vec<CpuidPatch>) {
|
||||
for entry in cpuid {
|
||||
for patch in patches.iter() {
|
||||
if entry.function == patch.function && entry.index == patch.index {
|
||||
if let Some(flags_bit) = patch.flags_bit {
|
||||
@@ -312,16 +306,15 @@ impl CpuidPatch {
|
||||
}
|
||||
|
||||
pub fn is_feature_enabled(
|
||||
cpuid: &CpuId,
|
||||
cpuid: &[CpuIdEntry],
|
||||
function: u32,
|
||||
index: u32,
|
||||
reg: CpuidReg,
|
||||
feature_bit: usize,
|
||||
) -> bool {
|
||||
let entries = cpuid.as_slice();
|
||||
let mask = 1 << feature_bit;
|
||||
|
||||
for entry in entries.iter() {
|
||||
for entry in cpuid {
|
||||
if entry.function == function && entry.index == index {
|
||||
let reg_val = match reg {
|
||||
CpuidReg::EAX => entry.eax,
|
||||
@@ -470,12 +463,12 @@ impl CpuidFeatureEntry {
|
||||
}
|
||||
|
||||
fn get_features_from_cpuid(
|
||||
cpuid: &CpuId,
|
||||
cpuid: &[CpuIdEntry],
|
||||
feature_entry_list: &[CpuidFeatureEntry],
|
||||
) -> Vec<u32> {
|
||||
let mut features = vec![0; feature_entry_list.len()];
|
||||
for (i, feature_entry) in feature_entry_list.iter().enumerate() {
|
||||
for cpuid_entry in cpuid.as_slice().iter() {
|
||||
for cpuid_entry in cpuid {
|
||||
if cpuid_entry.function == feature_entry.function
|
||||
&& cpuid_entry.index == feature_entry.index
|
||||
{
|
||||
@@ -505,8 +498,8 @@ impl CpuidFeatureEntry {
|
||||
// The function returns `Error` (a.k.a. "incompatible"), when the CPUID features from `src_vm_cpuid`
|
||||
// is not a subset of those of the `dest_vm_cpuid`.
|
||||
pub fn check_cpuid_compatibility(
|
||||
src_vm_cpuid: &CpuId,
|
||||
dest_vm_cpuid: &CpuId,
|
||||
src_vm_cpuid: &[CpuIdEntry],
|
||||
dest_vm_cpuid: &[CpuIdEntry],
|
||||
) -> Result<(), Error> {
|
||||
let feature_entry_list = &Self::checked_feature_entry_list();
|
||||
let src_vm_features = Self::get_features_from_cpuid(src_vm_cpuid, feature_entry_list);
|
||||
@@ -558,7 +551,7 @@ pub fn generate_common_cpuid(
|
||||
phys_bits: u8,
|
||||
kvm_hyperv: bool,
|
||||
#[cfg(feature = "tdx")] tdx_enabled: bool,
|
||||
) -> super::Result<CpuId> {
|
||||
) -> super::Result<Vec<CpuIdEntry>> {
|
||||
let cpuid_patches = vec![
|
||||
// Patch tsc deadline timer bit
|
||||
CpuidPatch {
|
||||
@@ -669,16 +662,14 @@ pub fn generate_common_cpuid(
|
||||
for i in 0x8000_0002..=0x8000_0004 {
|
||||
cpuid.retain(|c| c.function != i);
|
||||
let leaf = unsafe { std::arch::x86_64::__cpuid(i) };
|
||||
cpuid
|
||||
.push(CpuIdEntry {
|
||||
function: i,
|
||||
eax: leaf.eax,
|
||||
ebx: leaf.ebx,
|
||||
ecx: leaf.ecx,
|
||||
edx: leaf.edx,
|
||||
..Default::default()
|
||||
})
|
||||
.map_err(Error::CpuidIdentification)?;
|
||||
cpuid.push(CpuIdEntry {
|
||||
function: i,
|
||||
eax: leaf.eax,
|
||||
ebx: leaf.ebx,
|
||||
ecx: leaf.ecx,
|
||||
edx: leaf.edx,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
if kvm_hyperv {
|
||||
@@ -687,56 +678,44 @@ pub fn generate_common_cpuid(
|
||||
cpuid.retain(|c| c.function != 0x4000_0001);
|
||||
// See "Hypervisor Top Level Functional Specification" for details
|
||||
// Compliance with "Hv#1" requires leaves up to 0x4000_000a
|
||||
cpuid
|
||||
.push(CpuIdEntry {
|
||||
function: 0x40000000,
|
||||
eax: 0x4000000a, // Maximum cpuid leaf
|
||||
ebx: 0x756e694c, // "Linu"
|
||||
ecx: 0x564b2078, // "x KV"
|
||||
edx: 0x7648204d, // "M Hv"
|
||||
..Default::default()
|
||||
})
|
||||
.map_err(Error::CpuidKvmHyperV)?;
|
||||
cpuid
|
||||
.push(CpuIdEntry {
|
||||
function: 0x40000001,
|
||||
eax: 0x31237648, // "Hv#1"
|
||||
..Default::default()
|
||||
})
|
||||
.map_err(Error::CpuidKvmHyperV)?;
|
||||
cpuid
|
||||
.push(CpuIdEntry {
|
||||
function: 0x40000002,
|
||||
eax: 0x3839, // "Build number"
|
||||
ebx: 0xa0000, // "Version"
|
||||
..Default::default()
|
||||
})
|
||||
.map_err(Error::CpuidKvmHyperV)?;
|
||||
cpuid
|
||||
.push(CpuIdEntry {
|
||||
function: 0x4000_0003,
|
||||
eax: 1 << 1 // AccessPartitionReferenceCounter
|
||||
cpuid.push(CpuIdEntry {
|
||||
function: 0x40000000,
|
||||
eax: 0x4000000a, // Maximum cpuid leaf
|
||||
ebx: 0x756e694c, // "Linu"
|
||||
ecx: 0x564b2078, // "x KV"
|
||||
edx: 0x7648204d, // "M Hv"
|
||||
..Default::default()
|
||||
});
|
||||
cpuid.push(CpuIdEntry {
|
||||
function: 0x40000001,
|
||||
eax: 0x31237648, // "Hv#1"
|
||||
..Default::default()
|
||||
});
|
||||
cpuid.push(CpuIdEntry {
|
||||
function: 0x40000002,
|
||||
eax: 0x3839, // "Build number"
|
||||
ebx: 0xa0000, // "Version"
|
||||
..Default::default()
|
||||
});
|
||||
cpuid.push(CpuIdEntry {
|
||||
function: 0x4000_0003,
|
||||
eax: 1 << 1 // AccessPartitionReferenceCounter
|
||||
| 1 << 2 // AccessSynicRegs
|
||||
| 1 << 3 // AccessSyntheticTimerRegs
|
||||
| 1 << 9, // AccessPartitionReferenceTsc
|
||||
edx: 1 << 3, // CPU dynamic partitioning
|
||||
..Default::default()
|
||||
})
|
||||
.map_err(Error::CpuidKvmHyperV)?;
|
||||
cpuid
|
||||
.push(CpuIdEntry {
|
||||
function: 0x4000_0004,
|
||||
eax: 1 << 5, // Recommend relaxed timing
|
||||
..Default::default()
|
||||
})
|
||||
.map_err(Error::CpuidKvmHyperV)?;
|
||||
edx: 1 << 3, // CPU dynamic partitioning
|
||||
..Default::default()
|
||||
});
|
||||
cpuid.push(CpuIdEntry {
|
||||
function: 0x4000_0004,
|
||||
eax: 1 << 5, // Recommend relaxed timing
|
||||
..Default::default()
|
||||
});
|
||||
for i in 0x4000_0005..=0x4000_000a {
|
||||
cpuid
|
||||
.push(CpuIdEntry {
|
||||
function: i,
|
||||
..Default::default()
|
||||
})
|
||||
.map_err(Error::CpuidKvmHyperV)?;
|
||||
cpuid.push(CpuIdEntry {
|
||||
function: i,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -748,7 +727,7 @@ pub fn configure_vcpu(
|
||||
id: u8,
|
||||
kernel_entry_point: Option<EntryPoint>,
|
||||
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
cpuid: CpuId,
|
||||
cpuid: Vec<CpuIdEntry>,
|
||||
kvm_hyperv: bool,
|
||||
) -> super::Result<()> {
|
||||
// Per vCPU CPUID changes; common are handled via generate_common_cpuid()
|
||||
@@ -840,13 +819,16 @@ pub fn configure_system(
|
||||
rsdp_addr: Option<GuestAddress>,
|
||||
sgx_epc_region: Option<SgxEpcRegion>,
|
||||
serial_number: Option<&str>,
|
||||
uuid: Option<&str>,
|
||||
oem_strings: Option<&[&str]>,
|
||||
) -> super::Result<()> {
|
||||
// Write EBDA address to location where ACPICA expects to find it
|
||||
guest_mem
|
||||
.write_obj((layout::EBDA_START.0 >> 4) as u16, layout::EBDA_POINTER)
|
||||
.map_err(Error::EbdaSetup)?;
|
||||
|
||||
let size = smbios::setup_smbios(guest_mem, serial_number).map_err(Error::SmbiosSetup)?;
|
||||
let size = smbios::setup_smbios(guest_mem, serial_number, uuid, oem_strings)
|
||||
.map_err(Error::SmbiosSetup)?;
|
||||
|
||||
// Place the MP table after the SMIOS table aligned to 16 bytes
|
||||
let offset = GuestAddress(layout::SMBIOS_START).unchecked_add(size);
|
||||
@@ -1056,7 +1038,7 @@ pub fn get_host_cpu_phys_bits() -> u8 {
|
||||
}
|
||||
|
||||
fn update_cpuid_topology(
|
||||
cpuid: &mut CpuId,
|
||||
cpuid: &mut Vec<CpuIdEntry>,
|
||||
threads_per_core: u8,
|
||||
cores_per_die: u8,
|
||||
dies_per_package: u8,
|
||||
@@ -1120,7 +1102,10 @@ fn update_cpuid_topology(
|
||||
|
||||
// The goal is to update the CPUID sub-leaves to reflect the number of EPC
|
||||
// sections exposed to the guest.
|
||||
fn update_cpuid_sgx(cpuid: &mut CpuId, epc_sections: Vec<SgxEpcSection>) -> Result<(), Error> {
|
||||
fn update_cpuid_sgx(
|
||||
cpuid: &mut Vec<CpuIdEntry>,
|
||||
epc_sections: Vec<SgxEpcSection>,
|
||||
) -> Result<(), Error> {
|
||||
// Something's wrong if there's no EPC section.
|
||||
if epc_sections.is_empty() {
|
||||
return Err(Error::NoSgxEpcSection);
|
||||
@@ -1196,6 +1181,8 @@ mod tests {
|
||||
Some(layout::RSDP_POINTER),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
assert!(config_err.is_err());
|
||||
|
||||
@@ -1209,7 +1196,18 @@ mod tests {
|
||||
.collect();
|
||||
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
||||
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None, None).unwrap();
|
||||
configure_system(
|
||||
&gm,
|
||||
GuestAddress(0),
|
||||
&None,
|
||||
no_vcpus,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Now assigning some memory that is equal to the start of the 32bit memory hole.
|
||||
let mem_size = 3328 << 20;
|
||||
@@ -1220,9 +1218,31 @@ mod tests {
|
||||
.map(|r| (r.0, r.1))
|
||||
.collect();
|
||||
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None, None).unwrap();
|
||||
configure_system(
|
||||
&gm,
|
||||
GuestAddress(0),
|
||||
&None,
|
||||
no_vcpus,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None, None).unwrap();
|
||||
configure_system(
|
||||
&gm,
|
||||
GuestAddress(0),
|
||||
&None,
|
||||
no_vcpus,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Now assigning some memory that falls after the 32bit memory hole.
|
||||
let mem_size = 3330 << 20;
|
||||
@@ -1233,9 +1253,31 @@ mod tests {
|
||||
.map(|r| (r.0, r.1))
|
||||
.collect();
|
||||
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None, None).unwrap();
|
||||
configure_system(
|
||||
&gm,
|
||||
GuestAddress(0),
|
||||
&None,
|
||||
no_vcpus,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None, None).unwrap();
|
||||
configure_system(
|
||||
&gm,
|
||||
GuestAddress(0),
|
||||
&None,
|
||||
no_vcpus,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::layout::{BOOT_GDT_START, BOOT_IDT_START, PVH_INFO_START};
|
||||
use crate::GuestMemoryMmap;
|
||||
use hypervisor::arch::x86::gdt::{gdt_entry, segment_from_gdt};
|
||||
use hypervisor::arch::x86::regs::CR0_PE;
|
||||
use hypervisor::x86_64::{FpuState, SpecialRegisters, StandardRegisters};
|
||||
use hypervisor::arch::x86::{FpuState, SpecialRegisters, StandardRegisters};
|
||||
use std::sync::Arc;
|
||||
use std::{mem, result};
|
||||
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError};
|
||||
@@ -66,7 +66,7 @@ pub fn setup_fpu(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
|
||||
///
|
||||
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
||||
pub fn setup_msrs(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
|
||||
vcpu.set_msrs(&hypervisor::x86_64::boot_msr_entries())
|
||||
vcpu.set_msrs(&vcpu.boot_msr_entries())
|
||||
.map_err(Error::SetModelSpecificRegisters)?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -12,10 +12,10 @@ use std::fmt::{self, Display};
|
||||
use std::mem;
|
||||
use std::result;
|
||||
use std::slice;
|
||||
use uuid::Uuid;
|
||||
use vm_memory::ByteValued;
|
||||
use vm_memory::{Address, Bytes, GuestAddress};
|
||||
|
||||
#[allow(unused_variables)]
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// There was too little guest memory to store the entire SMBIOS table.
|
||||
@@ -28,6 +28,8 @@ pub enum Error {
|
||||
WriteSmbiosEp,
|
||||
/// Failure to write additional data to memory
|
||||
WriteData,
|
||||
/// Failure to parse uuid, uuid format may be error
|
||||
ParseUuid(uuid::Error),
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
@@ -37,11 +39,16 @@ impl Display for Error {
|
||||
use self::Error::*;
|
||||
|
||||
let description = match self {
|
||||
NotEnoughMemory => "There was too little guest memory to store the SMBIOS table",
|
||||
AddressOverflow => "The SMBIOS table has too little address space to be stored",
|
||||
Clear => "Failure while zeroing out the memory for the SMBIOS table",
|
||||
WriteSmbiosEp => "Failure to write SMBIOS entrypoint structure",
|
||||
WriteData => "Failure to write additional data to memory",
|
||||
NotEnoughMemory => {
|
||||
"There was too little guest memory to store the SMBIOS table".to_string()
|
||||
}
|
||||
AddressOverflow => {
|
||||
"The SMBIOS table has too little address space to be stored".to_string()
|
||||
}
|
||||
Clear => "Failure while zeroing out the memory for the SMBIOS table".to_string(),
|
||||
WriteSmbiosEp => "Failure to write SMBIOS entrypoint structure".to_string(),
|
||||
WriteData => "Failure to write additional data to memory".to_string(),
|
||||
ParseUuid(e) => format!("Failure to parse uuid: {}", e),
|
||||
};
|
||||
|
||||
write!(f, "SMBIOS error: {}", description)
|
||||
@@ -54,6 +61,7 @@ pub type Result<T> = result::Result<T, Error>;
|
||||
const SM3_MAGIC_IDENT: &[u8; 5usize] = b"_SM3_";
|
||||
const BIOS_INFORMATION: u8 = 0;
|
||||
const SYSTEM_INFORMATION: u8 = 1;
|
||||
const OEM_STRINGS: u8 = 11;
|
||||
const END_OF_TABLE: u8 = 127;
|
||||
const PCI_SUPPORTED: u64 = 1 << 7;
|
||||
const IS_VIRTUAL_MACHINE: u8 = 1 << 4;
|
||||
@@ -68,75 +76,81 @@ fn compute_checksum<T: Copy>(v: &T) -> u8 {
|
||||
(!checksum).wrapping_add(1)
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(packed)]
|
||||
#[derive(Default, Copy)]
|
||||
pub struct Smbios30Entrypoint {
|
||||
pub signature: [u8; 5usize],
|
||||
pub checksum: u8,
|
||||
pub length: u8,
|
||||
pub majorver: u8,
|
||||
pub minorver: u8,
|
||||
pub docrev: u8,
|
||||
pub revision: u8,
|
||||
pub reserved: u8,
|
||||
pub max_size: u32,
|
||||
pub physptr: u64,
|
||||
}
|
||||
|
||||
impl Clone for Smbios30Entrypoint {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
#[derive(Default, Copy, Clone)]
|
||||
struct Smbios30Entrypoint {
|
||||
signature: [u8; 5usize],
|
||||
checksum: u8,
|
||||
length: u8,
|
||||
majorver: u8,
|
||||
minorver: u8,
|
||||
docrev: u8,
|
||||
revision: u8,
|
||||
reserved: u8,
|
||||
max_size: u32,
|
||||
physptr: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(packed)]
|
||||
#[derive(Default, Copy)]
|
||||
pub struct SmbiosBiosInfo {
|
||||
pub typ: u8,
|
||||
pub length: u8,
|
||||
pub handle: u16,
|
||||
pub vendor: u8,
|
||||
pub version: u8,
|
||||
pub start_addr: u16,
|
||||
pub release_date: u8,
|
||||
pub rom_size: u8,
|
||||
pub characteristics: u64,
|
||||
pub characteristics_ext1: u8,
|
||||
pub characteristics_ext2: u8,
|
||||
}
|
||||
|
||||
impl Clone for SmbiosBiosInfo {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
#[derive(Default, Copy, Clone)]
|
||||
struct SmbiosBiosInfo {
|
||||
r#type: u8,
|
||||
length: u8,
|
||||
handle: u16,
|
||||
vendor: u8,
|
||||
version: u8,
|
||||
start_addr: u16,
|
||||
release_date: u8,
|
||||
rom_size: u8,
|
||||
characteristics: u64,
|
||||
characteristics_ext1: u8,
|
||||
characteristics_ext2: u8,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(packed)]
|
||||
#[derive(Default, Copy)]
|
||||
pub struct SmbiosSysInfo {
|
||||
pub typ: u8,
|
||||
pub length: u8,
|
||||
pub handle: u16,
|
||||
pub manufacturer: u8,
|
||||
pub product_name: u8,
|
||||
pub version: u8,
|
||||
pub serial_number: u8,
|
||||
pub uuid: [u8; 16usize],
|
||||
pub wake_up_type: u8,
|
||||
pub sku: u8,
|
||||
pub family: u8,
|
||||
#[derive(Default, Copy, Clone)]
|
||||
struct SmbiosSysInfo {
|
||||
r#type: u8,
|
||||
length: u8,
|
||||
handle: u16,
|
||||
manufacturer: u8,
|
||||
product_name: u8,
|
||||
version: u8,
|
||||
serial_number: u8,
|
||||
uuid: [u8; 16usize],
|
||||
wake_up_type: u8,
|
||||
sku: u8,
|
||||
family: u8,
|
||||
}
|
||||
|
||||
impl Clone for SmbiosSysInfo {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
#[repr(C)]
|
||||
#[repr(packed)]
|
||||
#[derive(Default, Copy, Clone)]
|
||||
struct SmbiosOemStrings {
|
||||
r#type: u8,
|
||||
length: u8,
|
||||
handle: u16,
|
||||
count: u8,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(packed)]
|
||||
#[derive(Default, Copy, Clone)]
|
||||
struct SmbiosEndOfTable {
|
||||
r#type: u8,
|
||||
length: u8,
|
||||
handle: u16,
|
||||
}
|
||||
|
||||
// SAFETY: These data structures only contain a series of integers
|
||||
unsafe impl ByteValued for Smbios30Entrypoint {}
|
||||
unsafe impl ByteValued for SmbiosBiosInfo {}
|
||||
unsafe impl ByteValued for SmbiosSysInfo {}
|
||||
unsafe impl ByteValued for SmbiosOemStrings {}
|
||||
unsafe impl ByteValued for SmbiosEndOfTable {}
|
||||
|
||||
fn write_and_incr<T: ByteValued>(
|
||||
mem: &GuestMemoryMmap,
|
||||
@@ -162,7 +176,12 @@ fn write_string(
|
||||
Ok(curptr)
|
||||
}
|
||||
|
||||
pub fn setup_smbios(mem: &GuestMemoryMmap, serial_number: Option<&str>) -> Result<u64> {
|
||||
pub fn setup_smbios(
|
||||
mem: &GuestMemoryMmap,
|
||||
serial_number: Option<&str>,
|
||||
uuid: Option<&str>,
|
||||
oem_strings: Option<&[&str]>,
|
||||
) -> Result<u64> {
|
||||
let physptr = GuestAddress(SMBIOS_START)
|
||||
.checked_add(mem::size_of::<Smbios30Entrypoint>() as u64)
|
||||
.ok_or(Error::NotEnoughMemory)?;
|
||||
@@ -172,7 +191,7 @@ pub fn setup_smbios(mem: &GuestMemoryMmap, serial_number: Option<&str>) -> Resul
|
||||
{
|
||||
handle += 1;
|
||||
let smbios_biosinfo = SmbiosBiosInfo {
|
||||
typ: BIOS_INFORMATION,
|
||||
r#type: BIOS_INFORMATION,
|
||||
length: mem::size_of::<SmbiosBiosInfo>() as u8,
|
||||
handle,
|
||||
vendor: 1, // First string written in this section
|
||||
@@ -189,13 +208,20 @@ pub fn setup_smbios(mem: &GuestMemoryMmap, serial_number: Option<&str>) -> Resul
|
||||
|
||||
{
|
||||
handle += 1;
|
||||
|
||||
let uuid_number = uuid
|
||||
.map(Uuid::parse_str)
|
||||
.transpose()
|
||||
.map_err(Error::ParseUuid)?
|
||||
.unwrap_or(Uuid::nil());
|
||||
let smbios_sysinfo = SmbiosSysInfo {
|
||||
typ: SYSTEM_INFORMATION,
|
||||
r#type: SYSTEM_INFORMATION,
|
||||
length: mem::size_of::<SmbiosSysInfo>() as u8,
|
||||
handle,
|
||||
manufacturer: 1, // First string written in this section
|
||||
product_name: 2, // Second string written in this section
|
||||
serial_number: serial_number.map(|_| 3).unwrap_or_default(), // 3rd string
|
||||
uuid: uuid_number.to_bytes_le(), // set uuid
|
||||
..Default::default()
|
||||
};
|
||||
curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
|
||||
@@ -207,15 +233,34 @@ pub fn setup_smbios(mem: &GuestMemoryMmap, serial_number: Option<&str>) -> Resul
|
||||
curptr = write_and_incr(mem, 0u8, curptr)?;
|
||||
}
|
||||
|
||||
if let Some(oem_strings) = oem_strings {
|
||||
handle += 1;
|
||||
|
||||
let smbios_oemstrings = SmbiosOemStrings {
|
||||
r#type: OEM_STRINGS,
|
||||
length: mem::size_of::<SmbiosOemStrings>() as u8,
|
||||
handle,
|
||||
count: oem_strings.len() as u8,
|
||||
};
|
||||
|
||||
curptr = write_and_incr(mem, smbios_oemstrings, curptr)?;
|
||||
|
||||
for s in oem_strings {
|
||||
curptr = write_string(mem, s, curptr)?;
|
||||
}
|
||||
|
||||
curptr = write_and_incr(mem, 0u8, curptr)?;
|
||||
}
|
||||
|
||||
{
|
||||
handle += 1;
|
||||
let smbios_sysinfo = SmbiosSysInfo {
|
||||
typ: END_OF_TABLE,
|
||||
length: mem::size_of::<SmbiosSysInfo>() as u8,
|
||||
let smbios_end = SmbiosEndOfTable {
|
||||
r#type: END_OF_TABLE,
|
||||
length: mem::size_of::<SmbiosEndOfTable>() as u8,
|
||||
handle,
|
||||
..Default::default()
|
||||
};
|
||||
curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
|
||||
curptr = write_and_incr(mem, smbios_end, curptr)?;
|
||||
curptr = write_and_incr(mem, 0u8, curptr)?;
|
||||
curptr = write_and_incr(mem, 0u8, curptr)?;
|
||||
}
|
||||
|
||||
@@ -237,7 +282,7 @@ pub fn setup_smbios(mem: &GuestMemoryMmap, serial_number: Option<&str>) -> Resul
|
||||
.map_err(|_| Error::WriteSmbiosEp)?;
|
||||
}
|
||||
|
||||
Ok(curptr.unchecked_offset_from(physptr))
|
||||
Ok(curptr.unchecked_offset_from(physptr) + std::mem::size_of::<Smbios30Entrypoint>() as u64)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -267,7 +312,7 @@ mod tests {
|
||||
fn entrypoint_checksum() {
|
||||
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap();
|
||||
|
||||
setup_smbios(&mem, None).unwrap();
|
||||
setup_smbios(&mem, None, None, None).unwrap();
|
||||
|
||||
let smbios_ep: Smbios30Entrypoint = mem.read_obj(GuestAddress(SMBIOS_START)).unwrap();
|
||||
|
||||
|
||||
@@ -8,17 +8,17 @@ edition = "2021"
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
io-uring = "0.5.2"
|
||||
libc = "0.2.126"
|
||||
io-uring = "0.5.3"
|
||||
libc = "0.2.131"
|
||||
log = "0.4.17"
|
||||
qcow = { path = "../qcow" }
|
||||
thiserror = "1.0.31"
|
||||
thiserror = "1.0.32"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
vhdx = { path = "../vhdx" }
|
||||
virtio-bindings = { version = "0.1.0", features = ["virtio-v5_0_0"] }
|
||||
virtio-queue = "0.4.0"
|
||||
virtio-queue = "0.5.0"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
vmm-sys-util = "0.10.0"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::fs::File;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use thiserror::Error;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
use vmm_sys_util::{ioctl_expr, ioctl_io_nr, ioctl_ioc_nr};
|
||||
use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum DiskFileError {
|
||||
|
||||
@@ -32,6 +32,7 @@ use std::path::Path;
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
use std::sync::MutexGuard;
|
||||
use thiserror::Error;
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use virtio_bindings::bindings::virtio_blk::*;
|
||||
@@ -48,25 +49,25 @@ type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
const SECTOR_SHIFT: u8 = 9;
|
||||
pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
/// Guest gave us bad memory addresses.
|
||||
#[error("Guest gave us bad memory addresses.")]
|
||||
GuestMemory(GuestMemoryError),
|
||||
/// Guest gave us offsets that would have overflowed a usize.
|
||||
#[error("Guest gave us offsets that would have overflowed a usize.")]
|
||||
CheckedOffset(GuestAddress, usize),
|
||||
/// Guest gave us a write only descriptor that protocol says to read from.
|
||||
#[error("Guest gave us a write only descriptor that protocol says to read from.")]
|
||||
UnexpectedWriteOnlyDescriptor,
|
||||
/// Guest gave us a read only descriptor that protocol says to write to.
|
||||
#[error("Guest gave us a read only descriptor that protocol says to write to.")]
|
||||
UnexpectedReadOnlyDescriptor,
|
||||
/// Guest gave us too few descriptors in a descriptor chain.
|
||||
#[error("Guest gave us too few descriptors in a descriptor chain.")]
|
||||
DescriptorChainTooShort,
|
||||
/// Guest gave us a descriptor that was too short to use.
|
||||
#[error("Guest gave us a descriptor that was too short to use.")]
|
||||
DescriptorLengthTooSmall,
|
||||
/// Getting a block's metadata fails for any reason.
|
||||
#[error("Getting a block's metadata fails for any reason.")]
|
||||
GetFileMetadata,
|
||||
/// The requested operation would cause a seek beyond disk end.
|
||||
#[error("The requested operation would cause a seek beyond disk end.")]
|
||||
InvalidOffset,
|
||||
/// The requested operation does not support multiple descriptors.
|
||||
#[error("The requested operation does not support multiple descriptors.")]
|
||||
TooManyDescriptors,
|
||||
}
|
||||
|
||||
@@ -102,20 +103,31 @@ pub fn build_disk_image_id(disk_path: &Path) -> Vec<u8> {
|
||||
default_disk_image_id
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ExecuteError {
|
||||
#[error("Bad request: {0}")]
|
||||
BadRequest(Error),
|
||||
#[error("Falied to flush: {0}")]
|
||||
Flush(io::Error),
|
||||
#[error("Failed to read: {0}")]
|
||||
Read(GuestMemoryError),
|
||||
#[error("Failed to seek: {0}")]
|
||||
Seek(io::Error),
|
||||
#[error("Failed to write: {0}")]
|
||||
Write(GuestMemoryError),
|
||||
#[error("Unsupported request: {0}")]
|
||||
Unsupported(u32),
|
||||
#[error("Failed to submit io uring: {0}")]
|
||||
SubmitIoUring(io::Error),
|
||||
#[error("Failed to get guest address: {0}")]
|
||||
GetHostAddress(GuestMemoryError),
|
||||
#[error("Failed to async read: {0}")]
|
||||
AsyncRead(AsyncIoError),
|
||||
#[error("Failed to async write: {0}")]
|
||||
AsyncWrite(AsyncIoError),
|
||||
#[error("failed to async flush: {0}")]
|
||||
AsyncFlush(AsyncIoError),
|
||||
/// Failed allocating a temporary buffer.
|
||||
#[error("Failed allocating a temporary buffer: {0}")]
|
||||
TemporaryBufferAllocation(io::Error),
|
||||
}
|
||||
|
||||
|
||||
@@ -6,20 +6,22 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { path = "../acpi_tables" }
|
||||
anyhow = "1.0.58"
|
||||
anyhow = "1.0.62"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "1.3.2"
|
||||
byteorder = "1.4.3"
|
||||
epoll = "4.3.1"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.126"
|
||||
libc = "0.2.131"
|
||||
log = "0.4.17"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = "0.8.0"
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
vmm-sys-util = "0.10.0"
|
||||
|
||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||
arch = { path = "../arch" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -372,7 +372,7 @@ impl BusDevice for Pl011 {
|
||||
UARTIFLS => self.ifl,
|
||||
UARTIMSC => self.int_enabled,
|
||||
UARTRIS => self.int_level,
|
||||
UARTMIS => (self.int_level & self.int_enabled),
|
||||
UARTMIS => self.int_level & self.int_enabled,
|
||||
UARTDMACR => self.dmacr,
|
||||
UARTDEBUG => self.debug,
|
||||
_ => {
|
||||
|
||||
102
docs/api.md
102
docs/api.md
@@ -1,21 +1,21 @@
|
||||
- [Cloud Hypervisor API](#cloud-hypervisor-api)
|
||||
* [External API](#external-api)
|
||||
+ [REST API](#rest-api)
|
||||
- [Location and availability](#location-and-availability)
|
||||
- [Endpoints](#endpoints)
|
||||
* [Virtual Machine Manager (VMM) Actions](#virtual-machine-manager-vmm-actions)
|
||||
* [Virtual Machine (VM) Actions](#virtual-machine-vm-actions)
|
||||
- [REST API Examples](#rest-api-examples)
|
||||
* [Create a Virtual Machine](#create-a-virtual-machine)
|
||||
* [Boot a Virtual Machine](#boot-a-virtual-machine)
|
||||
* [Dump a Virtual Machine Information](#dump-a-virtual-machine-information)
|
||||
* [Reboot a Virtual Machine](#reboot-a-virtual-machine)
|
||||
* [Shut a Virtual Machine Down](#shut-a-virtual-machine-down)
|
||||
+ [Command Line Interface](#command-line-interface)
|
||||
+ [REST API and CLI Architectural Relationship](#rest-api-and-cli-architectural-relationship)
|
||||
* [Internal API](#internal-api)
|
||||
+ [Goals and Design](#goals-and-design)
|
||||
* [End to End Example](#end-to-end-example)
|
||||
- [External API](#external-api)
|
||||
- [REST API](#rest-api)
|
||||
- [Location and availability](#location-and-availability)
|
||||
- [Endpoints](#endpoints)
|
||||
- [Virtual Machine Manager (VMM) Actions](#virtual-machine-manager-vmm-actions)
|
||||
- [Virtual Machine (VM) Actions](#virtual-machine-vm-actions)
|
||||
- [REST API Examples](#rest-api-examples)
|
||||
- [Create a Virtual Machine](#create-a-virtual-machine)
|
||||
- [Boot a Virtual Machine](#boot-a-virtual-machine)
|
||||
- [Dump a Virtual Machine Information](#dump-a-virtual-machine-information)
|
||||
- [Reboot a Virtual Machine](#reboot-a-virtual-machine)
|
||||
- [Shut a Virtual Machine Down](#shut-a-virtual-machine-down)
|
||||
- [Command Line Interface](#command-line-interface)
|
||||
- [REST API and CLI Architectural Relationship](#rest-api-and-cli-architectural-relationship)
|
||||
- [Internal API](#internal-api)
|
||||
- [Goals and Design](#goals-and-design)
|
||||
- [End to End Example](#end-to-end-example)
|
||||
|
||||
# Cloud Hypervisor API
|
||||
|
||||
@@ -71,40 +71,40 @@ The Cloud Hypervisor API exposes the following actions through its endpoints:
|
||||
|
||||
#### Virtual Machine Manager (VMM) Actions
|
||||
|
||||
Action | Endpoint | Request Body | Response Body | Prerequisites
|
||||
------------------------------------|-----------------|--------------|----------------------------|---------------------------
|
||||
Check for the REST API availability | `/vmm.ping` | N/A | `/schemas/VmmPingResponse` | N/A
|
||||
Shut the VMM down | `/vmm.shutdown` | N/A | N/A | The VMM is running
|
||||
| Action | Endpoint | Request Body | Response Body | Prerequisites |
|
||||
| ----------------------------------- | --------------- | ------------ | -------------------------- | ------------------ |
|
||||
| Check for the REST API availability | `/vmm.ping` | N/A | `/schemas/VmmPingResponse` | N/A |
|
||||
| Shut the VMM down | `/vmm.shutdown` | N/A | N/A | The VMM is running |
|
||||
|
||||
#### Virtual Machine (VM) Actions
|
||||
|
||||
Action | Endpoint | Request Body | Response Body | Prerequisites
|
||||
-----------------------------------|----------------------|---------------------------|--------------------------|---------------------------
|
||||
Create the VM | `/vm.create` | `/schemas/VmConfig` | N/A | The VM is not created yet
|
||||
Delete the VM | `/vm.delete` | N/A | N/A | N/A
|
||||
Boot the VM | `/vm.boot` | N/A | N/A | The VM is created but not booted
|
||||
Shut the VM down | `/vm.shutdown` | N/A | N/A | The VM is booted
|
||||
Reboot the VM | `/vm.reboot` | N/A | N/A | The VM is booted
|
||||
Trigger power button of the VM | `/vm.power-button` | N/A | N/A | The VM is booted
|
||||
Pause the VM | `/vm.pause` | N/A | N/A | The VM is booted
|
||||
Resume the VM | `/vm.resume` | N/A | N/A | The VM is paused
|
||||
Task a snapshot of the VM | `/vm.snapshot` | `/schemas/VmSnapshotConfig`| N/A | The VM is paused
|
||||
Perform a coredump of the VM | `/vm.coredump` | `/schemas/VmCoredumpData` | N/A | The VM is paused
|
||||
Restore the VM from a snapshot | `/vm.restore` | `/schemas/RestoreConfig` | N/A | The VM is created but not booted
|
||||
Add/remove CPUs to/from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
|
||||
Add/remove memory from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
|
||||
Add/remove memory from a zone | `/vm.resize-zone` | `/schemas/VmResizeZone` | N/A | The VM is booted
|
||||
Dump the VM information | `/vm.info` | N/A | `/schemas/VmInfo` | The VM is created
|
||||
Add VFIO PCI device to the VM | `/vm.add-device` | `/schemas/VmAddDevice` | `/schemas/PciDeviceInfo` | The VM is booted
|
||||
Add disk device to the VM | `/vm.add-disk` | `/schemas/DiskConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
||||
Add fs device to the VM | `/vm.add-fs` | `/schemas/FsConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
||||
Add pmem device to the VM | `/vm.add-pmem` | `/schemas/PmemConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
||||
Add network device to the VM | `/vm.add-net` | `/schemas/NetConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
||||
Add userspace PCI device to the VM | `/vm.add-user-device`| `/schemas/VmAddUserDevice`| `/schemas/PciDeviceInfo` | The VM is booted
|
||||
Add vdpa device to the VM | `/vm.add-vdpa` | `/schemas/VdpaConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
||||
Add vsock device to the VM | `/vm.add-vsock` | `/schemas/VsockConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
||||
Remove device from the VM | `/vm.remove-device` | `/schemas/VmRemoveDevice` | N/A | The VM is booted
|
||||
Dump the VM counters | `/vm.counters` | N/A | `/schemas/VmCounters` | The VM is booted
|
||||
| Action | Endpoint | Request Body | Response Body | Prerequisites |
|
||||
| ---------------------------------- | --------------------- | --------------------------- | ------------------------ | -------------------------------- |
|
||||
| Create the VM | `/vm.create` | `/schemas/VmConfig` | N/A | The VM is not created yet |
|
||||
| Delete the VM | `/vm.delete` | N/A | N/A | N/A |
|
||||
| Boot the VM | `/vm.boot` | N/A | N/A | The VM is created but not booted |
|
||||
| Shut the VM down | `/vm.shutdown` | N/A | N/A | The VM is booted |
|
||||
| Reboot the VM | `/vm.reboot` | N/A | N/A | The VM is booted |
|
||||
| Trigger power button of the VM | `/vm.power-button` | N/A | N/A | The VM is booted |
|
||||
| Pause the VM | `/vm.pause` | N/A | N/A | The VM is booted |
|
||||
| Resume the VM | `/vm.resume` | N/A | N/A | The VM is paused |
|
||||
| Task a snapshot of the VM | `/vm.snapshot` | `/schemas/VmSnapshotConfig` | N/A | The VM is paused |
|
||||
| Perform a coredump of the VM | `/vm.coredump` | `/schemas/VmCoredumpData` | N/A | The VM is paused |
|
||||
| Restore the VM from a snapshot | `/vm.restore` | `/schemas/RestoreConfig` | N/A | The VM is created but not booted |
|
||||
| Add/remove CPUs to/from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted |
|
||||
| Add/remove memory from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted |
|
||||
| Add/remove memory from a zone | `/vm.resize-zone` | `/schemas/VmResizeZone` | N/A | The VM is booted |
|
||||
| Dump the VM information | `/vm.info` | N/A | `/schemas/VmInfo` | The VM is created |
|
||||
| Add VFIO PCI device to the VM | `/vm.add-device` | `/schemas/VmAddDevice` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add disk device to the VM | `/vm.add-disk` | `/schemas/DiskConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add fs device to the VM | `/vm.add-fs` | `/schemas/FsConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add pmem device to the VM | `/vm.add-pmem` | `/schemas/PmemConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add network device to the VM | `/vm.add-net` | `/schemas/NetConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add userspace PCI device to the VM | `/vm.add-user-device` | `/schemas/VmAddUserDevice` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add vdpa device to the VM | `/vm.add-vdpa` | `/schemas/VdpaConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add vsock device to the VM | `/vm.add-vsock` | `/schemas/VsockConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Remove device from the VM | `/vm.remove-device` | `/schemas/VmRemoveDevice` | N/A | The VM is booted |
|
||||
| Dump the VM counters | `/vm.counters` | N/A | `/schemas/VmCounters` | The VM is booted |
|
||||
|
||||
### REST API Examples
|
||||
|
||||
@@ -143,8 +143,7 @@ curl --unix-socket /tmp/cloud-hypervisor.sock -i \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"cpus":{"boot_vcpus": 4, "max_vcpus": 4},
|
||||
"kernel":{"path":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu"},
|
||||
"cmdline":{"args":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
|
||||
"payload":{"kernel":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu", "cmdline":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
|
||||
"disks":[{"path":"/opt/clh/images/focal-server-cloudimg-amd64.raw"}],
|
||||
"rng":{"src":"/dev/urandom"},
|
||||
"net":[{"ip":"192.168.10.10", "mask":"255.255.255.0", "mac":"12:34:56:78:90:01"}]
|
||||
@@ -311,8 +310,7 @@ APIs work together, let's look at a complete VM creation flow, from the
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"cpus":{"boot_vcpus": 4, "max_vcpus": 4},
|
||||
"kernel":{"path":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu"},
|
||||
"cmdline":{"args":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
|
||||
"payload":{"kernel":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu", "cmdline":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
|
||||
"disks":[{"path":"/opt/clh/images/focal-server-cloudimg-amd64.raw"}],
|
||||
"rng":{"src":"/dev/urandom"},
|
||||
"net":[{"ip":"192.168.10.10", "mask":"255.255.255.0", "mac":"12:34:56:78:90:01"}]
|
||||
|
||||
@@ -59,6 +59,16 @@ mkdir -p /mnt
|
||||
sudo mount -o loop,offset=$((227328 * 512)) focal-server-cloudimg-amd64.raw /mnt
|
||||
```
|
||||
|
||||
### Set up DNS
|
||||
|
||||
The next step describes changing the root directory to the rootfs contained by
|
||||
the cloud image. For DNS to work in the root directory, you will need to first bind-mount
|
||||
the host `/etc/resolv.conf` onto the mounted linux partition of the cloud image.
|
||||
|
||||
```bash
|
||||
sudo mount -o bind /etc/resolv.conf /mnt/etc/resolv.conf
|
||||
```
|
||||
|
||||
### Change root directory
|
||||
|
||||
Changing the root directory will allow us to install new packages to the rootfs
|
||||
@@ -108,6 +118,7 @@ umount /dev/pts
|
||||
umount /proc
|
||||
history -c
|
||||
exit
|
||||
umount /mnt/etc/resolv.conf
|
||||
umount /mnt
|
||||
```
|
||||
|
||||
|
||||
11
docs/fs.md
11
docs/fs.md
@@ -29,10 +29,11 @@ mkdir /tmp/shared_dir
|
||||
_Run virtiofsd_
|
||||
```bash
|
||||
./virtiofsd \
|
||||
-d \
|
||||
--log-level debug \
|
||||
--socket-path=/tmp/virtiofs \
|
||||
--shared-dir=/tmp/shared_dir \
|
||||
--cache=never
|
||||
--cache=never \
|
||||
--thread-pool-size=$N
|
||||
```
|
||||
|
||||
The `cache=never` option is the default when using `virtiofsd` with
|
||||
@@ -44,6 +45,10 @@ The `cache=always` option will allow the host page cache to be used, which can
|
||||
result in better performance for the guest's workload at the cost of increasing
|
||||
the footprint on host memory.
|
||||
|
||||
The `thread-pool-size` option controls how many IO threads are spawned. For
|
||||
very fast storage like NVMe spawning enough worker threads is critical to
|
||||
getting an acceptable performance compared to native.
|
||||
|
||||
### Kernel support
|
||||
|
||||
Modern Linux kernels (at least v5.10) have support for virtio-fs. Use of older
|
||||
@@ -90,4 +95,4 @@ Cloud Hypervisor command line, which happens to be `myfs` in this example.
|
||||
## DAX feature
|
||||
|
||||
Given the DAX feature is not stable yet from a daemon standpoint, it is not
|
||||
available in Cloud Hypervisor.
|
||||
available in Cloud Hypervisor.
|
||||
|
||||
@@ -103,8 +103,18 @@ option as well.
|
||||
--disk path=tdx_guest_img
|
||||
```
|
||||
|
||||
### Guest kernel disables serial ports
|
||||
### Guest kernel limitations
|
||||
|
||||
#### Serial ports disabled
|
||||
|
||||
The latest guest kernel that can be found in the latest image
|
||||
`td-guest-rhel8.5.raw` disabled the support for serial ports. This means adding
|
||||
`console=ttyS0` will have no effect and will not print any log from the guest.
|
||||
`console=ttyS0` will have no effect and will not print any log from the guest.
|
||||
|
||||
#### PCI hotplug through ACPI
|
||||
|
||||
Unless you run the guest kernel with the parameter `tdx_disable_filter`, ACPI
|
||||
devices responsible for handling PCI hotplug (PCI hotplug controller, PCI
|
||||
Express Bus and Generic Event Device) will not be allowed, therefore the
|
||||
corresponding drivers will not be loaded and the PCI hotplug feature will not
|
||||
be supported.
|
||||
|
||||
@@ -5,6 +5,6 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.126"
|
||||
serde = { version = "1.0.138", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.82"
|
||||
libc = "0.2.131"
|
||||
serde = { version = "1.0.143", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.83"
|
||||
|
||||
191
fuzz/Cargo.lock
generated
191
fuzz/Cargo.lock
generated
@@ -11,9 +11,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.58"
|
||||
version = "1.0.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
|
||||
checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -30,15 +30,14 @@ checksum = "5a7924531f38b1970ff630f03eb20a2fde69db5c590c93b0f3482e95dcc5fd60"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.5.0"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f"
|
||||
checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164"
|
||||
|
||||
[[package]]
|
||||
name = "arch"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"acpi_tables",
|
||||
"anyhow",
|
||||
"byteorder",
|
||||
"fdt",
|
||||
@@ -48,6 +47,7 @@ dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"uuid",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"vm-fdt",
|
||||
@@ -127,9 +127,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.2.8"
|
||||
version = "3.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83"
|
||||
checksum = "29e724a68d9319343bb3328c9cc2dfde263f4b3142ee1059a9980580171c954b"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
@@ -153,7 +153,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "24.0.0"
|
||||
version = "25.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -179,15 +179,21 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"block_util",
|
||||
"cloud-hypervisor",
|
||||
"devices",
|
||||
"epoll",
|
||||
"libc",
|
||||
"libfuzzer-sys",
|
||||
"micro_http",
|
||||
"once_cell",
|
||||
"qcow",
|
||||
"seccompiler",
|
||||
"vhdx",
|
||||
"virtio-devices",
|
||||
"virtio-queue",
|
||||
"vm-device",
|
||||
"vm-memory",
|
||||
"vm-virtio",
|
||||
"vmm",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
@@ -206,6 +212,41 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55626594feae15d266d52440b26ff77de0e22230cf0c113abe619084c1ddc910"
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "devices"
|
||||
version = "0.1.0"
|
||||
@@ -215,7 +256,6 @@ dependencies = [
|
||||
"arch",
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"epoll",
|
||||
"hypervisor",
|
||||
"libc",
|
||||
"log",
|
||||
@@ -248,9 +288,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fdt"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b643857cf70949306b81d7e92cb9d47add673868edac9863c4a49c42feaf3f1e"
|
||||
checksum = "964f5becd44d069dca0beea2b4bc05639ae7bf3b3f5369c295aff360bb57cca2"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
@@ -265,9 +311,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.1"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
@@ -283,15 +329,16 @@ name = "hypervisor"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"epoll",
|
||||
"byteorder",
|
||||
"iced-x86",
|
||||
"kvm-bindings",
|
||||
"kvm-ioctls",
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"thiserror",
|
||||
"vfio-ioctls",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
@@ -306,6 +353,12 @@ dependencies = [
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.1"
|
||||
@@ -318,9 +371,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.5.2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d75829ed9377bab6c90039fe47b9d84caceb4b5063266142e21bcce6550cda8"
|
||||
checksum = "427496d698429d86ff947537dff68facb83fe753b7abea4fab4ac1c4b7a4b94b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -328,9 +381,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
||||
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
|
||||
|
||||
[[package]]
|
||||
name = "kvm-bindings"
|
||||
@@ -360,9 +413,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.126"
|
||||
version = "0.2.131"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||
checksum = "04c3b4822ccebfa39c02fc03d1534441b22ead323fa0f48bb7ddd8e6ba076a40"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
@@ -396,7 +449,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#9517a300370a158a7af0996b7eebf040d171e1a4"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#863b0370ba7e57f7df5b908ada9e5b44809ccae9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"vmm-sys-util",
|
||||
@@ -420,6 +473,7 @@ dependencies = [
|
||||
"net_gen",
|
||||
"rate_limiter",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"virtio-bindings",
|
||||
@@ -431,9 +485,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.13.0"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
||||
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
|
||||
|
||||
[[package]]
|
||||
name = "option_parser"
|
||||
@@ -441,9 +495,9 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.1.0"
|
||||
version = "6.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
|
||||
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
|
||||
|
||||
[[package]]
|
||||
name = "pci"
|
||||
@@ -470,9 +524,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.40"
|
||||
version = "1.0.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
|
||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -490,9 +544,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.20"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -508,9 +562,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "remain"
|
||||
version = "0.2.3"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c35270ea384ac1762895831cc8acb96f171468e52cec82ed9186f9416209fa4"
|
||||
checksum = "06df529c0d271b27ac4a2c260f5ce2914b60bd44702cecec7b9f271adbdde23b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -528,9 +582,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.10"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
|
||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||
|
||||
[[package]]
|
||||
name = "seccompiler"
|
||||
@@ -543,24 +597,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.12"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
|
||||
checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.138"
|
||||
version = "1.0.143"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47"
|
||||
checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.138"
|
||||
version = "1.0.143"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c"
|
||||
checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -569,15 +623,37 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.82"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
|
||||
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89df7a26519371a3cce44fbb914c2819c84d9b897890987fa3ab096491cc0ea8"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_with_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de337f322382fcdfbb21a014f7c224ee041a23785651db67b9827403178f698f"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.14"
|
||||
@@ -611,9 +687,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.98"
|
||||
version = "1.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
|
||||
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -650,18 +726,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.31"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
||||
checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.31"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
||||
checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -670,9 +746,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.1"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
|
||||
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
@@ -722,7 +798,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio-ioctls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#4630612f2ff300e78b6d55be324fb5481aa84661"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#1ff2324e5cb0fccab825f9c96bb9387a1a01863f"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
@@ -739,7 +815,6 @@ dependencies = [
|
||||
name = "vfio_user"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
@@ -762,7 +837,6 @@ dependencies = [
|
||||
"remain",
|
||||
"thiserror",
|
||||
"uuid",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -819,11 +893,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.3.0"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88f2d73c184c18f8acc32dab77fcb6e3af92d53262538d3a68aa474810d6863c"
|
||||
checksum = "b4f59652909f276e6edd8bf36e9f106480b2202f5f046717b3de14f1b4072a28"
|
||||
dependencies = [
|
||||
"log",
|
||||
"virtio-bindings",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
@@ -844,7 +919,6 @@ dependencies = [
|
||||
"anyhow",
|
||||
"hypervisor",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"vfio-ioctls",
|
||||
"vm-memory",
|
||||
@@ -885,7 +959,6 @@ name = "vm-virtio"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
]
|
||||
@@ -937,9 +1010,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vmm-sys-util"
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "733537bded03aaa93543f785ae997727b30d1d9f4a03b7861d23290474242e11"
|
||||
checksum = "08604d7be03eb26e33b3cee3ed4aef2bf550b305d1cca60e84da5d28d3790b62"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
|
||||
@@ -10,16 +10,22 @@ cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
block_util = { path = "../block_util" }
|
||||
libc = "0.2.126"
|
||||
devices = { path = "../devices" }
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.131"
|
||||
libfuzzer-sys = "0.4.3"
|
||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||
once_cell = "1.13.1"
|
||||
qcow = { path = "../qcow" }
|
||||
seccompiler = "0.2.0"
|
||||
vhdx = { path = "../vhdx" }
|
||||
virtio-devices = { path = "../virtio-devices" }
|
||||
virtio-queue = "0.4.0"
|
||||
vmm-sys-util = "0.9.0"
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
virtio-queue = "0.5.0"
|
||||
vmm = { path = "../vmm" }
|
||||
vmm-sys-util = "0.10.0"
|
||||
vm-memory = "0.8.0"
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
|
||||
[dependencies.cloud-hypervisor]
|
||||
path = ".."
|
||||
@@ -33,6 +39,24 @@ versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_deri
|
||||
[workspace]
|
||||
members = ["."]
|
||||
|
||||
[[bin]]
|
||||
name = "block"
|
||||
path = "fuzz_targets/block.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "cmos"
|
||||
path = "fuzz_targets/cmos.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "http_api"
|
||||
path = "fuzz_targets/http_api.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "qcow"
|
||||
path = "fuzz_targets/qcow.rs"
|
||||
@@ -40,8 +64,8 @@ test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "block"
|
||||
path = "fuzz_targets/block.rs"
|
||||
name = "serial"
|
||||
path = "fuzz_targets/serial.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
|
||||
@@ -9,84 +9,39 @@ use libfuzzer_sys::fuzz_target;
|
||||
use seccompiler::SeccompAction;
|
||||
use std::ffi;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Cursor, Read, Seek, SeekFrom};
|
||||
use std::mem::size_of;
|
||||
use std::io;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use virtio_devices::{Block, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||
use virtio_queue::{Queue, QueueState};
|
||||
use virtio_queue::{Queue, QueueT};
|
||||
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||
use vmm::{EpollContext, EpollDispatch};
|
||||
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
|
||||
const MEM_SIZE: u64 = 256 * 1024 * 1024;
|
||||
const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor.
|
||||
const QUEUE_SIZE: u16 = 16; // Max entries in the queue.
|
||||
const CMD_SIZE: usize = 16; // Bytes in the command.
|
||||
|
||||
fuzz_target!(|bytes| {
|
||||
let size_u64 = size_of::<u64>();
|
||||
if bytes.len() as u64 > MEM_SIZE {
|
||||
return;
|
||||
}
|
||||
|
||||
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE as usize)]).unwrap();
|
||||
|
||||
// The fuzz data is interpreted as:
|
||||
// starting index 8 bytes
|
||||
// command location 8 bytes
|
||||
// command 16 bytes
|
||||
// descriptors circular buffer 16 bytes * 3
|
||||
if bytes.len() < 4 * size_u64 {
|
||||
// Need an index to start.
|
||||
return;
|
||||
}
|
||||
|
||||
let mut data_image = Cursor::new(bytes);
|
||||
|
||||
let first_index = read_u64(&mut data_image);
|
||||
if first_index > MEM_SIZE / DESC_SIZE {
|
||||
return;
|
||||
}
|
||||
let first_offset = first_index * DESC_SIZE;
|
||||
if first_offset as usize + size_u64 > bytes.len() {
|
||||
return;
|
||||
}
|
||||
|
||||
let command_addr = read_u64(&mut data_image);
|
||||
if command_addr > MEM_SIZE - CMD_SIZE as u64 {
|
||||
return;
|
||||
}
|
||||
if mem
|
||||
.write_slice(
|
||||
&bytes[2 * size_u64..(2 * size_u64) + CMD_SIZE],
|
||||
GuestAddress(command_addr as u64),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
data_image.seek(SeekFrom::Start(first_offset)).unwrap();
|
||||
let desc_table = read_u64(&mut data_image);
|
||||
|
||||
if mem
|
||||
.write_slice(&bytes[32..], GuestAddress(desc_table as u64))
|
||||
.is_err()
|
||||
{
|
||||
if mem.write_slice(bytes, GuestAddress(0 as u64)).is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||
|
||||
let mut q = Queue::<
|
||||
GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
QueueState,
|
||||
>::new(guest_memory.clone(), QUEUE_SIZE);
|
||||
q.state.ready = true;
|
||||
q.state.size = QUEUE_SIZE / 2;
|
||||
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||
q.set_ready(true);
|
||||
q.set_size(QUEUE_SIZE / 2);
|
||||
|
||||
let queue_evts: Vec<EventFd> = vec![EventFd::new(0).unwrap()];
|
||||
let queue_fd = queue_evts[0].as_raw_fd();
|
||||
let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(queue_fd)) };
|
||||
let evt = EventFd::new(0).unwrap();
|
||||
let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) };
|
||||
|
||||
let shm = memfd_create(&ffi::CString::new("fuzz").unwrap(), 0).unwrap();
|
||||
let disk_file: File = unsafe { File::from_raw_fd(shm) };
|
||||
@@ -110,19 +65,16 @@ fuzz_target!(|bytes| {
|
||||
.activate(
|
||||
guest_memory,
|
||||
Arc::new(NoopVirtioInterrupt {}),
|
||||
vec![q],
|
||||
queue_evts,
|
||||
vec![(0, q, evt)],
|
||||
)
|
||||
.ok();
|
||||
|
||||
queue_evt.write(77).unwrap(); // Rings the doorbell, any byte will do.
|
||||
});
|
||||
|
||||
fn read_u64<T: Read>(readable: &mut T) -> u64 {
|
||||
let mut buf = [0u8; size_of::<u64>()];
|
||||
readable.read_exact(&mut buf[..]).unwrap();
|
||||
u64::from_le_bytes(buf)
|
||||
}
|
||||
wait_queue_event_processed(queue_evt);
|
||||
|
||||
block.reset(); // Ensure the virtio-block device thread is killed and joined
|
||||
});
|
||||
|
||||
fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> {
|
||||
let res = unsafe { libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags) };
|
||||
@@ -137,10 +89,33 @@ fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> {
|
||||
pub struct NoopVirtioInterrupt {}
|
||||
|
||||
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||
fn trigger(
|
||||
&self,
|
||||
_int_type: VirtioInterruptType,
|
||||
) -> std::result::Result<(), std::io::Error> {
|
||||
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn wait_queue_event_processed(queue_evt: EventFd) {
|
||||
let mut epoll = EpollContext::new().unwrap();
|
||||
epoll.add_event(&queue_evt, EpollDispatch::Api).unwrap();
|
||||
|
||||
let epoll_fd = epoll.as_raw_fd();
|
||||
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); 1];
|
||||
|
||||
loop {
|
||||
let num_events = match epoll::wait(epoll_fd, 0, &mut events[..]) {
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::Interrupted {
|
||||
continue;
|
||||
}
|
||||
|
||||
println!("Warning: Unexpected epoll::wait() error!");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if num_events == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
48
fuzz/fuzz_targets/cmos.rs
Normal file
48
fuzz/fuzz_targets/cmos.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright © 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![no_main]
|
||||
use devices::legacy::Cmos;
|
||||
use libc::EFD_NONBLOCK;
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use vm_device::BusDevice;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
fuzz_target!(|bytes| {
|
||||
// Need at least 16 bytes for the test
|
||||
if bytes.len() < 16 {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut below_4g = [0u8; 8];
|
||||
let mut above_4g = [0u8; 8];
|
||||
|
||||
below_4g.copy_from_slice(&bytes[0..8]);
|
||||
above_4g.copy_from_slice(&bytes[8..16]);
|
||||
|
||||
let mut cmos = Cmos::new(
|
||||
u64::from_le_bytes(below_4g),
|
||||
u64::from_le_bytes(above_4g),
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
);
|
||||
|
||||
let mut i = 16;
|
||||
while i < bytes.len() {
|
||||
let read = bytes.get(i).unwrap_or(&0) % 2 == 0;
|
||||
i += 1;
|
||||
|
||||
if read {
|
||||
let offset = (bytes.get(i).unwrap_or(&0) % 2) as u64;
|
||||
i += 1;
|
||||
let mut out_bytes = vec![0];
|
||||
cmos.read(0, offset, &mut out_bytes);
|
||||
} else {
|
||||
let offset = (bytes.get(i).unwrap_or(&0) % 2) as u64;
|
||||
i += 1;
|
||||
let data = vec![*bytes.get(i).unwrap_or(&0)];
|
||||
i += 1;
|
||||
cmos.write(0, offset, &data);
|
||||
}
|
||||
}
|
||||
});
|
||||
191
fuzz/fuzz_targets/http_api.rs
Normal file
191
fuzz/fuzz_targets/http_api.rs
Normal file
@@ -0,0 +1,191 @@
|
||||
// Copyright © 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![no_main]
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use micro_http::Request;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::sync::mpsc::{channel, Receiver};
|
||||
use std::thread;
|
||||
use vmm::api::{http::*, ApiRequest, ApiResponsePayload};
|
||||
use vmm::{EpollContext, EpollDispatch};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
// Need to be ordered for test case reproducibility
|
||||
static ROUTES: Lazy<Vec<&Box<dyn EndpointHandler + Sync + Send>>> =
|
||||
Lazy::new(|| HTTP_ROUTES.routes.values().collect());
|
||||
|
||||
fuzz_target!(|bytes| {
|
||||
if bytes.len() < 2 {
|
||||
return;
|
||||
}
|
||||
|
||||
let route = ROUTES[bytes[0] as usize % ROUTES.len()];
|
||||
if let Some(request) = generate_request(&bytes[1..]) {
|
||||
let exit_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
|
||||
let api_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
|
||||
let (api_sender, api_receiver) = channel();
|
||||
|
||||
let http_receiver_thread = {
|
||||
let exit_evt = exit_evt.try_clone().unwrap();
|
||||
let api_evt = api_evt.try_clone().unwrap();
|
||||
thread::Builder::new()
|
||||
.name("http_receiver".to_string())
|
||||
.spawn(move || {
|
||||
http_receiver_stub(exit_evt, api_evt, api_receiver);
|
||||
})
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
route.handle_request(&request, api_evt, api_sender);
|
||||
exit_evt.write(1).ok();
|
||||
http_receiver_thread.join().unwrap();
|
||||
};
|
||||
});
|
||||
|
||||
fn generate_request(bytes: &[u8]) -> Option<Request> {
|
||||
let req_method = match bytes[0] % 5 {
|
||||
0 => "GET",
|
||||
1 => "PUT",
|
||||
2 => "PATCH",
|
||||
3 => "POST",
|
||||
_ => "INVALID",
|
||||
};
|
||||
let request_line = format!("{} http://localhost/home HTTP/1.1\r\n", req_method);
|
||||
|
||||
let req_body = &bytes[1..];
|
||||
let request = if req_body.len() > 0 {
|
||||
[
|
||||
format!("{}Content-Length: {}\r\n", request_line, req_body.len()).as_bytes(),
|
||||
req_body,
|
||||
]
|
||||
.concat()
|
||||
} else {
|
||||
format!("{}\r\n", request_line).as_bytes().to_vec()
|
||||
};
|
||||
|
||||
Request::try_from(&request, None).ok()
|
||||
}
|
||||
|
||||
fn http_receiver_stub(exit_evt: EventFd, api_evt: EventFd, api_receiver: Receiver<ApiRequest>) {
|
||||
let mut epoll = EpollContext::new().unwrap();
|
||||
epoll.add_event(&exit_evt, EpollDispatch::Exit).unwrap();
|
||||
epoll.add_event(&api_evt, EpollDispatch::Api).unwrap();
|
||||
|
||||
let epoll_fd = epoll.as_raw_fd();
|
||||
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); 2];
|
||||
let num_events;
|
||||
loop {
|
||||
num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) {
|
||||
Ok(num_events) => num_events,
|
||||
Err(e) => match e.raw_os_error() {
|
||||
Some(libc::EAGAIN) | Some(libc::EINTR) => continue,
|
||||
_ => panic!("Unexpected epoll::wait error!"),
|
||||
},
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
for event in events.iter().take(num_events) {
|
||||
let dispatch_event: EpollDispatch = event.data.into();
|
||||
match dispatch_event {
|
||||
EpollDispatch::Exit => {
|
||||
break;
|
||||
}
|
||||
EpollDispatch::Api => {
|
||||
for _ in 0..api_evt.read().unwrap() {
|
||||
let api_request = api_receiver.recv().unwrap();
|
||||
match api_request {
|
||||
ApiRequest::VmCreate(_, sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmDelete(sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmBoot(sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmShutdown(sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmReboot(sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmInfo(sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmmPing(sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmPause(sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmResume(sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmSnapshot(_, sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmRestore(_, sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmmShutdown(sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmResize(_, sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmResizeZone(_, sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmAddDevice(_, sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmAddUserDevice(_, sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmRemoveDevice(_, sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmAddDisk(_, sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmAddFs(_, sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmAddPmem(_, sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmAddNet(_, sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmAddVdpa(_, sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmAddVsock(_, sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmCounters(sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmReceiveMigration(_, sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmSendMigration(_, sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
ApiRequest::VmPowerButton(sender) => {
|
||||
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
panic!("Unexpected Epoll event");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
73
fuzz/fuzz_targets/serial.rs
Normal file
73
fuzz/fuzz_targets/serial.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright © 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![no_main]
|
||||
use devices::legacy::Serial;
|
||||
use libc::EFD_NONBLOCK;
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use std::sync::Arc;
|
||||
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig, InterruptSourceGroup};
|
||||
use vm_device::BusDevice;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
fuzz_target!(|bytes| {
|
||||
let mut serial = Serial::new_sink(
|
||||
"serial".into(),
|
||||
Arc::new(TestInterrupt::new(EventFd::new(EFD_NONBLOCK).unwrap())),
|
||||
);
|
||||
|
||||
let mut i = 0;
|
||||
while i < bytes.len() {
|
||||
let choice = bytes.get(i).unwrap_or(&0) % 3;
|
||||
i += 1;
|
||||
|
||||
match choice {
|
||||
0 => {
|
||||
let offset = (bytes.get(i).unwrap_or(&0) % 8) as u64;
|
||||
i += 1;
|
||||
let mut out_bytes = vec![0];
|
||||
serial.read(0, offset, &mut out_bytes);
|
||||
}
|
||||
1 => {
|
||||
let offset = (bytes.get(i).unwrap_or(&0) % 8) as u64;
|
||||
i += 1;
|
||||
let data = vec![*bytes.get(i).unwrap_or(&0)];
|
||||
i += 1;
|
||||
serial.write(0, offset, &data);
|
||||
}
|
||||
_ => {
|
||||
let data = vec![*bytes.get(i).unwrap_or(&0)];
|
||||
i += 1;
|
||||
serial.queue_input_bytes(&data).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
struct TestInterrupt {
|
||||
event_fd: EventFd,
|
||||
}
|
||||
|
||||
impl InterruptSourceGroup for TestInterrupt {
|
||||
fn trigger(&self, _index: InterruptIndex) -> Result<(), std::io::Error> {
|
||||
self.event_fd.write(1)
|
||||
}
|
||||
fn update(
|
||||
&self,
|
||||
_index: InterruptIndex,
|
||||
_config: InterruptSourceConfig,
|
||||
_masked: bool,
|
||||
) -> Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
|
||||
Some(self.event_fd.try_clone().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl TestInterrupt {
|
||||
fn new(event_fd: EventFd) -> Self {
|
||||
TestInterrupt { event_fd }
|
||||
}
|
||||
}
|
||||
@@ -11,19 +11,20 @@ mshv = ["mshv-ioctls", "mshv-bindings"]
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.58"
|
||||
epoll = "4.3.1"
|
||||
thiserror = "1.0.31"
|
||||
libc = "0.2.126"
|
||||
anyhow = "1.0.62"
|
||||
byteorder = "1.4.3"
|
||||
thiserror = "1.0.32"
|
||||
libc = "0.2.131"
|
||||
log = "0.4.17"
|
||||
kvm-ioctls = { version = "0.11.0", optional = true }
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0-tdx", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
mshv-bindings = { git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", branch = "main", optional = true}
|
||||
serde = { version = "1.0.138", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.82"
|
||||
serde = { version = "1.0.143", features = ["rc", "derive"] }
|
||||
serde_with = { version = "2.0.0", default-features = false, features = ["macros"] }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = { version = "0.9.0", features = ["with-serde"] }
|
||||
vmm-sys-util = { version = "0.10.0", features = ["with-serde"] }
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
||||
version = "1.17.0"
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||
|
||||
use crate::{CpuState, Device, GicState, HypervisorDeviceError, HypervisorVmError};
|
||||
use crate::{CpuState, GicState, HypervisorDeviceError, HypervisorVmError};
|
||||
use std::any::Any;
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Errors thrown while setting up the VGIC.
|
||||
#[derive(Debug)]
|
||||
@@ -40,8 +39,6 @@ pub trait Vgic: Send + Sync {
|
||||
/// Returns the MSI reg property of the device
|
||||
fn msi_properties(&self) -> [u64; 2];
|
||||
|
||||
fn set_its_device(&mut self, its_device: Option<Arc<dyn Device>>);
|
||||
|
||||
/// Get the values of GICR_TYPER for each vCPU.
|
||||
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]);
|
||||
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
use crate::arch::emulator::{EmulationError, EmulationResult, PlatformEmulator, PlatformError};
|
||||
use crate::arch::x86::emulator::instructions::*;
|
||||
use crate::arch::x86::regs::{CR0_PE, EFER_LMA};
|
||||
use crate::arch::x86::{segment_type_expand_down, segment_type_ro, Exception, SegmentRegisterOps};
|
||||
use crate::x86_64::{SegmentRegister, SpecialRegisters, StandardRegisters};
|
||||
use crate::arch::x86::{
|
||||
segment_type_expand_down, segment_type_ro, Exception, SegmentRegister, SpecialRegisters,
|
||||
StandardRegisters,
|
||||
};
|
||||
use anyhow::Context;
|
||||
use iced_x86::*;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// found in the LICENSE-BSD-3-Clause file.
|
||||
|
||||
// For GDT details see arch/x86/include/asm/segment.h
|
||||
use crate::x86_64::SegmentRegister;
|
||||
use crate::arch::x86::SegmentRegister;
|
||||
|
||||
/// Constructor for a conventional segment GDT (or LDT) entry. Derived from the kernel's segment.h.
|
||||
pub fn gdt_entry(flags: u16, base: u32, limit: u32) -> u64 {
|
||||
@@ -51,7 +51,7 @@ fn get_limit(entry: u64) -> u32 {
|
||||
// Perform manual limit scaling if G flag is set
|
||||
match get_g(entry) {
|
||||
0 => limit,
|
||||
_ => ((limit << 12) | 0xFFF), // G flag is either 0 or 1
|
||||
_ => (limit << 12) | 0xFFF, // G flag is either 0 or 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,6 @@ pub fn segment_from_gdt(entry: u64, table_index: u8) -> SegmentRegister {
|
||||
l: get_l(entry),
|
||||
g: get_g(entry),
|
||||
avl: get_avl(entry),
|
||||
padding: 0,
|
||||
unusable: match get_p(entry) {
|
||||
0 => 1,
|
||||
_ => 0,
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
pub mod emulator;
|
||||
pub mod gdt;
|
||||
#[allow(non_upper_case_globals)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(non_upper_case_globals)]
|
||||
@@ -27,7 +26,7 @@ pub const MTRR_MEM_TYPE_WB: u64 = 0x6;
|
||||
pub const NUM_IOAPIC_PINS: usize = 24;
|
||||
|
||||
// X86 Exceptions
|
||||
#[allow(dead_code, clippy::upper_case_acronyms)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Exception {
|
||||
DE = 0, // Divide Error
|
||||
@@ -53,40 +52,86 @@ pub enum Exception {
|
||||
|
||||
pub mod regs;
|
||||
|
||||
// Abstracted segment register ops.
|
||||
// Each x86 hypervisor should implement those.
|
||||
pub trait SegmentRegisterOps {
|
||||
// Segment type
|
||||
fn segment_type(&self) -> u8;
|
||||
fn set_segment_type(&mut self, val: u8);
|
||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "with-serde", derive(Deserialize, Serialize))]
|
||||
pub struct SegmentRegister {
|
||||
pub base: u64,
|
||||
pub limit: u32,
|
||||
pub selector: u16,
|
||||
pub type_: u8,
|
||||
pub present: u8,
|
||||
pub dpl: u8,
|
||||
pub db: u8,
|
||||
pub s: u8,
|
||||
pub l: u8,
|
||||
pub g: u8,
|
||||
pub avl: u8,
|
||||
pub unusable: u8,
|
||||
}
|
||||
|
||||
// Descriptor Privilege Level (DPL)
|
||||
fn dpl(&self) -> u8;
|
||||
fn set_dpl(&mut self, val: u8);
|
||||
impl SegmentRegister {
|
||||
pub fn segment_type(&self) -> u8 {
|
||||
self.type_
|
||||
}
|
||||
pub fn set_segment_type(&mut self, val: u8) {
|
||||
self.type_ = val;
|
||||
}
|
||||
|
||||
// Granularity
|
||||
fn granularity(&self) -> u8;
|
||||
fn set_granularity(&mut self, val: u8);
|
||||
pub fn dpl(&self) -> u8 {
|
||||
self.dpl
|
||||
}
|
||||
|
||||
// Memory Presence
|
||||
fn present(&self) -> u8;
|
||||
fn set_present(&mut self, val: u8);
|
||||
pub fn set_dpl(&mut self, val: u8) {
|
||||
self.dpl = val;
|
||||
}
|
||||
|
||||
// Long mode
|
||||
fn long(&self) -> u8;
|
||||
fn set_long(&mut self, val: u8);
|
||||
pub fn present(&self) -> u8 {
|
||||
self.present
|
||||
}
|
||||
|
||||
// Available for system use (AVL)
|
||||
fn avl(&self) -> u8;
|
||||
fn set_avl(&mut self, val: u8);
|
||||
pub fn set_present(&mut self, val: u8) {
|
||||
self.present = val;
|
||||
}
|
||||
|
||||
// Descriptor type (System or code/data)
|
||||
fn desc_type(&self) -> u8;
|
||||
fn set_desc_type(&mut self, val: u8);
|
||||
pub fn long(&self) -> u8 {
|
||||
self.l
|
||||
}
|
||||
|
||||
// D/B
|
||||
fn db(&self) -> u8;
|
||||
fn set_db(&mut self, val: u8);
|
||||
pub fn set_long(&mut self, val: u8) {
|
||||
self.l = val;
|
||||
}
|
||||
|
||||
pub fn avl(&self) -> u8 {
|
||||
self.avl
|
||||
}
|
||||
|
||||
pub fn set_avl(&mut self, val: u8) {
|
||||
self.avl = val;
|
||||
}
|
||||
|
||||
pub fn desc_type(&self) -> u8 {
|
||||
self.s
|
||||
}
|
||||
|
||||
pub fn set_desc_type(&mut self, val: u8) {
|
||||
self.s = val;
|
||||
}
|
||||
|
||||
pub fn granularity(&self) -> u8 {
|
||||
self.g
|
||||
}
|
||||
|
||||
pub fn set_granularity(&mut self, val: u8) {
|
||||
self.g = val;
|
||||
}
|
||||
|
||||
pub fn db(&self) -> u8 {
|
||||
self.db
|
||||
}
|
||||
|
||||
pub fn set_db(&mut self, val: u8) {
|
||||
self.db = val;
|
||||
}
|
||||
}
|
||||
|
||||
// Code segment
|
||||
@@ -115,7 +160,6 @@ macro_rules! msr {
|
||||
MsrEntry {
|
||||
index: $msr,
|
||||
data: 0x0,
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -125,7 +169,143 @@ macro_rules! msr_data {
|
||||
MsrEntry {
|
||||
index: $msr,
|
||||
data: $data,
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "with-serde", derive(Deserialize, Serialize))]
|
||||
pub struct StandardRegisters {
|
||||
pub rax: u64,
|
||||
pub rbx: u64,
|
||||
pub rcx: u64,
|
||||
pub rdx: u64,
|
||||
pub rsi: u64,
|
||||
pub rdi: u64,
|
||||
pub rsp: u64,
|
||||
pub rbp: u64,
|
||||
pub r8: u64,
|
||||
pub r9: u64,
|
||||
pub r10: u64,
|
||||
pub r11: u64,
|
||||
pub r12: u64,
|
||||
pub r13: u64,
|
||||
pub r14: u64,
|
||||
pub r15: u64,
|
||||
pub rip: u64,
|
||||
pub rflags: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "with-serde", derive(Deserialize, Serialize))]
|
||||
pub struct DescriptorTable {
|
||||
pub base: u64,
|
||||
pub limit: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "with-serde", derive(Deserialize, Serialize))]
|
||||
pub struct SpecialRegisters {
|
||||
pub cs: SegmentRegister,
|
||||
pub ds: SegmentRegister,
|
||||
pub es: SegmentRegister,
|
||||
pub fs: SegmentRegister,
|
||||
pub gs: SegmentRegister,
|
||||
pub ss: SegmentRegister,
|
||||
pub tr: SegmentRegister,
|
||||
pub ldt: SegmentRegister,
|
||||
pub gdt: DescriptorTable,
|
||||
pub idt: DescriptorTable,
|
||||
pub cr0: u64,
|
||||
pub cr2: u64,
|
||||
pub cr3: u64,
|
||||
pub cr4: u64,
|
||||
pub cr8: u64,
|
||||
pub efer: u64,
|
||||
pub apic_base: u64,
|
||||
pub interrupt_bitmap: [u64; 4usize],
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
|
||||
pub struct CpuIdEntry {
|
||||
pub function: u32,
|
||||
pub index: u32,
|
||||
pub flags: u32,
|
||||
pub eax: u32,
|
||||
pub ebx: u32,
|
||||
pub ecx: u32,
|
||||
pub edx: u32,
|
||||
}
|
||||
|
||||
pub const CPUID_FLAG_VALID_INDEX: u32 = 1;
|
||||
|
||||
#[derive(Default, Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||
pub struct FpuState {
|
||||
pub fpr: [[u8; 16usize]; 8usize],
|
||||
pub fcw: u16,
|
||||
pub fsw: u16,
|
||||
pub ftwx: u8,
|
||||
pub last_opcode: u16,
|
||||
pub last_ip: u64,
|
||||
pub last_dp: u64,
|
||||
pub xmm: [[u8; 16usize]; 16usize],
|
||||
pub mxcsr: u32,
|
||||
}
|
||||
|
||||
#[serde_with::serde_as]
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LapicState {
|
||||
#[serde_as(as = "[_; 1024usize]")]
|
||||
pub(crate) regs: [::std::os::raw::c_char; 1024usize],
|
||||
}
|
||||
|
||||
impl Default for LapicState {
|
||||
fn default() -> Self {
|
||||
// SAFETY: this is plain old data structure
|
||||
unsafe { ::std::mem::zeroed() }
|
||||
}
|
||||
}
|
||||
|
||||
impl LapicState {
|
||||
pub fn get_klapic_reg(&self, reg_offset: usize) -> u32 {
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use std::io::Cursor;
|
||||
use std::mem;
|
||||
|
||||
let sliceu8 = unsafe {
|
||||
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
|
||||
// Cursors are only readable on arrays of u8, not i8(c_char).
|
||||
mem::transmute::<&[i8], &[u8]>(&self.regs[reg_offset..])
|
||||
};
|
||||
|
||||
let mut reader = Cursor::new(sliceu8);
|
||||
// Following call can't fail if the offsets defined above are correct.
|
||||
reader
|
||||
.read_u32::<LittleEndian>()
|
||||
.expect("Failed to read klapic register")
|
||||
}
|
||||
|
||||
pub fn set_klapic_reg(&mut self, reg_offset: usize, value: u32) {
|
||||
use byteorder::{LittleEndian, WriteBytesExt};
|
||||
use std::io::Cursor;
|
||||
use std::mem;
|
||||
|
||||
let sliceu8 = unsafe {
|
||||
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
|
||||
// Cursors are only readable on arrays of u8, not i8(c_char).
|
||||
mem::transmute::<&mut [i8], &mut [u8]>(&mut self.regs[reg_offset..])
|
||||
};
|
||||
|
||||
let mut writer = Cursor::new(sliceu8);
|
||||
// Following call can't fail if the offsets defined above are correct.
|
||||
writer
|
||||
.write_u32::<LittleEndian>(value)
|
||||
.expect("Failed to write klapic register")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
|
||||
pub struct MsrEntry {
|
||||
pub index: u32,
|
||||
pub data: u64,
|
||||
}
|
||||
|
||||
@@ -9,28 +9,17 @@
|
||||
//
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::aarch64::VcpuInit;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::aarch64::{RegList, Register, StandardRegisters};
|
||||
use crate::aarch64::{RegList, StandardRegisters, VcpuInit};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::arch::x86::{
|
||||
CpuIdEntry, FpuState, LapicState, MsrEntry, SpecialRegisters, StandardRegisters,
|
||||
};
|
||||
#[cfg(feature = "tdx")]
|
||||
use crate::kvm::{TdxExitDetails, TdxExitStatus};
|
||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||
use crate::x86_64::SuspendRegisters;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::x86_64::Xsave;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::x86_64::{CpuId, LapicState};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::x86_64::{
|
||||
ExtendedControlRegisters, FpuState, MsrEntries, SpecialRegisters, StandardRegisters, VcpuEvents,
|
||||
};
|
||||
use crate::CpuState;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::DeviceAttr;
|
||||
#[cfg(feature = "kvm")]
|
||||
use crate::MpState;
|
||||
use thiserror::Error;
|
||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
@@ -48,11 +37,6 @@ pub enum HypervisorCpuError {
|
||||
#[error("Failed to get standard registers: {0}")]
|
||||
GetStandardRegs(#[source] anyhow::Error),
|
||||
///
|
||||
/// Getting suspend registers error
|
||||
///
|
||||
#[error("Failed to get suspend registers: {0}")]
|
||||
GetSuspendRegs(#[source] anyhow::Error),
|
||||
///
|
||||
/// Setting special register error
|
||||
///
|
||||
#[error("Failed to set special registers: {0}")]
|
||||
@@ -248,6 +232,12 @@ pub enum HypervisorCpuError {
|
||||
#[cfg(feature = "tdx")]
|
||||
#[error("Unknown TDX VM call")]
|
||||
UnknownTdxVmCall,
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
///
|
||||
/// Failed to intialize PMU
|
||||
///
|
||||
#[error("Failed to initialize PMU")]
|
||||
InitializePmu,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -278,22 +268,10 @@ pub type Result<T> = anyhow::Result<T, HypervisorCpuError>;
|
||||
/// Trait to represent a generic Vcpu
|
||||
///
|
||||
pub trait Vcpu: Send + Sync {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Returns the vCPU general purpose registers.
|
||||
///
|
||||
fn get_regs(&self) -> Result<StandardRegisters>;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
///
|
||||
/// Sets vcpu attribute
|
||||
///
|
||||
fn set_vcpu_attr(&self, attr: &DeviceAttr) -> Result<()>;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
///
|
||||
/// Check if vcpu has attribute.
|
||||
///
|
||||
fn has_vcpu_attr(&self, attr: &DeviceAttr) -> Result<()>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the vCPU general purpose registers.
|
||||
///
|
||||
@@ -322,7 +300,7 @@ pub trait Vcpu: Send + Sync {
|
||||
///
|
||||
/// X86 specific call to setup the CPUID registers.
|
||||
///
|
||||
fn set_cpuid2(&self, cpuid: &CpuId) -> Result<()>;
|
||||
fn set_cpuid2(&self, cpuid: &[CpuIdEntry]) -> Result<()>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call to enable HyperV SynIC
|
||||
@@ -332,7 +310,7 @@ pub trait Vcpu: Send + Sync {
|
||||
///
|
||||
/// X86 specific call to retrieve the CPUID registers.
|
||||
///
|
||||
fn get_cpuid2(&self, num_entries: usize) -> Result<CpuId>;
|
||||
fn get_cpuid2(&self, num_entries: usize) -> Result<Vec<CpuIdEntry>>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Returns the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
|
||||
@@ -347,117 +325,67 @@ pub trait Vcpu: Send + Sync {
|
||||
///
|
||||
/// Returns the model-specific registers (MSR) for this vCPU.
|
||||
///
|
||||
fn get_msrs(&self, msrs: &mut MsrEntries) -> Result<usize>;
|
||||
fn get_msrs(&self, msrs: &mut Vec<MsrEntry>) -> Result<usize>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Setup the model-specific registers (MSR) for this vCPU.
|
||||
///
|
||||
fn set_msrs(&self, msrs: &MsrEntries) -> Result<usize>;
|
||||
#[cfg(feature = "kvm")]
|
||||
fn set_msrs(&self, msrs: &[MsrEntry]) -> Result<usize>;
|
||||
///
|
||||
/// Returns the vcpu's current "multiprocessing state".
|
||||
///
|
||||
fn get_mp_state(&self) -> Result<MpState>;
|
||||
#[cfg(feature = "kvm")]
|
||||
///
|
||||
/// Sets the vcpu's current "multiprocessing state".
|
||||
///
|
||||
fn set_mp_state(&self, mp_state: MpState) -> Result<()>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call that returns the vcpu's current "xsave struct".
|
||||
///
|
||||
fn get_xsave(&self) -> Result<Xsave>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call that sets the vcpu's current "xsave struct".
|
||||
///
|
||||
fn set_xsave(&self, xsave: &Xsave) -> Result<()>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call that returns the vcpu's current "xcrs".
|
||||
///
|
||||
fn get_xcrs(&self) -> Result<ExtendedControlRegisters>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call that sets the vcpu's current "xcrs".
|
||||
///
|
||||
fn set_xcrs(&self, xcrs: &ExtendedControlRegisters) -> Result<()>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Returns currently pending exceptions, interrupts, and NMIs as well as related
|
||||
/// states of the vcpu.
|
||||
///
|
||||
fn get_vcpu_events(&self) -> Result<VcpuEvents>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets pending exceptions, interrupts, and NMIs as well as related states
|
||||
/// of the vcpu.
|
||||
///
|
||||
fn set_vcpu_events(&self, events: &VcpuEvents) -> Result<()>;
|
||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||
///
|
||||
/// Let the guest know that it has been paused, which prevents from
|
||||
/// potential soft lockups when being resumed.
|
||||
///
|
||||
fn notify_guest_clock_paused(&self) -> Result<()>;
|
||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||
fn notify_guest_clock_paused(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets debug registers to set hardware breakpoints and/or enable single step.
|
||||
///
|
||||
fn set_guest_debug(&self, addrs: &[GuestAddress], singlestep: bool) -> Result<()>;
|
||||
fn set_guest_debug(&self, _addrs: &[GuestAddress], _singlestep: bool) -> Result<()> {
|
||||
Err(HypervisorCpuError::SetDebugRegs(anyhow!("unimplemented")))
|
||||
}
|
||||
///
|
||||
/// Sets the type of CPU to be exposed to the guest and optional features.
|
||||
///
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn vcpu_init(&self, kvi: &VcpuInit) -> Result<()>;
|
||||
///
|
||||
/// Sets the value of one register for this vCPU.
|
||||
///
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
fn set_reg(&self, reg_id: u64, data: u64) -> Result<()>;
|
||||
///
|
||||
/// Sets the value of one register for this vCPU.
|
||||
///
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
fn get_reg(&self, reg_id: u64) -> Result<u64>;
|
||||
///
|
||||
/// Gets a list of the guest registers that are supported for the
|
||||
/// KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
|
||||
///
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn get_reg_list(&self, reg_list: &mut RegList) -> Result<()>;
|
||||
///
|
||||
/// Save the state of the core registers.
|
||||
/// Gets the value of a system register
|
||||
///
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
fn core_registers(&self, state: &mut StandardRegisters) -> Result<()>;
|
||||
///
|
||||
/// Restore the state of the core registers.
|
||||
///
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
fn set_core_registers(&self, state: &StandardRegisters) -> Result<()>;
|
||||
///
|
||||
/// Save the state of the system registers.
|
||||
///
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
fn system_registers(&self, state: &mut Vec<Register>) -> Result<()>;
|
||||
///
|
||||
/// Restore the state of the system registers.
|
||||
///
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
fn set_system_registers(&self, state: &[Register]) -> Result<()>;
|
||||
///
|
||||
/// Read the MPIDR - Multiprocessor Affinity Register.
|
||||
///
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
fn read_mpidr(&self) -> Result<u64>;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn get_sys_reg(&self, sys_reg: u32) -> Result<u64>;
|
||||
///
|
||||
/// Configure core registers for a given CPU.
|
||||
///
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn setup_regs(&self, cpu_id: u8, boot_ip: u64, fdt_start: u64) -> Result<()>;
|
||||
///
|
||||
/// Check if the CPU supports PMU
|
||||
///
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn has_pmu_support(&self) -> bool;
|
||||
///
|
||||
/// Initialize PMU
|
||||
///
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn init_pmu(&self, irq: u32) -> Result<()>;
|
||||
///
|
||||
/// Retrieve the vCPU state.
|
||||
/// This function is necessary to snapshot the VM
|
||||
///
|
||||
@@ -481,16 +409,10 @@ pub trait Vcpu: Send + Sync {
|
||||
///
|
||||
#[cfg(feature = "tdx")]
|
||||
fn tdx_init(&self, hob_address: u64) -> Result<()>;
|
||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||
///
|
||||
/// Return suspend registers(explicit and intercept suspend registers)
|
||||
///
|
||||
fn get_suspend_regs(&self) -> Result<SuspendRegisters>;
|
||||
#[cfg(feature = "kvm")]
|
||||
///
|
||||
/// Set the "immediate_exit" state
|
||||
///
|
||||
fn set_immediate_exit(&self, exit: bool);
|
||||
fn set_immediate_exit(&self, _exit: bool) {}
|
||||
#[cfg(feature = "tdx")]
|
||||
///
|
||||
/// Returns the details about TDX exit reason
|
||||
@@ -501,4 +423,9 @@ pub trait Vcpu: Send + Sync {
|
||||
/// Set the status code for TDX exit
|
||||
///
|
||||
fn set_tdx_status(&mut self, status: TdxExitStatus);
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Return the list of initial MSR entries for a VCPU
|
||||
///
|
||||
fn boot_msr_entries(&self) -> Vec<MsrEntry>;
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
// Copyright 2020, ARM Limited
|
||||
//
|
||||
|
||||
use crate::DeviceAttr;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
@@ -28,20 +26,3 @@ pub enum HypervisorDeviceError {
|
||||
#[error("Failed to get device attribute: {0}")]
|
||||
GetDeviceAttribute(#[source] anyhow::Error),
|
||||
}
|
||||
|
||||
///
|
||||
/// Result type for returning from a function
|
||||
///
|
||||
pub type Result<T> = std::result::Result<T, HypervisorDeviceError>;
|
||||
|
||||
///
|
||||
/// Trait to represent a device
|
||||
///
|
||||
/// This crate provides a hypervisor-agnostic interfaces for device
|
||||
///
|
||||
pub trait Device: Send + Sync + AsRawFd {
|
||||
/// Set device attribute.
|
||||
fn set_device_attr(&self, attr: &DeviceAttr) -> Result<()>;
|
||||
/// Get device attribute.
|
||||
fn get_device_attr(&self, attr: &mut DeviceAttr) -> Result<()>;
|
||||
}
|
||||
|
||||
@@ -7,13 +7,12 @@
|
||||
// Copyright 2018-2019 CrowdStrike, Inc.
|
||||
//
|
||||
//
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::arch::x86::CpuIdEntry;
|
||||
#[cfg(feature = "tdx")]
|
||||
use crate::kvm::TdxCapabilities;
|
||||
use crate::vm::Vm;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::x86_64::CpuId;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::x86_64::MsrList;
|
||||
use crate::HypervisorType;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -21,6 +20,11 @@ use thiserror::Error;
|
||||
///
|
||||
///
|
||||
pub enum HypervisorError {
|
||||
///
|
||||
/// Hypervisor availability check error
|
||||
///
|
||||
#[error("Failed to check availability of the hypervisor: {0}")]
|
||||
HypervisorAvailableCheck(#[source] anyhow::Error),
|
||||
///
|
||||
/// hypervisor creation error
|
||||
///
|
||||
@@ -84,6 +88,10 @@ pub type Result<T> = std::result::Result<T, HypervisorError>;
|
||||
/// This crate provides a hypervisor-agnostic interfaces
|
||||
///
|
||||
pub trait Hypervisor: Send + Sync {
|
||||
///
|
||||
/// Returns the type of the hypervisor
|
||||
///
|
||||
fn hypervisor_type(&self) -> HypervisorType;
|
||||
///
|
||||
/// Create a Vm using the underlying hypervisor
|
||||
/// Return a hypervisor-agnostic Vm trait object
|
||||
@@ -100,18 +108,13 @@ pub trait Hypervisor: Send + Sync {
|
||||
///
|
||||
/// Get the supported CpuID
|
||||
///
|
||||
fn get_cpuid(&self) -> Result<CpuId>;
|
||||
fn get_cpuid(&self) -> Result<Vec<CpuIdEntry>>;
|
||||
///
|
||||
/// Check particular extensions if any
|
||||
///
|
||||
fn check_required_extensions(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Retrieve the list of MSRs supported by the hypervisor.
|
||||
///
|
||||
fn get_msr_list(&self) -> Result<MsrList>;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
///
|
||||
/// Retrieve AArch64 host maximum IPA size supported by KVM.
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::arch::aarch64::gic::{Error, Result};
|
||||
use crate::device::HypervisorDeviceError;
|
||||
use crate::kvm::kvm_bindings::{
|
||||
kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
|
||||
};
|
||||
use crate::Device;
|
||||
use std::sync::Arc;
|
||||
use kvm_ioctls::DeviceFd;
|
||||
|
||||
/*
|
||||
Distributor registers as detailed at page 456 from
|
||||
@@ -77,7 +77,7 @@ static VGIC_DIST_REGS: &[DistReg] = &[
|
||||
VGIC_DIST_REG!(GICD_IPRIORITYR, 8, 0),
|
||||
];
|
||||
|
||||
fn dist_attr_access(gic: &Arc<dyn Device>, offset: u32, val: &u32, set: bool) -> Result<()> {
|
||||
fn dist_attr_access(gic: &DeviceFd, offset: u32, val: &u32, set: bool) -> Result<()> {
|
||||
let mut gic_dist_attr = kvm_device_attr {
|
||||
group: KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
|
||||
attr: offset as u64,
|
||||
@@ -85,28 +85,30 @@ fn dist_attr_access(gic: &Arc<dyn Device>, offset: u32, val: &u32, set: bool) ->
|
||||
flags: 0,
|
||||
};
|
||||
if set {
|
||||
gic.set_device_attr(&gic_dist_attr)
|
||||
.map_err(Error::SetDeviceAttribute)?;
|
||||
gic.set_device_attr(&gic_dist_attr).map_err(|e| {
|
||||
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||
})?;
|
||||
} else {
|
||||
gic.get_device_attr(&mut gic_dist_attr)
|
||||
.map_err(Error::GetDeviceAttribute)?;
|
||||
gic.get_device_attr(&mut gic_dist_attr).map_err(|e| {
|
||||
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the distributor control register.
|
||||
pub fn read_ctlr(gic: &Arc<dyn Device>) -> Result<u32> {
|
||||
pub fn read_ctlr(gic: &DeviceFd) -> Result<u32> {
|
||||
let val: u32 = 0;
|
||||
dist_attr_access(gic, GICD_CTLR, &val, false)?;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
/// Set the distributor control register.
|
||||
pub fn write_ctlr(gic: &Arc<dyn Device>, val: u32) -> Result<()> {
|
||||
pub fn write_ctlr(gic: &DeviceFd, val: u32) -> Result<()> {
|
||||
dist_attr_access(gic, GICD_CTLR, &val, true)
|
||||
}
|
||||
|
||||
fn get_interrupts_num(gic: &Arc<dyn Device>) -> Result<u32> {
|
||||
fn get_interrupts_num(gic: &DeviceFd) -> Result<u32> {
|
||||
let num_irq = 0;
|
||||
|
||||
let mut nr_irqs_attr = kvm_device_attr {
|
||||
@@ -115,12 +117,13 @@ fn get_interrupts_num(gic: &Arc<dyn Device>) -> Result<u32> {
|
||||
addr: &num_irq as *const u32 as u64,
|
||||
flags: 0,
|
||||
};
|
||||
gic.get_device_attr(&mut nr_irqs_attr)
|
||||
.map_err(Error::GetDeviceAttribute)?;
|
||||
gic.get_device_attr(&mut nr_irqs_attr).map_err(|e| {
|
||||
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||
})?;
|
||||
Ok(num_irq)
|
||||
}
|
||||
|
||||
fn compute_reg_len(gic: &Arc<dyn Device>, reg: &DistReg, base: u32) -> Result<u32> {
|
||||
fn compute_reg_len(gic: &DeviceFd, reg: &DistReg, base: u32) -> Result<u32> {
|
||||
// FIXME:
|
||||
// Redefine some GIC constants to avoid the dependency on `layout` crate.
|
||||
// This is temporary solution, will be fixed in future refactoring.
|
||||
@@ -147,7 +150,7 @@ fn compute_reg_len(gic: &Arc<dyn Device>, reg: &DistReg, base: u32) -> Result<u3
|
||||
}
|
||||
|
||||
/// Set distributor registers of the GIC.
|
||||
pub fn set_dist_regs(gic: &Arc<dyn Device>, state: &[u32]) -> Result<()> {
|
||||
pub fn set_dist_regs(gic: &DeviceFd, state: &[u32]) -> Result<()> {
|
||||
let mut idx = 0;
|
||||
|
||||
for dreg in VGIC_DIST_REGS {
|
||||
@@ -164,7 +167,7 @@ pub fn set_dist_regs(gic: &Arc<dyn Device>, state: &[u32]) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
/// Get distributor registers of the GIC.
|
||||
pub fn get_dist_regs(gic: &Arc<dyn Device>) -> Result<Vec<u32>> {
|
||||
pub fn get_dist_regs(gic: &DeviceFd) -> Result<Vec<u32>> {
|
||||
let mut state = Vec::new();
|
||||
|
||||
for dreg in VGIC_DIST_REGS {
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::arch::aarch64::gic::{Error, Result};
|
||||
use crate::device::HypervisorDeviceError;
|
||||
use crate::kvm::kvm_bindings::{
|
||||
kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, KVM_REG_ARM64_SYSREG_CRM_MASK,
|
||||
KVM_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRN_MASK, KVM_REG_ARM64_SYSREG_CRN_SHIFT,
|
||||
KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP1_MASK,
|
||||
KVM_REG_ARM64_SYSREG_OP1_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_SHIFT,
|
||||
};
|
||||
use crate::Device;
|
||||
use std::sync::Arc;
|
||||
use kvm_ioctls::DeviceFd;
|
||||
|
||||
const KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT: u32 = 32;
|
||||
const KVM_DEV_ARM_VGIC_V3_MPIDR_MASK: u64 = 0xffffffff << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT as u64;
|
||||
@@ -79,13 +79,7 @@ static VGIC_ICC_REGS: &[u64] = &[
|
||||
SYS_ICC_AP1R3_EL1,
|
||||
];
|
||||
|
||||
fn icc_attr_access(
|
||||
gic: &Arc<dyn Device>,
|
||||
offset: u64,
|
||||
typer: u64,
|
||||
val: &u32,
|
||||
set: bool,
|
||||
) -> Result<()> {
|
||||
fn icc_attr_access(gic: &DeviceFd, offset: u64, typer: u64, val: &u32, set: bool) -> Result<()> {
|
||||
let mut gic_icc_attr = kvm_device_attr {
|
||||
group: KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
|
||||
attr: ((typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | offset), // this needs the mpidr
|
||||
@@ -93,17 +87,19 @@ fn icc_attr_access(
|
||||
flags: 0,
|
||||
};
|
||||
if set {
|
||||
gic.set_device_attr(&gic_icc_attr)
|
||||
.map_err(Error::SetDeviceAttribute)?;
|
||||
gic.set_device_attr(&gic_icc_attr).map_err(|e| {
|
||||
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||
})?;
|
||||
} else {
|
||||
gic.get_device_attr(&mut gic_icc_attr)
|
||||
.map_err(Error::GetDeviceAttribute)?;
|
||||
gic.get_device_attr(&mut gic_icc_attr).map_err(|e| {
|
||||
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get ICC registers.
|
||||
pub fn get_icc_regs(gic: &Arc<dyn Device>, gicr_typer: &[u64]) -> Result<Vec<u32>> {
|
||||
pub fn get_icc_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
|
||||
let mut state: Vec<u32> = Vec::new();
|
||||
// We need this for the ICC_AP<m>R<n>_EL1 registers.
|
||||
let mut num_priority_bits = 0;
|
||||
@@ -156,7 +152,7 @@ pub fn get_icc_regs(gic: &Arc<dyn Device>, gicr_typer: &[u64]) -> Result<Vec<u32
|
||||
}
|
||||
|
||||
/// Set ICC registers.
|
||||
pub fn set_icc_regs(gic: &Arc<dyn Device>, gicr_typer: &[u64], state: &[u32]) -> Result<()> {
|
||||
pub fn set_icc_regs(gic: &DeviceFd, gicr_typer: &[u64], state: &[u32]) -> Result<()> {
|
||||
let mut num_priority_bits = 0;
|
||||
let mut idx = 0;
|
||||
for ix in gicr_typer {
|
||||
|
||||
@@ -5,15 +5,16 @@ mod icc_regs;
|
||||
mod redist_regs;
|
||||
|
||||
use crate::arch::aarch64::gic::{Error, Result, Vgic};
|
||||
use crate::kvm::kvm_bindings;
|
||||
use crate::{CpuState, Device, Vm};
|
||||
use crate::device::HypervisorDeviceError;
|
||||
use crate::kvm::{kvm_bindings, KvmVm};
|
||||
use crate::{CpuState, Vm};
|
||||
use dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
||||
use icc_regs::{get_icc_regs, set_icc_regs};
|
||||
use kvm_ioctls::DeviceFd;
|
||||
use redist_regs::{construct_gicr_typers, get_redist_regs, set_redist_regs};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::any::Any;
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
|
||||
const GITS_CTLR: u32 = 0x0000;
|
||||
const GITS_IIDR: u32 = 0x0004;
|
||||
@@ -27,7 +28,7 @@ const GITS_BASER: u32 = 0x0100;
|
||||
/// This is a helper function to get/set the ITS device attribute depending
|
||||
/// the bool parameter `set` provided.
|
||||
pub fn gicv3_its_attr_access(
|
||||
its_device: &Arc<dyn Device>,
|
||||
its_device: &DeviceFd,
|
||||
group: u32,
|
||||
attr: u32,
|
||||
val: &u64,
|
||||
@@ -40,20 +41,22 @@ pub fn gicv3_its_attr_access(
|
||||
flags: 0,
|
||||
};
|
||||
if set {
|
||||
its_device
|
||||
.set_device_attr(&gicv3_its_attr)
|
||||
.map_err(Error::SetDeviceAttribute)
|
||||
its_device.set_device_attr(&gicv3_its_attr).map_err(|e| {
|
||||
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||
})
|
||||
} else {
|
||||
its_device
|
||||
.get_device_attr(&mut gicv3_its_attr)
|
||||
.map_err(Error::GetDeviceAttribute)
|
||||
.map_err(|e| {
|
||||
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Function that saves/restores ITS tables into guest RAM.
|
||||
///
|
||||
/// The tables get flushed to guest RAM whenever the VM gets stopped.
|
||||
pub fn gicv3_its_tables_access(its_device: &Arc<dyn Device>, save: bool) -> Result<()> {
|
||||
pub fn gicv3_its_tables_access(its_device: &DeviceFd, save: bool) -> Result<()> {
|
||||
let attr = if save {
|
||||
u64::from(kvm_bindings::KVM_DEV_ARM_ITS_SAVE_TABLES)
|
||||
} else {
|
||||
@@ -68,15 +71,15 @@ pub fn gicv3_its_tables_access(its_device: &Arc<dyn Device>, save: bool) -> Resu
|
||||
};
|
||||
its_device
|
||||
.set_device_attr(&init_gic_attr)
|
||||
.map_err(Error::SetDeviceAttribute)
|
||||
.map_err(|e| Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into())))
|
||||
}
|
||||
|
||||
pub struct KvmGicV3Its {
|
||||
/// The hypervisor agnostic device for the GicV3
|
||||
device: Arc<dyn Device>,
|
||||
/// The KVM device for the GicV3
|
||||
device: DeviceFd,
|
||||
|
||||
/// The hypervisor agnostic device for the Its device
|
||||
its_device: Option<Arc<dyn Device>>,
|
||||
/// The KVM device for the Its device
|
||||
its_device: Option<DeviceFd>,
|
||||
|
||||
/// Vector holding values of GICR_TYPER for each vCPU
|
||||
gicr_typers: Vec<u64>,
|
||||
@@ -127,22 +130,14 @@ impl KvmGicV3Its {
|
||||
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3
|
||||
}
|
||||
|
||||
fn device(&self) -> &Arc<dyn Device> {
|
||||
&self.device
|
||||
}
|
||||
|
||||
fn its_device(&self) -> Option<&Arc<dyn Device>> {
|
||||
self.its_device.as_ref()
|
||||
}
|
||||
|
||||
/// Setup the device-specific attributes
|
||||
fn init_device_attributes(&mut self, vm: &dyn Vm, nr_irqs: u32) -> Result<()> {
|
||||
fn init_device_attributes(&mut self, vm: &KvmVm, nr_irqs: u32) -> Result<()> {
|
||||
// GicV3 part attributes
|
||||
/* Setting up the distributor attribute.
|
||||
We are placing the GIC below 1GB so we need to substract the size of the distributor.
|
||||
*/
|
||||
Self::set_device_attribute(
|
||||
self.device(),
|
||||
&self.device,
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST),
|
||||
&self.dist_addr as *const u64 as u64,
|
||||
@@ -153,7 +148,7 @@ impl KvmGicV3Its {
|
||||
We are calculating here the start of the redistributors address. We have one per CPU.
|
||||
*/
|
||||
Self::set_device_attribute(
|
||||
self.device(),
|
||||
&self.device,
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST),
|
||||
&self.redists_addr as *const u64 as u64,
|
||||
@@ -171,6 +166,9 @@ impl KvmGicV3Its {
|
||||
.create_device(&mut its_device)
|
||||
.map_err(Error::CreateGic)?;
|
||||
|
||||
// We know vm is KvmVm
|
||||
let its_fd = its_fd.to_kvm().unwrap();
|
||||
|
||||
Self::set_device_attribute(
|
||||
&its_fd,
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
@@ -187,14 +185,14 @@ impl KvmGicV3Its {
|
||||
0,
|
||||
)?;
|
||||
|
||||
self.set_its_device(Some(its_fd));
|
||||
self.its_device = Some(its_fd);
|
||||
|
||||
/* We need to tell the kernel how many irqs to support with this vgic.
|
||||
* See the `layout` module for details.
|
||||
*/
|
||||
let nr_irqs_ptr = &nr_irqs as *const u32;
|
||||
Self::set_device_attribute(
|
||||
self.device(),
|
||||
&self.device,
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
|
||||
0,
|
||||
nr_irqs_ptr as u64,
|
||||
@@ -205,7 +203,7 @@ impl KvmGicV3Its {
|
||||
* See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
|
||||
*/
|
||||
Self::set_device_attribute(
|
||||
self.device(),
|
||||
&self.device,
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
|
||||
0,
|
||||
@@ -214,19 +212,24 @@ impl KvmGicV3Its {
|
||||
}
|
||||
|
||||
/// Create a KVM Vgic device
|
||||
fn create_device(vm: &dyn Vm) -> Result<Arc<dyn Device>> {
|
||||
fn create_device(vm: &KvmVm) -> Result<DeviceFd> {
|
||||
let mut gic_device = kvm_bindings::kvm_create_device {
|
||||
type_: Self::version(),
|
||||
fd: 0,
|
||||
flags: 0,
|
||||
};
|
||||
|
||||
vm.create_device(&mut gic_device).map_err(Error::CreateGic)
|
||||
let device_fd = vm
|
||||
.create_device(&mut gic_device)
|
||||
.map_err(Error::CreateGic)?;
|
||||
|
||||
// We know for sure this is a KVM fd
|
||||
Ok(device_fd.to_kvm().unwrap())
|
||||
}
|
||||
|
||||
/// Set a GIC device attribute
|
||||
fn set_device_attribute(
|
||||
device: &Arc<dyn Device>,
|
||||
device: &DeviceFd,
|
||||
group: u32,
|
||||
attr: u64,
|
||||
addr: u64,
|
||||
@@ -238,9 +241,9 @@ impl KvmGicV3Its {
|
||||
attr,
|
||||
addr,
|
||||
};
|
||||
device
|
||||
.set_device_attr(&attr)
|
||||
.map_err(Error::SetDeviceAttribute)
|
||||
device.set_device_attr(&attr).map_err(|e| {
|
||||
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||
})
|
||||
}
|
||||
|
||||
/// Method to initialize the GIC device
|
||||
@@ -254,6 +257,9 @@ impl KvmGicV3Its {
|
||||
msi_size: u64,
|
||||
nr_irqs: u32,
|
||||
) -> Result<KvmGicV3Its> {
|
||||
// This is inside KVM module
|
||||
let vm = vm.as_any().downcast_ref::<KvmVm>().expect("Wrong VM type?");
|
||||
|
||||
let vgic = Self::create_device(vm)?;
|
||||
let redists_size: u64 = redist_size * vcpu_count;
|
||||
let redists_addr: u64 = dist_addr - redists_size;
|
||||
@@ -312,10 +318,6 @@ impl Vgic for KvmGicV3Its {
|
||||
[self.msi_addr, self.msi_size]
|
||||
}
|
||||
|
||||
fn set_its_device(&mut self, its_device: Option<Arc<dyn Device>>) {
|
||||
self.its_device = its_device;
|
||||
}
|
||||
|
||||
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
|
||||
let gicr_typers = construct_gicr_typers(vcpu_states);
|
||||
self.gicr_typers = gicr_typers;
|
||||
@@ -329,18 +331,18 @@ impl Vgic for KvmGicV3Its {
|
||||
fn state(&self) -> Result<Gicv3ItsState> {
|
||||
let gicr_typers = self.gicr_typers.clone();
|
||||
|
||||
let gicd_ctlr = read_ctlr(self.device())?;
|
||||
let gicd_ctlr = read_ctlr(&self.device)?;
|
||||
|
||||
let dist_state = get_dist_regs(self.device())?;
|
||||
let dist_state = get_dist_regs(&self.device)?;
|
||||
|
||||
let rdist_state = get_redist_regs(self.device(), &gicr_typers)?;
|
||||
let rdist_state = get_redist_regs(&self.device, &gicr_typers)?;
|
||||
|
||||
let icc_state = get_icc_regs(self.device(), &gicr_typers)?;
|
||||
let icc_state = get_icc_regs(&self.device, &gicr_typers)?;
|
||||
|
||||
let its_baser_state: [u64; 8] = [0; 8];
|
||||
for i in 0..8 {
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_BASER + i * 8,
|
||||
&its_baser_state[i as usize],
|
||||
@@ -350,7 +352,7 @@ impl Vgic for KvmGicV3Its {
|
||||
|
||||
let its_ctlr_state: u64 = 0;
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CTLR,
|
||||
&its_ctlr_state,
|
||||
@@ -359,7 +361,7 @@ impl Vgic for KvmGicV3Its {
|
||||
|
||||
let its_cbaser_state: u64 = 0;
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CBASER,
|
||||
&its_cbaser_state,
|
||||
@@ -368,7 +370,7 @@ impl Vgic for KvmGicV3Its {
|
||||
|
||||
let its_creadr_state: u64 = 0;
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CREADR,
|
||||
&its_creadr_state,
|
||||
@@ -377,7 +379,7 @@ impl Vgic for KvmGicV3Its {
|
||||
|
||||
let its_cwriter_state: u64 = 0;
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CWRITER,
|
||||
&its_cwriter_state,
|
||||
@@ -386,7 +388,7 @@ impl Vgic for KvmGicV3Its {
|
||||
|
||||
let its_iidr_state: u64 = 0;
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_IIDR,
|
||||
&its_iidr_state,
|
||||
@@ -411,17 +413,17 @@ impl Vgic for KvmGicV3Its {
|
||||
fn set_state(&mut self, state: &Gicv3ItsState) -> Result<()> {
|
||||
let gicr_typers = self.gicr_typers.clone();
|
||||
|
||||
write_ctlr(self.device(), state.gicd_ctlr)?;
|
||||
write_ctlr(&self.device, state.gicd_ctlr)?;
|
||||
|
||||
set_dist_regs(self.device(), &state.dist)?;
|
||||
set_dist_regs(&self.device, &state.dist)?;
|
||||
|
||||
set_redist_regs(self.device(), &gicr_typers, &state.rdist)?;
|
||||
set_redist_regs(&self.device, &gicr_typers, &state.rdist)?;
|
||||
|
||||
set_icc_regs(self.device(), &gicr_typers, &state.icc)?;
|
||||
set_icc_regs(&self.device, &gicr_typers, &state.icc)?;
|
||||
|
||||
//Restore GICv3ITS registers
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_IIDR,
|
||||
&state.its_iidr,
|
||||
@@ -429,7 +431,7 @@ impl Vgic for KvmGicV3Its {
|
||||
)?;
|
||||
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CBASER,
|
||||
&state.its_cbaser,
|
||||
@@ -437,7 +439,7 @@ impl Vgic for KvmGicV3Its {
|
||||
)?;
|
||||
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CREADR,
|
||||
&state.its_creadr,
|
||||
@@ -445,7 +447,7 @@ impl Vgic for KvmGicV3Its {
|
||||
)?;
|
||||
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CWRITER,
|
||||
&state.its_cwriter,
|
||||
@@ -454,7 +456,7 @@ impl Vgic for KvmGicV3Its {
|
||||
|
||||
for i in 0..8 {
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_BASER + i * 8,
|
||||
&state.its_baser[i as usize],
|
||||
@@ -463,10 +465,10 @@ impl Vgic for KvmGicV3Its {
|
||||
}
|
||||
|
||||
// Restore ITS tables
|
||||
gicv3_its_tables_access(self.its_device().unwrap(), false)?;
|
||||
gicv3_its_tables_access(self.its_device.as_ref().unwrap(), false)?;
|
||||
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CTLR,
|
||||
&state.its_ctlr,
|
||||
@@ -485,11 +487,11 @@ impl Vgic for KvmGicV3Its {
|
||||
addr: 0,
|
||||
flags: 0,
|
||||
};
|
||||
self.device()
|
||||
.set_device_attr(&init_gic_attr)
|
||||
.map_err(Error::SetDeviceAttribute)?;
|
||||
self.device.set_device_attr(&init_gic_attr).map_err(|e| {
|
||||
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||
})?;
|
||||
// Flush ITS tables to guest RAM.
|
||||
gicv3_its_tables_access(self.its_device().unwrap(), true)
|
||||
gicv3_its_tables_access(self.its_device.as_ref().unwrap(), true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,12 +535,12 @@ mod tests {
|
||||
)
|
||||
.expect("Cannot create gic");
|
||||
|
||||
let res = get_dist_regs(gic.device());
|
||||
let res = get_dist_regs(&gic.device);
|
||||
assert!(res.is_ok());
|
||||
let state = res.unwrap();
|
||||
assert_eq!(state.len(), 568);
|
||||
|
||||
let res = set_dist_regs(gic.device(), &state);
|
||||
let res = set_dist_regs(&gic.device, &state);
|
||||
assert!(res.is_ok());
|
||||
}
|
||||
|
||||
@@ -559,13 +561,13 @@ mod tests {
|
||||
.expect("Cannot create gic");
|
||||
|
||||
let gicr_typer = vec![123];
|
||||
let res = get_redist_regs(gic.device(), &gicr_typer);
|
||||
let res = get_redist_regs(&gic.device, &gicr_typer);
|
||||
assert!(res.is_ok());
|
||||
let state = res.unwrap();
|
||||
println!("{}", state.len());
|
||||
assert!(state.len() == 24);
|
||||
|
||||
assert!(set_redist_regs(gic.device(), &gicr_typer, &state).is_ok());
|
||||
assert!(set_redist_regs(&gic.device, &gicr_typer, &state).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -585,13 +587,13 @@ mod tests {
|
||||
.expect("Cannot create gic");
|
||||
|
||||
let gicr_typer = vec![123];
|
||||
let res = get_icc_regs(gic.device(), &gicr_typer);
|
||||
let res = get_icc_regs(&gic.device, &gicr_typer);
|
||||
assert!(res.is_ok());
|
||||
let state = res.unwrap();
|
||||
println!("{}", state.len());
|
||||
assert!(state.len() == 9);
|
||||
|
||||
assert!(set_icc_regs(gic.device(), &gicr_typer, &state).is_ok());
|
||||
assert!(set_icc_regs(&gic.device, &gicr_typer, &state).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -3,9 +3,16 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::arch::aarch64::gic::{Error, Result};
|
||||
use crate::kvm::kvm_bindings::{kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS};
|
||||
use crate::{CpuState, Device};
|
||||
use std::sync::Arc;
|
||||
use crate::device::HypervisorDeviceError;
|
||||
use crate::kvm::kvm_bindings::{
|
||||
kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, KVM_REG_ARM64, KVM_REG_ARM64_SYSREG,
|
||||
KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK,
|
||||
KVM_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_SIZE_U64,
|
||||
};
|
||||
use crate::kvm::Register;
|
||||
use crate::kvm::VcpuKvmState;
|
||||
use crate::CpuState;
|
||||
use kvm_ioctls::DeviceFd;
|
||||
|
||||
// Relevant redistributor registers that we want to save/restore.
|
||||
const GICR_CTLR: u32 = 0x0000;
|
||||
@@ -32,6 +39,12 @@ const GICR_ICFGR0: u32 = GICR_SGI_OFFSET + 0x0C00;
|
||||
const KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT: u32 = 32;
|
||||
const KVM_DEV_ARM_VGIC_V3_MPIDR_MASK: u64 = 0xffffffff << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT as u64;
|
||||
|
||||
const KVM_ARM64_SYSREG_MPIDR_EL1: u64 = KVM_REG_ARM64 as u64
|
||||
| KVM_REG_SIZE_U64 as u64
|
||||
| KVM_REG_ARM64_SYSREG as u64
|
||||
| (((3_u64) << KVM_REG_ARM64_SYSREG_OP0_SHIFT) & KVM_REG_ARM64_SYSREG_OP0_MASK as u64)
|
||||
| (((5_u64) << KVM_REG_ARM64_SYSREG_OP2_SHIFT) & KVM_REG_ARM64_SYSREG_OP2_MASK as u64);
|
||||
|
||||
/// This is how we represent the registers of a distributor.
|
||||
/// It is relrvant their offset from the base address of the
|
||||
/// distributor.
|
||||
@@ -83,31 +96,27 @@ static VGIC_SGI_REGS: &[RdistReg] = &[
|
||||
VGIC_RDIST_REG!(GICR_IPRIORITYR0, 32),
|
||||
];
|
||||
|
||||
fn redist_attr_access(
|
||||
gic: &Arc<dyn Device>,
|
||||
offset: u32,
|
||||
typer: u64,
|
||||
val: &u32,
|
||||
set: bool,
|
||||
) -> Result<()> {
|
||||
let mut gic_dist_attr = kvm_device_attr {
|
||||
fn redist_attr_access(gic: &DeviceFd, offset: u32, typer: u64, val: &u32, set: bool) -> Result<()> {
|
||||
let mut gic_redist_attr = kvm_device_attr {
|
||||
group: KVM_DEV_ARM_VGIC_GRP_REDIST_REGS,
|
||||
attr: (typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | (offset as u64), // this needs the mpidr
|
||||
addr: val as *const u32 as u64,
|
||||
flags: 0,
|
||||
};
|
||||
if set {
|
||||
gic.set_device_attr(&gic_dist_attr)
|
||||
.map_err(Error::SetDeviceAttribute)?;
|
||||
gic.set_device_attr(&gic_redist_attr).map_err(|e| {
|
||||
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||
})?;
|
||||
} else {
|
||||
gic.get_device_attr(&mut gic_dist_attr)
|
||||
.map_err(Error::GetDeviceAttribute)?;
|
||||
gic.get_device_attr(&mut gic_redist_attr).map_err(|e| {
|
||||
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn access_redists_aux(
|
||||
gic: &Arc<dyn Device>,
|
||||
gic: &DeviceFd,
|
||||
gicr_typer: &[u64],
|
||||
state: &mut Vec<u32>,
|
||||
reg_list: &[RdistReg],
|
||||
@@ -137,7 +146,7 @@ fn access_redists_aux(
|
||||
}
|
||||
|
||||
/// Get redistributor registers.
|
||||
pub fn get_redist_regs(gic: &Arc<dyn Device>, gicr_typer: &[u64]) -> Result<Vec<u32>> {
|
||||
pub fn get_redist_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
|
||||
let mut state = Vec::new();
|
||||
let mut idx: usize = 0;
|
||||
access_redists_aux(
|
||||
@@ -154,7 +163,7 @@ pub fn get_redist_regs(gic: &Arc<dyn Device>, gicr_typer: &[u64]) -> Result<Vec<
|
||||
}
|
||||
|
||||
/// Set redistributor registers.
|
||||
pub fn set_redist_regs(gic: &Arc<dyn Device>, gicr_typer: &[u64], state: &[u32]) -> Result<()> {
|
||||
pub fn set_redist_regs(gic: &DeviceFd, gicr_typer: &[u64], state: &[u32]) -> Result<()> {
|
||||
let mut idx: usize = 0;
|
||||
let mut mut_state = state.to_owned();
|
||||
access_redists_aux(
|
||||
@@ -190,6 +199,7 @@ pub fn construct_gicr_typers(vcpu_states: &[CpuState]) -> Vec<u64> {
|
||||
*/
|
||||
let mut gicr_typers: Vec<u64> = Vec::new();
|
||||
for (index, state) in vcpu_states.iter().enumerate() {
|
||||
let state: VcpuKvmState = state.clone().into();
|
||||
let last = {
|
||||
if index == vcpu_states.len() - 1 {
|
||||
1
|
||||
@@ -197,8 +207,14 @@ pub fn construct_gicr_typers(vcpu_states: &[CpuState]) -> Vec<u64> {
|
||||
0
|
||||
}
|
||||
};
|
||||
// state.sys_regs is a big collection of system registers, including MIPDR_EL1
|
||||
let mpidr: Vec<Register> = state
|
||||
.sys_regs
|
||||
.into_iter()
|
||||
.filter(|reg| reg.id == KVM_ARM64_SYSREG_MPIDR_EL1)
|
||||
.collect();
|
||||
//calculate affinity
|
||||
let mut cpu_affid = state.mpidr & 1095233437695;
|
||||
let mut cpu_affid = mpidr[0].addr & 1095233437695;
|
||||
cpu_affid = ((cpu_affid & 0xFF00000000) >> 8) | (cpu_affid & 0xFFFFFF);
|
||||
gicr_typers.push((cpu_affid << 32) | (1 << 24) | (index as u64) << 8 | (last << 4));
|
||||
}
|
||||
|
||||
@@ -12,12 +12,8 @@ pub mod gic;
|
||||
|
||||
use crate::kvm::{KvmError, KvmResult};
|
||||
use kvm_bindings::{
|
||||
kvm_mp_state, kvm_one_reg, kvm_regs, KVM_REG_ARM64, KVM_REG_ARM64_SYSREG,
|
||||
KVM_REG_ARM64_SYSREG_CRM_MASK, KVM_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRN_MASK,
|
||||
KVM_REG_ARM64_SYSREG_CRN_SHIFT, KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT,
|
||||
KVM_REG_ARM64_SYSREG_OP1_MASK, KVM_REG_ARM64_SYSREG_OP1_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK,
|
||||
KVM_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_ARM_COPROC_MASK, KVM_REG_ARM_CORE, KVM_REG_SIZE_MASK,
|
||||
KVM_REG_SIZE_U32, KVM_REG_SIZE_U64,
|
||||
kvm_mp_state, kvm_one_reg, kvm_regs, KVM_REG_ARM_COPROC_MASK, KVM_REG_ARM_CORE,
|
||||
KVM_REG_SIZE_MASK, KVM_REG_SIZE_U32, KVM_REG_SIZE_U64,
|
||||
};
|
||||
pub use kvm_bindings::{
|
||||
kvm_one_reg as Register, kvm_regs as StandardRegisters, kvm_vcpu_init as VcpuInit, RegList,
|
||||
@@ -85,32 +81,6 @@ macro_rules! arm64_core_reg_id {
|
||||
};
|
||||
}
|
||||
|
||||
// This macro computes the ID of a specific ARM64 system register similar to how
|
||||
// the kernel C macro does.
|
||||
// https://elixir.bootlin.com/linux/v4.20.17/source/arch/arm64/include/uapi/asm/kvm.h#L203
|
||||
#[macro_export]
|
||||
macro_rules! arm64_sys_reg {
|
||||
($name: tt, $op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => {
|
||||
pub const $name: u64 = KVM_REG_ARM64 as u64
|
||||
| KVM_REG_SIZE_U64 as u64
|
||||
| KVM_REG_ARM64_SYSREG as u64
|
||||
| ((($op0 as u64) << KVM_REG_ARM64_SYSREG_OP0_SHIFT)
|
||||
& KVM_REG_ARM64_SYSREG_OP0_MASK as u64)
|
||||
| ((($op1 as u64) << KVM_REG_ARM64_SYSREG_OP1_SHIFT)
|
||||
& KVM_REG_ARM64_SYSREG_OP1_MASK as u64)
|
||||
| ((($crn as u64) << KVM_REG_ARM64_SYSREG_CRN_SHIFT)
|
||||
& KVM_REG_ARM64_SYSREG_CRN_MASK as u64)
|
||||
| ((($crm as u64) << KVM_REG_ARM64_SYSREG_CRM_SHIFT)
|
||||
& KVM_REG_ARM64_SYSREG_CRM_MASK as u64)
|
||||
| ((($op2 as u64) << KVM_REG_ARM64_SYSREG_OP2_SHIFT)
|
||||
& KVM_REG_ARM64_SYSREG_OP2_MASK as u64);
|
||||
};
|
||||
}
|
||||
|
||||
// Constant imported from the Linux kernel:
|
||||
// https://elixir.bootlin.com/linux/v4.20.17/source/arch/arm64/include/asm/sysreg.h#L135
|
||||
arm64_sys_reg!(MPIDR_EL1, 3, 0, 0, 0, 5);
|
||||
|
||||
/// Specifies whether a particular register is a system register or not.
|
||||
/// The kernel splits the registers on aarch64 in core registers and system registers.
|
||||
/// So, below we get the system registers by checking that they are not core registers.
|
||||
@@ -149,8 +119,4 @@ pub struct VcpuKvmState {
|
||||
pub mp_state: kvm_mp_state,
|
||||
pub core_regs: kvm_regs,
|
||||
pub sys_regs: Vec<kvm_one_reg>,
|
||||
// We will be using the mpidr for passing it to the VmState.
|
||||
// The VmState will give this away for saving restoring the icc and redistributor
|
||||
// registers.
|
||||
pub mpidr: u64,
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,10 @@
|
||||
//
|
||||
//
|
||||
|
||||
use crate::arch::x86::{msr_index, SegmentRegisterOps, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
||||
use crate::arch::x86::{
|
||||
CpuIdEntry, DescriptorTable, FpuState, LapicState, MsrEntry, SegmentRegister, SpecialRegisters,
|
||||
StandardRegisters, CPUID_FLAG_VALID_INDEX,
|
||||
};
|
||||
use crate::kvm::{Cap, Kvm, KvmError, KvmResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -16,101 +19,15 @@ use serde::{Deserialize, Serialize};
|
||||
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
|
||||
///
|
||||
pub use {
|
||||
kvm_bindings::kvm_cpuid_entry2 as CpuIdEntry, kvm_bindings::kvm_dtable as DescriptorTable,
|
||||
kvm_bindings::kvm_fpu as FpuState, kvm_bindings::kvm_lapic_state as LapicState,
|
||||
kvm_bindings::kvm_mp_state as MpState, kvm_bindings::kvm_msr_entry as MsrEntry,
|
||||
kvm_bindings::kvm_regs as StandardRegisters, kvm_bindings::kvm_segment as SegmentRegister,
|
||||
kvm_bindings::kvm_sregs as SpecialRegisters, kvm_bindings::kvm_vcpu_events as VcpuEvents,
|
||||
kvm_bindings::kvm_cpuid_entry2, kvm_bindings::kvm_dtable, kvm_bindings::kvm_fpu,
|
||||
kvm_bindings::kvm_lapic_state, kvm_bindings::kvm_mp_state as MpState,
|
||||
kvm_bindings::kvm_msr_entry, kvm_bindings::kvm_regs, kvm_bindings::kvm_segment,
|
||||
kvm_bindings::kvm_sregs, kvm_bindings::kvm_vcpu_events as VcpuEvents,
|
||||
kvm_bindings::kvm_xcrs as ExtendedControlRegisters, kvm_bindings::kvm_xsave as Xsave,
|
||||
kvm_bindings::CpuId, kvm_bindings::MsrList, kvm_bindings::Msrs as MsrEntries,
|
||||
kvm_bindings::KVM_CPUID_FLAG_SIGNIFCANT_INDEX as CPUID_FLAG_VALID_INDEX,
|
||||
kvm_bindings::KVM_CPUID_FLAG_SIGNIFCANT_INDEX,
|
||||
};
|
||||
|
||||
impl SegmentRegisterOps for SegmentRegister {
|
||||
fn segment_type(&self) -> u8 {
|
||||
self.type_
|
||||
}
|
||||
fn set_segment_type(&mut self, val: u8) {
|
||||
self.type_ = val;
|
||||
}
|
||||
|
||||
fn dpl(&self) -> u8 {
|
||||
self.dpl
|
||||
}
|
||||
|
||||
fn set_dpl(&mut self, val: u8) {
|
||||
self.dpl = val;
|
||||
}
|
||||
|
||||
fn present(&self) -> u8 {
|
||||
self.present
|
||||
}
|
||||
|
||||
fn set_present(&mut self, val: u8) {
|
||||
self.present = val;
|
||||
}
|
||||
|
||||
fn long(&self) -> u8 {
|
||||
self.l
|
||||
}
|
||||
|
||||
fn set_long(&mut self, val: u8) {
|
||||
self.l = val;
|
||||
}
|
||||
|
||||
fn avl(&self) -> u8 {
|
||||
self.avl
|
||||
}
|
||||
|
||||
fn set_avl(&mut self, val: u8) {
|
||||
self.avl = val;
|
||||
}
|
||||
|
||||
fn desc_type(&self) -> u8 {
|
||||
self.s
|
||||
}
|
||||
|
||||
fn set_desc_type(&mut self, val: u8) {
|
||||
self.s = val;
|
||||
}
|
||||
|
||||
fn granularity(&self) -> u8 {
|
||||
self.g
|
||||
}
|
||||
|
||||
fn set_granularity(&mut self, val: u8) {
|
||||
self.g = val;
|
||||
}
|
||||
|
||||
fn db(&self) -> u8 {
|
||||
self.db
|
||||
}
|
||||
|
||||
fn set_db(&mut self, val: u8) {
|
||||
self.db = val;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn boot_msr_entries() -> MsrEntries {
|
||||
MsrEntries::from_entries(&[
|
||||
msr!(msr_index::MSR_IA32_SYSENTER_CS),
|
||||
msr!(msr_index::MSR_IA32_SYSENTER_ESP),
|
||||
msr!(msr_index::MSR_IA32_SYSENTER_EIP),
|
||||
msr!(msr_index::MSR_STAR),
|
||||
msr!(msr_index::MSR_CSTAR),
|
||||
msr!(msr_index::MSR_LSTAR),
|
||||
msr!(msr_index::MSR_KERNEL_GS_BASE),
|
||||
msr!(msr_index::MSR_SYSCALL_MASK),
|
||||
msr!(msr_index::MSR_IA32_TSC),
|
||||
msr_data!(
|
||||
msr_index::MSR_IA32_MISC_ENABLE,
|
||||
msr_index::MSR_IA32_MISC_ENABLE_FAST_STRING as u64
|
||||
),
|
||||
msr_data!(msr_index::MSR_MTRRdefType, MTRR_ENABLE | MTRR_MEM_TYPE_WB),
|
||||
])
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
///
|
||||
/// Check KVM extension for Linux
|
||||
///
|
||||
@@ -137,14 +54,275 @@ pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
|
||||
}
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct VcpuKvmState {
|
||||
pub cpuid: CpuId,
|
||||
pub msrs: MsrEntries,
|
||||
pub cpuid: Vec<CpuIdEntry>,
|
||||
pub msrs: Vec<MsrEntry>,
|
||||
pub vcpu_events: VcpuEvents,
|
||||
pub regs: StandardRegisters,
|
||||
pub sregs: SpecialRegisters,
|
||||
pub regs: kvm_regs,
|
||||
pub sregs: kvm_sregs,
|
||||
pub fpu: FpuState,
|
||||
pub lapic_state: LapicState,
|
||||
pub xsave: Xsave,
|
||||
pub xcrs: ExtendedControlRegisters,
|
||||
pub mp_state: MpState,
|
||||
}
|
||||
|
||||
impl From<StandardRegisters> for kvm_regs {
|
||||
fn from(regs: StandardRegisters) -> Self {
|
||||
Self {
|
||||
rax: regs.rax,
|
||||
rbx: regs.rbx,
|
||||
rcx: regs.rcx,
|
||||
rdx: regs.rdx,
|
||||
rsi: regs.rsi,
|
||||
rdi: regs.rdi,
|
||||
rsp: regs.rsp,
|
||||
rbp: regs.rbp,
|
||||
r8: regs.r8,
|
||||
r9: regs.r9,
|
||||
r10: regs.r10,
|
||||
r11: regs.r11,
|
||||
r12: regs.r12,
|
||||
r13: regs.r13,
|
||||
r14: regs.r14,
|
||||
r15: regs.r15,
|
||||
rip: regs.rip,
|
||||
rflags: regs.rflags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<kvm_regs> for StandardRegisters {
|
||||
fn from(regs: kvm_regs) -> Self {
|
||||
Self {
|
||||
rax: regs.rax,
|
||||
rbx: regs.rbx,
|
||||
rcx: regs.rcx,
|
||||
rdx: regs.rdx,
|
||||
rsi: regs.rsi,
|
||||
rdi: regs.rdi,
|
||||
rsp: regs.rsp,
|
||||
rbp: regs.rbp,
|
||||
r8: regs.r8,
|
||||
r9: regs.r9,
|
||||
r10: regs.r10,
|
||||
r11: regs.r11,
|
||||
r12: regs.r12,
|
||||
r13: regs.r13,
|
||||
r14: regs.r14,
|
||||
r15: regs.r15,
|
||||
rip: regs.rip,
|
||||
rflags: regs.rflags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SegmentRegister> for kvm_segment {
|
||||
fn from(s: SegmentRegister) -> Self {
|
||||
Self {
|
||||
base: s.base,
|
||||
limit: s.limit,
|
||||
selector: s.selector,
|
||||
type_: s.type_,
|
||||
present: s.present,
|
||||
dpl: s.dpl,
|
||||
db: s.db,
|
||||
s: s.s,
|
||||
l: s.l,
|
||||
g: s.g,
|
||||
avl: s.avl,
|
||||
unusable: s.unusable,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<kvm_segment> for SegmentRegister {
|
||||
fn from(s: kvm_segment) -> Self {
|
||||
Self {
|
||||
base: s.base,
|
||||
limit: s.limit,
|
||||
selector: s.selector,
|
||||
type_: s.type_,
|
||||
present: s.present,
|
||||
dpl: s.dpl,
|
||||
db: s.db,
|
||||
s: s.s,
|
||||
l: s.l,
|
||||
g: s.g,
|
||||
avl: s.avl,
|
||||
unusable: s.unusable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DescriptorTable> for kvm_dtable {
|
||||
fn from(dt: DescriptorTable) -> Self {
|
||||
Self {
|
||||
base: dt.base,
|
||||
limit: dt.limit,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<kvm_dtable> for DescriptorTable {
|
||||
fn from(dt: kvm_dtable) -> Self {
|
||||
Self {
|
||||
base: dt.base,
|
||||
limit: dt.limit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SpecialRegisters> for kvm_sregs {
|
||||
fn from(s: SpecialRegisters) -> Self {
|
||||
Self {
|
||||
cs: s.cs.into(),
|
||||
ds: s.ds.into(),
|
||||
es: s.es.into(),
|
||||
fs: s.fs.into(),
|
||||
gs: s.gs.into(),
|
||||
ss: s.ss.into(),
|
||||
tr: s.tr.into(),
|
||||
ldt: s.ldt.into(),
|
||||
gdt: s.gdt.into(),
|
||||
idt: s.idt.into(),
|
||||
cr0: s.cr0,
|
||||
cr2: s.cr2,
|
||||
cr3: s.cr3,
|
||||
cr4: s.cr4,
|
||||
cr8: s.cr8,
|
||||
efer: s.efer,
|
||||
apic_base: s.apic_base,
|
||||
interrupt_bitmap: s.interrupt_bitmap,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<kvm_sregs> for SpecialRegisters {
|
||||
fn from(s: kvm_sregs) -> Self {
|
||||
Self {
|
||||
cs: s.cs.into(),
|
||||
ds: s.ds.into(),
|
||||
es: s.es.into(),
|
||||
fs: s.fs.into(),
|
||||
gs: s.gs.into(),
|
||||
ss: s.ss.into(),
|
||||
tr: s.tr.into(),
|
||||
ldt: s.ldt.into(),
|
||||
gdt: s.gdt.into(),
|
||||
idt: s.idt.into(),
|
||||
cr0: s.cr0,
|
||||
cr2: s.cr2,
|
||||
cr3: s.cr3,
|
||||
cr4: s.cr4,
|
||||
cr8: s.cr8,
|
||||
efer: s.efer,
|
||||
apic_base: s.apic_base,
|
||||
interrupt_bitmap: s.interrupt_bitmap,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CpuIdEntry> for kvm_cpuid_entry2 {
|
||||
fn from(e: CpuIdEntry) -> Self {
|
||||
let flags = if e.flags & CPUID_FLAG_VALID_INDEX != 0 {
|
||||
KVM_CPUID_FLAG_SIGNIFCANT_INDEX
|
||||
} else {
|
||||
0
|
||||
};
|
||||
Self {
|
||||
function: e.function,
|
||||
index: e.index,
|
||||
flags,
|
||||
eax: e.eax,
|
||||
ebx: e.ebx,
|
||||
ecx: e.ecx,
|
||||
edx: e.edx,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<kvm_cpuid_entry2> for CpuIdEntry {
|
||||
fn from(e: kvm_cpuid_entry2) -> Self {
|
||||
let flags = if e.flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX != 0 {
|
||||
CPUID_FLAG_VALID_INDEX
|
||||
} else {
|
||||
0
|
||||
};
|
||||
Self {
|
||||
function: e.function,
|
||||
index: e.index,
|
||||
flags,
|
||||
eax: e.eax,
|
||||
ebx: e.ebx,
|
||||
ecx: e.ecx,
|
||||
edx: e.edx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<kvm_fpu> for FpuState {
|
||||
fn from(s: kvm_fpu) -> Self {
|
||||
Self {
|
||||
fpr: s.fpr,
|
||||
fcw: s.fcw,
|
||||
fsw: s.fsw,
|
||||
ftwx: s.ftwx,
|
||||
last_opcode: s.last_opcode,
|
||||
last_ip: s.last_ip,
|
||||
last_dp: s.last_dp,
|
||||
xmm: s.xmm,
|
||||
mxcsr: s.mxcsr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FpuState> for kvm_fpu {
|
||||
fn from(s: FpuState) -> Self {
|
||||
Self {
|
||||
fpr: s.fpr,
|
||||
fcw: s.fcw,
|
||||
fsw: s.fsw,
|
||||
ftwx: s.ftwx,
|
||||
last_opcode: s.last_opcode,
|
||||
last_ip: s.last_ip,
|
||||
last_dp: s.last_dp,
|
||||
xmm: s.xmm,
|
||||
mxcsr: s.mxcsr,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LapicState> for kvm_lapic_state {
|
||||
fn from(s: LapicState) -> Self {
|
||||
Self { regs: s.regs }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<kvm_lapic_state> for LapicState {
|
||||
fn from(s: kvm_lapic_state) -> Self {
|
||||
Self { regs: s.regs }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<kvm_msr_entry> for MsrEntry {
|
||||
fn from(e: kvm_msr_entry) -> Self {
|
||||
Self {
|
||||
index: e.index,
|
||||
data: e.data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MsrEntry> for kvm_msr_entry {
|
||||
fn from(e: MsrEntry) -> Self {
|
||||
Self {
|
||||
index: e.index,
|
||||
data: e.data,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,40 +51,36 @@ mod cpu;
|
||||
mod device;
|
||||
|
||||
pub use cpu::{HypervisorCpuError, Vcpu, VmExit};
|
||||
pub use device::{Device, HypervisorDeviceError};
|
||||
pub use device::HypervisorDeviceError;
|
||||
pub use hypervisor::{Hypervisor, HypervisorError};
|
||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||
pub use kvm::x86_64;
|
||||
#[cfg(all(feature = "kvm", target_arch = "aarch64"))]
|
||||
pub use kvm::{aarch64, GicState};
|
||||
// Aliased types exposed from both hypervisors
|
||||
#[cfg(feature = "kvm")]
|
||||
pub use kvm::{
|
||||
ClockData, CpuState, CreateDevice, DeviceAttr, DeviceFd, IoEventAddress, IrqRoutingEntry,
|
||||
MemoryRegion, MpState, VcpuEvents, VcpuExit, VmState,
|
||||
};
|
||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||
pub use mshv::x86_64;
|
||||
// Aliased types exposed from both hypervisors
|
||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||
pub use mshv::{
|
||||
CpuState, CreateDevice, DeviceAttr, DeviceFd, IoEventAddress, IrqRoutingEntry, MemoryRegion,
|
||||
MpState, VcpuEvents, VcpuExit, VmState,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
pub use vm::{
|
||||
DataMatch, HypervisorVmError, InterruptSourceConfig, LegacyIrqSourceConfig, MsiIrqSourceConfig,
|
||||
Vm, VmOps,
|
||||
};
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum HypervisorType {
|
||||
Kvm,
|
||||
Mshv,
|
||||
}
|
||||
|
||||
pub fn new() -> std::result::Result<Arc<dyn Hypervisor>, HypervisorError> {
|
||||
#[cfg(feature = "kvm")]
|
||||
let hv = kvm::KvmHypervisor::new()?;
|
||||
if kvm::KvmHypervisor::is_available()? {
|
||||
return kvm::KvmHypervisor::new();
|
||||
}
|
||||
|
||||
#[cfg(feature = "mshv")]
|
||||
let hv = mshv::MshvHypervisor::new()?;
|
||||
if mshv::MshvHypervisor::is_available()? {
|
||||
return mshv::MshvHypervisor::new();
|
||||
}
|
||||
|
||||
Ok(Arc::new(hv))
|
||||
Err(HypervisorError::HypervisorCreate(anyhow!(
|
||||
"no supported hypervisor"
|
||||
)))
|
||||
}
|
||||
|
||||
// Returns a `Vec<T>` with a size in bytes at least as large as `size_in_bytes`.
|
||||
@@ -116,3 +112,75 @@ pub fn vec_with_array_field<T: Default, F>(count: usize) -> Vec<T> {
|
||||
let vec_size_bytes = size_of::<T>() + element_space;
|
||||
vec_with_size_in_bytes(vec_size_bytes)
|
||||
}
|
||||
|
||||
///
|
||||
/// User memory region structure
|
||||
///
|
||||
#[derive(Debug, Default, Eq, PartialEq)]
|
||||
pub struct UserMemoryRegion {
|
||||
pub slot: u32,
|
||||
pub guest_phys_addr: u64,
|
||||
pub memory_size: u64,
|
||||
pub userspace_addr: u64,
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
///
|
||||
/// Flags for user memory region
|
||||
///
|
||||
pub const USER_MEMORY_REGION_READ: u32 = 1;
|
||||
pub const USER_MEMORY_REGION_WRITE: u32 = 1 << 1;
|
||||
pub const USER_MEMORY_REGION_EXECUTE: u32 = 1 << 2;
|
||||
pub const USER_MEMORY_REGION_LOG_DIRTY: u32 = 1 << 3;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MpState {
|
||||
#[cfg(feature = "kvm")]
|
||||
Kvm(kvm_bindings::kvm_mp_state),
|
||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||
Mshv, /* MSHV does not supprt MpState yet */
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum IoEventAddress {
|
||||
Pio(u64),
|
||||
Mmio(u64),
|
||||
}
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum CpuState {
|
||||
#[cfg(feature = "kvm")]
|
||||
Kvm(kvm::VcpuKvmState),
|
||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||
Mshv(mshv::VcpuMshvState),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub enum ClockData {
|
||||
#[cfg(feature = "kvm")]
|
||||
Kvm(kvm_bindings::kvm_clock_data),
|
||||
#[cfg(feature = "mshv")]
|
||||
Mshv, /* MSHV does not supprt ClockData yet */
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
impl ClockData {
|
||||
pub fn reset_flags(&mut self) {
|
||||
match self {
|
||||
#[cfg(feature = "kvm")]
|
||||
ClockData::Kvm(s) => s.flags = 0,
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum IrqRoutingEntry {
|
||||
#[cfg(feature = "kvm")]
|
||||
Kvm(kvm_bindings::kvm_irq_routing_entry),
|
||||
#[cfg(feature = "mshv")]
|
||||
Mshv(mshv_bindings::mshv_msi_routing_entry),
|
||||
}
|
||||
|
||||
@@ -12,26 +12,35 @@ use crate::cpu::Vcpu;
|
||||
use crate::hypervisor;
|
||||
use crate::vec_with_array_field;
|
||||
use crate::vm::{self, InterruptSourceConfig, VmOps};
|
||||
use crate::HypervisorType;
|
||||
pub use mshv_bindings::*;
|
||||
pub use mshv_ioctls::IoEventAddress;
|
||||
use mshv_ioctls::{set_registers_64, Mshv, NoDatamatch, VcpuFd, VmFd};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use vfio_ioctls::VfioDeviceFd;
|
||||
use vm::DataMatch;
|
||||
// x86_64 dependencies
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub mod x86_64;
|
||||
use crate::device;
|
||||
use crate::{
|
||||
ClockData, CpuState, IoEventAddress, IrqRoutingEntry, MpState, UserMemoryRegion,
|
||||
USER_MEMORY_REGION_EXECUTE, USER_MEMORY_REGION_READ, USER_MEMORY_REGION_WRITE,
|
||||
};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use x86_64::VcpuMshvState as CpuState;
|
||||
pub use x86_64::VcpuMshvState;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use x86_64::*;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use std::fs::File;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::arch::x86::{
|
||||
CpuIdEntry, FpuState, LapicState, MsrEntry, SpecialRegisters, StandardRegisters,
|
||||
};
|
||||
|
||||
const DIRTY_BITMAP_CLEAR_DIRTY: u64 = 0x4;
|
||||
const DIRTY_BITMAP_SET_DIRTY: u64 = 0x8;
|
||||
@@ -41,18 +50,108 @@ const DIRTY_BITMAP_SET_DIRTY: u64 = 0x8;
|
||||
///
|
||||
pub use {
|
||||
mshv_bindings::mshv_create_device as CreateDevice,
|
||||
mshv_bindings::mshv_device_attr as DeviceAttr,
|
||||
mshv_bindings::mshv_msi_routing_entry as IrqRoutingEntry, mshv_ioctls::DeviceFd,
|
||||
mshv_bindings::mshv_device_attr as DeviceAttr, mshv_ioctls::DeviceFd,
|
||||
};
|
||||
|
||||
pub const PAGE_SHIFT: usize = 12;
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)]
|
||||
pub struct HvState {
|
||||
hypercall_page: u64,
|
||||
impl From<mshv_user_mem_region> for UserMemoryRegion {
|
||||
fn from(region: mshv_user_mem_region) -> Self {
|
||||
let mut flags: u32 = 0;
|
||||
if region.flags & HV_MAP_GPA_READABLE != 0 {
|
||||
flags |= USER_MEMORY_REGION_READ;
|
||||
}
|
||||
if region.flags & HV_MAP_GPA_WRITABLE != 0 {
|
||||
flags |= USER_MEMORY_REGION_WRITE;
|
||||
}
|
||||
if region.flags & HV_MAP_GPA_EXECUTABLE != 0 {
|
||||
flags |= USER_MEMORY_REGION_EXECUTE;
|
||||
}
|
||||
|
||||
UserMemoryRegion {
|
||||
guest_phys_addr: (region.guest_pfn << PAGE_SHIFT as u64)
|
||||
+ (region.userspace_addr & ((1 << PAGE_SHIFT) - 1)),
|
||||
memory_size: region.size,
|
||||
userspace_addr: region.userspace_addr,
|
||||
flags,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use HvState as VmState;
|
||||
impl From<UserMemoryRegion> for mshv_user_mem_region {
|
||||
fn from(region: UserMemoryRegion) -> Self {
|
||||
let mut flags: u32 = 0;
|
||||
if region.flags & USER_MEMORY_REGION_READ != 0 {
|
||||
flags |= HV_MAP_GPA_READABLE;
|
||||
}
|
||||
if region.flags & USER_MEMORY_REGION_WRITE != 0 {
|
||||
flags |= HV_MAP_GPA_WRITABLE;
|
||||
}
|
||||
if region.flags & USER_MEMORY_REGION_EXECUTE != 0 {
|
||||
flags |= HV_MAP_GPA_EXECUTABLE;
|
||||
}
|
||||
|
||||
mshv_user_mem_region {
|
||||
guest_pfn: region.guest_phys_addr >> PAGE_SHIFT,
|
||||
size: region.memory_size,
|
||||
userspace_addr: region.userspace_addr,
|
||||
flags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<mshv_ioctls::IoEventAddress> for IoEventAddress {
|
||||
fn from(a: mshv_ioctls::IoEventAddress) -> Self {
|
||||
match a {
|
||||
mshv_ioctls::IoEventAddress::Pio(x) => Self::Pio(x),
|
||||
mshv_ioctls::IoEventAddress::Mmio(x) => Self::Mmio(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IoEventAddress> for mshv_ioctls::IoEventAddress {
|
||||
fn from(a: IoEventAddress) -> Self {
|
||||
match a {
|
||||
IoEventAddress::Pio(x) => Self::Pio(x),
|
||||
IoEventAddress::Mmio(x) => Self::Mmio(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VcpuMshvState> for CpuState {
|
||||
fn from(s: VcpuMshvState) -> Self {
|
||||
CpuState::Mshv(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CpuState> for VcpuMshvState {
|
||||
fn from(s: CpuState) -> Self {
|
||||
match s {
|
||||
CpuState::Mshv(s) => s,
|
||||
/* Needed in case other hypervisors are enabled */
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => panic!("CpuState is not valid"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<mshv_msi_routing_entry> for IrqRoutingEntry {
|
||||
fn from(s: mshv_msi_routing_entry) -> Self {
|
||||
IrqRoutingEntry::Mshv(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IrqRoutingEntry> for mshv_msi_routing_entry {
|
||||
fn from(e: IrqRoutingEntry) -> Self {
|
||||
match e {
|
||||
IrqRoutingEntry::Mshv(e) => e,
|
||||
/* Needed in case other hypervisors are enabled */
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => panic!("IrqRoutingEntry is not valid"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MshvDirtyLogSlot {
|
||||
guest_pfn: u64,
|
||||
@@ -64,12 +163,35 @@ pub struct MshvHypervisor {
|
||||
mshv: Mshv,
|
||||
}
|
||||
|
||||
impl MshvHypervisor {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Retrieve the list of MSRs supported by MSHV.
|
||||
///
|
||||
fn get_msr_list(&self) -> hypervisor::Result<MsrList> {
|
||||
self.mshv
|
||||
.get_msr_index_list()
|
||||
.map_err(|e| hypervisor::HypervisorError::GetMsrList(e.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl MshvHypervisor {
|
||||
/// Create a hypervisor based on Mshv
|
||||
pub fn new() -> hypervisor::Result<MshvHypervisor> {
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new() -> hypervisor::Result<Arc<dyn hypervisor::Hypervisor>> {
|
||||
let mshv_obj =
|
||||
Mshv::new().map_err(|e| hypervisor::HypervisorError::HypervisorCreate(e.into()))?;
|
||||
Ok(MshvHypervisor { mshv: mshv_obj })
|
||||
Ok(Arc::new(MshvHypervisor { mshv: mshv_obj }))
|
||||
}
|
||||
/// Check if the hypervisor is available
|
||||
pub fn is_available() -> hypervisor::Result<bool> {
|
||||
match std::fs::metadata("/dev/mshv") {
|
||||
Ok(_) => Ok(true),
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(false),
|
||||
Err(err) => Err(hypervisor::HypervisorError::HypervisorAvailableCheck(
|
||||
err.into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Implementation of Hypervisor trait for Mshv
|
||||
@@ -81,6 +203,12 @@ impl MshvHypervisor {
|
||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||
///
|
||||
impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
///
|
||||
/// Returns the type of the hypervisor
|
||||
///
|
||||
fn hypervisor_type(&self) -> HypervisorType {
|
||||
HypervisorType::Mshv
|
||||
}
|
||||
/// Create a mshv vm object and return the object as Vm trait object
|
||||
/// Example
|
||||
/// # extern crate hypervisor;
|
||||
@@ -121,47 +249,38 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
|
||||
let msr_list = self.get_msr_list()?;
|
||||
let num_msrs = msr_list.as_fam_struct_ref().nmsrs as usize;
|
||||
let mut msrs = MsrEntries::new(num_msrs).unwrap();
|
||||
let mut msrs: Vec<MsrEntry> = vec![
|
||||
MsrEntry {
|
||||
..Default::default()
|
||||
};
|
||||
num_msrs
|
||||
];
|
||||
let indices = msr_list.as_slice();
|
||||
let msr_entries = msrs.as_mut_slice();
|
||||
for (pos, index) in indices.iter().enumerate() {
|
||||
msr_entries[pos].index = *index;
|
||||
msrs[pos].index = *index;
|
||||
}
|
||||
let vm_fd = Arc::new(fd);
|
||||
|
||||
Ok(Arc::new(MshvVm {
|
||||
fd: vm_fd,
|
||||
msrs,
|
||||
hv_state: hv_state_init(),
|
||||
vm_ops: None,
|
||||
dirty_log_slots: Arc::new(RwLock::new(HashMap::new())),
|
||||
}))
|
||||
}
|
||||
///
|
||||
/// Get the supported CpuID
|
||||
///
|
||||
fn get_cpuid(&self) -> hypervisor::Result<CpuId> {
|
||||
Ok(CpuId::new(1).unwrap())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Retrieve the list of MSRs supported by MSHV.
|
||||
///
|
||||
fn get_msr_list(&self) -> hypervisor::Result<MsrList> {
|
||||
self.mshv
|
||||
.get_msr_index_list()
|
||||
.map_err(|e| hypervisor::HypervisorError::GetMsrList(e.into()))
|
||||
fn get_cpuid(&self) -> hypervisor::Result<Vec<CpuIdEntry>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Vcpu struct for Microsoft Hypervisor
|
||||
pub struct MshvVcpu {
|
||||
fd: VcpuFd,
|
||||
vp_index: u8,
|
||||
cpuid: CpuId,
|
||||
msrs: MsrEntries,
|
||||
hv_state: Arc<RwLock<HvState>>, // Mshv State
|
||||
cpuid: Vec<CpuIdEntry>,
|
||||
msrs: Vec<MsrEntry>,
|
||||
vm_ops: Option<Arc<dyn vm::VmOps>>,
|
||||
}
|
||||
|
||||
@@ -181,17 +300,20 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
/// Returns the vCPU general purpose registers.
|
||||
///
|
||||
fn get_regs(&self) -> cpu::Result<StandardRegisters> {
|
||||
self.fd
|
||||
Ok(self
|
||||
.fd
|
||||
.get_regs()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetStandardRegs(e.into()))
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetStandardRegs(e.into()))?
|
||||
.into())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the vCPU general purpose registers.
|
||||
///
|
||||
fn set_regs(&self, regs: &StandardRegisters) -> cpu::Result<()> {
|
||||
let regs = (*regs).into();
|
||||
self.fd
|
||||
.set_regs(regs)
|
||||
.set_regs(®s)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetStandardRegs(e.into()))
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -199,17 +321,20 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
/// Returns the vCPU special registers.
|
||||
///
|
||||
fn get_sregs(&self) -> cpu::Result<SpecialRegisters> {
|
||||
self.fd
|
||||
Ok(self
|
||||
.fd
|
||||
.get_sregs()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetSpecialRegs(e.into()))
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetSpecialRegs(e.into()))?
|
||||
.into())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the vCPU special registers.
|
||||
///
|
||||
fn set_sregs(&self, sregs: &SpecialRegisters) -> cpu::Result<()> {
|
||||
let sregs = (*sregs).into();
|
||||
self.fd
|
||||
.set_sregs(sregs)
|
||||
.set_sregs(&sregs)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetSpecialRegs(e.into()))
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -217,17 +342,20 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
/// Returns the floating point state (FPU) from the vCPU.
|
||||
///
|
||||
fn get_fpu(&self) -> cpu::Result<FpuState> {
|
||||
self.fd
|
||||
Ok(self
|
||||
.fd
|
||||
.get_fpu()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetFloatingPointRegs(e.into()))
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetFloatingPointRegs(e.into()))?
|
||||
.into())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Set the floating point state (FPU) of a vCPU.
|
||||
///
|
||||
fn set_fpu(&self, fpu: &FpuState) -> cpu::Result<()> {
|
||||
let fpu: mshv_bindings::FloatingPointUnit = (*fpu).clone().into();
|
||||
self.fd
|
||||
.set_fpu(fpu)
|
||||
.set_fpu(&fpu)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetFloatingPointRegs(e.into()))
|
||||
}
|
||||
|
||||
@@ -235,60 +363,36 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
///
|
||||
/// Returns the model-specific registers (MSR) for this vCPU.
|
||||
///
|
||||
fn get_msrs(&self, msrs: &mut MsrEntries) -> cpu::Result<usize> {
|
||||
self.fd
|
||||
.get_msrs(msrs)
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetMsrEntries(e.into()))
|
||||
fn get_msrs(&self, msrs: &mut Vec<MsrEntry>) -> cpu::Result<usize> {
|
||||
let mshv_msrs: Vec<msr_entry> = msrs.iter().map(|e| (*e).into()).collect();
|
||||
let mut mshv_msrs = MsrEntries::from_entries(&mshv_msrs).unwrap();
|
||||
let succ = self
|
||||
.fd
|
||||
.get_msrs(&mut mshv_msrs)
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetMsrEntries(e.into()))?;
|
||||
|
||||
msrs[..succ].copy_from_slice(
|
||||
&mshv_msrs.as_slice()[..succ]
|
||||
.iter()
|
||||
.map(|e| (*e).into())
|
||||
.collect::<Vec<MsrEntry>>(),
|
||||
);
|
||||
|
||||
Ok(succ)
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Setup the model-specific registers (MSR) for this vCPU.
|
||||
/// Returns the number of MSR entries actually written.
|
||||
///
|
||||
fn set_msrs(&self, msrs: &MsrEntries) -> cpu::Result<usize> {
|
||||
fn set_msrs(&self, msrs: &[MsrEntry]) -> cpu::Result<usize> {
|
||||
let mshv_msrs: Vec<msr_entry> = msrs.iter().map(|e| (*e).into()).collect();
|
||||
let mshv_msrs = MsrEntries::from_entries(&mshv_msrs).unwrap();
|
||||
self.fd
|
||||
.set_msrs(msrs)
|
||||
.set_msrs(&mshv_msrs)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetMsrEntries(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call that returns the vcpu's current "xcrs".
|
||||
///
|
||||
fn get_xcrs(&self) -> cpu::Result<ExtendedControlRegisters> {
|
||||
self.fd
|
||||
.get_xcrs()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetXcsr(e.into()))
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call that sets the vcpu's current "xcrs".
|
||||
///
|
||||
fn set_xcrs(&self, xcrs: &ExtendedControlRegisters) -> cpu::Result<()> {
|
||||
self.fd
|
||||
.set_xcrs(xcrs)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetXcsr(e.into()))
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Returns currently pending exceptions, interrupts, and NMIs as well as related
|
||||
/// states of the vcpu.
|
||||
///
|
||||
fn get_vcpu_events(&self) -> cpu::Result<VcpuEvents> {
|
||||
self.fd
|
||||
.get_vcpu_events()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetVcpuEvents(e.into()))
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets pending exceptions, interrupts, and NMIs as well as related states
|
||||
/// of the vcpu.
|
||||
///
|
||||
fn set_vcpu_events(&self, events: &VcpuEvents) -> cpu::Result<()> {
|
||||
self.fd
|
||||
.set_vcpu_events(events)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetVcpuEvents(e.into()))
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call to enable HyperV SynIC
|
||||
@@ -463,14 +567,14 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
///
|
||||
/// X86 specific call to setup the CPUID registers.
|
||||
///
|
||||
fn set_cpuid2(&self, _cpuid: &CpuId) -> cpu::Result<()> {
|
||||
fn set_cpuid2(&self, _cpuid: &[CpuIdEntry]) -> cpu::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call to retrieve the CPUID registers.
|
||||
///
|
||||
fn get_cpuid2(&self, _num_entries: usize) -> cpu::Result<CpuId> {
|
||||
fn get_cpuid2(&self, _num_entries: usize) -> cpu::Result<Vec<CpuIdEntry>> {
|
||||
Ok(self.cpuid.clone())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -478,45 +582,43 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
/// Returns the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
|
||||
///
|
||||
fn get_lapic(&self) -> cpu::Result<LapicState> {
|
||||
self.fd
|
||||
Ok(self
|
||||
.fd
|
||||
.get_lapic()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetlapicState(e.into()))
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetlapicState(e.into()))?
|
||||
.into())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
|
||||
///
|
||||
fn set_lapic(&self, lapic: &LapicState) -> cpu::Result<()> {
|
||||
let lapic: mshv_bindings::LapicState = (*lapic).clone().into();
|
||||
self.fd
|
||||
.set_lapic(lapic)
|
||||
.set_lapic(&lapic)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetLapicState(e.into()))
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call that returns the vcpu's current "xsave struct".
|
||||
/// Returns the vcpu's current "multiprocessing state".
|
||||
///
|
||||
fn get_xsave(&self) -> cpu::Result<Xsave> {
|
||||
self.fd
|
||||
.get_xsave()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetXsaveState(e.into()))
|
||||
fn get_mp_state(&self) -> cpu::Result<MpState> {
|
||||
Ok(MpState::Mshv)
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call that sets the vcpu's current "xsave struct".
|
||||
/// Sets the vcpu's current "multiprocessing state".
|
||||
///
|
||||
fn set_xsave(&self, xsave: &Xsave) -> cpu::Result<()> {
|
||||
self.fd
|
||||
.set_xsave(xsave)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetXsaveState(e.into()))
|
||||
fn set_mp_state(&self, _mp_state: MpState) -> cpu::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
///
|
||||
/// Set CPU state
|
||||
///
|
||||
fn set_state(&self, state: &CpuState) -> cpu::Result<()> {
|
||||
let state: VcpuMshvState = state.clone().into();
|
||||
self.set_msrs(&state.msrs)?;
|
||||
self.set_vcpu_events(&state.vcpu_events)?;
|
||||
self.set_regs(&state.regs)?;
|
||||
self.set_sregs(&state.sregs)?;
|
||||
self.set_regs(&state.regs.into())?;
|
||||
self.set_sregs(&state.sregs.into())?;
|
||||
self.set_fpu(&state.fpu)?;
|
||||
self.set_xcrs(&state.xcrs)?;
|
||||
self.set_lapic(&state.lapic)?;
|
||||
@@ -555,18 +657,19 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
.get_debug_regs()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetDebugRegs(e.into()))?;
|
||||
|
||||
Ok(CpuState {
|
||||
Ok(VcpuMshvState {
|
||||
msrs,
|
||||
vcpu_events,
|
||||
regs,
|
||||
sregs,
|
||||
regs: regs.into(),
|
||||
sregs: sregs.into(),
|
||||
fpu,
|
||||
xcrs,
|
||||
lapic,
|
||||
dbg,
|
||||
xsave,
|
||||
misc,
|
||||
})
|
||||
}
|
||||
.into())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
@@ -586,42 +689,83 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call that returns the vcpu's current "suspend registers".
|
||||
/// Return the list of initial MSR entries for a VCPU
|
||||
///
|
||||
fn get_suspend_regs(&self) -> cpu::Result<SuspendRegisters> {
|
||||
self.fd
|
||||
.get_suspend_regs()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetSuspendRegs(e.into()))
|
||||
fn boot_msr_entries(&self) -> Vec<MsrEntry> {
|
||||
use crate::arch::x86::{msr_index, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
||||
|
||||
[
|
||||
msr!(msr_index::MSR_IA32_SYSENTER_CS),
|
||||
msr!(msr_index::MSR_IA32_SYSENTER_ESP),
|
||||
msr!(msr_index::MSR_IA32_SYSENTER_EIP),
|
||||
msr!(msr_index::MSR_STAR),
|
||||
msr!(msr_index::MSR_CSTAR),
|
||||
msr!(msr_index::MSR_LSTAR),
|
||||
msr!(msr_index::MSR_KERNEL_GS_BASE),
|
||||
msr!(msr_index::MSR_SYSCALL_MASK),
|
||||
msr!(msr_index::MSR_IA32_TSC),
|
||||
msr_data!(msr_index::MSR_MTRRdefType, MTRR_ENABLE | MTRR_MEM_TYPE_WB),
|
||||
]
|
||||
.to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
/// Device struct for MSHV
|
||||
pub struct MshvDevice {
|
||||
fd: DeviceFd,
|
||||
}
|
||||
|
||||
impl device::Device for MshvDevice {
|
||||
impl MshvVcpu {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Set device attribute
|
||||
/// X86 specific call that returns the vcpu's current "xsave struct".
|
||||
///
|
||||
fn set_device_attr(&self, attr: &DeviceAttr) -> device::Result<()> {
|
||||
fn get_xsave(&self) -> cpu::Result<Xsave> {
|
||||
self.fd
|
||||
.set_device_attr(attr)
|
||||
.map_err(|e| device::HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||
.get_xsave()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetXsaveState(e.into()))
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Get device attribute
|
||||
/// X86 specific call that sets the vcpu's current "xsave struct".
|
||||
///
|
||||
fn get_device_attr(&self, attr: &mut DeviceAttr) -> device::Result<()> {
|
||||
fn set_xsave(&self, xsave: &Xsave) -> cpu::Result<()> {
|
||||
self.fd
|
||||
.get_device_attr(attr)
|
||||
.map_err(|e| device::HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||
.set_xsave(xsave)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetXsaveState(e.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for MshvDevice {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.fd.as_raw_fd()
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call that returns the vcpu's current "xcrs".
|
||||
///
|
||||
fn get_xcrs(&self) -> cpu::Result<ExtendedControlRegisters> {
|
||||
self.fd
|
||||
.get_xcrs()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetXcsr(e.into()))
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call that sets the vcpu's current "xcrs".
|
||||
///
|
||||
fn set_xcrs(&self, xcrs: &ExtendedControlRegisters) -> cpu::Result<()> {
|
||||
self.fd
|
||||
.set_xcrs(xcrs)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetXcsr(e.into()))
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Returns currently pending exceptions, interrupts, and NMIs as well as related
|
||||
/// states of the vcpu.
|
||||
///
|
||||
fn get_vcpu_events(&self) -> cpu::Result<VcpuEvents> {
|
||||
self.fd
|
||||
.get_vcpu_events()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetVcpuEvents(e.into()))
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets pending exceptions, interrupts, and NMIs as well as related states
|
||||
/// of the vcpu.
|
||||
///
|
||||
fn set_vcpu_events(&self, events: &VcpuEvents) -> cpu::Result<()> {
|
||||
self.fd
|
||||
.set_vcpu_events(events)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetVcpuEvents(e.into()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -750,19 +894,25 @@ impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Wrapper over Mshv VM ioctls.
|
||||
pub struct MshvVm {
|
||||
fd: Arc<VmFd>,
|
||||
msrs: MsrEntries,
|
||||
// Hypervisor State
|
||||
hv_state: Arc<RwLock<HvState>>,
|
||||
vm_ops: Option<Arc<dyn vm::VmOps>>,
|
||||
msrs: Vec<MsrEntry>,
|
||||
dirty_log_slots: Arc<RwLock<HashMap<u64, MshvDirtyLogSlot>>>,
|
||||
}
|
||||
|
||||
fn hv_state_init() -> Arc<RwLock<HvState>> {
|
||||
Arc::new(RwLock::new(HvState { hypercall_page: 0 }))
|
||||
impl MshvVm {
|
||||
///
|
||||
/// Creates an in-kernel device.
|
||||
///
|
||||
/// See the documentation for `MSHV_CREATE_DEVICE`.
|
||||
fn create_device(&self, device: &mut CreateDevice) -> vm::Result<VfioDeviceFd> {
|
||||
let device_fd = self
|
||||
.fd
|
||||
.create_device(device)
|
||||
.map_err(|e| vm::HypervisorVmError::CreateDevice(e.into()))?;
|
||||
Ok(VfioDeviceFd::new_from_mshv(device_fd))
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
@@ -836,9 +986,8 @@ impl vm::Vm for MshvVm {
|
||||
let vcpu = MshvVcpu {
|
||||
fd: vcpu_fd,
|
||||
vp_index: id,
|
||||
cpuid: CpuId::new(1).unwrap(),
|
||||
cpuid: Vec::new(),
|
||||
msrs: self.msrs.clone(),
|
||||
hv_state: self.hv_state.clone(),
|
||||
vm_ops,
|
||||
};
|
||||
Ok(Arc::new(vcpu))
|
||||
@@ -857,6 +1006,7 @@ impl vm::Vm for MshvVm {
|
||||
addr: &IoEventAddress,
|
||||
datamatch: Option<DataMatch>,
|
||||
) -> vm::Result<()> {
|
||||
let addr = &mshv_ioctls::IoEventAddress::from(*addr);
|
||||
debug!(
|
||||
"register_ioevent fd {} addr {:x?} datamatch {:?}",
|
||||
fd.as_raw_fd(),
|
||||
@@ -882,6 +1032,7 @@ impl vm::Vm for MshvVm {
|
||||
}
|
||||
/// Unregister an event from a certain address it has been previously registered to.
|
||||
fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> vm::Result<()> {
|
||||
let addr = &mshv_ioctls::IoEventAddress::from(*addr);
|
||||
debug!("unregister_ioevent fd {} addr {:x?}", fd.as_raw_fd(), addr);
|
||||
|
||||
self.fd
|
||||
@@ -890,7 +1041,8 @@ impl vm::Vm for MshvVm {
|
||||
}
|
||||
|
||||
/// Creates a guest physical memory region.
|
||||
fn create_user_memory_region(&self, user_memory_region: MemoryRegion) -> vm::Result<()> {
|
||||
fn create_user_memory_region(&self, user_memory_region: UserMemoryRegion) -> vm::Result<()> {
|
||||
let user_memory_region: mshv_user_mem_region = user_memory_region.into();
|
||||
// No matter read only or not we keep track the slots.
|
||||
// For readonly hypervisor can enable the dirty bits,
|
||||
// but a VM exit happens before setting the dirty bits
|
||||
@@ -909,7 +1061,8 @@ impl vm::Vm for MshvVm {
|
||||
}
|
||||
|
||||
/// Removes a guest physical memory region.
|
||||
fn remove_user_memory_region(&self, user_memory_region: MemoryRegion) -> vm::Result<()> {
|
||||
fn remove_user_memory_region(&self, user_memory_region: UserMemoryRegion) -> vm::Result<()> {
|
||||
let user_memory_region: mshv_user_mem_region = user_memory_region.into();
|
||||
// Remove the corresponding entry from "self.dirty_log_slots" if needed
|
||||
self.dirty_log_slots
|
||||
.write()
|
||||
@@ -930,7 +1083,7 @@ impl vm::Vm for MshvVm {
|
||||
userspace_addr: u64,
|
||||
readonly: bool,
|
||||
_log_dirty_pages: bool,
|
||||
) -> MemoryRegion {
|
||||
) -> UserMemoryRegion {
|
||||
let mut flags = HV_MAP_GPA_READABLE | HV_MAP_GPA_EXECUTABLE;
|
||||
if !readonly {
|
||||
flags |= HV_MAP_GPA_WRITABLE;
|
||||
@@ -942,22 +1095,10 @@ impl vm::Vm for MshvVm {
|
||||
size: memory_size,
|
||||
userspace_addr: userspace_addr as u64,
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
///
|
||||
/// Creates an in-kernel device.
|
||||
///
|
||||
/// See the documentation for `MSHV_CREATE_DEVICE`.
|
||||
fn create_device(&self, device: &mut CreateDevice) -> vm::Result<Arc<dyn device::Device>> {
|
||||
let fd = self
|
||||
.fd
|
||||
.create_device(device)
|
||||
.map_err(|e| vm::HypervisorVmError::CreateDevice(e.into()))?;
|
||||
let device = MshvDevice { fd };
|
||||
Ok(Arc::new(device))
|
||||
}
|
||||
|
||||
fn create_passthrough_device(&self) -> vm::Result<Arc<dyn device::Device>> {
|
||||
fn create_passthrough_device(&self) -> vm::Result<VfioDeviceFd> {
|
||||
let mut vfio_dev = mshv_create_device {
|
||||
type_: mshv_device_type_MSHV_DEV_TYPE_VFIO,
|
||||
fd: 0,
|
||||
@@ -971,18 +1112,15 @@ impl vm::Vm for MshvVm {
|
||||
///
|
||||
/// Constructs a routing entry
|
||||
///
|
||||
fn make_routing_entry(
|
||||
&self,
|
||||
gsi: u32,
|
||||
config: &InterruptSourceConfig,
|
||||
) -> mshv_msi_routing_entry {
|
||||
fn make_routing_entry(&self, gsi: u32, config: &InterruptSourceConfig) -> IrqRoutingEntry {
|
||||
match config {
|
||||
InterruptSourceConfig::MsiIrq(cfg) => mshv_msi_routing_entry {
|
||||
gsi,
|
||||
address_lo: cfg.low_addr,
|
||||
address_hi: cfg.high_addr,
|
||||
data: cfg.data,
|
||||
},
|
||||
}
|
||||
.into(),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
@@ -994,13 +1132,22 @@ impl vm::Vm for MshvVm {
|
||||
vec_with_array_field::<mshv_msi_routing, mshv_msi_routing_entry>(entries.len());
|
||||
msi_routing[0].nr = entries.len() as u32;
|
||||
|
||||
let entries: Vec<mshv_msi_routing_entry> = entries
|
||||
.iter()
|
||||
.map(|entry| match entry {
|
||||
IrqRoutingEntry::Mshv(e) => *e,
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => panic!("IrqRoutingEntry type is wrong"),
|
||||
})
|
||||
.collect();
|
||||
|
||||
// SAFETY: msi_routing initialized with entries.len() and now it is being turned into
|
||||
// entries_slice with entries.len() again. It is guaranteed to be large enough to hold
|
||||
// everything from entries.
|
||||
unsafe {
|
||||
let entries_slice: &mut [mshv_msi_routing_entry] =
|
||||
msi_routing[0].entries.as_mut_slice(entries.len());
|
||||
entries_slice.copy_from_slice(entries);
|
||||
entries_slice.copy_from_slice(&entries);
|
||||
}
|
||||
|
||||
self.fd
|
||||
@@ -1008,19 +1155,6 @@ impl vm::Vm for MshvVm {
|
||||
.map_err(|e| vm::HypervisorVmError::SetGsiRouting(e.into()))
|
||||
}
|
||||
///
|
||||
/// Get the Vm state. Return VM specific data
|
||||
///
|
||||
fn state(&self) -> vm::Result<VmState> {
|
||||
Ok(*self.hv_state.read().unwrap())
|
||||
}
|
||||
///
|
||||
/// Set the VM state
|
||||
///
|
||||
fn set_state(&self, state: VmState) -> vm::Result<()> {
|
||||
self.hv_state.write().unwrap().hypercall_page = state.hypercall_page;
|
||||
Ok(())
|
||||
}
|
||||
///
|
||||
/// Start logging dirty pages
|
||||
///
|
||||
fn start_dirty_log(&self) -> vm::Result<()> {
|
||||
@@ -1058,4 +1192,18 @@ impl vm::Vm for MshvVm {
|
||||
)
|
||||
.map_err(|e| vm::HypervisorVmError::GetDirtyLog(e.into()))
|
||||
}
|
||||
/// Retrieve guest clock.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn get_clock(&self) -> vm::Result<ClockData> {
|
||||
Ok(ClockData::Mshv)
|
||||
}
|
||||
/// Set guest clock.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn set_clock(&self, _data: &ClockData) -> vm::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
/// Downcast to the underlying MshvVm type
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
// Copyright 2018-2019 CrowdStrike, Inc.
|
||||
//
|
||||
//
|
||||
use crate::arch::x86::{msr_index, SegmentRegisterOps, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
||||
use crate::arch::x86::{
|
||||
CpuIdEntry, DescriptorTable, FpuState, LapicState, MsrEntry, SegmentRegister, SpecialRegisters,
|
||||
StandardRegisters,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
@@ -15,25 +18,24 @@ use std::fmt;
|
||||
/// Export generically-named wrappers of mshv_bindings for Unix-based platforms
|
||||
///
|
||||
pub use {
|
||||
mshv_bindings::hv_cpuid_entry as CpuIdEntry,
|
||||
mshv_bindings::mshv_user_mem_region as MemoryRegion, mshv_bindings::msr_entry as MsrEntry,
|
||||
mshv_bindings::CpuId, mshv_bindings::DebugRegisters,
|
||||
mshv_bindings::FloatingPointUnit as FpuState, mshv_bindings::LapicState,
|
||||
mshv_bindings::hv_cpuid_entry, mshv_bindings::mshv_user_mem_region as MemoryRegion,
|
||||
mshv_bindings::msr_entry, mshv_bindings::CpuId, mshv_bindings::DebugRegisters,
|
||||
mshv_bindings::FloatingPointUnit, mshv_bindings::LapicState as MshvLapicState,
|
||||
mshv_bindings::MiscRegs as MiscRegisters, mshv_bindings::MsrList,
|
||||
mshv_bindings::Msrs as MsrEntries, mshv_bindings::Msrs, mshv_bindings::SegmentRegister,
|
||||
mshv_bindings::SpecialRegisters, mshv_bindings::StandardRegisters,
|
||||
mshv_bindings::SuspendRegisters, mshv_bindings::VcpuEvents, mshv_bindings::XSave as Xsave,
|
||||
mshv_bindings::Msrs as MsrEntries, mshv_bindings::Msrs,
|
||||
mshv_bindings::SegmentRegister as MshvSegmentRegister,
|
||||
mshv_bindings::SpecialRegisters as MshvSpecialRegisters,
|
||||
mshv_bindings::StandardRegisters as MshvStandardRegisters, mshv_bindings::SuspendRegisters,
|
||||
mshv_bindings::TableRegister, mshv_bindings::VcpuEvents, mshv_bindings::XSave as Xsave,
|
||||
mshv_bindings::Xcrs as ExtendedControlRegisters,
|
||||
};
|
||||
|
||||
pub const CPUID_FLAG_VALID_INDEX: u32 = 0;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct VcpuMshvState {
|
||||
pub msrs: MsrEntries,
|
||||
pub msrs: Vec<MsrEntry>,
|
||||
pub vcpu_events: VcpuEvents,
|
||||
pub regs: StandardRegisters,
|
||||
pub sregs: SpecialRegisters,
|
||||
pub regs: MshvStandardRegisters,
|
||||
pub sregs: MshvSpecialRegisters,
|
||||
pub fpu: FpuState,
|
||||
pub xcrs: ExtendedControlRegisters,
|
||||
pub lapic: LapicState,
|
||||
@@ -44,10 +46,10 @@ pub struct VcpuMshvState {
|
||||
|
||||
impl fmt::Display for VcpuMshvState {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let expected_num_msrs = self.msrs.as_fam_struct_ref().nmsrs as usize;
|
||||
let expected_num_msrs = self.msrs.len();
|
||||
let mut msr_entries = vec![vec![0; 2]; expected_num_msrs];
|
||||
|
||||
for (i, entry) in self.msrs.as_slice().iter().enumerate() {
|
||||
for (i, entry) in self.msrs.iter().enumerate() {
|
||||
msr_entries[i][1] = entry.data;
|
||||
msr_entries[i][0] = entry.index as u64;
|
||||
}
|
||||
@@ -66,87 +68,252 @@ impl fmt::Display for VcpuMshvState {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IrqRouting {}
|
||||
pub enum VcpuExit {}
|
||||
pub struct MpState {}
|
||||
|
||||
impl SegmentRegisterOps for SegmentRegister {
|
||||
fn segment_type(&self) -> u8 {
|
||||
self.type_
|
||||
}
|
||||
fn set_segment_type(&mut self, val: u8) {
|
||||
self.type_ = val;
|
||||
}
|
||||
|
||||
fn dpl(&self) -> u8 {
|
||||
self.dpl
|
||||
}
|
||||
|
||||
fn set_dpl(&mut self, val: u8) {
|
||||
self.dpl = val;
|
||||
}
|
||||
|
||||
fn present(&self) -> u8 {
|
||||
self.present
|
||||
}
|
||||
|
||||
fn set_present(&mut self, val: u8) {
|
||||
self.present = val;
|
||||
}
|
||||
|
||||
fn long(&self) -> u8 {
|
||||
self.l
|
||||
}
|
||||
|
||||
fn set_long(&mut self, val: u8) {
|
||||
self.l = val;
|
||||
}
|
||||
|
||||
fn avl(&self) -> u8 {
|
||||
self.avl
|
||||
}
|
||||
|
||||
fn set_avl(&mut self, val: u8) {
|
||||
self.avl = val;
|
||||
}
|
||||
|
||||
fn desc_type(&self) -> u8 {
|
||||
self.s
|
||||
}
|
||||
|
||||
fn set_desc_type(&mut self, val: u8) {
|
||||
self.s = val;
|
||||
}
|
||||
|
||||
fn granularity(&self) -> u8 {
|
||||
self.g
|
||||
}
|
||||
|
||||
fn set_granularity(&mut self, val: u8) {
|
||||
self.g = val;
|
||||
}
|
||||
|
||||
fn db(&self) -> u8 {
|
||||
self.db
|
||||
}
|
||||
|
||||
fn set_db(&mut self, val: u8) {
|
||||
self.db = val;
|
||||
impl From<StandardRegisters> for MshvStandardRegisters {
|
||||
fn from(regs: StandardRegisters) -> Self {
|
||||
Self {
|
||||
rax: regs.rax,
|
||||
rbx: regs.rbx,
|
||||
rcx: regs.rcx,
|
||||
rdx: regs.rdx,
|
||||
rsi: regs.rsi,
|
||||
rdi: regs.rdi,
|
||||
rsp: regs.rsp,
|
||||
rbp: regs.rbp,
|
||||
r8: regs.r8,
|
||||
r9: regs.r9,
|
||||
r10: regs.r10,
|
||||
r11: regs.r11,
|
||||
r12: regs.r12,
|
||||
r13: regs.r13,
|
||||
r14: regs.r14,
|
||||
r15: regs.r15,
|
||||
rip: regs.rip,
|
||||
rflags: regs.rflags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn boot_msr_entries() -> MsrEntries {
|
||||
MsrEntries::from_entries(&[
|
||||
msr!(msr_index::MSR_IA32_SYSENTER_CS),
|
||||
msr!(msr_index::MSR_IA32_SYSENTER_ESP),
|
||||
msr!(msr_index::MSR_IA32_SYSENTER_EIP),
|
||||
msr!(msr_index::MSR_STAR),
|
||||
msr!(msr_index::MSR_CSTAR),
|
||||
msr!(msr_index::MSR_LSTAR),
|
||||
msr!(msr_index::MSR_KERNEL_GS_BASE),
|
||||
msr!(msr_index::MSR_SYSCALL_MASK),
|
||||
msr!(msr_index::MSR_IA32_TSC),
|
||||
msr_data!(msr_index::MSR_MTRRdefType, MTRR_ENABLE | MTRR_MEM_TYPE_WB),
|
||||
])
|
||||
.unwrap()
|
||||
impl From<MshvStandardRegisters> for StandardRegisters {
|
||||
fn from(regs: MshvStandardRegisters) -> Self {
|
||||
Self {
|
||||
rax: regs.rax,
|
||||
rbx: regs.rbx,
|
||||
rcx: regs.rcx,
|
||||
rdx: regs.rdx,
|
||||
rsi: regs.rsi,
|
||||
rdi: regs.rdi,
|
||||
rsp: regs.rsp,
|
||||
rbp: regs.rbp,
|
||||
r8: regs.r8,
|
||||
r9: regs.r9,
|
||||
r10: regs.r10,
|
||||
r11: regs.r11,
|
||||
r12: regs.r12,
|
||||
r13: regs.r13,
|
||||
r14: regs.r14,
|
||||
r15: regs.r15,
|
||||
rip: regs.rip,
|
||||
rflags: regs.rflags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SegmentRegister> for MshvSegmentRegister {
|
||||
fn from(s: SegmentRegister) -> Self {
|
||||
Self {
|
||||
base: s.base,
|
||||
limit: s.limit,
|
||||
selector: s.selector,
|
||||
type_: s.type_,
|
||||
present: s.present,
|
||||
dpl: s.dpl,
|
||||
db: s.db,
|
||||
s: s.s,
|
||||
l: s.l,
|
||||
g: s.g,
|
||||
avl: s.avl,
|
||||
unusable: s.unusable,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MshvSegmentRegister> for SegmentRegister {
|
||||
fn from(s: MshvSegmentRegister) -> Self {
|
||||
Self {
|
||||
base: s.base,
|
||||
limit: s.limit,
|
||||
selector: s.selector,
|
||||
type_: s.type_,
|
||||
present: s.present,
|
||||
dpl: s.dpl,
|
||||
db: s.db,
|
||||
s: s.s,
|
||||
l: s.l,
|
||||
g: s.g,
|
||||
avl: s.avl,
|
||||
unusable: s.unusable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DescriptorTable> for TableRegister {
|
||||
fn from(dt: DescriptorTable) -> Self {
|
||||
Self {
|
||||
base: dt.base,
|
||||
limit: dt.limit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TableRegister> for DescriptorTable {
|
||||
fn from(dt: TableRegister) -> Self {
|
||||
Self {
|
||||
base: dt.base,
|
||||
limit: dt.limit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SpecialRegisters> for MshvSpecialRegisters {
|
||||
fn from(s: SpecialRegisters) -> Self {
|
||||
Self {
|
||||
cs: s.cs.into(),
|
||||
ds: s.ds.into(),
|
||||
es: s.es.into(),
|
||||
fs: s.fs.into(),
|
||||
gs: s.gs.into(),
|
||||
ss: s.ss.into(),
|
||||
tr: s.tr.into(),
|
||||
ldt: s.ldt.into(),
|
||||
gdt: s.gdt.into(),
|
||||
idt: s.idt.into(),
|
||||
cr0: s.cr0,
|
||||
cr2: s.cr2,
|
||||
cr3: s.cr3,
|
||||
cr4: s.cr4,
|
||||
cr8: s.cr8,
|
||||
efer: s.efer,
|
||||
apic_base: s.apic_base,
|
||||
interrupt_bitmap: s.interrupt_bitmap,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MshvSpecialRegisters> for SpecialRegisters {
|
||||
fn from(s: MshvSpecialRegisters) -> Self {
|
||||
Self {
|
||||
cs: s.cs.into(),
|
||||
ds: s.ds.into(),
|
||||
es: s.es.into(),
|
||||
fs: s.fs.into(),
|
||||
gs: s.gs.into(),
|
||||
ss: s.ss.into(),
|
||||
tr: s.tr.into(),
|
||||
ldt: s.ldt.into(),
|
||||
gdt: s.gdt.into(),
|
||||
idt: s.idt.into(),
|
||||
cr0: s.cr0,
|
||||
cr2: s.cr2,
|
||||
cr3: s.cr3,
|
||||
cr4: s.cr4,
|
||||
cr8: s.cr8,
|
||||
efer: s.efer,
|
||||
apic_base: s.apic_base,
|
||||
interrupt_bitmap: s.interrupt_bitmap,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CpuIdEntry> for hv_cpuid_entry {
|
||||
fn from(e: CpuIdEntry) -> Self {
|
||||
Self {
|
||||
function: e.function,
|
||||
index: e.index,
|
||||
flags: e.flags,
|
||||
eax: e.eax,
|
||||
ebx: e.ebx,
|
||||
ecx: e.ecx,
|
||||
edx: e.edx,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<hv_cpuid_entry> for CpuIdEntry {
|
||||
fn from(e: hv_cpuid_entry) -> Self {
|
||||
Self {
|
||||
function: e.function,
|
||||
index: e.index,
|
||||
flags: e.flags,
|
||||
eax: e.eax,
|
||||
ebx: e.ebx,
|
||||
ecx: e.ecx,
|
||||
edx: e.edx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FloatingPointUnit> for FpuState {
|
||||
fn from(s: FloatingPointUnit) -> Self {
|
||||
Self {
|
||||
fpr: s.fpr,
|
||||
fcw: s.fcw,
|
||||
fsw: s.fsw,
|
||||
ftwx: s.ftwx,
|
||||
last_opcode: s.last_opcode,
|
||||
last_ip: s.last_ip,
|
||||
last_dp: s.last_dp,
|
||||
xmm: s.xmm,
|
||||
mxcsr: s.mxcsr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FpuState> for FloatingPointUnit {
|
||||
fn from(s: FpuState) -> Self {
|
||||
Self {
|
||||
fpr: s.fpr,
|
||||
fcw: s.fcw,
|
||||
fsw: s.fsw,
|
||||
ftwx: s.ftwx,
|
||||
last_opcode: s.last_opcode,
|
||||
last_ip: s.last_ip,
|
||||
last_dp: s.last_dp,
|
||||
xmm: s.xmm,
|
||||
mxcsr: s.mxcsr,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LapicState> for MshvLapicState {
|
||||
fn from(s: LapicState) -> Self {
|
||||
Self { regs: s.regs }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MshvLapicState> for LapicState {
|
||||
fn from(s: MshvLapicState) -> Self {
|
||||
Self { regs: s.regs }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<msr_entry> for MsrEntry {
|
||||
fn from(e: msr_entry) -> Self {
|
||||
Self {
|
||||
index: e.index,
|
||||
data: e.data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MsrEntry> for msr_entry {
|
||||
fn from(e: MsrEntry) -> Self {
|
||||
Self {
|
||||
index: e.index,
|
||||
data: e.data,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,20 +12,14 @@
|
||||
use crate::aarch64::VcpuInit;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::arch::aarch64::gic::Vgic;
|
||||
use crate::cpu::Vcpu;
|
||||
use crate::device::Device;
|
||||
#[cfg(feature = "kvm")]
|
||||
use crate::kvm::KvmVmState as VmState;
|
||||
#[cfg(feature = "mshv")]
|
||||
use crate::mshv::HvState as VmState;
|
||||
#[cfg(feature = "tdx")]
|
||||
use crate::x86_64::CpuId;
|
||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||
use crate::arch::x86::CpuIdEntry;
|
||||
use crate::cpu::Vcpu;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::ClockData;
|
||||
use crate::CreateDevice;
|
||||
use crate::{IoEventAddress, IrqRoutingEntry, MemoryRegion};
|
||||
#[cfg(feature = "kvm")]
|
||||
use kvm_ioctls::Cap;
|
||||
use crate::UserMemoryRegion;
|
||||
use crate::{IoEventAddress, IrqRoutingEntry};
|
||||
use std::any::Any;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use std::fs::File;
|
||||
use std::sync::Arc;
|
||||
@@ -263,7 +257,7 @@ pub enum InterruptSourceConfig {
|
||||
///
|
||||
/// This crate provides a hypervisor-agnostic interfaces for Vm
|
||||
///
|
||||
pub trait Vm: Send + Sync {
|
||||
pub trait Vm: Send + Sync + Any {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
/// Sets the address of the one-page region in the VM's address space.
|
||||
fn set_identity_map_address(&self, address: u64) -> Result<()>;
|
||||
@@ -311,15 +305,13 @@ pub trait Vm: Send + Sync {
|
||||
userspace_addr: u64,
|
||||
readonly: bool,
|
||||
log_dirty_pages: bool,
|
||||
) -> MemoryRegion;
|
||||
) -> UserMemoryRegion;
|
||||
/// Creates a guest physical memory slot.
|
||||
fn create_user_memory_region(&self, user_memory_region: MemoryRegion) -> Result<()>;
|
||||
fn create_user_memory_region(&self, user_memory_region: UserMemoryRegion) -> Result<()>;
|
||||
/// Removes a guest physical memory slot.
|
||||
fn remove_user_memory_region(&self, user_memory_region: MemoryRegion) -> Result<()>;
|
||||
/// Creates an emulated device in the kernel.
|
||||
fn create_device(&self, device: &mut CreateDevice) -> Result<Arc<dyn Device>>;
|
||||
fn remove_user_memory_region(&self, user_memory_region: UserMemoryRegion) -> Result<()>;
|
||||
/// Returns the preferred CPU target type which can be emulated by KVM on underlying host.
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn get_preferred_target(&self, kvi: &mut VcpuInit) -> Result<()>;
|
||||
/// Enable split Irq capability
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -327,20 +319,13 @@ pub trait Vm: Send + Sync {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn enable_sgx_attribute(&self, file: File) -> Result<()>;
|
||||
/// Retrieve guest clock.
|
||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn get_clock(&self) -> Result<ClockData>;
|
||||
/// Set guest clock.
|
||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn set_clock(&self, data: &ClockData) -> Result<()>;
|
||||
#[cfg(feature = "kvm")]
|
||||
/// Checks if a particular `Cap` is available.
|
||||
fn check_extension(&self, c: Cap) -> bool;
|
||||
/// Create a device that is used for passthrough
|
||||
fn create_passthrough_device(&self) -> Result<Arc<dyn Device>>;
|
||||
/// Get the Vm state. Return VM specific data
|
||||
fn state(&self) -> Result<VmState>;
|
||||
/// Set the VM state
|
||||
fn set_state(&self, state: VmState) -> Result<()>;
|
||||
fn create_passthrough_device(&self) -> Result<vfio_ioctls::VfioDeviceFd>;
|
||||
/// Start logging dirty pages
|
||||
fn start_dirty_log(&self) -> Result<()>;
|
||||
/// Stop logging dirty pages
|
||||
@@ -349,7 +334,7 @@ pub trait Vm: Send + Sync {
|
||||
fn get_dirty_log(&self, slot: u32, base_gpa: u64, memory_size: u64) -> Result<Vec<u64>>;
|
||||
#[cfg(feature = "tdx")]
|
||||
/// Initalize TDX on this VM
|
||||
fn tdx_init(&self, cpuid: &CpuId, max_vcpus: u32) -> Result<()>;
|
||||
fn tdx_init(&self, cpuid: &[CpuIdEntry], max_vcpus: u32) -> Result<()>;
|
||||
#[cfg(feature = "tdx")]
|
||||
/// Finalize the configuration of TDX on this VM
|
||||
fn tdx_finalize(&self) -> Result<()>;
|
||||
@@ -362,6 +347,8 @@ pub trait Vm: Send + Sync {
|
||||
size: u64,
|
||||
measure: bool,
|
||||
) -> Result<()>;
|
||||
/// Downcast to the underlying hypervisor VM type
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
}
|
||||
|
||||
pub trait VmOps: Send + Sync {
|
||||
|
||||
@@ -5,4 +5,4 @@ authors = ["The Chromium OS Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
vmm-sys-util = "0.9.0"
|
||||
vmm-sys-util = "0.10.0"
|
||||
|
||||
@@ -6,22 +6,23 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
epoll = "4.3.1"
|
||||
getrandom = "0.2"
|
||||
libc = "0.2.126"
|
||||
getrandom = "0.2.7"
|
||||
libc = "0.2.131"
|
||||
log = "0.4.17"
|
||||
net_gen = { path = "../net_gen" }
|
||||
rate_limiter = { path = "../rate_limiter" }
|
||||
serde = "1.0.138"
|
||||
serde = "1.0.143"
|
||||
thiserror = "1.0.32"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
virtio-bindings = "0.1.0"
|
||||
virtio-queue = "0.4.0"
|
||||
virtio-queue = "0.5.0"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
vmm-sys-util = "0.10.0"
|
||||
|
||||
[dev-dependencies]
|
||||
once_cell = "1.13.0"
|
||||
once_cell = "1.13.1"
|
||||
pnet = "0.31.0"
|
||||
pnet_datalink = "0.31.0"
|
||||
serde_json = "1.0.82"
|
||||
serde_json = "1.0.83"
|
||||
|
||||
@@ -13,8 +13,8 @@ use virtio_bindings::bindings::virtio_net::{
|
||||
VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6,
|
||||
VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_OK,
|
||||
};
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::{ByteValued, Bytes, GuestMemoryAtomic, GuestMemoryError};
|
||||
use virtio_queue::{Queue, QueueT};
|
||||
use vm_memory::{ByteValued, Bytes, GuestMemoryError};
|
||||
use vm_virtio::{AccessPlatform, Translatable};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -58,98 +58,93 @@ impl CtrlQueue {
|
||||
|
||||
pub fn process(
|
||||
&mut self,
|
||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
mem: &GuestMemoryMmap,
|
||||
queue: &mut Queue,
|
||||
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
||||
) -> Result<()> {
|
||||
let mut used_desc_heads = Vec::new();
|
||||
loop {
|
||||
for mut desc_chain in queue.iter().map_err(Error::QueueIterator)? {
|
||||
let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?;
|
||||
while let Some(mut desc_chain) = queue.pop_descriptor_chain(mem) {
|
||||
let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?;
|
||||
|
||||
let ctrl_hdr: ControlHeader = desc_chain
|
||||
.memory()
|
||||
.read_obj(
|
||||
ctrl_desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, ctrl_desc.len() as usize),
|
||||
)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
let data_desc = desc_chain.next().ok_or(Error::NoDataDescriptor)?;
|
||||
let ctrl_hdr: ControlHeader = desc_chain
|
||||
.memory()
|
||||
.read_obj(
|
||||
ctrl_desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, ctrl_desc.len() as usize),
|
||||
)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
let data_desc = desc_chain.next().ok_or(Error::NoDataDescriptor)?;
|
||||
|
||||
let data_desc_addr = data_desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, data_desc.len() as usize);
|
||||
let data_desc_addr = data_desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, data_desc.len() as usize);
|
||||
|
||||
let status_desc = desc_chain.next().ok_or(Error::NoStatusDescriptor)?;
|
||||
let status_desc = desc_chain.next().ok_or(Error::NoStatusDescriptor)?;
|
||||
|
||||
let ok = match u32::from(ctrl_hdr.class) {
|
||||
VIRTIO_NET_CTRL_MQ => {
|
||||
let queue_pairs = desc_chain
|
||||
.memory()
|
||||
.read_obj::<u16>(data_desc_addr)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET {
|
||||
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
||||
false
|
||||
} else if (queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN as u16)
|
||||
|| (queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX as u16)
|
||||
{
|
||||
warn!("Number of MQ pairs out of range: {}", queue_pairs);
|
||||
false
|
||||
} else {
|
||||
info!("Number of MQ pairs requested: {}", queue_pairs);
|
||||
true
|
||||
}
|
||||
}
|
||||
VIRTIO_NET_CTRL_GUEST_OFFLOADS => {
|
||||
let features = desc_chain
|
||||
.memory()
|
||||
.read_obj::<u64>(data_desc_addr)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET {
|
||||
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
||||
false
|
||||
} else {
|
||||
let mut ok = true;
|
||||
for tap in self.taps.iter_mut() {
|
||||
info!("Reprogramming tap offload with features: {}", features);
|
||||
tap.set_offload(virtio_features_to_tap_offload(features))
|
||||
.map_err(|e| {
|
||||
error!("Error programming tap offload: {:?}", e);
|
||||
ok = false
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
ok
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
warn!("Unsupported command {:?}", ctrl_hdr);
|
||||
let ok = match u32::from(ctrl_hdr.class) {
|
||||
VIRTIO_NET_CTRL_MQ => {
|
||||
let queue_pairs = desc_chain
|
||||
.memory()
|
||||
.read_obj::<u16>(data_desc_addr)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET {
|
||||
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
||||
false
|
||||
} else if (queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN as u16)
|
||||
|| (queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX as u16)
|
||||
{
|
||||
warn!("Number of MQ pairs out of range: {}", queue_pairs);
|
||||
false
|
||||
} else {
|
||||
info!("Number of MQ pairs requested: {}", queue_pairs);
|
||||
true
|
||||
}
|
||||
};
|
||||
}
|
||||
VIRTIO_NET_CTRL_GUEST_OFFLOADS => {
|
||||
let features = desc_chain
|
||||
.memory()
|
||||
.read_obj::<u64>(data_desc_addr)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET {
|
||||
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
||||
false
|
||||
} else {
|
||||
let mut ok = true;
|
||||
for tap in self.taps.iter_mut() {
|
||||
info!("Reprogramming tap offload with features: {}", features);
|
||||
tap.set_offload(virtio_features_to_tap_offload(features))
|
||||
.map_err(|e| {
|
||||
error!("Error programming tap offload: {:?}", e);
|
||||
ok = false
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
ok
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
warn!("Unsupported command {:?}", ctrl_hdr);
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_obj(
|
||||
if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8,
|
||||
status_desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, status_desc.len() as usize),
|
||||
)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
let len = ctrl_desc.len() + data_desc.len() + status_desc.len();
|
||||
used_desc_heads.push((desc_chain.head_index(), len));
|
||||
}
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_obj(
|
||||
if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8,
|
||||
status_desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, status_desc.len() as usize),
|
||||
)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
let len = ctrl_desc.len() + data_desc.len() + status_desc.len();
|
||||
|
||||
for (desc_index, len) in used_desc_heads.iter() {
|
||||
queue
|
||||
.add_used(*desc_index, *len)
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
}
|
||||
queue
|
||||
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
|
||||
if !queue
|
||||
.enable_notification()
|
||||
.enable_notification(mem)
|
||||
.map_err(Error::QueueEnableNotification)?
|
||||
{
|
||||
break;
|
||||
|
||||
@@ -18,6 +18,7 @@ use std::io::Error as IoError;
|
||||
use std::os::raw::c_uint;
|
||||
use std::os::unix::io::{FromRawFd, RawFd};
|
||||
use std::{io, mem, net};
|
||||
use thiserror::Error;
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use virtio_bindings::bindings::virtio_net::{
|
||||
@@ -35,9 +36,9 @@ pub use open_tap::{open_tap, Error as OpenTapError};
|
||||
pub use queue_pair::{NetCounters, NetQueuePair, NetQueuePairError, RxVirtio, TxVirtio};
|
||||
pub use tap::{Error as TapError, Tap};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
/// Failed to create a socket.
|
||||
#[error("Failed to create a socket: {0}")]
|
||||
CreateSocket(IoError),
|
||||
}
|
||||
|
||||
|
||||
@@ -6,30 +6,31 @@ use super::{vnet_hdr_len, MacAddr, Tap, TapError};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::path::Path;
|
||||
use std::{fs, io};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
/// Failed to convert an hexadecimal string into an integer.
|
||||
#[error("Failed to convert an hexadecimal string into an integer: {0}")]
|
||||
ConvertHexStringToInt(std::num::ParseIntError),
|
||||
/// Error related to the multiqueue support (no support TAP side).
|
||||
#[error("Error related to the multiqueue support (no support TAP side).")]
|
||||
MultiQueueNoTapSupport,
|
||||
/// Error related to the multiqueue support (no support device side).
|
||||
#[error("Error related to the multiqueue support (no support device side).")]
|
||||
MultiQueueNoDeviceSupport,
|
||||
/// Failed to read the TAP flags from sysfs.
|
||||
#[error("Failed to read the TAP flags from sysfs: {0}")]
|
||||
ReadSysfsTunFlags(io::Error),
|
||||
/// Open tap device failed.
|
||||
#[error("Open tap device failed: {0}")]
|
||||
TapOpen(TapError),
|
||||
/// Setting tap IP failed.
|
||||
#[error("Setting tap IP failed: {0}")]
|
||||
TapSetIp(TapError),
|
||||
/// Setting tap netmask failed.
|
||||
#[error("Setting tap netmask failed: {0}")]
|
||||
TapSetNetmask(TapError),
|
||||
/// Setting MAC address failed
|
||||
#[error("Setting MAC address failed: {0}")]
|
||||
TapSetMac(TapError),
|
||||
/// Getting MAC address failed
|
||||
#[error("Getting MAC address failed: {0}")]
|
||||
TapGetMac(TapError),
|
||||
/// Setting vnet header size failed.
|
||||
#[error("Setting vnet header size failed: {0}")]
|
||||
TapSetVnetHdrSize(TapError),
|
||||
/// Enabling tap interface failed.
|
||||
#[error("Enabling tap interface failed: {0}")]
|
||||
TapEnable(TapError),
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,9 @@ use std::num::Wrapping;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::{Bytes, GuestMemory, GuestMemoryAtomic};
|
||||
use thiserror::Error;
|
||||
use virtio_queue::{Queue, QueueOwnedT, QueueT};
|
||||
use vm_memory::{Bytes, GuestMemory};
|
||||
use vm_virtio::{AccessPlatform, Translatable};
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -36,104 +37,95 @@ impl TxVirtio {
|
||||
|
||||
pub fn process_desc_chain(
|
||||
&mut self,
|
||||
mem: &GuestMemoryMmap,
|
||||
tap: &mut Tap,
|
||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
queue: &mut Queue,
|
||||
rate_limiter: &mut Option<RateLimiter>,
|
||||
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
||||
) -> Result<bool, NetQueuePairError> {
|
||||
let mut retry_write = false;
|
||||
let mut rate_limit_reached = false;
|
||||
|
||||
loop {
|
||||
let used_desc_head: (u16, u32);
|
||||
let mut avail_iter = queue
|
||||
.iter()
|
||||
.map_err(NetQueuePairError::QueueIteratorFailed)?;
|
||||
|
||||
if let Some(mut desc_chain) = avail_iter.next() {
|
||||
if rate_limit_reached {
|
||||
avail_iter.go_to_previous_position();
|
||||
break;
|
||||
}
|
||||
|
||||
let mut next_desc = desc_chain.next();
|
||||
|
||||
let mut iovecs = Vec::new();
|
||||
while let Some(desc) = next_desc {
|
||||
let desc_addr = desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, desc.len() as usize);
|
||||
if !desc.is_write_only() && desc.len() > 0 {
|
||||
let buf = desc_chain
|
||||
.memory()
|
||||
.get_slice(desc_addr, desc.len() as usize)
|
||||
.map_err(NetQueuePairError::GuestMemory)?
|
||||
.as_ptr();
|
||||
let iovec = libc::iovec {
|
||||
iov_base: buf as *mut libc::c_void,
|
||||
iov_len: desc.len() as libc::size_t,
|
||||
};
|
||||
iovecs.push(iovec);
|
||||
} else {
|
||||
error!(
|
||||
"Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}",
|
||||
desc_addr.0,
|
||||
desc.len(),
|
||||
desc.is_write_only()
|
||||
);
|
||||
return Err(NetQueuePairError::DescriptorChainInvalid);
|
||||
}
|
||||
next_desc = desc_chain.next();
|
||||
}
|
||||
|
||||
let len = if !iovecs.is_empty() {
|
||||
let result = unsafe {
|
||||
libc::writev(
|
||||
tap.as_raw_fd() as libc::c_int,
|
||||
iovecs.as_ptr() as *const libc::iovec,
|
||||
iovecs.len() as libc::c_int,
|
||||
)
|
||||
};
|
||||
|
||||
if result < 0 {
|
||||
let e = std::io::Error::last_os_error();
|
||||
|
||||
/* EAGAIN */
|
||||
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||
avail_iter.go_to_previous_position();
|
||||
retry_write = true;
|
||||
break;
|
||||
}
|
||||
error!("net: tx: failed writing to tap: {}", e);
|
||||
return Err(NetQueuePairError::WriteTap(e));
|
||||
}
|
||||
|
||||
self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
|
||||
self.counter_frames += Wrapping(1);
|
||||
|
||||
result as u32
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
used_desc_head = (desc_chain.head_index(), len);
|
||||
|
||||
// For the sake of simplicity (similar to the RX rate limiting), we always
|
||||
// let the 'last' descriptor chain go-through even if it was over the rate
|
||||
// limit, and simply stop processing oncoming `avail_desc` if any.
|
||||
if let Some(rate_limiter) = rate_limiter {
|
||||
rate_limit_reached = !rate_limiter.consume(1, TokenType::Ops)
|
||||
|| !rate_limiter.consume(len as u64, TokenType::Bytes);
|
||||
}
|
||||
} else {
|
||||
while let Some(mut desc_chain) = queue.pop_descriptor_chain(mem) {
|
||||
if rate_limit_reached {
|
||||
queue.go_to_previous_position();
|
||||
break;
|
||||
}
|
||||
|
||||
let mut next_desc = desc_chain.next();
|
||||
|
||||
let mut iovecs = Vec::new();
|
||||
while let Some(desc) = next_desc {
|
||||
let desc_addr = desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, desc.len() as usize);
|
||||
if !desc.is_write_only() && desc.len() > 0 {
|
||||
let buf = desc_chain
|
||||
.memory()
|
||||
.get_slice(desc_addr, desc.len() as usize)
|
||||
.map_err(NetQueuePairError::GuestMemory)?
|
||||
.as_ptr();
|
||||
let iovec = libc::iovec {
|
||||
iov_base: buf as *mut libc::c_void,
|
||||
iov_len: desc.len() as libc::size_t,
|
||||
};
|
||||
iovecs.push(iovec);
|
||||
} else {
|
||||
error!(
|
||||
"Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}",
|
||||
desc_addr.0,
|
||||
desc.len(),
|
||||
desc.is_write_only()
|
||||
);
|
||||
return Err(NetQueuePairError::DescriptorChainInvalid);
|
||||
}
|
||||
next_desc = desc_chain.next();
|
||||
}
|
||||
|
||||
let len = if !iovecs.is_empty() {
|
||||
let result = unsafe {
|
||||
libc::writev(
|
||||
tap.as_raw_fd() as libc::c_int,
|
||||
iovecs.as_ptr() as *const libc::iovec,
|
||||
iovecs.len() as libc::c_int,
|
||||
)
|
||||
};
|
||||
|
||||
if result < 0 {
|
||||
let e = std::io::Error::last_os_error();
|
||||
|
||||
/* EAGAIN */
|
||||
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||
queue.go_to_previous_position();
|
||||
retry_write = true;
|
||||
break;
|
||||
}
|
||||
error!("net: tx: failed writing to tap: {}", e);
|
||||
return Err(NetQueuePairError::WriteTap(e));
|
||||
}
|
||||
|
||||
self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
|
||||
self.counter_frames += Wrapping(1);
|
||||
|
||||
result as u32
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
// For the sake of simplicity (similar to the RX rate limiting), we always
|
||||
// let the 'last' descriptor chain go-through even if it was over the rate
|
||||
// limit, and simply stop processing oncoming `avail_desc` if any.
|
||||
if let Some(rate_limiter) = rate_limiter {
|
||||
rate_limit_reached = !rate_limiter.consume(1, TokenType::Ops)
|
||||
|| !rate_limiter.consume(len as u64, TokenType::Bytes);
|
||||
}
|
||||
|
||||
queue
|
||||
.add_used(used_desc_head.0, used_desc_head.1)
|
||||
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
|
||||
.map_err(NetQueuePairError::QueueAddUsed)?;
|
||||
|
||||
if !queue
|
||||
.enable_notification()
|
||||
.enable_notification(mem)
|
||||
.map_err(NetQueuePairError::QueueEnableNotification)?
|
||||
{
|
||||
break;
|
||||
@@ -166,125 +158,116 @@ impl RxVirtio {
|
||||
|
||||
pub fn process_desc_chain(
|
||||
&mut self,
|
||||
mem: &GuestMemoryMmap,
|
||||
tap: &mut Tap,
|
||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
queue: &mut Queue,
|
||||
rate_limiter: &mut Option<RateLimiter>,
|
||||
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
||||
) -> Result<bool, NetQueuePairError> {
|
||||
let mut exhausted_descs = true;
|
||||
let mut rate_limit_reached = false;
|
||||
|
||||
loop {
|
||||
let used_desc_head: (u16, u32);
|
||||
let mut avail_iter = queue
|
||||
.iter()
|
||||
.map_err(NetQueuePairError::QueueIteratorFailed)?;
|
||||
|
||||
if let Some(mut desc_chain) = avail_iter.next() {
|
||||
if rate_limit_reached {
|
||||
exhausted_descs = false;
|
||||
avail_iter.go_to_previous_position();
|
||||
break;
|
||||
}
|
||||
|
||||
let desc = desc_chain
|
||||
.next()
|
||||
.ok_or(NetQueuePairError::DescriptorChainTooShort)?;
|
||||
|
||||
let num_buffers_addr = desc_chain
|
||||
.memory()
|
||||
.checked_offset(
|
||||
desc.addr()
|
||||
.translate_gva(access_platform, desc.len() as usize),
|
||||
10,
|
||||
)
|
||||
.unwrap();
|
||||
let mut next_desc = Some(desc);
|
||||
|
||||
let mut iovecs = Vec::new();
|
||||
while let Some(desc) = next_desc {
|
||||
let desc_addr = desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, desc.len() as usize);
|
||||
if desc.is_write_only() && desc.len() > 0 {
|
||||
let buf = desc_chain
|
||||
.memory()
|
||||
.get_slice(desc_addr, desc.len() as usize)
|
||||
.map_err(NetQueuePairError::GuestMemory)?
|
||||
.as_ptr();
|
||||
let iovec = libc::iovec {
|
||||
iov_base: buf as *mut libc::c_void,
|
||||
iov_len: desc.len() as libc::size_t,
|
||||
};
|
||||
iovecs.push(iovec);
|
||||
} else {
|
||||
error!(
|
||||
"Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}",
|
||||
desc_addr.0,
|
||||
desc.len(),
|
||||
desc.is_write_only()
|
||||
);
|
||||
return Err(NetQueuePairError::DescriptorChainInvalid);
|
||||
}
|
||||
next_desc = desc_chain.next();
|
||||
}
|
||||
|
||||
let len = if !iovecs.is_empty() {
|
||||
let result = unsafe {
|
||||
libc::readv(
|
||||
tap.as_raw_fd() as libc::c_int,
|
||||
iovecs.as_ptr() as *const libc::iovec,
|
||||
iovecs.len() as libc::c_int,
|
||||
)
|
||||
};
|
||||
if result < 0 {
|
||||
let e = std::io::Error::last_os_error();
|
||||
exhausted_descs = false;
|
||||
avail_iter.go_to_previous_position();
|
||||
|
||||
/* EAGAIN */
|
||||
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||
break;
|
||||
}
|
||||
|
||||
error!("net: rx: failed reading from tap: {}", e);
|
||||
return Err(NetQueuePairError::ReadTap(e));
|
||||
}
|
||||
|
||||
// Write num_buffers to guest memory. We simply write 1 as we
|
||||
// never spread the frame over more than one descriptor chain.
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_obj(1u16, num_buffers_addr)
|
||||
.map_err(NetQueuePairError::GuestMemory)?;
|
||||
|
||||
self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
|
||||
self.counter_frames += Wrapping(1);
|
||||
|
||||
result as u32
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
used_desc_head = (desc_chain.head_index(), len);
|
||||
|
||||
// For the sake of simplicity (keeping the handling of RX_QUEUE_EVENT and
|
||||
// RX_TAP_EVENT totally asynchronous), we always let the 'last' descriptor
|
||||
// chain go-through even if it was over the rate limit, and simply stop
|
||||
// processing oncoming `avail_desc` if any.
|
||||
if let Some(rate_limiter) = rate_limiter {
|
||||
rate_limit_reached = !rate_limiter.consume(1, TokenType::Ops)
|
||||
|| !rate_limiter.consume(len as u64, TokenType::Bytes);
|
||||
}
|
||||
} else {
|
||||
while let Some(mut desc_chain) = queue.pop_descriptor_chain(mem) {
|
||||
if rate_limit_reached {
|
||||
exhausted_descs = false;
|
||||
queue.go_to_previous_position();
|
||||
break;
|
||||
}
|
||||
|
||||
let desc = desc_chain
|
||||
.next()
|
||||
.ok_or(NetQueuePairError::DescriptorChainTooShort)?;
|
||||
|
||||
let num_buffers_addr = desc_chain
|
||||
.memory()
|
||||
.checked_offset(
|
||||
desc.addr()
|
||||
.translate_gva(access_platform, desc.len() as usize),
|
||||
10,
|
||||
)
|
||||
.unwrap();
|
||||
let mut next_desc = Some(desc);
|
||||
|
||||
let mut iovecs = Vec::new();
|
||||
while let Some(desc) = next_desc {
|
||||
let desc_addr = desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, desc.len() as usize);
|
||||
if desc.is_write_only() && desc.len() > 0 {
|
||||
let buf = desc_chain
|
||||
.memory()
|
||||
.get_slice(desc_addr, desc.len() as usize)
|
||||
.map_err(NetQueuePairError::GuestMemory)?
|
||||
.as_ptr();
|
||||
let iovec = libc::iovec {
|
||||
iov_base: buf as *mut libc::c_void,
|
||||
iov_len: desc.len() as libc::size_t,
|
||||
};
|
||||
iovecs.push(iovec);
|
||||
} else {
|
||||
error!(
|
||||
"Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}",
|
||||
desc_addr.0,
|
||||
desc.len(),
|
||||
desc.is_write_only()
|
||||
);
|
||||
return Err(NetQueuePairError::DescriptorChainInvalid);
|
||||
}
|
||||
next_desc = desc_chain.next();
|
||||
}
|
||||
|
||||
let len = if !iovecs.is_empty() {
|
||||
let result = unsafe {
|
||||
libc::readv(
|
||||
tap.as_raw_fd() as libc::c_int,
|
||||
iovecs.as_ptr() as *const libc::iovec,
|
||||
iovecs.len() as libc::c_int,
|
||||
)
|
||||
};
|
||||
if result < 0 {
|
||||
let e = std::io::Error::last_os_error();
|
||||
exhausted_descs = false;
|
||||
queue.go_to_previous_position();
|
||||
|
||||
/* EAGAIN */
|
||||
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||
break;
|
||||
}
|
||||
|
||||
error!("net: rx: failed reading from tap: {}", e);
|
||||
return Err(NetQueuePairError::ReadTap(e));
|
||||
}
|
||||
|
||||
// Write num_buffers to guest memory. We simply write 1 as we
|
||||
// never spread the frame over more than one descriptor chain.
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_obj(1u16, num_buffers_addr)
|
||||
.map_err(NetQueuePairError::GuestMemory)?;
|
||||
|
||||
self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
|
||||
self.counter_frames += Wrapping(1);
|
||||
|
||||
result as u32
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
// For the sake of simplicity (keeping the handling of RX_QUEUE_EVENT and
|
||||
// RX_TAP_EVENT totally asynchronous), we always let the 'last' descriptor
|
||||
// chain go-through even if it was over the rate limit, and simply stop
|
||||
// processing oncoming `avail_desc` if any.
|
||||
if let Some(rate_limiter) = rate_limiter {
|
||||
rate_limit_reached = !rate_limiter.consume(1, TokenType::Ops)
|
||||
|| !rate_limiter.consume(len as u64, TokenType::Bytes);
|
||||
}
|
||||
|
||||
queue
|
||||
.add_used(used_desc_head.0, used_desc_head.1)
|
||||
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
|
||||
.map_err(NetQueuePairError::QueueAddUsed)?;
|
||||
|
||||
if !queue
|
||||
.enable_notification()
|
||||
.enable_notification(mem)
|
||||
.map_err(NetQueuePairError::QueueEnableNotification)?
|
||||
{
|
||||
break;
|
||||
@@ -303,31 +286,31 @@ pub struct NetCounters {
|
||||
pub rx_frames: Arc<AtomicU64>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum NetQueuePairError {
|
||||
/// No memory configured
|
||||
#[error("No memory configured.")]
|
||||
NoMemoryConfigured,
|
||||
/// Error registering listener
|
||||
#[error("Error registering listener: {0}")]
|
||||
RegisterListener(io::Error),
|
||||
/// Error unregistering listener
|
||||
#[error("Error unregistering listener: {0}")]
|
||||
UnregisterListener(io::Error),
|
||||
/// Error writing to the TAP device
|
||||
#[error("Error writing to the TAP device: {0}")]
|
||||
WriteTap(io::Error),
|
||||
/// Error reading from the TAP device
|
||||
#[error("Error reading from the TAP device: {0}")]
|
||||
ReadTap(io::Error),
|
||||
/// Error related to guest memory
|
||||
#[error("Error related to guest memory: {0}")]
|
||||
GuestMemory(vm_memory::GuestMemoryError),
|
||||
/// Returned an error while iterating through the queue
|
||||
#[error("Returned an error while iterating through the queue: {0}")]
|
||||
QueueIteratorFailed(virtio_queue::Error),
|
||||
/// Descriptor chain is too short
|
||||
#[error("Descriptor chain is too short.")]
|
||||
DescriptorChainTooShort,
|
||||
/// Descriptor chain does not contain valid descriptors
|
||||
#[error("Descriptor chain does not contain valid descriptors.")]
|
||||
DescriptorChainInvalid,
|
||||
/// Failed to determine if queue needed notification
|
||||
#[error("Failed to determine if queue needed notification: {0}")]
|
||||
QueueNeedsNotification(virtio_queue::Error),
|
||||
/// Failed to enable notification on the queue
|
||||
#[error("Failed to enable notification on the queue: {0}")]
|
||||
QueueEnableNotification(virtio_queue::Error),
|
||||
/// Failed to add used index to the queue
|
||||
#[error("Failed to add used index to the queue: {0}")]
|
||||
QueueAddUsed(virtio_queue::Error),
|
||||
}
|
||||
|
||||
@@ -355,9 +338,11 @@ pub struct NetQueuePair {
|
||||
impl NetQueuePair {
|
||||
pub fn process_tx(
|
||||
&mut self,
|
||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
mem: &GuestMemoryMmap,
|
||||
queue: &mut Queue,
|
||||
) -> Result<bool, NetQueuePairError> {
|
||||
let tx_tap_retry = self.tx.process_desc_chain(
|
||||
mem,
|
||||
&mut self.tap,
|
||||
queue,
|
||||
&mut self.tx_rate_limiter,
|
||||
@@ -397,15 +382,17 @@ impl NetQueuePair {
|
||||
self.tx.counter_frames = Wrapping(0);
|
||||
|
||||
queue
|
||||
.needs_notification()
|
||||
.needs_notification(mem)
|
||||
.map_err(NetQueuePairError::QueueNeedsNotification)
|
||||
}
|
||||
|
||||
pub fn process_rx(
|
||||
&mut self,
|
||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
mem: &GuestMemoryMmap,
|
||||
queue: &mut Queue,
|
||||
) -> Result<bool, NetQueuePairError> {
|
||||
self.rx_desc_avail = !self.rx.process_desc_chain(
|
||||
mem,
|
||||
&mut self.tap,
|
||||
queue,
|
||||
&mut self.rx_rate_limiter,
|
||||
@@ -440,7 +427,7 @@ impl NetQueuePair {
|
||||
self.rx.counter_frames = Wrapping(0);
|
||||
|
||||
queue
|
||||
.needs_notification()
|
||||
.needs_notification(mem)
|
||||
.map_err(NetQueuePairError::QueueNeedsNotification)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,24 +15,26 @@ use std::io::{Error as IoError, Read, Result as IoResult, Write};
|
||||
use std::net;
|
||||
use std::os::raw::*;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use thiserror::Error;
|
||||
use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
/// Couldn't open /dev/net/tun.
|
||||
#[error("Couldn't open /dev/net/tun: {0}")]
|
||||
OpenTun(IoError),
|
||||
/// Unable to configure tap interface.
|
||||
#[error("Unable to configure tap interface: {0}")]
|
||||
ConfigureTap(IoError),
|
||||
/// Unable to retrieve features.
|
||||
#[error("Unable to retrieve features: {0}")]
|
||||
GetFeatures(IoError),
|
||||
/// Missing multiqueue support in the kernel.
|
||||
#[error("Missing multiqueue support in the kernel.")]
|
||||
MultiQueueKernelSupport,
|
||||
/// ioctl failed.
|
||||
#[error("ioctl failed: {0}")]
|
||||
IoctlError(IoError),
|
||||
/// Failed to create a socket.
|
||||
#[error("Failed to create a socket: {0}")]
|
||||
NetUtil(NetUtilError),
|
||||
#[error("Invalid interface name.")]
|
||||
InvalidIfname,
|
||||
/// Error parsing MAC data
|
||||
#[error("Error parsing MAC data: {0}")]
|
||||
MacParsing(IoError),
|
||||
}
|
||||
|
||||
|
||||
@@ -31,35 +31,49 @@ impl fmt::Display for OptionParserError {
|
||||
OptionParserError::UnknownOption(s) => write!(f, "unknown option: {}", s),
|
||||
OptionParserError::InvalidSyntax(s) => write!(f, "invalid syntax:{}", s),
|
||||
OptionParserError::Conversion(field, value) => {
|
||||
write!(f, "unable to parse {} for {}", value, field)
|
||||
write!(f, "unable to convert {} for {}", value, field)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
type OptionParserResult<T> = std::result::Result<T, OptionParserError>;
|
||||
|
||||
fn split_commas_outside_brackets(s: &str) -> OptionParserResult<Vec<String>> {
|
||||
fn split_commas(s: &str) -> OptionParserResult<Vec<String>> {
|
||||
let mut list: Vec<String> = Vec::new();
|
||||
let mut opened_brackets: usize = 0;
|
||||
for element in s.trim().split(',') {
|
||||
if opened_brackets > 0 {
|
||||
if let Some(last) = list.last_mut() {
|
||||
*last = format!("{},{}", last, element);
|
||||
} else {
|
||||
return Err(OptionParserError::InvalidSyntax(s.to_owned()));
|
||||
}
|
||||
} else {
|
||||
list.push(element.to_string());
|
||||
}
|
||||
let mut opened_brackets = 0;
|
||||
let mut in_quotes = false;
|
||||
let mut current = String::new();
|
||||
|
||||
opened_brackets += element.matches('[').count();
|
||||
let closing_brackets = element.matches(']').count();
|
||||
if closing_brackets > opened_brackets {
|
||||
return Err(OptionParserError::InvalidSyntax(s.to_owned()));
|
||||
} else {
|
||||
opened_brackets -= closing_brackets;
|
||||
for c in s.trim().chars() {
|
||||
match c {
|
||||
'[' => {
|
||||
opened_brackets += 1;
|
||||
current.push('[');
|
||||
}
|
||||
']' => {
|
||||
opened_brackets -= 1;
|
||||
if opened_brackets < 0 {
|
||||
return Err(OptionParserError::InvalidSyntax(s.to_owned()));
|
||||
}
|
||||
current.push(']');
|
||||
}
|
||||
'"' => in_quotes = !in_quotes,
|
||||
',' => {
|
||||
if opened_brackets > 0 || in_quotes {
|
||||
current.push(',')
|
||||
} else {
|
||||
list.push(current);
|
||||
current = String::new();
|
||||
}
|
||||
}
|
||||
c => current.push(c),
|
||||
}
|
||||
}
|
||||
list.push(current);
|
||||
|
||||
if opened_brackets != 0 || in_quotes {
|
||||
return Err(OptionParserError::InvalidSyntax(s.to_owned()));
|
||||
}
|
||||
|
||||
Ok(list)
|
||||
}
|
||||
@@ -76,7 +90,7 @@ impl OptionParser {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for option in split_commas_outside_brackets(input)?.iter() {
|
||||
for option in split_commas(input)?.iter() {
|
||||
let parts: Vec<&str> = option.splitn(2, '=').collect();
|
||||
|
||||
match self.options.get_mut(parts[0]) {
|
||||
@@ -291,9 +305,13 @@ impl<S: FromStr, T: TupleValue> FromStr for Tuple<S, T> {
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
let mut list: Vec<(S, T)> = Vec::new();
|
||||
|
||||
let tuples_list =
|
||||
split_commas_outside_brackets(s.trim().trim_matches(|c| c == '[' || c == ']'))
|
||||
.map_err(TupleError::SplitOutsideBrackets)?;
|
||||
let body = s
|
||||
.trim()
|
||||
.strip_prefix('[')
|
||||
.and_then(|s| s.strip_suffix(']'))
|
||||
.ok_or_else(|| TupleError::InvalidValue(s.to_string()))?;
|
||||
|
||||
let tuples_list = split_commas(body).map_err(TupleError::SplitOutsideBrackets)?;
|
||||
for tuple in tuples_list.iter() {
|
||||
let items: Vec<&str> = tuple.split('@').collect();
|
||||
|
||||
@@ -334,3 +352,58 @@ impl FromStr for StringList {
|
||||
Ok(StringList(string_list))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_option_parser() {
|
||||
let mut parser = OptionParser::new();
|
||||
parser
|
||||
.add("size")
|
||||
.add("mergeable")
|
||||
.add("hotplug_method")
|
||||
.add("hotplug_size")
|
||||
.add("topology")
|
||||
.add("cmdline");
|
||||
|
||||
assert!(parser.parse("size=128M,hanging_param").is_err());
|
||||
assert!(parser.parse("size=128M,too_many_equals=foo=bar").is_err());
|
||||
assert!(parser.parse("size=128M,file=/dev/shm").is_err());
|
||||
|
||||
assert!(parser.parse("size=128M").is_ok());
|
||||
assert_eq!(parser.get("size"), Some("128M".to_owned()));
|
||||
assert!(!parser.is_set("mergeable"));
|
||||
assert!(parser.is_set("size"));
|
||||
|
||||
assert!(parser.parse("size=128M,mergeable=on").is_ok());
|
||||
assert_eq!(parser.get("size"), Some("128M".to_owned()));
|
||||
assert_eq!(parser.get("mergeable"), Some("on".to_owned()));
|
||||
|
||||
assert!(parser
|
||||
.parse("size=128M,mergeable=on,topology=[1,2]")
|
||||
.is_ok());
|
||||
assert_eq!(parser.get("size"), Some("128M".to_owned()));
|
||||
assert_eq!(parser.get("mergeable"), Some("on".to_owned()));
|
||||
assert_eq!(parser.get("topology"), Some("[1,2]".to_owned()));
|
||||
|
||||
assert!(parser
|
||||
.parse("size=128M,mergeable=on,topology=[[1,2],[3,4]]")
|
||||
.is_ok());
|
||||
assert_eq!(parser.get("size"), Some("128M".to_owned()));
|
||||
assert_eq!(parser.get("mergeable"), Some("on".to_owned()));
|
||||
assert_eq!(parser.get("topology"), Some("[[1,2],[3,4]]".to_owned()));
|
||||
|
||||
assert!(parser.parse("topology=[").is_err());
|
||||
assert!(parser.parse("topology=[[[]]]]").is_err());
|
||||
|
||||
assert!(parser.parse("cmdline=\"console=ttyS0,9600n8\"").is_ok());
|
||||
assert_eq!(
|
||||
parser.get("cmdline"),
|
||||
Some("console=ttyS0,9600n8".to_owned())
|
||||
);
|
||||
assert!(parser.parse("cmdline=\"").is_err());
|
||||
assert!(parser.parse("cmdline=\"\"\"").is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,16 +10,16 @@ kvm = ["vfio-ioctls/kvm"]
|
||||
mshv = ["vfio-ioctls/mshv"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.58"
|
||||
anyhow = "1.0.62"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vfio_user = { path = "../vfio_user" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
libc = "0.2.126"
|
||||
vmm-sys-util = "0.10.0"
|
||||
libc = "0.2.131"
|
||||
log = "0.4.17"
|
||||
serde = { version="1.0.138", features=["derive"] }
|
||||
thiserror = "1.0.31"
|
||||
serde = { version = "1.0.143", features = ["derive"] }
|
||||
thiserror = "1.0.32"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
|
||||
@@ -190,7 +190,7 @@ pub trait PciProgrammingInterface {
|
||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||
#[allow(dead_code)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
#[repr(u8)]
|
||||
pub enum PciCapabilityId {
|
||||
ListId = 0,
|
||||
PowerManagement = 0x01,
|
||||
@@ -244,6 +244,118 @@ impl From<u8> for PciCapabilityId {
|
||||
}
|
||||
}
|
||||
|
||||
/// Types of PCI Express capabilities.
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
#[allow(dead_code)]
|
||||
#[repr(u16)]
|
||||
pub enum PciExpressCapabilityId {
|
||||
NullCapability = 0x0000,
|
||||
AdvancedErrorReporting = 0x0001,
|
||||
VirtualChannelMultiFunctionVirtualChannelNotPresent = 0x0002,
|
||||
DeviceSerialNumber = 0x0003,
|
||||
PowerBudgeting = 0x0004,
|
||||
RootComplexLinkDeclaration = 0x0005,
|
||||
RootComplexInternalLinkControl = 0x0006,
|
||||
RootComplexEventCollectorEndpointAssociation = 0x0007,
|
||||
MultiFunctionVirtualChannel = 0x0008,
|
||||
VirtualChannelMultiFunctionVirtualChannelPresent = 0x0009,
|
||||
RootComplexRegisterBlock = 0x000a,
|
||||
VendorSpecificExtendedCapability = 0x000b,
|
||||
ConfigurationAccessCorrelation = 0x000c,
|
||||
AccessControlServices = 0x000d,
|
||||
AlternativeRoutingIdentificationIntepretation = 0x000e,
|
||||
AddressTranslationServices = 0x000f,
|
||||
SingleRootIoVirtualization = 0x0010,
|
||||
DeprecatedMultiRootIoVirtualzation = 0x0011,
|
||||
Multicast = 0x0012,
|
||||
PageRequestInterface = 0x0013,
|
||||
ReservedForAmd = 0x0014,
|
||||
ResizeableBar = 0x0015,
|
||||
DynamicPowerAllocation = 0x0016,
|
||||
ThpRequester = 0x0017,
|
||||
LatencyToleranceReporting = 0x0018,
|
||||
SecondaryPciExpress = 0x0019,
|
||||
ProtocolMultiplexing = 0x001a,
|
||||
ProcessAddressSpaceId = 0x001b,
|
||||
LnRequestor = 0x001c,
|
||||
DownstreamPortContainment = 0x001d,
|
||||
L1PmSubstates = 0x001e,
|
||||
PrecisionTimeMeasurement = 0x001f,
|
||||
PciExpressOverMphy = 0x0020,
|
||||
FRSQueueing = 0x0021,
|
||||
ReadinessTimeReporting = 0x0022,
|
||||
DesignatedVendorSpecificExtendedCapability = 0x0023,
|
||||
VfResizeableBar = 0x0024,
|
||||
DataLinkFeature = 0x0025,
|
||||
PhysicalLayerSixteenGts = 0x0026,
|
||||
LaneMargeningAtTheReceiver = 0x0027,
|
||||
HierarchyId = 0x0028,
|
||||
NativePcieEnclosureManagement = 0x0029,
|
||||
PhysicalLayerThirtyTwoGts = 0x002a,
|
||||
AlternateProtocol = 0x002b,
|
||||
SystemFirmwareIntermediary = 0x002c,
|
||||
ShadowFunctions = 0x002d,
|
||||
DataObjectExchange = 0x002e,
|
||||
Reserved = 0x002f,
|
||||
ExtendedCapabilitiesAbsence = 0xffff,
|
||||
}
|
||||
|
||||
impl From<u16> for PciExpressCapabilityId {
|
||||
fn from(c: u16) -> Self {
|
||||
match c {
|
||||
0x0000 => PciExpressCapabilityId::NullCapability,
|
||||
0x0001 => PciExpressCapabilityId::AdvancedErrorReporting,
|
||||
0x0002 => PciExpressCapabilityId::VirtualChannelMultiFunctionVirtualChannelNotPresent,
|
||||
0x0003 => PciExpressCapabilityId::DeviceSerialNumber,
|
||||
0x0004 => PciExpressCapabilityId::PowerBudgeting,
|
||||
0x0005 => PciExpressCapabilityId::RootComplexLinkDeclaration,
|
||||
0x0006 => PciExpressCapabilityId::RootComplexInternalLinkControl,
|
||||
0x0007 => PciExpressCapabilityId::RootComplexEventCollectorEndpointAssociation,
|
||||
0x0008 => PciExpressCapabilityId::MultiFunctionVirtualChannel,
|
||||
0x0009 => PciExpressCapabilityId::VirtualChannelMultiFunctionVirtualChannelPresent,
|
||||
0x000a => PciExpressCapabilityId::RootComplexRegisterBlock,
|
||||
0x000b => PciExpressCapabilityId::VendorSpecificExtendedCapability,
|
||||
0x000c => PciExpressCapabilityId::ConfigurationAccessCorrelation,
|
||||
0x000d => PciExpressCapabilityId::AccessControlServices,
|
||||
0x000e => PciExpressCapabilityId::AlternativeRoutingIdentificationIntepretation,
|
||||
0x000f => PciExpressCapabilityId::AddressTranslationServices,
|
||||
0x0010 => PciExpressCapabilityId::SingleRootIoVirtualization,
|
||||
0x0011 => PciExpressCapabilityId::DeprecatedMultiRootIoVirtualzation,
|
||||
0x0012 => PciExpressCapabilityId::Multicast,
|
||||
0x0013 => PciExpressCapabilityId::PageRequestInterface,
|
||||
0x0014 => PciExpressCapabilityId::ReservedForAmd,
|
||||
0x0015 => PciExpressCapabilityId::ResizeableBar,
|
||||
0x0016 => PciExpressCapabilityId::DynamicPowerAllocation,
|
||||
0x0017 => PciExpressCapabilityId::ThpRequester,
|
||||
0x0018 => PciExpressCapabilityId::LatencyToleranceReporting,
|
||||
0x0019 => PciExpressCapabilityId::SecondaryPciExpress,
|
||||
0x001a => PciExpressCapabilityId::ProtocolMultiplexing,
|
||||
0x001b => PciExpressCapabilityId::ProcessAddressSpaceId,
|
||||
0x001c => PciExpressCapabilityId::LnRequestor,
|
||||
0x001d => PciExpressCapabilityId::DownstreamPortContainment,
|
||||
0x001e => PciExpressCapabilityId::L1PmSubstates,
|
||||
0x001f => PciExpressCapabilityId::PrecisionTimeMeasurement,
|
||||
0x0020 => PciExpressCapabilityId::PciExpressOverMphy,
|
||||
0x0021 => PciExpressCapabilityId::FRSQueueing,
|
||||
0x0022 => PciExpressCapabilityId::ReadinessTimeReporting,
|
||||
0x0023 => PciExpressCapabilityId::DesignatedVendorSpecificExtendedCapability,
|
||||
0x0024 => PciExpressCapabilityId::VfResizeableBar,
|
||||
0x0025 => PciExpressCapabilityId::DataLinkFeature,
|
||||
0x0026 => PciExpressCapabilityId::PhysicalLayerSixteenGts,
|
||||
0x0027 => PciExpressCapabilityId::LaneMargeningAtTheReceiver,
|
||||
0x0028 => PciExpressCapabilityId::HierarchyId,
|
||||
0x0029 => PciExpressCapabilityId::NativePcieEnclosureManagement,
|
||||
0x002a => PciExpressCapabilityId::PhysicalLayerThirtyTwoGts,
|
||||
0x002b => PciExpressCapabilityId::AlternateProtocol,
|
||||
0x002c => PciExpressCapabilityId::SystemFirmwareIntermediary,
|
||||
0x002d => PciExpressCapabilityId::ShadowFunctions,
|
||||
0x002e => PciExpressCapabilityId::DataObjectExchange,
|
||||
0xffff => PciExpressCapabilityId::ExtendedCapabilitiesAbsence,
|
||||
_ => PciExpressCapabilityId::Reserved,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A PCI capability list. Devices can optionally specify capabilities in their configuration space.
|
||||
pub trait PciCapability {
|
||||
fn bytes(&self) -> &[u8];
|
||||
@@ -999,6 +1111,12 @@ impl PciBarConfiguration {
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn set_prefetchable(mut self, prefetchable: PciBarPrefetchable) -> Self {
|
||||
self.prefetchable = prefetchable;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn idx(&self) -> usize {
|
||||
self.idx
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ mod vfio_user;
|
||||
pub use self::bus::{PciBus, PciConfigIo, PciConfigMmio, PciRoot, PciRootError};
|
||||
pub use self::configuration::{
|
||||
PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciCapability, PciCapabilityId,
|
||||
PciClassCode, PciConfiguration, PciHeaderType, PciMassStorageSubclass,
|
||||
PciClassCode, PciConfiguration, PciExpressCapabilityId, PciHeaderType, PciMassStorageSubclass,
|
||||
PciNetworkControllerSubclass, PciProgrammingInterface, PciSerialBusSubClass, PciSubclass,
|
||||
};
|
||||
pub use self::device::{
|
||||
|
||||
@@ -5,14 +5,15 @@
|
||||
|
||||
use crate::{
|
||||
msi_num_enabled_vectors, BarReprogrammingParams, MsiCap, MsiConfig, MsixCap, MsixConfig,
|
||||
PciBarConfiguration, PciBarRegionType, PciBdf, PciCapabilityId, PciClassCode, PciConfiguration,
|
||||
PciDevice, PciDeviceError, PciHeaderType, PciSubclass, MSIX_TABLE_ENTRY_SIZE,
|
||||
PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciBdf, PciCapabilityId,
|
||||
PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciExpressCapabilityId,
|
||||
PciHeaderType, PciSubclass, MSIX_TABLE_ENTRY_SIZE,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use hypervisor::HypervisorVmError;
|
||||
use std::any::Any;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::io;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::ptr::null_mut;
|
||||
@@ -389,6 +390,11 @@ struct VfioCommonState {
|
||||
|
||||
impl VersionMapped for VfioCommonState {}
|
||||
|
||||
pub(crate) struct ConfigPatch {
|
||||
mask: u32,
|
||||
patch: u32,
|
||||
}
|
||||
|
||||
pub(crate) struct VfioCommon {
|
||||
pub(crate) configuration: PciConfiguration,
|
||||
pub(crate) mmio_regions: Vec<MmioRegion>,
|
||||
@@ -396,6 +402,7 @@ pub(crate) struct VfioCommon {
|
||||
pub(crate) msi_interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
pub(crate) legacy_interrupt_group: Option<Arc<dyn InterruptSourceGroup>>,
|
||||
pub(crate) vfio_wrapper: Arc<dyn Vfio>,
|
||||
pub(crate) patches: HashMap<usize, ConfigPatch>,
|
||||
}
|
||||
|
||||
impl VfioCommon {
|
||||
@@ -415,6 +422,7 @@ impl VfioCommon {
|
||||
while bar_id < VFIO_PCI_CONFIG_REGION_INDEX {
|
||||
let mut region_size: u64 = 0;
|
||||
let mut region_type = PciBarRegionType::Memory32BitRegion;
|
||||
let mut prefetchable = PciBarPrefetchable::NotPrefetchable;
|
||||
let mut flags: u32 = 0;
|
||||
|
||||
let mut restored_bar_addr = None;
|
||||
@@ -467,6 +475,13 @@ impl VfioCommon {
|
||||
false
|
||||
};
|
||||
|
||||
if matches!(
|
||||
flags & PCI_CONFIG_BAR_PREFETCHABLE,
|
||||
PCI_CONFIG_BAR_PREFETCHABLE
|
||||
) {
|
||||
prefetchable = PciBarPrefetchable::Prefetchable
|
||||
};
|
||||
|
||||
// To get size write all 1s
|
||||
self.vfio_wrapper
|
||||
.write_config_dword(bar_offset, 0xffff_ffff);
|
||||
@@ -566,7 +581,8 @@ impl VfioCommon {
|
||||
.set_index(bar_id as usize)
|
||||
.set_address(bar_addr.raw_value())
|
||||
.set_size(region_size)
|
||||
.set_region_type(region_type);
|
||||
.set_region_type(region_type)
|
||||
.set_prefetchable(prefetchable);
|
||||
|
||||
if bar_id == VFIO_PCI_ROM_REGION_INDEX {
|
||||
self.configuration
|
||||
@@ -684,6 +700,9 @@ impl VfioCommon {
|
||||
.vfio_wrapper
|
||||
.read_config_byte(PCI_CONFIG_CAPABILITY_OFFSET);
|
||||
|
||||
let mut pci_express_cap_found = false;
|
||||
let mut power_management_cap_found = false;
|
||||
|
||||
while cap_next != 0 {
|
||||
let cap_id = self.vfio_wrapper.read_config_byte(cap_next.into());
|
||||
|
||||
@@ -709,11 +728,50 @@ impl VfioCommon {
|
||||
}
|
||||
}
|
||||
}
|
||||
PciCapabilityId::PciExpress => pci_express_cap_found = true,
|
||||
PciCapabilityId::PowerManagement => power_management_cap_found = true,
|
||||
_ => {}
|
||||
};
|
||||
|
||||
cap_next = self.vfio_wrapper.read_config_byte((cap_next + 1).into());
|
||||
}
|
||||
|
||||
if pci_express_cap_found && power_management_cap_found {
|
||||
self.parse_extended_capabilities();
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_extended_capabilities(&mut self) {
|
||||
let mut current_offset = PCI_CONFIG_EXTENDED_CAPABILITY_OFFSET;
|
||||
|
||||
loop {
|
||||
let ext_cap_hdr = self.vfio_wrapper.read_config_dword(current_offset);
|
||||
|
||||
let cap_id: u16 = (ext_cap_hdr & 0xffff) as u16;
|
||||
let cap_next: u16 = ((ext_cap_hdr >> 20) & 0xfff) as u16;
|
||||
|
||||
match PciExpressCapabilityId::from(cap_id) {
|
||||
PciExpressCapabilityId::AlternativeRoutingIdentificationIntepretation
|
||||
| PciExpressCapabilityId::ResizeableBar
|
||||
| PciExpressCapabilityId::SingleRootIoVirtualization => {
|
||||
let reg_idx = (current_offset / 4) as usize;
|
||||
self.patches.insert(
|
||||
reg_idx,
|
||||
ConfigPatch {
|
||||
mask: 0x0000_ffff,
|
||||
patch: PciExpressCapabilityId::NullCapability as u32,
|
||||
},
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if cap_next == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
current_offset = cap_next.into();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enable_intx(&mut self) -> Result<(), VfioPciError> {
|
||||
@@ -1001,7 +1059,13 @@ impl VfioCommon {
|
||||
};
|
||||
|
||||
// The config register read comes from the VFIO device itself.
|
||||
self.vfio_wrapper.read_config_dword((reg_idx * 4) as u32) & mask
|
||||
let mut value = self.vfio_wrapper.read_config_dword((reg_idx * 4) as u32) & mask;
|
||||
|
||||
if let Some(config_patch) = self.patches.get(®_idx) {
|
||||
value = (value & !config_patch.mask) | config_patch.patch;
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
fn state(&self) -> VfioCommonState {
|
||||
@@ -1186,6 +1250,7 @@ impl VfioPciDevice {
|
||||
msi_interrupt_manager,
|
||||
legacy_interrupt_group,
|
||||
vfio_wrapper: Arc::new(vfio_wrapper) as Arc<dyn Vfio>,
|
||||
patches: HashMap::new(),
|
||||
};
|
||||
|
||||
// No need to parse capabilities from the device if on the restore path.
|
||||
@@ -1501,10 +1566,14 @@ impl BusDevice for VfioPciDevice {
|
||||
const PCI_CONFIG_BAR_OFFSET: u32 = 0x10;
|
||||
// Capability register offset in the PCI config space.
|
||||
const PCI_CONFIG_CAPABILITY_OFFSET: u32 = 0x34;
|
||||
// Extended capabilities register offset in the PCI config space.
|
||||
const PCI_CONFIG_EXTENDED_CAPABILITY_OFFSET: u32 = 0x100;
|
||||
// IO BAR when first BAR bit is 1.
|
||||
const PCI_CONFIG_IO_BAR: u32 = 0x1;
|
||||
// 64-bit memory bar flag.
|
||||
const PCI_CONFIG_MEMORY_BAR_64BIT: u32 = 0x4;
|
||||
// Prefetchable BAR bit
|
||||
const PCI_CONFIG_BAR_PREFETCHABLE: u32 = 0x8;
|
||||
// PCI config register size (4 bytes).
|
||||
const PCI_CONFIG_REGISTER_SIZE: usize = 4;
|
||||
// Number of BARs for a PCI device
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::{
|
||||
use anyhow::anyhow;
|
||||
use hypervisor::HypervisorVmError;
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::os::unix::prelude::AsRawFd;
|
||||
use std::ptr::null_mut;
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
@@ -112,6 +113,7 @@ impl VfioUserPciDevice {
|
||||
msi_interrupt_manager,
|
||||
legacy_interrupt_group,
|
||||
vfio_wrapper: Arc::new(vfio_wrapper) as Arc<dyn Vfio>,
|
||||
patches: HashMap::new(),
|
||||
};
|
||||
|
||||
// No need to parse capabilities from the device if on the restore path.
|
||||
|
||||
@@ -6,13 +6,13 @@ edition = "2021"
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.2.8", features = ["wrap_help","cargo"] }
|
||||
clap = { version = "3.2.17", features = ["wrap_help","cargo"] }
|
||||
dirs = "4.0.0"
|
||||
serde = { version = "1.0.138", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.82"
|
||||
serde = { version = "1.0.143", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.83"
|
||||
test_infra = { path = "../test_infra" }
|
||||
thiserror = "1.0.31"
|
||||
thiserror = "1.0.32"
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.2.8", features = ["cargo"] }
|
||||
clap = { version = "3.2.17", features = ["cargo"] }
|
||||
|
||||
@@ -312,7 +312,7 @@ fn measure_virtio_net_latency(guest: &Guest, test_timeout: u32) -> Result<Vec<f6
|
||||
.ssh_command(&format!("{} -s &> /dev/null &", ethr_remote_path))
|
||||
.map_err(InfraError::SshCommand)?;
|
||||
|
||||
thread::sleep(Duration::new(1, 0));
|
||||
thread::sleep(Duration::new(10, 0));
|
||||
|
||||
// Start the ethr client on the host
|
||||
let log_file = guest
|
||||
|
||||
@@ -10,7 +10,7 @@ path = "src/qcow.rs"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.4.3"
|
||||
libc = "0.2.126"
|
||||
libc = "0.2.131"
|
||||
log = "0.4.17"
|
||||
remain = "0.2.3"
|
||||
vmm-sys-util = "0.9.0"
|
||||
remain = "0.2.4"
|
||||
vmm-sys-util = "0.10.0"
|
||||
|
||||
@@ -1487,16 +1487,11 @@ impl Write for QcowFile {
|
||||
let offset = self.file_offset_write(curr_addr)?;
|
||||
let count = self.limit_range_cluster(curr_addr, write_count - nwritten);
|
||||
|
||||
if let Err(e) = self.raw_file.file_mut().seek(SeekFrom::Start(offset)) {
|
||||
return Err(e);
|
||||
}
|
||||
if let Err(e) = self
|
||||
self.raw_file.file_mut().seek(SeekFrom::Start(offset))?;
|
||||
let count = self
|
||||
.raw_file
|
||||
.file_mut()
|
||||
.write(&buf[nwritten..(nwritten + count)])
|
||||
{
|
||||
return Err(e);
|
||||
}
|
||||
.write(&buf[nwritten..(nwritten + count)])?;
|
||||
|
||||
nwritten += count;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.126"
|
||||
libc = "0.2.131"
|
||||
log = "0.4.17"
|
||||
vmm-sys-util = "0.9.0"
|
||||
vmm-sys-util = "0.10.0"
|
||||
|
||||
155
release-notes.md
155
release-notes.md
@@ -1,28 +1,35 @@
|
||||
- [v26.0](#v260)
|
||||
- [SMBIOS Improvements via `--platform`](#smbios-improvements-via---platform)
|
||||
- [Unified Binary MSHV and KVM Support](#unified-binary-mshv-and-kvm-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||
- [Deprecations](#deprecations)
|
||||
- [Removals](#removals)
|
||||
- [Contributors](#contributors)
|
||||
- [v25.0](#v250)
|
||||
- [`ch-remote` Improvements](#ch-remote-improvements)
|
||||
- [VM "Coredump" Support](#vm-coredump-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||
- [Removals](#removals)
|
||||
- [Contributors](#contributors)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
||||
- [Removals](#removals-1)
|
||||
- [Contributors](#contributors-1)
|
||||
- [v24.0](#v240)
|
||||
- [Bypass Mode for `virtio-iommu`](#bypass-mode-for-virtio-iommu)
|
||||
- [Ensure Identifiers Uniqueness](#ensure-identifiers-uniqueness)
|
||||
- [Sparse Mmap support](#sparse-mmap-support)
|
||||
- [Expose Platform Serial Number](#expose-platform-serial-number)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
||||
- [Notable Improvements](#notable-improvements)
|
||||
- [Deprecations](#deprecations)
|
||||
- [Deprecations](#deprecations-1)
|
||||
- [New on the Website](#new-on-the-website)
|
||||
- [Contributors](#contributors-1)
|
||||
- [Contributors](#contributors-2)
|
||||
- [v23.1](#v231)
|
||||
- [v23.0](#v230)
|
||||
- [vDPA Support](#vdpa-support)
|
||||
- [Updated OS Support list](#updated-os-support-list)
|
||||
- [`AArch64` Memory Map Improvements](#aarch64-memory-map-improvements)
|
||||
- [`AMX` Support](#amx-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
||||
- [Deprecations](#deprecations-1)
|
||||
- [Contributors](#contributors-2)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-3)
|
||||
- [Deprecations](#deprecations-2)
|
||||
- [Contributors](#contributors-3)
|
||||
- [v22.1](#v221)
|
||||
- [v22.0](#v220)
|
||||
- [GDB Debug Stub Support](#gdb-debug-stub-support)
|
||||
@@ -33,13 +40,13 @@
|
||||
- [PMU Support for AArch64](#pmu-support-for-aarch64)
|
||||
- [Documentation Under CC-BY-4.0 License](#documentation-under-cc-by-40-license)
|
||||
- [Deprecation of "Classic" `virtiofsd`](#deprecation-of-classic-virtiofsd)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-3)
|
||||
- [Contributors](#contributors-3)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-4)
|
||||
- [Contributors](#contributors-4)
|
||||
- [v21.0](#v210)
|
||||
- [Efficient Local Live Migration (for Live Upgrade)](#efficient-local-live-migration-for-live-upgrade)
|
||||
- [Recommended Kernel is Now 5.15](#recommended-kernel-is-now-515)
|
||||
- [Notable Bug fixes](#notable-bug-fixes-4)
|
||||
- [Contributors](#contributors-4)
|
||||
- [Notable Bug fixes](#notable-bug-fixes-5)
|
||||
- [Contributors](#contributors-5)
|
||||
- [v20.2](#v202)
|
||||
- [v20.1](#v201)
|
||||
- [v20.0](#v200)
|
||||
@@ -48,8 +55,8 @@
|
||||
- [Improved VFIO support](#improved-vfio-support)
|
||||
- [Safer code](#safer-code)
|
||||
- [Extended documentation](#extended-documentation)
|
||||
- [Notable bug fixes](#notable-bug-fixes-5)
|
||||
- [Contributors](#contributors-5)
|
||||
- [Notable bug fixes](#notable-bug-fixes-6)
|
||||
- [Contributors](#contributors-6)
|
||||
- [v19.0](#v190)
|
||||
- [Improved PTY handling for serial and `virtio-console`](#improved-pty-handling-for-serial-and-virtio-console)
|
||||
- [PCI boot time optimisations](#pci-boot-time-optimisations)
|
||||
@@ -57,8 +64,8 @@
|
||||
- [Live migration enhancements](#live-migration-enhancements)
|
||||
- [`virtio-mem` support with `vfio-user`](#virtio-mem-support-with-vfio-user)
|
||||
- [AArch64 for `virtio-iommu`](#aarch64-for-virtio-iommu)
|
||||
- [Notable bug fixes](#notable-bug-fixes-6)
|
||||
- [Contributors](#contributors-6)
|
||||
- [Notable bug fixes](#notable-bug-fixes-7)
|
||||
- [Contributors](#contributors-7)
|
||||
- [v18.0](#v180)
|
||||
- [Experimental User Device (`vfio-user`) support](#experimental-user-device-vfio-user-support)
|
||||
- [Migration support for `vhost-user` devices](#migration-support-for-vhost-user-devices)
|
||||
@@ -68,31 +75,31 @@
|
||||
- [Live migration on MSHV hypervisor](#live-migration-on-mshv-hypervisor)
|
||||
- [AArch64 CPU topology support](#aarch64-cpu-topology-support)
|
||||
- [Power button support on AArch64](#power-button-support-on-aarch64)
|
||||
- [Notable bug fixes](#notable-bug-fixes-7)
|
||||
- [Contributors](#contributors-7)
|
||||
- [Notable bug fixes](#notable-bug-fixes-8)
|
||||
- [Contributors](#contributors-8)
|
||||
- [v17.0](#v170)
|
||||
- [ARM64 NUMA support using ACPI](#arm64-numa-support-using-acpi)
|
||||
- [`Seccomp` support for MSHV backend](#seccomp-support-for-mshv-backend)
|
||||
- [Hotplug of `macvtap` devices](#hotplug-of-macvtap-devices)
|
||||
- [Improved SGX support](#improved-sgx-support)
|
||||
- [Inflight tracking for `vhost-user` devices](#inflight-tracking-for-vhost-user-devices)
|
||||
- [Notable bug fixes](#notable-bug-fixes-8)
|
||||
- [Contributors](#contributors-8)
|
||||
- [Notable bug fixes](#notable-bug-fixes-9)
|
||||
- [Contributors](#contributors-9)
|
||||
- [v16.0](#v160)
|
||||
- [Improved live migration support](#improved-live-migration-support)
|
||||
- [Improved `vhost-user` support](#improved-vhost-user-support)
|
||||
- [ARM64 ACPI and UEFI support](#arm64-acpi-and-uefi-support)
|
||||
- [Notable bug fixes](#notable-bug-fixes-9)
|
||||
- [Notable bug fixes](#notable-bug-fixes-10)
|
||||
- [Removed functionality](#removed-functionality)
|
||||
- [Contributors](#contributors-9)
|
||||
- [Contributors](#contributors-10)
|
||||
- [v15.0](#v150)
|
||||
- [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees)
|
||||
- [Network device rate limiting](#network-device-rate-limiting)
|
||||
- [Support for runtime control of `virtio-net` guest offload](#support-for-runtime-control-of-virtio-net-guest-offload)
|
||||
- [`--api-socket` supports file descriptor parameter](#--api-socket-supports-file-descriptor-parameter)
|
||||
- [Bug fixes](#bug-fixes)
|
||||
- [Deprecations](#deprecations-2)
|
||||
- [Contributors](#contributors-10)
|
||||
- [Deprecations](#deprecations-3)
|
||||
- [Contributors](#contributors-11)
|
||||
- [v0.14.1](#v0141)
|
||||
- [v0.14.0](#v0140)
|
||||
- [Structured event monitoring](#structured-event-monitoring)
|
||||
@@ -101,8 +108,8 @@
|
||||
- [Updated hotplug documentation](#updated-hotplug-documentation)
|
||||
- [PTY control for serial and `virtio-console`](#pty-control-for-serial-and-virtio-console)
|
||||
- [Block device rate limiting](#block-device-rate-limiting)
|
||||
- [Deprecations](#deprecations-3)
|
||||
- [Contributors](#contributors-11)
|
||||
- [Deprecations](#deprecations-4)
|
||||
- [Contributors](#contributors-12)
|
||||
- [v0.13.0](#v0130)
|
||||
- [Wider VFIO device support](#wider-vfio-device-support)
|
||||
- [Improved huge page support](#improved-huge-page-support)
|
||||
@@ -110,13 +117,13 @@
|
||||
- [VHD disk image support](#vhd-disk-image-support)
|
||||
- [Improved Virtio device threading](#improved-virtio-device-threading)
|
||||
- [Clean shutdown support via synthetic power button](#clean-shutdown-support-via-synthetic-power-button)
|
||||
- [Contributors](#contributors-12)
|
||||
- [Contributors](#contributors-13)
|
||||
- [v0.12.0](#v0120)
|
||||
- [ARM64 enhancements](#arm64-enhancements)
|
||||
- [Removal of `vhost-user-net` and `vhost-user-block` self spawning](#removal-of-vhost-user-net-and-vhost-user-block-self-spawning)
|
||||
- [Migration of `vhost-user-fs` backend](#migration-of-vhost-user-fs-backend)
|
||||
- [Enhanced "info" API](#enhanced-info-api)
|
||||
- [Contributors](#contributors-13)
|
||||
- [Contributors](#contributors-14)
|
||||
- [v0.11.0](#v0110)
|
||||
- [`io_uring` support by default for `virtio-block`](#io_uring-support-by-default-for-virtio-block)
|
||||
- [Windows Guest Support](#windows-guest-support)
|
||||
@@ -128,15 +135,15 @@
|
||||
- [Default Log Level Changed](#default-log-level-changed)
|
||||
- [New `--balloon` Parameter Added](#new---balloon-parameter-added)
|
||||
- [Experimental `virtio-watchdog` Support](#experimental-virtio-watchdog-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-10)
|
||||
- [Contributors](#contributors-14)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-11)
|
||||
- [Contributors](#contributors-15)
|
||||
- [v0.10.0](#v0100)
|
||||
- [`virtio-block` Support for Multiple Descriptors](#virtio-block-support-for-multiple-descriptors)
|
||||
- [Memory Zones](#memory-zones)
|
||||
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements)
|
||||
- [Preliminary KVM HyperV Emulation Control](#preliminary-kvm-hyperv-emulation-control)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-11)
|
||||
- [Contributors](#contributors-15)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-12)
|
||||
- [Contributors](#contributors-16)
|
||||
- [v0.9.0](#v090)
|
||||
- [`io_uring` Based Block Device Support](#io_uring-based-block-device-support)
|
||||
- [Block and Network Device Statistics](#block-and-network-device-statistics)
|
||||
@@ -149,17 +156,17 @@
|
||||
- [Enhancements to ARM64 Support](#enhancements-to-arm64-support)
|
||||
- [Intel SGX Support](#intel-sgx-support)
|
||||
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-12)
|
||||
- [Contributors](#contributors-16)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-13)
|
||||
- [Contributors](#contributors-17)
|
||||
- [v0.8.0](#v080)
|
||||
- [Experimental Snapshot and Restore Support](#experimental-snapshot-and-restore-support)
|
||||
- [Experimental ARM64 Support](#experimental-arm64-support)
|
||||
- [Support for Using 5-level Paging in Guests](#support-for-using-5-level-paging-in-guests)
|
||||
- [Virtio Device Interrupt Suppression for Network Devices](#virtio-device-interrupt-suppression-for-network-devices)
|
||||
- [`vhost_user_fs` Improvements](#vhost_user_fs-improvements)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-13)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-14)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes)
|
||||
- [Contributors](#contributors-17)
|
||||
- [Contributors](#contributors-18)
|
||||
- [v0.7.0](#v070)
|
||||
- [Block, Network, Persistent Memory (PMEM), VirtioFS and Vsock hotplug](#block-network-persistent-memory-pmem-virtiofs-and-vsock-hotplug)
|
||||
- [Alternative `libc` Support](#alternative-libc-support)
|
||||
@@ -169,14 +176,14 @@
|
||||
- [`Seccomp` Sandboxing](#seccomp-sandboxing)
|
||||
- [Updated Distribution Support](#updated-distribution-support)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes-1)
|
||||
- [Contributors](#contributors-18)
|
||||
- [Contributors](#contributors-19)
|
||||
- [v0.6.0](#v060)
|
||||
- [Directly Assigned Devices Hotplug](#directly-assigned-devices-hotplug)
|
||||
- [Shared Filesystem Improvements](#shared-filesystem-improvements)
|
||||
- [Block and Networking IO Self Offloading](#block-and-networking-io-self-offloading)
|
||||
- [Command Line Interface](#command-line-interface)
|
||||
- [PVH Boot](#pvh-boot)
|
||||
- [Contributors](#contributors-19)
|
||||
- [Contributors](#contributors-20)
|
||||
- [v0.5.1](#v051)
|
||||
- [v0.5.0](#v050)
|
||||
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
|
||||
@@ -184,7 +191,7 @@
|
||||
- [New Interrupt Management Framework](#new-interrupt-management-framework)
|
||||
- [Development Tools](#development-tools)
|
||||
- [Kata Containers Integration](#kata-containers-integration)
|
||||
- [Contributors](#contributors-20)
|
||||
- [Contributors](#contributors-21)
|
||||
- [v0.4.0](#v040)
|
||||
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
|
||||
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
|
||||
@@ -193,7 +200,7 @@
|
||||
- [Userspace IOAPIC by default](#userspace-ioapic-by-default)
|
||||
- [PCI BAR reprogramming](#pci-bar-reprogramming)
|
||||
- [New `cloud-hypervisor` organization](#new-cloud-hypervisor-organization)
|
||||
- [Contributors](#contributors-21)
|
||||
- [Contributors](#contributors-22)
|
||||
- [v0.3.0](#v030)
|
||||
- [Block device offloading](#block-device-offloading)
|
||||
- [Network device backend](#network-device-backend)
|
||||
@@ -220,6 +227,72 @@
|
||||
- [Unit testing](#unit-testing)
|
||||
- [Integration tests parallelization](#integration-tests-parallelization)
|
||||
|
||||
# v26.0
|
||||
|
||||
This release has been tracked through the [v26.0
|
||||
project](https://github.com/cloud-hypervisor/cloud-hypervisor/projects/30).
|
||||
|
||||
### SMBIOS Improvements via `--platform`
|
||||
|
||||
`--platform` and the appropriate API structure has gained support for supplying
|
||||
OEM strings (primarily used to communicate metadata to systemd in the guest)
|
||||
(#4319, #4446) and support for specifying the UUID (#4389.)
|
||||
|
||||
### Unified Binary MSHV and KVM Support
|
||||
|
||||
Support for both the MSHV and KVM hypervisors can be compiled into the same
|
||||
binary with the detection of the hypervisor to use made at runtime.
|
||||
|
||||
### Notable Bug Fixes
|
||||
|
||||
* The prefetchable flag is preserved on BARs for VFIO devices (#4353, #4454)
|
||||
* PCI Express capabilties for functionality we do not support are now filtered
|
||||
out (#4456)
|
||||
* GDB breakpoint support is more reliable (#4354, #4363)
|
||||
* `SIGINT` and `SIGTERM` signals are now handled before the VM has booted
|
||||
(#4269, #4293)
|
||||
* Multiple API event loop handling bug fixes (#4309, #4362)
|
||||
* Incorrect assumptions in virtio queue numbering were addressed, allowing
|
||||
the`virtio-fs` driver in OVMF to be used (#4341, #4314)
|
||||
* VHDX file format header fix (#4291)
|
||||
* The same VFIO device cannot be added twice (#4453, #4463)
|
||||
* SMBIOS tables were being incorrectly generated (#4442)
|
||||
|
||||
### Deprecations
|
||||
|
||||
Deprecated features will be removed in a subsequent release and users should
|
||||
plan to use alternatives.
|
||||
|
||||
* The top-level `kernel` and `initramfs` members on the `VmConfig` have been
|
||||
moved inside a `PayloadConfig` as the `payload` member. The OpenAPI document
|
||||
has been updated to reflect the change and the old API members continue to
|
||||
function and are mapped to the new version. The expectation is that these old
|
||||
versions will be removed in the v28.0 release.
|
||||
|
||||
### Removals
|
||||
|
||||
The following functionality has been removed:
|
||||
|
||||
* The unused `poll_queue` parameter has been removed from `--disk` and
|
||||
equivalent. This was residual from the the removal of the `vhost-user-block`
|
||||
spawning feature (#4402.)
|
||||
|
||||
### Contributors
|
||||
|
||||
Many thanks to everyone who has contributed to our release:
|
||||
|
||||
* Alyssa Ross <hi@alyssa.is>
|
||||
* Anatol Belski <ab@php.net>
|
||||
* Archana Shinde <archana.m.shinde@intel.com>
|
||||
* Bo Chen <chen.bo@intel.com>
|
||||
* lizhaoxin1 <Lxiaoyouling@163.com>
|
||||
* Maximilian Nitsch <maximilian.nitsch@d3tn.com>
|
||||
* Michael Zhao <michael.zhao@arm.com>
|
||||
* Rob Bradford <robert.bradford@intel.com>
|
||||
* Sebastien Boeuf <sebastien.boeuf@intel.com>
|
||||
* Steven Dake <sdake@lambdal.com>
|
||||
* Wei Liu <liuwe@microsoft.com>
|
||||
|
||||
# v25.0
|
||||
|
||||
This release has been tracked through the [v25.0
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
Name: cloud-hypervisor
|
||||
Summary: Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on top of KVM.
|
||||
Version: 25.0
|
||||
Version: 26.0
|
||||
Release: 0%{?dist}
|
||||
License: ASL 2.0 or BSD-3-clause
|
||||
Group: Applications/System
|
||||
@@ -145,6 +145,9 @@ rm -rf %{buildroot}
|
||||
|
||||
|
||||
%changelog
|
||||
* Wed Aug 17 2022 Rob Bradford <robert.bradford@intel.com> 26.0-0
|
||||
- Update to 26.0
|
||||
|
||||
* Thu Jul 07 2022 Rob Bradford <robert.bradford@intel.com> 25.0-0
|
||||
- Update to 25.0
|
||||
|
||||
|
||||
49
scripts/common-aarch64.sh
Normal file
49
scripts/common-aarch64.sh
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
export BUILD_TARGET=${BUILD_TARGET-aarch64-unknown-linux-gnu}
|
||||
|
||||
WORKLOADS_DIR="$HOME/workloads"
|
||||
|
||||
mkdir -p "$WORKLOADS_DIR"
|
||||
|
||||
build_edk2() {
|
||||
EDK2_BUILD_DIR="$WORKLOADS_DIR/edk2_build"
|
||||
EDK2_REPO="https://github.com/tianocore/edk2.git"
|
||||
EDK2_DIR="$EDK2_BUILD_DIR/edk2"
|
||||
EDK2_PLAT_REPO="https://github.com/tianocore/edk2-platforms.git"
|
||||
EDK2_PLAT_DIR="$EDK2_BUILD_DIR/edk2-platforms"
|
||||
ACPICA_REPO="https://github.com/acpica/acpica.git"
|
||||
ACPICA_DIR="$EDK2_BUILD_DIR/acpica"
|
||||
export WORKSPACE="$EDK2_BUILD_DIR"
|
||||
export PACKAGES_PATH="$EDK2_DIR:$EDK2_PLAT_DIR"
|
||||
export IASL_PREFIX="$ACPICA_DIR/generate/unix/bin/"
|
||||
|
||||
if [ ! -d "$EDK2_BUILD_DIR" ]; then
|
||||
mkdir -p "$EDK2_BUILD_DIR"
|
||||
fi
|
||||
|
||||
# Prepare source code
|
||||
checkout_repo "$EDK2_DIR" "$EDK2_REPO" master "46b4606ba23498d3d0e66b53e498eb3d5d592586"
|
||||
pushd "$EDK2_DIR"
|
||||
git submodule update --init
|
||||
popd
|
||||
checkout_repo "$EDK2_PLAT_DIR" "$EDK2_PLAT_REPO" master "8227e9e9f6a8aefbd772b40138f835121ccb2307"
|
||||
checkout_repo "$ACPICA_DIR" "$ACPICA_REPO" master "b9c69f81a05c45611c91ea9cbce8756078d76233"
|
||||
|
||||
if [[ ! -f "$EDK2_DIR/.built" || \
|
||||
! -f "$EDK2_PLAT_DIR/.built" || \
|
||||
! -f "$ACPICA_DIR/.built" ]]; then
|
||||
pushd "$EDK2_BUILD_DIR"
|
||||
# Build
|
||||
make -C acpica -j `nproc`
|
||||
source edk2/edksetup.sh
|
||||
make -C edk2/BaseTools -j `nproc`
|
||||
build -a AARCH64 -t GCC5 -p ArmVirtPkg/ArmVirtCloudHv.dsc -b RELEASE -n 0
|
||||
cp Build/ArmVirtCloudHv-AARCH64/RELEASE_GCC5/FV/CLOUDHV_EFI.fd "$WORKLOADS_DIR"
|
||||
touch "$EDK2_DIR"/.built
|
||||
touch "$EDK2_PLAT_DIR"/.built
|
||||
touch "$ACPICA_DIR"/.built
|
||||
popd
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -471,7 +471,7 @@ cmd_tests() {
|
||||
--env USER="root" \
|
||||
--env CH_LIBC="${libc}" \
|
||||
"$CTR_IMAGE" \
|
||||
./scripts/run_integration_tests_windows.sh "$@" || fix_dir_perms $? || exit $?
|
||||
./scripts/run_integration_tests_windows_"$(uname -m)".sh "$@" || fix_dir_perms $? || exit $?
|
||||
fi
|
||||
|
||||
if [ "$integration_live_migration" = true ]; then
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
set -x
|
||||
|
||||
sudo apt install -y libncurses-dev gawk flex bison openssl libssl-dev dkms libelf-dev libudev-dev libpci-dev libiberty-dev autoconf git make dpkg-dev libmnl-dev pkg-config
|
||||
sudo apt install -y libncurses-dev gawk flex bison openssl libssl-dev dkms libelf-dev libudev-dev libpci-dev libiberty-dev autoconf git make dpkg-dev libmnl-dev pkg-config iproute2
|
||||
sudo sed -i -- 's/# deb-src/deb-src/g' /etc/apt/sources.list
|
||||
sudo apt update
|
||||
apt-get source linux-image-unsigned-`uname -r`
|
||||
@@ -21,15 +21,6 @@ sudo modprobe vhost_vdpa
|
||||
sudo modprobe vdpa_sim
|
||||
sudo modprobe vdpa_sim_blk
|
||||
sudo modprobe vdpa_sim_net
|
||||
# After the Jenkins builder are moved to Ubuntu 22.04, manually building and
|
||||
# installing iproute2/vdpa won't be required.
|
||||
# Tracked by: https://github.com/cloud-hypervisor/cloud-hypervisor/issues/3862
|
||||
git clone https://github.com/shemminger/iproute2.git
|
||||
pushd iproute2
|
||||
./configure
|
||||
make -j
|
||||
sudo make install
|
||||
popd
|
||||
# Create /dev/vhost-vdpa-0
|
||||
sudo vdpa dev add name vdpa-blk1 mgmtdev vdpasim_blk
|
||||
# Create /dev/vhost-vdpa-1
|
||||
@@ -39,4 +30,4 @@ sudo vdpa dev add name vdpa-net1 mgmtdev vdpasim_net
|
||||
sudo chmod 660 /dev/vhost-vdpa-0
|
||||
sudo chmod 660 /dev/vhost-vdpa-1
|
||||
sudo chmod 660 /dev/vhost-vdpa-2
|
||||
vdpa dev show -jp
|
||||
vdpa dev show -jp
|
||||
|
||||
@@ -3,55 +3,10 @@ set -x
|
||||
|
||||
source $HOME/.cargo/env
|
||||
source $(dirname "$0")/test-util.sh
|
||||
source $(dirname "$0")/common-aarch64.sh
|
||||
|
||||
export BUILD_TARGET=${BUILD_TARGET-aarch64-unknown-linux-gnu}
|
||||
|
||||
WORKLOADS_DIR="$HOME/workloads"
|
||||
WORKLOADS_LOCK="$WORKLOADS_DIR/integration_test.lock"
|
||||
|
||||
mkdir -p "$WORKLOADS_DIR"
|
||||
|
||||
build_edk2() {
|
||||
EDK2_BUILD_DIR="$WORKLOADS_DIR/edk2_build"
|
||||
EDK2_REPO="https://github.com/tianocore/edk2.git"
|
||||
EDK2_DIR="$EDK2_BUILD_DIR/edk2"
|
||||
EDK2_PLAT_REPO="https://github.com/tianocore/edk2-platforms.git"
|
||||
EDK2_PLAT_DIR="$EDK2_BUILD_DIR/edk2-platforms"
|
||||
ACPICA_REPO="https://github.com/acpica/acpica.git"
|
||||
ACPICA_DIR="$EDK2_BUILD_DIR/acpica"
|
||||
export WORKSPACE="$EDK2_BUILD_DIR"
|
||||
export PACKAGES_PATH="$EDK2_DIR:$EDK2_PLAT_DIR"
|
||||
export IASL_PREFIX="$ACPICA_DIR/generate/unix/bin/"
|
||||
|
||||
if [ ! -d "$EDK2_BUILD_DIR" ]; then
|
||||
mkdir -p "$EDK2_BUILD_DIR"
|
||||
fi
|
||||
|
||||
# Prepare source code
|
||||
checkout_repo "$EDK2_DIR" "$EDK2_REPO" master "46b4606ba23498d3d0e66b53e498eb3d5d592586"
|
||||
pushd "$EDK2_DIR"
|
||||
git submodule update --init
|
||||
popd
|
||||
checkout_repo "$EDK2_PLAT_DIR" "$EDK2_PLAT_REPO" master "8227e9e9f6a8aefbd772b40138f835121ccb2307"
|
||||
checkout_repo "$ACPICA_DIR" "$ACPICA_REPO" master "b9c69f81a05c45611c91ea9cbce8756078d76233"
|
||||
|
||||
if [[ ! -f "$EDK2_DIR/.built" || \
|
||||
! -f "$EDK2_PLAT_DIR/.built" || \
|
||||
! -f "$ACPICA_DIR/.built" ]]; then
|
||||
pushd "$EDK2_BUILD_DIR"
|
||||
# Build
|
||||
make -C acpica -j `nproc`
|
||||
source edk2/edksetup.sh
|
||||
make -C edk2/BaseTools -j `nproc`
|
||||
build -a AARCH64 -t GCC5 -p ArmVirtPkg/ArmVirtCloudHv.dsc -b RELEASE -n 0
|
||||
cp Build/ArmVirtCloudHv-AARCH64/RELEASE_GCC5/FV/CLOUDHV_EFI.fd "$WORKLOADS_DIR"
|
||||
touch "$EDK2_DIR"/.built
|
||||
touch "$EDK2_PLAT_DIR"/.built
|
||||
touch "$ACPICA_DIR"/.built
|
||||
popd
|
||||
fi
|
||||
}
|
||||
|
||||
build_spdk_nvme() {
|
||||
SPDK_DIR="$WORKLOADS_DIR/spdk"
|
||||
SPDK_REPO="https://github.com/spdk/spdk.git"
|
||||
@@ -295,13 +250,13 @@ echo 6144 | sudo tee /proc/sys/vm/nr_hugepages
|
||||
sudo chmod a+rwX /dev/hugepages
|
||||
|
||||
# Run all direct kernel boot (Device Tree) test cases in mod `parallel`
|
||||
time cargo test $features "parallel::$test_filter" --target $BUILD_TARGET -- ${test_binary_args[*]}
|
||||
time cargo test $features "common_parallel::$test_filter" --target $BUILD_TARGET -- ${test_binary_args[*]}
|
||||
RES=$?
|
||||
|
||||
# Run some tests in sequence since the result could be affected by other tests
|
||||
# running in parallel.
|
||||
if [ $RES -eq 0 ]; then
|
||||
time cargo test $features "sequential::$test_filter" --target $BUILD_TARGET -- --test-threads=1 ${test_binary_args[*]}
|
||||
time cargo test $features "common_sequential::$test_filter" --target $BUILD_TARGET -- --test-threads=1 ${test_binary_args[*]}
|
||||
RES=$?
|
||||
else
|
||||
exit $RES
|
||||
@@ -317,7 +272,14 @@ fi
|
||||
|
||||
# Run all test cases related to live migration
|
||||
if [ $RES -eq 0 ]; then
|
||||
time cargo test $features "live_migration::$test_filter" --target $BUILD_TARGET -- --test-threads=1 ${test_binary_args[*]}
|
||||
time cargo test $features "live_migration_parallel::$test_filter" --target $BUILD_TARGET -- ${test_binary_args[*]}
|
||||
RES=$?
|
||||
else
|
||||
exit $RES
|
||||
fi
|
||||
|
||||
if [ $RES -eq 0 ]; then
|
||||
time cargo test $features "live_migration_sequential::$test_filter" --target $BUILD_TARGET -- --test-threads=1 ${test_binary_args[*]}
|
||||
RES=$?
|
||||
else
|
||||
exit $RES
|
||||
|
||||
@@ -96,7 +96,15 @@ echo 6144 | sudo tee /proc/sys/vm/nr_hugepages
|
||||
sudo chmod a+rwX /dev/hugepages
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
time cargo test $features "live_migration::$test_filter" -- --test-threads=1 ${test_binary_args[*]}
|
||||
time cargo test $features "live_migration_parallel::$test_filter" -- ${test_binary_args[*]}
|
||||
RES=$?
|
||||
|
||||
# Run some tests in sequence since the result could be affected by other tests
|
||||
# running in parallel.
|
||||
if [ $RES -eq 0 ]; then
|
||||
export RUST_BACKTRACE=1
|
||||
time cargo test $features "live_migration_sequential::$test_filter" -- --test-threads=1 ${test_binary_args[*]}
|
||||
RES=$?
|
||||
fi
|
||||
|
||||
exit $RES
|
||||
|
||||
@@ -8,6 +8,33 @@ process_common_args "$@"
|
||||
# For now these values are default for kvm
|
||||
features=""
|
||||
|
||||
WORKLOADS_DIR="$HOME/workloads"
|
||||
FOCAL_OS_IMAGE_NAME="focal-server-cloudimg-amd64-custom-20210609-0.raw"
|
||||
FOCAL_OS_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_IMAGE_NAME"
|
||||
FW="$WORKLOADS_DIR/hypervisor-fw"
|
||||
VMLINUX_IMAGE="$WORKLOADS_DIR/vmlinux"
|
||||
|
||||
BLK_IMAGE="$WORKLOADS_DIR/blk.img"
|
||||
MNT_DIR="mount_image"
|
||||
rm -rf $BLK_IMAGE
|
||||
pushd $WORKLOADS_DIR
|
||||
fallocate -l 16M $BLK_IMAGE
|
||||
mkfs.ext4 -j $BLK_IMAGE
|
||||
mkdir $MNT_DIR
|
||||
sudo mount -t ext4 $BLK_IMAGE $MNT_DIR
|
||||
sudo bash -c "echo bar > $MNT_DIR/foo" || exit 1
|
||||
sudo umount $BLK_IMAGE
|
||||
rm -r $MNT_DIR
|
||||
popd
|
||||
|
||||
VFIO_DIR="$WORKLOADS_DIR/vfio"
|
||||
VFIO_DISK_IMAGE="$WORKLOADS_DIR/vfio.img"
|
||||
rm -rf $VFIO_DIR $VFIO_DISK_IMAGE
|
||||
mkdir -p $VFIO_DIR
|
||||
cp $FOCAL_OS_IMAGE $VFIO_DIR
|
||||
cp $FW $VFIO_DIR
|
||||
cp $VMLINUX_IMAGE $VFIO_DIR || exit 1
|
||||
|
||||
BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
|
||||
CFLAGS=""
|
||||
TARGET_CC=""
|
||||
@@ -18,10 +45,23 @@ fi
|
||||
|
||||
cargo build --all --release $features --target $BUILD_TARGET
|
||||
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||
strip target/$BUILD_TARGET/release/ch-remote
|
||||
|
||||
# We always copy a fresh version of our binary for our L2 guest.
|
||||
cp target/$BUILD_TARGET/release/cloud-hypervisor $VFIO_DIR
|
||||
cp target/$BUILD_TARGET/release/ch-remote $VFIO_DIR
|
||||
|
||||
# test_vfio rely on hugepages
|
||||
echo 6144 | sudo tee /proc/sys/vm/nr_hugepages
|
||||
sudo chmod a+rwX /dev/hugepages
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
time cargo test $features "vfio::$test_filter" -- --test-threads=1 ${test_binary_args[*]}
|
||||
time cargo test $features "vfio::test_vfio" -- ${test_binary_args[*]}
|
||||
RES=$?
|
||||
|
||||
if [ $RES -eq 0 ]; then
|
||||
time cargo test $features "vfio::test_nvidia" -- --test-threads=1 ${test_binary_args[*]}
|
||||
RES=$?
|
||||
fi
|
||||
|
||||
exit $RES
|
||||
|
||||
60
scripts/run_integration_tests_windows_aarch64.sh
Executable file
60
scripts/run_integration_tests_windows_aarch64.sh
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
set -x
|
||||
|
||||
source $HOME/.cargo/env
|
||||
source $(dirname "$0")/test-util.sh
|
||||
source $(dirname "$0")/common-aarch64.sh
|
||||
|
||||
process_common_args "$@"
|
||||
# For now these values are default for kvm
|
||||
features=""
|
||||
|
||||
# aarch64 not supported for MSHV
|
||||
if [[ "$hypervisor" = "mshv" ]]; then
|
||||
echo "AArch64 is not supported in Microsoft Hypervisor"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WIN_IMAGE_BASENAME="windows-11-iot-enterprise-aarch64.raw"
|
||||
WIN_IMAGE_FILE="$WORKLOADS_DIR/$WIN_IMAGE_BASENAME"
|
||||
|
||||
# Checkout and build EDK2
|
||||
OVMF_FW="$WORKLOADS_DIR/CLOUDHV_EFI.fd"
|
||||
build_edk2
|
||||
|
||||
BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
|
||||
CFLAGS=""
|
||||
TARGET_CC=""
|
||||
if [[ "${BUILD_TARGET}" == "aarch64-unknown-linux-musl" ]]; then
|
||||
export TARGET_CC="musl-gcc"
|
||||
export RUSTFLAGS="-C link-arg=-lgcc -C link_arg=-specs -C link_arg=/usr/lib/aarch64-linux-musl/musl-gcc.specs"
|
||||
fi
|
||||
|
||||
# Check if the images are present
|
||||
if [[ ! -f ${WIN_IMAGE_FILE} || ! -f ${OVMF_FW} ]]; then
|
||||
echo "Windows image/firmware not present in the host"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Use device mapper to create a snapshot of the Windows image
|
||||
img_blk_size=$(du -b -B 512 ${WIN_IMAGE_FILE} | awk '{print $1;}')
|
||||
loop_device=$(losetup --find --show --read-only ${WIN_IMAGE_FILE})
|
||||
dmsetup create windows-base --table "0 $img_blk_size linear $loop_device 0"
|
||||
dmsetup mknodes
|
||||
dmsetup create windows-snapshot-base --table "0 $img_blk_size snapshot-origin /dev/mapper/windows-base"
|
||||
dmsetup mknodes
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
cargo build --all --release $features --target $BUILD_TARGET
|
||||
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||
|
||||
# Only run with 1 thread to avoid tests interfering with one another because
|
||||
# Windows has a static IP configured
|
||||
time cargo test $features "windows::$test_filter" --target $BUILD_TARGET -- ${test_binary_args[*]}
|
||||
RES=$?
|
||||
|
||||
dmsetup remove_all -f
|
||||
losetup -D
|
||||
|
||||
exit $RES
|
||||
@@ -166,7 +166,7 @@ VFIO_DIR="$WORKLOADS_DIR/vfio"
|
||||
VFIO_DISK_IMAGE="$WORKLOADS_DIR/vfio.img"
|
||||
rm -rf $VFIO_DIR $VFIO_DISK_IMAGE
|
||||
mkdir -p $VFIO_DIR
|
||||
cp $FOCAL_OS_IMAGE $VFIO_DIR
|
||||
cp $FOCAL_OS_RAW_IMAGE $VFIO_DIR
|
||||
cp $FW $VFIO_DIR
|
||||
cp $VMLINUX_IMAGE $VFIO_DIR || exit 1
|
||||
|
||||
@@ -195,14 +195,14 @@ sudo chmod a+rwX /dev/hugepages
|
||||
ulimit -l unlimited
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
time cargo test $features "parallel::$test_filter" -- ${test_binary_args[*]}
|
||||
time cargo test $features "common_parallel::$test_filter" -- ${test_binary_args[*]}
|
||||
RES=$?
|
||||
|
||||
# Run some tests in sequence since the result could be affected by other tests
|
||||
# running in parallel.
|
||||
if [ $RES -eq 0 ]; then
|
||||
export RUST_BACKTRACE=1
|
||||
time cargo test $features "sequential::$test_filter" -- --test-threads=1 ${test_binary_args[*]}
|
||||
time cargo test $features "common_sequential::$test_filter" -- --test-threads=1 ${test_binary_args[*]}
|
||||
RES=$?
|
||||
fi
|
||||
|
||||
|
||||
1
scripts/windows-11-iot-enterprise-aarch64.raw.sha1
Normal file
1
scripts/windows-11-iot-enterprise-aarch64.raw.sha1
Normal file
@@ -0,0 +1 @@
|
||||
707e939654b341717d3f54b41c57eea4432978e5 windows-11-iot-enterprise-aarch64.raw
|
||||
168
src/main.rs
168
src/main.rs
@@ -32,14 +32,7 @@ enum Error {
|
||||
#[cfg(feature = "gdb")]
|
||||
#[error("Failed to create Debug EventFd: {0}")]
|
||||
CreateDebugEventFd(#[source] std::io::Error),
|
||||
#[cfg_attr(
|
||||
feature = "kvm",
|
||||
error("Failed to open hypervisor interface (is /dev/kvm available?): {0}")
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "mshv",
|
||||
error("Failed to open hypervisor interface (is /dev/mshv available?): {0}")
|
||||
)]
|
||||
#[error("Failed to open hypervisor interface (is hypervisor interface available?): {0}")]
|
||||
CreateHypervisor(#[source] hypervisor::HypervisorError),
|
||||
#[error("Failed to start the VMM thread: {0}")]
|
||||
StartVmmThread(#[source] vmm::Error),
|
||||
@@ -162,7 +155,7 @@ fn create_app<'a>(
|
||||
Arg::new("platform")
|
||||
.long("platform")
|
||||
.help(
|
||||
"num_pci_segments=<num pci segments>,iommu_segments=<list_of_segments>,serial_number=<(DMI) device serial number>",
|
||||
"num_pci_segments=<num_pci_segments>,iommu_segments=<list_of_segments>,serial_number=<dmi_device_serial_number>,uuid=<dmi_device_uuid>,oem_strings=<list_of_strings>",
|
||||
)
|
||||
.takes_value(true)
|
||||
.group("vm-config"),
|
||||
@@ -199,11 +192,18 @@ fn create_app<'a>(
|
||||
.min_values(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("firmware")
|
||||
.long("firmware")
|
||||
.help("Path to firmware that is loaded in an architectural specific way")
|
||||
.takes_value(true)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("kernel")
|
||||
.long("kernel")
|
||||
.help(
|
||||
"Path to loaded kernel. This may be a kernel or firmware that supports a PVH \
|
||||
"Path to kernel to load. This may be a kernel or firmware that supports a PVH \
|
||||
entry point (e.g. vmlinux) or architecture equivalent",
|
||||
)
|
||||
.takes_value(true)
|
||||
@@ -521,7 +521,13 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
// Before we start any threads, mask the signals we'll be
|
||||
// installing handlers for, to make sure they only ever run on the
|
||||
// dedicated signal handling thread we'll start in a bit.
|
||||
for sig in &vmm::vm::HANDLED_SIGNALS {
|
||||
for sig in &vmm::vm::Vm::HANDLED_SIGNALS {
|
||||
if let Err(e) = block_signal(*sig) {
|
||||
eprintln!("Error blocking signals: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
for sig in &vmm::Vmm::HANDLED_SIGNALS {
|
||||
if let Err(e) = block_signal(*sig) {
|
||||
eprintln!("Error blocking signals: {}", e);
|
||||
}
|
||||
@@ -568,16 +574,15 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
)
|
||||
.map_err(Error::StartVmmThread)?;
|
||||
|
||||
let payload_present =
|
||||
cmd_arguments.is_present("kernel") || cmd_arguments.is_present("firmware");
|
||||
|
||||
// Can't test for "vm-config" group as some have default values. The kernel (or tdx if enabled)
|
||||
// is the only required option for booting the VM.
|
||||
#[cfg(feature = "tdx")]
|
||||
let tdx_or_kernel_present =
|
||||
cmd_arguments.is_present("kernel") || cmd_arguments.is_present("tdx");
|
||||
let payload_present = payload_present || cmd_arguments.is_present("tdx");
|
||||
|
||||
#[cfg(not(feature = "tdx"))]
|
||||
let tdx_or_kernel_present = cmd_arguments.is_present("kernel");
|
||||
|
||||
if tdx_or_kernel_present {
|
||||
if payload_present {
|
||||
let vm_params = config::VmParams::from_arg_matches(&cmd_arguments);
|
||||
let vm_config = config::VmConfig::parse(vm_params).map_err(Error::ParsingConfig)?;
|
||||
|
||||
@@ -640,8 +645,8 @@ mod unit_tests {
|
||||
use crate::{create_app, prepare_default_values};
|
||||
use std::path::PathBuf;
|
||||
use vmm::config::{
|
||||
CmdlineConfig, ConsoleConfig, ConsoleOutputMode, CpuFeatures, CpusConfig, KernelConfig,
|
||||
MemoryConfig, RngConfig, VmConfig, VmParams,
|
||||
CmdlineConfig, ConsoleConfig, ConsoleOutputMode, CpuFeatures, CpusConfig, MemoryConfig,
|
||||
PayloadConfig, RngConfig, VmConfig, VmParams,
|
||||
};
|
||||
|
||||
fn get_vm_config_from_vec(args: &[&str]) -> VmConfig {
|
||||
@@ -674,7 +679,7 @@ mod unit_tests {
|
||||
#[test]
|
||||
fn test_valid_vm_config_default() {
|
||||
let cli = vec!["cloud-hypervisor", "--kernel", "/path/to/kernel"];
|
||||
let openapi = r#"{ "kernel": {"path": "/path/to/kernel"} }"#;
|
||||
let openapi = r#"{ "payload": {"kernel": "/path/to/kernel"} }"#;
|
||||
|
||||
// First we check we get identical VmConfig structures.
|
||||
let (result_vm_config, _) = compare_vm_config_cli_vs_json(&cli, openapi, true);
|
||||
@@ -702,13 +707,15 @@ mod unit_tests {
|
||||
prefault: false,
|
||||
zones: None,
|
||||
},
|
||||
kernel: Some(KernelConfig {
|
||||
path: PathBuf::from("/path/to/kernel"),
|
||||
}),
|
||||
initramfs: None,
|
||||
kernel: None,
|
||||
cmdline: CmdlineConfig {
|
||||
args: String::from(""),
|
||||
args: String::default(),
|
||||
},
|
||||
initramfs: None,
|
||||
payload: Some(PayloadConfig {
|
||||
kernel: Some(PathBuf::from("/path/to/kernel")),
|
||||
..Default::default()
|
||||
}),
|
||||
disks: None,
|
||||
net: None,
|
||||
rng: RngConfig {
|
||||
@@ -759,7 +766,7 @@ mod unit_tests {
|
||||
"boot=1",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"cpus": {"boot_vcpus": 1, "max_vcpus": 1}
|
||||
}"#,
|
||||
true,
|
||||
@@ -773,7 +780,7 @@ mod unit_tests {
|
||||
"boot=1,max=3",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"cpus": {"boot_vcpus": 1, "max_vcpus": 3}
|
||||
}"#,
|
||||
true,
|
||||
@@ -787,7 +794,7 @@ mod unit_tests {
|
||||
"boot=2,max=4",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"cpus": {"boot_vcpus": 1, "max_vcpus": 3}
|
||||
}"#,
|
||||
false,
|
||||
@@ -805,7 +812,7 @@ mod unit_tests {
|
||||
(
|
||||
vec!["cloud-hypervisor", "--kernel", "/path/to/kernel", "--memory", "size=1073741824"],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"memory": {"size": 1073741824}
|
||||
}"#,
|
||||
true,
|
||||
@@ -813,7 +820,7 @@ mod unit_tests {
|
||||
(
|
||||
vec!["cloud-hypervisor", "--kernel", "/path/to/kernel", "--memory", "size=1G"],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"memory": {"size": 1073741824}
|
||||
}"#,
|
||||
true,
|
||||
@@ -821,7 +828,7 @@ mod unit_tests {
|
||||
(
|
||||
vec!["cloud-hypervisor", "--kernel", "/path/to/kernel", "--memory", "size=1G,mergeable=on"],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"memory": {"size": 1073741824, "mergeable": true}
|
||||
}"#,
|
||||
true,
|
||||
@@ -829,7 +836,7 @@ mod unit_tests {
|
||||
(
|
||||
vec!["cloud-hypervisor", "--kernel", "/path/to/kernel", "--memory", "size=1G,mergeable=off"],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"memory": {"size": 1073741824, "mergeable": false}
|
||||
}"#,
|
||||
true,
|
||||
@@ -837,7 +844,7 @@ mod unit_tests {
|
||||
(
|
||||
vec!["cloud-hypervisor", "--kernel", "/path/to/kernel", "--memory", "size=1G,mergeable=on"],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"memory": {"size": 1073741824, "mergeable": false}
|
||||
}"#,
|
||||
false,
|
||||
@@ -845,7 +852,7 @@ mod unit_tests {
|
||||
(
|
||||
vec!["cloud-hypervisor", "--kernel", "/path/to/kernel", "--memory", "size=1G,hotplug_size=1G"],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"memory": {"size": 1073741824, "hotplug_method": "Acpi", "hotplug_size": 1073741824}
|
||||
}"#,
|
||||
true,
|
||||
@@ -853,7 +860,7 @@ mod unit_tests {
|
||||
(
|
||||
vec!["cloud-hypervisor", "--kernel", "/path/to/kernel", "--memory", "size=1G,hotplug_method=virtio-mem,hotplug_size=1G"],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"memory": {"size": 1073741824, "hotplug_method": "VirtioMem", "hotplug_size": 1073741824}
|
||||
}"#,
|
||||
true,
|
||||
@@ -870,7 +877,7 @@ mod unit_tests {
|
||||
vec![(
|
||||
vec!["cloud-hypervisor", "--kernel", "/path/to/kernel"],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"}
|
||||
"payload": {"kernel": "/path/to/kernel"}
|
||||
}"#,
|
||||
true,
|
||||
)]
|
||||
@@ -891,8 +898,7 @@ mod unit_tests {
|
||||
"arg1=foo arg2=bar",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"cmdline": {"args": "arg1=foo arg2=bar"}
|
||||
"payload": {"kernel": "/path/to/kernel", "cmdline": "arg1=foo arg2=bar"}
|
||||
}"#,
|
||||
true,
|
||||
)]
|
||||
@@ -915,7 +921,7 @@ mod unit_tests {
|
||||
"path=/path/to/disk/2",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"disks": [
|
||||
{"path": "/path/to/disk/1"},
|
||||
{"path": "/path/to/disk/2"}
|
||||
@@ -933,7 +939,7 @@ mod unit_tests {
|
||||
"path=/path/to/disk/2",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"disks": [
|
||||
{"path": "/path/to/disk/1"}
|
||||
]
|
||||
@@ -951,7 +957,7 @@ mod unit_tests {
|
||||
"vhost_user=true,socket=/tmp/sock1",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"memory" : { "shared": true, "size": 536870912 },
|
||||
"disks": [
|
||||
{"vhost_user":true, "vhost_socket":"/tmp/sock1"}
|
||||
@@ -970,7 +976,7 @@ mod unit_tests {
|
||||
"vhost_user=true,socket=/tmp/sock1",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"memory" : { "shared": true, "size": 536870912 },
|
||||
"disks": [
|
||||
{"vhost_user":true, "vhost_socket":"/tmp/sock1"}
|
||||
@@ -994,7 +1000,7 @@ mod unit_tests {
|
||||
(
|
||||
vec!["cloud-hypervisor", "--kernel", "/path/to/kernel", "--net", "mac="],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"net": []
|
||||
}"#,
|
||||
false,
|
||||
@@ -1002,7 +1008,7 @@ mod unit_tests {
|
||||
(
|
||||
vec!["cloud-hypervisor", "--kernel", "/path/to/kernel", "--net", "mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd"],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"net": [
|
||||
{"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd"}
|
||||
]
|
||||
@@ -1016,7 +1022,7 @@ mod unit_tests {
|
||||
"mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"net": [
|
||||
{"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0"}
|
||||
]
|
||||
@@ -1030,7 +1036,7 @@ mod unit_tests {
|
||||
"mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"net": [
|
||||
{"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4"}
|
||||
]
|
||||
@@ -1044,7 +1050,7 @@ mod unit_tests {
|
||||
"mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4,mask=5.6.7.8",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"net": [
|
||||
{"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8"}
|
||||
]
|
||||
@@ -1059,7 +1065,7 @@ mod unit_tests {
|
||||
"mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=4",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"cpus": {"boot_vcpus": 2, "max_vcpus": 2},
|
||||
"net": [
|
||||
{"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 4}
|
||||
@@ -1075,7 +1081,7 @@ mod unit_tests {
|
||||
"mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=4,queue_size=128",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"cpus": {"boot_vcpus": 2, "max_vcpus": 2},
|
||||
"net": [
|
||||
{"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 4, "queue_size": 128}
|
||||
@@ -1090,7 +1096,7 @@ mod unit_tests {
|
||||
"mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=2,queue_size=256",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"net": [
|
||||
{"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8"}
|
||||
]
|
||||
@@ -1104,7 +1110,7 @@ mod unit_tests {
|
||||
"mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4,mask=5.6.7.8",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"net": [
|
||||
{"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 2, "queue_size": 256}
|
||||
]
|
||||
@@ -1119,7 +1125,7 @@ mod unit_tests {
|
||||
"mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=2,queue_size=256,iommu=on",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"net": [
|
||||
{"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 2, "queue_size": 256, "iommu": true}
|
||||
]
|
||||
@@ -1134,7 +1140,7 @@ mod unit_tests {
|
||||
"mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=2,queue_size=256,iommu=on",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"net": [
|
||||
{"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 2, "queue_size": 256, "iommu": true}
|
||||
],
|
||||
@@ -1149,7 +1155,7 @@ mod unit_tests {
|
||||
"mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=2,queue_size=256,iommu=off",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"net": [
|
||||
{"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 2, "queue_size": 256, "iommu": false}
|
||||
]
|
||||
@@ -1159,7 +1165,7 @@ mod unit_tests {
|
||||
(
|
||||
vec!["cloud-hypervisor", "--kernel", "/path/to/kernel", "--memory", "shared=true", "--net", "mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,vhost_user=true,socket=/tmp/sock"],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"memory" : { "shared": true, "size": 536870912 },
|
||||
"net": [
|
||||
{"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "vhost_user": true, "vhost_socket": "/tmp/sock"}
|
||||
@@ -1185,7 +1191,7 @@ mod unit_tests {
|
||||
"src=/path/to/entropy/source",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"rng": {"src": "/path/to/entropy/source"}
|
||||
}"#,
|
||||
true,
|
||||
@@ -1208,7 +1214,7 @@ mod unit_tests {
|
||||
"tag=virtiofs2,socket=/path/to/sock2",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"memory" : { "shared": true, "size": 536870912 },
|
||||
"fs": [
|
||||
{"tag": "virtiofs1", "socket": "/path/to/sock1"},
|
||||
@@ -1226,7 +1232,7 @@ mod unit_tests {
|
||||
"tag=virtiofs2,socket=/path/to/sock2",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"memory" : { "shared": true, "size": 536870912 },
|
||||
"fs": [
|
||||
{"tag": "virtiofs1", "socket": "/path/to/sock1"}
|
||||
@@ -1242,7 +1248,7 @@ mod unit_tests {
|
||||
"tag=virtiofs1,socket=/path/to/sock1,num_queues=4",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"memory" : { "shared": true, "size": 536870912 },
|
||||
"cpus": {"boot_vcpus": 4, "max_vcpus": 4},
|
||||
"fs": [
|
||||
@@ -1259,7 +1265,7 @@ mod unit_tests {
|
||||
"tag=virtiofs1,socket=/path/to/sock1,num_queues=4,queue_size=128"
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"memory" : { "shared": true, "size": 536870912 },
|
||||
"cpus": {"boot_vcpus": 4, "max_vcpus": 4},
|
||||
"fs": [
|
||||
@@ -1288,7 +1294,7 @@ mod unit_tests {
|
||||
"file=/path/to/img/2,size=2G",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"pmem": [
|
||||
{"file": "/path/to/img/1", "size": 1073741824},
|
||||
{"file": "/path/to/img/2", "size": 2147483648}
|
||||
@@ -1306,7 +1312,7 @@ mod unit_tests {
|
||||
"file=/path/to/img/1,size=1G,iommu=on",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"pmem": [
|
||||
{"file": "/path/to/img/1", "size": 1073741824, "iommu": true}
|
||||
],
|
||||
@@ -1324,7 +1330,7 @@ mod unit_tests {
|
||||
"file=/path/to/img/1,size=1G,iommu=on",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"pmem": [
|
||||
{"file": "/path/to/img/1", "size": 1073741824, "iommu": true}
|
||||
]
|
||||
@@ -1344,7 +1350,7 @@ mod unit_tests {
|
||||
(
|
||||
vec!["cloud-hypervisor", "--kernel", "/path/to/kernel"],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"serial": {"mode": "Null"},
|
||||
"console": {"mode": "Tty"}
|
||||
}"#,
|
||||
@@ -1361,7 +1367,7 @@ mod unit_tests {
|
||||
"tty",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"}
|
||||
"payload": {"kernel": "/path/to/kernel"}
|
||||
}"#,
|
||||
true,
|
||||
),
|
||||
@@ -1376,7 +1382,7 @@ mod unit_tests {
|
||||
"off",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"serial": {"mode": "Tty"},
|
||||
"console": {"mode": "Off"}
|
||||
}"#,
|
||||
@@ -1395,7 +1401,7 @@ mod unit_tests {
|
||||
(
|
||||
vec!["cloud-hypervisor", "--kernel", "/path/to/kernel"],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"serial": {"mode": "Null"},
|
||||
"console": {"mode": "Tty"}
|
||||
}"#,
|
||||
@@ -1412,7 +1418,7 @@ mod unit_tests {
|
||||
"tty",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"}
|
||||
"payload": {"kernel": "/path/to/kernel"}
|
||||
}"#,
|
||||
true,
|
||||
),
|
||||
@@ -1427,7 +1433,7 @@ mod unit_tests {
|
||||
"pty",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"serial": {"mode": "Pty"},
|
||||
"console": {"mode": "Pty"}
|
||||
}"#,
|
||||
@@ -1454,7 +1460,7 @@ mod unit_tests {
|
||||
"path=/path/to/device/2",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"devices": [
|
||||
{"path": "/path/to/device/1"},
|
||||
{"path": "/path/to/device/2"}
|
||||
@@ -1472,7 +1478,7 @@ mod unit_tests {
|
||||
"path=/path/to/device/2",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"devices": [
|
||||
{"path": "/path/to/device/1"}
|
||||
]
|
||||
@@ -1488,7 +1494,7 @@ mod unit_tests {
|
||||
"path=/path/to/device,iommu=on",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"devices": [
|
||||
{"path": "/path/to/device", "iommu": true}
|
||||
],
|
||||
@@ -1505,7 +1511,7 @@ mod unit_tests {
|
||||
"path=/path/to/device,iommu=on",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"devices": [
|
||||
{"path": "/path/to/device", "iommu": true}
|
||||
]
|
||||
@@ -1521,7 +1527,7 @@ mod unit_tests {
|
||||
"path=/path/to/device,iommu=off",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"devices": [
|
||||
{"path": "/path/to/device", "iommu": false}
|
||||
]
|
||||
@@ -1548,7 +1554,7 @@ mod unit_tests {
|
||||
"path=/path/to/device/2,num_queues=2",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"vdpa": [
|
||||
{"path": "/path/to/device/1", "num_queues": 1},
|
||||
{"path": "/path/to/device/2", "num_queues": 2}
|
||||
@@ -1566,7 +1572,7 @@ mod unit_tests {
|
||||
"path=/path/to/device/2",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"vdpa": [
|
||||
{"path": "/path/to/device/1"}
|
||||
]
|
||||
@@ -1592,7 +1598,7 @@ mod unit_tests {
|
||||
"cid=123,socket=/path/to/sock/1",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"vsock": {"cid": 123, "socket": "/path/to/sock/1"}
|
||||
}"#,
|
||||
true,
|
||||
@@ -1606,7 +1612,7 @@ mod unit_tests {
|
||||
"cid=124,socket=/path/to/sock/1",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"vsock": {"cid": 123, "socket": "/path/to/sock/1"}
|
||||
}"#,
|
||||
false,
|
||||
@@ -1621,7 +1627,7 @@ mod unit_tests {
|
||||
"cid=123,socket=/path/to/sock/1,iommu=on",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"vsock": {"cid": 123, "socket": "/path/to/sock/1", "iommu": true},
|
||||
"iommu": true
|
||||
}"#,
|
||||
@@ -1637,7 +1643,7 @@ mod unit_tests {
|
||||
"cid=123,socket=/path/to/sock/1,iommu=on",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"vsock": {"cid": 123, "socket": "/path/to/sock/1", "iommu": true}
|
||||
}"#,
|
||||
false,
|
||||
@@ -1651,7 +1657,7 @@ mod unit_tests {
|
||||
"cid=123,socket=/path/to/sock/1,iommu=off",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"vsock": {"cid": 123, "socket": "/path/to/sock/1", "iommu": false}
|
||||
}"#,
|
||||
true,
|
||||
|
||||
@@ -47,7 +47,7 @@ write_files:
|
||||
# 1G ram requires 512 pages
|
||||
echo 512 | sudo tee /proc/sys/vm/nr_hugepages
|
||||
sudo chmod a+rwX /dev/hugepages
|
||||
/mnt/cloud-hypervisor --kernel /mnt/vmlinux --cmdline "console=hvc0 reboot=k panic=1 nomodules root=/dev/vda1 VFIOTAG" --disk path=/mnt/focal-server-cloudimg-amd64-custom-20210609-0.qcow2 path=/mnt/cloudinit.img --cpus boot=1 --memory size=512M,hotplug_size=1G,hugepages=on --device path=/sys/bus/pci/devices/0000:00:05.0/ path=/sys/bus/pci/devices/0000:00:07.0/ path=/sys/bus/pci/devices/0000:00:08.0/ --api-socket /tmp/ch_api.sock
|
||||
/mnt/cloud-hypervisor --kernel /mnt/vmlinux --cmdline "console=hvc0 reboot=k panic=1 nomodules root=/dev/vda1 VFIOTAG" --disk path=/mnt/focal-server-cloudimg-amd64-custom-20210609-0.raw path=/mnt/cloudinit.img --cpus boot=1 --memory size=512M,hotplug_size=1G,hugepages=on --device path=/sys/bus/pci/devices/0000:00:05.0/ path=/sys/bus/pci/devices/0000:00:07.0/ path=/sys/bus/pci/devices/0000:00:08.0/ --api-socket /tmp/ch_api.sock
|
||||
|
||||
-
|
||||
path: /etc/systemd/system/notify-booted.service
|
||||
|
||||
@@ -7,8 +7,8 @@ edition = "2021"
|
||||
[dependencies]
|
||||
dirs = "4.0.0"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.126"
|
||||
once_cell = "1.13.0"
|
||||
ssh2 = { version = "0.9.1", features = ["vendored-openssl"]}
|
||||
vmm-sys-util = "0.9.0"
|
||||
libc = "0.2.131"
|
||||
once_cell = "1.13.1"
|
||||
ssh2 = { version = "0.9.3", features = ["vendored-openssl"] }
|
||||
vmm-sys-util = "0.10.0"
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
@@ -14,7 +14,7 @@ use std::io::{Read, Write};
|
||||
use std::net::TcpListener;
|
||||
use std::net::TcpStream;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||
use std::path::Path;
|
||||
use std::process::{Child, Command, ExitStatus, Output, Stdio};
|
||||
use std::str::FromStr;
|
||||
@@ -82,6 +82,8 @@ impl GuestNetworkConfig {
|
||||
|
||||
// Reply on epoll w/ timeout to wait for guest connections faithfully
|
||||
let epoll_fd = epoll::create(true).expect("Cannot create epoll fd");
|
||||
// Use 'File' to enforce closing on 'epoll_fd'
|
||||
let _epoll_file = unsafe { fs::File::from_raw_fd(epoll_fd) };
|
||||
epoll::ctl(
|
||||
epoll_fd,
|
||||
epoll::ControlOptions::EPOLL_CTL_ADD,
|
||||
@@ -829,7 +831,7 @@ impl Guest {
|
||||
}
|
||||
|
||||
pub fn api_create_body(&self, cpu_count: u8, kernel_path: &str, kernel_cmd: &str) -> String {
|
||||
format! {"{{\"cpus\":{{\"boot_vcpus\":{},\"max_vcpus\":{}}},\"kernel\":{{\"path\":\"{}\"}},\"cmdline\":{{\"args\": \"{}\"}},\"net\":[{{\"ip\":\"{}\", \"mask\":\"255.255.255.0\", \"mac\":\"{}\"}}], \"disks\":[{{\"path\":\"{}\"}}, {{\"path\":\"{}\"}}]}}",
|
||||
format! {"{{\"cpus\":{{\"boot_vcpus\":{},\"max_vcpus\":{}}},\"payload\":{{\"kernel\":\"{}\",\"cmdline\": \"{}\"}},\"net\":[{{\"ip\":\"{}\", \"mask\":\"255.255.255.0\", \"mac\":\"{}\"}}], \"disks\":[{{\"path\":\"{}\"}}, {{\"path\":\"{}\"}}]}}",
|
||||
cpu_count,
|
||||
cpu_count,
|
||||
kernel_path,
|
||||
@@ -1240,15 +1242,24 @@ impl<'a> GuestCommand<'a> {
|
||||
|
||||
let fd = child.stdout.as_ref().unwrap().as_raw_fd();
|
||||
let pipesize = unsafe { libc::fcntl(fd, libc::F_SETPIPE_SZ, PIPE_SIZE) };
|
||||
if pipesize == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
let fd = child.stderr.as_ref().unwrap().as_raw_fd();
|
||||
let pipesize1 = unsafe { libc::fcntl(fd, libc::F_SETPIPE_SZ, PIPE_SIZE) };
|
||||
if pipesize1 == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
if pipesize >= PIPE_SIZE && pipesize1 >= PIPE_SIZE {
|
||||
Ok(child)
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"resizing pipe w/ 'fnctl' failed!",
|
||||
format!(
|
||||
"resizing pipe w/ 'fnctl' failed: stdout pipesize {}, stderr pipesize {}",
|
||||
pipesize, pipesize1
|
||||
),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,15 +5,14 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.58"
|
||||
libc = "0.2.126"
|
||||
libc = "0.2.131"
|
||||
log = "0.4.17"
|
||||
serde = {version = ">=1.0.27", features = ["rc"] }
|
||||
serde_derive = ">=1.0.27"
|
||||
serde_json = ">=1.0.9"
|
||||
thiserror = "1.0.31"
|
||||
serde = { version = "1.0.143", features = ["rc"] }
|
||||
serde_derive = "1.0.142"
|
||||
serde_json = "1.0.83"
|
||||
thiserror = "1.0.32"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = ">=0.3.1"
|
||||
vmm-sys-util = "0.10.0"
|
||||
|
||||
[dependencies.vfio-bindings]
|
||||
version = "0.3.1"
|
||||
|
||||
@@ -552,8 +552,7 @@ impl Client {
|
||||
cap_size,
|
||||
reply.header.message_size - size_of::<DeviceGetRegionInfo>() as u32
|
||||
);
|
||||
let mut cap_data = Vec::with_capacity(cap_size as usize);
|
||||
cap_data.resize(cap_data.capacity(), 0u8);
|
||||
let mut cap_data = vec![0; cap_size as usize];
|
||||
self.stream
|
||||
.read_exact(cap_data.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
@@ -770,13 +769,9 @@ impl Client {
|
||||
}
|
||||
|
||||
pub fn region(&self, region_index: u32) -> Option<&Region> {
|
||||
for region in &self.regions {
|
||||
if region.index == region_index {
|
||||
return Some(region);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
self.regions
|
||||
.iter()
|
||||
.find(|®ion| region.index == region_index)
|
||||
}
|
||||
|
||||
pub fn resettable(&self) -> bool {
|
||||
|
||||
@@ -8,9 +8,8 @@ license = "Apache-2.0"
|
||||
[dependencies]
|
||||
byteorder = "1.4.3"
|
||||
crc32c = "0.6.3"
|
||||
libc = "0.2.126"
|
||||
libc = "0.2.131"
|
||||
log = "0.4.17"
|
||||
remain = "0.2.3"
|
||||
thiserror = "1.0"
|
||||
remain = "0.2.4"
|
||||
thiserror = "1.0.32"
|
||||
uuid = { version = "1.1.2", features = ["v4"] }
|
||||
vmm-sys-util = ">=0.3.1"
|
||||
@@ -397,26 +397,20 @@ impl VhdxHeader {
|
||||
header_1: Result<Header>,
|
||||
header_2: Result<Header>,
|
||||
) -> Result<(HeaderNo, Header)> {
|
||||
let mut header1_seq_num: u64 = 0;
|
||||
let mut header2_seq_num: u64 = 0;
|
||||
let mut valid_hdr_found: bool = false;
|
||||
let header_1 = header_1.ok();
|
||||
let header_2 = header_2.ok();
|
||||
|
||||
if let Ok(ref header_1) = header_1 {
|
||||
valid_hdr_found = true;
|
||||
header1_seq_num = header_1.sequence_number;
|
||||
}
|
||||
|
||||
if let Ok(ref header_2) = header_2 {
|
||||
valid_hdr_found = true;
|
||||
header2_seq_num = header_2.sequence_number;
|
||||
}
|
||||
|
||||
if !valid_hdr_found {
|
||||
Err(VhdxHeaderError::NoValidHeader)
|
||||
} else if header1_seq_num >= header2_seq_num {
|
||||
Ok((HeaderNo::First, header_1.unwrap()))
|
||||
} else {
|
||||
Ok((HeaderNo::Second, header_2.unwrap()))
|
||||
match (header_1, header_2) {
|
||||
(None, None) => Err(VhdxHeaderError::NoValidHeader),
|
||||
(Some(header_1), None) => Ok((HeaderNo::First, header_1)),
|
||||
(None, Some(header_2)) => Ok((HeaderNo::Second, header_2)),
|
||||
(Some(header_1), Some(header_2)) => {
|
||||
if header_1.sequence_number >= header_2.sequence_number {
|
||||
Ok((HeaderNo::First, header_1))
|
||||
} else {
|
||||
Ok((HeaderNo::Second, header_2))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,18 +6,19 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
block_util = { path = "../block_util" }
|
||||
clap = { version = "3.2.8", features = ["wrap_help","cargo"] }
|
||||
clap = { version = "3.2.17", features = ["wrap_help","cargo"] }
|
||||
env_logger = "0.9.0"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.126"
|
||||
libc = "0.2.131"
|
||||
log = "0.4.17"
|
||||
option_parser = { path = "../option_parser" }
|
||||
qcow = { path = "../qcow" }
|
||||
vhost = { version = "0.4.0", features = ["vhost-user-slave"] }
|
||||
vhost-user-backend = "0.5.0"
|
||||
vhost-user-backend = "0.6.0"
|
||||
virtio-bindings = "0.1.0"
|
||||
virtio-queue = "0.5.0"
|
||||
vm-memory = "0.8.0"
|
||||
vmm-sys-util = "0.9.0"
|
||||
vmm-sys-util = "0.10.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.2.8", features = ["cargo"] }
|
||||
clap = { version = "3.2.17", features = ["cargo"] }
|
||||
|
||||
@@ -19,6 +19,7 @@ use std::fs::File;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Read;
|
||||
use std::io::{Seek, SeekFrom, Write};
|
||||
use std::ops::Deref;
|
||||
use std::ops::DerefMut;
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
use std::path::PathBuf;
|
||||
@@ -34,6 +35,8 @@ use vhost::vhost_user::Listener;
|
||||
use vhost_user_backend::{VhostUserBackendMut, VhostUserDaemon, VringRwLock, VringState, VringT};
|
||||
use virtio_bindings::bindings::virtio_blk::*;
|
||||
use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
||||
use virtio_queue::QueueT;
|
||||
use vm_memory::GuestAddressSpace;
|
||||
use vm_memory::{bitmap::AtomicBitmap, ByteValued, Bytes, GuestMemoryAtomic};
|
||||
use vmm_sys_util::{epoll::EventSet, eventfd::EventFd};
|
||||
|
||||
@@ -95,6 +98,7 @@ struct VhostUserBlkThread {
|
||||
event_idx: bool,
|
||||
kill_evt: EventFd,
|
||||
writeback: Arc<AtomicBool>,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
}
|
||||
|
||||
impl VhostUserBlkThread {
|
||||
@@ -103,6 +107,7 @@ impl VhostUserBlkThread {
|
||||
disk_image_id: Vec<u8>,
|
||||
disk_nsectors: u64,
|
||||
writeback: Arc<AtomicBool>,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
) -> Result<Self> {
|
||||
Ok(VhostUserBlkThread {
|
||||
disk_image,
|
||||
@@ -111,6 +116,7 @@ impl VhostUserBlkThread {
|
||||
event_idx: false,
|
||||
kill_evt: EventFd::new(EFD_NONBLOCK).map_err(Error::CreateKillEventFd)?,
|
||||
writeback,
|
||||
mem,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -118,9 +124,12 @@ impl VhostUserBlkThread {
|
||||
&mut self,
|
||||
vring: &mut RwLockWriteGuard<VringState<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
) -> bool {
|
||||
let mut used_desc_heads = Vec::new();
|
||||
let mut used_descs = false;
|
||||
|
||||
for mut desc_chain in vring.get_queue_mut().iter().unwrap() {
|
||||
while let Some(mut desc_chain) = vring
|
||||
.get_queue_mut()
|
||||
.pop_descriptor_chain(self.mem.memory())
|
||||
{
|
||||
debug!("got an element in the queue");
|
||||
let len;
|
||||
match Request::parse(&mut desc_chain, None) {
|
||||
@@ -153,33 +162,35 @@ impl VhostUserBlkThread {
|
||||
}
|
||||
}
|
||||
|
||||
used_desc_heads.push((desc_chain.head_index(), len));
|
||||
vring
|
||||
.get_queue_mut()
|
||||
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
|
||||
.unwrap();
|
||||
used_descs = true;
|
||||
}
|
||||
|
||||
let mut needs_signalling = false;
|
||||
for (desc_head, len) in used_desc_heads.iter() {
|
||||
if self.event_idx {
|
||||
let queue = vring.get_queue_mut();
|
||||
if queue.add_used(*desc_head, *len).is_ok() {
|
||||
if queue.needs_notification().unwrap() {
|
||||
debug!("signalling queue");
|
||||
needs_signalling = true;
|
||||
} else {
|
||||
debug!("omitting signal (event_idx)");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if self.event_idx {
|
||||
if vring
|
||||
.get_queue_mut()
|
||||
.needs_notification(self.mem.memory().deref())
|
||||
.unwrap()
|
||||
{
|
||||
debug!("signalling queue");
|
||||
vring.get_queue_mut().add_used(*desc_head, *len).unwrap();
|
||||
needs_signalling = true;
|
||||
} else {
|
||||
debug!("omitting signal (event_idx)");
|
||||
}
|
||||
} else {
|
||||
debug!("signalling queue");
|
||||
needs_signalling = true;
|
||||
}
|
||||
|
||||
if needs_signalling {
|
||||
vring.signal_used_queue().unwrap();
|
||||
}
|
||||
|
||||
!used_desc_heads.is_empty()
|
||||
used_descs
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,6 +203,7 @@ struct VhostUserBlkBackend {
|
||||
queue_size: usize,
|
||||
acked_features: u64,
|
||||
writeback: Arc<AtomicBool>,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
}
|
||||
|
||||
impl VhostUserBlkBackend {
|
||||
@@ -202,6 +214,7 @@ impl VhostUserBlkBackend {
|
||||
direct: bool,
|
||||
poll_queue: bool,
|
||||
queue_size: usize,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
) -> Result<Self> {
|
||||
let mut options = OpenOptions::new();
|
||||
options.read(true);
|
||||
@@ -243,6 +256,7 @@ impl VhostUserBlkBackend {
|
||||
image_id.clone(),
|
||||
nsectors,
|
||||
writeback.clone(),
|
||||
mem.clone(),
|
||||
)?);
|
||||
threads.push(thread);
|
||||
queues_per_thread.push(0b1 << i);
|
||||
@@ -257,6 +271,7 @@ impl VhostUserBlkBackend {
|
||||
queue_size,
|
||||
acked_features: 0,
|
||||
writeback,
|
||||
mem,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -364,7 +379,10 @@ impl VhostUserBackendMut<VringRwLock<GuestMemoryAtomic<GuestMemoryMmap>>, Atomic
|
||||
// calling process_queue() until it stops finding new
|
||||
// requests on the queue.
|
||||
loop {
|
||||
vring.get_queue_mut().enable_notification().unwrap();
|
||||
vring
|
||||
.get_queue_mut()
|
||||
.enable_notification(self.mem.memory().deref())
|
||||
.unwrap();
|
||||
if !thread.process_queue(&mut vring) {
|
||||
break;
|
||||
}
|
||||
@@ -491,6 +509,8 @@ pub fn start_block_backend(backend_command: &str) {
|
||||
}
|
||||
};
|
||||
|
||||
let mem = GuestMemoryAtomic::new(GuestMemoryMmap::new());
|
||||
|
||||
let blk_backend = Arc::new(RwLock::new(
|
||||
VhostUserBlkBackend::new(
|
||||
backend_config.path,
|
||||
@@ -499,6 +519,7 @@ pub fn start_block_backend(backend_command: &str) {
|
||||
backend_config.direct,
|
||||
backend_config.poll_queue,
|
||||
backend_config.queue_size,
|
||||
mem.clone(),
|
||||
)
|
||||
.unwrap(),
|
||||
));
|
||||
@@ -508,12 +529,7 @@ pub fn start_block_backend(backend_command: &str) {
|
||||
let listener = Listener::new(&backend_config.socket, true).unwrap();
|
||||
|
||||
let name = "vhost-user-blk-backend";
|
||||
let mut blk_daemon = VhostUserDaemon::new(
|
||||
name.to_string(),
|
||||
blk_backend.clone(),
|
||||
GuestMemoryAtomic::new(GuestMemoryMmap::new()),
|
||||
)
|
||||
.unwrap();
|
||||
let mut blk_daemon = VhostUserDaemon::new(name.to_string(), blk_backend.clone(), mem).unwrap();
|
||||
|
||||
debug!("blk_daemon is created!\n");
|
||||
|
||||
|
||||
@@ -5,18 +5,18 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.2.8", features = ["wrap_help","cargo"] }
|
||||
clap = { version = "3.2.17", features = ["wrap_help","cargo"] }
|
||||
env_logger = "0.9.0"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.126"
|
||||
libc = "0.2.131"
|
||||
log = "0.4.17"
|
||||
net_util = { path = "../net_util" }
|
||||
option_parser = { path = "../option_parser" }
|
||||
vhost = { version = "0.4.0", features = ["vhost-user-slave"] }
|
||||
vhost-user-backend = "0.5.0"
|
||||
vhost-user-backend = "0.6.0"
|
||||
virtio-bindings = "0.1.0"
|
||||
vm-memory = "0.8.0"
|
||||
vmm-sys-util = "0.9.0"
|
||||
vmm-sys-util = "0.10.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.2.8", features = ["cargo"] }
|
||||
clap = { version = "3.2.17", features = ["cargo"] }
|
||||
|
||||
@@ -16,8 +16,9 @@ use net_util::{
|
||||
use option_parser::Toggle;
|
||||
use option_parser::{OptionParser, OptionParserError};
|
||||
use std::fmt;
|
||||
use std::io::{self};
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::ops::Deref;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::process;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
@@ -26,6 +27,7 @@ use vhost::vhost_user::message::*;
|
||||
use vhost::vhost_user::Listener;
|
||||
use vhost_user_backend::{VhostUserBackendMut, VhostUserDaemon, VringRwLock, VringT};
|
||||
use virtio_bindings::bindings::virtio_net::*;
|
||||
use vm_memory::GuestAddressSpace;
|
||||
use vm_memory::{bitmap::AtomicBitmap, GuestMemoryAtomic};
|
||||
use vmm_sys_util::{epoll::EventSet, eventfd::EventFd};
|
||||
|
||||
@@ -113,6 +115,7 @@ pub struct VhostUserNetBackend {
|
||||
num_queues: usize,
|
||||
queue_size: u16,
|
||||
queues_per_thread: Vec<u64>,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
}
|
||||
|
||||
impl VhostUserNetBackend {
|
||||
@@ -123,6 +126,7 @@ impl VhostUserNetBackend {
|
||||
num_queues: usize,
|
||||
queue_size: u16,
|
||||
ifname: Option<&str>,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
) -> Result<Self> {
|
||||
let mut taps = open_tap(
|
||||
ifname,
|
||||
@@ -147,6 +151,7 @@ impl VhostUserNetBackend {
|
||||
num_queues,
|
||||
queue_size,
|
||||
queues_per_thread,
|
||||
mem,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -214,7 +219,7 @@ impl VhostUserBackendMut<VringRwLock<GuestMemoryAtomic<GuestMemoryMmap>>, Atomic
|
||||
let mut vring = vrings[1].get_mut();
|
||||
if thread
|
||||
.net
|
||||
.process_tx(vring.get_queue_mut())
|
||||
.process_tx(self.mem.memory().deref(), vring.get_queue_mut())
|
||||
.map_err(Error::NetQueuePair)?
|
||||
{
|
||||
vring
|
||||
@@ -226,7 +231,7 @@ impl VhostUserBackendMut<VringRwLock<GuestMemoryAtomic<GuestMemoryMmap>>, Atomic
|
||||
let mut vring = vrings[0].get_mut();
|
||||
if thread
|
||||
.net
|
||||
.process_rx(vring.get_queue_mut())
|
||||
.process_rx(self.mem.memory().deref(), vring.get_queue_mut())
|
||||
.map_err(Error::NetQueuePair)?
|
||||
{
|
||||
vring
|
||||
@@ -342,6 +347,8 @@ pub fn start_net_backend(backend_command: &str) {
|
||||
|
||||
let tap = backend_config.tap.as_deref();
|
||||
|
||||
let mem = GuestMemoryAtomic::new(GuestMemoryMmap::new());
|
||||
|
||||
let net_backend = Arc::new(RwLock::new(
|
||||
VhostUserNetBackend::new(
|
||||
backend_config.ip,
|
||||
@@ -350,6 +357,7 @@ pub fn start_net_backend(backend_command: &str) {
|
||||
backend_config.num_queues,
|
||||
backend_config.queue_size,
|
||||
tap,
|
||||
mem.clone(),
|
||||
)
|
||||
.unwrap(),
|
||||
));
|
||||
@@ -357,7 +365,7 @@ pub fn start_net_backend(backend_command: &str) {
|
||||
let mut net_daemon = VhostUserDaemon::new(
|
||||
"vhost-user-net-backend".to_string(),
|
||||
net_backend.clone(),
|
||||
GuestMemoryAtomic::new(GuestMemoryMmap::new()),
|
||||
mem,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -6,34 +6,33 @@ edition = "2021"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
mshv = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.58"
|
||||
arc-swap = "1.5.0"
|
||||
anyhow = "1.0.62"
|
||||
arc-swap = "1.5.1"
|
||||
block_util = { path = "../block_util" }
|
||||
byteorder = "1.4.3"
|
||||
epoll = "4.3.1"
|
||||
event_monitor = { path = "../event_monitor" }
|
||||
io-uring = "0.5.2"
|
||||
libc = "0.2.126"
|
||||
io-uring = "0.5.3"
|
||||
libc = "0.2.131"
|
||||
log = "0.4.17"
|
||||
net_gen = { path = "../net_gen" }
|
||||
net_util = { path = "../net_util" }
|
||||
pci = { path = "../pci" }
|
||||
rate_limiter = { path = "../rate_limiter" }
|
||||
seccompiler = "0.2.0"
|
||||
serde = { version="1.0.138", features=["derive"] }
|
||||
serde_json = "1.0.82"
|
||||
thiserror = "1.0.31"
|
||||
serde = { version = "1.0.143", features = ["derive"] }
|
||||
serde_json = "1.0.83"
|
||||
thiserror = "1.0.32"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
vhost = { version = "0.4.0", features = ["vhost-user-master", "vhost-user-slave", "vhost-kern", "vhost-vdpa"] }
|
||||
virtio-bindings = { version = "0.1.0", features = ["virtio-v5_0_0"] }
|
||||
virtio-queue = "0.4.0"
|
||||
virtio-queue = "0.5.0"
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
vmm-sys-util = "0.10.0"
|
||||
|
||||
@@ -18,6 +18,7 @@ use crate::{
|
||||
VirtioDeviceType, VirtioInterrupt, VirtioInterruptType, EPOLL_HELPER_EVENT_LAST,
|
||||
VIRTIO_F_VERSION_1,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use libc::EFD_NONBLOCK;
|
||||
use seccompiler::SeccompAction;
|
||||
use std::io;
|
||||
@@ -28,12 +29,13 @@ use std::sync::{
|
||||
atomic::{AtomicBool, AtomicU64, Ordering},
|
||||
mpsc, Arc, Barrier, Mutex,
|
||||
};
|
||||
use thiserror::Error;
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use virtio_queue::Queue;
|
||||
use virtio_queue::{Queue, QueueT};
|
||||
use vm_memory::{
|
||||
Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryAtomic, GuestMemoryError,
|
||||
GuestMemoryRegion,
|
||||
Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
|
||||
GuestMemoryError, GuestMemoryRegion,
|
||||
};
|
||||
use vm_migration::{
|
||||
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, VersionMapped,
|
||||
@@ -62,35 +64,35 @@ const VIRTIO_BALLOON_F_DEFLATE_ON_OOM: u64 = 2;
|
||||
// pages.
|
||||
const VIRTIO_BALLOON_F_REPORTING: u64 = 5;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
// Guest gave us bad memory addresses.
|
||||
#[error("Guest gave us bad memory addresses.: {0}")]
|
||||
GuestMemory(GuestMemoryError),
|
||||
// Guest gave us a write only descriptor that protocol says to read from.
|
||||
#[error("Guest gave us a write only descriptor that protocol says to read from.")]
|
||||
UnexpectedWriteOnlyDescriptor,
|
||||
// Guest sent us invalid request.
|
||||
#[error("Guest sent us invalid request.")]
|
||||
InvalidRequest,
|
||||
// Fallocate fail.
|
||||
#[error("Fallocate fail.: {0}")]
|
||||
FallocateFail(std::io::Error),
|
||||
// Madvise fail.
|
||||
#[error("Madvise fail.: {0}")]
|
||||
MadviseFail(std::io::Error),
|
||||
// Failed to EventFd write.
|
||||
#[error("Failed to EventFd write.: {0}")]
|
||||
EventFdWriteFail(std::io::Error),
|
||||
// Failed to EventFd try_clone.
|
||||
#[error("Failed to EventFd try_clone.: {0}")]
|
||||
EventFdTryCloneFail(std::io::Error),
|
||||
// Failed to MpscRecv.
|
||||
#[error("Failed to MpscRecv.: {0}")]
|
||||
MpscRecvFail(mpsc::RecvError),
|
||||
// Resize invalid argument
|
||||
#[error("Resize invalid argument: {0}")]
|
||||
ResizeInval(String),
|
||||
// Invalid queue index
|
||||
#[error("Invalid queue index: {0}")]
|
||||
InvalidQueueIndex(usize),
|
||||
// Fail tp signal
|
||||
#[error("Fail tp signal: {0}")]
|
||||
FailedSignal(io::Error),
|
||||
/// Descriptor chain is too short
|
||||
#[error("Descriptor chain is too short")]
|
||||
DescriptorChainTooShort,
|
||||
/// Failed adding used index
|
||||
#[error("Failed adding used index: {0}")]
|
||||
QueueAddUsed(virtio_queue::Error),
|
||||
/// Failed creating an iterator over the queue
|
||||
#[error("Failed creating an iterator over the queue: {0}")]
|
||||
QueueIterator(virtio_queue::Error),
|
||||
}
|
||||
|
||||
@@ -161,9 +163,10 @@ impl VirtioBalloonResize {
|
||||
}
|
||||
|
||||
struct BalloonEpollHandler {
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
config: Arc<Mutex<VirtioBalloonConfig>>,
|
||||
resize_receiver: VirtioBalloonResizeReceiver,
|
||||
queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
queues: Vec<Queue>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
inflate_queue_evt: EventFd,
|
||||
deflate_queue_evt: EventFd,
|
||||
@@ -225,34 +228,13 @@ impl BalloonEpollHandler {
|
||||
Self::advise_memory_range(memory, range_base, range_len, libc::MADV_DONTNEED)
|
||||
}
|
||||
|
||||
fn notify_queue(
|
||||
&mut self,
|
||||
queue_index: usize,
|
||||
used_descs: Vec<(u16, u32)>,
|
||||
) -> result::Result<(), Error> {
|
||||
for (desc_index, len) in used_descs.iter() {
|
||||
self.queues[queue_index]
|
||||
.add_used(*desc_index, *len)
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
}
|
||||
|
||||
if !used_descs.is_empty() {
|
||||
self.signal(VirtioInterruptType::Queue(queue_index as u16))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_queue(&mut self, queue_index: usize) -> result::Result<(), Error> {
|
||||
let mut used_descs = Vec::new();
|
||||
for mut desc_chain in self.queues[queue_index]
|
||||
.iter()
|
||||
.map_err(Error::QueueIterator)?
|
||||
let mut used_descs = false;
|
||||
while let Some(mut desc_chain) =
|
||||
self.queues[queue_index].pop_descriptor_chain(self.mem.memory())
|
||||
{
|
||||
let desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
|
||||
|
||||
used_descs.push((desc_chain.head_index(), desc.len()));
|
||||
|
||||
let data_chunk_size = size_of::<u32>();
|
||||
|
||||
// The head contains the request type which MUST be readable.
|
||||
@@ -292,17 +274,24 @@ impl BalloonEpollHandler {
|
||||
_ => return Err(Error::InvalidQueueIndex(queue_index)),
|
||||
}
|
||||
}
|
||||
|
||||
self.queues[queue_index]
|
||||
.add_used(desc_chain.memory(), desc_chain.head_index(), desc.len())
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
used_descs = true;
|
||||
}
|
||||
|
||||
self.notify_queue(queue_index, used_descs)
|
||||
if used_descs {
|
||||
self.signal(VirtioInterruptType::Queue(queue_index as u16))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn process_reporting_queue(&mut self, queue_index: usize) -> result::Result<(), Error> {
|
||||
let mut used_descs = Vec::new();
|
||||
|
||||
for mut desc_chain in self.queues[queue_index]
|
||||
.iter()
|
||||
.map_err(Error::QueueIterator)?
|
||||
let mut used_descs = false;
|
||||
while let Some(mut desc_chain) =
|
||||
self.queues[queue_index].pop_descriptor_chain(self.mem.memory())
|
||||
{
|
||||
let mut descs_len = 0;
|
||||
while let Some(desc) = desc_chain.next() {
|
||||
@@ -310,10 +299,17 @@ impl BalloonEpollHandler {
|
||||
Self::release_memory_range(desc_chain.memory(), desc.addr(), desc.len() as usize)?;
|
||||
}
|
||||
|
||||
used_descs.push((desc_chain.head_index(), descs_len));
|
||||
self.queues[queue_index]
|
||||
.add_used(desc_chain.memory(), desc_chain.head_index(), descs_len)
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
used_descs = true;
|
||||
}
|
||||
|
||||
self.notify_queue(queue_index, used_descs)
|
||||
if used_descs {
|
||||
self.signal(VirtioInterruptType::Queue(queue_index as u16))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
@@ -335,77 +331,95 @@ impl BalloonEpollHandler {
|
||||
}
|
||||
|
||||
impl EpollHelperHandler for BalloonEpollHandler {
|
||||
fn handle_event(&mut self, _helper: &mut EpollHelper, event: &epoll::Event) -> bool {
|
||||
fn handle_event(
|
||||
&mut self,
|
||||
_helper: &mut EpollHelper,
|
||||
event: &epoll::Event,
|
||||
) -> result::Result<(), EpollHelperError> {
|
||||
let ev_type = event.data as u16;
|
||||
match ev_type {
|
||||
RESIZE_EVENT => {
|
||||
if let Err(e) = self.resize_receiver.evt.read() {
|
||||
error!("Failed to get resize event: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
let mut signal_error = false;
|
||||
let r = {
|
||||
let mut config = self.config.lock().unwrap();
|
||||
config.num_pages =
|
||||
(self.resize_receiver.get_size() >> VIRTIO_BALLOON_PFN_SHIFT) as u32;
|
||||
if let Err(e) = self.signal(VirtioInterruptType::Config) {
|
||||
signal_error = true;
|
||||
Err(e)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
if let Err(e) = &r {
|
||||
// This error will send back to resize caller.
|
||||
error!("Handle resize event get error: {:?}", e);
|
||||
}
|
||||
if let Err(e) = self.resize_receiver.send(r) {
|
||||
error!("Sending \"resize\" generated error: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
if signal_error {
|
||||
return true;
|
||||
}
|
||||
self.resize_receiver.evt.read().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!("Failed to get resize event: {:?}", e))
|
||||
})?;
|
||||
|
||||
let mut config = self.config.lock().unwrap();
|
||||
config.num_pages =
|
||||
(self.resize_receiver.get_size() >> VIRTIO_BALLOON_PFN_SHIFT) as u32;
|
||||
self.signal(VirtioInterruptType::Config).map_err(|e| {
|
||||
let ret_err = anyhow!("Handle resize event get error: {:?}", e);
|
||||
self.resize_receiver
|
||||
.send(Err(e))
|
||||
.map_err(|e| {
|
||||
error!("Sending \"resize\" generated error: {:?}", e);
|
||||
})
|
||||
.ok();
|
||||
EpollHelperError::HandleEvent(ret_err)
|
||||
})?;
|
||||
|
||||
self.resize_receiver.send(Ok(())).map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Sending \"resize\" generated error: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
INFLATE_QUEUE_EVENT => {
|
||||
if let Err(e) = self.inflate_queue_evt.read() {
|
||||
error!("Failed to get inflate queue event: {:?}", e);
|
||||
return true;
|
||||
} else if let Err(e) = self.process_queue(0) {
|
||||
error!("Failed to signal used inflate queue: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
self.inflate_queue_evt.read().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to get inflate queue event: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
self.process_queue(0).map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal used inflate queue: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
DEFLATE_QUEUE_EVENT => {
|
||||
if let Err(e) = self.deflate_queue_evt.read() {
|
||||
error!("Failed to get deflate queue event: {:?}", e);
|
||||
return true;
|
||||
} else if let Err(e) = self.process_queue(1) {
|
||||
error!("Failed to signal used deflate queue: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
self.deflate_queue_evt.read().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to get deflate queue event: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
self.process_queue(1).map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal used deflate queue: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
REPORTING_QUEUE_EVENT => {
|
||||
if let Some(reporting_queue_evt) = self.reporting_queue_evt.as_ref() {
|
||||
if let Err(e) = reporting_queue_evt.read() {
|
||||
error!("Failed to get reporting queue event: {:?}", e);
|
||||
return true;
|
||||
} else if let Err(e) = self.process_reporting_queue(2) {
|
||||
error!("Failed to signal used inflate queue: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
reporting_queue_evt.read().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to get reporting queue event: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
self.process_reporting_queue(2).map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal used inflate queue: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
} else {
|
||||
error!("Invalid reporting queue event as no eventfd registered");
|
||||
return true;
|
||||
return Err(EpollHelperError::HandleEvent(anyhow!(
|
||||
"Invalid reporting queue event as no eventfd registered"
|
||||
)));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("Unknown event for virtio-balloon");
|
||||
return true;
|
||||
return Err(EpollHelperError::HandleEvent(anyhow!(
|
||||
"Unknown event for virtio-balloon"
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,30 +554,37 @@ impl VirtioDevice for Balloon {
|
||||
|
||||
fn activate(
|
||||
&mut self,
|
||||
_mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
mut queue_evts: Vec<EventFd>,
|
||||
mut queues: Vec<(usize, Queue, EventFd)>,
|
||||
) -> ActivateResult {
|
||||
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
|
||||
self.common.activate(&queues, &interrupt_cb)?;
|
||||
let (kill_evt, pause_evt) = self.common.dup_eventfds();
|
||||
|
||||
let inflate_queue_evt = queue_evts.remove(0);
|
||||
let deflate_queue_evt = queue_evts.remove(0);
|
||||
let mut virtqueues = Vec::new();
|
||||
let (_, queue, queue_evt) = queues.remove(0);
|
||||
virtqueues.push(queue);
|
||||
let inflate_queue_evt = queue_evt;
|
||||
let (_, queue, queue_evt) = queues.remove(0);
|
||||
virtqueues.push(queue);
|
||||
let deflate_queue_evt = queue_evt;
|
||||
let reporting_queue_evt =
|
||||
if self.common.feature_acked(VIRTIO_BALLOON_F_REPORTING) && !queue_evts.is_empty() {
|
||||
Some(queue_evts.remove(0))
|
||||
if self.common.feature_acked(VIRTIO_BALLOON_F_REPORTING) && !queues.is_empty() {
|
||||
let (_, queue, queue_evt) = queues.remove(0);
|
||||
virtqueues.push(queue);
|
||||
Some(queue_evt)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut handler = BalloonEpollHandler {
|
||||
mem,
|
||||
config: self.config.clone(),
|
||||
resize_receiver: self.resize.get_receiver().map_err(|e| {
|
||||
error!("failed to clone resize EventFd: {:?}", e);
|
||||
ActivateError::BadActivate
|
||||
})?,
|
||||
queues,
|
||||
queues: virtqueues,
|
||||
interrupt_cb,
|
||||
inflate_queue_evt,
|
||||
deflate_queue_evt,
|
||||
@@ -582,11 +603,7 @@ impl VirtioDevice for Balloon {
|
||||
Thread::VirtioBalloon,
|
||||
&mut epoll_threads,
|
||||
&self.exit_evt,
|
||||
move || {
|
||||
if let Err(e) = handler.run(paused, paused_sync.unwrap()) {
|
||||
error!("Error running worker: {:?}", e);
|
||||
}
|
||||
},
|
||||
move || handler.run(paused, paused_sync.unwrap()),
|
||||
)?;
|
||||
self.common.epoll_threads = Some(epoll_threads);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ use crate::seccomp_filters::Thread;
|
||||
use crate::thread_helper::spawn_virtio_thread;
|
||||
use crate::GuestMemoryMmap;
|
||||
use crate::VirtioInterrupt;
|
||||
use anyhow::anyhow;
|
||||
use block_util::{
|
||||
async_io::AsyncIo, async_io::AsyncIoError, async_io::DiskFile, build_disk_image_id, Request,
|
||||
RequestType, VirtioBlockConfig,
|
||||
@@ -26,17 +27,19 @@ use rate_limiter::{RateLimiter, TokenType};
|
||||
use seccompiler::SeccompAction;
|
||||
use std::io;
|
||||
use std::num::Wrapping;
|
||||
use std::ops::Deref;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::path::PathBuf;
|
||||
use std::result;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::{collections::HashMap, convert::TryInto};
|
||||
use thiserror::Error;
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use virtio_bindings::bindings::virtio_blk::*;
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic};
|
||||
use virtio_queue::{Queue, QueueOwnedT, QueueT};
|
||||
use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryError};
|
||||
use vm_migration::VersionMapped;
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||
use vm_virtio::AccessPlatform;
|
||||
@@ -52,24 +55,26 @@ const COMPLETION_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
|
||||
// New 'wake up' event from the rate limiter
|
||||
const RATE_LIMITER_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 3;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
/// Failed to parse the request.
|
||||
#[error("Failed to parse the request: {0}")]
|
||||
RequestParsing(block_util::Error),
|
||||
/// Failed to execute the request.
|
||||
#[error("Failed to execute the request: {0}")]
|
||||
RequestExecuting(block_util::ExecuteError),
|
||||
/// Failed to complete the request.
|
||||
#[error("Failed to complete the request: {0}")]
|
||||
RequestCompleting(block_util::Error),
|
||||
/// Missing the expected entry in the list of requests.
|
||||
#[error("Missing the expected entry in the list of requests")]
|
||||
MissingEntryRequestList,
|
||||
/// The asynchronous request returned with failure.
|
||||
#[error("The asynchronous request returned with failure")]
|
||||
AsyncRequestFailure,
|
||||
/// Failed synchronizing the file
|
||||
#[error("Failed synchronizing the file: {0}")]
|
||||
Fsync(AsyncIoError),
|
||||
/// Failed adding used index
|
||||
#[error("Failed adding used index: {0}")]
|
||||
QueueAddUsed(virtio_queue::Error),
|
||||
/// Failed creating an iterator over the queue
|
||||
#[error("Failed creating an iterator over the queue: {0}")]
|
||||
QueueIterator(virtio_queue::Error),
|
||||
#[error("Failed to update request status: {0}")]
|
||||
RequestStatus(GuestMemoryError),
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
@@ -84,7 +89,7 @@ pub struct BlockCounters {
|
||||
|
||||
struct BlockEpollHandler {
|
||||
queue_index: u16,
|
||||
queue: Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
queue: Queue,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
disk_image: Box<dyn AsyncIo>,
|
||||
disk_nsectors: u64,
|
||||
@@ -104,11 +109,9 @@ impl BlockEpollHandler {
|
||||
fn process_queue_submit(&mut self) -> Result<bool> {
|
||||
let queue = &mut self.queue;
|
||||
|
||||
let mut used_desc_heads = Vec::new();
|
||||
let mut used_count = 0;
|
||||
let mut used_descs = false;
|
||||
|
||||
let mut avail_iter = queue.iter().map_err(Error::QueueIterator)?;
|
||||
for mut desc_chain in &mut avail_iter {
|
||||
while let Some(mut desc_chain) = queue.pop_descriptor_chain(self.mem.memory()) {
|
||||
let mut request = Request::parse(&mut desc_chain, self.access_platform.as_ref())
|
||||
.map_err(Error::RequestParsing)?;
|
||||
|
||||
@@ -118,7 +121,7 @@ impl BlockEpollHandler {
|
||||
if !rate_limiter.consume(1, TokenType::Ops) {
|
||||
// Stop processing the queue and return this descriptor chain to the
|
||||
// avail ring, for later processing.
|
||||
avail_iter.go_to_previous_position();
|
||||
queue.go_to_previous_position();
|
||||
break;
|
||||
}
|
||||
// Exercise the rate limiter only if this request is of data transfer type.
|
||||
@@ -137,7 +140,7 @@ impl BlockEpollHandler {
|
||||
rate_limiter.manual_replenish(1, TokenType::Ops);
|
||||
// Stop processing the queue and return this descriptor chain to the
|
||||
// avail ring, for later processing.
|
||||
avail_iter.go_to_previous_position();
|
||||
queue.go_to_previous_position();
|
||||
break;
|
||||
}
|
||||
};
|
||||
@@ -157,34 +160,27 @@ impl BlockEpollHandler {
|
||||
{
|
||||
self.request_list.insert(desc_chain.head_index(), request);
|
||||
} else {
|
||||
// We use unwrap because the request parsing process already
|
||||
// checked that the status_addr was valid.
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_obj(VIRTIO_BLK_S_OK, request.status_addr)
|
||||
.unwrap();
|
||||
.map_err(Error::RequestStatus)?;
|
||||
|
||||
// If no asynchronous operation has been submitted, we can
|
||||
// simply return the used descriptor.
|
||||
used_desc_heads.push((desc_chain.head_index(), 0));
|
||||
used_count += 1;
|
||||
queue
|
||||
.add_used(desc_chain.memory(), desc_chain.head_index(), 0)
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
used_descs = true;
|
||||
}
|
||||
}
|
||||
|
||||
for &(desc_index, len) in used_desc_heads.iter() {
|
||||
queue
|
||||
.add_used(desc_index, len)
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
}
|
||||
|
||||
Ok(used_count > 0)
|
||||
Ok(used_descs)
|
||||
}
|
||||
|
||||
fn process_queue_complete(&mut self) -> Result<bool> {
|
||||
let queue = &mut self.queue;
|
||||
|
||||
let mut used_desc_heads = Vec::new();
|
||||
let mut used_count = 0;
|
||||
let mut used_descs = false;
|
||||
let mem = self.mem.memory();
|
||||
let mut read_bytes = Wrapping(0);
|
||||
let mut write_bytes = Wrapping(0);
|
||||
@@ -229,18 +225,13 @@ impl BlockEpollHandler {
|
||||
return Err(Error::AsyncRequestFailure);
|
||||
};
|
||||
|
||||
// We use unwrap because the request parsing process already
|
||||
// checked that the status_addr was valid.
|
||||
mem.write_obj(status, request.status_addr).unwrap();
|
||||
mem.write_obj(status, request.status_addr)
|
||||
.map_err(Error::RequestStatus)?;
|
||||
|
||||
used_desc_heads.push((desc_index as u16, len));
|
||||
used_count += 1;
|
||||
}
|
||||
|
||||
for &(desc_index, len) in used_desc_heads.iter() {
|
||||
queue
|
||||
.add_used(desc_index, len)
|
||||
.add_used(mem.deref(), desc_index as u16, len)
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
used_descs = true;
|
||||
}
|
||||
|
||||
self.counters
|
||||
@@ -257,7 +248,7 @@ impl BlockEpollHandler {
|
||||
.read_ops
|
||||
.fetch_add(read_ops.0, Ordering::AcqRel);
|
||||
|
||||
Ok(used_count > 0)
|
||||
Ok(used_descs)
|
||||
}
|
||||
|
||||
fn signal_used_queue(&self) -> result::Result<(), DeviceError> {
|
||||
@@ -287,14 +278,17 @@ impl BlockEpollHandler {
|
||||
}
|
||||
|
||||
impl EpollHelperHandler for BlockEpollHandler {
|
||||
fn handle_event(&mut self, _helper: &mut EpollHelper, event: &epoll::Event) -> bool {
|
||||
fn handle_event(
|
||||
&mut self,
|
||||
_helper: &mut EpollHelper,
|
||||
event: &epoll::Event,
|
||||
) -> result::Result<(), EpollHelperError> {
|
||||
let ev_type = event.data as u16;
|
||||
match ev_type {
|
||||
QUEUE_AVAIL_EVENT => {
|
||||
if let Err(e) = self.queue_evt.read() {
|
||||
error!("Failed to get queue event: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
self.queue_evt.read().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
|
||||
})?;
|
||||
|
||||
let rate_limit_reached =
|
||||
self.rate_limiter.as_ref().map_or(false, |r| r.is_blocked());
|
||||
@@ -305,36 +299,43 @@ impl EpollHelperHandler for BlockEpollHandler {
|
||||
Ok(needs_notification) => {
|
||||
if needs_notification {
|
||||
if let Err(e) = self.signal_used_queue() {
|
||||
error!("Failed to signal used queue: {:?}", e);
|
||||
return true;
|
||||
return Err(EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal used queue: {:?}",
|
||||
e
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to process queue (submit): {:?}", e);
|
||||
return true;
|
||||
return Err(EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to process queue (submit): {:?}",
|
||||
e
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
COMPLETION_EVENT => {
|
||||
if let Err(e) = self.disk_image.notifier().read() {
|
||||
error!("Failed to get queue event: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
self.disk_image.notifier().read().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
|
||||
})?;
|
||||
|
||||
match self.process_queue_complete() {
|
||||
Ok(needs_notification) => {
|
||||
if needs_notification {
|
||||
if let Err(e) = self.signal_used_queue() {
|
||||
error!("Failed to signal used queue: {:?}", e);
|
||||
return true;
|
||||
return Err(EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal used queue: {:?}",
|
||||
e
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to process queue (complete): {:?}", e);
|
||||
return true;
|
||||
return Err(EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to process queue (complete): {:?}",
|
||||
e
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,28 +348,35 @@ impl EpollHelperHandler for BlockEpollHandler {
|
||||
Ok(needs_notification) => {
|
||||
if needs_notification {
|
||||
if let Err(e) = self.signal_used_queue() {
|
||||
error!("Failed to signal used queue: {:?}", e);
|
||||
return true;
|
||||
return Err(EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal used queue: {:?}",
|
||||
e
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to process queue (submit): {:?}", e);
|
||||
return true;
|
||||
return Err(EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to process queue (submit): {:?}",
|
||||
e
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("Unexpected 'RATE_LIMITER_EVENT' when rate_limiter is not enabled.");
|
||||
return true;
|
||||
return Err(EpollHelperError::HandleEvent(anyhow!(
|
||||
"Unexpected 'RATE_LIMITER_EVENT' when rate_limiter is not enabled."
|
||||
)));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("Unexpected event: {}", ev_type);
|
||||
return true;
|
||||
return Err(EpollHelperError::HandleEvent(anyhow!(
|
||||
"Unexpected event: {}",
|
||||
ev_type
|
||||
)));
|
||||
}
|
||||
}
|
||||
false
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,19 +595,17 @@ impl VirtioDevice for Block {
|
||||
&mut self,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
mut queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
mut queue_evts: Vec<EventFd>,
|
||||
mut queues: Vec<(usize, Queue, EventFd)>,
|
||||
) -> ActivateResult {
|
||||
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
|
||||
self.common.activate(&queues, &interrupt_cb)?;
|
||||
|
||||
let disk_image_id = build_disk_image_id(&self.disk_path);
|
||||
self.update_writeback();
|
||||
|
||||
let mut epoll_threads = Vec::new();
|
||||
for i in 0..queues.len() {
|
||||
let queue_evt = queue_evts.remove(0);
|
||||
let queue = queues.remove(0);
|
||||
let queue_size = queue.state.size;
|
||||
let (_, queue, queue_evt) = queues.remove(0);
|
||||
let queue_size = queue.size();
|
||||
let (kill_evt, pause_evt) = self.common.dup_eventfds();
|
||||
|
||||
let rate_limiter: Option<RateLimiter> = self
|
||||
@@ -641,11 +647,7 @@ impl VirtioDevice for Block {
|
||||
Thread::VirtioBlock,
|
||||
&mut epoll_threads,
|
||||
&self.exit_evt,
|
||||
move || {
|
||||
if let Err(e) = handler.run(paused, paused_sync.unwrap()) {
|
||||
error!("Error running worker: {:?}", e);
|
||||
}
|
||||
},
|
||||
move || handler.run(paused, paused_sync.unwrap()),
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::seccomp_filters::Thread;
|
||||
use crate::thread_helper::spawn_virtio_thread;
|
||||
use crate::GuestMemoryMmap;
|
||||
use crate::VirtioInterrupt;
|
||||
use anyhow::anyhow;
|
||||
use libc::{EFD_NONBLOCK, TIOCGWINSZ};
|
||||
use seccompiler::SeccompAction;
|
||||
use std::cmp;
|
||||
@@ -24,8 +25,8 @@ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::{ByteValued, Bytes, GuestMemoryAtomic};
|
||||
use virtio_queue::{Queue, QueueOwnedT, QueueT};
|
||||
use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic};
|
||||
use vm_migration::VersionMapped;
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||
use vm_virtio::{AccessPlatform, Translatable};
|
||||
@@ -74,7 +75,8 @@ impl Default for VirtioConsoleConfig {
|
||||
unsafe impl ByteValued for VirtioConsoleConfig {}
|
||||
|
||||
struct ConsoleEpollHandler {
|
||||
queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
queues: Vec<Queue>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
in_buffer: Arc<Mutex<VecDeque<u8>>>,
|
||||
resizer: Arc<ConsoleResizer>,
|
||||
@@ -135,15 +137,13 @@ impl ConsoleEpollHandler {
|
||||
fn process_input_queue(&mut self) -> bool {
|
||||
let mut in_buffer = self.in_buffer.lock().unwrap();
|
||||
let recv_queue = &mut self.queues[0]; //receiveq
|
||||
let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
|
||||
let mut used_count = 0;
|
||||
let mut used_descs = false;
|
||||
|
||||
if in_buffer.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut avail_iter = recv_queue.iter().unwrap();
|
||||
for mut desc_chain in &mut avail_iter {
|
||||
while let Some(mut desc_chain) = recv_queue.pop_descriptor_chain(self.mem.memory()) {
|
||||
let desc = desc_chain.next().unwrap();
|
||||
let len = cmp::min(desc.len() as u32, in_buffer.len() as u32);
|
||||
let source_slice = in_buffer.drain(..len as usize).collect::<Vec<u8>>();
|
||||
@@ -154,23 +154,21 @@ impl ConsoleEpollHandler {
|
||||
.translate_gva(self.access_platform.as_ref(), desc.len() as usize),
|
||||
) {
|
||||
error!("Failed to write slice: {:?}", e);
|
||||
avail_iter.go_to_previous_position();
|
||||
recv_queue.go_to_previous_position();
|
||||
break;
|
||||
}
|
||||
|
||||
used_desc_heads[used_count] = (desc_chain.head_index(), len);
|
||||
used_count += 1;
|
||||
recv_queue
|
||||
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
|
||||
.unwrap();
|
||||
used_descs = true;
|
||||
|
||||
if in_buffer.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for &(desc_index, len) in &used_desc_heads[..used_count] {
|
||||
recv_queue.add_used(desc_index, len).unwrap();
|
||||
}
|
||||
|
||||
used_count > 0
|
||||
used_descs
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -182,10 +180,9 @@ impl ConsoleEpollHandler {
|
||||
*/
|
||||
fn process_output_queue(&mut self) -> bool {
|
||||
let trans_queue = &mut self.queues[1]; //transmitq
|
||||
let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
|
||||
let mut used_count = 0;
|
||||
let mut used_descs = false;
|
||||
|
||||
for mut desc_chain in trans_queue.iter().unwrap() {
|
||||
while let Some(mut desc_chain) = trans_queue.pop_descriptor_chain(self.mem.memory()) {
|
||||
let desc = desc_chain.next().unwrap();
|
||||
if let Some(ref mut out) = self.endpoint.out_file() {
|
||||
let _ = desc_chain.memory().write_to(
|
||||
@@ -196,14 +193,13 @@ impl ConsoleEpollHandler {
|
||||
);
|
||||
let _ = out.flush();
|
||||
}
|
||||
used_desc_heads[used_count] = (desc_chain.head_index(), desc.len());
|
||||
used_count += 1;
|
||||
trans_queue
|
||||
.add_used(desc_chain.memory(), desc_chain.head_index(), desc.len())
|
||||
.unwrap();
|
||||
used_descs = true;
|
||||
}
|
||||
|
||||
for &(desc_index, len) in &used_desc_heads[..used_count] {
|
||||
trans_queue.add_used(desc_index, len).unwrap();
|
||||
}
|
||||
used_count > 0
|
||||
used_descs
|
||||
}
|
||||
|
||||
fn signal_used_queue(&self, queue_index: u16) -> result::Result<(), DeviceError> {
|
||||
@@ -238,57 +234,76 @@ impl ConsoleEpollHandler {
|
||||
}
|
||||
|
||||
impl EpollHelperHandler for ConsoleEpollHandler {
|
||||
fn handle_event(&mut self, _helper: &mut EpollHelper, event: &epoll::Event) -> bool {
|
||||
fn handle_event(
|
||||
&mut self,
|
||||
_helper: &mut EpollHelper,
|
||||
event: &epoll::Event,
|
||||
) -> result::Result<(), EpollHelperError> {
|
||||
let ev_type = event.data as u16;
|
||||
match ev_type {
|
||||
INPUT_QUEUE_EVENT => {
|
||||
if let Err(e) = self.input_queue_evt.read() {
|
||||
error!("Failed to get queue event: {:?}", e);
|
||||
return true;
|
||||
} else if self.process_input_queue() {
|
||||
if let Err(e) = self.signal_used_queue(0) {
|
||||
error!("Failed to signal used queue: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
self.input_queue_evt.read().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
|
||||
})?;
|
||||
if self.process_input_queue() {
|
||||
self.signal_used_queue(0).map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal used queue: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
}
|
||||
OUTPUT_QUEUE_EVENT => {
|
||||
if let Err(e) = self.output_queue_evt.read() {
|
||||
error!("Failed to get queue event: {:?}", e);
|
||||
return true;
|
||||
} else if self.process_output_queue() {
|
||||
if let Err(e) = self.signal_used_queue(1) {
|
||||
error!("Failed to signal used queue: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
self.output_queue_evt.read().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
|
||||
})?;
|
||||
if self.process_output_queue() {
|
||||
self.signal_used_queue(1).map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal used queue: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
}
|
||||
INPUT_EVENT => {
|
||||
if let Err(e) = self.input_evt.read() {
|
||||
error!("Failed to get input event: {:?}", e);
|
||||
return true;
|
||||
} else if self.process_input_queue() {
|
||||
if let Err(e) = self.signal_used_queue(0) {
|
||||
error!("Failed to signal used queue: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
self.input_evt.read().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!("Failed to get input event: {:?}", e))
|
||||
})?;
|
||||
if self.process_input_queue() {
|
||||
self.signal_used_queue(0).map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal used queue: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
}
|
||||
CONFIG_EVENT => {
|
||||
if let Err(e) = self.config_evt.read() {
|
||||
error!("Failed to get config event: {:?}", e);
|
||||
return true;
|
||||
} else if let Err(e) = self.interrupt_cb.trigger(VirtioInterruptType::Config) {
|
||||
error!("Failed to signal console driver: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
self.config_evt.read().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!("Failed to get config event: {:?}", e))
|
||||
})?;
|
||||
self.interrupt_cb
|
||||
.trigger(VirtioInterruptType::Config)
|
||||
.map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal console driver: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
RESIZE_EVENT => {
|
||||
if let Err(e) = self.resize_pipe.as_ref().unwrap().read_exact(&mut [0]) {
|
||||
error!("Failed to get resize event: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
|
||||
self.resize_pipe
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.read_exact(&mut [0])
|
||||
.map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to get resize event: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
self.resizer.update_console_size();
|
||||
}
|
||||
FILE_EVENT => {
|
||||
@@ -300,19 +315,22 @@ impl EpollHelperHandler for ConsoleEpollHandler {
|
||||
}
|
||||
|
||||
if self.process_input_queue() {
|
||||
if let Err(e) = self.signal_used_queue(0) {
|
||||
error!("Failed to signal used queue: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
self.signal_used_queue(0).map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal used queue: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("Unknown event for virtio-console");
|
||||
return true;
|
||||
return Err(EpollHelperError::HandleEvent(anyhow!(
|
||||
"Unknown event for virtio-console"
|
||||
)));
|
||||
}
|
||||
}
|
||||
false
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,12 +506,11 @@ impl VirtioDevice for Console {
|
||||
|
||||
fn activate(
|
||||
&mut self,
|
||||
_mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
mut queue_evts: Vec<EventFd>,
|
||||
mut queues: Vec<(usize, Queue, EventFd)>,
|
||||
) -> ActivateResult {
|
||||
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
|
||||
self.common.activate(&queues, &interrupt_cb)?;
|
||||
self.resizer
|
||||
.acked_features
|
||||
.store(self.common.acked_features, Ordering::Relaxed);
|
||||
@@ -507,13 +524,22 @@ impl VirtioDevice for Console {
|
||||
let (kill_evt, pause_evt) = self.common.dup_eventfds();
|
||||
let input_evt = EventFd::new(EFD_NONBLOCK).unwrap();
|
||||
|
||||
let mut virtqueues = Vec::new();
|
||||
let (_, queue, queue_evt) = queues.remove(0);
|
||||
virtqueues.push(queue);
|
||||
let input_queue_evt = queue_evt;
|
||||
let (_, queue, queue_evt) = queues.remove(0);
|
||||
virtqueues.push(queue);
|
||||
let output_queue_evt = queue_evt;
|
||||
|
||||
let mut handler = ConsoleEpollHandler {
|
||||
queues,
|
||||
mem,
|
||||
queues: virtqueues,
|
||||
interrupt_cb,
|
||||
in_buffer: self.in_buffer.clone(),
|
||||
endpoint: self.endpoint.clone(),
|
||||
input_queue_evt: queue_evts.remove(0),
|
||||
output_queue_evt: queue_evts.remove(0),
|
||||
input_queue_evt,
|
||||
output_queue_evt,
|
||||
input_evt,
|
||||
config_evt: self.resizer.config_evt.try_clone().unwrap(),
|
||||
resize_pipe: self.resize_pipe.as_ref().map(|p| p.try_clone().unwrap()),
|
||||
@@ -533,11 +559,7 @@ impl VirtioDevice for Console {
|
||||
Thread::VirtioConsole,
|
||||
&mut epoll_threads,
|
||||
&self.exit_evt,
|
||||
move || {
|
||||
if let Err(e) = handler.run(paused, paused_sync.unwrap()) {
|
||||
error!("Error running worker: {:?}", e);
|
||||
}
|
||||
},
|
||||
move || handler.run(paused, paused_sync.unwrap()),
|
||||
)?;
|
||||
|
||||
self.common.epoll_threads = Some(epoll_threads);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user