mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
479fef1b8e | ||
|
|
fc8f867879 | ||
|
|
e2eb59bcc8 | ||
|
|
1e557fdb3c | ||
|
|
10ce348aa6 | ||
|
|
63130d0f92 | ||
|
|
e6cc364703 | ||
|
|
a861918f06 |
16
.github/workflows/build.yaml
vendored
16
.github/workflows/build.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
- "1.60"
|
||||
- 1.56
|
||||
target:
|
||||
- x86_64-unknown-linux-gnu
|
||||
- x86_64-unknown-linux-musl
|
||||
@@ -39,25 +39,25 @@ jobs:
|
||||
git checkout $GITHUB_SHA
|
||||
|
||||
- name: Build (default features)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor -- -D warnings
|
||||
run: cargo rustc --bin cloud-hypervisor -- -D warnings
|
||||
|
||||
- name: Build (common + kvm)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "common,kvm" -- -D warnings
|
||||
run: cargo rustc --bin cloud-hypervisor --no-default-features --features "common,kvm" -- -D warnings
|
||||
|
||||
- name: Build (default features + tdx)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "tdx" -- -D warnings
|
||||
run: cargo rustc --bin cloud-hypervisor --features "tdx" -- -D warnings
|
||||
|
||||
- name: Build (default features + amx)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "amx" -- -D warnings
|
||||
run: cargo rustc --bin cloud-hypervisor --features "amx" -- -D warnings
|
||||
|
||||
- name: Build (default features + gdb)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "gdb" -- -D warnings
|
||||
run: cargo rustc --bin cloud-hypervisor --features "gdb" -- -D warnings
|
||||
|
||||
- name: Build (common + mshv)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "common,mshv" -- -D warnings
|
||||
run: cargo rustc --bin cloud-hypervisor --no-default-features --features "common,mshv" -- -D warnings
|
||||
|
||||
- name: Release Build (default features)
|
||||
run: cargo build --locked --all --release --target=${{ matrix.target }}
|
||||
run: cargo build --all --release --target=${{ matrix.target }}
|
||||
|
||||
- name: Check build did not modify any files
|
||||
run: test -z "$(git status --porcelain)"
|
||||
|
||||
10
.github/workflows/quality.yaml
vendored
10
.github/workflows/quality.yaml
vendored
@@ -34,19 +34,19 @@ jobs:
|
||||
run: cargo fmt -- --check
|
||||
|
||||
- name: Clippy (common + kvm)
|
||||
run: cargo clippy --locked --all --all-targets --no-default-features --tests --features "common,kvm" -- -D warnings
|
||||
run: cargo clippy --all --all-targets --no-default-features --tests --features "common,kvm" -- -D warnings
|
||||
|
||||
- name: Clippy (default features)
|
||||
run: cargo clippy --locked --all --all-targets --tests -- -D warnings
|
||||
run: cargo clippy --all --all-targets --tests -- -D warnings
|
||||
|
||||
- name: Clippy (default features + amx)
|
||||
run: cargo clippy --locked --all --all-targets --tests --features "amx" -- -D warnings
|
||||
run: cargo clippy --all --all-targets --tests --features "amx" -- -D warnings
|
||||
|
||||
- name: Clippy (default features + gdb)
|
||||
run: cargo clippy --locked --all --all-targets --tests --features "gdb" -- -D warnings
|
||||
run: cargo clippy --all --all-targets --tests --features "gdb" -- -D warnings
|
||||
|
||||
- name: Clippy (common + mshv)
|
||||
run: cargo clippy --locked --all --all-targets --no-default-features --tests --features "common,mshv" -- -D warnings
|
||||
run: cargo clippy --all --all-targets --no-default-features --tests --features "common,mshv" -- -D warnings
|
||||
|
||||
- name: Check build did not modify any files
|
||||
run: test -z "$(git status --porcelain)"
|
||||
|
||||
10
.github/workflows/release.yaml
vendored
10
.github/workflows/release.yaml
vendored
@@ -16,23 +16,23 @@ jobs:
|
||||
- name: Install Rust toolchain (x86_64-unknown-linux-gnu)
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "1.60"
|
||||
toolchain: 1.58
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- name: Install Rust toolchain (x86_64-unknown-linux-musl)
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "1.60"
|
||||
toolchain: 1.58
|
||||
target: x86_64-unknown-linux-musl
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
toolchain: "1.60"
|
||||
toolchain: 1.58
|
||||
command: build
|
||||
args: --all --release --target=x86_64-unknown-linux-gnu
|
||||
- name: Static Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
toolchain: "1.60"
|
||||
toolchain: 1.58
|
||||
command: build
|
||||
args: --all --release --target=x86_64-unknown-linux-musl
|
||||
- name: Strip cloud-hypervisor binaries
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
- name: Install Rust toolchain (aarch64-unknown-linux-musl)
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "1.60"
|
||||
toolchain: 1.58
|
||||
target: aarch64-unknown-linux-musl
|
||||
override: true
|
||||
- name: Static Build (AArch64)
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,4 +4,3 @@
|
||||
**/*.rs.bk
|
||||
**/Cargo.lock
|
||||
**/rusty-tags.vi
|
||||
/rpm/SOURCES
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Add the list of code owners here (using their GitHub username)
|
||||
* @cloud-hypervisor/cloud-hypervisor-reviewers
|
||||
@@ -2,14 +2,12 @@
|
||||
|
||||
Cloud Hypervisor is an open source project licensed under the [Apache v2
|
||||
License](https://opensource.org/licenses/Apache-2.0) and the [BSD 3
|
||||
Clause](https://opensource.org/licenses/BSD-3-Clause) license. Individual files
|
||||
contain details of their licensing and changes to that file are under the same
|
||||
license unless the contribution changes the license of the file. When importing
|
||||
code from a third party project (e.g. Firecracker or CrosVM) please respect the
|
||||
license of those projects.
|
||||
|
||||
New code should be under the [Apache v2
|
||||
License](https://opensource.org/licenses/Apache-2.0).
|
||||
Clause](https://opensource.org/licenses/BSD-3-Clause) license. Contributions
|
||||
can be made under either license or both. Individual files contain details of
|
||||
their licensing and changes to that file are under the same license unless the
|
||||
contribution changes the license of the file. When importing code from a third
|
||||
party project (e.g. Firecracker or CrosVM) please respect the license of those
|
||||
projects.
|
||||
|
||||
## Coding Style
|
||||
|
||||
@@ -65,9 +63,11 @@ you want to merge your changes to `cloud-hypervisor`:
|
||||
2. Within your fork, create a branch for your contribution.
|
||||
3. [Create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)
|
||||
against the main branch of the Cloud Hypervisor repository.
|
||||
4. To update your pull request amend existing commits whenever applicable and
|
||||
4. Add reviewers to your pull request and then work with your reviewers to address
|
||||
any comments and obtain minimum of 2 [maintainers](MAINTAINERS.md) approvals.
|
||||
To update your pull request amend existing commits whenever applicable and
|
||||
then push the new changes to your pull request branch.
|
||||
5. Once the pull request is approved it can be integrated.
|
||||
5. Once the pull request is approved, one of the maintainers will merge it.
|
||||
|
||||
## Issue tracking
|
||||
|
||||
|
||||
212
Cargo.lock
generated
212
Cargo.lock
generated
@@ -20,9 +20,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.57"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
|
||||
checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -50,6 +50,7 @@ dependencies = [
|
||||
"linux-loader",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
@@ -130,33 +131,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.1.18"
|
||||
version = "3.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
|
||||
checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"os_str_bytes",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"terminal_size",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "24.0.0"
|
||||
version = "23.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -265,6 +257,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
@@ -351,6 +344,7 @@ dependencies = [
|
||||
"mshv-bindings",
|
||||
"mshv-ioctls",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"vm-memory",
|
||||
@@ -405,20 +399,11 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnetwork"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f84f1612606f3753f205a4e9a2efd6fe5b4c573a6269b2cc6c3003d44a0d127"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.2"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
||||
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||
|
||||
[[package]]
|
||||
name = "kvm-bindings"
|
||||
@@ -448,9 +433,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.126"
|
||||
version = "0.2.123"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||
checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd"
|
||||
|
||||
[[package]]
|
||||
name = "libssh2-sys"
|
||||
@@ -468,9 +453,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.6"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92e7e15d7610cce1d9752e137625f14e61a28cd45929b6e12e47b50fe154ee2e"
|
||||
checksum = "6f35facd4a5673cb5a48822be2be1d4236c1c99cb4113cab7061ac720d5bf859"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -499,9 +484,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -514,9 +499,9 @@ checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
@@ -530,7 +515,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mshv-bindings"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#67e13faa36989a9226ad21c9ff3947e1d5738a54"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#75cf309d566c3d9ba91e81582a7864032ecc5bbb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"serde",
|
||||
@@ -542,7 +527,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mshv-ioctls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#67e13faa36989a9226ad21c9ff3947e1d5738a54"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#75cf309d566c3d9ba91e81582a7864032ecc5bbb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mshv-bindings",
|
||||
@@ -579,35 +564,29 @@ dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "no-std-net"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "111.20.0+1.1.1o"
|
||||
version = "111.18.0+1.1.1n"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92892c4f87d56e376e469ace79f1128fdaded07646ddf73aa0be4706ff712dec"
|
||||
checksum = "7897a926e1e8d00219127dc020130eca4292e5ca666dd592480d72c3eca2ff6c"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.73"
|
||||
version = "0.9.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d5fd19fb3e0a8191c1e34935718976a3e70c112ab9a24af6d7cadccd9d90bc0"
|
||||
checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
@@ -623,9 +602,12 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.0.1"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435"
|
||||
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
@@ -668,6 +650,7 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
@@ -688,6 +671,7 @@ dependencies = [
|
||||
"clap",
|
||||
"dirs",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"test_infra",
|
||||
"thiserror",
|
||||
@@ -702,11 +686,11 @@ checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
||||
|
||||
[[package]]
|
||||
name = "pnet"
|
||||
version = "0.30.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5cc57672f576f6b95370277fb445738d4887195c6cf4192bdf4f44697e2389b"
|
||||
checksum = "8750e073f82219c01e771133c64718d7685aef922da8a0d430a46aed05b6341a"
|
||||
dependencies = [
|
||||
"ipnetwork 0.19.0",
|
||||
"ipnetwork",
|
||||
"pnet_base",
|
||||
"pnet_datalink",
|
||||
"pnet_packet",
|
||||
@@ -716,20 +700,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pnet_base"
|
||||
version = "0.30.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e88341c6c842f89bdc7287f7b1e26b6fa64fa11c7ea3756971e6f18cd2510c4"
|
||||
dependencies = [
|
||||
"no-std-net",
|
||||
]
|
||||
checksum = "8205fe084bd43a3af79b3155c19feddd62e733640498842e631a2ffe107d1538"
|
||||
|
||||
[[package]]
|
||||
name = "pnet_datalink"
|
||||
version = "0.30.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc6e55d71c51db73372db35bc54f43abd8460adff1c3a9b717804ca6416d5df2"
|
||||
checksum = "6f85aef5e52e22ff06b1b11f2eb6d52959a9e0ecad3cb3f5cc2d78cadc077f0e"
|
||||
dependencies = [
|
||||
"ipnetwork 0.18.0",
|
||||
"ipnetwork",
|
||||
"libc",
|
||||
"pnet_base",
|
||||
"pnet_sys",
|
||||
@@ -738,9 +719,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pnet_macros"
|
||||
version = "0.30.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebfcdc9c072966723026b3596a1f655fb8bbfe0142f9770f8d481aee4459d6b9"
|
||||
checksum = "98cc3af95fed6dc318dfede3e81320f96ad5e237c6f7c4688108b19c8e67432d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -750,18 +731,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pnet_macros_support"
|
||||
version = "0.30.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bba532f5a4b320c029d89e612671fb621851b3b07e972c53850d34130033a5cd"
|
||||
checksum = "feaba58ba96abb218ec584d6caf0d3ff48922df05dbbeb1560553c197091b29e"
|
||||
dependencies = [
|
||||
"pnet_base",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pnet_packet"
|
||||
version = "0.30.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7009716ac86091c1b6e2cdec95a2b028c880f516054c1ec11edd02f9f463cbde"
|
||||
checksum = "f246edaaf1aaf82072d4cd38ee18bcc5dfc0464093f9ca39e4ac5962d68cf9d4"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"pnet_base",
|
||||
@@ -771,9 +752,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pnet_sys"
|
||||
version = "0.30.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e2a05efbc55c22f664c0ea475fbc4ffc4d09346aff9068438279d7e3d431f6f"
|
||||
checksum = "028c87a5e3a48fc07df099a2025f2ef16add5993712e1494ba69a6707ee7ed06"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
@@ -781,9 +762,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pnet_transport"
|
||||
version = "0.30.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b8ff06f37863f7590183f7044ab2e8d4dae991ecea0c791e3c6dd61ed2913d"
|
||||
checksum = "950f2a7961e19d22e19e84ff0a6e0955013185fe149673499662633d02b41b7a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pnet_base",
|
||||
@@ -793,11 +774,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.39"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
||||
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -851,9 +832,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.6"
|
||||
version = "1.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
|
||||
checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -862,15 +843,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.26"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "remain"
|
||||
version = "0.2.3"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c35270ea384ac1762895831cc8acb96f171468e52cec82ed9186f9416209fa4"
|
||||
checksum = "70ba1e78fa68412cb93ef642fd4d20b9a941be49ee9333875ebaf13112673ea7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -888,9 +869,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.10"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
|
||||
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
@@ -909,24 +890,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.9"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd"
|
||||
checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.137"
|
||||
version = "1.0.136"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.137"
|
||||
version = "1.0.136"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
|
||||
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -935,9 +913,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.81"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
|
||||
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -946,9 +924,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.14"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
|
||||
checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
@@ -995,13 +973,13 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.95"
|
||||
version = "1.0.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
|
||||
checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1059,41 +1037,35 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.31"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.31"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.3"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.0.0"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cfcd319456c4d6ea10087ed423473267e1a071f3bc0aa89f80d60997843c6f0"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
@@ -1203,7 +1175,8 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vhost-user-backend"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/rust-vmm/vhost-user-backend?rev=14f58eda14076e973704d4f904850be1146fbb05#14f58eda14076e973704d4f904850be1146fbb05"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1490f2028d4f119b2292efe218b5f8cfc6471f039b53b6a6eb5d9513e964facc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
@@ -1276,6 +1249,7 @@ dependencies = [
|
||||
"rate_limiter",
|
||||
"seccompiler",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
@@ -1293,9 +1267,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88f2d73c184c18f8acc32dab77fcb6e3af92d53262538d3a68aa474810d6863c"
|
||||
checksum = "3785325315e6496fa88673842ee6cd198b9658e88e8b0e1ad48a5dc818b221dc"
|
||||
dependencies = [
|
||||
"log",
|
||||
"vm-memory",
|
||||
@@ -1316,8 +1290,8 @@ name = "vm-device"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hypervisor",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"vfio-ioctls",
|
||||
@@ -1332,9 +1306,9 @@ source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#ca35d96191f8232bd7a
|
||||
|
||||
[[package]]
|
||||
name = "vm-memory"
|
||||
version = "0.8.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "767ed8aaebbff902e02e6d3749dc2baef55e46565f8a6414a065e5baee4b4a81"
|
||||
checksum = "339d4349c126fdcd87e034631d7274370cf19eb0e87b33166bcd956589fc72c5"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"libc",
|
||||
@@ -1347,6 +1321,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
@@ -1392,6 +1367,7 @@ dependencies = [
|
||||
"qcow",
|
||||
"seccompiler",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"signal-hook",
|
||||
"thiserror",
|
||||
|
||||
24
Cargo.toml
24
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cloud-hypervisor"
|
||||
version = "24.0.0"
|
||||
version = "23.1.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
default-run = "cloud-hypervisor"
|
||||
@@ -10,31 +10,31 @@ description = "Open source Virtual Machine Monitor (VMM) that runs on top of KVM
|
||||
homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
||||
# Minimum buildable version:
|
||||
# Keep in sync with version in .github/workflows/build.yaml
|
||||
rust-version = "1.60"
|
||||
rust-version = "1.56"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.56"
|
||||
api_client = { path = "api_client" }
|
||||
clap = { version = "3.1.18", features = ["wrap_help","cargo"] }
|
||||
clap = { version = "3.1.8", features = ["wrap_help","cargo"] }
|
||||
epoll = "4.3.1"
|
||||
event_monitor = { path = "event_monitor" }
|
||||
hypervisor = { path = "hypervisor" }
|
||||
libc = "0.2.126"
|
||||
log = { version = "0.4.17", features = ["std"] }
|
||||
libc = "0.2.123"
|
||||
log = { version = "0.4.16", features = ["std"] }
|
||||
option_parser = { path = "option_parser" }
|
||||
seccompiler = "0.2.0"
|
||||
serde_json = "1.0.81"
|
||||
signal-hook = "0.3.14"
|
||||
thiserror = "1.0.31"
|
||||
serde_json = "1.0.79"
|
||||
signal-hook = "0.3.13"
|
||||
thiserror = "1.0.30"
|
||||
vmm = { path = "vmm" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
vm-memory = "0.8.0"
|
||||
vm-memory = "0.7.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.1.18", features = ["cargo"] }
|
||||
clap = { version = "3.1.8", features = ["cargo"] }
|
||||
|
||||
# List of patched crates
|
||||
[patch.crates-io]
|
||||
@@ -46,7 +46,7 @@ versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_deri
|
||||
dirs = "4.0.0"
|
||||
lazy_static= "1.4.0"
|
||||
net_util = { path = "net_util" }
|
||||
serde_json = "1.0.81"
|
||||
serde_json = "1.0.79"
|
||||
test_infra = { path = "test_infra" }
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
|
||||
115
Jenkinsfile
vendored
115
Jenkinsfile
vendored
@@ -153,6 +153,88 @@ pipeline{
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Worker build SGX') {
|
||||
agent { node { label 'bionic-sgx' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
allOf {
|
||||
branch 'main'
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Run SGX integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-sgx"
|
||||
}
|
||||
}
|
||||
stage ('Run SGX integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-sgx --libc musl"
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||
deleteDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Worker build VFIO') {
|
||||
agent { node { label 'bionic-vfio' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
allOf {
|
||||
branch 'main'
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Run VFIO integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-vfio"
|
||||
}
|
||||
}
|
||||
stage ('Run VFIO integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-vfio --libc musl"
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||
deleteDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Worker build - Windows guest') {
|
||||
agent { node { label 'focal' } }
|
||||
when {
|
||||
@@ -233,6 +315,39 @@ pipeline{
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Worker build - Metrics') {
|
||||
agent { node { label 'focal-metrics' } }
|
||||
when {
|
||||
branch 'main'
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
environment {
|
||||
METRICS_PUBLISH_KEY = credentials('52e0945f-ce7a-43d1-87af-67d1d87cc40f')
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Run metrics tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --metrics -- -- --report-file /root/workloads/metrics.json'
|
||||
}
|
||||
}
|
||||
stage ('Upload metrics report') {
|
||||
steps {
|
||||
sh 'curl -X PUT https://cloud-hypervisor-metrics.azurewebsites.net/api/publishmetrics -H "x-functions-key: $METRICS_PUBLISH_KEY" -T ~/workloads/metrics.json'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
README.md
10
README.md
@@ -88,10 +88,12 @@ 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 git
|
||||
$ sudo apt install git
|
||||
# Install rust tool chain
|
||||
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
# Install build-essential
|
||||
$ sudo apt install build-essential
|
||||
# If you want to build statically linked binary please add musl target
|
||||
$ rustup target add x86_64-unknown-linux-musl
|
||||
```
|
||||
@@ -166,7 +168,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.3.2/hypervisor-fw
|
||||
$ popd
|
||||
```
|
||||
|
||||
@@ -289,8 +291,6 @@ As of 2022-04-05, the following cloud images are supported:
|
||||
Direct kernel boot to userspace should work with a rootfs from most
|
||||
distributions.
|
||||
|
||||
Further details can be found in the [release documentation](docs/releases.md).
|
||||
|
||||
## Hot Plug
|
||||
|
||||
Cloud Hypervisor supports hotplug of CPUs, passthrough devices (VFIO),
|
||||
|
||||
@@ -5,4 +5,4 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
vm-memory = "0.8.0"
|
||||
vm-memory = "0.7.0"
|
||||
|
||||
@@ -10,17 +10,18 @@ tdx = []
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { path = "../acpi_tables" }
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.56"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.126"
|
||||
libc = "0.2.123"
|
||||
linux-loader = { version = "0.4.0", features = ["elf", "bzimage", "pe"] }
|
||||
log = "0.4.17"
|
||||
serde = { version = "1.0.137", features = ["rc", "derive"] }
|
||||
thiserror = "1.0.31"
|
||||
log = "0.4.16"
|
||||
serde = { version = "1.0.136", features = ["rc"] }
|
||||
serde_derive = "1.0.136"
|
||||
thiserror = "1.0.30"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-bitmap"] }
|
||||
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-bitmap"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vmm-sys-util = { version = "0.9.0", features = ["with-serde"] }
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ pub enum Error {
|
||||
|
||||
impl From<Error> for super::Error {
|
||||
fn from(e: Error) -> super::Error {
|
||||
super::Error::PlatformSpecific(e)
|
||||
super::Error::AArch64Setup(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,16 +64,16 @@ pub struct EntryPoint {
|
||||
|
||||
/// Configure the specified VCPU, and return its MPIDR.
|
||||
pub fn configure_vcpu(
|
||||
vcpu: &Arc<dyn hypervisor::Vcpu>,
|
||||
fd: &Arc<dyn hypervisor::Vcpu>,
|
||||
id: u8,
|
||||
kernel_entry_point: Option<EntryPoint>,
|
||||
) -> super::Result<u64> {
|
||||
if let Some(kernel_entry_point) = kernel_entry_point {
|
||||
regs::setup_regs(vcpu, id, kernel_entry_point.entry_addr.raw_value())
|
||||
regs::setup_regs(fd, id, kernel_entry_point.entry_addr.raw_value())
|
||||
.map_err(Error::RegsConfiguration)?;
|
||||
}
|
||||
|
||||
let mpidr = vcpu.read_mpidr().map_err(Error::VcpuRegMpidr)?;
|
||||
let mpidr = fd.read_mpidr().map_err(Error::VcpuRegMpidr)?;
|
||||
Ok(mpidr)
|
||||
}
|
||||
|
||||
@@ -185,10 +185,10 @@ pub fn initramfs_load_addr(
|
||||
if guest_mem.address_in_range(offset) {
|
||||
Ok(offset.raw_value())
|
||||
} else {
|
||||
Err(super::Error::PlatformSpecific(Error::InitramfsAddress))
|
||||
Err(super::Error::AArch64Setup(Error::InitramfsAddress))
|
||||
}
|
||||
}
|
||||
None => Err(super::Error::PlatformSpecific(Error::InitramfsAddress)),
|
||||
None => Err(super::Error::AArch64Setup(Error::InitramfsAddress)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,15 +9,15 @@
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::x86_64::SgxEpcSection;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
use versionize::{VersionMap, Versionize, VersionizeError, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use vm_migration::VersionMapped;
|
||||
@@ -26,27 +26,31 @@ type GuestMemoryMmap = vm_memory::GuestMemoryMmap<vm_memory::bitmap::AtomicBitma
|
||||
type GuestRegionMmap = vm_memory::GuestRegionMmap<vm_memory::bitmap::AtomicBitmap>;
|
||||
|
||||
/// Type for returning error code.
|
||||
#[derive(Debug, Error)]
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[error("Platform specific error (x86_64): {0:?}")]
|
||||
PlatformSpecific(x86_64::Error),
|
||||
/// X86_64 specific error triggered during system configuration.
|
||||
X86_64Setup(x86_64::Error),
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[error("Platform specific error (aarch64): {0:?}")]
|
||||
PlatformSpecific(aarch64::Error),
|
||||
#[error("The memory map table extends past the end of guest memory")]
|
||||
/// AArch64 specific error triggered during system configuration.
|
||||
AArch64Setup(aarch64::Error),
|
||||
/// The zero page extends past the end of guest_mem.
|
||||
ZeroPagePastRamEnd,
|
||||
/// Error writing the zero page of guest memory.
|
||||
ZeroPageSetup(vm_memory::GuestMemoryError),
|
||||
/// The memory map table extends past the end of guest memory.
|
||||
MemmapTablePastRamEnd,
|
||||
#[error("Error writing memory map table to guest memory")]
|
||||
/// Error writing memory map table to guest memory.
|
||||
MemmapTableSetup,
|
||||
#[error("The hvm_start_info structure extends past the end of guest memory")]
|
||||
/// The hvm_start_info structure extends past the end of guest memory.
|
||||
StartInfoPastRamEnd,
|
||||
#[error("Error writing hvm_start_info to guest memory")]
|
||||
/// Error writing hvm_start_info to guest memory.
|
||||
StartInfoSetup,
|
||||
#[error("Failed to compute initramfs address")]
|
||||
/// Failed to compute initramfs address.
|
||||
InitramfsAddress,
|
||||
#[error("Error writing module entry to guest memory: {0}")]
|
||||
ModlistSetup(#[source] vm_memory::GuestMemoryError),
|
||||
#[error("RSDP extends past the end of guest memory")]
|
||||
/// Error writing module entry to guest memory.
|
||||
ModlistSetup(vm_memory::GuestMemoryError),
|
||||
/// RSDP Beyond Guest Memory
|
||||
RsdpPastRamEnd,
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +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::HypervisorError;
|
||||
use hypervisor::{CpuId, CpuIdEntry, HypervisorError, CPUID_FLAG_VALID_INDEX};
|
||||
use linux_loader::loader::bootparam::boot_params;
|
||||
use linux_loader::loader::elf::start_info::{
|
||||
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
|
||||
@@ -198,7 +197,7 @@ pub enum Error {
|
||||
|
||||
impl From<Error> for super::Error {
|
||||
fn from(e: Error) -> super::Error {
|
||||
super::Error::PlatformSpecific(e)
|
||||
super::Error::X86_64Setup(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -744,7 +743,7 @@ pub fn generate_common_cpuid(
|
||||
}
|
||||
|
||||
pub fn configure_vcpu(
|
||||
vcpu: &Arc<dyn hypervisor::Vcpu>,
|
||||
fd: &Arc<dyn hypervisor::Vcpu>,
|
||||
id: u8,
|
||||
kernel_entry_point: Option<EntryPoint>,
|
||||
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
@@ -756,23 +755,23 @@ pub fn configure_vcpu(
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id));
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1f, None, CpuidReg::EDX, u32::from(id));
|
||||
|
||||
vcpu.set_cpuid2(&cpuid)
|
||||
fd.set_cpuid2(&cpuid)
|
||||
.map_err(|e| Error::SetSupportedCpusFailed(e.into()))?;
|
||||
|
||||
if kvm_hyperv {
|
||||
vcpu.enable_hyperv_synic().unwrap();
|
||||
fd.enable_hyperv_synic().unwrap();
|
||||
}
|
||||
|
||||
regs::setup_msrs(vcpu).map_err(Error::MsrsConfiguration)?;
|
||||
regs::setup_msrs(fd).map_err(Error::MsrsConfiguration)?;
|
||||
if let Some(kernel_entry_point) = kernel_entry_point {
|
||||
if let Some(entry_addr) = kernel_entry_point.entry_addr {
|
||||
// Safe to unwrap because this method is called after the VM is configured
|
||||
regs::setup_regs(vcpu, entry_addr.raw_value()).map_err(Error::RegsConfiguration)?;
|
||||
regs::setup_fpu(vcpu).map_err(Error::FpuConfiguration)?;
|
||||
regs::setup_sregs(&vm_memory.memory(), vcpu).map_err(Error::SregsConfiguration)?;
|
||||
regs::setup_regs(fd, entry_addr.raw_value()).map_err(Error::RegsConfiguration)?;
|
||||
regs::setup_fpu(fd).map_err(Error::FpuConfiguration)?;
|
||||
regs::setup_sregs(&vm_memory.memory(), fd).map_err(Error::SregsConfiguration)?;
|
||||
}
|
||||
}
|
||||
interrupts::set_lint(vcpu).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
|
||||
interrupts::set_lint(fd).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -839,14 +838,13 @@ pub fn configure_system(
|
||||
_num_cpus: u8,
|
||||
rsdp_addr: Option<GuestAddress>,
|
||||
sgx_epc_region: Option<SgxEpcRegion>,
|
||||
serial_number: 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).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);
|
||||
@@ -1195,7 +1193,6 @@ mod tests {
|
||||
1,
|
||||
Some(layout::RSDP_POINTER),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
assert!(config_err.is_err());
|
||||
|
||||
@@ -1209,7 +1206,7 @@ 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).unwrap();
|
||||
|
||||
// Now assigning some memory that is equal to the start of the 32bit memory hole.
|
||||
let mem_size = 3328 << 20;
|
||||
@@ -1220,9 +1217,9 @@ 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).unwrap();
|
||||
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None, None).unwrap();
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None).unwrap();
|
||||
|
||||
// Now assigning some memory that falls after the 32bit memory hole.
|
||||
let mem_size = 3330 << 20;
|
||||
@@ -1233,9 +1230,9 @@ 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).unwrap();
|
||||
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None, None).unwrap();
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -9,7 +9,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::arch::x86::regs::*;
|
||||
use hypervisor::x86_64::{FpuState, SpecialRegisters, StandardRegisters};
|
||||
use std::sync::Arc;
|
||||
use std::{mem, result};
|
||||
|
||||
@@ -162,7 +162,7 @@ fn write_string(
|
||||
Ok(curptr)
|
||||
}
|
||||
|
||||
pub fn setup_smbios(mem: &GuestMemoryMmap, serial_number: Option<&str>) -> Result<u64> {
|
||||
pub fn setup_smbios(mem: &GuestMemoryMmap) -> Result<u64> {
|
||||
let physptr = GuestAddress(SMBIOS_START)
|
||||
.checked_add(mem::size_of::<Smbios30Entrypoint>() as u64)
|
||||
.ok_or(Error::NotEnoughMemory)?;
|
||||
@@ -195,15 +195,11 @@ pub fn setup_smbios(mem: &GuestMemoryMmap, serial_number: Option<&str>) -> Resul
|
||||
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
|
||||
..Default::default()
|
||||
};
|
||||
curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
|
||||
curptr = write_string(mem, "Cloud Hypervisor", curptr)?;
|
||||
curptr = write_string(mem, "cloud-hypervisor", curptr)?;
|
||||
if let Some(serial_number) = serial_number {
|
||||
curptr = write_string(mem, serial_number, curptr)?;
|
||||
}
|
||||
curptr = write_and_incr(mem, 0u8, curptr)?;
|
||||
}
|
||||
|
||||
@@ -267,7 +263,7 @@ mod tests {
|
||||
fn entrypoint_checksum() {
|
||||
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap();
|
||||
|
||||
setup_smbios(&mem, None).unwrap();
|
||||
setup_smbios(&mem).unwrap();
|
||||
|
||||
let smbios_ep: Smbios30Entrypoint = mem.read_obj(GuestAddress(SMBIOS_START)).unwrap();
|
||||
|
||||
|
||||
@@ -9,16 +9,16 @@ default = []
|
||||
|
||||
[dependencies]
|
||||
io-uring = "0.5.2"
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.123"
|
||||
log = "0.4.16"
|
||||
qcow = { path = "../qcow" }
|
||||
thiserror = "1.0.31"
|
||||
thiserror = "1.0.30"
|
||||
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.3.0"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
virtio-queue = "0.2.0"
|
||||
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
|
||||
@@ -6,17 +6,17 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { path = "../acpi_tables" }
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.56"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "1.3.2"
|
||||
byteorder = "1.4.3"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.123"
|
||||
log = "0.4.16"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = "0.8.0"
|
||||
vm-memory = "0.7.0"
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright © 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use std::fmt;
|
||||
use std::time::Instant;
|
||||
use vm_device::BusDevice;
|
||||
|
||||
/// Debug I/O port, see:
|
||||
/// https://www.intel.com/content/www/us/en/support/articles/000005500/boards-and-kits.html
|
||||
///
|
||||
/// Since we're not a physical platform, we can freely assign code ranges for
|
||||
/// debugging specific parts of our virtual platform.
|
||||
pub enum DebugIoPortRange {
|
||||
Firmware,
|
||||
Bootloader,
|
||||
Kernel,
|
||||
Userspace,
|
||||
Custom,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const DEBUG_IOPORT_PREFIX: &str = "Debug I/O port";
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
impl DebugIoPortRange {
|
||||
fn from_u8(value: u8) -> DebugIoPortRange {
|
||||
match value {
|
||||
0x00..=0x1f => DebugIoPortRange::Firmware,
|
||||
0x20..=0x3f => DebugIoPortRange::Bootloader,
|
||||
0x40..=0x5f => DebugIoPortRange::Kernel,
|
||||
0x60..=0x7f => DebugIoPortRange::Userspace,
|
||||
_ => DebugIoPortRange::Custom,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
impl fmt::Display for DebugIoPortRange {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
DebugIoPortRange::Firmware => write!(f, "{}: Firmware", DEBUG_IOPORT_PREFIX),
|
||||
DebugIoPortRange::Bootloader => write!(f, "{}: Bootloader", DEBUG_IOPORT_PREFIX),
|
||||
DebugIoPortRange::Kernel => write!(f, "{}: Kernel", DEBUG_IOPORT_PREFIX),
|
||||
DebugIoPortRange::Userspace => write!(f, "{}: Userspace", DEBUG_IOPORT_PREFIX),
|
||||
DebugIoPortRange::Custom => write!(f, "{}: Custom", DEBUG_IOPORT_PREFIX),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DebugPort {
|
||||
timestamp: Instant,
|
||||
}
|
||||
|
||||
impl DebugPort {
|
||||
pub fn new(timestamp: Instant) -> Self {
|
||||
Self { timestamp }
|
||||
}
|
||||
}
|
||||
|
||||
impl BusDevice for DebugPort {
|
||||
fn read(&mut self, _base: u64, _offset: u64, _data: &mut [u8]) {
|
||||
error!("Invalid read to debug port")
|
||||
}
|
||||
|
||||
fn write(
|
||||
&mut self,
|
||||
_base: u64,
|
||||
_offset: u64,
|
||||
data: &[u8],
|
||||
) -> Option<std::sync::Arc<std::sync::Barrier>> {
|
||||
let elapsed = self.timestamp.elapsed();
|
||||
|
||||
let code = data[0];
|
||||
warn!(
|
||||
"[{} code 0x{:x}] {}.{:>06} seconds",
|
||||
DebugIoPortRange::from_u8(code),
|
||||
code,
|
||||
elapsed.as_secs(),
|
||||
elapsed.as_micros()
|
||||
);
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,6 @@
|
||||
// found in the LICENSE-BSD-3-Clause file.
|
||||
|
||||
mod cmos;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
mod debug_port;
|
||||
#[cfg(feature = "fwdebug")]
|
||||
mod fwdebug;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
@@ -20,8 +18,6 @@ mod serial;
|
||||
mod uart_pl011;
|
||||
|
||||
pub use self::cmos::Cmos;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use self::debug_port::DebugPort;
|
||||
#[cfg(feature = "fwdebug")]
|
||||
pub use self::fwdebug::FwDebugDevice;
|
||||
pub use self::i8042::I8042Device;
|
||||
|
||||
@@ -10,7 +10,6 @@ use crate::{read_le_u32, write_le_u32};
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::time::Instant;
|
||||
use std::{io, result};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
@@ -121,7 +120,6 @@ impl Pl011 {
|
||||
id: String,
|
||||
irq: Arc<dyn InterruptSourceGroup>,
|
||||
out: Option<Box<dyn io::Write + Send>>,
|
||||
timestamp: Instant,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
@@ -142,7 +140,7 @@ impl Pl011 {
|
||||
read_trigger: 1u32,
|
||||
irq,
|
||||
out,
|
||||
timestamp,
|
||||
timestamp: std::time::Instant::now(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,7 +495,6 @@ mod tests {
|
||||
String::from(SERIAL_NAME),
|
||||
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||
Some(Box::new(pl011_out.clone())),
|
||||
Instant::now(),
|
||||
);
|
||||
|
||||
pl011.write(0, UARTDR as u64, &[b'x', b'y']);
|
||||
@@ -518,7 +515,6 @@ mod tests {
|
||||
String::from(SERIAL_NAME),
|
||||
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||
Some(Box::new(pl011_out)),
|
||||
Instant::now(),
|
||||
);
|
||||
|
||||
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd
|
||||
|
||||
103
docs/releases.md
103
docs/releases.md
@@ -1,103 +0,0 @@
|
||||
# Release Documentation
|
||||
|
||||
## Abstract
|
||||
|
||||
This document provides guidance to users, downstream maintainers and
|
||||
any other consumers of the Cloud Hypervisor project, this document
|
||||
describes the release process, release cadence, stability expectations and
|
||||
related topics.
|
||||
|
||||
## Basic Terms
|
||||
|
||||
### Stability
|
||||
|
||||
For Cloud Hypervisor the following areas are subject to stability guarantees:
|
||||
|
||||
- [REST API](api.md#rest-api)
|
||||
- [Command line options](api.md#command-line-interface)
|
||||
- [Device Model](device_model.md)
|
||||
- Device tree, device list, ACPI, Hyper-V enlightenments and any other
|
||||
features exposed to guest
|
||||
- KVM compatibility
|
||||
- Rust edition compatibility
|
||||
|
||||
This list is incomplete but this document serves as a best effort guide to stability
|
||||
across releases.
|
||||
|
||||
### Experimental features
|
||||
|
||||
Experimental features are under active development and no guarantees are made about their stability.
|
||||
|
||||
List of experimental features:
|
||||
|
||||
- TDX
|
||||
- vfio-user
|
||||
- vDPA
|
||||
|
||||
### Security
|
||||
|
||||
Security fixes should be included in a new point release.
|
||||
|
||||
For security issues an advisory will be published via the GitHub security advisory process along with the release. Watching the project on GitHub will notify you of those issues.
|
||||
|
||||
## Releases
|
||||
|
||||
### Versioning
|
||||
|
||||
The versioning scheme uses `MAJOR.POINT` pattern:
|
||||
|
||||
- `MAJOR` can introduce incompatible changes along with support for new features. Changes to the [API](api.md#rest-api),
|
||||
[CLI options](api.md#command-line-interface) and [device model](device_model.md)
|
||||
require a notice at least 2 releases in advance for the actual change to take
|
||||
place.
|
||||
- `POINT` contains bug fixes and/or security fixes.
|
||||
|
||||
### Major Release Cadence
|
||||
|
||||
Cloud Hypervisor is under active development. A new major release is issued approximately
|
||||
every 6 weeks. Point releases are issued on demand, when important bug fixes are in
|
||||
the queue. A major release would receive bug fixes for the next two cycles (~12 weeks)
|
||||
and then be considered EOL.
|
||||
|
||||
```
|
||||
+ - Active release support
|
||||
E - EOL
|
||||
|
||||
2021 2022 2023
|
||||
| | | | | | | | |
|
||||
18.0 | | | ++++++++E
|
||||
19.0 | | | |++++++++E
|
||||
20.0 | | | | ++++++++E
|
||||
21.0 | | | | | ++++++++E
|
||||
22.0 | | | | | +++++++++E
|
||||
23.0 | | | | | | +++++++++E
|
||||
|
||||
```
|
||||
|
||||
### Major Release Stability Considerations
|
||||
|
||||
Snapshot/restore support is not compatible across `MAJOR` versions.
|
||||
Live migration support is not compatible across `MAJOR` versions.
|
||||
|
||||
### LTS Release Cadence
|
||||
|
||||
A regular release is promoted to LTS every 12 months. An LTS release is supported for 18 months. This gives a 6 months window for users to move to the new LTS.
|
||||
|
||||
```
|
||||
+ - Active release support
|
||||
E - EOL
|
||||
|
||||
2022 2023 2024 2025 2026
|
||||
| | | | | | | | | | | | | | | | |
|
||||
23.0 | |+++++++++++++++++++++++++++++E
|
||||
43.0 | | | | | |+++++++++++++++++++++++++++++E
|
||||
63.0 | | | | | | | | | |+++++++++++++++++++++++++++++E
|
||||
|
||||
```
|
||||
|
||||
### LTS Stablity Considerations
|
||||
|
||||
An LTS release is just a `MAJOR` release for which point releases are made for
|
||||
longer following the same rules for what can be backported to a `POINT` release.
|
||||
The focus lays on critical and security bug fixes which are pulled at the
|
||||
maintainer's discretion.
|
||||
@@ -75,7 +75,8 @@ Here is an example how to create a bridge and add two DPDK ports to it
|
||||
ovs-vsctl add-br ovsbr0 -- set bridge ovsbr0 datapath_type=netdev
|
||||
# create two DPDK ports and add them to the bridge
|
||||
ovs-vsctl add-port ovsbr0 vhost-user1 -- set Interface vhost-user1 type=dpdkvhostuser
|
||||
ovs-vsctl add-port ovsbr0 vhost-user2 -- set Interface vhost-user2 type=dpdkvhostuser
|
||||
ovs-vsctl add-port ovsbr0 vhost-user2 -- set Interface vhost-user2
|
||||
type=dpdkvhostuser
|
||||
# set the number of rx queues
|
||||
ovs-vsctl set Interface vhost-user1 options:n_rxq=2
|
||||
ovs-vsctl set Interface vhost-user2 options:n_rxq=2
|
||||
|
||||
@@ -5,6 +5,7 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.126"
|
||||
serde = { version = "1.0.137", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.81"
|
||||
libc = "0.2.123"
|
||||
serde = { version = "1.0.136", features = ["rc"] }
|
||||
serde_derive = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use serde::Serialize;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
@@ -49,9 +50,6 @@ pub fn event_log(source: &str, event: &str, properties: Option<&HashMap<Cow<str>
|
||||
properties,
|
||||
};
|
||||
serde_json::to_writer_pretty(file, &e).ok();
|
||||
|
||||
let mut file = file;
|
||||
file.write_all(b"\n\n").ok();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
132
fuzz/Cargo.lock
generated
132
fuzz/Cargo.lock
generated
@@ -11,9 +11,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.57"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
|
||||
checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -47,6 +47,7 @@ dependencies = [
|
||||
"linux-loader",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
@@ -127,33 +128,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.1.18"
|
||||
version = "3.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
|
||||
checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"os_str_bytes",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"terminal_size",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "23.0.0"
|
||||
version = "22.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -242,6 +234,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
@@ -313,6 +306,7 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"vm-memory",
|
||||
@@ -351,9 +345,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.2"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
||||
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||
|
||||
[[package]]
|
||||
name = "kvm-bindings"
|
||||
@@ -383,9 +377,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.126"
|
||||
version = "0.2.123"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||
checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
@@ -409,9 +403,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -422,6 +416,12 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
version = "0.1.0"
|
||||
@@ -460,18 +460,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.12.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
|
||||
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
|
||||
|
||||
[[package]]
|
||||
name = "option_parser"
|
||||
@@ -479,9 +479,12 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.0.1"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435"
|
||||
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
@@ -499,6 +502,7 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
@@ -514,11 +518,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.39"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
||||
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -552,9 +556,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "remain"
|
||||
version = "0.2.3"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c35270ea384ac1762895831cc8acb96f171468e52cec82ed9186f9416209fa4"
|
||||
checksum = "70ba1e78fa68412cb93ef642fd4d20b9a941be49ee9333875ebaf13112673ea7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -572,9 +576,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.10"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
|
||||
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||
|
||||
[[package]]
|
||||
name = "seccompiler"
|
||||
@@ -587,24 +591,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.9"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd"
|
||||
checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.137"
|
||||
version = "1.0.136"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.137"
|
||||
version = "1.0.136"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
|
||||
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -613,9 +614,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.81"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
|
||||
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -624,9 +625,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.14"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
|
||||
checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
@@ -655,13 +656,13 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.95"
|
||||
version = "1.0.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
|
||||
checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -694,18 +695,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.31"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.31"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -713,16 +714,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.0"
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.0.0"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cfcd319456c4d6ea10087ed423473267e1a071f3bc0aa89f80d60997843c6f0"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
@@ -846,6 +847,7 @@ dependencies = [
|
||||
"rate_limiter",
|
||||
"seccompiler",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
@@ -863,9 +865,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88f2d73c184c18f8acc32dab77fcb6e3af92d53262538d3a68aa474810d6863c"
|
||||
checksum = "3785325315e6496fa88673842ee6cd198b9658e88e8b0e1ad48a5dc818b221dc"
|
||||
dependencies = [
|
||||
"log",
|
||||
"vm-memory",
|
||||
@@ -886,8 +888,8 @@ name = "vm-device"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hypervisor",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"vfio-ioctls",
|
||||
@@ -902,9 +904,9 @@ source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#720e48e435b791ec6cb
|
||||
|
||||
[[package]]
|
||||
name = "vm-memory"
|
||||
version = "0.8.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "767ed8aaebbff902e02e6d3749dc2baef55e46565f8a6414a065e5baee4b4a81"
|
||||
checksum = "339d4349c126fdcd87e034631d7274370cf19eb0e87b33166bcd956589fc72c5"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"libc",
|
||||
@@ -917,6 +919,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
@@ -962,6 +965,7 @@ dependencies = [
|
||||
"qcow",
|
||||
"seccompiler",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"signal-hook",
|
||||
"thiserror",
|
||||
|
||||
@@ -10,16 +10,16 @@ cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
block_util = { path = "../block_util" }
|
||||
libc = "0.2.126"
|
||||
libc = "0.2.123"
|
||||
libfuzzer-sys = "0.4.3"
|
||||
qcow = { path = "../qcow" }
|
||||
seccompiler = "0.2.0"
|
||||
vhdx = { path = "../vhdx" }
|
||||
virtio-devices = { path = "../virtio-devices" }
|
||||
virtio-queue = "0.3.0"
|
||||
virtio-queue = "0.2.0"
|
||||
vmm-sys-util = "0.9.0"
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vm-memory = "0.8.0"
|
||||
vm-memory = "0.7.0"
|
||||
|
||||
[dependencies.cloud-hypervisor]
|
||||
path = ".."
|
||||
|
||||
@@ -11,18 +11,19 @@ mshv = ["mshv-ioctls", "mshv-bindings"]
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.56"
|
||||
epoll = "4.3.1"
|
||||
thiserror = "1.0.31"
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
thiserror = "1.0.30"
|
||||
libc = "0.2.123"
|
||||
log = "0.4.16"
|
||||
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-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.137", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.81"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
serde = { version = "1.0.136", features = ["rc"] }
|
||||
serde_derive = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = { version = "0.9.0", features = ["with-serde"] }
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
|
||||
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::arch::x86::regs::*;
|
||||
use crate::arch::x86::*;
|
||||
use crate::arch::x86::{Exception, SegmentRegisterOps};
|
||||
use crate::x86_64::{SegmentRegister, SpecialRegisters, StandardRegisters};
|
||||
use anyhow::Context;
|
||||
use iced_x86::*;
|
||||
|
||||
@@ -12,12 +12,6 @@
|
||||
use crate::aarch64::VcpuInit;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::aarch64::{RegList, Register, 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")]
|
||||
@@ -29,6 +23,12 @@ use crate::CpuState;
|
||||
use crate::DeviceAttr;
|
||||
#[cfg(feature = "kvm")]
|
||||
use crate::MpState;
|
||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||
use crate::SuspendRegisters;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::Xsave;
|
||||
#[cfg(feature = "tdx")]
|
||||
use crate::{TdxExitDetails, TdxExitStatus};
|
||||
use thiserror::Error;
|
||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
// Copyright 2018-2019 CrowdStrike, Inc.
|
||||
//
|
||||
//
|
||||
#[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;
|
||||
#[cfg(feature = "tdx")]
|
||||
use crate::TdxCapabilities;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ use kvm_bindings::{
|
||||
pub use kvm_bindings::{
|
||||
kvm_one_reg as Register, kvm_regs as StandardRegisters, kvm_vcpu_init as VcpuInit, RegList,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
pub use {kvm_ioctls::Cap, kvm_ioctls::Kvm};
|
||||
|
||||
// This macro gets the offset of a structure (i.e `str`) member (i.e `field`) without having
|
||||
|
||||
@@ -17,11 +17,11 @@ use crate::cpu;
|
||||
use crate::device;
|
||||
use crate::hypervisor;
|
||||
use crate::vec_with_array_field;
|
||||
use crate::vm::{self, InterruptSourceConfig, VmOps};
|
||||
use crate::vm::{self, VmmOps};
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::{arm64_core_reg_id, offset__of};
|
||||
use kvm_ioctls::{NoDatamatch, VcpuFd, VmFd};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use std::convert::TryInto;
|
||||
@@ -234,7 +234,7 @@ impl vm::Vm for KvmVm {
|
||||
fn create_vcpu(
|
||||
&self,
|
||||
id: u8,
|
||||
vm_ops: Option<Arc<dyn VmOps>>,
|
||||
vmmops: Option<Arc<dyn VmmOps>>,
|
||||
) -> vm::Result<Arc<dyn cpu::Vcpu>> {
|
||||
let vc = self
|
||||
.fd
|
||||
@@ -244,7 +244,7 @@ impl vm::Vm for KvmVm {
|
||||
fd: vc,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
msrs: self.msrs.clone(),
|
||||
vm_ops,
|
||||
vmmops,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
hyperv_synic: AtomicBool::new(false),
|
||||
};
|
||||
@@ -284,64 +284,6 @@ impl vm::Vm for KvmVm {
|
||||
.unregister_ioevent(fd, addr, NoDatamatch)
|
||||
.map_err(|e| vm::HypervisorVmError::UnregisterIoEvent(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Constructs a routing entry
|
||||
///
|
||||
fn make_routing_entry(
|
||||
&self,
|
||||
gsi: u32,
|
||||
config: &InterruptSourceConfig,
|
||||
) -> kvm_irq_routing_entry {
|
||||
match &config {
|
||||
InterruptSourceConfig::MsiIrq(cfg) => {
|
||||
let mut kvm_route = kvm_irq_routing_entry {
|
||||
gsi,
|
||||
type_: KVM_IRQ_ROUTING_MSI,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
kvm_route.u.msi.address_lo = cfg.low_addr;
|
||||
kvm_route.u.msi.address_hi = cfg.high_addr;
|
||||
kvm_route.u.msi.data = cfg.data;
|
||||
|
||||
if self.check_extension(crate::kvm::Cap::MsiDevid) {
|
||||
// On AArch64, there is limitation on the range of the 'devid',
|
||||
// it can not be greater than 65536 (the max of u16).
|
||||
//
|
||||
// BDF can not be used directly, because 'segment' is in high
|
||||
// 16 bits. The layout of the u32 BDF is:
|
||||
// |---- 16 bits ----|-- 8 bits --|-- 5 bits --|-- 3 bits --|
|
||||
// | segment | bus | device | function |
|
||||
//
|
||||
// Now that we support 1 bus only in a segment, we can build a
|
||||
// 'devid' by replacing the 'bus' bits with the low 8 bits of
|
||||
// 'segment' data.
|
||||
// This way we can resolve the range checking problem and give
|
||||
// different `devid` to all the devices. Limitation is that at
|
||||
// most 256 segments can be supported.
|
||||
//
|
||||
let modified_devid = (cfg.devid & 0x00ff_0000) >> 8 | cfg.devid & 0xff;
|
||||
|
||||
kvm_route.flags = KVM_MSI_VALID_DEVID;
|
||||
kvm_route.u.msi.__bindgen_anon_1.devid = modified_devid;
|
||||
}
|
||||
kvm_route
|
||||
}
|
||||
InterruptSourceConfig::LegacyIrq(cfg) => {
|
||||
let mut kvm_route = kvm_irq_routing_entry {
|
||||
gsi,
|
||||
type_: KVM_IRQ_ROUTING_IRQCHIP,
|
||||
..Default::default()
|
||||
};
|
||||
kvm_route.u.irqchip.irqchip = cfg.irqchip;
|
||||
kvm_route.u.irqchip.pin = cfg.pin;
|
||||
|
||||
kvm_route
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Sets the GSI routing table entries, overwriting any previously set
|
||||
/// entries, as per the `KVM_SET_GSI_ROUTING` ioctl.
|
||||
@@ -874,7 +816,7 @@ pub struct KvmVcpu {
|
||||
fd: VcpuFd,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
msrs: MsrEntries,
|
||||
vm_ops: Option<Arc<dyn vm::VmOps>>,
|
||||
vmmops: Option<Arc<dyn vm::VmmOps>>,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
hyperv_synic: AtomicBool,
|
||||
}
|
||||
@@ -1114,8 +1056,8 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
Ok(run) => match run {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
VcpuExit::IoIn(addr, data) => {
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
return vm_ops
|
||||
if let Some(vmmops) = &self.vmmops {
|
||||
return vmmops
|
||||
.pio_read(addr.into(), data)
|
||||
.map(|_| cpu::VmExit::Ignore)
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()));
|
||||
@@ -1125,8 +1067,8 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
VcpuExit::IoOut(addr, data) => {
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
return vm_ops
|
||||
if let Some(vmmops) = &self.vmmops {
|
||||
return vmmops
|
||||
.pio_write(addr.into(), data)
|
||||
.map(|_| cpu::VmExit::Ignore)
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()));
|
||||
@@ -1158,8 +1100,8 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
}
|
||||
|
||||
VcpuExit::MmioRead(addr, data) => {
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
return vm_ops
|
||||
if let Some(vmmops) = &self.vmmops {
|
||||
return vmmops
|
||||
.mmio_read(addr, data)
|
||||
.map(|_| cpu::VmExit::Ignore)
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()));
|
||||
@@ -1168,8 +1110,8 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
Ok(cpu::VmExit::MmioRead(addr, data))
|
||||
}
|
||||
VcpuExit::MmioWrite(addr, data) => {
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
return vm_ops
|
||||
if let Some(vmmops) = &self.vmmops {
|
||||
return vmmops
|
||||
.mmio_write(addr, data)
|
||||
.map(|_| cpu::VmExit::Ignore)
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()));
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
use crate::arch::x86::{msr_index, SegmentRegisterOps, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
||||
use crate::kvm::{Cap, Kvm, KvmError, KvmResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
///
|
||||
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
|
||||
|
||||
@@ -37,10 +37,10 @@ pub mod kvm;
|
||||
pub mod mshv;
|
||||
|
||||
/// Hypevisor related module
|
||||
mod hypervisor;
|
||||
pub mod hypervisor;
|
||||
|
||||
/// Vm related module
|
||||
mod vm;
|
||||
pub mod vm;
|
||||
|
||||
/// CPU related module
|
||||
mod cpu;
|
||||
@@ -48,32 +48,18 @@ mod cpu;
|
||||
/// Device related module
|
||||
mod device;
|
||||
|
||||
pub use crate::hypervisor::{Hypervisor, HypervisorError};
|
||||
pub use cpu::{HypervisorCpuError, Vcpu, VmExit};
|
||||
pub use device::{Device, HypervisorDeviceError};
|
||||
pub use hypervisor::{Hypervisor, HypervisorError};
|
||||
#[cfg(all(feature = "kvm", target_arch = "aarch64"))]
|
||||
pub use kvm::aarch64;
|
||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||
pub use kvm::x86_64;
|
||||
// Aliased types exposed from both hypervisors
|
||||
#[cfg(feature = "tdx")]
|
||||
pub use kvm::TdxCapabilities;
|
||||
#[cfg(feature = "kvm")]
|
||||
pub use kvm::{
|
||||
ClockData, CpuState, CreateDevice, DeviceAttr, DeviceFd, IoEventAddress, IrqRoutingEntry,
|
||||
MemoryRegion, MpState, VcpuEvents, VcpuExit, VmState,
|
||||
};
|
||||
pub use kvm::*;
|
||||
#[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,
|
||||
};
|
||||
pub use mshv::*;
|
||||
pub use vm::{DataMatch, HypervisorVmError, Vm};
|
||||
|
||||
use std::sync::Arc;
|
||||
pub use vm::{
|
||||
DataMatch, HypervisorVmError, InterruptSourceConfig, LegacyIrqSourceConfig, MsiIrqSourceConfig,
|
||||
Vm, VmOps,
|
||||
};
|
||||
|
||||
pub fn new() -> std::result::Result<Arc<dyn Hypervisor>, HypervisorError> {
|
||||
#[cfg(feature = "kvm")]
|
||||
|
||||
@@ -11,11 +11,11 @@ use crate::cpu;
|
||||
use crate::cpu::Vcpu;
|
||||
use crate::hypervisor;
|
||||
use crate::vec_with_array_field;
|
||||
use crate::vm::{self, InterruptSourceConfig, VmOps};
|
||||
use crate::vm::{self, VmmOps};
|
||||
pub use mshv_bindings::*;
|
||||
pub use mshv_ioctls::IoEventAddress;
|
||||
use mshv_ioctls::{set_registers_64, Mshv, NoDatamatch, VcpuFd, VmFd};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use vm::DataMatch;
|
||||
@@ -122,7 +122,7 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
fd: vm_fd,
|
||||
msrs,
|
||||
hv_state: hv_state_init(),
|
||||
vm_ops: None,
|
||||
vmmops: None,
|
||||
dirty_log_slots: Arc::new(RwLock::new(HashMap::new())),
|
||||
}))
|
||||
}
|
||||
@@ -151,7 +151,7 @@ pub struct MshvVcpu {
|
||||
cpuid: CpuId,
|
||||
msrs: MsrEntries,
|
||||
hv_state: Arc<RwLock<HvState>>, // Mshv State
|
||||
vm_ops: Option<Arc<dyn vm::VmOps>>,
|
||||
vmmops: Option<Arc<dyn vm::VmmOps>>,
|
||||
}
|
||||
|
||||
/// Implementation of Vcpu trait for Microsoft Hypervisor
|
||||
@@ -355,14 +355,14 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
|
||||
if is_write {
|
||||
let data = (info.rax as u32).to_le_bytes();
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
vm_ops
|
||||
if let Some(vmmops) = &self.vmmops {
|
||||
vmmops
|
||||
.pio_write(port.into(), &data[0..len])
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
|
||||
}
|
||||
} else {
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
vm_ops
|
||||
if let Some(vmmops) = &self.vmmops {
|
||||
vmmops
|
||||
.pio_read(port.into(), &mut data[0..len])
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
|
||||
}
|
||||
@@ -655,9 +655,9 @@ impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
||||
gpa
|
||||
);
|
||||
|
||||
if let Some(vm_ops) = &self.vcpu.vm_ops {
|
||||
if vm_ops.guest_mem_read(gpa, data).is_err() {
|
||||
vm_ops
|
||||
if let Some(vmmops) = &self.vcpu.vmmops {
|
||||
if vmmops.guest_mem_read(gpa, data).is_err() {
|
||||
vmmops
|
||||
.mmio_read(gpa, data)
|
||||
.map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
|
||||
}
|
||||
@@ -675,9 +675,9 @@ impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
||||
gpa
|
||||
);
|
||||
|
||||
if let Some(vm_ops) = &self.vcpu.vm_ops {
|
||||
if vm_ops.guest_mem_write(gpa, data).is_err() {
|
||||
vm_ops
|
||||
if let Some(vmmops) = &self.vcpu.vmmops {
|
||||
if vmmops.guest_mem_write(gpa, data).is_err() {
|
||||
vmmops
|
||||
.mmio_write(gpa, data)
|
||||
.map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
|
||||
}
|
||||
@@ -746,7 +746,7 @@ pub struct MshvVm {
|
||||
msrs: MsrEntries,
|
||||
// Hypervisor State
|
||||
hv_state: Arc<RwLock<HvState>>,
|
||||
vm_ops: Option<Arc<dyn vm::VmOps>>,
|
||||
vmmops: Option<Arc<dyn vm::VmmOps>>,
|
||||
dirty_log_slots: Arc<RwLock<HashMap<u64, MshvDirtyLogSlot>>>,
|
||||
}
|
||||
|
||||
@@ -816,7 +816,7 @@ impl vm::Vm for MshvVm {
|
||||
fn create_vcpu(
|
||||
&self,
|
||||
id: u8,
|
||||
vm_ops: Option<Arc<dyn VmOps>>,
|
||||
vmmops: Option<Arc<dyn VmmOps>>,
|
||||
) -> vm::Result<Arc<dyn cpu::Vcpu>> {
|
||||
let vcpu_fd = self
|
||||
.fd
|
||||
@@ -828,7 +828,7 @@ impl vm::Vm for MshvVm {
|
||||
cpuid: CpuId::new(1).unwrap(),
|
||||
msrs: self.msrs.clone(),
|
||||
hv_state: self.hv_state.clone(),
|
||||
vm_ops,
|
||||
vmmops,
|
||||
};
|
||||
Ok(Arc::new(vcpu))
|
||||
}
|
||||
@@ -957,27 +957,6 @@ impl vm::Vm for MshvVm {
|
||||
.map_err(|e| vm::HypervisorVmError::CreatePassthroughDevice(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Constructs a routing entry
|
||||
///
|
||||
fn make_routing_entry(
|
||||
&self,
|
||||
gsi: u32,
|
||||
config: &InterruptSourceConfig,
|
||||
) -> mshv_msi_routing_entry {
|
||||
match config {
|
||||
InterruptSourceConfig::MsiIrq(cfg) => mshv_msi_routing_entry {
|
||||
gsi,
|
||||
address_lo: cfg.low_addr,
|
||||
address_hi: cfg.high_addr,
|
||||
data: cfg.data,
|
||||
},
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> vm::Result<()> {
|
||||
let mut msi_routing =
|
||||
vec_with_array_field::<mshv_msi_routing, mshv_msi_routing_entry>(entries.len());
|
||||
@@ -1048,3 +1027,6 @@ impl vm::Vm for MshvVm {
|
||||
.map_err(|e| vm::HypervisorVmError::GetDirtyLog(e.into()))
|
||||
}
|
||||
}
|
||||
pub use hv_cpuid_entry as CpuIdEntry;
|
||||
|
||||
pub const CPUID_FLAG_VALID_INDEX: u32 = 0;
|
||||
|
||||
@@ -8,14 +8,13 @@
|
||||
//
|
||||
//
|
||||
use crate::arch::x86::{msr_index, SegmentRegisterOps, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
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,
|
||||
@@ -26,8 +25,6 @@ pub use {
|
||||
mshv_bindings::Xcrs as ExtendedControlRegisters,
|
||||
};
|
||||
|
||||
pub const CPUID_FLAG_VALID_INDEX: u32 = 0;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct VcpuMshvState {
|
||||
pub msrs: MsrEntries,
|
||||
|
||||
@@ -12,15 +12,15 @@
|
||||
use crate::aarch64::VcpuInit;
|
||||
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::ClockData;
|
||||
use crate::CreateDevice;
|
||||
#[cfg(feature = "mshv")]
|
||||
use crate::HvState as VmState;
|
||||
#[cfg(feature = "kvm")]
|
||||
use crate::KvmVmState as VmState;
|
||||
use crate::{IoEventAddress, IrqRoutingEntry, MemoryRegion};
|
||||
#[cfg(feature = "kvm")]
|
||||
use kvm_ioctls::Cap;
|
||||
@@ -216,39 +216,6 @@ pub enum HypervisorVmError {
|
||||
///
|
||||
pub type Result<T> = std::result::Result<T, HypervisorVmError>;
|
||||
|
||||
/// Configuration data for legacy interrupts.
|
||||
///
|
||||
/// On x86 platforms, legacy interrupts means those interrupts routed through PICs or IOAPICs.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct LegacyIrqSourceConfig {
|
||||
pub irqchip: u32,
|
||||
pub pin: u32,
|
||||
}
|
||||
|
||||
/// Configuration data for MSI/MSI-X interrupts.
|
||||
///
|
||||
/// On x86 platforms, these interrupts are vectors delivered directly to the LAPIC.
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct MsiIrqSourceConfig {
|
||||
/// High address to delivery message signaled interrupt.
|
||||
pub high_addr: u32,
|
||||
/// Low address to delivery message signaled interrupt.
|
||||
pub low_addr: u32,
|
||||
/// Data to write to delivery message signaled interrupt.
|
||||
pub data: u32,
|
||||
/// Unique ID of the device to delivery message signaled interrupt.
|
||||
pub devid: u32,
|
||||
}
|
||||
|
||||
/// Configuration data for an interrupt source.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum InterruptSourceConfig {
|
||||
/// Configuration data for Legacy interrupts.
|
||||
LegacyIrq(LegacyIrqSourceConfig),
|
||||
/// Configuration data for PciMsi, PciMsix and generic MSI interrupts.
|
||||
MsiIrq(MsiIrqSourceConfig),
|
||||
}
|
||||
|
||||
///
|
||||
/// Trait to represent a Vm
|
||||
///
|
||||
@@ -268,7 +235,7 @@ pub trait Vm: Send + Sync {
|
||||
/// Unregister an event that will, when signaled, trigger the `gsi` IRQ.
|
||||
fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>;
|
||||
/// Creates a new KVM vCPU file descriptor and maps the memory corresponding
|
||||
fn create_vcpu(&self, id: u8, vm_ops: Option<Arc<dyn VmOps>>) -> Result<Arc<dyn Vcpu>>;
|
||||
fn create_vcpu(&self, id: u8, vmmops: Option<Arc<dyn VmmOps>>) -> Result<Arc<dyn Vcpu>>;
|
||||
/// Registers an event to be signaled whenever a certain address is written to.
|
||||
fn register_ioevent(
|
||||
&self,
|
||||
@@ -278,8 +245,6 @@ pub trait Vm: Send + Sync {
|
||||
) -> Result<()>;
|
||||
/// Unregister an event from a certain address it has been previously registered to.
|
||||
fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> Result<()>;
|
||||
// Construct a routing entry
|
||||
fn make_routing_entry(&self, gsi: u32, config: &InterruptSourceConfig) -> IrqRoutingEntry;
|
||||
/// Sets the GSI routing table entries, overwriting any previously set
|
||||
fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> Result<()>;
|
||||
/// Creates a memory region structure that can be used with {create/remove}_user_memory_region
|
||||
@@ -344,7 +309,7 @@ pub trait Vm: Send + Sync {
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
pub trait VmOps: Send + Sync {
|
||||
pub trait VmmOps: Send + Sync {
|
||||
fn guest_mem_write(&self, gpa: u64, buf: &[u8]) -> Result<usize>;
|
||||
fn guest_mem_read(&self, gpa: u64, buf: &mut [u8]) -> Result<usize>;
|
||||
fn mmio_read(&self, gpa: u64, data: &mut [u8]) -> Result<()>;
|
||||
|
||||
@@ -7,20 +7,20 @@ edition = "2021"
|
||||
[dependencies]
|
||||
epoll = "4.3.1"
|
||||
getrandom = "0.2"
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.123"
|
||||
log = "0.4.16"
|
||||
net_gen = { path = "../net_gen" }
|
||||
rate_limiter = { path = "../rate_limiter" }
|
||||
serde = "1.0.137"
|
||||
serde = "1.0.136"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
virtio-bindings = "0.1.0"
|
||||
virtio-queue = "0.3.0"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
virtio-queue = "0.2.0"
|
||||
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
[dev-dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
pnet = "0.30.0"
|
||||
serde_json = "1.0.81"
|
||||
pnet = "0.29.0"
|
||||
serde_json = "1.0.79"
|
||||
|
||||
@@ -77,7 +77,7 @@ impl OptionParser {
|
||||
}
|
||||
|
||||
for option in split_commas_outside_brackets(input)?.iter() {
|
||||
let parts: Vec<&str> = option.splitn(2, '=').collect();
|
||||
let parts: Vec<&str> = option.split('=').collect();
|
||||
|
||||
match self.options.get_mut(parts[0]) {
|
||||
None => return Err(OptionParserError::UnknownOption(parts[0].to_owned())),
|
||||
|
||||
@@ -10,21 +10,22 @@ kvm = ["vfio-ioctls/kvm"]
|
||||
mshv = ["vfio-ioctls/mshv"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.56"
|
||||
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"
|
||||
log = "0.4.17"
|
||||
serde = { version="1.0.137", features=["derive"] }
|
||||
thiserror = "1.0.31"
|
||||
libc = "0.2.123"
|
||||
log = "0.4.16"
|
||||
serde = "1.0.136"
|
||||
serde_derive = "1.0.136"
|
||||
thiserror = "1.0.30"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = "0.8.0"
|
||||
vm-memory = "0.7.0"
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
|
||||
[dependencies.vfio-bindings]
|
||||
|
||||
@@ -6,13 +6,13 @@ use crate::configuration::{
|
||||
PciBarRegionType, PciBridgeSubclass, PciClassCode, PciConfiguration, PciHeaderType,
|
||||
};
|
||||
use crate::device::{DeviceRelocation, Error as PciDeviceError, PciDevice};
|
||||
use crate::PciBarConfiguration;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::DerefMut;
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use vm_device::{Bus, BusDevice};
|
||||
use vm_memory::{Address, GuestAddress, GuestUsize};
|
||||
|
||||
const VENDOR_ID_INTEL: u16 = 0x8086;
|
||||
const DEVICE_ID_INTEL_VIRT_PCIE_HOST: u16 = 0x0d57;
|
||||
@@ -88,10 +88,6 @@ impl PciDevice for PciRoot {
|
||||
fn as_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PciBus {
|
||||
@@ -122,21 +118,21 @@ impl PciBus {
|
||||
dev: Arc<Mutex<dyn BusDevice>>,
|
||||
#[cfg(target_arch = "x86_64")] io_bus: &Bus,
|
||||
mmio_bus: &Bus,
|
||||
bars: Vec<PciBarConfiguration>,
|
||||
bars: Vec<(GuestAddress, GuestUsize, PciBarRegionType)>,
|
||||
) -> Result<()> {
|
||||
for bar in bars {
|
||||
match bar.region_type() {
|
||||
for (address, size, type_) in bars {
|
||||
match type_ {
|
||||
PciBarRegionType::IoRegion => {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
io_bus
|
||||
.insert(dev.clone(), bar.addr(), bar.size())
|
||||
.insert(dev.clone(), address.raw_value(), size)
|
||||
.map_err(PciRootError::PioInsert)?;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
error!("I/O region is not supported");
|
||||
}
|
||||
PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => {
|
||||
mmio_bus
|
||||
.insert(dev.clone(), bar.addr(), bar.size())
|
||||
.insert(dev.clone(), address.raw_value(), size)
|
||||
.map_err(PciRootError::MmioInsert)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ use std::fmt::{self, Display};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use versionize::{VersionMap, Versionize, VersionizeError, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use vm_device::PciBarType;
|
||||
use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable, VersionMapped};
|
||||
|
||||
// The number of 32bit registers in the config space, 4096 bytes.
|
||||
@@ -19,7 +18,6 @@ const STATUS_REG: usize = 1;
|
||||
const STATUS_REG_CAPABILITIES_USED_MASK: u32 = 0x0010_0000;
|
||||
const BAR0_REG: usize = 4;
|
||||
const ROM_BAR_REG: usize = 12;
|
||||
const ROM_BAR_IDX: usize = 6;
|
||||
const BAR_IO_ADDR_MASK: u32 = 0xffff_fffc;
|
||||
const BAR_MEM_ADDR_MASK: u32 = 0xffff_fff0;
|
||||
const ROM_BAR_ADDR_MASK: u32 = 0xffff_f800;
|
||||
@@ -328,48 +326,17 @@ pub enum PciBarRegionType {
|
||||
Memory64BitRegion = 0x04,
|
||||
}
|
||||
|
||||
impl From<PciBarType> for PciBarRegionType {
|
||||
fn from(type_: PciBarType) -> Self {
|
||||
match type_ {
|
||||
PciBarType::Io => PciBarRegionType::IoRegion,
|
||||
PciBarType::Mmio32 => PciBarRegionType::Memory32BitRegion,
|
||||
PciBarType::Mmio64 => PciBarRegionType::Memory64BitRegion,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::from_over_into)]
|
||||
impl Into<PciBarType> for PciBarRegionType {
|
||||
fn into(self) -> PciBarType {
|
||||
match self {
|
||||
PciBarRegionType::IoRegion => PciBarType::Io,
|
||||
PciBarRegionType::Memory32BitRegion => PciBarType::Mmio32,
|
||||
PciBarRegionType::Memory64BitRegion => PciBarType::Mmio64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum PciBarPrefetchable {
|
||||
NotPrefetchable = 0,
|
||||
Prefetchable = 0x08,
|
||||
}
|
||||
|
||||
#[allow(clippy::from_over_into)]
|
||||
impl Into<bool> for PciBarPrefetchable {
|
||||
fn into(self) -> bool {
|
||||
match self {
|
||||
PciBarPrefetchable::NotPrefetchable => false,
|
||||
PciBarPrefetchable::Prefetchable => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PciBarConfiguration {
|
||||
addr: u64,
|
||||
size: u64,
|
||||
idx: usize,
|
||||
reg_idx: usize,
|
||||
region_type: PciBarRegionType,
|
||||
prefetchable: PciBarPrefetchable,
|
||||
}
|
||||
@@ -589,23 +556,22 @@ impl PciConfiguration {
|
||||
|
||||
/// Adds a region specified by `config`. Configures the specified BAR(s) to
|
||||
/// report this region and size to the guest kernel. Enforces a few constraints
|
||||
/// (i.e, region size must be power of two, register not already used).
|
||||
pub fn add_pci_bar(&mut self, config: &PciBarConfiguration) -> Result<()> {
|
||||
let bar_idx = config.idx;
|
||||
let reg_idx = BAR0_REG + bar_idx;
|
||||
|
||||
if self.bars[bar_idx].used {
|
||||
return Err(Error::BarInUse(bar_idx));
|
||||
/// (i.e, region size must be power of two, register not already used). Returns 'None' on
|
||||
/// failure all, `Some(BarIndex)` on success.
|
||||
pub fn add_pci_bar(&mut self, config: &PciBarConfiguration) -> Result<usize> {
|
||||
if self.bars[config.reg_idx].used {
|
||||
return Err(Error::BarInUse(config.reg_idx));
|
||||
}
|
||||
|
||||
if config.size.count_ones() != 1 {
|
||||
return Err(Error::BarSizeInvalid(config.size));
|
||||
}
|
||||
|
||||
if bar_idx >= NUM_BAR_REGS {
|
||||
return Err(Error::BarInvalid(bar_idx));
|
||||
if config.reg_idx >= NUM_BAR_REGS {
|
||||
return Err(Error::BarInvalid(config.reg_idx));
|
||||
}
|
||||
|
||||
let bar_idx = BAR0_REG + config.reg_idx;
|
||||
let end_addr = config
|
||||
.addr
|
||||
.checked_add(config.size - 1)
|
||||
@@ -618,20 +584,20 @@ impl PciConfiguration {
|
||||
|
||||
// Encode the BAR size as expected by the software running in
|
||||
// the guest.
|
||||
self.bars[bar_idx].size =
|
||||
self.bars[config.reg_idx].size =
|
||||
encode_32_bits_bar_size(config.size as u32).ok_or(Error::Encode32BarSize)?;
|
||||
}
|
||||
PciBarRegionType::Memory64BitRegion => {
|
||||
if bar_idx + 1 >= NUM_BAR_REGS {
|
||||
return Err(Error::BarInvalid64(bar_idx));
|
||||
if config.reg_idx + 1 >= NUM_BAR_REGS {
|
||||
return Err(Error::BarInvalid64(config.reg_idx));
|
||||
}
|
||||
|
||||
if end_addr > u64::max_value() {
|
||||
return Err(Error::BarAddressInvalid(config.addr, config.size));
|
||||
}
|
||||
|
||||
if self.bars[bar_idx + 1].used {
|
||||
return Err(Error::BarInUse64(bar_idx));
|
||||
if self.bars[config.reg_idx + 1].used {
|
||||
return Err(Error::BarInUse64(config.reg_idx));
|
||||
}
|
||||
|
||||
// Encode the BAR size as expected by the software running in
|
||||
@@ -639,12 +605,12 @@ impl PciConfiguration {
|
||||
let (bar_size_hi, bar_size_lo) =
|
||||
encode_64_bits_bar_size(config.size).ok_or(Error::Encode64BarSize)?;
|
||||
|
||||
self.registers[reg_idx + 1] = (config.addr >> 32) as u32;
|
||||
self.writable_bits[reg_idx + 1] = 0xffff_ffff;
|
||||
self.bars[bar_idx + 1].addr = self.registers[reg_idx + 1];
|
||||
self.bars[bar_idx].size = bar_size_lo;
|
||||
self.bars[bar_idx + 1].size = bar_size_hi;
|
||||
self.bars[bar_idx + 1].used = true;
|
||||
self.registers[bar_idx + 1] = (config.addr >> 32) as u32;
|
||||
self.writable_bits[bar_idx + 1] = 0xffff_ffff;
|
||||
self.bars[config.reg_idx + 1].addr = self.registers[bar_idx + 1];
|
||||
self.bars[config.reg_idx].size = bar_size_lo;
|
||||
self.bars[config.reg_idx + 1].size = bar_size_hi;
|
||||
self.bars[config.reg_idx + 1].used = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,30 +622,26 @@ impl PciConfiguration {
|
||||
PciBarRegionType::IoRegion => (BAR_IO_ADDR_MASK, config.region_type as u32),
|
||||
};
|
||||
|
||||
self.registers[reg_idx] = ((config.addr as u32) & mask) | lower_bits;
|
||||
self.writable_bits[reg_idx] = mask;
|
||||
self.bars[bar_idx].addr = self.registers[reg_idx];
|
||||
self.bars[bar_idx].used = true;
|
||||
self.bars[bar_idx].r#type = Some(config.region_type);
|
||||
|
||||
Ok(())
|
||||
self.registers[bar_idx] = ((config.addr as u32) & mask) | lower_bits;
|
||||
self.writable_bits[bar_idx] = mask;
|
||||
self.bars[config.reg_idx].addr = self.registers[bar_idx];
|
||||
self.bars[config.reg_idx].used = true;
|
||||
self.bars[config.reg_idx].r#type = Some(config.region_type);
|
||||
Ok(config.reg_idx)
|
||||
}
|
||||
|
||||
/// Adds rom expansion BAR.
|
||||
pub fn add_pci_rom_bar(&mut self, config: &PciBarConfiguration, active: u32) -> Result<()> {
|
||||
let bar_idx = config.idx;
|
||||
let reg_idx = ROM_BAR_REG;
|
||||
|
||||
pub fn add_pci_rom_bar(&mut self, config: &PciBarConfiguration, active: u32) -> Result<usize> {
|
||||
if self.rom_bar_used {
|
||||
return Err(Error::RomBarInUse(bar_idx));
|
||||
return Err(Error::RomBarInUse(config.reg_idx));
|
||||
}
|
||||
|
||||
if config.size.count_ones() != 1 {
|
||||
return Err(Error::RomBarSizeInvalid(config.size));
|
||||
}
|
||||
|
||||
if bar_idx != ROM_BAR_IDX {
|
||||
return Err(Error::RomBarInvalid(bar_idx));
|
||||
if config.reg_idx != ROM_BAR_REG {
|
||||
return Err(Error::RomBarInvalid(config.reg_idx));
|
||||
}
|
||||
|
||||
let end_addr = config
|
||||
@@ -691,14 +653,13 @@ impl PciConfiguration {
|
||||
return Err(Error::RomBarAddressInvalid(config.addr, config.size));
|
||||
}
|
||||
|
||||
self.registers[reg_idx] = (config.addr as u32) | active;
|
||||
self.writable_bits[reg_idx] = ROM_BAR_ADDR_MASK;
|
||||
self.rom_bar_addr = self.registers[reg_idx];
|
||||
self.registers[config.reg_idx] = (config.addr as u32) | active;
|
||||
self.writable_bits[config.reg_idx] = ROM_BAR_ADDR_MASK;
|
||||
self.rom_bar_addr = self.registers[config.reg_idx];
|
||||
self.rom_bar_size =
|
||||
encode_32_bits_bar_size(config.size as u32).ok_or(Error::Encode32BarSize)?;
|
||||
self.rom_bar_used = true;
|
||||
|
||||
Ok(())
|
||||
Ok(config.reg_idx)
|
||||
}
|
||||
|
||||
/// Returns the address of the given BAR region.
|
||||
@@ -950,7 +911,7 @@ impl Snapshottable for PciConfiguration {
|
||||
impl Default for PciBarConfiguration {
|
||||
fn default() -> Self {
|
||||
PciBarConfiguration {
|
||||
idx: 0,
|
||||
reg_idx: 0,
|
||||
addr: 0,
|
||||
size: 0,
|
||||
region_type: PciBarRegionType::Memory64BitRegion,
|
||||
@@ -961,13 +922,13 @@ impl Default for PciBarConfiguration {
|
||||
|
||||
impl PciBarConfiguration {
|
||||
pub fn new(
|
||||
idx: usize,
|
||||
reg_idx: usize,
|
||||
size: u64,
|
||||
region_type: PciBarRegionType,
|
||||
prefetchable: PciBarPrefetchable,
|
||||
) -> Self {
|
||||
PciBarConfiguration {
|
||||
idx,
|
||||
reg_idx,
|
||||
addr: 0,
|
||||
size,
|
||||
region_type,
|
||||
@@ -976,8 +937,8 @@ impl PciBarConfiguration {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn set_index(mut self, idx: usize) -> Self {
|
||||
self.idx = idx;
|
||||
pub fn set_register_index(mut self, reg_idx: usize) -> Self {
|
||||
self.reg_idx = reg_idx;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -993,31 +954,15 @@ impl PciBarConfiguration {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> u64 {
|
||||
self.size
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn set_region_type(mut self, region_type: PciBarRegionType) -> Self {
|
||||
self.region_type = region_type;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn idx(&self) -> usize {
|
||||
self.idx
|
||||
}
|
||||
|
||||
pub fn addr(&self) -> u64 {
|
||||
self.addr
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u64 {
|
||||
self.size
|
||||
}
|
||||
|
||||
pub fn region_type(&self) -> PciBarRegionType {
|
||||
self.region_type
|
||||
}
|
||||
|
||||
pub fn prefetchable(&self) -> PciBarPrefetchable {
|
||||
self.prefetchable
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
// found in the LICENSE-BSD-3-Clause file.
|
||||
|
||||
use crate::configuration::{self, PciBarRegionType};
|
||||
use crate::PciBarConfiguration;
|
||||
use std::any::Any;
|
||||
use std::fmt::{self, Display};
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use std::{self, io, result};
|
||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||
use vm_device::{BusDevice, Resource};
|
||||
use vm_device::BusDevice;
|
||||
use vm_memory::{GuestAddress, GuestUsize};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
@@ -19,8 +19,6 @@ pub enum Error {
|
||||
IoAllocationFailed(u64),
|
||||
/// Registering an IO BAR failed.
|
||||
IoRegistrationFailed(u64, configuration::Error),
|
||||
/// Expected resource not found.
|
||||
MissingResource,
|
||||
}
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
@@ -36,7 +34,6 @@ impl Display for Error {
|
||||
IoRegistrationFailed(addr, e) => {
|
||||
write!(f, "failed to register an IO BAR, addr={} err={}", addr, e)
|
||||
}
|
||||
MissingResource => write!(f, "failed to find expected resource"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,8 +53,7 @@ pub trait PciDevice: BusDevice {
|
||||
&mut self,
|
||||
_allocator: &Arc<Mutex<SystemAllocator>>,
|
||||
_mmio_allocator: &mut AddressAllocator,
|
||||
_resources: Option<Vec<Resource>>,
|
||||
) -> Result<Vec<PciBarConfiguration>> {
|
||||
) -> Result<Vec<(GuestAddress, GuestUsize, PciBarRegionType)>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
@@ -107,9 +103,6 @@ pub trait PciDevice: BusDevice {
|
||||
/// Provides a mutable reference to the Any trait. This is useful to let
|
||||
/// the caller have access to the underlying type behind the trait.
|
||||
fn as_any(&mut self) -> &mut dyn Any;
|
||||
|
||||
/// Optionally returns a unique identifier.
|
||||
fn id(&self) -> Option<String>;
|
||||
}
|
||||
|
||||
/// This trait defines a set of functions which can be triggered whenever a
|
||||
|
||||
@@ -3,17 +3,11 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
|
||||
//
|
||||
|
||||
use anyhow::anyhow;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptSourceConfig, InterruptSourceGroup, MsiIrqSourceConfig,
|
||||
};
|
||||
use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable, VersionMapped};
|
||||
|
||||
// MSI control masks
|
||||
const MSI_CTL_ENABLE: u16 = 0x1;
|
||||
@@ -38,15 +32,7 @@ pub fn msi_num_enabled_vectors(msg_ctl: u16) -> usize {
|
||||
1 << field
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum Error {
|
||||
#[error("Failed enabling the interrupt route: {0}")]
|
||||
EnableInterruptRoute(io::Error),
|
||||
#[error("Failed updating the interrupt route: {0}")]
|
||||
UpdateInterruptRoute(io::Error),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, Versionize)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct MsiCap {
|
||||
// Message Control Register
|
||||
// 0: MSI enable.
|
||||
@@ -171,13 +157,6 @@ impl MsiCap {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Versionize)]
|
||||
struct MsiConfigState {
|
||||
cap: MsiCap,
|
||||
}
|
||||
|
||||
impl VersionMapped for MsiConfigState {}
|
||||
|
||||
pub struct MsiConfig {
|
||||
cap: MsiCap,
|
||||
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
|
||||
@@ -196,39 +175,6 @@ impl MsiConfig {
|
||||
}
|
||||
}
|
||||
|
||||
fn state(&self) -> MsiConfigState {
|
||||
MsiConfigState { cap: self.cap }
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &MsiConfigState) -> Result<(), Error> {
|
||||
self.cap = state.cap;
|
||||
|
||||
if self.enabled() {
|
||||
for idx in 0..self.num_enabled_vectors() {
|
||||
let config = MsiIrqSourceConfig {
|
||||
high_addr: self.cap.msg_addr_hi,
|
||||
low_addr: self.cap.msg_addr_lo,
|
||||
data: self.cap.msg_data as u32,
|
||||
devid: 0,
|
||||
};
|
||||
|
||||
self.interrupt_source_group
|
||||
.update(
|
||||
idx as InterruptIndex,
|
||||
InterruptSourceConfig::MsiIrq(config),
|
||||
self.cap.vector_masked(idx),
|
||||
)
|
||||
.map_err(Error::UpdateInterruptRoute)?;
|
||||
}
|
||||
|
||||
self.interrupt_source_group
|
||||
.enable()
|
||||
.map_err(Error::EnableInterruptRoute)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn enabled(&self) -> bool {
|
||||
self.cap.enabled()
|
||||
}
|
||||
@@ -276,26 +222,3 @@ impl MsiConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Pausable for MsiConfig {}
|
||||
|
||||
impl Snapshottable for MsiConfig {
|
||||
fn id(&self) -> String {
|
||||
String::from("msi_config")
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
Snapshot::new_from_versioned_state(&self.id(), &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id())?)
|
||||
.map_err(|e| {
|
||||
MigratableError::Restore(anyhow!(
|
||||
"Could not restore state for {}: {:?}",
|
||||
self.id(),
|
||||
e
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
684
pci/src/vfio.rs
684
pci/src/vfio.rs
File diff suppressed because it is too large
Load Diff
@@ -3,8 +3,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use crate::vfio::{Interrupt, UserMemoryRegion, Vfio, VfioCommon, VfioError};
|
||||
use crate::{BarReprogrammingParams, PciBarConfiguration, VfioPciError};
|
||||
use crate::vfio::{Interrupt, Vfio, VfioCommon, VfioError};
|
||||
use crate::{BarReprogrammingParams, PciBarRegionType, VfioPciError};
|
||||
use crate::{
|
||||
PciBdf, PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciSubclass,
|
||||
};
|
||||
@@ -21,17 +21,18 @@ use vfio_user::{Client, Error as VfioUserError};
|
||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||
use vm_device::dma_mapping::ExternalDmaMapping;
|
||||
use vm_device::interrupt::{InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig};
|
||||
use vm_device::{BusDevice, Resource};
|
||||
use vm_device::BusDevice;
|
||||
use vm_memory::bitmap::AtomicBitmap;
|
||||
use vm_memory::{
|
||||
Address, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryRegion, GuestRegionMmap,
|
||||
GuestUsize,
|
||||
};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
pub struct VfioUserPciDevice {
|
||||
id: String,
|
||||
vm: Arc<dyn hypervisor::Vm>,
|
||||
client: Arc<Mutex<Client>>,
|
||||
vfio_wrapper: VfioUserClientWrapper,
|
||||
common: VfioCommon,
|
||||
}
|
||||
|
||||
@@ -62,10 +63,9 @@ impl PciSubclass for PciVfioUserSubclass {
|
||||
|
||||
impl VfioUserPciDevice {
|
||||
pub fn new(
|
||||
id: String,
|
||||
vm: &Arc<dyn hypervisor::Vm>,
|
||||
client: Arc<Mutex<Client>>,
|
||||
msi_interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
msi_interrupt_manager: &Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
legacy_interrupt_group: Option<Arc<dyn InterruptSourceGroup>>,
|
||||
bdf: PciBdf,
|
||||
) -> Result<Self, VfioUserPciDeviceError> {
|
||||
@@ -103,20 +103,17 @@ impl VfioUserPciDevice {
|
||||
msi: None,
|
||||
msix: None,
|
||||
},
|
||||
msi_interrupt_manager,
|
||||
legacy_interrupt_group,
|
||||
vfio_wrapper: Arc::new(vfio_wrapper) as Arc<dyn Vfio>,
|
||||
};
|
||||
|
||||
common.parse_capabilities(bdf);
|
||||
common.parse_capabilities(msi_interrupt_manager, &vfio_wrapper, bdf);
|
||||
common
|
||||
.initialize_legacy_interrupt()
|
||||
.initialize_legacy_interrupt(legacy_interrupt_group, &vfio_wrapper)
|
||||
.map_err(VfioUserPciDeviceError::InitializeLegacyInterrupts)?;
|
||||
|
||||
Ok(Self {
|
||||
id,
|
||||
vm: vm.clone(),
|
||||
client,
|
||||
vfio_wrapper,
|
||||
common,
|
||||
})
|
||||
}
|
||||
@@ -146,15 +143,6 @@ impl VfioUserPciDevice {
|
||||
.file_offset
|
||||
.clone();
|
||||
|
||||
let sparse_areas = self
|
||||
.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.region(mmio_region.index)
|
||||
.unwrap()
|
||||
.sparse_areas
|
||||
.clone();
|
||||
|
||||
if region_flags & VFIO_REGION_INFO_FLAG_MMAP != 0 {
|
||||
let mut prot = 0;
|
||||
if region_flags & VFIO_REGION_INFO_FLAG_READ != 0 {
|
||||
@@ -164,57 +152,41 @@ impl VfioUserPciDevice {
|
||||
prot |= libc::PROT_WRITE;
|
||||
}
|
||||
|
||||
let mmaps = if sparse_areas.is_empty() {
|
||||
vec![vfio_region_sparse_mmap_area {
|
||||
offset: 0,
|
||||
size: mmio_region.length,
|
||||
}]
|
||||
} else {
|
||||
sparse_areas
|
||||
let host_addr = unsafe {
|
||||
libc::mmap(
|
||||
null_mut(),
|
||||
mmio_region.length as usize,
|
||||
prot,
|
||||
libc::MAP_SHARED,
|
||||
file_offset.as_ref().unwrap().file().as_raw_fd(),
|
||||
file_offset.as_ref().unwrap().start() as libc::off_t,
|
||||
)
|
||||
};
|
||||
|
||||
for s in mmaps.iter() {
|
||||
let host_addr = unsafe {
|
||||
libc::mmap(
|
||||
null_mut(),
|
||||
s.size as usize,
|
||||
prot,
|
||||
libc::MAP_SHARED,
|
||||
file_offset.as_ref().unwrap().file().as_raw_fd(),
|
||||
file_offset.as_ref().unwrap().start() as libc::off_t
|
||||
+ s.offset as libc::off_t,
|
||||
)
|
||||
};
|
||||
|
||||
if host_addr == libc::MAP_FAILED {
|
||||
error!(
|
||||
"Could not mmap regions, error:{}",
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let user_memory_region = UserMemoryRegion {
|
||||
slot: mem_slot(),
|
||||
start: mmio_region.start.0 + s.offset,
|
||||
size: s.size,
|
||||
host_addr: host_addr as u64,
|
||||
};
|
||||
|
||||
mmio_region.user_memory_regions.push(user_memory_region);
|
||||
|
||||
let mem_region = vm.make_user_memory_region(
|
||||
user_memory_region.slot,
|
||||
user_memory_region.start,
|
||||
user_memory_region.size,
|
||||
user_memory_region.host_addr,
|
||||
false,
|
||||
false,
|
||||
if host_addr == libc::MAP_FAILED {
|
||||
error!(
|
||||
"Could not mmap regions, error:{}",
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
|
||||
vm.create_user_memory_region(mem_region)
|
||||
.map_err(VfioUserPciDeviceError::MapRegionGuest)?;
|
||||
continue;
|
||||
}
|
||||
|
||||
let slot = mem_slot();
|
||||
let mem_region = vm.make_user_memory_region(
|
||||
slot,
|
||||
mmio_region.start.0,
|
||||
mmio_region.length as u64,
|
||||
host_addr as u64,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
vm.create_user_memory_region(mem_region)
|
||||
.map_err(VfioUserPciDeviceError::MapRegionGuest)?;
|
||||
|
||||
mmio_region.mem_slot = Some(slot);
|
||||
mmio_region.host_addr = Some(host_addr as u64);
|
||||
mmio_region.mmap_size = Some(mmio_region.length as usize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,13 +195,17 @@ impl VfioUserPciDevice {
|
||||
|
||||
pub fn unmap_mmio_regions(&mut self) {
|
||||
for mmio_region in self.common.mmio_regions.iter() {
|
||||
for user_memory_region in mmio_region.user_memory_regions.iter() {
|
||||
if let (Some(host_addr), Some(mmap_size), Some(mem_slot)) = (
|
||||
mmio_region.host_addr,
|
||||
mmio_region.mmap_size,
|
||||
mmio_region.mem_slot,
|
||||
) {
|
||||
// Remove region
|
||||
let r = self.vm.make_user_memory_region(
|
||||
user_memory_region.slot,
|
||||
user_memory_region.start,
|
||||
user_memory_region.size,
|
||||
user_memory_region.host_addr,
|
||||
mem_slot,
|
||||
mmio_region.start.raw_value(),
|
||||
mmap_size as u64,
|
||||
host_addr as u64,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
@@ -238,13 +214,7 @@ impl VfioUserPciDevice {
|
||||
error!("Could not remove the userspace memory region: {}", e);
|
||||
}
|
||||
|
||||
// Remove mmaps
|
||||
let ret = unsafe {
|
||||
libc::munmap(
|
||||
user_memory_region.host_addr as *mut libc::c_void,
|
||||
user_memory_region.size as usize,
|
||||
)
|
||||
};
|
||||
let ret = unsafe { libc::munmap(host_addr as *mut libc::c_void, mmap_size) };
|
||||
if ret != 0 {
|
||||
error!(
|
||||
"Could not unmap region {}, error:{}",
|
||||
@@ -422,10 +392,9 @@ impl PciDevice for VfioUserPciDevice {
|
||||
&mut self,
|
||||
allocator: &Arc<Mutex<SystemAllocator>>,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
resources: Option<Vec<Resource>>,
|
||||
) -> Result<Vec<PciBarConfiguration>, PciDeviceError> {
|
||||
) -> Result<Vec<(GuestAddress, GuestUsize, PciBarRegionType)>, PciDeviceError> {
|
||||
self.common
|
||||
.allocate_bars(allocator, mmio_allocator, resources)
|
||||
.allocate_bars(allocator, mmio_allocator, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn free_bars(
|
||||
@@ -456,19 +425,22 @@ impl PciDevice for VfioUserPciDevice {
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
) -> Option<Arc<Barrier>> {
|
||||
self.common.write_config_register(reg_idx, offset, data)
|
||||
self.common
|
||||
.write_config_register(reg_idx, offset, data, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
|
||||
self.common.read_config_register(reg_idx)
|
||||
self.common
|
||||
.read_config_register(reg_idx, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn read_bar(&mut self, base: u64, offset: u64, data: &mut [u8]) {
|
||||
self.common.read_bar(base, offset, data)
|
||||
self.common.read_bar(base, offset, data, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn write_bar(&mut self, base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
self.common.write_bar(base, offset, data)
|
||||
self.common
|
||||
.write_bar(base, offset, data, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn move_bar(&mut self, old_base: u64, new_base: u64) -> Result<(), std::io::Error> {
|
||||
@@ -477,41 +449,35 @@ impl PciDevice for VfioUserPciDevice {
|
||||
if mmio_region.start.raw_value() == old_base {
|
||||
mmio_region.start = GuestAddress(new_base);
|
||||
|
||||
for user_memory_region in mmio_region.user_memory_regions.iter_mut() {
|
||||
// Remove old region
|
||||
let old_region = self.vm.make_user_memory_region(
|
||||
user_memory_region.slot,
|
||||
user_memory_region.start,
|
||||
user_memory_region.size,
|
||||
user_memory_region.host_addr,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
if let Some(mem_slot) = mmio_region.mem_slot {
|
||||
if let Some(host_addr) = mmio_region.host_addr {
|
||||
// Remove original region
|
||||
let old_region = self.vm.make_user_memory_region(
|
||||
mem_slot,
|
||||
old_base,
|
||||
mmio_region.length as u64,
|
||||
host_addr as u64,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
self.vm
|
||||
.remove_user_memory_region(old_region)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
self.vm
|
||||
.remove_user_memory_region(old_region)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
|
||||
// Update the user memory region with the correct start address.
|
||||
if new_base > old_base {
|
||||
user_memory_region.start += new_base - old_base;
|
||||
} else {
|
||||
user_memory_region.start -= old_base - new_base;
|
||||
let new_region = self.vm.make_user_memory_region(
|
||||
mem_slot,
|
||||
new_base,
|
||||
mmio_region.length as u64,
|
||||
host_addr as u64,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
self.vm
|
||||
.create_user_memory_region(new_region)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
}
|
||||
|
||||
// Insert new region
|
||||
let new_region = self.vm.make_user_memory_region(
|
||||
user_memory_region.slot,
|
||||
user_memory_region.start,
|
||||
user_memory_region.size,
|
||||
user_memory_region.host_addr,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
self.vm
|
||||
.create_user_memory_region(new_region)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
}
|
||||
info!("Moved bar 0x{:x} -> 0x{:x}", old_base, new_base);
|
||||
}
|
||||
@@ -519,10 +485,6 @@ impl PciDevice for VfioUserPciDevice {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
Some(self.id.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VfioUserPciDevice {
|
||||
@@ -531,18 +493,18 @@ impl Drop for VfioUserPciDevice {
|
||||
|
||||
if let Some(msix) = &self.common.interrupt.msix {
|
||||
if msix.bar.enabled() {
|
||||
self.common.disable_msix();
|
||||
self.common.disable_msix(&self.vfio_wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(msi) = &self.common.interrupt.msi {
|
||||
if msi.cfg.enabled() {
|
||||
self.common.disable_msi()
|
||||
self.common.disable_msi(&self.vfio_wrapper)
|
||||
}
|
||||
}
|
||||
|
||||
if self.common.interrupt.intx_in_use() {
|
||||
self.common.disable_intx();
|
||||
self.common.disable_intx(&self.vfio_wrapper);
|
||||
}
|
||||
|
||||
if let Err(e) = self.client.lock().unwrap().shutdown() {
|
||||
|
||||
@@ -6,13 +6,14 @@ edition = "2021"
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.1.18", features = ["wrap_help","cargo"] }
|
||||
clap = { version = "3.1.8", features = ["wrap_help","cargo"] }
|
||||
dirs = "4.0.0"
|
||||
serde = { version = "1.0.137", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.81"
|
||||
serde = { version = "1.0.136", features = ["rc"] }
|
||||
serde_derive = "1.0.136"
|
||||
serde_json = "1.0.78"
|
||||
test_infra = { path = "../test_infra" }
|
||||
thiserror = "1.0.31"
|
||||
thiserror = "1.0.30"
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.1.18", features = ["cargo"] }
|
||||
clap = { version = "3.1.8", features = ["cargo"] }
|
||||
|
||||
@@ -12,14 +12,8 @@ mod performance_tests;
|
||||
|
||||
use clap::{Arg, Command as ClapCommand};
|
||||
use performance_tests::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
env, fmt,
|
||||
process::Command,
|
||||
sync::{mpsc::channel, Arc},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::{env, fmt, process::Command, sync::mpsc::channel, thread, time::Duration};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
@@ -90,21 +84,6 @@ impl Default for MetricsReport {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PerformanceTestOverrides {
|
||||
test_iterations: Option<u32>,
|
||||
}
|
||||
|
||||
impl fmt::Display for PerformanceTestOverrides {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if let Some(test_iterations) = self.test_iterations {
|
||||
write!(f, "test_iterations = {}", test_iterations)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PerformanceTestControl {
|
||||
test_timeout: u32,
|
||||
test_iterations: u32,
|
||||
@@ -112,7 +91,6 @@ pub struct PerformanceTestControl {
|
||||
queue_size: Option<u32>,
|
||||
net_rx: Option<bool>,
|
||||
fio_ops: Option<FioOps>,
|
||||
num_boot_vcpus: Option<u8>,
|
||||
}
|
||||
|
||||
impl fmt::Display for PerformanceTestControl {
|
||||
@@ -147,7 +125,6 @@ impl PerformanceTestControl {
|
||||
queue_size: None,
|
||||
net_rx: None,
|
||||
fio_ops: None,
|
||||
num_boot_vcpus: Some(1),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,12 +140,9 @@ struct PerformanceTest {
|
||||
}
|
||||
|
||||
impl PerformanceTest {
|
||||
pub fn run(&self, overrides: &PerformanceTestOverrides) -> PerformanceTestResult {
|
||||
pub fn run(&self) -> PerformanceTestResult {
|
||||
let mut metrics = Vec::new();
|
||||
for _ in 0..overrides
|
||||
.test_iterations
|
||||
.unwrap_or(self.control.test_iterations)
|
||||
{
|
||||
for _ in 0..self.control.test_iterations {
|
||||
metrics.push((self.func_ptr)(&self.control));
|
||||
}
|
||||
|
||||
@@ -188,9 +162,8 @@ impl PerformanceTest {
|
||||
|
||||
// Calculate the timeout for each test
|
||||
// Note: To cover the setup/cleanup time, 20s is added for each iteration of the test
|
||||
pub fn calc_timeout(&self, test_iterations: &Option<u32>) -> u64 {
|
||||
((self.control.test_timeout + 20) * test_iterations.unwrap_or(self.control.test_iterations))
|
||||
as u64
|
||||
pub fn calc_timeout(&self) -> u64 {
|
||||
((self.control.test_timeout + 20) * self.control.test_iterations) as u64
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +216,7 @@ mod adjuster {
|
||||
}
|
||||
}
|
||||
|
||||
const TEST_LIST: [PerformanceTest; 17] = [
|
||||
const TEST_LIST: [PerformanceTest; 15] = [
|
||||
PerformanceTest {
|
||||
name: "boot_time_ms",
|
||||
func_ptr: performance_boot_time,
|
||||
@@ -264,28 +237,6 @@ const TEST_LIST: [PerformanceTest; 17] = [
|
||||
},
|
||||
unit_adjuster: adjuster::s_to_ms,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "boot_time_16_vcpus_ms",
|
||||
func_ptr: performance_boot_time,
|
||||
control: PerformanceTestControl {
|
||||
test_timeout: 2,
|
||||
test_iterations: 10,
|
||||
num_boot_vcpus: Some(16),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::s_to_ms,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "boot_time_16_vcpus_pmem_ms",
|
||||
func_ptr: performance_boot_time_pmem,
|
||||
control: PerformanceTestControl {
|
||||
test_timeout: 2,
|
||||
test_iterations: 10,
|
||||
num_boot_vcpus: Some(16),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::s_to_ms,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "virtio_net_latency_us",
|
||||
func_ptr: performance_net_latency,
|
||||
@@ -430,20 +381,12 @@ const TEST_LIST: [PerformanceTest; 17] = [
|
||||
},
|
||||
];
|
||||
|
||||
fn run_test_with_timeout(
|
||||
test: &'static PerformanceTest,
|
||||
overrides: &Arc<PerformanceTestOverrides>,
|
||||
) -> Result<PerformanceTestResult, Error> {
|
||||
fn run_test_with_timeout(test: &'static PerformanceTest) -> Result<PerformanceTestResult, Error> {
|
||||
let (sender, receiver) = channel::<Result<PerformanceTestResult, Error>>();
|
||||
let test_iterations = overrides.test_iterations;
|
||||
let overrides = overrides.clone();
|
||||
thread::spawn(move || {
|
||||
println!(
|
||||
"Test '{}' running .. (control: {}, overrides: {})",
|
||||
test.name, test.control, overrides
|
||||
);
|
||||
println!("Test '{}' running .. ({})", test.name, test.control);
|
||||
|
||||
let output = match std::panic::catch_unwind(|| test.run(&overrides)) {
|
||||
let output = match std::panic::catch_unwind(|| test.run()) {
|
||||
Ok(test_result) => {
|
||||
println!(
|
||||
"Test '{}' .. ok: mean = {}, std_dev = {}",
|
||||
@@ -458,7 +401,7 @@ fn run_test_with_timeout(
|
||||
});
|
||||
|
||||
// Todo: Need to cleanup/kill all hanging child processes
|
||||
let test_timeout = test.calc_timeout(&test_iterations);
|
||||
let test_timeout = test.calc_timeout();
|
||||
receiver
|
||||
.recv_timeout(Duration::from_secs(test_timeout))
|
||||
.map_err(|_| {
|
||||
@@ -502,12 +445,6 @@ fn main() {
|
||||
.help("Report file. Standard error is used if not specified")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("iterations")
|
||||
.long("iterations")
|
||||
.help("Override number of test iterations")
|
||||
.takes_value(true),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
// It seems that the tool (ethr) used for testing the virtio-net latency
|
||||
@@ -536,17 +473,9 @@ fn main() {
|
||||
|
||||
init_tests();
|
||||
|
||||
let overrides = Arc::new(PerformanceTestOverrides {
|
||||
test_iterations: cmd_arguments
|
||||
.value_of("iterations")
|
||||
.map(|s| s.parse())
|
||||
.transpose()
|
||||
.unwrap_or_default(),
|
||||
});
|
||||
|
||||
for test in test_list.iter() {
|
||||
if test_filter.is_empty() || test_filter.iter().any(|&s| test.name.contains(s)) {
|
||||
match run_test_with_timeout(test, &overrides) {
|
||||
match run_test_with_timeout(test) {
|
||||
Ok(r) => {
|
||||
metrics_report.results.push(r);
|
||||
}
|
||||
|
||||
@@ -238,7 +238,6 @@ pub fn performance_net_throughput(control: &PerformanceTestControl) -> f64 {
|
||||
.default_disks()
|
||||
.args(&["--net", net_params.as_str()])
|
||||
.capture_output()
|
||||
.verbosity(VerbosityLevel::Warn)
|
||||
.set_print_cmd(false)
|
||||
.spawn()
|
||||
.unwrap();
|
||||
@@ -368,7 +367,6 @@ pub fn performance_net_latency(control: &PerformanceTestControl) -> f64 {
|
||||
.default_disks()
|
||||
.args(&["--net", net_params.as_str()])
|
||||
.capture_output()
|
||||
.verbosity(VerbosityLevel::Warn)
|
||||
.set_print_cmd(false)
|
||||
.spawn()
|
||||
.unwrap();
|
||||
@@ -474,12 +472,7 @@ fn parse_boot_time_output(output: &[u8]) -> Result<f64, Error> {
|
||||
}
|
||||
|
||||
fn measure_boot_time(cmd: &mut GuestCommand, test_timeout: u32) -> Result<f64, Error> {
|
||||
let mut child = cmd
|
||||
.capture_output()
|
||||
.verbosity(VerbosityLevel::Warn)
|
||||
.set_print_cmd(false)
|
||||
.spawn()
|
||||
.unwrap();
|
||||
let mut child = cmd.capture_output().set_print_cmd(false).spawn().unwrap();
|
||||
|
||||
thread::sleep(Duration::new(test_timeout as u64, 0));
|
||||
let _ = child.kill();
|
||||
@@ -506,10 +499,6 @@ pub fn performance_boot_time(control: &PerformanceTestControl) -> f64 {
|
||||
let mut cmd = GuestCommand::new(&guest);
|
||||
|
||||
let c = cmd
|
||||
.args(&[
|
||||
"--cpus",
|
||||
&format!("boot={}", control.num_boot_vcpus.unwrap_or(1)),
|
||||
])
|
||||
.args(&["--memory", "size=1G"])
|
||||
.args(&["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(&["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
@@ -533,10 +522,6 @@ pub fn performance_boot_time_pmem(control: &PerformanceTestControl) -> f64 {
|
||||
let guest = performance_test_new_guest(Box::new(focal));
|
||||
let mut cmd = GuestCommand::new(&guest);
|
||||
let c = cmd
|
||||
.args(&[
|
||||
"--cpus",
|
||||
&format!("boot={}", control.num_boot_vcpus.unwrap_or(1)),
|
||||
])
|
||||
.args(&["--memory", "size=1G,hugepages=on"])
|
||||
.args(&["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(&["--cmdline", "root=/dev/pmem0p1 console=ttyS0 quiet rw"])
|
||||
@@ -670,7 +655,6 @@ pub fn performance_block_io(control: &PerformanceTestControl) -> f64 {
|
||||
.default_net()
|
||||
.args(&["--api-socket", &api_socket])
|
||||
.capture_output()
|
||||
.verbosity(VerbosityLevel::Warn)
|
||||
.set_print_cmd(false)
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
@@ -10,7 +10,7 @@ path = "src/qcow.rs"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.4.3"
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
remain = "0.2.3"
|
||||
libc = "0.2.123"
|
||||
log = "0.4.16"
|
||||
remain = "0.2.2"
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
@@ -516,19 +516,6 @@ impl QcowFile {
|
||||
|
||||
let l2_entries = cluster_size / size_of::<u64>() as u64;
|
||||
|
||||
// Check for compressed blocks
|
||||
for l2_addr_disk in l1_table.get_values() {
|
||||
if *l2_addr_disk != 0 {
|
||||
if let Err(e) = Self::read_l2_cluster(&mut raw_file, *l2_addr_disk) {
|
||||
if let Some(os_error) = e.raw_os_error() {
|
||||
if os_error == ENOTSUP {
|
||||
return Err(Error::CompressedBlocksNotSupported);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut qcow = QcowFile {
|
||||
raw_file,
|
||||
header,
|
||||
|
||||
@@ -4,6 +4,6 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.123"
|
||||
log = "0.4.16"
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
159
release-notes.md
159
release-notes.md
@@ -1,22 +1,12 @@
|
||||
- [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)
|
||||
- [Notable Improvements](#notable-improvements)
|
||||
- [Deprecations](#deprecations)
|
||||
- [New on the Website](#new-on-the-website)
|
||||
- [Contributors](#contributors)
|
||||
- [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-1)
|
||||
- [Deprecations](#deprecations-1)
|
||||
- [Contributors](#contributors-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||
- [Deprecations](#deprecations)
|
||||
- [Contributors](#contributors)
|
||||
- [v22.1](#v221)
|
||||
- [v22.0](#v220)
|
||||
- [GDB Debug Stub Support](#gdb-debug-stub-support)
|
||||
@@ -27,13 +17,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-2)
|
||||
- [Contributors](#contributors-2)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
||||
- [Contributors](#contributors-1)
|
||||
- [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-3)
|
||||
- [Contributors](#contributors-3)
|
||||
- [Notable Bug fixes](#notable-bug-fixes-2)
|
||||
- [Contributors](#contributors-2)
|
||||
- [v20.2](#v202)
|
||||
- [v20.1](#v201)
|
||||
- [v20.0](#v200)
|
||||
@@ -42,8 +32,8 @@
|
||||
- [Improved VFIO support](#improved-vfio-support)
|
||||
- [Safer code](#safer-code)
|
||||
- [Extended documentation](#extended-documentation)
|
||||
- [Notable bug fixes](#notable-bug-fixes-4)
|
||||
- [Contributors](#contributors-4)
|
||||
- [Notable bug fixes](#notable-bug-fixes-3)
|
||||
- [Contributors](#contributors-3)
|
||||
- [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)
|
||||
@@ -51,8 +41,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-5)
|
||||
- [Contributors](#contributors-5)
|
||||
- [Notable bug fixes](#notable-bug-fixes-4)
|
||||
- [Contributors](#contributors-4)
|
||||
- [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)
|
||||
@@ -62,31 +52,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-6)
|
||||
- [Contributors](#contributors-6)
|
||||
- [Notable bug fixes](#notable-bug-fixes-5)
|
||||
- [Contributors](#contributors-5)
|
||||
- [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-7)
|
||||
- [Contributors](#contributors-7)
|
||||
- [Notable bug fixes](#notable-bug-fixes-6)
|
||||
- [Contributors](#contributors-6)
|
||||
- [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-8)
|
||||
- [Notable bug fixes](#notable-bug-fixes-7)
|
||||
- [Removed functionality](#removed-functionality)
|
||||
- [Contributors](#contributors-8)
|
||||
- [Contributors](#contributors-7)
|
||||
- [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-9)
|
||||
- [Deprecations](#deprecations-1)
|
||||
- [Contributors](#contributors-8)
|
||||
- [v0.14.1](#v0141)
|
||||
- [v0.14.0](#v0140)
|
||||
- [Structured event monitoring](#structured-event-monitoring)
|
||||
@@ -95,8 +85,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-10)
|
||||
- [Deprecations](#deprecations-2)
|
||||
- [Contributors](#contributors-9)
|
||||
- [v0.13.0](#v0130)
|
||||
- [Wider VFIO device support](#wider-vfio-device-support)
|
||||
- [Improved huge page support](#improved-huge-page-support)
|
||||
@@ -104,13 +94,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-11)
|
||||
- [Contributors](#contributors-10)
|
||||
- [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-12)
|
||||
- [Contributors](#contributors-11)
|
||||
- [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)
|
||||
@@ -122,15 +112,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-9)
|
||||
- [Contributors](#contributors-13)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-8)
|
||||
- [Contributors](#contributors-12)
|
||||
- [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-10)
|
||||
- [Contributors](#contributors-14)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-9)
|
||||
- [Contributors](#contributors-13)
|
||||
- [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)
|
||||
@@ -143,17 +133,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-11)
|
||||
- [Contributors](#contributors-15)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-10)
|
||||
- [Contributors](#contributors-14)
|
||||
- [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-12)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-11)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes)
|
||||
- [Contributors](#contributors-16)
|
||||
- [Contributors](#contributors-15)
|
||||
- [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)
|
||||
@@ -163,14 +153,14 @@
|
||||
- [`Seccomp` Sandboxing](#seccomp-sandboxing)
|
||||
- [Updated Distribution Support](#updated-distribution-support)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes-1)
|
||||
- [Contributors](#contributors-17)
|
||||
- [Contributors](#contributors-16)
|
||||
- [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-18)
|
||||
- [Contributors](#contributors-17)
|
||||
- [v0.5.1](#v051)
|
||||
- [v0.5.0](#v050)
|
||||
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
|
||||
@@ -178,7 +168,7 @@
|
||||
- [New Interrupt Management Framework](#new-interrupt-management-framework)
|
||||
- [Development Tools](#development-tools)
|
||||
- [Kata Containers Integration](#kata-containers-integration)
|
||||
- [Contributors](#contributors-19)
|
||||
- [Contributors](#contributors-18)
|
||||
- [v0.4.0](#v040)
|
||||
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
|
||||
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
|
||||
@@ -187,7 +177,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-20)
|
||||
- [Contributors](#contributors-19)
|
||||
- [v0.3.0](#v030)
|
||||
- [Block device offloading](#block-device-offloading)
|
||||
- [Network device backend](#network-device-backend)
|
||||
@@ -213,85 +203,6 @@
|
||||
- [Console over virtio](#console-over-virtio)
|
||||
- [Unit testing](#unit-testing)
|
||||
- [Integration tests parallelization](#integration-tests-parallelization)
|
||||
|
||||
# v24.0
|
||||
|
||||
This release has been tracked through the [v24.0
|
||||
project](https://github.com/cloud-hypervisor/cloud-hypervisor/projects/28).
|
||||
|
||||
### Bypass Mode for `virtio-iommu`
|
||||
|
||||
`virtio-iommu` specification describes how a device can be attached by default
|
||||
to a bypass domain. This feature is particularly helpful for booting a VM with
|
||||
guest software which doesn't support `virtio-iommu` but still need to access
|
||||
the device. Now that Cloud Hypervisor supports this feature, it can boot a VM
|
||||
with Rust Hypervisor Firmware or OVMF even if the `virtio-block` device exposing
|
||||
the disk image is placed behind a virtual IOMMU.
|
||||
|
||||
### Ensure Identifiers Uniqueness
|
||||
|
||||
Multiple checks have been added to the code to prevent devices with identical
|
||||
identifiers from being created, and therefore avoid unexpected behaviors at boot
|
||||
or whenever a device was hot plugged into the VM.
|
||||
|
||||
### Sparse Mmap support
|
||||
|
||||
Sparse mmap support has been added to both VFIO and vfio-user devices. This
|
||||
allows the device regions that are not fully mappable to be partially mapped.
|
||||
And the more a device region can be mapped into the guest address space, the
|
||||
fewer VM exits will be generated when this device is accessed. This directly
|
||||
impacts the performance related to this device.
|
||||
|
||||
### Expose Platform Serial Number
|
||||
|
||||
A new `serial_number` option has been added to `--platform`, allowing a user to
|
||||
set a specific serial number for the platform. This number is exposed to the
|
||||
guest through the SMBIOS.
|
||||
|
||||
### Notable Bug Fixes
|
||||
|
||||
* Fix loading RAW firmware (#4072)
|
||||
* Reject compressed QCOW images (#4055)
|
||||
* Reject virtio-mem resize if device is not activated (#4003)
|
||||
* Fix potential mmap leaks from VFIO/vfio-user MMIO regions (#4069)
|
||||
* Fix algorithm finding HOB memory resources (#3983)
|
||||
|
||||
### Notable Improvements
|
||||
|
||||
* Refactor interrupt handling (#4083)
|
||||
* Load kernel asynchronously (#4022)
|
||||
* Only create ACPI memory manager DSDT when resizable (#4013)
|
||||
|
||||
### Deprecations
|
||||
|
||||
Deprecated features will be removed in a subsequent release and users should
|
||||
plan to use alternatives
|
||||
|
||||
* The `mergeable` option from the `virtio-pmem` support has been deprecated
|
||||
(#3968)
|
||||
* The `dax` option from the `virtio-fs` support has been deprecated (#3889)
|
||||
|
||||
### New on the Website
|
||||
|
||||
A new blog post [Achieving Bare Metal Performance Within a Virtual
|
||||
Machine](https://www.cloudhypervisor.org/blog/achieving-bare-metal-performance-within-a-virtual-machine)
|
||||
has been added to the Cloud Hypervisor website.
|
||||
|
||||
### Contributors
|
||||
|
||||
Many thanks to everyone who has contributed to our release:
|
||||
|
||||
* Anatol Belski <anbelski@linux.microsoft.com>
|
||||
* Bo Chen <chen.bo@intel.com>
|
||||
* Fabiano Fidêncio <fabiano.fidencio@intel.com>
|
||||
* LiHui <andrewli@kubesphere.io>
|
||||
* Maksym Pavlenko <pavlenko.maksym@gmail.com>
|
||||
* Rob Bradford <robert.bradford@intel.com>
|
||||
* Sebastien Boeuf <sebastien.boeuf@intel.com>
|
||||
* Steven Dake <steven.dake@gmail.com>
|
||||
* Vincent Batts <vbatts@hashbangbash.com>
|
||||
* Wei Liu <liuwe@microsoft.com>
|
||||
|
||||
# v23.1
|
||||
|
||||
This is a bug fix release. The following issues have been addressed:
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
FROM ubuntu:20.04 as dev
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG RUST_TOOLCHAIN="1.60.0"
|
||||
ARG RUST_TOOLCHAIN="1.59.0"
|
||||
ARG CLH_SRC_DIR="/cloud-hypervisor"
|
||||
ARG CLH_BUILD_DIR="$CLH_SRC_DIR/build"
|
||||
ARG CARGO_REGISTRY_DIR="$CLH_BUILD_DIR/cargo_registry"
|
||||
|
||||
@@ -1,32 +1,25 @@
|
||||
# If this flag is set to 1, rustup installation needs to exist on the system and
|
||||
# <arch>-unknown-linux-gnu target is required.
|
||||
# If this flag is set to 0, distro specific Rust packages will be pulled into the build environment.
|
||||
# This spec file assumes you're building on an environment where:
|
||||
# * You have access to the internet during the build
|
||||
# * You have rustup installed on your system
|
||||
# * You have both x86_64-unknown-linux-gnu and x86_64-unknown-linux-musl
|
||||
# targets installed.
|
||||
|
||||
%define using_rustup 1
|
||||
# If this flag is set to 1, <arch>-unknown-linux-musl target is required.
|
||||
%define using_musl_libc 1
|
||||
# If this flag is set to 1, the vendored crates archive and cargo.toml need to be prepared and
|
||||
# offline build is implied. Attached script update_src can be used for the vendorization.
|
||||
# If this flag is set to 0, access to the internet is required during the build.
|
||||
%define using_vendored_crates 0
|
||||
|
||||
Name: cloud-hypervisor
|
||||
Summary: Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on top of KVM.
|
||||
Version: 24.0
|
||||
Version: 23.1
|
||||
Release: 0%{?dist}
|
||||
License: ASL 2.0 or BSD-3-clause
|
||||
Group: Applications/System
|
||||
Source0: https://github.com/cloud-hypervisor/cloud-hypervisor/archive/v%{version}.tar.gz
|
||||
%if 0%{?using_vendored_crates}
|
||||
Source1: vendor.tar.gz
|
||||
Source2: config.toml
|
||||
%endif
|
||||
ExclusiveArch: x86_64 aarch64
|
||||
ExclusiveArch: x86_64
|
||||
|
||||
BuildRequires: gcc
|
||||
BuildRequires: glibc-devel
|
||||
BuildRequires: binutils
|
||||
BuildRequires: git
|
||||
BuildRequires: openssl-devel
|
||||
|
||||
%if ! 0%{?using_rustup}
|
||||
BuildRequires: rust
|
||||
@@ -38,51 +31,29 @@ Requires: glibc
|
||||
Requires: libgcc
|
||||
Requires: libcap
|
||||
|
||||
%ifarch x86_64
|
||||
%define rust_def_target x86_64-unknown-linux-gnu
|
||||
%if 0%{?using_musl_libc}
|
||||
%define rust_musl_target x86_64-unknown-linux-musl
|
||||
%endif
|
||||
%endif
|
||||
%ifarch aarch64
|
||||
%define rust_def_target aarch64-unknown-linux-gnu
|
||||
%if 0%{?using_musl_libc}
|
||||
%define rust_musl_target aarch64-unknown-linux-musl
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?using_vendored_crates}
|
||||
%define cargo_offline --offline
|
||||
%endif
|
||||
|
||||
%description
|
||||
Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on top of KVM. The project focuses on exclusively running modern, cloud workloads, on top of a limited set of hardware architectures and platforms. Cloud workloads refers to those that are usually run by customers inside a cloud provider. For our purposes this means modern Linux* distributions with most I/O handled by paravirtualised devices (i.e. virtio), no requirement for legacy devices and recent CPUs and KVM.
|
||||
|
||||
%prep
|
||||
|
||||
%setup -q
|
||||
%if 0%{?using_vendored_crates}
|
||||
tar xf %{SOURCE1}
|
||||
mkdir -p .cargo
|
||||
cp %{SOURCE2} .cargo/
|
||||
%endif
|
||||
|
||||
%install
|
||||
rm -rf %{buildroot}
|
||||
install -d %{buildroot}%{_bindir}
|
||||
install -D -m755 ./target/%{rust_def_target}/release/cloud-hypervisor %{buildroot}%{_bindir}
|
||||
install -D -m755 ./target/%{rust_def_target}/release/ch-remote %{buildroot}%{_bindir}
|
||||
install -D -m755 ./target/x86_64-unknown-linux-gnu/release/cloud-hypervisor %{buildroot}%{_bindir}
|
||||
install -D -m755 ./target/x86_64-unknown-linux-gnu/release/ch-remote %{buildroot}%{_bindir}
|
||||
install -d %{buildroot}%{_libdir}
|
||||
install -d %{buildroot}%{_libdir}/cloud-hypervisor
|
||||
install -D -m755 target/%{rust_def_target}/release/vhost_user_block %{buildroot}%{_libdir}/cloud-hypervisor
|
||||
install -D -m755 target/%{rust_def_target}/release/vhost_user_net %{buildroot}%{_libdir}/cloud-hypervisor
|
||||
install -D -m755 target/x86_64-unknown-linux-gnu/release/vhost_user_block %{buildroot}%{_libdir}/cloud-hypervisor
|
||||
install -D -m755 target/x86_64-unknown-linux-gnu/release/vhost_user_net %{buildroot}%{_libdir}/cloud-hypervisor
|
||||
|
||||
%if 0%{?using_musl_libc}
|
||||
install -d %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
install -D -m755 target/%{rust_musl_target}/release/cloud-hypervisor %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
install -D -m755 target/%{rust_musl_target}/release/vhost_user_block %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
install -D -m755 target/%{rust_musl_target}/release/vhost_user_net %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
install -D -m755 target/%{rust_musl_target}/release/ch-remote %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
install -D -m755 target/x86_64-unknown-linux-musl/release/cloud-hypervisor %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
install -D -m755 target/x86_64-unknown-linux-musl/release/vhost_user_block %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
install -D -m755 target/x86_64-unknown-linux-musl/release/vhost_user_net %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
install -D -m755 target/x86_64-unknown-linux-musl/release/ch-remote %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
%endif
|
||||
|
||||
|
||||
@@ -103,25 +74,21 @@ fi
|
||||
echo ${cargo_version}
|
||||
|
||||
%if 0%{?using_rustup}
|
||||
rustup target list --installed | grep -e "%{rust_def_target}"
|
||||
rustup target list --installed | grep x86_64-unknown-linux-gnu
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Target %{rust_def_target} not found, please install(#rustup target add %{rust_def_target}). exiting"
|
||||
echo "Target x86_64-unknown-linux-gnu not found, please install(#rustup target add x86_64-unknown-linux-gnu). exiting"
|
||||
fi
|
||||
%if 0%{?using_musl_libc}
|
||||
rustup target list --installed | grep -e "%{rust_musl_target}"
|
||||
rustup target list --installed | grep x86_64-unknown-linux-musl
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Target %{rust_musl_target} not found, please install(#rustup target add %{rust_musl_target}). exiting"
|
||||
echo "Target x86_64-unknown-linux-musl not found, please install(#rustup target add x86_64-unknown-linux-musl). exiting"
|
||||
fi
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?using_vendored_crates}
|
||||
# For vendored build, prepend this so openssl-sys doesn't trigger full OpenSSL build
|
||||
export OPENSSL_NO_VENDOR=1
|
||||
%endif
|
||||
cargo build --release --target=%{rust_def_target} --all %{cargo_offline}
|
||||
cargo build --release --target=x86_64-unknown-linux-gnu --all
|
||||
%if 0%{?using_musl_libc}
|
||||
cargo build --release --target=%{rust_musl_target} --all %{cargo_offline}
|
||||
cargo build --release --target=x86_64-unknown-linux-musl --all
|
||||
%endif
|
||||
|
||||
|
||||
@@ -145,14 +112,8 @@ rm -rf %{buildroot}
|
||||
|
||||
|
||||
%changelog
|
||||
* Wed May 25 2022 Sebastien Boeuf <sebastien.boeuf@intel.com> 24.0-0
|
||||
- Update to 24.0
|
||||
|
||||
* Tue May 18 2022 Anatol Belski <anbelski@linux.microsoft.com> - 23.1-0
|
||||
* Mon May 09 2022 Rob Bradford <robert.bradford@intel.com> 23.1-0
|
||||
- Update to 23.1
|
||||
- Add support for aarch64 build
|
||||
- Add offline build configuration using vendored crates
|
||||
- Fix dependency for openssl-sys
|
||||
|
||||
* Thu Apr 13 2022 Rob Bradford <robert.bradford@intel.com> 23.0-0
|
||||
- Update to 23.0
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
CH_VER=$1
|
||||
CH_SPEC_DIR=$(pwd)
|
||||
CH_TMP_DIR=$CH_SPEC_DIR/ch_tmp
|
||||
CH_SRC_DIR=$CH_SPEC_DIR/SOURCES
|
||||
|
||||
if [ -z $CH_VER ]; then
|
||||
echo "Replace sources with the given <version>"
|
||||
echo "Usage: $0 <version>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -rf $CH_TMP_DIR
|
||||
if [ ! -d $CH_SRC_DIR ]; then
|
||||
mkdir $CH_SRC_DIR
|
||||
fi
|
||||
rm -f $CH_SRC_DIR/v*.tar.gz $CH_SRC_DIR/config.toml $CH_SRC_DIR/vendor.tar.gz
|
||||
|
||||
|
||||
wget https://github.com/cloud-hypervisor/cloud-hypervisor/archive/v${CH_VER}.tar.gz -O $CH_SRC_DIR/v${CH_VER}.tar.gz
|
||||
mkdir $CH_TMP_DIR
|
||||
tar xf $CH_SRC_DIR/v${CH_VER}.tar.gz -C $CH_TMP_DIR --strip-components=1
|
||||
|
||||
|
||||
pushd $CH_TMP_DIR
|
||||
|
||||
cargo vendor > $CH_SRC_DIR/config.toml
|
||||
tar czvf $CH_SRC_DIR/vendor.tar.gz vendor
|
||||
|
||||
popd
|
||||
|
||||
rm -rf $CH_TMP_DIR
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
CLI_NAME="Cloud Hypervisor"
|
||||
|
||||
CTR_IMAGE_TAG="cloudhypervisor/dev"
|
||||
CTR_IMAGE_VERSION="20220524-0"
|
||||
CTR_IMAGE_VERSION="20220405-0"
|
||||
CTR_IMAGE="${CTR_IMAGE_TAG}:${CTR_IMAGE_VERSION}"
|
||||
|
||||
DOCKER_RUNTIME="docker"
|
||||
@@ -226,11 +226,6 @@ cmd_build() {
|
||||
} ;;
|
||||
"--debug") { build="debug"; } ;;
|
||||
"--release") { build="release"; } ;;
|
||||
"--runtime")
|
||||
shift
|
||||
DOCKER_RUNTIME="$1"
|
||||
export DOCKER_RUNTIME
|
||||
;;
|
||||
"--libc")
|
||||
shift
|
||||
[[ "$1" =~ ^(musl|gnu)$ ]] ||
|
||||
|
||||
@@ -192,15 +192,6 @@ update_workloads() {
|
||||
fi
|
||||
popd
|
||||
|
||||
# Download Cloud Hypervisor binary from its last stable release
|
||||
LAST_RELEASE_VERSION="v23.0"
|
||||
CH_RELEASE_URL="https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/$LAST_RELEASE_VERSION/cloud-hypervisor-static-aarch64"
|
||||
CH_RELEASE_NAME="cloud-hypervisor-static-aarch64"
|
||||
pushd $WORKLOADS_DIR
|
||||
time wget --quiet $CH_RELEASE_URL -O "$CH_RELEASE_NAME" || exit 1
|
||||
chmod +x $CH_RELEASE_NAME
|
||||
popd
|
||||
|
||||
# Build custom kernel for guest VMs
|
||||
build_custom_linux
|
||||
|
||||
|
||||
@@ -45,15 +45,6 @@ if [ $? -ne 0 ]; then
|
||||
fi
|
||||
popd
|
||||
|
||||
# Download Cloud Hypervisor binary from its last stable release
|
||||
LAST_RELEASE_VERSION="v23.0"
|
||||
CH_RELEASE_URL="https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/$LAST_RELEASE_VERSION/cloud-hypervisor-static"
|
||||
CH_RELEASE_NAME="cloud-hypervisor-static"
|
||||
pushd $WORKLOADS_DIR
|
||||
time wget --quiet $CH_RELEASE_URL -O "$CH_RELEASE_NAME" || exit 1
|
||||
chmod +x $CH_RELEASE_NAME
|
||||
popd
|
||||
|
||||
# Build custom kernel based on virtio-pmem and virtio-fs upstream patches
|
||||
VMLINUX_IMAGE="$WORKLOADS_DIR/vmlinux"
|
||||
|
||||
|
||||
@@ -11,8 +11,6 @@ cargo_args=("")
|
||||
if [[ $hypervisor = "mshv" ]]; then
|
||||
cargo_args+=("--no-default-features")
|
||||
cargo_args+=("--features common,$hypervisor")
|
||||
elif [[ $(uname -m) = "x86_64" ]]; then
|
||||
cargo_args+=("--features tdx")
|
||||
fi
|
||||
|
||||
if [[ "${BUILD_TARGET}" == "aarch64-unknown-linux-musl" ]]; then
|
||||
|
||||
@@ -161,7 +161,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>",
|
||||
)
|
||||
.takes_value(true)
|
||||
.group("vm-config"),
|
||||
|
||||
@@ -8,7 +8,7 @@ edition = "2021"
|
||||
dirs = "4.0.0"
|
||||
epoll = "4.3.1"
|
||||
lazy_static = "1.4.0"
|
||||
libc = "0.2.126"
|
||||
libc = "0.2.123"
|
||||
ssh2 = { version = "0.9.1", features = ["vendored-openssl"]}
|
||||
vmm-sys-util = "0.9.0"
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
@@ -9,7 +9,6 @@ extern crate lazy_static;
|
||||
use ssh2::Session;
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::Debug;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
@@ -1160,12 +1159,7 @@ impl Guest {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn check_devices_common(
|
||||
&self,
|
||||
socket: Option<&String>,
|
||||
console_text: Option<&String>,
|
||||
pmem_path: Option<&String>,
|
||||
) {
|
||||
pub fn check_devices_common(&self, socket: Option<&String>, console_text: Option<&String>) {
|
||||
// Check block devices are readable
|
||||
self.ssh_command("sudo dd if=/dev/vda of=/dev/null bs=1M iflag=direct count=1024")
|
||||
.unwrap();
|
||||
@@ -1184,63 +1178,6 @@ impl Guest {
|
||||
self.ssh_command(&console_cmd).unwrap();
|
||||
}
|
||||
// The net device is 'automatically' exercised through the above 'ssh' commands
|
||||
|
||||
// Check if the pmem device is usable
|
||||
if let Some(pmem_path) = pmem_path {
|
||||
assert_eq!(
|
||||
self.ssh_command(&format!("ls {}", pmem_path))
|
||||
.unwrap()
|
||||
.trim(),
|
||||
pmem_path
|
||||
);
|
||||
assert_eq!(
|
||||
self.ssh_command(&format!("sudo mount {} /mnt", pmem_path))
|
||||
.unwrap(),
|
||||
""
|
||||
);
|
||||
assert_eq!(self.ssh_command("ls /mnt").unwrap(), "lost+found\n");
|
||||
self.ssh_command("echo test123 | sudo tee /mnt/test")
|
||||
.unwrap();
|
||||
assert_eq!(self.ssh_command("sudo umount /mnt").unwrap(), "");
|
||||
assert_eq!(self.ssh_command("ls /mnt").unwrap(), "");
|
||||
|
||||
assert_eq!(
|
||||
self.ssh_command(&format!("sudo mount {} /mnt", pmem_path))
|
||||
.unwrap(),
|
||||
""
|
||||
);
|
||||
assert_eq!(
|
||||
self.ssh_command("sudo cat /mnt/test || true")
|
||||
.unwrap()
|
||||
.trim(),
|
||||
"test123"
|
||||
);
|
||||
self.ssh_command("sudo rm /mnt/test").unwrap();
|
||||
assert_eq!(self.ssh_command("sudo umount /mnt").unwrap(), "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum VerbosityLevel {
|
||||
Warn,
|
||||
Info,
|
||||
Debug,
|
||||
}
|
||||
|
||||
impl Default for VerbosityLevel {
|
||||
fn default() -> Self {
|
||||
Self::Warn
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for VerbosityLevel {
|
||||
fn to_string(&self) -> String {
|
||||
use VerbosityLevel::*;
|
||||
match self {
|
||||
Warn => "".to_string(),
|
||||
Info => "-v".to_string(),
|
||||
Debug => "-vv".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1249,29 +1186,22 @@ pub struct GuestCommand<'a> {
|
||||
guest: &'a Guest,
|
||||
capture_output: bool,
|
||||
print_cmd: bool,
|
||||
verbosity: VerbosityLevel,
|
||||
}
|
||||
|
||||
impl<'a> GuestCommand<'a> {
|
||||
pub fn new(guest: &'a Guest) -> Self {
|
||||
Self::new_with_binary_path(guest, &clh_command("cloud-hypervisor"))
|
||||
Self::new_with_binary_name(guest, "cloud-hypervisor")
|
||||
}
|
||||
|
||||
pub fn new_with_binary_path(guest: &'a Guest, binary_path: &str) -> Self {
|
||||
pub fn new_with_binary_name(guest: &'a Guest, binary_name: &str) -> Self {
|
||||
Self {
|
||||
command: Command::new(binary_path),
|
||||
command: Command::new(clh_command(binary_name)),
|
||||
guest,
|
||||
capture_output: false,
|
||||
print_cmd: true,
|
||||
verbosity: VerbosityLevel::Info,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verbosity(&mut self, verbosity: VerbosityLevel) -> &mut Self {
|
||||
self.verbosity = verbosity;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn capture_output(&mut self) -> &mut Self {
|
||||
self.capture_output = true;
|
||||
self
|
||||
@@ -1283,17 +1213,6 @@ impl<'a> GuestCommand<'a> {
|
||||
}
|
||||
|
||||
pub fn spawn(&mut self) -> io::Result<Child> {
|
||||
use VerbosityLevel::*;
|
||||
match &self.verbosity {
|
||||
Warn => {}
|
||||
Info => {
|
||||
self.command.arg("-v");
|
||||
}
|
||||
Debug => {
|
||||
self.command.arg("-vv");
|
||||
}
|
||||
};
|
||||
|
||||
if self.print_cmd {
|
||||
println!(
|
||||
"\n\n==== Start cloud-hypervisor command-line ====\n\n\
|
||||
@@ -1306,6 +1225,7 @@ impl<'a> GuestCommand<'a> {
|
||||
if self.capture_output {
|
||||
let child = self
|
||||
.command
|
||||
.arg("-v")
|
||||
.stderr(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
@@ -1325,7 +1245,7 @@ impl<'a> GuestCommand<'a> {
|
||||
))
|
||||
}
|
||||
} else {
|
||||
self.command.spawn()
|
||||
self.command.arg("-v").spawn()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,14 +5,14 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
anyhow = "1.0.56"
|
||||
libc = "0.2.123"
|
||||
log = "0.4.16"
|
||||
serde = {version = ">=1.0.27", features = ["rc"] }
|
||||
serde_derive = ">=1.0.27"
|
||||
serde_json = ">=1.0.9"
|
||||
thiserror = "1.0.31"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
thiserror = "1.0.30"
|
||||
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = ">=0.3.1"
|
||||
|
||||
[dependencies.vfio-bindings]
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
//
|
||||
|
||||
use std::ffi::CString;
|
||||
use std::fs::File;
|
||||
use std::io::{IoSlice, Read, Write};
|
||||
use std::mem::size_of;
|
||||
use std::num::Wrapping;
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::os::unix::prelude::RawFd;
|
||||
@@ -236,7 +234,6 @@ pub struct Region {
|
||||
pub index: u32,
|
||||
pub size: u64,
|
||||
pub file_offset: Option<FileOffset>,
|
||||
pub sparse_areas: Vec<vfio_region_sparse_mmap_area>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -297,7 +294,7 @@ impl Client {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::Version,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: (size_of::<Version>() + version_data.len() + 1) as u32,
|
||||
message_size: (std::mem::size_of::<Version>() + version_data.len() + 1) as u32,
|
||||
..Default::default()
|
||||
},
|
||||
major: 0,
|
||||
@@ -333,7 +330,7 @@ impl Client {
|
||||
|
||||
let mut server_version_data = Vec::new();
|
||||
server_version_data.resize(
|
||||
server_version.header.message_size as usize - size_of::<Version>(),
|
||||
server_version.header.message_size as usize - std::mem::size_of::<Version>(),
|
||||
0,
|
||||
);
|
||||
self.stream
|
||||
@@ -364,10 +361,10 @@ impl Client {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::DmaMap,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: size_of::<DmaMap>() as u32,
|
||||
message_size: std::mem::size_of::<DmaMap>() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
argsz: (size_of::<DmaMap>() - size_of::<Header>()) as u32,
|
||||
argsz: (std::mem::size_of::<DmaMap>() - std::mem::size_of::<Header>()) as u32,
|
||||
flags: DmaMapFlags::ReadWrite,
|
||||
offset,
|
||||
address,
|
||||
@@ -394,10 +391,10 @@ impl Client {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::DmaUnmap,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: size_of::<DmaUnmap>() as u32,
|
||||
message_size: std::mem::size_of::<DmaUnmap>() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
argsz: (size_of::<DmaUnmap>() - size_of::<Header>()) as u32,
|
||||
argsz: (std::mem::size_of::<DmaUnmap>() - std::mem::size_of::<Header>()) as u32,
|
||||
flags: 0,
|
||||
address,
|
||||
size,
|
||||
@@ -423,7 +420,7 @@ impl Client {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::DeviceReset,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: size_of::<DeviceReset>() as u32,
|
||||
message_size: std::mem::size_of::<DeviceReset>() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
@@ -448,10 +445,10 @@ impl Client {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::DeviceGetInfo,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: size_of::<DeviceGetInfo>() as u32,
|
||||
message_size: std::mem::size_of::<DeviceGetInfo>() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
argsz: size_of::<DeviceGetInfo>() as u32,
|
||||
argsz: std::mem::size_of::<DeviceGetInfo>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
debug!("Command: {:?}", get_info);
|
||||
@@ -477,62 +474,20 @@ impl Client {
|
||||
let num_regions = reply.num_regions;
|
||||
let mut regions = Vec::new();
|
||||
for index in 0..num_regions {
|
||||
let (region_info, fd, sparse_areas) = self.get_region_info(index)?;
|
||||
regions.push(Region {
|
||||
flags: region_info.flags,
|
||||
index: region_info.index,
|
||||
size: region_info.size,
|
||||
file_offset: fd.map(|fd| FileOffset::new(fd, region_info.offset)),
|
||||
sparse_areas,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(regions)
|
||||
}
|
||||
|
||||
fn get_region_info(
|
||||
&mut self,
|
||||
index: u32,
|
||||
) -> Result<
|
||||
(
|
||||
vfio_region_info,
|
||||
Option<File>,
|
||||
Vec<vfio_region_sparse_mmap_area>,
|
||||
),
|
||||
Error,
|
||||
> {
|
||||
// Retrieve the region info without capability
|
||||
let mut get_region_info = DeviceGetRegionInfo {
|
||||
header: Header {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::DeviceGetRegionInfo,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: std::mem::size_of::<DeviceGetRegionInfo>() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
region_info: vfio_region_info {
|
||||
argsz: size_of::<vfio_region_info>() as u32,
|
||||
index,
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
debug!("Command: {:?}", get_region_info);
|
||||
self.next_message_id += Wrapping(1);
|
||||
|
||||
self.stream
|
||||
.write_all(get_region_info.as_slice())
|
||||
.map_err(Error::StreamWrite)?;
|
||||
|
||||
let mut reply = DeviceGetRegionInfo::default();
|
||||
let (_, fd) = self
|
||||
.stream
|
||||
.recv_with_fd(reply.as_mut_slice())
|
||||
.map_err(Error::ReceiveWithFd)?;
|
||||
debug!("Reply: {:?}", reply);
|
||||
|
||||
// Retrieve the region info again with capabilities if needed
|
||||
if reply.region_info.argsz > std::mem::size_of::<vfio_region_info>() as u32 {
|
||||
get_region_info.region_info.argsz = reply.region_info.argsz;
|
||||
let get_region_info = DeviceGetRegionInfo {
|
||||
header: Header {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::DeviceGetRegionInfo,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: std::mem::size_of::<DeviceGetRegionInfo>() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
region_info: vfio_region_info {
|
||||
argsz: 1024, // Arbitrary max size
|
||||
index,
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
debug!("Command: {:?}", get_region_info);
|
||||
self.next_message_id += Wrapping(1);
|
||||
|
||||
@@ -547,94 +502,24 @@ impl Client {
|
||||
.map_err(Error::ReceiveWithFd)?;
|
||||
debug!("Reply: {:?}", reply);
|
||||
|
||||
let cap_size = reply.region_info.argsz - std::mem::size_of::<vfio_region_info>() as u32;
|
||||
assert_eq!(
|
||||
cap_size,
|
||||
reply.header.message_size - size_of::<DeviceGetRegionInfo>() as u32
|
||||
regions.push(Region {
|
||||
flags: reply.region_info.flags,
|
||||
index: reply.region_info.index,
|
||||
size: reply.region_info.size,
|
||||
file_offset: fd.map(|fd| FileOffset::new(fd, reply.region_info.offset)),
|
||||
});
|
||||
|
||||
// TODO: Handle region with capabilities
|
||||
let mut _cap_data = Vec::with_capacity(
|
||||
reply.header.message_size as usize - std::mem::size_of::<DeviceGetRegionInfo>(),
|
||||
);
|
||||
let mut cap_data = Vec::with_capacity(cap_size as usize);
|
||||
cap_data.resize(cap_data.capacity(), 0u8);
|
||||
_cap_data.resize(_cap_data.capacity(), 0u8);
|
||||
self.stream
|
||||
.read_exact(cap_data.as_mut_slice())
|
||||
.read_exact(_cap_data.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
|
||||
let sparse_areas = Self::parse_region_caps(&cap_data, &reply.region_info)?;
|
||||
|
||||
Ok((reply.region_info, fd, sparse_areas))
|
||||
} else {
|
||||
Ok((reply.region_info, fd, Vec::new()))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_region_caps(
|
||||
cap_data: &[u8],
|
||||
region_info: &vfio_region_info,
|
||||
) -> Result<Vec<vfio_region_sparse_mmap_area>, Error> {
|
||||
let mut sparse_areas: Vec<vfio_region_sparse_mmap_area> = Vec::new();
|
||||
|
||||
let cap_size = cap_data.len() as u32;
|
||||
let cap_header_size = size_of::<vfio_info_cap_header>() as u32;
|
||||
let mmap_cap_size = size_of::<vfio_region_info_cap_sparse_mmap>() as u32;
|
||||
let mmap_area_size = size_of::<vfio_region_sparse_mmap_area>() as u32;
|
||||
|
||||
let cap_data_ptr = cap_data.as_ptr() as *const u8;
|
||||
let mut region_info_offset = region_info.cap_offset;
|
||||
while region_info_offset != 0 {
|
||||
// calculate the offset from the begining of the cap_data based on the offset
|
||||
// that is relative to the begining of the VFIO region info structure
|
||||
let cap_offset = region_info_offset - size_of::<vfio_region_info>() as u32;
|
||||
if cap_offset + cap_header_size > cap_size {
|
||||
warn!(
|
||||
"Unexpected end of cap data: 'cap_offset + cap_header_size > cap_size' \
|
||||
cap_offset = {}, cap_header_size = {}, cap_size = {}",
|
||||
cap_offset, cap_header_size, cap_size
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// Safe because the `cap_data_ptr` is valid and the `cap_offset` is checked above
|
||||
let cap_ptr = unsafe { cap_data_ptr.offset(cap_offset as isize) };
|
||||
let cap_header = unsafe { &*(cap_ptr as *const vfio_info_cap_header) };
|
||||
match cap_header.id as u32 {
|
||||
VFIO_REGION_INFO_CAP_SPARSE_MMAP => {
|
||||
if cap_offset + mmap_cap_size > cap_size {
|
||||
warn!(
|
||||
"Unexpected end of cap data: 'cap_offset + mmap_cap_size > cap_size' \
|
||||
cap_offset = {}, mmap_cap_size = {}, cap_size = {}",
|
||||
cap_offset, mmap_cap_size, cap_size
|
||||
);
|
||||
break;
|
||||
}
|
||||
// Safe because the `cap_ptr` is valid and its size is also checked above
|
||||
let sparse_mmap = unsafe {
|
||||
&*(cap_ptr as *mut u8 as *const vfio_region_info_cap_sparse_mmap)
|
||||
};
|
||||
|
||||
let area_num = sparse_mmap.nr_areas;
|
||||
if cap_offset + mmap_cap_size + area_num * mmap_area_size > cap_size {
|
||||
warn!("Unexpected end of cap data: 'cap_offset + mmap_cap_size + area_num * mmap_area_size > cap_size' \
|
||||
cap_offset = {}, mmap_cap_size = {}, area_num = {}, mmap_area_size = {}, cap_size = {}",
|
||||
cap_offset, mmap_cap_size, area_num, mmap_area_size, cap_size);
|
||||
break;
|
||||
}
|
||||
// Safe because the `sparse_mmap` is valid and its size is also checked above
|
||||
let areas =
|
||||
unsafe { sparse_mmap.areas.as_slice(sparse_mmap.nr_areas as usize) };
|
||||
for area in areas.iter() {
|
||||
sparse_areas.push(*area);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
warn!(
|
||||
"Ignoring unsupported vfio region capability (id = '{}')",
|
||||
cap_header.id
|
||||
);
|
||||
}
|
||||
}
|
||||
region_info_offset = cap_header.next;
|
||||
}
|
||||
|
||||
Ok(sparse_areas)
|
||||
Ok(regions)
|
||||
}
|
||||
|
||||
pub fn region_read(&mut self, region: u32, offset: u64, data: &mut [u8]) -> Result<(), Error> {
|
||||
@@ -643,7 +528,7 @@ impl Client {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::RegionRead,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: size_of::<RegionAccess>() as u32,
|
||||
message_size: std::mem::size_of::<RegionAccess>() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
offset,
|
||||
@@ -671,7 +556,7 @@ impl Client {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::RegionWrite,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: (size_of::<RegionAccess>() + data.len()) as u32,
|
||||
message_size: (std::mem::size_of::<RegionAccess>() + data.len()) as u32,
|
||||
..Default::default()
|
||||
},
|
||||
offset,
|
||||
@@ -703,10 +588,10 @@ impl Client {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::GetIrqInfo,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: size_of::<GetIrqInfo>() as u32,
|
||||
message_size: std::mem::size_of::<GetIrqInfo>() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
argsz: (size_of::<GetIrqInfo>() - size_of::<Header>()) as u32,
|
||||
argsz: (std::mem::size_of::<GetIrqInfo>() - std::mem::size_of::<Header>()) as u32,
|
||||
flags: 0,
|
||||
index,
|
||||
count: 0,
|
||||
@@ -744,10 +629,10 @@ impl Client {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::SetIrqs,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: size_of::<SetIrqs>() as u32,
|
||||
message_size: std::mem::size_of::<SetIrqs>() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
argsz: (size_of::<SetIrqs>() - size_of::<Header>()) as u32,
|
||||
argsz: (std::mem::size_of::<SetIrqs>() - std::mem::size_of::<Header>()) as u32,
|
||||
flags,
|
||||
start,
|
||||
index,
|
||||
|
||||
@@ -8,9 +8,9 @@ license = "Apache-2.0"
|
||||
[dependencies]
|
||||
byteorder = "1.4.3"
|
||||
crc32c = "0.6.3"
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
remain = "0.2.3"
|
||||
libc = "0.2.123"
|
||||
log = "0.4.16"
|
||||
remain = "0.2.2"
|
||||
thiserror = "1.0"
|
||||
uuid = { version = "1.0.0", features = ["v4"] }
|
||||
uuid = { version = "0.8.2", features = ["v4"] }
|
||||
vmm-sys-util = ">=0.3.1"
|
||||
@@ -3,6 +3,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use std::result::Result;
|
||||
use uuid::Uuid;
|
||||
|
||||
macro_rules! div_round_up {
|
||||
@@ -17,7 +18,7 @@ mod vhdx_header;
|
||||
mod vhdx_io;
|
||||
mod vhdx_metadata;
|
||||
|
||||
pub(crate) fn uuid_from_guid(buf: &[u8]) -> Uuid {
|
||||
pub(crate) fn uuid_from_guid(buf: &[u8]) -> Result<Uuid, uuid::Error> {
|
||||
// The first 3 fields of UUID are stored in Big Endian format, and
|
||||
// the last 8 bytes are stored as byte array. Therefore, we read the
|
||||
// first 3 fields in Big Endian format instead of Little Endian.
|
||||
@@ -25,6 +26,6 @@ pub(crate) fn uuid_from_guid(buf: &[u8]) -> Uuid {
|
||||
BigEndian::read_u32(&buf[0..4]),
|
||||
BigEndian::read_u16(&buf[4..6]),
|
||||
BigEndian::read_u16(&buf[6..8]),
|
||||
buf[8..16].try_into().unwrap(),
|
||||
&buf[8..16],
|
||||
)
|
||||
}
|
||||
|
||||
@@ -339,7 +339,7 @@ impl RegionTableEntry {
|
||||
pub fn new(buffer: &[u8]) -> Result<RegionTableEntry> {
|
||||
let mut region_table_entry = unsafe { *(buffer.as_ptr() as *mut RegionTableEntry) };
|
||||
|
||||
let uuid = crate::uuid_from_guid(buffer);
|
||||
let uuid = crate::uuid_from_guid(buffer).map_err(VhdxHeaderError::InvalidUuid)?;
|
||||
region_table_entry.guid = uuid;
|
||||
|
||||
Ok(region_table_entry)
|
||||
|
||||
@@ -303,7 +303,7 @@ impl MetadataTableEntry {
|
||||
fn new(buffer: &[u8]) -> Result<MetadataTableEntry> {
|
||||
let mut metadata_table_entry = unsafe { *(buffer.as_ptr() as *mut MetadataTableEntry) };
|
||||
|
||||
let uuid = crate::uuid_from_guid(buffer);
|
||||
let uuid = crate::uuid_from_guid(buffer).map_err(VhdxMetadataError::InvalidUuid)?;
|
||||
metadata_table_entry.item_id = uuid;
|
||||
|
||||
if metadata_table_entry.length > METADATA_LENGTH_MAX {
|
||||
|
||||
@@ -6,18 +6,18 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
block_util = { path = "../block_util" }
|
||||
clap = { version = "3.1.18", features = ["wrap_help","cargo"] }
|
||||
clap = { version = "3.1.8", features = ["wrap_help","cargo"] }
|
||||
env_logger = "0.9.0"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.123"
|
||||
log = "0.4.16"
|
||||
option_parser = { path = "../option_parser" }
|
||||
qcow = { path = "../qcow" }
|
||||
vhost = { version = "0.4.0", features = ["vhost-user-slave"] }
|
||||
vhost-user-backend = { git = "https://github.com/rust-vmm/vhost-user-backend", rev = "14f58eda14076e973704d4f904850be1146fbb05" }
|
||||
vhost-user-backend = "0.3.0"
|
||||
virtio-bindings = "0.1.0"
|
||||
vm-memory = "0.8.0"
|
||||
vm-memory = "0.7.0"
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.1.18", features = ["cargo"] }
|
||||
clap = { version = "3.1.8", features = ["cargo"] }
|
||||
|
||||
@@ -5,18 +5,18 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.1.18", features = ["wrap_help","cargo"] }
|
||||
clap = { version = "3.1.8", features = ["wrap_help","cargo"] }
|
||||
env_logger = "0.9.0"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.123"
|
||||
log = "0.4.16"
|
||||
net_util = { path = "../net_util" }
|
||||
option_parser = { path = "../option_parser" }
|
||||
vhost = { version = "0.4.0", features = ["vhost-user-slave"] }
|
||||
vhost-user-backend = { git = "https://github.com/rust-vmm/vhost-user-backend", rev = "14f58eda14076e973704d4f904850be1146fbb05" }
|
||||
vhost-user-backend = "0.3.0"
|
||||
virtio-bindings = "0.1.0"
|
||||
vm-memory = "0.8.0"
|
||||
vm-memory = "0.7.0"
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.1.18", features = ["cargo"] }
|
||||
clap = { version = "3.1.8", features = ["cargo"] }
|
||||
|
||||
@@ -9,31 +9,32 @@ default = []
|
||||
mshv = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.56"
|
||||
arc-swap = "1.5.0"
|
||||
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"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.123"
|
||||
log = "0.4.16"
|
||||
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.137", features=["derive"] }
|
||||
serde_json = "1.0.81"
|
||||
thiserror = "1.0.31"
|
||||
serde = "1.0.136"
|
||||
serde_derive = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
thiserror = "1.0.30"
|
||||
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.3.0"
|
||||
virtio-queue = "0.2.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-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::mem::size_of;
|
||||
use std::ops::Bound::Included;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::result;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::{Arc, Barrier, Mutex, RwLock};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
@@ -69,6 +69,7 @@ const VIRTIO_IOMMU_F_BYPASS: u32 = 3;
|
||||
const VIRTIO_IOMMU_F_PROBE: u32 = 4;
|
||||
#[allow(unused)]
|
||||
const VIRTIO_IOMMU_F_MMIO: u32 = 5;
|
||||
#[allow(unused)]
|
||||
const VIRTIO_IOMMU_F_BYPASS_CONFIG: u32 = 6;
|
||||
|
||||
// Support 2MiB and 4KiB page sizes.
|
||||
@@ -149,12 +150,9 @@ struct VirtioIommuReqTail {
|
||||
struct VirtioIommuReqAttach {
|
||||
domain: u32,
|
||||
endpoint: u32,
|
||||
flags: u32,
|
||||
_reserved: [u8; 4],
|
||||
_reserved: [u8; 8],
|
||||
}
|
||||
|
||||
const VIRTIO_IOMMU_ATTACH_F_BYPASS: u32 = 1;
|
||||
|
||||
/// DETACH request
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[repr(packed)]
|
||||
@@ -301,16 +299,8 @@ enum Error {
|
||||
InvalidDetachRequest,
|
||||
/// Guest sent us invalid MAP request.
|
||||
InvalidMapRequest,
|
||||
/// Invalid to map because the domain is in bypass mode.
|
||||
InvalidMapRequestBypassDomain,
|
||||
/// Invalid to map because the domain is missing.
|
||||
InvalidMapRequestMissingDomain,
|
||||
/// Guest sent us invalid UNMAP request.
|
||||
InvalidUnmapRequest,
|
||||
/// Invalid to unmap because the domain is in bypass mode.
|
||||
InvalidUnmapRequestBypassDomain,
|
||||
/// Invalid to unmap because the domain is missing.
|
||||
InvalidUnmapRequestMissingDomain,
|
||||
/// Guest sent us invalid PROBE request.
|
||||
InvalidProbeRequest,
|
||||
/// Failed to performing external mapping.
|
||||
@@ -331,19 +321,7 @@ impl Display for Error {
|
||||
InvalidAttachRequest => write!(f, "invalid attach request"),
|
||||
InvalidDetachRequest => write!(f, "invalid detach request"),
|
||||
InvalidMapRequest => write!(f, "invalid map request"),
|
||||
InvalidMapRequestBypassDomain => {
|
||||
write!(f, "invalid map request because domain in bypass mode")
|
||||
}
|
||||
InvalidMapRequestMissingDomain => {
|
||||
write!(f, "invalid map request because missing domain")
|
||||
}
|
||||
InvalidUnmapRequest => write!(f, "invalid unmap request"),
|
||||
InvalidUnmapRequestBypassDomain => {
|
||||
write!(f, "invalid unmap request because domain in bypass mode")
|
||||
}
|
||||
InvalidUnmapRequestMissingDomain => {
|
||||
write!(f, "invalid unmap request because missing domain")
|
||||
}
|
||||
InvalidProbeRequest => write!(f, "invalid probe request"),
|
||||
UnexpectedReadOnlyDescriptor => write!(f, "unexpected read-only descriptor"),
|
||||
UnexpectedWriteOnlyDescriptor => write!(f, "unexpected write-only descriptor"),
|
||||
@@ -368,6 +346,7 @@ impl Request {
|
||||
desc_chain: &mut DescriptorChain<GuestMemoryLoadGuard<GuestMemoryMmap>>,
|
||||
mapping: &Arc<IommuMapping>,
|
||||
ext_mapping: &BTreeMap<u32, Arc<dyn ExternalDmaMapping>>,
|
||||
ext_domain_mapping: &mut BTreeMap<u32, Arc<dyn ExternalDmaMapping>>,
|
||||
msi_iova_space: (u64, u64),
|
||||
) -> result::Result<usize, Error> {
|
||||
let desc = desc_chain
|
||||
@@ -403,230 +382,162 @@ impl Request {
|
||||
|
||||
// Create the reply
|
||||
let mut reply: Vec<u8> = Vec::new();
|
||||
let mut status = VIRTIO_IOMMU_S_OK;
|
||||
let mut hdr_len = 0;
|
||||
|
||||
let result = (|| {
|
||||
match req_head.type_ {
|
||||
VIRTIO_IOMMU_T_ATTACH => {
|
||||
if desc_size_left != size_of::<VirtioIommuReqAttach>() {
|
||||
status = VIRTIO_IOMMU_S_INVAL;
|
||||
return Err(Error::InvalidAttachRequest);
|
||||
}
|
||||
|
||||
let req: VirtioIommuReqAttach = desc_chain
|
||||
.memory()
|
||||
.read_obj(req_addr as GuestAddress)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
debug!("Attach request {:?}", req);
|
||||
|
||||
// Copy the value to use it as a proper reference.
|
||||
let domain_id = req.domain;
|
||||
let endpoint = req.endpoint;
|
||||
let bypass =
|
||||
(req.flags & VIRTIO_IOMMU_ATTACH_F_BYPASS) == VIRTIO_IOMMU_ATTACH_F_BYPASS;
|
||||
|
||||
// Add endpoint associated with specific domain
|
||||
mapping
|
||||
.endpoints
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(endpoint, domain_id);
|
||||
|
||||
// Add new domain with no mapping if the entry didn't exist yet
|
||||
let mut domains = mapping.domains.write().unwrap();
|
||||
let domain = Domain {
|
||||
mappings: BTreeMap::new(),
|
||||
bypass,
|
||||
};
|
||||
domains.entry(domain_id).or_insert_with(|| domain);
|
||||
let hdr_len = match req_head.type_ {
|
||||
VIRTIO_IOMMU_T_ATTACH => {
|
||||
if desc_size_left != size_of::<VirtioIommuReqAttach>() {
|
||||
return Err(Error::InvalidAttachRequest);
|
||||
}
|
||||
VIRTIO_IOMMU_T_DETACH => {
|
||||
if desc_size_left != size_of::<VirtioIommuReqDetach>() {
|
||||
status = VIRTIO_IOMMU_S_INVAL;
|
||||
return Err(Error::InvalidDetachRequest);
|
||||
}
|
||||
|
||||
let req: VirtioIommuReqDetach = desc_chain
|
||||
.memory()
|
||||
.read_obj(req_addr as GuestAddress)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
debug!("Detach request {:?}", req);
|
||||
let req: VirtioIommuReqAttach = desc_chain
|
||||
.memory()
|
||||
.read_obj(req_addr as GuestAddress)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
debug!("Attach request {:?}", req);
|
||||
|
||||
// Copy the value to use it as a proper reference.
|
||||
let domain_id = req.domain;
|
||||
let endpoint = req.endpoint;
|
||||
// Copy the value to use it as a proper reference.
|
||||
let domain = req.domain;
|
||||
let endpoint = req.endpoint;
|
||||
|
||||
// Remove endpoint associated with specific domain
|
||||
mapping.endpoints.write().unwrap().remove(&endpoint);
|
||||
// Add endpoint associated with specific domain
|
||||
mapping.endpoints.write().unwrap().insert(endpoint, domain);
|
||||
|
||||
// After all endpoints have been successfully detached from a
|
||||
// domain, the domain can be removed. This means we must remove
|
||||
// the mappings associated with this domain.
|
||||
if mapping
|
||||
.endpoints
|
||||
.write()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|(_, &d)| d == domain_id)
|
||||
.count()
|
||||
== 0
|
||||
{
|
||||
mapping.domains.write().unwrap().remove(&domain_id);
|
||||
}
|
||||
// If the endpoint is part of the list of devices with an
|
||||
// external mapping, insert a new entry for the corresponding
|
||||
// domain, with the same reference to the trait.
|
||||
if let Some(map) = ext_mapping.get(&endpoint) {
|
||||
ext_domain_mapping.insert(domain, map.clone());
|
||||
}
|
||||
VIRTIO_IOMMU_T_MAP => {
|
||||
if desc_size_left != size_of::<VirtioIommuReqMap>() {
|
||||
status = VIRTIO_IOMMU_S_INVAL;
|
||||
return Err(Error::InvalidMapRequest);
|
||||
}
|
||||
|
||||
let req: VirtioIommuReqMap = desc_chain
|
||||
.memory()
|
||||
.read_obj(req_addr as GuestAddress)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
debug!("Map request {:?}", req);
|
||||
// Add new domain with no mapping if the entry didn't exist yet
|
||||
let mut mappings = mapping.mappings.write().unwrap();
|
||||
mappings.entry(domain).or_insert_with(BTreeMap::new);
|
||||
|
||||
// Copy the value to use it as a proper reference.
|
||||
let domain_id = req.domain;
|
||||
|
||||
if let Some(domain) = mapping.domains.read().unwrap().get(&domain_id) {
|
||||
if domain.bypass {
|
||||
status = VIRTIO_IOMMU_S_INVAL;
|
||||
return Err(Error::InvalidMapRequestBypassDomain);
|
||||
}
|
||||
} else {
|
||||
status = VIRTIO_IOMMU_S_INVAL;
|
||||
return Err(Error::InvalidMapRequestMissingDomain);
|
||||
}
|
||||
|
||||
// Find the list of endpoints attached to the given domain.
|
||||
let endpoints: Vec<u32> = mapping
|
||||
.endpoints
|
||||
.write()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|(_, &d)| d == domain_id)
|
||||
.map(|(&e, _)| e)
|
||||
.collect();
|
||||
|
||||
// Trigger external mapping if necessary.
|
||||
for endpoint in endpoints {
|
||||
if let Some(ext_map) = ext_mapping.get(&endpoint) {
|
||||
let size = req.virt_end - req.virt_start + 1;
|
||||
ext_map
|
||||
.map(req.virt_start, req.phys_start, size)
|
||||
.map_err(Error::ExternalMapping)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new mapping associated with the domain
|
||||
mapping
|
||||
.domains
|
||||
.write()
|
||||
.unwrap()
|
||||
.get_mut(&domain_id)
|
||||
.unwrap()
|
||||
.mappings
|
||||
.insert(
|
||||
req.virt_start,
|
||||
Mapping {
|
||||
gpa: req.phys_start,
|
||||
size: req.virt_end - req.virt_start + 1,
|
||||
},
|
||||
);
|
||||
}
|
||||
VIRTIO_IOMMU_T_UNMAP => {
|
||||
if desc_size_left != size_of::<VirtioIommuReqUnmap>() {
|
||||
status = VIRTIO_IOMMU_S_INVAL;
|
||||
return Err(Error::InvalidUnmapRequest);
|
||||
}
|
||||
|
||||
let req: VirtioIommuReqUnmap = desc_chain
|
||||
.memory()
|
||||
.read_obj(req_addr as GuestAddress)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
debug!("Unmap request {:?}", req);
|
||||
|
||||
// Copy the value to use it as a proper reference.
|
||||
let domain_id = req.domain;
|
||||
let virt_start = req.virt_start;
|
||||
|
||||
if let Some(domain) = mapping.domains.read().unwrap().get(&domain_id) {
|
||||
if domain.bypass {
|
||||
status = VIRTIO_IOMMU_S_INVAL;
|
||||
return Err(Error::InvalidUnmapRequestBypassDomain);
|
||||
}
|
||||
} else {
|
||||
status = VIRTIO_IOMMU_S_INVAL;
|
||||
return Err(Error::InvalidUnmapRequestMissingDomain);
|
||||
}
|
||||
|
||||
// Find the list of endpoints attached to the given domain.
|
||||
let endpoints: Vec<u32> = mapping
|
||||
.endpoints
|
||||
.write()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|(_, &d)| d == domain_id)
|
||||
.map(|(&e, _)| e)
|
||||
.collect();
|
||||
|
||||
// Trigger external unmapping if necessary.
|
||||
for endpoint in endpoints {
|
||||
if let Some(ext_map) = ext_mapping.get(&endpoint) {
|
||||
let size = req.virt_end - virt_start + 1;
|
||||
ext_map
|
||||
.unmap(virt_start, size)
|
||||
.map_err(Error::ExternalUnmapping)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove mapping associated with the domain
|
||||
mapping
|
||||
.domains
|
||||
.write()
|
||||
.unwrap()
|
||||
.get_mut(&domain_id)
|
||||
.unwrap()
|
||||
.mappings
|
||||
.remove(&virt_start);
|
||||
}
|
||||
VIRTIO_IOMMU_T_PROBE => {
|
||||
if desc_size_left != size_of::<VirtioIommuReqProbe>() {
|
||||
status = VIRTIO_IOMMU_S_INVAL;
|
||||
return Err(Error::InvalidProbeRequest);
|
||||
}
|
||||
|
||||
let req: VirtioIommuReqProbe = desc_chain
|
||||
.memory()
|
||||
.read_obj(req_addr as GuestAddress)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
debug!("Probe request {:?}", req);
|
||||
|
||||
let probe_prop = VirtioIommuProbeProperty {
|
||||
type_: VIRTIO_IOMMU_PROBE_T_RESV_MEM,
|
||||
length: size_of::<VirtioIommuProbeResvMem>() as u16,
|
||||
};
|
||||
reply.extend_from_slice(probe_prop.as_slice());
|
||||
|
||||
let resv_mem = VirtioIommuProbeResvMem {
|
||||
subtype: VIRTIO_IOMMU_RESV_MEM_T_MSI,
|
||||
start: msi_iova_start,
|
||||
end: msi_iova_end,
|
||||
..Default::default()
|
||||
};
|
||||
reply.extend_from_slice(resv_mem.as_slice());
|
||||
|
||||
hdr_len = PROBE_PROP_SIZE;
|
||||
}
|
||||
_ => {
|
||||
status = VIRTIO_IOMMU_S_INVAL;
|
||||
return Err(Error::InvalidRequest);
|
||||
}
|
||||
0
|
||||
}
|
||||
Ok(())
|
||||
})();
|
||||
VIRTIO_IOMMU_T_DETACH => {
|
||||
if desc_size_left != size_of::<VirtioIommuReqDetach>() {
|
||||
return Err(Error::InvalidDetachRequest);
|
||||
}
|
||||
|
||||
let req: VirtioIommuReqDetach = desc_chain
|
||||
.memory()
|
||||
.read_obj(req_addr as GuestAddress)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
debug!("Detach request {:?}", req);
|
||||
|
||||
// Copy the value to use it as a proper reference.
|
||||
let domain = req.domain;
|
||||
let endpoint = req.endpoint;
|
||||
|
||||
// If the endpoint is part of the list of devices with an
|
||||
// external mapping, remove the entry for the corresponding
|
||||
// domain.
|
||||
if ext_mapping.contains_key(&endpoint) {
|
||||
ext_domain_mapping.remove(&domain);
|
||||
}
|
||||
|
||||
// Remove endpoint associated with specific domain
|
||||
mapping.endpoints.write().unwrap().remove(&endpoint);
|
||||
|
||||
0
|
||||
}
|
||||
VIRTIO_IOMMU_T_MAP => {
|
||||
if desc_size_left != size_of::<VirtioIommuReqMap>() {
|
||||
return Err(Error::InvalidMapRequest);
|
||||
}
|
||||
|
||||
let req: VirtioIommuReqMap = desc_chain
|
||||
.memory()
|
||||
.read_obj(req_addr as GuestAddress)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
debug!("Map request {:?}", req);
|
||||
|
||||
// Copy the value to use it as a proper reference.
|
||||
let domain = req.domain;
|
||||
|
||||
// Trigger external mapping if necessary.
|
||||
if let Some(ext_map) = ext_domain_mapping.get(&domain) {
|
||||
let size = req.virt_end - req.virt_start + 1;
|
||||
ext_map
|
||||
.map(req.virt_start, req.phys_start, size)
|
||||
.map_err(Error::ExternalMapping)?;
|
||||
}
|
||||
|
||||
// Add new mapping associated with the domain
|
||||
if let Some(entry) = mapping.mappings.write().unwrap().get_mut(&domain) {
|
||||
entry.insert(
|
||||
req.virt_start,
|
||||
Mapping {
|
||||
gpa: req.phys_start,
|
||||
size: req.virt_end - req.virt_start + 1,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return Err(Error::InvalidMapRequest);
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
VIRTIO_IOMMU_T_UNMAP => {
|
||||
if desc_size_left != size_of::<VirtioIommuReqUnmap>() {
|
||||
return Err(Error::InvalidUnmapRequest);
|
||||
}
|
||||
|
||||
let req: VirtioIommuReqUnmap = desc_chain
|
||||
.memory()
|
||||
.read_obj(req_addr as GuestAddress)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
debug!("Unmap request {:?}", req);
|
||||
|
||||
// Copy the value to use it as a proper reference.
|
||||
let domain = req.domain;
|
||||
let virt_start = req.virt_start;
|
||||
|
||||
// Trigger external unmapping if necessary.
|
||||
if let Some(ext_map) = ext_domain_mapping.get(&domain) {
|
||||
let size = req.virt_end - virt_start + 1;
|
||||
ext_map
|
||||
.unmap(virt_start, size)
|
||||
.map_err(Error::ExternalUnmapping)?;
|
||||
}
|
||||
|
||||
// Add new mapping associated with the domain
|
||||
if let Some(entry) = mapping.mappings.write().unwrap().get_mut(&domain) {
|
||||
entry.remove(&virt_start);
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
VIRTIO_IOMMU_T_PROBE => {
|
||||
if desc_size_left != size_of::<VirtioIommuReqProbe>() {
|
||||
return Err(Error::InvalidProbeRequest);
|
||||
}
|
||||
|
||||
let req: VirtioIommuReqProbe = desc_chain
|
||||
.memory()
|
||||
.read_obj(req_addr as GuestAddress)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
debug!("Probe request {:?}", req);
|
||||
|
||||
let probe_prop = VirtioIommuProbeProperty {
|
||||
type_: VIRTIO_IOMMU_PROBE_T_RESV_MEM,
|
||||
length: size_of::<VirtioIommuProbeResvMem>() as u16,
|
||||
};
|
||||
reply.extend_from_slice(probe_prop.as_slice());
|
||||
|
||||
let resv_mem = VirtioIommuProbeResvMem {
|
||||
subtype: VIRTIO_IOMMU_RESV_MEM_T_MSI,
|
||||
start: msi_iova_start,
|
||||
end: msi_iova_end,
|
||||
..Default::default()
|
||||
};
|
||||
reply.extend_from_slice(resv_mem.as_slice());
|
||||
|
||||
PROBE_PROP_SIZE
|
||||
}
|
||||
_ => return Err(Error::InvalidRequest),
|
||||
};
|
||||
|
||||
let status_desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
|
||||
|
||||
@@ -640,21 +551,16 @@ impl Request {
|
||||
}
|
||||
|
||||
let tail = VirtioIommuReqTail {
|
||||
status,
|
||||
status: VIRTIO_IOMMU_S_OK,
|
||||
..Default::default()
|
||||
};
|
||||
reply.extend_from_slice(tail.as_slice());
|
||||
|
||||
// Make sure we return the result of the request to the guest before
|
||||
// we return a potential error internally.
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_slice(reply.as_slice(), status_desc.addr())
|
||||
.map_err(Error::GuestMemory)?;
|
||||
|
||||
// Return the error if the result was not Ok().
|
||||
result?;
|
||||
|
||||
Ok((hdr_len as usize) + size_of::<VirtioIommuReqTail>())
|
||||
}
|
||||
}
|
||||
@@ -667,6 +573,7 @@ struct IommuEpollHandler {
|
||||
pause_evt: EventFd,
|
||||
mapping: Arc<IommuMapping>,
|
||||
ext_mapping: Arc<Mutex<BTreeMap<u32, Arc<dyn ExternalDmaMapping>>>>,
|
||||
ext_domain_mapping: BTreeMap<u32, Arc<dyn ExternalDmaMapping>>,
|
||||
msi_iova_space: (u64, u64),
|
||||
}
|
||||
|
||||
@@ -679,6 +586,7 @@ impl IommuEpollHandler {
|
||||
&mut desc_chain,
|
||||
&self.mapping,
|
||||
&self.ext_mapping.lock().unwrap(),
|
||||
&mut self.ext_domain_mapping,
|
||||
self.msi_iova_space,
|
||||
) {
|
||||
Ok(len) => len as u32,
|
||||
@@ -766,43 +674,25 @@ struct Mapping {
|
||||
size: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Domain {
|
||||
mappings: BTreeMap<u64, Mapping>,
|
||||
bypass: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IommuMapping {
|
||||
// Domain related to an endpoint.
|
||||
endpoints: Arc<RwLock<BTreeMap<u32, u32>>>,
|
||||
// Information related to each domain.
|
||||
domains: Arc<RwLock<BTreeMap<u32, Domain>>>,
|
||||
// Global flag indicating if endpoints that are not attached to any domain
|
||||
// are in bypass mode.
|
||||
bypass: AtomicBool,
|
||||
// List of mappings per domain.
|
||||
mappings: Arc<RwLock<BTreeMap<u32, BTreeMap<u64, Mapping>>>>,
|
||||
}
|
||||
|
||||
impl DmaRemapping for IommuMapping {
|
||||
fn translate_gva(&self, id: u32, addr: u64) -> std::result::Result<u64, std::io::Error> {
|
||||
debug!("Translate GVA addr 0x{:x}", addr);
|
||||
if let Some(domain_id) = self.endpoints.read().unwrap().get(&id) {
|
||||
if let Some(domain) = self.domains.read().unwrap().get(domain_id) {
|
||||
// Directly return identity mapping in case the domain is in
|
||||
// bypass mode.
|
||||
if domain.bypass {
|
||||
return Ok(addr);
|
||||
}
|
||||
|
||||
if let Some(domain) = self.endpoints.read().unwrap().get(&id) {
|
||||
if let Some(mapping) = self.mappings.read().unwrap().get(domain) {
|
||||
let range_start = if VIRTIO_IOMMU_PAGE_SIZE_MASK > addr {
|
||||
0
|
||||
} else {
|
||||
addr - VIRTIO_IOMMU_PAGE_SIZE_MASK
|
||||
};
|
||||
for (&key, &value) in domain
|
||||
.mappings
|
||||
.range((Included(&range_start), Included(&addr)))
|
||||
{
|
||||
for (&key, &value) in mapping.range((Included(&range_start), Included(&addr))) {
|
||||
if addr >= key && addr < key + value.size {
|
||||
let new_addr = addr - key + value.gpa;
|
||||
debug!("Into GPA addr 0x{:x}", new_addr);
|
||||
@@ -810,8 +700,6 @@ impl DmaRemapping for IommuMapping {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if self.bypass.load(Ordering::Acquire) {
|
||||
return Ok(addr);
|
||||
}
|
||||
|
||||
Err(io::Error::new(
|
||||
@@ -822,15 +710,9 @@ impl DmaRemapping for IommuMapping {
|
||||
|
||||
fn translate_gpa(&self, id: u32, addr: u64) -> std::result::Result<u64, std::io::Error> {
|
||||
debug!("Translate GPA addr 0x{:x}", addr);
|
||||
if let Some(domain_id) = self.endpoints.read().unwrap().get(&id) {
|
||||
if let Some(domain) = self.domains.read().unwrap().get(domain_id) {
|
||||
// Directly return identity mapping in case the domain is in
|
||||
// bypass mode.
|
||||
if domain.bypass {
|
||||
return Ok(addr);
|
||||
}
|
||||
|
||||
for (&key, &value) in domain.mappings.iter() {
|
||||
if let Some(domain) = self.endpoints.read().unwrap().get(&id) {
|
||||
if let Some(mapping) = self.mappings.read().unwrap().get(domain) {
|
||||
for (&key, &value) in mapping.iter() {
|
||||
if addr >= value.gpa && addr < value.gpa + value.size {
|
||||
let new_addr = addr - value.gpa + key;
|
||||
debug!("Into GVA addr 0x{:x}", new_addr);
|
||||
@@ -838,8 +720,6 @@ impl DmaRemapping for IommuMapping {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if self.bypass.load(Ordering::Acquire) {
|
||||
return Ok(addr);
|
||||
}
|
||||
|
||||
Err(io::Error::new(
|
||||
@@ -881,15 +761,12 @@ pub struct Iommu {
|
||||
msi_iova_space: (u64, u64),
|
||||
}
|
||||
|
||||
type EndpointsState = Vec<(u32, u32)>;
|
||||
type DomainsState = Vec<(u32, (Vec<(u64, Mapping)>, bool))>;
|
||||
|
||||
#[derive(Versionize)]
|
||||
struct IommuState {
|
||||
avail_features: u64,
|
||||
acked_features: u64,
|
||||
endpoints: EndpointsState,
|
||||
domains: DomainsState,
|
||||
endpoints: Vec<(u32, u32)>,
|
||||
mappings: Vec<(u32, Vec<(u64, Mapping)>)>,
|
||||
}
|
||||
|
||||
impl VersionMapped for IommuState {}
|
||||
@@ -909,8 +786,7 @@ impl Iommu {
|
||||
|
||||
let mapping = Arc::new(IommuMapping {
|
||||
endpoints: Arc::new(RwLock::new(BTreeMap::new())),
|
||||
domains: Arc::new(RwLock::new(BTreeMap::new())),
|
||||
bypass: AtomicBool::new(true),
|
||||
mappings: Arc::new(RwLock::new(BTreeMap::new())),
|
||||
});
|
||||
|
||||
Ok((
|
||||
@@ -921,8 +797,7 @@ impl Iommu {
|
||||
queue_sizes: QUEUE_SIZES.to_vec(),
|
||||
avail_features: 1u64 << VIRTIO_F_VERSION_1
|
||||
| 1u64 << VIRTIO_IOMMU_F_MAP_UNMAP
|
||||
| 1u64 << VIRTIO_IOMMU_F_PROBE
|
||||
| 1u64 << VIRTIO_IOMMU_F_BYPASS_CONFIG,
|
||||
| 1u64 << VIRTIO_IOMMU_F_PROBE,
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
..Default::default()
|
||||
},
|
||||
@@ -949,14 +824,14 @@ impl Iommu {
|
||||
.clone()
|
||||
.into_iter()
|
||||
.collect(),
|
||||
domains: self
|
||||
mappings: self
|
||||
.mapping
|
||||
.domains
|
||||
.mappings
|
||||
.read()
|
||||
.unwrap()
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, (v.mappings.into_iter().collect(), v.bypass)))
|
||||
.map(|(k, v)| (k, v.into_iter().collect()))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
@@ -965,36 +840,14 @@ impl Iommu {
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
*(self.mapping.endpoints.write().unwrap()) = state.endpoints.clone().into_iter().collect();
|
||||
*(self.mapping.domains.write().unwrap()) = state
|
||||
.domains
|
||||
*(self.mapping.mappings.write().unwrap()) = state
|
||||
.mappings
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|(k, v)| {
|
||||
(
|
||||
k,
|
||||
Domain {
|
||||
mappings: v.0.into_iter().collect(),
|
||||
bypass: v.1,
|
||||
},
|
||||
)
|
||||
})
|
||||
.map(|(k, v)| (k, v.into_iter().collect()))
|
||||
.collect();
|
||||
}
|
||||
|
||||
fn update_bypass(&mut self) {
|
||||
// Use bypass from config if VIRTIO_IOMMU_F_BYPASS_CONFIG has been negotiated
|
||||
if !self
|
||||
.common
|
||||
.feature_acked(VIRTIO_IOMMU_F_BYPASS_CONFIG.into())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let bypass = self.config.bypass == 1;
|
||||
info!("Updating bypass mode to {}", bypass);
|
||||
self.mapping.bypass.store(bypass, Ordering::Release);
|
||||
}
|
||||
|
||||
pub fn add_external_mapping(&mut self, device_id: u32, mapping: Arc<dyn ExternalDmaMapping>) {
|
||||
self.ext_mapping.lock().unwrap().insert(device_id, mapping);
|
||||
}
|
||||
@@ -1030,24 +883,6 @@ impl VirtioDevice for Iommu {
|
||||
self.read_config_from_slice(self.config.as_slice(), offset, data);
|
||||
}
|
||||
|
||||
fn write_config(&mut self, offset: u64, data: &[u8]) {
|
||||
// The "bypass" field is the only mutable field
|
||||
let bypass_offset =
|
||||
(&self.config.bypass as *const _ as u64) - (&self.config as *const _ as u64);
|
||||
if offset != bypass_offset || data.len() != std::mem::size_of_val(&self.config.bypass) {
|
||||
error!(
|
||||
"Attempt to write to read-only field: offset {:x} length {}",
|
||||
offset,
|
||||
data.len()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
self.config.bypass = data[0];
|
||||
|
||||
self.update_bypass();
|
||||
}
|
||||
|
||||
fn activate(
|
||||
&mut self,
|
||||
_mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
@@ -1065,6 +900,7 @@ impl VirtioDevice for Iommu {
|
||||
pause_evt,
|
||||
mapping: self.mapping.clone(),
|
||||
ext_mapping: self.ext_mapping.clone(),
|
||||
ext_domain_mapping: BTreeMap::new(),
|
||||
msi_iova_space: self.msi_iova_space,
|
||||
};
|
||||
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
extern crate event_monitor;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryInto;
|
||||
use std::io;
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ pub enum Thread {
|
||||
/// [`SeccompCondition`]: struct.SeccompCondition.html
|
||||
/// [`SeccompRule`]: struct.SeccompRule.html
|
||||
macro_rules! and {
|
||||
($($x:expr,)*) => (SeccompRule::new(vec![$($x),*]).unwrap());
|
||||
($($x:expr),*) => (SeccompRule::new(vec![$($x),*]).unwrap())
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,8 @@ use vm_device::dma_mapping::ExternalDmaMapping;
|
||||
use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig,
|
||||
};
|
||||
use vm_device::{BusDevice, Resource};
|
||||
use vm_memory::{Address, ByteValued, GuestAddress, GuestMemoryAtomic, Le32};
|
||||
use vm_device::BusDevice;
|
||||
use vm_memory::{Address, ByteValued, GuestAddress, GuestMemoryAtomic, GuestUsize, Le32};
|
||||
use vm_migration::{
|
||||
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, VersionMapped,
|
||||
};
|
||||
@@ -260,8 +260,6 @@ const MSIX_PBA_BAR_OFFSET: u64 = 0x48000;
|
||||
const MSIX_PBA_SIZE: u64 = 0x800;
|
||||
// The BAR size must be a power of 2.
|
||||
const CAPABILITY_BAR_SIZE: u64 = 0x80000;
|
||||
const VIRTIO_COMMON_BAR_INDEX: usize = 0;
|
||||
const VIRTIO_SHM_BAR_INDEX: usize = 2;
|
||||
|
||||
const NOTIFY_OFF_MULTIPLIER: u32 = 4; // A dword per notification address.
|
||||
|
||||
@@ -320,6 +318,7 @@ pub struct VirtioPciDevice {
|
||||
|
||||
// Settings PCI BAR
|
||||
settings_bar: u8,
|
||||
settings_bar_addr: Option<GuestAddress>,
|
||||
|
||||
// Whether to use 64-bit bar location or 32-bit
|
||||
use_64bit_bar: bool,
|
||||
@@ -333,7 +332,7 @@ pub struct VirtioPciDevice {
|
||||
cap_pci_cfg_info: VirtioPciCfgCapInfo,
|
||||
|
||||
// Details of bar regions to free
|
||||
bar_regions: Vec<PciBarConfiguration>,
|
||||
bar_regions: Vec<(GuestAddress, GuestUsize, PciBarRegionType)>,
|
||||
|
||||
// EventFd to signal on to request activation
|
||||
activate_evt: EventFd,
|
||||
@@ -453,6 +452,7 @@ impl VirtioPciDevice {
|
||||
queue_evts,
|
||||
memory: Some(memory),
|
||||
settings_bar: 0,
|
||||
settings_bar_addr: None,
|
||||
use_64bit_bar,
|
||||
interrupt_source_group,
|
||||
cap_pci_cfg_info: VirtioPciCfgCapInfo::default(),
|
||||
@@ -542,6 +542,12 @@ impl VirtioPciDevice {
|
||||
self.common_config.driver_status == DEVICE_INIT as u8
|
||||
}
|
||||
|
||||
// This function is used by the caller to provide the expected base address
|
||||
// for the virtio-pci configuration BAR.
|
||||
pub fn set_config_bar_addr(&mut self, bar_addr: u64) {
|
||||
self.settings_bar_addr = Some(GuestAddress(bar_addr));
|
||||
}
|
||||
|
||||
pub fn config_bar_addr(&self) -> u64 {
|
||||
self.configuration.get_bar_addr(self.settings_bar as usize)
|
||||
}
|
||||
@@ -672,8 +678,7 @@ impl VirtioPciDevice {
|
||||
let mem = self.memory.as_ref().unwrap().clone();
|
||||
let mut device = self.device.lock().unwrap();
|
||||
let mut queue_evts = Vec::new();
|
||||
let mut queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>> =
|
||||
self.queues.iter().map(vm_virtio::clone_queue).collect();
|
||||
let mut queues = self.queues.clone();
|
||||
queues.retain(|q| q.state.ready);
|
||||
for (i, queue) in queues.iter().enumerate() {
|
||||
queue_evts.push(self.queue_evts[i].try_clone().unwrap());
|
||||
@@ -842,39 +847,24 @@ impl PciDevice for VirtioPciDevice {
|
||||
&mut self,
|
||||
allocator: &Arc<Mutex<SystemAllocator>>,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
resources: Option<Vec<Resource>>,
|
||||
) -> std::result::Result<Vec<PciBarConfiguration>, PciDeviceError> {
|
||||
let mut bars = Vec::new();
|
||||
) -> std::result::Result<Vec<(GuestAddress, GuestUsize, PciBarRegionType)>, PciDeviceError>
|
||||
{
|
||||
let mut ranges = Vec::new();
|
||||
let device_clone = self.device.clone();
|
||||
let device = device_clone.lock().unwrap();
|
||||
|
||||
let mut settings_bar_addr = None;
|
||||
if let Some(resources) = &resources {
|
||||
for resource in resources {
|
||||
if let Resource::PciBar { index, base, .. } = resource {
|
||||
if *index == VIRTIO_COMMON_BAR_INDEX {
|
||||
settings_bar_addr = Some(GuestAddress(*base));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Error out if no resource was matching the BAR id.
|
||||
if settings_bar_addr.is_none() {
|
||||
return Err(PciDeviceError::MissingResource);
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate the virtio-pci capability BAR.
|
||||
// See http://docs.oasis-open.org/virtio/virtio/v1.0/cs04/virtio-v1.0-cs04.html#x1-740004
|
||||
let (virtio_pci_bar_addr, region_type) = if self.use_64bit_bar {
|
||||
let region_type = PciBarRegionType::Memory64BitRegion;
|
||||
let addr = mmio_allocator
|
||||
.allocate(
|
||||
settings_bar_addr,
|
||||
self.settings_bar_addr,
|
||||
CAPABILITY_BAR_SIZE,
|
||||
Some(CAPABILITY_BAR_SIZE),
|
||||
)
|
||||
.ok_or(PciDeviceError::IoAllocationFailed(CAPABILITY_BAR_SIZE))?;
|
||||
ranges.push((addr, CAPABILITY_BAR_SIZE, region_type));
|
||||
(addr, region_type)
|
||||
} else {
|
||||
let region_type = PciBarRegionType::Memory32BitRegion;
|
||||
@@ -882,44 +872,50 @@ impl PciDevice for VirtioPciDevice {
|
||||
.lock()
|
||||
.unwrap()
|
||||
.allocate_mmio_hole_addresses(
|
||||
settings_bar_addr,
|
||||
self.settings_bar_addr,
|
||||
CAPABILITY_BAR_SIZE,
|
||||
Some(CAPABILITY_BAR_SIZE),
|
||||
)
|
||||
.ok_or(PciDeviceError::IoAllocationFailed(CAPABILITY_BAR_SIZE))?;
|
||||
ranges.push((addr, CAPABILITY_BAR_SIZE, region_type));
|
||||
(addr, region_type)
|
||||
};
|
||||
self.bar_regions
|
||||
.push((virtio_pci_bar_addr, CAPABILITY_BAR_SIZE, region_type));
|
||||
|
||||
let bar = PciBarConfiguration::default()
|
||||
.set_index(VIRTIO_COMMON_BAR_INDEX)
|
||||
let config = PciBarConfiguration::default()
|
||||
.set_register_index(0)
|
||||
.set_address(virtio_pci_bar_addr.raw_value())
|
||||
.set_size(CAPABILITY_BAR_SIZE)
|
||||
.set_region_type(region_type);
|
||||
self.configuration.add_pci_bar(&bar).map_err(|e| {
|
||||
PciDeviceError::IoRegistrationFailed(virtio_pci_bar_addr.raw_value(), e)
|
||||
})?;
|
||||
|
||||
bars.push(bar);
|
||||
let virtio_pci_bar =
|
||||
self.configuration.add_pci_bar(&config).map_err(|e| {
|
||||
PciDeviceError::IoRegistrationFailed(virtio_pci_bar_addr.raw_value(), e)
|
||||
})? as u8;
|
||||
|
||||
// Once the BARs are allocated, the capabilities can be added to the PCI configuration.
|
||||
self.add_pci_capabilities(VIRTIO_COMMON_BAR_INDEX as u8)?;
|
||||
self.add_pci_capabilities(virtio_pci_bar)?;
|
||||
|
||||
// Allocate a dedicated BAR if there are some shared memory regions.
|
||||
if let Some(shm_list) = device.get_shm_regions() {
|
||||
let bar = PciBarConfiguration::default()
|
||||
.set_index(VIRTIO_SHM_BAR_INDEX)
|
||||
let config = PciBarConfiguration::default()
|
||||
.set_register_index(2)
|
||||
.set_address(shm_list.addr.raw_value())
|
||||
.set_size(shm_list.len);
|
||||
self.configuration
|
||||
.add_pci_bar(&bar)
|
||||
.map_err(|e| PciDeviceError::IoRegistrationFailed(shm_list.addr.raw_value(), e))?;
|
||||
let virtio_pci_shm_bar =
|
||||
self.configuration.add_pci_bar(&config).map_err(|e| {
|
||||
PciDeviceError::IoRegistrationFailed(shm_list.addr.raw_value(), e)
|
||||
})? as u8;
|
||||
|
||||
bars.push(bar);
|
||||
let region_type = PciBarRegionType::Memory64BitRegion;
|
||||
ranges.push((shm_list.addr, shm_list.len, region_type));
|
||||
self.bar_regions
|
||||
.push((shm_list.addr, shm_list.len, region_type));
|
||||
|
||||
for (idx, shm) in shm_list.region_list.iter().enumerate() {
|
||||
let shm_cap = VirtioPciCap64::new(
|
||||
PciCapabilityType::SharedMemoryConfig,
|
||||
VIRTIO_SHM_BAR_INDEX as u8,
|
||||
virtio_pci_shm_bar,
|
||||
idx as u8,
|
||||
shm.offset,
|
||||
shm.len,
|
||||
@@ -930,9 +926,7 @@ impl PciDevice for VirtioPciDevice {
|
||||
}
|
||||
}
|
||||
|
||||
self.bar_regions = bars.clone();
|
||||
|
||||
Ok(bars)
|
||||
Ok(ranges)
|
||||
}
|
||||
|
||||
fn free_bars(
|
||||
@@ -940,13 +934,13 @@ impl PciDevice for VirtioPciDevice {
|
||||
allocator: &mut SystemAllocator,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
) -> std::result::Result<(), PciDeviceError> {
|
||||
for bar in self.bar_regions.drain(..) {
|
||||
match bar.region_type() {
|
||||
for (addr, length, type_) in self.bar_regions.drain(..) {
|
||||
match type_ {
|
||||
PciBarRegionType::Memory32BitRegion => {
|
||||
allocator.free_mmio_hole_addresses(GuestAddress(bar.addr()), bar.size());
|
||||
allocator.free_mmio_hole_addresses(addr, length);
|
||||
}
|
||||
PciBarRegionType::Memory64BitRegion => {
|
||||
mmio_allocator.free(GuestAddress(bar.addr()), bar.size());
|
||||
mmio_allocator.free(addr, length);
|
||||
}
|
||||
_ => error!("Unexpected PCI bar type"),
|
||||
}
|
||||
@@ -957,9 +951,9 @@ impl PciDevice for VirtioPciDevice {
|
||||
fn move_bar(&mut self, old_base: u64, new_base: u64) -> result::Result<(), std::io::Error> {
|
||||
// We only update our idea of the bar in order to support free_bars() above.
|
||||
// The majority of the reallocation is done inside DeviceManager.
|
||||
for bar in self.bar_regions.iter_mut() {
|
||||
if bar.addr() == old_base {
|
||||
*bar = bar.set_address(new_base);
|
||||
for (addr, _, _) in self.bar_regions.iter_mut() {
|
||||
if (*addr).0 == old_base {
|
||||
*addr = GuestAddress(new_base);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1091,10 +1085,6 @@ impl PciDevice for VirtioPciDevice {
|
||||
fn as_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
Some(self.id.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl BusDevice for VirtioPciDevice {
|
||||
|
||||
@@ -224,7 +224,7 @@ impl<S: VhostUserMasterReqHandler> VhostUserEpollHandler<S> {
|
||||
vhost_user
|
||||
.reinitialize_vhost_user(
|
||||
self.mem.memory().deref(),
|
||||
self.queues.iter().map(vm_virtio::clone_queue).collect(),
|
||||
self.queues.clone(),
|
||||
self.queue_evts
|
||||
.iter()
|
||||
.map(|q| q.try_clone().unwrap())
|
||||
@@ -324,7 +324,7 @@ impl VhostUserCommon {
|
||||
.unwrap()
|
||||
.setup_vhost_user(
|
||||
&mem.memory(),
|
||||
queues.iter().map(vm_virtio::clone_queue).collect(),
|
||||
queues.clone(),
|
||||
queue_evts.iter().map(|q| q.try_clone().unwrap()).collect(),
|
||||
&interrupt_cb,
|
||||
acked_features,
|
||||
|
||||
@@ -5,6 +5,6 @@ authors = ["The Chromium OS Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.126"
|
||||
vm-memory = "0.8.0"
|
||||
libc = "0.2.123"
|
||||
vm-memory = "0.7.0"
|
||||
arch = { path = "../arch" }
|
||||
|
||||
@@ -10,12 +10,12 @@ kvm = ["vfio-ioctls/kvm"]
|
||||
mshv = ["vfio-ioctls/mshv"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
thiserror = "1.0.31"
|
||||
serde = { version = "1.0.137", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.81"
|
||||
anyhow = "1.0.56"
|
||||
thiserror = "1.0.30"
|
||||
serde = { version = "1.0.136", features = ["rc"] }
|
||||
serde_derive = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap"] }
|
||||
vm-memory = { version = "0.7.0", features = ["backend-mmap"] }
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
//! * The virtual device backend requests the interrupt manager to create an interrupt group
|
||||
//! according to guest configuration information
|
||||
|
||||
pub use hypervisor::{InterruptSourceConfig, LegacyIrqSourceConfig, MsiIrqSourceConfig};
|
||||
use std::sync::Arc;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
@@ -67,6 +66,39 @@ pub type Result<T> = std::io::Result<T>;
|
||||
/// Data type to store an interrupt source identifier.
|
||||
pub type InterruptIndex = u32;
|
||||
|
||||
/// Configuration data for legacy interrupts.
|
||||
///
|
||||
/// On x86 platforms, legacy interrupts means those interrupts routed through PICs or IOAPICs.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct LegacyIrqSourceConfig {
|
||||
pub irqchip: u32,
|
||||
pub pin: u32,
|
||||
}
|
||||
|
||||
/// Configuration data for MSI/MSI-X interrupts.
|
||||
///
|
||||
/// On x86 platforms, these interrupts are vectors delivered directly to the LAPIC.
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct MsiIrqSourceConfig {
|
||||
/// High address to delivery message signaled interrupt.
|
||||
pub high_addr: u32,
|
||||
/// Low address to delivery message signaled interrupt.
|
||||
pub low_addr: u32,
|
||||
/// Data to write to delivery message signaled interrupt.
|
||||
pub data: u32,
|
||||
/// Unique ID of the device to delivery message signaled interrupt.
|
||||
pub devid: u32,
|
||||
}
|
||||
|
||||
/// Configuration data for an interrupt source.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum InterruptSourceConfig {
|
||||
/// Configuration data for Legacy interrupts.
|
||||
LegacyIrq(LegacyIrqSourceConfig),
|
||||
/// Configuration data for PciMsi, PciMsix and generic MSI interrupts.
|
||||
MsiIrq(MsiIrqSourceConfig),
|
||||
}
|
||||
|
||||
/// Configuration data for legacy, pin based interrupt groups.
|
||||
///
|
||||
/// A legacy interrupt group only takes one irq number as its configuration.
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
mod bus;
|
||||
pub mod dma_mapping;
|
||||
@@ -22,13 +23,6 @@ pub enum MsiIrqType {
|
||||
GenericMsi,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, Debug)]
|
||||
pub enum PciBarType {
|
||||
Io,
|
||||
Mmio32,
|
||||
Mmio64,
|
||||
}
|
||||
|
||||
/// Enumeration for device resources.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
@@ -37,14 +31,6 @@ pub enum Resource {
|
||||
PioAddressRange { base: u16, size: u16 },
|
||||
/// Memory Mapped IO address range.
|
||||
MmioAddressRange { base: u64, size: u64 },
|
||||
/// PCI BAR
|
||||
PciBar {
|
||||
index: usize,
|
||||
base: u64,
|
||||
size: u64,
|
||||
type_: PciBarType,
|
||||
prefetchable: bool,
|
||||
},
|
||||
/// Legacy IRQ number.
|
||||
LegacyIrq(u32),
|
||||
/// Message Signaled Interrupt
|
||||
|
||||
@@ -5,10 +5,11 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
thiserror = "1.0.31"
|
||||
serde = { version = "1.0.137", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.81"
|
||||
anyhow = "1.0.56"
|
||||
thiserror = "1.0.30"
|
||||
serde = { version = "1.0.136", features = ["rc"] }
|
||||
serde_derive = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
//
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use crate::protocol::MemoryRangeTable;
|
||||
use anyhow::anyhow;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -12,7 +15,7 @@ use versionize::{VersionMap, Versionize};
|
||||
pub mod protocol;
|
||||
|
||||
/// Global VMM version for versioning
|
||||
const MAJOR_VERSION: u16 = 24;
|
||||
const MAJOR_VERSION: u16 = 23;
|
||||
const MINOR_VERSION: u16 = 0;
|
||||
const VMM_VERSION: u16 = MAJOR_VERSION << 12 | MINOR_VERSION & 0b1111;
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
//
|
||||
|
||||
use crate::{MigratableError, VersionMapped};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io::{Read, Write};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
|
||||
@@ -8,7 +8,7 @@ edition = "2021"
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.17"
|
||||
log = "0.4.16"
|
||||
virtio-bindings = { version = "0.1.0", features = ["virtio-v5_0_0"] }
|
||||
virtio-queue = "0.3.0"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
virtio-queue = "0.2.0"
|
||||
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
|
||||
@@ -12,10 +12,8 @@
|
||||
|
||||
use std::fmt::{self, Debug};
|
||||
use std::sync::Arc;
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::{bitmap::AtomicBitmap, GuestAddress, GuestMemoryAtomic};
|
||||
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
pub mod queue;
|
||||
pub use queue::*;
|
||||
@@ -123,24 +121,3 @@ impl Translatable for GuestAddress {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper for cloning a Queue since QueueState doesn't derive Clone
|
||||
pub fn clone_queue(
|
||||
queue: &Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
) -> Queue<GuestMemoryAtomic<GuestMemoryMmap>> {
|
||||
Queue::<GuestMemoryAtomic<GuestMemoryMmap>, virtio_queue::QueueState> {
|
||||
mem: queue.mem.clone(),
|
||||
state: virtio_queue::QueueState {
|
||||
max_size: queue.state.max_size,
|
||||
next_avail: queue.state.next_avail,
|
||||
next_used: queue.state.next_used,
|
||||
event_idx_enabled: queue.state.event_idx_enabled,
|
||||
num_added: queue.state.num_added,
|
||||
size: queue.state.size,
|
||||
ready: queue.state.ready,
|
||||
desc_table: queue.state.desc_table,
|
||||
avail_ring: queue.state.avail_ring,
|
||||
used_ring: queue.state.used_ring,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,12 @@ tdx = ["arch/tdx", "hypervisor/tdx"]
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { path = "../acpi_tables" }
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.56"
|
||||
arc-swap = "1.5.0"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "1.3.2"
|
||||
block_util = { path = "../block_util" }
|
||||
clap = "3.1.18"
|
||||
clap = "3.1.8"
|
||||
devices = { path = "../devices" }
|
||||
epoll = "4.3.1"
|
||||
event_monitor = { path = "../event_monitor" }
|
||||
@@ -29,30 +29,31 @@ gdbstub = "0.6.1"
|
||||
gdbstub_arch = "0.2.2"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
lazy_static = "1.4.0"
|
||||
libc = "0.2.126"
|
||||
libc = "0.2.123"
|
||||
linux-loader = { version = "0.4.0", features = ["elf", "bzimage", "pe"] }
|
||||
log = "0.4.17"
|
||||
log = "0.4.16"
|
||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||
net_util = { path = "../net_util" }
|
||||
option_parser = { path = "../option_parser" }
|
||||
pci = { path = "../pci" }
|
||||
qcow = { path = "../qcow" }
|
||||
seccompiler = "0.2.0"
|
||||
serde = { version = "1.0.137", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.81"
|
||||
signal-hook = "0.3.14"
|
||||
thiserror = "1.0.31"
|
||||
uuid = "1.0.0"
|
||||
serde = { version = "1.0.136", features = ["rc"] }
|
||||
serde_derive = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
signal-hook = "0.3.13"
|
||||
thiserror = "1.0.30"
|
||||
uuid = "0.8.2"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vfio_user = { path = "../vfio_user" }
|
||||
vhdx = { path = "../vhdx" }
|
||||
virtio-devices = { path = "../virtio-devices" }
|
||||
virtio-queue = "0.3.0"
|
||||
virtio-queue = "0.2.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-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = { version = "0.9.0", features = ["with-serde"] }
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
use crate::api::http_endpoint::{VmActionHandler, VmCreate, VmInfo, VmmPing, VmmShutdown};
|
||||
use crate::api::{ApiError, ApiRequest, VmAction};
|
||||
use crate::seccomp_filters::{get_seccomp_filter, Thread};
|
||||
use crate::{Error as VmmError, Result};
|
||||
use crate::{Error, Result};
|
||||
use micro_http::{Body, HttpServer, MediaType, Method, Request, Response, StatusCode, Version};
|
||||
use seccompiler::{apply_filter, SeccompAction};
|
||||
use serde_json::Error as SerdeError;
|
||||
@@ -36,8 +36,89 @@ pub enum HttpError {
|
||||
/// Internal Server Error
|
||||
InternalServerError,
|
||||
|
||||
/// Error from internal API
|
||||
ApiError(ApiError),
|
||||
/// Could not create a VM
|
||||
VmCreate(ApiError),
|
||||
|
||||
/// Could not boot a VM
|
||||
VmBoot(ApiError),
|
||||
|
||||
/// Could not delete a VM
|
||||
VmDelete(ApiError),
|
||||
|
||||
/// Could not get the VM information
|
||||
VmInfo(ApiError),
|
||||
|
||||
/// Could not pause the VM
|
||||
VmPause(ApiError),
|
||||
|
||||
/// Could not pause the VM
|
||||
VmResume(ApiError),
|
||||
|
||||
/// Could not shut a VM down
|
||||
VmShutdown(ApiError),
|
||||
|
||||
/// Could not reboot a VM
|
||||
VmReboot(ApiError),
|
||||
|
||||
/// Could not snapshot a VM
|
||||
VmSnapshot(ApiError),
|
||||
|
||||
/// Could not restore a VM
|
||||
VmRestore(ApiError),
|
||||
|
||||
/// Could not act on a VM
|
||||
VmAction(ApiError),
|
||||
|
||||
/// Could not resize a VM
|
||||
VmResize(ApiError),
|
||||
|
||||
/// Could not resize a memory zone
|
||||
VmResizeZone(ApiError),
|
||||
|
||||
/// Could not add a device to a VM
|
||||
VmAddDevice(ApiError),
|
||||
|
||||
/// Could not add a user device to the VM
|
||||
VmAddUserDevice(ApiError),
|
||||
|
||||
/// Could not remove a device from a VM
|
||||
VmRemoveDevice(ApiError),
|
||||
|
||||
/// Could not shut the VMM down
|
||||
VmmShutdown(ApiError),
|
||||
|
||||
/// Could not handle VMM ping
|
||||
VmmPing(ApiError),
|
||||
|
||||
/// Could not add a disk to a VM
|
||||
VmAddDisk(ApiError),
|
||||
|
||||
/// Could not add a fs to a VM
|
||||
VmAddFs(ApiError),
|
||||
|
||||
/// Could not add a pmem device to a VM
|
||||
VmAddPmem(ApiError),
|
||||
|
||||
/// Could not add a network device to a VM
|
||||
VmAddNet(ApiError),
|
||||
|
||||
/// Could not add a vDPA device to a VM
|
||||
VmAddVdpa(ApiError),
|
||||
|
||||
/// Could not add a vsock device to a VM
|
||||
VmAddVsock(ApiError),
|
||||
|
||||
/// Could not get counters from VM
|
||||
VmCounters(ApiError),
|
||||
|
||||
/// Error setting up migration received
|
||||
VmReceiveMigration(ApiError),
|
||||
|
||||
/// Error setting up migration sender
|
||||
VmSendMigration(ApiError),
|
||||
|
||||
/// Error activating power button
|
||||
VmPowerButton(ApiError),
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for HttpError {
|
||||
@@ -199,7 +280,7 @@ fn start_http_thread(
|
||||
) -> Result<thread::JoinHandle<Result<()>>> {
|
||||
// Retrieve seccomp filter for API thread
|
||||
let api_seccomp_filter =
|
||||
get_seccomp_filter(seccomp_action, Thread::Api).map_err(VmmError::CreateSeccompFilter)?;
|
||||
get_seccomp_filter(seccomp_action, Thread::Api).map_err(Error::CreateSeccompFilter)?;
|
||||
|
||||
thread::Builder::new()
|
||||
.name("http-server".to_string())
|
||||
@@ -207,7 +288,7 @@ fn start_http_thread(
|
||||
// Apply seccomp filter for API thread.
|
||||
if !api_seccomp_filter.is_empty() {
|
||||
apply_filter(&api_seccomp_filter)
|
||||
.map_err(VmmError::ApplySeccompFilter)
|
||||
.map_err(Error::ApplySeccompFilter)
|
||||
.map_err(|e| {
|
||||
error!("Error applying seccomp filter: {:?}", e);
|
||||
exit_evt.write(1).ok();
|
||||
@@ -245,7 +326,7 @@ fn start_http_thread(
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.map_err(VmmError::HttpThreadSpawn)
|
||||
.map_err(Error::HttpThreadSpawn)
|
||||
}
|
||||
|
||||
pub fn start_http_path_thread(
|
||||
@@ -256,9 +337,9 @@ pub fn start_http_path_thread(
|
||||
exit_evt: EventFd,
|
||||
) -> Result<thread::JoinHandle<Result<()>>> {
|
||||
let socket_path = PathBuf::from(path);
|
||||
let socket_fd = UnixListener::bind(socket_path).map_err(VmmError::CreateApiServerSocket)?;
|
||||
let socket_fd = UnixListener::bind(socket_path).map_err(Error::CreateApiServerSocket)?;
|
||||
let server =
|
||||
HttpServer::new_from_fd(socket_fd.into_raw_fd()).map_err(VmmError::CreateApiServer)?;
|
||||
HttpServer::new_from_fd(socket_fd.into_raw_fd()).map_err(Error::CreateApiServer)?;
|
||||
start_http_thread(server, api_notifier, api_sender, seccomp_action, exit_evt)
|
||||
}
|
||||
|
||||
@@ -269,6 +350,6 @@ pub fn start_http_fd_thread(
|
||||
seccomp_action: &SeccompAction,
|
||||
exit_evt: EventFd,
|
||||
) -> Result<thread::JoinHandle<Result<()>>> {
|
||||
let server = HttpServer::new_from_fd(fd).map_err(VmmError::CreateApiServer)?;
|
||||
let server = HttpServer::new_from_fd(fd).map_err(Error::CreateApiServer)?;
|
||||
start_http_thread(server, api_notifier, api_sender, seccomp_action, exit_evt)
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ impl EndpointHandler for VmCreate {
|
||||
|
||||
// Call vm_create()
|
||||
match vm_create(api_notifier, api_sender, Arc::new(Mutex::new(vm_config)))
|
||||
.map_err(HttpError::ApiError)
|
||||
.map_err(HttpError::VmCreate)
|
||||
{
|
||||
Ok(_) => Response::new(Version::Http11, StatusCode::NoContent),
|
||||
Err(e) => error_response(e, StatusCode::InternalServerError),
|
||||
@@ -85,22 +85,30 @@ impl EndpointHandler for VmActionHandler {
|
||||
api_notifier,
|
||||
api_sender,
|
||||
Arc::new(serde_json::from_slice(body.raw())?),
|
||||
),
|
||||
)
|
||||
.map_err(HttpError::VmAddDevice),
|
||||
|
||||
AddDisk(_) => vm_add_disk(
|
||||
api_notifier,
|
||||
api_sender,
|
||||
Arc::new(serde_json::from_slice(body.raw())?),
|
||||
),
|
||||
)
|
||||
.map_err(HttpError::VmAddDisk),
|
||||
|
||||
AddFs(_) => vm_add_fs(
|
||||
api_notifier,
|
||||
api_sender,
|
||||
Arc::new(serde_json::from_slice(body.raw())?),
|
||||
),
|
||||
)
|
||||
.map_err(HttpError::VmAddFs),
|
||||
|
||||
AddPmem(_) => vm_add_pmem(
|
||||
api_notifier,
|
||||
api_sender,
|
||||
Arc::new(serde_json::from_slice(body.raw())?),
|
||||
),
|
||||
)
|
||||
.map_err(HttpError::VmAddPmem),
|
||||
|
||||
AddNet(_) => {
|
||||
let mut net_cfg: NetConfig = serde_json::from_slice(body.raw())?;
|
||||
// Update network config with optional files that might have
|
||||
@@ -110,73 +118,95 @@ impl EndpointHandler for VmActionHandler {
|
||||
net_cfg.fds = Some(fds);
|
||||
}
|
||||
vm_add_net(api_notifier, api_sender, Arc::new(net_cfg))
|
||||
.map_err(HttpError::VmAddNet)
|
||||
}
|
||||
|
||||
AddVdpa(_) => vm_add_vdpa(
|
||||
api_notifier,
|
||||
api_sender,
|
||||
Arc::new(serde_json::from_slice(body.raw())?),
|
||||
),
|
||||
)
|
||||
.map_err(HttpError::VmAddVdpa),
|
||||
|
||||
AddVsock(_) => vm_add_vsock(
|
||||
api_notifier,
|
||||
api_sender,
|
||||
Arc::new(serde_json::from_slice(body.raw())?),
|
||||
),
|
||||
)
|
||||
.map_err(HttpError::VmAddVsock),
|
||||
|
||||
AddUserDevice(_) => vm_add_user_device(
|
||||
api_notifier,
|
||||
api_sender,
|
||||
Arc::new(serde_json::from_slice(body.raw())?),
|
||||
),
|
||||
)
|
||||
.map_err(HttpError::VmAddUserDevice),
|
||||
|
||||
RemoveDevice(_) => vm_remove_device(
|
||||
api_notifier,
|
||||
api_sender,
|
||||
Arc::new(serde_json::from_slice(body.raw())?),
|
||||
),
|
||||
)
|
||||
.map_err(HttpError::VmRemoveDevice),
|
||||
|
||||
Resize(_) => vm_resize(
|
||||
api_notifier,
|
||||
api_sender,
|
||||
Arc::new(serde_json::from_slice(body.raw())?),
|
||||
),
|
||||
)
|
||||
.map_err(HttpError::VmResize),
|
||||
|
||||
ResizeZone(_) => vm_resize_zone(
|
||||
api_notifier,
|
||||
api_sender,
|
||||
Arc::new(serde_json::from_slice(body.raw())?),
|
||||
),
|
||||
)
|
||||
.map_err(HttpError::VmResizeZone),
|
||||
|
||||
Restore(_) => vm_restore(
|
||||
api_notifier,
|
||||
api_sender,
|
||||
Arc::new(serde_json::from_slice(body.raw())?),
|
||||
),
|
||||
)
|
||||
.map_err(HttpError::VmRestore),
|
||||
|
||||
Snapshot(_) => vm_snapshot(
|
||||
api_notifier,
|
||||
api_sender,
|
||||
Arc::new(serde_json::from_slice(body.raw())?),
|
||||
),
|
||||
)
|
||||
.map_err(HttpError::VmSnapshot),
|
||||
|
||||
ReceiveMigration(_) => vm_receive_migration(
|
||||
api_notifier,
|
||||
api_sender,
|
||||
Arc::new(serde_json::from_slice(body.raw())?),
|
||||
),
|
||||
)
|
||||
.map_err(HttpError::VmReceiveMigration),
|
||||
|
||||
SendMigration(_) => vm_send_migration(
|
||||
api_notifier,
|
||||
api_sender,
|
||||
Arc::new(serde_json::from_slice(body.raw())?),
|
||||
),
|
||||
)
|
||||
.map_err(HttpError::VmSendMigration),
|
||||
|
||||
_ => return Err(HttpError::BadRequest),
|
||||
_ => Err(HttpError::BadRequest),
|
||||
}
|
||||
} else {
|
||||
match self.action {
|
||||
Boot => vm_boot(api_notifier, api_sender),
|
||||
Delete => vm_delete(api_notifier, api_sender),
|
||||
Shutdown => vm_shutdown(api_notifier, api_sender),
|
||||
Reboot => vm_reboot(api_notifier, api_sender),
|
||||
Pause => vm_pause(api_notifier, api_sender),
|
||||
Resume => vm_resume(api_notifier, api_sender),
|
||||
PowerButton => vm_power_button(api_notifier, api_sender),
|
||||
_ => return Err(HttpError::BadRequest),
|
||||
Boot => vm_boot(api_notifier, api_sender).map_err(HttpError::VmBoot),
|
||||
Delete => vm_delete(api_notifier, api_sender).map_err(HttpError::VmDelete),
|
||||
Shutdown => vm_shutdown(api_notifier, api_sender).map_err(HttpError::VmShutdown),
|
||||
Reboot => vm_reboot(api_notifier, api_sender).map_err(HttpError::VmReboot),
|
||||
Pause => vm_pause(api_notifier, api_sender).map_err(HttpError::VmPause),
|
||||
Resume => vm_resume(api_notifier, api_sender).map_err(HttpError::VmResume),
|
||||
PowerButton => {
|
||||
vm_power_button(api_notifier, api_sender).map_err(HttpError::VmPowerButton)
|
||||
}
|
||||
_ => Err(HttpError::BadRequest),
|
||||
}
|
||||
}
|
||||
.map_err(HttpError::ApiError)
|
||||
}
|
||||
|
||||
fn get_handler(
|
||||
@@ -187,7 +217,7 @@ impl EndpointHandler for VmActionHandler {
|
||||
) -> std::result::Result<Option<Body>, HttpError> {
|
||||
use VmAction::*;
|
||||
match self.action {
|
||||
Counters => vm_counters(api_notifier, api_sender).map_err(HttpError::ApiError),
|
||||
Counters => vm_counters(api_notifier, api_sender).map_err(HttpError::VmCounters),
|
||||
_ => Err(HttpError::BadRequest),
|
||||
}
|
||||
}
|
||||
@@ -204,7 +234,7 @@ impl EndpointHandler for VmInfo {
|
||||
api_sender: Sender<ApiRequest>,
|
||||
) -> Response {
|
||||
match req.method() {
|
||||
Method::Get => match vm_info(api_notifier, api_sender).map_err(HttpError::ApiError) {
|
||||
Method::Get => match vm_info(api_notifier, api_sender).map_err(HttpError::VmInfo) {
|
||||
Ok(info) => {
|
||||
let mut response = Response::new(Version::Http11, StatusCode::OK);
|
||||
let info_serialized = serde_json::to_string(&info).unwrap();
|
||||
@@ -230,7 +260,7 @@ impl EndpointHandler for VmmPing {
|
||||
api_sender: Sender<ApiRequest>,
|
||||
) -> Response {
|
||||
match req.method() {
|
||||
Method::Get => match vmm_ping(api_notifier, api_sender).map_err(HttpError::ApiError) {
|
||||
Method::Get => match vmm_ping(api_notifier, api_sender).map_err(HttpError::VmmPing) {
|
||||
Ok(pong) => {
|
||||
let mut response = Response::new(Version::Http11, StatusCode::OK);
|
||||
let info_serialized = serde_json::to_string(&pong).unwrap();
|
||||
@@ -258,7 +288,7 @@ impl EndpointHandler for VmmShutdown {
|
||||
) -> Response {
|
||||
match req.method() {
|
||||
Method::Put => {
|
||||
match vmm_shutdown(api_notifier, api_sender).map_err(HttpError::ApiError) {
|
||||
match vmm_shutdown(api_notifier, api_sender).map_err(HttpError::VmmShutdown) {
|
||||
Ok(_) => Response::new(Version::Http11, StatusCode::OK),
|
||||
Err(e) => error_response(e, StatusCode::InternalServerError),
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ use crate::config::{
|
||||
use crate::device_tree::DeviceTree;
|
||||
use crate::vm::{Error as VmError, VmState};
|
||||
use micro_http::Body;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io;
|
||||
use std::sync::mpsc::{channel, RecvError, SendError, Sender};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
@@ -616,8 +616,6 @@ components:
|
||||
items:
|
||||
type: integer
|
||||
format: int16
|
||||
serial_number:
|
||||
type: string
|
||||
|
||||
MemoryZoneConfig:
|
||||
required:
|
||||
|
||||
@@ -8,15 +8,14 @@ use net_util::MacAddr;
|
||||
use option_parser::{
|
||||
ByteSized, IntegerList, OptionParser, OptionParserError, StringList, Toggle, Tuple,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
use std::collections::HashMap;
|
||||
use std::convert::From;
|
||||
use std::fmt;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::path::PathBuf;
|
||||
use std::result;
|
||||
use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
|
||||
use virtio_devices::{RateLimiterConfig, TokenBucketConfig};
|
||||
|
||||
pub const DEFAULT_VCPUS: u8 = 1;
|
||||
@@ -37,7 +36,7 @@ pub const DEFAULT_NUM_PCI_SEGMENTS: u16 = 1;
|
||||
const MAX_NUM_PCI_SEGMENTS: u16 = 16;
|
||||
|
||||
/// Errors associated with VM configuration parameters.
|
||||
#[derive(Debug, Error)]
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Filesystem tag is missing
|
||||
ParseFsTagMissing,
|
||||
@@ -115,7 +114,7 @@ pub enum Error {
|
||||
ParseVdpaPathMissing,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Error)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ValidationError {
|
||||
/// Both console and serial are tty.
|
||||
DoubleTtyMode,
|
||||
@@ -170,13 +169,7 @@ pub enum ValidationError {
|
||||
/// On a IOMMU segment but not behind IOMMU
|
||||
OnIommuSegment(u16),
|
||||
// On a IOMMU segment but IOMMU not suported
|
||||
IommuNotSupportedOnSegment(u16),
|
||||
// Identifier is not unique
|
||||
IdentifierNotUnique(String),
|
||||
/// Invalid identifier
|
||||
InvalidIdentifier(String),
|
||||
/// Placing the device behind a virtual IOMMU is not supported
|
||||
IommuNotSupported,
|
||||
IommuNotSupported(u16),
|
||||
}
|
||||
|
||||
type ValidationResult<T> = std::result::Result<T, ValidationError>;
|
||||
@@ -256,22 +249,13 @@ impl fmt::Display for ValidationError {
|
||||
pci_segment
|
||||
)
|
||||
}
|
||||
IommuNotSupportedOnSegment(pci_segment) => {
|
||||
IommuNotSupported(pci_segment) => {
|
||||
write!(
|
||||
f,
|
||||
"Device is on an IOMMU PCI segment ({}) but does not support being placed behind IOMMU",
|
||||
"Device is on an IOMMU PCI segment ({}) but does support being placed behind IOMMU",
|
||||
pci_segment
|
||||
)
|
||||
}
|
||||
IdentifierNotUnique(s) => {
|
||||
write!(f, "Identifier {} is not unique", s)
|
||||
}
|
||||
InvalidIdentifier(s) => {
|
||||
write!(f, "Identifier {} is invalid", s)
|
||||
}
|
||||
IommuNotSupported => {
|
||||
write!(f, "Device does not support being placed behind IOMMU")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,7 +419,7 @@ impl<'a> VmParams<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub enum HotplugMethod {
|
||||
Acpi,
|
||||
VirtioMem,
|
||||
@@ -637,8 +621,6 @@ pub struct PlatformConfig {
|
||||
pub num_pci_segments: u16,
|
||||
#[serde(default)]
|
||||
pub iommu_segments: Option<Vec<u16>>,
|
||||
#[serde(default)]
|
||||
pub serial_number: Option<String>,
|
||||
}
|
||||
|
||||
impl PlatformConfig {
|
||||
@@ -646,7 +628,6 @@ impl PlatformConfig {
|
||||
let mut parser = OptionParser::new();
|
||||
parser.add("num_pci_segments");
|
||||
parser.add("iommu_segments");
|
||||
parser.add("serial_number");
|
||||
parser.parse(platform).map_err(Error::ParsePlatform)?;
|
||||
|
||||
let num_pci_segments: u16 = parser
|
||||
@@ -657,13 +638,9 @@ impl PlatformConfig {
|
||||
.convert::<IntegerList>("iommu_segments")
|
||||
.map_err(Error::ParsePlatform)?
|
||||
.map(|v| v.0.iter().map(|e| *e as u16).collect());
|
||||
let serial_number = parser
|
||||
.convert("serial_number")
|
||||
.map_err(Error::ParsePlatform)?;
|
||||
Ok(PlatformConfig {
|
||||
num_pci_segments,
|
||||
iommu_segments,
|
||||
serial_number,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -691,7 +668,6 @@ impl Default for PlatformConfig {
|
||||
PlatformConfig {
|
||||
num_pci_segments: DEFAULT_NUM_PCI_SEGMENTS,
|
||||
iommu_segments: None,
|
||||
serial_number: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1163,10 +1139,6 @@ impl DiskConfig {
|
||||
return Err(ValidationError::TooManyQueues);
|
||||
}
|
||||
|
||||
if self.vhost_user && self.iommu {
|
||||
return Err(ValidationError::IommuNotSupported);
|
||||
}
|
||||
|
||||
if let Some(platform_config) = vm_config.platform.as_ref() {
|
||||
if self.pci_segment >= platform_config.num_pci_segments {
|
||||
return Err(ValidationError::InvalidPciSegment(self.pci_segment));
|
||||
@@ -1463,10 +1435,6 @@ impl NetConfig {
|
||||
return Err(ValidationError::TooManyQueues);
|
||||
}
|
||||
|
||||
if self.vhost_user && self.iommu {
|
||||
return Err(ValidationError::IommuNotSupported);
|
||||
}
|
||||
|
||||
if let Some(platform_config) = vm_config.platform.as_ref() {
|
||||
if self.pci_segment >= platform_config.num_pci_segments {
|
||||
return Err(ValidationError::InvalidPciSegment(self.pci_segment));
|
||||
@@ -1696,9 +1664,7 @@ impl FsConfig {
|
||||
|
||||
if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() {
|
||||
if iommu_segments.contains(&self.pci_segment) {
|
||||
return Err(ValidationError::IommuNotSupportedOnSegment(
|
||||
self.pci_segment,
|
||||
));
|
||||
return Err(ValidationError::IommuNotSupported(self.pci_segment));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1987,9 +1953,7 @@ impl UserDeviceConfig {
|
||||
|
||||
if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() {
|
||||
if iommu_segments.contains(&self.pci_segment) {
|
||||
return Err(ValidationError::IommuNotSupportedOnSegment(
|
||||
self.pci_segment,
|
||||
));
|
||||
return Err(ValidationError::IommuNotSupported(self.pci_segment));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2351,29 +2315,7 @@ pub struct VmConfig {
|
||||
}
|
||||
|
||||
impl VmConfig {
|
||||
fn validate_identifier(
|
||||
id_list: &mut BTreeSet<String>,
|
||||
id: &Option<String>,
|
||||
) -> ValidationResult<()> {
|
||||
if let Some(id) = id.as_ref() {
|
||||
if id.starts_with("__") {
|
||||
return Err(ValidationError::InvalidIdentifier(id.clone()));
|
||||
}
|
||||
|
||||
if !id_list.insert(id.clone()) {
|
||||
return Err(ValidationError::IdentifierNotUnique(id.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Also enables virtio-iommu if the config needs it
|
||||
// Returns the list of unique identifiers provided through the
|
||||
// configuration.
|
||||
pub fn validate(&mut self) -> ValidationResult<BTreeSet<String>> {
|
||||
let mut id_list = BTreeSet::new();
|
||||
|
||||
pub fn validate(&self) -> ValidationResult<()> {
|
||||
#[cfg(not(feature = "tdx"))]
|
||||
self.kernel.as_ref().ok_or(ValidationError::KernelMissing)?;
|
||||
|
||||
@@ -2417,9 +2359,6 @@ impl VmConfig {
|
||||
return Err(ValidationError::VhostUserMissingSocket);
|
||||
}
|
||||
disk.validate(self)?;
|
||||
self.iommu |= disk.iommu;
|
||||
|
||||
Self::validate_identifier(&mut id_list, &disk.id)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2429,9 +2368,6 @@ impl VmConfig {
|
||||
return Err(ValidationError::VhostUserRequiresSharedMemory);
|
||||
}
|
||||
net.validate(self)?;
|
||||
self.iommu |= net.iommu;
|
||||
|
||||
Self::validate_identifier(&mut id_list, &net.id)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2441,23 +2377,15 @@ impl VmConfig {
|
||||
}
|
||||
for fs in fses {
|
||||
fs.validate(self)?;
|
||||
|
||||
Self::validate_identifier(&mut id_list, &fs.id)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(pmems) = &self.pmem {
|
||||
for pmem in pmems {
|
||||
pmem.validate(self)?;
|
||||
self.iommu |= pmem.iommu;
|
||||
|
||||
Self::validate_identifier(&mut id_list, &pmem.id)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.iommu |= self.rng.iommu;
|
||||
self.iommu |= self.console.iommu;
|
||||
|
||||
if let Some(t) = &self.cpus.topology {
|
||||
if t.threads_per_core == 0
|
||||
|| t.cores_per_die == 0
|
||||
@@ -2497,17 +2425,12 @@ impl VmConfig {
|
||||
|
||||
for user_device in user_devices {
|
||||
user_device.validate(self)?;
|
||||
|
||||
Self::validate_identifier(&mut id_list, &user_device.id)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(vdpa_devices) = &self.vdpa {
|
||||
for vdpa_device in vdpa_devices {
|
||||
vdpa_device.validate(self)?;
|
||||
self.iommu |= vdpa_device.iommu;
|
||||
|
||||
Self::validate_identifier(&mut id_list, &vdpa_device.id)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2531,17 +2454,11 @@ impl VmConfig {
|
||||
if let Some(devices) = &self.devices {
|
||||
for device in devices {
|
||||
device.validate(self)?;
|
||||
self.iommu |= device.iommu;
|
||||
|
||||
Self::validate_identifier(&mut id_list, &device.id)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(vsock) = &self.vsock {
|
||||
vsock.validate(self)?;
|
||||
self.iommu |= vsock.iommu;
|
||||
|
||||
Self::validate_identifier(&mut id_list, &vsock.id)?;
|
||||
}
|
||||
|
||||
if let Some(numa) = &self.numa {
|
||||
@@ -2562,37 +2479,22 @@ impl VmConfig {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(zones) = &self.memory.zones {
|
||||
for zone in zones.iter() {
|
||||
let id = zone.id.clone();
|
||||
Self::validate_identifier(&mut id_list, &Some(id))?;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
if let Some(sgx_epcs) = &self.sgx_epc {
|
||||
for sgx_epc in sgx_epcs.iter() {
|
||||
let id = sgx_epc.id.clone();
|
||||
Self::validate_identifier(&mut id_list, &Some(id))?;
|
||||
}
|
||||
}
|
||||
|
||||
self.platform.as_ref().map(|p| p.validate()).transpose()?;
|
||||
self.iommu |= self
|
||||
.platform
|
||||
.as_ref()
|
||||
.map(|p| p.iommu_segments.is_some())
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(id_list)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn parse(vm_params: VmParams) -> Result<Self> {
|
||||
let mut iommu = false;
|
||||
|
||||
let mut disks: Option<Vec<DiskConfig>> = None;
|
||||
if let Some(disk_list) = &vm_params.disks {
|
||||
let mut disk_config_list = Vec::new();
|
||||
for item in disk_list.iter() {
|
||||
let disk_config = DiskConfig::parse(item)?;
|
||||
if disk_config.iommu {
|
||||
iommu = true;
|
||||
}
|
||||
disk_config_list.push(disk_config);
|
||||
}
|
||||
disks = Some(disk_config_list);
|
||||
@@ -2603,12 +2505,18 @@ impl VmConfig {
|
||||
let mut net_config_list = Vec::new();
|
||||
for item in net_list.iter() {
|
||||
let net_config = NetConfig::parse(item)?;
|
||||
if net_config.iommu {
|
||||
iommu = true;
|
||||
}
|
||||
net_config_list.push(net_config);
|
||||
}
|
||||
net = Some(net_config_list);
|
||||
}
|
||||
|
||||
let rng = RngConfig::parse(vm_params.rng)?;
|
||||
if rng.iommu {
|
||||
iommu = true;
|
||||
}
|
||||
|
||||
let mut balloon: Option<BalloonConfig> = None;
|
||||
if let Some(balloon_params) = &vm_params.balloon {
|
||||
@@ -2629,12 +2537,18 @@ impl VmConfig {
|
||||
let mut pmem_config_list = Vec::new();
|
||||
for item in pmem_list.iter() {
|
||||
let pmem_config = PmemConfig::parse(item)?;
|
||||
if pmem_config.iommu {
|
||||
iommu = true;
|
||||
}
|
||||
pmem_config_list.push(pmem_config);
|
||||
}
|
||||
pmem = Some(pmem_config_list);
|
||||
}
|
||||
|
||||
let console = ConsoleConfig::parse(vm_params.console)?;
|
||||
if console.iommu {
|
||||
iommu = true;
|
||||
}
|
||||
let serial = ConsoleConfig::parse(vm_params.serial)?;
|
||||
|
||||
let mut devices: Option<Vec<DeviceConfig>> = None;
|
||||
@@ -2642,6 +2556,9 @@ impl VmConfig {
|
||||
let mut device_config_list = Vec::new();
|
||||
for item in device_list.iter() {
|
||||
let device_config = DeviceConfig::parse(item)?;
|
||||
if device_config.iommu {
|
||||
iommu = true;
|
||||
}
|
||||
device_config_list.push(device_config);
|
||||
}
|
||||
devices = Some(device_config_list);
|
||||
@@ -2662,6 +2579,9 @@ impl VmConfig {
|
||||
let mut vdpa_config_list = Vec::new();
|
||||
for item in vdpa_list.iter() {
|
||||
let vdpa_config = VdpaConfig::parse(item)?;
|
||||
if vdpa_config.iommu {
|
||||
iommu = true;
|
||||
}
|
||||
vdpa_config_list.push(vdpa_config);
|
||||
}
|
||||
vdpa = Some(vdpa_config_list);
|
||||
@@ -2670,10 +2590,18 @@ impl VmConfig {
|
||||
let mut vsock: Option<VsockConfig> = None;
|
||||
if let Some(vs) = &vm_params.vsock {
|
||||
let vsock_config = VsockConfig::parse(vs)?;
|
||||
if vsock_config.iommu {
|
||||
iommu = true;
|
||||
}
|
||||
vsock = Some(vsock_config);
|
||||
}
|
||||
|
||||
let platform = vm_params.platform.map(PlatformConfig::parse).transpose()?;
|
||||
if let Some(platform_config) = platform.as_ref() {
|
||||
if platform_config.iommu_segments.is_some() {
|
||||
iommu = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let mut sgx_epc: Option<Vec<SgxEpcConfig>> = None;
|
||||
@@ -2719,7 +2647,7 @@ impl VmConfig {
|
||||
#[cfg(feature = "gdb")]
|
||||
let gdb = vm_params.gdb;
|
||||
|
||||
let mut config = VmConfig {
|
||||
let config = VmConfig {
|
||||
cpus: CpusConfig::parse(vm_params.cpus)?,
|
||||
memory: MemoryConfig::parse(vm_params.memory, vm_params.memory_zones)?,
|
||||
kernel,
|
||||
@@ -2737,7 +2665,7 @@ impl VmConfig {
|
||||
user_devices,
|
||||
vdpa,
|
||||
vsock,
|
||||
iommu: false, // updated in VmConfig::validate()
|
||||
iommu,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
sgx_epc,
|
||||
numa,
|
||||
@@ -3333,7 +3261,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_config_validation() {
|
||||
let mut valid_config = VmConfig {
|
||||
let valid_config = VmConfig {
|
||||
cpus: CpusConfig {
|
||||
boot_vcpus: 1,
|
||||
max_vcpus: 1,
|
||||
@@ -3570,7 +3498,6 @@ mod tests {
|
||||
still_valid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
assert!(still_valid_config.validate().is_ok());
|
||||
|
||||
@@ -3578,7 +3505,6 @@ mod tests {
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
iommu_segments: Some(vec![17, 18]),
|
||||
..Default::default()
|
||||
});
|
||||
assert_eq!(
|
||||
invalid_config.validate(),
|
||||
@@ -3589,7 +3515,6 @@ mod tests {
|
||||
still_valid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
still_valid_config.disks = Some(vec![DiskConfig {
|
||||
iommu: true,
|
||||
@@ -3602,7 +3527,6 @@ mod tests {
|
||||
still_valid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
still_valid_config.net = Some(vec![NetConfig {
|
||||
iommu: true,
|
||||
@@ -3615,7 +3539,6 @@ mod tests {
|
||||
still_valid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
still_valid_config.pmem = Some(vec![PmemConfig {
|
||||
iommu: true,
|
||||
@@ -3628,7 +3551,6 @@ mod tests {
|
||||
still_valid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
still_valid_config.devices = Some(vec![DeviceConfig {
|
||||
iommu: true,
|
||||
@@ -3641,7 +3563,6 @@ mod tests {
|
||||
still_valid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
still_valid_config.vsock = Some(VsockConfig {
|
||||
iommu: true,
|
||||
@@ -3654,7 +3575,6 @@ mod tests {
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
invalid_config.disks = Some(vec![DiskConfig {
|
||||
iommu: false,
|
||||
@@ -3670,7 +3590,6 @@ mod tests {
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
invalid_config.net = Some(vec![NetConfig {
|
||||
iommu: false,
|
||||
@@ -3686,7 +3605,6 @@ mod tests {
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
invalid_config.pmem = Some(vec![PmemConfig {
|
||||
iommu: false,
|
||||
@@ -3702,7 +3620,6 @@ mod tests {
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
invalid_config.devices = Some(vec![DeviceConfig {
|
||||
iommu: false,
|
||||
@@ -3718,7 +3635,6 @@ mod tests {
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
invalid_config.vsock = Some(VsockConfig {
|
||||
iommu: false,
|
||||
@@ -3735,7 +3651,6 @@ mod tests {
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
invalid_config.user_devices = Some(vec![UserDeviceConfig {
|
||||
pci_segment: 1,
|
||||
@@ -3743,14 +3658,13 @@ mod tests {
|
||||
}]);
|
||||
assert_eq!(
|
||||
invalid_config.validate(),
|
||||
Err(ValidationError::IommuNotSupportedOnSegment(1))
|
||||
Err(ValidationError::IommuNotSupported(1))
|
||||
);
|
||||
|
||||
let mut invalid_config = valid_config.clone();
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
invalid_config.vdpa = Some(vec![VdpaConfig {
|
||||
pci_segment: 1,
|
||||
@@ -3766,7 +3680,6 @@ mod tests {
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
invalid_config.fs = Some(vec![FsConfig {
|
||||
pci_segment: 1,
|
||||
@@ -3774,7 +3687,7 @@ mod tests {
|
||||
}]);
|
||||
assert_eq!(
|
||||
invalid_config.validate(),
|
||||
Err(ValidationError::IommuNotSupportedOnSegment(1))
|
||||
Err(ValidationError::IommuNotSupported(1))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
125
vmm/src/cpu.rs
125
vmm/src/cpu.rs
@@ -30,13 +30,13 @@ use devices::interrupt_controller::InterruptController;
|
||||
use gdbstub_arch::x86::reg::{X86SegmentRegs, X86_64CoreRegs};
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use hypervisor::kvm::kvm_bindings;
|
||||
#[cfg(feature = "tdx")]
|
||||
use hypervisor::kvm::{TdxExitDetails, TdxExitStatus};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use hypervisor::x86_64::CpuId;
|
||||
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
||||
use hypervisor::x86_64::{SpecialRegisters, StandardRegisters};
|
||||
use hypervisor::{CpuState, HypervisorCpuError, VmExit, VmOps};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use hypervisor::CpuId;
|
||||
use hypervisor::{vm::VmmOps, CpuState, HypervisorCpuError, VmExit};
|
||||
#[cfg(feature = "tdx")]
|
||||
use hypervisor::{TdxExitDetails, TdxExitStatus};
|
||||
use libc::{c_void, siginfo_t};
|
||||
use seccompiler::{apply_filter, SeccompAction};
|
||||
use std::collections::BTreeMap;
|
||||
@@ -44,11 +44,9 @@ use std::os::unix::thread::JoinHandleExt;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use std::{cmp, io, result, thread};
|
||||
use thiserror::Error;
|
||||
use vm_device::BusDevice;
|
||||
#[cfg(feature = "gdb")]
|
||||
use vm_memory::{Bytes, GuestAddressSpace};
|
||||
use vm_memory::{GuestAddress, GuestMemoryAtomic};
|
||||
use vm_memory::GuestAddress;
|
||||
use vm_memory::GuestMemoryAtomic;
|
||||
use vm_migration::{
|
||||
Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable,
|
||||
Transportable,
|
||||
@@ -58,74 +56,75 @@ use vmm_sys_util::signal::{register_signal_handler, SIGRTMIN};
|
||||
|
||||
pub const CPU_MANAGER_ACPI_SIZE: usize = 0xc;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
#[error("Error creating vCPU: {0}")]
|
||||
VcpuCreate(#[source] anyhow::Error),
|
||||
/// Cannot create the vCPU.
|
||||
VcpuCreate(anyhow::Error),
|
||||
|
||||
#[error("Error running bCPU: {0}")]
|
||||
VcpuRun(#[source] anyhow::Error),
|
||||
/// Cannot run the VCPUs.
|
||||
VcpuRun(anyhow::Error),
|
||||
|
||||
#[error("Error spawning vCPU thread: {0}")]
|
||||
VcpuSpawn(#[source] io::Error),
|
||||
/// Cannot spawn a new vCPU thread.
|
||||
VcpuSpawn(io::Error),
|
||||
|
||||
#[error("Error generating common CPUID: {0}")]
|
||||
CommonCpuId(#[source] arch::Error),
|
||||
/// Cannot generate common CPUID
|
||||
CommonCpuId(arch::Error),
|
||||
|
||||
#[error("Error configuring vCPU: {0}")]
|
||||
VcpuConfiguration(#[source] arch::Error),
|
||||
/// Error configuring VCPU
|
||||
VcpuConfiguration(arch::Error),
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[error("Error fetching preferred target: {0}")]
|
||||
VcpuArmPreferredTarget(#[source] hypervisor::HypervisorVmError),
|
||||
/// Error fetching prefered target
|
||||
VcpuArmPreferredTarget(hypervisor::HypervisorVmError),
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[error("Error initialising vCPU: {0}")]
|
||||
VcpuArmInit(#[source] hypervisor::HypervisorCpuError),
|
||||
/// Error doing vCPU init on Arm.
|
||||
VcpuArmInit(hypervisor::HypervisorCpuError),
|
||||
|
||||
#[error("Failed to join on vCPU threads: {0:?}")]
|
||||
/// Failed to join on vCPU threads
|
||||
ThreadCleanup(std::boxed::Box<dyn std::any::Any + std::marker::Send>),
|
||||
|
||||
#[error("Error adding CpuManager to MMIO bus: {0}")]
|
||||
BusError(#[source] vm_device::BusError),
|
||||
/// Cannot add legacy device to Bus.
|
||||
BusError(vm_device::BusError),
|
||||
|
||||
#[error("Requested vCPUs exceed maximum")]
|
||||
/// Asking for more vCPUs that we can have
|
||||
DesiredVCpuCountExceedsMax,
|
||||
|
||||
#[error("Cannot create seccomp filter: {0}")]
|
||||
CreateSeccompFilter(#[source] seccompiler::Error),
|
||||
/// Cannot create seccomp filter
|
||||
CreateSeccompFilter(seccompiler::Error),
|
||||
|
||||
#[error("Cannot apply seccomp filter: {0}")]
|
||||
ApplySeccompFilter(#[source] seccompiler::Error),
|
||||
/// Cannot apply seccomp filter
|
||||
ApplySeccompFilter(seccompiler::Error),
|
||||
|
||||
#[error("Error starting vCPU after restore: {0}")]
|
||||
StartRestoreVcpu(#[source] anyhow::Error),
|
||||
/// Error starting vCPU after restore
|
||||
StartRestoreVcpu(anyhow::Error),
|
||||
|
||||
#[error("Unexpected VmExit")]
|
||||
/// Error because an unexpected VmExit type was received.
|
||||
UnexpectedVmExit,
|
||||
|
||||
#[error("Failed to allocate MMIO address for CpuManager")]
|
||||
/// Failed to allocate MMIO address
|
||||
AllocateMmmioAddress,
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
#[error("Error initializing TDX: {0}")]
|
||||
InitializeTdx(#[source] hypervisor::HypervisorCpuError),
|
||||
InitializeTdx(hypervisor::HypervisorCpuError),
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[error("Error initializing PMU: {0}")]
|
||||
InitPmu(#[source] hypervisor::HypervisorCpuError),
|
||||
InitPmu(hypervisor::HypervisorCpuError),
|
||||
|
||||
/// Failed scheduling the thread on the expected CPU set.
|
||||
ScheduleCpuSet,
|
||||
|
||||
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
||||
#[error("Error during CPU debug: {0}")]
|
||||
CpuDebug(#[source] hypervisor::HypervisorCpuError),
|
||||
/// Error on debug related CPU ops.
|
||||
CpuDebug(hypervisor::HypervisorCpuError),
|
||||
|
||||
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
||||
#[error("Error translating virtual address: {0}")]
|
||||
TranslateVirtualAddress(#[source] hypervisor::HypervisorCpuError),
|
||||
/// Failed to translate guest virtual address.
|
||||
TranslateVirtualAddress(hypervisor::HypervisorCpuError),
|
||||
|
||||
#[cfg(all(feature = "amx", target_arch = "x86_64"))]
|
||||
#[error("Error setting up AMX: {0}")]
|
||||
AmxEnable(#[source] anyhow::Error),
|
||||
/// "Failed to setup AMX.
|
||||
AmxEnable(anyhow::Error),
|
||||
}
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
@@ -255,14 +254,14 @@ impl Vcpu {
|
||||
///
|
||||
/// * `id` - Represents the CPU number between [0, max vcpus).
|
||||
/// * `vm` - The virtual machine this vcpu will get attached to.
|
||||
/// * `vm_ops` - Optional object for exit handling.
|
||||
/// * `vmmops` - Optional object for exit handling.
|
||||
pub fn new(
|
||||
id: u8,
|
||||
vm: &Arc<dyn hypervisor::Vm>,
|
||||
vm_ops: Option<Arc<dyn VmOps>>,
|
||||
vmmops: Option<Arc<dyn VmmOps>>,
|
||||
) -> Result<Self> {
|
||||
let vcpu = vm
|
||||
.create_vcpu(id, vm_ops)
|
||||
.create_vcpu(id, vmmops)
|
||||
.map_err(|e| Error::VcpuCreate(e.into()))?;
|
||||
// Initially the cpuid per vCPU is the one supported by this VM.
|
||||
Ok(Vcpu {
|
||||
@@ -412,7 +411,7 @@ pub struct CpuManager {
|
||||
selected_cpu: u8,
|
||||
vcpus: Vec<Arc<Mutex<Vcpu>>>,
|
||||
seccomp_action: SeccompAction,
|
||||
vm_ops: Arc<dyn VmOps>,
|
||||
vmmops: Arc<dyn VmmOps>,
|
||||
#[cfg_attr(target_arch = "aarch64", allow(dead_code))]
|
||||
acpi_address: Option<GuestAddress>,
|
||||
proximity_domain_per_cpu: BTreeMap<u8, u32>,
|
||||
@@ -562,7 +561,7 @@ impl CpuManager {
|
||||
#[cfg(feature = "gdb")] vm_debug_evt: EventFd,
|
||||
hypervisor: Arc<dyn hypervisor::Hypervisor>,
|
||||
seccomp_action: SeccompAction,
|
||||
vm_ops: Arc<dyn VmOps>,
|
||||
vmmops: Arc<dyn VmmOps>,
|
||||
#[cfg(feature = "tdx")] tdx_enabled: bool,
|
||||
numa_nodes: &NumaNodes,
|
||||
) -> Result<Arc<Mutex<CpuManager>>> {
|
||||
@@ -684,7 +683,7 @@ impl CpuManager {
|
||||
selected_cpu: 0,
|
||||
vcpus: Vec::with_capacity(usize::from(config.max_vcpus)),
|
||||
seccomp_action,
|
||||
vm_ops,
|
||||
vmmops,
|
||||
acpi_address,
|
||||
proximity_domain_per_cpu,
|
||||
affinity,
|
||||
@@ -713,7 +712,7 @@ impl CpuManager {
|
||||
) -> Result<()> {
|
||||
info!("Creating vCPU: cpu_id = {}", cpu_id);
|
||||
|
||||
let mut vcpu = Vcpu::new(cpu_id, &self.vm, Some(self.vm_ops.clone()))?;
|
||||
let mut vcpu = Vcpu::new(cpu_id, &self.vm, Some(self.vmmops.clone()))?;
|
||||
|
||||
if let Some(snapshot) = snapshot {
|
||||
// AArch64 vCPUs should be initialized after created.
|
||||
@@ -989,8 +988,8 @@ impl CpuManager {
|
||||
}
|
||||
#[cfg(feature = "tdx")]
|
||||
VmExit::Tdx => {
|
||||
if let Some(vcpu) = Arc::get_mut(&mut vcpu.vcpu) {
|
||||
match vcpu.get_tdx_exit_details() {
|
||||
if let Some(vcpu_fd) = Arc::get_mut(&mut vcpu.vcpu) {
|
||||
match vcpu_fd.get_tdx_exit_details() {
|
||||
Ok(details) => match details {
|
||||
TdxExitDetails::GetQuote => warn!("TDG_VP_VMCALL_GET_QUOTE not supported"),
|
||||
TdxExitDetails::SetupEventNotifyInterrupt => {
|
||||
@@ -999,7 +998,7 @@ impl CpuManager {
|
||||
},
|
||||
Err(e) => error!("Unexpected TDX VMCALL: {}", e),
|
||||
}
|
||||
vcpu.set_tdx_status(TdxExitStatus::InvalidOperand);
|
||||
vcpu_fd.set_tdx_status(TdxExitStatus::InvalidOperand);
|
||||
} else {
|
||||
// We should never reach this code as
|
||||
// this means the design from the code
|
||||
@@ -2047,11 +2046,10 @@ impl Debuggable for CpuManager {
|
||||
};
|
||||
let psize = arch::PAGE_SIZE as u64;
|
||||
let read_len = std::cmp::min(len as u64 - total_read, psize - (paddr & (psize - 1)));
|
||||
self.vm_memory
|
||||
.memory()
|
||||
.read(
|
||||
self.vmmops
|
||||
.guest_mem_read(
|
||||
paddr,
|
||||
&mut buf[total_read as usize..total_read as usize + read_len as usize],
|
||||
GuestAddress(paddr),
|
||||
)
|
||||
.map_err(DebuggableError::ReadMem)?;
|
||||
total_read += read_len;
|
||||
@@ -2080,11 +2078,10 @@ impl Debuggable for CpuManager {
|
||||
data.len() as u64 - total_written,
|
||||
psize - (paddr & (psize - 1)),
|
||||
);
|
||||
self.vm_memory
|
||||
.memory()
|
||||
.write(
|
||||
self.vmmops
|
||||
.guest_mem_write(
|
||||
paddr,
|
||||
&data[total_written as usize..total_written as usize + write_len as usize],
|
||||
GuestAddress(paddr),
|
||||
)
|
||||
.map_err(DebuggableError::WriteMem)?;
|
||||
total_written += write_len;
|
||||
|
||||
@@ -14,8 +14,11 @@ use crate::config::{
|
||||
VdpaConfig, VhostMode, VmConfig, VsockConfig,
|
||||
};
|
||||
use crate::device_tree::{DeviceNode, DeviceTree};
|
||||
#[cfg(feature = "kvm")]
|
||||
use crate::interrupt::kvm::KvmMsiInterruptManager as MsiInterruptManager;
|
||||
#[cfg(feature = "mshv")]
|
||||
use crate::interrupt::mshv::MshvMsiInterruptManager as MsiInterruptManager;
|
||||
use crate::interrupt::LegacyUserspaceInterruptManager;
|
||||
use crate::interrupt::MsiInterruptManager;
|
||||
use crate::memory_manager::MEMORY_MANAGER_ACPI_SIZE;
|
||||
use crate::memory_manager::{Error as MemoryManagerError, MemoryManager};
|
||||
use crate::pci_segment::PciSegment;
|
||||
@@ -53,7 +56,11 @@ use devices::legacy::Serial;
|
||||
use devices::{
|
||||
interrupt_controller, interrupt_controller::InterruptController, AcpiNotificationFlags,
|
||||
};
|
||||
use hypervisor::{DeviceFd, HypervisorVmError, IoEventAddress};
|
||||
#[cfg(feature = "kvm")]
|
||||
use hypervisor::kvm_ioctls::*;
|
||||
use hypervisor::DeviceFd;
|
||||
#[cfg(feature = "mshv")]
|
||||
use hypervisor::IoEventAddress;
|
||||
use libc::{
|
||||
cfmakeraw, isatty, tcgetattr, tcsetattr, termios, MAP_NORESERVE, MAP_PRIVATE, MAP_SHARED,
|
||||
O_TMPFILE, PROT_READ, PROT_WRITE, TCSANOW,
|
||||
@@ -65,8 +72,7 @@ use pci::{
|
||||
VfioUserPciDevice, VfioUserPciDeviceError,
|
||||
};
|
||||
use seccompiler::SeccompAction;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::fs::{read_link, File, OpenOptions};
|
||||
use std::io::{self, stdout, Seek, SeekFrom};
|
||||
@@ -77,7 +83,6 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::path::PathBuf;
|
||||
use std::result;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Instant;
|
||||
use vfio_ioctls::{VfioContainer, VfioDevice};
|
||||
use virtio_devices::transport::VirtioPciDevice;
|
||||
use virtio_devices::transport::VirtioTransport;
|
||||
@@ -110,28 +115,30 @@ use vmm_sys_util::eventfd::EventFd;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
const MMIO_LEN: u64 = 0x1000;
|
||||
|
||||
// Singleton devices / devices the user cannot name
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const IOAPIC_DEVICE_NAME: &str = "__ioapic";
|
||||
const SERIAL_DEVICE_NAME: &str = "__serial";
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
const GPIO_DEVICE_NAME: &str = "__gpio";
|
||||
const RNG_DEVICE_NAME: &str = "__rng";
|
||||
const IOMMU_DEVICE_NAME: &str = "__iommu";
|
||||
const BALLOON_DEVICE_NAME: &str = "__balloon";
|
||||
const CONSOLE_DEVICE_NAME: &str = "__console";
|
||||
const VFIO_DEVICE_NAME_PREFIX: &str = "_vfio";
|
||||
|
||||
// Devices that the user may name and for which we generate
|
||||
// identifiers if the user doesn't give one
|
||||
const VFIO_USER_DEVICE_NAME_PREFIX: &str = "_vfio_user";
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const IOAPIC_DEVICE_NAME: &str = "_ioapic";
|
||||
|
||||
const SERIAL_DEVICE_NAME_PREFIX: &str = "_serial";
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
const GPIO_DEVICE_NAME_PREFIX: &str = "_gpio";
|
||||
|
||||
const CONSOLE_DEVICE_NAME: &str = "_console";
|
||||
const DISK_DEVICE_NAME_PREFIX: &str = "_disk";
|
||||
const FS_DEVICE_NAME_PREFIX: &str = "_fs";
|
||||
const BALLOON_DEVICE_NAME: &str = "_balloon";
|
||||
const NET_DEVICE_NAME_PREFIX: &str = "_net";
|
||||
const PMEM_DEVICE_NAME_PREFIX: &str = "_pmem";
|
||||
const RNG_DEVICE_NAME: &str = "_rng";
|
||||
const VDPA_DEVICE_NAME_PREFIX: &str = "_vdpa";
|
||||
const VSOCK_DEVICE_NAME_PREFIX: &str = "_vsock";
|
||||
const WATCHDOG_DEVICE_NAME: &str = "__watchdog";
|
||||
const VFIO_DEVICE_NAME_PREFIX: &str = "_vfio";
|
||||
const VFIO_USER_DEVICE_NAME_PREFIX: &str = "_vfio_user";
|
||||
const WATCHDOG_DEVICE_NAME: &str = "_watchdog";
|
||||
|
||||
const IOMMU_DEVICE_NAME: &str = "_iommu";
|
||||
|
||||
const VIRTIO_PCI_DEVICE_NAME_PREFIX: &str = "_virtio-pci";
|
||||
|
||||
/// Errors associated with device manager
|
||||
@@ -392,6 +399,9 @@ pub enum DeviceManagerError {
|
||||
/// Resource was already found.
|
||||
ResourceAlreadyExists,
|
||||
|
||||
/// Expected resources for virtio-pci could not be found.
|
||||
MissingVirtioPciResources,
|
||||
|
||||
/// Expected resources for virtio-pmem could not be found.
|
||||
MissingVirtioPmemResources,
|
||||
|
||||
@@ -469,13 +479,7 @@ pub enum DeviceManagerError {
|
||||
InvalidIommuHotplug,
|
||||
|
||||
/// Failed to create UEFI flash
|
||||
CreateUefiFlash(HypervisorVmError),
|
||||
|
||||
/// Invalid identifier as it is not unique.
|
||||
IdentifierNotUnique(String),
|
||||
|
||||
/// Invalid identifier
|
||||
InvalidIdentifier(String),
|
||||
CreateUefiFlash(hypervisor::vm::HypervisorVmError),
|
||||
}
|
||||
pub type DeviceManagerResult<T> = result::Result<T, DeviceManagerError>;
|
||||
|
||||
@@ -659,13 +663,19 @@ impl DeviceRelocation for AddressManager {
|
||||
}
|
||||
}
|
||||
|
||||
// Update the device_tree resources associated with the device
|
||||
if let Some(id) = pci_dev.id() {
|
||||
if let Some(node) = self.device_tree.lock().unwrap().get_mut(&id) {
|
||||
let any_dev = pci_dev.as_any();
|
||||
if let Some(virtio_pci_dev) = any_dev.downcast_ref::<VirtioPciDevice>() {
|
||||
// Update the device_tree resources associated with the device
|
||||
if let Some(node) = self
|
||||
.device_tree
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get_mut(&virtio_pci_dev.id())
|
||||
{
|
||||
let mut resource_updated = false;
|
||||
for resource in node.resources.iter_mut() {
|
||||
if let Resource::PciBar { base, type_, .. } = resource {
|
||||
if PciBarRegionType::from(*type_) == region_type && *base == old_base {
|
||||
if let Resource::MmioAddressRange { base, .. } = resource {
|
||||
if *base == old_base {
|
||||
*base = new_base;
|
||||
resource_updated = true;
|
||||
break;
|
||||
@@ -678,20 +688,21 @@ impl DeviceRelocation for AddressManager {
|
||||
io::ErrorKind::Other,
|
||||
format!(
|
||||
"Couldn't find a resource with base 0x{:x} for device {}",
|
||||
old_base, id
|
||||
old_base,
|
||||
virtio_pci_dev.id()
|
||||
),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("Couldn't find device {} from device tree", id),
|
||||
format!(
|
||||
"Couldn't find device {} from device tree",
|
||||
virtio_pci_dev.id()
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let any_dev = pci_dev.as_any();
|
||||
if let Some(virtio_pci_dev) = any_dev.downcast_ref::<VirtioPciDevice>() {
|
||||
let bar_addr = virtio_pci_dev.config_bar_addr();
|
||||
if bar_addr == new_base {
|
||||
for (event, addr) in virtio_pci_dev.ioeventfds(old_base) {
|
||||
@@ -833,8 +844,7 @@ pub struct DeviceManager {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
interrupt_controller: Option<Arc<Mutex<gic::Gic>>>,
|
||||
|
||||
// Things to be added to the commandline (e.g. aarch64 early console)
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
// Things to be added to the commandline (i.e. for virtio-mmio)
|
||||
cmdline_additions: Vec<String>,
|
||||
|
||||
// ACPI GED notification device
|
||||
@@ -933,12 +943,6 @@ pub struct DeviceManager {
|
||||
|
||||
// io_uring availability if detected
|
||||
io_uring_supported: Option<bool>,
|
||||
|
||||
// List of unique identifiers provided at boot through the configuration.
|
||||
boot_id_list: BTreeSet<String>,
|
||||
|
||||
// Start time of the VM
|
||||
timestamp: Instant,
|
||||
}
|
||||
|
||||
impl DeviceManager {
|
||||
@@ -954,8 +958,6 @@ impl DeviceManager {
|
||||
activate_evt: &EventFd,
|
||||
force_iommu: bool,
|
||||
restoring: bool,
|
||||
boot_id_list: BTreeSet<String>,
|
||||
timestamp: Instant,
|
||||
) -> DeviceManagerResult<Arc<Mutex<Self>>> {
|
||||
let device_tree = Arc::new(Mutex::new(DeviceTree::new()));
|
||||
|
||||
@@ -1037,8 +1039,8 @@ impl DeviceManager {
|
||||
address_manager: Arc::clone(&address_manager),
|
||||
console: Arc::new(Console::default()),
|
||||
interrupt_controller: None,
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
cmdline_additions: Vec::new(),
|
||||
|
||||
ged_notification_device: None,
|
||||
config,
|
||||
memory_manager,
|
||||
@@ -1059,12 +1061,15 @@ impl DeviceManager {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
id_to_dev_info: HashMap::new(),
|
||||
seccomp_action,
|
||||
|
||||
numa_nodes,
|
||||
balloon: None,
|
||||
activate_evt: activate_evt
|
||||
.try_clone()
|
||||
.map_err(DeviceManagerError::EventFd)?,
|
||||
|
||||
acpi_address,
|
||||
|
||||
selected_segment: 0,
|
||||
serial_pty: None,
|
||||
serial_manager: None,
|
||||
@@ -1078,8 +1083,6 @@ impl DeviceManager {
|
||||
force_iommu,
|
||||
restoring,
|
||||
io_uring_supported: None,
|
||||
boot_id_list,
|
||||
timestamp,
|
||||
};
|
||||
|
||||
let device_manager = Arc::new(Mutex::new(device_manager));
|
||||
@@ -1521,15 +1524,6 @@ impl DeviceManager {
|
||||
.map_err(DeviceManagerError::BusError)?;
|
||||
}
|
||||
|
||||
// 0x80 debug port
|
||||
let debug_port = Arc::new(Mutex::new(devices::legacy::DebugPort::new(self.timestamp)));
|
||||
self.bus_devices
|
||||
.push(Arc::clone(&debug_port) as Arc<Mutex<dyn BusDevice>>);
|
||||
self.address_manager
|
||||
.io_bus
|
||||
.insert(debug_port, 0x80, 0x1)
|
||||
.map_err(DeviceManagerError::BusError)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1575,7 +1569,7 @@ impl DeviceManager {
|
||||
);
|
||||
|
||||
// Add a GPIO device
|
||||
let id = String::from(GPIO_DEVICE_NAME);
|
||||
let id = String::from(GPIO_DEVICE_NAME_PREFIX);
|
||||
let gpio_irq = self
|
||||
.address_manager
|
||||
.allocator
|
||||
@@ -1665,7 +1659,7 @@ impl DeviceManager {
|
||||
// Serial is tied to IRQ #4
|
||||
let serial_irq = 4;
|
||||
|
||||
let id = String::from(SERIAL_DEVICE_NAME);
|
||||
let id = String::from(SERIAL_DEVICE_NAME_PREFIX);
|
||||
|
||||
let interrupt_group = interrupt_manager
|
||||
.create_group(LegacyIrqGroupConfig {
|
||||
@@ -1711,7 +1705,7 @@ impl DeviceManager {
|
||||
interrupt_manager: &Arc<dyn InterruptManager<GroupConfig = LegacyIrqGroupConfig>>,
|
||||
serial_writer: Option<Box<dyn io::Write + Send>>,
|
||||
) -> DeviceManagerResult<Arc<Mutex<Pl011>>> {
|
||||
let id = String::from(SERIAL_DEVICE_NAME);
|
||||
let id = String::from(SERIAL_DEVICE_NAME_PREFIX);
|
||||
|
||||
let serial_irq = self
|
||||
.address_manager
|
||||
@@ -1731,7 +1725,6 @@ impl DeviceManager {
|
||||
id.clone(),
|
||||
interrupt_group,
|
||||
serial_writer,
|
||||
self.timestamp,
|
||||
)));
|
||||
|
||||
self.bus_devices
|
||||
@@ -2039,14 +2032,14 @@ impl DeviceManager {
|
||||
|
||||
info!("Creating virtio-block device: {:?}", disk_cfg);
|
||||
|
||||
let (virtio_device, migratable_device) = if disk_cfg.vhost_user {
|
||||
if disk_cfg.vhost_user {
|
||||
let socket = disk_cfg.vhost_socket.as_ref().unwrap().clone();
|
||||
let vu_cfg = VhostUserConfig {
|
||||
socket,
|
||||
num_queues: disk_cfg.num_queues,
|
||||
queue_size: disk_cfg.queue_size,
|
||||
};
|
||||
let vhost_user_block = Arc::new(Mutex::new(
|
||||
let vhost_user_block_device = Arc::new(Mutex::new(
|
||||
match virtio_devices::vhost_user::Blk::new(
|
||||
id.clone(),
|
||||
vu_cfg,
|
||||
@@ -2064,10 +2057,22 @@ impl DeviceManager {
|
||||
},
|
||||
));
|
||||
|
||||
(
|
||||
Arc::clone(&vhost_user_block) as Arc<Mutex<dyn virtio_devices::VirtioDevice>>,
|
||||
vhost_user_block as Arc<Mutex<dyn Migratable>>,
|
||||
)
|
||||
// Fill the device tree with a new node. In case of restore, we
|
||||
// know there is nothing to do, so we can simply override the
|
||||
// existing entry.
|
||||
self.device_tree
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(id.clone(), device_node!(id, vhost_user_block_device));
|
||||
|
||||
Ok(MetaVirtioDevice {
|
||||
virtio_device: Arc::clone(&vhost_user_block_device)
|
||||
as Arc<Mutex<dyn virtio_devices::VirtioDevice>>,
|
||||
iommu: false,
|
||||
id,
|
||||
pci_segment: disk_cfg.pci_segment,
|
||||
dma_handler: None,
|
||||
})
|
||||
} else {
|
||||
let mut options = OpenOptions::new();
|
||||
options.read(true);
|
||||
@@ -2133,7 +2138,7 @@ impl DeviceManager {
|
||||
}
|
||||
};
|
||||
|
||||
let virtio_block = Arc::new(Mutex::new(
|
||||
let dev = Arc::new(Mutex::new(
|
||||
virtio_devices::Block::new(
|
||||
id.clone(),
|
||||
image,
|
||||
@@ -2155,27 +2160,25 @@ impl DeviceManager {
|
||||
.map_err(DeviceManagerError::CreateVirtioBlock)?,
|
||||
));
|
||||
|
||||
(
|
||||
Arc::clone(&virtio_block) as Arc<Mutex<dyn virtio_devices::VirtioDevice>>,
|
||||
virtio_block as Arc<Mutex<dyn Migratable>>,
|
||||
)
|
||||
};
|
||||
let virtio_device = Arc::clone(&dev) as Arc<Mutex<dyn virtio_devices::VirtioDevice>>;
|
||||
let migratable_device = dev as Arc<Mutex<dyn Migratable>>;
|
||||
|
||||
// Fill the device tree with a new node. In case of restore, we
|
||||
// know there is nothing to do, so we can simply override the
|
||||
// existing entry.
|
||||
self.device_tree
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(id.clone(), device_node!(id, migratable_device));
|
||||
// Fill the device tree with a new node. In case of restore, we
|
||||
// know there is nothing to do, so we can simply override the
|
||||
// existing entry.
|
||||
self.device_tree
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(id.clone(), device_node!(id, migratable_device));
|
||||
|
||||
Ok(MetaVirtioDevice {
|
||||
virtio_device,
|
||||
iommu: disk_cfg.iommu,
|
||||
id,
|
||||
pci_segment: disk_cfg.pci_segment,
|
||||
dma_handler: None,
|
||||
})
|
||||
Ok(MetaVirtioDevice {
|
||||
virtio_device,
|
||||
iommu: disk_cfg.iommu,
|
||||
id,
|
||||
pci_segment: disk_cfg.pci_segment,
|
||||
dma_handler: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn make_virtio_block_devices(&mut self) -> DeviceManagerResult<Vec<MetaVirtioDevice>> {
|
||||
@@ -2205,7 +2208,7 @@ impl DeviceManager {
|
||||
};
|
||||
info!("Creating virtio-net device: {:?}", net_cfg);
|
||||
|
||||
let (virtio_device, migratable_device) = if net_cfg.vhost_user {
|
||||
if net_cfg.vhost_user {
|
||||
let socket = net_cfg.vhost_socket.as_ref().unwrap().clone();
|
||||
let vu_cfg = VhostUserConfig {
|
||||
socket,
|
||||
@@ -2216,7 +2219,7 @@ impl DeviceManager {
|
||||
VhostMode::Client => false,
|
||||
VhostMode::Server => true,
|
||||
};
|
||||
let vhost_user_net = Arc::new(Mutex::new(
|
||||
let vhost_user_net_device = Arc::new(Mutex::new(
|
||||
match virtio_devices::vhost_user::Net::new(
|
||||
id.clone(),
|
||||
net_cfg.mac,
|
||||
@@ -2236,12 +2239,24 @@ impl DeviceManager {
|
||||
},
|
||||
));
|
||||
|
||||
(
|
||||
Arc::clone(&vhost_user_net) as Arc<Mutex<dyn virtio_devices::VirtioDevice>>,
|
||||
vhost_user_net as Arc<Mutex<dyn Migratable>>,
|
||||
)
|
||||
// Fill the device tree with a new node. In case of restore, we
|
||||
// know there is nothing to do, so we can simply override the
|
||||
// existing entry.
|
||||
self.device_tree
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(id.clone(), device_node!(id, vhost_user_net_device));
|
||||
|
||||
Ok(MetaVirtioDevice {
|
||||
virtio_device: Arc::clone(&vhost_user_net_device)
|
||||
as Arc<Mutex<dyn virtio_devices::VirtioDevice>>,
|
||||
iommu: net_cfg.iommu,
|
||||
id,
|
||||
pci_segment: net_cfg.pci_segment,
|
||||
dma_handler: None,
|
||||
})
|
||||
} else {
|
||||
let virtio_net = if let Some(ref tap_if_name) = net_cfg.tap {
|
||||
let virtio_net_device = if let Some(ref tap_if_name) = net_cfg.tap {
|
||||
Arc::new(Mutex::new(
|
||||
virtio_devices::Net::new(
|
||||
id.clone(),
|
||||
@@ -2299,27 +2314,23 @@ impl DeviceManager {
|
||||
))
|
||||
};
|
||||
|
||||
(
|
||||
Arc::clone(&virtio_net) as Arc<Mutex<dyn virtio_devices::VirtioDevice>>,
|
||||
virtio_net as Arc<Mutex<dyn Migratable>>,
|
||||
)
|
||||
};
|
||||
// Fill the device tree with a new node. In case of restore, we
|
||||
// know there is nothing to do, so we can simply override the
|
||||
// existing entry.
|
||||
self.device_tree
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(id.clone(), device_node!(id, virtio_net_device));
|
||||
|
||||
// Fill the device tree with a new node. In case of restore, we
|
||||
// know there is nothing to do, so we can simply override the
|
||||
// existing entry.
|
||||
self.device_tree
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(id.clone(), device_node!(id, migratable_device));
|
||||
|
||||
Ok(MetaVirtioDevice {
|
||||
virtio_device,
|
||||
iommu: net_cfg.iommu,
|
||||
id,
|
||||
pci_segment: net_cfg.pci_segment,
|
||||
dma_handler: None,
|
||||
})
|
||||
Ok(MetaVirtioDevice {
|
||||
virtio_device: Arc::clone(&virtio_net_device)
|
||||
as Arc<Mutex<dyn virtio_devices::VirtioDevice>>,
|
||||
iommu: net_cfg.iommu,
|
||||
id,
|
||||
pci_segment: net_cfg.pci_segment,
|
||||
dma_handler: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Add virto-net and vhost-user-net devices
|
||||
@@ -3003,9 +3014,7 @@ impl DeviceManager {
|
||||
// Increment the counter.
|
||||
self.device_id_cnt += Wrapping(1);
|
||||
// Check if the name is already in use.
|
||||
if !self.boot_id_list.contains(&name)
|
||||
&& !self.device_tree.lock().unwrap().contains_key(&name)
|
||||
{
|
||||
if !self.device_tree.lock().unwrap().contains_key(&name) {
|
||||
return Ok(name);
|
||||
}
|
||||
|
||||
@@ -3073,20 +3082,8 @@ impl DeviceManager {
|
||||
&mut self,
|
||||
device_cfg: &mut DeviceConfig,
|
||||
) -> DeviceManagerResult<(PciBdf, String)> {
|
||||
let vfio_name = if let Some(id) = &device_cfg.id {
|
||||
if self.device_tree.lock().unwrap().contains_key(id) {
|
||||
return Err(DeviceManagerError::DeviceIdAlreadyInUse);
|
||||
}
|
||||
|
||||
id.clone()
|
||||
} else {
|
||||
let id = self.next_device_name(VFIO_DEVICE_NAME_PREFIX)?;
|
||||
device_cfg.id = Some(id.clone());
|
||||
id
|
||||
};
|
||||
|
||||
let (pci_segment_id, pci_device_bdf, resources) =
|
||||
self.pci_resources(&vfio_name, device_cfg.pci_segment)?;
|
||||
let pci_segment_id = device_cfg.pci_segment;
|
||||
let pci_device_bdf = self.pci_segments[pci_segment_id as usize].next_device_bdf()?;
|
||||
|
||||
let mut needs_dma_mapping = false;
|
||||
|
||||
@@ -3178,25 +3175,35 @@ impl DeviceManager {
|
||||
};
|
||||
|
||||
let vfio_pci_device = VfioPciDevice::new(
|
||||
vfio_name.clone(),
|
||||
&self.address_manager.vm,
|
||||
vfio_device,
|
||||
vfio_container,
|
||||
self.msi_interrupt_manager.clone(),
|
||||
&self.msi_interrupt_manager,
|
||||
legacy_interrupt_group,
|
||||
device_cfg.iommu,
|
||||
pci_device_bdf,
|
||||
)
|
||||
.map_err(DeviceManagerError::VfioPciCreate)?;
|
||||
|
||||
let vfio_name = if let Some(id) = &device_cfg.id {
|
||||
if self.device_tree.lock().unwrap().contains_key(id) {
|
||||
return Err(DeviceManagerError::DeviceIdAlreadyInUse);
|
||||
}
|
||||
|
||||
id.clone()
|
||||
} else {
|
||||
let id = self.next_device_name(VFIO_DEVICE_NAME_PREFIX)?;
|
||||
device_cfg.id = Some(id.clone());
|
||||
id
|
||||
};
|
||||
|
||||
let vfio_pci_device = Arc::new(Mutex::new(vfio_pci_device));
|
||||
|
||||
let new_resources = self.add_pci_device(
|
||||
self.add_pci_device(
|
||||
vfio_pci_device.clone(),
|
||||
vfio_pci_device.clone(),
|
||||
pci_segment_id,
|
||||
pci_device_bdf,
|
||||
resources,
|
||||
)?;
|
||||
|
||||
vfio_pci_device
|
||||
@@ -3209,8 +3216,13 @@ impl DeviceManager {
|
||||
|
||||
let mut node = device_node!(vfio_name);
|
||||
|
||||
// Update the device tree with correct resource information.
|
||||
node.resources = new_resources;
|
||||
for region in vfio_pci_device.lock().unwrap().mmio_regions() {
|
||||
node.resources.push(Resource::MmioAddressRange {
|
||||
base: region.start.0,
|
||||
size: region.length as u64,
|
||||
});
|
||||
}
|
||||
|
||||
node.pci_bdf = Some(pci_device_bdf);
|
||||
node.pci_device_handle = Some(PciDeviceHandle::Vfio(vfio_pci_device));
|
||||
|
||||
@@ -3228,8 +3240,7 @@ impl DeviceManager {
|
||||
pci_device: Arc<Mutex<dyn PciDevice>>,
|
||||
segment_id: u16,
|
||||
bdf: PciBdf,
|
||||
resources: Option<Vec<Resource>>,
|
||||
) -> DeviceManagerResult<Vec<Resource>> {
|
||||
) -> DeviceManagerResult<Vec<(GuestAddress, GuestUsize, PciBarRegionType)>> {
|
||||
let bars = pci_device
|
||||
.lock()
|
||||
.unwrap()
|
||||
@@ -3239,7 +3250,6 @@ impl DeviceManager {
|
||||
.allocator
|
||||
.lock()
|
||||
.unwrap(),
|
||||
resources,
|
||||
)
|
||||
.map_err(DeviceManagerError::AllocateBars)?;
|
||||
|
||||
@@ -3264,18 +3274,7 @@ impl DeviceManager {
|
||||
)
|
||||
.map_err(DeviceManagerError::AddPciDevice)?;
|
||||
|
||||
let mut new_resources = Vec::new();
|
||||
for bar in bars {
|
||||
new_resources.push(Resource::PciBar {
|
||||
index: bar.idx(),
|
||||
base: bar.addr(),
|
||||
size: bar.size(),
|
||||
type_: bar.region_type().into(),
|
||||
prefetchable: bar.prefetchable().into(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(new_resources)
|
||||
Ok(bars)
|
||||
}
|
||||
|
||||
fn add_vfio_devices(&mut self) -> DeviceManagerResult<Vec<PciBdf>> {
|
||||
@@ -3301,20 +3300,8 @@ impl DeviceManager {
|
||||
&mut self,
|
||||
device_cfg: &mut UserDeviceConfig,
|
||||
) -> DeviceManagerResult<(PciBdf, String)> {
|
||||
let vfio_user_name = if let Some(id) = &device_cfg.id {
|
||||
if self.device_tree.lock().unwrap().contains_key(id) {
|
||||
return Err(DeviceManagerError::DeviceIdAlreadyInUse);
|
||||
}
|
||||
|
||||
id.clone()
|
||||
} else {
|
||||
let id = self.next_device_name(VFIO_USER_DEVICE_NAME_PREFIX)?;
|
||||
device_cfg.id = Some(id.clone());
|
||||
id
|
||||
};
|
||||
|
||||
let (pci_segment_id, pci_device_bdf, resources) =
|
||||
self.pci_resources(&vfio_user_name, device_cfg.pci_segment)?;
|
||||
let pci_segment_id = device_cfg.pci_segment;
|
||||
let pci_device_bdf = self.pci_segments[pci_segment_id as usize].next_device_bdf()?;
|
||||
|
||||
let legacy_interrupt_group =
|
||||
if let Some(legacy_interrupt_manager) = &self.legacy_interrupt_manager {
|
||||
@@ -3337,15 +3324,20 @@ impl DeviceManager {
|
||||
));
|
||||
|
||||
let mut vfio_user_pci_device = VfioUserPciDevice::new(
|
||||
vfio_user_name.clone(),
|
||||
&self.address_manager.vm,
|
||||
client.clone(),
|
||||
self.msi_interrupt_manager.clone(),
|
||||
&self.msi_interrupt_manager,
|
||||
legacy_interrupt_group,
|
||||
pci_device_bdf,
|
||||
)
|
||||
.map_err(DeviceManagerError::VfioUserCreate)?;
|
||||
|
||||
vfio_user_pci_device
|
||||
.map_mmio_regions(&self.address_manager.vm, || {
|
||||
self.memory_manager.lock().unwrap().allocate_memory_slot()
|
||||
})
|
||||
.map_err(DeviceManagerError::VfioUserMapRegion)?;
|
||||
|
||||
let memory = self.memory_manager.lock().unwrap().guest_memory();
|
||||
let vfio_user_mapping = Arc::new(VfioUserDmaMapping::new(client, Arc::new(memory)));
|
||||
for virtio_mem_device in self.virtio_mem_devices.iter() {
|
||||
@@ -3369,28 +3361,27 @@ impl DeviceManager {
|
||||
|
||||
let vfio_user_pci_device = Arc::new(Mutex::new(vfio_user_pci_device));
|
||||
|
||||
let new_resources = self.add_pci_device(
|
||||
let vfio_user_name = if let Some(id) = &device_cfg.id {
|
||||
if self.device_tree.lock().unwrap().contains_key(id) {
|
||||
return Err(DeviceManagerError::DeviceIdAlreadyInUse);
|
||||
}
|
||||
|
||||
id.clone()
|
||||
} else {
|
||||
let id = self.next_device_name(VFIO_USER_DEVICE_NAME_PREFIX)?;
|
||||
device_cfg.id = Some(id.clone());
|
||||
id
|
||||
};
|
||||
|
||||
self.add_pci_device(
|
||||
vfio_user_pci_device.clone(),
|
||||
vfio_user_pci_device.clone(),
|
||||
pci_segment_id,
|
||||
pci_device_bdf,
|
||||
resources,
|
||||
)?;
|
||||
|
||||
// Note it is required to call 'add_pci_device()' in advance to have the list of
|
||||
// mmio regions provisioned correctly
|
||||
vfio_user_pci_device
|
||||
.lock()
|
||||
.unwrap()
|
||||
.map_mmio_regions(&self.address_manager.vm, || {
|
||||
self.memory_manager.lock().unwrap().allocate_memory_slot()
|
||||
})
|
||||
.map_err(DeviceManagerError::VfioUserMapRegion)?;
|
||||
|
||||
let mut node = device_node!(vfio_user_name);
|
||||
|
||||
// Update the device tree with correct resource information.
|
||||
node.resources = new_resources;
|
||||
node.pci_bdf = Some(pci_device_bdf);
|
||||
node.pci_device_handle = Some(PciDeviceHandle::VfioUser(vfio_user_pci_device));
|
||||
|
||||
@@ -3431,8 +3422,44 @@ impl DeviceManager {
|
||||
let mut node = device_node!(id);
|
||||
node.children = vec![virtio_device_id.clone()];
|
||||
|
||||
let (pci_segment_id, pci_device_bdf, resources) =
|
||||
self.pci_resources(&id, pci_segment_id)?;
|
||||
// Look for the id in the device tree. If it can be found, that means
|
||||
// the device is being restored, otherwise it's created from scratch.
|
||||
let (pci_segment_id, pci_device_bdf, config_bar_addr) = if let Some(node) =
|
||||
self.device_tree.lock().unwrap().get(&id)
|
||||
{
|
||||
info!("Restoring virtio-pci {} resources", id);
|
||||
let pci_device_bdf: PciBdf = node
|
||||
.pci_bdf
|
||||
.ok_or(DeviceManagerError::MissingDeviceNodePciBdf)?;
|
||||
let pci_segment_id = pci_device_bdf.segment();
|
||||
|
||||
self.pci_segments[pci_segment_id as usize]
|
||||
.pci_bus
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get_device_id(pci_device_bdf.device() as usize)
|
||||
.map_err(DeviceManagerError::GetPciDeviceId)?;
|
||||
|
||||
if node.resources.is_empty() {
|
||||
return Err(DeviceManagerError::MissingVirtioPciResources);
|
||||
}
|
||||
|
||||
// We know the configuration BAR address is stored on the first
|
||||
// resource in the list.
|
||||
let config_bar_addr = match node.resources[0] {
|
||||
Resource::MmioAddressRange { base, .. } => Some(base),
|
||||
_ => {
|
||||
error!("Unexpected resource {:?} for {}", node.resources[0], id);
|
||||
return Err(DeviceManagerError::MissingVirtioPciResources);
|
||||
}
|
||||
};
|
||||
|
||||
(pci_segment_id, pci_device_bdf, config_bar_addr)
|
||||
} else {
|
||||
let pci_device_bdf = self.pci_segments[pci_segment_id as usize].next_device_bdf()?;
|
||||
|
||||
(pci_segment_id, pci_device_bdf, None)
|
||||
};
|
||||
|
||||
// Update the existing virtio node by setting the parent.
|
||||
if let Some(node) = self.device_tree.lock().unwrap().get_mut(&virtio_device_id) {
|
||||
@@ -3502,34 +3529,38 @@ impl DeviceManager {
|
||||
}
|
||||
|
||||
let device_type = virtio_device.lock().unwrap().device_type();
|
||||
let virtio_pci_device = Arc::new(Mutex::new(
|
||||
VirtioPciDevice::new(
|
||||
id.clone(),
|
||||
memory,
|
||||
virtio_device,
|
||||
msix_num,
|
||||
access_platform,
|
||||
&self.msi_interrupt_manager,
|
||||
pci_device_bdf.into(),
|
||||
self.activate_evt
|
||||
.try_clone()
|
||||
.map_err(DeviceManagerError::EventFd)?,
|
||||
// All device types *except* virtio block devices should be allocated a 64-bit bar
|
||||
// The block devices should be given a 32-bit BAR so that they are easily accessible
|
||||
// to firmware without requiring excessive identity mapping.
|
||||
// The exception being if not on the default PCI segment.
|
||||
pci_segment_id > 0 || device_type != VirtioDeviceType::Block as u32,
|
||||
dma_handler,
|
||||
)
|
||||
.map_err(DeviceManagerError::VirtioDevice)?,
|
||||
));
|
||||
let mut virtio_pci_device = VirtioPciDevice::new(
|
||||
id.clone(),
|
||||
memory,
|
||||
virtio_device,
|
||||
msix_num,
|
||||
access_platform,
|
||||
&self.msi_interrupt_manager,
|
||||
pci_device_bdf.into(),
|
||||
self.activate_evt
|
||||
.try_clone()
|
||||
.map_err(DeviceManagerError::EventFd)?,
|
||||
// All device types *except* virtio block devices should be allocated a 64-bit bar
|
||||
// The block devices should be given a 32-bit BAR so that they are easily accessible
|
||||
// to firmware without requiring excessive identity mapping.
|
||||
// The exception being if not on the default PCI segment.
|
||||
pci_segment_id > 0 || device_type != VirtioDeviceType::Block as u32,
|
||||
dma_handler,
|
||||
)
|
||||
.map_err(DeviceManagerError::VirtioDevice)?;
|
||||
|
||||
let new_resources = self.add_pci_device(
|
||||
// This is important as this will set the BAR address if it exists,
|
||||
// which is mandatory on the restore path.
|
||||
if let Some(addr) = config_bar_addr {
|
||||
virtio_pci_device.set_config_bar_addr(addr);
|
||||
}
|
||||
|
||||
let virtio_pci_device = Arc::new(Mutex::new(virtio_pci_device));
|
||||
let bars = self.add_pci_device(
|
||||
virtio_pci_device.clone(),
|
||||
virtio_pci_device.clone(),
|
||||
pci_segment_id,
|
||||
pci_device_bdf,
|
||||
resources,
|
||||
)?;
|
||||
|
||||
let bar_addr = virtio_pci_device.lock().unwrap().config_bar_addr();
|
||||
@@ -3542,7 +3573,12 @@ impl DeviceManager {
|
||||
}
|
||||
|
||||
// Update the device tree with correct resource information.
|
||||
node.resources = new_resources;
|
||||
for pci_bar in bars.iter() {
|
||||
node.resources.push(Resource::MmioAddressRange {
|
||||
base: pci_bar.0.raw_value(),
|
||||
size: pci_bar.1 as u64,
|
||||
});
|
||||
}
|
||||
node.migratable = Some(Arc::clone(&virtio_pci_device) as Arc<Mutex<dyn Migratable>>);
|
||||
node.pci_bdf = Some(pci_device_bdf);
|
||||
node.pci_device_handle = Some(PciDeviceHandle::Virtio(virtio_pci_device));
|
||||
@@ -3551,38 +3587,6 @@ impl DeviceManager {
|
||||
Ok(pci_device_bdf)
|
||||
}
|
||||
|
||||
fn pci_resources(
|
||||
&self,
|
||||
id: &str,
|
||||
pci_segment_id: u16,
|
||||
) -> DeviceManagerResult<(u16, PciBdf, Option<Vec<Resource>>)> {
|
||||
// Look for the id in the device tree. If it can be found, that means
|
||||
// the device is being restored, otherwise it's created from scratch.
|
||||
Ok(
|
||||
if let Some(node) = self.device_tree.lock().unwrap().get(id) {
|
||||
info!("Restoring virtio-pci {} resources", id);
|
||||
let pci_device_bdf: PciBdf = node
|
||||
.pci_bdf
|
||||
.ok_or(DeviceManagerError::MissingDeviceNodePciBdf)?;
|
||||
let pci_segment_id = pci_device_bdf.segment();
|
||||
|
||||
self.pci_segments[pci_segment_id as usize]
|
||||
.pci_bus
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get_device_id(pci_device_bdf.device() as usize)
|
||||
.map_err(DeviceManagerError::GetPciDeviceId)?;
|
||||
|
||||
(pci_segment_id, pci_device_bdf, Some(node.resources.clone()))
|
||||
} else {
|
||||
let pci_device_bdf =
|
||||
self.pci_segments[pci_segment_id as usize].next_device_bdf()?;
|
||||
|
||||
(pci_segment_id, pci_device_bdf, None)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub fn io_bus(&self) -> &Arc<Bus> {
|
||||
&self.address_manager.io_bus
|
||||
@@ -3616,7 +3620,6 @@ impl DeviceManager {
|
||||
&self.console
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub fn cmdline_additions(&self) -> &[String] {
|
||||
self.cmdline_additions.as_slice()
|
||||
}
|
||||
@@ -3707,8 +3710,6 @@ impl DeviceManager {
|
||||
&mut self,
|
||||
device_cfg: &mut DeviceConfig,
|
||||
) -> DeviceManagerResult<PciDeviceInfo> {
|
||||
self.validate_identifier(&device_cfg.id)?;
|
||||
|
||||
if device_cfg.iommu && !self.is_iommu_segment(device_cfg.pci_segment) {
|
||||
return Err(DeviceManagerError::InvalidIommuHotplug);
|
||||
}
|
||||
@@ -3728,8 +3729,6 @@ impl DeviceManager {
|
||||
&mut self,
|
||||
device_cfg: &mut UserDeviceConfig,
|
||||
) -> DeviceManagerResult<PciDeviceInfo> {
|
||||
self.validate_identifier(&device_cfg.id)?;
|
||||
|
||||
let (bdf, device_name) = self.add_vfio_user_device(device_cfg)?;
|
||||
|
||||
// Update the PCIU bitmap
|
||||
@@ -3821,22 +3820,6 @@ impl DeviceManager {
|
||||
let pci_device_node = device_tree
|
||||
.remove_node_by_pci_bdf(pci_device_bdf)
|
||||
.ok_or(DeviceManagerError::MissingPciDevice)?;
|
||||
|
||||
// For VFIO and vfio-user the PCI device id is the id.
|
||||
// For virtio we overwrite it later as we want the id of the
|
||||
// underlying device.
|
||||
let mut id = pci_device_node.id;
|
||||
let pci_device_handle = pci_device_node
|
||||
.pci_device_handle
|
||||
.ok_or(DeviceManagerError::MissingPciDevice)?;
|
||||
if matches!(pci_device_handle, PciDeviceHandle::Virtio(_)) {
|
||||
// The virtio-pci device has a single child
|
||||
if !pci_device_node.children.is_empty() {
|
||||
assert_eq!(pci_device_node.children.len(), 1);
|
||||
let child_id = &pci_device_node.children[0];
|
||||
id = child_id.clone();
|
||||
}
|
||||
}
|
||||
for child in pci_device_node.children.iter() {
|
||||
device_tree.remove(child);
|
||||
}
|
||||
@@ -3848,6 +3831,9 @@ impl DeviceManager {
|
||||
}
|
||||
}
|
||||
|
||||
let pci_device_handle = pci_device_node
|
||||
.pci_device_handle
|
||||
.ok_or(DeviceManagerError::MissingPciDevice)?;
|
||||
let (pci_device, bus_device, virtio_device, remove_dma_handler) = match pci_device_handle {
|
||||
// No need to remove any virtio-mem mapping here as the container outlives all devices
|
||||
PciDeviceHandle::Vfio(vfio_pci_device) => (
|
||||
@@ -3977,15 +3963,6 @@ impl DeviceManager {
|
||||
.retain(|handler| !Arc::ptr_eq(&handler.virtio_device, &virtio_device));
|
||||
}
|
||||
|
||||
event!(
|
||||
"vm",
|
||||
"device-removed",
|
||||
"id",
|
||||
&id,
|
||||
"bdf",
|
||||
pci_device_bdf.to_string()
|
||||
);
|
||||
|
||||
// At this point, the device has been removed from all the list and
|
||||
// buses where it was stored. At the end of this function, after
|
||||
// any_device, bus_device and pci_device are released, the actual
|
||||
@@ -4039,8 +4016,6 @@ impl DeviceManager {
|
||||
}
|
||||
|
||||
pub fn add_disk(&mut self, disk_cfg: &mut DiskConfig) -> DeviceManagerResult<PciDeviceInfo> {
|
||||
self.validate_identifier(&disk_cfg.id)?;
|
||||
|
||||
if disk_cfg.iommu && !self.is_iommu_segment(disk_cfg.pci_segment) {
|
||||
return Err(DeviceManagerError::InvalidIommuHotplug);
|
||||
}
|
||||
@@ -4050,15 +4025,11 @@ impl DeviceManager {
|
||||
}
|
||||
|
||||
pub fn add_fs(&mut self, fs_cfg: &mut FsConfig) -> DeviceManagerResult<PciDeviceInfo> {
|
||||
self.validate_identifier(&fs_cfg.id)?;
|
||||
|
||||
let device = self.make_virtio_fs_device(fs_cfg)?;
|
||||
self.hotplug_virtio_pci_device(device)
|
||||
}
|
||||
|
||||
pub fn add_pmem(&mut self, pmem_cfg: &mut PmemConfig) -> DeviceManagerResult<PciDeviceInfo> {
|
||||
self.validate_identifier(&pmem_cfg.id)?;
|
||||
|
||||
if pmem_cfg.iommu && !self.is_iommu_segment(pmem_cfg.pci_segment) {
|
||||
return Err(DeviceManagerError::InvalidIommuHotplug);
|
||||
}
|
||||
@@ -4068,8 +4039,6 @@ impl DeviceManager {
|
||||
}
|
||||
|
||||
pub fn add_net(&mut self, net_cfg: &mut NetConfig) -> DeviceManagerResult<PciDeviceInfo> {
|
||||
self.validate_identifier(&net_cfg.id)?;
|
||||
|
||||
if net_cfg.iommu && !self.is_iommu_segment(net_cfg.pci_segment) {
|
||||
return Err(DeviceManagerError::InvalidIommuHotplug);
|
||||
}
|
||||
@@ -4079,8 +4048,6 @@ impl DeviceManager {
|
||||
}
|
||||
|
||||
pub fn add_vdpa(&mut self, vdpa_cfg: &mut VdpaConfig) -> DeviceManagerResult<PciDeviceInfo> {
|
||||
self.validate_identifier(&vdpa_cfg.id)?;
|
||||
|
||||
if vdpa_cfg.iommu && !self.is_iommu_segment(vdpa_cfg.pci_segment) {
|
||||
return Err(DeviceManagerError::InvalidIommuHotplug);
|
||||
}
|
||||
@@ -4090,8 +4057,6 @@ impl DeviceManager {
|
||||
}
|
||||
|
||||
pub fn add_vsock(&mut self, vsock_cfg: &mut VsockConfig) -> DeviceManagerResult<PciDeviceInfo> {
|
||||
self.validate_identifier(&vsock_cfg.id)?;
|
||||
|
||||
if vsock_cfg.iommu && !self.is_iommu_segment(vsock_cfg.pci_segment) {
|
||||
return Err(DeviceManagerError::InvalidIommuHotplug);
|
||||
}
|
||||
@@ -4219,20 +4184,6 @@ impl DeviceManager {
|
||||
pub fn uefi_flash(&self) -> GuestMemoryAtomic<GuestMemoryMmap> {
|
||||
self.uefi_flash.as_ref().unwrap().clone()
|
||||
}
|
||||
|
||||
fn validate_identifier(&self, id: &Option<String>) -> DeviceManagerResult<()> {
|
||||
if let Some(id) = id {
|
||||
if id.starts_with("__") {
|
||||
return Err(DeviceManagerError::InvalidIdentifier(id.clone()));
|
||||
}
|
||||
|
||||
if self.device_tree.lock().unwrap().contains_key(id) {
|
||||
return Err(DeviceManagerError::IdentifierNotUnique(id.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn numa_node_id_from_memory_zone_id(numa_nodes: &NumaNodes, memory_zone_id: &str) -> Option<u32> {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
use crate::device_manager::PciDeviceHandle;
|
||||
use pci::PciBdf;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use vm_device::Resource;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
use std::{os::unix::net::UnixListener, sync::mpsc};
|
||||
|
||||
use gdbstub::{
|
||||
arch::Arch,
|
||||
common::{Signal, Tid},
|
||||
@@ -28,8 +30,7 @@ use gdbstub::{
|
||||
use gdbstub_arch::x86::reg::X86_64CoreRegs as CoreRegs;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use gdbstub_arch::x86::X86_64_SSE as GdbArch;
|
||||
use std::{os::unix::net::UnixListener, sync::mpsc};
|
||||
use vm_memory::{GuestAddress, GuestMemoryError};
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
type ArchUsize = u64;
|
||||
@@ -41,8 +42,8 @@ pub enum DebuggableError {
|
||||
Resume(vm_migration::MigratableError),
|
||||
ReadRegs(crate::cpu::Error),
|
||||
WriteRegs(crate::cpu::Error),
|
||||
ReadMem(GuestMemoryError),
|
||||
WriteMem(GuestMemoryError),
|
||||
ReadMem(hypervisor::HypervisorVmError),
|
||||
WriteMem(hypervisor::HypervisorVmError),
|
||||
TranslateGva(crate::cpu::Error),
|
||||
PoisonedState,
|
||||
}
|
||||
|
||||
@@ -84,19 +84,19 @@ impl InterruptRoute {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RoutingEntry {
|
||||
pub struct RoutingEntry<IrqRoutingEntry> {
|
||||
route: IrqRoutingEntry,
|
||||
masked: bool,
|
||||
}
|
||||
|
||||
pub struct MsiInterruptGroup {
|
||||
pub struct MsiInterruptGroup<IrqRoutingEntry> {
|
||||
vm: Arc<dyn hypervisor::Vm>,
|
||||
gsi_msi_routes: Arc<Mutex<HashMap<u32, RoutingEntry>>>,
|
||||
gsi_msi_routes: Arc<Mutex<HashMap<u32, RoutingEntry<IrqRoutingEntry>>>>,
|
||||
irq_routes: HashMap<InterruptIndex, InterruptRoute>,
|
||||
}
|
||||
|
||||
impl MsiInterruptGroup {
|
||||
fn set_gsi_routes(&self, routes: &HashMap<u32, RoutingEntry>) -> Result<()> {
|
||||
impl MsiInterruptGroup<IrqRoutingEntry> {
|
||||
fn set_gsi_routes(&self, routes: &HashMap<u32, RoutingEntry<IrqRoutingEntry>>) -> Result<()> {
|
||||
let mut entry_vec: Vec<IrqRoutingEntry> = Vec::new();
|
||||
for (_, entry) in routes.iter() {
|
||||
if entry.masked {
|
||||
@@ -115,10 +115,10 @@ impl MsiInterruptGroup {
|
||||
}
|
||||
}
|
||||
|
||||
impl MsiInterruptGroup {
|
||||
impl MsiInterruptGroup<IrqRoutingEntry> {
|
||||
fn new(
|
||||
vm: Arc<dyn hypervisor::Vm>,
|
||||
gsi_msi_routes: Arc<Mutex<HashMap<u32, RoutingEntry>>>,
|
||||
gsi_msi_routes: Arc<Mutex<HashMap<u32, RoutingEntry<IrqRoutingEntry>>>>,
|
||||
irq_routes: HashMap<InterruptIndex, InterruptRoute>,
|
||||
) -> Self {
|
||||
MsiInterruptGroup {
|
||||
@@ -129,7 +129,7 @@ impl MsiInterruptGroup {
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptSourceGroup for MsiInterruptGroup {
|
||||
impl InterruptSourceGroup for MsiInterruptGroup<IrqRoutingEntry> {
|
||||
fn enable(&self) -> Result<()> {
|
||||
for (_, route) in self.irq_routes.iter() {
|
||||
route.enable(&self.vm)?;
|
||||
@@ -172,17 +172,15 @@ impl InterruptSourceGroup for MsiInterruptGroup {
|
||||
masked: bool,
|
||||
) -> Result<()> {
|
||||
if let Some(route) = self.irq_routes.get(&index) {
|
||||
let entry = RoutingEntry {
|
||||
route: self.vm.make_routing_entry(route.gsi, &config),
|
||||
masked,
|
||||
};
|
||||
let mut entry = RoutingEntry::<_>::make_entry(&self.vm, route.gsi, &config)?;
|
||||
entry.masked = masked;
|
||||
if masked {
|
||||
route.disable(&self.vm)?;
|
||||
} else {
|
||||
route.enable(&self.vm)?;
|
||||
}
|
||||
let mut routes = self.gsi_msi_routes.lock().unwrap();
|
||||
routes.insert(route.gsi, entry);
|
||||
routes.insert(route.gsi, *entry);
|
||||
return self.set_gsi_routes(&routes);
|
||||
}
|
||||
|
||||
@@ -236,10 +234,10 @@ pub struct LegacyUserspaceInterruptManager {
|
||||
ioapic: Arc<Mutex<dyn InterruptController>>,
|
||||
}
|
||||
|
||||
pub struct MsiInterruptManager {
|
||||
pub struct MsiInterruptManager<IrqRoutingEntry> {
|
||||
allocator: Arc<Mutex<SystemAllocator>>,
|
||||
vm: Arc<dyn hypervisor::Vm>,
|
||||
gsi_msi_routes: Arc<Mutex<HashMap<u32, RoutingEntry>>>,
|
||||
gsi_msi_routes: Arc<Mutex<HashMap<u32, RoutingEntry<IrqRoutingEntry>>>>,
|
||||
}
|
||||
|
||||
impl LegacyUserspaceInterruptManager {
|
||||
@@ -248,7 +246,7 @@ impl LegacyUserspaceInterruptManager {
|
||||
}
|
||||
}
|
||||
|
||||
impl MsiInterruptManager {
|
||||
impl MsiInterruptManager<IrqRoutingEntry> {
|
||||
pub fn new(allocator: Arc<Mutex<SystemAllocator>>, vm: Arc<dyn hypervisor::Vm>) -> Self {
|
||||
// Create a shared list of GSI that can be shared through all PCI
|
||||
// devices. This way, we can maintain the full list of used GSI,
|
||||
@@ -279,7 +277,7 @@ impl InterruptManager for LegacyUserspaceInterruptManager {
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptManager for MsiInterruptManager {
|
||||
impl InterruptManager for MsiInterruptManager<IrqRoutingEntry> {
|
||||
type GroupConfig = MsiIrqGroupConfig;
|
||||
|
||||
fn create_group(&self, config: Self::GroupConfig) -> Result<Arc<dyn InterruptSourceGroup>> {
|
||||
@@ -302,6 +300,123 @@ impl InterruptManager for MsiInterruptManager {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kvm")]
|
||||
pub mod kvm {
|
||||
use super::*;
|
||||
use hypervisor::kvm::KVM_MSI_VALID_DEVID;
|
||||
use hypervisor::kvm::{kvm_irq_routing_entry, KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI};
|
||||
use pci::PciBdf;
|
||||
|
||||
type KvmRoutingEntry = RoutingEntry<kvm_irq_routing_entry>;
|
||||
pub type KvmMsiInterruptManager = MsiInterruptManager<kvm_irq_routing_entry>;
|
||||
|
||||
impl KvmRoutingEntry {
|
||||
pub fn make_entry(
|
||||
vm: &Arc<dyn hypervisor::Vm>,
|
||||
gsi: u32,
|
||||
config: &InterruptSourceConfig,
|
||||
) -> Result<Box<Self>> {
|
||||
if let InterruptSourceConfig::MsiIrq(cfg) = &config {
|
||||
let mut kvm_route = kvm_irq_routing_entry {
|
||||
gsi,
|
||||
type_: KVM_IRQ_ROUTING_MSI,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
kvm_route.u.msi.address_lo = cfg.low_addr;
|
||||
kvm_route.u.msi.address_hi = cfg.high_addr;
|
||||
kvm_route.u.msi.data = cfg.data;
|
||||
|
||||
if vm.check_extension(hypervisor::Cap::MsiDevid) {
|
||||
// On AArch64, there is limitation on the range of the 'devid',
|
||||
// it can not be greater than 65536 (the max of u16).
|
||||
//
|
||||
// BDF can not be used directly, because 'segment' is in high
|
||||
// 16 bits. The layout of the u32 BDF is:
|
||||
// |---- 16 bits ----|-- 8 bits --|-- 5 bits --|-- 3 bits --|
|
||||
// | segment | bus | device | function |
|
||||
//
|
||||
// Now that we support 1 bus only in a segment, we can build a
|
||||
// 'devid' by replacing the 'bus' bits with the low 8 bits of
|
||||
// 'segment' data.
|
||||
// This way we can resolve the range checking problem and give
|
||||
// different `devid` to all the devices. Limitation is that at
|
||||
// most 256 segments can be supported.
|
||||
//
|
||||
let bdf: PciBdf = PciBdf::from(cfg.devid);
|
||||
let modified_bdf: PciBdf =
|
||||
PciBdf::new(0, bdf.segment() as u8, bdf.device(), bdf.function());
|
||||
kvm_route.flags = KVM_MSI_VALID_DEVID;
|
||||
kvm_route.u.msi.__bindgen_anon_1.devid = modified_bdf.into();
|
||||
}
|
||||
|
||||
let kvm_entry = KvmRoutingEntry {
|
||||
route: kvm_route,
|
||||
masked: false,
|
||||
};
|
||||
|
||||
return Ok(Box::new(kvm_entry));
|
||||
} else if let InterruptSourceConfig::LegacyIrq(cfg) = &config {
|
||||
let mut kvm_route = kvm_irq_routing_entry {
|
||||
gsi,
|
||||
type_: KVM_IRQ_ROUTING_IRQCHIP,
|
||||
..Default::default()
|
||||
};
|
||||
kvm_route.u.irqchip.irqchip = cfg.irqchip;
|
||||
kvm_route.u.irqchip.pin = cfg.pin;
|
||||
let kvm_entry = KvmRoutingEntry {
|
||||
route: kvm_route,
|
||||
masked: false,
|
||||
};
|
||||
|
||||
return Ok(Box::new(kvm_entry));
|
||||
}
|
||||
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Interrupt config type not supported",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mshv")]
|
||||
pub mod mshv {
|
||||
use super::*;
|
||||
use hypervisor::mshv::*;
|
||||
|
||||
type MshvRoutingEntry = RoutingEntry<mshv_msi_routing_entry>;
|
||||
pub type MshvMsiInterruptManager = MsiInterruptManager<mshv_msi_routing_entry>;
|
||||
|
||||
impl MshvRoutingEntry {
|
||||
pub fn make_entry(
|
||||
_vm: &Arc<dyn hypervisor::Vm>,
|
||||
gsi: u32,
|
||||
config: &InterruptSourceConfig,
|
||||
) -> Result<Box<Self>> {
|
||||
if let InterruptSourceConfig::MsiIrq(cfg) = &config {
|
||||
let route = mshv_msi_routing_entry {
|
||||
gsi,
|
||||
address_lo: cfg.low_addr,
|
||||
address_hi: cfg.high_addr,
|
||||
data: cfg.data,
|
||||
};
|
||||
let entry = MshvRoutingEntry {
|
||||
route,
|
||||
masked: false,
|
||||
};
|
||||
|
||||
return Ok(Box::new(entry));
|
||||
}
|
||||
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Interrupt config type not supported",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user