mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
242 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f58b6f8417 | ||
|
|
71ae9cfe06 | ||
|
|
f0b7a09a49 | ||
|
|
bc84a1c79b | ||
|
|
9c5be6f660 | ||
|
|
8e9a5a0dbb | ||
|
|
d561b45552 | ||
|
|
9353856426 | ||
|
|
9af477e964 | ||
|
|
27515a6ec4 | ||
|
|
169a6bb83b | ||
|
|
19167e7647 | ||
|
|
2cdc3e3546 | ||
|
|
de9471fc72 | ||
|
|
b5450ca72c | ||
|
|
8008aad545 | ||
|
|
3bd47ffdc1 | ||
|
|
acfbee5b7a | ||
|
|
62c6efb2e5 | ||
|
|
aec0d309ee | ||
|
|
5fc12862e6 | ||
|
|
f4159ca016 | ||
|
|
6d63018d9f | ||
|
|
073e0f979b | ||
|
|
50a995b63d | ||
|
|
ccdea0274c | ||
|
|
688ead51c6 | ||
|
|
9792c9aafa | ||
|
|
48963e322a | ||
|
|
a59fbf0e37 | ||
|
|
a6b839b35c | ||
|
|
aa3d5cfbfe | ||
|
|
ddd0552c83 | ||
|
|
1fc8c9165a | ||
|
|
47f47ad53f | ||
|
|
69d1186ccb | ||
|
|
d0601a1473 | ||
|
|
b91b829a28 | ||
|
|
7928a697dc | ||
|
|
b5fb744828 | ||
|
|
29607f38ad | ||
|
|
9c3a73706f | ||
|
|
024675d1d8 | ||
|
|
2c913b8130 | ||
|
|
c397c9c95e | ||
|
|
54f814f34a | ||
|
|
ad8adcb955 | ||
|
|
f24094392e | ||
|
|
eee218f1f5 | ||
|
|
5ada3f59c8 | ||
|
|
89008a49cf | ||
|
|
295f4df7bf | ||
|
|
769194cac8 | ||
|
|
13a3ac6a77 | ||
|
|
b26777cb01 | ||
|
|
24c8cce012 | ||
|
|
c6854c5a97 | ||
|
|
b2e5dbaecb | ||
|
|
5bbf2dca80 | ||
|
|
1df952726a | ||
|
|
9ea087597f | ||
|
|
952f9bd3fe | ||
|
|
c29caf2a85 | ||
|
|
5fbeacabad | ||
|
|
f7502057d9 | ||
|
|
ff54909d2c | ||
|
|
6664e5a6e7 | ||
|
|
5209026f52 | ||
|
|
5db9b0ec99 | ||
|
|
c4fb0f36bd | ||
|
|
9cdcbb5121 | ||
|
|
a194dad98c | ||
|
|
b339aa6b88 | ||
|
|
276c9a10fd | ||
|
|
36360c7630 | ||
|
|
b959849149 | ||
|
|
50a89c6808 | ||
|
|
192d69d601 | ||
|
|
b8342ebeb4 | ||
|
|
58009af85d | ||
|
|
0655f25da4 | ||
|
|
2547f144cd | ||
|
|
ec7700544c | ||
|
|
0c60fa8268 | ||
|
|
76e15a4240 | ||
|
|
7659055eec | ||
|
|
5209bd6120 | ||
|
|
c2acea5156 | ||
|
|
fd9972ecc5 | ||
|
|
ca4ab99f42 | ||
|
|
977d824cf8 | ||
|
|
38fa630ce7 | ||
|
|
a59b361058 | ||
|
|
fa5b5fd602 | ||
|
|
7765398045 | ||
|
|
9282ad82b0 | ||
|
|
a09feecc36 | ||
|
|
060df69f07 | ||
|
|
f6c8e4b045 | ||
|
|
2824642e80 | ||
|
|
41cfdb50cd | ||
|
|
12e20effd7 | ||
|
|
9fc86a91e2 | ||
|
|
da8ce25abf | ||
|
|
23e3b022eb | ||
|
|
30033bdaea | ||
|
|
d578c408b7 | ||
|
|
55a3a38e14 | ||
|
|
6006068951 | ||
|
|
28ab6cea0e | ||
|
|
4ebeeb1310 | ||
|
|
77f6cd5ef4 | ||
|
|
f2c2a44ca6 | ||
|
|
8d418e1fca | ||
|
|
c90f77e399 | ||
|
|
a105089702 | ||
|
|
c366efc19e | ||
|
|
c9b68a3be6 | ||
|
|
72b736153e | ||
|
|
79a2afe44e | ||
|
|
7d299f1055 | ||
|
|
23f9ec50fb | ||
|
|
c99d3f70ac | ||
|
|
33d339c406 | ||
|
|
22649c4a87 | ||
|
|
ef363d79fb | ||
|
|
92f3f86669 | ||
|
|
f6d6495b93 | ||
|
|
68d90cbe65 | ||
|
|
d0b59ca3a0 | ||
|
|
981bb72a09 | ||
|
|
843a010163 | ||
|
|
c381b36b87 | ||
|
|
ff4d378f1b | ||
|
|
74a4aba257 | ||
|
|
7b376fa8e0 | ||
|
|
23afe89089 | ||
|
|
3b43551d98 | ||
|
|
c3df42d42f | ||
|
|
6b424b5f6b | ||
|
|
b2dd03b2dc | ||
|
|
4ebca7f4ea | ||
|
|
d036210ecd | ||
|
|
39d080e0c1 | ||
|
|
d83c9a74f4 | ||
|
|
d26866e018 | ||
|
|
ba7864e111 | ||
|
|
5881809126 | ||
|
|
21ba34542d | ||
|
|
ab456affb3 | ||
|
|
852bf0b2ce | ||
|
|
642a1a610f | ||
|
|
bdfe6a69ef | ||
|
|
7889fc9207 | ||
|
|
cb826aa2f1 | ||
|
|
df522cf12c | ||
|
|
a920ef48de | ||
|
|
9cd8aa1af9 | ||
|
|
f6b63af06d | ||
|
|
8ff1142bf4 | ||
|
|
5de4d03238 | ||
|
|
302e4c17d2 | ||
|
|
b49c7dfd65 | ||
|
|
985da4d3fe | ||
|
|
2ec7d1ae05 | ||
|
|
fc47dd6645 | ||
|
|
2e924f458e | ||
|
|
20fde6b924 | ||
|
|
72d054bf40 | ||
|
|
e0d79196c8 | ||
|
|
a286e960ed | ||
|
|
f70852c04b | ||
|
|
8f740f5c02 | ||
|
|
17c9e2cd4b | ||
|
|
b5653d5278 | ||
|
|
28abe63f46 | ||
|
|
49b49421d0 | ||
|
|
ef12216c19 | ||
|
|
e05ed9240a | ||
|
|
c91d25ea64 | ||
|
|
315a730128 | ||
|
|
230e8a23a0 | ||
|
|
f583aa9d30 | ||
|
|
76adea46d0 | ||
|
|
7f1f335950 | ||
|
|
2e6f93e1a9 | ||
|
|
539708e45e | ||
|
|
2e87d5ae02 | ||
|
|
14af74cb5b | ||
|
|
73612401a5 | ||
|
|
858f64b3dc | ||
|
|
225bedcb20 | ||
|
|
8367784cf3 | ||
|
|
ecc1da95dd | ||
|
|
184baff355 | ||
|
|
3ad94ddf7c | ||
|
|
fabd63072b | ||
|
|
05cdef17f4 | ||
|
|
6ccd32c904 | ||
|
|
50dae04c05 | ||
|
|
44ac11efc7 | ||
|
|
f452fe7497 | ||
|
|
a2e1e13918 | ||
|
|
a4134f6b25 | ||
|
|
d2de263774 | ||
|
|
9f2e7f455f | ||
|
|
faba6a3fb3 | ||
|
|
2aaf0540b6 | ||
|
|
d82e74e4bf | ||
|
|
f9a3476016 | ||
|
|
aaa364a9c5 | ||
|
|
d20e07b271 | ||
|
|
3074b178bd | ||
|
|
4751a5949d | ||
|
|
25d567b6c6 | ||
|
|
bb1ef5f1b8 | ||
|
|
d9e1eec47d | ||
|
|
a4c076bd09 | ||
|
|
8a27735826 | ||
|
|
e8c72b1b01 | ||
|
|
ee537d9bde | ||
|
|
873c79a931 | ||
|
|
7a51be48c6 | ||
|
|
7f9cdf1183 | ||
|
|
72e5ed3372 | ||
|
|
ca48f1c995 | ||
|
|
444905071b | ||
|
|
385d734847 | ||
|
|
16c2eebfd1 | ||
|
|
21dcac7721 | ||
|
|
3ee56112cb | ||
|
|
03db48306b | ||
|
|
dee42ebb29 | ||
|
|
7cc729c7d9 | ||
|
|
a8643dc523 | ||
|
|
1fc6d50f3e | ||
|
|
dbf4a252ad | ||
|
|
add9b13497 | ||
|
|
3d097eef44 | ||
|
|
8c85dd32fa | ||
|
|
aac86f4523 | ||
|
|
f3e889c204 |
2
.github/workflows/cross-build.yaml
vendored
2
.github/workflows/cross-build.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- name: Install arm64 libfdt
|
||||
run: wget http://ftp.us.debian.org/debian/pool/main/d/device-tree-compiler/libfdt-dev_1.6.0-1_arm64.deb && dpkg-deb -xv libfdt-dev_1.6.0-1_arm64.deb ./tlibfdtdev && sudo mkdir /tmmmp && mkdir target && mkdir target/debug && mkdir target/debug/deps && sudo cp ./tlibfdtdev/usr/lib/aarch64-linux-gnu/libfdt.a target/debug/deps/libfdt.a && echo "libfdt installed"
|
||||
run: wget http://ftp.us.debian.org/debian/pool/main/d/device-tree-compiler/libfdt-dev_1.6.0-1_arm64.deb && dpkg-deb -xv libfdt-dev_1.6.0-1_arm64.deb ./tlibfdtdev && mkdir -p target/debug/deps && cp ./tlibfdtdev/usr/lib/aarch64-linux-gnu/libfdt.a target/debug/deps/ && echo "libfdt copied into build tree"
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
|
||||
3
.github/workflows/quality.yaml
vendored
3
.github/workflows/quality.yaml
vendored
@@ -43,3 +43,6 @@ jobs:
|
||||
|
||||
- name: Clippy (mshv)
|
||||
run: cargo clippy --all --all-targets --no-default-features --tests --features "mshv" -- -D warnings
|
||||
|
||||
- name: Clippy (integration tests)
|
||||
run: cargo clippy --all --all-targets --tests --features "integration_tests" -- -D warnings
|
||||
|
||||
265
Cargo.lock
generated
265
Cargo.lock
generated
@@ -9,9 +9,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.14.0"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423"
|
||||
checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
@@ -42,9 +42,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.35"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c0df63cb2955042487fad3aefd2c6e3ae7389ac5dc1beb28921de0b69f779d4"
|
||||
checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -52,9 +52,9 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.0.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0688b520bcc7498f6ca8fa006e8031d353e3fd4f51bd4a50fb03cc4230b28bd2"
|
||||
checksum = "d4d7d63395147b81a9e570bcc6243aaf71c017bd666d4909cfef0085bdda8d73"
|
||||
|
||||
[[package]]
|
||||
name = "arch"
|
||||
@@ -67,8 +67,8 @@ dependencies = [
|
||||
"hypervisor",
|
||||
"libc",
|
||||
"linux-loader",
|
||||
"log 0.4.11",
|
||||
"rand 0.7.3",
|
||||
"log 0.4.14",
|
||||
"rand 0.8.3",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
@@ -111,9 +111,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.55"
|
||||
version = "0.3.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598"
|
||||
checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if 1.0.0",
|
||||
@@ -158,10 +158,13 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"io-uring",
|
||||
"libc",
|
||||
"log 0.4.11",
|
||||
"log 0.4.14",
|
||||
"qcow",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"virtio-bindings",
|
||||
"vm-memory",
|
||||
"vm-virtio",
|
||||
@@ -210,7 +213,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "0.12.0"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -221,11 +224,12 @@ dependencies = [
|
||||
"hypervisor",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log 0.4.11",
|
||||
"log 0.4.14",
|
||||
"net_util",
|
||||
"option_parser",
|
||||
"seccomp",
|
||||
"serde_json",
|
||||
"signal-hook",
|
||||
"ssh2",
|
||||
"tempdir",
|
||||
"tempfile",
|
||||
@@ -282,7 +286,7 @@ dependencies = [
|
||||
"byteorder",
|
||||
"epoll",
|
||||
"libc",
|
||||
"log 0.4.11",
|
||||
"log 0.4.14",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
@@ -315,13 +319,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e"
|
||||
checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log 0.4.11",
|
||||
"log 0.4.14",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
@@ -376,9 +380,20 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.15"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
|
||||
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee8025cf36f917e6a52cce185b7c7177689b838b7ec138364e50cc2277a56cf4"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
@@ -399,18 +414,18 @@ checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.17"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
|
||||
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.0.1"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "hypervisor"
|
||||
@@ -423,8 +438,7 @@ dependencies = [
|
||||
"kvm-bindings",
|
||||
"kvm-ioctls",
|
||||
"libc",
|
||||
"linux-loader",
|
||||
"log 0.4.11",
|
||||
"log 0.4.14",
|
||||
"mshv-bindings",
|
||||
"mshv-ioctls",
|
||||
"serde",
|
||||
@@ -437,9 +451,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iced-x86"
|
||||
version = "1.9.1"
|
||||
version = "1.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "248ce8ff0784d2b15f3c3d8b01f529be0e18aa693a2ba7415df76857967c8fc3"
|
||||
checksum = "644fd8d7e49d1ccae568a8fbb873a7ee7fcb3bc9214a474a5e55c03b6d1fa5ac"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"rustc_version",
|
||||
@@ -448,9 +462,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
|
||||
checksum = "de910d521f7cc3135c4de8db1cb910e0b5ed1dc6f57c381cd07e8e661ce10094"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"unicode-bidi",
|
||||
@@ -478,9 +492,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.6"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
|
||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
@@ -504,8 +518,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kvm-ioctls"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/cloud-hypervisor/kvm-ioctls?branch=ch#64427395b60e00403aed3026362a1b19873230e6"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/cloud-hypervisor/kvm-ioctls?branch=ch#b7f21758bf8e46ac55a28a1b2ed008b5732bbb44"
|
||||
dependencies = [
|
||||
"kvm-bindings",
|
||||
"libc",
|
||||
@@ -520,15 +534,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.81"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
|
||||
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
|
||||
|
||||
[[package]]
|
||||
name = "libssh2-sys"
|
||||
version = "0.2.20"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df40b13fe7ea1be9b9dffa365a51273816c345fc1811478b57ed7d964fbfc4ce"
|
||||
checksum = "e0186af0d8f171ae6b9c4c90ec51898bad5d08a2d5e470903a50d9ad8959cbee"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -553,7 +567,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "linux-loader"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/rust-vmm/linux-loader#acf9c21591813c87dc77f667618d433f62c821af"
|
||||
source = "git+https://github.com/rust-vmm/linux-loader#2855be15a725e32df0b5198487d7b52f4a90ac0f"
|
||||
dependencies = [
|
||||
"vm-memory",
|
||||
]
|
||||
@@ -573,16 +587,16 @@ version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
|
||||
dependencies = [
|
||||
"log 0.4.11",
|
||||
"log 0.4.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.11"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -618,7 +632,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mshv-bindings"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/cloud-hypervisor/mshv?branch=master#2ded6daf97976aa27c1aca47964672c201a7d17d"
|
||||
source = "git+https://github.com/cloud-hypervisor/mshv?branch=master#3a7d7ba07e12409a3b23794c72c30b5052d221d9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"serde",
|
||||
@@ -630,7 +644,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mshv-ioctls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/cloud-hypervisor/mshv?branch=master#2ded6daf97976aa27c1aca47964672c201a7d17d"
|
||||
source = "git+https://github.com/cloud-hypervisor/mshv?branch=master#3a7d7ba07e12409a3b23794c72c30b5052d221d9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mshv-bindings",
|
||||
@@ -651,10 +665,10 @@ dependencies = [
|
||||
"epoll",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log 0.4.11",
|
||||
"log 0.4.14",
|
||||
"net_gen",
|
||||
"pnet",
|
||||
"rand 0.7.3",
|
||||
"rand 0.8.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"virtio-bindings",
|
||||
@@ -665,15 +679,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.22.0"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397"
|
||||
checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.59"
|
||||
version = "0.9.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de52d8eabd217311538a39bba130d7dea1f1e118010fee7a033d966845e7d5fe"
|
||||
checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
@@ -705,7 +725,7 @@ dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"cloudabi",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.1.57",
|
||||
"smallvec",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
@@ -718,7 +738,7 @@ dependencies = [
|
||||
"byteorder",
|
||||
"hypervisor",
|
||||
"libc",
|
||||
"log 0.4.11",
|
||||
"log 0.4.14",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
@@ -852,7 +872,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"libc",
|
||||
"log 0.4.11",
|
||||
"log 0.4.14",
|
||||
"remain",
|
||||
"tempfile",
|
||||
"vmm-sys-util",
|
||||
@@ -860,9 +880,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.7"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -882,25 +902,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core 0.5.1",
|
||||
"rand_core 0.6.0",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.5.1",
|
||||
"rand_core 0.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -920,20 +939,20 @@ checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
checksum = "a8b34ba8cfb21243bd8df91854c830ff0d785fff2e82ebd4434c2644cb9ada18"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
"rand_core 0.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -951,22 +970,31 @@ version = "0.1.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"redox_syscall",
|
||||
"getrandom 0.1.16",
|
||||
"redox_syscall 0.1.57",
|
||||
"rust-argon2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.4.2"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
|
||||
checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -976,9 +1004,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.21"
|
||||
version = "0.6.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
|
||||
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
|
||||
|
||||
[[package]]
|
||||
name = "remain"
|
||||
@@ -1070,15 +1098,15 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.118"
|
||||
version = "1.0.123"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
|
||||
checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.118"
|
||||
version = "1.0.123"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
|
||||
checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1087,9 +1115,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.60"
|
||||
version = "1.0.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779"
|
||||
checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -1098,9 +1126,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.2.1"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbae917d5719a9884206132a443899e8c9056b48a156233a823417b39f34044f"
|
||||
checksum = "780f5e3fe0c66f67197236097d89de1e86216f1f6fdeaf47c442f854ab46c240"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
@@ -1108,24 +1136,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.2.2"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce32ea0c6c56d5eacaeb814fbed9960547021d3edd010ded1425f180536b20ab"
|
||||
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.5.1"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75"
|
||||
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||
|
||||
[[package]]
|
||||
name = "ssh2"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed024de0a5e6944fe3080a3745e7a5649c5517ea2a6cfb16333f94f89c248985"
|
||||
checksum = "d876d4d57f6bbf2245d43f7ec53759461f801a446d3693704aa6d27b257844d7"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1",
|
||||
"libc",
|
||||
@@ -1147,9 +1175,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.54"
|
||||
version = "1.0.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44"
|
||||
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1229,14 +1257,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.1.0"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"rand 0.7.3",
|
||||
"redox_syscall",
|
||||
"rand 0.8.3",
|
||||
"redox_syscall 0.2.4",
|
||||
"remove_dir_all",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
@@ -1282,18 +1310,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.22"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e"
|
||||
checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.22"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56"
|
||||
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1302,18 +1330,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.0.1"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
|
||||
checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.1.0"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f"
|
||||
checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
@@ -1335,9 +1363,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.16"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606"
|
||||
checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
@@ -1374,9 +1402,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.10"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
|
||||
checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
@@ -1396,12 +1424,12 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio-ioctls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/cloud-hypervisor/vfio-ioctls?branch=ch#49cc3626f6787e2075493a511675c6851a13bbc0"
|
||||
source = "git+https://github.com/cloud-hypervisor/vfio-ioctls?branch=ch#6ea1b934bbff9102bbdc80c23a357a48645cd722"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
"kvm-ioctls",
|
||||
"log 0.4.11",
|
||||
"log 0.4.14",
|
||||
"vfio-bindings",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
@@ -1423,7 +1451,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"epoll",
|
||||
"libc",
|
||||
"log 0.4.11",
|
||||
"log 0.4.14",
|
||||
"vhost",
|
||||
"virtio-bindings",
|
||||
"vm-memory",
|
||||
@@ -1439,7 +1467,7 @@ dependencies = [
|
||||
"clap",
|
||||
"epoll",
|
||||
"libc",
|
||||
"log 0.4.11",
|
||||
"log 0.4.14",
|
||||
"option_parser",
|
||||
"qcow",
|
||||
"vhost",
|
||||
@@ -1456,7 +1484,7 @@ dependencies = [
|
||||
"clap",
|
||||
"epoll",
|
||||
"libc",
|
||||
"log 0.4.11",
|
||||
"log 0.4.14",
|
||||
"net_util",
|
||||
"option_parser",
|
||||
"vhost",
|
||||
@@ -1483,7 +1511,7 @@ dependencies = [
|
||||
"epoll",
|
||||
"io-uring",
|
||||
"libc",
|
||||
"log 0.4.11",
|
||||
"log 0.4.14",
|
||||
"net_gen",
|
||||
"net_util",
|
||||
"pci",
|
||||
@@ -1527,9 +1555,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vm-memory"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45b5b0a6f371f8147143b1adb95edddafc9cb9e40adaf94edb6f93a1d04b0330"
|
||||
checksum = "625f401b1b8b3ac3d43f53903cd138cfe840bd985f8581e553027b31d2bb8ae8"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"libc",
|
||||
@@ -1545,13 +1573,14 @@ dependencies = [
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"vm-memory",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vm-virtio"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log 0.4.11",
|
||||
"log 0.4.14",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
@@ -1577,7 +1606,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"linux-loader",
|
||||
"log 0.4.11",
|
||||
"log 0.4.14",
|
||||
"micro_http",
|
||||
"net_util",
|
||||
"option_parser",
|
||||
|
||||
17
Cargo.toml
17
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cloud-hypervisor"
|
||||
version = "0.12.0"
|
||||
version = "0.13.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2018"
|
||||
default-run = "cloud-hypervisor"
|
||||
@@ -18,16 +18,17 @@ api_client = { path = "api_client" }
|
||||
clap = { version = "2.33.3", features = ["wrap_help"] }
|
||||
epoll = ">=4.0.1"
|
||||
hypervisor = { path = "hypervisor" }
|
||||
libc = "0.2.81"
|
||||
log = { version = "0.4.11", features = ["std"] }
|
||||
libc = "0.2.86"
|
||||
log = { version = "0.4.14", features = ["std"] }
|
||||
option_parser = { path = "option_parser" }
|
||||
seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v0.22.0" }
|
||||
serde_json = "1.0.60"
|
||||
serde_json = "1.0.62"
|
||||
signal-hook = "0.3.4"
|
||||
thiserror = "1.0"
|
||||
vmm = { path = "vmm" }
|
||||
vmm-sys-util = "0.7.0"
|
||||
vm-memory = "0.5.0"
|
||||
wait-timeout = "0.2.0"
|
||||
vm-memory = "0.4.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "2.33.3", features = ["wrap_help"] }
|
||||
@@ -37,13 +38,13 @@ clap = { version = "2.33.3", features = ["wrap_help"] }
|
||||
vhost_rs = { git = "https://github.com/cloud-hypervisor/vhost", branch = "ch", package = "vhost", features = ["vhost-user-master", "vhost-user-slave"] }
|
||||
|
||||
[dev-dependencies]
|
||||
ssh2 = "0.9.0"
|
||||
ssh2 = "0.9.1"
|
||||
dirs = "3.0.1"
|
||||
credibility = "0.1.3"
|
||||
tempdir = "0.3.7"
|
||||
lazy_static= "1.4.0"
|
||||
tempfile = "3.1.0"
|
||||
serde_json = "1.0.60"
|
||||
tempfile = "3.2.0"
|
||||
serde_json = "1.0.62"
|
||||
net_util = { path = "net_util" }
|
||||
|
||||
[features]
|
||||
|
||||
3
Jenkinsfile
vendored
3
Jenkinsfile
vendored
@@ -22,8 +22,7 @@ pipeline{
|
||||
}
|
||||
}
|
||||
stage ('Build') {
|
||||
failFast true
|
||||
parallel {
|
||||
parallel {
|
||||
stage ('Worker build') {
|
||||
agent { node { label 'groovy' } }
|
||||
stages {
|
||||
|
||||
@@ -5,5 +5,5 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
vm-memory = "0.4.0"
|
||||
vm-memory = "0.5.0"
|
||||
|
||||
|
||||
@@ -11,12 +11,12 @@ acpi = ["acpi_tables"]
|
||||
anyhow = "1.0"
|
||||
byteorder = "1.3.4"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.81"
|
||||
log = "0.4.11"
|
||||
libc = "0.2.86"
|
||||
log = "0.4.14"
|
||||
serde = {version = ">=1.0.27", features = ["rc"] }
|
||||
serde_derive = ">=1.0.27"
|
||||
serde_json = ">=1.0.9"
|
||||
vm-memory = { version = "0.4.0", features = ["backend-mmap"] }
|
||||
vm-memory = { version = "0.5.0", features = ["backend-mmap"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
acpi_tables = { path = "../acpi_tables", optional = true }
|
||||
arch_gen = { path = "../arch_gen" }
|
||||
@@ -26,4 +26,4 @@ git = "https://github.com/rust-vmm/linux-loader"
|
||||
features = ["elf", "bzimage"]
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.7.3"
|
||||
rand = "0.8.3"
|
||||
|
||||
@@ -63,7 +63,6 @@ pub fn configure_vcpu(
|
||||
id: u8,
|
||||
kernel_entry_point: Option<EntryPoint>,
|
||||
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
_phys_bits: u8,
|
||||
) -> super::Result<u64> {
|
||||
if let Some(kernel_entry_point) = kernel_entry_point {
|
||||
regs::setup_regs(
|
||||
|
||||
@@ -162,6 +162,6 @@ impl DeviceInfoForFDT for MMIODeviceInfo {
|
||||
self.irq
|
||||
}
|
||||
fn length(&self) -> u64 {
|
||||
4096 as u64
|
||||
4096
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ pub fn set_lint(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "kvm")]
|
||||
mod tests {
|
||||
|
||||
extern crate rand;
|
||||
|
||||
@@ -173,9 +173,6 @@ pub enum Error {
|
||||
|
||||
/// Missing SGX_LC CPU feature
|
||||
MissingSgxLaunchControlFeature,
|
||||
|
||||
// Error populating Cpuid
|
||||
PopulatingCpuid,
|
||||
}
|
||||
|
||||
impl From<Error> for super::Error {
|
||||
@@ -336,87 +333,12 @@ pub fn configure_vcpu(
|
||||
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
cpuid: CpuId,
|
||||
kvm_hyperv: bool,
|
||||
phys_bits: u8,
|
||||
) -> super::Result<()> {
|
||||
// Per vCPU CPUID changes; common are handled via CpuManager::generate_common_cpuid()
|
||||
let mut cpuid = cpuid;
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id));
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1f, None, CpuidReg::EDX, u32::from(id));
|
||||
|
||||
if kvm_hyperv {
|
||||
// Remove conflicting entries
|
||||
cpuid.retain(|c| c.function != 0x4000_0000);
|
||||
cpuid.retain(|c| c.function != 0x4000_0001);
|
||||
|
||||
// See "Hypervisor Top Level Functional Specification" for details
|
||||
// Compliance with "Hv#1" requires leaves up to 0x4000_000a
|
||||
cpuid
|
||||
.push(CpuIdEntry {
|
||||
function: 0x40000000,
|
||||
eax: 0x4000000a, // Maximum cpuid leaf
|
||||
ebx: 0x756e694c, // "Linu"
|
||||
ecx: 0x564b2078, // "x KV"
|
||||
edx: 0x7648204d, // "M Hv"
|
||||
..Default::default()
|
||||
})
|
||||
.map_err(|_| Error::PopulatingCpuid)?;
|
||||
cpuid
|
||||
.push(CpuIdEntry {
|
||||
function: 0x40000001,
|
||||
eax: 0x31237648, // "Hv#1"
|
||||
..Default::default()
|
||||
})
|
||||
.map_err(|_| Error::PopulatingCpuid)?;
|
||||
cpuid
|
||||
.push(CpuIdEntry {
|
||||
function: 0x40000002,
|
||||
eax: 0x3839, // "Build number"
|
||||
ebx: 0xa0000, // "Version"
|
||||
..Default::default()
|
||||
})
|
||||
.map_err(|_| Error::PopulatingCpuid)?;
|
||||
cpuid
|
||||
.push(CpuIdEntry {
|
||||
function: 0x4000_0003,
|
||||
eax: 1 << 1 // AccessPartitionReferenceCounter
|
||||
| 1 << 2 // AccessSynicRegs
|
||||
| 1 << 3 // AccessSyntheticTimerRegs
|
||||
| 1 << 9, // AccessPartitionReferenceTsc
|
||||
..Default::default()
|
||||
})
|
||||
.map_err(|_| Error::PopulatingCpuid)?;
|
||||
for i in 0x4000_0004..=0x4000_000a {
|
||||
cpuid
|
||||
.push(CpuIdEntry {
|
||||
function: i,
|
||||
..Default::default()
|
||||
})
|
||||
.map_err(|_| Error::PopulatingCpuid)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy CPU identification string
|
||||
for i in 0x8000_0002..=0x8000_0004 {
|
||||
cpuid.retain(|c| c.function != i);
|
||||
let leaf = unsafe { x86_64::__cpuid(i) };
|
||||
cpuid
|
||||
.push(CpuIdEntry {
|
||||
function: i,
|
||||
eax: leaf.eax,
|
||||
ebx: leaf.ebx,
|
||||
ecx: leaf.ecx,
|
||||
edx: leaf.edx,
|
||||
..Default::default()
|
||||
})
|
||||
.map_err(|_| Error::PopulatingCpuid)?;
|
||||
}
|
||||
|
||||
// Set CPU physical bits
|
||||
for entry in cpuid.as_mut_slice().iter_mut() {
|
||||
if entry.function == 0x8000_0008 {
|
||||
entry.eax = (entry.eax & 0xffff_ff00) | (phys_bits as u32 & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
fd.set_cpuid2(&cpuid)
|
||||
.map_err(|e| Error::SetSupportedCpusFailed(e.into()))?;
|
||||
|
||||
@@ -594,7 +516,7 @@ fn configure_pvh(
|
||||
let mut memmap: Vec<hvm_memmap_table_entry> = Vec::new();
|
||||
|
||||
// Create the memory map entries.
|
||||
add_memmap_entry(&mut memmap, 0, layout::EBDA_START.raw_value(), E820_RAM)?;
|
||||
add_memmap_entry(&mut memmap, 0, layout::EBDA_START.raw_value(), E820_RAM);
|
||||
|
||||
let mem_end = guest_mem.last_addr();
|
||||
|
||||
@@ -604,21 +526,21 @@ fn configure_pvh(
|
||||
layout::HIGH_RAM_START.raw_value(),
|
||||
mem_end.unchecked_offset_from(layout::HIGH_RAM_START) + 1,
|
||||
E820_RAM,
|
||||
)?;
|
||||
);
|
||||
} else {
|
||||
add_memmap_entry(
|
||||
&mut memmap,
|
||||
layout::HIGH_RAM_START.raw_value(),
|
||||
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::HIGH_RAM_START),
|
||||
E820_RAM,
|
||||
)?;
|
||||
);
|
||||
if mem_end > layout::RAM_64BIT_START {
|
||||
add_memmap_entry(
|
||||
&mut memmap,
|
||||
layout::RAM_64BIT_START.raw_value(),
|
||||
mem_end.unchecked_offset_from(layout::RAM_64BIT_START) + 1,
|
||||
E820_RAM,
|
||||
)?;
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,7 +549,7 @@ fn configure_pvh(
|
||||
layout::PCI_MMCONFIG_START.0,
|
||||
layout::PCI_MMCONFIG_SIZE,
|
||||
E820_RESERVED,
|
||||
)?;
|
||||
);
|
||||
|
||||
if let Some(sgx_epc_region) = sgx_epc_region {
|
||||
add_memmap_entry(
|
||||
@@ -635,7 +557,7 @@ fn configure_pvh(
|
||||
sgx_epc_region.start().raw_value(),
|
||||
sgx_epc_region.size() as u64,
|
||||
E820_RESERVED,
|
||||
)?;
|
||||
);
|
||||
}
|
||||
|
||||
start_info.0.memmap_entries = memmap.len() as u32;
|
||||
@@ -680,12 +602,7 @@ fn configure_pvh(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_memmap_entry(
|
||||
memmap: &mut Vec<hvm_memmap_table_entry>,
|
||||
addr: u64,
|
||||
size: u64,
|
||||
mem_type: u32,
|
||||
) -> Result<(), Error> {
|
||||
fn add_memmap_entry(memmap: &mut Vec<hvm_memmap_table_entry>, addr: u64, size: u64, mem_type: u32) {
|
||||
// Add the table entry to the vector
|
||||
memmap.push(hvm_memmap_table_entry {
|
||||
addr,
|
||||
@@ -693,8 +610,6 @@ fn add_memmap_entry(
|
||||
type_: mem_type,
|
||||
reserved: 0,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn configure_64bit_boot(
|
||||
@@ -974,7 +889,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn regions_lt_4gb() {
|
||||
let regions = arch_memory_regions(1 << 29 as GuestUsize);
|
||||
let regions = arch_memory_regions(1 << 29);
|
||||
assert_eq!(3, regions.len());
|
||||
assert_eq!(GuestAddress(0), regions[0].0);
|
||||
assert_eq!(1usize << 29, regions[0].1);
|
||||
@@ -982,7 +897,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn regions_gt_4gb() {
|
||||
let regions = arch_memory_regions((1 << 32 as GuestUsize) + 0x8000);
|
||||
let regions = arch_memory_regions((1 << 32) + 0x8000);
|
||||
assert_eq!(4, regions.len());
|
||||
assert_eq!(GuestAddress(0), regions[0].0);
|
||||
assert_eq!(GuestAddress(1 << 32), regions[1].0);
|
||||
@@ -1170,8 +1085,8 @@ mod tests {
|
||||
},
|
||||
];
|
||||
|
||||
add_memmap_entry(&mut memmap, 0, 0x1000, E820_RAM).unwrap();
|
||||
add_memmap_entry(&mut memmap, 0x10000, 0xa000, E820_RESERVED).unwrap();
|
||||
add_memmap_entry(&mut memmap, 0, 0x1000, E820_RAM);
|
||||
add_memmap_entry(&mut memmap, 0x10000, 0xa000, E820_RESERVED);
|
||||
|
||||
assert_eq!(format!("{:?}", memmap), format!("{:?}", expected_memmap));
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ fn write_string(
|
||||
for c in val.as_bytes().iter() {
|
||||
curptr = write_and_incr(mem, *c, curptr)?;
|
||||
}
|
||||
curptr = write_and_incr(mem, 0 as u8, curptr)?;
|
||||
curptr = write_and_incr(mem, 0u8, curptr)?;
|
||||
Ok(curptr)
|
||||
}
|
||||
|
||||
@@ -170,55 +170,63 @@ pub fn setup_smbios(mem: &GuestMemoryMmap) -> Result<u64> {
|
||||
|
||||
{
|
||||
handle += 1;
|
||||
let mut smbios_biosinfo = SmbiosBiosInfo::default();
|
||||
smbios_biosinfo.typ = BIOS_INFORMATION;
|
||||
smbios_biosinfo.length = mem::size_of::<SmbiosBiosInfo>() as u8;
|
||||
smbios_biosinfo.handle = handle;
|
||||
smbios_biosinfo.vendor = 1; // First string written in this section
|
||||
smbios_biosinfo.version = 2; // Second string written in this section
|
||||
smbios_biosinfo.characteristics = PCI_SUPPORTED;
|
||||
smbios_biosinfo.characteristics_ext2 = IS_VIRTUAL_MACHINE;
|
||||
let smbios_biosinfo = SmbiosBiosInfo {
|
||||
typ: BIOS_INFORMATION,
|
||||
length: mem::size_of::<SmbiosBiosInfo>() as u8,
|
||||
handle,
|
||||
vendor: 1, // First string written in this section
|
||||
version: 2, // Second string written in this section
|
||||
characteristics: PCI_SUPPORTED,
|
||||
characteristics_ext2: IS_VIRTUAL_MACHINE,
|
||||
..Default::default()
|
||||
};
|
||||
curptr = write_and_incr(mem, smbios_biosinfo, curptr)?;
|
||||
curptr = write_string(mem, "cloud-hypervisor", curptr)?;
|
||||
curptr = write_string(mem, "0", curptr)?;
|
||||
curptr = write_and_incr(mem, 0 as u8, curptr)?;
|
||||
curptr = write_and_incr(mem, 0u8, curptr)?;
|
||||
}
|
||||
|
||||
{
|
||||
handle += 1;
|
||||
let mut smbios_sysinfo = SmbiosSysInfo::default();
|
||||
smbios_sysinfo.typ = SYSTEM_INFORMATION;
|
||||
smbios_sysinfo.length = mem::size_of::<SmbiosSysInfo>() as u8;
|
||||
smbios_sysinfo.handle = handle;
|
||||
smbios_sysinfo.manufacturer = 1; // First string written in this section
|
||||
smbios_sysinfo.product_name = 2; // Second string written in this section
|
||||
let smbios_sysinfo = SmbiosSysInfo {
|
||||
typ: SYSTEM_INFORMATION,
|
||||
length: mem::size_of::<SmbiosSysInfo>() as u8,
|
||||
handle,
|
||||
manufacturer: 1, // First string written in this section
|
||||
product_name: 2, // Second string written in this section
|
||||
..Default::default()
|
||||
};
|
||||
curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
|
||||
curptr = write_string(mem, "Cloud Hypervisor", curptr)?;
|
||||
curptr = write_string(mem, "cloud-hypervisor", curptr)?;
|
||||
curptr = write_and_incr(mem, 0 as u8, curptr)?;
|
||||
curptr = write_and_incr(mem, 0u8, curptr)?;
|
||||
}
|
||||
|
||||
{
|
||||
handle += 1;
|
||||
let mut smbios_sysinfo = SmbiosSysInfo::default();
|
||||
smbios_sysinfo.typ = END_OF_TABLE;
|
||||
smbios_sysinfo.length = mem::size_of::<SmbiosSysInfo>() as u8;
|
||||
smbios_sysinfo.handle = handle;
|
||||
let smbios_sysinfo = SmbiosSysInfo {
|
||||
typ: END_OF_TABLE,
|
||||
length: mem::size_of::<SmbiosSysInfo>() as u8,
|
||||
handle,
|
||||
..Default::default()
|
||||
};
|
||||
curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
|
||||
curptr = write_and_incr(mem, 0 as u8, curptr)?;
|
||||
curptr = write_and_incr(mem, 0u8, curptr)?;
|
||||
}
|
||||
|
||||
{
|
||||
let mut smbios_ep = Smbios30Entrypoint::default();
|
||||
smbios_ep.signature = *SM3_MAGIC_IDENT;
|
||||
smbios_ep.length = mem::size_of::<Smbios30Entrypoint>() as u8;
|
||||
// SMBIOS rev 3.2.0
|
||||
smbios_ep.majorver = 0x03;
|
||||
smbios_ep.minorver = 0x02;
|
||||
smbios_ep.docrev = 0x00;
|
||||
smbios_ep.revision = 0x01; // SMBIOS 3.0
|
||||
smbios_ep.max_size = curptr.unchecked_offset_from(physptr) as u32;
|
||||
smbios_ep.physptr = physptr.0;
|
||||
let mut smbios_ep = Smbios30Entrypoint {
|
||||
signature: *SM3_MAGIC_IDENT,
|
||||
length: mem::size_of::<Smbios30Entrypoint>() as u8,
|
||||
// SMBIOS rev 3.2.0
|
||||
majorver: 0x03,
|
||||
minorver: 0x02,
|
||||
docrev: 0x00,
|
||||
revision: 0x01, // SMBIOS 3.0
|
||||
max_size: curptr.unchecked_offset_from(physptr) as u32,
|
||||
physptr: physptr.0,
|
||||
..Default::default()
|
||||
};
|
||||
smbios_ep.checksum = compute_checksum(&smbios_ep);
|
||||
mem.write_obj(smbios_ep, GuestAddress(SMBIOS_START))
|
||||
.map_err(|_| Error::WriteSmbiosEp)?;
|
||||
|
||||
@@ -10,12 +10,17 @@ io_uring = []
|
||||
|
||||
[dependencies]
|
||||
io-uring = ">=0.4.0"
|
||||
libc = "0.2.81"
|
||||
log = "0.4.11"
|
||||
libc = "0.2.86"
|
||||
log = "0.4.14"
|
||||
qcow = { path = "../qcow" }
|
||||
serde = ">=1.0.27"
|
||||
serde_derive = ">=1.0.27"
|
||||
serde_json = ">=1.0.9"
|
||||
thiserror = "1.0"
|
||||
virtio-bindings = { version = "0.1", features = ["virtio-v5_0_0"]}
|
||||
vm-memory = { version = "0.4.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vm-memory = { version = "0.5.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = ">=0.3.1"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.2.0"
|
||||
|
||||
56
block_util/src/async_io.rs
Normal file
56
block_util/src/async_io.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright © 2021 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
use thiserror::Error;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum DiskFileError {
|
||||
/// Failed getting disk file size.
|
||||
#[error("Failed getting disk file size: {0}")]
|
||||
Size(#[source] std::io::Error),
|
||||
/// Failed creating a new AsyncIo.
|
||||
#[error("Failed creating a new AsyncIo: {0}")]
|
||||
NewAsyncIo(#[source] std::io::Error),
|
||||
}
|
||||
|
||||
pub type DiskFileResult<T> = std::result::Result<T, DiskFileError>;
|
||||
|
||||
pub trait DiskFile: Send + Sync {
|
||||
fn size(&mut self) -> DiskFileResult<u64>;
|
||||
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>>;
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AsyncIoError {
|
||||
/// Failed vectored reading from file.
|
||||
#[error("Failed vectored reading from file: {0}")]
|
||||
ReadVectored(#[source] std::io::Error),
|
||||
/// Failed vectored writing to file.
|
||||
#[error("Failed vectored writing to file: {0}")]
|
||||
WriteVectored(#[source] std::io::Error),
|
||||
/// Failed synchronizing file.
|
||||
#[error("Failed synchronizing file: {0}")]
|
||||
Fsync(#[source] std::io::Error),
|
||||
}
|
||||
|
||||
pub type AsyncIoResult<T> = std::result::Result<T, AsyncIoError>;
|
||||
|
||||
pub trait AsyncIo: Send + Sync {
|
||||
fn notifier(&self) -> &EventFd;
|
||||
fn read_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()>;
|
||||
fn write_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()>;
|
||||
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()>;
|
||||
fn complete(&mut self) -> Vec<(u64, i32)>;
|
||||
}
|
||||
110
block_util/src/fixed_vhd_async.rs
Normal file
110
block_util/src/fixed_vhd_async.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
// Copyright © 2021 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
||||
};
|
||||
use crate::raw_async::RawFileAsync;
|
||||
use crate::vhd::VhdFooter;
|
||||
use std::fs::File;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
pub struct FixedVhdDiskAsync {
|
||||
file: File,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
impl FixedVhdDiskAsync {
|
||||
pub fn new(mut file: File) -> std::io::Result<Self> {
|
||||
let footer = VhdFooter::new(&mut file)?;
|
||||
|
||||
Ok(FixedVhdDiskAsync {
|
||||
file,
|
||||
size: footer.current_size(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DiskFile for FixedVhdDiskAsync {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
Ok(self.size)
|
||||
}
|
||||
|
||||
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
Ok(Box::new(
|
||||
FixedVhdAsync::new(self.file.as_raw_fd(), ring_depth, self.size)
|
||||
.map_err(DiskFileError::NewAsyncIo)?,
|
||||
) as Box<dyn AsyncIo>)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FixedVhdAsync {
|
||||
raw_file_async: RawFileAsync,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
impl FixedVhdAsync {
|
||||
pub fn new(fd: RawFd, ring_depth: u32, size: u64) -> std::io::Result<Self> {
|
||||
let raw_file_async = RawFileAsync::new(fd, ring_depth)?;
|
||||
|
||||
Ok(FixedVhdAsync {
|
||||
raw_file_async,
|
||||
size,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncIo for FixedVhdAsync {
|
||||
fn notifier(&self) -> &EventFd {
|
||||
self.raw_file_async.notifier()
|
||||
}
|
||||
|
||||
fn read_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
if offset as u64 >= self.size {
|
||||
return Err(AsyncIoError::ReadVectored(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
format!(
|
||||
"Invalid offset {}, can't be larger than file size {}",
|
||||
offset, self.size
|
||||
),
|
||||
)));
|
||||
}
|
||||
|
||||
self.raw_file_async.read_vectored(offset, iovecs, user_data)
|
||||
}
|
||||
|
||||
fn write_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
if offset as u64 >= self.size {
|
||||
return Err(AsyncIoError::WriteVectored(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
format!(
|
||||
"Invalid offset {}, can't be larger than file size {}",
|
||||
offset, self.size
|
||||
),
|
||||
)));
|
||||
}
|
||||
|
||||
self.raw_file_async
|
||||
.write_vectored(offset, iovecs, user_data)
|
||||
}
|
||||
|
||||
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
|
||||
self.raw_file_async.fsync(user_data)
|
||||
}
|
||||
|
||||
fn complete(&mut self) -> Vec<(u64, i32)> {
|
||||
self.raw_file_async.complete()
|
||||
}
|
||||
}
|
||||
107
block_util/src/fixed_vhd_sync.rs
Normal file
107
block_util/src/fixed_vhd_sync.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright © 2021 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
||||
};
|
||||
use crate::raw_sync::RawFileSync;
|
||||
use crate::vhd::VhdFooter;
|
||||
use std::fs::File;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
pub struct FixedVhdDiskSync {
|
||||
file: File,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
impl FixedVhdDiskSync {
|
||||
pub fn new(mut file: File) -> std::io::Result<Self> {
|
||||
let footer = VhdFooter::new(&mut file)?;
|
||||
|
||||
Ok(FixedVhdDiskSync {
|
||||
file,
|
||||
size: footer.current_size(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DiskFile for FixedVhdDiskSync {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
Ok(self.size)
|
||||
}
|
||||
|
||||
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
Ok(Box::new(
|
||||
FixedVhdSync::new(self.file.as_raw_fd(), self.size)
|
||||
.map_err(DiskFileError::NewAsyncIo)?,
|
||||
) as Box<dyn AsyncIo>)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FixedVhdSync {
|
||||
raw_file_sync: RawFileSync,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
impl FixedVhdSync {
|
||||
pub fn new(fd: RawFd, size: u64) -> std::io::Result<Self> {
|
||||
Ok(FixedVhdSync {
|
||||
raw_file_sync: RawFileSync::new(fd),
|
||||
size,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncIo for FixedVhdSync {
|
||||
fn notifier(&self) -> &EventFd {
|
||||
self.raw_file_sync.notifier()
|
||||
}
|
||||
|
||||
fn read_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
if offset as u64 >= self.size {
|
||||
return Err(AsyncIoError::ReadVectored(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
format!(
|
||||
"Invalid offset {}, can't be larger than file size {}",
|
||||
offset, self.size
|
||||
),
|
||||
)));
|
||||
}
|
||||
|
||||
self.raw_file_sync.read_vectored(offset, iovecs, user_data)
|
||||
}
|
||||
|
||||
fn write_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
if offset as u64 >= self.size {
|
||||
return Err(AsyncIoError::WriteVectored(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
format!(
|
||||
"Invalid offset {}, can't be larger than file size {}",
|
||||
offset, self.size
|
||||
),
|
||||
)));
|
||||
}
|
||||
|
||||
self.raw_file_sync.write_vectored(offset, iovecs, user_data)
|
||||
}
|
||||
|
||||
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
|
||||
self.raw_file_sync.fsync(user_data)
|
||||
}
|
||||
|
||||
fn complete(&mut self) -> Vec<(u64, i32)> {
|
||||
self.raw_file_sync.complete()
|
||||
}
|
||||
}
|
||||
@@ -13,26 +13,35 @@ extern crate log;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
pub mod async_io;
|
||||
pub mod fixed_vhd_async;
|
||||
pub mod fixed_vhd_sync;
|
||||
pub mod qcow_sync;
|
||||
pub mod raw_async;
|
||||
pub mod raw_sync;
|
||||
pub mod vhd;
|
||||
|
||||
use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult, DiskFileError, DiskFileResult};
|
||||
#[cfg(feature = "io_uring")]
|
||||
use io_uring::Probe;
|
||||
use io_uring::{opcode, squeue, IoUring};
|
||||
use io_uring::{opcode, IoUring, Probe};
|
||||
use serde::ser::{Serialize, SerializeStruct, Serializer};
|
||||
use std::cmp;
|
||||
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||
use std::convert::TryInto;
|
||||
use std::fs::File;
|
||||
use std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
|
||||
use std::os::linux::fs::MetadataExt;
|
||||
#[cfg(feature = "io_uring")]
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::path::PathBuf;
|
||||
use std::result;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use virtio_bindings::bindings::virtio_blk::*;
|
||||
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryError, GuestMemoryMmap};
|
||||
use vm_virtio::DescriptorChain;
|
||||
#[cfg(feature = "io_uring")]
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
const SECTOR_SHIFT: u8 = 9;
|
||||
pub const SECTOR_SIZE: u64 = (0x01 as u64) << SECTOR_SHIFT;
|
||||
pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
@@ -98,6 +107,9 @@ pub enum ExecuteError {
|
||||
Unsupported(u32),
|
||||
SubmitIoUring(io::Error),
|
||||
GetHostAddress(GuestMemoryError),
|
||||
AsyncRead(AsyncIoError),
|
||||
AsyncWrite(AsyncIoError),
|
||||
AsyncFlush(AsyncIoError),
|
||||
}
|
||||
|
||||
impl ExecuteError {
|
||||
@@ -111,6 +123,9 @@ impl ExecuteError {
|
||||
ExecuteError::Unsupported(_) => VIRTIO_BLK_S_UNSUPP,
|
||||
ExecuteError::SubmitIoUring(_) => VIRTIO_BLK_S_IOERR,
|
||||
ExecuteError::GetHostAddress(_) => VIRTIO_BLK_S_IOERR,
|
||||
ExecuteError::AsyncRead(_) => VIRTIO_BLK_S_IOERR,
|
||||
ExecuteError::AsyncWrite(_) => VIRTIO_BLK_S_IOERR,
|
||||
ExecuteError::AsyncFlush(_) => VIRTIO_BLK_S_IOERR,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -268,12 +283,11 @@ impl Request {
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
pub fn execute_io_uring(
|
||||
pub fn execute_async(
|
||||
&self,
|
||||
mem: &GuestMemoryMmap,
|
||||
io_uring: &mut IoUring,
|
||||
disk_nsectors: u64,
|
||||
disk_image_fd: RawFd,
|
||||
disk_image: &mut dyn AsyncIo,
|
||||
disk_id: &[u8],
|
||||
user_data: u64,
|
||||
) -> result::Result<bool, ExecuteError> {
|
||||
@@ -281,9 +295,6 @@ impl Request {
|
||||
let request_type = self.request_type;
|
||||
let offset = (sector << SECTOR_SHIFT) as libc::off_t;
|
||||
|
||||
let (submitter, sq, _) = io_uring.split();
|
||||
let mut avail_sq = sq.available();
|
||||
|
||||
let mut iovecs = Vec::new();
|
||||
for (data_addr, data_len) in &self.data_descriptors {
|
||||
let mut top: u64 = u64::from(*data_len) / SECTOR_SIZE;
|
||||
@@ -311,49 +322,19 @@ impl Request {
|
||||
// Queue operations expected to be submitted.
|
||||
match request_type {
|
||||
RequestType::In => {
|
||||
// Safe because we know the file descriptor is valid and we
|
||||
// relied on vm-memory to provide the buffer address.
|
||||
let _ = unsafe {
|
||||
avail_sq.push(
|
||||
opcode::Readv::new(
|
||||
opcode::types::Fd(disk_image_fd),
|
||||
iovecs.as_ptr(),
|
||||
iovecs.len() as u32,
|
||||
)
|
||||
.offset(offset)
|
||||
.build()
|
||||
.flags(squeue::Flags::ASYNC)
|
||||
.user_data(user_data),
|
||||
)
|
||||
};
|
||||
disk_image
|
||||
.read_vectored(offset, iovecs, user_data)
|
||||
.map_err(ExecuteError::AsyncRead)?;
|
||||
}
|
||||
RequestType::Out => {
|
||||
// Safe because we know the file descriptor is valid and we
|
||||
// relied on vm-memory to provide the buffer address.
|
||||
let _ = unsafe {
|
||||
avail_sq.push(
|
||||
opcode::Writev::new(
|
||||
opcode::types::Fd(disk_image_fd),
|
||||
iovecs.as_ptr(),
|
||||
iovecs.len() as u32,
|
||||
)
|
||||
.offset(offset)
|
||||
.build()
|
||||
.flags(squeue::Flags::ASYNC)
|
||||
.user_data(user_data),
|
||||
)
|
||||
};
|
||||
disk_image
|
||||
.write_vectored(offset, iovecs, user_data)
|
||||
.map_err(ExecuteError::AsyncWrite)?;
|
||||
}
|
||||
RequestType::Flush => {
|
||||
// Safe because we know the file descriptor is valid.
|
||||
let _ = unsafe {
|
||||
avail_sq.push(
|
||||
opcode::Fsync::new(opcode::types::Fd(disk_image_fd))
|
||||
.build()
|
||||
.flags(squeue::Flags::ASYNC)
|
||||
.user_data(user_data),
|
||||
)
|
||||
};
|
||||
disk_image
|
||||
.fsync(Some(user_data))
|
||||
.map_err(ExecuteError::AsyncFlush)?;
|
||||
}
|
||||
RequestType::GetDeviceID => {
|
||||
let (data_addr, data_len) = if self.data_descriptors.len() == 1 {
|
||||
@@ -371,11 +352,6 @@ impl Request {
|
||||
RequestType::Unsupported(t) => return Err(ExecuteError::Unsupported(t)),
|
||||
}
|
||||
|
||||
// Update the submission queue and submit new operations to the
|
||||
// io_uring instance.
|
||||
avail_sq.sync();
|
||||
submitter.submit().map_err(ExecuteError::SubmitIoUring)?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
@@ -571,3 +547,145 @@ pub fn block_io_uring_is_supported() -> bool {
|
||||
pub fn block_io_uring_is_supported() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn disk_size(file: &mut dyn Seek, semaphore: &mut Arc<Mutex<()>>) -> DiskFileResult<u64> {
|
||||
// Take the semaphore to ensure other threads are not interacting with
|
||||
// the underlying file.
|
||||
let _lock = semaphore.lock().unwrap();
|
||||
|
||||
Ok(file.seek(SeekFrom::End(0)).map_err(DiskFileError::Size)? as u64)
|
||||
}
|
||||
|
||||
pub trait ReadSeekFile: Read + Seek {}
|
||||
impl<F: Read + Seek> ReadSeekFile for F {}
|
||||
|
||||
pub fn read_vectored_sync(
|
||||
offset: libc::off_t,
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
file: &mut dyn ReadSeekFile,
|
||||
eventfd: &EventFd,
|
||||
completion_list: &mut Vec<(u64, i32)>,
|
||||
semaphore: &mut Arc<Mutex<()>>,
|
||||
) -> AsyncIoResult<()> {
|
||||
// Convert libc::iovec into IoSliceMut
|
||||
let mut slices = Vec::new();
|
||||
for iovec in iovecs.iter() {
|
||||
slices.push(IoSliceMut::new(unsafe { std::mem::transmute(*iovec) }));
|
||||
}
|
||||
|
||||
let result = {
|
||||
// Take the semaphore to ensure other threads are not interacting
|
||||
// with the underlying file.
|
||||
let _lock = semaphore.lock().unwrap();
|
||||
|
||||
// Move the cursor to the right offset
|
||||
file.seek(SeekFrom::Start(offset as u64))
|
||||
.map_err(AsyncIoError::ReadVectored)?;
|
||||
|
||||
// Read vectored
|
||||
file.read_vectored(slices.as_mut_slice())
|
||||
.map_err(AsyncIoError::ReadVectored)?
|
||||
};
|
||||
|
||||
completion_list.push((user_data, result as i32));
|
||||
eventfd.write(1).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub trait WriteSeekFile: Write + Seek {}
|
||||
impl<F: Write + Seek> WriteSeekFile for F {}
|
||||
|
||||
pub fn write_vectored_sync(
|
||||
offset: libc::off_t,
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
file: &mut dyn WriteSeekFile,
|
||||
eventfd: &EventFd,
|
||||
completion_list: &mut Vec<(u64, i32)>,
|
||||
semaphore: &mut Arc<Mutex<()>>,
|
||||
) -> AsyncIoResult<()> {
|
||||
// Convert libc::iovec into IoSlice
|
||||
let mut slices = Vec::new();
|
||||
for iovec in iovecs.iter() {
|
||||
slices.push(IoSlice::new(unsafe { std::mem::transmute(*iovec) }));
|
||||
}
|
||||
|
||||
let result = {
|
||||
// Take the semaphore to ensure other threads are not interacting
|
||||
// with the underlying file.
|
||||
let _lock = semaphore.lock().unwrap();
|
||||
|
||||
// Move the cursor to the right offset
|
||||
file.seek(SeekFrom::Start(offset as u64))
|
||||
.map_err(AsyncIoError::WriteVectored)?;
|
||||
|
||||
// Write vectored
|
||||
file.write_vectored(slices.as_slice())
|
||||
.map_err(AsyncIoError::WriteVectored)?
|
||||
};
|
||||
|
||||
completion_list.push((user_data, result as i32));
|
||||
eventfd.write(1).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fsync_sync(
|
||||
user_data: Option<u64>,
|
||||
file: &mut dyn Write,
|
||||
eventfd: &EventFd,
|
||||
completion_list: &mut Vec<(u64, i32)>,
|
||||
semaphore: &mut Arc<Mutex<()>>,
|
||||
) -> AsyncIoResult<()> {
|
||||
let result: i32 = {
|
||||
// Take the semaphore to ensure other threads are not interacting
|
||||
// with the underlying file.
|
||||
let _lock = semaphore.lock().unwrap();
|
||||
|
||||
// Flush
|
||||
file.flush().map_err(AsyncIoError::Fsync)?;
|
||||
|
||||
0
|
||||
};
|
||||
|
||||
if let Some(user_data) = user_data {
|
||||
completion_list.push((user_data, result));
|
||||
eventfd.write(1).unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub enum ImageType {
|
||||
FixedVhd,
|
||||
Qcow2,
|
||||
Raw,
|
||||
}
|
||||
|
||||
const QCOW_MAGIC: u32 = 0x5146_49fb;
|
||||
|
||||
/// Determine image type through file parsing.
|
||||
pub fn detect_image_type(f: &mut File) -> std::io::Result<ImageType> {
|
||||
// We must create a buffer aligned on 512 bytes with a size being a
|
||||
// multiple of 512 bytes as the file might be opened with O_DIRECT flag.
|
||||
#[repr(align(512))]
|
||||
struct Sector {
|
||||
data: [u8; 512],
|
||||
}
|
||||
let mut s = Sector { data: [0; 512] };
|
||||
|
||||
f.read_exact(&mut s.data)?;
|
||||
|
||||
// Check 4 first bytes to get the header value and determine the image type
|
||||
let image_type = if u32::from_be_bytes(s.data[0..4].try_into().unwrap()) == QCOW_MAGIC {
|
||||
ImageType::Qcow2
|
||||
} else if vhd::is_fixed_vhd(f)? {
|
||||
ImageType::FixedVhd
|
||||
} else {
|
||||
ImageType::Raw
|
||||
};
|
||||
|
||||
Ok(image_type)
|
||||
}
|
||||
|
||||
111
block_util/src/qcow_sync.rs
Normal file
111
block_util/src/qcow_sync.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright © 2021 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileResult};
|
||||
use crate::{disk_size, fsync_sync, read_vectored_sync, write_vectored_sync};
|
||||
use qcow::{QcowFile, RawFile};
|
||||
use std::fs::File;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
pub struct QcowDiskSync {
|
||||
qcow_file: QcowFile,
|
||||
semaphore: Arc<Mutex<()>>,
|
||||
}
|
||||
|
||||
impl QcowDiskSync {
|
||||
pub fn new(file: File, direct_io: bool) -> Self {
|
||||
QcowDiskSync {
|
||||
qcow_file: QcowFile::from(RawFile::new(file, direct_io))
|
||||
.expect("Failed creating QcowFile"),
|
||||
semaphore: Arc::new(Mutex::new(())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DiskFile for QcowDiskSync {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
disk_size(&mut self.qcow_file, &mut self.semaphore)
|
||||
}
|
||||
|
||||
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
Ok(Box::new(QcowSync::new(
|
||||
self.qcow_file.clone(),
|
||||
self.semaphore.clone(),
|
||||
)) as Box<dyn AsyncIo>)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QcowSync {
|
||||
qcow_file: QcowFile,
|
||||
eventfd: EventFd,
|
||||
completion_list: Vec<(u64, i32)>,
|
||||
semaphore: Arc<Mutex<()>>,
|
||||
}
|
||||
|
||||
impl QcowSync {
|
||||
pub fn new(qcow_file: QcowFile, semaphore: Arc<Mutex<()>>) -> Self {
|
||||
QcowSync {
|
||||
qcow_file,
|
||||
eventfd: EventFd::new(libc::EFD_NONBLOCK)
|
||||
.expect("Failed creating EventFd for QcowSync"),
|
||||
completion_list: Vec::new(),
|
||||
semaphore,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncIo for QcowSync {
|
||||
fn notifier(&self) -> &EventFd {
|
||||
&self.eventfd
|
||||
}
|
||||
|
||||
fn read_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
read_vectored_sync(
|
||||
offset,
|
||||
iovecs,
|
||||
user_data,
|
||||
&mut self.qcow_file,
|
||||
&self.eventfd,
|
||||
&mut self.completion_list,
|
||||
&mut self.semaphore,
|
||||
)
|
||||
}
|
||||
|
||||
fn write_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
write_vectored_sync(
|
||||
offset,
|
||||
iovecs,
|
||||
user_data,
|
||||
&mut self.qcow_file,
|
||||
&self.eventfd,
|
||||
&mut self.completion_list,
|
||||
&mut self.semaphore,
|
||||
)
|
||||
}
|
||||
|
||||
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
|
||||
fsync_sync(
|
||||
user_data,
|
||||
&mut self.qcow_file,
|
||||
&self.eventfd,
|
||||
&mut self.completion_list,
|
||||
&mut self.semaphore,
|
||||
)
|
||||
}
|
||||
|
||||
fn complete(&mut self) -> Vec<(u64, i32)> {
|
||||
self.completion_list.drain(..).collect()
|
||||
}
|
||||
}
|
||||
170
block_util/src/raw_async.rs
Normal file
170
block_util/src/raw_async.rs
Normal file
@@ -0,0 +1,170 @@
|
||||
// Copyright © 2021 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
||||
};
|
||||
use io_uring::{opcode, squeue, IoUring};
|
||||
use std::fs::File;
|
||||
use std::io::{Seek, SeekFrom};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
pub struct RawFileDisk {
|
||||
file: File,
|
||||
}
|
||||
|
||||
impl RawFileDisk {
|
||||
pub fn new(file: File) -> Self {
|
||||
RawFileDisk { file }
|
||||
}
|
||||
}
|
||||
|
||||
impl DiskFile for RawFileDisk {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
Ok(self
|
||||
.file
|
||||
.seek(SeekFrom::End(0))
|
||||
.map_err(DiskFileError::Size)? as u64)
|
||||
}
|
||||
|
||||
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
Ok(Box::new(
|
||||
RawFileAsync::new(self.file.as_raw_fd(), ring_depth)
|
||||
.map_err(DiskFileError::NewAsyncIo)?,
|
||||
) as Box<dyn AsyncIo>)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RawFileAsync {
|
||||
fd: RawFd,
|
||||
io_uring: IoUring,
|
||||
eventfd: EventFd,
|
||||
}
|
||||
|
||||
impl RawFileAsync {
|
||||
pub fn new(fd: RawFd, ring_depth: u32) -> std::io::Result<Self> {
|
||||
let io_uring = IoUring::new(ring_depth)?;
|
||||
let eventfd = EventFd::new(libc::EFD_NONBLOCK)?;
|
||||
|
||||
// Register the io_uring eventfd that will notify when something in
|
||||
// the completion queue is ready.
|
||||
io_uring.submitter().register_eventfd(eventfd.as_raw_fd())?;
|
||||
|
||||
Ok(RawFileAsync {
|
||||
fd,
|
||||
io_uring,
|
||||
eventfd,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncIo for RawFileAsync {
|
||||
fn notifier(&self) -> &EventFd {
|
||||
&self.eventfd
|
||||
}
|
||||
|
||||
fn read_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
let (submitter, sq, _) = self.io_uring.split();
|
||||
let mut avail_sq = sq.available();
|
||||
|
||||
// Safe because we know the file descriptor is valid and we
|
||||
// relied on vm-memory to provide the buffer address.
|
||||
let _ = unsafe {
|
||||
avail_sq.push(
|
||||
opcode::Readv::new(
|
||||
opcode::types::Fd(self.fd),
|
||||
iovecs.as_ptr(),
|
||||
iovecs.len() as u32,
|
||||
)
|
||||
.offset(offset)
|
||||
.build()
|
||||
.flags(squeue::Flags::ASYNC)
|
||||
.user_data(user_data),
|
||||
)
|
||||
};
|
||||
|
||||
// Update the submission queue and submit new operations to the
|
||||
// io_uring instance.
|
||||
avail_sq.sync();
|
||||
submitter.submit().map_err(AsyncIoError::ReadVectored)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
let (submitter, sq, _) = self.io_uring.split();
|
||||
let mut avail_sq = sq.available();
|
||||
|
||||
// Safe because we know the file descriptor is valid and we
|
||||
// relied on vm-memory to provide the buffer address.
|
||||
let _ = unsafe {
|
||||
avail_sq.push(
|
||||
opcode::Writev::new(
|
||||
opcode::types::Fd(self.fd),
|
||||
iovecs.as_ptr(),
|
||||
iovecs.len() as u32,
|
||||
)
|
||||
.offset(offset)
|
||||
.build()
|
||||
.flags(squeue::Flags::ASYNC)
|
||||
.user_data(user_data),
|
||||
)
|
||||
};
|
||||
|
||||
// Update the submission queue and submit new operations to the
|
||||
// io_uring instance.
|
||||
avail_sq.sync();
|
||||
submitter.submit().map_err(AsyncIoError::WriteVectored)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
|
||||
if let Some(user_data) = user_data {
|
||||
let (submitter, sq, _) = self.io_uring.split();
|
||||
let mut avail_sq = sq.available();
|
||||
|
||||
// Safe because we know the file descriptor is valid.
|
||||
let _ = unsafe {
|
||||
avail_sq.push(
|
||||
opcode::Fsync::new(opcode::types::Fd(self.fd))
|
||||
.build()
|
||||
.flags(squeue::Flags::ASYNC)
|
||||
.user_data(user_data),
|
||||
)
|
||||
};
|
||||
|
||||
// Update the submission queue and submit new operations to the
|
||||
// io_uring instance.
|
||||
avail_sq.sync();
|
||||
submitter.submit().map_err(AsyncIoError::Fsync)?;
|
||||
} else {
|
||||
unsafe { libc::fsync(self.fd) };
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn complete(&mut self) -> Vec<(u64, i32)> {
|
||||
let mut completion_list = Vec::new();
|
||||
|
||||
let cq = self.io_uring.completion();
|
||||
for cq_entry in cq.available() {
|
||||
completion_list.push((cq_entry.user_data(), cq_entry.result()));
|
||||
}
|
||||
|
||||
completion_list
|
||||
}
|
||||
}
|
||||
122
block_util/src/raw_sync.rs
Normal file
122
block_util/src/raw_sync.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright © 2021 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
||||
};
|
||||
use std::fs::File;
|
||||
use std::io::{Seek, SeekFrom};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
pub struct RawFileDiskSync {
|
||||
file: File,
|
||||
}
|
||||
|
||||
impl RawFileDiskSync {
|
||||
pub fn new(file: File) -> Self {
|
||||
RawFileDiskSync { file }
|
||||
}
|
||||
}
|
||||
|
||||
impl DiskFile for RawFileDiskSync {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
Ok(self
|
||||
.file
|
||||
.seek(SeekFrom::End(0))
|
||||
.map_err(DiskFileError::Size)? as u64)
|
||||
}
|
||||
|
||||
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
Ok(Box::new(RawFileSync::new(self.file.as_raw_fd())) as Box<dyn AsyncIo>)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RawFileSync {
|
||||
fd: RawFd,
|
||||
eventfd: EventFd,
|
||||
completion_list: Vec<(u64, i32)>,
|
||||
}
|
||||
|
||||
impl RawFileSync {
|
||||
pub fn new(fd: RawFd) -> Self {
|
||||
RawFileSync {
|
||||
fd,
|
||||
eventfd: EventFd::new(libc::EFD_NONBLOCK).expect("Failed creating EventFd for RawFile"),
|
||||
completion_list: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncIo for RawFileSync {
|
||||
fn notifier(&self) -> &EventFd {
|
||||
&self.eventfd
|
||||
}
|
||||
|
||||
fn read_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
let result = unsafe {
|
||||
libc::preadv(
|
||||
self.fd as libc::c_int,
|
||||
iovecs.as_ptr() as *const libc::iovec,
|
||||
iovecs.len() as libc::c_int,
|
||||
offset,
|
||||
)
|
||||
};
|
||||
if result < 0 {
|
||||
return Err(AsyncIoError::ReadVectored(std::io::Error::last_os_error()));
|
||||
}
|
||||
|
||||
self.completion_list.push((user_data, result as i32));
|
||||
self.eventfd.write(1).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
let result = unsafe {
|
||||
libc::pwritev(
|
||||
self.fd as libc::c_int,
|
||||
iovecs.as_ptr() as *const libc::iovec,
|
||||
iovecs.len() as libc::c_int,
|
||||
offset,
|
||||
)
|
||||
};
|
||||
if result < 0 {
|
||||
return Err(AsyncIoError::WriteVectored(std::io::Error::last_os_error()));
|
||||
}
|
||||
|
||||
self.completion_list.push((user_data, result as i32));
|
||||
self.eventfd.write(1).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
|
||||
let result = unsafe { libc::fsync(self.fd as libc::c_int) };
|
||||
if result < 0 {
|
||||
return Err(AsyncIoError::Fsync(std::io::Error::last_os_error()));
|
||||
}
|
||||
|
||||
if let Some(user_data) = user_data {
|
||||
self.completion_list.push((user_data, result as i32));
|
||||
self.eventfd.write(1).unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn complete(&mut self) -> Vec<(u64, i32)> {
|
||||
self.completion_list.drain(..).collect()
|
||||
}
|
||||
}
|
||||
221
block_util/src/vhd.rs
Normal file
221
block_util/src/vhd.rs
Normal file
@@ -0,0 +1,221 @@
|
||||
// Copyright © 2021 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct VhdFooter {
|
||||
cookie: u64,
|
||||
features: u32,
|
||||
file_format_version: u32,
|
||||
data_offset: u64,
|
||||
time_stamp: u32,
|
||||
creator_application: u32,
|
||||
creator_version: u32,
|
||||
creator_host_os: u32,
|
||||
original_size: u64,
|
||||
current_size: u64,
|
||||
disk_geometry: u32,
|
||||
disk_type: u32,
|
||||
checksum: u32,
|
||||
unique_id: u128,
|
||||
saved_state: u8,
|
||||
}
|
||||
|
||||
impl VhdFooter {
|
||||
pub fn new(file: &mut File) -> std::io::Result<VhdFooter> {
|
||||
// We must create a buffer aligned on 512 bytes with a size being a
|
||||
// multiple of 512 bytes as the file might be opened with O_DIRECT flag.
|
||||
#[repr(align(512))]
|
||||
struct Sector {
|
||||
data: [u8; 512],
|
||||
}
|
||||
let mut s = Sector { data: [0; 512] };
|
||||
|
||||
// Place the cursor 512 bytes before the end of the file, as this is
|
||||
// where the footer starts.
|
||||
file.seek(SeekFrom::End(-512))?;
|
||||
|
||||
// Fill in the VhdFooter structure
|
||||
file.read_exact(&mut s.data)?;
|
||||
|
||||
Ok(VhdFooter {
|
||||
cookie: u64::from_be_bytes(s.data[0..8].try_into().unwrap()),
|
||||
features: u32::from_be_bytes(s.data[8..12].try_into().unwrap()),
|
||||
file_format_version: u32::from_be_bytes(s.data[12..16].try_into().unwrap()),
|
||||
data_offset: u64::from_be_bytes(s.data[16..24].try_into().unwrap()),
|
||||
time_stamp: u32::from_be_bytes(s.data[24..28].try_into().unwrap()),
|
||||
creator_application: u32::from_be_bytes(s.data[28..32].try_into().unwrap()),
|
||||
creator_version: u32::from_be_bytes(s.data[32..36].try_into().unwrap()),
|
||||
creator_host_os: u32::from_be_bytes(s.data[36..40].try_into().unwrap()),
|
||||
original_size: u64::from_be_bytes(s.data[40..48].try_into().unwrap()),
|
||||
current_size: u64::from_be_bytes(s.data[48..56].try_into().unwrap()),
|
||||
disk_geometry: u32::from_be_bytes(s.data[56..60].try_into().unwrap()),
|
||||
disk_type: u32::from_be_bytes(s.data[60..64].try_into().unwrap()),
|
||||
checksum: u32::from_be_bytes(s.data[64..68].try_into().unwrap()),
|
||||
unique_id: u128::from_be_bytes(s.data[68..84].try_into().unwrap()),
|
||||
saved_state: u8::from_be_bytes(s.data[84..85].try_into().unwrap()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn cookie(&self) -> u64 {
|
||||
self.cookie
|
||||
}
|
||||
pub fn features(&self) -> u32 {
|
||||
self.features
|
||||
}
|
||||
pub fn file_format_version(&self) -> u32 {
|
||||
self.file_format_version
|
||||
}
|
||||
pub fn data_offset(&self) -> u64 {
|
||||
self.data_offset
|
||||
}
|
||||
pub fn time_stamp(&self) -> u32 {
|
||||
self.time_stamp
|
||||
}
|
||||
pub fn creator_application(&self) -> u32 {
|
||||
self.creator_application
|
||||
}
|
||||
pub fn creator_version(&self) -> u32 {
|
||||
self.creator_version
|
||||
}
|
||||
pub fn creator_host_os(&self) -> u32 {
|
||||
self.creator_host_os
|
||||
}
|
||||
pub fn original_size(&self) -> u64 {
|
||||
self.original_size
|
||||
}
|
||||
pub fn current_size(&self) -> u64 {
|
||||
self.current_size
|
||||
}
|
||||
pub fn disk_geometry(&self) -> u32 {
|
||||
self.disk_geometry
|
||||
}
|
||||
pub fn disk_type(&self) -> u32 {
|
||||
self.disk_type
|
||||
}
|
||||
pub fn checksum(&self) -> u32 {
|
||||
self.checksum
|
||||
}
|
||||
pub fn unique_id(&self) -> u128 {
|
||||
self.unique_id
|
||||
}
|
||||
pub fn saved_state(&self) -> u8 {
|
||||
self.saved_state
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine image type through file parsing.
|
||||
pub fn is_fixed_vhd(f: &mut File) -> std::io::Result<bool> {
|
||||
let footer = VhdFooter::new(f)?;
|
||||
|
||||
// "conectix" => 0x636f6e6563746978
|
||||
Ok(footer.cookie() == 0x636f6e6563746978
|
||||
&& footer.file_format_version() == 0x0001_0000
|
||||
&& footer.data_offset() == 0xffff_ffff_ffff_ffff
|
||||
&& footer.disk_type() == 0x2)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{is_fixed_vhd, VhdFooter};
|
||||
use std::fs::File;
|
||||
use std::io::{Seek, SeekFrom, Write};
|
||||
use tempfile::tempfile;
|
||||
|
||||
fn valid_fixed_vhd_footer() -> Vec<u8> {
|
||||
vec![
|
||||
0x63, 0x6f, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x78, // cookie
|
||||
0x00, 0x00, 0x00, 0x02, // features
|
||||
0x00, 0x01, 0x00, 0x00, // file format version
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // data offset
|
||||
0x27, 0xa6, 0xa6, 0x5d, // time stamp
|
||||
0x71, 0x65, 0x6d, 0x75, // creator application
|
||||
0x00, 0x05, 0x00, 0x03, // creator version
|
||||
0x57, 0x69, 0x32, 0x6b, // creator host os
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // original size
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // current size
|
||||
0x11, 0xe0, 0x10, 0x3f, // disk geometry
|
||||
0x00, 0x00, 0x00, 0x02, // disk type
|
||||
0x00, 0x00, 0x00, 0x00, // checksum
|
||||
0x98, 0x7b, 0xb1, 0xcd, 0x84, 0x14, 0x41, 0xfc, 0xa4, 0xab, 0xd0, 0x69, 0x45, 0x2b,
|
||||
0xf2, 0x23, // unique id
|
||||
0x00, // saved state
|
||||
]
|
||||
}
|
||||
|
||||
fn valid_dynamic_vhd_footer() -> Vec<u8> {
|
||||
vec![
|
||||
0x63, 0x6f, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x78, // cookie
|
||||
0x00, 0x00, 0x00, 0x02, // features
|
||||
0x00, 0x01, 0x00, 0x00, // file format version
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // data offset
|
||||
0x27, 0xa6, 0xa6, 0x5d, // time stamp
|
||||
0x71, 0x65, 0x6d, 0x75, // creator application
|
||||
0x00, 0x05, 0x00, 0x03, // creator version
|
||||
0x57, 0x69, 0x32, 0x6b, // creator host os
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // original size
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // current size
|
||||
0x11, 0xe0, 0x10, 0x3f, // disk geometry
|
||||
0x00, 0x00, 0x00, 0x03, // disk type
|
||||
0x00, 0x00, 0x00, 0x00, // checksum
|
||||
0x98, 0x7b, 0xb1, 0xcd, 0x84, 0x14, 0x41, 0xfc, 0xa4, 0xab, 0xd0, 0x69, 0x45, 0x2b,
|
||||
0xf2, 0x23, // unique id
|
||||
0x00, // saved state
|
||||
]
|
||||
}
|
||||
|
||||
fn with_file<F>(footer: &[u8], mut testfn: F)
|
||||
where
|
||||
F: FnMut(File),
|
||||
{
|
||||
let mut disk_file: File = tempfile().unwrap();
|
||||
disk_file.set_len(0x1000_0200).unwrap();
|
||||
disk_file.seek(SeekFrom::Start(0x1000_0000)).unwrap();
|
||||
disk_file.write_all(&footer).unwrap();
|
||||
|
||||
testfn(disk_file); // File closed when the function exits.
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_vhd_footer() {
|
||||
with_file(&valid_fixed_vhd_footer(), |mut file: File| {
|
||||
let vhd_footer = VhdFooter::new(&mut file).expect("Failed to create VHD footer");
|
||||
assert_eq!(vhd_footer.cookie(), 0x636f_6e65_6374_6978);
|
||||
assert_eq!(vhd_footer.features(), 0x0000_0002);
|
||||
assert_eq!(vhd_footer.file_format_version(), 0x0001_0000);
|
||||
assert_eq!(vhd_footer.data_offset(), 0xffff_ffff_ffff_ffff);
|
||||
assert_eq!(vhd_footer.time_stamp(), 0x27a6_a65d);
|
||||
assert_eq!(vhd_footer.creator_application(), 0x7165_6d75);
|
||||
assert_eq!(vhd_footer.creator_version(), 0x0005_0003);
|
||||
assert_eq!(vhd_footer.creator_host_os(), 0x5769_326b);
|
||||
assert_eq!(vhd_footer.original_size(), 0x0000_0000_1000_0000);
|
||||
assert_eq!(vhd_footer.current_size(), 0x0000_0000_1000_0000);
|
||||
assert_eq!(vhd_footer.disk_geometry(), 0x11e0_103f);
|
||||
assert_eq!(vhd_footer.disk_type(), 0x0000_0002);
|
||||
assert_eq!(vhd_footer.checksum(), 0x0000_0000);
|
||||
assert_eq!(
|
||||
vhd_footer.unique_id(),
|
||||
0x987b_b1cd_8414_41fc_a4ab_d069_452b_f223
|
||||
);
|
||||
assert_eq!(vhd_footer.saved_state(), 0x00);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_fixed_vhd() {
|
||||
with_file(&valid_fixed_vhd_footer(), |mut file: File| {
|
||||
assert!(is_fixed_vhd(&mut file).unwrap());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_not_fixed_vhd() {
|
||||
with_file(&valid_dynamic_vhd_footer(), |mut file: File| {
|
||||
assert!(!(is_fixed_vhd(&mut file).unwrap()));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -8,19 +8,19 @@ anyhow = "1.0"
|
||||
bitflags = ">=1.2.1"
|
||||
byteorder = "1.3.4"
|
||||
epoll = ">=4.0.1"
|
||||
libc = "0.2.81"
|
||||
log = "0.4.11"
|
||||
libc = "0.2.86"
|
||||
log = "0.4.14"
|
||||
serde = {version = ">=1.0.27", features = ["rc"] }
|
||||
serde_derive = ">=1.0.27"
|
||||
serde_json = ">=1.0.9"
|
||||
vm-device = { path = "../vm-device" }
|
||||
acpi_tables = { path = "../acpi_tables", optional = true }
|
||||
vm-memory = "0.4.0"
|
||||
vm-memory = "0.5.0"
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vmm-sys-util = ">=0.3.1"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.1.0"
|
||||
tempfile = "3.2.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -4,12 +4,15 @@
|
||||
//
|
||||
|
||||
use acpi_tables::{aml, aml::Aml};
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::time::Instant;
|
||||
use vm_device::interrupt::InterruptSourceGroup;
|
||||
use vm_device::BusDevice;
|
||||
use vm_memory::GuestAddress;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
use HotPlugNotificationFlags;
|
||||
use AcpiNotificationFlags;
|
||||
|
||||
pub const GED_DEVICE_ACPI_SIZE: usize = 0x1;
|
||||
|
||||
/// A device for handling ACPI shutdown and reboot
|
||||
pub struct AcpiShutdownDevice {
|
||||
@@ -36,7 +39,7 @@ impl BusDevice for AcpiShutdownDevice {
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
if data[0] == 1 {
|
||||
debug!("ACPI Reboot signalled");
|
||||
if let Err(e) = self.reset_evt.write(1) {
|
||||
@@ -54,28 +57,35 @@ impl BusDevice for AcpiShutdownDevice {
|
||||
error!("Error triggering ACPI shutdown event: {}", e);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// A device for handling ACPI GED event generation
|
||||
pub struct AcpiGEDDevice {
|
||||
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
|
||||
notification_type: HotPlugNotificationFlags,
|
||||
notification_type: AcpiNotificationFlags,
|
||||
ged_irq: u32,
|
||||
address: GuestAddress,
|
||||
}
|
||||
|
||||
impl AcpiGEDDevice {
|
||||
pub fn new(interrupt: Arc<Box<dyn InterruptSourceGroup>>, ged_irq: u32) -> AcpiGEDDevice {
|
||||
pub fn new(
|
||||
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
|
||||
ged_irq: u32,
|
||||
address: GuestAddress,
|
||||
) -> AcpiGEDDevice {
|
||||
AcpiGEDDevice {
|
||||
interrupt,
|
||||
notification_type: HotPlugNotificationFlags::NO_DEVICES_CHANGED,
|
||||
notification_type: AcpiNotificationFlags::NO_DEVICES_CHANGED,
|
||||
ged_irq,
|
||||
address,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify(
|
||||
&mut self,
|
||||
notification_type: HotPlugNotificationFlags,
|
||||
notification_type: AcpiNotificationFlags,
|
||||
) -> Result<(), std::io::Error> {
|
||||
self.notification_type |= notification_type;
|
||||
self.interrupt.trigger(0)
|
||||
@@ -91,10 +101,8 @@ impl BusDevice for AcpiGEDDevice {
|
||||
// Spec has all fields as zero
|
||||
fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
|
||||
data[0] = self.notification_type.bits();
|
||||
self.notification_type = HotPlugNotificationFlags::NO_DEVICES_CHANGED;
|
||||
self.notification_type = AcpiNotificationFlags::NO_DEVICES_CHANGED;
|
||||
}
|
||||
|
||||
fn write(&mut self, _base: u64, _offset: u64, _data: &[u8]) {}
|
||||
}
|
||||
|
||||
#[cfg(feature = "acpi")]
|
||||
@@ -115,7 +123,12 @@ impl Aml for AcpiGEDDevice {
|
||||
self.ged_irq,
|
||||
)]),
|
||||
),
|
||||
&aml::OpRegion::new("GDST".into(), aml::OpRegionSpace::SystemIO, 0xb000, 0x1),
|
||||
&aml::OpRegion::new(
|
||||
"GDST".into(),
|
||||
aml::OpRegionSpace::SystemMemory,
|
||||
self.address.0 as usize,
|
||||
GED_DEVICE_ACPI_SIZE,
|
||||
),
|
||||
&aml::Field::new(
|
||||
"GDST".into(),
|
||||
aml::FieldAccessType::Byte,
|
||||
@@ -143,6 +156,14 @@ impl Aml for AcpiGEDDevice {
|
||||
&aml::Equal::new(&aml::Local(1), &4usize),
|
||||
vec![&aml::MethodCall::new("\\_SB_.PCI0.PCNT".into(), vec![])],
|
||||
),
|
||||
&aml::And::new(&aml::Local(1), &aml::Local(0), &8usize),
|
||||
&aml::If::new(
|
||||
&aml::Equal::new(&aml::Local(1), &8usize),
|
||||
vec![&aml::Notify::new(
|
||||
&aml::Path::new("\\_SB_.PWRB"),
|
||||
&0x80usize,
|
||||
)],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -183,6 +204,4 @@ impl BusDevice for AcpiPMTimerDevice {
|
||||
|
||||
data.copy_from_slice(&counter.to_le_bytes());
|
||||
}
|
||||
|
||||
fn write(&mut self, _base: u64, _offset: u64, _data: &[u8]) {}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use std::sync::Arc;
|
||||
use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig,
|
||||
};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
@@ -61,4 +62,8 @@ impl InterruptController for Gic {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn notifier(&self, irq: usize) -> Option<EventFd> {
|
||||
self.interrupt_source_group.notifier(irq as InterruptIndex)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
use std::io;
|
||||
use std::result;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
@@ -56,4 +57,5 @@ pub trait InterruptController: Send {
|
||||
fn enable(&self) -> Result<()>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn end_of_interrupt(&mut self, vec: u8);
|
||||
fn notifier(&self, irq: usize) -> Option<EventFd>;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use super::interrupt_controller::{Error, InterruptController};
|
||||
use anyhow::anyhow;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Barrier};
|
||||
use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
|
||||
MsiIrqGroupConfig, MsiIrqSourceConfig,
|
||||
@@ -24,6 +24,7 @@ use vm_migration::{
|
||||
Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable,
|
||||
Transportable,
|
||||
};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(remote = "GuestAddress")]
|
||||
@@ -167,7 +168,7 @@ impl BusDevice for Ioapic {
|
||||
LittleEndian::write_u32(data, value);
|
||||
}
|
||||
|
||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
assert!(data.len() == 4);
|
||||
|
||||
debug!("IOAPIC_W @ offset 0x{:x}", offset);
|
||||
@@ -181,6 +182,7 @@ impl BusDevice for Ioapic {
|
||||
error!("IOAPIC: failed writing at offset {}", offset);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +194,7 @@ impl Ioapic {
|
||||
) -> Result<Ioapic> {
|
||||
let interrupt_source_group = interrupt_manager
|
||||
.create_group(MsiIrqGroupConfig {
|
||||
base: 0 as InterruptIndex,
|
||||
base: 0,
|
||||
count: NUM_IOAPIC_PINS as InterruptIndex,
|
||||
})
|
||||
.map_err(Error::CreateInterruptSourceGroup)?;
|
||||
@@ -404,6 +406,10 @@ impl InterruptController for Ioapic {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn notifier(&self, irq: usize) -> Option<EventFd> {
|
||||
self.interrupt_source_group.notifier(irq as InterruptIndex)
|
||||
}
|
||||
}
|
||||
|
||||
impl Snapshottable for Ioapic {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
use libc::{clock_gettime, gmtime_r, time_t, timespec, tm, CLOCK_REALTIME};
|
||||
use std::cmp::min;
|
||||
use std::mem;
|
||||
use std::sync::{Arc, Barrier};
|
||||
use vm_device::BusDevice;
|
||||
|
||||
const INDEX_MASK: u8 = 0x7f;
|
||||
@@ -44,16 +45,17 @@ impl Cmos {
|
||||
}
|
||||
|
||||
impl BusDevice for Cmos {
|
||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
if data.len() != 1 {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
match offset {
|
||||
INDEX_OFFSET => self.index = data[0] & INDEX_MASK,
|
||||
DATA_OFFSET => self.data[self.index as usize] = data[0],
|
||||
o => panic!("bad write offset on CMOS device: {}", o),
|
||||
}
|
||||
};
|
||||
None
|
||||
}
|
||||
|
||||
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
//
|
||||
|
||||
use std::sync::{Arc, Barrier};
|
||||
use vm_device::BusDevice;
|
||||
|
||||
/// Provides firmware debug output via I/O port controls
|
||||
@@ -30,11 +31,13 @@ impl BusDevice for FwDebugDevice {
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
if data.len() == 1 {
|
||||
print!("{}", data[0] as char);
|
||||
} else {
|
||||
error!("Invalid write size on debug port: {}", data.len())
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE-BSD-3-Clause file.
|
||||
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use std::sync::{Arc, Barrier};
|
||||
use vm_device::BusDevice;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
/// A i8042 PS/2 controller that emulates just enough to shutdown the machine.
|
||||
pub struct I8042Device {
|
||||
@@ -32,12 +32,14 @@ impl BusDevice for I8042Device {
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
if data.len() == 1 && data[0] == 0xfe && offset == 3 {
|
||||
debug!("i8042 reset signalled");
|
||||
if let Err(e) = self.reset_evt.write(1) {
|
||||
error!("Error triggering i8042 reset event: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//! a real-time clock input.
|
||||
//!
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::time::Instant;
|
||||
use std::{io, result};
|
||||
use vm_device::interrupt::InterruptSourceGroup;
|
||||
@@ -339,7 +339,7 @@ impl BusDevice for RTC {
|
||||
let v;
|
||||
let mut read_ok = true;
|
||||
|
||||
if offset < AMBA_ID_HIGH && offset >= AMBA_ID_LOW {
|
||||
if (AMBA_ID_LOW..AMBA_ID_HIGH).contains(&offset) {
|
||||
let index = ((offset - AMBA_ID_LOW) >> 2) as usize;
|
||||
v = u32::from(PL031_ID[index]);
|
||||
} else {
|
||||
@@ -371,7 +371,7 @@ impl BusDevice for RTC {
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
if data.len() <= 4 {
|
||||
let v = read_le_u32(&data[..]);
|
||||
if let Err(e) = self.handle_write(offset, v) {
|
||||
@@ -384,6 +384,8 @@ impl BusDevice for RTC {
|
||||
data.len()
|
||||
);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,8 +476,8 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn notifier(&self, _index: InterruptIndex) -> Option<&EventFd> {
|
||||
Some(&self.event_fd)
|
||||
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
|
||||
Some(self.event_fd.try_clone().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
use anyhow::anyhow;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::{io, result};
|
||||
use vm_device::interrupt::InterruptSourceGroup;
|
||||
use vm_device::BusDevice;
|
||||
@@ -275,12 +275,14 @@ impl BusDevice for Serial {
|
||||
};
|
||||
}
|
||||
|
||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
if data.len() != 1 {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Err(_e) = self.handle_write(offset as u8, data[0]) {}
|
||||
self.handle_write(offset as u8, data[0]).ok();
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,6 +356,9 @@ mod tests {
|
||||
) -> result::Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
|
||||
Some(self.event_fd.try_clone().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl TestInterrupt {
|
||||
@@ -472,13 +477,13 @@ mod tests {
|
||||
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
|
||||
);
|
||||
|
||||
serial.write(0, LCR as u64, &[LCR_DLAB_BIT as u8]);
|
||||
serial.write(0, DLAB_LOW as u64, &[0x12 as u8]);
|
||||
serial.write(0, DLAB_HIGH as u64, &[0x34 as u8]);
|
||||
serial.write(0, LCR as u64, &[LCR_DLAB_BIT]);
|
||||
serial.write(0, DLAB_LOW as u64, &[0x12]);
|
||||
serial.write(0, DLAB_HIGH as u64, &[0x34]);
|
||||
|
||||
let mut data = [0u8];
|
||||
serial.read(0, LCR as u64, &mut data[..]);
|
||||
assert_eq!(data[0], LCR_DLAB_BIT as u8);
|
||||
assert_eq!(data[0], LCR_DLAB_BIT);
|
||||
serial.read(0, DLAB_LOW as u64, &mut data[..]);
|
||||
assert_eq!(data[0], 0x12);
|
||||
serial.read(0, DLAB_HIGH as u64, &mut data[..]);
|
||||
@@ -493,16 +498,16 @@ mod tests {
|
||||
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
|
||||
);
|
||||
|
||||
serial.write(0, MCR as u64, &[MCR_LOOP_BIT as u8]);
|
||||
serial.write(0, MCR as u64, &[MCR_LOOP_BIT]);
|
||||
serial.write(0, DATA as u64, &[b'a']);
|
||||
serial.write(0, DATA as u64, &[b'b']);
|
||||
serial.write(0, DATA as u64, &[b'c']);
|
||||
|
||||
let mut data = [0u8];
|
||||
serial.read(0, MSR as u64, &mut data[..]);
|
||||
assert_eq!(data[0], DEFAULT_MODEM_STATUS as u8);
|
||||
assert_eq!(data[0], DEFAULT_MODEM_STATUS);
|
||||
serial.read(0, MCR as u64, &mut data[..]);
|
||||
assert_eq!(data[0], MCR_LOOP_BIT as u8);
|
||||
assert_eq!(data[0], MCR_LOOP_BIT);
|
||||
serial.read(0, DATA as u64, &mut data[..]);
|
||||
assert_eq!(data[0], b'a');
|
||||
serial.read(0, DATA as u64, &mut data[..]);
|
||||
@@ -519,10 +524,10 @@ mod tests {
|
||||
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
|
||||
);
|
||||
|
||||
serial.write(0, SCR as u64, &[0x12 as u8]);
|
||||
serial.write(0, SCR as u64, &[0x12]);
|
||||
|
||||
let mut data = [0u8];
|
||||
serial.read(0, SCR as u64, &mut data[..]);
|
||||
assert_eq!(data[0], 0x12 as u8);
|
||||
assert_eq!(data[0], 0x12);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ extern crate serde_derive;
|
||||
extern crate serde_json;
|
||||
|
||||
#[cfg(feature = "acpi")]
|
||||
mod acpi;
|
||||
pub mod acpi;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub mod gic;
|
||||
pub mod interrupt_controller;
|
||||
@@ -38,10 +38,11 @@ pub mod legacy;
|
||||
pub use self::acpi::{AcpiGEDDevice, AcpiPMTimerDevice, AcpiShutdownDevice};
|
||||
|
||||
bitflags! {
|
||||
pub struct HotPlugNotificationFlags: u8 {
|
||||
pub struct AcpiNotificationFlags: u8 {
|
||||
const NO_DEVICES_CHANGED = 0;
|
||||
const CPU_DEVICES_CHANGED = 0b1;
|
||||
const MEMORY_DEVICES_CHANGED = 0b10;
|
||||
const PCI_DEVICES_CHANGED = 0b100;
|
||||
const POWER_BUTTON_CHANGED = 0b1000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,16 +81,24 @@ apt update
|
||||
apt install fio iperf iperf3 socat
|
||||
```
|
||||
|
||||
### Remove snapd
|
||||
### Remove counterproductive packages
|
||||
|
||||
* snapd:
|
||||
|
||||
This prevents snapd from trying to mount squashfs filesystem when the kernel
|
||||
might not support it. This might be the case when the image is used with direct
|
||||
kernel boot. This step is specific to Ubuntu distributions.
|
||||
|
||||
* pollinate:
|
||||
|
||||
Remove this package which can fail and lead to the SSH daemon failing to start.
|
||||
See #2113 for details.
|
||||
|
||||
```bash
|
||||
apt remove --purge snapd
|
||||
apt remove --purge snapd pollinate
|
||||
```
|
||||
|
||||
|
||||
### Cleanup the image
|
||||
|
||||
Leave no trace in the image before unmounting its content.
|
||||
@@ -108,15 +116,18 @@ umount /mnt
|
||||
Renaming is important to identify this is a modified image.
|
||||
|
||||
```bash
|
||||
mv focal-server-cloudimg-amd64.raw focal-server-cloudimg-amd64-custom.raw
|
||||
mv focal-server-cloudimg-amd64.raw focal-server-cloudimg-amd64-custom-$(date "+%Y%m%d")-0.raw
|
||||
```
|
||||
|
||||
The `-0` is the revision and is only necessary to change if multiple images are
|
||||
updated on the same day.
|
||||
|
||||
### Create QCOW2 from RAW
|
||||
|
||||
Last step is to create the QCOW2 image back from the modified image.
|
||||
|
||||
```bash
|
||||
qemu-img convert -p -f raw -O qcow2 focal-server-cloudimg-amd64-custom.raw focal-server-cloudimg-amd64-custom.qcow2
|
||||
qemu-img convert -p -f raw -O qcow2 focal-server-cloudimg-amd64-custom-$(date "+%Y%m%d")-0.raw focal-server-cloudimg-amd64-custom-$(date "+%Y%m%d")-0.qcow2
|
||||
```
|
||||
|
||||
## Switch CI to use the new image
|
||||
@@ -134,3 +145,5 @@ Last step is about updating the integration tests to work with this new image.
|
||||
The key point is to identify where the Linux filesystem partition is located,
|
||||
as we might need to update the direct kernel boot command line, replacing
|
||||
`/dev/vda1` with the appropriate partition number.
|
||||
|
||||
Update all references to the previous image name to the new one.
|
||||
|
||||
@@ -8,7 +8,7 @@ the host kernel. The required Linux and KVM changes can be found in the
|
||||
[KVM SGX Tree](https://github.com/intel/kvm-sgx).
|
||||
|
||||
Utilizing SGX in the guest requires a kernel/OS with SGX support, e.g. a kernel
|
||||
built using the [SGX Linux Development Tree](https://github.com/jsakkine-intel/linux-sgx.git)
|
||||
built using the [SGX Linux Development Tree](https://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-sgx.git)
|
||||
or the [KVM SGX Tree](https://github.com/intel/kvm-sgx). Running KVM SGX as the
|
||||
guest kernel allows nested virtualization of SGX.
|
||||
|
||||
@@ -19,7 +19,7 @@ following [instructions](https://github.com/intel/linux-sgx).
|
||||
|
||||
## Cloud-Hypervisor support
|
||||
|
||||
Assuming the host exposes `/dev/sgx/virt_epc`, we can pass SGX enclaves through
|
||||
Assuming the host exposes `/dev/sgx_virt_epc`, we can pass SGX enclaves through
|
||||
the guest.
|
||||
|
||||
In order to use SGX enclaves within a Cloud-Hypervisor VM, we must define one
|
||||
@@ -42,12 +42,12 @@ Once booted, and assuming your guest kernel contains the patches from the
|
||||
have been correctly created under `/dev/sgx`:
|
||||
|
||||
```bash
|
||||
ls /dev/sgx
|
||||
enclave provision virt_epc
|
||||
ls /dev/sgx*
|
||||
/dev/sgx_enclave /dev/sgx_provision /dev/sgx_virt_epc
|
||||
```
|
||||
|
||||
From this point, it is possible to run any SGX application from the guest, as
|
||||
it will access `/dev/sgx/enclave` device to create dedicated SGX enclaves.
|
||||
it will access `/dev/sgx_enclave` device to create dedicated SGX enclaves.
|
||||
|
||||
Note: There is only one contiguous SGX EPC region, which contains all SGX EPC
|
||||
sections. This region is exposed through ACPI and marked as reserved through
|
||||
|
||||
32
docs/macvtap-bridge.md
Normal file
32
docs/macvtap-bridge.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Using MACVTAP to Bridge onto Host Network
|
||||
|
||||
Cloud Hypervisor supports using a MACVTAP device which is derived from a MACVLAN. Full details of configuring MACVLAN or MACVTAP is out of scope of this document. However the example below indicates how to bridge the guest directly onto the the network the host is on. Due to the lack of hairpin mode it not usually possible to reach the guest directly from the host.
|
||||
|
||||
```bash
|
||||
# The MAC address must be attached to the macvtap and be used inside the guest
|
||||
mac="c2:67:4f:53:29:cb"
|
||||
# Host network adapter to bridge the guest onto
|
||||
host_net="eno1"
|
||||
|
||||
# Create the macvtap0 as a new virtual MAC associated with the host network
|
||||
sudo ip link add link "$host_net" name macvtap0 type macvtap
|
||||
sudo ip link set macvtap0 address "$mac" up
|
||||
sudo ip link show macvtap0
|
||||
|
||||
# A new character device is created for this interface
|
||||
tapindex=$(< /sys/class/net/macvtap0/ifindex)
|
||||
tapdevice="/dev/tap$tapindex"
|
||||
|
||||
# Ensure that we can access this device
|
||||
sudo chown "$UID.$UID" "$tapdevice"
|
||||
|
||||
# Use --net fd=3 to point to fd 3 which the shell has opened to point to the /dev/tapN device
|
||||
target/debug/cloud-hypervisor \
|
||||
--kernel ~/src/linux/vmlinux \
|
||||
--disk path=~/workloads/focal.raw \
|
||||
--cpus boot=1 --memory size=512M \
|
||||
--cmdline "root=/dev/vda1 console=hvc0" \
|
||||
--net fd=3,mac=$mac 3<>$"$tapdevice"
|
||||
```
|
||||
|
||||
As the guest is now connected to the same L2 network as the host you can obtain an IP address based on your host network (potentially including via DHCP)
|
||||
@@ -15,6 +15,7 @@ struct MemoryConfig {
|
||||
mergeable: bool,
|
||||
shared: bool,
|
||||
hugepages: bool,
|
||||
hugepage_size: Option<u64>,
|
||||
hotplug_method: HotplugMethod,
|
||||
hotplug_size: Option<u64>,
|
||||
hotplugged_size: Option<u64>,
|
||||
@@ -76,23 +77,24 @@ _Example_
|
||||
--memory size=1G,shared=on
|
||||
```
|
||||
|
||||
### `hugepages`
|
||||
### `hugepages` and `hugepage_size`
|
||||
|
||||
Specifies if the memory must be `mmap(2)` with `MAP_HUGETLB` and `MAP_HUGE_2MB`
|
||||
flags. This performs a memory mapping relying on 2MiB pages instead of the
|
||||
default 4kiB pages.
|
||||
Specifies if the memory must be created and `mmap(2)` with `MAP_HUGETLB` and size
|
||||
flags. This performs a memory mapping relying on the specified huge page size. If no huge page size is supplied the system's default huge page size is used.
|
||||
|
||||
By using hugepages, one can improve the overall performance of the VM, assuming
|
||||
the guest will allocate hugepages as well. Another interesting use case is VFIO
|
||||
as it speeds up the VM's boot time since the amount of IOMMU mappings are
|
||||
reduced.
|
||||
|
||||
The user is responsible for ensuring there are sufficient huge pages of the specified size for the VMM to use. Failure to do so may result in strange VMM behaviour.
|
||||
|
||||
By default this option is turned off.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--memory size=1G,hugepages=on
|
||||
--memory size=1G,hugepages=on,hugepage_size=2M
|
||||
```
|
||||
|
||||
### `hotplug_method`
|
||||
|
||||
@@ -78,5 +78,3 @@ takes the device's sysfs path as an argument. In our example it is
|
||||
The guest kernel will then detect the card reader on its PCI bus and provided
|
||||
that support for this device is enabled, it will probe and enable it for the
|
||||
guest to use.
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ edition = "2018"
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
block_util = { path = "../block_util" }
|
||||
libc = "0.2.72"
|
||||
libfuzzer-sys = "0.3"
|
||||
qcow = { path = "../qcow" }
|
||||
@@ -16,7 +17,7 @@ seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v
|
||||
virtio-devices = { path = "../virtio-devices" }
|
||||
vmm-sys-util = ">=0.3.1"
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vm-memory = "0.4.0"
|
||||
vm-memory = "0.5.0"
|
||||
|
||||
[dependencies.cloud-hypervisor]
|
||||
path = ".."
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
#![no_main]
|
||||
|
||||
use block_util::{async_io::DiskFile, qcow_sync::QcowDiskSync};
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use seccomp::SeccompAction;
|
||||
use std::ffi;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Cursor, Read, Seek, SeekFrom};
|
||||
@@ -16,7 +18,6 @@ use virtio_devices::{Block, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||
use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryMmap};
|
||||
use vm_virtio::Queue;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
use seccomp::SeccompAction;
|
||||
|
||||
const MEM_SIZE: u64 = 256 * 1024 * 1024;
|
||||
const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor.
|
||||
@@ -83,11 +84,11 @@ fuzz_target!(|bytes| {
|
||||
|
||||
let shm = memfd_create(&ffi::CString::new("fuzz").unwrap(), 0).unwrap();
|
||||
let disk_file: File = unsafe { File::from_raw_fd(shm) };
|
||||
let raw_img = qcow::RawFile::new(disk_file, false);
|
||||
let qcow_disk = Box::new(QcowDiskSync::new(disk_file, false)) as Box<dyn DiskFile>;
|
||||
|
||||
let mut block = Block::new(
|
||||
"tmp".to_owned(),
|
||||
raw_img,
|
||||
qcow_disk,
|
||||
PathBuf::from(""),
|
||||
false,
|
||||
false,
|
||||
|
||||
@@ -13,8 +13,8 @@ mshv = []
|
||||
anyhow = "1.0"
|
||||
epoll = ">=4.0.1"
|
||||
thiserror = "1.0"
|
||||
libc = "0.2.81"
|
||||
log = "0.4.11"
|
||||
libc = "0.2.86"
|
||||
log = "0.4.14"
|
||||
kvm-ioctls = { git = "https://github.com/cloud-hypervisor/kvm-ioctls", branch = "ch" }
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch", features = ["with-serde", "fam-wrappers"] }
|
||||
|
||||
@@ -24,17 +24,13 @@ mshv-ioctls = { git = "https://github.com/cloud-hypervisor/mshv", branch = "mast
|
||||
serde = {version = ">=1.0.27", features = ["rc"] }
|
||||
serde_derive = ">=1.0.27"
|
||||
serde_json = ">=1.0.9"
|
||||
vm-memory = { version = "0.4.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vm-memory = { version = "0.5.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = { version = ">=0.5.0", features = ["with-serde"] }
|
||||
|
||||
[dependencies.linux-loader]
|
||||
git = "https://github.com/rust-vmm/linux-loader"
|
||||
features = ["elf", "bzimage"]
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
||||
version = "1.9.1"
|
||||
version = "1.10"
|
||||
default-features = false
|
||||
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.8.2"
|
||||
env_logger = "0.8.3"
|
||||
|
||||
@@ -14,4 +14,5 @@
|
||||
pub mod emulator;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[macro_use]
|
||||
pub mod x86;
|
||||
|
||||
@@ -71,8 +71,6 @@ macro_rules! cmp_rm_r {
|
||||
|
||||
state.set_flags((state.flags() & !FLAGS_MASK) | cpazso);
|
||||
|
||||
state.set_ip(insn.ip());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
@@ -95,8 +93,6 @@ macro_rules! cmp_r_rm {
|
||||
|
||||
state.set_flags((state.flags() & !FLAGS_MASK) | cpazso);
|
||||
|
||||
state.set_ip(insn.ip());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
@@ -119,8 +115,6 @@ macro_rules! cmp_rm_imm {
|
||||
|
||||
state.set_flags((state.flags() & !FLAGS_MASK) | cpazso);
|
||||
|
||||
state.set_ip(insn.ip());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
@@ -230,7 +224,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
// cmp ah,al
|
||||
fn test_cmp_rm8_r8_1() -> MockResult {
|
||||
fn test_cmp_rm8_r8_1() {
|
||||
let rax: u64 = 0x0;
|
||||
let ip: u64 = 0x1000;
|
||||
let cpu_id = 0;
|
||||
@@ -240,13 +234,11 @@ mod tests {
|
||||
|
||||
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
|
||||
assert_eq!(0b1000100, rflags);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
// cmp eax,100
|
||||
fn test_cmp_rm32_imm8_1() -> MockResult {
|
||||
fn test_cmp_rm32_imm8_1() {
|
||||
let rax: u64 = 0xabcdef;
|
||||
let ip: u64 = 0x1000;
|
||||
let cpu_id = 0;
|
||||
@@ -256,13 +248,11 @@ mod tests {
|
||||
|
||||
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
|
||||
assert_eq!(0b100, rflags);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
// cmp eax,-1
|
||||
fn test_cmp_rm32_imm8_2() -> MockResult {
|
||||
fn test_cmp_rm32_imm8_2() {
|
||||
let rax: u64 = 0xabcdef;
|
||||
let ip: u64 = 0x1000;
|
||||
let cpu_id = 0;
|
||||
@@ -272,13 +262,11 @@ mod tests {
|
||||
|
||||
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
|
||||
assert_eq!(0b101, rflags);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
// cmp rax,rbx
|
||||
fn test_cmp_rm64_r64() -> MockResult {
|
||||
fn test_cmp_rm64_r64() {
|
||||
let rax: u64 = 0xabcdef;
|
||||
let rbx: u64 = 0x1234;
|
||||
let ip: u64 = 0x1000;
|
||||
@@ -289,12 +277,10 @@ mod tests {
|
||||
|
||||
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
|
||||
assert_eq!(0b100, rflags);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmp_64() -> MockResult {
|
||||
fn test_cmp_64() {
|
||||
let data = [
|
||||
(0xabcdef, 0x1234, 0b100),
|
||||
(0x0, 0x101, 0b1001_0101),
|
||||
@@ -319,12 +305,10 @@ mod tests {
|
||||
let rflags: u64 = vmm.cpu_state(0).unwrap().flags() & FLAGS_MASK;
|
||||
assert_eq!(d.2, rflags);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmp_32() -> MockResult {
|
||||
fn test_cmp_32() {
|
||||
let data = [
|
||||
(0xabcdef, 0x1234, 0b100),
|
||||
(0x0, 0x101, 0b1001_0101),
|
||||
@@ -349,7 +333,5 @@ mod tests {
|
||||
let rflags: u64 = vmm.cpu_state(0).unwrap().flags() & FLAGS_MASK;
|
||||
assert_eq!(d.2, rflags);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,8 +38,14 @@ fn get_op<T: CpuStateManager>(
|
||||
}
|
||||
}
|
||||
|
||||
let value = match insn.op_kind(op_index) {
|
||||
OpKind::Register => state.read_reg(insn.op_register(op_index))?,
|
||||
let value = match insn
|
||||
.try_op_kind(op_index)
|
||||
.map_err(|e| PlatformError::InvalidOperand(e.into()))?
|
||||
{
|
||||
OpKind::Register => state.read_reg(
|
||||
insn.try_op_register(op_index)
|
||||
.map_err(|e| PlatformError::InvalidOperand(e.into()))?,
|
||||
)?,
|
||||
OpKind::Memory => {
|
||||
let addr = memory_operand_address(insn, state, false)?;
|
||||
let mut memory: [u8; 8] = [0; 8];
|
||||
@@ -85,8 +91,15 @@ fn set_op<T: CpuStateManager>(
|
||||
}
|
||||
}
|
||||
|
||||
match insn.op_kind(op_index) {
|
||||
OpKind::Register => state.write_reg(insn.op_register(op_index), value)?,
|
||||
match insn
|
||||
.try_op_kind(op_index)
|
||||
.map_err(|e| PlatformError::InvalidOperand(e.into()))?
|
||||
{
|
||||
OpKind::Register => state.write_reg(
|
||||
insn.try_op_register(op_index)
|
||||
.map_err(|e| PlatformError::InvalidOperand(e.into()))?,
|
||||
value,
|
||||
)?,
|
||||
OpKind::Memory => {
|
||||
let addr = memory_operand_address(insn, state, true)?;
|
||||
platform.write_memory(addr, &value.to_le_bytes()[..op_size])?;
|
||||
|
||||
@@ -39,8 +39,6 @@ macro_rules! mov_rm_r {
|
||||
)
|
||||
.map_err(EmulationError::PlatformEmulationError)?;
|
||||
|
||||
state.set_ip(insn.ip());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
@@ -67,41 +65,50 @@ macro_rules! mov_rm_imm {
|
||||
)
|
||||
.map_err(EmulationError::PlatformEmulationError)?;
|
||||
|
||||
state.set_ip(insn.ip());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! mov_r_rm {
|
||||
($bound:ty) => {
|
||||
macro_rules! movzx {
|
||||
($src_op_size:ty, $dest_op_size:ty) => {
|
||||
fn emulate(
|
||||
&self,
|
||||
insn: &Instruction,
|
||||
state: &mut T,
|
||||
platform: &mut dyn PlatformEmulator<CpuState = T>,
|
||||
) -> Result<(), EmulationError<Exception>> {
|
||||
let src_value = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
|
||||
.map_err(EmulationError::PlatformEmulationError)?;
|
||||
let src_value = get_op(
|
||||
&insn,
|
||||
1,
|
||||
std::mem::size_of::<$src_op_size>(),
|
||||
state,
|
||||
platform,
|
||||
)
|
||||
.map_err(EmulationError::PlatformEmulationError)?;
|
||||
|
||||
set_op(
|
||||
&insn,
|
||||
0,
|
||||
std::mem::size_of::<$bound>(),
|
||||
std::mem::size_of::<$dest_op_size>(),
|
||||
state,
|
||||
platform,
|
||||
src_value,
|
||||
)
|
||||
.map_err(EmulationError::PlatformEmulationError)?;
|
||||
|
||||
state.set_ip(insn.ip());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// MOV r/rm is a special case of MOVZX, where both operands have the same size.
|
||||
macro_rules! mov_r_rm {
|
||||
($op_size:ty) => {
|
||||
movzx!($op_size, $op_size);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! mov_r_imm {
|
||||
($bound:ty) => {
|
||||
fn emulate(
|
||||
@@ -123,8 +130,6 @@ macro_rules! mov_r_imm {
|
||||
)
|
||||
.map_err(EmulationError::PlatformEmulationError)?;
|
||||
|
||||
state.set_ip(insn.ip());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
@@ -210,6 +215,32 @@ impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm64_r64 {
|
||||
mov_rm_r!(u64);
|
||||
}
|
||||
|
||||
// MOVZX
|
||||
pub struct Movzx_r16_rm8;
|
||||
impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r16_rm8 {
|
||||
movzx!(u16, u8);
|
||||
}
|
||||
|
||||
pub struct Movzx_r32_rm8;
|
||||
impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r32_rm8 {
|
||||
movzx!(u32, u8);
|
||||
}
|
||||
|
||||
pub struct Movzx_r64_rm8;
|
||||
impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r64_rm8 {
|
||||
movzx!(u64, u8);
|
||||
}
|
||||
|
||||
pub struct Movzx_r32_rm16;
|
||||
impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r32_rm16 {
|
||||
movzx!(u32, u16);
|
||||
}
|
||||
|
||||
pub struct Movzx_r64_rm16;
|
||||
impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r64_rm16 {
|
||||
movzx!(u64, u16);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(unused_mut)]
|
||||
@@ -218,7 +249,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
// mov rax,rbx
|
||||
fn test_mov_r64_r64() -> MockResult {
|
||||
fn test_mov_r64_r64() {
|
||||
let rbx: u64 = 0x8899aabbccddeeff;
|
||||
let ip: u64 = 0x1000;
|
||||
let cpu_id = 0;
|
||||
@@ -232,13 +263,11 @@ mod tests {
|
||||
.read_reg(Register::RAX)
|
||||
.unwrap();
|
||||
assert_eq!(rax, rbx);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
// mov rax,0x1122334411223344
|
||||
fn test_mov_r64_imm64() -> MockResult {
|
||||
fn test_mov_r64_imm64() {
|
||||
let imm64: u64 = 0x1122334411223344;
|
||||
let ip: u64 = 0x1000;
|
||||
let cpu_id = 0;
|
||||
@@ -252,13 +281,11 @@ mod tests {
|
||||
.read_reg(Register::RAX)
|
||||
.unwrap();
|
||||
assert_eq!(rax, imm64);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
// mov rax, [rax+rax]
|
||||
fn test_mov_r64_m64() -> MockResult {
|
||||
fn test_mov_r64_m64() {
|
||||
let target_rax: u64 = 0x1234567812345678;
|
||||
let mut rax: u64 = 0x100;
|
||||
let ip: u64 = 0x1000;
|
||||
@@ -274,13 +301,11 @@ mod tests {
|
||||
.read_reg(Register::RAX)
|
||||
.unwrap();
|
||||
assert_eq!(rax, target_rax);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
// mov al,0x11
|
||||
fn test_mov_r8_imm8() -> MockResult {
|
||||
fn test_mov_r8_imm8() {
|
||||
let imm8: u8 = 0x11;
|
||||
let ip: u64 = 0x1000;
|
||||
let cpu_id = 0;
|
||||
@@ -294,13 +319,11 @@ mod tests {
|
||||
.read_reg(Register::AL)
|
||||
.unwrap();
|
||||
assert_eq!(al as u8, imm8);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
// mov eax,0x11
|
||||
fn test_mov_r32_imm8() -> MockResult {
|
||||
fn test_mov_r32_imm8() {
|
||||
let imm8: u8 = 0x11;
|
||||
let ip: u64 = 0x1000;
|
||||
let cpu_id = 0;
|
||||
@@ -314,13 +337,11 @@ mod tests {
|
||||
.read_reg(Register::EAX)
|
||||
.unwrap();
|
||||
assert_eq!(eax as u8, imm8);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
// mov rax,0x11223344
|
||||
fn test_mov_r64_imm32() -> MockResult {
|
||||
fn test_mov_r64_imm32() {
|
||||
let imm32: u32 = 0x11223344;
|
||||
let ip: u64 = 0x1000;
|
||||
let cpu_id = 0;
|
||||
@@ -334,13 +355,11 @@ mod tests {
|
||||
.read_reg(Register::RAX)
|
||||
.unwrap();
|
||||
assert_eq!(rax, imm32 as u64);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
// mov byte ptr [rax],dh
|
||||
fn test_mov_m8_r8() -> MockResult {
|
||||
fn test_mov_m8_r8() {
|
||||
let rax: u64 = 0x100;
|
||||
let dh: u8 = 0x99;
|
||||
let ip: u64 = 0x1000;
|
||||
@@ -357,13 +376,11 @@ mod tests {
|
||||
vmm.read_memory(rax, &mut memory).unwrap();
|
||||
|
||||
assert_eq!(u8::from_le_bytes(memory), dh);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
// mov dword ptr [rax],esi
|
||||
fn test_mov_m32_r32() -> MockResult {
|
||||
fn test_mov_m32_r32() {
|
||||
let rax: u64 = 0x100;
|
||||
let esi: u32 = 0x8899;
|
||||
let ip: u64 = 0x1000;
|
||||
@@ -380,13 +397,11 @@ mod tests {
|
||||
vmm.read_memory(rax, &mut memory).unwrap();
|
||||
|
||||
assert_eq!(u32::from_le_bytes(memory), esi);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
// mov dword ptr [rax+0x00000001],edi
|
||||
fn test_mov_m32imm32_r32() -> MockResult {
|
||||
fn test_mov_m32imm32_r32() {
|
||||
let rax: u64 = 0x100;
|
||||
let displacement: u64 = 0x1;
|
||||
let edi: u32 = 0x8899;
|
||||
@@ -404,13 +419,11 @@ mod tests {
|
||||
vmm.read_memory(rax + displacement, &mut memory).unwrap();
|
||||
|
||||
assert_eq!(u32::from_le_bytes(memory), edi);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
// mov eax,dword ptr [rax+10h]
|
||||
fn test_mov_r32_m32imm32() -> MockResult {
|
||||
fn test_mov_r32_m32imm32() {
|
||||
let rax: u64 = 0x100;
|
||||
let displacement: u64 = 0x10;
|
||||
let eax: u32 = 0xaabbccdd;
|
||||
@@ -431,13 +444,11 @@ mod tests {
|
||||
.read_reg(Register::EAX)
|
||||
.unwrap();
|
||||
assert_eq!(new_eax, eax as u64);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
// mov al,byte ptr [rax+10h]
|
||||
fn test_mov_r8_m32imm32() -> MockResult {
|
||||
fn test_mov_r8_m32imm32() {
|
||||
let rax: u64 = 0x100;
|
||||
let displacement: u64 = 0x10;
|
||||
let al: u8 = 0xaa;
|
||||
@@ -458,14 +469,12 @@ mod tests {
|
||||
.read_reg(Register::AL)
|
||||
.unwrap();
|
||||
assert_eq!(new_al, al as u64);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
// mov rax, 0x100
|
||||
// mov rbx, qword ptr [rax+10h]
|
||||
fn test_mov_r64_imm64_and_r64_m64() -> MockResult {
|
||||
fn test_mov_r64_imm64_and_r64_m64() {
|
||||
let target_rax: u64 = 0x1234567812345678;
|
||||
let rax: u64 = 0x100;
|
||||
let displacement: u64 = 0x10;
|
||||
@@ -485,14 +494,12 @@ mod tests {
|
||||
.read_reg(Register::RBX)
|
||||
.unwrap();
|
||||
assert_eq!(rbx, target_rax);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
// mov rax, 0x100
|
||||
// mov rbx, qword ptr [rax+10h]
|
||||
fn test_mov_r64_imm64_and_r64_m64_first_insn() -> MockResult {
|
||||
fn test_mov_r64_imm64_and_r64_m64_first_insn() {
|
||||
let target_rax: u64 = 0x1234567812345678;
|
||||
let rax: u64 = 0x100;
|
||||
let displacement: u64 = 0x10;
|
||||
@@ -508,7 +515,7 @@ mod tests {
|
||||
// Only run the first instruction.
|
||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||
|
||||
assert_eq!(ip + 7 as u64, vmm.cpu_state(cpu_id).unwrap().ip());
|
||||
assert_eq!(ip + 7, vmm.cpu_state(cpu_id).unwrap().ip());
|
||||
|
||||
let new_rax: u64 = vmm
|
||||
.cpu_state(cpu_id)
|
||||
@@ -516,15 +523,13 @@ mod tests {
|
||||
.read_reg(Register::RAX)
|
||||
.unwrap();
|
||||
assert_eq!(rax, new_rax);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
// mov rax, 0x100
|
||||
// mov rbx, qword ptr [rax+10h]
|
||||
// mov rax, 0x200
|
||||
fn test_mov_r64_imm64_and_r64_m64_two_insns() -> MockResult {
|
||||
fn test_mov_r64_imm64_and_r64_m64_two_insns() {
|
||||
let target_rax: u64 = 0x1234567812345678;
|
||||
let rax: u64 = 0x100;
|
||||
let displacement: u64 = 0x10;
|
||||
@@ -541,7 +546,7 @@ mod tests {
|
||||
// Run the 2 first instructions.
|
||||
assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
|
||||
|
||||
assert_eq!(ip + 7 + 4 as u64, vmm.cpu_state(cpu_id).unwrap().ip());
|
||||
assert_eq!(ip + 7 + 4, vmm.cpu_state(cpu_id).unwrap().ip());
|
||||
|
||||
let rbx: u64 = vmm
|
||||
.cpu_state(cpu_id)
|
||||
@@ -557,7 +562,61 @@ mod tests {
|
||||
.read_reg(Register::RAX)
|
||||
.unwrap();
|
||||
assert_eq!(rax, new_rax);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
#[test]
|
||||
// movzx eax, bl
|
||||
fn test_movzx_r32_r8l() {
|
||||
let bx: u16 = 0x8899;
|
||||
let ip: u64 = 0x1000;
|
||||
let cpu_id = 0;
|
||||
let insn = [0x0f, 0xb6, 0xc3];
|
||||
let mut vmm = MockVMM::new(ip, vec![(Register::BX, bx as u64)], None);
|
||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||
|
||||
let eax: u64 = vmm
|
||||
.cpu_state(cpu_id)
|
||||
.unwrap()
|
||||
.read_reg(Register::EAX)
|
||||
.unwrap();
|
||||
assert_eq!(eax, (bx & 0xff) as u64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// movzx eax, bh
|
||||
fn test_movzx_r32_r8h() {
|
||||
let bx: u16 = 0x8899;
|
||||
let ip: u64 = 0x1000;
|
||||
let cpu_id = 0;
|
||||
let insn = [0x0f, 0xb6, 0xc7];
|
||||
let mut vmm = MockVMM::new(ip, vec![(Register::BX, bx as u64)], None);
|
||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||
|
||||
let eax: u64 = vmm
|
||||
.cpu_state(cpu_id)
|
||||
.unwrap()
|
||||
.read_reg(Register::EAX)
|
||||
.unwrap();
|
||||
assert_eq!(eax, (bx >> 8) as u64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// movzx eax, byte ptr [rbx]
|
||||
fn test_movzx_r32_m8() {
|
||||
let rbx: u64 = 0x100;
|
||||
let value: u8 = 0xaa;
|
||||
let ip: u64 = 0x1000;
|
||||
let cpu_id = 0;
|
||||
let insn = [0x0f, 0xb7, 0x03];
|
||||
let memory: [u8; 1] = value.to_le_bytes();
|
||||
let mut vmm = MockVMM::new(ip, vec![(Register::RBX, rbx)], Some((rbx, &memory)));
|
||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||
|
||||
let eax: u64 = vmm
|
||||
.cpu_state(cpu_id)
|
||||
.unwrap()
|
||||
.read_reg(Register::EAX)
|
||||
.unwrap();
|
||||
assert_eq!(eax, value as u64);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,7 +518,13 @@ impl<'a, T: CpuStateManager> Emulator<'a, T> {
|
||||
(mov, Mov_rm32_imm32),
|
||||
(mov, Mov_rm32_r32),
|
||||
(mov, Mov_rm64_imm32),
|
||||
(mov, Mov_rm64_r64)
|
||||
(mov, Mov_rm64_r64),
|
||||
// MOVZX
|
||||
(mov, Movzx_r16_rm8),
|
||||
(mov, Movzx_r32_rm8),
|
||||
(mov, Movzx_r64_rm8),
|
||||
(mov, Movzx_r32_rm16),
|
||||
(mov, Movzx_r64_rm16)
|
||||
);
|
||||
|
||||
handler
|
||||
@@ -656,10 +662,12 @@ mod mock_vmm {
|
||||
let _ = env_logger::try_init();
|
||||
let cs_reg = segment_from_gdt(gdt_entry(0xc09b, 0, 0xffffffff), 1);
|
||||
let ds_reg = segment_from_gdt(gdt_entry(0xc093, 0, 0xffffffff), 2);
|
||||
let es_reg = segment_from_gdt(gdt_entry(0xc093, 0, 0xffffffff), 3);
|
||||
let mut initial_state = CpuState::default();
|
||||
initial_state.set_ip(ip);
|
||||
initial_state.write_segment(Register::CS, cs_reg).unwrap();
|
||||
initial_state.write_segment(Register::DS, ds_reg).unwrap();
|
||||
initial_state.write_segment(Register::ES, es_reg).unwrap();
|
||||
for (reg, value) in regs {
|
||||
initial_state.write_reg(reg, value).unwrap();
|
||||
}
|
||||
@@ -765,7 +773,7 @@ mod tests {
|
||||
//
|
||||
// mov rax, 0x1000
|
||||
// Test with a first instruction truncated.
|
||||
fn test_fetch_first_instruction() -> MockResult {
|
||||
fn test_fetch_first_instruction() {
|
||||
let ip: u64 = 0x1000;
|
||||
let cpu_id = 0;
|
||||
let memory = [
|
||||
@@ -791,8 +799,6 @@ mod tests {
|
||||
.read_reg(Register::RAX)
|
||||
.unwrap();
|
||||
assert_eq!(rax, ip);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -801,7 +807,7 @@ mod tests {
|
||||
// mov rax, 0x1000
|
||||
// mov rbx, qword ptr [rax+10h]
|
||||
// Test with a 2nd instruction truncated.
|
||||
fn test_fetch_second_instruction() -> MockResult {
|
||||
fn test_fetch_second_instruction() {
|
||||
let target_rax: u64 = 0x1234567812345678;
|
||||
let ip: u64 = 0x1000;
|
||||
let cpu_id = 0;
|
||||
@@ -828,8 +834,6 @@ mod tests {
|
||||
.read_reg(Register::RBX)
|
||||
.unwrap();
|
||||
assert_eq!(rbx, target_rax);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -838,7 +842,7 @@ mod tests {
|
||||
// mov rax, 0x1000
|
||||
// Test with a first instruction truncated and a bad fetched instruction.
|
||||
// Verify that the instruction emulation returns an error.
|
||||
fn test_fetch_bad_insn() -> MockResult {
|
||||
fn test_fetch_bad_insn() {
|
||||
let ip: u64 = 0x1000;
|
||||
let cpu_id = 0;
|
||||
let memory = [
|
||||
@@ -853,7 +857,5 @@ mod tests {
|
||||
|
||||
let mut vmm = MockVMM::new(ip, vec![], Some((ip, &memory)));
|
||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,8 +43,8 @@ pub const NUM_IOAPIC_PINS: usize = 24;
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Exception {
|
||||
DE = 1, // Divide Error
|
||||
DB = 2, // Debug Exception
|
||||
DE = 0, // Divide Error
|
||||
DB = 1, // Debug Exception
|
||||
BP = 3, // Breakpoint
|
||||
OF = 4, // Overflow
|
||||
BR = 5, // BOUND Range Exceeded
|
||||
@@ -122,3 +122,23 @@ pub fn segment_type_ro(t: u8) -> bool {
|
||||
pub fn segment_type_expand_down(t: u8) -> bool {
|
||||
!segment_type_code(t) && (t & EXPAND_DOWN_SEGMENT_TYPE != 0)
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! msr {
|
||||
($msr:expr) => {
|
||||
MsrEntry {
|
||||
index: $msr,
|
||||
data: 0x0,
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! msr_data {
|
||||
($msr:expr, $data:expr) => {
|
||||
MsrEntry {
|
||||
index: $msr,
|
||||
data: $data,
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ impl vm::Vm for KvmVm {
|
||||
) -> vm::Result<Arc<dyn cpu::Vcpu>> {
|
||||
let vc = self
|
||||
.fd
|
||||
.create_vcpu(id)
|
||||
.create_vcpu(id as u64)
|
||||
.map_err(|e| vm::HypervisorVmError::CreateVcpu(e.into()))?;
|
||||
let vcpu = KvmVcpu {
|
||||
fd: vc,
|
||||
@@ -314,8 +314,10 @@ impl vm::Vm for KvmVm {
|
||||
// Create split irqchip
|
||||
// Only the local APIC is emulated in kernel, both PICs and IOAPIC
|
||||
// are not.
|
||||
let mut cap: kvm_enable_cap = Default::default();
|
||||
cap.cap = KVM_CAP_SPLIT_IRQCHIP;
|
||||
let mut cap = kvm_enable_cap {
|
||||
cap: KVM_CAP_SPLIT_IRQCHIP,
|
||||
..Default::default()
|
||||
};
|
||||
cap.args[0] = NUM_IOAPIC_PINS as u64;
|
||||
self.fd
|
||||
.enable_cap(&cap)
|
||||
@@ -604,8 +606,10 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
// emulated as it will influence later which MSRs should be saved.
|
||||
self.hyperv_synic.store(true, Ordering::Release);
|
||||
|
||||
let mut cap: kvm_enable_cap = Default::default();
|
||||
cap.cap = KVM_CAP_HYPERV_SYNIC;
|
||||
let cap = kvm_enable_cap {
|
||||
cap: KVM_CAP_HYPERV_SYNIC,
|
||||
..Default::default()
|
||||
};
|
||||
self.fd
|
||||
.enable_cap(&cap)
|
||||
.map_err(|e| cpu::HypervisorCpuError::EnableHyperVSynIC(e.into()))
|
||||
@@ -1230,12 +1234,13 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
///
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn state(&self) -> cpu::Result<CpuState> {
|
||||
let mut state = CpuState::default();
|
||||
// Get this vCPUs multiprocessing state.
|
||||
state.mp_state = self.get_mp_state()?;
|
||||
let mut state = CpuState {
|
||||
mp_state: self.get_mp_state()?,
|
||||
mpidr: self.read_mpidr()?,
|
||||
..Default::default()
|
||||
};
|
||||
self.core_registers(&mut state.core_regs)?;
|
||||
self.system_registers(&mut state.sys_regs)?;
|
||||
state.mpidr = self.read_mpidr()?;
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
@@ -8,11 +8,10 @@
|
||||
//
|
||||
//
|
||||
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
use crate::arch::x86::{msr_index, SegmentRegisterOps, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
||||
use crate::kvm::{Cap, Kvm, KvmError, KvmResult};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
///
|
||||
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
|
||||
@@ -95,25 +94,6 @@ impl SegmentRegisterOps for SegmentRegister {
|
||||
|
||||
pub const KVM_TSS_ADDRESS: GuestAddress = GuestAddress(0xfffb_d000);
|
||||
|
||||
macro_rules! msr {
|
||||
($msr:expr) => {
|
||||
MsrEntry {
|
||||
index: $msr,
|
||||
data: 0x0,
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! msr_data {
|
||||
($msr:expr, $data:expr) => {
|
||||
MsrEntry {
|
||||
index: $msr,
|
||||
data: $data,
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn boot_msr_entries() -> MsrEntries {
|
||||
MsrEntries::from_entries(&[
|
||||
msr!(msr_index::MSR_IA32_SYSENTER_CS),
|
||||
|
||||
@@ -28,6 +28,10 @@ extern crate serde_derive;
|
||||
extern crate serde_json;
|
||||
extern crate thiserror;
|
||||
|
||||
/// Architecture specific definitions
|
||||
#[macro_use]
|
||||
pub mod arch;
|
||||
|
||||
#[cfg(feature = "kvm")]
|
||||
/// KVM implementation module
|
||||
pub mod kvm;
|
||||
@@ -42,9 +46,6 @@ pub mod hypervisor;
|
||||
/// Vm related module
|
||||
pub mod vm;
|
||||
|
||||
/// Architecture specific definitions
|
||||
pub mod arch;
|
||||
|
||||
/// CPU related module
|
||||
mod cpu;
|
||||
|
||||
|
||||
@@ -3,13 +3,10 @@
|
||||
// Copyright © 2020, Microsoft Corporation
|
||||
//
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(unused_variables)]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
use crate::arch::emulator::{PlatformEmulator, PlatformError};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::arch::emulator::{EmulationError, PlatformEmulator, PlatformError};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::arch::x86::emulator::{Emulator, EmulatorCpuState};
|
||||
use crate::cpu;
|
||||
@@ -17,7 +14,8 @@ use crate::cpu::Vcpu;
|
||||
use crate::hypervisor;
|
||||
use crate::vm::{self, VmmOps};
|
||||
pub use mshv_bindings::*;
|
||||
use mshv_ioctls::{set_registers_64, InterruptRequest, Mshv, VcpuFd, VmFd};
|
||||
pub use mshv_ioctls::IoEventAddress;
|
||||
use mshv_ioctls::{set_registers_64, InterruptRequest, Mshv, NoDatamatch, VcpuFd, VmFd};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
use vm::DataMatch;
|
||||
@@ -25,19 +23,15 @@ use vm::DataMatch;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub mod x86_64;
|
||||
use crate::device;
|
||||
use std::convert::TryInto;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use x86_64::VcpuMshvState as CpuState;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use x86_64::*;
|
||||
// Wei: for emulating irqfd and ioeventfd
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::sync::{Mutex, RwLock};
|
||||
use std::thread;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::sync::RwLock;
|
||||
|
||||
pub const PAGE_SHIFT: usize = 12;
|
||||
|
||||
@@ -48,195 +42,6 @@ pub struct HvState {
|
||||
|
||||
pub use HvState as VmState;
|
||||
|
||||
struct IrqfdCtrlEpollHandler {
|
||||
vm_fd: Arc<VmFd>, /* For issuing hypercall */
|
||||
irqfd: EventFd, /* Registered by caller */
|
||||
kill: EventFd, /* Created by us, signal thread exit */
|
||||
epoll_fd: RawFd, /* epoll fd */
|
||||
gsi: u32,
|
||||
gsi_routes: Arc<RwLock<HashMap<u32, MshvIrqRoutingEntry>>>,
|
||||
}
|
||||
|
||||
fn register_listener(
|
||||
epoll_fd: RawFd,
|
||||
fd: RawFd,
|
||||
ev_type: epoll::Events,
|
||||
data: u64,
|
||||
) -> std::result::Result<(), io::Error> {
|
||||
epoll::ctl(
|
||||
epoll_fd,
|
||||
epoll::ControlOptions::EPOLL_CTL_ADD,
|
||||
fd,
|
||||
epoll::Event::new(ev_type, data),
|
||||
)
|
||||
}
|
||||
|
||||
const KILL_EVENT: u16 = 1;
|
||||
const IRQFD_EVENT: u16 = 2;
|
||||
|
||||
impl IrqfdCtrlEpollHandler {
|
||||
fn assert_virtual_interrupt(&self, e: &MshvIrqRoutingEntry) -> vm::Result<()> {
|
||||
// GSI routing contains MSI information.
|
||||
// We still need to translate that to APIC ID etc
|
||||
|
||||
debug!("Inject {:x?}", e);
|
||||
|
||||
let MshvIrqRouting::Msi(msi) = e.route;
|
||||
|
||||
/* Make an assumption here ... */
|
||||
if msi.address_hi != 0 {
|
||||
panic!("MSI high address part is not zero");
|
||||
}
|
||||
|
||||
let typ = self
|
||||
.get_interrupt_type(self.get_delivery_mode(msi.data))
|
||||
.unwrap();
|
||||
let apic_id = self.get_destination(msi.address_lo);
|
||||
let vector = self.get_vector(msi.data);
|
||||
let level_triggered = self.get_trigger_mode(msi.data);
|
||||
let logical_destination_mode = self.get_destination_mode(msi.address_lo);
|
||||
|
||||
debug!(
|
||||
"{:x} {:x} {:x} {} {}",
|
||||
typ, apic_id, vector, level_triggered, logical_destination_mode
|
||||
);
|
||||
|
||||
let request: InterruptRequest = InterruptRequest {
|
||||
interrupt_type: typ,
|
||||
apic_id,
|
||||
vector: vector.into(),
|
||||
level_triggered,
|
||||
logical_destination_mode,
|
||||
long_mode: false,
|
||||
};
|
||||
|
||||
self.vm_fd
|
||||
.request_virtual_interrupt(&request)
|
||||
.map_err(|e| vm::HypervisorVmError::AsserttVirtualInterrupt(e.into()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn run_ctrl(&mut self) {
|
||||
self.epoll_fd = epoll::create(true).unwrap();
|
||||
let epoll_file = unsafe { File::from_raw_fd(self.epoll_fd) };
|
||||
|
||||
register_listener(
|
||||
epoll_file.as_raw_fd(),
|
||||
self.kill.as_raw_fd(),
|
||||
epoll::Events::EPOLLIN,
|
||||
u64::from(KILL_EVENT),
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
info!(
|
||||
"IrqfdCtrlEpollHandler: failed to register listener: {:?}",
|
||||
err
|
||||
);
|
||||
});
|
||||
|
||||
register_listener(
|
||||
epoll_file.as_raw_fd(),
|
||||
self.irqfd.as_raw_fd(),
|
||||
epoll::Events::EPOLLIN,
|
||||
u64::from(IRQFD_EVENT),
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
info!(
|
||||
"IrqfdCtrlEpollHandler: failed to register listener: {:?}",
|
||||
err
|
||||
);
|
||||
});
|
||||
|
||||
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); 2];
|
||||
|
||||
'epoll: loop {
|
||||
let num_events = match epoll::wait(epoll_file.as_raw_fd(), -1, &mut events[..]) {
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
if e.kind() == std::io::ErrorKind::Interrupted {
|
||||
continue;
|
||||
}
|
||||
panic!("irqfd epoll ???");
|
||||
}
|
||||
};
|
||||
|
||||
for event in events.iter().take(num_events) {
|
||||
let ev_type = event.data as u16;
|
||||
|
||||
match ev_type {
|
||||
KILL_EVENT => {
|
||||
break 'epoll;
|
||||
}
|
||||
IRQFD_EVENT => {
|
||||
debug!("IRQFD_EVENT received, inject to guest");
|
||||
let _ = self.irqfd.read().unwrap();
|
||||
let gsi_routes = self.gsi_routes.read().unwrap();
|
||||
|
||||
if let Some(e) = gsi_routes.get(&self.gsi) {
|
||||
self.assert_virtual_interrupt(&e).unwrap();
|
||||
} else {
|
||||
debug!("No routing info found for GSI {}", self.gsi);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("Unknown event");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// See Intel SDM vol3 10.11.1
|
||||
/// We assume APIC ID and Hyper-V Vcpu ID are the same value
|
||||
///
|
||||
|
||||
fn get_destination(&self, message_address: u32) -> u64 {
|
||||
((message_address >> 12) & 0xff).into()
|
||||
}
|
||||
|
||||
fn get_destination_mode(&self, message_address: u32) -> bool {
|
||||
if (message_address >> 2) & 0x1 == 0x1 {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn get_vector(&self, message_data: u32) -> u8 {
|
||||
(message_data & 0xff) as u8
|
||||
}
|
||||
|
||||
///
|
||||
/// True means level triggered
|
||||
///
|
||||
fn get_trigger_mode(&self, message_data: u32) -> bool {
|
||||
if (message_data >> 15) & 0x1 == 0x1 {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn get_delivery_mode(&self, message_data: u32) -> u8 {
|
||||
((message_data & 0x700) >> 8) as u8
|
||||
}
|
||||
///
|
||||
/// Translate from architectural defined delivery mode to Hyper-V type
|
||||
/// See Intel SDM vol3 10.11.2
|
||||
///
|
||||
fn get_interrupt_type(&self, delivery_mode: u8) -> Option<hv_interrupt_type> {
|
||||
match delivery_mode {
|
||||
0 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_FIXED),
|
||||
1 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_LOWESTPRIORITY),
|
||||
2 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_SMI),
|
||||
4 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_NMI),
|
||||
5 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_INIT),
|
||||
7 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_EXTINT),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper over mshv system ioctls.
|
||||
pub struct MshvHypervisor {
|
||||
mshv: Mshv,
|
||||
@@ -296,15 +101,11 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
}
|
||||
let vm_fd = Arc::new(fd);
|
||||
|
||||
let irqfds = Mutex::new(HashMap::new());
|
||||
let ioeventfds = Arc::new(RwLock::new(HashMap::new()));
|
||||
let gsi_routes = Arc::new(RwLock::new(HashMap::new()));
|
||||
|
||||
Ok(Arc::new(MshvVm {
|
||||
fd: vm_fd,
|
||||
msrs,
|
||||
irqfds,
|
||||
ioeventfds,
|
||||
gsi_routes,
|
||||
hv_state: hv_state_init(),
|
||||
vmmops: None,
|
||||
@@ -314,7 +115,7 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
/// Get the supported CpuID
|
||||
///
|
||||
fn get_cpuid(&self) -> hypervisor::Result<CpuId> {
|
||||
Ok(CpuId::new(1 as usize))
|
||||
Ok(CpuId::new(1))
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
@@ -343,9 +144,8 @@ impl SoftTLB {
|
||||
}
|
||||
|
||||
// Adds a gva -> gpa mapping into the TLB.
|
||||
fn add_mapping(&mut self, gva: u64, gpa: u64) -> Result<(), PlatformError> {
|
||||
fn add_mapping(&mut self, gva: u64, gpa: u64) {
|
||||
*self.addr_map.entry(gva).or_insert(gpa) = gpa;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Do the actual gva -> gpa translation
|
||||
@@ -358,23 +158,15 @@ impl SoftTLB {
|
||||
// TODO Check if we could fallback to e.g. an hypercall for doing
|
||||
// the translation for us.
|
||||
}
|
||||
|
||||
// FLush the TLB, all mappings are removed.
|
||||
fn flush(&mut self) -> Result<(), PlatformError> {
|
||||
self.addr_map.clear();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[allow(dead_code)]
|
||||
/// Vcpu struct for Microsoft Hypervisor
|
||||
pub struct MshvVcpu {
|
||||
fd: VcpuFd,
|
||||
vp_index: u8,
|
||||
cpuid: CpuId,
|
||||
msrs: MsrEntries,
|
||||
ioeventfds: Arc<RwLock<HashMap<IoEventAddress, (Option<DataMatch>, EventFd)>>>,
|
||||
gsi_routes: Arc<RwLock<HashMap<u32, MshvIrqRoutingEntry>>>,
|
||||
hv_state: Arc<RwLock<HvState>>, // Mshv State
|
||||
vmmops: Option<Arc<Box<dyn vm::VmmOps>>>,
|
||||
@@ -512,6 +304,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
/* We always have SynIC enabled on MSHV */
|
||||
Ok(())
|
||||
}
|
||||
#[allow(non_upper_case_globals)]
|
||||
fn run(&self) -> std::result::Result<cpu::VmExit, cpu::HypervisorCpuError> {
|
||||
// Safe because this is just only done during initialization.
|
||||
// TODO don't zero it everytime we enter this function.
|
||||
@@ -589,8 +382,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
// Add the GVA <-> GPA mapping.
|
||||
context
|
||||
.tlb
|
||||
.add_mapping(info.guest_virtual_address, info.guest_physical_address)
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
|
||||
.add_mapping(info.guest_virtual_address, info.guest_physical_address);
|
||||
|
||||
// Create a new emulator.
|
||||
let mut emul = Emulator::new(&mut context);
|
||||
@@ -614,7 +406,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
}
|
||||
hv_message_type_HVMSG_X64_MSR_INTERCEPT => {
|
||||
let info = x.to_msr_info().unwrap();
|
||||
if info.header.intercept_access_type == 0 as u8 {
|
||||
if info.header.intercept_access_type == 0 {
|
||||
debug!("msr read: {:x}", info.msr_number);
|
||||
} else {
|
||||
debug!("msr write: {:x}", info.msr_number);
|
||||
@@ -646,14 +438,14 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
///
|
||||
/// X86 specific call to setup the CPUID registers.
|
||||
///
|
||||
fn set_cpuid2(&self, cpuid: &CpuId) -> cpu::Result<()> {
|
||||
fn set_cpuid2(&self, _cpuid: &CpuId) -> cpu::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call to retrieve the CPUID registers.
|
||||
///
|
||||
fn get_cpuid2(&self, num_entries: usize) -> cpu::Result<CpuId> {
|
||||
fn get_cpuid2(&self, _num_entries: usize) -> cpu::Result<CpuId> {
|
||||
Ok(self.cpuid.clone())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -776,19 +568,6 @@ impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
||||
gpa
|
||||
);
|
||||
|
||||
if let Some((datamatch, efd)) = self
|
||||
.vcpu
|
||||
.ioeventfds
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&IoEventAddress::Mmio(gpa))
|
||||
{
|
||||
debug!("ioevent {:x} {:x?} {}", gpa, datamatch, efd.as_raw_fd());
|
||||
|
||||
/* TODO: use datamatch to provide the correct semantics */
|
||||
efd.write(1).unwrap();
|
||||
}
|
||||
|
||||
if let Some(vmmops) = &self.vcpu.vmmops {
|
||||
vmmops
|
||||
.mmio_write(gpa, data)
|
||||
@@ -846,20 +625,17 @@ impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
||||
self.tlb.translate(gva)
|
||||
}
|
||||
|
||||
fn fetch(&self, ip: u64, instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
|
||||
fn fetch(&self, _ip: u64, _instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
|
||||
Err(PlatformError::MemoryReadFailure(anyhow!("unimplemented")))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[allow(dead_code)]
|
||||
/// Wrapper over Mshv VM ioctls.
|
||||
pub struct MshvVm {
|
||||
fd: Arc<VmFd>,
|
||||
msrs: MsrEntries,
|
||||
// Emulate irqfd
|
||||
irqfds: Mutex<HashMap<u32, (EventFd, EventFd)>>,
|
||||
// Emulate ioeventfd
|
||||
ioeventfds: Arc<RwLock<HashMap<IoEventAddress, (Option<DataMatch>, EventFd)>>>,
|
||||
// GSI routing information
|
||||
gsi_routes: Arc<RwLock<HashMap<u32, MshvIrqRoutingEntry>>>,
|
||||
// Hypervisor State
|
||||
@@ -887,7 +663,7 @@ impl vm::Vm for MshvVm {
|
||||
///
|
||||
/// Sets the address of the three-page region in the VM's address space.
|
||||
///
|
||||
fn set_tss_address(&self, offset: usize) -> vm::Result<()> {
|
||||
fn set_tss_address(&self, _offset: usize) -> vm::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
///
|
||||
@@ -900,36 +676,34 @@ impl vm::Vm for MshvVm {
|
||||
/// Registers an event that will, when signaled, trigger the `gsi` IRQ.
|
||||
///
|
||||
fn register_irqfd(&self, fd: &EventFd, gsi: u32) -> vm::Result<()> {
|
||||
let dup_fd = fd.try_clone().unwrap();
|
||||
let kill_fd = EventFd::new(libc::EFD_NONBLOCK).unwrap();
|
||||
|
||||
let mut ctrl_handler = IrqfdCtrlEpollHandler {
|
||||
vm_fd: self.fd.clone(),
|
||||
kill: kill_fd.try_clone().unwrap(),
|
||||
irqfd: fd.try_clone().unwrap(),
|
||||
epoll_fd: 0,
|
||||
gsi,
|
||||
gsi_routes: self.gsi_routes.clone(),
|
||||
};
|
||||
|
||||
debug!("register_irqfd fd {} gsi {}", fd.as_raw_fd(), gsi);
|
||||
|
||||
thread::Builder::new()
|
||||
.name(format!("irqfd_{}", gsi))
|
||||
.spawn(move || ctrl_handler.run_ctrl())
|
||||
.unwrap();
|
||||
let gsi_routes = self.gsi_routes.read().unwrap();
|
||||
|
||||
self.irqfds.lock().unwrap().insert(gsi, (dup_fd, kill_fd));
|
||||
if let Some(e) = gsi_routes.get(&gsi) {
|
||||
let msi = e
|
||||
.get_msi_routing()
|
||||
.map_err(|e| vm::HypervisorVmError::RegisterIrqFd(e.into()))?;
|
||||
let request = msi.to_interrupt_request();
|
||||
self.fd
|
||||
.register_irqfd(&fd, gsi, &request)
|
||||
.map_err(|e| vm::HypervisorVmError::RegisterIrqFd(e.into()))?;
|
||||
} else {
|
||||
error!("No routing info found for GSI {}", gsi)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
///
|
||||
/// Unregisters an event that will, when signaled, trigger the `gsi` IRQ.
|
||||
///
|
||||
fn unregister_irqfd(&self, _fd: &EventFd, gsi: u32) -> vm::Result<()> {
|
||||
debug!("unregister_irqfd fd {} gsi {}", _fd.as_raw_fd(), gsi);
|
||||
let (_, kill_fd) = self.irqfds.lock().unwrap().remove(&gsi).unwrap();
|
||||
kill_fd.write(1).unwrap();
|
||||
fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> vm::Result<()> {
|
||||
debug!("unregister_irqfd fd {} gsi {}", fd.as_raw_fd(), gsi);
|
||||
|
||||
self.fd
|
||||
.unregister_irqfd(&fd, gsi)
|
||||
.map_err(|e| vm::HypervisorVmError::UnregisterIrqFd(e.into()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
///
|
||||
@@ -947,9 +721,8 @@ impl vm::Vm for MshvVm {
|
||||
let vcpu = MshvVcpu {
|
||||
fd: vcpu_fd,
|
||||
vp_index: id,
|
||||
cpuid: CpuId::new(1 as usize),
|
||||
cpuid: CpuId::new(1),
|
||||
msrs: self.msrs.clone(),
|
||||
ioeventfds: self.ioeventfds.clone(),
|
||||
gsi_routes: self.gsi_routes.clone(),
|
||||
hv_state: self.hv_state.clone(),
|
||||
vmmops,
|
||||
@@ -966,26 +739,36 @@ impl vm::Vm for MshvVm {
|
||||
addr: &IoEventAddress,
|
||||
datamatch: Option<DataMatch>,
|
||||
) -> vm::Result<()> {
|
||||
let dup_fd = fd.try_clone().unwrap();
|
||||
|
||||
debug!(
|
||||
"register_ioevent fd {} addr {:x?} datamatch {:?}",
|
||||
fd.as_raw_fd(),
|
||||
addr,
|
||||
datamatch
|
||||
);
|
||||
|
||||
self.ioeventfds
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(*addr, (datamatch, dup_fd));
|
||||
Ok(())
|
||||
if let Some(dm) = datamatch {
|
||||
match dm {
|
||||
vm::DataMatch::DataMatch32(mshv_dm32) => self
|
||||
.fd
|
||||
.register_ioevent(fd, addr, mshv_dm32)
|
||||
.map_err(|e| vm::HypervisorVmError::RegisterIoEvent(e.into())),
|
||||
vm::DataMatch::DataMatch64(mshv_dm64) => self
|
||||
.fd
|
||||
.register_ioevent(fd, addr, mshv_dm64)
|
||||
.map_err(|e| vm::HypervisorVmError::RegisterIoEvent(e.into())),
|
||||
}
|
||||
} else {
|
||||
self.fd
|
||||
.register_ioevent(fd, addr, NoDatamatch)
|
||||
.map_err(|e| vm::HypervisorVmError::RegisterIoEvent(e.into()))
|
||||
}
|
||||
}
|
||||
/// Unregister an event from a certain address it has been previously registered to.
|
||||
fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> vm::Result<()> {
|
||||
debug!("unregister_ioevent fd {} addr {:x?}", fd.as_raw_fd(), addr);
|
||||
self.ioeventfds.write().unwrap().remove(addr).unwrap();
|
||||
Ok(())
|
||||
|
||||
self.fd
|
||||
.unregister_ioevent(fd, addr, NoDatamatch)
|
||||
.map_err(|e| vm::HypervisorVmError::UnregisterIoEvent(e.into()))
|
||||
}
|
||||
|
||||
/// Creates/modifies a guest physical memory slot.
|
||||
@@ -1003,7 +786,7 @@ impl vm::Vm for MshvVm {
|
||||
memory_size: u64,
|
||||
userspace_addr: u64,
|
||||
readonly: bool,
|
||||
log_dirty_pages: bool,
|
||||
_log_dirty_pages: bool,
|
||||
) -> MemoryRegion {
|
||||
let mut flags = HV_MAP_GPA_READABLE | HV_MAP_GPA_EXECUTABLE;
|
||||
if !readonly {
|
||||
@@ -1052,7 +835,7 @@ impl vm::Vm for MshvVm {
|
||||
///
|
||||
/// Get dirty pages bitmap (one bit per page)
|
||||
///
|
||||
fn get_dirty_log(&self, slot: u32, memory_size: u64) -> vm::Result<Vec<u64>> {
|
||||
fn get_dirty_log(&self, _slot: u32, _memory_size: u64) -> vm::Result<Vec<u64>> {
|
||||
Err(vm::HypervisorVmError::GetDirtyLog(anyhow!(
|
||||
"get_dirty_log not implemented"
|
||||
)))
|
||||
@@ -1072,6 +855,12 @@ pub enum MshvIrqRouting {
|
||||
Msi(MshvIrqRoutingMsi),
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum MshvIrqRoutingEntryError {
|
||||
#[error("Invalid MSI address: {0}")]
|
||||
InvalidMsiAddress(#[source] anyhow::Error),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct MshvIrqRoutingEntry {
|
||||
pub gsi: u32,
|
||||
@@ -1079,4 +868,79 @@ pub struct MshvIrqRoutingEntry {
|
||||
}
|
||||
pub type IrqRoutingEntry = MshvIrqRoutingEntry;
|
||||
|
||||
impl MshvIrqRoutingEntry {
|
||||
fn get_msi_routing(&self) -> Result<MshvIrqRoutingMsi, MshvIrqRoutingEntryError> {
|
||||
let MshvIrqRouting::Msi(msi) = self.route;
|
||||
if msi.address_hi != 0 {
|
||||
return Err(MshvIrqRoutingEntryError::InvalidMsiAddress(anyhow!(
|
||||
"MSI high address part is not zero"
|
||||
)));
|
||||
}
|
||||
Ok(msi)
|
||||
}
|
||||
}
|
||||
|
||||
impl MshvIrqRoutingMsi {
|
||||
///
|
||||
/// See Intel SDM vol3 10.11.1
|
||||
/// We assume APIC ID and Hyper-V Vcpu ID are the same value
|
||||
///
|
||||
|
||||
fn get_destination(&self) -> u64 {
|
||||
((self.address_lo >> 12) & 0xff).into()
|
||||
}
|
||||
|
||||
fn get_destination_mode(&self) -> bool {
|
||||
if (self.address_lo >> 2) & 0x1 == 0x1 {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn get_vector(&self) -> u8 {
|
||||
(self.data & 0xff) as u8
|
||||
}
|
||||
|
||||
///
|
||||
/// True means level triggered
|
||||
///
|
||||
fn get_trigger_mode(&self) -> bool {
|
||||
if (self.data >> 15) & 0x1 == 0x1 {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn get_delivery_mode(&self) -> u8 {
|
||||
((self.data & 0x700) >> 8) as u8
|
||||
}
|
||||
|
||||
///
|
||||
/// Translate from architectural defined delivery mode to Hyper-V type
|
||||
/// See Intel SDM vol3 10.11.2
|
||||
///
|
||||
fn get_interrupt_type(&self) -> Option<hv_interrupt_type> {
|
||||
match self.get_delivery_mode() {
|
||||
0 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_FIXED),
|
||||
1 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_LOWESTPRIORITY),
|
||||
2 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_SMI),
|
||||
4 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_NMI),
|
||||
5 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_INIT),
|
||||
7 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_EXTINT),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_interrupt_request(&self) -> InterruptRequest {
|
||||
InterruptRequest {
|
||||
interrupt_type: self.get_interrupt_type().unwrap(),
|
||||
apic_id: self.get_destination(),
|
||||
vector: self.get_vector() as u32,
|
||||
level_triggered: self.get_trigger_mode(),
|
||||
logical_destination_mode: self.get_destination_mode(),
|
||||
long_mode: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const CPUID_FLAG_VALID_INDEX: u32 = 0;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//
|
||||
//
|
||||
|
||||
use crate::arch::x86::{msr_index, SegmentRegisterOps, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
||||
use crate::arch::x86::{msr_index, SegmentRegisterOps};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
///
|
||||
/// Export generically-named wrappers of mshv_bindings for Unix-based platforms
|
||||
@@ -42,32 +42,6 @@ pub struct IrqRouting {}
|
||||
pub enum VcpuExit {}
|
||||
pub struct MpState {}
|
||||
|
||||
#[derive(Eq, PartialEq, Hash, Clone, Debug, Copy)]
|
||||
pub enum IoEventAddress {
|
||||
/// Representation of an programmable I/O address.
|
||||
Pio(u64),
|
||||
/// Representation of an memory mapped I/O address.
|
||||
Mmio(u64),
|
||||
}
|
||||
macro_rules! msr {
|
||||
($msr:expr) => {
|
||||
MsrEntry {
|
||||
index: $msr,
|
||||
data: 0x0,
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! msr_data {
|
||||
($msr:expr, $data:expr) => {
|
||||
MsrEntry {
|
||||
index: $msr,
|
||||
data: $data,
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl SegmentRegisterOps for SegmentRegister {
|
||||
fn segment_type(&self) -> u8 {
|
||||
self.type_
|
||||
|
||||
@@ -238,12 +238,12 @@ pub trait Vm: Send + Sync {
|
||||
}
|
||||
|
||||
pub trait VmmOps: Send + Sync {
|
||||
fn guest_mem_write(&self, buf: &[u8], gpa: u64) -> Result<usize>;
|
||||
fn guest_mem_read(&self, buf: &mut [u8], gpa: u64) -> Result<usize>;
|
||||
fn mmio_read(&self, addr: u64, data: &mut [u8]) -> Result<()>;
|
||||
fn mmio_write(&self, addr: u64, data: &[u8]) -> Result<()>;
|
||||
fn guest_mem_write(&self, gpa: u64, buf: &[u8]) -> Result<usize>;
|
||||
fn guest_mem_read(&self, gpa: u64, buf: &mut [u8]) -> Result<usize>;
|
||||
fn mmio_read(&self, gpa: u64, data: &mut [u8]) -> Result<()>;
|
||||
fn mmio_write(&self, gpa: u64, data: &[u8]) -> Result<()>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn pio_read(&self, addr: u64, data: &mut [u8]) -> Result<()>;
|
||||
fn pio_read(&self, port: u64, data: &mut [u8]) -> Result<()>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn pio_write(&self, addr: u64, data: &[u8]) -> Result<()>;
|
||||
fn pio_write(&self, port: u64, data: &[u8]) -> Result<()>;
|
||||
}
|
||||
|
||||
@@ -5,17 +5,17 @@ authors = ["The Chromium OS Authors"]
|
||||
|
||||
[dependencies]
|
||||
epoll = ">=4.0.1"
|
||||
libc = "0.2.81"
|
||||
log = "0.4.11"
|
||||
libc = "0.2.86"
|
||||
log = "0.4.14"
|
||||
net_gen = { path = "../net_gen" }
|
||||
rand = "0.7.3"
|
||||
serde = "1.0.118"
|
||||
rand = "0.8.3"
|
||||
serde = "1.0.123"
|
||||
virtio-bindings = "0.1.0"
|
||||
vm-memory = { version = "0.4.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vm-memory = { version = "0.5.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = ">=0.3.1"
|
||||
|
||||
[dev-dependencies]
|
||||
lazy_static = "1.3.0"
|
||||
pnet = "0.27.2"
|
||||
serde_json = "1.0.60"
|
||||
serde_json = "1.0.62"
|
||||
|
||||
@@ -65,9 +65,12 @@ impl MacAddr {
|
||||
|
||||
// An error can only occur if the slice length is different from MAC_ADDR_LEN.
|
||||
#[inline]
|
||||
pub fn from_bytes(src: &[u8]) -> Result<MacAddr, ()> {
|
||||
pub fn from_bytes(src: &[u8]) -> Result<MacAddr, io::Error> {
|
||||
if src.len() != MAC_ADDR_LEN {
|
||||
return Err(());
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("invalid length of slice: {} vs {}", src.len(), MAC_ADDR_LEN),
|
||||
));
|
||||
}
|
||||
Ok(MacAddr::from_bytes_unchecked(src))
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ pub fn open_tap(
|
||||
netmask: Option<Ipv4Addr>,
|
||||
host_mac: &mut Option<MacAddr>,
|
||||
num_rx_q: usize,
|
||||
flags: Option<i32>,
|
||||
) -> Result<Vec<Tap>> {
|
||||
let mut taps: Vec<Tap> = Vec::new();
|
||||
let mut ifname: String = String::new();
|
||||
@@ -82,7 +83,7 @@ pub fn open_tap(
|
||||
let tap: Tap;
|
||||
if i == 0 {
|
||||
tap = match if_name {
|
||||
Some(name) => Tap::open_named(name, num_rx_q).map_err(Error::TapOpen)?,
|
||||
Some(name) => Tap::open_named(name, num_rx_q, flags).map_err(Error::TapOpen)?,
|
||||
None => Tap::new(num_rx_q).map_err(Error::TapOpen)?,
|
||||
};
|
||||
if let Some(ip) = ip_addr {
|
||||
@@ -104,7 +105,7 @@ pub fn open_tap(
|
||||
|
||||
ifname = String::from_utf8(tap.get_if_name()).unwrap();
|
||||
} else {
|
||||
tap = Tap::open_named(ifname.as_str(), num_rx_q).map_err(Error::TapOpen)?;
|
||||
tap = Tap::open_named(ifname.as_str(), num_rx_q, flags).map_err(Error::TapOpen)?;
|
||||
tap.set_offload(flag).map_err(Error::TapSetOffload)?;
|
||||
|
||||
tap.set_vnet_hdr_size(vnet_hdr_size)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the THIRD-PARTY file.
|
||||
|
||||
use super::{create_sockaddr, create_socket, Error as NetUtilError, MacAddr};
|
||||
use super::{create_sockaddr, create_socket, vnet_hdr_len, Error as NetUtilError, MacAddr};
|
||||
use mac::MAC_ADDR_LEN;
|
||||
use net_gen;
|
||||
use std::fs::File;
|
||||
@@ -31,7 +31,7 @@ pub enum Error {
|
||||
NetUtil(NetUtilError),
|
||||
InvalidIfname,
|
||||
/// Error parsing MAC data
|
||||
MacParsing(()),
|
||||
MacParsing(IoError),
|
||||
}
|
||||
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
@@ -83,7 +83,7 @@ fn build_terminated_if_name(if_name: &str) -> Result<Vec<u8>> {
|
||||
}
|
||||
|
||||
impl Tap {
|
||||
pub fn open_named(if_name: &str, num_queue_pairs: usize) -> Result<Tap> {
|
||||
pub fn open_named(if_name: &str, num_queue_pairs: usize, flags: Option<i32>) -> Result<Tap> {
|
||||
let terminated_if_name = build_terminated_if_name(if_name)?;
|
||||
|
||||
let fd = unsafe {
|
||||
@@ -91,7 +91,7 @@ impl Tap {
|
||||
// string and verify the result.
|
||||
libc::open(
|
||||
b"/dev/net/tun\0".as_ptr() as *const c_char,
|
||||
libc::O_RDWR | libc::O_NONBLOCK | libc::O_CLOEXEC,
|
||||
flags.unwrap_or(libc::O_RDWR | libc::O_NONBLOCK | libc::O_CLOEXEC),
|
||||
)
|
||||
};
|
||||
if fd < 0 {
|
||||
@@ -150,7 +150,54 @@ impl Tap {
|
||||
|
||||
/// Create a new tap interface.
|
||||
pub fn new(num_queue_pairs: usize) -> Result<Tap> {
|
||||
Self::open_named("vmtap%d", num_queue_pairs)
|
||||
Self::open_named("vmtap%d", num_queue_pairs, None)
|
||||
}
|
||||
|
||||
pub fn from_tap_fd(fd: RawFd, num_queue_pairs: usize) -> Result<Tap> {
|
||||
// Ensure that the file is opened non-blocking, this is particularly
|
||||
// needed when opened via the shell for macvtap.
|
||||
let ret = unsafe {
|
||||
let mut flags = libc::fcntl(fd, libc::F_GETFL);
|
||||
flags |= libc::O_NONBLOCK;
|
||||
libc::fcntl(fd, libc::F_SETFL, flags)
|
||||
};
|
||||
if ret < 0 {
|
||||
return Err(Error::ConfigureTap(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
let tap_file = unsafe { File::from_raw_fd(fd) };
|
||||
let mut ifreq: net_gen::ifreq = Default::default();
|
||||
|
||||
// Get current config including name
|
||||
let ret = unsafe { ioctl_with_mut_ref(&tap_file, net_gen::TUNGETIFF(), &mut ifreq) };
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(IoError::last_os_error()));
|
||||
}
|
||||
let if_name = unsafe { *ifreq.ifr_ifrn.ifrn_name.as_ref() }.to_vec();
|
||||
|
||||
// Try and update flags. Depending on how the tap was created (macvtap
|
||||
// or via open_named()) this might return -EEXIST so we just ignore that.
|
||||
unsafe {
|
||||
let ifru_flags = ifreq.ifr_ifru.ifru_flags.as_mut();
|
||||
*ifru_flags =
|
||||
(net_gen::IFF_TAP | net_gen::IFF_NO_PI | net_gen::IFF_VNET_HDR) as c_short;
|
||||
if num_queue_pairs > 1 {
|
||||
*ifru_flags |= net_gen::IFF_MULTI_QUEUE as c_short;
|
||||
}
|
||||
}
|
||||
let ret = unsafe { ioctl_with_mut_ref(&tap_file, net_gen::TUNSETIFF(), &mut ifreq) };
|
||||
if ret < 0 && IoError::last_os_error().raw_os_error().unwrap() != libc::EEXIST {
|
||||
return Err(Error::ConfigureTap(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
let tap = Tap { if_name, tap_file };
|
||||
let offload_flags =
|
||||
net_gen::TUN_F_CSUM | net_gen::TUN_F_UFO | net_gen::TUN_F_TSO4 | net_gen::TUN_F_TSO6;
|
||||
let vnet_hdr_size = vnet_hdr_len() as i32;
|
||||
tap.set_offload(offload_flags)?;
|
||||
tap.set_vnet_hdr_size(vnet_hdr_size)?;
|
||||
|
||||
Ok(tap)
|
||||
}
|
||||
|
||||
/// Set the host-side IP address for the tap interface.
|
||||
@@ -535,10 +582,21 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_tap_create() {
|
||||
let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
|
||||
|
||||
let t = Tap::new(1).unwrap();
|
||||
println!("created tap: {:?}", t);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tap_from_fd() {
|
||||
let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
|
||||
|
||||
let orig_tap = Tap::new(1).unwrap();
|
||||
let fd = orig_tap.as_raw_fd();
|
||||
let _new_tap = Tap::from_tap_fd(fd, 1).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tap_configure() {
|
||||
// This should be the first thing to be called inside the function, so everything else
|
||||
@@ -559,6 +617,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_set_options() {
|
||||
let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
|
||||
|
||||
// This line will fail to provide an initialized FD if the test is not run as root.
|
||||
let tap = Tap::new(1).unwrap();
|
||||
tap.set_vnet_hdr_size(16).unwrap();
|
||||
@@ -567,6 +627,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_tap_enable() {
|
||||
let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
|
||||
|
||||
let tap = Tap::new(1).unwrap();
|
||||
let ret = tap.enable();
|
||||
assert!(ret.is_ok());
|
||||
@@ -574,6 +636,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_tap_get_ifreq() {
|
||||
let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
|
||||
|
||||
let tap = Tap::new(1).unwrap();
|
||||
let ret = tap.get_ifreq();
|
||||
assert_eq!(
|
||||
@@ -584,6 +648,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_raw_fd() {
|
||||
let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
|
||||
|
||||
let tap = Tap::new(1).unwrap();
|
||||
assert_eq!(tap.as_raw_fd(), tap.tap_file.as_raw_fd());
|
||||
}
|
||||
|
||||
@@ -10,14 +10,14 @@ byteorder = "1.3.4"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
vfio-ioctls = { git = "https://github.com/cloud-hypervisor/vfio-ioctls", branch = "ch" }
|
||||
vmm-sys-util = ">=0.3.1"
|
||||
libc = "0.2.81"
|
||||
log = "0.4.11"
|
||||
libc = "0.2.86"
|
||||
log = "0.4.14"
|
||||
serde = {version = ">=1.0.27", features = ["rc"] }
|
||||
serde_derive = ">=1.0.27"
|
||||
serde_json = ">=1.0.9"
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = "0.4.0"
|
||||
vm-memory = "0.5.0"
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
|
||||
[dependencies.vfio-bindings]
|
||||
|
||||
@@ -10,7 +10,7 @@ use byteorder::{ByteOrder, LittleEndian};
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::DerefMut;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use vm_device::{Bus, BusDevice};
|
||||
use vm_memory::{Address, GuestAddress, GuestUsize};
|
||||
|
||||
@@ -71,8 +71,14 @@ impl PciRoot {
|
||||
impl BusDevice for PciRoot {}
|
||||
|
||||
impl PciDevice for PciRoot {
|
||||
fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
|
||||
fn write_config_register(
|
||||
&mut self,
|
||||
reg_idx: usize,
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
) -> Option<Arc<Barrier>> {
|
||||
self.config.write_config_register(reg_idx, offset, data);
|
||||
None
|
||||
}
|
||||
|
||||
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
|
||||
@@ -225,14 +231,14 @@ impl PciConfigIo {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn config_space_write(&mut self, offset: u64, data: &[u8]) {
|
||||
pub fn config_space_write(&mut self, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
if offset as usize + data.len() > 4 {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
let enabled = (self.config_address & 0x8000_0000) != 0;
|
||||
if !enabled {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
let (bus, device, _function, register) =
|
||||
@@ -240,7 +246,7 @@ impl PciConfigIo {
|
||||
|
||||
// Only support one bus.
|
||||
if bus != 0 {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
let pci_bus = self.pci_bus.lock().unwrap();
|
||||
@@ -265,7 +271,9 @@ impl PciConfigIo {
|
||||
}
|
||||
|
||||
// Update the register value
|
||||
device.write_config_register(register, offset, data);
|
||||
device.write_config_register(register, offset, data)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,13 +320,16 @@ impl BusDevice for PciConfigIo {
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
// `offset` is relative to 0xcf8
|
||||
match offset {
|
||||
o @ 0..=3 => self.set_config_address(o, data),
|
||||
o @ 0..=3 => {
|
||||
self.set_config_address(o, data);
|
||||
None
|
||||
}
|
||||
o @ 4..=7 => self.config_space_write(o - 4, data),
|
||||
_ => (),
|
||||
};
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,11 +418,13 @@ impl BusDevice for PciConfigMmio {
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
if offset > u64::from(u32::max_value()) {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
self.config_space_write(offset as u32, offset % 4, data)
|
||||
self.config_space_write(offset as u32, offset % 4, data);
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -481,7 +481,7 @@ impl PciConfiguration {
|
||||
pub fn write_reg(&mut self, reg_idx: usize, value: u32) {
|
||||
let mut mask = self.writable_bits[reg_idx];
|
||||
|
||||
if reg_idx >= BAR0_REG && reg_idx < BAR0_REG + NUM_BAR_REGS {
|
||||
if (BAR0_REG..BAR0_REG + NUM_BAR_REGS).contains(®_idx) {
|
||||
// Handle very specific case where the BAR is being written with
|
||||
// all 1's to retrieve the BAR size during next BAR reading.
|
||||
if value == 0xffff_ffff {
|
||||
@@ -768,7 +768,7 @@ impl PciConfiguration {
|
||||
let value = LittleEndian::read_u32(data);
|
||||
|
||||
let mask = self.writable_bits[reg_idx];
|
||||
if reg_idx >= BAR0_REG && reg_idx < BAR0_REG + NUM_BAR_REGS {
|
||||
if (BAR0_REG..BAR0_REG + NUM_BAR_REGS).contains(®_idx) {
|
||||
let bar_idx = reg_idx - 4;
|
||||
if (value & mask) != (self.bars[bar_idx].addr & mask) {
|
||||
// Handle special case where the address being written is
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
use crate::configuration::{self, PciBarRegionType};
|
||||
use std::any::Any;
|
||||
use std::fmt::{self, Display};
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::{self, io, result};
|
||||
use vm_allocator::SystemAllocator;
|
||||
use vm_device::BusDevice;
|
||||
@@ -63,7 +64,12 @@ pub trait PciDevice: BusDevice {
|
||||
/// Sets a register in the configuration space.
|
||||
/// * `reg_idx` - The index of the config register to modify.
|
||||
/// * `offset` - Offset in to the register.
|
||||
fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]);
|
||||
fn write_config_register(
|
||||
&mut self,
|
||||
reg_idx: usize,
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
) -> Option<Arc<Barrier>>;
|
||||
/// Gets a register from the configuration space.
|
||||
/// * `reg_idx` - The index of the config register to read.
|
||||
fn read_config_register(&mut self, reg_idx: usize) -> u32;
|
||||
@@ -82,7 +88,9 @@ pub trait PciDevice: BusDevice {
|
||||
/// Writes to a BAR region mapped in to the device.
|
||||
/// * `addr` - The guest address inside the BAR.
|
||||
/// * `data` - The data to write.
|
||||
fn write_bar(&mut self, _base: u64, _offset: u64, _data: &[u8]) {}
|
||||
fn write_bar(&mut self, _base: u64, _offset: u64, _data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
None
|
||||
}
|
||||
/// Relocates the BAR to a different address in guest address space.
|
||||
fn move_bar(&mut self, _old_base: u64, _new_base: u64) -> result::Result<(), io::Error> {
|
||||
Ok(())
|
||||
|
||||
279
pci/src/vfio.rs
279
pci/src/vfio.rs
@@ -15,7 +15,7 @@ use std::any::Any;
|
||||
use std::ops::Deref;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::ptr::null_mut;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::{fmt, io, result};
|
||||
use vfio_bindings::bindings::vfio::*;
|
||||
use vfio_ioctls::{VfioDevice, VfioError};
|
||||
@@ -33,17 +33,19 @@ use vmm_sys_util::eventfd::EventFd;
|
||||
#[derive(Debug)]
|
||||
pub enum VfioPciError {
|
||||
AllocateGsi,
|
||||
EnableIntx(VfioError),
|
||||
EnableMsi(VfioError),
|
||||
EnableMsix(VfioError),
|
||||
EventFd(io::Error),
|
||||
InterruptSourceGroupCreate(io::Error),
|
||||
IrqFd(hypervisor::HypervisorVmError),
|
||||
NewVfioPciDevice,
|
||||
MapRegionGuest(anyhow::Error),
|
||||
SetGsiRouting(hypervisor::HypervisorVmError),
|
||||
MissingNotifier,
|
||||
MsiNotConfigured,
|
||||
MsixNotConfigured,
|
||||
NewVfioPciDevice,
|
||||
SetGsiRouting(hypervisor::HypervisorVmError),
|
||||
UpdateMemory(VfioError),
|
||||
UpdateMsiEventFd,
|
||||
UpdateMsixEventFd,
|
||||
}
|
||||
pub type Result<T> = std::result::Result<T, VfioPciError>;
|
||||
|
||||
@@ -51,21 +53,23 @@ impl fmt::Display for VfioPciError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
VfioPciError::AllocateGsi => write!(f, "failed to allocate GSI"),
|
||||
VfioPciError::EnableIntx(e) => write!(f, "failed to enable INTx: {}", e),
|
||||
VfioPciError::EnableMsi(e) => write!(f, "failed to enable MSI: {}", e),
|
||||
VfioPciError::EnableMsix(e) => write!(f, "failed to enable MSI-X: {}", e),
|
||||
VfioPciError::EventFd(e) => write!(f, "failed to create eventfd: {}", e),
|
||||
VfioPciError::InterruptSourceGroupCreate(e) => {
|
||||
write!(f, "failed to create interrupt source group: {}", e)
|
||||
}
|
||||
VfioPciError::IrqFd(e) => write!(f, "failed to register irqfd: {}", e),
|
||||
VfioPciError::NewVfioPciDevice => write!(f, "failed to create VFIO PCI device"),
|
||||
VfioPciError::MapRegionGuest(e) => {
|
||||
write!(f, "failed to map VFIO PCI region into guest: {}", e)
|
||||
}
|
||||
VfioPciError::SetGsiRouting(e) => write!(f, "failed to set GSI routes: {}", e),
|
||||
VfioPciError::MissingNotifier => write!(f, "failed to notifier's eventfd"),
|
||||
VfioPciError::MsiNotConfigured => write!(f, "MSI interrupt not yet configured"),
|
||||
VfioPciError::MsixNotConfigured => write!(f, "MSI-X interrupt not yet configured"),
|
||||
VfioPciError::NewVfioPciDevice => write!(f, "failed to create VFIO PCI device"),
|
||||
VfioPciError::SetGsiRouting(e) => write!(f, "failed to set GSI routes: {}", e),
|
||||
VfioPciError::UpdateMemory(e) => write!(f, "failed to update memory: {}", e),
|
||||
VfioPciError::UpdateMsiEventFd => write!(f, "failed to update MSI eventfd"),
|
||||
VfioPciError::UpdateMsixEventFd => write!(f, "failed to update MSI-X eventfd"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,6 +92,11 @@ enum InterruptUpdateAction {
|
||||
DisableMsix,
|
||||
}
|
||||
|
||||
struct VfioIntx {
|
||||
interrupt_source_group: Arc<Box<dyn InterruptSourceGroup>>,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
struct VfioMsi {
|
||||
cfg: MsiConfig,
|
||||
cap_offset: u32,
|
||||
@@ -153,6 +162,7 @@ impl VfioMsix {
|
||||
}
|
||||
|
||||
struct Interrupt {
|
||||
intx: Option<VfioIntx>,
|
||||
msi: Option<VfioMsi>,
|
||||
msix: Option<VfioMsix>,
|
||||
}
|
||||
@@ -218,6 +228,14 @@ impl Interrupt {
|
||||
msix.bar.read_table(offset, data)
|
||||
}
|
||||
}
|
||||
|
||||
fn intx_in_use(&self) -> bool {
|
||||
if let Some(intx) = &self.intx {
|
||||
return intx.enabled;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@@ -292,7 +310,8 @@ impl VfioPciDevice {
|
||||
pub fn new(
|
||||
vm: &Arc<dyn hypervisor::Vm>,
|
||||
device: VfioDevice,
|
||||
interrupt_manager: &Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
msi_interrupt_manager: &Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
legacy_interrupt_group: Option<Arc<Box<dyn InterruptSourceGroup>>>,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
) -> Result<Self> {
|
||||
let device = Arc::new(device);
|
||||
@@ -320,17 +339,124 @@ impl VfioPciDevice {
|
||||
vfio_pci_configuration,
|
||||
mmio_regions: Vec::new(),
|
||||
interrupt: Interrupt {
|
||||
intx: None,
|
||||
msi: None,
|
||||
msix: None,
|
||||
},
|
||||
mem,
|
||||
};
|
||||
|
||||
vfio_pci_device.parse_capabilities(interrupt_manager);
|
||||
vfio_pci_device.parse_capabilities(msi_interrupt_manager);
|
||||
|
||||
vfio_pci_device.initialize_legacy_interrupt(legacy_interrupt_group)?;
|
||||
|
||||
Ok(vfio_pci_device)
|
||||
}
|
||||
|
||||
fn enable_intx(&mut self) -> Result<()> {
|
||||
if let Some(intx) = &mut self.interrupt.intx {
|
||||
if !intx.enabled {
|
||||
if let Some(eventfd) = intx.interrupt_source_group.notifier(0) {
|
||||
self.device
|
||||
.enable_irq(VFIO_PCI_INTX_IRQ_INDEX, vec![&eventfd])
|
||||
.map_err(VfioPciError::EnableIntx)?;
|
||||
|
||||
intx.enabled = true;
|
||||
} else {
|
||||
return Err(VfioPciError::MissingNotifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable_intx(&mut self) {
|
||||
if let Some(intx) = &mut self.interrupt.intx {
|
||||
if intx.enabled {
|
||||
if let Err(e) = self.device.disable_irq(VFIO_PCI_INTX_IRQ_INDEX) {
|
||||
error!("Could not disable INTx: {}", e);
|
||||
} else {
|
||||
intx.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_msi(&self) -> Result<()> {
|
||||
if let Some(msi) = &self.interrupt.msi {
|
||||
let mut irq_fds: Vec<EventFd> = Vec::new();
|
||||
for i in 0..msi.cfg.num_enabled_vectors() {
|
||||
if let Some(eventfd) = msi.interrupt_source_group.notifier(i as InterruptIndex) {
|
||||
irq_fds.push(eventfd);
|
||||
} else {
|
||||
return Err(VfioPciError::MissingNotifier);
|
||||
}
|
||||
}
|
||||
|
||||
self.device
|
||||
.enable_msi(irq_fds.iter().collect())
|
||||
.map_err(VfioPciError::EnableMsi)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable_msi(&self) {
|
||||
if let Err(e) = self.device.disable_msi() {
|
||||
error!("Could not disable MSI: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_msix(&self) -> Result<()> {
|
||||
if let Some(msix) = &self.interrupt.msix {
|
||||
let mut irq_fds: Vec<EventFd> = Vec::new();
|
||||
for i in 0..msix.bar.table_entries.len() {
|
||||
if let Some(eventfd) = msix.interrupt_source_group.notifier(i as InterruptIndex) {
|
||||
irq_fds.push(eventfd);
|
||||
} else {
|
||||
return Err(VfioPciError::MissingNotifier);
|
||||
}
|
||||
}
|
||||
|
||||
self.device
|
||||
.enable_msix(irq_fds.iter().collect())
|
||||
.map_err(VfioPciError::EnableMsi)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable_msix(&self) {
|
||||
if let Err(e) = self.device.disable_msix() {
|
||||
error!("Could not disable MSI-X: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn initialize_legacy_interrupt(
|
||||
&mut self,
|
||||
legacy_interrupt_group: Option<Arc<Box<dyn InterruptSourceGroup>>>,
|
||||
) -> Result<()> {
|
||||
if let Some(irq_info) = self.device.get_irq_info(VFIO_PCI_INTX_IRQ_INDEX) {
|
||||
if irq_info.count == 0 {
|
||||
// A count of 0 means the INTx IRQ is not supported, therefore
|
||||
// it shouldn't be initialized.
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(interrupt_source_group) = legacy_interrupt_group {
|
||||
self.interrupt.intx = Some(VfioIntx {
|
||||
interrupt_source_group,
|
||||
enabled: false,
|
||||
});
|
||||
|
||||
self.enable_intx()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_msix_capabilities(
|
||||
&mut self,
|
||||
cap: u8,
|
||||
@@ -411,10 +537,22 @@ impl VfioPciDevice {
|
||||
|
||||
match PciCapabilityID::from(cap_id) {
|
||||
PciCapabilityID::MessageSignalledInterrupts => {
|
||||
self.parse_msi_capabilities(cap_next, interrupt_manager);
|
||||
if let Some(irq_info) = self.device.get_irq_info(VFIO_PCI_MSI_IRQ_INDEX) {
|
||||
if irq_info.count > 0 {
|
||||
// Parse capability only if the VFIO device
|
||||
// supports MSI.
|
||||
self.parse_msi_capabilities(cap_next, interrupt_manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
PciCapabilityID::MSIX => {
|
||||
self.parse_msix_capabilities(cap_next, interrupt_manager);
|
||||
if let Some(irq_info) = self.device.get_irq_info(VFIO_PCI_MSIX_IRQ_INDEX) {
|
||||
if irq_info.count > 0 {
|
||||
// Parse capability only if the VFIO device
|
||||
// supports MSI-X.
|
||||
self.parse_msix_capabilities(cap_next, interrupt_manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
@@ -428,27 +566,14 @@ impl VfioPciDevice {
|
||||
fn update_msi_capabilities(&mut self, offset: u64, data: &[u8]) -> Result<()> {
|
||||
match self.interrupt.update_msi(offset, data) {
|
||||
Some(InterruptUpdateAction::EnableMsi) => {
|
||||
if let Some(msi) = &self.interrupt.msi {
|
||||
let mut irq_fds: Vec<&EventFd> = Vec::new();
|
||||
for i in 0..msi.cfg.num_enabled_vectors() {
|
||||
if let Some(eventfd) =
|
||||
msi.interrupt_source_group.notifier(i as InterruptIndex)
|
||||
{
|
||||
irq_fds.push(eventfd);
|
||||
} else {
|
||||
return Err(VfioPciError::UpdateMsiEventFd);
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = self.device.enable_msi(irq_fds) {
|
||||
warn!("Could not enable MSI: {}", e);
|
||||
}
|
||||
}
|
||||
// Disable INTx before we can enable MSI
|
||||
self.disable_intx();
|
||||
self.enable_msi()?;
|
||||
}
|
||||
Some(InterruptUpdateAction::DisableMsi) => {
|
||||
if let Err(e) = self.device.disable_msi() {
|
||||
warn!("Could not disable MSI: {}", e);
|
||||
}
|
||||
// Fallback onto INTx when disabling MSI
|
||||
self.disable_msi();
|
||||
self.enable_intx()?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -459,27 +584,14 @@ impl VfioPciDevice {
|
||||
fn update_msix_capabilities(&mut self, offset: u64, data: &[u8]) -> Result<()> {
|
||||
match self.interrupt.update_msix(offset, data) {
|
||||
Some(InterruptUpdateAction::EnableMsix) => {
|
||||
if let Some(msix) = &self.interrupt.msix {
|
||||
let mut irq_fds: Vec<&EventFd> = Vec::new();
|
||||
for i in 0..msix.bar.table_entries.len() {
|
||||
if let Some(eventfd) =
|
||||
msix.interrupt_source_group.notifier(i as InterruptIndex)
|
||||
{
|
||||
irq_fds.push(eventfd);
|
||||
} else {
|
||||
return Err(VfioPciError::UpdateMsiEventFd);
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = self.device.enable_msix(irq_fds) {
|
||||
warn!("Could not enable MSI-X: {}", e);
|
||||
}
|
||||
}
|
||||
// Disable INTx before we can enable MSI-X
|
||||
self.disable_intx();
|
||||
self.enable_msix()?;
|
||||
}
|
||||
Some(InterruptUpdateAction::DisableMsix) => {
|
||||
if let Err(e) = self.device.disable_msix() {
|
||||
warn!("Could not disable MSI-X: {}", e);
|
||||
}
|
||||
// Fallback onto INTx when disabling MSI-X
|
||||
self.disable_msix();
|
||||
self.enable_intx()?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -626,17 +738,21 @@ impl Drop for VfioPciDevice {
|
||||
self.unmap_mmio_regions();
|
||||
|
||||
if let Some(msix) = &self.interrupt.msix {
|
||||
if msix.bar.enabled() && self.device.disable_msix().is_err() {
|
||||
error!("Could not disable MSI-X");
|
||||
if msix.bar.enabled() {
|
||||
self.disable_msix();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(msi) = &self.interrupt.msi {
|
||||
if msi.cfg.enabled() && self.device.disable_msi().is_err() {
|
||||
error!("Could not disable MSI");
|
||||
if msi.cfg.enabled() {
|
||||
self.disable_msi();
|
||||
}
|
||||
}
|
||||
|
||||
if self.interrupt.intx_in_use() {
|
||||
self.disable_intx();
|
||||
}
|
||||
|
||||
if self
|
||||
.device
|
||||
.unset_dma_map(self.mem.memory().deref())
|
||||
@@ -652,7 +768,7 @@ impl BusDevice for VfioPciDevice {
|
||||
self.read_bar(base, offset, data)
|
||||
}
|
||||
|
||||
fn write(&mut self, base: u64, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
self.write_bar(base, offset, data)
|
||||
}
|
||||
}
|
||||
@@ -677,8 +793,6 @@ const PCI_HEADER_TYPE_REG_INDEX: usize = 3;
|
||||
const PCI_CONFIG_BAR0_INDEX: usize = 4;
|
||||
// PCI ROM expansion BAR register index
|
||||
const PCI_ROM_EXP_BAR_INDEX: usize = 12;
|
||||
// PCI interrupt pin and line register index
|
||||
const PCI_INTX_REG_INDEX: usize = 15;
|
||||
|
||||
impl PciDevice for VfioPciDevice {
|
||||
fn allocate_bars(
|
||||
@@ -883,19 +997,24 @@ impl PciDevice for VfioPciDevice {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
|
||||
fn write_config_register(
|
||||
&mut self,
|
||||
reg_idx: usize,
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
) -> Option<Arc<Barrier>> {
|
||||
// When the guest wants to write to a BAR, we trap it into
|
||||
// our local configuration space. We're not reprogramming
|
||||
// VFIO device.
|
||||
if (reg_idx >= PCI_CONFIG_BAR0_INDEX && reg_idx < PCI_CONFIG_BAR0_INDEX + BAR_NUMS)
|
||||
if (PCI_CONFIG_BAR0_INDEX..PCI_CONFIG_BAR0_INDEX + BAR_NUMS).contains(®_idx)
|
||||
|| reg_idx == PCI_ROM_EXP_BAR_INDEX
|
||||
{
|
||||
// We keep our local cache updated with the BARs.
|
||||
// We'll read it back from there when the guest is asking
|
||||
// for BARs (see read_config_register()).
|
||||
return self
|
||||
.configuration
|
||||
self.configuration
|
||||
.write_config_register(reg_idx, offset, data);
|
||||
return None;
|
||||
}
|
||||
|
||||
let reg = (reg_idx * PCI_CONFIG_REGISTER_SIZE) as u64;
|
||||
@@ -931,6 +1050,8 @@ impl PciDevice for VfioPciDevice {
|
||||
// to the device region to update the MSI Enable bit.
|
||||
self.device
|
||||
.region_write(VFIO_PCI_CONFIG_REGION_INDEX, data, reg + offset);
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
|
||||
@@ -938,24 +1059,16 @@ impl PciDevice for VfioPciDevice {
|
||||
// from our local configuration space. We want the guest to
|
||||
// use that and not the VFIO device BARs as it does not map
|
||||
// with the guest address space.
|
||||
if (reg_idx >= PCI_CONFIG_BAR0_INDEX && reg_idx < PCI_CONFIG_BAR0_INDEX + BAR_NUMS)
|
||||
if (PCI_CONFIG_BAR0_INDEX..PCI_CONFIG_BAR0_INDEX + BAR_NUMS).contains(®_idx)
|
||||
|| reg_idx == PCI_ROM_EXP_BAR_INDEX
|
||||
{
|
||||
return self.configuration.read_reg(reg_idx);
|
||||
}
|
||||
|
||||
// Since we don't support INTx (only MSI and MSI-X), we should not
|
||||
// expose an invalid Interrupt Pin to the guest. By using a specific
|
||||
// mask in case the register being read correspond to the interrupt
|
||||
// register, this code makes sure to always expose an Interrupt Pin
|
||||
// value of 0, which stands for no interrupt pin support.
|
||||
//
|
||||
// Since we don't support passing multi-functions devices, we should
|
||||
// mask the multi-function bit, bit 7 of the Header Type byte on the
|
||||
// register 3.
|
||||
let mask = if reg_idx == PCI_INTX_REG_INDEX {
|
||||
0xffff_00ff
|
||||
} else if reg_idx == PCI_HEADER_TYPE_REG_INDEX {
|
||||
let mask = if reg_idx == PCI_HEADER_TYPE_REG_INDEX {
|
||||
0xff7f_ffff
|
||||
} else {
|
||||
0xffff_ffff
|
||||
@@ -986,9 +1099,18 @@ impl PciDevice for VfioPciDevice {
|
||||
self.device.region_read(region.index, data, offset);
|
||||
}
|
||||
}
|
||||
|
||||
// INTx EOI
|
||||
// The guest reading from the BAR potentially means the interrupt has
|
||||
// been received and can be acknowledged.
|
||||
if self.interrupt.intx_in_use() {
|
||||
if let Err(e) = self.device.unmask_irq(VFIO_PCI_INTX_IRQ_INDEX) {
|
||||
error!("Failed unmasking INTx IRQ: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_bar(&mut self, base: u64, offset: u64, data: &[u8]) {
|
||||
fn write_bar(&mut self, base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
let addr = base + offset;
|
||||
if let Some(region) = self.find_region(addr) {
|
||||
let offset = addr - region.start.raw_value();
|
||||
@@ -1000,6 +1122,17 @@ impl PciDevice for VfioPciDevice {
|
||||
self.device.region_write(region.index, data, offset);
|
||||
}
|
||||
}
|
||||
|
||||
// INTx EOI
|
||||
// The guest writing to the BAR potentially means the interrupt has
|
||||
// been received and can be acknowledged.
|
||||
if self.interrupt.intx_in_use() {
|
||||
if let Err(e) = self.device.unmask_irq(VFIO_PCI_INTX_IRQ_INDEX) {
|
||||
error!("Failed unmasking INTx IRQ: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn move_bar(&mut self, old_base: u64, new_base: u64) -> result::Result<(), io::Error> {
|
||||
|
||||
@@ -10,10 +10,10 @@ path = "src/qcow.rs"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.3.4"
|
||||
libc = "0.2.81"
|
||||
log = "0.4.11"
|
||||
libc = "0.2.86"
|
||||
log = "0.4.14"
|
||||
remain = "0.2.2"
|
||||
vmm-sys-util = ">=0.3.1"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.1.0"
|
||||
tempfile = "3.2.0"
|
||||
|
||||
@@ -409,7 +409,7 @@ impl QcowFile {
|
||||
}
|
||||
|
||||
let cluster_bits: u32 = header.cluster_bits;
|
||||
if cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS {
|
||||
if !(MIN_CLUSTER_BITS..=MAX_CLUSTER_BITS).contains(&cluster_bits) {
|
||||
return Err(Error::InvalidClusterSize);
|
||||
}
|
||||
let cluster_size = 0x01u64 << cluster_bits;
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
- [v0.13.0](#v0130)
|
||||
- [Wider VFIO device support](#wider-vfio-device-support)
|
||||
- [Improve huge page support](#improve-huge-page-support)
|
||||
- [MACvTAP support](#macvtap-support)
|
||||
- [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)
|
||||
- [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)
|
||||
- [Contributors](#contributors-1)
|
||||
- [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)
|
||||
@@ -16,14 +24,14 @@
|
||||
- [New `--balloon` Parameter Added](#new---balloon-parameter-added)
|
||||
- [Experimental `virtio-watchdog` Support](#experimental-virtio-watchdog-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||
- [Contributors](#contributors-1)
|
||||
- [Contributors](#contributors-2)
|
||||
- [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-1)
|
||||
- [Contributors](#contributors-2)
|
||||
- [Contributors](#contributors-3)
|
||||
- [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)
|
||||
@@ -37,7 +45,7 @@
|
||||
- [Intel SGX Support](#intel-sgx-support)
|
||||
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
||||
- [Contributors](#contributors-3)
|
||||
- [Contributors](#contributors-4)
|
||||
- [v0.8.0](#v080)
|
||||
- [Experimental Snapshot and Restore Support](#experimental-snapshot-and-restore-support)
|
||||
- [Experimental ARM64 Support](#experimental-arm64-support)
|
||||
@@ -46,7 +54,7 @@
|
||||
- [`vhost_user_fs` Improvements](#vhost_user_fs-improvements)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-3)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes)
|
||||
- [Contributors](#contributors-4)
|
||||
- [Contributors](#contributors-5)
|
||||
- [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)
|
||||
@@ -56,14 +64,14 @@
|
||||
- [`Seccomp` Sandboxing](#seccomp-sandboxing)
|
||||
- [Updated Distribution Support](#updated-distribution-support)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes-1)
|
||||
- [Contributors](#contributors-5)
|
||||
- [Contributors](#contributors-6)
|
||||
- [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-6)
|
||||
- [Contributors](#contributors-7)
|
||||
- [v0.5.1](#v051)
|
||||
- [v0.5.0](#v050)
|
||||
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
|
||||
@@ -71,7 +79,7 @@
|
||||
- [New Interrupt Management Framework](#new-interrupt-management-framework)
|
||||
- [Development Tools](#development-tools)
|
||||
- [Kata Containers Integration](#kata-containers-integration)
|
||||
- [Contributors](#contributors-7)
|
||||
- [Contributors](#contributors-8)
|
||||
- [v0.4.0](#v040)
|
||||
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
|
||||
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
|
||||
@@ -80,7 +88,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-8)
|
||||
- [Contributors](#contributors-9)
|
||||
- [v0.3.0](#v030)
|
||||
- [Block device offloading](#block-device-offloading)
|
||||
- [Network device backend](#network-device-backend)
|
||||
@@ -107,6 +115,68 @@
|
||||
- [Unit testing](#unit-testing)
|
||||
- [Integration tests parallelization](#integration-tests-parallelization)
|
||||
|
||||
# v0.13.0
|
||||
|
||||
This release has been tracked through the [0.13.0 project](https://github.com/cloud-hypervisor/cloud-hypervisor/projects/16).
|
||||
|
||||
Highlights for `cloud-hypervisor` version 0.13.0 include:
|
||||
|
||||
### Wider VFIO device support
|
||||
|
||||
It is now possible to use Cloud Hypervisor's VFIO support to passthrough PCI
|
||||
devices that do not support MSI or MSI-X and instead rely on INTx interrupts.
|
||||
Most notably this widens the support to most NVIDIA cards with the proprietary
|
||||
drivers.
|
||||
|
||||
### Improve huge page support
|
||||
|
||||
Through the addition of `hugepage_size` on `--memory` it is now possible to
|
||||
specify the desired size of the huge pages used when allocating the guest
|
||||
memory. The user is required to ensure they have sufficient pages of the
|
||||
desired size in their pool.
|
||||
|
||||
### MACvTAP support
|
||||
|
||||
It is now possible to provide file descriptors using the `fd` parameter to
|
||||
`--net` which point at TAP devices that have already been opened by the user.
|
||||
This aids integration with `libvirt` but also permits the use of MACvTAP
|
||||
support. This is documented in dedicated [macvtap documentation](docs/macvtap-bridge.md).
|
||||
|
||||
### VHD disk image support
|
||||
|
||||
It is now possible to use VHD (fixed) disk images as well as QCOWv2 and raw
|
||||
disk image with Cloud Hypervisor.
|
||||
|
||||
### Improved Virtio device threading
|
||||
|
||||
Device threads are now derived from the main VMM thread which allows more
|
||||
restrictive seccomp filters to be applied to them. The threads also have a
|
||||
predictable name derived from the device id.
|
||||
|
||||
### Clean shutdown support via synthetic power button
|
||||
|
||||
It is now possible to request that the guest VM shut itself down by triggering
|
||||
a synthetic ACPI power button press from the VMM. If the guest is listening for
|
||||
such an event (e.g. using systemd) then it will process the event and cleanly
|
||||
shut down. This functionality is exposed through the HTTP API and can be
|
||||
triggered via `ch-remote --api-socket=<API socket> power-button`.
|
||||
|
||||
### Contributors
|
||||
|
||||
Many thanks to everyone who has contributed to our 0.13.0 release including
|
||||
some new faces.
|
||||
|
||||
* Bo Chen <chen.bo@intel.com>
|
||||
* Mikko Ylinen <mikko.ylinen@intel.com>
|
||||
* Muminul Islam <muislam@microsoft.com>
|
||||
* Rob Bradford <robert.bradford@intel.com>
|
||||
* Samuel Ortiz <sameo@linux.intel.com>
|
||||
* Sebastien Boeuf <sebastien.boeuf@intel.com>
|
||||
* Vineeth Pillai <viremana@linux.microsoft.com>
|
||||
* Wei Liu <liuwe@microsoft.com>
|
||||
* William Douglas <william.r.douglas@gmail.com>
|
||||
* Xie Yongji <xieyongji@bytedance.com>
|
||||
|
||||
# v0.12.0
|
||||
|
||||
This release has been tracked through the [0.12.0 project](https://github.com/cloud-hypervisor/cloud-hypervisor/projects/15).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
FROM ubuntu:18.04 as dev
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG RUST_TOOLCHAIN="1.46.0"
|
||||
ARG RUST_TOOLCHAIN="1.49.0"
|
||||
ARG CLH_SRC_DIR="/cloud-hypervisor"
|
||||
ARG CLH_BUILD_DIR="$CLH_SRC_DIR/build"
|
||||
ARG CARGO_REGISTRY_DIR="$CLH_BUILD_DIR/cargo_registry"
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#
|
||||
# Automatically generated file; DO NOT EDIT.
|
||||
# Linux/arm64 5.8.0-rc4 Kernel Configuration
|
||||
# Linux/arm64 5.10.0 Kernel Configuration
|
||||
#
|
||||
CONFIG_CC_VERSION_TEXT="gcc (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0"
|
||||
CONFIG_CC_VERSION_TEXT="gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0"
|
||||
CONFIG_CC_IS_GCC=y
|
||||
CONFIG_GCC_VERSION=70500
|
||||
CONFIG_LD_VERSION=230000000
|
||||
CONFIG_GCC_VERSION=90300
|
||||
CONFIG_LD_VERSION=234000000
|
||||
CONFIG_CLANG_VERSION=0
|
||||
CONFIG_LLD_VERSION=0
|
||||
CONFIG_CC_CAN_LINK=y
|
||||
CONFIG_CC_CAN_LINK_STATIC=y
|
||||
CONFIG_CC_HAS_ASM_GOTO=y
|
||||
@@ -48,6 +49,7 @@ CONFIG_GENERIC_IRQ_MIGRATION=y
|
||||
CONFIG_HARDIRQS_SW_RESEND=y
|
||||
CONFIG_IRQ_DOMAIN=y
|
||||
CONFIG_IRQ_DOMAIN_HIERARCHY=y
|
||||
CONFIG_GENERIC_IRQ_IPI=y
|
||||
CONFIG_GENERIC_MSI_IRQ=y
|
||||
CONFIG_GENERIC_MSI_IRQ_DOMAIN=y
|
||||
CONFIG_IRQ_MSI_IOMMU=y
|
||||
@@ -86,7 +88,6 @@ CONFIG_TICK_CPU_ACCOUNTING=y
|
||||
# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set
|
||||
# CONFIG_IRQ_TIME_ACCOUNTING is not set
|
||||
CONFIG_HAVE_SCHED_AVG_IRQ=y
|
||||
# CONFIG_SCHED_THERMAL_PRESSURE is not set
|
||||
CONFIG_BSD_PROCESS_ACCT=y
|
||||
CONFIG_BSD_PROCESS_ACCT_V3=y
|
||||
CONFIG_TASKSTATS=y
|
||||
@@ -105,6 +106,8 @@ CONFIG_TREE_RCU=y
|
||||
# CONFIG_RCU_EXPERT is not set
|
||||
CONFIG_SRCU=y
|
||||
CONFIG_TREE_SRCU=y
|
||||
CONFIG_TASKS_RCU_GENERIC=y
|
||||
CONFIG_TASKS_TRACE_RCU=y
|
||||
CONFIG_RCU_STALL_COMMON=y
|
||||
CONFIG_RCU_NEED_SEGCBLIST=y
|
||||
# end of RCU Subsystem
|
||||
@@ -151,6 +154,7 @@ CONFIG_CGROUP_BPF=y
|
||||
CONFIG_SOCK_CGROUP_DATA=y
|
||||
CONFIG_NAMESPACES=y
|
||||
CONFIG_UTS_NS=y
|
||||
CONFIG_TIME_NS=y
|
||||
CONFIG_IPC_NS=y
|
||||
CONFIG_USER_NS=y
|
||||
CONFIG_PID_NS=y
|
||||
@@ -167,9 +171,11 @@ CONFIG_RD_LZMA=y
|
||||
CONFIG_RD_XZ=y
|
||||
CONFIG_RD_LZO=y
|
||||
CONFIG_RD_LZ4=y
|
||||
# CONFIG_RD_ZSTD is not set
|
||||
# CONFIG_BOOT_CONFIG is not set
|
||||
CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y
|
||||
# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
|
||||
CONFIG_LD_ORPHAN_WARN=y
|
||||
CONFIG_SYSCTL=y
|
||||
CONFIG_SYSCTL_EXCEPTION_TRACE=y
|
||||
CONFIG_BPF=y
|
||||
@@ -201,6 +207,7 @@ CONFIG_KALLSYMS=y
|
||||
CONFIG_KALLSYMS_BASE_RELATIVE=y
|
||||
CONFIG_BPF_SYSCALL=y
|
||||
CONFIG_ARCH_WANT_DEFAULT_BPF_JIT=y
|
||||
# CONFIG_BPF_PRELOAD is not set
|
||||
CONFIG_USERFAULTFD=y
|
||||
CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y
|
||||
CONFIG_RSEQ=y
|
||||
@@ -235,7 +242,8 @@ CONFIG_ARM64=y
|
||||
CONFIG_64BIT=y
|
||||
CONFIG_MMU=y
|
||||
CONFIG_ARM64_PAGE_SHIFT=12
|
||||
CONFIG_ARM64_CONT_SHIFT=4
|
||||
CONFIG_ARM64_CONT_PTE_SHIFT=4
|
||||
CONFIG_ARM64_CONT_PMD_SHIFT=4
|
||||
CONFIG_ARCH_MMAP_RND_BITS_MIN=18
|
||||
CONFIG_ARCH_MMAP_RND_BITS_MAX=33
|
||||
CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11
|
||||
@@ -273,10 +281,12 @@ CONFIG_ARCH_PROC_KCORE_TEXT=y
|
||||
# CONFIG_ARCH_BITMAIN is not set
|
||||
# CONFIG_ARCH_BRCMSTB is not set
|
||||
# CONFIG_ARCH_EXYNOS is not set
|
||||
# CONFIG_ARCH_SPARX5 is not set
|
||||
# CONFIG_ARCH_K3 is not set
|
||||
# CONFIG_ARCH_LAYERSCAPE is not set
|
||||
# CONFIG_ARCH_LG1K is not set
|
||||
# CONFIG_ARCH_HISI is not set
|
||||
# CONFIG_ARCH_KEEMBAY is not set
|
||||
# CONFIG_ARCH_MEDIATEK is not set
|
||||
# CONFIG_ARCH_MESON is not set
|
||||
# CONFIG_ARCH_MVEBU is not set
|
||||
@@ -295,6 +305,7 @@ CONFIG_ARCH_PROC_KCORE_TEXT=y
|
||||
# CONFIG_ARCH_THUNDER2 is not set
|
||||
# CONFIG_ARCH_UNIPHIER is not set
|
||||
# CONFIG_ARCH_VEXPRESS is not set
|
||||
# CONFIG_ARCH_VISCONTI is not set
|
||||
# CONFIG_ARCH_XGENE is not set
|
||||
# CONFIG_ARCH_ZX is not set
|
||||
# CONFIG_ARCH_ZYNQMP is not set
|
||||
@@ -324,6 +335,7 @@ CONFIG_ARM64_WORKAROUND_REPEAT_TLBI=y
|
||||
CONFIG_ARM64_ERRATUM_1286807=y
|
||||
CONFIG_ARM64_ERRATUM_1463225=y
|
||||
CONFIG_ARM64_ERRATUM_1542419=y
|
||||
CONFIG_ARM64_ERRATUM_1508412=y
|
||||
CONFIG_CAVIUM_ERRATUM_22375=y
|
||||
CONFIG_CAVIUM_ERRATUM_23144=y
|
||||
CONFIG_CAVIUM_ERRATUM_23154=y
|
||||
@@ -375,7 +387,6 @@ CONFIG_SYS_SUPPORTS_HUGETLBFS=y
|
||||
CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y
|
||||
CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y
|
||||
CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK=y
|
||||
CONFIG_SECCOMP=y
|
||||
CONFIG_PARAVIRT=y
|
||||
CONFIG_PARAVIRT_TIME_ACCOUNTING=y
|
||||
# CONFIG_KEXEC is not set
|
||||
@@ -385,9 +396,6 @@ CONFIG_KEXEC_FILE=y
|
||||
# CONFIG_XEN is not set
|
||||
CONFIG_FORCE_MAX_ZONEORDER=11
|
||||
CONFIG_UNMAP_KERNEL_AT_EL0=y
|
||||
CONFIG_HARDEN_BRANCH_PREDICTOR=y
|
||||
CONFIG_HARDEN_EL2_VECTORS=y
|
||||
CONFIG_ARM64_SSBD=y
|
||||
CONFIG_RODATA_FULL_DEFAULT_ENABLED=y
|
||||
# CONFIG_ARM64_SW_TTBR0_PAN is not set
|
||||
CONFIG_ARM64_TAGGED_ADDR_ABI=y
|
||||
@@ -416,22 +424,29 @@ CONFIG_ARM64_CNP=y
|
||||
# ARMv8.3 architectural features
|
||||
#
|
||||
CONFIG_ARM64_PTR_AUTH=y
|
||||
CONFIG_CC_HAS_BRANCH_PROT_PAC_RET=y
|
||||
CONFIG_CC_HAS_SIGN_RETURN_ADDRESS=y
|
||||
CONFIG_AS_HAS_PAC=y
|
||||
CONFIG_AS_HAS_CFI_NEGATE_RA_STATE=y
|
||||
# end of ARMv8.3 architectural features
|
||||
|
||||
#
|
||||
# ARMv8.4 architectural features
|
||||
#
|
||||
CONFIG_ARM64_AMU_EXTN=y
|
||||
CONFIG_AS_HAS_ARMV8_4=y
|
||||
CONFIG_ARM64_TLB_RANGE=y
|
||||
# end of ARMv8.4 architectural features
|
||||
|
||||
#
|
||||
# ARMv8.5 architectural features
|
||||
#
|
||||
CONFIG_ARM64_BTI=y
|
||||
CONFIG_CC_HAS_BRANCH_PROT_PAC_RET_BTI=y
|
||||
CONFIG_ARM64_E0PD=y
|
||||
CONFIG_ARCH_RANDOM=y
|
||||
CONFIG_ARM64_AS_HAS_MTE=y
|
||||
CONFIG_ARM64_MTE=y
|
||||
# end of ARMv8.5 architectural features
|
||||
|
||||
CONFIG_ARM64_SVE=y
|
||||
@@ -439,6 +454,8 @@ CONFIG_ARM64_SVE=y
|
||||
CONFIG_RELOCATABLE=y
|
||||
CONFIG_RANDOMIZE_BASE=y
|
||||
CONFIG_RANDOMIZE_MODULE_REGION_FULL=y
|
||||
CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y
|
||||
CONFIG_STACKPROTECTOR_PER_TASK=y
|
||||
# end of Kernel Features
|
||||
|
||||
#
|
||||
@@ -452,6 +469,7 @@ CONFIG_DMI=y
|
||||
# end of Boot options
|
||||
|
||||
CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION=y
|
||||
CONFIG_ARCH_ENABLE_THP_MIGRATION=y
|
||||
|
||||
#
|
||||
# Power management options
|
||||
@@ -537,13 +555,13 @@ CONFIG_DMI_SYSFS=y
|
||||
#
|
||||
# EFI (Extensible Firmware Interface) Support
|
||||
#
|
||||
# CONFIG_EFI_VARS is not set
|
||||
CONFIG_EFI_ESRT=y
|
||||
CONFIG_EFI_PARAMS_FROM_FDT=y
|
||||
CONFIG_EFI_RUNTIME_WRAPPERS=y
|
||||
CONFIG_EFI_GENERIC_STUB=y
|
||||
CONFIG_EFI_ARMSTUB_DTB_LOADER=y
|
||||
CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER=y
|
||||
# CONFIG_EFI_BOOTLOADER_CONTROL is not set
|
||||
# CONFIG_EFI_CAPSULE_LOADER is not set
|
||||
# CONFIG_EFI_TEST is not set
|
||||
# CONFIG_RESET_ATTACK_MITIGATION is not set
|
||||
@@ -551,10 +569,12 @@ CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER=y
|
||||
# end of EFI (Extensible Firmware Interface) Support
|
||||
|
||||
CONFIG_EFI_EARLYCON=y
|
||||
# CONFIG_EFI_CUSTOM_SSDT_OVERLAYS is not set
|
||||
CONFIG_ARM_PSCI_FW=y
|
||||
# CONFIG_ARM_PSCI_CHECKER is not set
|
||||
CONFIG_HAVE_ARM_SMCCC=y
|
||||
CONFIG_HAVE_ARM_SMCCC_DISCOVERY=y
|
||||
# CONFIG_ARM_SMCCC_SOC_ID is not set
|
||||
|
||||
#
|
||||
# Tegra firmware driver
|
||||
@@ -595,11 +615,11 @@ CONFIG_ACPI_NUMA=y
|
||||
# CONFIG_ACPI_HMAT is not set
|
||||
CONFIG_HAVE_ACPI_APEI=y
|
||||
# CONFIG_ACPI_APEI is not set
|
||||
# CONFIG_PMIC_OPREGION is not set
|
||||
# CONFIG_ACPI_CONFIGFS is not set
|
||||
CONFIG_ACPI_IORT=y
|
||||
CONFIG_ACPI_GTDT=y
|
||||
CONFIG_ACPI_PPTT=y
|
||||
# CONFIG_PMIC_OPREGION is not set
|
||||
CONFIG_IRQ_BYPASS_MANAGER=y
|
||||
CONFIG_VIRTUALIZATION=y
|
||||
CONFIG_KVM=y
|
||||
@@ -616,7 +636,6 @@ CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT=y
|
||||
CONFIG_HAVE_KVM_IRQ_BYPASS=y
|
||||
CONFIG_HAVE_KVM_VCPU_RUN_PID_CHANGE=y
|
||||
CONFIG_KVM_ARM_PMU=y
|
||||
CONFIG_KVM_INDIRECT_VECTORS=y
|
||||
# CONFIG_ARM64_CRYPTO is not set
|
||||
|
||||
#
|
||||
@@ -624,6 +643,7 @@ CONFIG_KVM_INDIRECT_VECTORS=y
|
||||
#
|
||||
CONFIG_CRASH_CORE=y
|
||||
CONFIG_KEXEC_CORE=y
|
||||
CONFIG_SET_FS=y
|
||||
CONFIG_JUMP_LABEL=y
|
||||
# CONFIG_STATIC_KEYS_SELFTEST is not set
|
||||
CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
|
||||
@@ -655,16 +675,18 @@ CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y
|
||||
CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y
|
||||
CONFIG_HAVE_CMPXCHG_LOCAL=y
|
||||
CONFIG_HAVE_CMPXCHG_DOUBLE=y
|
||||
CONFIG_HAVE_ARCH_SECCOMP=y
|
||||
CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
|
||||
CONFIG_SECCOMP=y
|
||||
CONFIG_SECCOMP_FILTER=y
|
||||
CONFIG_HAVE_ARCH_STACKLEAK=y
|
||||
CONFIG_HAVE_STACKPROTECTOR=y
|
||||
CONFIG_CC_HAS_STACKPROTECTOR_NONE=y
|
||||
CONFIG_STACKPROTECTOR=y
|
||||
CONFIG_STACKPROTECTOR_STRONG=y
|
||||
CONFIG_HAVE_CONTEXT_TRACKING=y
|
||||
CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
|
||||
CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
|
||||
CONFIG_HAVE_MOVE_PMD=y
|
||||
CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y
|
||||
CONFIG_HAVE_ARCH_HUGE_VMAP=y
|
||||
CONFIG_MODULES_USE_ELF_RELA=y
|
||||
@@ -672,7 +694,6 @@ CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
|
||||
CONFIG_HAVE_ARCH_MMAP_RND_BITS=y
|
||||
CONFIG_ARCH_MMAP_RND_BITS=18
|
||||
CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT=y
|
||||
CONFIG_HAVE_COPY_THREAD_TLS=y
|
||||
CONFIG_CLONE_BACKWARDS=y
|
||||
# CONFIG_COMPAT_32BIT_TIME is not set
|
||||
CONFIG_HAVE_ARCH_VMAP_STACK=y
|
||||
@@ -685,6 +706,7 @@ CONFIG_HAVE_ARCH_PREL32_RELOCATIONS=y
|
||||
CONFIG_ARCH_USE_MEMREMAP_PROT=y
|
||||
# CONFIG_LOCK_EVENT_COUNTS is not set
|
||||
CONFIG_ARCH_HAS_RELR=y
|
||||
CONFIG_ARCH_WANT_LD_ORPHAN_WARN=y
|
||||
|
||||
#
|
||||
# GCOV-based kernel profiling
|
||||
@@ -836,7 +858,6 @@ CONFIG_SELECT_MEMORY_MODEL=y
|
||||
CONFIG_SPARSEMEM_MANUAL=y
|
||||
CONFIG_SPARSEMEM=y
|
||||
CONFIG_NEED_MULTIPLE_NODES=y
|
||||
CONFIG_HAVE_MEMORY_PRESENT=y
|
||||
CONFIG_SPARSEMEM_EXTREME=y
|
||||
CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
|
||||
CONFIG_SPARSEMEM_VMEMMAP=y
|
||||
@@ -885,7 +906,6 @@ CONFIG_ZPOOL=y
|
||||
CONFIG_ZBUD=y
|
||||
# CONFIG_Z3FOLD is not set
|
||||
CONFIG_ZSMALLOC=y
|
||||
# CONFIG_ZSMALLOC_PGTABLE_MAPPING is not set
|
||||
CONFIG_ZSMALLOC_STAT=y
|
||||
CONFIG_GENERIC_EARLY_IOREMAP=y
|
||||
# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set
|
||||
@@ -894,6 +914,7 @@ CONFIG_ARCH_HAS_PTE_DEVMAP=y
|
||||
CONFIG_ZONE_DEVICE=y
|
||||
CONFIG_DEV_PAGEMAP_OPS=y
|
||||
# CONFIG_DEVICE_PRIVATE is not set
|
||||
CONFIG_ARCH_USES_HIGH_VMA_FLAGS=y
|
||||
CONFIG_PERCPU_STATS=y
|
||||
# CONFIG_GUP_BENCHMARK is not set
|
||||
# CONFIG_READ_ONLY_THP_FOR_FS is not set
|
||||
@@ -1062,6 +1083,11 @@ CONFIG_PCI_ECAM=y
|
||||
# CONFIG_PCI_PASID is not set
|
||||
# CONFIG_PCI_P2PDMA is not set
|
||||
CONFIG_PCI_LABEL=y
|
||||
# CONFIG_PCIE_BUS_TUNE_OFF is not set
|
||||
CONFIG_PCIE_BUS_DEFAULT=y
|
||||
# CONFIG_PCIE_BUS_SAFE is not set
|
||||
# CONFIG_PCIE_BUS_PERFORMANCE is not set
|
||||
# CONFIG_PCIE_BUS_PEER2PEER is not set
|
||||
CONFIG_HOTPLUG_PCI=y
|
||||
CONFIG_HOTPLUG_PCI_ACPI=y
|
||||
# CONFIG_HOTPLUG_PCI_ACPI_IBM is not set
|
||||
@@ -1100,6 +1126,7 @@ CONFIG_PCI_HOST_GENERIC=y
|
||||
# Cadence PCIe controllers support
|
||||
#
|
||||
# CONFIG_PCIE_CADENCE_PLAT_HOST is not set
|
||||
# CONFIG_PCI_J721E_HOST is not set
|
||||
# end of Cadence PCIe controllers support
|
||||
# end of PCI controller drivers
|
||||
|
||||
@@ -1243,13 +1270,6 @@ CONFIG_VIRTIO_BLK=y
|
||||
#
|
||||
# Altera FPGA firmware download module (requires I2C)
|
||||
#
|
||||
|
||||
#
|
||||
# Intel MIC & related support
|
||||
#
|
||||
# CONFIG_VOP_BUS is not set
|
||||
# end of Intel MIC & related support
|
||||
|
||||
# CONFIG_GENWQE is not set
|
||||
# CONFIG_ECHO is not set
|
||||
# CONFIG_MISC_ALCOR_PCI is not set
|
||||
@@ -1310,8 +1330,14 @@ CONFIG_VIRTIO_NET=y
|
||||
# CONFIG_FDDI is not set
|
||||
# CONFIG_HIPPI is not set
|
||||
# CONFIG_NET_SB1000 is not set
|
||||
# CONFIG_MDIO_DEVICE is not set
|
||||
# CONFIG_PHYLIB is not set
|
||||
# CONFIG_MDIO_DEVICE is not set
|
||||
|
||||
#
|
||||
# PCS device drivers
|
||||
#
|
||||
# end of PCS device drivers
|
||||
|
||||
# CONFIG_PPP is not set
|
||||
# CONFIG_SLIP is not set
|
||||
|
||||
@@ -1459,10 +1485,12 @@ CONFIG_VIRTIO_CONSOLE=y
|
||||
# CONFIG_IPMI_HANDLER is not set
|
||||
CONFIG_HW_RANDOM=y
|
||||
# CONFIG_HW_RANDOM_TIMERIOMEM is not set
|
||||
# CONFIG_HW_RANDOM_BA431 is not set
|
||||
CONFIG_HW_RANDOM_VIRTIO=y
|
||||
CONFIG_HW_RANDOM_HISI_V2=y
|
||||
CONFIG_HW_RANDOM_CAVIUM=y
|
||||
# CONFIG_HW_RANDOM_CCTRNG is not set
|
||||
# CONFIG_HW_RANDOM_XIPHERA is not set
|
||||
# CONFIG_APPLICOM is not set
|
||||
CONFIG_DEVMEM=y
|
||||
CONFIG_RAW_DRIVER=y
|
||||
@@ -1512,7 +1540,6 @@ CONFIG_PTP_1588_CLOCK=y
|
||||
# CONFIG_PINCTRL is not set
|
||||
# CONFIG_GPIOLIB is not set
|
||||
# CONFIG_W1 is not set
|
||||
# CONFIG_POWER_AVS is not set
|
||||
CONFIG_POWER_RESET=y
|
||||
# CONFIG_POWER_RESET_RESTART is not set
|
||||
# CONFIG_POWER_RESET_XGENE is not set
|
||||
@@ -1529,6 +1556,7 @@ CONFIG_POWER_SUPPLY=y
|
||||
# CONFIG_CHARGER_MAX8903 is not set
|
||||
# CONFIG_HWMON is not set
|
||||
CONFIG_THERMAL=y
|
||||
# CONFIG_THERMAL_NETLINK is not set
|
||||
# CONFIG_THERMAL_STATISTICS is not set
|
||||
CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0
|
||||
CONFIG_THERMAL_OF=y
|
||||
@@ -1621,8 +1649,6 @@ CONFIG_VGA_ARB_MAX_GPUS=16
|
||||
#
|
||||
# end of ARM devices
|
||||
|
||||
CONFIG_DRM_RCAR_WRITEBACK=y
|
||||
|
||||
#
|
||||
# Frame buffer Devices
|
||||
#
|
||||
@@ -1680,7 +1706,6 @@ CONFIG_FB=y
|
||||
CONFIG_LCD_CLASS_DEVICE=y
|
||||
# CONFIG_LCD_PLATFORM is not set
|
||||
CONFIG_BACKLIGHT_CLASS_DEVICE=y
|
||||
CONFIG_BACKLIGHT_GENERIC=y
|
||||
# CONFIG_BACKLIGHT_QCOM_WLED is not set
|
||||
# end of Backlight & LCD device support
|
||||
|
||||
@@ -1731,6 +1756,7 @@ CONFIG_HID_GENERIC=y
|
||||
# CONFIG_HID_GEMBIRD is not set
|
||||
# CONFIG_HID_GFRM is not set
|
||||
# CONFIG_HID_GLORIOUS is not set
|
||||
# CONFIG_HID_VIVALDI is not set
|
||||
# CONFIG_HID_KEYTOUCH is not set
|
||||
# CONFIG_HID_KYE is not set
|
||||
# CONFIG_HID_WALTOP is not set
|
||||
@@ -1870,6 +1896,7 @@ CONFIG_DMA_OF=y
|
||||
# CONFIG_PLX_DMA is not set
|
||||
# CONFIG_XILINX_DMA is not set
|
||||
# CONFIG_XILINX_ZYNQMP_DMA is not set
|
||||
# CONFIG_XILINX_ZYNQMP_DPDMA is not set
|
||||
# CONFIG_QCOM_HIDMA_MGMT is not set
|
||||
# CONFIG_QCOM_HIDMA is not set
|
||||
# CONFIG_DW_DMAC is not set
|
||||
@@ -1936,14 +1963,12 @@ CONFIG_VHOST_MENU=y
|
||||
# CONFIG_GREYBUS is not set
|
||||
# CONFIG_STAGING is not set
|
||||
# CONFIG_GOLDFISH is not set
|
||||
# CONFIG_MFD_CROS_EC is not set
|
||||
# CONFIG_CHROME_PLATFORMS is not set
|
||||
# CONFIG_MELLANOX_PLATFORM is not set
|
||||
CONFIG_HAVE_CLK=y
|
||||
CONFIG_CLKDEV_LOOKUP=y
|
||||
CONFIG_HAVE_CLK_PREPARE=y
|
||||
CONFIG_COMMON_CLK=y
|
||||
# CONFIG_CLK_HSDK is not set
|
||||
# CONFIG_CLK_QORIQ is not set
|
||||
# CONFIG_COMMON_CLK_XGENE is not set
|
||||
# CONFIG_COMMON_CLK_FIXED_MMIO is not set
|
||||
@@ -2102,6 +2127,7 @@ CONFIG_PARTITION_PERCPU=y
|
||||
#
|
||||
# CONFIG_ARM_CCI_PMU is not set
|
||||
# CONFIG_ARM_CCN is not set
|
||||
# CONFIG_ARM_CMN is not set
|
||||
CONFIG_ARM_PMU=y
|
||||
CONFIG_ARM_PMU_ACPI=y
|
||||
# CONFIG_ARM_DSU_PMU is not set
|
||||
@@ -2197,6 +2223,7 @@ CONFIG_AUTOFS_FS=y
|
||||
CONFIG_FUSE_FS=y
|
||||
CONFIG_CUSE=y
|
||||
CONFIG_VIRTIO_FS=y
|
||||
CONFIG_FUSE_DAX=y
|
||||
CONFIG_OVERLAY_FS=y
|
||||
# CONFIG_OVERLAY_FS_REDIRECT_DIR is not set
|
||||
CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW=y
|
||||
@@ -2250,6 +2277,7 @@ CONFIG_SYSFS=y
|
||||
CONFIG_TMPFS=y
|
||||
CONFIG_TMPFS_POSIX_ACL=y
|
||||
CONFIG_TMPFS_XATTR=y
|
||||
# CONFIG_TMPFS_INODE64 is not set
|
||||
CONFIG_HUGETLBFS=y
|
||||
CONFIG_HUGETLB_PAGE=y
|
||||
CONFIG_MEMFD_CREATE=y
|
||||
@@ -2423,6 +2451,7 @@ CONFIG_CRYPTO_CRYPTD=y
|
||||
# CONFIG_CRYPTO_DH is not set
|
||||
# CONFIG_CRYPTO_ECDH is not set
|
||||
# CONFIG_CRYPTO_ECRDSA is not set
|
||||
# CONFIG_CRYPTO_SM2 is not set
|
||||
# CONFIG_CRYPTO_CURVE25519 is not set
|
||||
|
||||
#
|
||||
@@ -2491,21 +2520,16 @@ CONFIG_CRYPTO_SHA512=y
|
||||
#
|
||||
CONFIG_CRYPTO_AES=y
|
||||
# CONFIG_CRYPTO_AES_TI is not set
|
||||
# CONFIG_CRYPTO_ANUBIS is not set
|
||||
CONFIG_CRYPTO_ARC4=y
|
||||
# CONFIG_CRYPTO_BLOWFISH is not set
|
||||
# CONFIG_CRYPTO_CAMELLIA is not set
|
||||
# CONFIG_CRYPTO_CAST5 is not set
|
||||
# CONFIG_CRYPTO_CAST6 is not set
|
||||
CONFIG_CRYPTO_DES=y
|
||||
# CONFIG_CRYPTO_FCRYPT is not set
|
||||
# CONFIG_CRYPTO_KHAZAD is not set
|
||||
# CONFIG_CRYPTO_SALSA20 is not set
|
||||
# CONFIG_CRYPTO_CHACHA20 is not set
|
||||
# CONFIG_CRYPTO_SEED is not set
|
||||
# CONFIG_CRYPTO_SERPENT is not set
|
||||
# CONFIG_CRYPTO_SM4 is not set
|
||||
# CONFIG_CRYPTO_TEA is not set
|
||||
# CONFIG_CRYPTO_TWOFISH is not set
|
||||
|
||||
#
|
||||
@@ -2532,13 +2556,14 @@ CONFIG_CRYPTO_USER_API=y
|
||||
# CONFIG_CRYPTO_USER_API_HASH is not set
|
||||
# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
|
||||
CONFIG_CRYPTO_USER_API_RNG=y
|
||||
# CONFIG_CRYPTO_USER_API_RNG_CAVP is not set
|
||||
# CONFIG_CRYPTO_USER_API_AEAD is not set
|
||||
# CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE is not set
|
||||
|
||||
#
|
||||
# Crypto library routines
|
||||
#
|
||||
CONFIG_CRYPTO_LIB_AES=y
|
||||
CONFIG_CRYPTO_LIB_ARC4=y
|
||||
# CONFIG_CRYPTO_LIB_BLAKE2S is not set
|
||||
# CONFIG_CRYPTO_LIB_CHACHA is not set
|
||||
# CONFIG_CRYPTO_LIB_CURVE25519 is not set
|
||||
@@ -2621,6 +2646,7 @@ CONFIG_ASSOCIATIVE_ARRAY=y
|
||||
CONFIG_HAS_IOMEM=y
|
||||
CONFIG_HAS_IOPORT_MAP=y
|
||||
CONFIG_HAS_DMA=y
|
||||
CONFIG_DMA_OPS=y
|
||||
CONFIG_NEED_SG_DMA_LENGTH=y
|
||||
CONFIG_NEED_DMA_MAP_STATE=y
|
||||
CONFIG_ARCH_DMA_ADDR_T_64BIT=y
|
||||
@@ -2645,11 +2671,13 @@ CONFIG_LIBFDT=y
|
||||
CONFIG_UCS2_STRING=y
|
||||
CONFIG_HAVE_GENERIC_VDSO=y
|
||||
CONFIG_GENERIC_GETTIMEOFDAY=y
|
||||
CONFIG_GENERIC_VDSO_TIME_NS=y
|
||||
CONFIG_FONT_SUPPORT=y
|
||||
# CONFIG_FONTS is not set
|
||||
CONFIG_FONT_8x8=y
|
||||
CONFIG_FONT_8x16=y
|
||||
CONFIG_MEMREGION=y
|
||||
CONFIG_ARCH_STACKWALK=y
|
||||
CONFIG_SBITMAP=y
|
||||
# CONFIG_STRING_SELFTEST is not set
|
||||
# end of Library routines
|
||||
@@ -2684,6 +2712,7 @@ CONFIG_STRIP_ASM_SYMS=y
|
||||
# CONFIG_HEADERS_INSTALL is not set
|
||||
CONFIG_DEBUG_SECTION_MISMATCH=y
|
||||
CONFIG_SECTION_MISMATCH_WARN_ONLY=y
|
||||
# CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_32B is not set
|
||||
CONFIG_ARCH_WANT_FRAME_POINTERS=y
|
||||
CONFIG_FRAME_POINTER=y
|
||||
# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
|
||||
@@ -2697,6 +2726,9 @@ CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1
|
||||
CONFIG_MAGIC_SYSRQ_SERIAL=y
|
||||
CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE=""
|
||||
CONFIG_DEBUG_FS=y
|
||||
CONFIG_DEBUG_FS_ALLOW_ALL=y
|
||||
# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set
|
||||
# CONFIG_DEBUG_FS_ALLOW_NONE is not set
|
||||
CONFIG_HAVE_ARCH_KGDB=y
|
||||
# CONFIG_KGDB is not set
|
||||
CONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y
|
||||
@@ -2735,7 +2767,8 @@ CONFIG_DEBUG_MEMORY_INIT=y
|
||||
CONFIG_HAVE_ARCH_KASAN=y
|
||||
CONFIG_HAVE_ARCH_KASAN_SW_TAGS=y
|
||||
CONFIG_CC_HAS_KASAN_GENERIC=y
|
||||
CONFIG_KASAN_STACK=1
|
||||
CONFIG_CC_HAS_WORKING_NOSANITIZE_ADDRESS=y
|
||||
# CONFIG_KASAN is not set
|
||||
# end of Memory Debugging
|
||||
|
||||
# CONFIG_DEBUG_SHIRQ is not set
|
||||
@@ -2749,7 +2782,6 @@ CONFIG_PANIC_TIMEOUT=0
|
||||
# CONFIG_SOFTLOCKUP_DETECTOR is not set
|
||||
# CONFIG_DETECT_HUNG_TASK is not set
|
||||
# CONFIG_WQ_WATCHDOG is not set
|
||||
# CONFIG_TEST_LOCKUP is not set
|
||||
# end of Debug Oops, Lockups and Hangs
|
||||
|
||||
#
|
||||
@@ -2778,6 +2810,8 @@ CONFIG_LOCK_DEBUGGING_SUPPORT=y
|
||||
# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
|
||||
# CONFIG_LOCK_TORTURE_TEST is not set
|
||||
# CONFIG_WW_MUTEX_SELFTEST is not set
|
||||
# CONFIG_SCF_TORTURE_TEST is not set
|
||||
# CONFIG_CSD_LOCK_WAIT_DEBUG is not set
|
||||
# end of Lock Debugging (spinlocks, mutexes, etc...)
|
||||
|
||||
CONFIG_STACKTRACE=y
|
||||
@@ -2800,8 +2834,9 @@ CONFIG_BUG_ON_DATA_CORRUPTION=y
|
||||
#
|
||||
# RCU Debugging
|
||||
#
|
||||
# CONFIG_RCU_PERF_TEST is not set
|
||||
# CONFIG_RCU_SCALE_TEST is not set
|
||||
# CONFIG_RCU_TORTURE_TEST is not set
|
||||
# CONFIG_RCU_REF_SCALE_TEST is not set
|
||||
CONFIG_RCU_CPU_STALL_TIMEOUT=59
|
||||
# CONFIG_RCU_TRACE is not set
|
||||
# CONFIG_RCU_EQS_DEBUG is not set
|
||||
@@ -2814,6 +2849,7 @@ CONFIG_RCU_CPU_STALL_TIMEOUT=59
|
||||
CONFIG_HAVE_FUNCTION_TRACER=y
|
||||
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
|
||||
CONFIG_HAVE_DYNAMIC_FTRACE=y
|
||||
CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y
|
||||
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
|
||||
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
|
||||
CONFIG_HAVE_C_RECORDMCOUNT=y
|
||||
@@ -2856,7 +2892,6 @@ CONFIG_RUNTIME_TESTING_MENU=y
|
||||
# CONFIG_TEST_KSTRTOX is not set
|
||||
# CONFIG_TEST_PRINTF is not set
|
||||
# CONFIG_TEST_BITMAP is not set
|
||||
# CONFIG_TEST_BITFIELD is not set
|
||||
# CONFIG_TEST_UUID is not set
|
||||
# CONFIG_TEST_XARRAY is not set
|
||||
# CONFIG_TEST_OVERFLOW is not set
|
||||
@@ -2870,6 +2905,7 @@ CONFIG_RUNTIME_TESTING_MENU=y
|
||||
# CONFIG_TEST_MEMCAT_P is not set
|
||||
# CONFIG_TEST_STACKINIT is not set
|
||||
# CONFIG_TEST_MEMINIT is not set
|
||||
# CONFIG_TEST_FREE_PAGES is not set
|
||||
# CONFIG_MEMTEST is not set
|
||||
# end of Kernel Testing and Coverage
|
||||
# end of Kernel hacking
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#
|
||||
# Automatically generated file; DO NOT EDIT.
|
||||
# Linux/x86 5.8.0-rc4 Kernel Configuration
|
||||
# Linux/x86 5.10.0 Kernel Configuration
|
||||
#
|
||||
CONFIG_CC_VERSION_TEXT="gcc (Ubuntu 9.3.0-10ubuntu2) 9.3.0"
|
||||
CONFIG_CC_VERSION_TEXT="gcc (GCC) 10.2.1 20201125 (Red Hat 10.2.1-9)"
|
||||
CONFIG_CC_IS_GCC=y
|
||||
CONFIG_GCC_VERSION=90300
|
||||
CONFIG_GCC_VERSION=100201
|
||||
CONFIG_LD_VERSION=234000000
|
||||
CONFIG_CLANG_VERSION=0
|
||||
CONFIG_LLD_VERSION=0
|
||||
CONFIG_CC_CAN_LINK=y
|
||||
CONFIG_CC_CAN_LINK_STATIC=y
|
||||
CONFIG_CC_HAS_ASM_GOTO=y
|
||||
@@ -29,12 +30,14 @@ CONFIG_HAVE_KERNEL_LZMA=y
|
||||
CONFIG_HAVE_KERNEL_XZ=y
|
||||
CONFIG_HAVE_KERNEL_LZO=y
|
||||
CONFIG_HAVE_KERNEL_LZ4=y
|
||||
CONFIG_HAVE_KERNEL_ZSTD=y
|
||||
CONFIG_KERNEL_GZIP=y
|
||||
# CONFIG_KERNEL_BZIP2 is not set
|
||||
# CONFIG_KERNEL_LZMA is not set
|
||||
# CONFIG_KERNEL_XZ is not set
|
||||
# CONFIG_KERNEL_LZO is not set
|
||||
# CONFIG_KERNEL_LZ4 is not set
|
||||
# CONFIG_KERNEL_ZSTD is not set
|
||||
CONFIG_DEFAULT_INIT=""
|
||||
CONFIG_DEFAULT_HOSTNAME="cloud-hypervisor"
|
||||
CONFIG_SWAP=y
|
||||
@@ -78,6 +81,8 @@ CONFIG_GENERIC_CLOCKEVENTS=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST=y
|
||||
CONFIG_GENERIC_CMOS_UPDATE=y
|
||||
CONFIG_HAVE_POSIX_CPU_TIMERS_TASK_WORK=y
|
||||
CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y
|
||||
|
||||
#
|
||||
# Timers subsystem
|
||||
@@ -102,7 +107,6 @@ CONFIG_TICK_CPU_ACCOUNTING=y
|
||||
# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set
|
||||
# CONFIG_IRQ_TIME_ACCOUNTING is not set
|
||||
CONFIG_HAVE_SCHED_AVG_IRQ=y
|
||||
# CONFIG_SCHED_THERMAL_PRESSURE is not set
|
||||
CONFIG_BSD_PROCESS_ACCT=y
|
||||
CONFIG_BSD_PROCESS_ACCT_V3=y
|
||||
CONFIG_TASKSTATS=y
|
||||
@@ -121,6 +125,8 @@ CONFIG_TREE_RCU=y
|
||||
# CONFIG_RCU_EXPERT is not set
|
||||
CONFIG_SRCU=y
|
||||
CONFIG_TREE_SRCU=y
|
||||
CONFIG_TASKS_RCU_GENERIC=y
|
||||
CONFIG_TASKS_TRACE_RCU=y
|
||||
CONFIG_RCU_STALL_COMMON=y
|
||||
CONFIG_RCU_NEED_SEGCBLIST=y
|
||||
# end of RCU Subsystem
|
||||
@@ -187,9 +193,11 @@ CONFIG_RD_LZMA=y
|
||||
CONFIG_RD_XZ=y
|
||||
CONFIG_RD_LZO=y
|
||||
CONFIG_RD_LZ4=y
|
||||
CONFIG_RD_ZSTD=y
|
||||
# CONFIG_BOOT_CONFIG is not set
|
||||
CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y
|
||||
# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
|
||||
CONFIG_LD_ORPHAN_WARN=y
|
||||
CONFIG_SYSCTL=y
|
||||
CONFIG_SYSCTL_EXCEPTION_TRACE=y
|
||||
CONFIG_HAVE_PCSPKR_PLATFORM=y
|
||||
@@ -224,6 +232,7 @@ CONFIG_KALLSYMS_ABSOLUTE_PERCPU=y
|
||||
CONFIG_KALLSYMS_BASE_RELATIVE=y
|
||||
CONFIG_BPF_SYSCALL=y
|
||||
CONFIG_ARCH_WANT_DEFAULT_BPF_JIT=y
|
||||
# CONFIG_BPF_PRELOAD is not set
|
||||
CONFIG_USERFAULTFD=y
|
||||
CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y
|
||||
CONFIG_RSEQ=y
|
||||
@@ -308,6 +317,7 @@ CONFIG_HYPERVISOR_GUEST=y
|
||||
CONFIG_PARAVIRT=y
|
||||
# CONFIG_PARAVIRT_DEBUG is not set
|
||||
CONFIG_PARAVIRT_SPINLOCKS=y
|
||||
CONFIG_X86_HV_CALLBACK_VECTOR=y
|
||||
# CONFIG_XEN is not set
|
||||
CONFIG_KVM_GUEST=y
|
||||
CONFIG_ARCH_CPUIDLE_HALTPOLL=y
|
||||
@@ -405,7 +415,6 @@ CONFIG_X86_INTEL_TSX_MODE_OFF=y
|
||||
CONFIG_EFI=y
|
||||
CONFIG_EFI_STUB=y
|
||||
# CONFIG_EFI_MIXED is not set
|
||||
CONFIG_SECCOMP=y
|
||||
# CONFIG_HZ_100 is not set
|
||||
CONFIG_HZ_250=y
|
||||
# CONFIG_HZ_300 is not set
|
||||
@@ -471,13 +480,11 @@ CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT=y
|
||||
CONFIG_ACPI_SPCR_TABLE=y
|
||||
CONFIG_ACPI_LPIT=y
|
||||
CONFIG_ACPI_SLEEP=y
|
||||
# CONFIG_ACPI_PROCFS_POWER is not set
|
||||
CONFIG_ACPI_REV_OVERRIDE_POSSIBLE=y
|
||||
# CONFIG_ACPI_EC_DEBUGFS is not set
|
||||
# CONFIG_ACPI_AC is not set
|
||||
# CONFIG_ACPI_BATTERY is not set
|
||||
# CONFIG_ACPI_BUTTON is not set
|
||||
# CONFIG_ACPI_TINY_POWER_BUTTON is not set
|
||||
CONFIG_ACPI_BUTTON=y
|
||||
# CONFIG_ACPI_VIDEO is not set
|
||||
# CONFIG_ACPI_FAN is not set
|
||||
# CONFIG_ACPI_TAD is not set
|
||||
@@ -508,9 +515,9 @@ CONFIG_ACPI_NUMA=y
|
||||
CONFIG_HAVE_ACPI_APEI=y
|
||||
CONFIG_HAVE_ACPI_APEI_NMI=y
|
||||
# CONFIG_ACPI_APEI is not set
|
||||
# CONFIG_DPTF_POWER is not set
|
||||
# CONFIG_PMIC_OPREGION is not set
|
||||
# CONFIG_ACPI_DPTF is not set
|
||||
# CONFIG_ACPI_CONFIGFS is not set
|
||||
# CONFIG_PMIC_OPREGION is not set
|
||||
CONFIG_X86_PM_TIMER=y
|
||||
# CONFIG_SFI is not set
|
||||
|
||||
@@ -523,8 +530,6 @@ CONFIG_CPU_FREQ_GOV_ATTR_SET=y
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set
|
||||
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
|
||||
# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
|
||||
@@ -601,6 +606,7 @@ CONFIG_EFI_RUNTIME_MAP=y
|
||||
# CONFIG_EFI_FAKE_MEMMAP is not set
|
||||
CONFIG_EFI_RUNTIME_WRAPPERS=y
|
||||
CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER=y
|
||||
# CONFIG_EFI_BOOTLOADER_CONTROL is not set
|
||||
# CONFIG_EFI_CAPSULE_LOADER is not set
|
||||
# CONFIG_EFI_TEST is not set
|
||||
# CONFIG_APPLE_PROPERTIES is not set
|
||||
@@ -610,6 +616,7 @@ CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER=y
|
||||
# end of EFI (Extensible Firmware Interface) Support
|
||||
|
||||
CONFIG_EFI_EARLYCON=y
|
||||
# CONFIG_EFI_CUSTOM_SSDT_OVERLAYS is not set
|
||||
|
||||
#
|
||||
# Tegra firmware driver
|
||||
@@ -630,6 +637,7 @@ CONFIG_KVM_VFIO=y
|
||||
CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT=y
|
||||
CONFIG_HAVE_KVM_IRQ_BYPASS=y
|
||||
CONFIG_HAVE_KVM_NO_POLL=y
|
||||
CONFIG_KVM_XFER_TO_GUEST_WORK=y
|
||||
CONFIG_VIRTUALIZATION=y
|
||||
CONFIG_KVM=y
|
||||
# CONFIG_KVM_WERROR is not set
|
||||
@@ -646,10 +654,12 @@ CONFIG_AS_TPAUSE=y
|
||||
CONFIG_CRASH_CORE=y
|
||||
CONFIG_KEXEC_CORE=y
|
||||
CONFIG_HOTPLUG_SMT=y
|
||||
CONFIG_GENERIC_ENTRY=y
|
||||
CONFIG_HAVE_OPROFILE=y
|
||||
CONFIG_OPROFILE_NMI_TIMER=y
|
||||
CONFIG_JUMP_LABEL=y
|
||||
# CONFIG_STATIC_KEYS_SELFTEST is not set
|
||||
# CONFIG_STATIC_CALL_SELFTEST is not set
|
||||
CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
|
||||
CONFIG_ARCH_USE_BUILTIN_BSWAP=y
|
||||
CONFIG_USER_RETURN_NOTIFIER=y
|
||||
@@ -687,11 +697,12 @@ CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y
|
||||
CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y
|
||||
CONFIG_HAVE_CMPXCHG_LOCAL=y
|
||||
CONFIG_HAVE_CMPXCHG_DOUBLE=y
|
||||
CONFIG_HAVE_ARCH_SECCOMP=y
|
||||
CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
|
||||
CONFIG_SECCOMP=y
|
||||
CONFIG_SECCOMP_FILTER=y
|
||||
CONFIG_HAVE_ARCH_STACKLEAK=y
|
||||
CONFIG_HAVE_STACKPROTECTOR=y
|
||||
CONFIG_CC_HAS_STACKPROTECTOR_NONE=y
|
||||
CONFIG_STACKPROTECTOR=y
|
||||
CONFIG_STACKPROTECTOR_STRONG=y
|
||||
CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES=y
|
||||
@@ -710,7 +721,6 @@ CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
|
||||
CONFIG_HAVE_ARCH_MMAP_RND_BITS=y
|
||||
CONFIG_HAVE_EXIT_THREAD=y
|
||||
CONFIG_ARCH_MMAP_RND_BITS=28
|
||||
CONFIG_HAVE_COPY_THREAD_TLS=y
|
||||
CONFIG_HAVE_STACK_VALIDATION=y
|
||||
CONFIG_HAVE_RELIABLE_STACKTRACE=y
|
||||
# CONFIG_COMPAT_32BIT_TIME is not set
|
||||
@@ -723,6 +733,9 @@ CONFIG_HAVE_ARCH_PREL32_RELOCATIONS=y
|
||||
CONFIG_ARCH_USE_MEMREMAP_PROT=y
|
||||
# CONFIG_LOCK_EVENT_COUNTS is not set
|
||||
CONFIG_ARCH_HAS_MEM_ENCRYPT=y
|
||||
CONFIG_HAVE_STATIC_CALL=y
|
||||
CONFIG_HAVE_STATIC_CALL_INLINE=y
|
||||
CONFIG_ARCH_WANT_LD_ORPHAN_WARN=y
|
||||
|
||||
#
|
||||
# GCOV-based kernel profiling
|
||||
@@ -826,7 +839,6 @@ CONFIG_SELECT_MEMORY_MODEL=y
|
||||
CONFIG_SPARSEMEM_MANUAL=y
|
||||
CONFIG_SPARSEMEM=y
|
||||
CONFIG_NEED_MULTIPLE_NODES=y
|
||||
CONFIG_HAVE_MEMORY_PRESENT=y
|
||||
CONFIG_SPARSEMEM_EXTREME=y
|
||||
CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
|
||||
CONFIG_SPARSEMEM_VMEMMAP=y
|
||||
@@ -876,7 +888,6 @@ CONFIG_ZPOOL=y
|
||||
CONFIG_ZBUD=y
|
||||
# CONFIG_Z3FOLD is not set
|
||||
CONFIG_ZSMALLOC=y
|
||||
# CONFIG_ZSMALLOC_PGTABLE_MAPPING is not set
|
||||
CONFIG_ZSMALLOC_STAT=y
|
||||
CONFIG_GENERIC_EARLY_IOREMAP=y
|
||||
# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set
|
||||
@@ -1052,6 +1063,11 @@ CONFIG_PCI_LOCKLESS_CONFIG=y
|
||||
# CONFIG_PCI_PASID is not set
|
||||
# CONFIG_PCI_P2PDMA is not set
|
||||
CONFIG_PCI_LABEL=y
|
||||
# CONFIG_PCIE_BUS_TUNE_OFF is not set
|
||||
CONFIG_PCIE_BUS_DEFAULT=y
|
||||
# CONFIG_PCIE_BUS_SAFE is not set
|
||||
# CONFIG_PCIE_BUS_PERFORMANCE is not set
|
||||
# CONFIG_PCIE_BUS_PEER2PEER is not set
|
||||
CONFIG_HOTPLUG_PCI=y
|
||||
CONFIG_HOTPLUG_PCI_ACPI=y
|
||||
# CONFIG_HOTPLUG_PCI_ACPI_IBM is not set
|
||||
@@ -1212,15 +1228,6 @@ CONFIG_VIRTIO_BLK=y
|
||||
# CONFIG_INTEL_MEI_ME is not set
|
||||
# CONFIG_INTEL_MEI_TXE is not set
|
||||
# CONFIG_VMWARE_VMCI is not set
|
||||
|
||||
#
|
||||
# Intel MIC & related support
|
||||
#
|
||||
# CONFIG_INTEL_MIC_BUS is not set
|
||||
# CONFIG_SCIF_BUS is not set
|
||||
# CONFIG_VOP_BUS is not set
|
||||
# end of Intel MIC & related support
|
||||
|
||||
# CONFIG_GENWQE is not set
|
||||
# CONFIG_ECHO is not set
|
||||
# CONFIG_MISC_ALCOR_PCI is not set
|
||||
@@ -1284,8 +1291,14 @@ CONFIG_VIRTIO_NET=y
|
||||
# CONFIG_FDDI is not set
|
||||
# CONFIG_HIPPI is not set
|
||||
# CONFIG_NET_SB1000 is not set
|
||||
# CONFIG_MDIO_DEVICE is not set
|
||||
# CONFIG_PHYLIB is not set
|
||||
# CONFIG_MDIO_DEVICE is not set
|
||||
|
||||
#
|
||||
# PCS device drivers
|
||||
#
|
||||
# end of PCS device drivers
|
||||
|
||||
# CONFIG_PPP is not set
|
||||
# CONFIG_SLIP is not set
|
||||
|
||||
@@ -1431,8 +1444,10 @@ CONFIG_HW_RANDOM=y
|
||||
# CONFIG_HW_RANDOM_TIMERIOMEM is not set
|
||||
CONFIG_HW_RANDOM_INTEL=y
|
||||
CONFIG_HW_RANDOM_AMD=y
|
||||
# CONFIG_HW_RANDOM_BA431 is not set
|
||||
CONFIG_HW_RANDOM_VIA=y
|
||||
CONFIG_HW_RANDOM_VIRTIO=y
|
||||
# CONFIG_HW_RANDOM_XIPHERA is not set
|
||||
# CONFIG_APPLICOM is not set
|
||||
# CONFIG_MWAVE is not set
|
||||
CONFIG_DEVMEM=y
|
||||
@@ -1490,11 +1505,11 @@ CONFIG_PTP_1588_CLOCK_KVM=y
|
||||
# CONFIG_PINCTRL is not set
|
||||
# CONFIG_GPIOLIB is not set
|
||||
# CONFIG_W1 is not set
|
||||
# CONFIG_POWER_AVS is not set
|
||||
# CONFIG_POWER_RESET is not set
|
||||
# CONFIG_POWER_SUPPLY is not set
|
||||
# CONFIG_HWMON is not set
|
||||
CONFIG_THERMAL=y
|
||||
# CONFIG_THERMAL_NETLINK is not set
|
||||
# CONFIG_THERMAL_STATISTICS is not set
|
||||
CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0
|
||||
CONFIG_THERMAL_WRITABLE_TRIPS=y
|
||||
@@ -1693,7 +1708,6 @@ CONFIG_FB=y
|
||||
CONFIG_LCD_CLASS_DEVICE=y
|
||||
# CONFIG_LCD_PLATFORM is not set
|
||||
CONFIG_BACKLIGHT_CLASS_DEVICE=y
|
||||
CONFIG_BACKLIGHT_GENERIC=y
|
||||
# CONFIG_BACKLIGHT_APPLE is not set
|
||||
# CONFIG_BACKLIGHT_QCOM_WLED is not set
|
||||
# CONFIG_BACKLIGHT_SAHARA is not set
|
||||
@@ -1703,7 +1717,6 @@ CONFIG_BACKLIGHT_GENERIC=y
|
||||
# Console display driver support
|
||||
#
|
||||
CONFIG_VGA_CONSOLE=y
|
||||
# CONFIG_VGACON_SOFT_SCROLLBACK is not set
|
||||
CONFIG_DUMMY_CONSOLE=y
|
||||
CONFIG_DUMMY_CONSOLE_COLUMNS=80
|
||||
CONFIG_DUMMY_CONSOLE_ROWS=25
|
||||
@@ -1748,6 +1761,7 @@ CONFIG_HID_GENERIC=y
|
||||
# CONFIG_HID_GEMBIRD is not set
|
||||
# CONFIG_HID_GFRM is not set
|
||||
# CONFIG_HID_GLORIOUS is not set
|
||||
# CONFIG_HID_VIVALDI is not set
|
||||
# CONFIG_HID_KEYTOUCH is not set
|
||||
# CONFIG_HID_KYE is not set
|
||||
# CONFIG_HID_WALTOP is not set
|
||||
@@ -1828,6 +1842,7 @@ CONFIG_DMA_ACPI=y
|
||||
# CONFIG_INTEL_IDXD is not set
|
||||
# CONFIG_INTEL_IOATDMA is not set
|
||||
# CONFIG_PLX_DMA is not set
|
||||
# CONFIG_XILINX_ZYNQMP_DPDMA is not set
|
||||
# CONFIG_QCOM_HIDMA_MGMT is not set
|
||||
# CONFIG_QCOM_HIDMA is not set
|
||||
CONFIG_DW_DMAC_CORE=y
|
||||
@@ -1898,7 +1913,6 @@ CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y
|
||||
# CONFIG_STAGING is not set
|
||||
# CONFIG_X86_PLATFORM_DEVICES is not set
|
||||
CONFIG_PMC_ATOM=y
|
||||
# CONFIG_MFD_CROS_EC is not set
|
||||
# CONFIG_CHROME_PLATFORMS is not set
|
||||
# CONFIG_MELLANOX_PLATFORM is not set
|
||||
CONFIG_HAVE_CLK=y
|
||||
@@ -2019,7 +2033,7 @@ CONFIG_ACPI_VIOT=y
|
||||
# CONFIG_BCM_KONA_USB2_PHY is not set
|
||||
# CONFIG_PHY_PXA_28NM_HSIC is not set
|
||||
# CONFIG_PHY_PXA_28NM_USB2 is not set
|
||||
# CONFIG_PHY_INTEL_EMMC is not set
|
||||
# CONFIG_PHY_INTEL_LGM_EMMC is not set
|
||||
# end of PHY Subsystem
|
||||
|
||||
# CONFIG_POWERCAP is not set
|
||||
@@ -2114,6 +2128,7 @@ CONFIG_AUTOFS_FS=y
|
||||
CONFIG_FUSE_FS=y
|
||||
CONFIG_CUSE=y
|
||||
CONFIG_VIRTIO_FS=y
|
||||
CONFIG_FUSE_DAX=y
|
||||
CONFIG_OVERLAY_FS=y
|
||||
# CONFIG_OVERLAY_FS_REDIRECT_DIR is not set
|
||||
CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW=y
|
||||
@@ -2168,6 +2183,7 @@ CONFIG_SYSFS=y
|
||||
CONFIG_TMPFS=y
|
||||
CONFIG_TMPFS_POSIX_ACL=y
|
||||
CONFIG_TMPFS_XATTR=y
|
||||
# CONFIG_TMPFS_INODE64 is not set
|
||||
CONFIG_HUGETLBFS=y
|
||||
CONFIG_HUGETLB_PAGE=y
|
||||
CONFIG_MEMFD_CREATE=y
|
||||
@@ -2309,6 +2325,7 @@ CONFIG_CRYPTO_GLUE_HELPER_X86=y
|
||||
# CONFIG_CRYPTO_DH is not set
|
||||
# CONFIG_CRYPTO_ECDH is not set
|
||||
# CONFIG_CRYPTO_ECRDSA is not set
|
||||
# CONFIG_CRYPTO_SM2 is not set
|
||||
# CONFIG_CRYPTO_CURVE25519 is not set
|
||||
# CONFIG_CRYPTO_CURVE25519_X86 is not set
|
||||
|
||||
@@ -2391,8 +2408,6 @@ CONFIG_CRYPTO_SHA512=y
|
||||
CONFIG_CRYPTO_AES=y
|
||||
# CONFIG_CRYPTO_AES_TI is not set
|
||||
CONFIG_CRYPTO_AES_NI_INTEL=y
|
||||
# CONFIG_CRYPTO_ANUBIS is not set
|
||||
CONFIG_CRYPTO_ARC4=y
|
||||
# CONFIG_CRYPTO_BLOWFISH is not set
|
||||
# CONFIG_CRYPTO_BLOWFISH_X86_64 is not set
|
||||
# CONFIG_CRYPTO_CAMELLIA is not set
|
||||
@@ -2406,17 +2421,14 @@ CONFIG_CRYPTO_ARC4=y
|
||||
CONFIG_CRYPTO_DES=y
|
||||
# CONFIG_CRYPTO_DES3_EDE_X86_64 is not set
|
||||
# CONFIG_CRYPTO_FCRYPT is not set
|
||||
# CONFIG_CRYPTO_KHAZAD is not set
|
||||
# CONFIG_CRYPTO_SALSA20 is not set
|
||||
# CONFIG_CRYPTO_CHACHA20 is not set
|
||||
# CONFIG_CRYPTO_CHACHA20_X86_64 is not set
|
||||
# CONFIG_CRYPTO_SEED is not set
|
||||
# CONFIG_CRYPTO_SERPENT is not set
|
||||
# CONFIG_CRYPTO_SERPENT_SSE2_X86_64 is not set
|
||||
# CONFIG_CRYPTO_SERPENT_AVX_X86_64 is not set
|
||||
# CONFIG_CRYPTO_SERPENT_AVX2_X86_64 is not set
|
||||
# CONFIG_CRYPTO_SM4 is not set
|
||||
# CONFIG_CRYPTO_TEA is not set
|
||||
# CONFIG_CRYPTO_TWOFISH is not set
|
||||
CONFIG_CRYPTO_TWOFISH_COMMON=y
|
||||
CONFIG_CRYPTO_TWOFISH_X86_64=y
|
||||
@@ -2447,13 +2459,14 @@ CONFIG_CRYPTO_USER_API=y
|
||||
# CONFIG_CRYPTO_USER_API_HASH is not set
|
||||
# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
|
||||
CONFIG_CRYPTO_USER_API_RNG=y
|
||||
# CONFIG_CRYPTO_USER_API_RNG_CAVP is not set
|
||||
# CONFIG_CRYPTO_USER_API_AEAD is not set
|
||||
# CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE is not set
|
||||
|
||||
#
|
||||
# Crypto library routines
|
||||
#
|
||||
CONFIG_CRYPTO_LIB_AES=y
|
||||
CONFIG_CRYPTO_LIB_ARC4=y
|
||||
# CONFIG_CRYPTO_LIB_BLAKE2S is not set
|
||||
# CONFIG_CRYPTO_LIB_CHACHA is not set
|
||||
# CONFIG_CRYPTO_LIB_CURVE25519 is not set
|
||||
@@ -2512,6 +2525,7 @@ CONFIG_ZLIB_DEFLATE=y
|
||||
CONFIG_LZO_COMPRESS=y
|
||||
CONFIG_LZO_DECOMPRESS=y
|
||||
CONFIG_LZ4_DECOMPRESS=y
|
||||
CONFIG_ZSTD_DECOMPRESS=y
|
||||
CONFIG_XZ_DEC=y
|
||||
CONFIG_XZ_DEC_X86=y
|
||||
CONFIG_XZ_DEC_POWERPC=y
|
||||
@@ -2527,6 +2541,7 @@ CONFIG_DECOMPRESS_LZMA=y
|
||||
CONFIG_DECOMPRESS_XZ=y
|
||||
CONFIG_DECOMPRESS_LZO=y
|
||||
CONFIG_DECOMPRESS_LZ4=y
|
||||
CONFIG_DECOMPRESS_ZSTD=y
|
||||
CONFIG_GENERIC_ALLOCATOR=y
|
||||
CONFIG_INTERVAL_TREE=y
|
||||
CONFIG_XARRAY_MULTI=y
|
||||
@@ -2534,6 +2549,7 @@ CONFIG_ASSOCIATIVE_ARRAY=y
|
||||
CONFIG_HAS_IOMEM=y
|
||||
CONFIG_HAS_IOPORT_MAP=y
|
||||
CONFIG_HAS_DMA=y
|
||||
CONFIG_DMA_OPS=y
|
||||
CONFIG_NEED_SG_DMA_LENGTH=y
|
||||
CONFIG_NEED_DMA_MAP_STATE=y
|
||||
CONFIG_ARCH_DMA_ADDR_T_64BIT=y
|
||||
@@ -2557,6 +2573,7 @@ CONFIG_FONT_8x16=y
|
||||
CONFIG_ARCH_HAS_PMEM_API=y
|
||||
CONFIG_MEMREGION=y
|
||||
CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE=y
|
||||
CONFIG_ARCH_HAS_COPY_MC=y
|
||||
CONFIG_ARCH_STACKWALK=y
|
||||
CONFIG_SBITMAP=y
|
||||
# CONFIG_STRING_SELFTEST is not set
|
||||
@@ -2592,6 +2609,7 @@ CONFIG_STRIP_ASM_SYMS=y
|
||||
# CONFIG_HEADERS_INSTALL is not set
|
||||
CONFIG_DEBUG_SECTION_MISMATCH=y
|
||||
CONFIG_SECTION_MISMATCH_WARN_ONLY=y
|
||||
# CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_32B is not set
|
||||
CONFIG_FRAME_POINTER=y
|
||||
CONFIG_STACK_VALIDATION=y
|
||||
# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
|
||||
@@ -2605,10 +2623,14 @@ CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1
|
||||
CONFIG_MAGIC_SYSRQ_SERIAL=y
|
||||
CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE=""
|
||||
CONFIG_DEBUG_FS=y
|
||||
CONFIG_DEBUG_FS_ALLOW_ALL=y
|
||||
# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set
|
||||
# CONFIG_DEBUG_FS_ALLOW_NONE is not set
|
||||
CONFIG_HAVE_ARCH_KGDB=y
|
||||
# CONFIG_KGDB is not set
|
||||
CONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y
|
||||
# CONFIG_UBSAN is not set
|
||||
CONFIG_HAVE_ARCH_KCSAN=y
|
||||
# end of Generic Kernel Debugging Instruments
|
||||
|
||||
CONFIG_DEBUG_KERNEL=y
|
||||
@@ -2645,7 +2667,6 @@ CONFIG_HAVE_ARCH_KASAN_VMALLOC=y
|
||||
CONFIG_CC_HAS_KASAN_GENERIC=y
|
||||
CONFIG_CC_HAS_WORKING_NOSANITIZE_ADDRESS=y
|
||||
# CONFIG_KASAN is not set
|
||||
CONFIG_KASAN_STACK=1
|
||||
# end of Memory Debugging
|
||||
|
||||
# CONFIG_DEBUG_SHIRQ is not set
|
||||
@@ -2661,7 +2682,6 @@ CONFIG_HARDLOCKUP_CHECK_TIMESTAMP=y
|
||||
# CONFIG_HARDLOCKUP_DETECTOR is not set
|
||||
# CONFIG_DETECT_HUNG_TASK is not set
|
||||
# CONFIG_WQ_WATCHDOG is not set
|
||||
# CONFIG_TEST_LOCKUP is not set
|
||||
# end of Debug Oops, Lockups and Hangs
|
||||
|
||||
#
|
||||
@@ -2690,6 +2710,8 @@ CONFIG_LOCK_DEBUGGING_SUPPORT=y
|
||||
# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
|
||||
# CONFIG_LOCK_TORTURE_TEST is not set
|
||||
# CONFIG_WW_MUTEX_SELFTEST is not set
|
||||
# CONFIG_SCF_TORTURE_TEST is not set
|
||||
# CONFIG_CSD_LOCK_WAIT_DEBUG is not set
|
||||
# end of Lock Debugging (spinlocks, mutexes, etc...)
|
||||
|
||||
CONFIG_STACKTRACE=y
|
||||
@@ -2711,8 +2733,9 @@ CONFIG_BUG_ON_DATA_CORRUPTION=y
|
||||
#
|
||||
# RCU Debugging
|
||||
#
|
||||
# CONFIG_RCU_PERF_TEST is not set
|
||||
# CONFIG_RCU_SCALE_TEST is not set
|
||||
# CONFIG_RCU_TORTURE_TEST is not set
|
||||
# CONFIG_RCU_REF_SCALE_TEST is not set
|
||||
CONFIG_RCU_CPU_STALL_TIMEOUT=59
|
||||
# CONFIG_RCU_TRACE is not set
|
||||
# CONFIG_RCU_EQS_DEBUG is not set
|
||||
@@ -2736,7 +2759,6 @@ CONFIG_TRACING_SUPPORT=y
|
||||
# CONFIG_FTRACE is not set
|
||||
# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set
|
||||
# CONFIG_SAMPLES is not set
|
||||
CONFIG_HAVE_ARCH_KCSAN=y
|
||||
CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y
|
||||
CONFIG_STRICT_DEVMEM=y
|
||||
# CONFIG_IO_STRICT_DEVMEM is not set
|
||||
@@ -2745,6 +2767,7 @@ CONFIG_STRICT_DEVMEM=y
|
||||
# x86 Debugging
|
||||
#
|
||||
CONFIG_TRACE_IRQFLAGS_SUPPORT=y
|
||||
CONFIG_TRACE_IRQFLAGS_NMI_SUPPORT=y
|
||||
CONFIG_X86_VERBOSE_BOOTUP=y
|
||||
CONFIG_EARLY_PRINTK=y
|
||||
# CONFIG_EARLY_PRINTK_DBGP is not set
|
||||
@@ -2793,7 +2816,6 @@ CONFIG_RUNTIME_TESTING_MENU=y
|
||||
# CONFIG_TEST_KSTRTOX is not set
|
||||
# CONFIG_TEST_PRINTF is not set
|
||||
# CONFIG_TEST_BITMAP is not set
|
||||
# CONFIG_TEST_BITFIELD is not set
|
||||
# CONFIG_TEST_UUID is not set
|
||||
# CONFIG_TEST_XARRAY is not set
|
||||
# CONFIG_TEST_OVERFLOW is not set
|
||||
@@ -2807,6 +2829,8 @@ CONFIG_RUNTIME_TESTING_MENU=y
|
||||
# CONFIG_TEST_MEMCAT_P is not set
|
||||
# CONFIG_TEST_STACKINIT is not set
|
||||
# CONFIG_TEST_MEMINIT is not set
|
||||
# CONFIG_TEST_FREE_PAGES is not set
|
||||
# CONFIG_TEST_FPU is not set
|
||||
# CONFIG_MEMTEST is not set
|
||||
# end of Kernel Testing and Coverage
|
||||
# end of Kernel hacking
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Name: cloud-hypervisor
|
||||
Summary: Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on top of KVM.
|
||||
Version: 0.12.0
|
||||
Version: 0.13.0
|
||||
Release: 0%{?dist}
|
||||
License: ASL 2.0 or BSD-3-clause
|
||||
Group: Applications/System
|
||||
|
||||
@@ -28,8 +28,7 @@ CTR_CLH_CARGO_TARGET="${CTR_CLH_CARGO_BUILT_DIR}/cargo_target"
|
||||
CTR_CLH_INTEGRATION_WORKLOADS="/root/workloads"
|
||||
|
||||
# Container networking option
|
||||
CTR_CLH_NET="host"
|
||||
[ $(uname -m) = "aarch64" ] && CTR_CLH_NET="bridge"
|
||||
CTR_CLH_NET="bridge"
|
||||
|
||||
# Cargo paths
|
||||
# Full path to the cargo registry dir on the host. This appears on the host
|
||||
@@ -209,7 +208,7 @@ cmd_build() {
|
||||
libc="gnu"
|
||||
hypervisor="kvm"
|
||||
features_build=""
|
||||
|
||||
exported_device="/dev/kvm"
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
"-h"|"--help") { cmd_help; exit 1; } ;;
|
||||
@@ -236,18 +235,26 @@ cmd_build() {
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
ensure_build_dir
|
||||
if [ $(uname -m) = "x86_64" ]; then
|
||||
ensure_latest_ctr
|
||||
fi
|
||||
|
||||
process_volumes_args
|
||||
if [[ "$hypervisor" != "kvm" ]]; then
|
||||
die "Hypervisor value must be kvm"
|
||||
fi
|
||||
|
||||
if [[ "$hypervisor" = "mshv" ]]; then
|
||||
exported_device="/dev/mshv"
|
||||
fi
|
||||
target="$(uname -m)-unknown-linux-${libc}"
|
||||
|
||||
cargo_args=("$@")
|
||||
[ $build = "release" ] && cargo_args+=("--release")
|
||||
cargo_args+=(--target "$target")
|
||||
[ $(uname -m) = "aarch64" ] && cargo_args+=("--no-default-features")
|
||||
[ $(uname -m) = "aarch64" ] && cargo_args+=(--features "kvm")
|
||||
[ $(uname -m) = "aarch64" ] && cargo_args+=(--features $hypervisor)
|
||||
|
||||
rustflags=""
|
||||
if [ $(uname -m) = "aarch64" ] && [ $libc = "musl" ] ; then
|
||||
@@ -258,7 +265,7 @@ cmd_build() {
|
||||
--user "$(id -u):$(id -g)" \
|
||||
--workdir "$CTR_CLH_ROOT_DIR" \
|
||||
--rm \
|
||||
--volume /dev:/dev \
|
||||
--volume $exported_device \
|
||||
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" $exported_volumes \
|
||||
--env RUSTFLAGS="$rustflags" \
|
||||
"$CTR_IMAGE" \
|
||||
@@ -270,6 +277,11 @@ cmd_build() {
|
||||
cmd_clean() {
|
||||
cargo_args=("$@")
|
||||
|
||||
ensure_build_dir
|
||||
if [ $(uname -m) = "x86_64" ]; then
|
||||
ensure_latest_ctr
|
||||
fi
|
||||
|
||||
$DOCKER_RUNTIME run \
|
||||
--user "$(id -u):$(id -g)" \
|
||||
--workdir "$CTR_CLH_ROOT_DIR" \
|
||||
@@ -290,6 +302,7 @@ cmd_tests() {
|
||||
libc="gnu"
|
||||
arg_vols=""
|
||||
hypervisor="kvm"
|
||||
exported_device="/dev/kvm"
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
"-h"|"--help") { cmd_help; exit 1; } ;;
|
||||
@@ -323,8 +336,16 @@ cmd_tests() {
|
||||
if [[ "$hypervisor" != "kvm" ]]; then
|
||||
die "Hypervisor value must be kvm"
|
||||
fi
|
||||
if [[ "$hypervisor" = "mshv" ]]; then
|
||||
exported_device="/dev/mshv"
|
||||
fi
|
||||
set -- "$@" '--hypervisor' $hypervisor
|
||||
|
||||
ensure_build_dir
|
||||
if [ $(uname -m) = "x86_64" ]; then
|
||||
ensure_latest_ctr
|
||||
fi
|
||||
|
||||
process_volumes_args
|
||||
target="$(uname -m)-unknown-linux-${libc}"
|
||||
cflags=""
|
||||
@@ -334,12 +355,12 @@ cmd_tests() {
|
||||
cflags="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
|
||||
fi
|
||||
|
||||
if [[ "$unit" = true && $hypervisor = "kvm" ]] ; then
|
||||
if [[ "$unit" = true ]] ; then
|
||||
say "Running unit tests for $target..."
|
||||
$DOCKER_RUNTIME run \
|
||||
--workdir "$CTR_CLH_ROOT_DIR" \
|
||||
--rm \
|
||||
--device /dev/kvm \
|
||||
--device $exported_device \
|
||||
--device /dev/net/tun \
|
||||
--cap-add net_admin \
|
||||
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" $exported_volumes \
|
||||
@@ -434,6 +455,11 @@ cmd_build-container() {
|
||||
shift
|
||||
done
|
||||
|
||||
ensure_build_dir
|
||||
if [ $(uname -m) = "x86_64" ]; then
|
||||
ensure_latest_ctr
|
||||
fi
|
||||
|
||||
BUILD_DIR=/tmp/cloud-hypervisor/container/
|
||||
|
||||
mkdir -p $BUILD_DIR
|
||||
@@ -451,6 +477,10 @@ cmd_build-container() {
|
||||
}
|
||||
|
||||
cmd_shell() {
|
||||
ensure_build_dir
|
||||
if [ $(uname -m) = "x86_64" ]; then
|
||||
ensure_latest_ctr
|
||||
fi
|
||||
say_warn "Starting a privileged shell prompt as root ..."
|
||||
say_warn "WARNING: Your $CLH_ROOT_DIR folder will be bind-mounted in the container under $CTR_CLH_ROOT_DIR"
|
||||
$DOCKER_RUNTIME run \
|
||||
@@ -497,9 +527,5 @@ ok_or_die "Unknown command: $1. Please use \`$0 help\` for help."
|
||||
cmd=cmd_$1
|
||||
shift
|
||||
|
||||
ensure_build_dir
|
||||
if [ $(uname -m) = "x86_64" ]; then
|
||||
ensure_latest_ctr
|
||||
fi
|
||||
|
||||
$cmd "$@"
|
||||
|
||||
@@ -18,9 +18,9 @@ time cargo rustc --bin cloud-hypervisor -- -D warnings
|
||||
time cargo rustc -p vhost_user_net --bin vhost_user_net -- -D warnings
|
||||
time cargo test
|
||||
time cargo audit
|
||||
time cargo clippy --all-targets --no-default-features --features "acpi,kvm" -- -D warnings
|
||||
time cargo rustc --bin cloud-hypervisor --no-default-features --features "acpi,kvm" -- -D warnings
|
||||
time cargo clippy --all-targets --no-default-features --features "kvm" -- -D warnings
|
||||
time cargo rustc --bin cloud-hypervisor --no-default-features --features "kvm" -- -D warnings
|
||||
time cargo clippy --all-targets --no-default-features --features "acpi,$hypervisor" -- -D warnings
|
||||
time cargo rustc --bin cloud-hypervisor --no-default-features --features "acpi,$hypervisor" -- -D warnings
|
||||
time cargo clippy --all-targets --no-default-features --features "$hypervisor" -- -D warnings
|
||||
time cargo rustc --bin cloud-hypervisor --no-default-features --features "$hypervisor" -- -D warnings
|
||||
time cargo fmt -- --check
|
||||
time cargo build --all --release
|
||||
|
||||
@@ -107,7 +107,7 @@ update_workloads() {
|
||||
}
|
||||
|
||||
SRCDIR=$PWD
|
||||
LINUX_CUSTOM_BRANCH="virtio-fs-virtio-iommu-5.8-rc4"
|
||||
LINUX_CUSTOM_BRANCH="ch-5.10.6"
|
||||
|
||||
# Check whether the local HEAD commit same as the remote HEAD or not. Remove the folder if they are different.
|
||||
if [ -d "$LINUX_CUSTOM_DIR" ]; then
|
||||
@@ -179,8 +179,15 @@ update_workloads() {
|
||||
}
|
||||
|
||||
process_common_args "$@"
|
||||
features_build="--no-default-features --features kvm "
|
||||
features_test="--no-default-features --features integration_tests,kvm"
|
||||
|
||||
# aarch64 not supported for MSHV
|
||||
if [[ "$hypervisor" = "mshv" ]]; then
|
||||
echo "Aarch64 is not supported in Microsoft Hypervisor"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
features_build="--no-default-features --features $hypervisor "
|
||||
features_test="--no-default-features --features integration_tests,$hypervisor"
|
||||
|
||||
# lock the workloads folder to avoid parallel updating by different containers
|
||||
(
|
||||
@@ -221,7 +228,7 @@ sudo bash -c "echo 10 > /sys/kernel/mm/ksm/sleep_millisecs"
|
||||
sudo bash -c "echo 1 > /sys/kernel/mm/ksm/run"
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
time cargo test $features_test "tests::parallel::"
|
||||
time cargo test $features_test "tests::parallel::$test_filter"
|
||||
RES=$?
|
||||
|
||||
# Tear vhost_user_net test network down
|
||||
|
||||
@@ -22,7 +22,7 @@ strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
time cargo test $features_test "tests::sgx::"
|
||||
time cargo test $features_test "tests::sgx::$test_filter"
|
||||
RES=$?
|
||||
|
||||
exit $RES
|
||||
|
||||
@@ -17,6 +17,14 @@ TARGET_CC="musl-gcc"
|
||||
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
|
||||
fi
|
||||
|
||||
# Use device mapper to create a snapshot of the Windows image
|
||||
img_blk_size=$(du -b -B 512 /root/workloads/windows-server-2019.raw | awk '{print $1;}')
|
||||
loop_device=$(losetup --find --show --read-only /root/workloads/windows-server-2019.raw)
|
||||
dmsetup create windows-base --table "0 $img_blk_size linear $loop_device 0"
|
||||
dmsetup mknodes
|
||||
dmsetup create windows-snapshot-base --table "0 $img_blk_size snapshot-origin /dev/mapper/windows-base"
|
||||
dmsetup mknodes
|
||||
|
||||
cargo build --all --release $features_build --target $BUILD_TARGET
|
||||
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||
|
||||
@@ -24,7 +32,10 @@ export RUST_BACKTRACE=1
|
||||
|
||||
# Only run with 1 thread to avoid tests interfering with one another because
|
||||
# Windows has a static IP configured
|
||||
time cargo test $features_test "tests::windows::" -- --test-threads=1
|
||||
time cargo test $features_test "tests::windows::$test_filter" -- --test-threads=1
|
||||
RES=$?
|
||||
|
||||
dmsetup remove_all -f
|
||||
losetup -D
|
||||
|
||||
exit $RES
|
||||
|
||||
@@ -9,7 +9,6 @@ export BUILD_TARGET=${BUILD_TARGET-x86_64-unknown-linux-gnu}
|
||||
WORKLOADS_DIR="$HOME/workloads"
|
||||
mkdir -p "$WORKLOADS_DIR"
|
||||
|
||||
|
||||
process_common_args "$@"
|
||||
|
||||
# For now these values are deafult for kvm
|
||||
@@ -44,7 +43,7 @@ if [ ! -f "$BIONIC_OS_RAW_IMAGE" ]; then
|
||||
fi
|
||||
|
||||
|
||||
FOCAL_OS_IMAGE_NAME="focal-server-cloudimg-amd64-custom.qcow2"
|
||||
FOCAL_OS_IMAGE_NAME="focal-server-cloudimg-amd64-custom-20210106-1.qcow2"
|
||||
FOCAL_OS_IMAGE_URL="https://cloudhypervisorstorage.blob.core.windows.net/images/$FOCAL_OS_IMAGE_NAME"
|
||||
FOCAL_OS_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_IMAGE_NAME"
|
||||
if [ ! -f "$FOCAL_OS_IMAGE" ]; then
|
||||
@@ -53,7 +52,7 @@ if [ ! -f "$FOCAL_OS_IMAGE" ]; then
|
||||
popd
|
||||
fi
|
||||
|
||||
FOCAL_OS_RAW_IMAGE_NAME="focal-server-cloudimg-amd64-custom.raw"
|
||||
FOCAL_OS_RAW_IMAGE_NAME="focal-server-cloudimg-amd64-custom-20210106-1.raw"
|
||||
FOCAL_OS_RAW_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_RAW_IMAGE_NAME"
|
||||
if [ ! -f "$FOCAL_OS_RAW_IMAGE" ]; then
|
||||
pushd $WORKLOADS_DIR
|
||||
@@ -105,7 +104,7 @@ LINUX_CUSTOM_DIR="$WORKLOADS_DIR/linux-custom"
|
||||
if [ ! -f "$VMLINUX_IMAGE" ] || [ ! -f "$VMLINUX_PVH_IMAGE" ]; then
|
||||
SRCDIR=$PWD
|
||||
pushd $WORKLOADS_DIR
|
||||
time git clone --depth 1 "https://github.com/cloud-hypervisor/linux.git" -b "virtio-fs-virtio-iommu-5.8-rc4" $LINUX_CUSTOM_DIR
|
||||
time git clone --depth 1 "https://github.com/cloud-hypervisor/linux.git" -b "ch-5.10.6" $LINUX_CUSTOM_DIR
|
||||
cp $SRCDIR/resources/linux-config-x86_64 $LINUX_CUSTOM_DIR/.config
|
||||
popd
|
||||
fi
|
||||
@@ -189,10 +188,10 @@ cp $FW $VFIO_DIR
|
||||
cp $VMLINUX_IMAGE $VFIO_DIR || exit 1
|
||||
|
||||
# VFIO test network setup.
|
||||
# We reserve a different IP class for it: 172.17.0.0/24.
|
||||
# We reserve a different IP class for it: 172.18.0.0/24.
|
||||
sudo ip link add name vfio-br0 type bridge
|
||||
sudo ip link set vfio-br0 up
|
||||
sudo ip addr add 172.17.0.1/24 dev vfio-br0
|
||||
sudo ip addr add 172.18.0.1/24 dev vfio-br0
|
||||
|
||||
sudo ip tuntap add vfio-tap0 mode tap
|
||||
sudo ip link set vfio-tap0 master vfio-br0
|
||||
@@ -243,14 +242,14 @@ echo 4096 | sudo tee /proc/sys/vm/nr_hugepages
|
||||
sudo chmod a+rwX /dev/hugepages
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
time cargo test $features_test "tests::parallel::"
|
||||
time cargo test $features_test "tests::parallel::$test_filter"
|
||||
RES=$?
|
||||
|
||||
# Run some tests in sequence since the result could be affected by other tests
|
||||
# running in parallel.
|
||||
if [ $RES -eq 0 ]; then
|
||||
export RUST_BACKTRACE=1
|
||||
time cargo test $features_test "tests::sequential::" -- --test-threads=1
|
||||
time cargo test $features_test "tests::sequential::$test_filter" -- --test-threads=1
|
||||
RES=$?
|
||||
fi
|
||||
|
||||
|
||||
@@ -7,8 +7,10 @@ process_common_args "$@"
|
||||
|
||||
BUILD_TARGET=${BUILD_TARGET-x86_64-unknown-linux-gnu}
|
||||
cargo_args=("")
|
||||
[ $(uname -m) = "aarch64" ] && cargo_args+=("--no-default-features")
|
||||
[ $(uname -m) = "aarch64" ] && cargo_args+=("--features kvm")
|
||||
|
||||
if [[ $(uname -m) = "aarch64" || $hypervisor = "mshv" ]]; then
|
||||
cargo_args+=("--no-default-features")
|
||||
cargo_args+=("--features $hypervisor")
|
||||
fi
|
||||
export RUST_BACKTRACE=1
|
||||
cargo test --target $BUILD_TARGET --workspace ${cargo_args[@]} || exit 1;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
27f3b17962ace69b51f0ddc2012095e3109e6ed8 bionic-server-cloudimg-amd64.qcow2
|
||||
8db9cc58b01452ce2d06c313177e6e74d8582d93 bionic-server-cloudimg-amd64.raw
|
||||
d4a44acc6014d5f83dea1c625c43d677a95fa75f alpine-minirootfs-x86_64.tar.gz
|
||||
f131563d3aa466cb5d2c25ffe7007c9cb8d15818 focal-server-cloudimg-amd64-custom.qcow2
|
||||
d5a08d2b35d48042d2c08fdc0c31ae656073f43e focal-server-cloudimg-amd64-custom.raw
|
||||
4ee774e9e10f3cd985203c3ea20b487c285dfa1d focal-server-cloudimg-amd64-custom-20210106-1.qcow2
|
||||
7cd439f9dfe63b5f354011ea0e893256a8111174 focal-server-cloudimg-amd64-custom-20210106-1.raw
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/bin/bash
|
||||
hypervisor="kvm"
|
||||
test_filter=""
|
||||
|
||||
cmd_help() {
|
||||
echo ""
|
||||
echo "Cloud Hypervisor $(basename $0)"
|
||||
@@ -8,6 +10,7 @@ cmd_help() {
|
||||
echo "Available arguments:"
|
||||
echo ""
|
||||
echo " --hypervisor Underlying hypervisor. Options kvm, mshv"
|
||||
echo " --test-filter Tests to run"
|
||||
echo ""
|
||||
echo " --help Display this help message."
|
||||
echo ""
|
||||
@@ -21,8 +24,11 @@ process_common_args() {
|
||||
shift
|
||||
hypervisor="$1"
|
||||
;;
|
||||
"--test-filter")
|
||||
shift
|
||||
test_filter="$1"
|
||||
;;
|
||||
*)
|
||||
# We only care about hypervisor , do nothing for other arguments
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
@@ -31,4 +37,4 @@ process_common_args() {
|
||||
echo "Hypervisor value must be kvm"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,6 +464,7 @@ fn main() {
|
||||
.subcommand(SubCommand::with_name("counters").about("Counters from the VM"))
|
||||
.subcommand(SubCommand::with_name("pause").about("Pause the VM"))
|
||||
.subcommand(SubCommand::with_name("reboot").about("Reboot the VM"))
|
||||
.subcommand(SubCommand::with_name("power-button").about("Trigger a power button in the VM"))
|
||||
.subcommand(
|
||||
SubCommand::with_name("resize")
|
||||
.about("Resize the VM")
|
||||
|
||||
100
src/main.rs
100
src/main.rs
@@ -4,6 +4,7 @@
|
||||
//
|
||||
|
||||
extern crate anyhow;
|
||||
extern crate signal_hook;
|
||||
extern crate vmm;
|
||||
extern crate vmm_sys_util;
|
||||
|
||||
@@ -14,9 +15,14 @@ use clap::{App, Arg, ArgGroup, ArgMatches};
|
||||
use libc::EFD_NONBLOCK;
|
||||
use log::LevelFilter;
|
||||
use seccomp::SeccompAction;
|
||||
use signal_hook::{
|
||||
consts::SIGSYS,
|
||||
iterator::{exfiltrator::WithRawSiginfo, SignalsInfo},
|
||||
};
|
||||
use std::env;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use thiserror::Error;
|
||||
use vmm::config;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
@@ -73,8 +79,9 @@ impl log::Log for Logger {
|
||||
if record.file().is_some() && record.line().is_some() {
|
||||
writeln!(
|
||||
*(*(self.output.lock().unwrap())),
|
||||
"cloud-hypervisor: {:?}: {}:{}:{} -- {}",
|
||||
"cloud-hypervisor: {:?}: <{}> {}:{}:{} -- {}",
|
||||
duration,
|
||||
std::thread::current().name().unwrap_or("anonymous"),
|
||||
record.level(),
|
||||
record.file().unwrap(),
|
||||
record.line().unwrap(),
|
||||
@@ -83,8 +90,9 @@ impl log::Log for Logger {
|
||||
} else {
|
||||
writeln!(
|
||||
*(*(self.output.lock().unwrap())),
|
||||
"cloud-hypervisor: {:?}: {}:{} -- {}",
|
||||
"cloud-hypervisor: {:?}: <{}> {}:{} -- {}",
|
||||
duration,
|
||||
std::thread::current().name().unwrap_or("anonymous"),
|
||||
record.level(),
|
||||
record.target(),
|
||||
record.args()
|
||||
@@ -139,7 +147,8 @@ fn create_app<'a, 'b>(
|
||||
.long("memory")
|
||||
.help(
|
||||
"Memory parameters \
|
||||
\"size=<guest_memory_size>,mergeable=on|off,shared=on|off,hugepages=on|off,\
|
||||
\"size=<guest_memory_size>,mergeable=on|off,shared=on|off,\
|
||||
hugepages=on|off,hugepage_size=<hugepage_size>\
|
||||
hotplug_method=acpi|virtio-mem,\
|
||||
hotplug_size=<hotpluggable_memory_size>,\
|
||||
hotplugged_size=<hotplugged_memory_size>\"",
|
||||
@@ -153,7 +162,9 @@ fn create_app<'a, 'b>(
|
||||
.help(
|
||||
"User defined memory zone parameters \
|
||||
\"size=<guest_memory_region_size>,file=<backing_file>,\
|
||||
shared=on|off,hugepages=on|off,host_numa_node=<node_id>,\
|
||||
shared=on|off,\
|
||||
hugepages=on|off,hugepage_size=<hugepage_size>\
|
||||
host_numa_node=<node_id>,\
|
||||
id=<zone_identifier>,hotplug_size=<hotpluggable_memory_size>,\
|
||||
hotplugged_size=<hotplugged_memory_size>\"",
|
||||
)
|
||||
@@ -236,7 +247,7 @@ fn create_app<'a, 'b>(
|
||||
.arg(
|
||||
Arg::with_name("serial")
|
||||
.long("serial")
|
||||
.help("Control serial port: off|null|tty|file=/path/to/a/file")
|
||||
.help("Control serial port: off|null|pty|tty|file=/path/to/a/file")
|
||||
.default_value("null")
|
||||
.group("vm-config"),
|
||||
)
|
||||
@@ -244,7 +255,7 @@ fn create_app<'a, 'b>(
|
||||
Arg::with_name("console")
|
||||
.long("console")
|
||||
.help(
|
||||
"Control (virtio) console: \"off|null|tty|file=/path/to/a/file,iommu=on|off\"",
|
||||
"Control (virtio) console: \"off|null|pty|tty|file=/path/to/a/file,iommu=on|off\"",
|
||||
)
|
||||
.default_value("tty")
|
||||
.group("vm-config"),
|
||||
@@ -353,6 +364,31 @@ fn start_vmm(cmd_arguments: ArgMatches, api_socket_path: &str) -> Result<(), Err
|
||||
} else {
|
||||
SeccompAction::Trap
|
||||
};
|
||||
|
||||
// See https://github.com/rust-lang/libc/issues/716 why we can't get the details from siginfo_t
|
||||
if seccomp_action == SeccompAction::Trap {
|
||||
thread::Builder::new()
|
||||
.name("seccomp_signal_handler".to_string())
|
||||
.spawn(move || {
|
||||
for si in SignalsInfo::<WithRawSiginfo>::new(&[SIGSYS])
|
||||
.unwrap()
|
||||
.forever()
|
||||
{
|
||||
/* SYS_SECCOMP */
|
||||
if si.si_code == 1 {
|
||||
eprint!(
|
||||
"\n==== seccomp violation ====\n\
|
||||
Try running with `strace -ff` to identify the cause and open an issue: \
|
||||
https://github.com/cloud-hypervisor/cloud-hypervisor/issues/new\n"
|
||||
);
|
||||
|
||||
signal_hook::low_level::emulate_default_handler(SIGSYS).unwrap();
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let hypervisor = hypervisor::new().map_err(Error::CreateHypervisor)?;
|
||||
let vmm_thread = vmm::start_vmm_thread(
|
||||
env!("CARGO_PKG_VERSION").to_string(),
|
||||
@@ -552,6 +588,7 @@ mod unit_tests {
|
||||
shared: false,
|
||||
hugepages: false,
|
||||
zones: None,
|
||||
hugepage_size: None,
|
||||
},
|
||||
kernel: Some(KernelConfig {
|
||||
path: PathBuf::from("/path/to/kernel"),
|
||||
@@ -1375,6 +1412,57 @@ mod unit_tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_valid_vm_config_serial_pty_console_pty() {
|
||||
vec![
|
||||
(
|
||||
vec!["cloud-hypervisor", "--kernel", "/path/to/kernel"],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"serial": {"mode": "Null"},
|
||||
"console": {"mode": "Tty"}
|
||||
}"#,
|
||||
true,
|
||||
),
|
||||
(
|
||||
vec![
|
||||
"cloud-hypervisor",
|
||||
"--kernel",
|
||||
"/path/to/kernel",
|
||||
"--serial",
|
||||
"null",
|
||||
"--console",
|
||||
"tty",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"}
|
||||
}"#,
|
||||
true,
|
||||
),
|
||||
(
|
||||
vec![
|
||||
"cloud-hypervisor",
|
||||
"--kernel",
|
||||
"/path/to/kernel",
|
||||
"--serial",
|
||||
"pty",
|
||||
"--console",
|
||||
"pty",
|
||||
],
|
||||
r#"{
|
||||
"kernel": {"path": "/path/to/kernel"},
|
||||
"serial": {"mode": "Pty"},
|
||||
"console": {"mode": "Pty"}
|
||||
}"#,
|
||||
true,
|
||||
),
|
||||
]
|
||||
.iter()
|
||||
.for_each(|(cli, openapi, equal)| {
|
||||
compare_vm_config_cli_vs_json(cli, openapi, *equal);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn test_valid_vm_config_devices() {
|
||||
|
||||
@@ -43,7 +43,7 @@ write_files:
|
||||
# 1G ram requires 512 pages
|
||||
echo 512 | sudo tee /proc/sys/vm/nr_hugepages
|
||||
sudo chmod a+rwX /dev/hugepages
|
||||
/mnt/cloud-hypervisor --kernel /mnt/vmlinux --cmdline "console=hvc0 reboot=k panic=1 nomodules i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd root=/dev/vda1 VFIOTAG" --disk path=/mnt/focal-server-cloudimg-amd64-custom.qcow2 path=/mnt/cloudinit.img --cpus boot=1 --memory size=512M,hotplug_size=1G,hugepages=on --device path=/sys/bus/pci/devices/0000:00:06.0/ path=/sys/bus/pci/devices/0000:00:07.0/ --api-socket /tmp/ch_api.sock
|
||||
/mnt/cloud-hypervisor --kernel /mnt/vmlinux --cmdline "console=hvc0 reboot=k panic=1 nomodules i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd root=/dev/vda1 VFIOTAG" --disk path=/mnt/focal-server-cloudimg-amd64-custom-20210106-1.qcow2 path=/mnt/cloudinit.img --cpus boot=1 --memory size=512M,hotplug_size=1G,hugepages=on --device path=/sys/bus/pci/devices/0000:00:06.0/ path=/sys/bus/pci/devices/0000:00:07.0/ --api-socket /tmp/ch_api.sock
|
||||
|
||||
-
|
||||
path: /etc/systemd/system/notify-booted.service
|
||||
@@ -56,6 +56,8 @@ write_files:
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/cloud-hypervisor-notify-booted.sh
|
||||
Restart=on-failure
|
||||
RestartSec=2
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -65,5 +67,6 @@ write_files:
|
||||
permissions: 0755
|
||||
content: |
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo -n "@DEFAULT_TCP_LISTENER_MESSAGE" | nc -w0 @HOST_IP @TCP_LISTENER_PORT
|
||||
|
||||
1144
tests/integration.rs
1144
tests/integration.rs
File diff suppressed because it is too large
Load Diff
@@ -9,10 +9,10 @@ default = []
|
||||
|
||||
[dependencies]
|
||||
epoll = ">=4.0.1"
|
||||
libc = "0.2.81"
|
||||
log = "0.4.11"
|
||||
libc = "0.2.86"
|
||||
log = "0.4.14"
|
||||
virtio-bindings = "0.1.0"
|
||||
vm-memory = "0.4.0"
|
||||
vm-memory = "0.5.0"
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = ">=0.3.1"
|
||||
vhost_rs = { git = "https://github.com/rust-vmm/vhost", branch = "master", package = "vhost", features = ["vhost-user-slave"] }
|
||||
|
||||
@@ -8,12 +8,12 @@ edition = "2018"
|
||||
block_util = { path = "../block_util" }
|
||||
clap = { version = "2.33.3", features=["wrap_help"] }
|
||||
epoll = ">=4.0.1"
|
||||
libc = "0.2.81"
|
||||
log = "0.4.11"
|
||||
libc = "0.2.86"
|
||||
log = "0.4.14"
|
||||
option_parser = { path = "../option_parser" }
|
||||
qcow = { path = "../qcow" }
|
||||
vhost_user_backend = { path = "../vhost_user_backend" }
|
||||
vhost_rs = { git = "https://github.com/rust-vmm/vhost", branch = "master", package = "vhost", features = ["vhost-user-slave"] }
|
||||
virtio-bindings = "0.1.0"
|
||||
vm-memory = "0.4.0"
|
||||
vm-memory = "0.5.0"
|
||||
vmm-sys-util = ">=0.3.1"
|
||||
|
||||
@@ -43,7 +43,7 @@ use vm_memory::{Bytes, GuestMemoryMmap};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
const SECTOR_SHIFT: u8 = 9;
|
||||
const SECTOR_SIZE: u64 = (0x01 as u64) << SECTOR_SHIFT;
|
||||
const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
|
||||
const BLK_SIZE: u32 = 512;
|
||||
// Current (2020) enterprise SSDs have a latency lower than 30us.
|
||||
// Polling for 50us should be enough to cover for the device latency
|
||||
@@ -218,16 +218,17 @@ impl VhostUserBlkBackend {
|
||||
};
|
||||
|
||||
let nsectors = (image.lock().unwrap().seek(SeekFrom::End(0)).unwrap() as u64) / SECTOR_SIZE;
|
||||
let mut config = VirtioBlockConfig::default();
|
||||
|
||||
config.capacity = nsectors;
|
||||
config.blk_size = BLK_SIZE;
|
||||
config.size_max = 65535;
|
||||
config.seg_max = 128 - 2;
|
||||
config.min_io_size = 1;
|
||||
config.opt_io_size = 1;
|
||||
config.num_queues = num_queues as u16;
|
||||
config.writeback = 1;
|
||||
let config = VirtioBlockConfig {
|
||||
capacity: nsectors,
|
||||
blk_size: BLK_SIZE,
|
||||
size_max: 65535,
|
||||
seg_max: 128 - 2,
|
||||
min_io_size: 1,
|
||||
opt_io_size: 1,
|
||||
num_queues: num_queues as u16,
|
||||
writeback: 1,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut queues_per_thread = Vec::new();
|
||||
let mut threads = Vec::new();
|
||||
|
||||
@@ -7,12 +7,12 @@ edition = "2018"
|
||||
[dependencies]
|
||||
clap = { version = "2.33.3", features=["wrap_help"] }
|
||||
epoll = ">=4.0.1"
|
||||
libc = "0.2.81"
|
||||
log = "0.4.11"
|
||||
libc = "0.2.86"
|
||||
log = "0.4.14"
|
||||
net_util = { path = "../net_util" }
|
||||
option_parser = { path = "../option_parser" }
|
||||
vhost_user_backend = { path = "../vhost_user_backend" }
|
||||
vhost_rs = { git = "https://github.com/rust-vmm/vhost", branch = "master", package = "vhost", features = ["vhost-user-slave"] }
|
||||
virtio-bindings = "0.1.0"
|
||||
vm-memory = "0.4.0"
|
||||
vm-memory = "0.5.0"
|
||||
vmm-sys-util = ">=0.3.1"
|
||||
|
||||
@@ -143,6 +143,7 @@ impl VhostUserNetBackend {
|
||||
Some(netmask),
|
||||
&mut Some(host_mac),
|
||||
num_queues / 2,
|
||||
None,
|
||||
)
|
||||
.map_err(Error::OpenTap)?;
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ block_util = { path = "../block_util" }
|
||||
byteorder = "1.3.4"
|
||||
epoll = ">=4.0.1"
|
||||
io-uring = ">=0.4.0"
|
||||
libc = "0.2.81"
|
||||
log = "0.4.11"
|
||||
libc = "0.2.86"
|
||||
log = "0.4.14"
|
||||
net_gen = { path = "../net_gen" }
|
||||
net_util = { path = "../net_util" }
|
||||
pci = { path = "../pci" }
|
||||
@@ -24,13 +24,13 @@ seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v
|
||||
serde = ">=1.0.27"
|
||||
serde_derive = ">=1.0.27"
|
||||
serde_json = ">=1.0.9"
|
||||
tempfile = "3.1.0"
|
||||
tempfile = "3.2.0"
|
||||
vfio-ioctls = { git = "https://github.com/cloud-hypervisor/vfio-ioctls", branch = "ch" }
|
||||
vhost_rs = { git = "https://github.com/rust-vmm/vhost", branch = "master", package = "vhost", features = ["vhost-user-master", "vhost-user-slave"] }
|
||||
virtio-bindings = { version = "0.1", features = ["virtio-v5_0_0"]}
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = { version = "0.4.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vm-memory = { version = "0.5.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = ">=0.3.1"
|
||||
|
||||
@@ -321,8 +321,10 @@ impl Balloon {
|
||||
pub fn new(id: String, size: u64, seccomp_action: SeccompAction) -> io::Result<Self> {
|
||||
let avail_features = 1u64 << VIRTIO_F_VERSION_1;
|
||||
|
||||
let mut config = VirtioBalloonConfig::default();
|
||||
config.num_pages = (size >> VIRTIO_BALLOON_PFN_SHIFT) as u32;
|
||||
let config = VirtioBalloonConfig {
|
||||
num_pages: (size >> VIRTIO_BALLOON_PFN_SHIFT) as u32,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(Balloon {
|
||||
common: VirtioCommon {
|
||||
@@ -330,6 +332,7 @@ impl Balloon {
|
||||
avail_features,
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
queue_sizes: QUEUE_SIZES.to_vec(),
|
||||
min_queues: NUM_QUEUES as u16,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
@@ -444,7 +447,7 @@ impl VirtioDevice for Balloon {
|
||||
get_seccomp_filter(&self.seccomp_action, Thread::VirtioBalloon)
|
||||
.map_err(ActivateError::CreateSeccompFilter)?;
|
||||
thread::Builder::new()
|
||||
.name("virtio_balloon".to_string())
|
||||
.name(self.id.clone())
|
||||
.spawn(move || {
|
||||
if let Err(e) = SeccompFilter::apply(virtio_balloon_seccomp_filter) {
|
||||
error!("Error applying seccomp filter: {:?}", e);
|
||||
@@ -462,7 +465,7 @@ impl VirtioDevice for Balloon {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> Option<(Arc<dyn VirtioInterrupt>, Vec<EventFd>)> {
|
||||
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
||||
self.common.reset()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE-BSD-3-Clause file.
|
||||
//
|
||||
// Copyright © 2019 Intel Corporation
|
||||
// Copyright © 2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
@@ -16,20 +16,21 @@ use super::{
|
||||
use crate::seccomp_filters::{get_seccomp_filter, Thread};
|
||||
use crate::VirtioInterrupt;
|
||||
use anyhow::anyhow;
|
||||
use block_util::{build_disk_image_id, Request, RequestType, VirtioBlockConfig};
|
||||
use block_util::{
|
||||
async_io::AsyncIo, async_io::AsyncIoError, async_io::DiskFile, build_disk_image_id, Request,
|
||||
RequestType, VirtioBlockConfig,
|
||||
};
|
||||
use seccomp::{SeccompAction, SeccompFilter};
|
||||
use std::collections::HashMap;
|
||||
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||
use std::io;
|
||||
use std::num::Wrapping;
|
||||
use std::ops::DerefMut;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::path::PathBuf;
|
||||
use std::result;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::thread;
|
||||
use virtio_bindings::bindings::virtio_blk::*;
|
||||
use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
||||
use vm_memory::{
|
||||
ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryError,
|
||||
GuestMemoryMmap,
|
||||
@@ -41,10 +42,12 @@ use vm_migration::{
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
const SECTOR_SHIFT: u8 = 9;
|
||||
pub const SECTOR_SIZE: u64 = (0x01 as u64) << SECTOR_SHIFT;
|
||||
pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
|
||||
|
||||
// New descriptors are pending on the virtio queue.
|
||||
const QUEUE_AVAIL_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
|
||||
// New completed tasks are pending on the completion ring.
|
||||
const COMPLETION_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
@@ -64,10 +67,21 @@ pub enum Error {
|
||||
GetFileMetadata,
|
||||
/// The requested operation would cause a seek beyond disk end.
|
||||
InvalidOffset,
|
||||
/// Unsupported operation on the disk.
|
||||
Unsupported(u32),
|
||||
/// Failed to parse the request.
|
||||
RequestParsing(block_util::Error),
|
||||
/// Failed to execute the request.
|
||||
RequestExecuting(block_util::ExecuteError),
|
||||
/// Missing the expected entry in the list of requests.
|
||||
MissingEntryRequestList,
|
||||
/// The asynchronous request returned with failure.
|
||||
AsyncRequestFailure,
|
||||
/// Failed synchronizing the file
|
||||
Fsync(AsyncIoError),
|
||||
}
|
||||
|
||||
pub trait DiskFile: Read + Seek + Write + Clone {}
|
||||
impl<D: Read + Seek + Write + Clone> DiskFile for D {}
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct BlockCounters {
|
||||
@@ -77,23 +91,64 @@ pub struct BlockCounters {
|
||||
write_ops: Arc<AtomicU64>,
|
||||
}
|
||||
|
||||
struct BlockEpollHandler<T: DiskFile> {
|
||||
struct BlockEpollHandler {
|
||||
queue: Queue,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
disk_image: Arc<Mutex<T>>,
|
||||
disk_image: Box<dyn AsyncIo>,
|
||||
disk_nsectors: u64,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
disk_image_id: Vec<u8>,
|
||||
kill_evt: EventFd,
|
||||
pause_evt: EventFd,
|
||||
event_idx: bool,
|
||||
writeback: Arc<AtomicBool>,
|
||||
counters: BlockCounters,
|
||||
queue_evt: EventFd,
|
||||
request_list: HashMap<u16, Request>,
|
||||
}
|
||||
|
||||
impl<T: DiskFile> BlockEpollHandler<T> {
|
||||
fn process_queue(&mut self) -> bool {
|
||||
impl BlockEpollHandler {
|
||||
fn process_queue_submit(&mut self) -> Result<bool> {
|
||||
let queue = &mut self.queue;
|
||||
let mem = self.mem.memory();
|
||||
|
||||
let mut used_desc_heads = Vec::new();
|
||||
let mut used_count = 0;
|
||||
|
||||
for avail_desc in queue.iter(&mem) {
|
||||
let mut request = Request::parse(&avail_desc, &mem).map_err(Error::RequestParsing)?;
|
||||
request.set_writeback(self.writeback.load(Ordering::Acquire));
|
||||
|
||||
if request
|
||||
.execute_async(
|
||||
&mem,
|
||||
self.disk_nsectors,
|
||||
self.disk_image.as_mut(),
|
||||
&self.disk_image_id,
|
||||
avail_desc.index as u64,
|
||||
)
|
||||
.map_err(Error::RequestExecuting)?
|
||||
{
|
||||
self.request_list.insert(avail_desc.index, request);
|
||||
} else {
|
||||
// We use unwrap because the request parsing process already
|
||||
// checked that the status_addr was valid.
|
||||
mem.write_obj(VIRTIO_BLK_S_OK, request.status_addr).unwrap();
|
||||
|
||||
// If no asynchronous operation has been submitted, we can
|
||||
// simply return the used descriptor.
|
||||
used_desc_heads.push((avail_desc.index, 0));
|
||||
used_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for &(desc_index, len) in used_desc_heads.iter() {
|
||||
queue.add_used(&mem, desc_index, len);
|
||||
}
|
||||
|
||||
Ok(used_count > 0)
|
||||
}
|
||||
|
||||
fn process_queue_complete(&mut self) -> Result<bool> {
|
||||
let queue = &mut self.queue;
|
||||
|
||||
let mut used_desc_heads = Vec::new();
|
||||
@@ -104,55 +159,48 @@ impl<T: DiskFile> BlockEpollHandler<T> {
|
||||
let mut read_ops = Wrapping(0);
|
||||
let mut write_ops = Wrapping(0);
|
||||
|
||||
for avail_desc in queue.iter(&mem) {
|
||||
let len;
|
||||
match Request::parse(&avail_desc, &mem) {
|
||||
Ok(mut request) => {
|
||||
request.set_writeback(self.writeback.load(Ordering::Acquire));
|
||||
let completion_list = self.disk_image.complete();
|
||||
for (user_data, result) in completion_list {
|
||||
let desc_index = user_data as u16;
|
||||
let request = self
|
||||
.request_list
|
||||
.remove(&desc_index)
|
||||
.ok_or(Error::MissingEntryRequestList)?;
|
||||
|
||||
let mut disk_image_locked = self.disk_image.lock().unwrap();
|
||||
let mut disk_image = disk_image_locked.deref_mut();
|
||||
let status = match request.execute(
|
||||
&mut disk_image,
|
||||
self.disk_nsectors,
|
||||
&mem,
|
||||
&self.disk_image_id,
|
||||
) {
|
||||
Ok(l) => {
|
||||
len = l;
|
||||
match request.request_type {
|
||||
RequestType::In => {
|
||||
for (_, data_len) in &request.data_descriptors {
|
||||
read_bytes += Wrapping(*data_len as u64);
|
||||
}
|
||||
read_ops += Wrapping(1);
|
||||
}
|
||||
RequestType::Out => {
|
||||
for (_, data_len) in &request.data_descriptors {
|
||||
write_bytes += Wrapping(*data_len as u64);
|
||||
}
|
||||
write_ops += Wrapping(1);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
VIRTIO_BLK_S_OK
|
||||
let (status, len) = if result >= 0 {
|
||||
match request.request_type {
|
||||
RequestType::In => {
|
||||
for (_, data_len) in &request.data_descriptors {
|
||||
read_bytes += Wrapping(*data_len as u64);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to execute request: {:?}", e);
|
||||
len = 1; // We need at least 1 byte for the status.
|
||||
e.status()
|
||||
read_ops += Wrapping(1);
|
||||
}
|
||||
RequestType::Out => {
|
||||
if !request.writeback {
|
||||
self.disk_image.fsync(None).map_err(Error::Fsync)?;
|
||||
}
|
||||
};
|
||||
// We use unwrap because the request parsing process already checked that the
|
||||
// status_addr was valid.
|
||||
mem.write_obj(status, request.status_addr).unwrap();
|
||||
for (_, data_len) in &request.data_descriptors {
|
||||
write_bytes += Wrapping(*data_len as u64);
|
||||
}
|
||||
write_ops += Wrapping(1);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to parse available descriptor chain: {:?}", e);
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
used_desc_heads.push((avail_desc.index, len));
|
||||
|
||||
(VIRTIO_BLK_S_OK, result as u32)
|
||||
} else {
|
||||
error!(
|
||||
"Request failed: {:?}",
|
||||
io::Error::from_raw_os_error(-result)
|
||||
);
|
||||
return Err(Error::AsyncRequestFailure);
|
||||
};
|
||||
|
||||
// We use unwrap because the request parsing process already
|
||||
// checked that the status_addr was valid.
|
||||
mem.write_obj(status, request.status_addr).unwrap();
|
||||
|
||||
used_desc_heads.push((desc_index as u16, len));
|
||||
used_count += 1;
|
||||
}
|
||||
|
||||
@@ -174,7 +222,7 @@ impl<T: DiskFile> BlockEpollHandler<T> {
|
||||
.read_ops
|
||||
.fetch_add(read_ops.0, Ordering::AcqRel);
|
||||
|
||||
used_count > 0
|
||||
Ok(used_count > 0)
|
||||
}
|
||||
|
||||
fn signal_used_queue(&self) -> result::Result<(), DeviceError> {
|
||||
@@ -186,21 +234,6 @@ impl<T: DiskFile> BlockEpollHandler<T> {
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn update_disk_image(
|
||||
&mut self,
|
||||
mut disk_image: T,
|
||||
disk_path: &PathBuf,
|
||||
) -> result::Result<(), DeviceError> {
|
||||
self.disk_nsectors = disk_image
|
||||
.seek(SeekFrom::End(0))
|
||||
.map_err(DeviceError::IoError)?
|
||||
/ SECTOR_SIZE;
|
||||
self.disk_image_id = build_disk_image_id(disk_path);
|
||||
self.disk_image = Arc::new(Mutex::new(disk_image));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
paused: Arc<AtomicBool>,
|
||||
@@ -208,13 +241,14 @@ impl<T: DiskFile> BlockEpollHandler<T> {
|
||||
) -> result::Result<(), EpollHelperError> {
|
||||
let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?;
|
||||
helper.add_event(self.queue_evt.as_raw_fd(), QUEUE_AVAIL_EVENT)?;
|
||||
helper.add_event(self.disk_image.notifier().as_raw_fd(), COMPLETION_EVENT)?;
|
||||
helper.run(paused, paused_sync, self)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: DiskFile> EpollHelperHandler for BlockEpollHandler<T> {
|
||||
impl EpollHelperHandler for BlockEpollHandler {
|
||||
fn handle_event(&mut self, _helper: &mut EpollHelper, event: &epoll::Event) -> bool {
|
||||
let ev_type = event.data as u16;
|
||||
match ev_type {
|
||||
@@ -222,31 +256,40 @@ impl<T: DiskFile> EpollHelperHandler for BlockEpollHandler<T> {
|
||||
if let Err(e) = self.queue_evt.read() {
|
||||
error!("Failed to get queue event: {:?}", e);
|
||||
return true;
|
||||
} else if self.event_idx {
|
||||
// vm-virtio's Queue implementation only checks avail_index
|
||||
// once, so to properly support EVENT_IDX we need to keep
|
||||
// calling process_queue() until it stops finding new
|
||||
// requests on the queue.
|
||||
loop {
|
||||
if self.process_queue() {
|
||||
self.queue.update_avail_event(&self.mem.memory());
|
||||
}
|
||||
|
||||
if self
|
||||
.queue
|
||||
.needs_notification(&self.mem.memory(), self.queue.next_used)
|
||||
{
|
||||
if let Err(e) = self.signal_used_queue() {
|
||||
error!("Failed to signal used queue: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
match self.process_queue_submit() {
|
||||
Ok(needs_notification) => {
|
||||
if needs_notification {
|
||||
if let Err(e) = self.signal_used_queue() {
|
||||
error!("Failed to signal used queue: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if self.process_queue() {
|
||||
if let Err(e) = self.signal_used_queue() {
|
||||
error!("Failed to signal used queue: {:?}", e);
|
||||
Err(e) => {
|
||||
error!("Failed to process queue (submit): {:?}", e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
COMPLETION_EVENT => {
|
||||
if let Err(e) = self.disk_image.notifier().read() {
|
||||
error!("Failed to get queue event: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
|
||||
match self.process_queue_complete() {
|
||||
Ok(needs_notification) => {
|
||||
if needs_notification {
|
||||
if let Err(e) = self.signal_used_queue() {
|
||||
error!("Failed to signal used queue: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to process queue (complete): {:?}", e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -261,10 +304,10 @@ impl<T: DiskFile> EpollHelperHandler for BlockEpollHandler<T> {
|
||||
}
|
||||
|
||||
/// Virtio device for exposing block level read/write operations on a host file.
|
||||
pub struct Block<T: DiskFile> {
|
||||
pub struct Block {
|
||||
common: VirtioCommon,
|
||||
id: String,
|
||||
disk_image: Arc<Mutex<T>>,
|
||||
disk_image: Box<dyn DiskFile>,
|
||||
disk_path: PathBuf,
|
||||
disk_nsectors: u64,
|
||||
config: VirtioBlockConfig,
|
||||
@@ -282,22 +325,25 @@ pub struct BlockState {
|
||||
pub config: VirtioBlockConfig,
|
||||
}
|
||||
|
||||
impl<T: DiskFile> Block<T> {
|
||||
impl Block {
|
||||
/// Create a new virtio block device that operates on the given file.
|
||||
///
|
||||
/// The given file must be seekable and sizable.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
id: String,
|
||||
mut disk_image: T,
|
||||
mut disk_image: Box<dyn DiskFile>,
|
||||
disk_path: PathBuf,
|
||||
is_disk_read_only: bool,
|
||||
iommu: bool,
|
||||
num_queues: usize,
|
||||
queue_size: u16,
|
||||
seccomp_action: SeccompAction,
|
||||
) -> io::Result<Block<T>> {
|
||||
let disk_size = disk_image.seek(SeekFrom::End(0))? as u64;
|
||||
) -> io::Result<Self> {
|
||||
let disk_size = disk_image.size().map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("Failed getting disk size: {}", e),
|
||||
)
|
||||
})?;
|
||||
if disk_size % SECTOR_SIZE != 0 {
|
||||
warn!(
|
||||
"Disk size {} is not a multiple of sector size {}; \
|
||||
@@ -308,7 +354,6 @@ impl<T: DiskFile> Block<T> {
|
||||
|
||||
let mut avail_features = (1u64 << VIRTIO_F_VERSION_1)
|
||||
| (1u64 << VIRTIO_BLK_F_FLUSH)
|
||||
| (1u64 << VIRTIO_RING_F_EVENT_IDX)
|
||||
| (1u64 << VIRTIO_BLK_F_CONFIG_WCE);
|
||||
|
||||
if iommu {
|
||||
@@ -337,10 +382,11 @@ impl<T: DiskFile> Block<T> {
|
||||
avail_features,
|
||||
paused_sync: Some(Arc::new(Barrier::new(num_queues + 1))),
|
||||
queue_sizes: vec![queue_size; num_queues],
|
||||
min_queues: 1,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
disk_image: Arc::new(Mutex::new(disk_image)),
|
||||
disk_image,
|
||||
disk_path,
|
||||
disk_nsectors,
|
||||
config,
|
||||
@@ -360,14 +406,12 @@ impl<T: DiskFile> Block<T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &BlockState) -> io::Result<()> {
|
||||
fn set_state(&mut self, state: &BlockState) {
|
||||
self.disk_path = state.disk_path.clone();
|
||||
self.disk_nsectors = state.disk_nsectors;
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
self.config = state.config;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_writeback(&mut self) {
|
||||
@@ -391,13 +435,22 @@ impl<T: DiskFile> Block<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static + DiskFile + Send> VirtioDevice for Block<T> {
|
||||
impl Drop for Block {
|
||||
fn drop(&mut self) {
|
||||
if let Some(kill_evt) = self.common.kill_evt.take() {
|
||||
// Ignore the result because there is nothing we can do about it.
|
||||
let _ = kill_evt.write(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioDevice for Block {
|
||||
fn device_type(&self) -> u32 {
|
||||
self.common.device_type
|
||||
}
|
||||
|
||||
fn queue_max_sizes(&self) -> &[u16] {
|
||||
self.common.queue_sizes.as_slice()
|
||||
&self.common.queue_sizes
|
||||
}
|
||||
|
||||
fn features(&self) -> u64 {
|
||||
@@ -440,12 +493,13 @@ impl<T: 'static + DiskFile + Send> VirtioDevice for Block<T> {
|
||||
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
|
||||
|
||||
let disk_image_id = build_disk_image_id(&self.disk_path);
|
||||
let event_idx = self.common.feature_acked(VIRTIO_RING_F_EVENT_IDX.into());
|
||||
self.update_writeback();
|
||||
|
||||
let mut epoll_threads = Vec::new();
|
||||
for _ in 0..self.common.queue_sizes.len() {
|
||||
for i in 0..queues.len() {
|
||||
let queue_evt = queue_evts.remove(0);
|
||||
let queue = queues.remove(0);
|
||||
let queue_size = queue.size;
|
||||
let kill_evt = self
|
||||
.common
|
||||
.kill_evt
|
||||
@@ -466,35 +520,40 @@ impl<T: 'static + DiskFile + Send> VirtioDevice for Block<T> {
|
||||
error!("failed to clone pause_evt eventfd: {}", e);
|
||||
ActivateError::BadActivate
|
||||
})?;
|
||||
|
||||
let mut handler = BlockEpollHandler {
|
||||
queue: queues.remove(0),
|
||||
queue,
|
||||
mem: mem.clone(),
|
||||
disk_image: self.disk_image.clone(),
|
||||
disk_image: self
|
||||
.disk_image
|
||||
.new_async_io(queue_size as u32)
|
||||
.map_err(|e| {
|
||||
error!("failed to create new AsyncIo: {}", e);
|
||||
ActivateError::BadActivate
|
||||
})?,
|
||||
disk_nsectors: self.disk_nsectors,
|
||||
interrupt_cb: interrupt_cb.clone(),
|
||||
disk_image_id: disk_image_id.clone(),
|
||||
kill_evt,
|
||||
pause_evt,
|
||||
event_idx,
|
||||
writeback: self.writeback.clone(),
|
||||
counters: self.counters.clone(),
|
||||
queue_evt,
|
||||
request_list: HashMap::with_capacity(queue_size.into()),
|
||||
};
|
||||
|
||||
handler.queue.set_event_idx(event_idx);
|
||||
|
||||
let paused = self.common.paused.clone();
|
||||
let paused_sync = self.common.paused_sync.clone();
|
||||
|
||||
// Retrieve seccomp filter for virtio_blk thread
|
||||
let virtio_blk_seccomp_filter =
|
||||
get_seccomp_filter(&self.seccomp_action, Thread::VirtioBlk)
|
||||
// Retrieve seccomp filter for virtio_block thread
|
||||
let virtio_block_seccomp_filter =
|
||||
get_seccomp_filter(&self.seccomp_action, Thread::VirtioBlock)
|
||||
.map_err(ActivateError::CreateSeccompFilter)?;
|
||||
|
||||
thread::Builder::new()
|
||||
.name("virtio_blk".to_string())
|
||||
.name(format!("{}_q{}", self.id.clone(), i))
|
||||
.spawn(move || {
|
||||
if let Err(e) = SeccompFilter::apply(virtio_blk_seccomp_filter) {
|
||||
if let Err(e) = SeccompFilter::apply(virtio_block_seccomp_filter) {
|
||||
error!("Error applying seccomp filter: {:?}", e);
|
||||
} else if let Err(e) = handler.run(paused, paused_sync.unwrap()) {
|
||||
error!("Error running worker: {:?}", e);
|
||||
@@ -502,7 +561,7 @@ impl<T: 'static + DiskFile + Send> VirtioDevice for Block<T> {
|
||||
})
|
||||
.map(|thread| epoll_threads.push(thread))
|
||||
.map_err(|e| {
|
||||
error!("failed to clone the virtio-blk epoll thread: {}", e);
|
||||
error!("failed to clone the virtio-block epoll thread: {}", e);
|
||||
ActivateError::BadActivate
|
||||
})?;
|
||||
}
|
||||
@@ -512,7 +571,7 @@ impl<T: 'static + DiskFile + Send> VirtioDevice for Block<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> Option<(Arc<dyn VirtioInterrupt>, Vec<EventFd>)> {
|
||||
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
||||
self.common.reset()
|
||||
}
|
||||
|
||||
@@ -540,16 +599,7 @@ impl<T: 'static + DiskFile + Send> VirtioDevice for Block<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: DiskFile> Drop for Block<T> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(kill_evt) = self.common.kill_evt.take() {
|
||||
// Ignore the result because there is nothing we can do about it.
|
||||
let _ = kill_evt.write(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static + DiskFile + Send> Pausable for Block<T> {
|
||||
impl Pausable for Block {
|
||||
fn pause(&mut self) -> result::Result<(), MigratableError> {
|
||||
self.common.pause()
|
||||
}
|
||||
@@ -559,7 +609,7 @@ impl<T: 'static + DiskFile + Send> Pausable for Block<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static + DiskFile + Send> Snapshottable for Block<T> {
|
||||
impl Snapshottable for Block {
|
||||
fn id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -589,9 +639,8 @@ impl<T: 'static + DiskFile + Send> Snapshottable for Block<T> {
|
||||
}
|
||||
};
|
||||
|
||||
return self.set_state(&block_state).map_err(|e| {
|
||||
MigratableError::Restore(anyhow!("Could not restore BLOCK state {:?}", e))
|
||||
});
|
||||
self.set_state(&block_state);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(MigratableError::Restore(anyhow!(
|
||||
@@ -599,5 +648,5 @@ impl<T: 'static + DiskFile + Send> Snapshottable for Block<T> {
|
||||
)))
|
||||
}
|
||||
}
|
||||
impl<T: 'static + DiskFile + Send> Transportable for Block<T> {}
|
||||
impl<T: 'static + DiskFile + Send> Migratable for Block<T> {}
|
||||
impl Transportable for Block {}
|
||||
impl Migratable for Block {}
|
||||
|
||||
@@ -1,663 +0,0 @@
|
||||
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
//
|
||||
// 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 LICENSE-BSD-3-Clause file.
|
||||
//
|
||||
// Copyright © 2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
use super::Error as DeviceError;
|
||||
use super::{
|
||||
ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, Queue,
|
||||
VirtioCommon, VirtioDevice, VirtioDeviceType, VirtioInterruptType, EPOLL_HELPER_EVENT_LAST,
|
||||
};
|
||||
use crate::seccomp_filters::{get_seccomp_filter, Thread};
|
||||
use crate::VirtioInterrupt;
|
||||
use anyhow::anyhow;
|
||||
use block_util::{build_disk_image_id, Request, RequestType, VirtioBlockConfig};
|
||||
use io_uring::IoUring;
|
||||
use libc::EFD_NONBLOCK;
|
||||
use seccomp::{SeccompAction, SeccompFilter};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Seek, SeekFrom};
|
||||
use std::num::Wrapping;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::path::PathBuf;
|
||||
use std::result;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::thread;
|
||||
use virtio_bindings::bindings::virtio_blk::*;
|
||||
use vm_memory::{
|
||||
ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryError,
|
||||
GuestMemoryMmap,
|
||||
};
|
||||
use vm_migration::{
|
||||
Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable,
|
||||
Transportable,
|
||||
};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
const SECTOR_SHIFT: u8 = 9;
|
||||
pub const SECTOR_SIZE: u64 = (0x01 as u64) << SECTOR_SHIFT;
|
||||
|
||||
// New descriptors are pending on the virtio queue.
|
||||
const QUEUE_AVAIL_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
|
||||
// New completed tasks are pending on the completion ring.
|
||||
const IO_URING_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Guest gave us bad memory addresses.
|
||||
GuestMemory(GuestMemoryError),
|
||||
/// Guest gave us offsets that would have overflowed a usize.
|
||||
CheckedOffset(GuestAddress, usize),
|
||||
/// Guest gave us a write only descriptor that protocol says to read from.
|
||||
UnexpectedWriteOnlyDescriptor,
|
||||
/// Guest gave us a read only descriptor that protocol says to write to.
|
||||
UnexpectedReadOnlyDescriptor,
|
||||
/// Guest gave us too few descriptors in a descriptor chain.
|
||||
DescriptorChainTooShort,
|
||||
/// Guest gave us a descriptor that was too short to use.
|
||||
DescriptorLengthTooSmall,
|
||||
/// Getting a block's metadata fails for any reason.
|
||||
GetFileMetadata,
|
||||
/// The requested operation would cause a seek beyond disk end.
|
||||
InvalidOffset,
|
||||
/// Unsupported operation on the disk.
|
||||
Unsupported(u32),
|
||||
/// Failed to parse the request.
|
||||
RequestParsing(block_util::Error),
|
||||
/// Failed to execute the request.
|
||||
RequestExecuting(block_util::ExecuteError),
|
||||
/// Missing the expected entry in the list of requests.
|
||||
MissingEntryRequestList,
|
||||
/// The asynchronous request returned with failure.
|
||||
AsyncRequestFailure,
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct BlockCounters {
|
||||
read_bytes: Arc<AtomicU64>,
|
||||
read_ops: Arc<AtomicU64>,
|
||||
write_bytes: Arc<AtomicU64>,
|
||||
write_ops: Arc<AtomicU64>,
|
||||
}
|
||||
|
||||
struct BlockIoUringEpollHandler {
|
||||
queue: Queue,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
disk_image_fd: RawFd,
|
||||
disk_nsectors: u64,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
disk_image_id: Vec<u8>,
|
||||
kill_evt: EventFd,
|
||||
pause_evt: EventFd,
|
||||
writeback: Arc<AtomicBool>,
|
||||
counters: BlockCounters,
|
||||
queue_evt: EventFd,
|
||||
io_uring: IoUring,
|
||||
io_uring_evt: EventFd,
|
||||
request_list: HashMap<u16, Request>,
|
||||
}
|
||||
|
||||
impl BlockIoUringEpollHandler {
|
||||
fn process_queue_submit(&mut self) -> Result<bool> {
|
||||
let queue = &mut self.queue;
|
||||
let mem = self.mem.memory();
|
||||
|
||||
let mut used_desc_heads = Vec::new();
|
||||
let mut used_count = 0;
|
||||
|
||||
for avail_desc in queue.iter(&mem) {
|
||||
let mut request = Request::parse(&avail_desc, &mem).map_err(Error::RequestParsing)?;
|
||||
request.set_writeback(self.writeback.load(Ordering::Acquire));
|
||||
if request
|
||||
.execute_io_uring(
|
||||
&mem,
|
||||
&mut self.io_uring,
|
||||
self.disk_nsectors,
|
||||
self.disk_image_fd,
|
||||
&self.disk_image_id,
|
||||
avail_desc.index as u64,
|
||||
)
|
||||
.map_err(Error::RequestExecuting)?
|
||||
{
|
||||
self.request_list.insert(avail_desc.index, request);
|
||||
} else {
|
||||
// We use unwrap because the request parsing process already
|
||||
// checked that the status_addr was valid.
|
||||
mem.write_obj(VIRTIO_BLK_S_OK, request.status_addr).unwrap();
|
||||
|
||||
// If no asynchronous operation has been submitted, we can
|
||||
// simply return the used descriptor.
|
||||
used_desc_heads.push((avail_desc.index, 0));
|
||||
used_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for &(desc_index, len) in used_desc_heads.iter() {
|
||||
queue.add_used(&mem, desc_index, len);
|
||||
}
|
||||
|
||||
Ok(used_count > 0)
|
||||
}
|
||||
|
||||
fn process_queue_complete(&mut self) -> Result<bool> {
|
||||
let queue = &mut self.queue;
|
||||
|
||||
let mut used_desc_heads = Vec::new();
|
||||
let mut used_count = 0;
|
||||
let mem = self.mem.memory();
|
||||
let mut read_bytes = Wrapping(0);
|
||||
let mut write_bytes = Wrapping(0);
|
||||
let mut read_ops = Wrapping(0);
|
||||
let mut write_ops = Wrapping(0);
|
||||
|
||||
let cq = self.io_uring.completion();
|
||||
for cq_entry in cq.available() {
|
||||
let result = cq_entry.result();
|
||||
let desc_index = cq_entry.user_data() as u16;
|
||||
let request = self
|
||||
.request_list
|
||||
.remove(&desc_index)
|
||||
.ok_or(Error::MissingEntryRequestList)?;
|
||||
|
||||
let (status, len) = if result >= 0 {
|
||||
match request.request_type {
|
||||
RequestType::In => {
|
||||
for (_, data_len) in &request.data_descriptors {
|
||||
read_bytes += Wrapping(*data_len as u64);
|
||||
}
|
||||
read_ops += Wrapping(1);
|
||||
}
|
||||
RequestType::Out => {
|
||||
if !request.writeback {
|
||||
unsafe { libc::fsync(self.disk_image_fd) };
|
||||
}
|
||||
for (_, data_len) in &request.data_descriptors {
|
||||
write_bytes += Wrapping(*data_len as u64);
|
||||
}
|
||||
write_ops += Wrapping(1);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
(VIRTIO_BLK_S_OK, result as u32)
|
||||
} else {
|
||||
error!(
|
||||
"Request failed: {:?}",
|
||||
io::Error::from_raw_os_error(-result)
|
||||
);
|
||||
return Err(Error::AsyncRequestFailure);
|
||||
};
|
||||
|
||||
// We use unwrap because the request parsing process already
|
||||
// checked that the status_addr was valid.
|
||||
mem.write_obj(status, request.status_addr).unwrap();
|
||||
|
||||
used_desc_heads.push((desc_index, len));
|
||||
used_count += 1;
|
||||
}
|
||||
|
||||
for &(desc_index, len) in used_desc_heads.iter() {
|
||||
queue.add_used(&mem, desc_index, len);
|
||||
}
|
||||
|
||||
self.counters
|
||||
.write_bytes
|
||||
.fetch_add(write_bytes.0, Ordering::AcqRel);
|
||||
self.counters
|
||||
.write_ops
|
||||
.fetch_add(write_ops.0, Ordering::AcqRel);
|
||||
|
||||
self.counters
|
||||
.read_bytes
|
||||
.fetch_add(read_bytes.0, Ordering::AcqRel);
|
||||
self.counters
|
||||
.read_ops
|
||||
.fetch_add(read_ops.0, Ordering::AcqRel);
|
||||
|
||||
Ok(used_count > 0)
|
||||
}
|
||||
|
||||
fn signal_used_queue(&self) -> result::Result<(), DeviceError> {
|
||||
self.interrupt_cb
|
||||
.trigger(&VirtioInterruptType::Queue, Some(&self.queue))
|
||||
.map_err(|e| {
|
||||
error!("Failed to signal used queue: {:?}", e);
|
||||
DeviceError::FailedSignalingUsedQueue(e)
|
||||
})
|
||||
}
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
paused: Arc<AtomicBool>,
|
||||
paused_sync: Arc<Barrier>,
|
||||
) -> result::Result<(), EpollHelperError> {
|
||||
let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?;
|
||||
helper.add_event(self.queue_evt.as_raw_fd(), QUEUE_AVAIL_EVENT)?;
|
||||
helper.add_event(self.io_uring_evt.as_raw_fd(), IO_URING_EVENT)?;
|
||||
helper.run(paused, paused_sync, self)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl EpollHelperHandler for BlockIoUringEpollHandler {
|
||||
fn handle_event(&mut self, _helper: &mut EpollHelper, event: &epoll::Event) -> bool {
|
||||
let ev_type = event.data as u16;
|
||||
match ev_type {
|
||||
QUEUE_AVAIL_EVENT => {
|
||||
if let Err(e) = self.queue_evt.read() {
|
||||
error!("Failed to get queue event: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
|
||||
match self.process_queue_submit() {
|
||||
Ok(needs_notification) => {
|
||||
if needs_notification {
|
||||
if let Err(e) = self.signal_used_queue() {
|
||||
error!("Failed to signal used queue: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to process queue (submit): {:?}", e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
IO_URING_EVENT => {
|
||||
if let Err(e) = self.io_uring_evt.read() {
|
||||
error!("Failed to get queue event: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
|
||||
match self.process_queue_complete() {
|
||||
Ok(needs_notification) => {
|
||||
if needs_notification {
|
||||
if let Err(e) = self.signal_used_queue() {
|
||||
error!("Failed to signal used queue: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to process queue (complete): {:?}", e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("Unexpected event: {}", ev_type);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Virtio device for exposing block level read/write operations on a host file.
|
||||
pub struct BlockIoUring {
|
||||
common: VirtioCommon,
|
||||
id: String,
|
||||
disk_image: File,
|
||||
disk_path: PathBuf,
|
||||
disk_nsectors: u64,
|
||||
config: VirtioBlockConfig,
|
||||
writeback: Arc<AtomicBool>,
|
||||
counters: BlockCounters,
|
||||
seccomp_action: SeccompAction,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct BlockState {
|
||||
pub disk_path: PathBuf,
|
||||
pub disk_nsectors: u64,
|
||||
pub avail_features: u64,
|
||||
pub acked_features: u64,
|
||||
pub config: VirtioBlockConfig,
|
||||
}
|
||||
|
||||
impl BlockIoUring {
|
||||
/// Create a new virtio block device that operates on the given file.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
id: String,
|
||||
mut disk_image: File,
|
||||
disk_path: PathBuf,
|
||||
is_disk_read_only: bool,
|
||||
iommu: bool,
|
||||
num_queues: usize,
|
||||
queue_size: u16,
|
||||
seccomp_action: SeccompAction,
|
||||
) -> io::Result<Self> {
|
||||
let disk_size = disk_image.seek(SeekFrom::End(0))? as u64;
|
||||
if disk_size % SECTOR_SIZE != 0 {
|
||||
warn!(
|
||||
"Disk size {} is not a multiple of sector size {}; \
|
||||
the remainder will not be visible to the guest.",
|
||||
disk_size, SECTOR_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
let mut avail_features = (1u64 << VIRTIO_F_VERSION_1)
|
||||
| (1u64 << VIRTIO_BLK_F_FLUSH)
|
||||
| (1u64 << VIRTIO_BLK_F_CONFIG_WCE);
|
||||
|
||||
if iommu {
|
||||
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
|
||||
}
|
||||
|
||||
if is_disk_read_only {
|
||||
avail_features |= 1u64 << VIRTIO_BLK_F_RO;
|
||||
}
|
||||
|
||||
let disk_nsectors = disk_size / SECTOR_SIZE;
|
||||
let mut config = VirtioBlockConfig {
|
||||
capacity: disk_nsectors,
|
||||
writeback: 1,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if num_queues > 1 {
|
||||
avail_features |= 1u64 << VIRTIO_BLK_F_MQ;
|
||||
config.num_queues = num_queues as u16;
|
||||
}
|
||||
|
||||
Ok(BlockIoUring {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::TYPE_BLOCK as u32,
|
||||
avail_features,
|
||||
paused_sync: Some(Arc::new(Barrier::new(num_queues + 1))),
|
||||
queue_sizes: vec![queue_size; num_queues],
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
disk_image,
|
||||
disk_path,
|
||||
disk_nsectors,
|
||||
config,
|
||||
writeback: Arc::new(AtomicBool::new(true)),
|
||||
counters: BlockCounters::default(),
|
||||
seccomp_action,
|
||||
})
|
||||
}
|
||||
|
||||
fn state(&self) -> BlockState {
|
||||
BlockState {
|
||||
disk_path: self.disk_path.clone(),
|
||||
disk_nsectors: self.disk_nsectors,
|
||||
avail_features: self.common.avail_features,
|
||||
acked_features: self.common.acked_features,
|
||||
config: self.config,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &BlockState) -> io::Result<()> {
|
||||
self.disk_path = state.disk_path.clone();
|
||||
self.disk_nsectors = state.disk_nsectors;
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
self.config = state.config;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_writeback(&mut self) {
|
||||
// Use writeback from config if VIRTIO_BLK_F_CONFIG_WCE
|
||||
let writeback = if self.common.feature_acked(VIRTIO_BLK_F_CONFIG_WCE.into()) {
|
||||
self.config.writeback == 1
|
||||
} else {
|
||||
// Else check if VIRTIO_BLK_F_FLUSH negotiated
|
||||
self.common.feature_acked(VIRTIO_BLK_F_FLUSH.into())
|
||||
};
|
||||
|
||||
info!(
|
||||
"Changing cache mode to {}",
|
||||
if writeback {
|
||||
"writeback"
|
||||
} else {
|
||||
"writethrough"
|
||||
}
|
||||
);
|
||||
self.writeback.store(writeback, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BlockIoUring {
|
||||
fn drop(&mut self) {
|
||||
if let Some(kill_evt) = self.common.kill_evt.take() {
|
||||
// Ignore the result because there is nothing we can do about it.
|
||||
let _ = kill_evt.write(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioDevice for BlockIoUring {
|
||||
fn device_type(&self) -> u32 {
|
||||
self.common.device_type
|
||||
}
|
||||
|
||||
fn queue_max_sizes(&self) -> &[u16] {
|
||||
&self.common.queue_sizes
|
||||
}
|
||||
|
||||
fn features(&self) -> u64 {
|
||||
self.common.avail_features
|
||||
}
|
||||
|
||||
fn ack_features(&mut self, value: u64) {
|
||||
self.common.ack_features(value)
|
||||
}
|
||||
|
||||
fn read_config(&self, offset: u64, data: &mut [u8]) {
|
||||
self.read_config_from_slice(self.config.as_slice(), offset, data);
|
||||
}
|
||||
|
||||
fn write_config(&mut self, offset: u64, data: &[u8]) {
|
||||
// The "writeback" field is the only mutable field
|
||||
let writeback_offset =
|
||||
(&self.config.writeback as *const _ as u64) - (&self.config as *const _ as u64);
|
||||
if offset != writeback_offset || data.len() != std::mem::size_of_val(&self.config.writeback)
|
||||
{
|
||||
error!(
|
||||
"Attempt to write to read-only field: offset {:x} length {}",
|
||||
offset,
|
||||
data.len()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
self.config.writeback = data[0];
|
||||
self.update_writeback();
|
||||
}
|
||||
|
||||
fn activate(
|
||||
&mut self,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
mut queues: Vec<Queue>,
|
||||
mut queue_evts: Vec<EventFd>,
|
||||
) -> ActivateResult {
|
||||
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
|
||||
|
||||
let disk_image_id = build_disk_image_id(&self.disk_path);
|
||||
self.update_writeback();
|
||||
|
||||
let mut epoll_threads = Vec::new();
|
||||
for i in 0..self.common.queue_sizes.len() {
|
||||
let queue_size = self.common.queue_sizes[i] as usize;
|
||||
let queue_evt = queue_evts.remove(0);
|
||||
let io_uring = IoUring::new(queue_size as u32).map_err(|e| {
|
||||
error!("failed to create io_uring instance: {}", e);
|
||||
ActivateError::BadActivate
|
||||
})?;
|
||||
let kill_evt = self
|
||||
.common
|
||||
.kill_evt
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.try_clone()
|
||||
.map_err(|e| {
|
||||
error!("failed to clone kill_evt eventfd: {}", e);
|
||||
ActivateError::BadActivate
|
||||
})?;
|
||||
let pause_evt = self
|
||||
.common
|
||||
.pause_evt
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.try_clone()
|
||||
.map_err(|e| {
|
||||
error!("failed to clone pause_evt eventfd: {}", e);
|
||||
ActivateError::BadActivate
|
||||
})?;
|
||||
|
||||
let mut handler = BlockIoUringEpollHandler {
|
||||
queue: queues.remove(0),
|
||||
mem: mem.clone(),
|
||||
disk_image_fd: self.disk_image.as_raw_fd(),
|
||||
disk_nsectors: self.disk_nsectors,
|
||||
interrupt_cb: interrupt_cb.clone(),
|
||||
disk_image_id: disk_image_id.clone(),
|
||||
kill_evt,
|
||||
pause_evt,
|
||||
writeback: self.writeback.clone(),
|
||||
counters: self.counters.clone(),
|
||||
queue_evt,
|
||||
io_uring,
|
||||
io_uring_evt: EventFd::new(EFD_NONBLOCK).map_err(|e| {
|
||||
error!("failed to create io_uring eventfd: {}", e);
|
||||
ActivateError::BadActivate
|
||||
})?,
|
||||
request_list: HashMap::with_capacity(queue_size),
|
||||
};
|
||||
|
||||
let paused = self.common.paused.clone();
|
||||
let paused_sync = self.common.paused_sync.clone();
|
||||
|
||||
// Register the io_uring eventfd that will notify the epoll loop
|
||||
// when something in the completion queue is ready.
|
||||
handler
|
||||
.io_uring
|
||||
.submitter()
|
||||
.register_eventfd(handler.io_uring_evt.as_raw_fd())
|
||||
.map_err(|e| {
|
||||
error!("failed to register eventfd for io_uring: {}", e);
|
||||
ActivateError::BadActivate
|
||||
})?;
|
||||
|
||||
// Retrieve seccomp filter for virtio_blk_io_uring thread
|
||||
let virtio_blk_io_uring_seccomp_filter =
|
||||
get_seccomp_filter(&self.seccomp_action, Thread::VirtioBlkIoUring)
|
||||
.map_err(ActivateError::CreateSeccompFilter)?;
|
||||
|
||||
thread::Builder::new()
|
||||
.name("virtio_blk_io_uring".to_string())
|
||||
.spawn(move || {
|
||||
if let Err(e) = SeccompFilter::apply(virtio_blk_io_uring_seccomp_filter) {
|
||||
error!("Error applying seccomp filter: {:?}", e);
|
||||
} else if let Err(e) = handler.run(paused, paused_sync.unwrap()) {
|
||||
error!("Error running worker: {:?}", e);
|
||||
}
|
||||
})
|
||||
.map(|thread| epoll_threads.push(thread))
|
||||
.map_err(|e| {
|
||||
error!("failed to clone the virtio-blk epoll thread: {}", e);
|
||||
ActivateError::BadActivate
|
||||
})?;
|
||||
}
|
||||
|
||||
self.common.epoll_threads = Some(epoll_threads);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> Option<(Arc<dyn VirtioInterrupt>, Vec<EventFd>)> {
|
||||
self.common.reset()
|
||||
}
|
||||
|
||||
fn counters(&self) -> Option<HashMap<&'static str, Wrapping<u64>>> {
|
||||
let mut counters = HashMap::new();
|
||||
|
||||
counters.insert(
|
||||
"read_bytes",
|
||||
Wrapping(self.counters.read_bytes.load(Ordering::Acquire)),
|
||||
);
|
||||
counters.insert(
|
||||
"write_bytes",
|
||||
Wrapping(self.counters.write_bytes.load(Ordering::Acquire)),
|
||||
);
|
||||
counters.insert(
|
||||
"read_ops",
|
||||
Wrapping(self.counters.read_ops.load(Ordering::Acquire)),
|
||||
);
|
||||
counters.insert(
|
||||
"write_ops",
|
||||
Wrapping(self.counters.write_ops.load(Ordering::Acquire)),
|
||||
);
|
||||
|
||||
Some(counters)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pausable for BlockIoUring {
|
||||
fn pause(&mut self) -> result::Result<(), MigratableError> {
|
||||
self.common.pause()
|
||||
}
|
||||
|
||||
fn resume(&mut self) -> result::Result<(), MigratableError> {
|
||||
self.common.resume()
|
||||
}
|
||||
}
|
||||
|
||||
impl Snapshottable for BlockIoUring {
|
||||
fn id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
let snapshot =
|
||||
serde_json::to_vec(&self.state()).map_err(|e| MigratableError::Snapshot(e.into()))?;
|
||||
|
||||
let mut block_snapshot = Snapshot::new(self.id.as_str());
|
||||
block_snapshot.add_data_section(SnapshotDataSection {
|
||||
id: format!("{}-section", self.id),
|
||||
snapshot,
|
||||
});
|
||||
|
||||
Ok(block_snapshot)
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
if let Some(block_section) = snapshot.snapshot_data.get(&format!("{}-section", self.id)) {
|
||||
let block_state = match serde_json::from_slice(&block_section.snapshot) {
|
||||
Ok(state) => state,
|
||||
Err(error) => {
|
||||
return Err(MigratableError::Restore(anyhow!(
|
||||
"Could not deserialize BLOCK {}",
|
||||
error
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
||||
return self.set_state(&block_state).map_err(|e| {
|
||||
MigratableError::Restore(anyhow!("Could not restore BLOCK state {:?}", e))
|
||||
});
|
||||
}
|
||||
|
||||
Err(MigratableError::Restore(anyhow!(
|
||||
"Could not find BLOCK snapshot section"
|
||||
)))
|
||||
}
|
||||
}
|
||||
impl Transportable for BlockIoUring {}
|
||||
impl Migratable for BlockIoUring {}
|
||||
@@ -348,6 +348,7 @@ impl Console {
|
||||
queue_sizes: QUEUE_SIZES.to_vec(),
|
||||
avail_features,
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
min_queues: NUM_QUEUES as u16,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
@@ -369,13 +370,11 @@ impl Console {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &ConsoleState) -> io::Result<()> {
|
||||
fn set_state(&mut self, state: &ConsoleState) {
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
*(self.config.lock().unwrap()) = state.config;
|
||||
*(self.input.in_buffer.lock().unwrap()) = state.in_buffer.clone();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,7 +469,7 @@ impl VirtioDevice for Console {
|
||||
get_seccomp_filter(&self.seccomp_action, Thread::VirtioConsole)
|
||||
.map_err(ActivateError::CreateSeccompFilter)?;
|
||||
thread::Builder::new()
|
||||
.name("virtio_console".to_string())
|
||||
.name(self.id.clone())
|
||||
.spawn(move || {
|
||||
if let Err(e) = SeccompFilter::apply(virtio_console_seccomp_filter) {
|
||||
error!("Error applying seccomp filter: {:?}", e);
|
||||
@@ -489,7 +488,7 @@ impl VirtioDevice for Console {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> Option<(Arc<dyn VirtioInterrupt>, Vec<EventFd>)> {
|
||||
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
||||
self.common.reset()
|
||||
}
|
||||
}
|
||||
@@ -534,9 +533,8 @@ impl Snapshottable for Console {
|
||||
}
|
||||
};
|
||||
|
||||
return self.set_state(&console_state).map_err(|e| {
|
||||
MigratableError::Restore(anyhow!("Could not restore CONSOLE state {:?}", e))
|
||||
});
|
||||
self.set_state(&console_state);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(MigratableError::Restore(anyhow!(
|
||||
|
||||
@@ -32,11 +32,7 @@ pub trait VirtioInterrupt: Send + Sync {
|
||||
int_type: &VirtioInterruptType,
|
||||
queue: Option<&Queue>,
|
||||
) -> std::result::Result<(), std::io::Error>;
|
||||
fn notifier(
|
||||
&self,
|
||||
_int_type: &VirtioInterruptType,
|
||||
_queue: Option<&Queue>,
|
||||
) -> Option<&EventFd> {
|
||||
fn notifier(&self, _int_type: &VirtioInterruptType, _queue: Option<&Queue>) -> Option<EventFd> {
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -116,7 +112,7 @@ pub trait VirtioDevice: Send {
|
||||
|
||||
/// Optionally deactivates this device and returns ownership of the guest memory map, interrupt
|
||||
/// event, and queue events.
|
||||
fn reset(&mut self) -> Option<(Arc<dyn VirtioInterrupt>, Vec<EventFd>)> {
|
||||
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -224,6 +220,7 @@ pub struct VirtioCommon {
|
||||
pub epoll_threads: Option<Vec<thread::JoinHandle<()>>>,
|
||||
pub queue_sizes: Vec<u16>,
|
||||
pub device_type: u32,
|
||||
pub min_queues: u16,
|
||||
}
|
||||
|
||||
impl VirtioCommon {
|
||||
@@ -250,15 +247,24 @@ impl VirtioCommon {
|
||||
queue_evts: &[EventFd],
|
||||
interrupt_cb: &Arc<dyn VirtioInterrupt>,
|
||||
) -> ActivateResult {
|
||||
if queues.len() != self.queue_sizes.len() || queue_evts.len() != self.queue_sizes.len() {
|
||||
if queues.len() != queue_evts.len() {
|
||||
error!(
|
||||
"Cannot perform activate. Expected {} queue(s), got {}",
|
||||
self.queue_sizes.len(),
|
||||
"Cannot activate: length mismatch: queue_evts={} queues={}",
|
||||
queue_evts.len(),
|
||||
queues.len()
|
||||
);
|
||||
return Err(ActivateError::BadActivate);
|
||||
}
|
||||
|
||||
if queues.len() < self.min_queues.into() {
|
||||
error!(
|
||||
"Number of enabled queues lower tham min: {} vs {}",
|
||||
queues.len(),
|
||||
self.min_queues
|
||||
);
|
||||
return Err(ActivateError::BadActivate);
|
||||
}
|
||||
|
||||
let kill_evt = EventFd::new(EFD_NONBLOCK).map_err(|e| {
|
||||
error!("failed creating kill EventFd: {}", e);
|
||||
ActivateError::BadActivate
|
||||
@@ -288,7 +294,7 @@ impl VirtioCommon {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) -> Option<(Arc<dyn VirtioInterrupt>, Vec<EventFd>)> {
|
||||
pub fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
||||
// We first must resume the virtio thread if it was paused.
|
||||
if self.pause_evt.take().is_some() {
|
||||
self.resume().ok()?;
|
||||
@@ -299,11 +305,16 @@ impl VirtioCommon {
|
||||
let _ = kill_evt.write(1);
|
||||
}
|
||||
|
||||
// Return the interrupt and queue EventFDs
|
||||
Some((
|
||||
self.interrupt_cb.take().unwrap(),
|
||||
self.queue_evts.take().unwrap(),
|
||||
))
|
||||
if let Some(mut threads) = self.epoll_threads.take() {
|
||||
for t in threads.drain(..) {
|
||||
if let Err(e) = t.join() {
|
||||
error!("Error joining thread: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the interrupt
|
||||
Some(self.interrupt_cb.take().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -127,10 +127,10 @@ struct VirtioIommuTopoPciRange {
|
||||
reserved: u8,
|
||||
length: u16,
|
||||
endpoint_start: u32,
|
||||
segment: u16,
|
||||
segment_start: u16,
|
||||
segment_end: u16,
|
||||
bdf_start: u16,
|
||||
bdf_end: u16,
|
||||
padding: u16,
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for VirtioIommuTopoPciRange {}
|
||||
@@ -796,13 +796,11 @@ impl Iommu {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &IommuState) -> io::Result<()> {
|
||||
fn set_state(&mut self, state: &IommuState) {
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
*(self.mapping.endpoints.write().unwrap()) = state.endpoints.clone();
|
||||
*(self.mapping.mappings.write().unwrap()) = state.mappings.clone();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// This function lets the caller specify a list of devices attached to the
|
||||
@@ -831,7 +829,8 @@ impl Iommu {
|
||||
type_: VIRTIO_IOMMU_TOPO_PCI_RANGE,
|
||||
length: size_of::<VirtioIommuTopoPciRange>() as u16,
|
||||
endpoint_start: dev_id,
|
||||
segment,
|
||||
segment_start: segment,
|
||||
segment_end: segment,
|
||||
bdf_start: dev_id as u16,
|
||||
bdf_end: dev_id as u16,
|
||||
..Default::default()
|
||||
@@ -933,7 +932,7 @@ impl VirtioDevice for Iommu {
|
||||
get_seccomp_filter(&self.seccomp_action, Thread::VirtioIommu)
|
||||
.map_err(ActivateError::CreateSeccompFilter)?;
|
||||
thread::Builder::new()
|
||||
.name("virtio_iommu".to_string())
|
||||
.name(self.id.clone())
|
||||
.spawn(move || {
|
||||
if let Err(e) = SeccompFilter::apply(virtio_iommu_seccomp_filter) {
|
||||
error!("Error applying seccomp filter: {:?}", e);
|
||||
@@ -952,7 +951,7 @@ impl VirtioDevice for Iommu {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> Option<(Arc<dyn VirtioInterrupt>, Vec<EventFd>)> {
|
||||
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
||||
self.common.reset()
|
||||
}
|
||||
}
|
||||
@@ -997,9 +996,8 @@ impl Snapshottable for Iommu {
|
||||
}
|
||||
};
|
||||
|
||||
return self.set_state(&iommu_state).map_err(|e| {
|
||||
MigratableError::Restore(anyhow!("Could not restore IOMMU state {:?}", e))
|
||||
});
|
||||
self.set_state(&iommu_state);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(MigratableError::Restore(anyhow!(
|
||||
|
||||
@@ -30,7 +30,6 @@ use std::io;
|
||||
mod device;
|
||||
pub mod balloon;
|
||||
pub mod block;
|
||||
pub mod block_io_uring;
|
||||
mod console;
|
||||
pub mod epoll_helper;
|
||||
mod iommu;
|
||||
@@ -47,7 +46,6 @@ pub mod watchdog;
|
||||
|
||||
pub use self::balloon::*;
|
||||
pub use self::block::*;
|
||||
pub use self::block_io_uring::*;
|
||||
pub use self::console::*;
|
||||
pub use self::device::*;
|
||||
pub use self::epoll_helper::*;
|
||||
|
||||
@@ -21,9 +21,9 @@ use super::{
|
||||
|
||||
use crate::seccomp_filters::{get_seccomp_filter, Thread};
|
||||
use crate::{VirtioInterrupt, VirtioInterruptType};
|
||||
use anyhow::anyhow;
|
||||
use libc::EFD_NONBLOCK;
|
||||
use seccomp::{SeccompAction, SeccompFilter};
|
||||
use std::cmp;
|
||||
use std::io;
|
||||
use std::mem::size_of;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
@@ -44,10 +44,9 @@ const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
|
||||
|
||||
// 128MiB is the standard memory block size in Linux. A virtio-mem region must
|
||||
// be aligned on this size, and the region size must be a multiple of it.
|
||||
pub const VIRTIO_MEM_ALIGN_SIZE: u64 = 128 * 1024 * 1024;
|
||||
pub const VIRTIO_MEM_ALIGN_SIZE: u64 = 128 << 20;
|
||||
// Use 2 MiB alignment so transparent hugepages can be used by KVM.
|
||||
const VIRTIO_MEM_DEFAULT_BLOCK_SIZE: u64 = 512 * 4096;
|
||||
const VIRTIO_MEM_USABLE_EXTENT: u64 = 256 * 1024 * 1024;
|
||||
const VIRTIO_MEM_DEFAULT_BLOCK_SIZE: u64 = 2 << 20;
|
||||
|
||||
// Request processed successfully, applicable for
|
||||
// - VIRTIO_MEM_REQ_PLUG
|
||||
@@ -64,7 +63,8 @@ const VIRTIO_MEM_RESP_NACK: u16 = 1;
|
||||
// - VIRTIO_MEM_REQ_PLUG
|
||||
// - VIRTIO_MEM_REQ_UNPLUG
|
||||
// - VIRTIO_MEM_REQ_UNPLUG_ALL
|
||||
// VIRTIO_MEM_RESP_BUSY: u16 = 2;
|
||||
#[allow(unused)]
|
||||
const VIRTIO_MEM_RESP_BUSY: u16 = 2;
|
||||
|
||||
// Error in request (e.g. addresses/alignment), applicable for
|
||||
// - VIRTIO_MEM_REQ_PLUG
|
||||
@@ -117,16 +117,15 @@ pub enum Error {
|
||||
// Failed to MpscRecv.
|
||||
MpscRecvFail(mpsc::RecvError),
|
||||
// Resize invalid argument
|
||||
ResizeInval(String),
|
||||
ResizeError(anyhow::Error),
|
||||
// Fail to resize trigger
|
||||
ResizeTriggerFail(DeviceError),
|
||||
// Invalid configuration
|
||||
ValidateError(anyhow::Error),
|
||||
// Failed discarding memory range
|
||||
DiscardMemoryRange(std::io::Error),
|
||||
}
|
||||
|
||||
// Got from qemu/include/standard-headers/linux/virtio_mem.h
|
||||
// rust union doesn't support std::default::Default that
|
||||
// need by mem.read_obj.
|
||||
// Then move virtio_mem_req_plug, virtio_mem_req_unplug and
|
||||
// virtio_mem_req_state to virtio_mem_req.
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
struct VirtioMemReq {
|
||||
@@ -134,31 +133,23 @@ struct VirtioMemReq {
|
||||
padding: [u16; 3],
|
||||
addr: u64,
|
||||
nb_blocks: u16,
|
||||
padding_1: [u16; 3],
|
||||
}
|
||||
|
||||
// Safe because it only has data and has no implicit padding.
|
||||
unsafe impl ByteValued for VirtioMemReq {}
|
||||
|
||||
// Got from qemu/include/standard-headers/linux/virtio_mem.h
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
struct VirtioMemRespState {
|
||||
state: u16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
struct VirtioMemResp {
|
||||
resp_type: u16,
|
||||
padding: [u16; 3],
|
||||
|
||||
state: VirtioMemRespState,
|
||||
state: u16,
|
||||
}
|
||||
|
||||
// Safe because it only has data and has no implicit padding.
|
||||
unsafe impl ByteValued for VirtioMemResp {}
|
||||
|
||||
// Got from qemu/include/standard-headers/linux/virtio_mem.h
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
struct VirtioMemConfig {
|
||||
@@ -185,34 +176,85 @@ struct VirtioMemConfig {
|
||||
// Safe because it only has data and has no implicit padding.
|
||||
unsafe impl ByteValued for VirtioMemConfig {}
|
||||
|
||||
fn virtio_mem_config_resize(config: &mut VirtioMemConfig, size: u64) -> result::Result<(), Error> {
|
||||
if config.requested_size == size {
|
||||
return Err(Error::ResizeInval(format!(
|
||||
"Virtio-mem resize {} is same with current config.requested_size",
|
||||
size
|
||||
)));
|
||||
} else if size > config.region_size {
|
||||
let region_size = config.region_size;
|
||||
return Err(Error::ResizeInval(format!(
|
||||
"Virtio-mem resize {} is bigger than config.region_size {}",
|
||||
size, region_size
|
||||
)));
|
||||
} else if size % (config.block_size as u64) != 0 {
|
||||
let block_size = config.block_size;
|
||||
return Err(Error::ResizeInval(format!(
|
||||
"Virtio-mem resize {} is not aligned with config.block_size {}",
|
||||
size, block_size
|
||||
)));
|
||||
impl VirtioMemConfig {
|
||||
fn validate(&self) -> result::Result<(), Error> {
|
||||
if self.addr % self.block_size != 0 {
|
||||
return Err(Error::ValidateError(anyhow!(
|
||||
"addr 0x{:x} is not aligned on block_size 0x{:x}",
|
||||
self.addr,
|
||||
self.block_size
|
||||
)));
|
||||
}
|
||||
if self.region_size % self.block_size != 0 {
|
||||
return Err(Error::ValidateError(anyhow!(
|
||||
"region_size 0x{:x} is not aligned on block_size 0x{:x}",
|
||||
self.region_size,
|
||||
self.block_size
|
||||
)));
|
||||
}
|
||||
if self.usable_region_size % self.block_size != 0 {
|
||||
return Err(Error::ValidateError(anyhow!(
|
||||
"usable_region_size 0x{:x} is not aligned on block_size 0x{:x}",
|
||||
self.usable_region_size,
|
||||
self.block_size
|
||||
)));
|
||||
}
|
||||
if self.plugged_size % self.block_size != 0 {
|
||||
return Err(Error::ValidateError(anyhow!(
|
||||
"plugged_size 0x{:x} is not aligned on block_size 0x{:x}",
|
||||
self.plugged_size,
|
||||
self.block_size
|
||||
)));
|
||||
}
|
||||
if self.requested_size % self.block_size != 0 {
|
||||
return Err(Error::ValidateError(anyhow!(
|
||||
"requested_size 0x{:x} is not aligned on block_size 0x{:x}",
|
||||
self.requested_size,
|
||||
self.block_size
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
config.requested_size = size;
|
||||
let tmp_size = cmp::min(
|
||||
config.region_size,
|
||||
config.requested_size + VIRTIO_MEM_USABLE_EXTENT,
|
||||
);
|
||||
config.usable_region_size = cmp::max(config.usable_region_size, tmp_size);
|
||||
fn resize(&mut self, size: u64) -> result::Result<(), Error> {
|
||||
if self.requested_size == size {
|
||||
return Err(Error::ResizeError(anyhow!(
|
||||
"new size 0x{:x} and requested_size are identical",
|
||||
size
|
||||
)));
|
||||
} else if size > self.region_size {
|
||||
return Err(Error::ResizeError(anyhow!(
|
||||
"new size 0x{:x} is bigger than region_size 0x{:x}",
|
||||
size,
|
||||
self.region_size
|
||||
)));
|
||||
} else if size % (self.block_size as u64) != 0 {
|
||||
return Err(Error::ResizeError(anyhow!(
|
||||
"new size 0x{:x} is not aligned on block_size 0x{:x}",
|
||||
size,
|
||||
self.block_size
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
self.requested_size = size;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_valid_range(&self, addr: u64, size: u64) -> bool {
|
||||
// Start address must be aligned on block_size, the size must be
|
||||
// greater than 0, and all blocks covered by the request must be
|
||||
// in the usable region.
|
||||
if addr % self.block_size != 0
|
||||
|| size == 0
|
||||
|| (addr < self.addr || addr + size >= self.addr + self.usable_region_size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
struct Request {
|
||||
@@ -252,12 +294,56 @@ impl Request {
|
||||
status_addr: status_desc.addr,
|
||||
})
|
||||
}
|
||||
|
||||
fn send_response(&self, mem: &GuestMemoryMmap, resp_type: u16, state: u16) -> u32 {
|
||||
let resp = VirtioMemResp {
|
||||
resp_type,
|
||||
state,
|
||||
..Default::default()
|
||||
};
|
||||
match mem.write_obj(resp, self.status_addr) {
|
||||
Ok(_) => size_of::<VirtioMemResp>() as u32,
|
||||
Err(e) => {
|
||||
error!("bad guest memory address: {}", e);
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResizeSender {
|
||||
size: Arc<AtomicU64>,
|
||||
tx: mpsc::Sender<Result<(), Error>>,
|
||||
evt: EventFd,
|
||||
}
|
||||
|
||||
impl ResizeSender {
|
||||
fn size(&self) -> u64 {
|
||||
self.size.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
fn send(&self, r: Result<(), Error>) -> Result<(), mpsc::SendError<Result<(), Error>>> {
|
||||
self.tx.send(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ResizeSender {
|
||||
fn clone(&self) -> Self {
|
||||
ResizeSender {
|
||||
size: self.size.clone(),
|
||||
tx: self.tx.clone(),
|
||||
evt: self
|
||||
.evt
|
||||
.try_clone()
|
||||
.expect("Failed cloning EventFd from ResizeSender"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Resize {
|
||||
size: Arc<AtomicU64>,
|
||||
tx: mpsc::Sender<Result<(), Error>>,
|
||||
rx: Option<mpsc::Receiver<Result<(), Error>>>,
|
||||
rx: mpsc::Receiver<Result<(), Error>>,
|
||||
evt: EventFd,
|
||||
}
|
||||
|
||||
@@ -268,96 +354,36 @@ impl Resize {
|
||||
Ok(Resize {
|
||||
size: Arc::new(AtomicU64::new(0)),
|
||||
tx,
|
||||
rx: Some(rx),
|
||||
rx,
|
||||
evt: EventFd::new(EFD_NONBLOCK)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn try_clone(&self) -> Result<Self, Error> {
|
||||
Ok(Resize {
|
||||
pub fn new_resize_sender(&self) -> Result<ResizeSender, Error> {
|
||||
Ok(ResizeSender {
|
||||
size: self.size.clone(),
|
||||
tx: self.tx.clone(),
|
||||
rx: None,
|
||||
evt: self.evt.try_clone().map_err(Error::EventFdTryCloneFail)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn work(&self, size: u64) -> Result<(), Error> {
|
||||
if let Some(rx) = &self.rx {
|
||||
self.size.store(size, Ordering::Release);
|
||||
self.evt.write(1).map_err(Error::EventFdWriteFail)?;
|
||||
rx.recv().map_err(Error::MpscRecvFail)?
|
||||
} else {
|
||||
panic!("work should not work with cloned resize")
|
||||
}
|
||||
}
|
||||
|
||||
fn get_size(&self) -> u64 {
|
||||
self.size.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
fn send(&self, r: Result<(), Error>) -> Result<(), mpsc::SendError<Result<(), Error>>> {
|
||||
self.tx.send(r)
|
||||
self.size.store(size, Ordering::Release);
|
||||
self.evt.write(1).map_err(Error::EventFdWriteFail)?;
|
||||
self.rx.recv().map_err(Error::MpscRecvFail)?
|
||||
}
|
||||
}
|
||||
|
||||
struct MemEpollHandler {
|
||||
host_addr: u64,
|
||||
host_fd: Option<RawFd>,
|
||||
mem_state: Vec<bool>,
|
||||
config: Arc<Mutex<VirtioMemConfig>>,
|
||||
resize: Resize,
|
||||
queue: Queue,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
queue_evt: EventFd,
|
||||
kill_evt: EventFd,
|
||||
pause_evt: EventFd,
|
||||
}
|
||||
struct BlocksState(Vec<bool>);
|
||||
|
||||
struct StateChangeRequest<'a> {
|
||||
config: VirtioMemConfig,
|
||||
addr: u64,
|
||||
size: u64,
|
||||
nb_blocks: u16,
|
||||
mem_state: &'a mut Vec<bool>,
|
||||
host_addr: u64,
|
||||
host_fd: Option<RawFd>,
|
||||
plug: bool,
|
||||
}
|
||||
|
||||
impl MemEpollHandler {
|
||||
fn virtio_mem_valid_range(config: &VirtioMemConfig, addr: u64, size: u64) -> bool {
|
||||
// address properly aligned?
|
||||
if addr % config.block_size as u64 != 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// reasonable size
|
||||
if addr + size <= addr || size == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// start address in usable range?
|
||||
if addr < config.addr || addr >= config.addr + config.usable_region_size {
|
||||
return false;
|
||||
}
|
||||
|
||||
// end address in usable range?
|
||||
if addr + size > config.addr + config.usable_region_size {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn virtio_mem_check_bitmap(
|
||||
bit_index: usize,
|
||||
nb_blocks: u16,
|
||||
mem_state: &[bool],
|
||||
plug: bool,
|
||||
) -> bool {
|
||||
for state in mem_state.iter().skip(bit_index).take(nb_blocks as usize) {
|
||||
impl BlocksState {
|
||||
fn is_range_state(&self, first_block_index: usize, nb_blocks: u16, plug: bool) -> bool {
|
||||
for state in self
|
||||
.0
|
||||
.iter()
|
||||
.skip(first_block_index)
|
||||
.take(nb_blocks as usize)
|
||||
{
|
||||
if *state != plug {
|
||||
return false;
|
||||
}
|
||||
@@ -365,145 +391,156 @@ impl MemEpollHandler {
|
||||
true
|
||||
}
|
||||
|
||||
fn virtio_mem_set_bitmap(
|
||||
bit_index: usize,
|
||||
nb_blocks: u16,
|
||||
mem_state: &mut Vec<bool>,
|
||||
plug: bool,
|
||||
) {
|
||||
for state in mem_state
|
||||
fn set_range(&mut self, first_block_index: usize, nb_blocks: u16, plug: bool) {
|
||||
for state in self
|
||||
.0
|
||||
.iter_mut()
|
||||
.skip(bit_index)
|
||||
.skip(first_block_index)
|
||||
.take(nb_blocks as usize)
|
||||
{
|
||||
*state = plug;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn virtio_mem_state_change_request(r: StateChangeRequest) -> u16 {
|
||||
if r.plug && (r.config.plugged_size + r.size > r.config.requested_size) {
|
||||
return VIRTIO_MEM_RESP_NACK;
|
||||
}
|
||||
if !MemEpollHandler::virtio_mem_valid_range(&r.config, r.addr, r.size) {
|
||||
return VIRTIO_MEM_RESP_ERROR;
|
||||
}
|
||||
struct MemEpollHandler {
|
||||
host_addr: u64,
|
||||
host_fd: Option<RawFd>,
|
||||
blocks_state: BlocksState,
|
||||
config: Arc<Mutex<VirtioMemConfig>>,
|
||||
resize: ResizeSender,
|
||||
queue: Queue,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
queue_evt: EventFd,
|
||||
kill_evt: EventFd,
|
||||
pause_evt: EventFd,
|
||||
hugepages: bool,
|
||||
}
|
||||
|
||||
let offset = r.addr - r.config.addr;
|
||||
|
||||
let bit_index = (offset / r.config.block_size as u64) as usize;
|
||||
if !MemEpollHandler::virtio_mem_check_bitmap(bit_index, r.nb_blocks, r.mem_state, !r.plug) {
|
||||
return VIRTIO_MEM_RESP_ERROR;
|
||||
}
|
||||
|
||||
if !r.plug {
|
||||
if let Some(fd) = r.host_fd {
|
||||
let res = unsafe {
|
||||
libc::fallocate64(
|
||||
fd,
|
||||
libc::FALLOC_FL_PUNCH_HOLE | libc::FALLOC_FL_KEEP_SIZE,
|
||||
offset as libc::off64_t,
|
||||
r.size as libc::off64_t,
|
||||
)
|
||||
};
|
||||
if res != 0 {
|
||||
error!("fallocate64 get error {}", io::Error::last_os_error());
|
||||
return VIRTIO_MEM_RESP_ERROR;
|
||||
}
|
||||
impl MemEpollHandler {
|
||||
fn discard_memory_range(&self, offset: u64, size: u64) -> Result<(), Error> {
|
||||
// Use fallocate if the memory region is backed by a file.
|
||||
if let Some(fd) = self.host_fd {
|
||||
let res = unsafe {
|
||||
libc::fallocate64(
|
||||
fd,
|
||||
libc::FALLOC_FL_PUNCH_HOLE | libc::FALLOC_FL_KEEP_SIZE,
|
||||
offset as libc::off64_t,
|
||||
size as libc::off64_t,
|
||||
)
|
||||
};
|
||||
if res != 0 {
|
||||
let err = io::Error::last_os_error();
|
||||
error!("Deallocating file space failed: {}", err);
|
||||
return Err(Error::DiscardMemoryRange(err));
|
||||
}
|
||||
}
|
||||
|
||||
// Only use madvise if the memory region is not allocated with
|
||||
// hugepages.
|
||||
if !self.hugepages {
|
||||
let res = unsafe {
|
||||
libc::madvise(
|
||||
(r.host_addr + offset) as *mut libc::c_void,
|
||||
r.size as libc::size_t,
|
||||
(self.host_addr + offset) as *mut libc::c_void,
|
||||
size as libc::size_t,
|
||||
libc::MADV_DONTNEED,
|
||||
)
|
||||
};
|
||||
if res != 0 {
|
||||
error!("madvise get error {}", io::Error::last_os_error());
|
||||
let err = io::Error::last_os_error();
|
||||
error!("Advising kernel about pages range failed: {}", err);
|
||||
return Err(Error::DiscardMemoryRange(err));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn state_change_request(&mut self, addr: u64, nb_blocks: u16, plug: bool) -> u16 {
|
||||
let mut config = self.config.lock().unwrap();
|
||||
let size: u64 = nb_blocks as u64 * config.block_size;
|
||||
|
||||
if plug && (config.plugged_size + size > config.requested_size) {
|
||||
return VIRTIO_MEM_RESP_NACK;
|
||||
}
|
||||
if !config.is_valid_range(addr, size) {
|
||||
return VIRTIO_MEM_RESP_ERROR;
|
||||
}
|
||||
|
||||
let offset = addr - config.addr;
|
||||
|
||||
let first_block_index = (offset / config.block_size) as usize;
|
||||
if !self
|
||||
.blocks_state
|
||||
.is_range_state(first_block_index, nb_blocks, !plug)
|
||||
{
|
||||
return VIRTIO_MEM_RESP_ERROR;
|
||||
}
|
||||
|
||||
if !plug {
|
||||
if let Err(e) = self.discard_memory_range(offset, size) {
|
||||
error!("failed discarding memory range: {:?}", e);
|
||||
return VIRTIO_MEM_RESP_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
MemEpollHandler::virtio_mem_set_bitmap(bit_index, r.nb_blocks, r.mem_state, r.plug);
|
||||
self.blocks_state
|
||||
.set_range(first_block_index, nb_blocks, plug);
|
||||
|
||||
VIRTIO_MEM_RESP_ACK
|
||||
}
|
||||
|
||||
fn virtio_mem_unplug_all(
|
||||
config: VirtioMemConfig,
|
||||
mem_state: &mut Vec<bool>,
|
||||
host_addr: u64,
|
||||
host_fd: Option<RawFd>,
|
||||
) -> u16 {
|
||||
for x in 0..(config.region_size / config.block_size as u64) as usize {
|
||||
if mem_state[x] {
|
||||
let resp_type =
|
||||
MemEpollHandler::virtio_mem_state_change_request(StateChangeRequest {
|
||||
config,
|
||||
addr: config.addr + x as u64 * config.block_size as u64,
|
||||
size: config.block_size as u64,
|
||||
nb_blocks: 1,
|
||||
mem_state,
|
||||
host_addr,
|
||||
host_fd,
|
||||
plug: false,
|
||||
});
|
||||
if resp_type != VIRTIO_MEM_RESP_ACK {
|
||||
return resp_type;
|
||||
}
|
||||
mem_state[x] = false;
|
||||
}
|
||||
if plug {
|
||||
config.plugged_size += size;
|
||||
} else {
|
||||
config.plugged_size -= size;
|
||||
}
|
||||
|
||||
VIRTIO_MEM_RESP_ACK
|
||||
}
|
||||
|
||||
fn virtio_mem_state_request(
|
||||
config: VirtioMemConfig,
|
||||
addr: u64,
|
||||
nb_blocks: u16,
|
||||
mem_state: &mut Vec<bool>,
|
||||
) -> (u16, u16) {
|
||||
let size: u64 = nb_blocks as u64 * config.block_size as u64;
|
||||
let resp_type = if MemEpollHandler::virtio_mem_valid_range(&config, addr, size) {
|
||||
fn unplug_all(&mut self) -> u16 {
|
||||
let mut config = self.config.lock().unwrap();
|
||||
if let Err(e) = self.discard_memory_range(0, config.region_size) {
|
||||
error!("failed discarding memory range: {:?}", e);
|
||||
return VIRTIO_MEM_RESP_ERROR;
|
||||
}
|
||||
|
||||
self.blocks_state
|
||||
.set_range(0, (config.region_size / config.block_size) as u16, false);
|
||||
|
||||
config.plugged_size = 0;
|
||||
|
||||
VIRTIO_MEM_RESP_ACK
|
||||
}
|
||||
|
||||
fn state_request(&self, addr: u64, nb_blocks: u16) -> (u16, u16) {
|
||||
let config = self.config.lock().unwrap();
|
||||
let size: u64 = nb_blocks as u64 * config.block_size;
|
||||
|
||||
let resp_type = if config.is_valid_range(addr, size) {
|
||||
VIRTIO_MEM_RESP_ACK
|
||||
} else {
|
||||
VIRTIO_MEM_RESP_ERROR
|
||||
};
|
||||
|
||||
let offset = addr - config.addr;
|
||||
let bit_index = (offset / config.block_size as u64) as usize;
|
||||
let resp_state =
|
||||
if MemEpollHandler::virtio_mem_check_bitmap(bit_index, nb_blocks, mem_state, true) {
|
||||
VIRTIO_MEM_STATE_PLUGGED
|
||||
} else if MemEpollHandler::virtio_mem_check_bitmap(
|
||||
bit_index, nb_blocks, mem_state, false,
|
||||
) {
|
||||
VIRTIO_MEM_STATE_UNPLUGGED
|
||||
} else {
|
||||
VIRTIO_MEM_STATE_MIXED
|
||||
};
|
||||
let first_block_index = (offset / config.block_size) as usize;
|
||||
let resp_state = if self
|
||||
.blocks_state
|
||||
.is_range_state(first_block_index, nb_blocks, true)
|
||||
{
|
||||
VIRTIO_MEM_STATE_PLUGGED
|
||||
} else if self
|
||||
.blocks_state
|
||||
.is_range_state(first_block_index, nb_blocks, false)
|
||||
{
|
||||
VIRTIO_MEM_STATE_UNPLUGGED
|
||||
} else {
|
||||
VIRTIO_MEM_STATE_MIXED
|
||||
};
|
||||
|
||||
(resp_type, resp_state)
|
||||
}
|
||||
|
||||
fn virtio_mem_send_response(
|
||||
mem: &GuestMemoryMmap,
|
||||
resp_type: u16,
|
||||
resp_state: u16,
|
||||
status_addr: GuestAddress,
|
||||
) -> u32 {
|
||||
let mut resp = VirtioMemResp::default();
|
||||
resp.resp_type = resp_type;
|
||||
resp.state.state = resp_state;
|
||||
match mem.write_obj(resp, status_addr) {
|
||||
Ok(_) => size_of::<VirtioMemResp>() as u32,
|
||||
Err(e) => {
|
||||
error!("bad guest memory address: {}", e);
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn signal(&self, int_type: &VirtioInterruptType) -> result::Result<(), DeviceError> {
|
||||
self.interrupt_cb
|
||||
.trigger(int_type, Some(&self.queue))
|
||||
@@ -514,115 +551,51 @@ impl MemEpollHandler {
|
||||
}
|
||||
|
||||
fn process_queue(&mut self) -> bool {
|
||||
let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
|
||||
let mut request_list = Vec::new();
|
||||
let mut used_count = 0;
|
||||
let mem = self.mem.memory();
|
||||
for avail_desc in self.queue.iter(&mem) {
|
||||
let len = match Request::parse(&avail_desc, &mem) {
|
||||
request_list.push((avail_desc.index, Request::parse(&avail_desc, &mem)));
|
||||
}
|
||||
|
||||
for (desc_index, request) in request_list.iter() {
|
||||
let len = match request {
|
||||
Err(e) => {
|
||||
error!("failed parse VirtioMemReq: {:?}", e);
|
||||
0
|
||||
}
|
||||
Ok(r) => {
|
||||
let mut config = self.config.lock().unwrap();
|
||||
match r.req.req_type {
|
||||
VIRTIO_MEM_REQ_PLUG => {
|
||||
let size: u64 = r.req.nb_blocks as u64 * config.block_size as u64;
|
||||
let resp_type = MemEpollHandler::virtio_mem_state_change_request(
|
||||
StateChangeRequest {
|
||||
config: *config,
|
||||
addr: r.req.addr,
|
||||
size,
|
||||
nb_blocks: r.req.nb_blocks,
|
||||
mem_state: &mut self.mem_state,
|
||||
host_addr: self.host_addr,
|
||||
host_fd: self.host_fd,
|
||||
plug: true,
|
||||
},
|
||||
);
|
||||
if resp_type == VIRTIO_MEM_RESP_ACK {
|
||||
config.plugged_size += size;
|
||||
}
|
||||
MemEpollHandler::virtio_mem_send_response(
|
||||
&mem,
|
||||
resp_type,
|
||||
0u16,
|
||||
r.status_addr,
|
||||
)
|
||||
}
|
||||
VIRTIO_MEM_REQ_UNPLUG => {
|
||||
let size: u64 = r.req.nb_blocks as u64 * config.block_size as u64;
|
||||
let resp_type = MemEpollHandler::virtio_mem_state_change_request(
|
||||
StateChangeRequest {
|
||||
config: *config,
|
||||
addr: r.req.addr,
|
||||
size,
|
||||
nb_blocks: r.req.nb_blocks,
|
||||
mem_state: &mut self.mem_state,
|
||||
host_addr: self.host_addr,
|
||||
host_fd: self.host_fd,
|
||||
plug: false,
|
||||
},
|
||||
);
|
||||
if resp_type == VIRTIO_MEM_RESP_ACK {
|
||||
config.plugged_size -= size;
|
||||
}
|
||||
MemEpollHandler::virtio_mem_send_response(
|
||||
&mem,
|
||||
resp_type,
|
||||
0u16,
|
||||
r.status_addr,
|
||||
)
|
||||
}
|
||||
VIRTIO_MEM_REQ_UNPLUG_ALL => {
|
||||
let resp_type = MemEpollHandler::virtio_mem_unplug_all(
|
||||
*config,
|
||||
&mut self.mem_state,
|
||||
self.host_addr,
|
||||
self.host_fd,
|
||||
);
|
||||
if resp_type == VIRTIO_MEM_RESP_ACK {
|
||||
config.plugged_size = 0;
|
||||
config.usable_region_size = cmp::min(
|
||||
config.region_size,
|
||||
config.requested_size + VIRTIO_MEM_USABLE_EXTENT,
|
||||
);
|
||||
}
|
||||
MemEpollHandler::virtio_mem_send_response(
|
||||
&mem,
|
||||
resp_type,
|
||||
0u16,
|
||||
r.status_addr,
|
||||
)
|
||||
}
|
||||
VIRTIO_MEM_REQ_STATE => {
|
||||
let (resp_type, resp_state) = MemEpollHandler::virtio_mem_state_request(
|
||||
*config,
|
||||
r.req.addr,
|
||||
r.req.nb_blocks,
|
||||
&mut self.mem_state,
|
||||
);
|
||||
MemEpollHandler::virtio_mem_send_response(
|
||||
&mem,
|
||||
resp_type,
|
||||
resp_state,
|
||||
r.status_addr,
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
error!("VirtioMemReq unknown request type {:?}", r.req.req_type);
|
||||
0
|
||||
}
|
||||
Ok(r) => match r.req.req_type {
|
||||
VIRTIO_MEM_REQ_PLUG => {
|
||||
let resp_type =
|
||||
self.state_change_request(r.req.addr, r.req.nb_blocks, true);
|
||||
r.send_response(&mem, resp_type, 0u16)
|
||||
}
|
||||
}
|
||||
VIRTIO_MEM_REQ_UNPLUG => {
|
||||
let resp_type =
|
||||
self.state_change_request(r.req.addr, r.req.nb_blocks, false);
|
||||
r.send_response(&mem, resp_type, 0u16)
|
||||
}
|
||||
VIRTIO_MEM_REQ_UNPLUG_ALL => {
|
||||
let resp_type = self.unplug_all();
|
||||
r.send_response(&mem, resp_type, 0u16)
|
||||
}
|
||||
VIRTIO_MEM_REQ_STATE => {
|
||||
let (resp_type, resp_state) =
|
||||
self.state_request(r.req.addr, r.req.nb_blocks);
|
||||
r.send_response(&mem, resp_type, resp_state)
|
||||
}
|
||||
_ => {
|
||||
error!("VirtioMemReq unknown request type {:?}", r.req.req_type);
|
||||
0
|
||||
}
|
||||
},
|
||||
};
|
||||
used_desc_heads[used_count] = (avail_desc.index, len);
|
||||
|
||||
self.queue.add_used(&mem, *desc_index, len);
|
||||
|
||||
used_count += 1;
|
||||
}
|
||||
|
||||
for &(desc_index, len) in &used_desc_heads[..used_count] {
|
||||
self.queue.add_used(&mem, desc_index, len);
|
||||
}
|
||||
used_count > 0
|
||||
}
|
||||
|
||||
@@ -649,10 +622,10 @@ impl EpollHelperHandler for MemEpollHandler {
|
||||
error!("Failed to get resize event: {:?}", e);
|
||||
return true;
|
||||
} else {
|
||||
let size = self.resize.get_size();
|
||||
let size = self.resize.size();
|
||||
let mut config = self.config.lock().unwrap();
|
||||
let mut signal_error = false;
|
||||
let mut r = virtio_mem_config_resize(&mut config, size);
|
||||
let mut r = config.resize(size);
|
||||
r = match r {
|
||||
Err(e) => Err(e),
|
||||
_ => match self.signal(&VirtioInterruptType::Config) {
|
||||
@@ -696,11 +669,12 @@ impl EpollHelperHandler for MemEpollHandler {
|
||||
pub struct Mem {
|
||||
common: VirtioCommon,
|
||||
id: String,
|
||||
resize: Resize,
|
||||
resize: ResizeSender,
|
||||
host_addr: u64,
|
||||
host_fd: Option<RawFd>,
|
||||
config: Arc<Mutex<VirtioMemConfig>>,
|
||||
seccomp_action: SeccompAction,
|
||||
hugepages: bool,
|
||||
}
|
||||
|
||||
impl Mem {
|
||||
@@ -708,10 +682,11 @@ impl Mem {
|
||||
pub fn new(
|
||||
id: String,
|
||||
region: &Arc<GuestRegionMmap>,
|
||||
resize: Resize,
|
||||
resize: ResizeSender,
|
||||
seccomp_action: SeccompAction,
|
||||
numa_node_id: Option<u16>,
|
||||
initial_size: u64,
|
||||
hugepages: bool,
|
||||
) -> io::Result<Mem> {
|
||||
let region_len = region.len();
|
||||
|
||||
@@ -727,20 +702,24 @@ impl Mem {
|
||||
|
||||
let mut avail_features = 1u64 << VIRTIO_F_VERSION_1;
|
||||
|
||||
let mut config = VirtioMemConfig::default();
|
||||
config.block_size = VIRTIO_MEM_DEFAULT_BLOCK_SIZE;
|
||||
config.addr = region.start_addr().raw_value();
|
||||
config.region_size = region.len();
|
||||
config.usable_region_size = cmp::min(
|
||||
config.region_size,
|
||||
config.requested_size + VIRTIO_MEM_USABLE_EXTENT,
|
||||
);
|
||||
let mut config = VirtioMemConfig {
|
||||
block_size: VIRTIO_MEM_DEFAULT_BLOCK_SIZE,
|
||||
addr: region.start_addr().raw_value(),
|
||||
region_size: region.len(),
|
||||
usable_region_size: region.len(),
|
||||
plugged_size: 0,
|
||||
requested_size: 0,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if initial_size != 0 {
|
||||
virtio_mem_config_resize(&mut config, initial_size).map_err(|e| {
|
||||
config.resize(initial_size).map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("Virtio-mem resize {} got {:?}", initial_size, e),
|
||||
format!(
|
||||
"Failed to resize virtio-mem configuration to {}: {:?}",
|
||||
initial_size, e
|
||||
),
|
||||
)
|
||||
})?;
|
||||
}
|
||||
@@ -750,11 +729,18 @@ impl Mem {
|
||||
config.node_id = node_id;
|
||||
}
|
||||
|
||||
let host_fd = if let Some(f_offset) = region.file_offset() {
|
||||
Some(f_offset.file().as_raw_fd())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// Make sure the virtio-mem configuration complies with the
|
||||
// specification.
|
||||
config.validate().map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("Invalid virtio-mem configuration: {:?}", e),
|
||||
)
|
||||
})?;
|
||||
|
||||
let host_fd = region
|
||||
.file_offset()
|
||||
.map(|f_offset| f_offset.file().as_raw_fd());
|
||||
|
||||
Ok(Mem {
|
||||
common: VirtioCommon {
|
||||
@@ -762,6 +748,7 @@ impl Mem {
|
||||
avail_features,
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
queue_sizes: QUEUE_SIZES.to_vec(),
|
||||
min_queues: 1,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
@@ -770,6 +757,7 @@ impl Mem {
|
||||
host_fd,
|
||||
config: Arc::new(Mutex::new(config)),
|
||||
seccomp_action,
|
||||
hugepages,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -837,20 +825,28 @@ impl VirtioDevice for Mem {
|
||||
let mut handler = MemEpollHandler {
|
||||
host_addr: self.host_addr,
|
||||
host_fd: self.host_fd,
|
||||
mem_state: vec![false; config.region_size as usize / config.block_size as usize],
|
||||
blocks_state: BlocksState(vec![
|
||||
false;
|
||||
(config.region_size / config.block_size) as usize
|
||||
]),
|
||||
config: self.config.clone(),
|
||||
resize: self.resize.try_clone().map_err(|e| {
|
||||
error!("failed to clone resize EventFd: {:?}", e);
|
||||
ActivateError::BadActivate
|
||||
})?,
|
||||
resize: self.resize.clone(),
|
||||
queue: queues.remove(0),
|
||||
mem,
|
||||
interrupt_cb,
|
||||
queue_evt: queue_evts.remove(0),
|
||||
kill_evt,
|
||||
pause_evt,
|
||||
hugepages: self.hugepages,
|
||||
};
|
||||
|
||||
handler
|
||||
.discard_memory_range(0, config.region_size)
|
||||
.map_err(|e| {
|
||||
error!("failed discarding memory range: {:?}", e);
|
||||
ActivateError::BadActivate
|
||||
})?;
|
||||
|
||||
let paused = self.common.paused.clone();
|
||||
let paused_sync = self.common.paused_sync.clone();
|
||||
let mut epoll_threads = Vec::new();
|
||||
@@ -858,7 +854,7 @@ impl VirtioDevice for Mem {
|
||||
let virtio_mem_seccomp_filter = get_seccomp_filter(&self.seccomp_action, Thread::VirtioMem)
|
||||
.map_err(ActivateError::CreateSeccompFilter)?;
|
||||
thread::Builder::new()
|
||||
.name("virtio_mem".to_string())
|
||||
.name(self.id.clone())
|
||||
.spawn(move || {
|
||||
if let Err(e) = SeccompFilter::apply(virtio_mem_seccomp_filter) {
|
||||
error!("Error applying seccomp filter: {:?}", e);
|
||||
@@ -876,7 +872,7 @@ impl VirtioDevice for Mem {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> Option<(Arc<dyn VirtioInterrupt>, Vec<EventFd>)> {
|
||||
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
||||
self.common.reset()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,13 @@ use crate::seccomp_filters::{get_seccomp_filter, Thread};
|
||||
use crate::VirtioInterrupt;
|
||||
use anyhow::anyhow;
|
||||
use net_util::{
|
||||
open_tap, MacAddr, NetCounters, NetQueuePair, OpenTapError, RxVirtio, Tap, TxVirtio,
|
||||
open_tap, MacAddr, NetCounters, NetQueuePair, OpenTapError, RxVirtio, Tap, TapError, TxVirtio,
|
||||
};
|
||||
use seccomp::{SeccompAction, SeccompFilter};
|
||||
use std::collections::HashMap;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::num::Wrapping;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::result;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Barrier};
|
||||
@@ -50,6 +50,9 @@ pub const RX_TAP_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 3;
|
||||
pub enum Error {
|
||||
/// Failed to open taps.
|
||||
OpenTap(OpenTapError),
|
||||
|
||||
// Using existing tap
|
||||
TapError(TapError),
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
@@ -91,9 +94,9 @@ impl NetEpollHandler {
|
||||
|| !self.driver_awake
|
||||
{
|
||||
self.signal_used_queue(&self.queue_pair[0])?;
|
||||
info!("Signalling RX queue");
|
||||
debug!("Signalling RX queue");
|
||||
} else {
|
||||
info!("Not signalling RX queue");
|
||||
debug!("Not signalling RX queue");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -111,9 +114,9 @@ impl NetEpollHandler {
|
||||
|| !self.driver_awake
|
||||
{
|
||||
self.signal_used_queue(&self.queue_pair[1])?;
|
||||
info!("Signalling TX queue");
|
||||
debug!("Signalling TX queue");
|
||||
} else {
|
||||
info!("Not signalling TX queue");
|
||||
debug!("Not signalling TX queue");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -126,9 +129,9 @@ impl NetEpollHandler {
|
||||
|| !self.driver_awake
|
||||
{
|
||||
self.signal_used_queue(&self.queue_pair[0])?;
|
||||
info!("Signalling RX queue");
|
||||
debug!("Signalling RX queue");
|
||||
} else {
|
||||
info!("Not signalling RX queue");
|
||||
debug!("Not signalling RX queue");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -253,6 +256,7 @@ impl Net {
|
||||
avail_features,
|
||||
queue_sizes: vec![queue_size; queue_num],
|
||||
paused_sync: Some(Arc::new(Barrier::new((num_queues / 2) + 1))),
|
||||
min_queues: 2,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
@@ -279,7 +283,7 @@ impl Net {
|
||||
queue_size: u16,
|
||||
seccomp_action: SeccompAction,
|
||||
) -> Result<Self> {
|
||||
let taps = open_tap(if_name, ip_addr, netmask, host_mac, num_queues / 2)
|
||||
let taps = open_tap(if_name, ip_addr, netmask, host_mac, num_queues / 2, None)
|
||||
.map_err(Error::OpenTap)?;
|
||||
|
||||
Self::new_with_tap(
|
||||
@@ -293,6 +297,33 @@ impl Net {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_tap_fds(
|
||||
id: String,
|
||||
fds: &[RawFd],
|
||||
guest_mac: Option<MacAddr>,
|
||||
iommu: bool,
|
||||
queue_size: u16,
|
||||
seccomp_action: SeccompAction,
|
||||
) -> Result<Self> {
|
||||
let mut taps: Vec<Tap> = Vec::new();
|
||||
let num_queue_pairs = fds.len();
|
||||
|
||||
for fd in fds.iter() {
|
||||
let tap = Tap::from_tap_fd(*fd, num_queue_pairs).map_err(Error::TapError)?;
|
||||
taps.push(tap);
|
||||
}
|
||||
|
||||
Self::new_with_tap(
|
||||
id,
|
||||
taps,
|
||||
guest_mac,
|
||||
iommu,
|
||||
num_queue_pairs * 2,
|
||||
queue_size,
|
||||
seccomp_action,
|
||||
)
|
||||
}
|
||||
|
||||
fn state(&self) -> NetState {
|
||||
NetState {
|
||||
avail_features: self.common.avail_features,
|
||||
@@ -302,13 +333,11 @@ impl Net {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &NetState) -> Result<()> {
|
||||
fn set_state(&mut self, state: &NetState) {
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
self.config = state.config;
|
||||
self.common.queue_sizes = state.queue_size.clone();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,7 +427,7 @@ impl VirtioDevice for Net {
|
||||
get_seccomp_filter(&self.seccomp_action, Thread::VirtioNetCtl)
|
||||
.map_err(ActivateError::CreateSeccompFilter)?;
|
||||
thread::Builder::new()
|
||||
.name("virtio_net_ctl".to_string())
|
||||
.name(format!("{}_ctrl", self.id))
|
||||
.spawn(move || {
|
||||
if let Err(e) = SeccompFilter::apply(virtio_net_ctl_seccomp_filter) {
|
||||
error!("Error applying seccomp filter: {:?}", e);
|
||||
@@ -416,7 +445,7 @@ impl VirtioDevice for Net {
|
||||
let event_idx = self.common.feature_acked(VIRTIO_RING_F_EVENT_IDX.into());
|
||||
|
||||
let mut epoll_threads = Vec::new();
|
||||
for _ in 0..taps.len() {
|
||||
for i in 0..taps.len() {
|
||||
let rx = RxVirtio::new();
|
||||
let tx = TxVirtio::new();
|
||||
let rx_tap_listening = false;
|
||||
@@ -478,7 +507,7 @@ impl VirtioDevice for Net {
|
||||
get_seccomp_filter(&self.seccomp_action, Thread::VirtioNet)
|
||||
.map_err(ActivateError::CreateSeccompFilter)?;
|
||||
thread::Builder::new()
|
||||
.name("virtio_net".to_string())
|
||||
.name(format!("{}_qp{}", self.id.clone(), i))
|
||||
.spawn(move || {
|
||||
if let Err(e) = SeccompFilter::apply(virtio_net_seccomp_filter) {
|
||||
error!("Error applying seccomp filter: {:?}", e);
|
||||
@@ -500,7 +529,7 @@ impl VirtioDevice for Net {
|
||||
Err(ActivateError::BadActivate)
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> Option<(Arc<dyn VirtioInterrupt>, Vec<EventFd>)> {
|
||||
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
||||
self.common.reset()
|
||||
}
|
||||
|
||||
@@ -573,9 +602,8 @@ impl Snapshottable for Net {
|
||||
}
|
||||
};
|
||||
|
||||
return self.set_state(&net_state).map_err(|e| {
|
||||
MigratableError::Restore(anyhow!("Could not restore NET state {:?}", e))
|
||||
});
|
||||
self.set_state(&net_state);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(MigratableError::Restore(anyhow!(
|
||||
|
||||
@@ -304,6 +304,7 @@ impl Pmem {
|
||||
queue_sizes: QUEUE_SIZES.to_vec(),
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
avail_features,
|
||||
min_queues: 1,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
@@ -323,12 +324,10 @@ impl Pmem {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &PmemState) -> io::Result<()> {
|
||||
fn set_state(&mut self, state: &PmemState) {
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
self.config = state.config;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,7 +412,7 @@ impl VirtioDevice for Pmem {
|
||||
get_seccomp_filter(&self.seccomp_action, Thread::VirtioPmem)
|
||||
.map_err(ActivateError::CreateSeccompFilter)?;
|
||||
thread::Builder::new()
|
||||
.name("virtio_pmem".to_string())
|
||||
.name(self.id.clone())
|
||||
.spawn(move || {
|
||||
if let Err(e) = SeccompFilter::apply(virtio_pmem_seccomp_filter) {
|
||||
error!("Error applying seccomp filter: {:?}", e);
|
||||
@@ -434,7 +433,7 @@ impl VirtioDevice for Pmem {
|
||||
Err(ActivateError::BadActivate)
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> Option<(Arc<dyn VirtioInterrupt>, Vec<EventFd>)> {
|
||||
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
||||
self.common.reset()
|
||||
}
|
||||
|
||||
@@ -483,9 +482,8 @@ impl Snapshottable for Pmem {
|
||||
}
|
||||
};
|
||||
|
||||
return self.set_state(&pmem_state).map_err(|e| {
|
||||
MigratableError::Restore(anyhow!("Could not restore PMEM state {:?}", e))
|
||||
});
|
||||
self.set_state(&pmem_state);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(MigratableError::Restore(anyhow!(
|
||||
|
||||
@@ -158,6 +158,7 @@ impl Rng {
|
||||
queue_sizes: QUEUE_SIZES.to_vec(),
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
avail_features,
|
||||
min_queues: 1,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
@@ -173,10 +174,9 @@ impl Rng {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &RngState) -> io::Result<()> {
|
||||
fn set_state(&mut self, state: &RngState) {
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ impl VirtioDevice for Rng {
|
||||
get_seccomp_filter(&self.seccomp_action, Thread::VirtioRng)
|
||||
.map_err(ActivateError::CreateSeccompFilter)?;
|
||||
thread::Builder::new()
|
||||
.name("virtio_rng".to_string())
|
||||
.name(self.id.clone())
|
||||
.spawn(move || {
|
||||
if let Err(e) = SeccompFilter::apply(virtio_rng_seccomp_filter) {
|
||||
error!("Error applying seccomp filter: {:?}", e);
|
||||
@@ -279,7 +279,7 @@ impl VirtioDevice for Rng {
|
||||
Err(ActivateError::BadActivate)
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> Option<(Arc<dyn VirtioInterrupt>, Vec<EventFd>)> {
|
||||
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
||||
self.common.reset()
|
||||
}
|
||||
}
|
||||
@@ -324,9 +324,8 @@ impl Snapshottable for Rng {
|
||||
}
|
||||
};
|
||||
|
||||
return self.set_state(&rng_state).map_err(|e| {
|
||||
MigratableError::Restore(anyhow!("Could not restore RNG state {:?}", e))
|
||||
});
|
||||
self.set_state(&rng_state);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(MigratableError::Restore(anyhow!(
|
||||
|
||||
@@ -13,8 +13,7 @@ use std::convert::TryInto;
|
||||
|
||||
pub enum Thread {
|
||||
VirtioBalloon,
|
||||
VirtioBlk,
|
||||
VirtioBlkIoUring,
|
||||
VirtioBlock,
|
||||
VirtioConsole,
|
||||
VirtioIommu,
|
||||
VirtioMem,
|
||||
@@ -56,8 +55,8 @@ const SYS_IO_URING_ENTER: i64 = 426;
|
||||
// See include/uapi/asm-generic/ioctls.h in the kernel code.
|
||||
const FIONBIO: u64 = 0x5421;
|
||||
|
||||
fn virtio_balloon_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
Ok(vec![
|
||||
fn virtio_balloon_thread_rules() -> Vec<SyscallRuleSet> {
|
||||
vec![
|
||||
allow_syscall(libc::SYS_brk),
|
||||
allow_syscall(libc::SYS_close),
|
||||
allow_syscall(libc::SYS_dup),
|
||||
@@ -74,13 +73,11 @@ fn virtio_balloon_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
allow_syscall(libc::SYS_rt_sigprocmask),
|
||||
allow_syscall(libc::SYS_sigaltstack),
|
||||
allow_syscall(libc::SYS_write),
|
||||
])
|
||||
]
|
||||
}
|
||||
|
||||
// The filter containing the allowed syscall rules required by the
|
||||
// virtio_blk thread to function.
|
||||
fn virtio_blk_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
Ok(vec![
|
||||
fn virtio_block_thread_rules() -> Vec<SyscallRuleSet> {
|
||||
vec![
|
||||
allow_syscall(libc::SYS_brk),
|
||||
allow_syscall(libc::SYS_close),
|
||||
allow_syscall(libc::SYS_dup),
|
||||
@@ -100,6 +97,7 @@ fn virtio_blk_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
// Use a hard-code number instead.
|
||||
allow_syscall(46),
|
||||
allow_syscall(libc::SYS_futex),
|
||||
allow_syscall(SYS_IO_URING_ENTER),
|
||||
allow_syscall(libc::SYS_lseek),
|
||||
allow_syscall(libc::SYS_madvise),
|
||||
allow_syscall(libc::SYS_mmap),
|
||||
@@ -107,40 +105,20 @@ fn virtio_blk_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
allow_syscall(libc::SYS_munmap),
|
||||
allow_syscall(libc::SYS_openat),
|
||||
allow_syscall(libc::SYS_prctl),
|
||||
allow_syscall(libc::SYS_pread64),
|
||||
allow_syscall(libc::SYS_preadv),
|
||||
allow_syscall(libc::SYS_pwritev),
|
||||
allow_syscall(libc::SYS_read),
|
||||
allow_syscall(libc::SYS_rt_sigprocmask),
|
||||
allow_syscall(libc::SYS_sched_getaffinity),
|
||||
allow_syscall(libc::SYS_set_robust_list),
|
||||
allow_syscall(libc::SYS_sigaltstack),
|
||||
allow_syscall(libc::SYS_write),
|
||||
])
|
||||
]
|
||||
}
|
||||
|
||||
fn virtio_blk_io_uring_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
Ok(vec![
|
||||
allow_syscall(libc::SYS_brk),
|
||||
allow_syscall(libc::SYS_close),
|
||||
allow_syscall(libc::SYS_dup),
|
||||
allow_syscall(libc::SYS_epoll_create1),
|
||||
allow_syscall(libc::SYS_epoll_ctl),
|
||||
allow_syscall(libc::SYS_epoll_pwait),
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
allow_syscall(libc::SYS_epoll_wait),
|
||||
allow_syscall(libc::SYS_exit),
|
||||
allow_syscall(libc::SYS_fsync),
|
||||
allow_syscall(libc::SYS_futex),
|
||||
allow_syscall(SYS_IO_URING_ENTER),
|
||||
allow_syscall(libc::SYS_madvise),
|
||||
allow_syscall(libc::SYS_munmap),
|
||||
allow_syscall(libc::SYS_read),
|
||||
allow_syscall(libc::SYS_rt_sigprocmask),
|
||||
allow_syscall(libc::SYS_sigaltstack),
|
||||
allow_syscall(libc::SYS_write),
|
||||
])
|
||||
}
|
||||
|
||||
fn virtio_console_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
Ok(vec![
|
||||
fn virtio_console_thread_rules() -> Vec<SyscallRuleSet> {
|
||||
vec![
|
||||
allow_syscall(libc::SYS_brk),
|
||||
allow_syscall(libc::SYS_close),
|
||||
allow_syscall(libc::SYS_dup),
|
||||
@@ -162,11 +140,11 @@ fn virtio_console_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
allow_syscall(libc::SYS_set_robust_list),
|
||||
allow_syscall(libc::SYS_sigaltstack),
|
||||
allow_syscall(libc::SYS_write),
|
||||
])
|
||||
]
|
||||
}
|
||||
|
||||
fn virtio_iommu_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
Ok(vec![
|
||||
fn virtio_iommu_thread_rules() -> Vec<SyscallRuleSet> {
|
||||
vec![
|
||||
allow_syscall(libc::SYS_brk),
|
||||
allow_syscall(libc::SYS_close),
|
||||
allow_syscall(libc::SYS_dup),
|
||||
@@ -184,11 +162,11 @@ fn virtio_iommu_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
allow_syscall(libc::SYS_read),
|
||||
allow_syscall(libc::SYS_sigaltstack),
|
||||
allow_syscall(libc::SYS_write),
|
||||
])
|
||||
]
|
||||
}
|
||||
|
||||
fn virtio_mem_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
Ok(vec![
|
||||
fn virtio_mem_thread_rules() -> Vec<SyscallRuleSet> {
|
||||
vec![
|
||||
allow_syscall(libc::SYS_brk),
|
||||
allow_syscall(libc::SYS_close),
|
||||
allow_syscall(libc::SYS_dup),
|
||||
@@ -206,11 +184,33 @@ fn virtio_mem_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
allow_syscall(libc::SYS_rt_sigprocmask),
|
||||
allow_syscall(libc::SYS_sigaltstack),
|
||||
allow_syscall(libc::SYS_write),
|
||||
])
|
||||
]
|
||||
}
|
||||
|
||||
fn virtio_net_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
Ok(vec![
|
||||
fn virtio_net_thread_rules() -> Vec<SyscallRuleSet> {
|
||||
vec![
|
||||
allow_syscall(libc::SYS_brk),
|
||||
allow_syscall(libc::SYS_close),
|
||||
allow_syscall(libc::SYS_dup),
|
||||
allow_syscall(libc::SYS_epoll_create1),
|
||||
allow_syscall(libc::SYS_epoll_ctl),
|
||||
allow_syscall(libc::SYS_epoll_pwait),
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
allow_syscall(libc::SYS_epoll_wait),
|
||||
allow_syscall(libc::SYS_exit),
|
||||
allow_syscall(libc::SYS_futex),
|
||||
allow_syscall(libc::SYS_madvise),
|
||||
allow_syscall(libc::SYS_munmap),
|
||||
allow_syscall(libc::SYS_openat),
|
||||
allow_syscall(libc::SYS_read),
|
||||
allow_syscall(libc::SYS_rt_sigprocmask),
|
||||
allow_syscall(libc::SYS_sigaltstack),
|
||||
allow_syscall(libc::SYS_write),
|
||||
]
|
||||
}
|
||||
|
||||
fn virtio_net_ctl_thread_rules() -> Vec<SyscallRuleSet> {
|
||||
vec![
|
||||
allow_syscall(libc::SYS_brk),
|
||||
allow_syscall(libc::SYS_close),
|
||||
allow_syscall(libc::SYS_dup),
|
||||
@@ -227,32 +227,11 @@ fn virtio_net_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
allow_syscall(libc::SYS_rt_sigprocmask),
|
||||
allow_syscall(libc::SYS_sigaltstack),
|
||||
allow_syscall(libc::SYS_write),
|
||||
])
|
||||
]
|
||||
}
|
||||
|
||||
fn virtio_net_ctl_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
Ok(vec![
|
||||
allow_syscall(libc::SYS_brk),
|
||||
allow_syscall(libc::SYS_close),
|
||||
allow_syscall(libc::SYS_dup),
|
||||
allow_syscall(libc::SYS_epoll_create1),
|
||||
allow_syscall(libc::SYS_epoll_ctl),
|
||||
allow_syscall(libc::SYS_epoll_pwait),
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
allow_syscall(libc::SYS_epoll_wait),
|
||||
allow_syscall(libc::SYS_exit),
|
||||
allow_syscall(libc::SYS_futex),
|
||||
allow_syscall(libc::SYS_madvise),
|
||||
allow_syscall(libc::SYS_munmap),
|
||||
allow_syscall(libc::SYS_read),
|
||||
allow_syscall(libc::SYS_rt_sigprocmask),
|
||||
allow_syscall(libc::SYS_sigaltstack),
|
||||
allow_syscall(libc::SYS_write),
|
||||
])
|
||||
}
|
||||
|
||||
fn virtio_pmem_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
Ok(vec![
|
||||
fn virtio_pmem_thread_rules() -> Vec<SyscallRuleSet> {
|
||||
vec![
|
||||
allow_syscall(libc::SYS_brk),
|
||||
allow_syscall(libc::SYS_close),
|
||||
allow_syscall(libc::SYS_dup),
|
||||
@@ -270,11 +249,11 @@ fn virtio_pmem_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
allow_syscall(libc::SYS_rt_sigprocmask),
|
||||
allow_syscall(libc::SYS_sigaltstack),
|
||||
allow_syscall(libc::SYS_write),
|
||||
])
|
||||
]
|
||||
}
|
||||
|
||||
fn virtio_rng_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
Ok(vec![
|
||||
fn virtio_rng_thread_rules() -> Vec<SyscallRuleSet> {
|
||||
vec![
|
||||
allow_syscall(libc::SYS_brk),
|
||||
allow_syscall(libc::SYS_close),
|
||||
allow_syscall(libc::SYS_dup),
|
||||
@@ -296,11 +275,11 @@ fn virtio_rng_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
allow_syscall(libc::SYS_set_robust_list),
|
||||
allow_syscall(libc::SYS_sigaltstack),
|
||||
allow_syscall(libc::SYS_write),
|
||||
])
|
||||
]
|
||||
}
|
||||
|
||||
fn virtio_vhost_blk_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
Ok(vec![
|
||||
fn virtio_vhost_blk_thread_rules() -> Vec<SyscallRuleSet> {
|
||||
vec![
|
||||
allow_syscall(libc::SYS_brk),
|
||||
allow_syscall(libc::SYS_close),
|
||||
allow_syscall(libc::SYS_dup),
|
||||
@@ -317,11 +296,11 @@ fn virtio_vhost_blk_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
allow_syscall(libc::SYS_rt_sigprocmask),
|
||||
allow_syscall(libc::SYS_sigaltstack),
|
||||
allow_syscall(libc::SYS_write),
|
||||
])
|
||||
]
|
||||
}
|
||||
|
||||
fn virtio_vhost_fs_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
Ok(vec![
|
||||
fn virtio_vhost_fs_thread_rules() -> Vec<SyscallRuleSet> {
|
||||
vec![
|
||||
allow_syscall(libc::SYS_brk),
|
||||
allow_syscall(libc::SYS_close),
|
||||
allow_syscall(libc::SYS_dup),
|
||||
@@ -341,11 +320,11 @@ fn virtio_vhost_fs_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
allow_syscall(libc::SYS_sendmsg),
|
||||
allow_syscall(libc::SYS_sigaltstack),
|
||||
allow_syscall(libc::SYS_write),
|
||||
])
|
||||
]
|
||||
}
|
||||
|
||||
fn virtio_vhost_net_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
Ok(vec![
|
||||
fn virtio_vhost_net_thread_rules() -> Vec<SyscallRuleSet> {
|
||||
vec![
|
||||
allow_syscall(libc::SYS_brk),
|
||||
allow_syscall(libc::SYS_close),
|
||||
allow_syscall(libc::SYS_dup),
|
||||
@@ -363,11 +342,11 @@ fn virtio_vhost_net_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
allow_syscall(libc::SYS_madvise),
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
allow_syscall(libc::SYS_exit),
|
||||
])
|
||||
]
|
||||
}
|
||||
|
||||
fn virtio_vhost_net_ctl_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
Ok(vec![
|
||||
fn virtio_vhost_net_ctl_thread_rules() -> Vec<SyscallRuleSet> {
|
||||
vec![
|
||||
allow_syscall(libc::SYS_brk),
|
||||
allow_syscall(libc::SYS_close),
|
||||
allow_syscall(libc::SYS_dup),
|
||||
@@ -386,15 +365,15 @@ fn virtio_vhost_net_ctl_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
allow_syscall(libc::SYS_madvise),
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
allow_syscall(libc::SYS_exit),
|
||||
])
|
||||
]
|
||||
}
|
||||
|
||||
fn create_vsock_ioctl_seccomp_rule() -> Result<Vec<SeccompRule>, Error> {
|
||||
Ok(or![and![Cond::new(1, ArgLen::DWORD, Eq, FIONBIO,)?],])
|
||||
fn create_vsock_ioctl_seccomp_rule() -> Vec<SeccompRule> {
|
||||
or![and![Cond::new(1, ArgLen::DWORD, Eq, FIONBIO,).unwrap()],]
|
||||
}
|
||||
|
||||
fn virtio_vsock_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
Ok(vec![
|
||||
fn virtio_vsock_thread_rules() -> Vec<SyscallRuleSet> {
|
||||
vec![
|
||||
allow_syscall(libc::SYS_accept4),
|
||||
allow_syscall(libc::SYS_brk),
|
||||
allow_syscall(libc::SYS_close),
|
||||
@@ -405,7 +384,7 @@ fn virtio_vsock_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
allow_syscall(libc::SYS_epoll_wait),
|
||||
allow_syscall(libc::SYS_exit),
|
||||
allow_syscall_if(libc::SYS_ioctl, create_vsock_ioctl_seccomp_rule()?),
|
||||
allow_syscall_if(libc::SYS_ioctl, create_vsock_ioctl_seccomp_rule()),
|
||||
allow_syscall(libc::SYS_futex),
|
||||
allow_syscall(libc::SYS_madvise),
|
||||
allow_syscall(libc::SYS_munmap),
|
||||
@@ -414,11 +393,11 @@ fn virtio_vsock_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
allow_syscall(libc::SYS_rt_sigprocmask),
|
||||
allow_syscall(libc::SYS_sigaltstack),
|
||||
allow_syscall(libc::SYS_write),
|
||||
])
|
||||
]
|
||||
}
|
||||
|
||||
fn virtio_watchdog_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
Ok(vec![
|
||||
fn virtio_watchdog_thread_rules() -> Vec<SyscallRuleSet> {
|
||||
vec![
|
||||
allow_syscall(libc::SYS_brk),
|
||||
allow_syscall(libc::SYS_close),
|
||||
allow_syscall(libc::SYS_dup),
|
||||
@@ -441,53 +420,48 @@ fn virtio_watchdog_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
|
||||
allow_syscall(libc::SYS_sigaltstack),
|
||||
allow_syscall(libc::SYS_timerfd_settime),
|
||||
allow_syscall(libc::SYS_write),
|
||||
])
|
||||
]
|
||||
}
|
||||
|
||||
fn get_seccomp_filter_trap(thread_type: Thread) -> Result<SeccompFilter, Error> {
|
||||
let rules = match thread_type {
|
||||
Thread::VirtioBalloon => virtio_balloon_thread_rules()?,
|
||||
Thread::VirtioBlk => virtio_blk_thread_rules()?,
|
||||
Thread::VirtioBlkIoUring => virtio_blk_io_uring_thread_rules()?,
|
||||
Thread::VirtioConsole => virtio_console_thread_rules()?,
|
||||
Thread::VirtioIommu => virtio_iommu_thread_rules()?,
|
||||
Thread::VirtioMem => virtio_mem_thread_rules()?,
|
||||
Thread::VirtioNet => virtio_net_thread_rules()?,
|
||||
Thread::VirtioNetCtl => virtio_net_ctl_thread_rules()?,
|
||||
Thread::VirtioPmem => virtio_pmem_thread_rules()?,
|
||||
Thread::VirtioRng => virtio_rng_thread_rules()?,
|
||||
Thread::VirtioVhostBlk => virtio_vhost_blk_thread_rules()?,
|
||||
Thread::VirtioVhostFs => virtio_vhost_fs_thread_rules()?,
|
||||
Thread::VirtioVhostNet => virtio_vhost_net_thread_rules()?,
|
||||
Thread::VirtioVhostNetCtl => virtio_vhost_net_ctl_thread_rules()?,
|
||||
Thread::VirtioVsock => virtio_vsock_thread_rules()?,
|
||||
Thread::VirtioWatchdog => virtio_watchdog_thread_rules()?,
|
||||
Thread::VirtioBalloon => virtio_balloon_thread_rules(),
|
||||
Thread::VirtioBlock => virtio_block_thread_rules(),
|
||||
Thread::VirtioConsole => virtio_console_thread_rules(),
|
||||
Thread::VirtioIommu => virtio_iommu_thread_rules(),
|
||||
Thread::VirtioMem => virtio_mem_thread_rules(),
|
||||
Thread::VirtioNet => virtio_net_thread_rules(),
|
||||
Thread::VirtioNetCtl => virtio_net_ctl_thread_rules(),
|
||||
Thread::VirtioPmem => virtio_pmem_thread_rules(),
|
||||
Thread::VirtioRng => virtio_rng_thread_rules(),
|
||||
Thread::VirtioVhostBlk => virtio_vhost_blk_thread_rules(),
|
||||
Thread::VirtioVhostFs => virtio_vhost_fs_thread_rules(),
|
||||
Thread::VirtioVhostNet => virtio_vhost_net_thread_rules(),
|
||||
Thread::VirtioVhostNetCtl => virtio_vhost_net_ctl_thread_rules(),
|
||||
Thread::VirtioVsock => virtio_vsock_thread_rules(),
|
||||
Thread::VirtioWatchdog => virtio_watchdog_thread_rules(),
|
||||
};
|
||||
|
||||
Ok(SeccompFilter::new(
|
||||
rules.into_iter().collect(),
|
||||
SeccompAction::Trap,
|
||||
)?)
|
||||
SeccompFilter::new(rules.into_iter().collect(), SeccompAction::Trap)
|
||||
}
|
||||
|
||||
fn get_seccomp_filter_log(thread_type: Thread) -> Result<SeccompFilter, Error> {
|
||||
let rules = match thread_type {
|
||||
Thread::VirtioBalloon => virtio_balloon_thread_rules()?,
|
||||
Thread::VirtioBlk => virtio_blk_thread_rules()?,
|
||||
Thread::VirtioBlkIoUring => virtio_blk_io_uring_thread_rules()?,
|
||||
Thread::VirtioConsole => virtio_console_thread_rules()?,
|
||||
Thread::VirtioIommu => virtio_iommu_thread_rules()?,
|
||||
Thread::VirtioMem => virtio_mem_thread_rules()?,
|
||||
Thread::VirtioNet => virtio_net_thread_rules()?,
|
||||
Thread::VirtioNetCtl => virtio_net_ctl_thread_rules()?,
|
||||
Thread::VirtioPmem => virtio_pmem_thread_rules()?,
|
||||
Thread::VirtioRng => virtio_rng_thread_rules()?,
|
||||
Thread::VirtioVhostBlk => virtio_vhost_blk_thread_rules()?,
|
||||
Thread::VirtioVhostFs => virtio_vhost_fs_thread_rules()?,
|
||||
Thread::VirtioVhostNet => virtio_vhost_net_thread_rules()?,
|
||||
Thread::VirtioVhostNetCtl => virtio_vhost_net_ctl_thread_rules()?,
|
||||
Thread::VirtioVsock => virtio_vsock_thread_rules()?,
|
||||
Thread::VirtioWatchdog => virtio_watchdog_thread_rules()?,
|
||||
Thread::VirtioBalloon => virtio_balloon_thread_rules(),
|
||||
Thread::VirtioBlock => virtio_block_thread_rules(),
|
||||
Thread::VirtioConsole => virtio_console_thread_rules(),
|
||||
Thread::VirtioIommu => virtio_iommu_thread_rules(),
|
||||
Thread::VirtioMem => virtio_mem_thread_rules(),
|
||||
Thread::VirtioNet => virtio_net_thread_rules(),
|
||||
Thread::VirtioNetCtl => virtio_net_ctl_thread_rules(),
|
||||
Thread::VirtioPmem => virtio_pmem_thread_rules(),
|
||||
Thread::VirtioRng => virtio_rng_thread_rules(),
|
||||
Thread::VirtioVhostBlk => virtio_vhost_blk_thread_rules(),
|
||||
Thread::VirtioVhostFs => virtio_vhost_fs_thread_rules(),
|
||||
Thread::VirtioVhostNet => virtio_vhost_net_thread_rules(),
|
||||
Thread::VirtioVhostNetCtl => virtio_vhost_net_ctl_thread_rules(),
|
||||
Thread::VirtioVsock => virtio_vsock_thread_rules(),
|
||||
Thread::VirtioWatchdog => virtio_watchdog_thread_rules(),
|
||||
};
|
||||
|
||||
Ok(SeccompFilter::new(
|
||||
|
||||
@@ -14,7 +14,7 @@ extern crate vmm_sys_util;
|
||||
use super::VirtioPciCommonConfig;
|
||||
use crate::transport::VirtioTransport;
|
||||
use crate::{
|
||||
Queue, VirtioDevice, VirtioDeviceType, VirtioInterrupt, VirtioInterruptType,
|
||||
ActivateResult, Queue, VirtioDevice, VirtioDeviceType, VirtioInterrupt, VirtioInterruptType,
|
||||
DEVICE_ACKNOWLEDGE, DEVICE_DRIVER, DEVICE_DRIVER_OK, DEVICE_FAILED, DEVICE_FEATURES_OK,
|
||||
DEVICE_INIT, VIRTIO_MSI_NO_VECTOR,
|
||||
};
|
||||
@@ -30,8 +30,8 @@ use std::cmp;
|
||||
use std::io::Write;
|
||||
use std::num::Wrapping;
|
||||
use std::result;
|
||||
use std::sync::atomic::{AtomicU16, AtomicUsize, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::atomic::{AtomicBool, AtomicU16, AtomicUsize, Ordering};
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use vm_allocator::SystemAllocator;
|
||||
use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig,
|
||||
@@ -292,7 +292,7 @@ pub struct VirtioPciDevice {
|
||||
|
||||
// Virtio device reference and status
|
||||
device: Arc<Mutex<dyn VirtioDevice>>,
|
||||
device_activated: bool,
|
||||
device_activated: Arc<AtomicBool>,
|
||||
|
||||
// PCI interrupts.
|
||||
interrupt_status: Arc<AtomicUsize>,
|
||||
@@ -323,10 +323,17 @@ pub struct VirtioPciDevice {
|
||||
|
||||
// Details of bar regions to free
|
||||
bar_regions: Vec<(GuestAddress, GuestUsize, PciBarRegionType)>,
|
||||
|
||||
// EventFd to signal on to request activation
|
||||
activate_evt: EventFd,
|
||||
|
||||
// Barrier that is used to wait on for activation
|
||||
activate_barrier: Arc<Barrier>,
|
||||
}
|
||||
|
||||
impl VirtioPciDevice {
|
||||
/// Constructs a new PCI transport for the given virtio device.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
id: String,
|
||||
memory: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
@@ -335,6 +342,7 @@ impl VirtioPciDevice {
|
||||
iommu_mapping_cb: Option<Arc<VirtioIommuRemapping>>,
|
||||
interrupt_manager: &Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
pci_device_bdf: u32,
|
||||
activate_evt: EventFd,
|
||||
) -> Result<Self> {
|
||||
let device_clone = device.clone();
|
||||
let locked_device = device_clone.lock().unwrap();
|
||||
@@ -420,7 +428,7 @@ impl VirtioPciDevice {
|
||||
msix_config,
|
||||
msix_num,
|
||||
device,
|
||||
device_activated: false,
|
||||
device_activated: Arc::new(AtomicBool::new(false)),
|
||||
interrupt_status: Arc::new(AtomicUsize::new(0)),
|
||||
virtio_interrupt: None,
|
||||
queues,
|
||||
@@ -432,6 +440,8 @@ impl VirtioPciDevice {
|
||||
interrupt_source_group,
|
||||
cap_pci_cfg_info: VirtioPciCfgCapInfo::default(),
|
||||
bar_regions: vec![],
|
||||
activate_evt,
|
||||
activate_barrier: Arc::new(Barrier::new(2)),
|
||||
};
|
||||
|
||||
if let Some(msix_config) = &virtio_pci_device.msix_config {
|
||||
@@ -447,14 +457,15 @@ impl VirtioPciDevice {
|
||||
|
||||
fn state(&self) -> VirtioPciDeviceState {
|
||||
VirtioPciDeviceState {
|
||||
device_activated: self.device_activated,
|
||||
device_activated: self.device_activated.load(Ordering::Acquire),
|
||||
interrupt_status: self.interrupt_status.load(Ordering::Acquire),
|
||||
queues: self.queues.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &VirtioPciDeviceState) -> std::result::Result<(), Error> {
|
||||
self.device_activated = state.device_activated;
|
||||
self.device_activated
|
||||
.store(state.device_activated, Ordering::Release);
|
||||
self.interrupt_status
|
||||
.store(state.interrupt_status, Ordering::Release);
|
||||
|
||||
@@ -504,14 +515,6 @@ impl VirtioPciDevice {
|
||||
self.common_config.driver_status == DEVICE_INIT as u8
|
||||
}
|
||||
|
||||
fn are_queues_valid(&self) -> bool {
|
||||
if let Some(mem) = self.memory.as_ref() {
|
||||
self.queues.iter().all(|q| q.is_valid(&mem.memory()))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// This function is used by the caller to provide the expected base address
|
||||
// for the virtio-pci configuration BAR.
|
||||
pub fn set_config_bar_addr(&mut self, bar_addr: u64) {
|
||||
@@ -617,18 +620,19 @@ impl VirtioPciDevice {
|
||||
}
|
||||
}
|
||||
|
||||
fn write_cap_pci_cfg(&mut self, offset: usize, data: &[u8]) {
|
||||
fn write_cap_pci_cfg(&mut self, offset: usize, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
let cap_slice = self.cap_pci_cfg_info.cap.as_mut_slice();
|
||||
let data_len = data.len();
|
||||
let cap_len = cap_slice.len();
|
||||
if offset + data_len > cap_len {
|
||||
error!("Failed to write cap_pci_cfg to config space");
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
if offset < std::mem::size_of::<VirtioPciCap>() {
|
||||
let (_, right) = cap_slice.split_at_mut(offset);
|
||||
right[..data_len].copy_from_slice(&data[..]);
|
||||
None
|
||||
} else {
|
||||
// Safe since we know self.cap_pci_cfg_info.cap.cap.offset is 32bits long.
|
||||
let bar_offset: u32 =
|
||||
@@ -640,6 +644,42 @@ impl VirtioPciDevice {
|
||||
pub fn virtio_device(&self) -> Arc<Mutex<dyn VirtioDevice>> {
|
||||
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 = self.queues.clone();
|
||||
queues.retain(|q| q.ready);
|
||||
for (i, queue) in queues.iter().enumerate() {
|
||||
queue_evts.push(self.queue_evts[i].try_clone().unwrap());
|
||||
if !queue.is_valid(&mem.memory()) {
|
||||
error!("Queue {} is not valid", i);
|
||||
}
|
||||
}
|
||||
return device.activate(mem, virtio_interrupt, queues, queue_evts);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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 needs_activation(&self) -> bool {
|
||||
!self.device_activated.load(Ordering::SeqCst) && self.is_driver_ready()
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioTransport for VirtioPciDevice {
|
||||
@@ -715,7 +755,7 @@ impl VirtioInterrupt for VirtioInterruptMsix {
|
||||
.trigger(vector as InterruptIndex)
|
||||
}
|
||||
|
||||
fn notifier(&self, int_type: &VirtioInterruptType, queue: Option<&Queue>) -> Option<&EventFd> {
|
||||
fn notifier(&self, int_type: &VirtioInterruptType, queue: Option<&Queue>) -> Option<EventFd> {
|
||||
let vector = match int_type {
|
||||
VirtioInterruptType::Config => self.config_vector.load(Ordering::Acquire),
|
||||
VirtioInterruptType::Queue => {
|
||||
@@ -733,7 +773,12 @@ impl VirtioInterrupt for VirtioInterruptMsix {
|
||||
}
|
||||
|
||||
impl PciDevice for VirtioPciDevice {
|
||||
fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
|
||||
fn write_config_register(
|
||||
&mut self,
|
||||
reg_idx: usize,
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
) -> Option<Arc<Barrier>> {
|
||||
// Handle the special case where the capability VIRTIO_PCI_CAP_PCI_CFG
|
||||
// is accessed. This capability has a special meaning as it allows the
|
||||
// guest to access other capabilities without mapping the PCI BAR.
|
||||
@@ -743,10 +788,11 @@ impl PciDevice for VirtioPciDevice {
|
||||
<= self.cap_pci_cfg_info.offset + self.cap_pci_cfg_info.cap.bytes().len()
|
||||
{
|
||||
let offset = base + offset as usize - self.cap_pci_cfg_info.offset;
|
||||
self.write_cap_pci_cfg(offset, data);
|
||||
self.write_cap_pci_cfg(offset, data)
|
||||
} else {
|
||||
self.configuration
|
||||
.write_config_register(reg_idx, offset, data);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -925,7 +971,7 @@ impl PciDevice for VirtioPciDevice {
|
||||
}
|
||||
}
|
||||
|
||||
fn write_bar(&mut self, _base: u64, offset: u64, data: &[u8]) {
|
||||
fn write_bar(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
match offset {
|
||||
o if o < COMMON_CONFIG_BAR_OFFSET + COMMON_CONFIG_SIZE => self.common_config.write(
|
||||
o - COMMON_CONFIG_BAR_OFFSET,
|
||||
@@ -969,33 +1015,24 @@ impl PciDevice for VirtioPciDevice {
|
||||
_ => (),
|
||||
};
|
||||
|
||||
if !self.device_activated && self.is_driver_ready() && self.are_queues_valid() {
|
||||
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();
|
||||
device
|
||||
.activate(
|
||||
mem,
|
||||
virtio_interrupt,
|
||||
self.queues.clone(),
|
||||
self.queue_evts.split_off(0),
|
||||
)
|
||||
.expect("Failed to activate device");
|
||||
self.device_activated = true;
|
||||
}
|
||||
}
|
||||
// Try and activate the device if the driver status has changed
|
||||
if self.needs_activation() {
|
||||
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());
|
||||
}
|
||||
|
||||
// Device has been reset by the driver
|
||||
if self.device_activated && self.is_driver_init() {
|
||||
if self.device_activated.load(Ordering::SeqCst) && self.is_driver_init() {
|
||||
let mut device = self.device.lock().unwrap();
|
||||
if let Some((virtio_interrupt, mut queue_evts)) = device.reset() {
|
||||
// Upon reset the device returns its interrupt EventFD and it's queue EventFDs
|
||||
if let Some(virtio_interrupt) = device.reset() {
|
||||
// Upon reset the device returns its interrupt EventFD
|
||||
self.virtio_interrupt = Some(virtio_interrupt);
|
||||
self.queue_evts.append(&mut queue_evts);
|
||||
|
||||
self.device_activated = false;
|
||||
self.device_activated.store(false, Ordering::SeqCst);
|
||||
|
||||
// Reset queue readiness (changes queue_enable), queue sizes
|
||||
// and selected_queue as per spec for reset
|
||||
@@ -1006,6 +1043,8 @@ impl PciDevice for VirtioPciDevice {
|
||||
self.common_config.driver_status = crate::DEVICE_FAILED as u8;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn as_any(&mut self) -> &mut dyn Any {
|
||||
@@ -1018,7 +1057,7 @@ impl BusDevice for VirtioPciDevice {
|
||||
self.read_bar(base, offset, data)
|
||||
}
|
||||
|
||||
fn write(&mut self, base: u64, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
self.write_bar(base, offset, data)
|
||||
}
|
||||
}
|
||||
@@ -1110,26 +1149,10 @@ impl Snapshottable for VirtioPciDevice {
|
||||
// Then we can activate the device, as we know at this point that
|
||||
// the virtqueues are in the right state and the device is ready
|
||||
// to be activated, which will spawn each virtio worker thread.
|
||||
if self.device_activated && self.is_driver_ready() && self.are_queues_valid() {
|
||||
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();
|
||||
device
|
||||
.activate(
|
||||
mem,
|
||||
virtio_interrupt,
|
||||
self.queues.clone(),
|
||||
self.queue_evts.split_off(0),
|
||||
)
|
||||
.map_err(|e| {
|
||||
MigratableError::Restore(anyhow!(
|
||||
"Failed activating the device: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
}
|
||||
if self.device_activated.load(Ordering::SeqCst) && self.is_driver_ready() {
|
||||
self.activate().map_err(|e| {
|
||||
MigratableError::Restore(anyhow!("Failed activating the device: {:?}", e))
|
||||
})?;
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user