mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
171 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b132cd2145 | ||
|
|
a650b8645f | ||
|
|
f94a5fb4e3 | ||
|
|
00800b8499 | ||
|
|
0b311f6c4c | ||
|
|
8063c00e67 | ||
|
|
4f611c8654 | ||
|
|
d291b87288 | ||
|
|
51787d30da | ||
|
|
63579341e1 | ||
|
|
9c0872def7 | ||
|
|
5dfc49c9fa | ||
|
|
65f8c3682f | ||
|
|
c87466eea2 | ||
|
|
6c365617ee | ||
|
|
30da293d8f | ||
|
|
67bfd4e234 | ||
|
|
46bbe0e612 | ||
|
|
216f330f4e | ||
|
|
bd075872d4 | ||
|
|
7fd5f41623 | ||
|
|
93237f0106 | ||
|
|
0c82e7593d | ||
|
|
1999ab5707 | ||
|
|
5c22f02f32 | ||
|
|
c7afb44c5a | ||
|
|
b0afeeeb65 | ||
|
|
cb53a8b40c | ||
|
|
adf5881757 | ||
|
|
44eca31642 | ||
|
|
b57d7b258d | ||
|
|
d7decfd525 | ||
|
|
2716bc3311 | ||
|
|
9cf0dee264 | ||
|
|
d8b3d07825 | ||
|
|
3d66e86604 | ||
|
|
a2164873ac | ||
|
|
7f8cf4f8e7 | ||
|
|
aedbd7ae27 | ||
|
|
bb4461e951 | ||
|
|
54fe5fb9f5 | ||
|
|
5a9e7ee7fe | ||
|
|
2e664dca64 | ||
|
|
a26f86f8ad | ||
|
|
fde13889f4 | ||
|
|
65ec6631fb | ||
|
|
6ac8dc8745 | ||
|
|
2f514523dd | ||
|
|
92f559f6f2 | ||
|
|
3972e38371 | ||
|
|
a1a016526f | ||
|
|
a5692a95c1 | ||
|
|
0fd25b3246 | ||
|
|
e6127df703 | ||
|
|
559657eecc | ||
|
|
66f1ec0063 | ||
|
|
13d081ac47 | ||
|
|
b12f4ccade | ||
|
|
1f8bca4840 | ||
|
|
833eb2ebe6 | ||
|
|
7c7f0f1f5f | ||
|
|
d445ec7633 | ||
|
|
039e758f15 | ||
|
|
eb55d0809e | ||
|
|
ecde44617b | ||
|
|
58e567b309 | ||
|
|
fa0140a183 | ||
|
|
c6c5e4ee87 | ||
|
|
a64bdcf3f5 | ||
|
|
439823e7ea | ||
|
|
9019086625 | ||
|
|
bccd7c7e48 | ||
|
|
f1dc7f442a | ||
|
|
32b855df3a | ||
|
|
8fa1098629 | ||
|
|
41cec53bb8 | ||
|
|
fd9f14893b | ||
|
|
7c8719b4ea | ||
|
|
335a4e1cc0 | ||
|
|
6dbbd0363f | ||
|
|
40e1ad3f62 | ||
|
|
094625242d | ||
|
|
b3569f084d | ||
|
|
3800212def | ||
|
|
e9f5052fcd | ||
|
|
a583b055f5 | ||
|
|
2b33e66bb4 | ||
|
|
92ceef52c8 | ||
|
|
d7baa58e11 | ||
|
|
2d39b6ed06 | ||
|
|
ed31ca9041 | ||
|
|
06b880e99f | ||
|
|
742ca68070 | ||
|
|
f950cb1fdb | ||
|
|
c6c5dca651 | ||
|
|
d646e10079 | ||
|
|
efe0429a9a | ||
|
|
1f3321c50e | ||
|
|
c0d51eecf1 | ||
|
|
1b9dc80469 | ||
|
|
43d32a7288 | ||
|
|
27e04e41bf | ||
|
|
41ac48d332 | ||
|
|
2afaed9111 | ||
|
|
f1ea69474d | ||
|
|
cac42301f8 | ||
|
|
ada85f68f6 | ||
|
|
64b4b7205e | ||
|
|
4a4352f9d9 | ||
|
|
81ba70a497 | ||
|
|
7df7061610 | ||
|
|
c021dda267 | ||
|
|
f48b05eee6 | ||
|
|
3b59e57001 | ||
|
|
49069d8474 | ||
|
|
8eaefa6e8e | ||
|
|
a1b996ac37 | ||
|
|
4ddc6ffd73 | ||
|
|
94fb9f817d | ||
|
|
133a5a858a | ||
|
|
3a18860326 | ||
|
|
0d0013c46e | ||
|
|
86d243938e | ||
|
|
a7a15d56dd | ||
|
|
65dc1c83a9 | ||
|
|
3edaa8adb6 | ||
|
|
9260c3816e | ||
|
|
5d45d6d0fb | ||
|
|
957d3a7443 | ||
|
|
04949755c0 | ||
|
|
b8dbb26647 | ||
|
|
3fe7d61a02 | ||
|
|
c2862b6947 | ||
|
|
03b01c02cd | ||
|
|
435d769511 | ||
|
|
9ba6767a90 | ||
|
|
3742cc42ae | ||
|
|
e0dd2e41c2 | ||
|
|
f1495b5767 | ||
|
|
6bd4bdfa26 | ||
|
|
ade3a9c8f6 | ||
|
|
15e2763753 | ||
|
|
8b87b752b5 | ||
|
|
7864f91a2b | ||
|
|
6ab93a08ec | ||
|
|
fd41892065 | ||
|
|
dbeb922882 | ||
|
|
0bb2060ca7 | ||
|
|
8b585b96c1 | ||
|
|
ccb604e1e1 | ||
|
|
0e65ca4a6c | ||
|
|
7e280b6f70 | ||
|
|
90034fd6ba | ||
|
|
642309f141 | ||
|
|
5da8a1c08e | ||
|
|
465db7f08c | ||
|
|
55c5961f43 | ||
|
|
7c3582b4a8 | ||
|
|
979797786d | ||
|
|
62120f9afe | ||
|
|
56c11600db | ||
|
|
f2336413be | ||
|
|
0fd6521759 | ||
|
|
4a2d3cb4f5 | ||
|
|
3fe20cc09a | ||
|
|
fd581b1afb | ||
|
|
1e732bf532 | ||
|
|
ad8295e11d | ||
|
|
fe1f0ef49e | ||
|
|
f9fcc3722f | ||
|
|
c0cbaff519 |
3
.github/workflows/build.yaml
vendored
3
.github/workflows/build.yaml
vendored
@@ -53,6 +53,9 @@ jobs:
|
||||
- name: Build (default features + gdb)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "gdb" -- -D warnings
|
||||
|
||||
- name: Build (default features + guest_debug)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "guest_debug" -- -D warnings
|
||||
|
||||
- name: Build (common + mshv)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "common,mshv" -- -D warnings
|
||||
|
||||
|
||||
3
.github/workflows/quality.yaml
vendored
3
.github/workflows/quality.yaml
vendored
@@ -45,6 +45,9 @@ jobs:
|
||||
- name: Clippy (default features + gdb)
|
||||
run: cargo clippy --locked --all --all-targets --tests --features "gdb" -- -D warnings
|
||||
|
||||
- name: Clippy (default features + guest_debug)
|
||||
run: cargo clippy --locked --all --all-targets --tests --features "guest_debug" -- -D warnings
|
||||
|
||||
- name: Clippy (common + mshv)
|
||||
run: cargo clippy --locked --all --all-targets --no-default-features --tests --features "common,mshv" -- -D warnings
|
||||
|
||||
|
||||
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.62"
|
||||
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.62"
|
||||
target: x86_64-unknown-linux-musl
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
toolchain: "1.60"
|
||||
toolchain: "1.62"
|
||||
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.62"
|
||||
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.62"
|
||||
target: aarch64-unknown-linux-musl
|
||||
override: true
|
||||
- name: Static Build (AArch64)
|
||||
|
||||
186
Cargo.lock
generated
186
Cargo.lock
generated
@@ -20,9 +20,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.57"
|
||||
version = "1.0.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
|
||||
checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -130,15 +130,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.1.18"
|
||||
version = "3.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
|
||||
checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"terminal_size",
|
||||
@@ -147,16 +147,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.0"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
|
||||
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "24.0.0"
|
||||
version = "25.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -165,10 +165,10 @@ dependencies = [
|
||||
"epoll",
|
||||
"event_monitor",
|
||||
"hypervisor",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"net_util",
|
||||
"once_cell",
|
||||
"option_parser",
|
||||
"seccompiler",
|
||||
"serde_json",
|
||||
@@ -206,6 +206,7 @@ dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"epoll",
|
||||
"hypervisor",
|
||||
"libc",
|
||||
"log",
|
||||
"versionize",
|
||||
@@ -276,9 +277,9 @@ checksum = "b643857cf70949306b81d7e92cb9d47add673868edac9863c4a49c42feaf3f1e"
|
||||
|
||||
[[package]]
|
||||
name = "gdbstub"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa2ca5d6b045de372cef3991f873389b421b4cabcfbe52f7787fae8b8b37906"
|
||||
checksum = "1c1f9371c87c11642ee94dcf92cb48b1484ba250b8e8bff3df71c28651f3f4e7"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
@@ -290,9 +291,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gdbstub_arch"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51dc4b5718ac76d21e8605c0966dd32d80273b89b11e6cfef467b04e45934d37"
|
||||
checksum = "c24f469ba9556c5a063d6df35a8a338025fccf96ecae44f330a156b686f7a268"
|
||||
dependencies = [
|
||||
"gdbstub",
|
||||
"num-traits",
|
||||
@@ -300,9 +301,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.6"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@@ -317,9 +318,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
@@ -369,9 +370,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.8.1"
|
||||
version = "1.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
|
||||
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
@@ -396,15 +397,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnetwork"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4088d739b183546b239688ddbc79891831df421773df95e236daf7867866d355"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnetwork"
|
||||
version = "0.19.0"
|
||||
@@ -433,7 +425,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kvm-ioctls"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/rust-vmm/kvm-ioctls?branch=main#1e03e29cdfbb0cb108a98de7a78045a5a517f18e"
|
||||
source = "git+https://github.com/rust-vmm/kvm-ioctls?branch=main#ccf0bda07485b2433616de998c000aa5f04ce806"
|
||||
dependencies = [
|
||||
"kvm-bindings",
|
||||
"libc",
|
||||
@@ -468,9 +460,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.6"
|
||||
version = "1.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92e7e15d7610cce1d9752e137625f14e61a28cd45929b6e12e47b50fe154ee2e"
|
||||
checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -521,7 +513,7 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#a730d86940081ad044cdfbc1285c1db6d3048392"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#863b0370ba7e57f7df5b908ada9e5b44809ccae9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"vmm-sys-util",
|
||||
@@ -530,7 +522,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#7ac1b80bff7220171c9a626b23b51620ca774468"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"serde",
|
||||
@@ -542,7 +534,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#7ac1b80bff7220171c9a626b23b51620ca774468"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mshv-bindings",
|
||||
@@ -562,11 +554,12 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"epoll",
|
||||
"getrandom",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"net_gen",
|
||||
"once_cell",
|
||||
"pnet",
|
||||
"pnet_datalink",
|
||||
"rate_limiter",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -595,19 +588,25 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "111.20.0+1.1.1o"
|
||||
name = "once_cell"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92892c4f87d56e376e469ace79f1128fdaded07646ddf73aa0be4706ff712dec"
|
||||
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "111.22.0+1.1.1q"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f31f0d509d1c1ae9cada2f9539ff8f37933831fd5098879e482aa687d659853"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.73"
|
||||
version = "0.9.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d5fd19fb3e0a8191c1e34935718976a3e70c112ab9a24af6d7cadccd9d90bc0"
|
||||
checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
@@ -623,9 +622,9 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.0.1"
|
||||
version = "6.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435"
|
||||
checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
@@ -702,34 +701,32 @@ checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
||||
|
||||
[[package]]
|
||||
name = "pnet"
|
||||
version = "0.30.0"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5cc57672f576f6b95370277fb445738d4887195c6cf4192bdf4f44697e2389b"
|
||||
checksum = "0caaf5b11fd907ff15cf14a4477bfabca4b37ab9e447a4f8dead969a59cdafad"
|
||||
dependencies = [
|
||||
"ipnetwork 0.19.0",
|
||||
"pnet_base",
|
||||
"pnet_datalink",
|
||||
"pnet_packet",
|
||||
"pnet_sys",
|
||||
"pnet_transport",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pnet_base"
|
||||
version = "0.30.0"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e88341c6c842f89bdc7287f7b1e26b6fa64fa11c7ea3756971e6f18cd2510c4"
|
||||
checksum = "f9d3a993d49e5fd5d4d854d6999d4addca1f72d86c65adf224a36757161c02b6"
|
||||
dependencies = [
|
||||
"no-std-net",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pnet_datalink"
|
||||
version = "0.30.0"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc6e55d71c51db73372db35bc54f43abd8460adff1c3a9b717804ca6416d5df2"
|
||||
checksum = "e466faf03a98ad27f6e15cd27a2b7cc89e73e640a43527742977bc503c37f8aa"
|
||||
dependencies = [
|
||||
"ipnetwork 0.18.0",
|
||||
"ipnetwork",
|
||||
"libc",
|
||||
"pnet_base",
|
||||
"pnet_sys",
|
||||
@@ -738,9 +735,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pnet_macros"
|
||||
version = "0.30.0"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebfcdc9c072966723026b3596a1f655fb8bbfe0142f9770f8d481aee4459d6b9"
|
||||
checksum = "48dd52a5211fac27e7acb14cfc9f30ae16ae0e956b7b779c8214c74559cef4c3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -750,18 +747,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pnet_macros_support"
|
||||
version = "0.30.0"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bba532f5a4b320c029d89e612671fb621851b3b07e972c53850d34130033a5cd"
|
||||
checksum = "89de095dc7739349559913aed1ef6a11e73ceade4897dadc77c5e09de6740750"
|
||||
dependencies = [
|
||||
"pnet_base",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pnet_packet"
|
||||
version = "0.30.0"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7009716ac86091c1b6e2cdec95a2b028c880f516054c1ec11edd02f9f463cbde"
|
||||
checksum = "bc3b5111e697c39c8b9795b9fdccbc301ab696699e88b9ea5a4e4628978f495f"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"pnet_base",
|
||||
@@ -771,9 +768,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pnet_sys"
|
||||
version = "0.30.0"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e2a05efbc55c22f664c0ea475fbc4ffc4d09346aff9068438279d7e3d431f6f"
|
||||
checksum = "328e231f0add6d247d82421bf3790b4b33b39c8930637f428eef24c4c6a90805"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
@@ -781,9 +778,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pnet_transport"
|
||||
version = "0.30.0"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b8ff06f37863f7590183f7044ab2e8d4dae991ecea0c791e3c6dd61ed2913d"
|
||||
checksum = "ff597185e6f1f5671b3122e4dba892a1c73e17c17e723d7669bd9299cbe7f124"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pnet_base",
|
||||
@@ -793,9 +790,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.39"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
||||
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -813,9 +810,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.18"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
||||
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -851,9 +848,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.6"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
|
||||
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -862,9 +859,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.26"
|
||||
version = "0.6.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
|
||||
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||
|
||||
[[package]]
|
||||
name = "remain"
|
||||
@@ -909,24 +906,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.9"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd"
|
||||
checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.137"
|
||||
version = "1.0.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
||||
checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.137"
|
||||
version = "1.0.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
|
||||
checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -935,9 +932,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.81"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
|
||||
checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -965,9 +962,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.8.0"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
||||
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
|
||||
|
||||
[[package]]
|
||||
name = "ssh2"
|
||||
@@ -995,9 +992,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.95"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
|
||||
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1041,8 +1038,8 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"epoll",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"ssh2",
|
||||
"vmm-sys-util",
|
||||
"wait-timeout",
|
||||
@@ -1079,9 +1076,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
||||
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
@@ -1091,9 +1088,9 @@ checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.0.0"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cfcd319456c4d6ea10087ed423473267e1a071f3bc0aa89f80d60997843c6f0"
|
||||
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
@@ -1143,7 +1140,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio-ioctls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#f75a77c1ab6349c105bc1462a65508726b4c2e0f"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#38272d3f06bbc0b4b3cfa3f5da07fd1c1c89e4d1"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
@@ -1202,8 +1199,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vhost-user-backend"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/rust-vmm/vhost-user-backend?rev=14f58eda14076e973704d4f904850be1146fbb05#14f58eda14076e973704d4f904850be1146fbb05"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3eeb6723bbee20dbc2db95c575941922fb96e77eb41786ecd1c8c03348709c4c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
@@ -1293,9 +1291,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88f2d73c184c18f8acc32dab77fcb6e3af92d53262538d3a68aa474810d6863c"
|
||||
checksum = "519c0a333c871650269cba303bc108075d52a0c0d64f9b91fae61829b53725af"
|
||||
dependencies = [
|
||||
"log",
|
||||
"vm-memory",
|
||||
@@ -1381,12 +1379,12 @@ dependencies = [
|
||||
"gdbstub",
|
||||
"gdbstub_arch",
|
||||
"hypervisor",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"linux-loader",
|
||||
"log",
|
||||
"micro_http",
|
||||
"net_util",
|
||||
"once_cell",
|
||||
"option_parser",
|
||||
"pci",
|
||||
"qcow",
|
||||
@@ -1434,9 +1432,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
|
||||
15
Cargo.toml
15
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cloud-hypervisor"
|
||||
version = "24.0.0"
|
||||
version = "25.0.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
default-run = "cloud-hypervisor"
|
||||
@@ -16,9 +16,9 @@ rust-version = "1.60"
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.58"
|
||||
api_client = { path = "api_client" }
|
||||
clap = { version = "3.1.18", features = ["wrap_help","cargo"] }
|
||||
clap = { version = "3.2.8", features = ["wrap_help","cargo"] }
|
||||
epoll = "4.3.1"
|
||||
event_monitor = { path = "event_monitor" }
|
||||
hypervisor = { path = "hypervisor" }
|
||||
@@ -26,7 +26,7 @@ libc = "0.2.126"
|
||||
log = { version = "0.4.17", features = ["std"] }
|
||||
option_parser = { path = "option_parser" }
|
||||
seccompiler = "0.2.0"
|
||||
serde_json = "1.0.81"
|
||||
serde_json = "1.0.82"
|
||||
signal-hook = "0.3.14"
|
||||
thiserror = "1.0.31"
|
||||
vmm = { path = "vmm" }
|
||||
@@ -34,7 +34,7 @@ vmm-sys-util = "0.9.0"
|
||||
vm-memory = "0.8.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.1.18", features = ["cargo"] }
|
||||
clap = { version = "3.2.8", features = ["cargo"] }
|
||||
|
||||
# List of patched crates
|
||||
[patch.crates-io]
|
||||
@@ -44,9 +44,9 @@ versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_deri
|
||||
|
||||
[dev-dependencies]
|
||||
dirs = "4.0.0"
|
||||
lazy_static= "1.4.0"
|
||||
net_util = { path = "net_util" }
|
||||
serde_json = "1.0.81"
|
||||
once_cell = "1.13.0"
|
||||
serde_json = "1.0.82"
|
||||
test_infra = { path = "test_infra" }
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
@@ -58,6 +58,7 @@ amx = ["vmm/amx"]
|
||||
cmos = ["vmm/cmos"]
|
||||
fwdebug = ["vmm/fwdebug"]
|
||||
gdb = ["vmm/gdb"]
|
||||
guest_debug = ["vmm/guest_debug"]
|
||||
kvm = ["vmm/kvm"]
|
||||
mshv = ["vmm/mshv"]
|
||||
tdx = ["vmm/tdx"]
|
||||
|
||||
139
Jenkinsfile
vendored
139
Jenkinsfile
vendored
@@ -23,6 +23,19 @@ pipeline{
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Check for fuzzer cargo files only changes') {
|
||||
when {
|
||||
expression {
|
||||
return fuzzCargoFileOnly()
|
||||
}
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
runWorkers = false
|
||||
echo "Fuzzer cargo files only changes, no need to run the CI"
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Check for RFC/WIP builds') {
|
||||
when {
|
||||
changeRequest comparator: 'REGEXP', title: '.*(rfc|RFC|wip|WIP).*'
|
||||
@@ -153,6 +166,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 +328,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'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -287,3 +415,14 @@ def boolean docsFileOnly() {
|
||||
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v '\\.md'"
|
||||
) != 0
|
||||
}
|
||||
|
||||
def boolean fuzzCargoFileOnly() {
|
||||
if (env.CHANGE_TARGET == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return sh(
|
||||
returnStatus: true,
|
||||
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v -E 'fuzz\\/Cargo.(toml|lock)'"
|
||||
) != 0
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ impl Aml for Path {
|
||||
};
|
||||
|
||||
for part in self.name_parts.clone().iter_mut() {
|
||||
bytes.extend_from_slice(&part.to_vec());
|
||||
bytes.extend_from_slice(part.as_ref());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@ tdx = []
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { path = "../acpi_tables" }
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.58"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.126"
|
||||
linux-loader = { version = "0.4.0", features = ["elf", "bzimage", "pe"] }
|
||||
log = "0.4.17"
|
||||
serde = { version = "1.0.137", features = ["rc", "derive"] }
|
||||
serde = { version = "1.0.138", features = ["rc", "derive"] }
|
||||
thiserror = "1.0.31"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
|
||||
@@ -8,17 +8,18 @@
|
||||
|
||||
use crate::{NumaNodes, PciSpaceInfo};
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use hypervisor::arch::aarch64::gic::Vgic;
|
||||
use std::cmp;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CStr;
|
||||
use std::fmt::Debug;
|
||||
use std::result;
|
||||
use std::str;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use super::super::DeviceType;
|
||||
use super::super::GuestMemoryMmap;
|
||||
use super::super::InitramfsConfig;
|
||||
use super::gic::GicDevice;
|
||||
use super::layout::{
|
||||
IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_PCI_IO_SIZE, MEM_PCI_IO_START,
|
||||
PCI_HIGH_BASE, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
|
||||
@@ -90,7 +91,7 @@ pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHash
|
||||
vcpu_mpidr: Vec<u64>,
|
||||
vcpu_topology: Option<(u8, u8, u8)>,
|
||||
device_info: &HashMap<(DeviceType, String), T, S>,
|
||||
gic_device: &dyn GicDevice,
|
||||
gic_device: &Arc<Mutex<dyn Vgic>>,
|
||||
initrd: &Option<InitramfsConfig>,
|
||||
pci_space_info: &[PciSpaceInfo],
|
||||
numa_nodes: &NumaNodes,
|
||||
@@ -315,18 +316,18 @@ fn create_chosen_node(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_gic_node(fdt: &mut FdtWriter, gic_device: &dyn GicDevice) -> FdtWriterResult<()> {
|
||||
let gic_reg_prop = gic_device.device_properties();
|
||||
fn create_gic_node(fdt: &mut FdtWriter, gic_device: &Arc<Mutex<dyn Vgic>>) -> FdtWriterResult<()> {
|
||||
let gic_reg_prop = gic_device.lock().unwrap().device_properties();
|
||||
|
||||
let intc_node = fdt.begin_node("intc")?;
|
||||
|
||||
fdt.property_string("compatible", gic_device.fdt_compatibility())?;
|
||||
fdt.property_string("compatible", gic_device.lock().unwrap().fdt_compatibility())?;
|
||||
fdt.property_null("interrupt-controller")?;
|
||||
// "interrupt-cells" field specifies the number of cells needed to encode an
|
||||
// interrupt source. The type shall be a <u32> and the value shall be 3 if no PPI affinity description
|
||||
// is required.
|
||||
fdt.property_u32("#interrupt-cells", 3)?;
|
||||
fdt.property_array_u64("reg", gic_reg_prop)?;
|
||||
fdt.property_array_u64("reg", &gic_reg_prop)?;
|
||||
fdt.property_u32("phandle", GIC_PHANDLE)?;
|
||||
fdt.property_u32("#address-cells", 2)?;
|
||||
fdt.property_u32("#size-cells", 2)?;
|
||||
@@ -334,18 +335,18 @@ fn create_gic_node(fdt: &mut FdtWriter, gic_device: &dyn GicDevice) -> FdtWriter
|
||||
|
||||
let gic_intr_prop = [
|
||||
GIC_FDT_IRQ_TYPE_PPI,
|
||||
gic_device.fdt_maint_irq(),
|
||||
gic_device.lock().unwrap().fdt_maint_irq(),
|
||||
IRQ_TYPE_LEVEL_HI,
|
||||
];
|
||||
fdt.property_array_u32("interrupts", &gic_intr_prop)?;
|
||||
|
||||
if gic_device.msi_compatible() {
|
||||
if gic_device.lock().unwrap().msi_compatible() {
|
||||
let msic_node = fdt.begin_node("msic")?;
|
||||
fdt.property_string("compatible", gic_device.msi_compatibility())?;
|
||||
fdt.property_string("compatible", gic_device.lock().unwrap().msi_compatibility())?;
|
||||
fdt.property_null("msi-controller")?;
|
||||
fdt.property_u32("phandle", MSI_PHANDLE)?;
|
||||
let msi_reg_prop = gic_device.msi_properties();
|
||||
fdt.property_array_u64("reg", msi_reg_prop)?;
|
||||
let msi_reg_prop = gic_device.lock().unwrap().msi_properties();
|
||||
fdt.property_array_u64("reg", &msi_reg_prop)?;
|
||||
fdt.end_node(msic_node)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,262 +0,0 @@
|
||||
// Copyright 2021 Arm Limited (or its affiliates). All rights reserved.
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// This file implements the GicV3 device.
|
||||
|
||||
pub mod kvm {
|
||||
use crate::aarch64::gic::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
||||
use crate::aarch64::gic::icc_regs::{get_icc_regs, set_icc_regs};
|
||||
use crate::aarch64::gic::kvm::{save_pending_tables, KvmGicDevice};
|
||||
use crate::aarch64::gic::redist_regs::{
|
||||
construct_gicr_typers, get_redist_regs, set_redist_regs,
|
||||
};
|
||||
use crate::aarch64::gic::GicDevice;
|
||||
use crate::layout;
|
||||
use anyhow::anyhow;
|
||||
use hypervisor::kvm::kvm_bindings;
|
||||
use hypervisor::CpuState;
|
||||
use std::any::Any;
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
use std::{boxed::Box, result};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use vm_memory::Address;
|
||||
use vm_migration::{
|
||||
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable,
|
||||
VersionMapped,
|
||||
};
|
||||
|
||||
/// Errors thrown while saving/restoring the GICv3.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Error in saving RDIST pending tables into guest RAM.
|
||||
SavePendingTables(crate::aarch64::gic::Error),
|
||||
/// Error in saving GIC distributor registers.
|
||||
SaveDistributorRegisters(crate::aarch64::gic::Error),
|
||||
/// Error in restoring GIC distributor registers.
|
||||
RestoreDistributorRegisters(crate::aarch64::gic::Error),
|
||||
/// Error in saving GIC distributor control registers.
|
||||
SaveDistributorCtrlRegisters(crate::aarch64::gic::Error),
|
||||
/// Error in restoring GIC distributor control registers.
|
||||
RestoreDistributorCtrlRegisters(crate::aarch64::gic::Error),
|
||||
/// Error in saving GIC redistributor registers.
|
||||
SaveRedistributorRegisters(crate::aarch64::gic::Error),
|
||||
/// Error in restoring GIC redistributor registers.
|
||||
RestoreRedistributorRegisters(crate::aarch64::gic::Error),
|
||||
/// Error in saving GIC CPU interface registers.
|
||||
SaveIccRegisters(crate::aarch64::gic::Error),
|
||||
/// Error in restoring GIC CPU interface registers.
|
||||
RestoreIccRegisters(crate::aarch64::gic::Error),
|
||||
}
|
||||
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
pub struct KvmGicV3 {
|
||||
/// The hypervisor agnostic device for the GicV3
|
||||
device: Arc<dyn hypervisor::Device>,
|
||||
|
||||
/// Vector holding values of GICR_TYPER for each vCPU
|
||||
gicr_typers: Vec<u64>,
|
||||
|
||||
/// GIC device properties, to be used for setting up the fdt entry
|
||||
properties: [u64; 4],
|
||||
|
||||
/// Number of CPUs handled by the device
|
||||
vcpu_count: u64,
|
||||
}
|
||||
|
||||
#[derive(Versionize)]
|
||||
pub struct Gicv3State {
|
||||
dist: Vec<u32>,
|
||||
rdist: Vec<u32>,
|
||||
icc: Vec<u32>,
|
||||
// special register that enables interrupts and affinity routing
|
||||
gicd_ctlr: u32,
|
||||
}
|
||||
|
||||
impl VersionMapped for Gicv3State {}
|
||||
|
||||
impl KvmGicV3 {
|
||||
// Device trees specific constants
|
||||
pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
|
||||
|
||||
/// Get the address of the GIC distributor.
|
||||
pub fn get_dist_addr() -> u64 {
|
||||
layout::GIC_V3_DIST_START.raw_value()
|
||||
}
|
||||
|
||||
/// Get the size of the GIC distributor.
|
||||
pub fn get_dist_size() -> u64 {
|
||||
layout::GIC_V3_DIST_SIZE
|
||||
}
|
||||
|
||||
/// Get the address of the GIC redistributors.
|
||||
pub fn get_redists_addr(vcpu_count: u64) -> u64 {
|
||||
KvmGicV3::get_dist_addr() - KvmGicV3::get_redists_size(vcpu_count)
|
||||
}
|
||||
|
||||
/// Get the size of the GIC redistributors.
|
||||
pub fn get_redists_size(vcpu_count: u64) -> u64 {
|
||||
vcpu_count * layout::GIC_V3_REDIST_SIZE
|
||||
}
|
||||
|
||||
/// Save the state of GIC.
|
||||
fn state(&self, gicr_typers: &[u64]) -> Result<Gicv3State> {
|
||||
let gicd_ctlr =
|
||||
read_ctlr(self.device()).map_err(Error::SaveDistributorCtrlRegisters)?;
|
||||
|
||||
let dist_state =
|
||||
get_dist_regs(self.device()).map_err(Error::SaveDistributorRegisters)?;
|
||||
|
||||
let rdist_state = get_redist_regs(self.device(), gicr_typers)
|
||||
.map_err(Error::SaveRedistributorRegisters)?;
|
||||
|
||||
let icc_state =
|
||||
get_icc_regs(self.device(), gicr_typers).map_err(Error::SaveIccRegisters)?;
|
||||
|
||||
Ok(Gicv3State {
|
||||
dist: dist_state,
|
||||
rdist: rdist_state,
|
||||
icc: icc_state,
|
||||
gicd_ctlr,
|
||||
})
|
||||
}
|
||||
|
||||
/// Restore the state of GIC.
|
||||
fn set_state(&mut self, gicr_typers: &[u64], state: &Gicv3State) -> Result<()> {
|
||||
write_ctlr(self.device(), state.gicd_ctlr)
|
||||
.map_err(Error::RestoreDistributorCtrlRegisters)?;
|
||||
|
||||
set_dist_regs(self.device(), &state.dist)
|
||||
.map_err(Error::RestoreDistributorRegisters)?;
|
||||
|
||||
set_redist_regs(self.device(), gicr_typers, &state.rdist)
|
||||
.map_err(Error::RestoreRedistributorRegisters)?;
|
||||
|
||||
set_icc_regs(self.device(), gicr_typers, &state.icc)
|
||||
.map_err(Error::RestoreIccRegisters)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl GicDevice for KvmGicV3 {
|
||||
fn device(&self) -> &Arc<dyn hypervisor::Device> {
|
||||
&self.device
|
||||
}
|
||||
|
||||
fn fdt_compatibility(&self) -> &str {
|
||||
"arm,gic-v3"
|
||||
}
|
||||
|
||||
fn fdt_maint_irq(&self) -> u32 {
|
||||
KvmGicV3::ARCH_GIC_V3_MAINT_IRQ
|
||||
}
|
||||
|
||||
fn device_properties(&self) -> &[u64] {
|
||||
&self.properties
|
||||
}
|
||||
|
||||
fn vcpu_count(&self) -> u64 {
|
||||
self.vcpu_count
|
||||
}
|
||||
|
||||
fn set_its_device(&mut self, _its_device: Option<Arc<dyn hypervisor::Device>>) {}
|
||||
|
||||
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
|
||||
let gicr_typers = construct_gicr_typers(vcpu_states);
|
||||
self.gicr_typers = gicr_typers;
|
||||
}
|
||||
|
||||
fn as_any_concrete_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl KvmGicDevice for KvmGicV3 {
|
||||
fn version() -> u32 {
|
||||
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3
|
||||
}
|
||||
|
||||
fn create_device(
|
||||
device: Arc<dyn hypervisor::Device>,
|
||||
vcpu_count: u64,
|
||||
) -> Box<dyn GicDevice> {
|
||||
Box::new(KvmGicV3 {
|
||||
device,
|
||||
gicr_typers: vec![0; vcpu_count.try_into().unwrap()],
|
||||
properties: [
|
||||
KvmGicV3::get_dist_addr(),
|
||||
KvmGicV3::get_dist_size(),
|
||||
KvmGicV3::get_redists_addr(vcpu_count),
|
||||
KvmGicV3::get_redists_size(vcpu_count),
|
||||
],
|
||||
vcpu_count,
|
||||
})
|
||||
}
|
||||
|
||||
fn init_device_attributes(
|
||||
_vm: &Arc<dyn hypervisor::Vm>,
|
||||
gic_device: &mut dyn GicDevice,
|
||||
) -> crate::aarch64::gic::Result<()> {
|
||||
/* Setting up the distributor attribute.
|
||||
We are placing the GIC below 1GB so we need to substract the size of the distributor.
|
||||
*/
|
||||
Self::set_device_attribute(
|
||||
gic_device.device(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST),
|
||||
&KvmGicV3::get_dist_addr() as *const u64 as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
/* Setting up the redistributors' attribute.
|
||||
We are calculating here the start of the redistributors address. We have one per CPU.
|
||||
*/
|
||||
Self::set_device_attribute(
|
||||
gic_device.device(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST),
|
||||
&KvmGicV3::get_redists_addr(gic_device.vcpu_count()) as *const u64 as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub const GIC_V3_SNAPSHOT_ID: &str = "gic-v3";
|
||||
impl Snapshottable for KvmGicV3 {
|
||||
fn id(&self) -> String {
|
||||
GIC_V3_SNAPSHOT_ID.to_string()
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
let gicr_typers = self.gicr_typers.clone();
|
||||
Snapshot::new_from_versioned_state(&self.id(), &self.state(&gicr_typers).unwrap())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
let gicr_typers = self.gicr_typers.clone();
|
||||
self.set_state(&gicr_typers, &snapshot.to_versioned_state(&self.id())?)
|
||||
.map_err(|e| {
|
||||
MigratableError::Restore(anyhow!("Could not restore GICv3 state {:?}", e))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Pausable for KvmGicV3 {
|
||||
fn pause(&mut self) -> std::result::Result<(), MigratableError> {
|
||||
// Flush redistributors pending tables to guest RAM.
|
||||
save_pending_tables(self.device()).map_err(|e| {
|
||||
MigratableError::Pause(anyhow!("Could not save GICv3 GIC pending tables {:?}", e))
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for KvmGicV3 {}
|
||||
impl Migratable for KvmGicV3 {}
|
||||
}
|
||||
@@ -1,514 +0,0 @@
|
||||
// Copyright 2021 Arm Limited (or its affiliates). All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// This file implements the GicV3 device with ITS (Virtual Interrupt Translation Service).
|
||||
|
||||
pub mod kvm {
|
||||
use crate::aarch64::gic::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
||||
use crate::aarch64::gic::icc_regs::{get_icc_regs, set_icc_regs};
|
||||
use crate::aarch64::gic::redist_regs::{
|
||||
construct_gicr_typers, get_redist_regs, set_redist_regs,
|
||||
};
|
||||
|
||||
use crate::aarch64::gic::gicv3::kvm::KvmGicV3;
|
||||
use crate::aarch64::gic::kvm::{save_pending_tables, KvmGicDevice};
|
||||
use crate::aarch64::gic::GicDevice;
|
||||
use crate::layout;
|
||||
use anyhow::anyhow;
|
||||
use hypervisor::kvm::kvm_bindings;
|
||||
use hypervisor::CpuState;
|
||||
use std::any::Any;
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
use std::{boxed::Box, result};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use vm_migration::{
|
||||
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable,
|
||||
VersionMapped,
|
||||
};
|
||||
|
||||
const GITS_CTLR: u32 = 0x0000;
|
||||
const GITS_IIDR: u32 = 0x0004;
|
||||
const GITS_CBASER: u32 = 0x0080;
|
||||
const GITS_CWRITER: u32 = 0x0088;
|
||||
const GITS_CREADR: u32 = 0x0090;
|
||||
const GITS_BASER: u32 = 0x0100;
|
||||
|
||||
/// Errors thrown while saving/restoring the GICv3ITS.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Error in saving RDIST pending tables into guest RAM.
|
||||
SavePendingTables(crate::aarch64::gic::Error),
|
||||
/// Error in saving GIC distributor registers.
|
||||
SaveDistributorRegisters(crate::aarch64::gic::Error),
|
||||
/// Error in restoring GIC distributor registers.
|
||||
RestoreDistributorRegisters(crate::aarch64::gic::Error),
|
||||
/// Error in saving GIC distributor control registers.
|
||||
SaveDistributorCtrlRegisters(crate::aarch64::gic::Error),
|
||||
/// Error in restoring GIC distributor control registers.
|
||||
RestoreDistributorCtrlRegisters(crate::aarch64::gic::Error),
|
||||
/// Error in saving GIC redistributor registers.
|
||||
SaveRedistributorRegisters(crate::aarch64::gic::Error),
|
||||
/// Error in restoring GIC redistributor registers.
|
||||
RestoreRedistributorRegisters(crate::aarch64::gic::Error),
|
||||
/// Error in saving GIC CPU interface registers.
|
||||
SaveIccRegisters(crate::aarch64::gic::Error),
|
||||
/// Error in restoring GIC CPU interface registers.
|
||||
RestoreIccRegisters(crate::aarch64::gic::Error),
|
||||
/// Error in saving GICv3ITS IIDR register.
|
||||
SaveITSIIDR(crate::aarch64::gic::Error),
|
||||
/// Error in restoring GICv3ITS IIDR register.
|
||||
RestoreITSIIDR(crate::aarch64::gic::Error),
|
||||
/// Error in saving GICv3ITS CBASER register.
|
||||
SaveITSCBASER(crate::aarch64::gic::Error),
|
||||
/// Error in restoring GICv3ITS CBASER register.
|
||||
RestoreITSCBASER(crate::aarch64::gic::Error),
|
||||
/// Error in saving GICv3ITS CREADR register.
|
||||
SaveITSCREADR(crate::aarch64::gic::Error),
|
||||
/// Error in restoring GICv3ITS CREADR register.
|
||||
RestoreITSCREADR(crate::aarch64::gic::Error),
|
||||
/// Error in saving GICv3ITS CWRITER register.
|
||||
SaveITSCWRITER(crate::aarch64::gic::Error),
|
||||
/// Error in restoring GICv3ITS CWRITER register.
|
||||
RestoreITSCWRITER(crate::aarch64::gic::Error),
|
||||
/// Error in saving GICv3ITS BASER register.
|
||||
SaveITSBASER(crate::aarch64::gic::Error),
|
||||
/// Error in restoring GICv3ITS BASER register.
|
||||
RestoreITSBASER(crate::aarch64::gic::Error),
|
||||
/// Error in saving GICv3ITS CTLR register.
|
||||
SaveITSCTLR(crate::aarch64::gic::Error),
|
||||
/// Error in restoring GICv3ITS CTLR register.
|
||||
RestoreITSCTLR(crate::aarch64::gic::Error),
|
||||
/// Error in saving GICv3ITS restore tables.
|
||||
SaveITSTables(crate::aarch64::gic::Error),
|
||||
/// Error in restoring GICv3ITS restore tables.
|
||||
RestoreITSTables(crate::aarch64::gic::Error),
|
||||
}
|
||||
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
/// Access an ITS device attribute.
|
||||
///
|
||||
/// This is a helper function to get/set the ITS device attribute depending
|
||||
/// the bool parameter `set` provided.
|
||||
pub fn gicv3_its_attr_access(
|
||||
its_device: &Arc<dyn hypervisor::Device>,
|
||||
group: u32,
|
||||
attr: u32,
|
||||
val: &u64,
|
||||
set: bool,
|
||||
) -> crate::aarch64::gic::Result<()> {
|
||||
let mut gicv3_its_attr = kvm_bindings::kvm_device_attr {
|
||||
group,
|
||||
attr: attr as u64,
|
||||
addr: val as *const u64 as u64,
|
||||
flags: 0,
|
||||
};
|
||||
if set {
|
||||
its_device
|
||||
.set_device_attr(&gicv3_its_attr)
|
||||
.map_err(crate::aarch64::gic::Error::SetDeviceAttribute)?;
|
||||
} else {
|
||||
its_device
|
||||
.get_device_attr(&mut gicv3_its_attr)
|
||||
.map_err(crate::aarch64::gic::Error::GetDeviceAttribute)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Function that saves/restores ITS tables into guest RAM.
|
||||
///
|
||||
/// The tables get flushed to guest RAM whenever the VM gets stopped.
|
||||
pub fn gicv3_its_tables_access(
|
||||
its_device: &Arc<dyn hypervisor::Device>,
|
||||
save: bool,
|
||||
) -> crate::aarch64::gic::Result<()> {
|
||||
let attr = if save {
|
||||
u64::from(kvm_bindings::KVM_DEV_ARM_ITS_SAVE_TABLES)
|
||||
} else {
|
||||
u64::from(kvm_bindings::KVM_DEV_ARM_ITS_RESTORE_TABLES)
|
||||
};
|
||||
|
||||
let init_gic_attr = kvm_bindings::kvm_device_attr {
|
||||
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
attr,
|
||||
addr: 0,
|
||||
flags: 0,
|
||||
};
|
||||
its_device
|
||||
.set_device_attr(&init_gic_attr)
|
||||
.map_err(crate::aarch64::gic::Error::SetDeviceAttribute)
|
||||
}
|
||||
|
||||
pub struct KvmGicV3Its {
|
||||
/// The hypervisor agnostic device for the GicV3
|
||||
device: Arc<dyn hypervisor::Device>,
|
||||
|
||||
/// The hypervisor agnostic device for the Its device
|
||||
its_device: Option<Arc<dyn hypervisor::Device>>,
|
||||
|
||||
/// Vector holding values of GICR_TYPER for each vCPU
|
||||
gicr_typers: Vec<u64>,
|
||||
|
||||
/// GIC device properties, to be used for setting up the fdt entry
|
||||
gic_properties: [u64; 4],
|
||||
|
||||
/// MSI device properties, to be used for setting up the fdt entry
|
||||
msi_properties: [u64; 2],
|
||||
|
||||
/// Number of CPUs handled by the device
|
||||
vcpu_count: u64,
|
||||
}
|
||||
|
||||
#[derive(Versionize)]
|
||||
pub struct Gicv3ItsState {
|
||||
dist: Vec<u32>,
|
||||
rdist: Vec<u32>,
|
||||
icc: Vec<u32>,
|
||||
// special register that enables interrupts and affinity routing
|
||||
gicd_ctlr: u32,
|
||||
its_ctlr: u64,
|
||||
its_iidr: u64,
|
||||
its_cbaser: u64,
|
||||
its_cwriter: u64,
|
||||
its_creadr: u64,
|
||||
its_baser: [u64; 8],
|
||||
}
|
||||
|
||||
impl VersionMapped for Gicv3ItsState {}
|
||||
|
||||
impl KvmGicV3Its {
|
||||
fn get_msi_size() -> u64 {
|
||||
layout::GIC_V3_ITS_SIZE
|
||||
}
|
||||
|
||||
fn get_msi_addr(vcpu_count: u64) -> u64 {
|
||||
KvmGicV3::get_redists_addr(vcpu_count) - KvmGicV3Its::get_msi_size()
|
||||
}
|
||||
|
||||
/// Save the state of GICv3ITS.
|
||||
fn state(&self, gicr_typers: &[u64]) -> Result<Gicv3ItsState> {
|
||||
let gicd_ctlr =
|
||||
read_ctlr(self.device()).map_err(Error::SaveDistributorCtrlRegisters)?;
|
||||
|
||||
let dist_state =
|
||||
get_dist_regs(self.device()).map_err(Error::SaveDistributorRegisters)?;
|
||||
|
||||
let rdist_state = get_redist_regs(self.device(), gicr_typers)
|
||||
.map_err(Error::SaveRedistributorRegisters)?;
|
||||
|
||||
let icc_state =
|
||||
get_icc_regs(self.device(), gicr_typers).map_err(Error::SaveIccRegisters)?;
|
||||
|
||||
let its_baser_state: [u64; 8] = [0; 8];
|
||||
for i in 0..8 {
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_BASER + i * 8,
|
||||
&its_baser_state[i as usize],
|
||||
false,
|
||||
)
|
||||
.map_err(Error::SaveITSBASER)?;
|
||||
}
|
||||
|
||||
let its_ctlr_state: u64 = 0;
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CTLR,
|
||||
&its_ctlr_state,
|
||||
false,
|
||||
)
|
||||
.map_err(Error::SaveITSCTLR)?;
|
||||
|
||||
let its_cbaser_state: u64 = 0;
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CBASER,
|
||||
&its_cbaser_state,
|
||||
false,
|
||||
)
|
||||
.map_err(Error::SaveITSCBASER)?;
|
||||
|
||||
let its_creadr_state: u64 = 0;
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CREADR,
|
||||
&its_creadr_state,
|
||||
false,
|
||||
)
|
||||
.map_err(Error::SaveITSCREADR)?;
|
||||
|
||||
let its_cwriter_state: u64 = 0;
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CWRITER,
|
||||
&its_cwriter_state,
|
||||
false,
|
||||
)
|
||||
.map_err(Error::SaveITSCWRITER)?;
|
||||
|
||||
let its_iidr_state: u64 = 0;
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_IIDR,
|
||||
&its_iidr_state,
|
||||
false,
|
||||
)
|
||||
.map_err(Error::SaveITSIIDR)?;
|
||||
|
||||
Ok(Gicv3ItsState {
|
||||
dist: dist_state,
|
||||
rdist: rdist_state,
|
||||
icc: icc_state,
|
||||
gicd_ctlr,
|
||||
its_ctlr: its_ctlr_state,
|
||||
its_iidr: its_iidr_state,
|
||||
its_cbaser: its_cbaser_state,
|
||||
its_cwriter: its_cwriter_state,
|
||||
its_creadr: its_creadr_state,
|
||||
its_baser: its_baser_state,
|
||||
})
|
||||
}
|
||||
|
||||
/// Restore the state of GICv3ITS.
|
||||
fn set_state(&mut self, gicr_typers: &[u64], state: &Gicv3ItsState) -> Result<()> {
|
||||
write_ctlr(self.device(), state.gicd_ctlr)
|
||||
.map_err(Error::RestoreDistributorCtrlRegisters)?;
|
||||
|
||||
set_dist_regs(self.device(), &state.dist)
|
||||
.map_err(Error::RestoreDistributorRegisters)?;
|
||||
|
||||
set_redist_regs(self.device(), gicr_typers, &state.rdist)
|
||||
.map_err(Error::RestoreRedistributorRegisters)?;
|
||||
|
||||
set_icc_regs(self.device(), gicr_typers, &state.icc)
|
||||
.map_err(Error::RestoreIccRegisters)?;
|
||||
|
||||
//Restore GICv3ITS registers
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_IIDR,
|
||||
&state.its_iidr,
|
||||
true,
|
||||
)
|
||||
.map_err(Error::RestoreITSIIDR)?;
|
||||
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CBASER,
|
||||
&state.its_cbaser,
|
||||
true,
|
||||
)
|
||||
.map_err(Error::RestoreITSCBASER)?;
|
||||
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CREADR,
|
||||
&state.its_creadr,
|
||||
true,
|
||||
)
|
||||
.map_err(Error::RestoreITSCREADR)?;
|
||||
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CWRITER,
|
||||
&state.its_cwriter,
|
||||
true,
|
||||
)
|
||||
.map_err(Error::RestoreITSCWRITER)?;
|
||||
|
||||
for i in 0..8 {
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_BASER + i * 8,
|
||||
&state.its_baser[i as usize],
|
||||
true,
|
||||
)
|
||||
.map_err(Error::RestoreITSBASER)?;
|
||||
}
|
||||
|
||||
// Restore ITS tables
|
||||
gicv3_its_tables_access(self.its_device().unwrap(), false)
|
||||
.map_err(Error::RestoreITSTables)?;
|
||||
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CTLR,
|
||||
&state.its_ctlr,
|
||||
true,
|
||||
)
|
||||
.map_err(Error::RestoreITSCTLR)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl GicDevice for KvmGicV3Its {
|
||||
fn device(&self) -> &Arc<dyn hypervisor::Device> {
|
||||
&self.device
|
||||
}
|
||||
|
||||
fn its_device(&self) -> Option<&Arc<dyn hypervisor::Device>> {
|
||||
self.its_device.as_ref()
|
||||
}
|
||||
|
||||
fn fdt_compatibility(&self) -> &str {
|
||||
"arm,gic-v3"
|
||||
}
|
||||
|
||||
fn msi_compatible(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn msi_compatibility(&self) -> &str {
|
||||
"arm,gic-v3-its"
|
||||
}
|
||||
|
||||
fn fdt_maint_irq(&self) -> u32 {
|
||||
KvmGicV3::ARCH_GIC_V3_MAINT_IRQ
|
||||
}
|
||||
|
||||
fn msi_properties(&self) -> &[u64] {
|
||||
&self.msi_properties
|
||||
}
|
||||
|
||||
fn device_properties(&self) -> &[u64] {
|
||||
&self.gic_properties
|
||||
}
|
||||
|
||||
fn vcpu_count(&self) -> u64 {
|
||||
self.vcpu_count
|
||||
}
|
||||
|
||||
fn set_its_device(&mut self, its_device: Option<Arc<dyn hypervisor::Device>>) {
|
||||
self.its_device = its_device;
|
||||
}
|
||||
|
||||
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
|
||||
let gicr_typers = construct_gicr_typers(vcpu_states);
|
||||
self.gicr_typers = gicr_typers;
|
||||
}
|
||||
|
||||
fn as_any_concrete_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl KvmGicDevice for KvmGicV3Its {
|
||||
fn version() -> u32 {
|
||||
KvmGicV3::version()
|
||||
}
|
||||
|
||||
fn create_device(
|
||||
device: Arc<dyn hypervisor::Device>,
|
||||
vcpu_count: u64,
|
||||
) -> Box<dyn GicDevice> {
|
||||
Box::new(KvmGicV3Its {
|
||||
device,
|
||||
its_device: None,
|
||||
gicr_typers: vec![0; vcpu_count.try_into().unwrap()],
|
||||
gic_properties: [
|
||||
KvmGicV3::get_dist_addr(),
|
||||
KvmGicV3::get_dist_size(),
|
||||
KvmGicV3::get_redists_addr(vcpu_count),
|
||||
KvmGicV3::get_redists_size(vcpu_count),
|
||||
],
|
||||
msi_properties: [
|
||||
KvmGicV3Its::get_msi_addr(vcpu_count),
|
||||
KvmGicV3Its::get_msi_size(),
|
||||
],
|
||||
vcpu_count,
|
||||
})
|
||||
}
|
||||
|
||||
fn init_device_attributes(
|
||||
vm: &Arc<dyn hypervisor::Vm>,
|
||||
gic_device: &mut dyn GicDevice,
|
||||
) -> crate::aarch64::gic::Result<()> {
|
||||
KvmGicV3::init_device_attributes(vm, gic_device)?;
|
||||
|
||||
let mut its_device = kvm_bindings::kvm_create_device {
|
||||
type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS,
|
||||
fd: 0,
|
||||
flags: 0,
|
||||
};
|
||||
|
||||
let its_fd = vm
|
||||
.create_device(&mut its_device)
|
||||
.map_err(crate::aarch64::gic::Error::CreateGic)?;
|
||||
|
||||
Self::set_device_attribute(
|
||||
&its_fd,
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE),
|
||||
&KvmGicV3Its::get_msi_addr(gic_device.vcpu_count()) as *const u64 as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
Self::set_device_attribute(
|
||||
&its_fd,
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
|
||||
0,
|
||||
0,
|
||||
)?;
|
||||
|
||||
gic_device.set_its_device(Some(its_fd));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub const GIC_V3_ITS_SNAPSHOT_ID: &str = "gic-v3-its";
|
||||
impl Snapshottable for KvmGicV3Its {
|
||||
fn id(&self) -> String {
|
||||
GIC_V3_ITS_SNAPSHOT_ID.to_string()
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
let gicr_typers = self.gicr_typers.clone();
|
||||
Snapshot::new_from_versioned_state(&self.id(), &self.state(&gicr_typers).unwrap())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
let gicr_typers = self.gicr_typers.clone();
|
||||
self.set_state(&gicr_typers, &snapshot.to_versioned_state(&self.id())?)
|
||||
.map_err(|e| {
|
||||
MigratableError::Restore(anyhow!("Could not restore GICv3ITS state {:?}", e))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Pausable for KvmGicV3Its {
|
||||
fn pause(&mut self) -> std::result::Result<(), MigratableError> {
|
||||
// Flush redistributors pending tables to guest RAM.
|
||||
save_pending_tables(self.device()).map_err(|e| {
|
||||
MigratableError::Pause(anyhow!(
|
||||
"Could not save GICv3ITS GIC pending tables {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
// Flush ITS tables to guest RAM.
|
||||
gicv3_its_tables_access(self.its_device().unwrap(), true).map_err(|e| {
|
||||
MigratableError::Pause(anyhow!("Could not save GICv3ITS ITS tables {:?}", e))
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for KvmGicV3Its {}
|
||||
impl Migratable for KvmGicV3Its {}
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
// Copyright 2021 Arm Limited (or its affiliates). All rights reserved.
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod dist_regs;
|
||||
pub mod gicv3;
|
||||
pub mod gicv3_its;
|
||||
pub mod icc_regs;
|
||||
pub mod redist_regs;
|
||||
|
||||
pub use self::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
||||
pub use self::icc_regs::{get_icc_regs, set_icc_regs};
|
||||
pub use self::redist_regs::{get_redist_regs, set_redist_regs};
|
||||
use hypervisor::CpuState;
|
||||
use std::any::Any;
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Errors thrown while setting up the GIC.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Error while calling KVM ioctl for setting up the global interrupt controller.
|
||||
CreateGic(hypervisor::HypervisorVmError),
|
||||
/// Error while setting device attributes for the GIC.
|
||||
SetDeviceAttribute(hypervisor::HypervisorDeviceError),
|
||||
/// Error while getting device attributes for the GIC.
|
||||
GetDeviceAttribute(hypervisor::HypervisorDeviceError),
|
||||
}
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
pub trait GicDevice: Send {
|
||||
/// Returns the hypervisor agnostic Device of the GIC device
|
||||
fn device(&self) -> &Arc<dyn hypervisor::Device>;
|
||||
|
||||
/// Returns the hypervisor agnostic Device of the ITS device
|
||||
fn its_device(&self) -> Option<&Arc<dyn hypervisor::Device>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the fdt compatibility property of the device
|
||||
fn fdt_compatibility(&self) -> &str;
|
||||
|
||||
/// Returns the maint_irq fdt property of the device
|
||||
fn fdt_maint_irq(&self) -> u32;
|
||||
|
||||
/// Returns an array with GIC device properties
|
||||
fn device_properties(&self) -> &[u64];
|
||||
|
||||
/// Returns the number of vCPUs this GIC handles
|
||||
fn vcpu_count(&self) -> u64;
|
||||
|
||||
/// Returns whether the GIC device is MSI compatible or not
|
||||
fn msi_compatible(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns the MSI compatibility property of the device
|
||||
fn msi_compatibility(&self) -> &str {
|
||||
""
|
||||
}
|
||||
|
||||
/// Returns the MSI reg property of the device
|
||||
fn msi_properties(&self) -> &[u64] {
|
||||
&[]
|
||||
}
|
||||
|
||||
fn set_its_device(&mut self, its_device: Option<Arc<dyn hypervisor::Device>>);
|
||||
|
||||
/// Get the values of GICR_TYPER for each vCPU.
|
||||
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]);
|
||||
|
||||
/// Downcast the trait object to its concrete type.
|
||||
fn as_any_concrete_mut(&mut self) -> &mut dyn Any;
|
||||
}
|
||||
|
||||
pub mod kvm {
|
||||
use super::GicDevice;
|
||||
use super::Result;
|
||||
use crate::aarch64::gic::gicv3_its::kvm::KvmGicV3Its;
|
||||
use crate::layout;
|
||||
use hypervisor::kvm::kvm_bindings;
|
||||
use std::boxed::Box;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Trait for GIC devices.
|
||||
pub trait KvmGicDevice: Send + Sync + GicDevice {
|
||||
/// Returns the GIC version of the device
|
||||
fn version() -> u32;
|
||||
|
||||
/// Create the GIC device object
|
||||
fn create_device(
|
||||
device: Arc<dyn hypervisor::Device>,
|
||||
vcpu_count: u64,
|
||||
) -> Box<dyn GicDevice>;
|
||||
|
||||
/// Setup the device-specific attributes
|
||||
fn init_device_attributes(
|
||||
vm: &Arc<dyn hypervisor::Vm>,
|
||||
gic_device: &mut dyn GicDevice,
|
||||
) -> Result<()>;
|
||||
|
||||
/// Initialize a GIC device
|
||||
fn init_device(vm: &Arc<dyn hypervisor::Vm>) -> Result<Arc<dyn hypervisor::Device>> {
|
||||
let mut gic_device = kvm_bindings::kvm_create_device {
|
||||
type_: Self::version(),
|
||||
fd: 0,
|
||||
flags: 0,
|
||||
};
|
||||
|
||||
vm.create_device(&mut gic_device)
|
||||
.map_err(super::Error::CreateGic)
|
||||
}
|
||||
|
||||
/// Set a GIC device attribute
|
||||
fn set_device_attribute(
|
||||
device: &Arc<dyn hypervisor::Device>,
|
||||
group: u32,
|
||||
attr: u64,
|
||||
addr: u64,
|
||||
flags: u32,
|
||||
) -> Result<()> {
|
||||
let attr = kvm_bindings::kvm_device_attr {
|
||||
flags,
|
||||
group,
|
||||
attr,
|
||||
addr,
|
||||
};
|
||||
device
|
||||
.set_device_attr(&attr)
|
||||
.map_err(super::Error::SetDeviceAttribute)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get a GIC device attribute
|
||||
fn get_device_attribute(
|
||||
device: &Arc<dyn hypervisor::Device>,
|
||||
group: u32,
|
||||
attr: u64,
|
||||
addr: u64,
|
||||
flags: u32,
|
||||
) -> Result<()> {
|
||||
let mut attr = kvm_bindings::kvm_device_attr {
|
||||
flags,
|
||||
group,
|
||||
attr,
|
||||
addr,
|
||||
};
|
||||
device
|
||||
.get_device_attr(&mut attr)
|
||||
.map_err(super::Error::GetDeviceAttribute)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Finalize the setup of a GIC device
|
||||
fn finalize_device(gic_device: &dyn GicDevice) -> Result<()> {
|
||||
/* We need to tell the kernel how many irqs to support with this vgic.
|
||||
* See the `layout` module for details.
|
||||
*/
|
||||
let nr_irqs: u32 = layout::IRQ_NUM;
|
||||
let nr_irqs_ptr = &nr_irqs as *const u32;
|
||||
Self::set_device_attribute(
|
||||
gic_device.device(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
|
||||
0,
|
||||
nr_irqs_ptr as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
/* Finalize the GIC.
|
||||
* See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
|
||||
*/
|
||||
Self::set_device_attribute(
|
||||
gic_device.device(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
|
||||
0,
|
||||
0,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Method to initialize the GIC device
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
fn new(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GicDevice>> {
|
||||
let vgic_fd = Self::init_device(vm)?;
|
||||
|
||||
let mut device = Self::create_device(vgic_fd, vcpu_count);
|
||||
|
||||
Self::init_device_attributes(vm, &mut *device)?;
|
||||
|
||||
Self::finalize_device(&*device)?;
|
||||
|
||||
Ok(device)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a GICv3-ITS device.
|
||||
///
|
||||
pub fn create_gic(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GicDevice>> {
|
||||
debug!("creating a GICv3-ITS");
|
||||
KvmGicV3Its::new(vm, vcpu_count)
|
||||
}
|
||||
|
||||
/// Function that saves RDIST pending tables into guest RAM.
|
||||
///
|
||||
/// The tables get flushed to guest RAM whenever the VM gets stopped.
|
||||
pub fn save_pending_tables(gic: &Arc<dyn hypervisor::Device>) -> Result<()> {
|
||||
let init_gic_attr = kvm_bindings::kvm_device_attr {
|
||||
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES),
|
||||
addr: 0,
|
||||
flags: 0,
|
||||
};
|
||||
gic.set_device_attr(&init_gic_attr)
|
||||
.map_err(super::Error::SetDeviceAttribute)
|
||||
}
|
||||
}
|
||||
@@ -4,23 +4,19 @@
|
||||
|
||||
/// Module for the flattened device tree.
|
||||
pub mod fdt;
|
||||
/// Module for the global interrupt controller configuration.
|
||||
pub mod gic;
|
||||
/// Layout for this aarch64 system.
|
||||
pub mod layout;
|
||||
/// Logic for configuring aarch64 registers.
|
||||
pub mod regs;
|
||||
/// Module for loading UEFI binary.
|
||||
pub mod uefi;
|
||||
|
||||
pub use self::fdt::DeviceInfoForFdt;
|
||||
use crate::{DeviceType, GuestMemoryMmap, NumaNodes, PciSpaceInfo, RegionType};
|
||||
use gic::GicDevice;
|
||||
use hypervisor::arch::aarch64::gic::Vgic;
|
||||
use log::{log_enabled, Level};
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use vm_memory::{Address, GuestAddress, GuestMemory, GuestUsize};
|
||||
|
||||
/// Errors thrown while configuring aarch64 system.
|
||||
@@ -33,13 +29,13 @@ pub enum Error {
|
||||
WriteFdtToMemory(fdt::Error),
|
||||
|
||||
/// Failed to create a GIC.
|
||||
SetupGic(gic::Error),
|
||||
SetupGic,
|
||||
|
||||
/// Failed to compute the initramfs address.
|
||||
InitramfsAddress,
|
||||
|
||||
/// Error configuring the general purpose registers
|
||||
RegsConfiguration(regs::Error),
|
||||
RegsConfiguration(hypervisor::HypervisorCpuError),
|
||||
|
||||
/// Error configuring the MPIDR register
|
||||
VcpuRegMpidr(hypervisor::HypervisorCpuError),
|
||||
@@ -69,8 +65,12 @@ pub fn configure_vcpu(
|
||||
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())
|
||||
.map_err(Error::RegsConfiguration)?;
|
||||
vcpu.setup_regs(
|
||||
id,
|
||||
kernel_entry_point.entry_addr.raw_value(),
|
||||
super::layout::FDT_START.raw_value(),
|
||||
)
|
||||
.map_err(Error::RegsConfiguration)?;
|
||||
}
|
||||
|
||||
let mpidr = vcpu.read_mpidr().map_err(Error::VcpuRegMpidr)?;
|
||||
@@ -143,7 +143,7 @@ pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::Bui
|
||||
initrd: &Option<super::InitramfsConfig>,
|
||||
pci_space_info: &[PciSpaceInfo],
|
||||
virtio_iommu_bdf: Option<u32>,
|
||||
gic_device: &dyn GicDevice,
|
||||
gic_device: &Arc<Mutex<dyn Vgic>>,
|
||||
numa_nodes: &NumaNodes,
|
||||
pmu_supported: bool,
|
||||
) -> super::Result<()> {
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the THIRD-PARTY file.
|
||||
|
||||
use hypervisor::kvm::kvm_bindings::{
|
||||
kvm_regs, user_pt_regs, KVM_REG_ARM64, KVM_REG_ARM_CORE, KVM_REG_SIZE_U64,
|
||||
};
|
||||
use hypervisor::{arm64_core_reg_id, offset__of};
|
||||
use std::sync::Arc;
|
||||
use std::{mem, result};
|
||||
use vm_memory::Address;
|
||||
|
||||
/// Errors thrown while setting aarch64 registers.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Failed to set core register (PC, PSTATE or general purpose ones).
|
||||
SetCoreRegister(hypervisor::HypervisorCpuError),
|
||||
/// Failed to get a system register.
|
||||
GetSysRegister(hypervisor::HypervisorCpuError),
|
||||
}
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
// PSR (Processor State Register) bits.
|
||||
// Taken from arch/arm64/include/uapi/asm/ptrace.h.
|
||||
const PSR_MODE_EL1h: u64 = 0x0000_0005;
|
||||
const PSR_F_BIT: u64 = 0x0000_0040;
|
||||
const PSR_I_BIT: u64 = 0x0000_0080;
|
||||
const PSR_A_BIT: u64 = 0x0000_0100;
|
||||
const PSR_D_BIT: u64 = 0x0000_0200;
|
||||
// Taken from arch/arm64/kvm/inject_fault.c.
|
||||
const PSTATE_FAULT_BITS_64: u64 = PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT;
|
||||
|
||||
/// Configure core registers for a given CPU.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
||||
/// * `cpu_id` - Index of current vcpu.
|
||||
/// * `boot_ip` - Starting instruction pointer.
|
||||
/// * `mem` - Reserved DRAM for current VM.
|
||||
pub fn setup_regs(vcpu: &Arc<dyn hypervisor::Vcpu>, cpu_id: u8, boot_ip: u64) -> Result<()> {
|
||||
let kreg_off = offset__of!(kvm_regs, regs);
|
||||
|
||||
// Get the register index of the PSTATE (Processor State) register.
|
||||
let pstate = offset__of!(user_pt_regs, pstate) + kreg_off;
|
||||
vcpu.set_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, pstate),
|
||||
PSTATE_FAULT_BITS_64,
|
||||
)
|
||||
.map_err(Error::SetCoreRegister)?;
|
||||
|
||||
// Other vCPUs are powered off initially awaiting PSCI wakeup.
|
||||
if cpu_id == 0 {
|
||||
// Setting the PC (Processor Counter) to the current program address (kernel address).
|
||||
let pc = offset__of!(user_pt_regs, pc) + kreg_off;
|
||||
vcpu.set_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, pc), boot_ip as u64)
|
||||
.map_err(Error::SetCoreRegister)?;
|
||||
|
||||
// Last mandatory thing to set -> the address pointing to the FDT (also called DTB).
|
||||
// "The device tree blob (dtb) must be placed on an 8-byte boundary and must
|
||||
// not exceed 2 megabytes in size." -> https://www.kernel.org/doc/Documentation/arm64/booting.txt.
|
||||
// We are choosing to place it the end of DRAM. See `get_fdt_addr`.
|
||||
let regs0 = offset__of!(user_pt_regs, regs) + kreg_off;
|
||||
vcpu.set_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, regs0),
|
||||
super::layout::FDT_START.raw_value(),
|
||||
)
|
||||
.map_err(Error::SetCoreRegister)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -54,7 +54,7 @@ pub enum Error {
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
/// Type for memory region types.
|
||||
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize, Versionize)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize, Versionize)]
|
||||
pub enum RegionType {
|
||||
/// RAM type
|
||||
Ram,
|
||||
|
||||
@@ -17,7 +17,7 @@ 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"
|
||||
virtio-queue = "0.4.0"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
@@ -104,7 +104,7 @@ impl DiskTopology {
|
||||
|
||||
pub type DiskFileResult<T> = std::result::Result<T, DiskFileError>;
|
||||
|
||||
pub trait DiskFile: Send + Sync {
|
||||
pub trait DiskFile: Send {
|
||||
fn size(&mut self) -> DiskFileResult<u64>;
|
||||
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>>;
|
||||
fn topology(&mut self) -> DiskTopology {
|
||||
@@ -127,7 +127,7 @@ pub enum AsyncIoError {
|
||||
|
||||
pub type AsyncIoResult<T> = std::result::Result<T, AsyncIoError>;
|
||||
|
||||
pub trait AsyncIo: Send + Sync {
|
||||
pub trait AsyncIo: Send {
|
||||
fn notifier(&self) -> &EventFd;
|
||||
fn read_vectored(
|
||||
&mut self,
|
||||
|
||||
@@ -138,7 +138,7 @@ impl ExecuteError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum RequestType {
|
||||
In,
|
||||
Out,
|
||||
|
||||
@@ -6,11 +6,12 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { path = "../acpi_tables" }
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.58"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "1.3.2"
|
||||
byteorder = "1.4.3"
|
||||
epoll = "4.3.1"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
versionize = "0.1.6"
|
||||
|
||||
@@ -4,30 +4,34 @@
|
||||
|
||||
use super::interrupt_controller::{Error, InterruptController};
|
||||
extern crate arch;
|
||||
use arch::aarch64::gic::GicDevice;
|
||||
use anyhow::anyhow;
|
||||
use arch::layout;
|
||||
use hypervisor::{arch::aarch64::gic::Vgic, CpuState};
|
||||
use std::result;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
|
||||
LegacyIrqSourceConfig, MsiIrqGroupConfig,
|
||||
};
|
||||
use vm_memory::Address;
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
// Reserve 32 IRQs for legacy device.
|
||||
pub const IRQ_LEGACY_BASE: usize = arch::layout::IRQ_BASE as usize;
|
||||
// Reserve 32 IRQs for legacy devices.
|
||||
pub const IRQ_LEGACY_BASE: usize = layout::IRQ_BASE as usize;
|
||||
pub const IRQ_LEGACY_COUNT: usize = 32;
|
||||
|
||||
// This Gic struct implements InterruptController to provide interrupt delivery service.
|
||||
// The Gic source files in arch/ folder maintain the Aarch64 specific Gic device.
|
||||
// The 2 Gic instances could be merged together.
|
||||
// Leave this refactoring to future. Two options may be considered:
|
||||
// 1. Move Gic*.rs from arch/ folder here.
|
||||
// 2. Move this file and ioapic.rs to arch/, as they are architecture specific.
|
||||
// Gic (Generic Interupt Controller) struct provides all the functionality of a
|
||||
// GIC device. It wraps a hypervisor-emulated GIC device (Vgic) provided by the
|
||||
// `hypervisor` crate.
|
||||
// Gic struct also implements InterruptController to provide interrupt delivery
|
||||
// service.
|
||||
pub struct Gic {
|
||||
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
|
||||
gic_device: Option<Arc<Mutex<Box<dyn GicDevice>>>>,
|
||||
// The hypervisor agnostic virtual GIC
|
||||
vgic: Option<Arc<Mutex<dyn Vgic>>>,
|
||||
}
|
||||
|
||||
impl Gic {
|
||||
@@ -44,16 +48,32 @@ impl Gic {
|
||||
|
||||
Ok(Gic {
|
||||
interrupt_source_group,
|
||||
gic_device: None,
|
||||
vgic: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_gic_device(&mut self, gic_device: Arc<Mutex<Box<dyn GicDevice>>>) {
|
||||
self.gic_device = Some(gic_device);
|
||||
pub fn create_vgic(
|
||||
&mut self,
|
||||
vm: &Arc<dyn hypervisor::Vm>,
|
||||
vcpu_count: u64,
|
||||
) -> Result<Arc<Mutex<dyn Vgic>>> {
|
||||
let vgic = vm
|
||||
.create_vgic(
|
||||
vcpu_count,
|
||||
layout::GIC_V3_DIST_START.raw_value(),
|
||||
layout::GIC_V3_DIST_SIZE,
|
||||
layout::GIC_V3_REDIST_SIZE,
|
||||
layout::GIC_V3_ITS_SIZE,
|
||||
layout::IRQ_NUM,
|
||||
)
|
||||
.map_err(Error::CreateGic)?;
|
||||
self.vgic = Some(vgic.clone());
|
||||
Ok(vgic.clone())
|
||||
}
|
||||
|
||||
pub fn get_gic_device(&self) -> Option<&Arc<Mutex<Box<dyn GicDevice>>>> {
|
||||
self.gic_device.as_ref()
|
||||
pub fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
|
||||
let vgic = self.vgic.as_ref().unwrap().clone();
|
||||
vgic.lock().unwrap().set_gicr_typers(vcpu_states);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,3 +117,43 @@ impl InterruptController for Gic {
|
||||
self.interrupt_source_group.notifier(irq as InterruptIndex)
|
||||
}
|
||||
}
|
||||
|
||||
pub const GIC_V3_ITS_SNAPSHOT_ID: &str = "gic-v3-its";
|
||||
impl Snapshottable for Gic {
|
||||
fn id(&self) -> String {
|
||||
GIC_V3_ITS_SNAPSHOT_ID.to_string()
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
let vgic = self.vgic.as_ref().unwrap().clone();
|
||||
let state = vgic.lock().unwrap().state().unwrap();
|
||||
Snapshot::new_from_state(&self.id(), &state)
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
let vgic = self.vgic.as_ref().unwrap().clone();
|
||||
vgic.lock()
|
||||
.unwrap()
|
||||
.set_state(&snapshot.to_state(&self.id())?)
|
||||
.map_err(|e| {
|
||||
MigratableError::Restore(anyhow!("Could not restore GICv3ITS state {:?}", e))
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Pausable for Gic {
|
||||
fn pause(&mut self) -> std::result::Result<(), MigratableError> {
|
||||
// Flush tables to guest RAM
|
||||
let vgic = self.vgic.as_ref().unwrap().clone();
|
||||
vgic.lock().unwrap().save_data_tables().map_err(|e| {
|
||||
MigratableError::Pause(anyhow!(
|
||||
"Could not save GICv3ITS GIC pending tables {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for Gic {}
|
||||
impl Migratable for Gic {}
|
||||
|
||||
@@ -24,6 +24,8 @@ pub enum Error {
|
||||
UpdateInterrupt(io::Error),
|
||||
/// Failed enabling the interrupt.
|
||||
EnableInterrupt(io::Error),
|
||||
/// Failed creating GIC device.
|
||||
CreateGic(hypervisor::HypervisorVmError),
|
||||
}
|
||||
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
@@ -89,6 +89,7 @@ Trigger power button of the VM | `/vm.power-button` | N/A
|
||||
Pause the VM | `/vm.pause` | N/A | N/A | The VM is booted
|
||||
Resume the VM | `/vm.resume` | N/A | N/A | The VM is paused
|
||||
Task a snapshot of the VM | `/vm.snapshot` | `/schemas/VmSnapshotConfig`| N/A | The VM is paused
|
||||
Perform a coredump of the VM | `/vm.coredump` | `/schemas/VmCoredumpData` | N/A | The VM is paused
|
||||
Restore the VM from a snapshot | `/vm.restore` | `/schemas/RestoreConfig` | N/A | The VM is created but not booted
|
||||
Add/remove CPUs to/from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
|
||||
Add/remove memory from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
|
||||
|
||||
@@ -6,22 +6,22 @@ This document describes the device model supported by `cloud-hypervisor`.
|
||||
|
||||
| Device | Build configurable | Enabled by default | Runtime configurable |
|
||||
| :----: | :----: | :----: | :----: |
|
||||
| Serial port | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
||||
| RTC/CMOS | :heavy_check_mark: | :heavy_check_mark: | :negative_squared_cross_mark: |
|
||||
| I/O APIC | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
||||
| i8042 shutdown/reboot | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :negative_squared_cross_mark: |
|
||||
| ACPI shutdown/reboot | :negative_squared_cross_mark: | :heavy_check_mark: | :negative_squared_cross_mark: |
|
||||
| virtio-blk | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
||||
| virtio-console | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
||||
| virtio-iommu | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
||||
| virtio-net | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
||||
| virtio-pmem | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
||||
| virtio-rng | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
||||
| virtio-vsock | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
||||
| vhost-user-blk | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
||||
| vhost-user-fs | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
||||
| vhost-user-net | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
||||
| VFIO | :heavy_check_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
||||
| Serial port | :x: | :x: | :heavy_check_mark: |
|
||||
| RTC/CMOS | :heavy_check_mark: | :heavy_check_mark: | :x: |
|
||||
| I/O APIC | :x: | :x: | :heavy_check_mark: |
|
||||
| i8042 shutdown/reboot | :x: | :x: | :x: |
|
||||
| ACPI shutdown/reboot | :x: | :heavy_check_mark: | :x: |
|
||||
| virtio-blk | :x: | :x: | :heavy_check_mark: |
|
||||
| virtio-console | :x: | :x: | :heavy_check_mark: |
|
||||
| virtio-iommu | :x: | :x: | :heavy_check_mark: |
|
||||
| virtio-net | :x: | :x: | :heavy_check_mark: |
|
||||
| virtio-pmem | :x: | :x: | :heavy_check_mark: |
|
||||
| virtio-rng | :x: | :x: | :heavy_check_mark: |
|
||||
| virtio-vsock | :x: | :x: | :heavy_check_mark: |
|
||||
| vhost-user-blk | :x: | :x: | :heavy_check_mark: |
|
||||
| vhost-user-fs | :x: | :x: | :heavy_check_mark: |
|
||||
| vhost-user-net | :x: | :x: | :heavy_check_mark: |
|
||||
| VFIO | :heavy_check_mark: | :x: | :heavy_check_mark: |
|
||||
|
||||
## Legacy devices
|
||||
|
||||
|
||||
@@ -43,3 +43,22 @@ $ perf report -g
|
||||
```
|
||||
|
||||
If profiling with a network device attached either the TAP device must be already created and configured or the profiling must be done as root so that the TAP device can be created.
|
||||
|
||||
## Userspace only profiling with LBR
|
||||
|
||||
The use of LBR (Last Branch Record; available since Haswell) offers lower
|
||||
overhead if only userspace profiling is required. This lower overhead can allow
|
||||
a higher frequency of sampling. This also removes the requirement to compile
|
||||
with custom `RUSTFLAGS` however debug symbols should still be included:
|
||||
|
||||
e.g.
|
||||
|
||||
```
|
||||
$ perf record --call-graph lbr --all-user --user-callchains -g target/release/cloud-hypervisor \
|
||||
--kernel ~/src/linux/vmlinux \
|
||||
--pmem file=~/workloads/focal.raw \
|
||||
--cpus boot=1 --memory size=1G \
|
||||
--cmdline "root=/dev/pmem0p1 console=ttyS0" \
|
||||
--serial tty --console off \
|
||||
--api-socket=/tmp/api1
|
||||
```
|
||||
|
||||
@@ -6,5 +6,5 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.126"
|
||||
serde = { version = "1.0.137", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.81"
|
||||
serde = { version = "1.0.138", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.82"
|
||||
|
||||
130
fuzz/Cargo.lock
generated
130
fuzz/Cargo.lock
generated
@@ -11,9 +11,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.57"
|
||||
version = "1.0.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
|
||||
checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -24,9 +24,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.1.0"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c38b6b6b79f671c25e1a3e785b7b82d7562ffc9cd3efdc98627e5668a2472490"
|
||||
checksum = "5a7924531f38b1970ff630f03eb20a2fde69db5c590c93b0f3482e95dcc5fd60"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
@@ -127,15 +127,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.1.18"
|
||||
version = "3.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
|
||||
checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"terminal_size",
|
||||
@@ -144,16 +144,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.0"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
|
||||
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "23.0.0"
|
||||
version = "24.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -216,6 +216,7 @@ dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"epoll",
|
||||
"hypervisor",
|
||||
"libc",
|
||||
"log",
|
||||
"versionize",
|
||||
@@ -251,35 +252,11 @@ version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b643857cf70949306b81d7e92cb9d47add673868edac9863c4a49c42feaf3f1e"
|
||||
|
||||
[[package]]
|
||||
name = "gdbstub"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa2ca5d6b045de372cef3991f873389b421b4cabcfbe52f7787fae8b8b37906"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"log",
|
||||
"managed",
|
||||
"num-traits",
|
||||
"paste",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gdbstub_arch"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51dc4b5718ac76d21e8605c0966dd32d80273b89b11e6cfef467b04e45934d37"
|
||||
dependencies = [
|
||||
"gdbstub",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.6"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@@ -288,9 +265,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
@@ -331,9 +308,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.8.1"
|
||||
version = "1.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
|
||||
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
@@ -416,12 +393,6 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "managed"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
|
||||
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
version = "0.1.0"
|
||||
@@ -458,20 +429,11 @@ dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.12.0"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
|
||||
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
||||
|
||||
[[package]]
|
||||
name = "option_parser"
|
||||
@@ -479,15 +441,9 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.0.1"
|
||||
version = "6.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435"
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc"
|
||||
checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
|
||||
|
||||
[[package]]
|
||||
name = "pci"
|
||||
@@ -514,9 +470,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.39"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
||||
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -534,9 +490,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.18"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
||||
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -587,24 +543,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.9"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd"
|
||||
checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.137"
|
||||
version = "1.0.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
||||
checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.137"
|
||||
version = "1.0.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
|
||||
checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -613,9 +569,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.81"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
|
||||
checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -655,9 +611,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.95"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
|
||||
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -714,15 +670,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
||||
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.0.0"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cfcd319456c4d6ea10087ed423473267e1a071f3bc0aa89f80d60997843c6f0"
|
||||
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
@@ -948,15 +904,13 @@ dependencies = [
|
||||
"devices",
|
||||
"epoll",
|
||||
"event_monitor",
|
||||
"gdbstub",
|
||||
"gdbstub_arch",
|
||||
"hypervisor",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"linux-loader",
|
||||
"log",
|
||||
"micro_http",
|
||||
"net_util",
|
||||
"once_cell",
|
||||
"option_parser",
|
||||
"pci",
|
||||
"qcow",
|
||||
@@ -995,9 +949,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
|
||||
@@ -16,7 +16,7 @@ qcow = { path = "../qcow" }
|
||||
seccompiler = "0.2.0"
|
||||
vhdx = { path = "../vhdx" }
|
||||
virtio-devices = { path = "../virtio-devices" }
|
||||
virtio-queue = "0.3.0"
|
||||
virtio-queue = "0.4.0"
|
||||
vmm-sys-util = "0.9.0"
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vm-memory = "0.8.0"
|
||||
|
||||
@@ -11,7 +11,7 @@ mshv = ["mshv-ioctls", "mshv-bindings"]
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.58"
|
||||
epoll = "4.3.1"
|
||||
thiserror = "1.0.31"
|
||||
libc = "0.2.126"
|
||||
@@ -20,8 +20,8 @@ kvm-ioctls = { version = "0.11.0", optional = true }
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0-tdx", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
mshv-bindings = { git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", branch = "main", optional = true}
|
||||
serde = { version = "1.0.137", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.81"
|
||||
serde = { version = "1.0.138", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.82"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = { version = "0.9.0", features = ["with-serde"] }
|
||||
|
||||
|
||||
59
hypervisor/src/arch/aarch64/gic.rs
Normal file
59
hypervisor/src/arch/aarch64/gic.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||
|
||||
use crate::{CpuState, Device, GicState, HypervisorDeviceError, HypervisorVmError};
|
||||
use std::any::Any;
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Errors thrown while setting up the VGIC.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Error while calling KVM ioctl for setting up the global interrupt controller.
|
||||
CreateGic(HypervisorVmError),
|
||||
/// Error while setting device attributes for the GIC.
|
||||
SetDeviceAttribute(HypervisorDeviceError),
|
||||
/// Error while getting device attributes for the GIC.
|
||||
GetDeviceAttribute(HypervisorDeviceError),
|
||||
}
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
/// Hypervisor agnostic interface for a virtualized GIC
|
||||
pub trait Vgic: Send + Sync {
|
||||
/// Returns the fdt compatibility property of the device
|
||||
fn fdt_compatibility(&self) -> &str;
|
||||
|
||||
/// Returns the maint_irq fdt property of the device
|
||||
fn fdt_maint_irq(&self) -> u32;
|
||||
|
||||
/// Returns an array with GIC device properties
|
||||
fn device_properties(&self) -> [u64; 4];
|
||||
|
||||
/// Returns the number of vCPUs this GIC handles
|
||||
fn vcpu_count(&self) -> u64;
|
||||
|
||||
/// Returns whether the GIC device is MSI compatible or not
|
||||
fn msi_compatible(&self) -> bool;
|
||||
|
||||
/// Returns the MSI compatibility property of the device
|
||||
fn msi_compatibility(&self) -> &str;
|
||||
|
||||
/// Returns the MSI reg property of the device
|
||||
fn msi_properties(&self) -> [u64; 2];
|
||||
|
||||
fn set_its_device(&mut self, its_device: Option<Arc<dyn Device>>);
|
||||
|
||||
/// Get the values of GICR_TYPER for each vCPU.
|
||||
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]);
|
||||
|
||||
/// Downcast the trait object to its concrete type.
|
||||
fn as_any_concrete_mut(&mut self) -> &mut dyn Any;
|
||||
|
||||
/// Save the state of GICv3ITS.
|
||||
fn state(&self) -> Result<GicState>;
|
||||
|
||||
/// Restore the state of GICv3ITS.
|
||||
fn set_state(&mut self, state: &GicState) -> Result<()>;
|
||||
|
||||
/// Saves GIC internal data tables into RAM.
|
||||
fn save_data_tables(&self) -> Result<()>;
|
||||
}
|
||||
3
hypervisor/src/arch/aarch64/mod.rs
Normal file
3
hypervisor/src/arch/aarch64/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||
|
||||
pub mod gic;
|
||||
@@ -99,7 +99,7 @@ pub enum EmulationError<T: Debug> {
|
||||
/// A CpuState is an architecture specific type, representing a CPU state.
|
||||
/// The emulator and its instruction handlers modify a given CPU state and
|
||||
/// eventually ask the platform to commit it back through `set_cpu_state`.
|
||||
pub trait PlatformEmulator: Send + Sync {
|
||||
pub trait PlatformEmulator {
|
||||
type CpuState: Clone;
|
||||
|
||||
/// Read guest memory into a u8 slice.
|
||||
|
||||
@@ -10,9 +10,14 @@
|
||||
//
|
||||
// Copyright © 2020, Microsoft Corporation
|
||||
//
|
||||
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||
//
|
||||
|
||||
pub mod emulator;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[macro_use]
|
||||
pub mod x86;
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub mod aarch64;
|
||||
|
||||
@@ -28,14 +28,11 @@ fn get_op<T: CpuStateManager>(
|
||||
)));
|
||||
}
|
||||
|
||||
match op_size {
|
||||
1 | 2 | 4 | 8 => {}
|
||||
_ => {
|
||||
return Err(PlatformError::InvalidOperand(anyhow!(
|
||||
"Invalid operand size {:?}",
|
||||
op_size
|
||||
)))
|
||||
}
|
||||
if !matches!(op_size, 1 | 2 | 4 | 8) {
|
||||
return Err(PlatformError::InvalidOperand(anyhow!(
|
||||
"Invalid operand size {:?}",
|
||||
op_size
|
||||
)));
|
||||
}
|
||||
|
||||
let value = match insn
|
||||
@@ -81,14 +78,11 @@ fn set_op<T: CpuStateManager>(
|
||||
)));
|
||||
}
|
||||
|
||||
match op_size {
|
||||
1 | 2 | 4 | 8 => {}
|
||||
_ => {
|
||||
return Err(PlatformError::InvalidOperand(anyhow!(
|
||||
"Invalid operand size {:?}",
|
||||
op_size
|
||||
)))
|
||||
}
|
||||
if !matches!(op_size, 1 | 2 | 4 | 8) {
|
||||
return Err(PlatformError::InvalidOperand(anyhow!(
|
||||
"Invalid operand size {:?}",
|
||||
op_size
|
||||
)));
|
||||
}
|
||||
|
||||
match insn
|
||||
|
||||
@@ -16,7 +16,7 @@ use iced_x86::*;
|
||||
mod instructions;
|
||||
|
||||
/// x86 CPU modes
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum CpuMode {
|
||||
/// Real mode
|
||||
Real,
|
||||
|
||||
@@ -453,6 +453,11 @@ pub trait Vcpu: Send + Sync {
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
fn read_mpidr(&self) -> Result<u64>;
|
||||
///
|
||||
/// Configure core registers for a given CPU.
|
||||
///
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
fn setup_regs(&self, cpu_id: u8, boot_ip: u64, fdt_start: u64) -> Result<()>;
|
||||
///
|
||||
/// Retrieve the vCPU state.
|
||||
/// This function is necessary to snapshot the VM
|
||||
///
|
||||
|
||||
@@ -66,6 +66,11 @@ pub enum HypervisorError {
|
||||
///
|
||||
#[error("Failed to retrieve TDX capabilities:{0}")]
|
||||
TdxCapabilities(#[source] anyhow::Error),
|
||||
///
|
||||
/// Failed to set partition property
|
||||
///
|
||||
#[error("Failed to set partition property:{0}")]
|
||||
SetPartitionProperty(#[source] anyhow::Error),
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::{Error, Result};
|
||||
use crate::layout::IRQ_BASE;
|
||||
use hypervisor::kvm::kvm_bindings::{
|
||||
use crate::arch::aarch64::gic::{Error, Result};
|
||||
use crate::kvm::kvm_bindings::{
|
||||
kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
|
||||
};
|
||||
use crate::Device;
|
||||
use std::sync::Arc;
|
||||
|
||||
/*
|
||||
@@ -77,12 +77,7 @@ static VGIC_DIST_REGS: &[DistReg] = &[
|
||||
VGIC_DIST_REG!(GICD_IPRIORITYR, 8, 0),
|
||||
];
|
||||
|
||||
fn dist_attr_access(
|
||||
gic: &Arc<dyn hypervisor::Device>,
|
||||
offset: u32,
|
||||
val: &u32,
|
||||
set: bool,
|
||||
) -> Result<()> {
|
||||
fn dist_attr_access(gic: &Arc<dyn Device>, offset: u32, val: &u32, set: bool) -> Result<()> {
|
||||
let mut gic_dist_attr = kvm_device_attr {
|
||||
group: KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
|
||||
attr: offset as u64,
|
||||
@@ -100,18 +95,18 @@ fn dist_attr_access(
|
||||
}
|
||||
|
||||
/// Get the distributor control register.
|
||||
pub fn read_ctlr(gic: &Arc<dyn hypervisor::Device>) -> Result<u32> {
|
||||
pub fn read_ctlr(gic: &Arc<dyn Device>) -> Result<u32> {
|
||||
let val: u32 = 0;
|
||||
dist_attr_access(gic, GICD_CTLR, &val, false)?;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
/// Set the distributor control register.
|
||||
pub fn write_ctlr(gic: &Arc<dyn hypervisor::Device>, val: u32) -> Result<()> {
|
||||
pub fn write_ctlr(gic: &Arc<dyn Device>, val: u32) -> Result<()> {
|
||||
dist_attr_access(gic, GICD_CTLR, &val, true)
|
||||
}
|
||||
|
||||
fn get_interrupts_num(gic: &Arc<dyn hypervisor::Device>) -> Result<u32> {
|
||||
fn get_interrupts_num(gic: &Arc<dyn Device>) -> Result<u32> {
|
||||
let num_irq = 0;
|
||||
|
||||
let mut nr_irqs_attr = kvm_device_attr {
|
||||
@@ -125,7 +120,12 @@ fn get_interrupts_num(gic: &Arc<dyn hypervisor::Device>) -> Result<u32> {
|
||||
Ok(num_irq)
|
||||
}
|
||||
|
||||
fn compute_reg_len(gic: &Arc<dyn hypervisor::Device>, reg: &DistReg, base: u32) -> Result<u32> {
|
||||
fn compute_reg_len(gic: &Arc<dyn Device>, reg: &DistReg, base: u32) -> Result<u32> {
|
||||
// FIXME:
|
||||
// Redefine some GIC constants to avoid the dependency on `layout` crate.
|
||||
// This is temporary solution, will be fixed in future refactoring.
|
||||
const LAYOUT_IRQ_BASE: u32 = 32;
|
||||
|
||||
let mut end = base;
|
||||
let num_irq = get_interrupts_num(gic)?;
|
||||
if reg.length > 0 {
|
||||
@@ -138,8 +138,8 @@ fn compute_reg_len(gic: &Arc<dyn hypervisor::Device>, reg: &DistReg, base: u32)
|
||||
// This is the type of register that takes into account the number of interrupts
|
||||
// that the model has. It is also the type of register where
|
||||
// a register relates to multiple interrupts.
|
||||
end = base + (reg.bpi as u32 * (num_irq - IRQ_BASE) / 8);
|
||||
if reg.bpi as u32 * (num_irq - IRQ_BASE) % 8 > 0 {
|
||||
end = base + (reg.bpi as u32 * (num_irq - LAYOUT_IRQ_BASE) / 8);
|
||||
if reg.bpi as u32 * (num_irq - LAYOUT_IRQ_BASE) % 8 > 0 {
|
||||
end += REG_SIZE as u32;
|
||||
}
|
||||
}
|
||||
@@ -147,7 +147,7 @@ fn compute_reg_len(gic: &Arc<dyn hypervisor::Device>, reg: &DistReg, base: u32)
|
||||
}
|
||||
|
||||
/// Set distributor registers of the GIC.
|
||||
pub fn set_dist_regs(gic: &Arc<dyn hypervisor::Device>, state: &[u32]) -> Result<()> {
|
||||
pub fn set_dist_regs(gic: &Arc<dyn Device>, state: &[u32]) -> Result<()> {
|
||||
let mut idx = 0;
|
||||
|
||||
for dreg in VGIC_DIST_REGS {
|
||||
@@ -164,7 +164,7 @@ pub fn set_dist_regs(gic: &Arc<dyn hypervisor::Device>, state: &[u32]) -> Result
|
||||
Ok(())
|
||||
}
|
||||
/// Get distributor registers of the GIC.
|
||||
pub fn get_dist_regs(gic: &Arc<dyn hypervisor::Device>) -> Result<Vec<u32>> {
|
||||
pub fn get_dist_regs(gic: &Arc<dyn Device>) -> Result<Vec<u32>> {
|
||||
let mut state = Vec::new();
|
||||
|
||||
for dreg in VGIC_DIST_REGS {
|
||||
@@ -1,13 +1,15 @@
|
||||
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::{Error, Result};
|
||||
use hypervisor::kvm::kvm_bindings::{
|
||||
use crate::arch::aarch64::gic::{Error, Result};
|
||||
use crate::kvm::kvm_bindings::{
|
||||
kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, KVM_REG_ARM64_SYSREG_CRM_MASK,
|
||||
KVM_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRN_MASK, KVM_REG_ARM64_SYSREG_CRN_SHIFT,
|
||||
KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP1_MASK,
|
||||
KVM_REG_ARM64_SYSREG_OP1_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_SHIFT,
|
||||
};
|
||||
use crate::Device;
|
||||
use std::sync::Arc;
|
||||
|
||||
const KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT: u32 = 32;
|
||||
@@ -78,7 +80,7 @@ static VGIC_ICC_REGS: &[u64] = &[
|
||||
];
|
||||
|
||||
fn icc_attr_access(
|
||||
gic: &Arc<dyn hypervisor::Device>,
|
||||
gic: &Arc<dyn Device>,
|
||||
offset: u64,
|
||||
typer: u64,
|
||||
val: &u32,
|
||||
@@ -101,7 +103,7 @@ fn icc_attr_access(
|
||||
}
|
||||
|
||||
/// Get ICC registers.
|
||||
pub fn get_icc_regs(gic: &Arc<dyn hypervisor::Device>, gicr_typer: &[u64]) -> Result<Vec<u32>> {
|
||||
pub fn get_icc_regs(gic: &Arc<dyn Device>, gicr_typer: &[u64]) -> Result<Vec<u32>> {
|
||||
let mut state: Vec<u32> = Vec::new();
|
||||
// We need this for the ICC_AP<m>R<n>_EL1 registers.
|
||||
let mut num_priority_bits = 0;
|
||||
@@ -154,11 +156,7 @@ pub fn get_icc_regs(gic: &Arc<dyn hypervisor::Device>, gicr_typer: &[u64]) -> Re
|
||||
}
|
||||
|
||||
/// Set ICC registers.
|
||||
pub fn set_icc_regs(
|
||||
gic: &Arc<dyn hypervisor::Device>,
|
||||
gicr_typer: &[u64],
|
||||
state: &[u32],
|
||||
) -> Result<()> {
|
||||
pub fn set_icc_regs(gic: &Arc<dyn Device>, gicr_typer: &[u64], state: &[u32]) -> Result<()> {
|
||||
let mut num_priority_bits = 0;
|
||||
let mut idx = 0;
|
||||
for ix in gicr_typer {
|
||||
615
hypervisor/src/kvm/aarch64/gic/mod.rs
Normal file
615
hypervisor/src/kvm/aarch64/gic/mod.rs
Normal file
@@ -0,0 +1,615 @@
|
||||
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||
|
||||
mod dist_regs;
|
||||
mod icc_regs;
|
||||
mod redist_regs;
|
||||
|
||||
use crate::arch::aarch64::gic::{Error, Result, Vgic};
|
||||
use crate::kvm::kvm_bindings;
|
||||
use crate::{CpuState, Device, Vm};
|
||||
use dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
||||
use icc_regs::{get_icc_regs, set_icc_regs};
|
||||
use redist_regs::{construct_gicr_typers, get_redist_regs, set_redist_regs};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::any::Any;
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
|
||||
const GITS_CTLR: u32 = 0x0000;
|
||||
const GITS_IIDR: u32 = 0x0004;
|
||||
const GITS_CBASER: u32 = 0x0080;
|
||||
const GITS_CWRITER: u32 = 0x0088;
|
||||
const GITS_CREADR: u32 = 0x0090;
|
||||
const GITS_BASER: u32 = 0x0100;
|
||||
|
||||
/// Access an ITS device attribute.
|
||||
///
|
||||
/// This is a helper function to get/set the ITS device attribute depending
|
||||
/// the bool parameter `set` provided.
|
||||
pub fn gicv3_its_attr_access(
|
||||
its_device: &Arc<dyn Device>,
|
||||
group: u32,
|
||||
attr: u32,
|
||||
val: &u64,
|
||||
set: bool,
|
||||
) -> Result<()> {
|
||||
let mut gicv3_its_attr = kvm_bindings::kvm_device_attr {
|
||||
group,
|
||||
attr: attr as u64,
|
||||
addr: val as *const u64 as u64,
|
||||
flags: 0,
|
||||
};
|
||||
if set {
|
||||
its_device
|
||||
.set_device_attr(&gicv3_its_attr)
|
||||
.map_err(Error::SetDeviceAttribute)
|
||||
} else {
|
||||
its_device
|
||||
.get_device_attr(&mut gicv3_its_attr)
|
||||
.map_err(Error::GetDeviceAttribute)
|
||||
}
|
||||
}
|
||||
|
||||
/// Function that saves/restores ITS tables into guest RAM.
|
||||
///
|
||||
/// The tables get flushed to guest RAM whenever the VM gets stopped.
|
||||
pub fn gicv3_its_tables_access(its_device: &Arc<dyn Device>, save: bool) -> Result<()> {
|
||||
let attr = if save {
|
||||
u64::from(kvm_bindings::KVM_DEV_ARM_ITS_SAVE_TABLES)
|
||||
} else {
|
||||
u64::from(kvm_bindings::KVM_DEV_ARM_ITS_RESTORE_TABLES)
|
||||
};
|
||||
|
||||
let init_gic_attr = kvm_bindings::kvm_device_attr {
|
||||
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
attr,
|
||||
addr: 0,
|
||||
flags: 0,
|
||||
};
|
||||
its_device
|
||||
.set_device_attr(&init_gic_attr)
|
||||
.map_err(Error::SetDeviceAttribute)
|
||||
}
|
||||
|
||||
pub struct KvmGicV3Its {
|
||||
/// The hypervisor agnostic device for the GicV3
|
||||
device: Arc<dyn Device>,
|
||||
|
||||
/// The hypervisor agnostic device for the Its device
|
||||
its_device: Option<Arc<dyn Device>>,
|
||||
|
||||
/// Vector holding values of GICR_TYPER for each vCPU
|
||||
gicr_typers: Vec<u64>,
|
||||
|
||||
/// GIC distributor address
|
||||
dist_addr: u64,
|
||||
|
||||
/// GIC distributor size
|
||||
dist_size: u64,
|
||||
|
||||
/// GIC distributors address
|
||||
redists_addr: u64,
|
||||
|
||||
/// GIC distributors size
|
||||
redists_size: u64,
|
||||
|
||||
/// GIC MSI address
|
||||
msi_addr: u64,
|
||||
|
||||
/// GIC MSI size
|
||||
msi_size: u64,
|
||||
|
||||
/// Number of CPUs handled by the device
|
||||
vcpu_count: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize)]
|
||||
pub struct Gicv3ItsState {
|
||||
dist: Vec<u32>,
|
||||
rdist: Vec<u32>,
|
||||
icc: Vec<u32>,
|
||||
// special register that enables interrupts and affinity routing
|
||||
gicd_ctlr: u32,
|
||||
its_ctlr: u64,
|
||||
its_iidr: u64,
|
||||
its_cbaser: u64,
|
||||
its_cwriter: u64,
|
||||
its_creadr: u64,
|
||||
its_baser: [u64; 8],
|
||||
}
|
||||
|
||||
impl KvmGicV3Its {
|
||||
/// Device trees specific constants
|
||||
pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
|
||||
|
||||
/// Returns the GIC version of the device
|
||||
fn version() -> u32 {
|
||||
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3
|
||||
}
|
||||
|
||||
fn device(&self) -> &Arc<dyn Device> {
|
||||
&self.device
|
||||
}
|
||||
|
||||
fn its_device(&self) -> Option<&Arc<dyn Device>> {
|
||||
self.its_device.as_ref()
|
||||
}
|
||||
|
||||
/// Setup the device-specific attributes
|
||||
fn init_device_attributes(&mut self, vm: &dyn Vm, nr_irqs: u32) -> Result<()> {
|
||||
// GicV3 part attributes
|
||||
/* Setting up the distributor attribute.
|
||||
We are placing the GIC below 1GB so we need to substract the size of the distributor.
|
||||
*/
|
||||
Self::set_device_attribute(
|
||||
self.device(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST),
|
||||
&self.dist_addr as *const u64 as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
/* Setting up the redistributors' attribute.
|
||||
We are calculating here the start of the redistributors address. We have one per CPU.
|
||||
*/
|
||||
Self::set_device_attribute(
|
||||
self.device(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST),
|
||||
&self.redists_addr as *const u64 as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
// ITS part attributes
|
||||
let mut its_device = kvm_bindings::kvm_create_device {
|
||||
type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS,
|
||||
fd: 0,
|
||||
flags: 0,
|
||||
};
|
||||
|
||||
let its_fd = vm
|
||||
.create_device(&mut its_device)
|
||||
.map_err(Error::CreateGic)?;
|
||||
|
||||
Self::set_device_attribute(
|
||||
&its_fd,
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE),
|
||||
&self.msi_addr as *const u64 as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
Self::set_device_attribute(
|
||||
&its_fd,
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
|
||||
0,
|
||||
0,
|
||||
)?;
|
||||
|
||||
self.set_its_device(Some(its_fd));
|
||||
|
||||
/* We need to tell the kernel how many irqs to support with this vgic.
|
||||
* See the `layout` module for details.
|
||||
*/
|
||||
let nr_irqs_ptr = &nr_irqs as *const u32;
|
||||
Self::set_device_attribute(
|
||||
self.device(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
|
||||
0,
|
||||
nr_irqs_ptr as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
/* Finalize the GIC.
|
||||
* See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
|
||||
*/
|
||||
Self::set_device_attribute(
|
||||
self.device(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a KVM Vgic device
|
||||
fn create_device(vm: &dyn Vm) -> Result<Arc<dyn Device>> {
|
||||
let mut gic_device = kvm_bindings::kvm_create_device {
|
||||
type_: Self::version(),
|
||||
fd: 0,
|
||||
flags: 0,
|
||||
};
|
||||
|
||||
vm.create_device(&mut gic_device).map_err(Error::CreateGic)
|
||||
}
|
||||
|
||||
/// Set a GIC device attribute
|
||||
fn set_device_attribute(
|
||||
device: &Arc<dyn Device>,
|
||||
group: u32,
|
||||
attr: u64,
|
||||
addr: u64,
|
||||
flags: u32,
|
||||
) -> Result<()> {
|
||||
let attr = kvm_bindings::kvm_device_attr {
|
||||
flags,
|
||||
group,
|
||||
attr,
|
||||
addr,
|
||||
};
|
||||
device
|
||||
.set_device_attr(&attr)
|
||||
.map_err(Error::SetDeviceAttribute)
|
||||
}
|
||||
|
||||
/// Method to initialize the GIC device
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new(
|
||||
vm: &dyn Vm,
|
||||
vcpu_count: u64,
|
||||
dist_addr: u64,
|
||||
dist_size: u64,
|
||||
redist_size: u64,
|
||||
msi_size: u64,
|
||||
nr_irqs: u32,
|
||||
) -> Result<KvmGicV3Its> {
|
||||
let vgic = Self::create_device(vm)?;
|
||||
let redists_size: u64 = redist_size * vcpu_count;
|
||||
let redists_addr: u64 = dist_addr - redists_size;
|
||||
let msi_addr: u64 = redists_addr - msi_size;
|
||||
|
||||
let mut gic_device = KvmGicV3Its {
|
||||
device: vgic,
|
||||
its_device: None,
|
||||
gicr_typers: vec![0; vcpu_count.try_into().unwrap()],
|
||||
dist_addr,
|
||||
dist_size,
|
||||
redists_addr,
|
||||
redists_size,
|
||||
msi_addr,
|
||||
msi_size,
|
||||
vcpu_count,
|
||||
};
|
||||
|
||||
gic_device.init_device_attributes(vm, nr_irqs)?;
|
||||
|
||||
Ok(gic_device)
|
||||
}
|
||||
}
|
||||
|
||||
impl Vgic for KvmGicV3Its {
|
||||
fn fdt_compatibility(&self) -> &str {
|
||||
"arm,gic-v3"
|
||||
}
|
||||
|
||||
fn msi_compatible(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn msi_compatibility(&self) -> &str {
|
||||
"arm,gic-v3-its"
|
||||
}
|
||||
|
||||
fn fdt_maint_irq(&self) -> u32 {
|
||||
KvmGicV3Its::ARCH_GIC_V3_MAINT_IRQ
|
||||
}
|
||||
|
||||
fn vcpu_count(&self) -> u64 {
|
||||
self.vcpu_count
|
||||
}
|
||||
|
||||
fn device_properties(&self) -> [u64; 4] {
|
||||
[
|
||||
self.dist_addr,
|
||||
self.dist_size,
|
||||
self.redists_addr,
|
||||
self.redists_size,
|
||||
]
|
||||
}
|
||||
|
||||
fn msi_properties(&self) -> [u64; 2] {
|
||||
[self.msi_addr, self.msi_size]
|
||||
}
|
||||
|
||||
fn set_its_device(&mut self, its_device: Option<Arc<dyn Device>>) {
|
||||
self.its_device = its_device;
|
||||
}
|
||||
|
||||
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
|
||||
let gicr_typers = construct_gicr_typers(vcpu_states);
|
||||
self.gicr_typers = gicr_typers;
|
||||
}
|
||||
|
||||
fn as_any_concrete_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
/// Save the state of GICv3ITS.
|
||||
fn state(&self) -> Result<Gicv3ItsState> {
|
||||
let gicr_typers = self.gicr_typers.clone();
|
||||
|
||||
let gicd_ctlr = read_ctlr(self.device())?;
|
||||
|
||||
let dist_state = get_dist_regs(self.device())?;
|
||||
|
||||
let rdist_state = get_redist_regs(self.device(), &gicr_typers)?;
|
||||
|
||||
let icc_state = get_icc_regs(self.device(), &gicr_typers)?;
|
||||
|
||||
let its_baser_state: [u64; 8] = [0; 8];
|
||||
for i in 0..8 {
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_BASER + i * 8,
|
||||
&its_baser_state[i as usize],
|
||||
false,
|
||||
)?;
|
||||
}
|
||||
|
||||
let its_ctlr_state: u64 = 0;
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CTLR,
|
||||
&its_ctlr_state,
|
||||
false,
|
||||
)?;
|
||||
|
||||
let its_cbaser_state: u64 = 0;
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CBASER,
|
||||
&its_cbaser_state,
|
||||
false,
|
||||
)?;
|
||||
|
||||
let its_creadr_state: u64 = 0;
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CREADR,
|
||||
&its_creadr_state,
|
||||
false,
|
||||
)?;
|
||||
|
||||
let its_cwriter_state: u64 = 0;
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CWRITER,
|
||||
&its_cwriter_state,
|
||||
false,
|
||||
)?;
|
||||
|
||||
let its_iidr_state: u64 = 0;
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_IIDR,
|
||||
&its_iidr_state,
|
||||
false,
|
||||
)?;
|
||||
|
||||
Ok(Gicv3ItsState {
|
||||
dist: dist_state,
|
||||
rdist: rdist_state,
|
||||
icc: icc_state,
|
||||
gicd_ctlr,
|
||||
its_ctlr: its_ctlr_state,
|
||||
its_iidr: its_iidr_state,
|
||||
its_cbaser: its_cbaser_state,
|
||||
its_cwriter: its_cwriter_state,
|
||||
its_creadr: its_creadr_state,
|
||||
its_baser: its_baser_state,
|
||||
})
|
||||
}
|
||||
|
||||
/// Restore the state of GICv3ITS.
|
||||
fn set_state(&mut self, state: &Gicv3ItsState) -> Result<()> {
|
||||
let gicr_typers = self.gicr_typers.clone();
|
||||
|
||||
write_ctlr(self.device(), state.gicd_ctlr)?;
|
||||
|
||||
set_dist_regs(self.device(), &state.dist)?;
|
||||
|
||||
set_redist_regs(self.device(), &gicr_typers, &state.rdist)?;
|
||||
|
||||
set_icc_regs(self.device(), &gicr_typers, &state.icc)?;
|
||||
|
||||
//Restore GICv3ITS registers
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_IIDR,
|
||||
&state.its_iidr,
|
||||
true,
|
||||
)?;
|
||||
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CBASER,
|
||||
&state.its_cbaser,
|
||||
true,
|
||||
)?;
|
||||
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CREADR,
|
||||
&state.its_creadr,
|
||||
true,
|
||||
)?;
|
||||
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CWRITER,
|
||||
&state.its_cwriter,
|
||||
true,
|
||||
)?;
|
||||
|
||||
for i in 0..8 {
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_BASER + i * 8,
|
||||
&state.its_baser[i as usize],
|
||||
true,
|
||||
)?;
|
||||
}
|
||||
|
||||
// Restore ITS tables
|
||||
gicv3_its_tables_access(self.its_device().unwrap(), false)?;
|
||||
|
||||
gicv3_its_attr_access(
|
||||
self.its_device().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CTLR,
|
||||
&state.its_ctlr,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
/// Saves GIC internal data tables into RAM, including:
|
||||
/// - RDIST pending tables
|
||||
/// - ITS tables into guest RAM.
|
||||
fn save_data_tables(&self) -> Result<()> {
|
||||
// Flash RDIST pending tables
|
||||
let init_gic_attr = kvm_bindings::kvm_device_attr {
|
||||
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES),
|
||||
addr: 0,
|
||||
flags: 0,
|
||||
};
|
||||
self.device()
|
||||
.set_device_attr(&init_gic_attr)
|
||||
.map_err(Error::SetDeviceAttribute)?;
|
||||
// Flush ITS tables to guest RAM.
|
||||
gicv3_its_tables_access(self.its_device().unwrap(), true)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::aarch64::gic::{
|
||||
get_dist_regs, get_icc_regs, get_redist_regs, set_dist_regs, set_icc_regs, set_redist_regs,
|
||||
};
|
||||
use crate::kvm::KvmGicV3Its;
|
||||
|
||||
#[test]
|
||||
fn test_create_gic() {
|
||||
let hv = crate::new().unwrap();
|
||||
let vm = hv.create_vm().unwrap();
|
||||
|
||||
assert!(KvmGicV3Its::new(
|
||||
&*vm,
|
||||
1,
|
||||
0x0900_0000 - 0x01_0000,
|
||||
0x01_0000,
|
||||
0x02_0000,
|
||||
0x02_0000,
|
||||
256
|
||||
)
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_set_dist_regs() {
|
||||
let hv = crate::new().unwrap();
|
||||
let vm = hv.create_vm().unwrap();
|
||||
let _ = vm.create_vcpu(0, None).unwrap();
|
||||
let gic = KvmGicV3Its::new(
|
||||
&*vm,
|
||||
1,
|
||||
0x0900_0000 - 0x01_0000,
|
||||
0x01_0000,
|
||||
0x02_0000,
|
||||
0x02_0000,
|
||||
256,
|
||||
)
|
||||
.expect("Cannot create gic");
|
||||
|
||||
let res = get_dist_regs(gic.device());
|
||||
assert!(res.is_ok());
|
||||
let state = res.unwrap();
|
||||
assert_eq!(state.len(), 568);
|
||||
|
||||
let res = set_dist_regs(gic.device(), &state);
|
||||
assert!(res.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_set_redist_regs() {
|
||||
let hv = crate::new().unwrap();
|
||||
let vm = hv.create_vm().unwrap();
|
||||
let _ = vm.create_vcpu(0, None).unwrap();
|
||||
let gic = KvmGicV3Its::new(
|
||||
&*vm,
|
||||
1,
|
||||
0x0900_0000 - 0x01_0000,
|
||||
0x01_0000,
|
||||
0x02_0000,
|
||||
0x02_0000,
|
||||
256,
|
||||
)
|
||||
.expect("Cannot create gic");
|
||||
|
||||
let gicr_typer = vec![123];
|
||||
let res = get_redist_regs(gic.device(), &gicr_typer);
|
||||
assert!(res.is_ok());
|
||||
let state = res.unwrap();
|
||||
println!("{}", state.len());
|
||||
assert!(state.len() == 24);
|
||||
|
||||
assert!(set_redist_regs(gic.device(), &gicr_typer, &state).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_set_icc_regs() {
|
||||
let hv = crate::new().unwrap();
|
||||
let vm = hv.create_vm().unwrap();
|
||||
let _ = vm.create_vcpu(0, None).unwrap();
|
||||
let gic = KvmGicV3Its::new(
|
||||
&*vm,
|
||||
1,
|
||||
0x0900_0000 - 0x01_0000,
|
||||
0x01_0000,
|
||||
0x02_0000,
|
||||
0x02_0000,
|
||||
256,
|
||||
)
|
||||
.expect("Cannot create gic");
|
||||
|
||||
let gicr_typer = vec![123];
|
||||
let res = get_icc_regs(gic.device(), &gicr_typer);
|
||||
assert!(res.is_ok());
|
||||
let state = res.unwrap();
|
||||
println!("{}", state.len());
|
||||
assert!(state.len() == 9);
|
||||
|
||||
assert!(set_icc_regs(gic.device(), &gicr_typer, &state).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_save_data_tables() {
|
||||
let hv = crate::new().unwrap();
|
||||
let vm = hv.create_vm().unwrap();
|
||||
let _ = vm.create_vcpu(0, None).unwrap();
|
||||
let gic = vm
|
||||
.create_vgic(
|
||||
1,
|
||||
0x0900_0000 - 0x01_0000,
|
||||
0x01_0000,
|
||||
0x02_0000,
|
||||
0x02_0000,
|
||||
256,
|
||||
)
|
||||
.expect("Cannot create gic");
|
||||
|
||||
assert!(gic.lock().unwrap().save_data_tables().is_ok());
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::{Error, Result};
|
||||
use hypervisor::kvm::kvm_bindings::{kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS};
|
||||
use hypervisor::CpuState;
|
||||
use crate::arch::aarch64::gic::{Error, Result};
|
||||
use crate::kvm::kvm_bindings::{kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS};
|
||||
use crate::{CpuState, Device};
|
||||
use std::sync::Arc;
|
||||
|
||||
// Relevant redistributor registers that we want to save/restore.
|
||||
@@ -83,7 +84,7 @@ static VGIC_SGI_REGS: &[RdistReg] = &[
|
||||
];
|
||||
|
||||
fn redist_attr_access(
|
||||
gic: &Arc<dyn hypervisor::Device>,
|
||||
gic: &Arc<dyn Device>,
|
||||
offset: u32,
|
||||
typer: u64,
|
||||
val: &u32,
|
||||
@@ -106,7 +107,7 @@ fn redist_attr_access(
|
||||
}
|
||||
|
||||
fn access_redists_aux(
|
||||
gic: &Arc<dyn hypervisor::Device>,
|
||||
gic: &Arc<dyn Device>,
|
||||
gicr_typer: &[u64],
|
||||
state: &mut Vec<u32>,
|
||||
reg_list: &[RdistReg],
|
||||
@@ -136,7 +137,7 @@ fn access_redists_aux(
|
||||
}
|
||||
|
||||
/// Get redistributor registers.
|
||||
pub fn get_redist_regs(gic: &Arc<dyn hypervisor::Device>, gicr_typer: &[u64]) -> Result<Vec<u32>> {
|
||||
pub fn get_redist_regs(gic: &Arc<dyn Device>, gicr_typer: &[u64]) -> Result<Vec<u32>> {
|
||||
let mut state = Vec::new();
|
||||
let mut idx: usize = 0;
|
||||
access_redists_aux(
|
||||
@@ -153,11 +154,7 @@ pub fn get_redist_regs(gic: &Arc<dyn hypervisor::Device>, gicr_typer: &[u64]) ->
|
||||
}
|
||||
|
||||
/// Set redistributor registers.
|
||||
pub fn set_redist_regs(
|
||||
gic: &Arc<dyn hypervisor::Device>,
|
||||
gicr_typer: &[u64],
|
||||
state: &[u32],
|
||||
) -> Result<()> {
|
||||
pub fn set_redist_regs(gic: &Arc<dyn Device>, gicr_typer: &[u64], state: &[u32]) -> Result<()> {
|
||||
let mut idx: usize = 0;
|
||||
let mut mut_state = state.to_owned();
|
||||
access_redists_aux(
|
||||
@@ -8,9 +8,8 @@
|
||||
//
|
||||
//
|
||||
|
||||
///
|
||||
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
|
||||
///
|
||||
pub mod gic;
|
||||
|
||||
use crate::kvm::{KvmError, KvmResult};
|
||||
use kvm_bindings::{
|
||||
kvm_mp_state, kvm_one_reg, kvm_regs, KVM_REG_ARM64, KVM_REG_ARM64_SYSREG,
|
||||
|
||||
@@ -8,11 +8,15 @@
|
||||
//
|
||||
//
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::aarch64::gic::KvmGicV3Its;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub use crate::aarch64::{
|
||||
check_required_kvm_extensions, is_system_register, VcpuInit, VcpuKvmState as CpuState,
|
||||
MPIDR_EL1,
|
||||
check_required_kvm_extensions, gic::Gicv3ItsState as GicState, is_system_register, VcpuInit,
|
||||
VcpuKvmState as CpuState, MPIDR_EL1,
|
||||
};
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::arch::aarch64::gic::Vgic;
|
||||
use crate::cpu;
|
||||
use crate::device;
|
||||
use crate::hypervisor;
|
||||
@@ -31,6 +35,8 @@ use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::result;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use std::sync::Mutex;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
// x86_64 dependencies
|
||||
@@ -154,7 +160,7 @@ pub struct TdxCapabilities {
|
||||
pub cpuid_configs: [TdxCpuidConfig; TDX_MAX_NR_CPUID_CONFIGS],
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct KvmVmState {}
|
||||
|
||||
pub use KvmVmState as VmState;
|
||||
@@ -250,6 +256,31 @@ impl vm::Vm for KvmVm {
|
||||
};
|
||||
Ok(Arc::new(vcpu))
|
||||
}
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
///
|
||||
/// Creates a virtual GIC device.
|
||||
///
|
||||
fn create_vgic(
|
||||
&self,
|
||||
vcpu_count: u64,
|
||||
dist_addr: u64,
|
||||
dist_size: u64,
|
||||
redist_size: u64,
|
||||
msi_size: u64,
|
||||
nr_irqs: u32,
|
||||
) -> vm::Result<Arc<Mutex<dyn Vgic>>> {
|
||||
let gic_device = KvmGicV3Its::new(
|
||||
self,
|
||||
vcpu_count,
|
||||
dist_addr,
|
||||
dist_size,
|
||||
redist_size,
|
||||
msi_size,
|
||||
nr_irqs,
|
||||
)
|
||||
.map_err(|e| vm::HypervisorVmError::CreateVgic(anyhow!("Vgic error {:?}", e)))?;
|
||||
Ok(Arc::new(Mutex::new(gic_device)))
|
||||
}
|
||||
///
|
||||
/// Registers an event to be signaled whenever a certain address is written to.
|
||||
///
|
||||
@@ -1535,6 +1566,51 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.get_one_reg(MPIDR_EL1)
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetSysRegister(e.into()))
|
||||
}
|
||||
///
|
||||
/// Configure core registers for a given CPU.
|
||||
///
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
fn setup_regs(&self, cpu_id: u8, boot_ip: u64, fdt_start: u64) -> cpu::Result<()> {
|
||||
#[allow(non_upper_case_globals)]
|
||||
// PSR (Processor State Register) bits.
|
||||
// Taken from arch/arm64/include/uapi/asm/ptrace.h.
|
||||
const PSR_MODE_EL1h: u64 = 0x0000_0005;
|
||||
const PSR_F_BIT: u64 = 0x0000_0040;
|
||||
const PSR_I_BIT: u64 = 0x0000_0080;
|
||||
const PSR_A_BIT: u64 = 0x0000_0100;
|
||||
const PSR_D_BIT: u64 = 0x0000_0200;
|
||||
// Taken from arch/arm64/kvm/inject_fault.c.
|
||||
const PSTATE_FAULT_BITS_64: u64 =
|
||||
PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT;
|
||||
|
||||
let kreg_off = offset__of!(kvm_regs, regs);
|
||||
|
||||
// Get the register index of the PSTATE (Processor State) register.
|
||||
let pstate = offset__of!(user_pt_regs, pstate) + kreg_off;
|
||||
self.set_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, pstate),
|
||||
PSTATE_FAULT_BITS_64,
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
|
||||
// Other vCPUs are powered off initially awaiting PSCI wakeup.
|
||||
if cpu_id == 0 {
|
||||
// Setting the PC (Processor Counter) to the current program address (kernel address).
|
||||
let pc = offset__of!(user_pt_regs, pc) + kreg_off;
|
||||
self.set_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, pc), boot_ip as u64)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
|
||||
// Last mandatory thing to set -> the address pointing to the FDT (also called DTB).
|
||||
// "The device tree blob (dtb) must be placed on an 8-byte boundary and must
|
||||
// not exceed 2 megabytes in size." -> https://www.kernel.org/doc/Documentation/arm64/booting.txt.
|
||||
// We are choosing to place it the end of DRAM. See `get_fdt_addr`.
|
||||
let regs0 = offset__of!(user_pt_regs, regs) + kreg_off;
|
||||
self.set_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, regs0), fdt_start)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Get the current CPU state
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
//! - arm64
|
||||
//!
|
||||
|
||||
#![allow(clippy::significant_drop_in_scrutinee)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate anyhow;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -51,10 +53,10 @@ mod device;
|
||||
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;
|
||||
#[cfg(all(feature = "kvm", target_arch = "aarch64"))]
|
||||
pub use kvm::{aarch64, GicState};
|
||||
// Aliased types exposed from both hypervisors
|
||||
#[cfg(feature = "kvm")]
|
||||
pub use kvm::{
|
||||
|
||||
@@ -108,6 +108,17 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
break;
|
||||
}
|
||||
|
||||
// Default Microsoft Hypervisor behavior for unimplemented MSR is to
|
||||
// send a fault to the guest if it tries to access it. It is possible
|
||||
// to override this behavior with a more suitable option i.e., ignore
|
||||
// writes from the guest and return zero in attempt to read unimplemented
|
||||
// MSR.
|
||||
fd.set_partition_property(
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_UNIMPLEMENTED_MSR_ACTION,
|
||||
hv_unimplemented_msr_action_HV_UNIMPLEMENTED_MSR_ACTION_IGNORE_WRITE_READ_ZERO as u64,
|
||||
)
|
||||
.map_err(|e| hypervisor::HypervisorError::SetPartitionProperty(e.into()))?;
|
||||
|
||||
let msr_list = self.get_msr_list()?;
|
||||
let num_msrs = msr_list.as_fam_struct_ref().nmsrs as usize;
|
||||
let mut msrs = MsrEntries::new(num_msrs).unwrap();
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::aarch64::VcpuInit;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::arch::aarch64::gic::Vgic;
|
||||
use crate::cpu::Vcpu;
|
||||
use crate::device::Device;
|
||||
#[cfg(feature = "kvm")]
|
||||
@@ -27,6 +29,8 @@ use kvm_ioctls::Cap;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use std::fs::File;
|
||||
use std::sync::Arc;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use std::sync::Mutex;
|
||||
use thiserror::Error;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
@@ -210,6 +214,11 @@ pub enum HypervisorVmError {
|
||||
///
|
||||
#[error("Failed to initialize memory region TDX: {0}")]
|
||||
InitMemRegionTdx(#[source] std::io::Error),
|
||||
///
|
||||
/// Create Vgic error
|
||||
///
|
||||
#[error("Failed to create Vgic: {0}")]
|
||||
CreateVgic(#[source] anyhow::Error),
|
||||
}
|
||||
///
|
||||
/// Result type for returning from a function
|
||||
@@ -269,6 +278,17 @@ pub trait Vm: Send + Sync {
|
||||
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>>;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn create_vgic(
|
||||
&self,
|
||||
vcpu_count: u64,
|
||||
dist_addr: u64,
|
||||
dist_size: u64,
|
||||
redist_size: u64,
|
||||
msi_size: u64,
|
||||
nr_irqs: u32,
|
||||
) -> Result<Arc<Mutex<dyn Vgic>>>;
|
||||
|
||||
/// Registers an event to be signaled whenever a certain address is written to.
|
||||
fn register_ioevent(
|
||||
&self,
|
||||
|
||||
@@ -11,16 +11,17 @@ libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
net_gen = { path = "../net_gen" }
|
||||
rate_limiter = { path = "../rate_limiter" }
|
||||
serde = "1.0.137"
|
||||
serde = "1.0.138"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
virtio-bindings = "0.1.0"
|
||||
virtio-queue = "0.3.0"
|
||||
virtio-queue = "0.4.0"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
[dev-dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
pnet = "0.30.0"
|
||||
serde_json = "1.0.81"
|
||||
once_cell = "1.13.0"
|
||||
pnet = "0.31.0"
|
||||
pnet_datalink = "0.31.0"
|
||||
serde_json = "1.0.82"
|
||||
|
||||
@@ -5,11 +5,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the THIRD-PARTY file.
|
||||
|
||||
// This is only used by the tests module from tap.rs, but we cannot use #[macro_use] unless the
|
||||
// reference to lazy_static is declared at the root level of the importing crate.
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ use serde::ser::{Serialize, Serializer};
|
||||
|
||||
pub const MAC_ADDR_LEN: usize = 6;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct MacAddr {
|
||||
bytes: [u8; MAC_ADDR_LEN],
|
||||
}
|
||||
|
||||
@@ -321,15 +321,13 @@ impl Tap {
|
||||
|
||||
// If TAP device is already up don't try and enable it
|
||||
let ifru_flags = unsafe { ifreq.ifr_ifru.ifru_flags };
|
||||
if ifru_flags
|
||||
& (net_gen::net_device_flags_IFF_UP | net_gen::net_device_flags_IFF_RUNNING) as i16
|
||||
== (net_gen::net_device_flags_IFF_UP | net_gen::net_device_flags_IFF_RUNNING) as i16
|
||||
if ifru_flags & net_gen::net_device_flags_IFF_UP as i16
|
||||
== net_gen::net_device_flags_IFF_UP as i16
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
ifreq.ifr_ifru.ifru_flags =
|
||||
(net_gen::net_device_flags_IFF_UP | net_gen::net_device_flags_IFF_RUNNING) as i16;
|
||||
ifreq.ifr_ifru.ifru_flags = net_gen::net_device_flags_IFF_UP as i16;
|
||||
|
||||
// ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
let ret =
|
||||
@@ -395,38 +393,35 @@ impl AsRawFd for Tap {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate pnet;
|
||||
|
||||
use std::net::Ipv4Addr;
|
||||
use std::str;
|
||||
use std::sync::{mpsc, Mutex};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use self::pnet::datalink::Channel::Ethernet;
|
||||
use self::pnet::datalink::{self, DataLinkReceiver, DataLinkSender, NetworkInterface};
|
||||
use self::pnet::packet::ethernet::{EtherTypes, EthernetPacket, MutableEthernetPacket};
|
||||
use self::pnet::packet::ip::IpNextHeaderProtocols;
|
||||
use self::pnet::packet::ipv4::{Ipv4Packet, MutableIpv4Packet};
|
||||
use self::pnet::packet::udp::{MutableUdpPacket, UdpPacket};
|
||||
use self::pnet::packet::{MutablePacket, Packet};
|
||||
use self::pnet::util::MacAddr;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use pnet::packet::ethernet::{EtherTypes, EthernetPacket, MutableEthernetPacket};
|
||||
use pnet::packet::ip::IpNextHeaderProtocols;
|
||||
use pnet::packet::ipv4::{Ipv4Packet, MutableIpv4Packet};
|
||||
use pnet::packet::udp::{MutableUdpPacket, UdpPacket};
|
||||
use pnet::packet::{MutablePacket, Packet};
|
||||
use pnet::util::MacAddr;
|
||||
use pnet_datalink::Channel::Ethernet;
|
||||
use pnet_datalink::{DataLinkReceiver, DataLinkSender, NetworkInterface};
|
||||
|
||||
use super::*;
|
||||
|
||||
static DATA_STRING: &str = "test for tap";
|
||||
static SUBNET_MASK: &str = "255.255.255.0";
|
||||
|
||||
// We needed to have a mutex as a global variable, so we used the crate that provides the
|
||||
// lazy_static! macro for testing. The main potential problem, caused by tests being run in
|
||||
// parallel by cargo, is creating different TAPs and trying to associate the same address,
|
||||
// so we hide the IP address &str behind this mutex, more as a convention to remember to lock
|
||||
// it at the very beginning of each function susceptible to this issue. Another variant is
|
||||
// to use a different IP address per function, but we must remember to pick an unique one
|
||||
// each time.
|
||||
lazy_static! {
|
||||
static ref TAP_IP_LOCK: Mutex<&'static str> = Mutex::new("192.168.241.1");
|
||||
}
|
||||
// We needed to have a mutex as a global variable, so we used once_cell for testing. The main
|
||||
// potential problem, caused by tests being run in parallel by cargo, is creating different
|
||||
// TAPs and trying to associate the same address, so we hide the IP address &str behind this
|
||||
// mutex, more as a convention to remember to lock it at the very beginning of each function
|
||||
// susceptible to this issue. Another variant is to use a different IP address per function,
|
||||
// but we must remember to pick an unique one each time.
|
||||
static TAP_IP_LOCK: Lazy<Mutex<&'static str>> = Lazy::new(|| Mutex::new("192.168.241.1"));
|
||||
|
||||
// Describes the outcomes we are currently interested in when parsing a packet (we use
|
||||
// an UDP packet for testing).
|
||||
@@ -543,10 +538,10 @@ mod tests {
|
||||
let interface_name_matches = |iface: &NetworkInterface| iface.name == ifname;
|
||||
|
||||
// Find the network interface with the provided name.
|
||||
let interfaces = datalink::interfaces();
|
||||
let interfaces = pnet_datalink::interfaces();
|
||||
let interface = interfaces.into_iter().find(interface_name_matches).unwrap();
|
||||
|
||||
if let Ok(Ethernet(tx, rx)) = datalink::channel(&interface, Default::default()) {
|
||||
if let Ok(Ethernet(tx, rx)) = pnet_datalink::channel(&interface, Default::default()) {
|
||||
(interface.mac.unwrap(), tx, rx)
|
||||
} else {
|
||||
panic!("datalink channel error or unhandled channel type");
|
||||
|
||||
@@ -10,7 +10,7 @@ kvm = ["vfio-ioctls/kvm"]
|
||||
mshv = ["vfio-ioctls/mshv"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.58"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
@@ -18,7 +18,7 @@ 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"] }
|
||||
serde = { version="1.0.138", features=["derive"] }
|
||||
thiserror = "1.0.31"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
|
||||
@@ -187,7 +187,7 @@ pub trait PciProgrammingInterface {
|
||||
}
|
||||
|
||||
/// Types of PCI capabilities.
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||
#[allow(dead_code)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
@@ -321,7 +321,7 @@ pub struct PciConfiguration {
|
||||
}
|
||||
|
||||
/// See pci_regs.h in kernel
|
||||
#[derive(Copy, Clone, PartialEq, Versionize, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Versionize, Debug)]
|
||||
pub enum PciBarRegionType {
|
||||
Memory32BitRegion = 0,
|
||||
IoRegion = 0x01,
|
||||
|
||||
@@ -52,7 +52,7 @@ pub const PCI_CONFIG_IO_PORT: u64 = 0xcf8;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub const PCI_CONFIG_IO_PORT_SIZE: u64 = 0x8;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd)]
|
||||
pub struct PciBdf(u32);
|
||||
|
||||
struct PciBdfVisitor;
|
||||
|
||||
@@ -179,7 +179,7 @@ struct MsiConfigState {
|
||||
impl VersionMapped for MsiConfigState {}
|
||||
|
||||
pub struct MsiConfig {
|
||||
cap: MsiCap,
|
||||
pub cap: MsiCap,
|
||||
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
|
||||
}
|
||||
|
||||
|
||||
@@ -441,7 +441,7 @@ impl Snapshottable for MsixConfig {
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
#[derive(Clone, Copy, Default, Versionize)]
|
||||
pub struct MsixCap {
|
||||
// Message Control Register
|
||||
// 10-0: MSI-X Table size
|
||||
|
||||
219
pci/src/vfio.rs
219
pci/src/vfio.rs
@@ -4,10 +4,11 @@
|
||||
//
|
||||
|
||||
use crate::{
|
||||
msi_num_enabled_vectors, BarReprogrammingParams, MsiConfig, MsixCap, MsixConfig,
|
||||
msi_num_enabled_vectors, BarReprogrammingParams, MsiCap, MsiConfig, MsixCap, MsixConfig,
|
||||
PciBarConfiguration, PciBarRegionType, PciBdf, PciCapabilityId, PciClassCode, PciConfiguration,
|
||||
PciDevice, PciDeviceError, PciHeaderType, PciSubclass, MSIX_TABLE_ENTRY_SIZE,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use hypervisor::HypervisorVmError;
|
||||
use std::any::Any;
|
||||
@@ -17,6 +18,8 @@ use std::os::unix::io::AsRawFd;
|
||||
use std::ptr::null_mut;
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use thiserror::Error;
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use vfio_bindings::bindings::vfio::*;
|
||||
use vfio_ioctls::{
|
||||
VfioContainer, VfioDevice, VfioIrq, VfioRegionInfoCap, VfioRegionSparseMmapArea,
|
||||
@@ -27,6 +30,9 @@ use vm_device::interrupt::{
|
||||
};
|
||||
use vm_device::{BusDevice, Resource};
|
||||
use vm_memory::{Address, GuestAddress, GuestUsize};
|
||||
use vm_migration::{
|
||||
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, VersionMapped,
|
||||
};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -71,11 +77,22 @@ enum InterruptUpdateAction {
|
||||
DisableMsix,
|
||||
}
|
||||
|
||||
#[derive(Versionize)]
|
||||
struct IntxState {
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
pub(crate) struct VfioIntx {
|
||||
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Versionize)]
|
||||
struct MsiState {
|
||||
cap: MsiCap,
|
||||
cap_offset: u32,
|
||||
}
|
||||
|
||||
pub(crate) struct VfioMsi {
|
||||
pub(crate) cfg: MsiConfig,
|
||||
cap_offset: u32,
|
||||
@@ -102,6 +119,13 @@ impl VfioMsi {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Versionize)]
|
||||
struct MsixState {
|
||||
cap: MsixCap,
|
||||
cap_offset: u32,
|
||||
bdf: u32,
|
||||
}
|
||||
|
||||
pub(crate) struct VfioMsix {
|
||||
pub(crate) bar: MsixConfig,
|
||||
cap: MsixCap,
|
||||
@@ -356,6 +380,15 @@ impl Vfio for VfioDeviceWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Versionize)]
|
||||
struct VfioCommonState {
|
||||
intx_state: Option<IntxState>,
|
||||
msi_state: Option<MsiState>,
|
||||
msix_state: Option<MsixState>,
|
||||
}
|
||||
|
||||
impl VersionMapped for VfioCommonState {}
|
||||
|
||||
pub(crate) struct VfioCommon {
|
||||
pub(crate) configuration: PciConfiguration,
|
||||
pub(crate) mmio_regions: Vec<MmioRegion>,
|
||||
@@ -970,6 +1003,127 @@ impl VfioCommon {
|
||||
// The config register read comes from the VFIO device itself.
|
||||
self.vfio_wrapper.read_config_dword((reg_idx * 4) as u32) & mask
|
||||
}
|
||||
|
||||
fn state(&self) -> VfioCommonState {
|
||||
let intx_state = self.interrupt.intx.as_ref().map(|intx| IntxState {
|
||||
enabled: intx.enabled,
|
||||
});
|
||||
|
||||
let msi_state = self.interrupt.msi.as_ref().map(|msi| MsiState {
|
||||
cap: msi.cfg.cap,
|
||||
cap_offset: msi.cap_offset,
|
||||
});
|
||||
|
||||
let msix_state = self.interrupt.msix.as_ref().map(|msix| MsixState {
|
||||
cap: msix.cap,
|
||||
cap_offset: msix.cap_offset,
|
||||
bdf: msix.bar.devid,
|
||||
});
|
||||
|
||||
VfioCommonState {
|
||||
intx_state,
|
||||
msi_state,
|
||||
msix_state,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &VfioCommonState) -> Result<(), VfioPciError> {
|
||||
if let (Some(intx), Some(interrupt_source_group)) =
|
||||
(&state.intx_state, self.legacy_interrupt_group.clone())
|
||||
{
|
||||
self.interrupt.intx = Some(VfioIntx {
|
||||
interrupt_source_group,
|
||||
enabled: false,
|
||||
});
|
||||
|
||||
if intx.enabled {
|
||||
self.enable_intx()?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(msi) = &state.msi_state {
|
||||
self.initialize_msi(msi.cap.msg_ctl, msi.cap_offset);
|
||||
}
|
||||
|
||||
if let Some(msix) = &state.msix_state {
|
||||
self.initialize_msix(msix.cap, msix.cap_offset, msix.bdf.into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Pausable for VfioCommon {}
|
||||
|
||||
impl Snapshottable for VfioCommon {
|
||||
fn id(&self) -> String {
|
||||
String::from("vfio_common")
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
let mut vfio_common_snapshot =
|
||||
Snapshot::new_from_versioned_state(&self.id(), &self.state())?;
|
||||
|
||||
// Snapshot PciConfiguration
|
||||
vfio_common_snapshot.add_snapshot(self.configuration.snapshot()?);
|
||||
|
||||
// Snapshot MSI
|
||||
if let Some(msi) = &mut self.interrupt.msi {
|
||||
vfio_common_snapshot.add_snapshot(msi.cfg.snapshot()?);
|
||||
}
|
||||
|
||||
// Snapshot MSI-X
|
||||
if let Some(msix) = &mut self.interrupt.msix {
|
||||
vfio_common_snapshot.add_snapshot(msix.bar.snapshot()?);
|
||||
}
|
||||
|
||||
Ok(vfio_common_snapshot)
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
if let Some(vfio_common_section) = snapshot
|
||||
.snapshot_data
|
||||
.get(&format!("{}-section", self.id()))
|
||||
{
|
||||
// It has to be invoked first as we want Interrupt to be initialized
|
||||
// correctly before we try to restore MSI and MSI-X configurations.
|
||||
self.set_state(&vfio_common_section.to_versioned_state()?)
|
||||
.map_err(|e| {
|
||||
MigratableError::Restore(anyhow!("Could not restore VFIO_COMMON state {:?}", e))
|
||||
})?;
|
||||
|
||||
// Restore PciConfiguration
|
||||
if let Some(pci_config_snapshot) = snapshot.snapshots.get(&self.configuration.id()) {
|
||||
self.configuration.restore(*pci_config_snapshot.clone())?;
|
||||
}
|
||||
|
||||
// Restore MSI
|
||||
if let Some(msi) = &mut self.interrupt.msi {
|
||||
if let Some(msi_snapshot) = snapshot.snapshots.get(&msi.cfg.id()) {
|
||||
msi.cfg.restore(*msi_snapshot.clone())?;
|
||||
}
|
||||
if msi.cfg.enabled() {
|
||||
self.enable_msi().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Restore MSI-X
|
||||
if let Some(msix) = &mut self.interrupt.msix {
|
||||
if let Some(msix_snapshot) = snapshot.snapshots.get(&msix.bar.id()) {
|
||||
msix.bar.restore(*msix_snapshot.clone())?;
|
||||
}
|
||||
if msix.bar.enabled() {
|
||||
self.enable_msix().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(MigratableError::Restore(anyhow!(
|
||||
"Could not find VFIO_COMMON snapshot section"
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
/// VfioPciDevice represents a VFIO PCI device.
|
||||
@@ -985,6 +1139,7 @@ pub struct VfioPciDevice {
|
||||
container: Arc<VfioContainer>,
|
||||
common: VfioCommon,
|
||||
iommu_attached: bool,
|
||||
memory_slot: Arc<dyn Fn() -> u32 + Send + Sync>,
|
||||
}
|
||||
|
||||
impl VfioPciDevice {
|
||||
@@ -999,6 +1154,8 @@ impl VfioPciDevice {
|
||||
legacy_interrupt_group: Option<Arc<dyn InterruptSourceGroup>>,
|
||||
iommu_attached: bool,
|
||||
bdf: PciBdf,
|
||||
restoring: bool,
|
||||
memory_slot: Arc<dyn Fn() -> u32 + Send + Sync>,
|
||||
) -> Result<Self, VfioPciError> {
|
||||
let device = Arc::new(device);
|
||||
device.reset();
|
||||
@@ -1031,8 +1188,13 @@ impl VfioPciDevice {
|
||||
vfio_wrapper: Arc::new(vfio_wrapper) as Arc<dyn Vfio>,
|
||||
};
|
||||
|
||||
common.parse_capabilities(bdf);
|
||||
common.initialize_legacy_interrupt()?;
|
||||
// No need to parse capabilities from the device if on the restore path.
|
||||
// The initialization will be performed later when restore() will be
|
||||
// called.
|
||||
if !restoring {
|
||||
common.parse_capabilities(bdf);
|
||||
common.initialize_legacy_interrupt()?;
|
||||
}
|
||||
|
||||
let vfio_pci_device = VfioPciDevice {
|
||||
id,
|
||||
@@ -1041,6 +1203,7 @@ impl VfioPciDevice {
|
||||
container,
|
||||
common,
|
||||
iommu_attached,
|
||||
memory_slot,
|
||||
};
|
||||
|
||||
Ok(vfio_pci_device)
|
||||
@@ -1150,14 +1313,7 @@ impl VfioPciDevice {
|
||||
/// * `vm` - The VM object. It is used to set the VFIO MMIO regions
|
||||
/// as user memory regions.
|
||||
/// * `mem_slot` - The closure to return a memory slot.
|
||||
pub fn map_mmio_regions<F>(
|
||||
&mut self,
|
||||
vm: &Arc<dyn hypervisor::Vm>,
|
||||
mem_slot: F,
|
||||
) -> Result<(), VfioPciError>
|
||||
where
|
||||
F: Fn() -> u32,
|
||||
{
|
||||
pub fn map_mmio_regions(&mut self) -> Result<(), VfioPciError> {
|
||||
let fd = self.device.as_raw_fd();
|
||||
|
||||
for region in self.common.mmio_regions.iter_mut() {
|
||||
@@ -1223,7 +1379,7 @@ impl VfioPciDevice {
|
||||
}
|
||||
|
||||
let user_memory_region = UserMemoryRegion {
|
||||
slot: mem_slot(),
|
||||
slot: (self.memory_slot)(),
|
||||
start: region.start.0 + area.offset,
|
||||
size: area.size,
|
||||
host_addr: host_addr as u64,
|
||||
@@ -1231,7 +1387,7 @@ impl VfioPciDevice {
|
||||
|
||||
region.user_memory_regions.push(user_memory_region);
|
||||
|
||||
let mem_region = vm.make_user_memory_region(
|
||||
let mem_region = self.vm.make_user_memory_region(
|
||||
user_memory_region.slot,
|
||||
user_memory_region.start,
|
||||
user_memory_region.size,
|
||||
@@ -1240,7 +1396,8 @@ impl VfioPciDevice {
|
||||
false,
|
||||
);
|
||||
|
||||
vm.create_user_memory_region(mem_region)
|
||||
self.vm
|
||||
.create_user_memory_region(mem_region)
|
||||
.map_err(VfioPciError::CreateUserMemoryRegion)?;
|
||||
}
|
||||
}
|
||||
@@ -1464,3 +1621,37 @@ impl PciDevice for VfioPciDevice {
|
||||
Some(self.id.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Pausable for VfioPciDevice {}
|
||||
|
||||
impl Snapshottable for VfioPciDevice {
|
||||
fn id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
let mut vfio_pci_dev_snapshot = Snapshot::new(&self.id);
|
||||
|
||||
// Snapshot VfioCommon
|
||||
vfio_pci_dev_snapshot.add_snapshot(self.common.snapshot()?);
|
||||
|
||||
Ok(vfio_pci_dev_snapshot)
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
// Restore VfioCommon
|
||||
if let Some(vfio_common_snapshot) = snapshot.snapshots.get(&self.common.id()) {
|
||||
self.common.restore(*vfio_common_snapshot.clone())?;
|
||||
self.map_mmio_regions().map_err(|e| {
|
||||
MigratableError::Restore(anyhow!(
|
||||
"Could not map MMIO regions for VfioPciDevice on restore {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for VfioPciDevice {}
|
||||
impl Migratable for VfioPciDevice {}
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::{BarReprogrammingParams, PciBarConfiguration, VfioPciError};
|
||||
use crate::{
|
||||
PciBdf, PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciSubclass,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use hypervisor::HypervisorVmError;
|
||||
use std::any::Any;
|
||||
use std::os::unix::prelude::AsRawFd;
|
||||
@@ -26,6 +27,7 @@ use vm_memory::bitmap::AtomicBitmap;
|
||||
use vm_memory::{
|
||||
Address, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryRegion, GuestRegionMmap,
|
||||
};
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
pub struct VfioUserPciDevice {
|
||||
@@ -33,6 +35,7 @@ pub struct VfioUserPciDevice {
|
||||
vm: Arc<dyn hypervisor::Vm>,
|
||||
client: Arc<Mutex<Client>>,
|
||||
common: VfioCommon,
|
||||
memory_slot: Arc<dyn Fn() -> u32 + Send + Sync>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
@@ -61,6 +64,7 @@ impl PciSubclass for PciVfioUserSubclass {
|
||||
}
|
||||
|
||||
impl VfioUserPciDevice {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
id: String,
|
||||
vm: &Arc<dyn hypervisor::Vm>,
|
||||
@@ -68,6 +72,8 @@ impl VfioUserPciDevice {
|
||||
msi_interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
legacy_interrupt_group: Option<Arc<dyn InterruptSourceGroup>>,
|
||||
bdf: PciBdf,
|
||||
restoring: bool,
|
||||
memory_slot: Arc<dyn Fn() -> u32 + Send + Sync>,
|
||||
) -> Result<Self, VfioUserPciDeviceError> {
|
||||
// This is used for the BAR and capabilities only
|
||||
let configuration = PciConfiguration::new(
|
||||
@@ -108,27 +114,26 @@ impl VfioUserPciDevice {
|
||||
vfio_wrapper: Arc::new(vfio_wrapper) as Arc<dyn Vfio>,
|
||||
};
|
||||
|
||||
common.parse_capabilities(bdf);
|
||||
common
|
||||
.initialize_legacy_interrupt()
|
||||
.map_err(VfioUserPciDeviceError::InitializeLegacyInterrupts)?;
|
||||
// No need to parse capabilities from the device if on the restore path.
|
||||
// The initialization will be performed later when restore() will be
|
||||
// called.
|
||||
if !restoring {
|
||||
common.parse_capabilities(bdf);
|
||||
common
|
||||
.initialize_legacy_interrupt()
|
||||
.map_err(VfioUserPciDeviceError::InitializeLegacyInterrupts)?;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
id,
|
||||
vm: vm.clone(),
|
||||
client,
|
||||
common,
|
||||
memory_slot,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn map_mmio_regions<F>(
|
||||
&mut self,
|
||||
vm: &Arc<dyn hypervisor::Vm>,
|
||||
mem_slot: F,
|
||||
) -> Result<(), VfioUserPciDeviceError>
|
||||
where
|
||||
F: Fn() -> u32,
|
||||
{
|
||||
pub fn map_mmio_regions(&mut self) -> Result<(), VfioUserPciDeviceError> {
|
||||
for mmio_region in &mut self.common.mmio_regions {
|
||||
let region_flags = self
|
||||
.client
|
||||
@@ -195,7 +200,7 @@ impl VfioUserPciDevice {
|
||||
}
|
||||
|
||||
let user_memory_region = UserMemoryRegion {
|
||||
slot: mem_slot(),
|
||||
slot: (self.memory_slot)(),
|
||||
start: mmio_region.start.0 + s.offset,
|
||||
size: s.size,
|
||||
host_addr: host_addr as u64,
|
||||
@@ -203,7 +208,7 @@ impl VfioUserPciDevice {
|
||||
|
||||
mmio_region.user_memory_regions.push(user_memory_region);
|
||||
|
||||
let mem_region = vm.make_user_memory_region(
|
||||
let mem_region = self.vm.make_user_memory_region(
|
||||
user_memory_region.slot,
|
||||
user_memory_region.start,
|
||||
user_memory_region.size,
|
||||
@@ -212,7 +217,8 @@ impl VfioUserPciDevice {
|
||||
false,
|
||||
);
|
||||
|
||||
vm.create_user_memory_region(mem_region)
|
||||
self.vm
|
||||
.create_user_memory_region(mem_region)
|
||||
.map_err(VfioUserPciDeviceError::MapRegionGuest)?;
|
||||
}
|
||||
}
|
||||
@@ -551,6 +557,40 @@ impl Drop for VfioUserPciDevice {
|
||||
}
|
||||
}
|
||||
|
||||
impl Pausable for VfioUserPciDevice {}
|
||||
|
||||
impl Snapshottable for VfioUserPciDevice {
|
||||
fn id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
let mut vfio_pci_dev_snapshot = Snapshot::new(&self.id);
|
||||
|
||||
// Snapshot VfioCommon
|
||||
vfio_pci_dev_snapshot.add_snapshot(self.common.snapshot()?);
|
||||
|
||||
Ok(vfio_pci_dev_snapshot)
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
// Restore VfioCommon
|
||||
if let Some(vfio_common_snapshot) = snapshot.snapshots.get(&self.common.id()) {
|
||||
self.common.restore(*vfio_common_snapshot.clone())?;
|
||||
self.map_mmio_regions().map_err(|e| {
|
||||
MigratableError::Restore(anyhow!(
|
||||
"Could not map MMIO regions for VfioUserPciDevice on restore {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for VfioUserPciDevice {}
|
||||
impl Migratable for VfioUserPciDevice {}
|
||||
|
||||
pub struct VfioUserDmaMapping<M: GuestAddressSpace> {
|
||||
client: Arc<Mutex<Client>>,
|
||||
memory: Arc<M>,
|
||||
@@ -584,10 +624,10 @@ impl<M: GuestAddressSpace + Sync + Send> ExternalDmaMapping for VfioUserDmaMappi
|
||||
)
|
||||
})
|
||||
} else {
|
||||
return Err(std::io::Error::new(
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("Region not found for 0x{:x}", gpa),
|
||||
));
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@ edition = "2021"
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.1.18", features = ["wrap_help","cargo"] }
|
||||
clap = { version = "3.2.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.138", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.82"
|
||||
test_infra = { path = "../test_infra" }
|
||||
thiserror = "1.0.31"
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.1.18", features = ["cargo"] }
|
||||
clap = { version = "3.2.8", features = ["cargo"] }
|
||||
|
||||
@@ -53,18 +53,30 @@ impl Default for MetricsReport {
|
||||
let mut git_human_readable = "".to_string();
|
||||
if let Ok(git_out) = Command::new("git").args(&["describe", "--dirty"]).output() {
|
||||
if git_out.status.success() {
|
||||
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {
|
||||
git_human_readable = git_out_str.trim().to_string();
|
||||
}
|
||||
git_human_readable = String::from_utf8(git_out.stdout)
|
||||
.unwrap()
|
||||
.trim()
|
||||
.to_string();
|
||||
} else {
|
||||
eprintln!(
|
||||
"Error generating human readable git reference: {}",
|
||||
String::from_utf8(git_out.stderr).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut git_revision = "".to_string();
|
||||
if let Ok(git_out) = Command::new("git").args(&["rev-parse", "HEAD"]).output() {
|
||||
if git_out.status.success() {
|
||||
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {
|
||||
git_revision = git_out_str.trim().to_string();
|
||||
}
|
||||
git_revision = String::from_utf8(git_out.stdout)
|
||||
.unwrap()
|
||||
.trim()
|
||||
.to_string();
|
||||
} else {
|
||||
eprintln!(
|
||||
"Error generating git reference: {}",
|
||||
String::from_utf8(git_out.stderr).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,9 +86,15 @@ impl Default for MetricsReport {
|
||||
.output()
|
||||
{
|
||||
if git_out.status.success() {
|
||||
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {
|
||||
git_commit_date = git_out_str.trim().to_string();
|
||||
}
|
||||
git_commit_date = String::from_utf8(git_out.stdout)
|
||||
.unwrap()
|
||||
.trim()
|
||||
.to_string();
|
||||
} else {
|
||||
eprintln!(
|
||||
"Error generating git commit date: {}",
|
||||
String::from_utf8(git_out.stderr).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ pub enum BucketReduction {
|
||||
|
||||
/// TokenBucket provides a lower level interface to rate limiting with a
|
||||
/// configurable capacity, refill-rate and initial burst.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct TokenBucket {
|
||||
// Bucket defining traits.
|
||||
size: u64,
|
||||
|
||||
120
release-notes.md
120
release-notes.md
@@ -1,22 +1,28 @@
|
||||
- [v25.0](#v250)
|
||||
- [`ch-remote` Improvements](#ch-remote-improvements)
|
||||
- [VM "Coredump" Support](#vm-coredump-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||
- [Removals](#removals)
|
||||
- [Contributors](#contributors)
|
||||
- [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 Bug Fixes](#notable-bug-fixes-1)
|
||||
- [Notable Improvements](#notable-improvements)
|
||||
- [Deprecations](#deprecations)
|
||||
- [New on the Website](#new-on-the-website)
|
||||
- [Contributors](#contributors)
|
||||
- [Contributors](#contributors-1)
|
||||
- [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)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
||||
- [Deprecations](#deprecations-1)
|
||||
- [Contributors](#contributors-1)
|
||||
- [Contributors](#contributors-2)
|
||||
- [v22.1](#v221)
|
||||
- [v22.0](#v220)
|
||||
- [GDB Debug Stub Support](#gdb-debug-stub-support)
|
||||
@@ -27,13 +33,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-3)
|
||||
- [Contributors](#contributors-3)
|
||||
- [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-4)
|
||||
- [Contributors](#contributors-4)
|
||||
- [v20.2](#v202)
|
||||
- [v20.1](#v201)
|
||||
- [v20.0](#v200)
|
||||
@@ -42,8 +48,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-5)
|
||||
- [Contributors](#contributors-5)
|
||||
- [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 +57,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-6)
|
||||
- [Contributors](#contributors-6)
|
||||
- [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,23 +68,23 @@
|
||||
- [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-7)
|
||||
- [Contributors](#contributors-7)
|
||||
- [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-8)
|
||||
- [Contributors](#contributors-8)
|
||||
- [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-9)
|
||||
- [Removed functionality](#removed-functionality)
|
||||
- [Contributors](#contributors-8)
|
||||
- [Contributors](#contributors-9)
|
||||
- [v15.0](#v150)
|
||||
- [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees)
|
||||
- [Network device rate limiting](#network-device-rate-limiting)
|
||||
@@ -86,7 +92,7 @@
|
||||
- [`--api-socket` supports file descriptor parameter](#--api-socket-supports-file-descriptor-parameter)
|
||||
- [Bug fixes](#bug-fixes)
|
||||
- [Deprecations](#deprecations-2)
|
||||
- [Contributors](#contributors-9)
|
||||
- [Contributors](#contributors-10)
|
||||
- [v0.14.1](#v0141)
|
||||
- [v0.14.0](#v0140)
|
||||
- [Structured event monitoring](#structured-event-monitoring)
|
||||
@@ -96,7 +102,7 @@
|
||||
- [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)
|
||||
- [Contributors](#contributors-11)
|
||||
- [v0.13.0](#v0130)
|
||||
- [Wider VFIO device support](#wider-vfio-device-support)
|
||||
- [Improved huge page support](#improved-huge-page-support)
|
||||
@@ -104,13 +110,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-12)
|
||||
- [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-13)
|
||||
- [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 +128,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-10)
|
||||
- [Contributors](#contributors-14)
|
||||
- [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-11)
|
||||
- [Contributors](#contributors-15)
|
||||
- [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 +149,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-12)
|
||||
- [Contributors](#contributors-16)
|
||||
- [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-13)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes)
|
||||
- [Contributors](#contributors-16)
|
||||
- [Contributors](#contributors-17)
|
||||
- [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 +169,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-18)
|
||||
- [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-19)
|
||||
- [v0.5.1](#v051)
|
||||
- [v0.5.0](#v050)
|
||||
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
|
||||
@@ -178,7 +184,7 @@
|
||||
- [New Interrupt Management Framework](#new-interrupt-management-framework)
|
||||
- [Development Tools](#development-tools)
|
||||
- [Kata Containers Integration](#kata-containers-integration)
|
||||
- [Contributors](#contributors-19)
|
||||
- [Contributors](#contributors-20)
|
||||
- [v0.4.0](#v040)
|
||||
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
|
||||
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
|
||||
@@ -187,7 +193,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-21)
|
||||
- [v0.3.0](#v030)
|
||||
- [Block device offloading](#block-device-offloading)
|
||||
- [Network device backend](#network-device-backend)
|
||||
@@ -214,6 +220,52 @@
|
||||
- [Unit testing](#unit-testing)
|
||||
- [Integration tests parallelization](#integration-tests-parallelization)
|
||||
|
||||
# v25.0
|
||||
|
||||
This release has been tracked through the [v25.0
|
||||
project](https://github.com/cloud-hypervisor/cloud-hypervisor/projects/29).
|
||||
|
||||
### `ch-remote` Improvements
|
||||
|
||||
The `ch-remote` command has gained support for creating the VM from a JSON
|
||||
config and support for booting and deleting the VM from the VMM.
|
||||
|
||||
### VM "Coredump" Support
|
||||
|
||||
Under the `guest_debug` feature flag it is now possible to extract the memory
|
||||
of the guest for use in debugging with e.g. the `crash` utility. (#4012)
|
||||
|
||||
### Notable Bug Fixes
|
||||
|
||||
* Always restore console mode on exit (#4249, #4248)
|
||||
* Restore vCPUs in numerical order which fixes aarch64 snapshot/restore (#4244)
|
||||
* Don't try and configure `IFF_RUNNING` on TAP devices (#4279)
|
||||
* Propagate configured queue size through to vhost-user backend (#4286)
|
||||
* Always Program vCPU CPUID before running the vCPU to fix running on Linux
|
||||
5.16 (#4156)
|
||||
* Enable ACPI MADT "Online Capable" flag for hotpluggable vCPUs to fix newer
|
||||
Linux guest
|
||||
|
||||
### Removals
|
||||
|
||||
The following functionality has been removed:
|
||||
|
||||
* The `mergeable` option from the `virtio-pmem` support has been removed
|
||||
(#3968)
|
||||
* The `dax` option from the `virtio-fs` support has been removed (#3889)
|
||||
|
||||
### Contributors
|
||||
|
||||
Many thanks to everyone who has contributed to our release:
|
||||
|
||||
* Dylan Bargatze <dbargatz@users.noreply.github.com>
|
||||
* Jinank Jain <jinankjain@microsoft.com>
|
||||
* Michael Zhao <michael.zhao@arm.com>
|
||||
* Rob Bradford <robert.bradford@intel.com>
|
||||
* Sebastien Boeuf <sebastien.boeuf@intel.com>
|
||||
* Wei Liu <liuwe@microsoft.com>
|
||||
* Yi Wang <wang.yi59@zte.com.cn>
|
||||
|
||||
# v24.0
|
||||
|
||||
This release has been tracked through the [v24.0
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
FROM ubuntu:20.04 as dev
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG RUST_TOOLCHAIN="1.60.0"
|
||||
ARG RUST_TOOLCHAIN="1.62.0"
|
||||
ARG CLH_SRC_DIR="/cloud-hypervisor"
|
||||
ARG CLH_BUILD_DIR="$CLH_SRC_DIR/build"
|
||||
ARG CARGO_REGISTRY_DIR="$CLH_BUILD_DIR/cargo_registry"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
Name: cloud-hypervisor
|
||||
Summary: Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on top of KVM.
|
||||
Version: 24.0
|
||||
Version: 25.0
|
||||
Release: 0%{?dist}
|
||||
License: ASL 2.0 or BSD-3-clause
|
||||
Group: Applications/System
|
||||
@@ -145,6 +145,9 @@ rm -rf %{buildroot}
|
||||
|
||||
|
||||
%changelog
|
||||
* Thu Jul 07 2022 Rob Bradford <robert.bradford@intel.com> 25.0-0
|
||||
- Update to 25.0
|
||||
|
||||
* Wed May 25 2022 Sebastien Boeuf <sebastien.boeuf@intel.com> 24.0-0
|
||||
- Update to 24.0
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
CLI_NAME="Cloud Hypervisor"
|
||||
|
||||
CTR_IMAGE_TAG="cloudhypervisor/dev"
|
||||
CTR_IMAGE_VERSION="20220524-0"
|
||||
CTR_IMAGE_VERSION="20220705-0"
|
||||
CTR_IMAGE="${CTR_IMAGE_TAG}:${CTR_IMAGE_VERSION}"
|
||||
|
||||
DOCKER_RUNTIME="docker"
|
||||
|
||||
@@ -113,6 +113,9 @@ if [ -n "$test_filter" ]; then
|
||||
test_binary_args+=("--test-filter $test_filter")
|
||||
fi
|
||||
|
||||
# Ensure that git commands can be run in this directory (for metrics report)
|
||||
git config --global --add safe.directory $PWD
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
time target/$BUILD_TARGET/release/performance-metrics ${test_binary_args[*]}
|
||||
RES=$?
|
||||
|
||||
@@ -12,6 +12,7 @@ use api_client::Error as ApiClientError;
|
||||
use clap::{Arg, ArgMatches, Command};
|
||||
use option_parser::{ByteSized, ByteSizedParseError};
|
||||
use std::fmt;
|
||||
use std::io::Read;
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::process;
|
||||
|
||||
@@ -31,6 +32,8 @@ enum Error {
|
||||
AddVdpaConfig(vmm::config::Error),
|
||||
AddVsockConfig(vmm::config::Error),
|
||||
Restore(vmm::config::Error),
|
||||
ReadingStdin(std::io::Error),
|
||||
ReadingFile(std::io::Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
@@ -51,6 +54,8 @@ impl fmt::Display for Error {
|
||||
AddVdpaConfig(e) => write!(f, "Error parsing vDPA device syntax: {}", e),
|
||||
AddVsockConfig(e) => write!(f, "Error parsing vsock syntax: {}", e),
|
||||
Restore(e) => write!(f, "Error parsing restore syntax: {}", e),
|
||||
ReadingStdin(e) => write!(f, "Error reading from stdin: {}", e),
|
||||
ReadingFile(e) => write!(f, "Error reading from file: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -264,6 +269,20 @@ fn restore_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Erro
|
||||
.map_err(Error::ApiClient)
|
||||
}
|
||||
|
||||
fn coredump_api_command(socket: &mut UnixStream, destination_url: &str) -> Result<(), Error> {
|
||||
let coredump_config = vmm::api::VmCoredumpData {
|
||||
destination_url: String::from(destination_url),
|
||||
};
|
||||
|
||||
simple_api_command(
|
||||
socket,
|
||||
"PUT",
|
||||
"coredump",
|
||||
Some(&serde_json::to_string(&coredump_config).unwrap()),
|
||||
)
|
||||
.map_err(Error::ApiClient)
|
||||
}
|
||||
|
||||
fn receive_migration_api_command(socket: &mut UnixStream, url: &str) -> Result<(), Error> {
|
||||
let receive_migration_data = vmm::api::VmReceiveMigrationData {
|
||||
receiver_url: url.to_owned(),
|
||||
@@ -295,6 +314,19 @@ fn send_migration_api_command(
|
||||
.map_err(Error::ApiClient)
|
||||
}
|
||||
|
||||
fn create_api_command(socket: &mut UnixStream, path: &str) -> Result<(), Error> {
|
||||
let mut data = String::default();
|
||||
if path == "-" {
|
||||
std::io::stdin()
|
||||
.read_to_string(&mut data)
|
||||
.map_err(Error::ReadingStdin)?;
|
||||
} else {
|
||||
data = std::fs::read_to_string(path).map_err(Error::ReadingFile)?;
|
||||
}
|
||||
|
||||
simple_api_command(socket, "PUT", "create", Some(&data)).map_err(Error::ApiClient)
|
||||
}
|
||||
|
||||
fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
let mut socket =
|
||||
UnixStream::connect(matches.value_of("api-socket").unwrap()).map_err(Error::Connect)?;
|
||||
@@ -422,6 +454,14 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
.value_of("restore_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("coredump") => coredump_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("coredump")
|
||||
.unwrap()
|
||||
.value_of("coredump_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("send-migration") => send_migration_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
@@ -442,6 +482,14 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
.value_of("receive_migration_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("create") => create_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("create")
|
||||
.unwrap()
|
||||
.value_of("path")
|
||||
.unwrap(),
|
||||
),
|
||||
Some(c) => simple_api_command(&mut socket, "PUT", c, None).map_err(Error::ApiClient),
|
||||
None => unreachable!(),
|
||||
}
|
||||
@@ -576,6 +624,8 @@ fn main() {
|
||||
),
|
||||
)
|
||||
.subcommand(Command::new("resume").about("Resume the VM"))
|
||||
.subcommand(Command::new("boot").about("Boot a created VM"))
|
||||
.subcommand(Command::new("delete").about("Delete a VM"))
|
||||
.subcommand(Command::new("shutdown").about("Shutdown the VM"))
|
||||
.subcommand(
|
||||
Command::new("snapshot")
|
||||
@@ -595,6 +645,11 @@ fn main() {
|
||||
.help(vmm::config::RestoreConfig::SYNTAX),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("coredump")
|
||||
.about("Create a coredump from VM")
|
||||
.arg(Arg::new("coredump_config").index(1).help("<file_path>")),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("send-migration")
|
||||
.about("Initiate a VM migration")
|
||||
@@ -617,6 +672,11 @@ fn main() {
|
||||
.index(1)
|
||||
.help("<receiver_url>"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("create")
|
||||
.about("Create VM from a JSON configuration")
|
||||
.arg(Arg::new("path").index(1).default_value("-")),
|
||||
);
|
||||
|
||||
let matches = app.get_matches();
|
||||
|
||||
42
src/main.rs
42
src/main.rs
@@ -23,6 +23,7 @@ use thiserror::Error;
|
||||
use vmm::config;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
use vmm_sys_util::signal::block_signal;
|
||||
use vmm_sys_util::terminal::Terminal;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum Error {
|
||||
@@ -623,6 +624,13 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
let on_tty = unsafe { libc::isatty(libc::STDIN_FILENO as i32) } != 0;
|
||||
if on_tty {
|
||||
// Don't forget to set the terminal in canonical mode
|
||||
// before to exit.
|
||||
std::io::stdin().lock().set_canon_mode().unwrap();
|
||||
}
|
||||
|
||||
std::process::exit(exit_code);
|
||||
}
|
||||
|
||||
@@ -1323,40 +1331,6 @@ mod unit_tests {
|
||||
}"#,
|
||||
false,
|
||||
),
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
(
|
||||
vec![
|
||||
"cloud-hypervisor",
|
||||
"--kernel",
|
||||
"/path/to/kernel",
|
||||
"--pmem",
|
||||
"file=/path/to/img/1,size=1G,mergeable=on",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"pmem": [
|
||||
{"file": "/path/to/img/1", "size": 1073741824, "mergeable": true}
|
||||
]
|
||||
}"#,
|
||||
true,
|
||||
),
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
(
|
||||
vec![
|
||||
"cloud-hypervisor",
|
||||
"--kernel",
|
||||
"/path/to/kernel",
|
||||
"--pmem",
|
||||
"file=/path/to/img/1,size=1G,mergeable=off",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"pmem": [
|
||||
{"file": "/path/to/img/1", "size": 1073741824, "mergeable": false}
|
||||
]
|
||||
}"#,
|
||||
true,
|
||||
),
|
||||
]
|
||||
.iter()
|
||||
.for_each(|(cli, openapi, equal)| {
|
||||
|
||||
@@ -7,8 +7,8 @@ edition = "2021"
|
||||
[dependencies]
|
||||
dirs = "4.0.0"
|
||||
epoll = "4.3.1"
|
||||
lazy_static = "1.4.0"
|
||||
libc = "0.2.126"
|
||||
once_cell = "1.13.0"
|
||||
ssh2 = { version = "0.9.1", features = ["vendored-openssl"]}
|
||||
vmm-sys-util = "0.9.0"
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use ssh2::Session;
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
@@ -52,7 +50,7 @@ pub struct GuestNetworkConfig {
|
||||
|
||||
pub const DEFAULT_TCP_LISTENER_MESSAGE: &str = "booted";
|
||||
pub const DEFAULT_TCP_LISTENER_PORT: u16 = 8000;
|
||||
pub const DEFAULT_TCP_LISTENER_TIMEOUT: i32 = 80;
|
||||
pub const DEFAULT_TCP_LISTENER_TIMEOUT: i32 = 120;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WaitForBootError {
|
||||
@@ -722,9 +720,7 @@ pub fn exec_host_command_output(command: &str) -> Output {
|
||||
|
||||
pub const PIPE_SIZE: i32 = 32 << 20;
|
||||
|
||||
lazy_static! {
|
||||
static ref NEXT_VM_ID: Mutex<u8> = Mutex::new(1);
|
||||
}
|
||||
static NEXT_VM_ID: Lazy<Mutex<u8>> = Lazy::new(|| Mutex::new(1));
|
||||
|
||||
pub struct Guest {
|
||||
pub tmp_dir: TempDir,
|
||||
@@ -832,30 +828,12 @@ impl Guest {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn api_create_body(
|
||||
&self,
|
||||
cpu_count: u8,
|
||||
_fw_path: &str,
|
||||
_kernel_path: &str,
|
||||
_kernel_cmd: &str,
|
||||
) -> String {
|
||||
#[cfg(all(target_arch = "x86_64", not(feature = "mshv")))]
|
||||
format! {"{{\"cpus\":{{\"boot_vcpus\":{},\"max_vcpus\":{}}},\"kernel\":{{\"path\":\"{}\"}},\"cmdline\":{{\"args\": \"\"}},\"net\":[{{\"ip\":\"{}\", \"mask\":\"255.255.255.0\", \"mac\":\"{}\"}}], \"disks\":[{{\"path\":\"{}\"}}, {{\"path\":\"{}\"}}]}}",
|
||||
cpu_count,
|
||||
cpu_count,
|
||||
_fw_path,
|
||||
self.network.host_ip,
|
||||
self.network.guest_mac,
|
||||
self.disk_config.disk(DiskType::OperatingSystem).unwrap().as_str(),
|
||||
self.disk_config.disk(DiskType::CloudInit).unwrap().as_str(),
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", feature = "mshv"))]
|
||||
pub fn api_create_body(&self, cpu_count: u8, kernel_path: &str, kernel_cmd: &str) -> String {
|
||||
format! {"{{\"cpus\":{{\"boot_vcpus\":{},\"max_vcpus\":{}}},\"kernel\":{{\"path\":\"{}\"}},\"cmdline\":{{\"args\": \"{}\"}},\"net\":[{{\"ip\":\"{}\", \"mask\":\"255.255.255.0\", \"mac\":\"{}\"}}], \"disks\":[{{\"path\":\"{}\"}}, {{\"path\":\"{}\"}}]}}",
|
||||
cpu_count,
|
||||
cpu_count,
|
||||
_kernel_path,
|
||||
_kernel_cmd,
|
||||
kernel_path,
|
||||
kernel_cmd,
|
||||
self.network.host_ip,
|
||||
self.network.guest_mac,
|
||||
self.disk_config.disk(DiskType::OperatingSystem).unwrap().as_str(),
|
||||
@@ -985,13 +963,6 @@ impl Guest {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_entropy(&self) -> Result<u32, Error> {
|
||||
self.ssh_command("cat /proc/sys/kernel/random/entropy_avail")?
|
||||
.trim()
|
||||
.parse()
|
||||
.map_err(Error::Parsing)
|
||||
}
|
||||
|
||||
pub fn get_pci_bridge_class(&self) -> Result<String, Error> {
|
||||
Ok(self
|
||||
.ssh_command("cat /sys/bus/pci/devices/0000:00:00.0/class")?
|
||||
@@ -1037,50 +1008,6 @@ impl Guest {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
pub fn valid_virtio_fs_cache_size(
|
||||
&self,
|
||||
dax: bool,
|
||||
cache_size: Option<u64>,
|
||||
) -> Result<bool, Error> {
|
||||
// SHM region is called different things depending on kernel
|
||||
let shm_region = self
|
||||
.ssh_command("sudo grep 'virtio[0-9]\\|virtio-pci-shm' /proc/iomem || true")?
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
if shm_region.is_empty() {
|
||||
return Ok(!dax);
|
||||
}
|
||||
|
||||
// From this point, the region is not empty, hence it is an error
|
||||
// if DAX is off.
|
||||
if !dax {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let cache = if let Some(cache) = cache_size {
|
||||
cache
|
||||
} else {
|
||||
// 8Gib by default
|
||||
0x0002_0000_0000
|
||||
};
|
||||
|
||||
let args: Vec<&str> = shm_region.split(':').collect();
|
||||
if args.is_empty() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let args: Vec<&str> = args[0].trim().split('-').collect();
|
||||
if args.len() != 2 {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let start_addr = u64::from_str_radix(args[0], 16).map_err(Error::Parsing)?;
|
||||
let end_addr = u64::from_str_radix(args[1], 16).map_err(Error::Parsing)?;
|
||||
|
||||
Ok(cache == (end_addr - start_addr + 1))
|
||||
}
|
||||
|
||||
pub fn check_vsock(&self, socket: &str) {
|
||||
// Listen from guest on vsock CID=3 PORT=16
|
||||
// SOCKET-LISTEN:<domain>:<protocol>:<local-address>
|
||||
|
||||
@@ -8,10 +8,6 @@
|
||||
// related warnings for our quality workflow to pass.
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
extern crate test_infra;
|
||||
|
||||
use net_util::MacAddr;
|
||||
@@ -75,11 +71,7 @@ const DIRECT_KERNEL_BOOT_CMDLINE: &str =
|
||||
|
||||
const CONSOLE_TEST_STRING: &str = "Started OpenBSD Secure Shell server";
|
||||
|
||||
fn prepare_virtiofsd(
|
||||
tmp_dir: &TempDir,
|
||||
shared_dir: &str,
|
||||
cache: &str,
|
||||
) -> (std::process::Child, String) {
|
||||
fn prepare_virtiofsd(tmp_dir: &TempDir, shared_dir: &str) -> (std::process::Child, String) {
|
||||
let mut workload_path = dirs::home_dir().unwrap();
|
||||
workload_path.push("workloads");
|
||||
|
||||
@@ -94,7 +86,7 @@ fn prepare_virtiofsd(
|
||||
let child = Command::new(virtiofsd_path.as_str())
|
||||
.args(&["--shared-dir", shared_dir])
|
||||
.args(&["--socket-path", virtiofsd_socket_path.as_str()])
|
||||
.args(&["--cache", cache])
|
||||
.args(&["--cache", "never"])
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
@@ -162,6 +154,11 @@ fn temp_snapshot_dir_path(tmp_dir: &TempDir) -> String {
|
||||
snapshot_dir
|
||||
}
|
||||
|
||||
fn temp_vmcore_file_path(tmp_dir: &TempDir) -> String {
|
||||
let vmcore_file = String::from(tmp_dir.as_path().join("vmcore").to_str().unwrap());
|
||||
vmcore_file
|
||||
}
|
||||
|
||||
// Creates the path for direct kernel boot and return the path.
|
||||
// For x86_64, this function returns the vmlinux kernel path.
|
||||
// For AArch64, this function returns the PE kernel path.
|
||||
@@ -256,8 +253,15 @@ fn remote_command(api_socket: &str, command: &str, arg: Option<&str>) -> bool {
|
||||
if let Some(arg) = arg {
|
||||
cmd.arg(arg);
|
||||
}
|
||||
|
||||
cmd.status().expect("Failed to launch ch-remote").success()
|
||||
let output = cmd.output().unwrap();
|
||||
if output.status.success() {
|
||||
true
|
||||
} else {
|
||||
eprintln!("Error running ch-remote command: {:?}", &cmd);
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
eprintln!("stderr: {}", stderr);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn remote_command_w_output(api_socket: &str, command: &str, arg: Option<&str>) -> (bool, Vec<u8>) {
|
||||
@@ -1126,11 +1130,8 @@ fn test_boot_from_vhost_user_blk(
|
||||
handle_child_output(r, &output);
|
||||
}
|
||||
|
||||
fn test_virtio_fs(
|
||||
dax: bool,
|
||||
cache_size: Option<u64>,
|
||||
virtiofsd_cache: &str,
|
||||
prepare_daemon: &dyn Fn(&TempDir, &str, &str) -> (std::process::Child, String),
|
||||
fn _test_virtio_fs(
|
||||
prepare_daemon: &dyn Fn(&TempDir, &str) -> (std::process::Child, String),
|
||||
hotplug: bool,
|
||||
pci_segment: Option<u16>,
|
||||
) {
|
||||
@@ -1161,18 +1162,8 @@ fn test_virtio_fs(
|
||||
direct_kernel_boot_path()
|
||||
};
|
||||
|
||||
let (dax_vmm_param, dax_mount_param) = if dax { ("on", "-o dax") } else { ("off", "") };
|
||||
let cache_size_vmm_param = if let Some(cache) = cache_size {
|
||||
format!(",cache_size={}", cache)
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
let (mut daemon_child, virtiofsd_socket_path) = prepare_daemon(
|
||||
&guest.tmp_dir,
|
||||
shared_dir.to_str().unwrap(),
|
||||
virtiofsd_cache,
|
||||
);
|
||||
let (mut daemon_child, virtiofsd_socket_path) =
|
||||
prepare_daemon(&guest.tmp_dir, shared_dir.to_str().unwrap());
|
||||
|
||||
let mut guest_command = GuestCommand::new(&guest);
|
||||
guest_command
|
||||
@@ -1188,10 +1179,8 @@ fn test_virtio_fs(
|
||||
}
|
||||
|
||||
let fs_params = format!(
|
||||
"id=myfs0,tag=myfs,socket={},num_queues=1,queue_size=1024,dax={}{}{}",
|
||||
"id=myfs0,tag=myfs,socket={},num_queues=1,queue_size=1024{}",
|
||||
virtiofsd_socket_path,
|
||||
dax_vmm_param,
|
||||
cache_size_vmm_param,
|
||||
if let Some(pci_segment) = pci_segment {
|
||||
format!(",pci_segment={}", pci_segment)
|
||||
} else {
|
||||
@@ -1228,16 +1217,9 @@ fn test_virtio_fs(
|
||||
}
|
||||
|
||||
// Mount shared directory through virtio_fs filesystem
|
||||
let mount_cmd = format!(
|
||||
"mkdir -p mount_dir && \
|
||||
sudo mount -t virtiofs {} myfs mount_dir/",
|
||||
dax_mount_param
|
||||
);
|
||||
guest.ssh_command(&mount_cmd).unwrap();
|
||||
|
||||
assert!(guest
|
||||
.valid_virtio_fs_cache_size(dax, cache_size)
|
||||
.unwrap_or_default());
|
||||
guest
|
||||
.ssh_command("mkdir -p mount_dir && sudo mount -t virtiofs myfs mount_dir/")
|
||||
.unwrap();
|
||||
|
||||
// Check file1 exists and its content is "foo"
|
||||
assert_eq!(
|
||||
@@ -1284,19 +1266,14 @@ fn test_virtio_fs(
|
||||
|
||||
let (r, hotplug_daemon_child) = if r.is_ok() && hotplug {
|
||||
thread::sleep(std::time::Duration::new(10, 0));
|
||||
let (daemon_child, virtiofsd_socket_path) = prepare_daemon(
|
||||
&guest.tmp_dir,
|
||||
shared_dir.to_str().unwrap(),
|
||||
virtiofsd_cache,
|
||||
);
|
||||
let (daemon_child, virtiofsd_socket_path) =
|
||||
prepare_daemon(&guest.tmp_dir, shared_dir.to_str().unwrap());
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
thread::sleep(std::time::Duration::new(10, 0));
|
||||
let fs_params = format!(
|
||||
"id=myfs0,tag=myfs,socket={},num_queues=1,queue_size=1024,dax={}{}{}",
|
||||
"id=myfs0,tag=myfs,socket={},num_queues=1,queue_size=1024{}",
|
||||
virtiofsd_socket_path,
|
||||
dax_vmm_param,
|
||||
cache_size_vmm_param,
|
||||
if let Some(pci_segment) = pci_segment {
|
||||
format!(",pci_segment={}", pci_segment)
|
||||
} else {
|
||||
@@ -1320,12 +1297,10 @@ fn test_virtio_fs(
|
||||
|
||||
thread::sleep(std::time::Duration::new(10, 0));
|
||||
// Mount shared directory through virtio_fs filesystem
|
||||
let mount_cmd = format!(
|
||||
"mkdir -p mount_dir && \
|
||||
sudo mount -t virtiofs {} myfs mount_dir/",
|
||||
dax_mount_param
|
||||
);
|
||||
guest.ssh_command(&mount_cmd).unwrap();
|
||||
guest
|
||||
.ssh_command("mkdir -p mount_dir && sudo mount -t virtiofs myfs mount_dir/")
|
||||
.unwrap();
|
||||
|
||||
// Check file1 exists and its content is "foo"
|
||||
assert_eq!(
|
||||
guest.ssh_command("cat mount_dir/file1").unwrap().trim(),
|
||||
@@ -1646,7 +1621,7 @@ fn process_rss_kib(pid: u32) -> usize {
|
||||
// 10MB is our maximum accepted overhead.
|
||||
const MAXIMUM_VMM_OVERHEAD_KB: u32 = 10 * 1024;
|
||||
|
||||
#[derive(PartialEq, PartialOrd)]
|
||||
#[derive(PartialEq, Eq, PartialOrd)]
|
||||
struct Counters {
|
||||
rx_bytes: u64,
|
||||
rx_frames: u64,
|
||||
@@ -2028,7 +2003,6 @@ mod parallel {
|
||||
assert_eq!(guest.get_cpu_count().unwrap_or_default(), 1);
|
||||
assert_eq!(guest.get_initial_apicid().unwrap_or(1), 0);
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
|
||||
assert!(guest.get_entropy().unwrap_or_default() >= 900);
|
||||
assert_eq!(guest.get_pci_bridge_class().unwrap_or_default(), "0x060000");
|
||||
|
||||
let expected_sequential_events = [
|
||||
@@ -2654,7 +2628,6 @@ mod parallel {
|
||||
|
||||
assert_eq!(guest.get_cpu_count().unwrap_or_default(), 1);
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
|
||||
assert!(guest.get_entropy().unwrap_or_default() >= 900);
|
||||
|
||||
let grep_cmd = if cfg!(target_arch = "x86_64") {
|
||||
"grep -c PCI-MSI /proc/interrupts"
|
||||
@@ -3119,25 +3092,25 @@ mod parallel {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_virtio_fs_dax_off() {
|
||||
test_virtio_fs(false, None, "never", &prepare_virtiofsd, false, None)
|
||||
fn test_virtio_fs() {
|
||||
_test_virtio_fs(&prepare_virtiofsd, false, None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_virtio_fs_hotplug_dax_off() {
|
||||
test_virtio_fs(false, None, "never", &prepare_virtiofsd, true, None)
|
||||
fn test_virtio_fs_hotplug() {
|
||||
_test_virtio_fs(&prepare_virtiofsd, true, None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_virtio_fs_multi_segment_hotplug() {
|
||||
test_virtio_fs(false, None, "never", &prepare_virtiofsd, true, Some(15))
|
||||
_test_virtio_fs(&prepare_virtiofsd, true, Some(15))
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_virtio_fs_multi_segment() {
|
||||
test_virtio_fs(false, None, "never", &prepare_virtiofsd, false, Some(15))
|
||||
_test_virtio_fs(&prepare_virtiofsd, false, Some(15))
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -3984,7 +3957,6 @@ mod parallel {
|
||||
|
||||
assert_eq!(guest.get_cpu_count().unwrap_or_default(), 1);
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
|
||||
assert!(guest.get_entropy().unwrap_or_default() >= 900);
|
||||
});
|
||||
|
||||
let _ = child.kill();
|
||||
@@ -4073,7 +4045,60 @@ mod parallel {
|
||||
let cpu_count: u8 = 4;
|
||||
let http_body = guest.api_create_body(
|
||||
cpu_count,
|
||||
fw_path(FwType::RustHypervisorFirmware).as_str(),
|
||||
direct_kernel_boot_path().to_str().unwrap(),
|
||||
DIRECT_KERNEL_BOOT_CMDLINE,
|
||||
);
|
||||
|
||||
let temp_config_path = guest.tmp_dir.as_path().join("config");
|
||||
std::fs::write(&temp_config_path, http_body).unwrap();
|
||||
|
||||
remote_command(
|
||||
&api_socket,
|
||||
"create",
|
||||
Some(temp_config_path.as_os_str().to_str().unwrap()),
|
||||
);
|
||||
|
||||
// Then boot it
|
||||
remote_command(&api_socket, "boot", None);
|
||||
thread::sleep(std::time::Duration::new(20, 0));
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
// Check that the VM booted as expected
|
||||
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
|
||||
});
|
||||
|
||||
let _ = child.kill();
|
||||
let output = child.wait_with_output().unwrap();
|
||||
|
||||
handle_child_output(r, &output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Start cloud-hypervisor with no VM parameters, only the API server running.
|
||||
// From the API: Create a VM, boot it and check it can be shutdown and then
|
||||
// booted again
|
||||
fn test_api_shutdown() {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(focal));
|
||||
|
||||
let api_socket = temp_api_path(&guest.tmp_dir);
|
||||
|
||||
let mut child = GuestCommand::new(&guest)
|
||||
.args(&["--api-socket", &api_socket])
|
||||
.capture_output()
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
thread::sleep(std::time::Duration::new(1, 0));
|
||||
|
||||
// Verify API server is running
|
||||
curl_command(&api_socket, "GET", "http://localhost/api/v1/vmm.ping", None);
|
||||
|
||||
// Create the VM first
|
||||
let cpu_count: u8 = 4;
|
||||
let http_body = guest.api_create_body(
|
||||
cpu_count,
|
||||
direct_kernel_boot_path().to_str().unwrap(),
|
||||
DIRECT_KERNEL_BOOT_CMDLINE,
|
||||
);
|
||||
@@ -4087,13 +4112,113 @@ mod parallel {
|
||||
|
||||
// Then boot it
|
||||
curl_command(&api_socket, "PUT", "http://localhost/api/v1/vm.boot", None);
|
||||
thread::sleep(std::time::Duration::new(20, 0));
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
|
||||
// Check that the VM booted as expected
|
||||
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
|
||||
|
||||
// Shutdown without powering off to prevent filesystem corruption
|
||||
guest.ssh_command("sudo shutdown -H now").unwrap();
|
||||
|
||||
// Then shut it down
|
||||
curl_command(
|
||||
&api_socket,
|
||||
"PUT",
|
||||
"http://localhost/api/v1/vm.shutdown",
|
||||
None,
|
||||
);
|
||||
|
||||
// Then boot it again
|
||||
curl_command(&api_socket, "PUT", "http://localhost/api/v1/vm.boot", None);
|
||||
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
|
||||
// Check that the VM booted as expected
|
||||
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
|
||||
});
|
||||
|
||||
let _ = child.kill();
|
||||
let output = child.wait_with_output().unwrap();
|
||||
|
||||
handle_child_output(r, &output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Start cloud-hypervisor with no VM parameters, only the API server running.
|
||||
// From the API: Create a VM, boot it and check it can be deleted and then recreated
|
||||
// booted again.
|
||||
fn test_api_delete() {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(focal));
|
||||
|
||||
let api_socket = temp_api_path(&guest.tmp_dir);
|
||||
|
||||
let mut child = GuestCommand::new(&guest)
|
||||
.args(&["--api-socket", &api_socket])
|
||||
.capture_output()
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
thread::sleep(std::time::Duration::new(1, 0));
|
||||
|
||||
// Verify API server is running
|
||||
curl_command(&api_socket, "GET", "http://localhost/api/v1/vmm.ping", None);
|
||||
|
||||
// Create the VM first
|
||||
let cpu_count: u8 = 4;
|
||||
let http_body = guest.api_create_body(
|
||||
cpu_count,
|
||||
direct_kernel_boot_path().to_str().unwrap(),
|
||||
DIRECT_KERNEL_BOOT_CMDLINE,
|
||||
);
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
curl_command(
|
||||
&api_socket,
|
||||
"PUT",
|
||||
"http://localhost/api/v1/vm.create",
|
||||
Some(&http_body),
|
||||
);
|
||||
|
||||
// Then boot it
|
||||
curl_command(&api_socket, "PUT", "http://localhost/api/v1/vm.boot", None);
|
||||
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
|
||||
// Check that the VM booted as expected
|
||||
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
|
||||
|
||||
// Shutdown without powering off to prevent filesystem corruption
|
||||
guest.ssh_command("sudo shutdown -H now").unwrap();
|
||||
|
||||
// Then delete it
|
||||
curl_command(
|
||||
&api_socket,
|
||||
"PUT",
|
||||
"http://localhost/api/v1/vm.delete",
|
||||
None,
|
||||
);
|
||||
|
||||
curl_command(
|
||||
&api_socket,
|
||||
"PUT",
|
||||
"http://localhost/api/v1/vm.create",
|
||||
Some(&http_body),
|
||||
);
|
||||
|
||||
// Then boot it again
|
||||
curl_command(&api_socket, "PUT", "http://localhost/api/v1/vm.boot", None);
|
||||
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
|
||||
// Check that the VM booted as expected
|
||||
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
|
||||
assert!(guest.get_entropy().unwrap_or_default() >= 900);
|
||||
});
|
||||
|
||||
let _ = child.kill();
|
||||
@@ -4128,7 +4253,6 @@ mod parallel {
|
||||
let cpu_count: u8 = 4;
|
||||
let http_body = guest.api_create_body(
|
||||
cpu_count,
|
||||
fw_path(FwType::RustHypervisorFirmware).as_str(),
|
||||
direct_kernel_boot_path().to_str().unwrap(),
|
||||
DIRECT_KERNEL_BOOT_CMDLINE,
|
||||
);
|
||||
@@ -4147,7 +4271,6 @@ mod parallel {
|
||||
// Check that the VM booted as expected
|
||||
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
|
||||
assert!(guest.get_entropy().unwrap_or_default() >= 900);
|
||||
|
||||
// We now pause the VM
|
||||
assert!(remote_command(&api_socket, "pause", None));
|
||||
@@ -5664,6 +5787,56 @@ mod parallel {
|
||||
handle_child_output(r, &output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "guest_debug")]
|
||||
fn test_coredump() {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(focal));
|
||||
let api_socket = temp_api_path(&guest.tmp_dir);
|
||||
|
||||
let mut cmd = GuestCommand::new(&guest);
|
||||
cmd.args(&["--cpus", "boot=4"])
|
||||
.args(&["--memory", "size=4G"])
|
||||
.args(&["--kernel", fw_path(FwType::RustHypervisorFirmware).as_str()])
|
||||
.default_disks()
|
||||
.args(&["--net", guest.default_net_string().as_str()])
|
||||
.args(&["--api-socket", &api_socket])
|
||||
.capture_output();
|
||||
|
||||
let mut child = cmd.spawn().unwrap();
|
||||
let vmcore_file = temp_vmcore_file_path(&guest.tmp_dir);
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
|
||||
assert!(remote_command(&api_socket, "pause", None));
|
||||
|
||||
assert!(remote_command(
|
||||
&api_socket,
|
||||
"coredump",
|
||||
Some(format!("file://{}", vmcore_file).as_str()),
|
||||
));
|
||||
|
||||
// the num of CORE notes should equals to vcpu
|
||||
let readelf_core_num_cmd = format!(
|
||||
"readelf --all {} |grep CORE |grep -v Type |wc -l",
|
||||
vmcore_file
|
||||
);
|
||||
let core_num_in_elf = exec_host_command_output(&readelf_core_num_cmd);
|
||||
assert_eq!(String::from_utf8_lossy(&core_num_in_elf.stdout).trim(), "4");
|
||||
|
||||
// the num of QEMU notes should equals to vcpu
|
||||
let readelf_vmm_num_cmd = format!("readelf --all {} |grep QEMU |wc -l", vmcore_file);
|
||||
let vmm_num_in_elf = exec_host_command_output(&readelf_vmm_num_cmd);
|
||||
assert_eq!(String::from_utf8_lossy(&vmm_num_in_elf.stdout).trim(), "4");
|
||||
});
|
||||
|
||||
let _ = child.kill();
|
||||
let output = child.wait_with_output().unwrap();
|
||||
|
||||
handle_child_output(r, &output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_watchdog() {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
@@ -6388,10 +6561,9 @@ mod sequential {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
mod windows {
|
||||
use crate::*;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
lazy_static! {
|
||||
static ref NEXT_DISK_ID: Mutex<u8> = Mutex::new(1);
|
||||
}
|
||||
static NEXT_DISK_ID: Lazy<Mutex<u8>> = Lazy::new(|| Mutex::new(1));
|
||||
|
||||
struct WindowsGuest {
|
||||
guest: Guest,
|
||||
@@ -7619,9 +7791,9 @@ mod live_migration {
|
||||
"--memory",
|
||||
"size=0,hotplug_method=virtio-mem",
|
||||
"--memory-zone",
|
||||
"id=mem0,size=1G,hotplug_size=32G",
|
||||
"id=mem1,size=1G,hotplug_size=32G",
|
||||
"id=mem2,size=2G,hotplug_size=32G",
|
||||
"id=mem0,size=1G,hotplug_size=4G",
|
||||
"id=mem1,size=1G,hotplug_size=4G",
|
||||
"id=mem2,size=2G,hotplug_size=4G",
|
||||
"--numa",
|
||||
"guest_numa_id=0,cpus=[0-2,9],distances=[1@15,2@20],memory_zones=mem0",
|
||||
"guest_numa_id=1,cpus=[3-4,6-8],distances=[0@20,2@25],memory_zones=mem1",
|
||||
@@ -7632,9 +7804,9 @@ mod live_migration {
|
||||
"--memory",
|
||||
"size=0,hotplug_method=virtio-mem,shared=on",
|
||||
"--memory-zone",
|
||||
"id=mem0,size=1G,hotplug_size=32G,shared=on",
|
||||
"id=mem1,size=1G,hotplug_size=32G,shared=on",
|
||||
"id=mem2,size=2G,hotplug_size=32G,shared=on",
|
||||
"id=mem0,size=1G,hotplug_size=4G,shared=on",
|
||||
"id=mem1,size=1G,hotplug_size=4G,shared=on",
|
||||
"id=mem2,size=2G,hotplug_size=4G,shared=on",
|
||||
"--numa",
|
||||
"guest_numa_id=0,cpus=[0-2,9],distances=[1@15,2@20],memory_zones=mem0",
|
||||
"guest_numa_id=1,cpus=[3-4,6-8],distances=[0@20,2@25],memory_zones=mem1",
|
||||
@@ -8346,7 +8518,6 @@ mod aarch64_acpi {
|
||||
|
||||
assert_eq!(guest.get_cpu_count().unwrap_or_default(), 1);
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 400_000);
|
||||
assert!(guest.get_entropy().unwrap_or_default() >= 900);
|
||||
assert_eq!(guest.get_pci_bridge_class().unwrap_or_default(), "0x060000");
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.58"
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
serde = {version = ">=1.0.27", features = ["rc"] }
|
||||
|
||||
@@ -51,7 +51,7 @@ impl Default for Command {
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(u32)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum HeaderFlags {
|
||||
Command = 0,
|
||||
Reply = 1,
|
||||
|
||||
@@ -12,5 +12,5 @@ libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
remain = "0.2.3"
|
||||
thiserror = "1.0"
|
||||
uuid = { version = "1.0.0", features = ["v4"] }
|
||||
uuid = { version = "1.1.2", features = ["v4"] }
|
||||
vmm-sys-util = ">=0.3.1"
|
||||
@@ -6,7 +6,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
block_util = { path = "../block_util" }
|
||||
clap = { version = "3.1.18", features = ["wrap_help","cargo"] }
|
||||
clap = { version = "3.2.8", features = ["wrap_help","cargo"] }
|
||||
env_logger = "0.9.0"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.126"
|
||||
@@ -14,10 +14,10 @@ log = "0.4.17"
|
||||
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.5.0"
|
||||
virtio-bindings = "0.1.0"
|
||||
vm-memory = "0.8.0"
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.1.18", features = ["cargo"] }
|
||||
clap = { version = "3.2.8", features = ["cargo"] }
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
//
|
||||
// SPDX-License-Identifier: (Apache-2.0 AND BSD-3-Clause)
|
||||
|
||||
#![allow(clippy::significant_drop_in_scrutinee)]
|
||||
|
||||
use block_util::{build_disk_image_id, Request, VirtioBlockConfig};
|
||||
use libc::EFD_NONBLOCK;
|
||||
use log::*;
|
||||
@@ -45,8 +47,8 @@ const BLK_SIZE: u32 = 512;
|
||||
// and the overhead of the emulation layer.
|
||||
const POLL_QUEUE_US: u128 = 50;
|
||||
|
||||
trait DiskFile: Read + Seek + Write + Send + Sync {}
|
||||
impl<D: Read + Seek + Write + Send + Sync> DiskFile for D {}
|
||||
trait DiskFile: Read + Seek + Write + Send {}
|
||||
impl<D: Read + Seek + Write + Send> DiskFile for D {}
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
type VhostUserBackendResult<T> = std::result::Result<T, std::io::Error>;
|
||||
|
||||
@@ -5,7 +5,7 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.1.18", features = ["wrap_help","cargo"] }
|
||||
clap = { version = "3.2.8", features = ["wrap_help","cargo"] }
|
||||
env_logger = "0.9.0"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.126"
|
||||
@@ -13,10 +13,10 @@ log = "0.4.17"
|
||||
net_util = { path = "../net_util" }
|
||||
option_parser = { path = "../option_parser" }
|
||||
vhost = { version = "0.4.0", features = ["vhost-user-slave"] }
|
||||
vhost-user-backend = { git = "https://github.com/rust-vmm/vhost-user-backend", rev = "14f58eda14076e973704d4f904850be1146fbb05" }
|
||||
vhost-user-backend = "0.5.0"
|
||||
virtio-bindings = "0.1.0"
|
||||
vm-memory = "0.8.0"
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.1.18", features = ["cargo"] }
|
||||
clap = { version = "3.2.8", features = ["cargo"] }
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
//
|
||||
// SPDX-License-Identifier: (Apache-2.0 AND BSD-3-Clause)
|
||||
|
||||
#![allow(clippy::significant_drop_in_scrutinee)]
|
||||
|
||||
use libc::{self, EFD_NONBLOCK};
|
||||
use log::*;
|
||||
use net_util::{
|
||||
|
||||
@@ -9,7 +9,7 @@ default = []
|
||||
mshv = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.58"
|
||||
arc-swap = "1.5.0"
|
||||
block_util = { path = "../block_util" }
|
||||
byteorder = "1.4.3"
|
||||
@@ -23,14 +23,14 @@ 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"
|
||||
serde = { version="1.0.138", features=["derive"] }
|
||||
serde_json = "1.0.82"
|
||||
thiserror = "1.0.31"
|
||||
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.4.0"
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
|
||||
@@ -206,7 +206,7 @@ pub trait VirtioDevice: Send {
|
||||
/// address translation before they try to read from the guest physical address.
|
||||
/// On the other side, the implementation itself should be provided by the code
|
||||
/// emulating the IOMMU for the guest.
|
||||
pub trait DmaRemapping: Send + Sync {
|
||||
pub trait DmaRemapping {
|
||||
/// Provide a way to translate GVA address ranges into GPAs.
|
||||
fn translate_gva(&self, id: u32, addr: u64) -> std::result::Result<u64, std::io::Error>;
|
||||
/// Provide a way to translate GPA address ranges into GVAs.
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
//! Implements virtio devices, queues, and transport mechanisms.
|
||||
|
||||
#![allow(clippy::significant_drop_in_scrutinee)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate event_monitor;
|
||||
#[macro_use]
|
||||
@@ -123,14 +125,14 @@ pub enum Error {
|
||||
QueueIterator(virtio_queue::Error),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub struct TokenBucketConfig {
|
||||
pub size: u64,
|
||||
pub one_time_burst: Option<u64>,
|
||||
pub refill_time: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RateLimiterConfig {
|
||||
pub bandwidth: Option<TokenBucketConfig>,
|
||||
|
||||
@@ -106,7 +106,7 @@ impl Display for Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum RequestType {
|
||||
Flush,
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use vmm_sys_util::eventfd::EventFd;
|
||||
mod pci_common_config;
|
||||
mod pci_device;
|
||||
pub use pci_common_config::VirtioPciCommonConfig;
|
||||
pub use pci_device::VirtioPciDevice;
|
||||
pub use pci_device::{VirtioPciDevice, VirtioPciDeviceActivator};
|
||||
|
||||
pub trait VirtioTransport {
|
||||
fn ioeventfds(&self, base_addr: u64) -> Vec<(&EventFd, u64)>;
|
||||
|
||||
@@ -287,6 +287,37 @@ struct VirtioPciDeviceState {
|
||||
|
||||
impl VersionMapped for VirtioPciDeviceState {}
|
||||
|
||||
pub struct VirtioPciDeviceActivator {
|
||||
interrupt: Option<Arc<dyn VirtioInterrupt>>,
|
||||
memory: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
device: Arc<Mutex<dyn VirtioDevice>>,
|
||||
device_activated: Arc<AtomicBool>,
|
||||
queues: Option<Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>>,
|
||||
queue_evts: Option<Vec<EventFd>>,
|
||||
barrier: Option<Arc<Barrier>>,
|
||||
id: String,
|
||||
}
|
||||
|
||||
impl VirtioPciDeviceActivator {
|
||||
pub fn activate(&mut self) -> ActivateResult {
|
||||
self.device.lock().unwrap().activate(
|
||||
self.memory.take().unwrap(),
|
||||
self.interrupt.take().unwrap(),
|
||||
self.queues.take().unwrap(),
|
||||
self.queue_evts.take().unwrap(),
|
||||
)?;
|
||||
self.device_activated.store(true, Ordering::SeqCst);
|
||||
|
||||
if let Some(barrier) = self.barrier.take() {
|
||||
info!("{}: Waiting for barrier", self.id);
|
||||
barrier.wait();
|
||||
info!("{}: Barrier released", self.id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VirtioPciDevice {
|
||||
id: String,
|
||||
|
||||
@@ -338,11 +369,11 @@ pub struct VirtioPciDevice {
|
||||
// EventFd to signal on to request activation
|
||||
activate_evt: EventFd,
|
||||
|
||||
// Barrier that is used to wait on for activation
|
||||
activate_barrier: Arc<Barrier>,
|
||||
|
||||
// Optional DMA handler
|
||||
dma_handler: Option<Arc<dyn ExternalDmaMapping>>,
|
||||
|
||||
// Pending activations
|
||||
pending_activations: Arc<Mutex<Vec<VirtioPciDeviceActivator>>>,
|
||||
}
|
||||
|
||||
impl VirtioPciDevice {
|
||||
@@ -359,6 +390,7 @@ impl VirtioPciDevice {
|
||||
activate_evt: EventFd,
|
||||
use_64bit_bar: bool,
|
||||
dma_handler: Option<Arc<dyn ExternalDmaMapping>>,
|
||||
pending_activations: Arc<Mutex<Vec<VirtioPciDeviceActivator>>>,
|
||||
) -> Result<Self> {
|
||||
let device_clone = device.clone();
|
||||
let mut locked_device = device_clone.lock().unwrap();
|
||||
@@ -458,8 +490,8 @@ impl VirtioPciDevice {
|
||||
cap_pci_cfg_info: VirtioPciCfgCapInfo::default(),
|
||||
bar_regions: vec![],
|
||||
activate_evt,
|
||||
activate_barrier: Arc::new(Barrier::new(2)),
|
||||
dma_handler,
|
||||
pending_activations,
|
||||
};
|
||||
|
||||
if let Some(msix_config) = &virtio_pci_device.msix_config {
|
||||
@@ -666,37 +698,37 @@ impl VirtioPciDevice {
|
||||
self.device.clone()
|
||||
}
|
||||
|
||||
fn activate(&mut self) -> ActivateResult {
|
||||
if let Some(virtio_interrupt) = self.virtio_interrupt.take() {
|
||||
if self.memory.is_some() {
|
||||
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();
|
||||
queues.retain(|q| q.state.ready);
|
||||
for (i, queue) in queues.iter().enumerate() {
|
||||
queue_evts.push(self.queue_evts[i].try_clone().unwrap());
|
||||
if !queue.is_valid() {
|
||||
error!("Queue {} is not valid", i);
|
||||
}
|
||||
}
|
||||
return device.activate(mem, virtio_interrupt, queues, queue_evts);
|
||||
fn prepare_activator(&mut self, barrier: Option<Arc<Barrier>>) -> VirtioPciDeviceActivator {
|
||||
let mut queue_evts = Vec::new();
|
||||
let mut queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>> =
|
||||
self.queues.iter().map(vm_virtio::clone_queue).collect();
|
||||
queues.retain(|q| q.state.ready);
|
||||
for (i, queue) in queues.iter().enumerate() {
|
||||
queue_evts.push(self.queue_evts[i].try_clone().unwrap());
|
||||
if !queue.is_valid() {
|
||||
error!("Queue {} is not valid", i);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
VirtioPciDeviceActivator {
|
||||
interrupt: self.virtio_interrupt.take(),
|
||||
memory: self.memory.clone(),
|
||||
device: self.device.clone(),
|
||||
queues: Some(queues),
|
||||
device_activated: self.device_activated.clone(),
|
||||
queue_evts: Some(
|
||||
queue_evts
|
||||
.iter()
|
||||
.map(|fd| fd.try_clone().unwrap())
|
||||
.collect(),
|
||||
),
|
||||
barrier,
|
||||
id: self.id.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maybe_activate(&mut self) {
|
||||
if self.needs_activation() {
|
||||
self.activate().expect("Failed to activate device");
|
||||
self.device_activated.store(true, Ordering::SeqCst);
|
||||
info!("{}: Waiting for barrier", self.id);
|
||||
self.activate_barrier.wait();
|
||||
info!("{}: Barrier released", self.id);
|
||||
} else {
|
||||
info!("{}: Device does not need activation", self.id)
|
||||
}
|
||||
fn activate(&mut self) -> ActivateResult {
|
||||
self.prepare_activator(None).activate()
|
||||
}
|
||||
|
||||
fn needs_activation(&self) -> bool {
|
||||
@@ -1020,7 +1052,7 @@ impl PciDevice for VirtioPciDevice {
|
||||
self.device.clone(),
|
||||
),
|
||||
o if (ISR_CONFIG_BAR_OFFSET..ISR_CONFIG_BAR_OFFSET + ISR_CONFIG_SIZE).contains(&o) => {
|
||||
if let Some(v) = data.get(0) {
|
||||
if let Some(v) = data.first() {
|
||||
self.interrupt_status
|
||||
.fetch_and(!(*v as usize), Ordering::AcqRel);
|
||||
}
|
||||
@@ -1058,13 +1090,16 @@ impl PciDevice for VirtioPciDevice {
|
||||
|
||||
// Try and activate the device if the driver status has changed
|
||||
if self.needs_activation() {
|
||||
let barrier = Arc::new(Barrier::new(2));
|
||||
let activator = self.prepare_activator(Some(barrier.clone()));
|
||||
self.pending_activations.lock().unwrap().push(activator);
|
||||
info!(
|
||||
"{}: Needs activation; writing to activate event fd",
|
||||
self.id
|
||||
);
|
||||
self.activate_evt.write(1).ok();
|
||||
info!("{}: Needs activation; returning barrier", self.id);
|
||||
return Some(self.activate_barrier.clone());
|
||||
return Some(barrier);
|
||||
}
|
||||
|
||||
// Device has been reset by the driver
|
||||
|
||||
@@ -173,7 +173,7 @@ impl VhostUserHandle {
|
||||
// at early stage.
|
||||
for (queue_index, queue) in queues.iter().enumerate() {
|
||||
self.vu
|
||||
.set_vring_num(queue_index, queue.max_size())
|
||||
.set_vring_num(queue_index, queue.state.size)
|
||||
.map_err(Error::VhostUserSetVringNum)?;
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ impl VhostUserHandle {
|
||||
mmap_size: 0,
|
||||
mmap_offset: 0,
|
||||
num_queues: queues.len() as u16,
|
||||
queue_size: queues[0].max_size(),
|
||||
queue_size: queues[0].state.size,
|
||||
};
|
||||
let (info, fd) = self
|
||||
.vu
|
||||
@@ -203,11 +203,11 @@ impl VhostUserHandle {
|
||||
|
||||
let mut vrings_info = Vec::new();
|
||||
for (queue_index, queue) in queues.into_iter().enumerate() {
|
||||
let actual_size: usize = queue.max_size().try_into().unwrap();
|
||||
let actual_size: usize = queue.state.size.try_into().unwrap();
|
||||
|
||||
let config_data = VringConfigData {
|
||||
queue_max_size: queue.max_size(),
|
||||
queue_size: queue.max_size(),
|
||||
queue_size: queue.state.size,
|
||||
flags: 0u32,
|
||||
desc_table_addr: get_host_address_range(
|
||||
mem,
|
||||
@@ -290,10 +290,7 @@ impl VhostUserHandle {
|
||||
}
|
||||
|
||||
pub fn reset_vhost_user(&mut self, num_queues: usize) -> Result<()> {
|
||||
self.enable_vhost_user_vrings(num_queues, false)?;
|
||||
|
||||
// Reset the owner.
|
||||
self.vu.reset_owner().map_err(Error::VhostUserResetOwner)
|
||||
self.enable_vhost_user_vrings(num_queues, false)
|
||||
}
|
||||
|
||||
pub fn set_protocol_features_vhost_user(
|
||||
|
||||
@@ -38,7 +38,7 @@ type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// A vsock connection state.
|
||||
///
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum ConnState {
|
||||
/// The connection has been initiated by the host end, but is yet to be confirmed by the guest.
|
||||
LocalInit,
|
||||
@@ -61,7 +61,7 @@ pub enum ConnState {
|
||||
/// (via `notify()`), the connection will store a `PendingRx::Rw` to be later inspected by
|
||||
/// `recv_pkt()`.
|
||||
///
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum PendingRx {
|
||||
/// We need to yield a connection request packet (VSOCK_OP_REQUEST).
|
||||
Request = 0,
|
||||
|
||||
@@ -10,11 +10,11 @@ kvm = ["vfio-ioctls/kvm"]
|
||||
mshv = ["vfio-ioctls/mshv"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.58"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
thiserror = "1.0.31"
|
||||
serde = { version = "1.0.137", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.81"
|
||||
serde = { version = "1.0.138", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.82"
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap"] }
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
//! Handles routing to devices in an address space.
|
||||
|
||||
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
|
||||
use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
|
||||
use std::collections::btree_map::BTreeMap;
|
||||
use std::sync::{Arc, Barrier, Mutex, RwLock, Weak};
|
||||
use std::{convert, error, fmt, io, result};
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#![allow(clippy::significant_drop_in_scrutinee)]
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
mod bus;
|
||||
@@ -12,7 +14,7 @@ pub mod interrupt;
|
||||
pub use self::bus::{Bus, BusDevice, Error as BusError};
|
||||
|
||||
/// Type of Message Signalled Interrupt
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum MsiIrqType {
|
||||
/// PCI MSI IRQ numbers.
|
||||
PciMsi,
|
||||
@@ -22,7 +24,7 @@ pub enum MsiIrqType {
|
||||
GenericMsi,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
||||
pub enum PciBarType {
|
||||
Io,
|
||||
Mmio32,
|
||||
|
||||
@@ -5,10 +5,10 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.58"
|
||||
thiserror = "1.0.31"
|
||||
serde = { version = "1.0.137", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.81"
|
||||
serde = { version = "1.0.138", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.82"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
|
||||
@@ -12,7 +12,7 @@ use versionize::{VersionMap, Versionize};
|
||||
pub mod protocol;
|
||||
|
||||
/// Global VMM version for versioning
|
||||
const MAJOR_VERSION: u16 = 24;
|
||||
const MAJOR_VERSION: u16 = 25;
|
||||
const MINOR_VERSION: u16 = 0;
|
||||
const VMM_VERSION: u16 = MAJOR_VERSION << 12 | MINOR_VERSION & 0b1111;
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ impl Request {
|
||||
}
|
||||
|
||||
#[repr(u16)]
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Status {
|
||||
Invalid,
|
||||
Ok,
|
||||
|
||||
@@ -10,5 +10,5 @@ default = []
|
||||
[dependencies]
|
||||
log = "0.4.17"
|
||||
virtio-bindings = { version = "0.1.0", features = ["virtio-v5_0_0"] }
|
||||
virtio-queue = "0.3.0"
|
||||
virtio-queue = "0.4.0"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
|
||||
@@ -9,47 +9,48 @@ default = []
|
||||
amx = []
|
||||
cmos = ["devices/cmos"]
|
||||
fwdebug = ["devices/fwdebug"]
|
||||
gdb = ["kvm"]
|
||||
gdb = ["kvm", "gdbstub", "gdbstub_arch"]
|
||||
guest_debug = ["kvm"]
|
||||
kvm = ["hypervisor/kvm", "vfio-ioctls/kvm", "vm-device/kvm", "pci/kvm"]
|
||||
mshv = ["hypervisor/mshv", "virtio-devices/mshv", "vfio-ioctls/mshv", "vm-device/mshv", "pci/mshv"]
|
||||
tdx = ["arch/tdx", "hypervisor/tdx"]
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { path = "../acpi_tables" }
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.58"
|
||||
arc-swap = "1.5.0"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "1.3.2"
|
||||
block_util = { path = "../block_util" }
|
||||
clap = "3.1.18"
|
||||
clap = "3.2.8"
|
||||
devices = { path = "../devices" }
|
||||
epoll = "4.3.1"
|
||||
event_monitor = { path = "../event_monitor" }
|
||||
gdbstub = "0.6.1"
|
||||
gdbstub_arch = "0.2.2"
|
||||
gdbstub = { version = "0.6.2", optional = true }
|
||||
gdbstub_arch = { version = "0.2.3", optional = true }
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
lazy_static = "1.4.0"
|
||||
libc = "0.2.126"
|
||||
linux-loader = { version = "0.4.0", features = ["elf", "bzimage", "pe"] }
|
||||
log = "0.4.17"
|
||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||
net_util = { path = "../net_util" }
|
||||
once_cell = "1.13.0"
|
||||
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"
|
||||
serde = { version = "1.0.138", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.82"
|
||||
signal-hook = "0.3.14"
|
||||
thiserror = "1.0.31"
|
||||
uuid = "1.0.0"
|
||||
uuid = "1.1.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.4.0"
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::api::{ApiError, ApiRequest, VmAction};
|
||||
use crate::seccomp_filters::{get_seccomp_filter, Thread};
|
||||
use crate::{Error as VmmError, Result};
|
||||
use micro_http::{Body, HttpServer, MediaType, Method, Request, Response, StatusCode, Version};
|
||||
use once_cell::sync::Lazy;
|
||||
use seccompiler::{apply_filter, SeccompAction};
|
||||
use serde_json::Error as SerdeError;
|
||||
use std::collections::HashMap;
|
||||
@@ -56,7 +57,7 @@ pub fn error_response(error: HttpError, status: StatusCode) -> Response {
|
||||
}
|
||||
|
||||
/// An HTTP endpoint handler interface
|
||||
pub trait EndpointHandler: Sync + Send {
|
||||
pub trait EndpointHandler {
|
||||
/// Handles an HTTP request.
|
||||
/// After parsing the request, the handler could decide to send an
|
||||
/// associated API request down to the VMM API server to e.g. create
|
||||
@@ -129,44 +130,125 @@ macro_rules! endpoint {
|
||||
};
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// HTTP_ROUTES contain all the cloud-hypervisor HTTP routes.
|
||||
pub static ref HTTP_ROUTES: HttpRoutes = {
|
||||
let mut r = HttpRoutes {
|
||||
routes: HashMap::new(),
|
||||
};
|
||||
|
||||
r.routes.insert(endpoint!("/vm.add-device"), Box::new(VmActionHandler::new(VmAction::AddDevice(Arc::default()))));
|
||||
r.routes.insert(endpoint!("/vm.add-user-device"), Box::new(VmActionHandler::new(VmAction::AddUserDevice(Arc::default()))));
|
||||
r.routes.insert(endpoint!("/vm.add-disk"), Box::new(VmActionHandler::new(VmAction::AddDisk(Arc::default()))));
|
||||
r.routes.insert(endpoint!("/vm.add-fs"), Box::new(VmActionHandler::new(VmAction::AddFs(Arc::default()))));
|
||||
r.routes.insert(endpoint!("/vm.add-net"), Box::new(VmActionHandler::new(VmAction::AddNet(Arc::default()))));
|
||||
r.routes.insert(endpoint!("/vm.add-pmem"), Box::new(VmActionHandler::new(VmAction::AddPmem(Arc::default()))));
|
||||
r.routes.insert(endpoint!("/vm.add-vdpa"), Box::new(VmActionHandler::new(VmAction::AddVdpa(Arc::default()))));
|
||||
r.routes.insert(endpoint!("/vm.add-vsock"), Box::new(VmActionHandler::new(VmAction::AddVsock(Arc::default()))));
|
||||
r.routes.insert(endpoint!("/vm.boot"), Box::new(VmActionHandler::new(VmAction::Boot)));
|
||||
r.routes.insert(endpoint!("/vm.counters"), Box::new(VmActionHandler::new(VmAction::Counters)));
|
||||
r.routes.insert(endpoint!("/vm.create"), Box::new(VmCreate {}));
|
||||
r.routes.insert(endpoint!("/vm.delete"), Box::new(VmActionHandler::new(VmAction::Delete)));
|
||||
r.routes.insert(endpoint!("/vm.info"), Box::new(VmInfo {}));
|
||||
r.routes.insert(endpoint!("/vm.pause"), Box::new(VmActionHandler::new(VmAction::Pause)));
|
||||
r.routes.insert(endpoint!("/vm.power-button"), Box::new(VmActionHandler::new(VmAction::PowerButton)));
|
||||
r.routes.insert(endpoint!("/vm.reboot"), Box::new(VmActionHandler::new(VmAction::Reboot)));
|
||||
r.routes.insert(endpoint!("/vm.receive-migration"), Box::new(VmActionHandler::new(VmAction::ReceiveMigration(Arc::default()))));
|
||||
r.routes.insert(endpoint!("/vm.remove-device"), Box::new(VmActionHandler::new(VmAction::RemoveDevice(Arc::default()))));
|
||||
r.routes.insert(endpoint!("/vm.resize"), Box::new(VmActionHandler::new(VmAction::Resize(Arc::default()))));
|
||||
r.routes.insert(endpoint!("/vm.resize-zone"), Box::new(VmActionHandler::new(VmAction::ResizeZone(Arc::default()))));
|
||||
r.routes.insert(endpoint!("/vm.restore"), Box::new(VmActionHandler::new(VmAction::Restore(Arc::default()))));
|
||||
r.routes.insert(endpoint!("/vm.resume"), Box::new(VmActionHandler::new(VmAction::Resume)));
|
||||
r.routes.insert(endpoint!("/vm.send-migration"), Box::new(VmActionHandler::new(VmAction::SendMigration(Arc::default()))));
|
||||
r.routes.insert(endpoint!("/vm.shutdown"), Box::new(VmActionHandler::new(VmAction::Shutdown)));
|
||||
r.routes.insert(endpoint!("/vm.snapshot"), Box::new(VmActionHandler::new(VmAction::Snapshot(Arc::default()))));
|
||||
r.routes.insert(endpoint!("/vmm.ping"), Box::new(VmmPing {}));
|
||||
r.routes.insert(endpoint!("/vmm.shutdown"), Box::new(VmmShutdown {}));
|
||||
|
||||
r
|
||||
/// HTTP_ROUTES contain all the cloud-hypervisor HTTP routes.
|
||||
pub static HTTP_ROUTES: Lazy<HttpRoutes> = Lazy::new(|| {
|
||||
let mut r = HttpRoutes {
|
||||
routes: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.add-device"),
|
||||
Box::new(VmActionHandler::new(VmAction::AddDevice(Arc::default()))),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.add-user-device"),
|
||||
Box::new(VmActionHandler::new(
|
||||
VmAction::AddUserDevice(Arc::default()),
|
||||
)),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.add-disk"),
|
||||
Box::new(VmActionHandler::new(VmAction::AddDisk(Arc::default()))),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.add-fs"),
|
||||
Box::new(VmActionHandler::new(VmAction::AddFs(Arc::default()))),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.add-net"),
|
||||
Box::new(VmActionHandler::new(VmAction::AddNet(Arc::default()))),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.add-pmem"),
|
||||
Box::new(VmActionHandler::new(VmAction::AddPmem(Arc::default()))),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.add-vdpa"),
|
||||
Box::new(VmActionHandler::new(VmAction::AddVdpa(Arc::default()))),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.add-vsock"),
|
||||
Box::new(VmActionHandler::new(VmAction::AddVsock(Arc::default()))),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.boot"),
|
||||
Box::new(VmActionHandler::new(VmAction::Boot)),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.counters"),
|
||||
Box::new(VmActionHandler::new(VmAction::Counters)),
|
||||
);
|
||||
r.routes
|
||||
.insert(endpoint!("/vm.create"), Box::new(VmCreate {}));
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.delete"),
|
||||
Box::new(VmActionHandler::new(VmAction::Delete)),
|
||||
);
|
||||
r.routes.insert(endpoint!("/vm.info"), Box::new(VmInfo {}));
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.pause"),
|
||||
Box::new(VmActionHandler::new(VmAction::Pause)),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.power-button"),
|
||||
Box::new(VmActionHandler::new(VmAction::PowerButton)),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.reboot"),
|
||||
Box::new(VmActionHandler::new(VmAction::Reboot)),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.receive-migration"),
|
||||
Box::new(VmActionHandler::new(VmAction::ReceiveMigration(
|
||||
Arc::default(),
|
||||
))),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.remove-device"),
|
||||
Box::new(VmActionHandler::new(VmAction::RemoveDevice(Arc::default()))),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.resize"),
|
||||
Box::new(VmActionHandler::new(VmAction::Resize(Arc::default()))),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.resize-zone"),
|
||||
Box::new(VmActionHandler::new(VmAction::ResizeZone(Arc::default()))),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.restore"),
|
||||
Box::new(VmActionHandler::new(VmAction::Restore(Arc::default()))),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.resume"),
|
||||
Box::new(VmActionHandler::new(VmAction::Resume)),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.send-migration"),
|
||||
Box::new(VmActionHandler::new(
|
||||
VmAction::SendMigration(Arc::default()),
|
||||
)),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.shutdown"),
|
||||
Box::new(VmActionHandler::new(VmAction::Shutdown)),
|
||||
);
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.snapshot"),
|
||||
Box::new(VmActionHandler::new(VmAction::Snapshot(Arc::default()))),
|
||||
);
|
||||
#[cfg(feature = "guest_debug")]
|
||||
r.routes.insert(
|
||||
endpoint!("/vm.coredump"),
|
||||
Box::new(VmActionHandler::new(VmAction::Coredump(Arc::default()))),
|
||||
);
|
||||
r.routes
|
||||
.insert(endpoint!("/vmm.ping"), Box::new(VmmPing {}));
|
||||
r.routes
|
||||
.insert(endpoint!("/vmm.shutdown"), Box::new(VmmShutdown {}));
|
||||
|
||||
r
|
||||
});
|
||||
|
||||
fn handle_http_request(
|
||||
request: &Request,
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
//
|
||||
|
||||
use crate::api::http::{error_response, EndpointHandler, HttpError};
|
||||
#[cfg(feature = "guest_debug")]
|
||||
use crate::api::vm_coredump;
|
||||
use crate::api::{
|
||||
vm_add_device, vm_add_disk, vm_add_fs, vm_add_net, vm_add_pmem, vm_add_user_device,
|
||||
vm_add_vdpa, vm_add_vsock, vm_boot, vm_counters, vm_create, vm_delete, vm_info, vm_pause,
|
||||
@@ -151,6 +153,12 @@ impl EndpointHandler for VmActionHandler {
|
||||
api_sender,
|
||||
Arc::new(serde_json::from_slice(body.raw())?),
|
||||
),
|
||||
#[cfg(feature = "guest_debug")]
|
||||
Coredump(_) => vm_coredump(
|
||||
api_notifier,
|
||||
api_sender,
|
||||
Arc::new(serde_json::from_slice(body.raw())?),
|
||||
),
|
||||
ReceiveMigration(_) => vm_receive_migration(
|
||||
api_notifier,
|
||||
api_sender,
|
||||
|
||||
@@ -99,6 +99,9 @@ pub enum ApiError {
|
||||
/// The VM could not restored.
|
||||
VmRestore(VmError),
|
||||
|
||||
/// The VM could not be coredumped.
|
||||
VmCoredump(VmError),
|
||||
|
||||
/// The VMM could not shutdown.
|
||||
VmmShutdown(VmError),
|
||||
|
||||
@@ -189,6 +192,12 @@ pub struct VmSnapshotConfig {
|
||||
pub destination_url: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize, Default, Debug)]
|
||||
pub struct VmCoredumpData {
|
||||
/// The coredump destination file
|
||||
pub destination_url: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize, Default, Debug)]
|
||||
pub struct VmReceiveMigrationData {
|
||||
/// URL for the reception of migration state
|
||||
@@ -310,6 +319,10 @@ pub enum ApiRequest {
|
||||
/// Restore from a VM snapshot
|
||||
VmRestore(Arc<RestoreConfig>, Sender<ApiResponse>),
|
||||
|
||||
/// Take a VM coredump
|
||||
#[cfg(feature = "guest_debug")]
|
||||
VmCoredump(Arc<VmCoredumpData>, Sender<ApiResponse>),
|
||||
|
||||
/// Incoming migration
|
||||
VmReceiveMigration(Arc<VmReceiveMigrationData>, Sender<ApiResponse>),
|
||||
|
||||
@@ -402,6 +415,10 @@ pub enum VmAction {
|
||||
/// Snapshot VM
|
||||
Snapshot(Arc<VmSnapshotConfig>),
|
||||
|
||||
/// Coredump VM
|
||||
#[cfg(feature = "guest_debug")]
|
||||
Coredump(Arc<VmCoredumpData>),
|
||||
|
||||
/// Incoming migration
|
||||
ReceiveMigration(Arc<VmReceiveMigrationData>),
|
||||
|
||||
@@ -441,6 +458,8 @@ fn vm_action(
|
||||
ResizeZone(v) => ApiRequest::VmResizeZone(v, response_sender),
|
||||
Restore(v) => ApiRequest::VmRestore(v, response_sender),
|
||||
Snapshot(v) => ApiRequest::VmSnapshot(v, response_sender),
|
||||
#[cfg(feature = "guest_debug")]
|
||||
Coredump(v) => ApiRequest::VmCoredump(v, response_sender),
|
||||
ReceiveMigration(v) => ApiRequest::VmReceiveMigration(v, response_sender),
|
||||
SendMigration(v) => ApiRequest::VmSendMigration(v, response_sender),
|
||||
PowerButton => ApiRequest::VmPowerButton(response_sender),
|
||||
@@ -526,6 +545,15 @@ pub fn vm_restore(
|
||||
vm_action(api_evt, api_sender, VmAction::Restore(data))
|
||||
}
|
||||
|
||||
#[cfg(feature = "guest_debug")]
|
||||
pub fn vm_coredump(
|
||||
api_evt: EventFd,
|
||||
api_sender: Sender<ApiRequest>,
|
||||
data: Arc<VmCoredumpData>,
|
||||
) -> ApiResult<Option<Body>> {
|
||||
vm_action(api_evt, api_sender, VmAction::Coredump(data))
|
||||
}
|
||||
|
||||
pub fn vm_info(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<VmInfo> {
|
||||
let (response_sender, response_receiver) = channel();
|
||||
|
||||
|
||||
@@ -366,6 +366,24 @@ paths:
|
||||
405:
|
||||
description: The VM instance could not be snapshotted because it is not booted.
|
||||
|
||||
/vm.coredump:
|
||||
put:
|
||||
summary: Takes a VM coredump.
|
||||
requestBody:
|
||||
description: The coredump configuration
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/VmCoredumpData'
|
||||
required: true
|
||||
responses:
|
||||
204:
|
||||
description: The VM instance was successfully coredumped.
|
||||
404:
|
||||
description: The VM instance could not be coredumped because it is not created.
|
||||
405:
|
||||
description: The VM instance could not be coredumped because it is not booted.
|
||||
|
||||
/vm.restore:
|
||||
put:
|
||||
summary: Restore a VM from a snapshot.
|
||||
@@ -596,6 +614,9 @@ components:
|
||||
type: integer
|
||||
topology:
|
||||
$ref: '#/components/schemas/CpuTopology'
|
||||
kvm_hyperv:
|
||||
type: boolean
|
||||
default: false
|
||||
max_phys_bits:
|
||||
type: integer
|
||||
affinity:
|
||||
@@ -870,8 +891,6 @@ components:
|
||||
|
||||
FsConfig:
|
||||
required:
|
||||
- cache_size
|
||||
- dax
|
||||
- num_queues
|
||||
- queue_size
|
||||
- socket
|
||||
@@ -888,13 +907,6 @@ components:
|
||||
queue_size:
|
||||
type: integer
|
||||
default: 1024
|
||||
dax:
|
||||
type: boolean
|
||||
default: true
|
||||
cache_size:
|
||||
type: integer
|
||||
format: int64
|
||||
default: 8589934592
|
||||
pci_segment:
|
||||
type: integer
|
||||
format: int16
|
||||
@@ -914,9 +926,6 @@ components:
|
||||
iommu:
|
||||
type: boolean
|
||||
default: false
|
||||
mergeable:
|
||||
type: boolean
|
||||
default: false
|
||||
discard_writes:
|
||||
type: boolean
|
||||
default: false
|
||||
@@ -1110,6 +1119,12 @@ components:
|
||||
destination_url:
|
||||
type: string
|
||||
|
||||
VmCoredumpData:
|
||||
type: object
|
||||
properties:
|
||||
destination_url:
|
||||
type: string
|
||||
|
||||
RestoreConfig:
|
||||
required:
|
||||
- source_url
|
||||
|
||||
@@ -43,8 +43,6 @@ pub enum Error {
|
||||
ParseFsTagMissing,
|
||||
/// Filesystem socket is missing
|
||||
ParseFsSockMissing,
|
||||
/// Cannot have dax=off along with cache_size parameter.
|
||||
InvalidCacheSizeWithDaxOff,
|
||||
/// Missing persistent memory file parameter.
|
||||
ParsePmemFileMissing,
|
||||
/// Missing vsock socket path parameter.
|
||||
@@ -115,7 +113,7 @@ pub enum Error {
|
||||
ParseVdpaPathMissing,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Error)]
|
||||
#[derive(Debug, PartialEq, Eq, Error)]
|
||||
pub enum ValidationError {
|
||||
/// Both console and serial are tty.
|
||||
DoubleTtyMode,
|
||||
@@ -291,9 +289,6 @@ impl fmt::Display for Error {
|
||||
ParseFileSystem(o) => write!(f, "Error parsing --fs: {}", o),
|
||||
ParseFsSockMissing => write!(f, "Error parsing --fs: socket missing"),
|
||||
ParseFsTagMissing => write!(f, "Error parsing --fs: tag missing"),
|
||||
InvalidCacheSizeWithDaxOff => {
|
||||
write!(f, "Error parsing --fs: cache_size used with dax=on")
|
||||
}
|
||||
ParsePersistentMemory(o) => write!(f, "Error parsing --pmem: {}", o),
|
||||
ParsePmemFileMissing => write!(f, "Error parsing --pmem: file missing"),
|
||||
ParseVsock(o) => write!(f, "Error parsing --vsock: {}", o),
|
||||
@@ -435,7 +430,7 @@ impl<'a> VmParams<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub enum HotplugMethod {
|
||||
Acpi,
|
||||
VirtioMem,
|
||||
@@ -464,13 +459,13 @@ impl FromStr for HotplugMethod {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct CpuAffinity {
|
||||
pub vcpu: u8,
|
||||
pub host_cpus: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct CpuFeatures {
|
||||
#[cfg(all(feature = "amx", target_arch = "x86_64"))]
|
||||
pub amx: bool,
|
||||
@@ -480,7 +475,7 @@ pub enum CpuTopologyParseError {
|
||||
InvalidValue(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct CpuTopology {
|
||||
pub threads_per_core: u8,
|
||||
pub cores_per_die: u8,
|
||||
@@ -521,7 +516,7 @@ fn default_cpuconfig_max_phys_bits() -> u8 {
|
||||
DEFAULT_MAX_PHYS_BITS
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct CpusConfig {
|
||||
pub boot_vcpus: u8,
|
||||
pub max_vcpus: u8,
|
||||
@@ -631,7 +626,7 @@ fn default_platformconfig_num_pci_segments() -> u16 {
|
||||
DEFAULT_NUM_PCI_SEGMENTS
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct PlatformConfig {
|
||||
#[serde(default = "default_platformconfig_num_pci_segments")]
|
||||
pub num_pci_segments: u16,
|
||||
@@ -696,7 +691,7 @@ impl Default for PlatformConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct MemoryZoneConfig {
|
||||
pub id: String,
|
||||
pub size: u64,
|
||||
@@ -718,7 +713,7 @@ pub struct MemoryZoneConfig {
|
||||
pub prefault: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct MemoryConfig {
|
||||
pub size: u64,
|
||||
#[serde(default)]
|
||||
@@ -923,17 +918,17 @@ impl Default for MemoryConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct KernelConfig {
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct InitramfsConfig {
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct CmdlineConfig {
|
||||
pub args: String,
|
||||
}
|
||||
@@ -948,7 +943,7 @@ impl CmdlineConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct DiskConfig {
|
||||
pub path: Option<PathBuf>,
|
||||
#[serde(default)]
|
||||
@@ -1183,7 +1178,7 @@ impl DiskConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub enum VhostMode {
|
||||
Client,
|
||||
Server,
|
||||
@@ -1212,7 +1207,7 @@ impl FromStr for VhostMode {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct NetConfig {
|
||||
#[serde(default = "default_netconfig_tap")]
|
||||
pub tap: Option<String>,
|
||||
@@ -1483,7 +1478,7 @@ impl NetConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct RngConfig {
|
||||
pub src: PathBuf,
|
||||
#[serde(default)]
|
||||
@@ -1520,7 +1515,7 @@ impl Default for RngConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct BalloonConfig {
|
||||
pub size: u64,
|
||||
/// Option to deflate the balloon in case the guest is out of memory.
|
||||
@@ -1569,7 +1564,7 @@ impl BalloonConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct FsConfig {
|
||||
pub tag: String,
|
||||
pub socket: PathBuf,
|
||||
@@ -1577,10 +1572,6 @@ pub struct FsConfig {
|
||||
pub num_queues: usize,
|
||||
#[serde(default = "default_fsconfig_queue_size")]
|
||||
pub queue_size: u16,
|
||||
#[serde(default = "default_fsconfig_dax")]
|
||||
pub dax: bool,
|
||||
#[serde(default = "default_fsconfig_cache_size")]
|
||||
pub cache_size: u64,
|
||||
#[serde(default)]
|
||||
pub id: Option<String>,
|
||||
#[serde(default)]
|
||||
@@ -1595,14 +1586,6 @@ fn default_fsconfig_queue_size() -> u16 {
|
||||
1024
|
||||
}
|
||||
|
||||
fn default_fsconfig_dax() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn default_fsconfig_cache_size() -> u64 {
|
||||
0x0002_0000_0000
|
||||
}
|
||||
|
||||
impl Default for FsConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
@@ -1610,8 +1593,6 @@ impl Default for FsConfig {
|
||||
socket: PathBuf::new(),
|
||||
num_queues: default_fsconfig_num_queues(),
|
||||
queue_size: default_fsconfig_queue_size(),
|
||||
dax: default_fsconfig_dax(),
|
||||
cache_size: default_fsconfig_cache_size(),
|
||||
id: None,
|
||||
pci_segment: 0,
|
||||
}
|
||||
@@ -1621,15 +1602,12 @@ impl Default for FsConfig {
|
||||
impl FsConfig {
|
||||
pub const SYNTAX: &'static str = "virtio-fs parameters \
|
||||
\"tag=<tag_name>,socket=<socket_path>,num_queues=<number_of_queues>,\
|
||||
queue_size=<size_of_each_queue>,dax=on|off,cache_size=<DAX cache size: \
|
||||
default 8Gib>,id=<device_id>,pci_segment=<segment_id>\"";
|
||||
queue_size=<size_of_each_queue>,id=<device_id>,pci_segment=<segment_id>\"";
|
||||
|
||||
pub fn parse(fs: &str) -> Result<Self> {
|
||||
let mut parser = OptionParser::new();
|
||||
parser
|
||||
.add("tag")
|
||||
.add("dax")
|
||||
.add("cache_size")
|
||||
.add("queue_size")
|
||||
.add("num_queues")
|
||||
.add("socket")
|
||||
@@ -1649,22 +1627,6 @@ impl FsConfig {
|
||||
.map_err(Error::ParseFileSystem)?
|
||||
.unwrap_or_else(default_fsconfig_num_queues);
|
||||
|
||||
let dax = parser
|
||||
.convert::<Toggle>("dax")
|
||||
.map_err(Error::ParseFileSystem)?
|
||||
.unwrap_or_else(|| Toggle(default_fsconfig_dax()))
|
||||
.0;
|
||||
|
||||
if parser.is_set("cache_size") && !dax {
|
||||
return Err(Error::InvalidCacheSizeWithDaxOff);
|
||||
}
|
||||
|
||||
let cache_size = parser
|
||||
.convert::<ByteSized>("cache_size")
|
||||
.map_err(Error::ParseFileSystem)?
|
||||
.unwrap_or_else(|| ByteSized(default_fsconfig_cache_size()))
|
||||
.0;
|
||||
|
||||
let id = parser.get("id");
|
||||
|
||||
let pci_segment = parser
|
||||
@@ -1677,8 +1639,6 @@ impl FsConfig {
|
||||
socket,
|
||||
num_queues,
|
||||
queue_size,
|
||||
dax,
|
||||
cache_size,
|
||||
id,
|
||||
pci_segment,
|
||||
})
|
||||
@@ -1703,20 +1663,11 @@ impl FsConfig {
|
||||
}
|
||||
}
|
||||
|
||||
if self.dax {
|
||||
// TODO: Remove the dax parameter.
|
||||
// Tracked by https://github.com/cloud-hypervisor/cloud-hypervisor/issues/3889
|
||||
warn!(
|
||||
"The experimental DAX feature is deprecated and will be \
|
||||
removed in a future release. A request to enable it is ignored."
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
|
||||
pub struct PmemConfig {
|
||||
pub file: PathBuf,
|
||||
#[serde(default)]
|
||||
@@ -1724,8 +1675,6 @@ pub struct PmemConfig {
|
||||
#[serde(default)]
|
||||
pub iommu: bool,
|
||||
#[serde(default)]
|
||||
pub mergeable: bool,
|
||||
#[serde(default)]
|
||||
pub discard_writes: bool,
|
||||
#[serde(default)]
|
||||
pub id: Option<String>,
|
||||
@@ -1742,7 +1691,6 @@ impl PmemConfig {
|
||||
parser
|
||||
.add("size")
|
||||
.add("file")
|
||||
.add("mergeable")
|
||||
.add("iommu")
|
||||
.add("discard_writes")
|
||||
.add("id")
|
||||
@@ -1754,11 +1702,6 @@ impl PmemConfig {
|
||||
.convert::<ByteSized>("size")
|
||||
.map_err(Error::ParsePersistentMemory)?
|
||||
.map(|v| v.0);
|
||||
let mergeable = parser
|
||||
.convert::<Toggle>("mergeable")
|
||||
.map_err(Error::ParsePersistentMemory)?
|
||||
.unwrap_or(Toggle(false))
|
||||
.0;
|
||||
let iommu = parser
|
||||
.convert::<Toggle>("iommu")
|
||||
.map_err(Error::ParsePersistentMemory)?
|
||||
@@ -1779,7 +1722,6 @@ impl PmemConfig {
|
||||
file,
|
||||
size,
|
||||
iommu,
|
||||
mergeable,
|
||||
discard_writes,
|
||||
id,
|
||||
pci_segment,
|
||||
@@ -1787,10 +1729,6 @@ impl PmemConfig {
|
||||
}
|
||||
|
||||
pub fn validate(&self, vm_config: &VmConfig) -> ValidationResult<()> {
|
||||
if self.mergeable {
|
||||
warn!("Enabling mergable pages for PMEM devices is ineffectual. This option is deprecated and will be removed in a later release");
|
||||
}
|
||||
|
||||
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));
|
||||
@@ -1807,7 +1745,7 @@ impl PmemConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub enum ConsoleOutputMode {
|
||||
Off,
|
||||
Pty,
|
||||
@@ -1816,7 +1754,7 @@ pub enum ConsoleOutputMode {
|
||||
Null,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct ConsoleConfig {
|
||||
#[serde(default = "default_consoleconfig_file")]
|
||||
pub file: Option<PathBuf>,
|
||||
@@ -1886,7 +1824,7 @@ impl ConsoleConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
|
||||
pub struct DeviceConfig {
|
||||
pub path: PathBuf,
|
||||
#[serde(default)]
|
||||
@@ -1945,7 +1883,7 @@ impl DeviceConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
|
||||
pub struct UserDeviceConfig {
|
||||
pub socket: PathBuf,
|
||||
#[serde(default)]
|
||||
@@ -1998,7 +1936,7 @@ impl UserDeviceConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
|
||||
pub struct VdpaConfig {
|
||||
pub path: PathBuf,
|
||||
#[serde(default = "default_vdpaconfig_num_queues")]
|
||||
@@ -2074,7 +2012,7 @@ impl VdpaConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
|
||||
pub struct VsockConfig {
|
||||
pub cid: u64,
|
||||
pub socket: PathBuf,
|
||||
@@ -2145,7 +2083,7 @@ impl VsockConfig {
|
||||
}
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
|
||||
pub struct TdxConfig {
|
||||
pub firmware: PathBuf,
|
||||
}
|
||||
@@ -2165,7 +2103,7 @@ impl TdxConfig {
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
|
||||
pub struct SgxEpcConfig {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
@@ -2199,7 +2137,7 @@ impl SgxEpcConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
|
||||
pub struct NumaDistance {
|
||||
#[serde(default)]
|
||||
pub destination: u32,
|
||||
@@ -2207,7 +2145,7 @@ pub struct NumaDistance {
|
||||
pub distance: u8,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
|
||||
pub struct NumaConfig {
|
||||
#[serde(default)]
|
||||
pub guest_numa_id: u32,
|
||||
@@ -2276,7 +2214,7 @@ impl NumaConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
|
||||
pub struct RestoreConfig {
|
||||
pub source_url: PathBuf,
|
||||
#[serde(default)]
|
||||
@@ -2310,7 +2248,7 @@ impl RestoreConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct VmConfig {
|
||||
#[serde(default)]
|
||||
pub cpus: CpusConfig,
|
||||
@@ -3116,29 +3054,7 @@ mod tests {
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
// DAX on -> default cache size
|
||||
assert_eq!(
|
||||
FsConfig::parse("tag=mytag,socket=/tmp/sock,dax=on")?,
|
||||
FsConfig {
|
||||
socket: PathBuf::from("/tmp/sock"),
|
||||
tag: "mytag".to_owned(),
|
||||
dax: true,
|
||||
cache_size: default_fsconfig_cache_size(),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
FsConfig::parse("tag=mytag,socket=/tmp/sock,dax=on,cache_size=4G")?,
|
||||
FsConfig {
|
||||
socket: PathBuf::from("/tmp/sock"),
|
||||
tag: "mytag".to_owned(),
|
||||
dax: true,
|
||||
cache_size: 4 << 30,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
// Cache size without DAX is an error
|
||||
assert!(FsConfig::parse("tag=mytag,socket=/tmp/sock,dax=off,cache_size=4G").is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3165,11 +3081,10 @@ mod tests {
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
PmemConfig::parse("file=/tmp/pmem,size=128M,iommu=on,mergeable=on,discard_writes=on")?,
|
||||
PmemConfig::parse("file=/tmp/pmem,size=128M,iommu=on,discard_writes=on")?,
|
||||
PmemConfig {
|
||||
file: PathBuf::from("/tmp/pmem"),
|
||||
size: Some(128 << 20),
|
||||
mergeable: true,
|
||||
discard_writes: true,
|
||||
iommu: true,
|
||||
..Default::default()
|
||||
|
||||
348
vmm/src/coredump.rs
Normal file
348
vmm/src/coredump.rs
Normal file
@@ -0,0 +1,348 @@
|
||||
// Copyright © 2022 ZTE Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use hypervisor::kvm::kvm_bindings::kvm_dtable as DTableRegister;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use hypervisor::x86_64::SegmentRegister;
|
||||
use linux_loader::elf;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use vm_memory::ByteValued;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CoredumpMemoryRegion {
|
||||
pub mem_offset_in_elf: u64,
|
||||
pub mem_size: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CoredumpMemoryRegions {
|
||||
pub ram_maps: std::collections::BTreeMap<u64, CoredumpMemoryRegion>,
|
||||
}
|
||||
|
||||
/// Platform information
|
||||
#[derive(Default)]
|
||||
pub struct DumpState {
|
||||
pub elf_note_size: isize,
|
||||
pub elf_phdr_num: u16,
|
||||
pub elf_sh_info: u32,
|
||||
pub mem_offset: u64,
|
||||
pub mem_info: Option<CoredumpMemoryRegions>,
|
||||
pub file: Option<File>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GuestDebuggableError {
|
||||
Coredump(anyhow::Error),
|
||||
CoredumpFile(std::io::Error),
|
||||
}
|
||||
|
||||
pub trait GuestDebuggable: vm_migration::Pausable {
|
||||
fn coredump(
|
||||
&mut self,
|
||||
_destination_url: &str,
|
||||
) -> std::result::Result<(), GuestDebuggableError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! div_round_up {
|
||||
($n:expr,$d:expr) => {
|
||||
($n + $d - 1) / $d
|
||||
};
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Copy, Clone)]
|
||||
pub struct X86_64UserRegs {
|
||||
/// r15, r14, r13, r12, rbp, rbx, r11, r10;
|
||||
pub regs1: [u64; 8],
|
||||
/// r9, r8, rax, rcx, rdx, rsi, rdi, orig_rax;
|
||||
pub regs2: [u64; 8],
|
||||
pub rip: u64,
|
||||
pub cs: u64,
|
||||
pub eflags: u64,
|
||||
pub rsp: u64,
|
||||
pub ss: u64,
|
||||
pub fs_base: u64,
|
||||
pub gs_base: u64,
|
||||
pub ds: u64,
|
||||
pub es: u64,
|
||||
pub fs: u64,
|
||||
pub gs: u64,
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for X86_64UserRegs {}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct X86_64ElfPrStatus {
|
||||
pub pad1: [u8; 32],
|
||||
pub pid: u32,
|
||||
pub pads2: [u8; 76],
|
||||
pub regs: X86_64UserRegs,
|
||||
pub pad3: [u8; 8],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Copy, Clone)]
|
||||
pub struct CpuSegment {
|
||||
pub selector: u32,
|
||||
pub limit: u32,
|
||||
pub flags: u32,
|
||||
pub pad: u32,
|
||||
pub base: u64,
|
||||
}
|
||||
|
||||
const DESC_TYPE_SHIFT: u32 = 8;
|
||||
const DESC_S_SHIFT: u32 = 12;
|
||||
const DESC_DPL_SHIFT: u32 = 13;
|
||||
const DESC_P_SHIFT: u32 = 15;
|
||||
const DESC_P_MASK: u32 = 1 << DESC_P_SHIFT;
|
||||
const DESC_AVL_SHIFT: u32 = 20;
|
||||
const DESC_AVL_MASK: u32 = 1 << DESC_AVL_SHIFT;
|
||||
const DESC_L_SHIFT: u32 = 21;
|
||||
const DESC_B_SHIFT: u32 = 22;
|
||||
const DESC_S_MASK: u32 = 1 << DESC_S_SHIFT;
|
||||
const DESC_G_SHIFT: u32 = 23;
|
||||
const DESC_G_MASK: u32 = 1 << DESC_G_SHIFT;
|
||||
|
||||
impl CpuSegment {
|
||||
pub fn new(reg: SegmentRegister) -> Self {
|
||||
let p_mask = if (reg.present > 0) && (reg.unusable == 0) {
|
||||
DESC_P_MASK
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let flags = ((reg.type_ as u32) << DESC_TYPE_SHIFT)
|
||||
| p_mask
|
||||
| ((reg.dpl as u32) << DESC_DPL_SHIFT)
|
||||
| ((reg.db as u32) << DESC_B_SHIFT)
|
||||
| ((reg.s as u32) * DESC_S_MASK)
|
||||
| ((reg.l as u32) << DESC_L_SHIFT)
|
||||
| ((reg.g as u32) * DESC_G_MASK)
|
||||
| ((reg.avl as u32) * DESC_AVL_MASK);
|
||||
|
||||
CpuSegment {
|
||||
selector: reg.selector as u32,
|
||||
limit: reg.limit as u32,
|
||||
flags,
|
||||
pad: 0,
|
||||
base: reg.base,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_table(reg: DTableRegister) -> Self {
|
||||
CpuSegment {
|
||||
selector: 0,
|
||||
limit: reg.limit as u32,
|
||||
flags: 0,
|
||||
pad: 0,
|
||||
base: reg.base,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Copy, Clone)]
|
||||
pub struct CpuState {
|
||||
pub version: u32,
|
||||
pub size: u32,
|
||||
/// rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp
|
||||
pub regs1: [u64; 8],
|
||||
/// r8, r9, r10, r11, r12, r13, r14, r15
|
||||
pub regs2: [u64; 8],
|
||||
pub rip: u64,
|
||||
pub rflags: u64,
|
||||
pub cs: CpuSegment,
|
||||
pub ds: CpuSegment,
|
||||
pub es: CpuSegment,
|
||||
pub fs: CpuSegment,
|
||||
pub gs: CpuSegment,
|
||||
pub ss: CpuSegment,
|
||||
pub ldt: CpuSegment,
|
||||
pub tr: CpuSegment,
|
||||
pub gdt: CpuSegment,
|
||||
pub idt: CpuSegment,
|
||||
pub cr: [u64; 5],
|
||||
pub kernel_gs_base: u64,
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for CpuState {}
|
||||
|
||||
pub enum NoteDescType {
|
||||
Elf = 0,
|
||||
Vmm = 1,
|
||||
ElfAndVmm = 2,
|
||||
}
|
||||
|
||||
// "CORE" or "QEMU"
|
||||
pub const COREDUMP_NAME_SIZE: u32 = 5;
|
||||
pub const NT_PRSTATUS: u32 = 1;
|
||||
|
||||
/// Core file.
|
||||
const ET_CORE: u16 = 4;
|
||||
/// 64-bit object.
|
||||
const ELFCLASS64: u8 = 2;
|
||||
/// Current ELF version.
|
||||
const EV_CURRENT: u8 = 1;
|
||||
/// AMD x86-64 architecture
|
||||
const EM_X86_64: u16 = 62;
|
||||
|
||||
pub trait Elf64Writable {
|
||||
fn write_header(
|
||||
&mut self,
|
||||
dump_state: &DumpState,
|
||||
) -> std::result::Result<(), GuestDebuggableError> {
|
||||
let e_ident = [
|
||||
elf::ELFMAG0 as u8, // magic
|
||||
elf::ELFMAG1,
|
||||
elf::ELFMAG2,
|
||||
elf::ELFMAG3,
|
||||
ELFCLASS64, // class
|
||||
elf::ELFDATA2LSB as u8, //data
|
||||
EV_CURRENT, // version
|
||||
0, // os_abi
|
||||
0, // abi_version
|
||||
0, // padding
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
];
|
||||
let elf64_ehdr_size = std::mem::size_of::<elf::Elf64_Ehdr>();
|
||||
let elf64_phdr_size = std::mem::size_of::<elf::Elf64_Phdr>();
|
||||
let mut elf64_ehdr = elf::Elf64_Ehdr {
|
||||
e_ident,
|
||||
e_type: ET_CORE,
|
||||
e_machine: EM_X86_64,
|
||||
e_version: EV_CURRENT as u32,
|
||||
e_entry: 0,
|
||||
e_phoff: elf64_ehdr_size as u64,
|
||||
e_shoff: 0,
|
||||
e_flags: 0,
|
||||
e_ehsize: 0,
|
||||
e_phentsize: elf64_phdr_size as u16,
|
||||
e_phnum: dump_state.elf_phdr_num,
|
||||
e_shentsize: 0,
|
||||
e_shnum: 0,
|
||||
e_shstrndx: 0,
|
||||
};
|
||||
elf64_ehdr.e_ehsize = std::mem::size_of_val(&elf64_ehdr) as u16;
|
||||
|
||||
let mut coredump_file = dump_state.file.as_ref().unwrap();
|
||||
let bytes: &[u8] = elf64_ehdr.as_slice();
|
||||
coredump_file
|
||||
.write(bytes)
|
||||
.map_err(GuestDebuggableError::CoredumpFile)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_note(
|
||||
&mut self,
|
||||
dump_state: &DumpState,
|
||||
) -> std::result::Result<(), GuestDebuggableError> {
|
||||
let begin = dump_state.mem_offset - dump_state.elf_note_size as u64;
|
||||
let elf64_phdr = elf::Elf64_Phdr {
|
||||
p_type: elf::PT_NOTE,
|
||||
p_flags: 0,
|
||||
p_offset: begin,
|
||||
p_vaddr: 0,
|
||||
p_paddr: 0,
|
||||
p_filesz: dump_state.elf_note_size as u64,
|
||||
p_memsz: dump_state.elf_note_size as u64,
|
||||
p_align: 0,
|
||||
};
|
||||
|
||||
let mut coredump_file = dump_state.file.as_ref().unwrap();
|
||||
let bytes: &[u8] = elf64_phdr.as_slice();
|
||||
coredump_file
|
||||
.write(bytes)
|
||||
.map_err(GuestDebuggableError::CoredumpFile)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_load(
|
||||
&mut self,
|
||||
offset: u64,
|
||||
phys_addr: u64,
|
||||
length: u64,
|
||||
virt_addr: u64,
|
||||
dump_state: &DumpState,
|
||||
) -> std::result::Result<(), GuestDebuggableError> {
|
||||
let elf64_load = elf::Elf64_Phdr {
|
||||
p_type: elf::PT_LOAD,
|
||||
p_flags: 0,
|
||||
p_offset: offset,
|
||||
p_vaddr: virt_addr,
|
||||
p_paddr: phys_addr,
|
||||
p_filesz: length,
|
||||
p_memsz: length,
|
||||
p_align: 0,
|
||||
};
|
||||
|
||||
let mut coredump_file = dump_state.file.as_ref().unwrap();
|
||||
let bytes: &[u8] = elf64_load.as_slice();
|
||||
coredump_file
|
||||
.write(bytes)
|
||||
.map_err(GuestDebuggableError::CoredumpFile)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_loads(
|
||||
&mut self,
|
||||
dump_state: &DumpState,
|
||||
) -> std::result::Result<(), GuestDebuggableError> {
|
||||
let mem_info = dump_state.mem_info.as_ref().unwrap();
|
||||
|
||||
for (gpa, load) in &mem_info.ram_maps {
|
||||
self.write_load(load.mem_offset_in_elf, *gpa, load.mem_size, 0, dump_state)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn elf_note_size(&self, hdr_size: u32, name_size: u32, desc_size: u32) -> u32 {
|
||||
(div_round_up!(hdr_size, 4) + div_round_up!(name_size, 4) + div_round_up!(desc_size, 4)) * 4
|
||||
}
|
||||
|
||||
fn get_note_size(&self, desc_type: NoteDescType, nr_cpus: u32) -> u32 {
|
||||
let note_head_size = std::mem::size_of::<elf::Elf64_Nhdr>() as u32;
|
||||
let elf_desc_size = std::mem::size_of::<X86_64ElfPrStatus>() as u32;
|
||||
let cpu_state_desc_size = std::mem::size_of::<CpuState>() as u32;
|
||||
|
||||
let elf_note_size = self.elf_note_size(note_head_size, COREDUMP_NAME_SIZE, elf_desc_size);
|
||||
let vmm_note_size =
|
||||
self.elf_note_size(note_head_size, COREDUMP_NAME_SIZE, cpu_state_desc_size);
|
||||
|
||||
match desc_type {
|
||||
NoteDescType::Elf => elf_note_size * nr_cpus,
|
||||
NoteDescType::Vmm => vmm_note_size * nr_cpus,
|
||||
NoteDescType::ElfAndVmm => (elf_note_size + vmm_note_size) * nr_cpus,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CpuElf64Writable {
|
||||
fn cpu_write_elf64_note(
|
||||
&mut self,
|
||||
_dump_state: &DumpState,
|
||||
) -> std::result::Result<(), GuestDebuggableError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cpu_write_vmm_note(
|
||||
&mut self,
|
||||
_dump_state: &DumpState,
|
||||
) -> std::result::Result<(), GuestDebuggableError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
288
vmm/src/cpu.rs
288
vmm/src/cpu.rs
@@ -12,6 +12,12 @@
|
||||
//
|
||||
|
||||
use crate::config::CpusConfig;
|
||||
#[cfg(feature = "guest_debug")]
|
||||
use crate::coredump::{
|
||||
CpuElf64Writable, CpuSegment, CpuState as DumpCpusState, DumpState, Elf64Writable,
|
||||
GuestDebuggableError, NoteDescType, X86_64ElfPrStatus, X86_64UserRegs, COREDUMP_NAME_SIZE,
|
||||
NT_PRSTATUS,
|
||||
};
|
||||
use crate::device_manager::DeviceManager;
|
||||
#[cfg(feature = "gdb")]
|
||||
use crate::gdb::{get_raw_tid, Debuggable, DebuggableError};
|
||||
@@ -28,24 +34,36 @@ use arch::NumaNodes;
|
||||
use devices::interrupt_controller::InterruptController;
|
||||
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
||||
use gdbstub_arch::x86::reg::{X86SegmentRegs, X86_64CoreRegs};
|
||||
#[cfg(feature = "guest_debug")]
|
||||
use hypervisor::arch::x86::msr_index;
|
||||
#[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(feature = "guest_debug")]
|
||||
use hypervisor::x86_64::{MsrEntries, MsrEntry};
|
||||
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
||||
use hypervisor::x86_64::{SpecialRegisters, StandardRegisters};
|
||||
use hypervisor::{CpuState, HypervisorCpuError, VmExit, VmOps};
|
||||
use libc::{c_void, siginfo_t};
|
||||
#[cfg(feature = "guest_debug")]
|
||||
use linux_loader::elf::Elf64_Nhdr;
|
||||
use seccompiler::{apply_filter, SeccompAction};
|
||||
use std::collections::BTreeMap;
|
||||
#[cfg(feature = "guest_debug")]
|
||||
use std::io::Write;
|
||||
#[cfg(feature = "guest_debug")]
|
||||
use std::mem::size_of;
|
||||
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 = "guest_debug")]
|
||||
use vm_memory::ByteValued;
|
||||
#[cfg(feature = "gdb")]
|
||||
use vm_memory::{Bytes, GuestAddressSpace};
|
||||
use vm_memory::{GuestAddress, GuestMemoryAtomic};
|
||||
@@ -238,6 +256,13 @@ struct InterruptSourceOverride {
|
||||
pub flags: u16,
|
||||
}
|
||||
|
||||
#[cfg(feature = "guest_debug")]
|
||||
macro_rules! round_up {
|
||||
($n:expr,$d:expr) => {
|
||||
(($n / ($d + 1)) + 1) * $d
|
||||
};
|
||||
}
|
||||
|
||||
/// A wrapper around creating and using a kvm-based VCPU.
|
||||
pub struct Vcpu {
|
||||
// The hypervisor abstracted CPU.
|
||||
@@ -350,43 +375,38 @@ impl Vcpu {
|
||||
}
|
||||
|
||||
const VCPU_SNAPSHOT_ID: &str = "vcpu";
|
||||
impl Pausable for Vcpu {
|
||||
fn pause(&mut self) -> std::result::Result<(), MigratableError> {
|
||||
self.saved_state =
|
||||
Some(self.vcpu.state().map_err(|e| {
|
||||
MigratableError::Pause(anyhow!("Could not get vCPU state {:?}", e))
|
||||
})?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resume(&mut self) -> std::result::Result<(), MigratableError> {
|
||||
if let Some(vcpu_state) = &self.saved_state {
|
||||
self.vcpu.set_state(vcpu_state).map_err(|e| {
|
||||
MigratableError::Pause(anyhow!("Could not set the vCPU state {:?}", e))
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Pausable for Vcpu {}
|
||||
impl Snapshottable for Vcpu {
|
||||
fn id(&self) -> String {
|
||||
VCPU_SNAPSHOT_ID.to_string()
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
let mut vcpu_snapshot = Snapshot::new(&format!("{}", self.id));
|
||||
let saved_state = self
|
||||
.vcpu
|
||||
.state()
|
||||
.map_err(|e| MigratableError::Pause(anyhow!("Could not get vCPU state {:?}", e)))?;
|
||||
|
||||
let mut vcpu_snapshot = Snapshot::new(&format!("{:03}", self.id));
|
||||
vcpu_snapshot.add_data_section(SnapshotDataSection::new_from_state(
|
||||
VCPU_SNAPSHOT_ID,
|
||||
&self.saved_state,
|
||||
&saved_state,
|
||||
)?);
|
||||
|
||||
self.saved_state = Some(saved_state);
|
||||
|
||||
Ok(vcpu_snapshot)
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.saved_state = Some(snapshot.to_state(VCPU_SNAPSHOT_ID)?);
|
||||
let saved_state: CpuState = snapshot.to_state(VCPU_SNAPSHOT_ID)?;
|
||||
|
||||
self.vcpu
|
||||
.set_state(&saved_state)
|
||||
.map_err(|e| MigratableError::Pause(anyhow!("Could not set the vCPU state {:?}", e)))?;
|
||||
|
||||
self.saved_state = Some(saved_state);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1251,7 +1271,7 @@ impl CpuManager {
|
||||
1 << MADT_CPU_ENABLE_FLAG
|
||||
} else {
|
||||
0
|
||||
},
|
||||
} | 1 << MADT_CPU_ONLINE_CAPABLE_FLAG,
|
||||
};
|
||||
madt.append(lapic);
|
||||
}
|
||||
@@ -1504,6 +1524,9 @@ struct Cpu {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const MADT_CPU_ENABLE_FLAG: usize = 0;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const MADT_CPU_ONLINE_CAPABLE_FLAG: usize = 1;
|
||||
|
||||
impl Cpu {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn generate_mat(&self) -> Vec<u8> {
|
||||
@@ -2097,6 +2120,217 @@ impl Debuggable for CpuManager {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "guest_debug")]
|
||||
impl Elf64Writable for CpuManager {}
|
||||
|
||||
#[cfg(feature = "guest_debug")]
|
||||
impl CpuElf64Writable for CpuManager {
|
||||
fn cpu_write_elf64_note(
|
||||
&mut self,
|
||||
dump_state: &DumpState,
|
||||
) -> std::result::Result<(), GuestDebuggableError> {
|
||||
let mut coredump_file = dump_state.file.as_ref().unwrap();
|
||||
for vcpu in &self.vcpus {
|
||||
let note_size = self.get_note_size(NoteDescType::Elf, 1);
|
||||
let mut pos: usize = 0;
|
||||
let mut buf = vec![0; note_size as usize];
|
||||
let descsz = size_of::<X86_64ElfPrStatus>();
|
||||
let vcpu_id = vcpu.lock().unwrap().id;
|
||||
|
||||
let note = Elf64_Nhdr {
|
||||
n_namesz: COREDUMP_NAME_SIZE,
|
||||
n_descsz: descsz as u32,
|
||||
n_type: NT_PRSTATUS,
|
||||
};
|
||||
|
||||
let bytes: &[u8] = note.as_slice();
|
||||
buf.splice(0.., bytes.to_vec());
|
||||
pos += round_up!(size_of::<Elf64_Nhdr>(), 4);
|
||||
buf.resize(pos + 4, 0);
|
||||
buf.splice(pos.., "CORE".to_string().into_bytes());
|
||||
|
||||
pos += round_up!(COREDUMP_NAME_SIZE as usize, 4);
|
||||
buf.resize(pos + 32 + 4, 0);
|
||||
let pid = vcpu_id as u64;
|
||||
let bytes: &[u8] = pid.as_slice();
|
||||
buf.splice(pos + 32.., bytes.to_vec()); /* pr_pid */
|
||||
|
||||
pos += descsz - size_of::<X86_64UserRegs>() - size_of::<u64>();
|
||||
|
||||
let orig_rax: u64 = 0;
|
||||
let gregs = self.vcpus[usize::from(vcpu_id)]
|
||||
.lock()
|
||||
.unwrap()
|
||||
.vcpu
|
||||
.get_regs()
|
||||
.map_err(|_e| GuestDebuggableError::Coredump(anyhow!("get regs failed")))?;
|
||||
|
||||
let regs1 = [
|
||||
gregs.r15, gregs.r14, gregs.r13, gregs.r12, gregs.rbp, gregs.rbx, gregs.r11,
|
||||
gregs.r10,
|
||||
];
|
||||
let regs2 = [
|
||||
gregs.r9, gregs.r8, gregs.rax, gregs.rcx, gregs.rdx, gregs.rsi, gregs.rdi, orig_rax,
|
||||
];
|
||||
|
||||
let sregs = self.vcpus[usize::from(vcpu_id)]
|
||||
.lock()
|
||||
.unwrap()
|
||||
.vcpu
|
||||
.get_sregs()
|
||||
.map_err(|_e| GuestDebuggableError::Coredump(anyhow!("get sregs failed")))?;
|
||||
|
||||
debug!(
|
||||
"rip 0x{:x} rsp 0x{:x} gs 0x{:x} cs 0x{:x} ss 0x{:x} ds 0x{:x}",
|
||||
gregs.rip,
|
||||
gregs.rsp,
|
||||
sregs.gs.base,
|
||||
sregs.cs.selector,
|
||||
sregs.ss.selector,
|
||||
sregs.ds.selector,
|
||||
);
|
||||
|
||||
let regs = X86_64UserRegs {
|
||||
regs1,
|
||||
regs2,
|
||||
rip: gregs.rip,
|
||||
cs: sregs.cs.selector as u64,
|
||||
eflags: gregs.rflags,
|
||||
rsp: gregs.rsp,
|
||||
ss: sregs.ss.selector as u64,
|
||||
fs_base: sregs.fs.base as u64,
|
||||
gs_base: sregs.gs.base as u64,
|
||||
ds: sregs.ds.selector as u64,
|
||||
es: sregs.es.selector as u64,
|
||||
fs: sregs.fs.selector as u64,
|
||||
gs: sregs.gs.selector as u64,
|
||||
};
|
||||
|
||||
// let bytes: &[u8] = unsafe { any_as_u8_slice(®s) };
|
||||
let bytes: &[u8] = regs.as_slice();
|
||||
buf.resize(note_size as usize, 0);
|
||||
buf.splice(pos.., bytes.to_vec());
|
||||
buf.resize(note_size as usize, 0);
|
||||
|
||||
coredump_file
|
||||
.write(&buf)
|
||||
.map_err(GuestDebuggableError::CoredumpFile)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cpu_write_vmm_note(
|
||||
&mut self,
|
||||
dump_state: &DumpState,
|
||||
) -> std::result::Result<(), GuestDebuggableError> {
|
||||
let mut coredump_file = dump_state.file.as_ref().unwrap();
|
||||
for vcpu in &self.vcpus {
|
||||
let note_size = self.get_note_size(NoteDescType::Vmm, 1);
|
||||
let mut pos: usize = 0;
|
||||
let mut buf = vec![0; note_size as usize];
|
||||
let descsz = size_of::<DumpCpusState>();
|
||||
let vcpu_id = vcpu.lock().unwrap().id;
|
||||
|
||||
let note = Elf64_Nhdr {
|
||||
n_namesz: COREDUMP_NAME_SIZE,
|
||||
n_descsz: descsz as u32,
|
||||
n_type: 0,
|
||||
};
|
||||
|
||||
let bytes: &[u8] = note.as_slice();
|
||||
buf.splice(0.., bytes.to_vec());
|
||||
pos += round_up!(size_of::<Elf64_Nhdr>(), 4);
|
||||
|
||||
buf.resize(pos + 4, 0);
|
||||
buf.splice(pos.., "QEMU".to_string().into_bytes());
|
||||
|
||||
pos += round_up!(COREDUMP_NAME_SIZE as usize, 4);
|
||||
|
||||
let gregs = self.vcpus[usize::from(vcpu_id)]
|
||||
.lock()
|
||||
.unwrap()
|
||||
.vcpu
|
||||
.get_regs()
|
||||
.map_err(|_e| GuestDebuggableError::Coredump(anyhow!("get regs failed")))?;
|
||||
|
||||
let regs1 = [
|
||||
gregs.rax, gregs.rbx, gregs.rcx, gregs.rdx, gregs.rsi, gregs.rdi, gregs.rsp,
|
||||
gregs.rbp,
|
||||
];
|
||||
|
||||
let regs2 = [
|
||||
gregs.r8, gregs.r9, gregs.r10, gregs.r11, gregs.r12, gregs.r13, gregs.r14,
|
||||
gregs.r15,
|
||||
];
|
||||
|
||||
let sregs = self.vcpus[usize::from(vcpu_id)]
|
||||
.lock()
|
||||
.unwrap()
|
||||
.vcpu
|
||||
.get_sregs()
|
||||
.map_err(|_e| GuestDebuggableError::Coredump(anyhow!("get sregs failed")))?;
|
||||
|
||||
let mut msrs = MsrEntries::from_entries(&[MsrEntry {
|
||||
index: msr_index::MSR_KERNEL_GS_BASE,
|
||||
..Default::default()
|
||||
}])
|
||||
.map_err(|_e| GuestDebuggableError::Coredump(anyhow!("get msr failed")))?;
|
||||
|
||||
self.vcpus[vcpu_id as usize]
|
||||
.lock()
|
||||
.unwrap()
|
||||
.vcpu
|
||||
.get_msrs(&mut msrs)
|
||||
.map_err(|_e| GuestDebuggableError::Coredump(anyhow!("get msr failed")))?;
|
||||
let kernel_gs_base = msrs.as_slice()[0].data;
|
||||
|
||||
let cs = CpuSegment::new(sregs.cs);
|
||||
let ds = CpuSegment::new(sregs.ds);
|
||||
let es = CpuSegment::new(sregs.es);
|
||||
let fs = CpuSegment::new(sregs.fs);
|
||||
let gs = CpuSegment::new(sregs.gs);
|
||||
let ss = CpuSegment::new(sregs.ss);
|
||||
let ldt = CpuSegment::new(sregs.ldt);
|
||||
let tr = CpuSegment::new(sregs.tr);
|
||||
let gdt = CpuSegment::new_from_table(sregs.gdt);
|
||||
let idt = CpuSegment::new_from_table(sregs.idt);
|
||||
let cr = [sregs.cr0, sregs.cr8, sregs.cr2, sregs.cr3, sregs.cr4];
|
||||
let regs = DumpCpusState {
|
||||
version: 1,
|
||||
size: size_of::<DumpCpusState>() as u32,
|
||||
regs1,
|
||||
regs2,
|
||||
rip: gregs.rip,
|
||||
rflags: gregs.rflags,
|
||||
cs,
|
||||
ds,
|
||||
es,
|
||||
fs,
|
||||
gs,
|
||||
ss,
|
||||
ldt,
|
||||
tr,
|
||||
gdt,
|
||||
idt,
|
||||
cr,
|
||||
kernel_gs_base,
|
||||
};
|
||||
|
||||
let bytes: &[u8] = regs.as_slice();
|
||||
buf.resize(note_size as usize, 0);
|
||||
buf.splice(pos.., bytes.to_vec());
|
||||
buf.resize(note_size as usize, 0);
|
||||
|
||||
coredump_file
|
||||
.write(&buf)
|
||||
.map_err(GuestDebuggableError::CoredumpFile)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@@ -2205,7 +2439,7 @@ mod tests {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use arch::aarch64::regs::*;
|
||||
use arch::layout;
|
||||
use hypervisor::kvm::aarch64::{is_system_register, MPIDR_EL1};
|
||||
use hypervisor::kvm::kvm_bindings::{
|
||||
kvm_one_reg, kvm_regs, kvm_vcpu_init, user_pt_regs, KVM_REG_ARM64, KVM_REG_ARM64_SYSREG,
|
||||
@@ -2220,7 +2454,7 @@ mod tests {
|
||||
let vm = hv.create_vm().unwrap();
|
||||
let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||
|
||||
let res = setup_regs(&vcpu, 0, 0x0);
|
||||
let res = vcpu.setup_regs(0, 0x0, layout::FDT_START.0);
|
||||
// Must fail when vcpu is not initialized yet.
|
||||
assert!(res.is_err());
|
||||
|
||||
@@ -2228,7 +2462,7 @@ mod tests {
|
||||
vm.get_preferred_target(&mut kvi).unwrap();
|
||||
vcpu.vcpu_init(&kvi).unwrap();
|
||||
|
||||
assert!(setup_regs(&vcpu, 0, 0x0).is_ok());
|
||||
assert!(vcpu.setup_regs(0, 0x0, layout::FDT_START.0).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -16,8 +16,7 @@ use crate::config::{
|
||||
use crate::device_tree::{DeviceNode, DeviceTree};
|
||||
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::memory_manager::{Error as MemoryManagerError, MemoryManager, MEMORY_MANAGER_ACPI_SIZE};
|
||||
use crate::pci_segment::PciSegment;
|
||||
use crate::seccomp_filters::{get_seccomp_filter, Thread};
|
||||
use crate::serial_manager::{Error as SerialManagerError, SerialManager};
|
||||
@@ -29,8 +28,6 @@ use crate::PciDeviceInfo;
|
||||
use crate::{device_node, DEVICE_MANAGER_SNAPSHOT_ID};
|
||||
use acpi_tables::{aml, aml::Aml};
|
||||
use anyhow::anyhow;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use arch::aarch64::gic::gicv3_its::kvm::KvmGicV3Its;
|
||||
use arch::layout;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use arch::layout::{APIC_START, IOAPIC_SIZE, IOAPIC_START};
|
||||
@@ -79,12 +76,13 @@ 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;
|
||||
use virtio_devices::transport::{VirtioPciDevice, VirtioPciDeviceActivator};
|
||||
use virtio_devices::vhost_user::VhostUserConfig;
|
||||
use virtio_devices::{AccessPlatformMapping, VdpaDmaMapping, VirtioMemMappingSource};
|
||||
use virtio_devices::{
|
||||
AccessPlatformMapping, ActivateError, VdpaDmaMapping, VirtioMemMappingSource,
|
||||
};
|
||||
use virtio_devices::{Endpoint, IommuMapping};
|
||||
use virtio_devices::{VirtioSharedMemory, VirtioSharedMemoryList};
|
||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||
use vm_device::dma_mapping::vfio::VfioDmaMapping;
|
||||
use vm_device::dma_mapping::ExternalDmaMapping;
|
||||
@@ -359,9 +357,6 @@ pub enum DeviceManagerError {
|
||||
/// Could not give the PCI device ID back.
|
||||
PutPciDeviceId(pci::PciRootError),
|
||||
|
||||
/// Incorrect device ID as it is already used by another device.
|
||||
DeviceIdAlreadyInUse,
|
||||
|
||||
/// No disk path was specified when one was expected
|
||||
NoDiskPath,
|
||||
|
||||
@@ -476,6 +471,9 @@ pub enum DeviceManagerError {
|
||||
|
||||
/// Invalid identifier
|
||||
InvalidIdentifier(String),
|
||||
|
||||
/// Error activating virtio device
|
||||
VirtioActivate(ActivateError),
|
||||
}
|
||||
pub type DeviceManagerResult<T> = result::Result<T, DeviceManagerError>;
|
||||
|
||||
@@ -939,6 +937,9 @@ pub struct DeviceManager {
|
||||
|
||||
// Start time of the VM
|
||||
timestamp: Instant,
|
||||
|
||||
// Pending activations
|
||||
pending_activations: Arc<Mutex<Vec<VirtioPciDeviceActivator>>>,
|
||||
}
|
||||
|
||||
impl DeviceManager {
|
||||
@@ -1080,6 +1081,7 @@ impl DeviceManager {
|
||||
io_uring_supported: None,
|
||||
boot_id_list,
|
||||
timestamp,
|
||||
pending_activations: Arc::new(Mutex::new(Vec::default())),
|
||||
};
|
||||
|
||||
let device_manager = Arc::new(Mutex::new(device_manager));
|
||||
@@ -2394,109 +2396,7 @@ impl DeviceManager {
|
||||
|
||||
let mut node = device_node!(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 cache_range = if let Some(node) = self.device_tree.lock().unwrap().get(&id) {
|
||||
info!("Restoring virtio-fs {} resources", id);
|
||||
|
||||
let mut cache_range: Option<(u64, u64)> = None;
|
||||
for resource in node.resources.iter() {
|
||||
match resource {
|
||||
Resource::MmioAddressRange { base, size } => {
|
||||
if cache_range.is_some() {
|
||||
return Err(DeviceManagerError::ResourceAlreadyExists);
|
||||
}
|
||||
|
||||
cache_range = Some((*base, *size));
|
||||
}
|
||||
_ => {
|
||||
error!("Unexpected resource {:?} for {}", resource, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cache_range
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// DAX is not supported, we override the config by disabling the option.
|
||||
fs_cfg.dax = false;
|
||||
|
||||
if let Some(fs_socket) = fs_cfg.socket.to_str() {
|
||||
let cache = if fs_cfg.dax {
|
||||
let (cache_base, cache_size) = if let Some((base, size)) = cache_range {
|
||||
// The memory needs to be 2MiB aligned in order to support
|
||||
// hugepages.
|
||||
self.pci_segments[fs_cfg.pci_segment as usize]
|
||||
.allocator
|
||||
.lock()
|
||||
.unwrap()
|
||||
.allocate(
|
||||
Some(GuestAddress(base)),
|
||||
size as GuestUsize,
|
||||
Some(0x0020_0000),
|
||||
)
|
||||
.ok_or(DeviceManagerError::FsRangeAllocation)?;
|
||||
|
||||
(base, size)
|
||||
} else {
|
||||
let size = fs_cfg.cache_size;
|
||||
// The memory needs to be 2MiB aligned in order to support
|
||||
// hugepages.
|
||||
let base = self.pci_segments[fs_cfg.pci_segment as usize]
|
||||
.allocator
|
||||
.lock()
|
||||
.unwrap()
|
||||
.allocate(None, size as GuestUsize, Some(0x0020_0000))
|
||||
.ok_or(DeviceManagerError::FsRangeAllocation)?;
|
||||
|
||||
(base.raw_value(), size)
|
||||
};
|
||||
|
||||
// Update the node with correct resource information.
|
||||
node.resources.push(Resource::MmioAddressRange {
|
||||
base: cache_base,
|
||||
size: cache_size,
|
||||
});
|
||||
|
||||
let mmap_region = MmapRegion::build(
|
||||
None,
|
||||
cache_size as usize,
|
||||
libc::PROT_NONE,
|
||||
libc::MAP_ANONYMOUS | libc::MAP_PRIVATE,
|
||||
)
|
||||
.map_err(DeviceManagerError::NewMmapRegion)?;
|
||||
let host_addr: u64 = mmap_region.as_ptr() as u64;
|
||||
|
||||
let mem_slot = self
|
||||
.memory_manager
|
||||
.lock()
|
||||
.unwrap()
|
||||
.create_userspace_mapping(
|
||||
cache_base, cache_size, host_addr, false, false, false,
|
||||
)
|
||||
.map_err(DeviceManagerError::MemoryManager)?;
|
||||
|
||||
let region_list = vec![VirtioSharedMemory {
|
||||
offset: 0,
|
||||
len: cache_size,
|
||||
}];
|
||||
|
||||
Some((
|
||||
VirtioSharedMemoryList {
|
||||
host_addr,
|
||||
mem_slot,
|
||||
addr: GuestAddress(cache_base),
|
||||
len: cache_size as GuestUsize,
|
||||
region_list,
|
||||
},
|
||||
mmap_region,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let virtio_fs_device = Arc::new(Mutex::new(
|
||||
virtio_devices::vhost_user::Fs::new(
|
||||
id.clone(),
|
||||
@@ -2504,7 +2404,7 @@ impl DeviceManager {
|
||||
&fs_cfg.tag,
|
||||
fs_cfg.num_queues,
|
||||
fs_cfg.queue_size,
|
||||
cache,
|
||||
None,
|
||||
self.seccomp_action.clone(),
|
||||
self.restoring,
|
||||
self.exit_evt
|
||||
@@ -3074,10 +2974,6 @@ impl DeviceManager {
|
||||
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)?;
|
||||
@@ -3177,6 +3073,8 @@ impl DeviceManager {
|
||||
None
|
||||
};
|
||||
|
||||
let memory_manager = self.memory_manager.clone();
|
||||
|
||||
let vfio_pci_device = VfioPciDevice::new(
|
||||
vfio_name.clone(),
|
||||
&self.address_manager.vm,
|
||||
@@ -3186,6 +3084,8 @@ impl DeviceManager {
|
||||
legacy_interrupt_group,
|
||||
device_cfg.iommu,
|
||||
pci_device_bdf,
|
||||
self.restoring,
|
||||
Arc::new(move || memory_manager.lock().unwrap().allocate_memory_slot()),
|
||||
)
|
||||
.map_err(DeviceManagerError::VfioPciCreate)?;
|
||||
|
||||
@@ -3199,15 +3099,17 @@ impl DeviceManager {
|
||||
resources,
|
||||
)?;
|
||||
|
||||
vfio_pci_device
|
||||
.lock()
|
||||
.unwrap()
|
||||
.map_mmio_regions(&self.address_manager.vm, || {
|
||||
self.memory_manager.lock().unwrap().allocate_memory_slot()
|
||||
})
|
||||
.map_err(DeviceManagerError::VfioMapRegion)?;
|
||||
// When restoring a VM, the restore codepath will take care of mapping
|
||||
// the MMIO regions based on the information from the snapshot.
|
||||
if !self.restoring {
|
||||
vfio_pci_device
|
||||
.lock()
|
||||
.unwrap()
|
||||
.map_mmio_regions()
|
||||
.map_err(DeviceManagerError::VfioMapRegion)?;
|
||||
}
|
||||
|
||||
let mut node = device_node!(vfio_name);
|
||||
let mut node = device_node!(vfio_name, vfio_pci_device);
|
||||
|
||||
// Update the device tree with correct resource information.
|
||||
node.resources = new_resources;
|
||||
@@ -3302,10 +3204,6 @@ impl DeviceManager {
|
||||
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)?;
|
||||
@@ -3336,6 +3234,8 @@ impl DeviceManager {
|
||||
.map_err(DeviceManagerError::VfioUserCreateClient)?,
|
||||
));
|
||||
|
||||
let memory_manager = self.memory_manager.clone();
|
||||
|
||||
let mut vfio_user_pci_device = VfioUserPciDevice::new(
|
||||
vfio_user_name.clone(),
|
||||
&self.address_manager.vm,
|
||||
@@ -3343,6 +3243,8 @@ impl DeviceManager {
|
||||
self.msi_interrupt_manager.clone(),
|
||||
legacy_interrupt_group,
|
||||
pci_device_bdf,
|
||||
self.restoring,
|
||||
Arc::new(move || memory_manager.lock().unwrap().allocate_memory_slot()),
|
||||
)
|
||||
.map_err(DeviceManagerError::VfioUserCreate)?;
|
||||
|
||||
@@ -3377,17 +3279,19 @@ impl DeviceManager {
|
||||
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)?;
|
||||
// When restoring a VM, the restore codepath will take care of mapping
|
||||
// the MMIO regions based on the information from the snapshot.
|
||||
if !self.restoring {
|
||||
// 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()
|
||||
.map_err(DeviceManagerError::VfioUserMapRegion)?;
|
||||
}
|
||||
|
||||
let mut node = device_node!(vfio_user_name);
|
||||
let mut node = device_node!(vfio_user_name, vfio_user_pci_device);
|
||||
|
||||
// Update the device tree with correct resource information.
|
||||
node.resources = new_resources;
|
||||
@@ -3520,6 +3424,7 @@ impl DeviceManager {
|
||||
// The exception being if not on the default PCI segment.
|
||||
pci_segment_id > 0 || device_type != VirtioDeviceType::Block as u32,
|
||||
dma_handler,
|
||||
self.pending_activations.clone(),
|
||||
)
|
||||
.map_err(DeviceManagerError::VirtioDevice)?,
|
||||
));
|
||||
@@ -3674,17 +3579,10 @@ impl DeviceManager {
|
||||
}
|
||||
|
||||
pub fn activate_virtio_devices(&self) -> DeviceManagerResult<()> {
|
||||
// Find virtio pci devices and activate any pending ones
|
||||
let device_tree = self.device_tree.lock().unwrap();
|
||||
for pci_device_node in device_tree.pci_devices() {
|
||||
#[allow(irrefutable_let_patterns)]
|
||||
if let PciDeviceHandle::Virtio(virtio_pci_device) = &pci_device_node
|
||||
.pci_device_handle
|
||||
.as_ref()
|
||||
.ok_or(DeviceManagerError::MissingPciDevice)?
|
||||
{
|
||||
virtio_pci_device.lock().unwrap().maybe_activate();
|
||||
}
|
||||
for mut activator in self.pending_activations.lock().unwrap().drain(..) {
|
||||
activator
|
||||
.activate()
|
||||
.map_err(DeviceManagerError::VirtioActivate)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -4427,26 +4325,11 @@ impl Pausable for DeviceManager {
|
||||
// and ITS tables to guest RAM.
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
{
|
||||
let gic_device = Arc::clone(
|
||||
self.get_interrupt_controller()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get_gic_device()
|
||||
.unwrap(),
|
||||
);
|
||||
if let Some(gicv3_its) = gic_device
|
||||
self.get_interrupt_controller()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.as_any_concrete_mut()
|
||||
.downcast_mut::<KvmGicV3Its>()
|
||||
{
|
||||
gicv3_its.pause()?;
|
||||
} else {
|
||||
return Err(MigratableError::Pause(anyhow!(
|
||||
"GicDevice downcast to KvmGicV3Its failed when pausing device manager!"
|
||||
)));
|
||||
};
|
||||
.pause()?;
|
||||
};
|
||||
|
||||
Ok(())
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user