mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
187 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52605cc0e4 | ||
|
|
92beda1e32 | ||
|
|
167fef382a | ||
|
|
5126e9b26e | ||
|
|
59bd682b1f | ||
|
|
8e3b351038 | ||
|
|
3f8d06b47e | ||
|
|
66460765a3 | ||
|
|
b748b8987d | ||
|
|
c9483fb080 | ||
|
|
f52cd3bb85 | ||
|
|
57cd12fabc | ||
|
|
869c649e81 | ||
|
|
a9e30c88df | ||
|
|
1666375a31 | ||
|
|
9a0be7db3a | ||
|
|
324c5deb37 | ||
|
|
09e79a5e9b | ||
|
|
af261f231c | ||
|
|
7122e2989c | ||
|
|
d0b253d15f | ||
|
|
59baa29deb | ||
|
|
19fdf8bc79 | ||
|
|
58b902d036 | ||
|
|
e3213c8a79 | ||
|
|
2e2ce47271 | ||
|
|
d3a8332282 | ||
|
|
c64004b9a9 | ||
|
|
149e424b6e | ||
|
|
b07d471d4f | ||
|
|
345e65c9c2 | ||
|
|
f93aa42319 | ||
|
|
fa4bf92feb | ||
|
|
f30d460fa3 | ||
|
|
2c94773bdc | ||
|
|
9f1d2d34e8 | ||
|
|
57508a4b1c | ||
|
|
012f2572d5 | ||
|
|
b9dbe3a2f7 | ||
|
|
6230929d51 | ||
|
|
04d034a0bc | ||
|
|
f603afc46e | ||
|
|
b68add2d0d | ||
|
|
0f44db5da4 | ||
|
|
a7dccf94cf | ||
|
|
0e6e539d9b | ||
|
|
1cb1cff882 | ||
|
|
fefbc356a2 | ||
|
|
8b37448d28 | ||
|
|
92d083f593 | ||
|
|
58d8795b53 | ||
|
|
b37e2ed378 | ||
|
|
ef8fb9bd25 | ||
|
|
cfafc85b9c | ||
|
|
683491a955 | ||
|
|
2bbb08b2a4 | ||
|
|
b77eb63688 | ||
|
|
6722c303b0 | ||
|
|
6e0bd73c90 | ||
|
|
103fe1f48b | ||
|
|
f4e1b72477 | ||
|
|
65628e8d94 | ||
|
|
851de1e0b3 | ||
|
|
a9ec0f33c0 | ||
|
|
9266ea4995 | ||
|
|
6b8070de95 | ||
|
|
a5712641d2 | ||
|
|
aad4dc3b6b | ||
|
|
f4495de143 | ||
|
|
99d9a3d299 | ||
|
|
df7c728399 | ||
|
|
1e5a4e8d77 | ||
|
|
ff3fb91ba6 | ||
|
|
86c19816c6 | ||
|
|
ad1cb130a5 | ||
|
|
0c75e7f7b2 | ||
|
|
cb171d4a23 | ||
|
|
bc310bb173 | ||
|
|
76f4641004 | ||
|
|
dcb12c05a7 | ||
|
|
078c0408b3 | ||
|
|
b99b2bc990 | ||
|
|
0cd6d96874 | ||
|
|
a5d0ff7039 | ||
|
|
da1ab77848 | ||
|
|
989d3d3960 | ||
|
|
1559b7b684 | ||
|
|
e96474a5bd | ||
|
|
2a36a18be3 | ||
|
|
0e386581e0 | ||
|
|
1f0e5eb66a | ||
|
|
157db33d65 | ||
|
|
40df6c3787 | ||
|
|
b425c5f57c | ||
|
|
b4e3942708 | ||
|
|
802f489e4d | ||
|
|
fdecd94b20 | ||
|
|
2af2cc539f | ||
|
|
7d71319210 | ||
|
|
e710e21744 | ||
|
|
9c658e21a5 | ||
|
|
38620eaea8 | ||
|
|
a388d76228 | ||
|
|
710a860e9b | ||
|
|
83f22ac779 | ||
|
|
71b4aad3f1 | ||
|
|
d7afa3c47e | ||
|
|
073374e2b0 | ||
|
|
d4f40487e3 | ||
|
|
f63cf2ebc0 | ||
|
|
46031cb211 | ||
|
|
c52ccf3992 | ||
|
|
7b31871a36 | ||
|
|
1b58c63734 | ||
|
|
d1435a44af | ||
|
|
a75d71f2c8 | ||
|
|
96209e7a16 | ||
|
|
7a68689794 | ||
|
|
6e6b519a74 | ||
|
|
958ef69295 | ||
|
|
ffc222b26e | ||
|
|
5b706422e8 | ||
|
|
84105992b7 | ||
|
|
0235ed3388 | ||
|
|
58066e2da4 | ||
|
|
62d6553714 | ||
|
|
2a45e44bda | ||
|
|
099cdd2af8 | ||
|
|
340fd6571a | ||
|
|
02f951a9c3 | ||
|
|
22be5f9d0f | ||
|
|
a9ccb20e5a | ||
|
|
2b150ac2ea | ||
|
|
204bf2e3f5 | ||
|
|
2a40fe5541 | ||
|
|
7ae4e2b8fe | ||
|
|
f17f4079b4 | ||
|
|
342851c88c | ||
|
|
37c3b0429a | ||
|
|
af14044fb8 | ||
|
|
fa39d1e9b9 | ||
|
|
00714dc54a | ||
|
|
87fb9cedc9 | ||
|
|
ce2928d842 | ||
|
|
a18b08c682 | ||
|
|
43935c3b95 | ||
|
|
c3c550c454 | ||
|
|
7da632def6 | ||
|
|
f38ef20e3b | ||
|
|
a033b1ef67 | ||
|
|
ffa7d27b5e | ||
|
|
da725daca0 | ||
|
|
0fe0eb4c97 | ||
|
|
8877211d9e | ||
|
|
f907ff5d82 | ||
|
|
89a2562c5a | ||
|
|
29cf637f3f | ||
|
|
fd9fa2a681 | ||
|
|
756aebafda | ||
|
|
d4d63502be | ||
|
|
961793ba8c | ||
|
|
cbeed02a7c | ||
|
|
10eb687687 | ||
|
|
83cc554f90 | ||
|
|
7d8d27c1b4 | ||
|
|
d692dfb8e3 | ||
|
|
7ad58457b0 | ||
|
|
62d2189ade | ||
|
|
7e7855bc8f | ||
|
|
11cc35e6b5 | ||
|
|
89677c3181 | ||
|
|
307cfeba27 | ||
|
|
43bb09df50 | ||
|
|
81d570e271 | ||
|
|
31ca22d4b6 | ||
|
|
cf995451a2 | ||
|
|
4fea40f008 | ||
|
|
2daab89987 | ||
|
|
1bc63e7848 | ||
|
|
2115a41568 | ||
|
|
4b4354a52c | ||
|
|
67751d07d6 | ||
|
|
509fd8b03e | ||
|
|
1294f052b2 | ||
|
|
01b38b9c59 | ||
|
|
db51e9e209 | ||
|
|
57dfcc06d3 |
415
Cargo.lock
generated
415
Cargo.lock
generated
@@ -11,18 +11,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.19"
|
||||
version = "0.7.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
|
||||
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.65"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
|
||||
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -59,17 +59,6 @@ dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
@@ -118,9 +107,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
version = "1.0.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -130,33 +119,31 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.2.22"
|
||||
version = "4.0.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750"
|
||||
checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"is-terminal",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"terminal_size",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.4"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||
checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "27.0.0"
|
||||
version = "28.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -175,6 +162,7 @@ dependencies = [
|
||||
"signal-hook",
|
||||
"test_infra",
|
||||
"thiserror",
|
||||
"tpm",
|
||||
"tracer",
|
||||
"vm-memory",
|
||||
"vmm",
|
||||
@@ -199,9 +187,9 @@ checksum = "55626594feae15d266d52440b26ff77de0e22230cf0c113abe619084c1ddc910"
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.14.1"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02"
|
||||
checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
@@ -209,9 +197,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.14.1"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f"
|
||||
checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
@@ -223,9 +211,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.14.1"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5"
|
||||
checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
@@ -244,6 +232,9 @@ dependencies = [
|
||||
"hypervisor",
|
||||
"libc",
|
||||
"log",
|
||||
"phf",
|
||||
"thiserror",
|
||||
"tpm",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"vm-device",
|
||||
@@ -274,12 +265,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.9.1"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272"
|
||||
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"is-terminal",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
@@ -363,9 +354,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.7"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@@ -378,17 +369,11 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.20"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7a30908dbce072eca83216eab939d2290080e00ca71611b96a09e5cdce5f3fa"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -437,16 +422,6 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
@@ -458,15 +433,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "0.7.3"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ea37f355c05dde75b84bba2d767906ad522e97cd9e2eef2be7a4ab7fb442c06"
|
||||
checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.5.6"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a645fd8e7048c2f54050f7d0d4de8f624ada437e043bc7d72609bd08d70120b"
|
||||
checksum = "7ba34abb5175052fc1a2227a10d2275b7386c9990167de9786c0b88d8b062330"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -482,15 +461,27 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.3"
|
||||
name = "is-terminal"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
|
||||
checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||
|
||||
[[package]]
|
||||
name = "kvm-bindings"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.5.0-tdx#52e56d0e8ef0f6ea32fc0492e6a175b73617a49f"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.6.0-tdx#7d9ffb47e5b9b1989577258800a0f57c93f1445f"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -499,8 +490,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kvm-ioctls"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/rust-vmm/kvm-ioctls?branch=main#e6739aeebcc56dfea62216b2cc4cddd13d0a1182"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3a321cabd827642499c77e27314f388dd83a717a5ca716b86476fb947f73ae4"
|
||||
dependencies = [
|
||||
"kvm-bindings",
|
||||
"libc",
|
||||
@@ -515,9 +507,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.133"
|
||||
version = "0.2.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
|
||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||
|
||||
[[package]]
|
||||
name = "libssh2-sys"
|
||||
@@ -547,18 +539,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-loader"
|
||||
version = "0.6.0"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62a2f912deca034ec34b0a43a390059ea98daac40e440ebe8bea88f3315fe168"
|
||||
checksum = "b9259ddbfbb52cc918f6bbc60390004ddd0228cf1d85f402009ff2b3d95de83f"
|
||||
dependencies = [
|
||||
"vm-memory",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.0.46"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d"
|
||||
checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
@@ -594,7 +586,7 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#863b0370ba7e57f7df5b908ada9e5b44809ccae9"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#4b18a043e997da5b5f679e3defc279fec908753e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"vmm-sys-util",
|
||||
@@ -603,7 +595,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mshv-bindings"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#2d0aaa16b00eec5470aed0d4e8f8ce24550df6d1"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#10d0c5208cc41a3958e0fd31fabf2c88b18a97b5"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"serde",
|
||||
@@ -615,7 +607,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mshv-ioctls"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#2d0aaa16b00eec5470aed0d4e8f8ce24550df6d1"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#10d0c5208cc41a3958e0fd31fabf2c88b18a97b5"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mshv-bindings",
|
||||
@@ -671,24 +663,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.15.0"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
|
||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "111.22.0+1.1.1q"
|
||||
version = "111.24.0+1.1.1s"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f31f0d509d1c1ae9cada2f9539ff8f37933831fd5098879e482aa687d659853"
|
||||
checksum = "3498f259dab01178c6228c6b00dcef0ed2a2d5e20d648c017861227773ea4abd"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.76"
|
||||
version = "0.9.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce"
|
||||
checksum = "5454462c0eced1e97f2ec09036abc8da362e66802f66fd20f86854d9d8cbcbc4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
@@ -704,9 +696,9 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.3.0"
|
||||
version = "6.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
|
||||
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
@@ -721,9 +713,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.5"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
|
||||
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"instant",
|
||||
@@ -735,9 +727,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.9"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
|
||||
checksum = "cf1c2c742266c2f1041c914ba65355a83ae8747b05f208319784083583494b4b"
|
||||
|
||||
[[package]]
|
||||
name = "pci"
|
||||
@@ -776,10 +768,52 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.25"
|
||||
name = "phf"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
||||
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||
|
||||
[[package]]
|
||||
name = "pnet"
|
||||
@@ -872,9 +906,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.44"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58"
|
||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -899,6 +933,21 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "rate_limiter"
|
||||
version = "0.1.0"
|
||||
@@ -930,9 +979,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.6.0"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -941,15 +990,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.27"
|
||||
version = "0.6.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||
|
||||
[[package]]
|
||||
name = "remain"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06df529c0d271b27ac4a2c260f5ce2914b60bd44702cecec7b9f271adbdde23b"
|
||||
checksum = "1a81bc6a3aaeb180f767bd2bda8c03bac5b93b3b69a783c66f35d16a4df276cf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -967,9 +1016,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.35.10"
|
||||
version = "0.36.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af895b90e5c071badc3136fc10ff0bcfc98747eadbaf43ed8f214e07ba8f8477"
|
||||
checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -1008,18 +1057,18 @@ checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.145"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
|
||||
checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.145"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
|
||||
checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1028,9 +1077,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.85"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
|
||||
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -1039,9 +1088,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "2.0.1"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368f2d60d049ea019a84dcd6687b0d1e0030fe663ae105039bdf967ed5e6a9a7"
|
||||
checksum = "25bf4a5a814902cd1014dbccfa4d4560fb8432c779471e96e035602519f82eef"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_with_macros",
|
||||
@@ -1049,9 +1098,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "2.0.1"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ccadfacf6cf10faad22bbadf55986bdd0856edfb5d9210aa1dcf1f516e84e93"
|
||||
checksum = "e3452b4c0f6c1e357f73fdb87cd1efabaa12acf328c7a528e252893baeb3f4aa"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
@@ -1083,10 +1132,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.9.0"
|
||||
name = "siphasher"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
|
||||
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "ssh2"
|
||||
@@ -1114,27 +1169,15 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.101"
|
||||
version = "1.0.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
|
||||
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.12.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
@@ -1146,9 +1189,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.2.1"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8440c860cf79def6164e4a0a983bcc2305d82419177a0e0c71930d049e3ac5a1"
|
||||
checksum = "cb20089a8ba2b69debd491f8d2d023761cbf196e999218c591fa1e7e15a21907"
|
||||
dependencies = [
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
@@ -1169,15 +1212,6 @@ dependencies = [
|
||||
"wait-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
|
||||
dependencies = [
|
||||
"terminal_size",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.37"
|
||||
@@ -1198,6 +1232,18 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tpm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"byteorder",
|
||||
"libc",
|
||||
"log",
|
||||
"thiserror",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracer"
|
||||
version = "0.1.0"
|
||||
@@ -1211,21 +1257,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.4"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.1.2"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
|
||||
checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
@@ -1238,9 +1278,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "versionize"
|
||||
version = "0.1.6"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7429cf68de8f091b667d27323ed323afd39584a56d533995b12ddd748e5e6ca9"
|
||||
checksum = "d6e2495726cf917e7ba7ec8bf0f0fceab543dd38d0a4195ed6bef331e38a290f"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"crc64",
|
||||
@@ -1265,17 +1305,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vfio-bindings"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43449b404c488f70507dca193debd4bea361fe8089869b947adc19720e464bce"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#7ef33f6759ae6d37a54cd66f749aa47c1c81509c"
|
||||
dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vfio-ioctls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#bf7c9b997b4ad0ca1ab4bfe96dc1b10d4bc5be24"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#7ef33f6759ae6d37a54cd66f749aa47c1c81509c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
@@ -1320,9 +1359,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vhost"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79243657c76e5c90dcbf60187c842614f6dfc7123972c55bb3bcc446792aca93"
|
||||
checksum = "c9b791c5b0717a0558888a4cf7240cea836f39a99cb342e12ce633dcaa078072"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -1332,9 +1371,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vhost-user-backend"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a0fc7d5f8e2943cd9f2ecd58be3f2078add863a49573d14dd9d64e1ab26544c"
|
||||
checksum = "9f237b91db4ac339d639fb43398b52d785fa51e3c7760ac9425148863c1f4303"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
@@ -1426,9 +1465,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.6.1"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "435dd49c7b38419729afd43675850c7b5dc4728f2fabd70c7a9079a331e4f8c6"
|
||||
checksum = "19e927d93d54c365034fd7f31a5f458a1f540de4a37c52e892670dad9692173c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"virtio-bindings",
|
||||
@@ -1461,13 +1500,13 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vm-fdt"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#8a19dcc94a4fb0044ff6e879e069901e0a74a943"
|
||||
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#c5a99ab71b130435927d19b50c85fcd5ce904a8c"
|
||||
|
||||
[[package]]
|
||||
name = "vm-memory"
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "583f213899e8a5eea23d9c507252d4bed5bc88f0ecbe0783262f80034630744b"
|
||||
checksum = "688a70366615b45575a424d9c665561c1b5ab2224d494f706b6a6812911a827c"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"libc",
|
||||
@@ -1547,9 +1586,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vmm-sys-util"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08604d7be03eb26e33b3cee3ed4aef2bf550b305d1cca60e84da5d28d3790b62"
|
||||
checksum = "cc06a16ee8ebf0d9269aed304030b0d20a866b8b3dd3d4ce532596ac567a0d24"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -1605,46 +1644,60 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.36.1"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.36.1"
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.36.1"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.36.1"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.36.1"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.36.1"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
@@ -1658,11 +1711,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0fbc82b82efe24da867ee52e015e58178684bd9dd64c34e66bdf21da2582a9f"
|
||||
checksum = "6505e6815af7de1746a08f69c69606bb45695a17149517680f3b2149713b19a3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
24
Cargo.toml
24
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cloud-hypervisor"
|
||||
version = "27.0.0"
|
||||
version = "28.1.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
default-run = "cloud-hypervisor"
|
||||
@@ -18,38 +18,38 @@ codegen-units = 1
|
||||
opt-level = "s"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.65"
|
||||
anyhow = "1.0.66"
|
||||
api_client = { path = "api_client" }
|
||||
clap = { version = "3.2.22", features = ["wrap_help","cargo"] }
|
||||
clap = { version = "4.0.29", features = ["wrap_help","cargo","string"] }
|
||||
epoll = "4.3.1"
|
||||
event_monitor = { path = "event_monitor" }
|
||||
hypervisor = { path = "hypervisor" }
|
||||
libc = "0.2.133"
|
||||
libc = "0.2.138"
|
||||
log = { version = "0.4.17", features = ["std"] }
|
||||
option_parser = { path = "option_parser" }
|
||||
seccompiler = "0.3.0"
|
||||
serde_json = "1.0.85"
|
||||
serde_json = "1.0.89"
|
||||
signal-hook = "0.3.14"
|
||||
thiserror = "1.0.37"
|
||||
tpm = { path = "tpm"}
|
||||
tracer = { path = "tracer" }
|
||||
vmm = { path = "vmm" }
|
||||
vmm-sys-util = "0.10.0"
|
||||
vm-memory = "0.9.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
vm-memory = "0.10.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.2.22", features = ["cargo"] }
|
||||
clap = { version = "4.0.29", features = ["cargo"] }
|
||||
|
||||
# List of patched crates
|
||||
[patch.crates-io]
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0-tdx" }
|
||||
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "main" }
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx" }
|
||||
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
|
||||
|
||||
[dev-dependencies]
|
||||
dirs = "4.0.0"
|
||||
net_util = { path = "net_util" }
|
||||
once_cell = "1.15.0"
|
||||
serde_json = "1.0.85"
|
||||
once_cell = "1.16.0"
|
||||
serde_json = "1.0.89"
|
||||
test_infra = { path = "test_infra" }
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
|
||||
642
Jenkinsfile
vendored
642
Jenkinsfile
vendored
@@ -1,337 +1,333 @@
|
||||
def runWorkers = true
|
||||
pipeline{
|
||||
agent none
|
||||
stages {
|
||||
stage ('Early checks') {
|
||||
agent { node { label 'built-in' } }
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Check for documentation only changes') {
|
||||
when {
|
||||
expression {
|
||||
return docsFileOnly()
|
||||
}
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
runWorkers = false
|
||||
echo "Documentation only changes, no need to run the CI"
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Check for fuzzer files only changes') {
|
||||
when {
|
||||
expression {
|
||||
return fuzzFileOnly()
|
||||
}
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
runWorkers = false
|
||||
echo "Fuzzer cargo files only changes, no need to run the CI"
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Check for RFC/WIP builds') {
|
||||
when {
|
||||
changeRequest comparator: 'REGEXP', title: '.*(rfc|RFC|wip|WIP).*'
|
||||
beforeAgent true
|
||||
}
|
||||
steps {
|
||||
error("Failing as this is marked as a WIP or RFC PR.")
|
||||
}
|
||||
}
|
||||
stage ('Cancel older builds') {
|
||||
when { not { branch 'main' } }
|
||||
steps {
|
||||
cancelPreviousBuilds()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Build') {
|
||||
parallel {
|
||||
stage ('Worker build') {
|
||||
agent { node { label 'jammy' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Prepare environment') {
|
||||
steps {
|
||||
sh "scripts/prepare_vdpa.sh"
|
||||
}
|
||||
}
|
||||
stage ('Run OpenAPI tests') {
|
||||
steps {
|
||||
sh "scripts/run_openapi_tests.sh"
|
||||
}
|
||||
}
|
||||
stage ('Run unit tests') {
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --unit"
|
||||
}
|
||||
}
|
||||
stage ('Run integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "sudo modprobe openvswitch"
|
||||
sh "scripts/dev_cli.sh tests --integration"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('AArch64 worker build') {
|
||||
agent { node { label 'bionic-arm64' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
environment {
|
||||
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Run unit tests') {
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --unit --libc musl"
|
||||
}
|
||||
}
|
||||
stage ('Run integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "sudo modprobe openvswitch"
|
||||
sh "scripts/dev_cli.sh tests --integration --libc musl"
|
||||
}
|
||||
}
|
||||
stage ('Install azure-cli') {
|
||||
steps {
|
||||
installAzureCli("bionic", "arm64")
|
||||
}
|
||||
}
|
||||
stage ('Download Windows image') {
|
||||
steps {
|
||||
sh '''#!/bin/bash -x
|
||||
IMG_BASENAME=windows-11-iot-enterprise-aarch64.raw
|
||||
IMG_PATH=$HOME/workloads/$IMG_BASENAME
|
||||
IMG_GZ_PATH=$HOME/workloads/$IMG_BASENAME.gz
|
||||
IMG_GZ_BLOB_NAME=windows-11-iot-enterprise-aarch64-9-min.raw.gz
|
||||
cp "scripts/$IMG_BASENAME.sha1" "$HOME/workloads/"
|
||||
pushd "$HOME/workloads"
|
||||
if sha1sum "$IMG_BASENAME.sha1" --check; then
|
||||
exit
|
||||
fi
|
||||
popd
|
||||
mkdir -p "$HOME/workloads"
|
||||
az storage blob download \
|
||||
--container-name private-images \
|
||||
--file "$IMG_GZ_PATH" \
|
||||
--name "$IMG_GZ_BLOB_NAME" \
|
||||
--connection-string "$AZURE_CONNECTION_STRING"
|
||||
gzip -d $IMG_GZ_PATH
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage ('Run Windows guest integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-windows --libc musl"
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||
deleteDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Worker build (musl)') {
|
||||
agent { node { label 'jammy' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Prepare environment') {
|
||||
steps {
|
||||
sh "scripts/prepare_vdpa.sh"
|
||||
}
|
||||
}
|
||||
stage ('Run unit tests for musl') {
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --unit --libc musl"
|
||||
}
|
||||
}
|
||||
stage ('Run integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "sudo modprobe openvswitch"
|
||||
sh "scripts/dev_cli.sh tests --integration --libc musl"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Worker build - Windows guest') {
|
||||
agent { node { label 'jammy' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
environment {
|
||||
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Install azure-cli') {
|
||||
steps {
|
||||
installAzureCli("jammy", "amd64")
|
||||
}
|
||||
}
|
||||
stage ('Download assets') {
|
||||
steps {
|
||||
sh "mkdir ${env.HOME}/workloads"
|
||||
sh 'az storage blob download --container-name private-images --file "$HOME/workloads/windows-server-2019.raw" --name windows-server-2019.raw --connection-string "$AZURE_CONNECTION_STRING"'
|
||||
}
|
||||
}
|
||||
stage ('Run Windows guest integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-windows"
|
||||
}
|
||||
}
|
||||
stage ('Run Windows guest integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-windows --libc musl"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Worker build - Live Migration') {
|
||||
agent { node { label 'jammy' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Run live-migration integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "sudo modprobe openvswitch"
|
||||
sh "scripts/dev_cli.sh tests --integration-live-migration"
|
||||
}
|
||||
}
|
||||
stage ('Run live-migration integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "sudo modprobe openvswitch"
|
||||
sh "scripts/dev_cli.sh tests --integration-live-migration --libc musl"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
regression {
|
||||
script {
|
||||
if (env.BRANCH_NAME == 'main') {
|
||||
slackSend (color: '#ff0000', message: '"main" branch build is now failing', channel: '#jenkins-ci')
|
||||
}
|
||||
}
|
||||
}
|
||||
fixed {
|
||||
script {
|
||||
if (env.BRANCH_NAME == 'main') {
|
||||
slackSend (color: '#00ff00', message: '"main" branch build is now fixed', channel: '#jenkins-ci')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pipeline {
|
||||
agent none
|
||||
stages {
|
||||
stage('Early checks') {
|
||||
agent { node { label 'built-in' } }
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage('Check for documentation only changes') {
|
||||
when {
|
||||
expression {
|
||||
return docsFileOnly()
|
||||
}
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
runWorkers = false
|
||||
echo 'Documentation only changes, no need to run the CI'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Check for fuzzer files only changes') {
|
||||
when {
|
||||
expression {
|
||||
return fuzzFileOnly()
|
||||
}
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
runWorkers = false
|
||||
echo 'Fuzzer cargo files only changes, no need to run the CI'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Check for RFC/WIP builds') {
|
||||
when {
|
||||
changeRequest comparator: 'REGEXP', title: '.*(rfc|RFC|wip|WIP).*'
|
||||
beforeAgent true
|
||||
}
|
||||
steps {
|
||||
error('Failing as this is marked as a WIP or RFC PR.')
|
||||
}
|
||||
}
|
||||
stage('Cancel older builds') {
|
||||
when { not { branch 'main' } }
|
||||
steps {
|
||||
cancelPreviousBuilds()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Build') {
|
||||
parallel {
|
||||
stage('Worker build') {
|
||||
agent { node { label 'jammy' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage('Prepare environment') {
|
||||
steps {
|
||||
sh 'scripts/prepare_vdpa.sh'
|
||||
}
|
||||
}
|
||||
stage('Run OpenAPI tests') {
|
||||
steps {
|
||||
sh 'scripts/run_openapi_tests.sh'
|
||||
}
|
||||
}
|
||||
stage('Run unit tests') {
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --unit'
|
||||
}
|
||||
}
|
||||
stage('Run integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'sudo modprobe openvswitch'
|
||||
sh 'scripts/dev_cli.sh tests --integration'
|
||||
}
|
||||
}
|
||||
stage('Run live-migration integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'sudo modprobe openvswitch'
|
||||
sh 'scripts/dev_cli.sh tests --integration-live-migration'
|
||||
}
|
||||
}
|
||||
stage('Run unit tests for musl') {
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --unit --libc musl'
|
||||
}
|
||||
}
|
||||
stage('Run integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'sudo modprobe openvswitch'
|
||||
sh 'scripts/dev_cli.sh tests --integration --libc musl'
|
||||
}
|
||||
}
|
||||
stage('Run live-migration integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'sudo modprobe openvswitch'
|
||||
sh 'scripts/dev_cli.sh tests --integration-live-migration --libc musl'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('AArch64 worker build') {
|
||||
agent { node { label 'bionic-arm64' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
environment {
|
||||
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage('Run unit tests') {
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --unit --libc musl'
|
||||
}
|
||||
}
|
||||
stage('Run integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'sudo modprobe openvswitch'
|
||||
sh 'scripts/dev_cli.sh tests --integration --libc musl'
|
||||
}
|
||||
}
|
||||
stage('Install azure-cli') {
|
||||
steps {
|
||||
installAzureCli('bionic', 'arm64')
|
||||
}
|
||||
}
|
||||
stage('Download Windows image') {
|
||||
steps {
|
||||
sh '''#!/bin/bash -x
|
||||
IMG_BASENAME=windows-11-iot-enterprise-aarch64.raw
|
||||
IMG_PATH=$HOME/workloads/$IMG_BASENAME
|
||||
IMG_GZ_PATH=$HOME/workloads/$IMG_BASENAME.gz
|
||||
IMG_GZ_BLOB_NAME=windows-11-iot-enterprise-aarch64-9-min.raw.gz
|
||||
cp "scripts/$IMG_BASENAME.sha1" "$HOME/workloads/"
|
||||
pushd "$HOME/workloads"
|
||||
if sha1sum "$IMG_BASENAME.sha1" --check; then
|
||||
exit
|
||||
fi
|
||||
popd
|
||||
mkdir -p "$HOME/workloads"
|
||||
az storage blob download \
|
||||
--container-name private-images \
|
||||
--file "$IMG_GZ_PATH" \
|
||||
--name "$IMG_GZ_BLOB_NAME" \
|
||||
--connection-string "$AZURE_CONNECTION_STRING"
|
||||
gzip -d $IMG_GZ_PATH
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Run Windows guest integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --integration-windows --libc musl'
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||
deleteDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Worker build - Windows guest') {
|
||||
agent { node { label 'jammy' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
environment {
|
||||
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage('Install azure-cli') {
|
||||
steps {
|
||||
installAzureCli('jammy', 'amd64')
|
||||
}
|
||||
}
|
||||
stage('Download assets') {
|
||||
steps {
|
||||
sh "mkdir ${env.HOME}/workloads"
|
||||
sh 'az storage blob download --container-name private-images --file "$HOME/workloads/windows-server-2019.raw" --name windows-server-2019.raw --connection-string "$AZURE_CONNECTION_STRING"'
|
||||
}
|
||||
}
|
||||
stage('Run Windows guest integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --integration-windows'
|
||||
}
|
||||
}
|
||||
stage('Run Windows guest integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --integration-windows --libc musl'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Worker build - Metrics') {
|
||||
agent { node { label 'jammy-metrics' } }
|
||||
when {
|
||||
branch 'main'
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
environment {
|
||||
METRICS_PUBLISH_KEY = credentials('52e0945f-ce7a-43d1-87af-67d1d87cc40f')
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage('Run metrics tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --metrics -- -- --report-file /root/workloads/metrics.json'
|
||||
}
|
||||
}
|
||||
stage('Upload metrics report') {
|
||||
steps {
|
||||
sh 'curl -X PUT https://cloud-hypervisor-metrics.azurewebsites.net/api/publishmetrics -H "x-functions-key: $METRICS_PUBLISH_KEY" -T ~/workloads/metrics.json'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
regression {
|
||||
script {
|
||||
if (env.BRANCH_NAME == 'main') {
|
||||
slackSend(color: '#ff0000', message: '"main" branch build is now failing', channel: '#jenkins-ci')
|
||||
}
|
||||
}
|
||||
}
|
||||
fixed {
|
||||
script {
|
||||
if (env.BRANCH_NAME == 'main') {
|
||||
slackSend(color: '#00ff00', message: '"main" branch build is now fixed', channel: '#jenkins-ci')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def cancelPreviousBuilds() {
|
||||
// Check for other instances of this particular build, cancel any that are older than the current one
|
||||
def jobName = env.JOB_NAME
|
||||
def currentBuildNumber = env.BUILD_NUMBER.toInteger()
|
||||
def currentJob = Jenkins.instance.getItemByFullName(jobName)
|
||||
// Check for other instances of this particular build, cancel any that are older than the current one
|
||||
def jobName = env.JOB_NAME
|
||||
def currentBuildNumber = env.BUILD_NUMBER.toInteger()
|
||||
def currentJob = Jenkins.instance.getItemByFullName(jobName)
|
||||
|
||||
// Loop through all instances of this particular job/branch
|
||||
for (def build : currentJob.builds) {
|
||||
if (build.isBuilding() && (build.number.toInteger() < currentBuildNumber)) {
|
||||
echo "Older build still queued. Sending kill signal to build number: ${build.number}"
|
||||
build.doStop()
|
||||
}
|
||||
}
|
||||
// Loop through all instances of this particular job/branch
|
||||
for (def build : currentJob.builds) {
|
||||
if (build.isBuilding() && (build.number.toInteger() < currentBuildNumber)) {
|
||||
echo "Older build still queued. Sending kill signal to build number: ${build.number}"
|
||||
build.doStop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def installAzureCli(distro, arch) {
|
||||
sh "sudo apt install -y ca-certificates curl apt-transport-https lsb-release gnupg"
|
||||
sh "curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null"
|
||||
sh "echo \"deb [arch=${arch}] https://packages.microsoft.com/repos/azure-cli/ ${distro} main\" | sudo tee /etc/apt/sources.list.d/azure-cli.list"
|
||||
sh "sudo apt update"
|
||||
sh "sudo apt install -y azure-cli"
|
||||
sh 'sudo apt install -y ca-certificates curl apt-transport-https lsb-release gnupg'
|
||||
sh 'curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null'
|
||||
sh "echo \"deb [arch=${arch}] https://packages.microsoft.com/repos/azure-cli/ ${distro} main\" | sudo tee /etc/apt/sources.list.d/azure-cli.list"
|
||||
sh 'sudo apt update'
|
||||
sh 'sudo apt install -y azure-cli'
|
||||
}
|
||||
|
||||
def boolean docsFileOnly() {
|
||||
if (env.CHANGE_TARGET == null) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
return sh(
|
||||
@@ -342,7 +338,7 @@ def boolean docsFileOnly() {
|
||||
|
||||
def boolean fuzzFileOnly() {
|
||||
if (env.CHANGE_TARGET == null) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
return sh(
|
||||
|
||||
328
README.md
328
README.md
@@ -5,24 +5,21 @@
|
||||
- [Guest OS](#guest-os)
|
||||
- [2. Getting Started](#2-getting-started)
|
||||
- [Host OS](#host-os)
|
||||
- [Preparation](#preparation)
|
||||
- [Install prerequisites](#install-prerequisites)
|
||||
- [Clone and build](#clone-and-build)
|
||||
- [Containerized builds and tests](#containerized-builds-and-tests)
|
||||
- [Use Prebuilt Binaries](#use-prebuilt-binaries)
|
||||
- [Run](#run)
|
||||
- [Cloud image](#cloud-image)
|
||||
- [Custom kernel and disk image](#custom-kernel-and-disk-image)
|
||||
- [Building your kernel](#building-your-kernel)
|
||||
- [Use Pre-built Binaries](#use-pre-built-binaries)
|
||||
- [Packages](#packages)
|
||||
- [Building from Source](#building-from-source)
|
||||
- [Booting Linux](#booting-linux)
|
||||
- [Firmware Booting](#firmware-booting)
|
||||
- [Custom Kernel and Disk Image](#custom-kernel-and-disk-image)
|
||||
- [Building your Kernel](#building-your-kernel)
|
||||
- [Disk image](#disk-image)
|
||||
- [Booting the guest VM](#booting-the-guest-vm)
|
||||
- [3. Status](#3-status)
|
||||
- [Hot Plug](#hot-plug)
|
||||
- [Device Model](#device-model)
|
||||
- [TODO](#todo)
|
||||
- [Roadmap](#roadmap)
|
||||
- [4. `rust-vmm` project dependency](#4-rust-vmm-project-dependency)
|
||||
- [Firecracker and crosvm](#firecracker-and-crosvm)
|
||||
- [4. Relationship with _Rust VMM_ Project](#4-relationship-with-rust-vmm-project)
|
||||
- [Differences with Firecracker and crosvm](#differences-with-firecracker-and-crosvm)
|
||||
- [5. Community](#5-community)
|
||||
- [Contribute](#contribute)
|
||||
- [Slack](#slack)
|
||||
@@ -32,18 +29,18 @@
|
||||
# 1. What is Cloud Hypervisor?
|
||||
|
||||
Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on
|
||||
top of [KVM](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt)
|
||||
hypervisor and Microsoft Hypervisor (MSHV).
|
||||
top of the [KVM](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt)
|
||||
hypervisor and the Microsoft Hypervisor (MSHV).
|
||||
|
||||
The project focuses on exclusively running modern, cloud workloads, on top of
|
||||
a limited set of hardware architectures and platforms. Cloud workloads refers
|
||||
to those that are usually run by customers inside a cloud provider. For our
|
||||
purposes this means modern operating systems with most I/O handled by
|
||||
paravirtualised devices (i.e. virtio), no requirement for legacy devices, and
|
||||
The project focuses on running modern, _Cloud Workloads_, on specific, common,
|
||||
hardware architectures. In this case _Cloud Workloads_ refers to those that are
|
||||
run by customers inside a Cloud Service Provider. This means modern operating
|
||||
systems with most I/O handled by
|
||||
paravirtualised devices (e.g. _virtio_), no requirement for legacy devices, and
|
||||
64-bit CPUs.
|
||||
|
||||
Cloud Hypervisor is implemented in [Rust](https://www.rust-lang.org/) and is
|
||||
based on the [rust-vmm](https://github.com/rust-vmm) crates.
|
||||
based on the [Rust VMM](https://github.com/rust-vmm) crates.
|
||||
|
||||
## Objectives
|
||||
|
||||
@@ -63,7 +60,7 @@ based on the [rust-vmm](https://github.com/rust-vmm) crates.
|
||||
### Architectures
|
||||
|
||||
Cloud Hypervisor supports the `x86-64` and `AArch64` architectures. There are
|
||||
some small differences in functionality between the two architectures
|
||||
minor differences in functionality between the two architectures
|
||||
(see [#1125](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/1125)).
|
||||
|
||||
### Guest OS
|
||||
@@ -72,157 +69,121 @@ Cloud Hypervisor supports `64-bit Linux` and Windows 10/Windows Server 2019.
|
||||
|
||||
# 2. Getting Started
|
||||
|
||||
Below sections describe how to build and run Cloud Hypervisor on the `x86_64`
|
||||
platform. For getting started on the `AArch64` platform, please refer to the
|
||||
[Arm64 documentation](docs/arm64.md).
|
||||
The following sections describe how to build and run Cloud Hypervisor on the
|
||||
`x86-64` platform. For getting started on the `AArch64` platform, please refer
|
||||
to the
|
||||
[AArch64 documentation](docs/arm64.md).
|
||||
|
||||
## Host OS
|
||||
|
||||
Based on required KVM functionality the minimum host kernel version is 4.11.
|
||||
For adequate peformance the minimum recommended host kernel vesion is 5.6. The
|
||||
For required KVM functionality the minimum host kernel version is 4.11. For
|
||||
adequate performance the minimum recommended host kernel version is 5.6. The
|
||||
majority of the CI currently tests with kernel version 5.15.
|
||||
|
||||
## Preparation
|
||||
## Use Pre-built Binaries
|
||||
|
||||
We create a folder to build and run `cloud-hypervisor` at `$HOME/cloud-hypervisor`
|
||||
The recommended approach to getting started with Cloud Hypervisor is by using a
|
||||
pre-built binary. Binaries are available for the [latest
|
||||
release](https://github.com/cloud-hypervisor/cloud-hypervisor/releases/latest).
|
||||
Use `cloud-hypervisor-static` for `x86-64` or `cloud-hypervisor-static-aarch64`
|
||||
for `AArch64` platform.
|
||||
|
||||
```shell
|
||||
$ export CLOUDH=$HOME/cloud-hypervisor
|
||||
$ mkdir $CLOUDH
|
||||
```
|
||||
## Packages
|
||||
|
||||
## Install prerequisites
|
||||
|
||||
You need to install some prerequisite packages in order to build and test Cloud
|
||||
Hypervisor. Here, all the steps are based on Ubuntu, for other Linux
|
||||
distributions please replace the package manager and package name.
|
||||
|
||||
```shell
|
||||
# Install build-essential, git, and qemu-utils
|
||||
$ sudo apt install git build-essential qemu-utils
|
||||
# Install rust tool chain
|
||||
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
# If you want to build statically linked binary please add musl target
|
||||
$ rustup target add x86_64-unknown-linux-musl
|
||||
```
|
||||
|
||||
## Clone and build
|
||||
|
||||
First you need to clone and build the cloud-hypervisor repo:
|
||||
|
||||
```shell
|
||||
$ pushd $CLOUDH
|
||||
$ git clone https://github.com/cloud-hypervisor/cloud-hypervisor.git
|
||||
$ cd cloud-hypervisor
|
||||
$ cargo build --release
|
||||
|
||||
# We need to give the cloud-hypervisor binary the NET_ADMIN capabilities for it to set TAP interfaces up on the host.
|
||||
$ sudo setcap cap_net_admin+ep ./target/release/cloud-hypervisor
|
||||
|
||||
# If you want to build statically linked binary
|
||||
$ cargo build --release --target=x86_64-unknown-linux-musl --all
|
||||
$ popd
|
||||
```
|
||||
|
||||
This will build a `cloud-hypervisor` binary under
|
||||
`$CLOUDH/cloud-hypervisor/target/release/cloud-hypervisor`.
|
||||
|
||||
### Containerized builds and tests
|
||||
|
||||
If you want to build and test Cloud Hypervisor without having to install all the
|
||||
required dependencies (The rust toolchain, cargo tools, etc), you can also use
|
||||
Cloud Hypervisor's development script: `dev_cli.sh`. Please note that upon its
|
||||
first invocation, this script will pull a fairly large container image.
|
||||
|
||||
For example, to build the Cloud Hypervisor release binary:
|
||||
|
||||
```shell
|
||||
$ pushd $CLOUDH
|
||||
$ cd cloud-hypervisor
|
||||
$ ./scripts/dev_cli.sh build --release
|
||||
```
|
||||
|
||||
With `dev_cli.sh`, one can also run the Cloud Hypervisor CI locally. This can be
|
||||
very convenient for debugging CI errors without having to fully rely on the
|
||||
Cloud Hypervisor CI infrastructure.
|
||||
|
||||
For example, to run the Cloud Hypervisor unit tests:
|
||||
|
||||
```shell
|
||||
$ ./scripts/dev_cli.sh tests --unit
|
||||
```
|
||||
|
||||
Run the `./scripts/dev_cli.sh --help` command to view all the supported
|
||||
development script commands and their related options.
|
||||
|
||||
## Use Prebuilt Binaries
|
||||
|
||||
Cloud Hypervisor packages targeting some popular Linux distributions are available
|
||||
thanks to the [Open Build Service](https://build.opensuse.org). The
|
||||
[OBS README](https://github.com/cloud-hypervisor/obs-packaging) explains how to
|
||||
For convenience, packages are also available targeting some popular Linux
|
||||
distributions. This is thanks to the [Open Build
|
||||
Service](https://build.opensuse.org). The [OBS
|
||||
README](https://github.com/cloud-hypervisor/obs-packaging) explains how to
|
||||
enable the repository in a supported Linux distribution and install Cloud Hypervisor
|
||||
and accompanying packages. Please report any packaging issues in the
|
||||
[obs-packaging](https://github.com/cloud-hypervisor/obs-packaging) repository.
|
||||
|
||||
## Run
|
||||
## Building from Source
|
||||
|
||||
You can run a guest VM by either using an existing cloud image or booting into
|
||||
your own kernel and disk image.
|
||||
Please see the [instructions for building from source](docs/building.md) if you
|
||||
do not wish to use the pre-built binaries.
|
||||
|
||||
### Cloud image
|
||||
## Booting Linux
|
||||
|
||||
Cloud Hypervisor supports booting disk images containing all needed
|
||||
components to run cloud workloads, a.k.a. cloud images. To do that we rely on
|
||||
the [Rust Hypervisor
|
||||
Firmware](https://github.com/cloud-hypervisor/rust-hypervisor-firmware) project
|
||||
to provide an ELF formatted KVM firmware for `cloud-hypervisor` to directly
|
||||
boot into.
|
||||
The instructions below are for the `x86-64` platform. For `AArch64` please see
|
||||
the [AArch64 specific documentation](docs/arm64.md).
|
||||
|
||||
We need to get the latest `rust-hypervisor-firmware` release and also a working
|
||||
cloud image. Here we will use a Ubuntu image:
|
||||
Cloud Hypervisor supports direct kernel boot (if the kernel is built with PVH
|
||||
support) or booting via a firmware (either [Rust Hypervisor
|
||||
Firmware](https://github.com/cloud-hypervisor/rust-hypervisor-firmware) or an
|
||||
edk2 UEFI firmware called `CLOUDHV`.)
|
||||
|
||||
Binary builds of the firmware files are available for the latest release of
|
||||
[Rust Hyperivor
|
||||
Firmware](https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/latest)
|
||||
and [our edk2
|
||||
repository](https://github.com/cloud-hypervisor/edk2/releases/latest)
|
||||
|
||||
The choice of firmware depends on your guest OS choice; some experimentation
|
||||
may be required.
|
||||
|
||||
### Firmware Booting
|
||||
|
||||
Cloud Hypervisor supports booting disk images containing all needed components
|
||||
to run cloud workloads, a.k.a. cloud images.
|
||||
|
||||
The following sample commands will download an Ubuntu Cloud image, converting
|
||||
it into a format that Cloud Hypervisor can use and a firmware to boot the image
|
||||
with.
|
||||
|
||||
```shell
|
||||
$ pushd $CLOUDH
|
||||
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
|
||||
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-amd64.img focal-server-cloudimg-amd64.raw
|
||||
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.4.1/hypervisor-fw
|
||||
$ popd
|
||||
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.4.2/hypervisor-fw
|
||||
```
|
||||
|
||||
The Ubuntu cloud images do not ship with a default password so it necessary to
|
||||
use a `cloud-init` disk image to customise the image on the first boot. A basic
|
||||
`cloud-init` image is generated by this [script](scripts/create-cloud-init.sh).
|
||||
This seeds the image with a default username/password of `cloud/cloud123`. It
|
||||
is only necessary to add this disk image on the first boot.
|
||||
|
||||
```shell
|
||||
$ pushd $CLOUDH
|
||||
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor/target/release/cloud-hypervisor
|
||||
$ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor
|
||||
$ ./create-cloud-init.sh
|
||||
$ ./cloud-hypervisor \
|
||||
--kernel ./hypervisor-fw \
|
||||
--disk path=focal-server-cloudimg-amd64.raw \
|
||||
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
--net "tap=,mac=,ip=,mask="
|
||||
$ popd
|
||||
```
|
||||
|
||||
Multiple arguments can be given to the `--disk` parameter.
|
||||
If access to the firmware messages or interaction with the boot loader (e.g.
|
||||
GRUB) is required then it necessary to switch to the serial console instead of
|
||||
`virtio-console`.
|
||||
|
||||
### Custom kernel and disk image
|
||||
```shell
|
||||
$ ./cloud-hypervisor \
|
||||
--kernel ./hypervisor-fw \
|
||||
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
--net "tap=,mac=,ip=,mask=" \
|
||||
--serial tty \
|
||||
--console off
|
||||
```
|
||||
|
||||
#### Building your kernel
|
||||
### Custom Kernel and Disk Image
|
||||
|
||||
Cloud Hypervisor also supports direct kernel boot into a `vmlinux` ELF kernel.
|
||||
In order to support virtio-watchdog we have our own development branch. You are
|
||||
of course able to use your own kernel but these instructions will continue with
|
||||
the version that we develop and test against.
|
||||
#### Building your Kernel
|
||||
|
||||
Cloud Hypervisor also supports direct kernel boot into a `vmlinux` ELF kernel (compiled with PVH support). In order to support development there is a custom branch; however provided the required options are enabled any recent kernel will suffice.
|
||||
|
||||
To build the kernel:
|
||||
|
||||
```shell
|
||||
|
||||
# Clone the Cloud Hypervisor Linux branch
|
||||
$ pushd $CLOUDH
|
||||
$ git clone --depth 1 https://github.com/cloud-hypervisor/linux.git -b ch-5.15.12 linux-cloud-hypervisor
|
||||
$ pushd linux-cloud-hypervisor
|
||||
|
||||
# Use the cloud-hypervisor kernel config to build your kernel
|
||||
$ cp $CLOUDH/cloud-hypervisor/resources/linux-config-x86_64 .config
|
||||
$ wget https://raw.githubusercontent.com/cloud-hypervisor/cloud-hypervisor/main/resources/linux-config-x86_64
|
||||
$ cp linux-config-x86_64 .config
|
||||
$ KCFLAGS="-Wa,-mx86-used-note=no" make bzImage -j `nproc`
|
||||
$ popd
|
||||
```
|
||||
@@ -232,42 +193,34 @@ The `vmlinux` kernel image will then be located at
|
||||
|
||||
#### Disk image
|
||||
|
||||
For the disk image, we will use a Ubuntu cloud image that contains a root
|
||||
partition:
|
||||
For the disk image the same Ubuntu image as before can be used. This contains
|
||||
an `ext4` root filesystem.
|
||||
|
||||
```shell
|
||||
$ pushd $CLOUDH
|
||||
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
|
||||
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-amd64.img focal-server-cloudimg-amd64.raw
|
||||
$ popd
|
||||
```
|
||||
|
||||
#### Booting the guest VM
|
||||
|
||||
Now we can directly boot into our custom kernel and make it use the Ubuntu root
|
||||
partition. If we want to have 4 vCPUs and 1024 MBytes of memory:
|
||||
These sample commands boot the disk image using the custom kernel whilst also
|
||||
supplying the desired kernel command line.
|
||||
|
||||
```shell
|
||||
$ pushd $CLOUDH
|
||||
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor/target/release/cloud-hypervisor
|
||||
$ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor
|
||||
$ ./create-cloud-init.sh
|
||||
$ ./cloud-hypervisor \
|
||||
--kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
|
||||
--disk path=focal-server-cloudimg-amd64.raw \
|
||||
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||
--cmdline "console=hvc0 root=/dev/vda1 rw" \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
--net "tap=,mac=,ip=,mask="
|
||||
```
|
||||
|
||||
The above example use the `virtio-console` device as the guest console, and this
|
||||
device may not be enabled soon enough by the guest kernel to get early kernel
|
||||
debug messages.
|
||||
If earlier kernel messages are required the serial console should be used instead of `virtio-console`.
|
||||
|
||||
When in need for earlier debug messages, using the legacy serial device based
|
||||
console is preferred:
|
||||
|
||||
```
|
||||
$ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||
```./cloud-hypervisor \
|
||||
--kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
|
||||
--console off \
|
||||
--serial tty \
|
||||
@@ -280,8 +233,8 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||
|
||||
# 3. Status
|
||||
|
||||
Cloud Hypervisor is under active development. The following stability guarantees
|
||||
are currently made:
|
||||
Cloud Hypervisor is under active development. The following stability
|
||||
guarantees are currently made:
|
||||
|
||||
* The API (including command line options) will not be removed or changed in a
|
||||
breaking way without a minimum of 2 major releases notice. Where possible
|
||||
@@ -299,16 +252,18 @@ Currently the following items are **not** guaranteed across updates:
|
||||
* The following features are considered experimental and may change
|
||||
substantially between releases: TDX, vfio-user, vDPA.
|
||||
|
||||
As of 2022-04-05, the following cloud images are supported:
|
||||
Further details can be found in the [release documentation](docs/releases.md).
|
||||
|
||||
- [Ubuntu Bionic](https://cloud-images.ubuntu.com/bionic/current/) (cloudimg)
|
||||
- [Ubuntu Focal](https://cloud-images.ubuntu.com/focal/current/) (cloudimg)
|
||||
- [Ubuntu Jammy](https://cloud-images.ubuntu.com/jammy/current/) (cloudimg)
|
||||
As of 2022-10-13, the following cloud images are supported:
|
||||
|
||||
- [Ubuntu Bionic](https://cloud-images.ubuntu.com/bionic/current/) (bionic-server-cloudimg-amd64.img)
|
||||
- [Ubuntu Focal](https://cloud-images.ubuntu.com/focal/current/) (focal-server-cloudimg-amd64.img)
|
||||
- [Ubuntu Jammy](https://cloud-images.ubuntu.com/jammy/current/) (jammy-server-cloudimg-amd64.img )
|
||||
- [Fedora 36](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/x86_64/images/) (Fedora-Cloud-Base-36-1.5.x86_64.raw.xz)
|
||||
|
||||
Direct kernel boot to userspace should work with a rootfs from most
|
||||
distributions.
|
||||
|
||||
Further details can be found in the [release documentation](docs/releases.md).
|
||||
distributions although you may need to enable exotic filesystem types in the
|
||||
reference kernel configuration (e.g. XFS or btrfs.)
|
||||
|
||||
## Hot Plug
|
||||
|
||||
@@ -321,18 +276,12 @@ Cloud Hypervisor supports hotplug of CPUs, passthrough devices (VFIO),
|
||||
Details of the device model can be found in this
|
||||
[documentation](docs/device_model.md).
|
||||
|
||||
## TODO
|
||||
|
||||
We are not tracking the Cloud Hypervisor TODO list from a specific git tracked
|
||||
file but through
|
||||
[github issues](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/new)
|
||||
instead.
|
||||
|
||||
## Roadmap
|
||||
|
||||
The project roadmap is tracked through a [GitHub project](https://github.com/orgs/cloud-hypervisor/projects/6).
|
||||
The project roadmap is tracked through a [GitHub
|
||||
project](https://github.com/orgs/cloud-hypervisor/projects/6).
|
||||
|
||||
# 4. `rust-vmm` project dependency
|
||||
# 4. Relationship with _Rust VMM_ Project
|
||||
|
||||
In order to satisfy the design goal of having a high-performance,
|
||||
security-focused hypervisor the decision was made to use the
|
||||
@@ -341,39 +290,26 @@ focus on memory and thread safety makes it an ideal candidate for implementing
|
||||
VMMs.
|
||||
|
||||
Instead of implementing the VMM components from scratch, Cloud Hypervisor is
|
||||
importing the [rust-vmm](https://github.com/rust-vmm) crates, and sharing code
|
||||
importing the [Rust VMM](https://github.com/rust-vmm) crates, and sharing code
|
||||
and architecture together with other VMMs like e.g. Amazon's
|
||||
[Firecracker](https://firecracker-microvm.github.io/) and Google's
|
||||
[crosvm](https://chromium.googlesource.com/chromiumos/platform/crosvm/).
|
||||
|
||||
Cloud Hypervisor embraces the rust-vmm project goals, which is to be able to
|
||||
share and re-use as many virtualization crates as possible. As such, the Cloud
|
||||
Hypervisor relationship with the rust-vmm project is twofold:
|
||||
Cloud Hypervisor embraces the _Rust VMM_ project's goals, which is to be able
|
||||
to share and re-use as many virtualization crates as possible.
|
||||
|
||||
1. It will use as much of the rust-vmm code as possible. Any new rust-vmm crate
|
||||
that's relevant to the project goals will be integrated as soon as possible.
|
||||
2. As it is likely that the rust-vmm project will lack some of the features that
|
||||
Cloud Hypervisor needs (e.g. ACPI, VFIO, vhost-user, etc), we will be using
|
||||
the Cloud Hypervisor VMM to implement and test them, and contribute them back
|
||||
to the rust-vmm project.
|
||||
|
||||
## Firecracker and crosvm
|
||||
## Differences with Firecracker and crosvm
|
||||
|
||||
A large part of the Cloud Hypervisor code is based on either the Firecracker or
|
||||
the crosvm projects implementations. Both of these are VMMs written in Rust with
|
||||
a focus on safety and security, like Cloud Hypervisor.
|
||||
the crosvm project's implementations. Both of these are VMMs written in Rust
|
||||
with a focus on safety and security, like Cloud Hypervisor.
|
||||
|
||||
However we want to emphasize that the Cloud Hypervisor project is neither a fork
|
||||
nor a reimplementation of any of those projects. The goals and use cases we're
|
||||
trying to meet are different. We're aiming at supporting cloud workloads, i.e.
|
||||
those modern, full Linux distribution images currently being run by Cloud
|
||||
Service Provider (CSP) tenants.
|
||||
The goal of the Cloud Hypervisor project differs from the aforementioned
|
||||
projects in that it aims to be a general purpose VMM for _Cloud Workloads_ and
|
||||
not limited to container/serverless or client workloads.
|
||||
|
||||
Our primary target is not to support client or serverless use cases, and as such
|
||||
our code base already diverges from the crosvm and Firecracker ones. As we add
|
||||
more features to support our use cases, we believe that the divergence will
|
||||
increase while at the same time sharing as much of the fundamental
|
||||
virtualization code through the rust-vmm project crates as possible.
|
||||
The Cloud Hypervisor community thanks the communities of both the Firecracker
|
||||
and crosvm projects for their excellent work.
|
||||
|
||||
# 5. Community
|
||||
|
||||
@@ -383,12 +319,12 @@ repository.
|
||||
|
||||
## Contribute
|
||||
|
||||
We are working on building a global, diverse and collaborative community around
|
||||
the Cloud Hypervisor project. Anyone who is interested in
|
||||
The project strongly believes in building a global, diverse and collaborative
|
||||
community around the Cloud Hypervisor project. Anyone who is interested in
|
||||
[contributing](CONTRIBUTING.md) to the project is welcome to participate.
|
||||
|
||||
We believe that contributing to a open source project like Cloud Hypervisor
|
||||
covers a lot more than just sending code. Testing, documentation, pull request
|
||||
Contributing to a open source project like Cloud Hypervisor covers a lot more
|
||||
than just sending code. Testing, documentation, pull request
|
||||
reviews, bug reports, feature requests, project improvement suggestions, etc,
|
||||
are all equal and welcome means of contribution. See the
|
||||
[CONTRIBUTING](CONTRIBUTING.md) document for more details.
|
||||
|
||||
@@ -5,4 +5,4 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
vm-memory = "0.9.0"
|
||||
vm-memory = "0.10.0"
|
||||
|
||||
@@ -5,4 +5,4 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
vmm-sys-util = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
@@ -9,20 +9,20 @@ default = []
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.65"
|
||||
anyhow = "1.0.66"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.133"
|
||||
linux-loader = { version = "0.6.0", features = ["elf", "bzimage", "pe"] }
|
||||
libc = "0.2.138"
|
||||
linux-loader = { version = "0.8.1", features = ["elf", "bzimage", "pe"] }
|
||||
log = "0.4.17"
|
||||
serde = { version = "1.0.145", features = ["rc", "derive"] }
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
thiserror = "1.0.37"
|
||||
uuid = "1.1.2"
|
||||
versionize = "0.1.6"
|
||||
uuid = "1.2.2"
|
||||
versionize = "0.1.9"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-memory = { version = "0.9.0", features = ["backend-mmap", "backend-bitmap"] }
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-bitmap"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vmm-sys-util = { version = "0.10.0", features = ["with-serde"] }
|
||||
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }
|
||||
|
||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||
fdt_parser = { version = "0.1.4", package = "fdt" }
|
||||
|
||||
@@ -51,8 +51,6 @@ const SIZE_CELLS: u32 = 0x2;
|
||||
// Look for "The 1st cell..."
|
||||
const GIC_FDT_IRQ_TYPE_SPI: u32 = 0;
|
||||
const GIC_FDT_IRQ_TYPE_PPI: u32 = 1;
|
||||
const GIC_FDT_IRQ_PPI_CPU_SHIFT: u32 = 8;
|
||||
const GIC_FDT_IRQ_PPI_CPU_MASK: u32 = 0xff << GIC_FDT_IRQ_PPI_CPU_SHIFT;
|
||||
|
||||
// From https://elixir.bootlin.com/linux/v4.9.62/source/include/dt-bindings/interrupt-controller/irq.h#L17
|
||||
const IRQ_TYPE_EDGE_RISING: u32 = 1;
|
||||
@@ -121,7 +119,7 @@ pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHash
|
||||
create_gic_node(&mut fdt, gic_device)?;
|
||||
create_timer_node(&mut fdt)?;
|
||||
if pmu_supported {
|
||||
create_pmu_node(&mut fdt, vcpu_mpidr.len())?;
|
||||
create_pmu_node(&mut fdt)?;
|
||||
}
|
||||
create_clock_node(&mut fdt)?;
|
||||
create_psci_node(&mut fdt)?;
|
||||
@@ -541,16 +539,9 @@ fn create_devices_node<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::Buil
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_pmu_node(fdt: &mut FdtWriter, cpu_nums: usize) -> FdtWriterResult<()> {
|
||||
let num_cpus = cpu_nums as u64 as u32;
|
||||
fn create_pmu_node(fdt: &mut FdtWriter) -> FdtWriterResult<()> {
|
||||
let compatible = "arm,armv8-pmuv3";
|
||||
let cpu_mask: u32 =
|
||||
(((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK;
|
||||
let irq = [
|
||||
GIC_FDT_IRQ_TYPE_PPI,
|
||||
AARCH64_PMU_IRQ,
|
||||
cpu_mask | IRQ_TYPE_LEVEL_HI,
|
||||
];
|
||||
let irq = [GIC_FDT_IRQ_TYPE_PPI, AARCH64_PMU_IRQ, IRQ_TYPE_LEVEL_HI];
|
||||
|
||||
let pmu_node = fdt.begin_node("pmu")?;
|
||||
fdt.property_string("compatible", compatible)?;
|
||||
|
||||
@@ -98,6 +98,11 @@ pub const RAM_START: GuestAddress = GuestAddress(0x4000_0000);
|
||||
pub const MEM_32BIT_RESERVED_START: GuestAddress = GuestAddress(0xfc00_0000);
|
||||
pub const MEM_32BIT_RESERVED_SIZE: u64 = 0x0400_0000;
|
||||
|
||||
/// TPM Address Range
|
||||
/// This Address range is specific to CRB Interface
|
||||
pub const TPM_START: GuestAddress = GuestAddress(0xfed4_0000);
|
||||
pub const TPM_SIZE: u64 = 0x1000;
|
||||
|
||||
/// Start of 64-bit RAM.
|
||||
pub const RAM_64BIT_START: GuestAddress = GuestAddress(0x1_0000_0000);
|
||||
|
||||
|
||||
@@ -107,6 +107,11 @@ pub const KVM_TSS_SIZE: u64 = (3 * 4) << 10;
|
||||
pub const KVM_IDENTITY_MAP_START: GuestAddress = GuestAddress(KVM_TSS_START.0 + KVM_TSS_SIZE);
|
||||
pub const KVM_IDENTITY_MAP_SIZE: u64 = 4 << 10;
|
||||
|
||||
/// TPM Address Range
|
||||
/// This Address range is specific to CRB Interface
|
||||
pub const TPM_START: GuestAddress = GuestAddress(0xfed4_0000);
|
||||
pub const TPM_SIZE: u64 = 0x1000;
|
||||
|
||||
// IOAPIC
|
||||
pub const IOAPIC_START: GuestAddress = GuestAddress(0xfec0_0000);
|
||||
pub const IOAPIC_SIZE: u64 = 0x20;
|
||||
|
||||
@@ -631,6 +631,20 @@ pub fn generate_common_cpuid(
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copy host L2 cache details if not populated by KVM
|
||||
0x8000_0006 => {
|
||||
if entry.eax == 0 && entry.ebx == 0 && entry.ecx == 0 && entry.edx == 0 {
|
||||
// SAFETY: cpuid called with valid leaves
|
||||
if unsafe { std::arch::x86_64::__cpuid(0x8000_0000).eax } >= 0x8000_0006 {
|
||||
// SAFETY: cpuid called with valid leaves
|
||||
let leaf = unsafe { std::arch::x86_64::__cpuid(0x8000_0006) };
|
||||
entry.eax = leaf.eax;
|
||||
entry.ebx = leaf.ebx;
|
||||
entry.ecx = leaf.ecx;
|
||||
entry.edx = leaf.edx;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set CPU physical bits
|
||||
0x8000_0008 => {
|
||||
entry.eax = (entry.eax & 0xffff_ff00) | (phys_bits as u32 & 0xff);
|
||||
@@ -764,11 +778,11 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
|
||||
.checked_add(layout::MEM_32BIT_DEVICES_SIZE)
|
||||
.expect("32-bit reserved region is too large");
|
||||
|
||||
let requested_memory_size = GuestAddress(size as u64);
|
||||
let requested_memory_size = GuestAddress(size);
|
||||
let mut regions = Vec::new();
|
||||
|
||||
// case1: guest memory fits before the gap
|
||||
if size as u64 <= layout::MEM_32BIT_RESERVED_START.raw_value() {
|
||||
if size <= layout::MEM_32BIT_RESERVED_START.raw_value() {
|
||||
regions.push((GuestAddress(0), size as usize, RegionType::Ram));
|
||||
// case2: guest memory extends beyond the gap
|
||||
} else {
|
||||
@@ -865,7 +879,7 @@ fn configure_pvh(
|
||||
start_info.0.magic = XEN_HVM_START_MAGIC_VALUE;
|
||||
start_info.0.version = 1; // pvh has version 1
|
||||
start_info.0.nr_modules = 0;
|
||||
start_info.0.cmdline_paddr = cmdline_addr.raw_value() as u64;
|
||||
start_info.0.cmdline_paddr = cmdline_addr.raw_value();
|
||||
start_info.0.memmap_paddr = layout::MEMMAP_START.raw_value();
|
||||
|
||||
if let Some(rsdp_addr) = rsdp_addr {
|
||||
@@ -934,7 +948,7 @@ fn configure_pvh(
|
||||
add_memmap_entry(
|
||||
&mut memmap,
|
||||
sgx_epc_region.start().raw_value(),
|
||||
sgx_epc_region.size() as u64,
|
||||
sgx_epc_region.size(),
|
||||
E820_RESERVED,
|
||||
);
|
||||
}
|
||||
@@ -1126,7 +1140,7 @@ fn update_cpuid_sgx(
|
||||
for (i, epc_section) in epc_sections.iter().enumerate() {
|
||||
let subleaf_idx = i + 2;
|
||||
let start = epc_section.start().raw_value();
|
||||
let size = epc_section.size() as u64;
|
||||
let size = epc_section.size();
|
||||
let eax = (start & 0xffff_f000) as u32 | 0x1;
|
||||
let ebx = (start >> 32) as u32;
|
||||
let ecx = (size & 0xffff_f000) as u32 | (leaf.ecx & 0xf);
|
||||
|
||||
@@ -124,7 +124,7 @@ pub fn configure_segments_and_sregs(
|
||||
mem: &GuestMemoryMmap,
|
||||
sregs: &mut SpecialRegisters,
|
||||
) -> Result<()> {
|
||||
let gdt_table: [u64; BOOT_GDT_MAX as usize] = {
|
||||
let gdt_table: [u64; BOOT_GDT_MAX] = {
|
||||
// Configure GDT entries as specified by PVH boot protocol
|
||||
[
|
||||
gdt_entry(0, 0, 0), // NULL
|
||||
|
||||
@@ -8,17 +8,17 @@ edition = "2021"
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
io-uring = "0.5.6"
|
||||
libc = "0.2.133"
|
||||
io-uring = "0.5.9"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
qcow = { path = "../qcow" }
|
||||
thiserror = "1.0.37"
|
||||
versionize = "0.1.6"
|
||||
versionize = "0.1.9"
|
||||
versionize_derive = "0.1.4"
|
||||
vhdx = { path = "../vhdx" }
|
||||
virtio-bindings = { version = "0.1.0", features = ["virtio-v5_0_0"] }
|
||||
virtio-queue = "0.6.1"
|
||||
vm-memory = { version = "0.9.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
virtio-queue = "0.7.0"
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
|
||||
@@ -51,23 +51,23 @@ pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Guest gave us bad memory addresses.")]
|
||||
#[error("Guest gave us bad memory addresses")]
|
||||
GuestMemory(GuestMemoryError),
|
||||
#[error("Guest gave us offsets that would have overflowed a usize.")]
|
||||
#[error("Guest gave us offsets that would have overflowed a usize")]
|
||||
CheckedOffset(GuestAddress, usize),
|
||||
#[error("Guest gave us a write only descriptor that protocol says to read from.")]
|
||||
#[error("Guest gave us a write only descriptor that protocol says to read from")]
|
||||
UnexpectedWriteOnlyDescriptor,
|
||||
#[error("Guest gave us a read only descriptor that protocol says to write to.")]
|
||||
#[error("Guest gave us a read only descriptor that protocol says to write to")]
|
||||
UnexpectedReadOnlyDescriptor,
|
||||
#[error("Guest gave us too few descriptors in a descriptor chain.")]
|
||||
#[error("Guest gave us too few descriptors in a descriptor chain")]
|
||||
DescriptorChainTooShort,
|
||||
#[error("Guest gave us a descriptor that was too short to use.")]
|
||||
#[error("Guest gave us a descriptor that was too short to use")]
|
||||
DescriptorLengthTooSmall,
|
||||
#[error("Getting a block's metadata fails for any reason.")]
|
||||
#[error("Getting a block's metadata fails for any reason")]
|
||||
GetFileMetadata,
|
||||
#[error("The requested operation would cause a seek beyond disk end.")]
|
||||
#[error("The requested operation would cause a seek beyond disk end")]
|
||||
InvalidOffset,
|
||||
#[error("The requested operation does not support multiple descriptors.")]
|
||||
#[error("The requested operation does not support multiple descriptors")]
|
||||
TooManyDescriptors,
|
||||
}
|
||||
|
||||
@@ -566,15 +566,15 @@ pub fn block_io_uring_is_supported() -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check IORING_OP_READ is supported
|
||||
if !probe.is_supported(opcode::Read::CODE) {
|
||||
info!("{} IORING_OP_READ operation not supported", error_msg);
|
||||
// Check IORING_OP_READV is supported
|
||||
if !probe.is_supported(opcode::Readv::CODE) {
|
||||
info!("{} IORING_OP_READV operation not supported", error_msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check IORING_OP_WRITE is supported
|
||||
if !probe.is_supported(opcode::Write::CODE) {
|
||||
info!("{} IORING_OP_WRITE operation not supported", error_msg);
|
||||
// Check IORING_OP_WRITEV is supported
|
||||
if !probe.is_supported(opcode::Writev::CODE) {
|
||||
info!("{} IORING_OP_WRITEV operation not supported", error_msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ impl DiskFile for QcowDiskSync {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
let mut file = self.qcow_file.lock().unwrap();
|
||||
|
||||
Ok(file.seek(SeekFrom::End(0)).map_err(DiskFileError::Size)? as u64)
|
||||
file.seek(SeekFrom::End(0)).map_err(DiskFileError::Size)
|
||||
}
|
||||
|
||||
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
|
||||
@@ -23,10 +23,9 @@ impl RawFileDisk {
|
||||
|
||||
impl DiskFile for RawFileDisk {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
Ok(self
|
||||
.file
|
||||
self.file
|
||||
.seek(SeekFrom::End(0))
|
||||
.map_err(DiskFileError::Size)? as u64)
|
||||
.map_err(DiskFileError::Size)
|
||||
}
|
||||
|
||||
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
|
||||
@@ -22,10 +22,9 @@ impl RawFileDiskSync {
|
||||
|
||||
impl DiskFile for RawFileDiskSync {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
Ok(self
|
||||
.file
|
||||
self.file
|
||||
.seek(SeekFrom::End(0))
|
||||
.map_err(DiskFileError::Size)? as u64)
|
||||
.map_err(DiskFileError::Size)
|
||||
}
|
||||
|
||||
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
@@ -118,7 +117,7 @@ impl AsyncIo for RawFileSync {
|
||||
}
|
||||
|
||||
if let Some(user_data) = user_data {
|
||||
self.completion_list.push((user_data, result as i32));
|
||||
self.completion_list.push((user_data, result));
|
||||
self.eventfd.write(1).unwrap();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,19 +6,22 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { path = "../acpi_tables" }
|
||||
anyhow = "1.0.65"
|
||||
anyhow = "1.0.66"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "1.3.2"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.133"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
versionize = "0.1.6"
|
||||
phf = { version = "0.11.1", features = ["macros"] }
|
||||
thiserror = "1.0.37"
|
||||
tpm = { path = "../tpm" }
|
||||
versionize = "0.1.9"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = "0.9.0"
|
||||
vm-memory = "0.10.0"
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vmm-sys-util = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||
arch = { path = "../arch" }
|
||||
|
||||
@@ -114,7 +114,7 @@ impl Aml for AcpiGedDevice {
|
||||
&aml::ResourceTemplate::new(vec![&aml::AddressSpace::new_memory(
|
||||
aml::AddressSpaceCachable::NotCacheable,
|
||||
true,
|
||||
self.address.0 as u64,
|
||||
self.address.0,
|
||||
self.address.0 + GED_DEVICE_ACPI_SIZE as u64 - 1,
|
||||
)]),
|
||||
),
|
||||
|
||||
@@ -199,7 +199,7 @@ impl Serial {
|
||||
}
|
||||
|
||||
fn handle_write(&mut self, offset: u8, v: u8) -> Result<()> {
|
||||
match offset as u8 {
|
||||
match offset {
|
||||
DLAB_LOW if self.is_dlab_set() => {
|
||||
self.baud_divisor = (self.baud_divisor & 0xff00) | u16::from(v)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ pub mod interrupt_controller;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub mod ioapic;
|
||||
pub mod legacy;
|
||||
pub mod tpm;
|
||||
|
||||
pub use self::acpi::{AcpiGedDevice, AcpiPmTimerDevice, AcpiShutdownDevice};
|
||||
|
||||
|
||||
470
devices/src/tpm.rs
Normal file
470
devices/src/tpm.rs
Normal file
@@ -0,0 +1,470 @@
|
||||
// Copyright © 2022, Microsoft Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use anyhow::anyhow;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use arch::aarch64::layout::{TPM_SIZE, TPM_START};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use arch::x86_64::layout::{TPM_SIZE, TPM_START};
|
||||
use phf::phf_map;
|
||||
use std::cmp;
|
||||
use std::sync::{Arc, Barrier};
|
||||
use thiserror::Error;
|
||||
use tpm::emulator::{BackendCmd, Emulator};
|
||||
use tpm::TPM_CRB_BUFFER_MAX;
|
||||
use tpm::TPM_SUCCESS;
|
||||
use vm_device::BusDevice;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Emulator doesn't implement min required capabilities: {0}")]
|
||||
CheckCaps(#[source] anyhow::Error),
|
||||
#[error("Failed to initialize tpm: {0}")]
|
||||
Init(#[source] anyhow::Error),
|
||||
#[error("Failed to deliver tpm Command: {0}")]
|
||||
DeliverRequest(#[source] anyhow::Error),
|
||||
}
|
||||
type Result<T> = anyhow::Result<T, Error>;
|
||||
|
||||
/* crb 32-bit registers */
|
||||
const CRB_LOC_STATE: u32 = 0x0;
|
||||
//Register Fields
|
||||
// Field => (start, length)
|
||||
// start: lowest bit in the bit field numbered from 0
|
||||
// length: length of the bit field
|
||||
const CRB_LOC_STATE_FIELDS: phf::Map<&str, [u32; 2]> = phf_map! {
|
||||
"tpmEstablished" => [0, 1],
|
||||
"locAssigned" => [1,1],
|
||||
"activeLocality"=> [2, 3],
|
||||
"reserved" => [5, 2],
|
||||
"tpmRegValidSts" => [7, 1]
|
||||
};
|
||||
const CRB_LOC_CTRL: u32 = 0x08;
|
||||
const CRB_LOC_CTRL_REQUEST_ACCESS: u32 = 1 << 0;
|
||||
const CRB_LOC_CTRL_RELINQUISH: u32 = 1 << 1;
|
||||
const CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT: u32 = 1 << 3;
|
||||
const CRB_LOC_STS: u32 = 0x0C;
|
||||
const CRB_LOC_STS_FIELDS: phf::Map<&str, [u32; 2]> = phf_map! {
|
||||
"Granted" => [0, 1],
|
||||
"beenSeized" => [1,1]
|
||||
};
|
||||
const CRB_INTF_ID: u32 = 0x30;
|
||||
const CRB_INTF_ID_FIELDS: phf::Map<&str, [u32; 2]> = phf_map! {
|
||||
"InterfaceType" => [0, 4],
|
||||
"InterfaceVersion" => [4, 4],
|
||||
"CapLocality" => [8, 1],
|
||||
"CapCRBIdleBypass" => [9, 1],
|
||||
"Reserved1" => [10, 1],
|
||||
"CapDataXferSizeSupport" => [11, 2],
|
||||
"CapFIFO" => [13, 1],
|
||||
"CapCRB" => [14, 1],
|
||||
"CapIFRes" => [15, 2],
|
||||
"InterfaceSelector" => [17, 2],
|
||||
"IntfSelLock" => [19, 1],
|
||||
"Reserved2" => [20, 4],
|
||||
"RID" => [24, 8]
|
||||
};
|
||||
const CRB_INTF_ID2: u32 = 0x34;
|
||||
const CRB_INTF_ID2_FIELDS: phf::Map<&str, [u32; 2]> = phf_map! {
|
||||
"VID" => [0, 16],
|
||||
"DID" => [16, 16]
|
||||
};
|
||||
const CRB_CTRL_REQ: u32 = 0x40;
|
||||
const CRB_CTRL_REQ_CMD_READY: u32 = 1 << 0;
|
||||
const CRB_CTRL_REQ_GO_IDLE: u32 = 1 << 1;
|
||||
const CRB_CTRL_STS: u32 = 0x44;
|
||||
const CRB_CTRL_STS_FIELDS: phf::Map<&str, [u32; 2]> = phf_map! {
|
||||
"tpmSts" => [0, 1],
|
||||
"tpmIdle" => [1, 1]
|
||||
};
|
||||
const CRB_CTRL_CANCEL: u32 = 0x48;
|
||||
const CRB_CANCEL_INVOKE: u32 = 1 << 0;
|
||||
const CRB_CTRL_START: u32 = 0x4C;
|
||||
const CRB_START_INVOKE: u32 = 1 << 0;
|
||||
const CRB_CTRL_CMD_LADDR: u32 = 0x5C;
|
||||
const CRB_CTRL_CMD_HADDR: u32 = 0x60;
|
||||
const CRB_CTRL_RSP_SIZE: u32 = 0x64;
|
||||
const CRB_CTRL_RSP_ADDR: u32 = 0x68;
|
||||
const CRB_DATA_BUFFER: u32 = 0x80;
|
||||
|
||||
const TPM_CRB_NO_LOCALITY: u32 = 0xff;
|
||||
|
||||
const TPM_CRB_ADDR_BASE: u32 = TPM_START.0 as u32;
|
||||
const TPM_CRB_ADDR_SIZE: usize = TPM_SIZE as usize;
|
||||
|
||||
const TPM_CRB_R_MAX: u32 = CRB_DATA_BUFFER;
|
||||
|
||||
// CRB Protocol details
|
||||
const CRB_INTF_TYPE_CRB_ACTIVE: u32 = 0b1;
|
||||
const CRB_INTF_VERSION_CRB: u32 = 0b1;
|
||||
const CRB_INTF_CAP_LOCALITY_0_ONLY: u32 = 0b0;
|
||||
const CRB_INTF_CAP_IDLE_FAST: u32 = 0b0;
|
||||
const CRB_INTF_CAP_XFER_SIZE_64: u32 = 0b11;
|
||||
const CRB_INTF_CAP_FIFO_NOT_SUPPORTED: u32 = 0b0;
|
||||
const CRB_INTF_CAP_CRB_SUPPORTED: u32 = 0b1;
|
||||
const CRB_INTF_IF_SELECTOR_CRB: u32 = 0b1;
|
||||
const PCI_VENDOR_ID_IBM: u32 = 0x1014;
|
||||
const CRB_CTRL_CMD_SIZE_REG: u32 = 0x58;
|
||||
const CRB_CTRL_CMD_SIZE: usize = TPM_CRB_ADDR_SIZE - CRB_DATA_BUFFER as usize;
|
||||
|
||||
fn get_fields_map(reg: u32) -> phf::Map<&'static str, [u32; 2]> {
|
||||
match reg {
|
||||
CRB_LOC_STATE => CRB_LOC_STATE_FIELDS,
|
||||
CRB_LOC_STS => CRB_LOC_STS_FIELDS,
|
||||
CRB_INTF_ID => CRB_INTF_ID_FIELDS,
|
||||
CRB_INTF_ID2 => CRB_INTF_ID2_FIELDS,
|
||||
CRB_CTRL_STS => CRB_CTRL_STS_FIELDS,
|
||||
_ => {
|
||||
panic!(
|
||||
"Fields in '{:?}' register were accessed which are Invalid",
|
||||
reg
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a particular field in a Register
|
||||
fn set_reg_field(regs: &mut [u32; TPM_CRB_R_MAX as usize], reg: u32, field: &str, value: u32) {
|
||||
let reg_fields = get_fields_map(reg);
|
||||
if reg_fields.contains_key(field) {
|
||||
let start = reg_fields.get(field).unwrap()[0];
|
||||
let len = reg_fields.get(field).unwrap()[1];
|
||||
let mask = (!(0_u32) >> (32 - len)) << start;
|
||||
regs[reg as usize] = (regs[reg as usize] & !mask) | ((value << start) & mask);
|
||||
} else {
|
||||
error!(
|
||||
"Failed to tpm Register. {:?} is not a valid field in Reg {:#X}",
|
||||
field, reg
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the value of a particular field in a Register
|
||||
fn get_reg_field(regs: &[u32; TPM_CRB_R_MAX as usize], reg: u32, field: &str) -> u32 {
|
||||
let reg_fields = get_fields_map(reg);
|
||||
if reg_fields.contains_key(field) {
|
||||
let start = reg_fields.get(field).unwrap()[0];
|
||||
let len = reg_fields.get(field).unwrap()[1];
|
||||
let mask = (!(0_u32) >> (32 - len)) << start;
|
||||
(regs[reg as usize] & mask) >> start
|
||||
} else {
|
||||
// TODO: Sensible return value if fields do not exist
|
||||
0x0
|
||||
}
|
||||
}
|
||||
|
||||
fn locality_from_addr(addr: u32) -> u8 {
|
||||
(addr >> 12) as u8
|
||||
}
|
||||
|
||||
pub struct Tpm {
|
||||
emulator: Emulator,
|
||||
cmd: Option<BackendCmd>,
|
||||
regs: [u32; TPM_CRB_R_MAX as usize],
|
||||
backend_buff_size: usize,
|
||||
data_buff: [u8; TPM_CRB_BUFFER_MAX],
|
||||
data_buff_len: usize,
|
||||
}
|
||||
|
||||
impl Tpm {
|
||||
pub fn new(path: String) -> Result<Self> {
|
||||
let emulator = Emulator::new(path)
|
||||
.map_err(|e| Error::Init(anyhow!("Failed while initializing tpm Emulator: {:?}", e)))?;
|
||||
let mut tpm = Tpm {
|
||||
emulator,
|
||||
cmd: None,
|
||||
regs: [0; TPM_CRB_R_MAX as usize],
|
||||
backend_buff_size: TPM_CRB_BUFFER_MAX,
|
||||
data_buff: [0; TPM_CRB_BUFFER_MAX],
|
||||
data_buff_len: 0,
|
||||
};
|
||||
tpm.reset()?;
|
||||
Ok(tpm)
|
||||
}
|
||||
|
||||
fn get_active_locality(&mut self) -> u32 {
|
||||
if get_reg_field(&self.regs, CRB_LOC_STATE, "locAssigned") == 0 {
|
||||
return TPM_CRB_NO_LOCALITY;
|
||||
}
|
||||
get_reg_field(&self.regs, CRB_LOC_STATE, "activeLocality")
|
||||
}
|
||||
|
||||
fn request_completed(&mut self, result: isize) {
|
||||
self.regs[CRB_CTRL_START as usize] = !CRB_START_INVOKE;
|
||||
if result != 0 {
|
||||
set_reg_field(&mut self.regs, CRB_CTRL_STS, "tpmSts", 1);
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> Result<()> {
|
||||
let cur_buff_size = self.emulator.get_buffer_size().unwrap();
|
||||
self.regs = [0; TPM_CRB_R_MAX as usize];
|
||||
set_reg_field(&mut self.regs, CRB_LOC_STATE, "tpmRegValidSts", 1);
|
||||
set_reg_field(&mut self.regs, CRB_CTRL_STS, "tpmIdle", 1);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CRB_INTF_ID,
|
||||
"InterfaceType",
|
||||
CRB_INTF_TYPE_CRB_ACTIVE,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CRB_INTF_ID,
|
||||
"InterfaceVersion",
|
||||
CRB_INTF_VERSION_CRB,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CRB_INTF_ID,
|
||||
"CapLocality",
|
||||
CRB_INTF_CAP_LOCALITY_0_ONLY,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CRB_INTF_ID,
|
||||
"CapCRBIdleBypass",
|
||||
CRB_INTF_CAP_IDLE_FAST,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CRB_INTF_ID,
|
||||
"CapDataXferSizeSupport",
|
||||
CRB_INTF_CAP_XFER_SIZE_64,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CRB_INTF_ID,
|
||||
"CapFIFO",
|
||||
CRB_INTF_CAP_FIFO_NOT_SUPPORTED,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CRB_INTF_ID,
|
||||
"CapCRB",
|
||||
CRB_INTF_CAP_CRB_SUPPORTED,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CRB_INTF_ID,
|
||||
"InterfaceSelector",
|
||||
CRB_INTF_IF_SELECTOR_CRB,
|
||||
);
|
||||
set_reg_field(&mut self.regs, CRB_INTF_ID, "RID", 0b0000);
|
||||
set_reg_field(&mut self.regs, CRB_INTF_ID2, "VID", PCI_VENDOR_ID_IBM);
|
||||
|
||||
self.regs[CRB_CTRL_CMD_SIZE_REG as usize] = CRB_CTRL_CMD_SIZE as u32;
|
||||
self.regs[CRB_CTRL_CMD_LADDR as usize] = TPM_CRB_ADDR_BASE + CRB_DATA_BUFFER;
|
||||
self.regs[CRB_CTRL_RSP_SIZE as usize] = CRB_CTRL_CMD_SIZE as u32;
|
||||
self.regs[CRB_CTRL_RSP_ADDR as usize] = TPM_CRB_ADDR_BASE + CRB_DATA_BUFFER;
|
||||
|
||||
self.backend_buff_size = cmp::min(cur_buff_size, TPM_CRB_BUFFER_MAX);
|
||||
|
||||
if let Err(e) = self.emulator.startup_tpm(self.backend_buff_size) {
|
||||
return Err(Error::Init(anyhow!(
|
||||
"Failed while running Startup TPM. Error: {:?}",
|
||||
e
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
//impl BusDevice for TPM
|
||||
impl BusDevice for Tpm {
|
||||
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
|
||||
let mut offset: u32 = offset as u32;
|
||||
let read_len: usize = data.len();
|
||||
|
||||
if offset >= CRB_DATA_BUFFER
|
||||
&& (offset + read_len as u32) < (CRB_DATA_BUFFER + self.data_buff.len() as u32)
|
||||
{
|
||||
// Read from Data Buffer
|
||||
let start: usize = (offset as usize) - (CRB_DATA_BUFFER as usize);
|
||||
let end: usize = start + read_len;
|
||||
data[..].clone_from_slice(&self.data_buff[start..end]);
|
||||
} else {
|
||||
offset &= 0xff;
|
||||
let mut val = self.regs[offset as usize];
|
||||
|
||||
if offset == CRB_LOC_STATE && !self.emulator.get_established_flag() {
|
||||
val |= 0x1;
|
||||
}
|
||||
|
||||
if data.len() <= 4 {
|
||||
data.clone_from_slice(val.to_ne_bytes()[0..read_len].as_ref());
|
||||
} else {
|
||||
error!(
|
||||
"Invalid tpm read: offset {:#X}, data length {:?}",
|
||||
offset,
|
||||
data.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
debug!(
|
||||
"MMIO Read: offset {:#X} len {:?} val = {:02X?} ",
|
||||
offset,
|
||||
data.len(),
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
debug!(
|
||||
"MMIO Write: offset {:#X} len {:?} input data {:02X?}",
|
||||
offset,
|
||||
data.len(),
|
||||
data
|
||||
);
|
||||
let mut offset: u32 = offset as u32;
|
||||
if offset < CRB_DATA_BUFFER {
|
||||
offset &= 0xff;
|
||||
}
|
||||
let locality = locality_from_addr(offset) as u32;
|
||||
let write_len = data.len();
|
||||
|
||||
if offset >= CRB_DATA_BUFFER
|
||||
&& (offset + write_len as u32) < (CRB_DATA_BUFFER + self.data_buff.len() as u32)
|
||||
{
|
||||
let start: usize = (offset as usize) - (CRB_DATA_BUFFER as usize);
|
||||
if start == 0 {
|
||||
// If filling data_buff at index 0, reset length to 0
|
||||
self.data_buff_len = 0;
|
||||
self.data_buff.fill(0);
|
||||
}
|
||||
let end: usize = start + data.len();
|
||||
self.data_buff[start..end].clone_from_slice(data);
|
||||
self.data_buff_len += data.len();
|
||||
} else {
|
||||
// Ctrl Commands that take more than 4 bytes as input are not yet supported
|
||||
// CTRL_RSP_ADDR usually gets 8 byte write request. Last 4 bytes are zeros.
|
||||
if write_len > 4 && offset != CRB_CTRL_RSP_ADDR {
|
||||
error!(
|
||||
"Invalid tpm write: offset {:#X}, data length {}",
|
||||
offset,
|
||||
data.len()
|
||||
);
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut input: [u8; 4] = [0; 4];
|
||||
input.copy_from_slice(&data[0..4]);
|
||||
let v = u32::from_le_bytes(input);
|
||||
|
||||
match offset {
|
||||
CRB_CTRL_CMD_SIZE_REG => {
|
||||
self.regs[CRB_CTRL_CMD_SIZE_REG as usize] = v;
|
||||
}
|
||||
CRB_CTRL_CMD_LADDR => {
|
||||
self.regs[CRB_CTRL_CMD_LADDR as usize] = v;
|
||||
}
|
||||
CRB_CTRL_CMD_HADDR => {
|
||||
self.regs[CRB_CTRL_CMD_HADDR as usize] = v;
|
||||
}
|
||||
CRB_CTRL_RSP_SIZE => {
|
||||
self.regs[CRB_CTRL_RSP_SIZE as usize] = v;
|
||||
}
|
||||
CRB_CTRL_RSP_ADDR => {
|
||||
self.regs[CRB_CTRL_RSP_ADDR as usize] = v;
|
||||
}
|
||||
CRB_CTRL_REQ => match v {
|
||||
CRB_CTRL_REQ_CMD_READY => {
|
||||
set_reg_field(&mut self.regs, CRB_CTRL_STS, "tpmIdle", 0);
|
||||
}
|
||||
CRB_CTRL_REQ_GO_IDLE => {
|
||||
set_reg_field(&mut self.regs, CRB_CTRL_STS, "tpmIdle", 1);
|
||||
}
|
||||
_ => {
|
||||
error!("Invalid value passed to CRTL_REQ register");
|
||||
return None;
|
||||
}
|
||||
},
|
||||
CRB_CTRL_CANCEL => {
|
||||
if v == CRB_CANCEL_INVOKE
|
||||
&& (self.regs[CRB_CTRL_START as usize] & CRB_START_INVOKE != 0)
|
||||
{
|
||||
if let Err(e) = self.emulator.cancel_cmd() {
|
||||
error!("Failed to run cancel command. Error: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
CRB_CTRL_START => {
|
||||
if v == CRB_START_INVOKE
|
||||
&& ((self.regs[CRB_CTRL_START as usize] & CRB_START_INVOKE) == 0)
|
||||
&& self.get_active_locality() == locality
|
||||
{
|
||||
self.regs[CRB_CTRL_START as usize] |= CRB_START_INVOKE;
|
||||
|
||||
self.cmd = Some(BackendCmd {
|
||||
locality: locality as u8,
|
||||
input: self.data_buff[0..self.data_buff_len].to_vec(),
|
||||
input_len: cmp::min(self.data_buff_len, TPM_CRB_BUFFER_MAX),
|
||||
output: self.data_buff.to_vec(),
|
||||
output_len: TPM_CRB_BUFFER_MAX,
|
||||
selftest_done: false,
|
||||
});
|
||||
|
||||
let mut cmd = self.cmd.as_ref().unwrap().clone();
|
||||
let output = self.emulator.deliver_request(&mut cmd).map_err(|e| {
|
||||
Error::DeliverRequest(anyhow!(
|
||||
"Failed to deliver tpm request. Error :{:?}",
|
||||
e
|
||||
))
|
||||
});
|
||||
//TODO: drop the copy here
|
||||
self.data_buff.fill(0);
|
||||
self.data_buff.clone_from_slice(output.unwrap().as_slice());
|
||||
|
||||
self.request_completed(TPM_SUCCESS as isize);
|
||||
}
|
||||
}
|
||||
CRB_LOC_CTRL => {
|
||||
warn!(
|
||||
"CRB_LOC_CTRL locality to write = {:?} val = {:?}",
|
||||
locality, v
|
||||
);
|
||||
match v {
|
||||
CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT => {}
|
||||
CRB_LOC_CTRL_RELINQUISH => {
|
||||
set_reg_field(&mut self.regs, CRB_LOC_STATE, "locAssigned", 0);
|
||||
set_reg_field(&mut self.regs, CRB_LOC_STS, "Granted", 0);
|
||||
}
|
||||
CRB_LOC_CTRL_REQUEST_ACCESS => {
|
||||
set_reg_field(&mut self.regs, CRB_LOC_STS, "Granted", 1);
|
||||
set_reg_field(&mut self.regs, CRB_LOC_STS, "beenSeized", 0);
|
||||
set_reg_field(&mut self.regs, CRB_LOC_STATE, "locAssigned", 1);
|
||||
}
|
||||
_ => {
|
||||
error!("Invalid value to write in CRB_LOC_CTRL {:#X} ", v);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!(
|
||||
"Invalid tpm write: offset {:#X}, data length {:?}",
|
||||
offset,
|
||||
data.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_set_get_reg_field() {
|
||||
let mut regs: [u32; TPM_CRB_R_MAX as usize] = [0; TPM_CRB_R_MAX as usize];
|
||||
set_reg_field(&mut regs, CRB_INTF_ID, "RID", 0xAC);
|
||||
assert_eq!(
|
||||
get_reg_field(®s, CRB_INTF_ID, "RID"),
|
||||
0xAC,
|
||||
concat!("Test: ", stringify!(set_get_reg_field))
|
||||
);
|
||||
}
|
||||
}
|
||||
82
docs/building.md
Normal file
82
docs/building.md
Normal file
@@ -0,0 +1,82 @@
|
||||
- [Building Cloud Hypervisor](#building-cloud-hypervisor)
|
||||
- [Preparation](#preparation)
|
||||
- [Install prerequisites](#install-prerequisites)
|
||||
- [Clone and build](#clone-and-build)
|
||||
- [Containerized builds and tests](#containerized-builds-and-tests)
|
||||
|
||||
# Building Cloud Hypervisor
|
||||
|
||||
We recommend users use the pre-built binaries that are mentioned in the README.md file in the root of the repository. Building from source is only necessary if you wish to make modifications.
|
||||
|
||||
## Preparation
|
||||
|
||||
We create a folder to build and run `cloud-hypervisor` at `$HOME/cloud-hypervisor`
|
||||
|
||||
```shell
|
||||
$ export CLOUDH=$HOME/cloud-hypervisor
|
||||
$ mkdir $CLOUDH
|
||||
```
|
||||
|
||||
## Install prerequisites
|
||||
|
||||
You need to install some prerequisite packages in order to build and test Cloud
|
||||
Hypervisor. Here, all the steps are based on Ubuntu, for other Linux
|
||||
distributions please replace the package manager and package name.
|
||||
|
||||
```shell
|
||||
# Install build-essential, git, and qemu-utils
|
||||
$ sudo apt install git build-essential qemu-utils
|
||||
# Install rust tool chain
|
||||
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
# If you want to build statically linked binary please add musl target
|
||||
$ rustup target add x86_64-unknown-linux-musl
|
||||
```
|
||||
|
||||
## Clone and build
|
||||
|
||||
First you need to clone and build the Cloud Hypervisor repository:
|
||||
|
||||
```shell
|
||||
$ pushd $CLOUDH
|
||||
$ git clone https://github.com/cloud-hypervisor/cloud-hypervisor.git
|
||||
$ cd cloud-hypervisor
|
||||
$ cargo build --release
|
||||
|
||||
# We need to give the cloud-hypervisor binary the NET_ADMIN capabilities for it to set TAP interfaces up on the host.
|
||||
$ sudo setcap cap_net_admin+ep ./target/release/cloud-hypervisor
|
||||
|
||||
# If you want to build statically linked binary
|
||||
$ cargo build --release --target=x86_64-unknown-linux-musl --all
|
||||
$ popd
|
||||
```
|
||||
|
||||
This will build a `cloud-hypervisor` binary under
|
||||
`$CLOUDH/cloud-hypervisor/target/release/cloud-hypervisor`.
|
||||
|
||||
### Containerized builds and tests
|
||||
|
||||
If you want to build and test Cloud Hypervisor without having to install all the
|
||||
required dependencies (The rust toolchain, cargo tools, etc), you can also use
|
||||
Cloud Hypervisor's development script: `dev_cli.sh`. Please note that upon its
|
||||
first invocation, this script will pull a fairly large container image.
|
||||
|
||||
For example, to build the Cloud Hypervisor release binary:
|
||||
|
||||
```shell
|
||||
$ pushd $CLOUDH
|
||||
$ cd cloud-hypervisor
|
||||
$ ./scripts/dev_cli.sh build --release
|
||||
```
|
||||
|
||||
With `dev_cli.sh`, one can also run the Cloud Hypervisor CI locally. This can be
|
||||
very convenient for debugging CI errors without having to fully rely on the
|
||||
Cloud Hypervisor CI infrastructure.
|
||||
|
||||
For example, to run the Cloud Hypervisor unit tests:
|
||||
|
||||
```shell
|
||||
$ ./scripts/dev_cli.sh tests --unit
|
||||
```
|
||||
|
||||
Run the `./scripts/dev_cli.sh --help` command to view all the supported
|
||||
development script commands and their related options.
|
||||
@@ -20,12 +20,13 @@ struct MemoryConfig {
|
||||
hugepages: bool,
|
||||
hugepage_size: Option<u64>,
|
||||
prefault: bool,
|
||||
thp: bool
|
||||
zones: Option<Vec<MemoryZoneConfig>>,
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
--memory <memory> Memory parameters "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>,prefault=on|off" [default: size=512M]
|
||||
--memory <memory> Memory parameters "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>,prefault=on|off,thp=on|off" [default: size=512M,thp=on]
|
||||
```
|
||||
|
||||
### `size`
|
||||
@@ -117,6 +118,9 @@ needing access to the guest RAM content.
|
||||
By default this option is turned off, which results in performing `mmap(2)`
|
||||
with `MAP_PRIVATE` flag.
|
||||
|
||||
If `hugepages=on` then the value of this field is ignored as huge pages always
|
||||
requires `MAP_SHARED`.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
@@ -140,6 +144,9 @@ behaviour, e.g. error with `ReadKernelImage` is common. If there is a strange
|
||||
error with `hugepages` enabled, just disable it or check whether there are enough
|
||||
huge pages.
|
||||
|
||||
If `hugepages=on` then the value of `shared` is ignored as huge pages always
|
||||
requires `MAP_SHARED`.
|
||||
|
||||
By default this option is turned off.
|
||||
|
||||
_Example_
|
||||
@@ -171,6 +178,25 @@ _Example_
|
||||
--memory size=1G,prefault=on
|
||||
```
|
||||
|
||||
### `thp`
|
||||
|
||||
Specifies if private anonymous memory for the guest (i.e. `shared=off` and no
|
||||
backing file) should be labelled `MADV_HUGEPAGE` with `madvise(2)` indicating
|
||||
to the kernel that this memory may be backed with huge pages transparently.
|
||||
|
||||
The use of transparent huge pages can improve the performance of the guest as
|
||||
there will fewer virtualisation related page faults. Unlike using
|
||||
`hugepages=on` a specific number of huge pages do not need to be allocated by
|
||||
the kernel.
|
||||
|
||||
By default this option is turned on.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--memory size=1G,thp=on
|
||||
```
|
||||
|
||||
## Advanced Parameters
|
||||
|
||||
`MemoryZoneConfig` or what is known as `--memory-zone` from the CLI perspective
|
||||
@@ -271,6 +297,9 @@ other processes running on the host. One can use this option when running
|
||||
vhost-user devices as part of the VM device model, as they will be driven
|
||||
by standalone daemons needing access to the guest RAM content.
|
||||
|
||||
If `hugepages=on` then the value of this field is ignored as huge pages always
|
||||
requires `MAP_SHARED`.
|
||||
|
||||
By default this option is turned off, which result in performing `mmap(2)`
|
||||
with `MAP_PRIVATE` flag.
|
||||
|
||||
@@ -298,6 +327,9 @@ behaviour, e.g. error with `ReadKernelImage` is common. If there is a strange
|
||||
error with `hugepages` enabled, just disable it or check whether there are enough
|
||||
huge pages.
|
||||
|
||||
If `hugepages=on` then the value of `shared` is ignored as huge pages always
|
||||
requires `MAP_SHARED`.
|
||||
|
||||
By default this option is turned off.
|
||||
|
||||
_Example_
|
||||
|
||||
@@ -64,5 +64,5 @@ it will log every system call issued by the process. It is important to use
|
||||
`-f` option in order to trace each and every thread belonging to the process.
|
||||
|
||||
```
|
||||
strace -f ./cloud-hypervisor ...
|
||||
strace --decode-pids=comm -f ./cloud-hypervisor ...
|
||||
```
|
||||
|
||||
122
docs/tpm.md
Normal file
122
docs/tpm.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# TPM
|
||||
Tpm in Cloud-Hypervisor is emulated using `swtpm` as the backend. [swtpm](https://github.com/stefanberger/swtpm) is the link to swtpm project.
|
||||
|
||||
Current implementation only supports TPM `2.0` version. At the moment only
|
||||
`CRB Interface` is implemented. This interface is described in
|
||||
[TCG PC Client Platform TPM Profile Specification for TPM 2.0, Revision 01.05 v4](https://trustedcomputinggroup.org/wp-content/uploads/PC-Client-Specific-Platform-TPM-Profile-for-TPM-2p0-v1p05p_r14_pub.pdf).
|
||||
|
||||
|
||||
## Usage
|
||||
`--tpm`, an optional argument, can be passed to enable tpm device.
|
||||
This argument takes an UNIX domain Socket as a `socket` value.
|
||||
|
||||
_Example_
|
||||
|
||||
An Example invocation with `--tpm` argument:
|
||||
|
||||
```
|
||||
./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||
--kernel ./hypervisor-fw \
|
||||
--disk path=focal-server-cloudimg-amd64.raw \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
--net "tap=,mac=,ip=,mask=" \
|
||||
--tpm socket="/var/run/swtpm.socket"
|
||||
```
|
||||
|
||||
## swtpm
|
||||
Before invoking cloud-hypervisor with `--tpm` argument, a `swtpm`
|
||||
process should be started to listen at the input socket. Below is an
|
||||
example invocation of swtpm process.
|
||||
|
||||
```
|
||||
swtpm socket --tpmstate dir=/var/run/swtpm \
|
||||
--ctrl type=unixio,path="/var/run/swtpm.socket" \
|
||||
--flags startup-clear \
|
||||
--tpm2
|
||||
```
|
||||
|
||||
## Guest
|
||||
After starting a guest with the above commands, ensure below listed modules are
|
||||
loaded in the guest:
|
||||
|
||||
```
|
||||
# lsmod | grep tpm
|
||||
tpm_crb 20480 0
|
||||
tpm 81920 1 tpm_crb
|
||||
```
|
||||
|
||||
Below is the IO Memory map configured in the guest:
|
||||
|
||||
```
|
||||
# cat /proc/iomem | grep MSFT
|
||||
fed40000-fed40fff : MSFT0101:00
|
||||
fed40000-fed40fff : MSFT0101:00
|
||||
```
|
||||
Below are the devices created in the guest:
|
||||
|
||||
```
|
||||
# ls /dev/tpm*
|
||||
/dev/tpm0 /dev/tpmrm0
|
||||
```
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
Inside the guest install `tpm2-tools` package. This package provides some
|
||||
commands to run against TPM that supports 2.0 version.
|
||||
|
||||
_Examples_
|
||||
```
|
||||
// Run Self Test
|
||||
# tpm2_selftest -f
|
||||
# echo $?
|
||||
0
|
||||
|
||||
|
||||
# echo "hello" > input.txt
|
||||
// this command generates hash of the input file using all the algos supported by TPM
|
||||
|
||||
# tpm2_pcrevent input.txt
|
||||
sha1: f572d396fae9206628714fb2ce00f72e94f2258f
|
||||
sha256: 5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03
|
||||
sha384: 1d0f284efe3edea4b9ca3bd514fa134b17eae361ccc7a1eefeff801b9bd6604e01f21f6bf249ef030599f0c
|
||||
218f2ba8c
|
||||
sha512: e7c22b994c59d9cf2b48e549b1e24666636045930d3da7c1acb299d1c3b7f931f94aae41edda2c2b207a36e
|
||||
10f8bcb8d45223e54878f5b316e7ce3b6bc019629
|
||||
|
||||
// verify one of the hashes
|
||||
# sha256sum input.txt
|
||||
5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03 input.txt
|
||||
```
|
||||
|
||||
### Bundled Functional Test
|
||||
|
||||
Build time dependencies for `tpm2-tss` are captured in [INSTALL](https://github.com/tpm2-software/tpm2-tss/blob/master/INSTALL.md).
|
||||
|
||||
```
|
||||
# git clone https://github.com/tpm2-software/tpm2-tss.git
|
||||
# cd tpm2-tss
|
||||
# ./configure --enable-integration --with-devicetests="mandatory,optional" --with-device=/dev/tpm0
|
||||
# sudo make check-device
|
||||
.
|
||||
.
|
||||
.
|
||||
.
|
||||
============================================================================
|
||||
Testsuite summary for tpm2-tss 3.2.0-74-ge03617d9
|
||||
============================================================================
|
||||
# TOTAL: 154
|
||||
# PASS: 88
|
||||
# SKIP: 7
|
||||
# XFAIL: 0
|
||||
# FAIL: 59
|
||||
# XPASS: 0
|
||||
# ERROR: 0
|
||||
============================================================================
|
||||
See ./test-suite.log
|
||||
Please report to https://github.com/tpm2-software/tpm2-tss/issues
|
||||
============================================================================
|
||||
```
|
||||
The same set of failures are noticed while running these tests on `Qemu` with
|
||||
its TPM implementation.
|
||||
@@ -131,7 +131,7 @@ See also the [links](#Links) section for a more extended SAC documentation.
|
||||
|
||||
## Network
|
||||
|
||||
This section illustrates the Windows specific corner points for the VM network configuration. For the extended networking guide, including bridging for multiple VMs, follow [networking.md](networking.md).
|
||||
This section illustrates the Windows specific aspects of the VM network configuration.
|
||||
|
||||
### Basic Networking
|
||||
|
||||
|
||||
@@ -5,6 +5,6 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.133"
|
||||
serde = { version = "1.0.145", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.85"
|
||||
libc = "0.2.138"
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.89"
|
||||
|
||||
353
fuzz/Cargo.lock
generated
353
fuzz/Cargo.lock
generated
@@ -11,9 +11,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.65"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
|
||||
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -24,9 +24,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.1.6"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f44124848854b941eafdb34f05b3bcf59472f643c7e151eba7c2b69daa469ed5"
|
||||
checksum = "29d47fbf90d5149a107494b15a7dc8d69b351be2db3bb9691740e88ec17fd880"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
@@ -56,23 +56,6 @@ dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
@@ -115,9 +98,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
version = "1.0.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
@@ -130,33 +113,31 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.2.22"
|
||||
version = "4.0.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750"
|
||||
checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"is-terminal",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"terminal_size",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.4"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||
checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "26.0.0"
|
||||
version = "28.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -171,6 +152,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"signal-hook",
|
||||
"thiserror",
|
||||
"tpm",
|
||||
"tracer",
|
||||
"vm-memory",
|
||||
"vmm",
|
||||
@@ -218,9 +200,9 @@ checksum = "55626594feae15d266d52440b26ff77de0e22230cf0c113abe619084c1ddc910"
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.14.1"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02"
|
||||
checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
@@ -228,9 +210,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.14.1"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f"
|
||||
checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
@@ -242,9 +224,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.14.1"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5"
|
||||
checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
@@ -263,6 +245,9 @@ dependencies = [
|
||||
"hypervisor",
|
||||
"libc",
|
||||
"log",
|
||||
"phf",
|
||||
"thiserror",
|
||||
"tpm",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"vm-device",
|
||||
@@ -325,26 +310,20 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.7"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.20"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7a30908dbce072eca83216eab939d2290080e00ca71611b96a09e5cdce5f3fa"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -385,36 +364,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.1"
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||
checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ea37f355c05dde75b84bba2d767906ad522e97cd9e2eef2be7a4ab7fb442c06"
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.5.6"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a645fd8e7048c2f54050f7d0d4de8f624ada437e043bc7d72609bd08d70120b"
|
||||
checksum = "7ba34abb5175052fc1a2227a10d2275b7386c9990167de9786c0b88d8b062330"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.3"
|
||||
name = "is-terminal"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
|
||||
checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
@@ -427,8 +412,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kvm-bindings"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.5.0-tdx#52e56d0e8ef0f6ea32fc0492e6a175b73617a49f"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.6.0-tdx#7d9ffb47e5b9b1989577258800a0f57c93f1445f"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -437,8 +422,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kvm-ioctls"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/rust-vmm/kvm-ioctls?branch=main#1e03e29cdfbb0cb108a98de7a78045a5a517f18e"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3a321cabd827642499c77e27314f388dd83a717a5ca716b86476fb947f73ae4"
|
||||
dependencies = [
|
||||
"kvm-bindings",
|
||||
"libc",
|
||||
@@ -453,15 +439,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.133"
|
||||
version = "0.2.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
|
||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.4.4"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae185684fe19814afd066da15a7cc41e126886c21282934225d9fc847582da58"
|
||||
checksum = "c8fff891139ee62800da71b7fd5b508d570b9ad95e614a53c6f453ca08366038"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"cc",
|
||||
@@ -470,18 +456,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-loader"
|
||||
version = "0.6.0"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62a2f912deca034ec34b0a43a390059ea98daac40e440ebe8bea88f3315fe168"
|
||||
checksum = "b9259ddbfbb52cc918f6bbc60390004ddd0228cf1d85f402009ff2b3d95de83f"
|
||||
dependencies = [
|
||||
"vm-memory",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.0.46"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d"
|
||||
checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@@ -495,7 +481,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#863b0370ba7e57f7df5b908ada9e5b44809ccae9"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#4b18a043e997da5b5f679e3defc279fec908753e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"vmm-sys-util",
|
||||
@@ -531,9 +517,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.15.0"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
|
||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||
|
||||
[[package]]
|
||||
name = "option_parser"
|
||||
@@ -541,9 +527,9 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.3.0"
|
||||
version = "6.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
|
||||
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||
|
||||
[[package]]
|
||||
name = "pci"
|
||||
@@ -569,10 +555,52 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.45"
|
||||
name = "phf"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3edcd08cf4fea98d1ae6c9ddd3b8ccb1acac7c3693d62625969a7daa04a2ae36"
|
||||
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -597,6 +625,21 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "rate_limiter"
|
||||
version = "0.1.0"
|
||||
@@ -608,9 +651,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "remain"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06df529c0d271b27ac4a2c260f5ce2914b60bd44702cecec7b9f271adbdde23b"
|
||||
checksum = "1a81bc6a3aaeb180f767bd2bda8c03bac5b93b3b69a783c66f35d16a4df276cf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -628,9 +671,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.35.11"
|
||||
version = "0.36.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbb2fda4666def1433b1b05431ab402e42a1084285477222b72d6c564c417cef"
|
||||
checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -663,18 +706,18 @@ checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.145"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
|
||||
checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.145"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
|
||||
checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -683,9 +726,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.85"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
|
||||
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -694,9 +737,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "2.0.1"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368f2d60d049ea019a84dcd6687b0d1e0030fe663ae105039bdf967ed5e6a9a7"
|
||||
checksum = "25bf4a5a814902cd1014dbccfa4d4560fb8432c779471e96e035602519f82eef"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_with_macros",
|
||||
@@ -704,9 +747,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "2.0.1"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ccadfacf6cf10faad22bbadf55986bdd0856edfb5d9210aa1dcf1f516e84e93"
|
||||
checksum = "e3452b4c0f6c1e357f73fdb87cd1efabaa12acf328c7a528e252893baeb3f4aa"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
@@ -737,6 +780,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
@@ -751,9 +800,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.101"
|
||||
version = "1.0.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
|
||||
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -771,23 +820,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.2.1"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8440c860cf79def6164e4a0a983bcc2305d82419177a0e0c71930d049e3ac5a1"
|
||||
checksum = "cb20089a8ba2b69debd491f8d2d023761cbf196e999218c591fa1e7e15a21907"
|
||||
dependencies = [
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
|
||||
dependencies = [
|
||||
"terminal_size",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.37"
|
||||
@@ -808,6 +848,18 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tpm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"byteorder",
|
||||
"libc",
|
||||
"log",
|
||||
"thiserror",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracer"
|
||||
version = "0.1.0"
|
||||
@@ -821,24 +873,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.4"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.1.2"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
|
||||
checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "versionize"
|
||||
version = "0.1.6"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7429cf68de8f091b667d27323ed323afd39584a56d533995b12ddd748e5e6ca9"
|
||||
checksum = "d6e2495726cf917e7ba7ec8bf0f0fceab543dd38d0a4195ed6bef331e38a290f"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"crc64",
|
||||
@@ -863,17 +915,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vfio-bindings"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43449b404c488f70507dca193debd4bea361fe8089869b947adc19720e464bce"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#7ef33f6759ae6d37a54cd66f749aa47c1c81509c"
|
||||
dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vfio-ioctls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#bf7c9b997b4ad0ca1ab4bfe96dc1b10d4bc5be24"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#7ef33f6759ae6d37a54cd66f749aa47c1c81509c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
@@ -916,9 +967,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vhost"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79243657c76e5c90dcbf60187c842614f6dfc7123972c55bb3bcc446792aca93"
|
||||
checksum = "c9b791c5b0717a0558888a4cf7240cea836f39a99cb342e12ce633dcaa078072"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -969,9 +1020,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e2b7bea73248006630c9611a61ac3b8e44eece09342801fe1faec583b7a9f02"
|
||||
checksum = "19e927d93d54c365034fd7f31a5f458a1f540de4a37c52e892670dad9692173c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"virtio-bindings",
|
||||
@@ -1003,14 +1054,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vm-fdt"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#720e48e435b791ec6cbe8d2b229448b71dcb1ab9"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#c5a99ab71b130435927d19b50c85fcd5ce904a8c"
|
||||
|
||||
[[package]]
|
||||
name = "vm-memory"
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "583f213899e8a5eea23d9c507252d4bed5bc88f0ecbe0783262f80034630744b"
|
||||
checksum = "688a70366615b45575a424d9c665561c1b5ab2224d494f706b6a6812911a827c"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"libc",
|
||||
@@ -1088,9 +1139,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vmm-sys-util"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08604d7be03eb26e33b3cee3ed4aef2bf550b305d1cca60e84da5d28d3790b62"
|
||||
checksum = "cc06a16ee8ebf0d9269aed304030b0d20a866b8b3dd3d4ce532596ac567a0d24"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -1137,43 +1188,57 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.36.1"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.36.1"
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.36.1"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.36.1"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.36.1"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.36.1"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||
|
||||
@@ -12,18 +12,18 @@ cargo-fuzz = true
|
||||
block_util = { path = "../block_util" }
|
||||
devices = { path = "../devices" }
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.133"
|
||||
libfuzzer-sys = "0.4.4"
|
||||
libc = "0.2.135"
|
||||
libfuzzer-sys = "0.4.5"
|
||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||
once_cell = "1.15.0"
|
||||
once_cell = "1.16.0"
|
||||
qcow = { path = "../qcow" }
|
||||
seccompiler = "0.3.0"
|
||||
vhdx = { path = "../vhdx" }
|
||||
virtio-devices = { path = "../virtio-devices" }
|
||||
virtio-queue = "0.6.1"
|
||||
virtio-queue = "0.7.0"
|
||||
vmm = { path = "../vmm" }
|
||||
vmm-sys-util = "0.10.0"
|
||||
vm-memory = "0.9.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
vm-memory = "0.10.0"
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
|
||||
@@ -31,8 +31,7 @@ vm-virtio = { path = "../vm-virtio" }
|
||||
path = ".."
|
||||
|
||||
[patch.crates-io]
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0-tdx" }
|
||||
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "main" }
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx" }
|
||||
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
@@ -57,12 +56,30 @@ path = "fuzz_targets/cmos.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "console"
|
||||
path = "fuzz_targets/console.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "http_api"
|
||||
path = "fuzz_targets/http_api.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "iommu"
|
||||
path = "fuzz_targets/iommu.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "mem"
|
||||
path = "fuzz_targets/mem.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "pmem"
|
||||
path = "fuzz_targets/pmem.rs"
|
||||
|
||||
@@ -16,12 +16,12 @@ use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
|
||||
const QUEUE_DATA_SIZE: usize = 4;
|
||||
const MEM_SIZE: usize = 1 * 1024 * 1024;
|
||||
const MEM_SIZE: usize = 512 * 1024;
|
||||
const BALLOON_SIZE: u64 = 512 * 1024;
|
||||
// Number of queues
|
||||
const QUEUE_NUM: usize = 3;
|
||||
// Max entries in the queue.
|
||||
const QUEUE_SIZE: u16 = 256;
|
||||
const QUEUE_SIZE: u16 = 64;
|
||||
// Descriptor table alignment
|
||||
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||
// Avalable ring alignment
|
||||
@@ -49,6 +49,7 @@ fuzz_target!(|bytes| {
|
||||
true,
|
||||
SeccompAction::Allow,
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ fuzz_target!(|bytes| {
|
||||
SeccompAction::Allow,
|
||||
None,
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
194
fuzz/fuzz_targets/console.rs
Normal file
194
fuzz/fuzz_targets/console.rs
Normal file
@@ -0,0 +1,194 @@
|
||||
// Copyright © 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use seccompiler::SeccompAction;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::sync::Arc;
|
||||
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||
use virtio_queue::{Queue, QueueT};
|
||||
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
|
||||
macro_rules! align {
|
||||
($n:expr, $align:expr) => {{
|
||||
(($n + $align - 1) / $align) * $align
|
||||
}};
|
||||
}
|
||||
|
||||
const CONSOLE_INPUT_SIZE: usize = 128;
|
||||
const QUEUE_DATA_SIZE: usize = 4;
|
||||
const MEM_SIZE: usize = 32 * 1024 * 1024;
|
||||
// Guest memory gap
|
||||
const GUEST_MEM_GAP: u64 = 1 * 1024 * 1024;
|
||||
// Guest physical address for the first virt queue
|
||||
const BASE_VIRT_QUEUE_ADDR: u64 = MEM_SIZE as u64 + GUEST_MEM_GAP;
|
||||
// Number of queues
|
||||
const QUEUE_NUM: usize = 2;
|
||||
// Max entries in the queue.
|
||||
const QUEUE_SIZE: u16 = 256;
|
||||
// Descriptor table alignment
|
||||
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||
// Used ring alignment
|
||||
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||
// Descriptor table size
|
||||
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
|
||||
// Available ring size
|
||||
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
||||
// Padding size before used ring
|
||||
const PADDING_SIZE: u64 = align!(AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE) - AVAIL_RING_SIZE;
|
||||
// Used ring size
|
||||
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
|
||||
// Virtio-queue size in bytes
|
||||
const QUEUE_BYTES_SIZE: usize = align!(
|
||||
DESC_TABLE_SIZE + AVAIL_RING_SIZE + PADDING_SIZE + USED_RING_SIZE,
|
||||
DESC_TABLE_ALIGN_SIZE
|
||||
) as usize;
|
||||
|
||||
fuzz_target!(|bytes| {
|
||||
if bytes.len() < (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + CONSOLE_INPUT_SIZE
|
||||
|| bytes.len()
|
||||
> (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + CONSOLE_INPUT_SIZE + MEM_SIZE
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let (pipe_rx, mut pipe_tx) = create_pipe().unwrap();
|
||||
let output = unsafe {
|
||||
File::from_raw_fd(
|
||||
memfd_create(&std::ffi::CString::new("fuzz_console_output").unwrap()).unwrap(),
|
||||
)
|
||||
};
|
||||
let endpoint = virtio_devices::Endpoint::FilePair(output, pipe_rx);
|
||||
|
||||
let (mut console, _) = virtio_devices::Console::new(
|
||||
"fuzzer_console".to_owned(),
|
||||
endpoint,
|
||||
None, // resize_pipe
|
||||
false, // iommu
|
||||
SeccompAction::Allow,
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let console_input_bytes = &bytes[..CONSOLE_INPUT_SIZE];
|
||||
let queue_data = &bytes[CONSOLE_INPUT_SIZE..CONSOLE_INPUT_SIZE + QUEUE_DATA_SIZE * QUEUE_NUM];
|
||||
let queue_bytes = &bytes[CONSOLE_INPUT_SIZE + QUEUE_DATA_SIZE * QUEUE_NUM
|
||||
..CONSOLE_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM];
|
||||
let mem_bytes = &bytes[CONSOLE_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM..];
|
||||
|
||||
// Setup the virt queues with the input bytes
|
||||
let mut queues = setup_virt_queues(
|
||||
&[
|
||||
&queue_data[..QUEUE_DATA_SIZE].try_into().unwrap(),
|
||||
&queue_data[QUEUE_DATA_SIZE..QUEUE_DATA_SIZE * 2]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
],
|
||||
BASE_VIRT_QUEUE_ADDR,
|
||||
);
|
||||
|
||||
// Setup the guest memory with the input bytes
|
||||
let mem = GuestMemoryMmap::from_ranges(&[
|
||||
(GuestAddress(0), MEM_SIZE),
|
||||
(GuestAddress(BASE_VIRT_QUEUE_ADDR), queue_bytes.len()),
|
||||
])
|
||||
.unwrap();
|
||||
if mem
|
||||
.write_slice(queue_bytes, GuestAddress(BASE_VIRT_QUEUE_ADDR))
|
||||
.is_err()
|
||||
{
|
||||
return;
|
||||
}
|
||||
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
||||
return;
|
||||
}
|
||||
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||
|
||||
let input_queue = queues.remove(0);
|
||||
let input_evt = EventFd::new(0).unwrap();
|
||||
let input_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(input_evt.as_raw_fd())) };
|
||||
let output_queue = queues.remove(0);
|
||||
let output_evt = EventFd::new(0).unwrap();
|
||||
let output_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(output_evt.as_raw_fd())) };
|
||||
|
||||
// Kick the 'queue' events and endpoint event before activate the console device
|
||||
input_queue_evt.write(1).unwrap();
|
||||
output_queue_evt.write(1).unwrap();
|
||||
pipe_tx.write_all(console_input_bytes).unwrap(); // To use fuzzed data;
|
||||
|
||||
console
|
||||
.activate(
|
||||
guest_memory,
|
||||
Arc::new(NoopVirtioInterrupt {}),
|
||||
vec![(0, input_queue, input_evt), (1, output_queue, output_evt)],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Wait for the events to finish and console device worker thread to return
|
||||
console.wait_for_epoll_threads();
|
||||
});
|
||||
|
||||
pub struct NoopVirtioInterrupt {}
|
||||
|
||||
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_virt_queues(bytes: &[&[u8; QUEUE_DATA_SIZE]], base_addr: u64) -> Vec<Queue> {
|
||||
let mut queues = Vec::new();
|
||||
for (i, b) in bytes.iter().enumerate() {
|
||||
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||
|
||||
let desc_table_addr = base_addr + (QUEUE_BYTES_SIZE * i) as u64;
|
||||
let avail_ring_addr = desc_table_addr + DESC_TABLE_SIZE;
|
||||
let used_ring_addr = avail_ring_addr + PADDING_SIZE + AVAIL_RING_SIZE;
|
||||
q.try_set_desc_table_address(GuestAddress(desc_table_addr))
|
||||
.unwrap();
|
||||
q.try_set_avail_ring_address(GuestAddress(avail_ring_addr))
|
||||
.unwrap();
|
||||
q.try_set_used_ring_address(GuestAddress(used_ring_addr))
|
||||
.unwrap();
|
||||
|
||||
q.set_next_avail(b[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
|
||||
q.set_next_used(b[1] as u16);
|
||||
q.set_event_idx(b[2] % 2 != 0);
|
||||
q.set_size(b[3] as u16 % QUEUE_SIZE);
|
||||
|
||||
q.set_ready(true);
|
||||
queues.push(q);
|
||||
}
|
||||
|
||||
queues
|
||||
}
|
||||
|
||||
fn memfd_create(name: &std::ffi::CStr) -> Result<RawFd, std::io::Error> {
|
||||
let res = unsafe { libc::syscall(libc::SYS_memfd_create, name.as_ptr(), 0) };
|
||||
|
||||
if res < 0 {
|
||||
Err(std::io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(res as RawFd)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_pipe() -> Result<(File, File), std::io::Error> {
|
||||
let mut pipe = [-1; 2];
|
||||
if unsafe { libc::pipe2(pipe.as_mut_ptr(), libc::O_CLOEXEC) } == -1 {
|
||||
return Err(std::io::Error::last_os_error());
|
||||
}
|
||||
let rx = unsafe { File::from_raw_fd(pipe[0]) };
|
||||
let tx = unsafe { File::from_raw_fd(pipe[1]) };
|
||||
|
||||
Ok((rx, tx))
|
||||
}
|
||||
145
fuzz/fuzz_targets/iommu.rs
Normal file
145
fuzz/fuzz_targets/iommu.rs
Normal file
@@ -0,0 +1,145 @@
|
||||
// Copyright © 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use seccompiler::SeccompAction;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||
use std::sync::Arc;
|
||||
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||
use virtio_queue::{Queue, QueueT};
|
||||
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
|
||||
macro_rules! align {
|
||||
($n:expr, $align:expr) => {{
|
||||
(($n + $align - 1) / $align) * $align
|
||||
}};
|
||||
}
|
||||
|
||||
const QUEUE_DATA_SIZE: usize = 4;
|
||||
const MEM_SIZE: usize = 32 * 1024 * 1024;
|
||||
// Reuse what's being done from DeviceManager::get_msi_iova_space()
|
||||
const IOVA_SPACE_SIZE: usize = (0xfeef_ffff - 0xfee0_0000) + 1;
|
||||
|
||||
// Max entries in the queue.
|
||||
const QUEUE_SIZE: u16 = 256;
|
||||
// Descriptor table alignment
|
||||
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||
// Avalable ring alignment
|
||||
const AVAIL_RING_ALIGN_SIZE: u64 = 2;
|
||||
// Used ring alignment
|
||||
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||
// Descriptor table size
|
||||
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
|
||||
// Available ring size
|
||||
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
||||
// Used ring size
|
||||
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
|
||||
|
||||
// Guest memory gap
|
||||
const GUEST_MEM_GAP: u64 = 1 * 1024 * 1024;
|
||||
// Guest physical address for descriptor table.
|
||||
const DESC_TABLE_ADDR: u64 = align!(MEM_SIZE as u64 + GUEST_MEM_GAP, DESC_TABLE_ALIGN_SIZE);
|
||||
// Guest physical address for available ring
|
||||
const AVAIL_RING_ADDR: u64 = align!(DESC_TABLE_ADDR + DESC_TABLE_SIZE, AVAIL_RING_ALIGN_SIZE);
|
||||
// Guest physical address for used ring
|
||||
const USED_RING_ADDR: u64 = align!(AVAIL_RING_ADDR + AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE);
|
||||
// Virtio-queue size in bytes
|
||||
const QUEUE_BYTES_SIZE: usize = (USED_RING_ADDR + USED_RING_SIZE - DESC_TABLE_ADDR) as usize;
|
||||
|
||||
fuzz_target!(|bytes| {
|
||||
if bytes.len() < (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE)
|
||||
|| bytes.len() > (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE + MEM_SIZE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let (mut iommu, _) = virtio_devices::Iommu::new(
|
||||
"fuzzer_iommu".to_owned(),
|
||||
SeccompAction::Allow,
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
((MEM_SIZE - IOVA_SPACE_SIZE) as u64, (MEM_SIZE - 1) as u64),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let queue_data = &bytes[..QUEUE_DATA_SIZE];
|
||||
let queue_bytes = &bytes[QUEUE_DATA_SIZE..QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE];
|
||||
let mem_bytes = &bytes[QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE..];
|
||||
|
||||
// Setup the request queue with the input bytes
|
||||
let request_queue = setup_virt_queue(queue_data.try_into().unwrap());
|
||||
// Given the "event queue" events are not handled from the current
|
||||
// implementation of virtio-iommu, we simply setup the 'event_queue'
|
||||
// with exactly the same content as the 'request_queue'.
|
||||
let _event_queue = setup_virt_queue(queue_data.try_into().unwrap());
|
||||
|
||||
// Setup the guest memory with the input bytes
|
||||
let mem = GuestMemoryMmap::from_ranges(&[
|
||||
(GuestAddress(0), MEM_SIZE),
|
||||
(GuestAddress(DESC_TABLE_ADDR), QUEUE_BYTES_SIZE),
|
||||
])
|
||||
.unwrap();
|
||||
if mem
|
||||
.write_slice(queue_bytes, GuestAddress(DESC_TABLE_ADDR))
|
||||
.is_err()
|
||||
{
|
||||
return;
|
||||
}
|
||||
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
||||
return;
|
||||
}
|
||||
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||
|
||||
let request_evt = EventFd::new(0).unwrap();
|
||||
let request_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(request_evt.as_raw_fd())) };
|
||||
let _event_evt = EventFd::new(0).unwrap();
|
||||
|
||||
// Kick the 'queue' event before activate the vIOMMU device
|
||||
request_queue_evt.write(1).unwrap();
|
||||
|
||||
iommu
|
||||
.activate(
|
||||
guest_memory,
|
||||
Arc::new(NoopVirtioInterrupt {}),
|
||||
vec![
|
||||
(0, request_queue, request_evt),
|
||||
(0, _event_queue, _event_evt),
|
||||
],
|
||||
)
|
||||
.ok();
|
||||
|
||||
// Wait for the events to finish and vIOMMU device worker thread to return
|
||||
iommu.wait_for_epoll_threads();
|
||||
});
|
||||
|
||||
pub struct NoopVirtioInterrupt {}
|
||||
|
||||
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue {
|
||||
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||
q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
|
||||
q.set_next_used(bytes[1] as u16);
|
||||
q.set_event_idx(bytes[2] % 2 != 0);
|
||||
q.set_size(bytes[3] as u16 % QUEUE_SIZE);
|
||||
|
||||
q.try_set_desc_table_address(GuestAddress(DESC_TABLE_ADDR))
|
||||
.unwrap();
|
||||
q.try_set_avail_ring_address(GuestAddress(AVAIL_RING_ADDR))
|
||||
.unwrap();
|
||||
q.try_set_used_ring_address(GuestAddress(USED_RING_ADDR))
|
||||
.unwrap();
|
||||
q.set_ready(true);
|
||||
|
||||
q
|
||||
}
|
||||
179
fuzz/fuzz_targets/mem.rs
Normal file
179
fuzz/fuzz_targets/mem.rs
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright © 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use seccompiler::SeccompAction;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use virtio_devices::{BlocksState, Mem, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||
use virtio_queue::{Queue, QueueT};
|
||||
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
type GuestRegionMmap = vm_memory::GuestRegionMmap<AtomicBitmap>;
|
||||
|
||||
macro_rules! align {
|
||||
($n:expr, $align:expr) => {{
|
||||
(($n + $align - 1) / $align) * $align
|
||||
}};
|
||||
}
|
||||
|
||||
const VIRTIO_MEM_DATA_SIZE: usize = 1;
|
||||
const QUEUE_DATA_SIZE: usize = 4;
|
||||
// The size of the guest memory for the virtio-mem region
|
||||
const MEM_SIZE: usize = 128 * 1024 * 1024;
|
||||
// The start address of the virtio-mem region in the guest memory
|
||||
const VIRTIO_MEM_REGION_ADDRESS: u64 = 0;
|
||||
|
||||
// Max entries in the queue.
|
||||
const QUEUE_SIZE: u16 = 64;
|
||||
// Descriptor table alignment
|
||||
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||
// Avalable ring alignment
|
||||
const AVAIL_RING_ALIGN_SIZE: u64 = 2;
|
||||
// Used ring alignment
|
||||
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||
// Descriptor table size
|
||||
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
|
||||
// Available ring size
|
||||
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
||||
// Used ring size
|
||||
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
|
||||
|
||||
// Guest memory gap
|
||||
const GUEST_MEM_GAP: u64 = 1 * 1024 * 1024;
|
||||
// Guest physical address for descriptor table.
|
||||
const DESC_TABLE_ADDR: u64 = align!(MEM_SIZE as u64 + GUEST_MEM_GAP, DESC_TABLE_ALIGN_SIZE);
|
||||
// Guest physical address for available ring
|
||||
const AVAIL_RING_ADDR: u64 = align!(DESC_TABLE_ADDR + DESC_TABLE_SIZE, AVAIL_RING_ALIGN_SIZE);
|
||||
// Guest physical address for used ring
|
||||
const USED_RING_ADDR: u64 = align!(AVAIL_RING_ADDR + AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE);
|
||||
// Virtio-queue size in bytes
|
||||
const QUEUE_BYTES_SIZE: usize = (USED_RING_ADDR + USED_RING_SIZE - DESC_TABLE_ADDR) as usize;
|
||||
|
||||
fuzz_target!(|bytes| {
|
||||
if bytes.len() < VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE
|
||||
|| bytes.len() > (VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE + MEM_SIZE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let virtio_mem_data = &bytes[..VIRTIO_MEM_DATA_SIZE];
|
||||
let queue_data = &bytes[VIRTIO_MEM_DATA_SIZE..VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE];
|
||||
let queue_bytes = &bytes[VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE
|
||||
..VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE];
|
||||
let mem_bytes = &bytes[VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE..];
|
||||
|
||||
// Create a virtio-mem device based on the input bytes;
|
||||
let (mut virtio_mem, virtio_mem_region) =
|
||||
create_dummy_virtio_mem(virtio_mem_data.try_into().unwrap());
|
||||
|
||||
// Setup the virt queue with the input bytes
|
||||
let q = setup_virt_queue(queue_data.try_into().unwrap());
|
||||
|
||||
// Setup the guest memory with the input bytes
|
||||
let mem = GuestMemoryMmap::from_ranges(&[
|
||||
(GuestAddress(DESC_TABLE_ADDR), QUEUE_BYTES_SIZE), // guest region for the virt queue
|
||||
])
|
||||
.unwrap();
|
||||
if mem
|
||||
.write_slice(queue_bytes, GuestAddress(DESC_TABLE_ADDR))
|
||||
.is_err()
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Add the memory region for the virtio-mem device
|
||||
let mem = mem.insert_region(virtio_mem_region).unwrap();
|
||||
if mem
|
||||
.write_slice(mem_bytes, GuestAddress(VIRTIO_MEM_REGION_ADDRESS))
|
||||
.is_err()
|
||||
{
|
||||
return;
|
||||
}
|
||||
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||
|
||||
let evt = EventFd::new(0).unwrap();
|
||||
let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) };
|
||||
|
||||
// Kick the 'queue' event before activate the virtio-mem device
|
||||
queue_evt.write(1).unwrap();
|
||||
|
||||
virtio_mem
|
||||
.activate(
|
||||
guest_memory,
|
||||
Arc::new(NoopVirtioInterrupt {}),
|
||||
vec![(0, q, evt)],
|
||||
)
|
||||
.ok();
|
||||
|
||||
// Wait for the events to finish and virtio-mem device worker thread to return
|
||||
virtio_mem.wait_for_epoll_threads();
|
||||
});
|
||||
|
||||
pub struct NoopVirtioInterrupt {}
|
||||
|
||||
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Create a dummy virtio-mem device for fuzzing purpose only
|
||||
fn create_dummy_virtio_mem(bytes: &[u8; VIRTIO_MEM_DATA_SIZE]) -> (Mem, Arc<GuestRegionMmap>) {
|
||||
let numa_id = if bytes[0] % 2 != 0 { Some(0) } else { None };
|
||||
|
||||
let region = vmm::memory_manager::MemoryManager::create_ram_region(
|
||||
&None,
|
||||
0,
|
||||
GuestAddress(VIRTIO_MEM_REGION_ADDRESS),
|
||||
MEM_SIZE,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
numa_id,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let blocks_state = Arc::new(Mutex::new(BlocksState::new(region.size() as u64)));
|
||||
|
||||
(
|
||||
Mem::new(
|
||||
"fuzzer_mem".to_owned(),
|
||||
®ion,
|
||||
SeccompAction::Allow,
|
||||
numa_id.map(|i| i as u16),
|
||||
0,
|
||||
false,
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
blocks_state.clone(),
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
region,
|
||||
)
|
||||
}
|
||||
|
||||
fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue {
|
||||
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||
q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
|
||||
q.set_next_used(bytes[1] as u16);
|
||||
q.set_event_idx(bytes[2] % 2 != 0);
|
||||
q.set_size(bytes[3] as u16 % QUEUE_SIZE);
|
||||
|
||||
q.try_set_desc_table_address(GuestAddress(DESC_TABLE_ADDR))
|
||||
.unwrap();
|
||||
q.try_set_avail_ring_address(GuestAddress(AVAIL_RING_ADDR))
|
||||
.unwrap();
|
||||
q.try_set_used_ring_address(GuestAddress(USED_RING_ADDR))
|
||||
.unwrap();
|
||||
q.set_ready(true);
|
||||
|
||||
q
|
||||
}
|
||||
@@ -127,6 +127,7 @@ fn create_dummy_pmem() -> Pmem {
|
||||
false,
|
||||
SeccompAction::Allow,
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ fuzz_target!(|bytes| {
|
||||
false,
|
||||
SeccompAction::Allow,
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ fuzz_target!(|bytes| {
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
SeccompAction::Allow,
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -11,20 +11,20 @@ mshv = ["mshv-ioctls", "mshv-bindings"]
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.65"
|
||||
anyhow = "1.0.66"
|
||||
byteorder = "1.4.3"
|
||||
thiserror = "1.0.37"
|
||||
libc = "0.2.133"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
kvm-ioctls = { version = "0.11.0", optional = true }
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0-tdx", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
kvm-ioctls = { version = "0.12.0", optional = true }
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
mshv-bindings = { git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", branch = "main", optional = true}
|
||||
serde = { version = "1.0.145", features = ["rc", "derive"] }
|
||||
serde_with = { version = "2.0.1", default-features = false, features = ["macros"] }
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
serde_with = { version = "2.1.0", default-features = false, features = ["macros"] }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vm-memory = { version = "0.9.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = { version = "0.10.0", features = ["with-serde"] }
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
||||
version = "1.17.0"
|
||||
@@ -32,4 +32,4 @@ default-features = false
|
||||
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.9.1"
|
||||
env_logger = "0.10.0"
|
||||
|
||||
@@ -56,7 +56,7 @@ fn get_op<T: CpuStateManager>(
|
||||
OpKind::Immediate16 => insn.immediate16() as u64,
|
||||
OpKind::Immediate32 => insn.immediate32() as u64,
|
||||
OpKind::Immediate32to64 => insn.immediate32to64() as u64,
|
||||
OpKind::Immediate64 => insn.immediate64() as u64,
|
||||
OpKind::Immediate64 => insn.immediate64(),
|
||||
k => return Err(PlatformError::InvalidOperand(anyhow!("{:?}", k))),
|
||||
};
|
||||
|
||||
|
||||
@@ -48,12 +48,12 @@ pub enum HypervisorCpuError {
|
||||
///
|
||||
/// Setting floating point registers error
|
||||
///
|
||||
#[error("Failed to set special register: {0}")]
|
||||
#[error("Failed to set floating point registers: {0}")]
|
||||
SetFloatingPointRegs(#[source] anyhow::Error),
|
||||
///
|
||||
/// Getting floating point register error
|
||||
///
|
||||
#[error("Failed to get special register: {0}")]
|
||||
#[error("Failed to get floating points registers: {0}")]
|
||||
GetFloatingPointRegs(#[source] anyhow::Error),
|
||||
///
|
||||
/// Setting Cpuid error
|
||||
|
||||
@@ -703,7 +703,6 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
msr!(msr_index::MSR_LSTAR),
|
||||
msr!(msr_index::MSR_KERNEL_GS_BASE),
|
||||
msr!(msr_index::MSR_SYSCALL_MASK),
|
||||
msr!(msr_index::MSR_IA32_TSC),
|
||||
msr_data!(msr_index::MSR_MTRRdefType, MTRR_ENABLE | MTRR_MEM_TYPE_WB),
|
||||
]
|
||||
.to_vec()
|
||||
@@ -1093,7 +1092,7 @@ impl vm::Vm for MshvVm {
|
||||
flags,
|
||||
guest_pfn: guest_phys_addr >> PAGE_SHIFT,
|
||||
size: memory_size,
|
||||
userspace_addr: userspace_addr as u64,
|
||||
userspace_addr,
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
@@ -5,4 +5,4 @@ authors = ["The Chromium OS Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
vmm-sys-util = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
@@ -6,23 +6,23 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
epoll = "4.3.1"
|
||||
getrandom = "0.2.7"
|
||||
libc = "0.2.133"
|
||||
getrandom = "0.2.8"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
net_gen = { path = "../net_gen" }
|
||||
rate_limiter = { path = "../rate_limiter" }
|
||||
serde = "1.0.145"
|
||||
serde = "1.0.150"
|
||||
thiserror = "1.0.37"
|
||||
versionize = "0.1.6"
|
||||
versionize = "0.1.9"
|
||||
versionize_derive = "0.1.4"
|
||||
virtio-bindings = "0.1.0"
|
||||
virtio-queue = "0.6.1"
|
||||
vm-memory = { version = "0.9.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
virtio-queue = "0.7.0"
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
[dev-dependencies]
|
||||
once_cell = "1.15.0"
|
||||
once_cell = "1.16.0"
|
||||
pnet = "0.31.0"
|
||||
pnet_datalink = "0.31.0"
|
||||
serde_json = "1.0.85"
|
||||
serde_json = "1.0.89"
|
||||
|
||||
@@ -12,9 +12,9 @@ use thiserror::Error;
|
||||
pub enum Error {
|
||||
#[error("Failed to convert an hexadecimal string into an integer: {0}")]
|
||||
ConvertHexStringToInt(std::num::ParseIntError),
|
||||
#[error("Error related to the multiqueue support (no support TAP side).")]
|
||||
#[error("Error related to the multiqueue support (no support TAP side)")]
|
||||
MultiQueueNoTapSupport,
|
||||
#[error("Error related to the multiqueue support (no support device side).")]
|
||||
#[error("Error related to the multiqueue support (no support device side)")]
|
||||
MultiQueueNoDeviceSupport,
|
||||
#[error("Failed to read the TAP flags from sysfs: {0}")]
|
||||
ReadSysfsTunFlags(io::Error),
|
||||
|
||||
@@ -104,6 +104,10 @@ impl TxVirtio {
|
||||
return Err(NetQueuePairError::WriteTap(e));
|
||||
}
|
||||
|
||||
if (result as usize) < vnet_hdr_len() {
|
||||
return Err(NetQueuePairError::InvalidVirtioNetHeader);
|
||||
}
|
||||
|
||||
self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
|
||||
self.counter_frames += Wrapping(1);
|
||||
|
||||
@@ -185,7 +189,7 @@ impl RxVirtio {
|
||||
.translate_gva(access_platform, desc.len() as usize),
|
||||
10,
|
||||
)
|
||||
.unwrap();
|
||||
.ok_or(NetQueuePairError::DescriptorInvalidHeader)?;
|
||||
let mut next_desc = Some(desc);
|
||||
|
||||
let mut iovecs = Vec::new();
|
||||
@@ -238,6 +242,10 @@ impl RxVirtio {
|
||||
return Err(NetQueuePairError::ReadTap(e));
|
||||
}
|
||||
|
||||
if (result as usize) < vnet_hdr_len() {
|
||||
return Err(NetQueuePairError::InvalidVirtioNetHeader);
|
||||
}
|
||||
|
||||
// Write num_buffers to guest memory. We simply write 1 as we
|
||||
// never spread the frame over more than one descriptor chain.
|
||||
desc_chain
|
||||
@@ -288,7 +296,7 @@ pub struct NetCounters {
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum NetQueuePairError {
|
||||
#[error("No memory configured.")]
|
||||
#[error("No memory configured")]
|
||||
NoMemoryConfigured,
|
||||
#[error("Error registering listener: {0}")]
|
||||
RegisterListener(io::Error),
|
||||
@@ -302,9 +310,9 @@ pub enum NetQueuePairError {
|
||||
GuestMemory(vm_memory::GuestMemoryError),
|
||||
#[error("Returned an error while iterating through the queue: {0}")]
|
||||
QueueIteratorFailed(virtio_queue::Error),
|
||||
#[error("Descriptor chain is too short.")]
|
||||
#[error("Descriptor chain is too short")]
|
||||
DescriptorChainTooShort,
|
||||
#[error("Descriptor chain does not contain valid descriptors.")]
|
||||
#[error("Descriptor chain does not contain valid descriptors")]
|
||||
DescriptorChainInvalid,
|
||||
#[error("Failed to determine if queue needed notification: {0}")]
|
||||
QueueNeedsNotification(virtio_queue::Error),
|
||||
@@ -312,6 +320,10 @@ pub enum NetQueuePairError {
|
||||
QueueEnableNotification(virtio_queue::Error),
|
||||
#[error("Failed to add used index to the queue: {0}")]
|
||||
QueueAddUsed(virtio_queue::Error),
|
||||
#[error("Descriptor with invalid virtio-net header")]
|
||||
DescriptorInvalidHeader,
|
||||
#[error("Invalid virtio-net header")]
|
||||
InvalidVirtioNetHeader,
|
||||
}
|
||||
|
||||
pub struct NetQueuePair {
|
||||
|
||||
@@ -26,13 +26,13 @@ pub enum Error {
|
||||
ConfigureTap(IoError),
|
||||
#[error("Unable to retrieve features: {0}")]
|
||||
GetFeatures(IoError),
|
||||
#[error("Missing multiqueue support in the kernel.")]
|
||||
#[error("Missing multiqueue support in the kernel")]
|
||||
MultiQueueKernelSupport,
|
||||
#[error("ioctl failed: {0}")]
|
||||
IoctlError(IoError),
|
||||
#[error("Failed to create a socket: {0}")]
|
||||
NetUtil(NetUtilError),
|
||||
#[error("Invalid interface name.")]
|
||||
#[error("Invalid interface name")]
|
||||
InvalidIfname,
|
||||
#[error("Error parsing MAC data: {0}")]
|
||||
MacParsing(IoError),
|
||||
|
||||
@@ -10,23 +10,20 @@ kvm = ["vfio-ioctls/kvm"]
|
||||
mshv = ["vfio-ioctls/mshv"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.65"
|
||||
anyhow = "1.0.66"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main", features = ["fam-wrappers"] }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vfio_user = { path = "../vfio_user" }
|
||||
vmm-sys-util = "0.10.0"
|
||||
libc = "0.2.133"
|
||||
vmm-sys-util = "0.11.0"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
serde = { version = "1.0.145", features = ["derive"] }
|
||||
serde = { version = "1.0.150", features = ["derive"] }
|
||||
thiserror = "1.0.37"
|
||||
versionize = "0.1.6"
|
||||
versionize = "0.1.9"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = "0.9.0"
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
|
||||
[dependencies.vfio-bindings]
|
||||
version = "0.3.1"
|
||||
features = ["fam-wrappers"]
|
||||
vm-memory = "0.10.0"
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
@@ -21,6 +21,8 @@ pub enum Error {
|
||||
IoRegistrationFailed(u64, configuration::Error),
|
||||
/// Expected resource not found.
|
||||
MissingResource,
|
||||
/// Invalid resource.
|
||||
InvalidResource(Resource),
|
||||
}
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
@@ -37,6 +39,7 @@ impl Display for Error {
|
||||
write!(f, "failed to register an IO BAR, addr={} err={}", addr, e)
|
||||
}
|
||||
MissingResource => write!(f, "failed to find expected resource"),
|
||||
InvalidResource(r) => write!(f, "invalid resource {:?}", r),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ enum Error {
|
||||
UpdateInterruptRoute(io::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Versionize)]
|
||||
#[derive(Debug, Clone, Versionize, Eq, PartialEq)]
|
||||
pub struct MsixTableEntry {
|
||||
pub msg_addr_lo: u32,
|
||||
pub msg_addr_hi: u32,
|
||||
@@ -258,7 +258,7 @@ impl MsixConfig {
|
||||
let modulo_offset = offset % MSIX_TABLE_ENTRIES_MODULO;
|
||||
|
||||
// Store the value of the entry before modification
|
||||
let mut old_entry: Option<MsixTableEntry> = None;
|
||||
let old_entry = self.table_entries[index].clone();
|
||||
|
||||
match data.len() {
|
||||
4 => {
|
||||
@@ -268,7 +268,6 @@ impl MsixConfig {
|
||||
0x4 => self.table_entries[index].msg_addr_hi = value,
|
||||
0x8 => self.table_entries[index].msg_data = value,
|
||||
0xc => {
|
||||
old_entry = Some(self.table_entries[index].clone());
|
||||
self.table_entries[index].vector_ctl = value;
|
||||
}
|
||||
_ => error!("invalid offset"),
|
||||
@@ -284,7 +283,6 @@ impl MsixConfig {
|
||||
self.table_entries[index].msg_addr_hi = (value >> 32) as u32;
|
||||
}
|
||||
0x8 => {
|
||||
old_entry = Some(self.table_entries[index].clone());
|
||||
self.table_entries[index].msg_data = (value & 0xffff_ffffu64) as u32;
|
||||
self.table_entries[index].vector_ctl = (value >> 32) as u32;
|
||||
}
|
||||
@@ -296,10 +294,18 @@ impl MsixConfig {
|
||||
_ => error!("invalid data length"),
|
||||
};
|
||||
|
||||
// Update interrupt routes
|
||||
if self.enabled && !self.masked {
|
||||
let table_entry = &self.table_entries[index];
|
||||
let table_entry = &self.table_entries[index];
|
||||
|
||||
// Optimisation to avoid excessive updates
|
||||
if &old_entry == table_entry {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update interrupt routes
|
||||
// Optimisation: only update routes if the entry is not masked;
|
||||
// this is safe because if the entry is masked (starts masked as per spec)
|
||||
// in the table then it won't be triggered. (See: #4273)
|
||||
if self.enabled && !self.masked && !table_entry.masked() {
|
||||
let config = MsiIrqSourceConfig {
|
||||
high_addr: table_entry.msg_addr_hi,
|
||||
low_addr: table_entry.msg_addr_lo,
|
||||
@@ -323,15 +329,15 @@ impl MsixConfig {
|
||||
// has been injected, the pending bit in the PBA needs to be cleared.
|
||||
// All of this is valid only if MSI-X has not been masked for the whole
|
||||
// device.
|
||||
if let Some(old_entry) = old_entry {
|
||||
// Check if bit has been flipped
|
||||
if !self.masked()
|
||||
&& old_entry.masked()
|
||||
&& !self.table_entries[index].masked()
|
||||
&& self.get_pba_bit(index as u16) == 1
|
||||
{
|
||||
self.inject_msix_and_clear_pba(index);
|
||||
}
|
||||
|
||||
// Check if bit has been flipped
|
||||
if !self.masked()
|
||||
&& self.enabled()
|
||||
&& old_entry.masked()
|
||||
&& !table_entry.masked()
|
||||
&& self.get_pba_bit(index as u16) == 1
|
||||
{
|
||||
self.inject_msix_and_clear_pba(index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -413,7 +413,7 @@ impl VfioCommon {
|
||||
resources: Option<Vec<Resource>>,
|
||||
) -> Result<Vec<PciBarConfiguration>, PciDeviceError> {
|
||||
let mut bars = Vec::new();
|
||||
let mut bar_id = VFIO_PCI_BAR0_REGION_INDEX as u32;
|
||||
let mut bar_id = VFIO_PCI_BAR0_REGION_INDEX;
|
||||
|
||||
// Going through all regular regions to compute the BAR size.
|
||||
// We're not saving the BAR address to restore it, because we
|
||||
@@ -526,7 +526,7 @@ impl VfioCommon {
|
||||
}
|
||||
|
||||
// Invert and add 1 to to find size
|
||||
region_size = (!combined_size + 1) as u64;
|
||||
region_size = !combined_size + 1;
|
||||
} else {
|
||||
region_type = PciBarRegionType::Memory32BitRegion;
|
||||
|
||||
@@ -599,7 +599,7 @@ impl VfioCommon {
|
||||
start: bar_addr,
|
||||
length: region_size,
|
||||
type_: region_type,
|
||||
index: bar_id as u32,
|
||||
index: bar_id,
|
||||
user_memory_regions: Vec::new(),
|
||||
});
|
||||
|
||||
|
||||
@@ -276,12 +276,7 @@ impl VfioUserPciDevice {
|
||||
self.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.dma_map(
|
||||
offset,
|
||||
region.start_addr().raw_value(),
|
||||
region.len() as u64,
|
||||
fd,
|
||||
)
|
||||
.dma_map(offset, region.start_addr().raw_value(), region.len(), fd)
|
||||
.map_err(VfioUserPciDeviceError::DmaMap)
|
||||
}
|
||||
|
||||
@@ -292,7 +287,7 @@ impl VfioUserPciDevice {
|
||||
self.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.dma_unmap(region.start_addr().raw_value(), region.len() as u64)
|
||||
.dma_unmap(region.start_addr().raw_value(), region.len())
|
||||
.map_err(VfioUserPciDeviceError::DmaUnmap)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ edition = "2021"
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.2.22", features = ["wrap_help","cargo"] }
|
||||
clap = { version = "4.0.29", features = ["wrap_help","cargo"] }
|
||||
dirs = "4.0.0"
|
||||
serde = { version = "1.0.145", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.85"
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.89"
|
||||
test_infra = { path = "../test_infra" }
|
||||
thiserror = "1.0.37"
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.2.22", features = ["cargo"] }
|
||||
clap = { version = "4.0.29", features = ["cargo"] }
|
||||
|
||||
@@ -10,7 +10,7 @@ extern crate clap;
|
||||
|
||||
mod performance_tests;
|
||||
|
||||
use clap::{Arg, Command as ClapCommand};
|
||||
use clap::{Arg, ArgAction, Command as ClapCommand};
|
||||
use performance_tests::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
@@ -503,29 +503,28 @@ fn main() {
|
||||
Arg::new("test-filter")
|
||||
.long("test-filter")
|
||||
.help("Filter metrics tests to run based on provided keywords")
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("list-tests")
|
||||
.long("list-tests")
|
||||
.help("Print the list of availale metrics tests")
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.action(ArgAction::SetTrue)
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("report-file")
|
||||
.long("report-file")
|
||||
.help("Report file. Standard error is used if not specified")
|
||||
.takes_value(true),
|
||||
.num_args(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("iterations")
|
||||
.long("iterations")
|
||||
.help("Override number of test iterations")
|
||||
.takes_value(true),
|
||||
.num_args(1),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
@@ -537,7 +536,7 @@ fn main() {
|
||||
.filter(|t| !(cfg!(target_arch = "aarch64") && t.name == "virtio_net_latency_us"))
|
||||
.collect();
|
||||
|
||||
if cmd_arguments.is_present("list-tests") {
|
||||
if cmd_arguments.get_flag("list-tests") {
|
||||
for test in test_list.iter() {
|
||||
println!("\"{}\" ({})", test.name, test.control);
|
||||
}
|
||||
@@ -545,7 +544,7 @@ fn main() {
|
||||
return;
|
||||
}
|
||||
|
||||
let test_filter = match cmd_arguments.values_of("test-filter") {
|
||||
let test_filter = match cmd_arguments.get_many::<String>("test-filter") {
|
||||
Some(s) => s.collect(),
|
||||
None => Vec::new(),
|
||||
};
|
||||
@@ -557,7 +556,7 @@ fn main() {
|
||||
|
||||
let overrides = Arc::new(PerformanceTestOverrides {
|
||||
test_iterations: cmd_arguments
|
||||
.value_of("iterations")
|
||||
.get_one::<String>("iterations")
|
||||
.map(|s| s.parse())
|
||||
.transpose()
|
||||
.unwrap_or_default(),
|
||||
@@ -580,7 +579,7 @@ fn main() {
|
||||
cleanup_tests();
|
||||
|
||||
let mut report_file: Box<dyn std::io::Write + Send> =
|
||||
if let Some(file) = cmd_arguments.value_of("report-file") {
|
||||
if let Some(file) = cmd_arguments.get_one::<String>("report-file") {
|
||||
Box::new(
|
||||
std::fs::File::create(std::path::Path::new(file))
|
||||
.map_err(|e| {
|
||||
|
||||
@@ -10,7 +10,7 @@ path = "src/qcow.rs"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.4.3"
|
||||
libc = "0.2.133"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
remain = "0.2.4"
|
||||
vmm-sys-util = "0.10.0"
|
||||
remain = "0.2.5"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
@@ -255,10 +255,10 @@ impl QcowHeader {
|
||||
let cluster_bits: u32 = DEFAULT_CLUSTER_BITS;
|
||||
let cluster_size: u32 = 0x01 << cluster_bits;
|
||||
// L2 blocks are always one cluster long. They contain cluster_size/sizeof(u64) addresses.
|
||||
let l2_size: u32 = cluster_size / size_of::<u64>() as u32;
|
||||
let entries_per_cluster: u32 = cluster_size / size_of::<u64>() as u32;
|
||||
let num_clusters: u32 = div_round_up_u64(size, u64::from(cluster_size)) as u32;
|
||||
let num_l2_clusters: u32 = div_round_up_u32(num_clusters, l2_size);
|
||||
let l1_clusters: u32 = div_round_up_u32(num_l2_clusters, cluster_size);
|
||||
let num_l2_clusters: u32 = div_round_up_u32(num_clusters, entries_per_cluster);
|
||||
let l1_clusters: u32 = div_round_up_u32(num_l2_clusters, entries_per_cluster);
|
||||
let header_clusters = div_round_up_u32(size_of::<QcowHeader>() as u32, cluster_size);
|
||||
QcowHeader {
|
||||
magic: QCOW_MAGIC,
|
||||
@@ -473,10 +473,10 @@ impl QcowFile {
|
||||
QcowFile::rebuild_refcounts(&mut raw_file, header)?;
|
||||
}
|
||||
|
||||
let l2_size = cluster_size / size_of::<u64>() as u64;
|
||||
let entries_per_cluster = cluster_size / size_of::<u64>() as u64;
|
||||
let num_clusters = div_round_up_u64(header.size, cluster_size);
|
||||
let num_l2_clusters = div_round_up_u64(num_clusters, l2_size);
|
||||
let l1_clusters = div_round_up_u64(num_l2_clusters, cluster_size);
|
||||
let num_l2_clusters = div_round_up_u64(num_clusters, entries_per_cluster);
|
||||
let l1_clusters = div_round_up_u64(num_l2_clusters, entries_per_cluster);
|
||||
let header_clusters = div_round_up_u64(size_of::<QcowHeader>() as u64, cluster_size);
|
||||
if num_l2_clusters > MAX_RAM_POINTER_TABLE_SIZE {
|
||||
return Err(Error::TooManyL1Entries(num_l2_clusters));
|
||||
@@ -566,7 +566,7 @@ impl QcowFile {
|
||||
|
||||
// Set the refcount for each refcount table cluster.
|
||||
let cluster_size = 0x01u64 << qcow.header.cluster_bits;
|
||||
let refcount_table_base = qcow.header.refcount_table_offset as u64;
|
||||
let refcount_table_base = qcow.header.refcount_table_offset;
|
||||
let end_cluster_addr =
|
||||
refcount_table_base + u64::from(qcow.header.refcount_table_clusters) * cluster_size;
|
||||
|
||||
@@ -705,7 +705,8 @@ impl QcowFile {
|
||||
header: QcowHeader,
|
||||
cluster_size: u64,
|
||||
) -> Result<()> {
|
||||
let l1_clusters = div_round_up_u64(u64::from(header.l1_size), cluster_size);
|
||||
let entries_per_cluster = cluster_size / size_of::<u64>() as u64;
|
||||
let l1_clusters = div_round_up_u64(u64::from(header.l1_size), entries_per_cluster);
|
||||
let l1_table_offset = header.l1_table_offset;
|
||||
for i in 0..l1_clusters {
|
||||
add_ref(refcounts, cluster_size, l1_table_offset + i * cluster_size)?;
|
||||
@@ -872,7 +873,7 @@ impl QcowFile {
|
||||
let pointers_per_cluster = cluster_size / size_of::<u64>() as u64;
|
||||
let data_clusters = div_round_up_u64(header.size, cluster_size);
|
||||
let l2_clusters = div_round_up_u64(data_clusters, pointers_per_cluster);
|
||||
let l1_clusters = div_round_up_u64(l2_clusters, cluster_size);
|
||||
let l1_clusters = div_round_up_u64(l2_clusters, pointers_per_cluster);
|
||||
let header_clusters = div_round_up_u64(size_of::<QcowHeader>() as u64, cluster_size);
|
||||
let max_clusters = data_clusters + l2_clusters + l1_clusters + header_clusters;
|
||||
let mut max_valid_cluster_index = max_clusters;
|
||||
@@ -962,7 +963,7 @@ impl QcowFile {
|
||||
// Gets the offset of the given guest address in the host file. If L1, L2, or data clusters have
|
||||
// yet to be allocated, return None.
|
||||
fn file_offset_read(&mut self, address: u64) -> std::io::Result<Option<u64>> {
|
||||
if address >= self.virtual_size() as u64 {
|
||||
if address >= self.virtual_size() {
|
||||
return Err(std::io::Error::from_raw_os_error(EINVAL));
|
||||
}
|
||||
|
||||
@@ -1005,7 +1006,7 @@ impl QcowFile {
|
||||
// Gets the offset of the given guest address in the host file. If L1, L2, or data clusters need
|
||||
// to be allocated, they will be.
|
||||
fn file_offset_write(&mut self, address: u64) -> std::io::Result<u64> {
|
||||
if address >= self.virtual_size() as u64 {
|
||||
if address >= self.virtual_size() {
|
||||
return Err(std::io::Error::from_raw_os_error(EINVAL));
|
||||
}
|
||||
|
||||
@@ -1122,7 +1123,7 @@ impl QcowFile {
|
||||
|
||||
// Returns true if the cluster containing `address` is already allocated.
|
||||
fn cluster_allocated(&mut self, address: u64) -> std::io::Result<bool> {
|
||||
if address >= self.virtual_size() as u64 {
|
||||
if address >= self.virtual_size() {
|
||||
return Err(std::io::Error::from_raw_os_error(EINVAL));
|
||||
}
|
||||
|
||||
@@ -1194,7 +1195,7 @@ impl QcowFile {
|
||||
// Deallocate the storage for the cluster starting at `address`.
|
||||
// Any future reads of this cluster will return all zeroes.
|
||||
fn deallocate_cluster(&mut self, address: u64) -> std::io::Result<()> {
|
||||
if address >= self.virtual_size() as u64 {
|
||||
if address >= self.virtual_size() {
|
||||
return Err(std::io::Error::from_raw_os_error(EINVAL));
|
||||
}
|
||||
|
||||
@@ -1416,7 +1417,7 @@ impl Drop for QcowFile {
|
||||
|
||||
impl Read for QcowFile {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
let address: u64 = self.current_offset as u64;
|
||||
let address: u64 = self.current_offset;
|
||||
let read_count: usize = self.limit_range_file(address, buf.len());
|
||||
|
||||
let mut nread: usize = 0;
|
||||
@@ -1478,7 +1479,7 @@ impl Seek for QcowFile {
|
||||
|
||||
impl Write for QcowFile {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
let address: u64 = self.current_offset as u64;
|
||||
let address: u64 = self.current_offset;
|
||||
let write_count: usize = self.limit_range_file(address, buf.len());
|
||||
|
||||
let mut nwritten: usize = 0;
|
||||
|
||||
@@ -4,6 +4,6 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.133"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
vmm-sys-util = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
180
release-notes.md
180
release-notes.md
@@ -1,3 +1,13 @@
|
||||
- [v28.1](#v281)
|
||||
- [v28.0](#v280)
|
||||
- [Community Engagement (Reminder)](#community-engagement-reminder)
|
||||
- [Long Term Support (LTS) Release](#long-term-support-lts-release)
|
||||
- [Virtualised TPM Support](#virtualised-tpm-support)
|
||||
- [Transparent Huge Page Support](#transparent-huge-page-support)
|
||||
- [README Quick Start Improved](#readme-quick-start-improved)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||
- [Removals](#removals)
|
||||
- [Contributors](#contributors)
|
||||
- [v27.0](#v270)
|
||||
- [Community Engagement](#community-engagement)
|
||||
- [Prebuilt Packages](#prebuilt-packages)
|
||||
@@ -6,41 +16,41 @@
|
||||
- [Simplified Build Feature Flags](#simplified-build-feature-flags)
|
||||
- [Asynchronous Kernel Loading](#asynchronous-kernel-loading)
|
||||
- [GDB Support for AArch64](#gdb-support-for-aarch64)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
||||
- [Deprecations](#deprecations)
|
||||
- [Contributors](#contributors)
|
||||
- [Contributors](#contributors-1)
|
||||
- [v26.0](#v260)
|
||||
- [SMBIOS Improvements via `--platform`](#smbios-improvements-via---platform)
|
||||
- [Unified Binary MSHV and KVM Support](#unified-binary-mshv-and-kvm-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
||||
- [Deprecations](#deprecations-1)
|
||||
- [Removals](#removals)
|
||||
- [Contributors](#contributors-1)
|
||||
- [Removals](#removals-1)
|
||||
- [Contributors](#contributors-2)
|
||||
- [v25.0](#v250)
|
||||
- [`ch-remote` Improvements](#ch-remote-improvements)
|
||||
- [VM "Coredump" Support](#vm-coredump-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
||||
- [Removals](#removals-1)
|
||||
- [Contributors](#contributors-2)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-3)
|
||||
- [Removals](#removals-2)
|
||||
- [Contributors](#contributors-3)
|
||||
- [v24.0](#v240)
|
||||
- [Bypass Mode for `virtio-iommu`](#bypass-mode-for-virtio-iommu)
|
||||
- [Ensure Identifiers Uniqueness](#ensure-identifiers-uniqueness)
|
||||
- [Sparse Mmap support](#sparse-mmap-support)
|
||||
- [Expose Platform Serial Number](#expose-platform-serial-number)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-3)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-4)
|
||||
- [Notable Improvements](#notable-improvements)
|
||||
- [Deprecations](#deprecations-2)
|
||||
- [New on the Website](#new-on-the-website)
|
||||
- [Contributors](#contributors-3)
|
||||
- [Contributors](#contributors-4)
|
||||
- [v23.1](#v231)
|
||||
- [v23.0](#v230)
|
||||
- [vDPA Support](#vdpa-support)
|
||||
- [Updated OS Support list](#updated-os-support-list)
|
||||
- [`AArch64` Memory Map Improvements](#aarch64-memory-map-improvements)
|
||||
- [`AMX` Support](#amx-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-4)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-5)
|
||||
- [Deprecations](#deprecations-3)
|
||||
- [Contributors](#contributors-4)
|
||||
- [Contributors](#contributors-5)
|
||||
- [v22.1](#v221)
|
||||
- [v22.0](#v220)
|
||||
- [GDB Debug Stub Support](#gdb-debug-stub-support)
|
||||
@@ -51,13 +61,13 @@
|
||||
- [PMU Support for AArch64](#pmu-support-for-aarch64)
|
||||
- [Documentation Under CC-BY-4.0 License](#documentation-under-cc-by-40-license)
|
||||
- [Deprecation of "Classic" `virtiofsd`](#deprecation-of-classic-virtiofsd)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-5)
|
||||
- [Contributors](#contributors-5)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-6)
|
||||
- [Contributors](#contributors-6)
|
||||
- [v21.0](#v210)
|
||||
- [Efficient Local Live Migration (for Live Upgrade)](#efficient-local-live-migration-for-live-upgrade)
|
||||
- [Recommended Kernel is Now 5.15](#recommended-kernel-is-now-515)
|
||||
- [Notable Bug fixes](#notable-bug-fixes-6)
|
||||
- [Contributors](#contributors-6)
|
||||
- [Notable Bug fixes](#notable-bug-fixes-7)
|
||||
- [Contributors](#contributors-7)
|
||||
- [v20.2](#v202)
|
||||
- [v20.1](#v201)
|
||||
- [v20.0](#v200)
|
||||
@@ -66,8 +76,8 @@
|
||||
- [Improved VFIO support](#improved-vfio-support)
|
||||
- [Safer code](#safer-code)
|
||||
- [Extended documentation](#extended-documentation)
|
||||
- [Notable bug fixes](#notable-bug-fixes-7)
|
||||
- [Contributors](#contributors-7)
|
||||
- [Notable bug fixes](#notable-bug-fixes-8)
|
||||
- [Contributors](#contributors-8)
|
||||
- [v19.0](#v190)
|
||||
- [Improved PTY handling for serial and `virtio-console`](#improved-pty-handling-for-serial-and-virtio-console)
|
||||
- [PCI boot time optimisations](#pci-boot-time-optimisations)
|
||||
@@ -75,8 +85,8 @@
|
||||
- [Live migration enhancements](#live-migration-enhancements)
|
||||
- [`virtio-mem` support with `vfio-user`](#virtio-mem-support-with-vfio-user)
|
||||
- [AArch64 for `virtio-iommu`](#aarch64-for-virtio-iommu)
|
||||
- [Notable bug fixes](#notable-bug-fixes-8)
|
||||
- [Contributors](#contributors-8)
|
||||
- [Notable bug fixes](#notable-bug-fixes-9)
|
||||
- [Contributors](#contributors-9)
|
||||
- [v18.0](#v180)
|
||||
- [Experimental User Device (`vfio-user`) support](#experimental-user-device-vfio-user-support)
|
||||
- [Migration support for `vhost-user` devices](#migration-support-for-vhost-user-devices)
|
||||
@@ -86,23 +96,23 @@
|
||||
- [Live migration on MSHV hypervisor](#live-migration-on-mshv-hypervisor)
|
||||
- [AArch64 CPU topology support](#aarch64-cpu-topology-support)
|
||||
- [Power button support on AArch64](#power-button-support-on-aarch64)
|
||||
- [Notable bug fixes](#notable-bug-fixes-9)
|
||||
- [Contributors](#contributors-9)
|
||||
- [Notable bug fixes](#notable-bug-fixes-10)
|
||||
- [Contributors](#contributors-10)
|
||||
- [v17.0](#v170)
|
||||
- [ARM64 NUMA support using ACPI](#arm64-numa-support-using-acpi)
|
||||
- [`Seccomp` support for MSHV backend](#seccomp-support-for-mshv-backend)
|
||||
- [Hotplug of `macvtap` devices](#hotplug-of-macvtap-devices)
|
||||
- [Improved SGX support](#improved-sgx-support)
|
||||
- [Inflight tracking for `vhost-user` devices](#inflight-tracking-for-vhost-user-devices)
|
||||
- [Notable bug fixes](#notable-bug-fixes-10)
|
||||
- [Contributors](#contributors-10)
|
||||
- [Notable bug fixes](#notable-bug-fixes-11)
|
||||
- [Contributors](#contributors-11)
|
||||
- [v16.0](#v160)
|
||||
- [Improved live migration support](#improved-live-migration-support)
|
||||
- [Improved `vhost-user` support](#improved-vhost-user-support)
|
||||
- [ARM64 ACPI and UEFI support](#arm64-acpi-and-uefi-support)
|
||||
- [Notable bug fixes](#notable-bug-fixes-11)
|
||||
- [Notable bug fixes](#notable-bug-fixes-12)
|
||||
- [Removed functionality](#removed-functionality)
|
||||
- [Contributors](#contributors-11)
|
||||
- [Contributors](#contributors-12)
|
||||
- [v15.0](#v150)
|
||||
- [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees)
|
||||
- [Network device rate limiting](#network-device-rate-limiting)
|
||||
@@ -110,7 +120,7 @@
|
||||
- [`--api-socket` supports file descriptor parameter](#--api-socket-supports-file-descriptor-parameter)
|
||||
- [Bug fixes](#bug-fixes)
|
||||
- [Deprecations](#deprecations-4)
|
||||
- [Contributors](#contributors-12)
|
||||
- [Contributors](#contributors-13)
|
||||
- [v0.14.1](#v0141)
|
||||
- [v0.14.0](#v0140)
|
||||
- [Structured event monitoring](#structured-event-monitoring)
|
||||
@@ -120,7 +130,7 @@
|
||||
- [PTY control for serial and `virtio-console`](#pty-control-for-serial-and-virtio-console)
|
||||
- [Block device rate limiting](#block-device-rate-limiting)
|
||||
- [Deprecations](#deprecations-5)
|
||||
- [Contributors](#contributors-13)
|
||||
- [Contributors](#contributors-14)
|
||||
- [v0.13.0](#v0130)
|
||||
- [Wider VFIO device support](#wider-vfio-device-support)
|
||||
- [Improved huge page support](#improved-huge-page-support)
|
||||
@@ -128,13 +138,13 @@
|
||||
- [VHD disk image support](#vhd-disk-image-support)
|
||||
- [Improved Virtio device threading](#improved-virtio-device-threading)
|
||||
- [Clean shutdown support via synthetic power button](#clean-shutdown-support-via-synthetic-power-button)
|
||||
- [Contributors](#contributors-14)
|
||||
- [Contributors](#contributors-15)
|
||||
- [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-15)
|
||||
- [Contributors](#contributors-16)
|
||||
- [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)
|
||||
@@ -146,15 +156,15 @@
|
||||
- [Default Log Level Changed](#default-log-level-changed)
|
||||
- [New `--balloon` Parameter Added](#new---balloon-parameter-added)
|
||||
- [Experimental `virtio-watchdog` Support](#experimental-virtio-watchdog-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-12)
|
||||
- [Contributors](#contributors-16)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-13)
|
||||
- [Contributors](#contributors-17)
|
||||
- [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-13)
|
||||
- [Contributors](#contributors-17)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-14)
|
||||
- [Contributors](#contributors-18)
|
||||
- [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)
|
||||
@@ -167,17 +177,17 @@
|
||||
- [Enhancements to ARM64 Support](#enhancements-to-arm64-support)
|
||||
- [Intel SGX Support](#intel-sgx-support)
|
||||
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-14)
|
||||
- [Contributors](#contributors-18)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-15)
|
||||
- [Contributors](#contributors-19)
|
||||
- [v0.8.0](#v080)
|
||||
- [Experimental Snapshot and Restore Support](#experimental-snapshot-and-restore-support)
|
||||
- [Experimental ARM64 Support](#experimental-arm64-support)
|
||||
- [Support for Using 5-level Paging in Guests](#support-for-using-5-level-paging-in-guests)
|
||||
- [Virtio Device Interrupt Suppression for Network Devices](#virtio-device-interrupt-suppression-for-network-devices)
|
||||
- [`vhost_user_fs` Improvements](#vhost_user_fs-improvements)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-15)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-16)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes)
|
||||
- [Contributors](#contributors-19)
|
||||
- [Contributors](#contributors-20)
|
||||
- [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)
|
||||
@@ -187,14 +197,14 @@
|
||||
- [`Seccomp` Sandboxing](#seccomp-sandboxing)
|
||||
- [Updated Distribution Support](#updated-distribution-support)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes-1)
|
||||
- [Contributors](#contributors-20)
|
||||
- [Contributors](#contributors-21)
|
||||
- [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-21)
|
||||
- [Contributors](#contributors-22)
|
||||
- [v0.5.1](#v051)
|
||||
- [v0.5.0](#v050)
|
||||
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
|
||||
@@ -202,7 +212,7 @@
|
||||
- [New Interrupt Management Framework](#new-interrupt-management-framework)
|
||||
- [Development Tools](#development-tools)
|
||||
- [Kata Containers Integration](#kata-containers-integration)
|
||||
- [Contributors](#contributors-22)
|
||||
- [Contributors](#contributors-23)
|
||||
- [v0.4.0](#v040)
|
||||
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
|
||||
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
|
||||
@@ -211,7 +221,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-23)
|
||||
- [Contributors](#contributors-24)
|
||||
- [v0.3.0](#v030)
|
||||
- [Block device offloading](#block-device-offloading)
|
||||
- [Network device backend](#network-device-backend)
|
||||
@@ -238,6 +248,92 @@
|
||||
- [Unit testing](#unit-testing)
|
||||
- [Integration tests parallelization](#integration-tests-parallelization)
|
||||
|
||||
# v28.1
|
||||
This is a bug fix release. The following issues have been addressed:
|
||||
|
||||
* Update dependencies including a version of `linux-loader` that
|
||||
addresses an infinite loop issue ([details](https://github.com/rust-vmm/linux-loader/security/advisories/GHSA-52h2-m2cf-9jh6))
|
||||
* Fix bugs related to `virtio-net` including an integer overflow issue
|
||||
(#4924, #4949)
|
||||
* Use host `cpuid` information for L2 cache for older KVM on x86 (#4920)
|
||||
* Improve README and documentation
|
||||
|
||||
# v28.0
|
||||
|
||||
This release has been tracked in our new [roadmap
|
||||
project](https://github.com/orgs/cloud-hypervisor/projects/6) as iteration
|
||||
v28.0.
|
||||
|
||||
### Community Engagement (Reminder)
|
||||
|
||||
Just a reminder that we have a new mailing list to support broader community
|
||||
discussions. Please consider
|
||||
[subscribing](https://lists.cloudhypervisor.org/g/dev/). We plan to use
|
||||
this to announce a regular meeting for those interested in talking about Cloud
|
||||
Hypervisor development.
|
||||
|
||||
### Long Term Support (LTS) Release
|
||||
|
||||
This is the first version of Cloud Hypervisor to be released under the LTS
|
||||
release process. Point releases for bug fixes will be made for the next 18
|
||||
months; live migration and live upgrade will be supported between the point
|
||||
releases of the LTS.
|
||||
|
||||
### Virtualised TPM Support
|
||||
|
||||
Support for adding an emulated CRB TPM has been added. This has it's own [TPM
|
||||
documentation](docs/tpm.md).
|
||||
|
||||
### Transparent Huge Page Support
|
||||
|
||||
By default, but controllable through `--memory thp=off` if it possible to back
|
||||
the guest memory with Transparent Huge Pages (no file backing/`shared=off`)
|
||||
then this will be used resulting in improved boot performance.
|
||||
|
||||
### README Quick Start Improved
|
||||
|
||||
The README has been refreshed with the quick start guide updated to reflect the
|
||||
different firmware options and to recommend the use of pre-built binaries.
|
||||
|
||||
### Notable Bug Fixes
|
||||
|
||||
* Inappropriate Copy-on-Write of pinned pages (e.g. VFIO) leading to higher
|
||||
memory consumption (#4835)
|
||||
* Multiple `virtio` device bug fixes found through fuzzing (#4859, #4799)
|
||||
* Large QCOW files (> 4TiB) are now supported (#4767)
|
||||
* Support for > 31 vCPUS on aarch64 (#4863)
|
||||
* Multiple fixes to OpenAPI specification file (#4720, #4811)
|
||||
* Programming of the MSI-X table has been optimised leading to faster boot on
|
||||
newer Linux kernels (#4744)
|
||||
* Error on reboot from race to close TAP devices (#4871)
|
||||
* Non-spec compliant virtio-block read-only support (#4888)
|
||||
|
||||
### Removals
|
||||
|
||||
The following functionality has been removed:
|
||||
|
||||
* Support for non-PVH firmware booting has been removed (#4511)
|
||||
* I/O ports used for older versions of firmware have been removed (#3926)
|
||||
* Deprecated API options for kernel/cmdline/initramfs have been removed (#4737)
|
||||
|
||||
### Contributors
|
||||
|
||||
Many thanks to everyone who has contributed to our release:
|
||||
|
||||
* Anatol Belski <anbelski@linux.microsoft.com>
|
||||
* Bo Chen <chen.bo@intel.com>
|
||||
* Fabiano Fidêncio <fabiano.fidencio@intel.com>
|
||||
* Jianyong Wu <jianyong.wu@arm.com>
|
||||
* Jinank Jain <jinankjain@microsoft.com>
|
||||
* Jinrong Liang <cloudliang@tencent.com>
|
||||
* lv.mengzhao <lv.mengzhao@zte.com.cn>
|
||||
* Michael Zhao <michael.zhao@arm.com>
|
||||
* Muminul Islam <muislam@microsoft.com>
|
||||
* Praveen K Paladugu <prapal@linux.microsoft.com>
|
||||
* Rob Bradford <robert.bradford@intel.com>
|
||||
* Sebastien Boeuf <sebastien.boeuf@intel.com>
|
||||
* Wei Liu <liuwe@microsoft.com>
|
||||
|
||||
# v27.0
|
||||
|
||||
This release has been tracked in our new [roadmap
|
||||
|
||||
@@ -124,7 +124,9 @@ popd
|
||||
|
||||
# Build custom kernel based on virtio-pmem and virtio-fs upstream patches
|
||||
VMLINUX_IMAGE="$WORKLOADS_DIR/vmlinux"
|
||||
build_custom_linux
|
||||
if [ ! -f "$VMLINUX_IMAGE" ]; then
|
||||
build_custom_linux
|
||||
fi
|
||||
|
||||
VIRTIOFSD="$WORKLOADS_DIR/virtiofsd"
|
||||
VIRTIOFSD_DIR="virtiofsd_build"
|
||||
|
||||
@@ -9,7 +9,7 @@ extern crate clap;
|
||||
use api_client::simple_api_command;
|
||||
use api_client::simple_api_command_with_fds;
|
||||
use api_client::Error as ApiClientError;
|
||||
use clap::{Arg, ArgMatches, Command};
|
||||
use clap::{Arg, ArgAction, ArgMatches, Command};
|
||||
use option_parser::{ByteSized, ByteSizedParseError};
|
||||
use std::fmt;
|
||||
use std::io::Read;
|
||||
@@ -328,8 +328,8 @@ fn create_api_command(socket: &mut UnixStream, path: &str) -> Result<(), Error>
|
||||
}
|
||||
|
||||
fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
let mut socket =
|
||||
UnixStream::connect(matches.value_of("api-socket").unwrap()).map_err(Error::Connect)?;
|
||||
let mut socket = UnixStream::connect(matches.get_one::<String>("api-socket").unwrap())
|
||||
.map_err(Error::Connect)?;
|
||||
|
||||
match matches.subcommand_name() {
|
||||
Some("info") => {
|
||||
@@ -343,27 +343,30 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("resize")
|
||||
.unwrap()
|
||||
.value_of("cpus"),
|
||||
.get_one::<String>("cpus")
|
||||
.map(|x| x as &str),
|
||||
matches
|
||||
.subcommand_matches("resize")
|
||||
.unwrap()
|
||||
.value_of("memory"),
|
||||
.get_one::<String>("memory")
|
||||
.map(|x| x as &str),
|
||||
matches
|
||||
.subcommand_matches("resize")
|
||||
.unwrap()
|
||||
.value_of("balloon"),
|
||||
.get_one::<String>("balloon")
|
||||
.map(|x| x as &str),
|
||||
),
|
||||
Some("resize-zone") => resize_zone_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("resize-zone")
|
||||
.unwrap()
|
||||
.value_of("id")
|
||||
.get_one::<String>("id")
|
||||
.unwrap(),
|
||||
matches
|
||||
.subcommand_matches("resize-zone")
|
||||
.unwrap()
|
||||
.value_of("size")
|
||||
.get_one::<String>("size")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-device") => add_device_api_command(
|
||||
@@ -371,7 +374,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("add-device")
|
||||
.unwrap()
|
||||
.value_of("device_config")
|
||||
.get_one::<String>("device_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("remove-device") => remove_device_api_command(
|
||||
@@ -379,7 +382,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("remove-device")
|
||||
.unwrap()
|
||||
.value_of("id")
|
||||
.get_one::<String>("id")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-disk") => add_disk_api_command(
|
||||
@@ -387,7 +390,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("add-disk")
|
||||
.unwrap()
|
||||
.value_of("disk_config")
|
||||
.get_one::<String>("disk_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-fs") => add_fs_api_command(
|
||||
@@ -395,7 +398,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("add-fs")
|
||||
.unwrap()
|
||||
.value_of("fs_config")
|
||||
.get_one::<String>("fs_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-pmem") => add_pmem_api_command(
|
||||
@@ -403,7 +406,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("add-pmem")
|
||||
.unwrap()
|
||||
.value_of("pmem_config")
|
||||
.get_one::<String>("pmem_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-net") => add_net_api_command(
|
||||
@@ -411,7 +414,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("add-net")
|
||||
.unwrap()
|
||||
.value_of("net_config")
|
||||
.get_one::<String>("net_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-user-device") => add_user_device_api_command(
|
||||
@@ -419,7 +422,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("add-user-device")
|
||||
.unwrap()
|
||||
.value_of("device_config")
|
||||
.get_one::<String>("device_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-vdpa") => add_vdpa_api_command(
|
||||
@@ -427,7 +430,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("add-vdpa")
|
||||
.unwrap()
|
||||
.value_of("vdpa_config")
|
||||
.get_one::<String>("vdpa_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-vsock") => add_vsock_api_command(
|
||||
@@ -435,7 +438,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("add-vsock")
|
||||
.unwrap()
|
||||
.value_of("vsock_config")
|
||||
.get_one::<String>("vsock_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("snapshot") => snapshot_api_command(
|
||||
@@ -443,7 +446,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("snapshot")
|
||||
.unwrap()
|
||||
.value_of("snapshot_config")
|
||||
.get_one::<String>("snapshot_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("restore") => restore_api_command(
|
||||
@@ -451,7 +454,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("restore")
|
||||
.unwrap()
|
||||
.value_of("restore_config")
|
||||
.get_one::<String>("restore_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("coredump") => coredump_api_command(
|
||||
@@ -459,7 +462,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("coredump")
|
||||
.unwrap()
|
||||
.value_of("coredump_config")
|
||||
.get_one::<String>("coredump_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("send-migration") => send_migration_api_command(
|
||||
@@ -467,19 +470,19 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("send-migration")
|
||||
.unwrap()
|
||||
.value_of("send_migration_config")
|
||||
.get_one::<String>("send_migration_config")
|
||||
.unwrap(),
|
||||
matches
|
||||
.subcommand_matches("send-migration")
|
||||
.unwrap()
|
||||
.is_present("send_migration_local"),
|
||||
.get_flag("send_migration_local"),
|
||||
),
|
||||
Some("receive-migration") => receive_migration_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("receive-migration")
|
||||
.unwrap()
|
||||
.value_of("receive_migration_config")
|
||||
.get_one::<String>("receive_migration_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("create") => create_api_command(
|
||||
@@ -487,7 +490,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("create")
|
||||
.unwrap()
|
||||
.value_of("path")
|
||||
.get_one::<String>("path")
|
||||
.unwrap(),
|
||||
),
|
||||
Some(c) => simple_api_command(&mut socket, "PUT", c, None).map_err(Error::ApiClient),
|
||||
@@ -504,8 +507,7 @@ fn main() {
|
||||
Arg::new("api-socket")
|
||||
.long("api-socket")
|
||||
.help("HTTP API socket path (UNIX domain socket).")
|
||||
.takes_value(true)
|
||||
.number_of_values(1)
|
||||
.num_args(1)
|
||||
.required(true),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -587,22 +589,19 @@ fn main() {
|
||||
Arg::new("cpus")
|
||||
.long("cpus")
|
||||
.help("New vCPUs count")
|
||||
.takes_value(true)
|
||||
.number_of_values(1),
|
||||
.num_args(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("memory")
|
||||
.long("memory")
|
||||
.help("New memory size in bytes (supports K/M/G suffix)")
|
||||
.takes_value(true)
|
||||
.number_of_values(1),
|
||||
.num_args(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("balloon")
|
||||
.long("balloon")
|
||||
.help("New balloon size in bytes (supports K/M/G suffix)")
|
||||
.takes_value(true)
|
||||
.number_of_values(1),
|
||||
.num_args(1),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -612,15 +611,13 @@ fn main() {
|
||||
Arg::new("id")
|
||||
.long("id")
|
||||
.help("Memory zone identifier")
|
||||
.takes_value(true)
|
||||
.number_of_values(1),
|
||||
.num_args(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("size")
|
||||
.long("size")
|
||||
.help("New memory zone size in bytes (supports K/M/G suffix)")
|
||||
.takes_value(true)
|
||||
.number_of_values(1),
|
||||
.num_args(1),
|
||||
),
|
||||
)
|
||||
.subcommand(Command::new("resume").about("Resume the VM"))
|
||||
@@ -661,7 +658,8 @@ fn main() {
|
||||
.arg(
|
||||
Arg::new("send_migration_local")
|
||||
.long("local")
|
||||
.takes_value(false),
|
||||
.num_args(0)
|
||||
.action(ArgAction::SetTrue),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
|
||||
147
src/main.rs
147
src/main.rs
@@ -8,7 +8,7 @@ extern crate clap;
|
||||
#[macro_use]
|
||||
extern crate event_monitor;
|
||||
|
||||
use clap::{Arg, ArgGroup, ArgMatches, Command};
|
||||
use clap::{Arg, ArgAction, ArgGroup, ArgMatches, Command};
|
||||
use libc::EFD_NONBLOCK;
|
||||
use log::LevelFilter;
|
||||
use option_parser::OptionParser;
|
||||
@@ -124,11 +124,7 @@ fn prepare_default_values() -> (String, String, String) {
|
||||
(default_vcpus, default_memory, default_rng)
|
||||
}
|
||||
|
||||
fn create_app<'a>(
|
||||
default_vcpus: &'a str,
|
||||
default_memory: &'a str,
|
||||
default_rng: &'a str,
|
||||
) -> Command<'a> {
|
||||
fn create_app(default_vcpus: String, default_memory: String, default_rng: String) -> Command {
|
||||
let app = Command::new("cloud-hypervisor")
|
||||
// 'BUILT_VERSION' is set by the build script 'build.rs' at
|
||||
// compile time
|
||||
@@ -157,7 +153,7 @@ fn create_app<'a>(
|
||||
.help(
|
||||
"num_pci_segments=<num_pci_segments>,iommu_segments=<list_of_segments>,serial_number=<dmi_device_serial_number>,uuid=<dmi_device_uuid>,oem_strings=<list_of_strings>",
|
||||
)
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
@@ -170,7 +166,7 @@ fn create_app<'a>(
|
||||
hotplug_method=acpi|virtio-mem,\
|
||||
hotplug_size=<hotpluggable_memory_size>,\
|
||||
hotplugged_size=<hotplugged_memory_size>,\
|
||||
prefault=on|off\"",
|
||||
prefault=on|off,thp=on|off\"",
|
||||
)
|
||||
.default_value(default_memory)
|
||||
.group("vm-config"),
|
||||
@@ -188,15 +184,14 @@ fn create_app<'a>(
|
||||
hotplugged_size=<hotplugged_memory_size>,\
|
||||
prefault=on|off\"",
|
||||
)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("firmware")
|
||||
.long("firmware")
|
||||
.help("Path to firmware that is loaded in an architectural specific way")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
@@ -206,37 +201,35 @@ fn create_app<'a>(
|
||||
"Path to kernel to load. This may be a kernel or firmware that supports a PVH \
|
||||
entry point (e.g. vmlinux) or architecture equivalent",
|
||||
)
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("initramfs")
|
||||
.long("initramfs")
|
||||
.help("Path to initramfs image")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("cmdline")
|
||||
.long("cmdline")
|
||||
.help("Kernel command line")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("disk")
|
||||
.long("disk")
|
||||
.help(config::DiskConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("net")
|
||||
.long("net")
|
||||
.help(config::NetConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
@@ -252,23 +245,21 @@ fn create_app<'a>(
|
||||
Arg::new("balloon")
|
||||
.long("balloon")
|
||||
.help(config::BalloonConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("fs")
|
||||
.long("fs")
|
||||
.help(config::FsConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("pmem")
|
||||
.long("pmem")
|
||||
.help(config::PmemConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
@@ -291,53 +282,49 @@ fn create_app<'a>(
|
||||
Arg::new("device")
|
||||
.long("device")
|
||||
.help(config::DeviceConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("user-device")
|
||||
.long("user-device")
|
||||
.help(config::UserDeviceConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("vdpa")
|
||||
.long("vdpa")
|
||||
.help(config::VdpaConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("vsock")
|
||||
.long("vsock")
|
||||
.help(config::VsockConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.number_of_values(1)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("numa")
|
||||
.long("numa")
|
||||
.help(config::NumaConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("watchdog")
|
||||
.long("watchdog")
|
||||
.help("Enable virtio-watchdog")
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.action(ArgAction::SetTrue)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("v")
|
||||
.short('v')
|
||||
.multiple_occurrences(true)
|
||||
.action(ArgAction::Count)
|
||||
.help("Sets the level of debugging output")
|
||||
.group("logging"),
|
||||
)
|
||||
@@ -345,40 +332,44 @@ fn create_app<'a>(
|
||||
Arg::new("log-file")
|
||||
.long("log-file")
|
||||
.help("Log file. Standard error is used if not specified")
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1)
|
||||
.group("logging"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("api-socket")
|
||||
.long("api-socket")
|
||||
.help("HTTP API socket (UNIX domain socket): path=</path/to/a/file> or fd=<fd>.")
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1)
|
||||
.group("vmm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("event-monitor")
|
||||
.long("event-monitor")
|
||||
.help("File to report events on: path=</path/to/a/file> or fd=<fd>")
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1)
|
||||
.group("vmm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("restore")
|
||||
.long("restore")
|
||||
.help(config::RestoreConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1)
|
||||
.group("vmm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("seccomp")
|
||||
.long("seccomp")
|
||||
.takes_value(true)
|
||||
.possible_values(["true", "false", "log"])
|
||||
.num_args(1)
|
||||
.value_parser(["true", "false", "log"])
|
||||
.default_value("true"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("tpm")
|
||||
.long("tpm")
|
||||
.num_args(1)
|
||||
.help(config::TpmConfig::SYNTAX)
|
||||
.group("vmm-config"),
|
||||
|
||||
);
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -386,8 +377,7 @@ fn create_app<'a>(
|
||||
Arg::new("sgx-epc")
|
||||
.long("sgx-epc")
|
||||
.help(config::SgxEpcConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
);
|
||||
|
||||
@@ -396,7 +386,7 @@ fn create_app<'a>(
|
||||
Arg::new("gdb")
|
||||
.long("gdb")
|
||||
.help("GDB socket (UNIX domain socket): path=</path/to/a/file>")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.group("vmm-config"),
|
||||
);
|
||||
|
||||
@@ -404,7 +394,7 @@ fn create_app<'a>(
|
||||
}
|
||||
|
||||
fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
let log_level = match cmd_arguments.occurrences_of("v") {
|
||||
let log_level = match cmd_arguments.get_count("v") {
|
||||
0 => LevelFilter::Warn,
|
||||
1 => LevelFilter::Info,
|
||||
2 => LevelFilter::Debug,
|
||||
@@ -412,7 +402,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
};
|
||||
|
||||
let log_file: Box<dyn std::io::Write + Send> = if let Some(file) =
|
||||
cmd_arguments.value_of("log-file")
|
||||
cmd_arguments.get_one::<String>("log-file")
|
||||
{
|
||||
Box::new(std::fs::File::create(std::path::Path::new(file)).map_err(Error::LogFileCreation)?)
|
||||
} else {
|
||||
@@ -427,7 +417,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
.map_err(Error::LoggerSetup)?;
|
||||
|
||||
let (api_socket_path, api_socket_fd) =
|
||||
if let Some(socket_config) = cmd_arguments.value_of("api-socket") {
|
||||
if let Some(socket_config) = cmd_arguments.get_one::<String>("api-socket") {
|
||||
let mut parser = OptionParser::new();
|
||||
parser.add("path").add("fd");
|
||||
parser.parse(socket_config).unwrap_or_default();
|
||||
@@ -441,7 +431,9 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
(Some(path), None)
|
||||
} else {
|
||||
(
|
||||
cmd_arguments.value_of("api-socket").map(|s| s.to_string()),
|
||||
cmd_arguments
|
||||
.get_one::<String>("api-socket")
|
||||
.map(|s| s.to_string()),
|
||||
None,
|
||||
)
|
||||
}
|
||||
@@ -449,7 +441,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
if let Some(monitor_config) = cmd_arguments.value_of("event-monitor") {
|
||||
if let Some(monitor_config) = cmd_arguments.get_one::<String>("event-monitor") {
|
||||
let mut parser = OptionParser::new();
|
||||
parser.add("path").add("fd");
|
||||
parser
|
||||
@@ -478,8 +470,8 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
let api_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateApiEventFd)?;
|
||||
|
||||
let http_sender = api_request_sender.clone();
|
||||
let seccomp_action = if let Some(seccomp_value) = cmd_arguments.value_of("seccomp") {
|
||||
match seccomp_value {
|
||||
let seccomp_action = if let Some(seccomp_value) = cmd_arguments.get_one::<String>("seccomp") {
|
||||
match seccomp_value as &str {
|
||||
"true" => SeccompAction::Trap,
|
||||
"false" => SeccompAction::Allow,
|
||||
"log" => SeccompAction::Log,
|
||||
@@ -529,7 +521,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
let hypervisor = hypervisor::new().map_err(Error::CreateHypervisor)?;
|
||||
|
||||
#[cfg(feature = "guest_debug")]
|
||||
let gdb_socket_path = if let Some(gdb_config) = cmd_arguments.value_of("gdb") {
|
||||
let gdb_socket_path = if let Some(gdb_config) = cmd_arguments.get_one::<String>("gdb") {
|
||||
let mut parser = OptionParser::new();
|
||||
parser.add("path");
|
||||
parser.parse(gdb_config).map_err(Error::ParsingGdb)?;
|
||||
@@ -566,7 +558,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
.map_err(Error::StartVmmThread)?;
|
||||
|
||||
let payload_present =
|
||||
cmd_arguments.is_present("kernel") || cmd_arguments.is_present("firmware");
|
||||
cmd_arguments.contains_id("kernel") || cmd_arguments.contains_id("firmware");
|
||||
|
||||
if payload_present {
|
||||
let vm_params = config::VmParams::from_arg_matches(&cmd_arguments);
|
||||
@@ -581,7 +573,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
)
|
||||
.map_err(Error::VmCreate)?;
|
||||
vmm::api::vm_boot(api_evt.try_clone().unwrap(), sender).map_err(Error::VmBoot)?;
|
||||
} else if let Some(restore_params) = cmd_arguments.value_of("restore") {
|
||||
} else if let Some(restore_params) = cmd_arguments.get_one::<String>("restore") {
|
||||
vmm::api::vm_restore(
|
||||
api_evt.try_clone().unwrap(),
|
||||
api_request_sender,
|
||||
@@ -603,7 +595,7 @@ fn main() {
|
||||
let _ = unsafe { libc::umask(0o077) };
|
||||
|
||||
let (default_vcpus, default_memory, default_rng) = prepare_default_values();
|
||||
let cmd_arguments = create_app(&default_vcpus, &default_memory, &default_rng).get_matches();
|
||||
let cmd_arguments = create_app(default_vcpus, default_memory, default_rng).get_matches();
|
||||
let exit_code = match start_vmm(cmd_arguments) {
|
||||
Ok(path) => {
|
||||
path.map(|s| std::fs::remove_file(s).ok());
|
||||
@@ -615,7 +607,7 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
let on_tty = unsafe { libc::isatty(libc::STDIN_FILENO as i32) } != 0;
|
||||
let on_tty = unsafe { libc::isatty(libc::STDIN_FILENO) } != 0;
|
||||
if on_tty {
|
||||
// Don't forget to set the terminal in canonical mode
|
||||
// before to exit.
|
||||
@@ -631,14 +623,14 @@ mod unit_tests {
|
||||
use crate::{create_app, prepare_default_values};
|
||||
use std::path::PathBuf;
|
||||
use vmm::config::{
|
||||
CmdlineConfig, ConsoleConfig, ConsoleOutputMode, CpuFeatures, CpusConfig, MemoryConfig,
|
||||
PayloadConfig, RngConfig, VmConfig, VmParams,
|
||||
ConsoleConfig, ConsoleOutputMode, CpuFeatures, CpusConfig, MemoryConfig, PayloadConfig,
|
||||
RngConfig, VmConfig, VmParams,
|
||||
};
|
||||
|
||||
fn get_vm_config_from_vec(args: &[&str]) -> VmConfig {
|
||||
let (default_vcpus, default_memory, default_rng) = prepare_default_values();
|
||||
let cmd_arguments =
|
||||
create_app(&default_vcpus, &default_memory, &default_rng).get_matches_from(args);
|
||||
create_app(default_vcpus, default_memory, default_rng).get_matches_from(args);
|
||||
|
||||
let vm_params = VmParams::from_arg_matches(&cmd_arguments);
|
||||
|
||||
@@ -692,12 +684,8 @@ mod unit_tests {
|
||||
hugepage_size: None,
|
||||
prefault: false,
|
||||
zones: None,
|
||||
thp: true,
|
||||
},
|
||||
kernel: None,
|
||||
cmdline: CmdlineConfig {
|
||||
args: String::default(),
|
||||
},
|
||||
initramfs: None,
|
||||
payload: Some(PayloadConfig {
|
||||
kernel: Some(PathBuf::from("/path/to/kernel")),
|
||||
..Default::default()
|
||||
@@ -733,6 +721,7 @@ mod unit_tests {
|
||||
#[cfg(feature = "guest_debug")]
|
||||
gdb: false,
|
||||
platform: None,
|
||||
tpm: None,
|
||||
};
|
||||
|
||||
assert_eq!(expected_vm_config, result_vm_config);
|
||||
@@ -1652,4 +1641,26 @@ mod unit_tests {
|
||||
compare_vm_config_cli_vs_json(cli, openapi, *equal);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_valid_vm_config_tpm_socket() {
|
||||
vec![(
|
||||
vec![
|
||||
"cloud-hypervisor",
|
||||
"--kernel",
|
||||
"/path/to/kernel",
|
||||
"--tpm",
|
||||
"socket=/path/to/tpm/sock",
|
||||
],
|
||||
r#"{
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"tpm": {"socket": "/path/to/tpm/sock"}
|
||||
}"#,
|
||||
true,
|
||||
)]
|
||||
.iter()
|
||||
.for_each(|(cli, openapi, equal)| {
|
||||
compare_vm_config_cli_vs_json(cli, openapi, *equal);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,4 +73,4 @@ write_files:
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo -n "@DEFAULT_TCP_LISTENER_MESSAGE" | nc -w0 @HOST_IP @TCP_LISTENER_PORT
|
||||
echo -n "@DEFAULT_TCP_LISTENER_MESSAGE" > /dev/tcp/@HOST_IP/@TCP_LISTENER_PORT
|
||||
|
||||
@@ -7,10 +7,10 @@ edition = "2021"
|
||||
[dependencies]
|
||||
dirs = "4.0.0"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.133"
|
||||
once_cell = "1.15.0"
|
||||
serde = { version = "1.0.145", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.85"
|
||||
libc = "0.2.138"
|
||||
once_cell = "1.16.0"
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.89"
|
||||
ssh2 = { version = "0.9.3", features = ["vendored-openssl"] }
|
||||
vmm-sys-util = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
@@ -1085,7 +1085,7 @@ impl Guest {
|
||||
}
|
||||
|
||||
pub fn reboot_linux(&self, current_reboot_count: u32, custom_timeout: Option<i32>) {
|
||||
let list_boots_cmd = "sudo journalctl --list-boots | wc -l";
|
||||
let list_boots_cmd = "sudo last | grep -c reboot";
|
||||
let boot_count = self
|
||||
.ssh_command(list_boots_cmd)
|
||||
.unwrap()
|
||||
@@ -1421,7 +1421,7 @@ pub fn parse_fio_output(output: &str, fio_ops: &FioOps, num_jobs: u32) -> Result
|
||||
.expect("'fio' parse error: missing entry 'read.runtime'")
|
||||
as f64
|
||||
/ 1000_f64;
|
||||
total_bps += bytes as f64 / runtime as f64;
|
||||
total_bps += bytes as f64 / runtime;
|
||||
}
|
||||
if write {
|
||||
let bytes = j["write"]["io_bytes"]
|
||||
@@ -1432,7 +1432,7 @@ pub fn parse_fio_output(output: &str, fio_ops: &FioOps, num_jobs: u32) -> Result
|
||||
.expect("'fio' parse error: missing entry 'write.runtime'")
|
||||
as f64
|
||||
/ 1000_f64;
|
||||
total_bps += bytes as f64 / runtime as f64;
|
||||
total_bps += bytes as f64 / runtime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1477,7 +1477,7 @@ pub fn parse_fio_output_iops(output: &str, fio_ops: &FioOps, num_jobs: u32) -> R
|
||||
.expect("'fio' parse error: missing entry 'read.runtime'")
|
||||
as f64
|
||||
/ 1000_f64;
|
||||
total_iops += ios as f64 / runtime as f64;
|
||||
total_iops += ios as f64 / runtime;
|
||||
}
|
||||
if write {
|
||||
let ios = j["write"]["total_ios"]
|
||||
@@ -1488,7 +1488,7 @@ pub fn parse_fio_output_iops(output: &str, fio_ops: &FioOps, num_jobs: u32) -> R
|
||||
.expect("'fio' parse error: missing entry 'write.runtime'")
|
||||
as f64
|
||||
/ 1000_f64;
|
||||
total_iops += ios as f64 / runtime as f64;
|
||||
total_iops += ios as f64 / runtime;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1839,7 +1839,7 @@ fn _test_virtio_iommu(acpi: bool) {
|
||||
// contains "0000:00:02.0" which is the first disk.
|
||||
//
|
||||
// Verify the iommu group of the first disk.
|
||||
let iommu_group = if acpi { 0 } else { 1 };
|
||||
let iommu_group = !acpi as i32;
|
||||
assert_eq!(
|
||||
guest
|
||||
.ssh_command(
|
||||
@@ -1882,11 +1882,8 @@ fn _test_virtio_iommu(acpi: bool) {
|
||||
}
|
||||
|
||||
fn get_reboot_count(guest: &Guest) -> u32 {
|
||||
// Ensure that the current boot journal is written so reboot counts are valid
|
||||
guest.ssh_command("sudo journalctl --sync").unwrap();
|
||||
|
||||
guest
|
||||
.ssh_command("sudo journalctl --list-boots | wc -l")
|
||||
.ssh_command("sudo last | grep -c reboot")
|
||||
.unwrap()
|
||||
.trim()
|
||||
.parse::<u32>()
|
||||
@@ -2876,7 +2873,7 @@ mod common_parallel {
|
||||
let mut os_path = os_dir.as_path().to_path_buf();
|
||||
os_path.push("osdisk.img");
|
||||
rate_limited_copy(
|
||||
&guest.disk_config.disk(DiskType::OperatingSystem).unwrap(),
|
||||
guest.disk_config.disk(DiskType::OperatingSystem).unwrap(),
|
||||
os_path.as_path(),
|
||||
)
|
||||
.expect("copying of OS disk failed");
|
||||
@@ -3219,7 +3216,7 @@ mod common_parallel {
|
||||
format!(
|
||||
"file={},size={}",
|
||||
guest.disk_config.disk(DiskType::OperatingSystem).unwrap(),
|
||||
fs::metadata(&guest.disk_config.disk(DiskType::OperatingSystem).unwrap())
|
||||
fs::metadata(guest.disk_config.disk(DiskType::OperatingSystem).unwrap())
|
||||
.unwrap()
|
||||
.len()
|
||||
)
|
||||
@@ -3766,7 +3763,7 @@ mod common_parallel {
|
||||
// We copy our cloudinit into the vfio mount point, for the nested
|
||||
// cloud-hypervisor guest to use.
|
||||
rate_limited_copy(
|
||||
&guest.disk_config.disk(DiskType::CloudInit).unwrap(),
|
||||
guest.disk_config.disk(DiskType::CloudInit).unwrap(),
|
||||
&cloud_init_vfio_base_path,
|
||||
)
|
||||
.expect("copying of cloud-init disk failed");
|
||||
@@ -4032,52 +4029,6 @@ mod common_parallel {
|
||||
handle_child_output(r, &output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reboot() {
|
||||
let bionic = UbuntuDiskConfig::new(BIONIC_IMAGE_NAME.to_string());
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
|
||||
vec![Box::new(bionic), Box::new(focal)]
|
||||
.drain(..)
|
||||
.for_each(|disk_config| {
|
||||
let guest = Guest::new(disk_config);
|
||||
|
||||
let mut cmd = GuestCommand::new(&guest);
|
||||
cmd.args(["--cpus", "boot=1"])
|
||||
.args(["--memory", "size=512M"])
|
||||
.args(["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.default_disks()
|
||||
.default_net()
|
||||
.capture_output();
|
||||
|
||||
let mut child = cmd.spawn().unwrap();
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
guest.wait_vm_boot(Some(120)).unwrap();
|
||||
|
||||
let fd_count_1 = get_fd_count(child.id());
|
||||
guest.reboot_linux(0, Some(120));
|
||||
let fd_count_2 = get_fd_count(child.id());
|
||||
assert_eq!(fd_count_1, fd_count_2);
|
||||
|
||||
guest.ssh_command("sudo shutdown -h now").unwrap();
|
||||
});
|
||||
|
||||
let _ = child.wait_timeout(std::time::Duration::from_secs(40));
|
||||
let _ = child.kill();
|
||||
let output = child.wait_with_output().unwrap();
|
||||
handle_child_output(r, &output);
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
// Check that the cloud-hypervisor binary actually terminated
|
||||
assert!(output.status.success());
|
||||
});
|
||||
|
||||
handle_child_output(r, &output);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_virtio_vsock() {
|
||||
_test_virtio_vsock(false)
|
||||
@@ -5692,7 +5643,17 @@ mod common_parallel {
|
||||
// through each ssh command. There's no need to perform a dedicated test to
|
||||
// verify the migration went well for virtio-net.
|
||||
#[test]
|
||||
fn test_snapshot_restore() {
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_snapshot_restore_hotplug_virtiomem() {
|
||||
_test_snapshot_restore(true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_snapshot_restore_basic() {
|
||||
_test_snapshot_restore(false);
|
||||
}
|
||||
|
||||
fn _test_snapshot_restore(use_hotplug: bool) {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(focal));
|
||||
let kernel_path = direct_kernel_boot_path();
|
||||
@@ -5704,6 +5665,11 @@ mod common_parallel {
|
||||
"id={},tap=,mac={},ip={},mask=255.255.255.0",
|
||||
net_id, guest.network.guest_mac, guest.network.host_ip
|
||||
);
|
||||
let mut mem_params = "size=4G";
|
||||
|
||||
if use_hotplug {
|
||||
mem_params = "size=4G,hotplug_method=virtio-mem,hotplug_size=32G"
|
||||
}
|
||||
|
||||
let cloudinit_params = format!(
|
||||
"path={},iommu=on",
|
||||
@@ -5717,10 +5683,7 @@ mod common_parallel {
|
||||
.args(["--api-socket", &api_socket_source])
|
||||
.args(["--event-monitor", format!("path={}", event_path).as_str()])
|
||||
.args(["--cpus", "boot=4"])
|
||||
.args([
|
||||
"--memory",
|
||||
"size=4G,hotplug_method=virtio-mem,hotplug_size=32G",
|
||||
])
|
||||
.args(["--memory", mem_params])
|
||||
.args(["--balloon", "size=0"])
|
||||
.args(["--kernel", kernel_path.to_str().unwrap()])
|
||||
.args([
|
||||
@@ -5750,28 +5713,30 @@ mod common_parallel {
|
||||
assert_eq!(guest.get_cpu_count().unwrap_or_default(), 4);
|
||||
// Check the guest RAM
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 3_840_000);
|
||||
// Increase guest RAM with virtio-mem
|
||||
resize_command(
|
||||
&api_socket_source,
|
||||
None,
|
||||
Some(6 << 30),
|
||||
None,
|
||||
Some(&event_path),
|
||||
);
|
||||
thread::sleep(std::time::Duration::new(5, 0));
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 5_760_000);
|
||||
// Use balloon to remove RAM from the VM
|
||||
resize_command(
|
||||
&api_socket_source,
|
||||
None,
|
||||
None,
|
||||
Some(1 << 30),
|
||||
Some(&event_path),
|
||||
);
|
||||
thread::sleep(std::time::Duration::new(5, 0));
|
||||
let total_memory = guest.get_total_memory().unwrap_or_default();
|
||||
assert!(total_memory > 4_800_000);
|
||||
assert!(total_memory < 5_760_000);
|
||||
if use_hotplug {
|
||||
// Increase guest RAM with virtio-mem
|
||||
resize_command(
|
||||
&api_socket_source,
|
||||
None,
|
||||
Some(6 << 30),
|
||||
None,
|
||||
Some(&event_path),
|
||||
);
|
||||
thread::sleep(std::time::Duration::new(5, 0));
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 5_760_000);
|
||||
// Use balloon to remove RAM from the VM
|
||||
resize_command(
|
||||
&api_socket_source,
|
||||
None,
|
||||
None,
|
||||
Some(1 << 30),
|
||||
Some(&event_path),
|
||||
);
|
||||
thread::sleep(std::time::Duration::new(5, 0));
|
||||
let total_memory = guest.get_total_memory().unwrap_or_default();
|
||||
assert!(total_memory > 4_800_000);
|
||||
assert!(total_memory < 5_760_000);
|
||||
}
|
||||
// Check the guest virtio-devices, e.g. block, rng, vsock, console, and net
|
||||
guest.check_devices_common(Some(&socket), Some(&console_text), None);
|
||||
|
||||
@@ -5924,18 +5889,22 @@ mod common_parallel {
|
||||
// Perform same checks to validate VM has been properly restored
|
||||
assert_eq!(guest.get_cpu_count().unwrap_or_default(), 4);
|
||||
let total_memory = guest.get_total_memory().unwrap_or_default();
|
||||
assert!(total_memory > 4_800_000);
|
||||
assert!(total_memory < 5_760_000);
|
||||
// Deflate balloon to restore entire RAM to the VM
|
||||
resize_command(&api_socket_restored, None, None, Some(0), None);
|
||||
thread::sleep(std::time::Duration::new(5, 0));
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 5_760_000);
|
||||
// Decrease guest RAM with virtio-mem
|
||||
resize_command(&api_socket_restored, None, Some(5 << 30), None, None);
|
||||
thread::sleep(std::time::Duration::new(5, 0));
|
||||
let total_memory = guest.get_total_memory().unwrap_or_default();
|
||||
assert!(total_memory > 4_800_000);
|
||||
assert!(total_memory < 5_760_000);
|
||||
if !use_hotplug {
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 3_840_000);
|
||||
} else {
|
||||
assert!(total_memory > 4_800_000);
|
||||
assert!(total_memory < 5_760_000);
|
||||
// Deflate balloon to restore entire RAM to the VM
|
||||
resize_command(&api_socket_restored, None, None, Some(0), None);
|
||||
thread::sleep(std::time::Duration::new(5, 0));
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 5_760_000);
|
||||
// Decrease guest RAM with virtio-mem
|
||||
resize_command(&api_socket_restored, None, Some(5 << 30), None, None);
|
||||
thread::sleep(std::time::Duration::new(5, 0));
|
||||
let total_memory = guest.get_total_memory().unwrap_or_default();
|
||||
assert!(total_memory > 4_800_000);
|
||||
assert!(total_memory < 5_760_000);
|
||||
}
|
||||
|
||||
guest.check_devices_common(Some(&socket), Some(&console_text), None);
|
||||
});
|
||||
@@ -6225,7 +6194,7 @@ mod common_parallel {
|
||||
);
|
||||
|
||||
let tap_index =
|
||||
fs::read_to_string(&format!("/sys/class/net/{}/ifindex", guest_macvtap_name)).unwrap();
|
||||
fs::read_to_string(format!("/sys/class/net/{}/ifindex", guest_macvtap_name)).unwrap();
|
||||
let tap_device = format!("/dev/tap{}", tap_index.trim());
|
||||
|
||||
assert!(
|
||||
@@ -7895,7 +7864,7 @@ mod vfio {
|
||||
// We copy our cloudinit into the vfio mount point, for the nested
|
||||
// cloud-hypervisor guest to use.
|
||||
rate_limited_copy(
|
||||
&guest.disk_config.disk(DiskType::CloudInit).unwrap(),
|
||||
guest.disk_config.disk(DiskType::CloudInit).unwrap(),
|
||||
&cloud_init_vfio_base_path,
|
||||
)
|
||||
.expect("copying of cloud-init disk failed");
|
||||
|
||||
14
tpm/Cargo.toml
Normal file
14
tpm/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "tpm"
|
||||
edition = "2021"
|
||||
authors = ["Microsoft Authors"]
|
||||
license = "Apache-2.0"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.66"
|
||||
byteorder = "1.4.3"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
thiserror = "1.0.37"
|
||||
vmm-sys-util = "0.11.0"
|
||||
465
tpm/src/emulator.rs
Normal file
465
tpm/src/emulator.rs
Normal file
@@ -0,0 +1,465 @@
|
||||
// Copyright © 2022, Microsoft Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use crate::socket::SocketDev;
|
||||
use crate::{Commands, MemberType, Ptm, PtmCap, PtmEst, PtmInit, PtmResult, PtmSetBufferSize};
|
||||
use crate::{TPM_CRB_BUFFER_MAX, TPM_SUCCESS};
|
||||
use anyhow::anyhow;
|
||||
use libc::c_void;
|
||||
use libc::{sockaddr_storage, socklen_t};
|
||||
use std::convert::TryInto;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::path::Path;
|
||||
use std::{mem, ptr};
|
||||
use thiserror::Error;
|
||||
|
||||
const TPM_REQ_HDR_SIZE: usize = 10;
|
||||
|
||||
/* capability flags returned by PTM_GET_CAPABILITY */
|
||||
const PTM_CAP_INIT: u64 = 1;
|
||||
const PTM_CAP_SHUTDOWN: u64 = 1 << 1;
|
||||
const PTM_CAP_GET_TPMESTABLISHED: u64 = 1 << 2;
|
||||
const PTM_CAP_SET_LOCALITY: u64 = 1 << 3;
|
||||
const PTM_CAP_CANCEL_TPM_CMD: u64 = 1 << 5;
|
||||
const PTM_CAP_RESET_TPMESTABLISHED: u64 = 1 << 7;
|
||||
const PTM_CAP_STOP: u64 = 1 << 10;
|
||||
const PTM_CAP_SET_DATAFD: u64 = 1 << 12;
|
||||
const PTM_CAP_SET_BUFFERSIZE: u64 = 1 << 13;
|
||||
|
||||
///Check if the input command is selftest
|
||||
///
|
||||
pub fn is_selftest(input: Vec<u8>, in_len: usize) -> bool {
|
||||
if in_len >= TPM_REQ_HDR_SIZE {
|
||||
let ordinal: &[u8; 4] = input[6..6 + 4]
|
||||
.try_into()
|
||||
.expect("slice with incorrect length");
|
||||
|
||||
return u32::from_ne_bytes(*ordinal).to_be() == 0x143;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Could not initialize emulator's backend: {0}")]
|
||||
InitializeEmulator(#[source] anyhow::Error),
|
||||
#[error("Failed to create data fd to pass to swtpm: {0}")]
|
||||
PrepareDataFd(#[source] anyhow::Error),
|
||||
#[error("Failed to run Control Cmd: {0}")]
|
||||
RunControlCmd(#[source] anyhow::Error),
|
||||
#[error("Emulator doesn't implement min required capabilities: {0}")]
|
||||
CheckCaps(#[source] anyhow::Error),
|
||||
#[error("Emulator failed to deliver request: {0}")]
|
||||
DeliverRequest(#[source] anyhow::Error),
|
||||
#[error("Emulator failed to send/receive msg on data fd: {0}")]
|
||||
SendReceive(#[source] anyhow::Error),
|
||||
#[error("Incorrect response to Self Test: {0}")]
|
||||
SelfTest(#[source] anyhow::Error),
|
||||
}
|
||||
|
||||
type Result<T> = anyhow::Result<T, Error>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BackendCmd {
|
||||
pub locality: u8,
|
||||
pub input: Vec<u8>,
|
||||
pub input_len: usize,
|
||||
pub output: Vec<u8>,
|
||||
pub output_len: usize,
|
||||
pub selftest_done: bool,
|
||||
}
|
||||
|
||||
pub struct Emulator {
|
||||
cmd: Option<BackendCmd>,
|
||||
caps: PtmCap, /* capabilities of the TPM */
|
||||
control_socket: SocketDev,
|
||||
data_fd: RawFd,
|
||||
established_flag_cached: bool,
|
||||
established_flag: bool,
|
||||
}
|
||||
|
||||
impl Emulator {
|
||||
/// Create Emulator Instance
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `path` - A path to the Unix Domain Socket swtpm is listening on
|
||||
///
|
||||
pub fn new(path: String) -> Result<Self> {
|
||||
if !Path::new(&path).exists() {
|
||||
return Err(Error::InitializeEmulator(anyhow!(
|
||||
"The input TPM Socket path: {:?} does not exist",
|
||||
path
|
||||
)));
|
||||
}
|
||||
let mut socket = SocketDev::new();
|
||||
socket.init(path).map_err(|e| {
|
||||
Error::InitializeEmulator(anyhow!("Failed while initializing tpm emulator: {:?}", e))
|
||||
})?;
|
||||
|
||||
let mut emulator = Self {
|
||||
cmd: None,
|
||||
caps: 0,
|
||||
control_socket: socket,
|
||||
data_fd: -1,
|
||||
established_flag_cached: false,
|
||||
established_flag: false,
|
||||
};
|
||||
|
||||
emulator.prepare_data_fd()?;
|
||||
|
||||
emulator.probe_caps()?;
|
||||
if !emulator.check_caps() {
|
||||
return Err(Error::InitializeEmulator(anyhow!(
|
||||
"Required capabilities not supported by tpm backend"
|
||||
)));
|
||||
}
|
||||
|
||||
if !emulator.get_established_flag() {
|
||||
return Err(Error::InitializeEmulator(anyhow!(
|
||||
"TPM not in established state"
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(emulator)
|
||||
}
|
||||
|
||||
/// Create socketpair, assign one socket/FD as data_fd to Control Socket
|
||||
/// The other socket/FD will be assigned to msg_fd, which will be sent to swtpm
|
||||
/// via CmdSetDatafd control command
|
||||
fn prepare_data_fd(&mut self) -> Result<()> {
|
||||
let mut res: PtmResult = 0;
|
||||
|
||||
let mut fds = [-1, -1];
|
||||
// Safe because return value of the unsafe call is checked
|
||||
unsafe {
|
||||
let ret = libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr());
|
||||
if ret == -1 {
|
||||
return Err(Error::PrepareDataFd(anyhow!(
|
||||
"Failed to prepare data fd for tpm emulator. Error Code {:?}",
|
||||
std::io::Error::last_os_error()
|
||||
)));
|
||||
}
|
||||
}
|
||||
self.control_socket.set_msgfd(fds[1]);
|
||||
debug!("data fd to be configured in swtpm = {:?}", fds[1]);
|
||||
self.run_control_cmd(Commands::CmdSetDatafd, &mut res, 0, mem::size_of::<u32>())?;
|
||||
debug!("data fd in cloud-hypervisor = {:?}", fds[0]);
|
||||
self.data_fd = fds[0];
|
||||
self.control_socket.set_datafd(fds[0]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gather TPM Capabilities and cache them in Emulator
|
||||
///
|
||||
fn probe_caps(&mut self) -> Result<()> {
|
||||
let mut caps: u64 = 0;
|
||||
self.run_control_cmd(
|
||||
Commands::CmdGetCapability,
|
||||
&mut caps,
|
||||
0,
|
||||
mem::size_of::<u64>(),
|
||||
)?;
|
||||
self.caps = caps;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check if minimum set of capabitlies are supported
|
||||
fn check_caps(&mut self) -> bool {
|
||||
/* min. required capabilities for TPM 2.0*/
|
||||
let caps: PtmCap = PTM_CAP_INIT
|
||||
| PTM_CAP_SHUTDOWN
|
||||
| PTM_CAP_GET_TPMESTABLISHED
|
||||
| PTM_CAP_SET_LOCALITY
|
||||
| PTM_CAP_RESET_TPMESTABLISHED
|
||||
| PTM_CAP_SET_DATAFD
|
||||
| PTM_CAP_STOP
|
||||
| PTM_CAP_SET_BUFFERSIZE;
|
||||
|
||||
if (self.caps & caps) != caps {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `cmd` - Control Command to run
|
||||
/// * `msg` - Optional msg to be sent along with Control Command
|
||||
/// * `msg_len_in` - len of 'msg' in bytes, if passed
|
||||
/// * `msg_len_out` - length of expected output from Control Command in bytes
|
||||
///
|
||||
fn run_control_cmd(
|
||||
&mut self,
|
||||
cmd: Commands,
|
||||
msg: &mut dyn Ptm,
|
||||
msg_len_in: usize,
|
||||
msg_len_out: usize,
|
||||
) -> Result<()> {
|
||||
debug!("Control Cmd to send : {:02X?}", cmd);
|
||||
|
||||
let cmd_no = (cmd as u32).to_be_bytes();
|
||||
let n: isize = (mem::size_of::<u32>() + msg_len_in) as isize;
|
||||
|
||||
let converted_req = msg.ptm_to_request();
|
||||
debug!("converted request: {:02X?}", converted_req);
|
||||
|
||||
let mut buf = Vec::<u8>::with_capacity(n as usize);
|
||||
|
||||
buf.extend(cmd_no);
|
||||
buf.extend(converted_req);
|
||||
debug!("full Control request {:02X?}", buf);
|
||||
|
||||
let _res = self.control_socket.write(&buf).map_err(|e| {
|
||||
Error::RunControlCmd(anyhow!(
|
||||
"Failed while running {:02X?} Control Cmd. Error: {:?}",
|
||||
cmd,
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
let mut output = [0_u8; TPM_CRB_BUFFER_MAX];
|
||||
|
||||
// Every Control Cmd gets atleast a result code in response. Read it
|
||||
let read_size = self.control_socket.read(&mut output).map_err(|e| {
|
||||
Error::RunControlCmd(anyhow!(
|
||||
"Failed while reading response for Control Cmd: {:02X?}. Error: {:?}",
|
||||
cmd,
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
if msg_len_out != 0 {
|
||||
msg.update_ptm_with_response(&output[0..read_size])
|
||||
.map_err(|e| {
|
||||
Error::RunControlCmd(anyhow!(
|
||||
"Failed while converting response of Control Cmd: {:02X?} to PTM. Error: {:?}",
|
||||
cmd,
|
||||
e
|
||||
))
|
||||
})?;
|
||||
} else {
|
||||
// No response expected, only handle return code
|
||||
msg.set_member_type(MemberType::Response);
|
||||
}
|
||||
|
||||
if msg.get_result_code() != TPM_SUCCESS {
|
||||
return Err(Error::RunControlCmd(anyhow!(
|
||||
"Control Cmd returned error code : {:?}",
|
||||
msg.get_result_code()
|
||||
)));
|
||||
}
|
||||
debug!("Control Cmd Response : {:02X?}", &output[0..read_size]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_established_flag(&mut self) -> bool {
|
||||
let mut est: PtmEst = PtmEst::new();
|
||||
|
||||
if self.established_flag_cached {
|
||||
return self.established_flag;
|
||||
}
|
||||
|
||||
if let Err(e) = self.run_control_cmd(
|
||||
Commands::CmdGetTpmEstablished,
|
||||
&mut est,
|
||||
0,
|
||||
2 * mem::size_of::<u32>(),
|
||||
) {
|
||||
error!(
|
||||
"Failed to run CmdGetTpmEstablished Control Cmd. Error: {:?}",
|
||||
e
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
self.established_flag_cached = true;
|
||||
if est.resp.bit != 0 {
|
||||
self.established_flag = false;
|
||||
} else {
|
||||
self.established_flag = true;
|
||||
}
|
||||
|
||||
self.established_flag
|
||||
}
|
||||
|
||||
/// Function to write to data socket and read the response from it
|
||||
fn unix_tx_bufs(&mut self) -> Result<()> {
|
||||
let isselftest: bool;
|
||||
// Safe as type "sockaddr_storage" is valid with an all-zero byte-pattern value
|
||||
let mut addr: sockaddr_storage = unsafe { mem::zeroed() };
|
||||
let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;
|
||||
|
||||
if let Some(ref mut cmd) = self.cmd {
|
||||
cmd.selftest_done = false;
|
||||
isselftest = is_selftest(cmd.input.to_vec(), cmd.input_len);
|
||||
|
||||
debug!(
|
||||
"Send cmd: {:02X?} of len {:?} on data_ioc ",
|
||||
cmd.input, cmd.input_len
|
||||
);
|
||||
|
||||
let data_vecs = [libc::iovec {
|
||||
iov_base: cmd.input.as_mut_ptr() as *mut libc::c_void,
|
||||
iov_len: cmd.input.len(),
|
||||
}; 1];
|
||||
|
||||
// Safe because all zero values from the unsafe method are updated before usage
|
||||
let mut msghdr: libc::msghdr = unsafe { mem::zeroed() };
|
||||
msghdr.msg_name = ptr::null_mut();
|
||||
msghdr.msg_namelen = 0;
|
||||
msghdr.msg_iov = data_vecs.as_ptr() as *mut libc::iovec;
|
||||
msghdr.msg_iovlen = data_vecs.len() as _;
|
||||
msghdr.msg_control = ptr::null_mut();
|
||||
msghdr.msg_controllen = 0;
|
||||
msghdr.msg_flags = 0;
|
||||
// Safe as the return value of the unsafe method is checked
|
||||
unsafe {
|
||||
let ret = libc::sendmsg(self.data_fd, &msghdr, 0);
|
||||
if ret == -1 {
|
||||
return Err(Error::SendReceive(anyhow!(
|
||||
"Failed to send tpm command over Data FD. Error Code {:?}",
|
||||
std::io::Error::last_os_error()
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
cmd.output.fill(0);
|
||||
// Safe as return value from unsafe method is checked
|
||||
unsafe {
|
||||
let ret = libc::recvfrom(
|
||||
self.data_fd,
|
||||
cmd.output.as_ptr() as *mut c_void,
|
||||
cmd.output.len(),
|
||||
0,
|
||||
&mut addr as *mut libc::sockaddr_storage as *mut libc::sockaddr,
|
||||
&mut len as *mut socklen_t,
|
||||
);
|
||||
if ret == -1 {
|
||||
return Err(Error::SendReceive(anyhow!(
|
||||
"Failed to receive response for tpm command over Data FD. Error Code {:?}",
|
||||
std::io::Error::last_os_error()
|
||||
)));
|
||||
}
|
||||
cmd.output_len = ret as usize;
|
||||
}
|
||||
debug!(
|
||||
"response = {:02X?} len = {:?} selftest = {:?}",
|
||||
cmd.output, cmd.output_len, isselftest
|
||||
);
|
||||
|
||||
if isselftest {
|
||||
if cmd.output_len < 10 {
|
||||
return Err(Error::SelfTest(anyhow!(
|
||||
"Self test response should have 10 bytes. Only {:?} returned",
|
||||
cmd.output_len
|
||||
)));
|
||||
}
|
||||
let mut errcode: [u8; 4] = [0; 4];
|
||||
errcode.copy_from_slice(&cmd.output[6..6 + 4]);
|
||||
cmd.selftest_done = u32::from_ne_bytes(errcode).to_be() == 0;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn deliver_request(&mut self, in_cmd: &mut BackendCmd) -> Result<Vec<u8>> {
|
||||
if self.cmd.is_some() {
|
||||
//previous request did not finish cleanly
|
||||
return Err(Error::DeliverRequest(anyhow!(
|
||||
"Cannot deliver tpm Request as previous cmd did not complete."
|
||||
)));
|
||||
}
|
||||
self.cmd = Some(in_cmd.clone());
|
||||
|
||||
self.unix_tx_bufs()?;
|
||||
|
||||
let output = self.cmd.as_ref().unwrap().output.clone();
|
||||
in_cmd.output.fill(0);
|
||||
in_cmd.output.clone_from(&output);
|
||||
|
||||
self.backend_request_completed();
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
pub fn backend_request_completed(&mut self) {
|
||||
self.cmd = None;
|
||||
}
|
||||
|
||||
pub fn cancel_cmd(&mut self) -> Result<()> {
|
||||
let mut res: PtmResult = 0;
|
||||
|
||||
// Check if emulator implements Cancel Cmd
|
||||
if (self.caps & PTM_CAP_CANCEL_TPM_CMD) != PTM_CAP_CANCEL_TPM_CMD {
|
||||
return Err(Error::CheckCaps(anyhow!(
|
||||
"Emulator does not implement 'Cancel Command' Capability"
|
||||
)));
|
||||
}
|
||||
self.run_control_cmd(
|
||||
Commands::CmdCancelTpmCmd,
|
||||
&mut res,
|
||||
0,
|
||||
mem::size_of::<u32>(),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Configure buffersize to use while communicating with swtpm
|
||||
fn set_buffer_size(&mut self, wantedsize: usize, actualsize: &mut usize) -> Result<()> {
|
||||
let mut psbs: PtmSetBufferSize = PtmSetBufferSize::new(wantedsize as u32);
|
||||
|
||||
self.stop_tpm()?;
|
||||
|
||||
self.run_control_cmd(
|
||||
Commands::CmdSetBufferSize,
|
||||
&mut psbs,
|
||||
mem::size_of::<u32>(),
|
||||
4 * mem::size_of::<u32>(),
|
||||
)?;
|
||||
|
||||
*actualsize = psbs.get_bufsize() as usize;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn startup_tpm(&mut self, buffersize: usize) -> Result<()> {
|
||||
let mut init: PtmInit = PtmInit::new();
|
||||
|
||||
let mut actual_size: usize = 0;
|
||||
|
||||
if buffersize != 0 {
|
||||
self.set_buffer_size(buffersize, &mut actual_size)?;
|
||||
debug!("set tpm buffersize to {:?} during Startup", buffersize);
|
||||
}
|
||||
|
||||
self.run_control_cmd(
|
||||
Commands::CmdInit,
|
||||
&mut init,
|
||||
mem::size_of::<u32>(),
|
||||
mem::size_of::<u32>(),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stop_tpm(&mut self) -> Result<()> {
|
||||
let mut res: PtmResult = 0;
|
||||
|
||||
self.run_control_cmd(Commands::CmdStop, &mut res, 0, mem::size_of::<u32>())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_buffer_size(&mut self) -> Result<usize> {
|
||||
let mut curr_buf_size: usize = 0;
|
||||
|
||||
match self.set_buffer_size(0, &mut curr_buf_size) {
|
||||
Err(_) => Ok(TPM_CRB_BUFFER_MAX),
|
||||
_ => Ok(curr_buf_size),
|
||||
}
|
||||
}
|
||||
}
|
||||
440
tpm/src/lib.rs
Normal file
440
tpm/src/lib.rs
Normal file
@@ -0,0 +1,440 @@
|
||||
// Copyright © 2022, Microsoft Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod emulator;
|
||||
pub mod socket;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use std::convert::TryInto;
|
||||
use thiserror::Error;
|
||||
|
||||
pub const TPM_CRB_BUFFER_MAX: usize = 3968; // 0x1_000 - 0x80
|
||||
pub const TPM_SUCCESS: u32 = 0x0;
|
||||
|
||||
/*
|
||||
* Structures required to process Request and Responses of Control commands
|
||||
* used by control channel over UNIX socket interface
|
||||
*
|
||||
* All messages contain big-endian data.
|
||||
*
|
||||
* Reference: https://github.com/stefanberger/swtpm/blob/master/man/man3/swtpm_ioctls.pod
|
||||
*/
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Commands {
|
||||
CmdGetCapability = 1,
|
||||
CmdInit,
|
||||
CmdShutdown,
|
||||
CmdGetTpmEstablished,
|
||||
CmdSetLocality,
|
||||
CmdHashStart,
|
||||
CmdHashData,
|
||||
CmdHashEnd,
|
||||
CmdCancelTpmCmd,
|
||||
CmdStoreVolatile,
|
||||
CmdResetTpmEstablished,
|
||||
CmdGetStateBlob,
|
||||
CmdSetStateBlob,
|
||||
CmdStop,
|
||||
CmdGetConfig,
|
||||
CmdSetDatafd,
|
||||
CmdSetBufferSize,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Failed converting buf to PTM : {0}")]
|
||||
ConvertToPtm(#[source] anyhow::Error),
|
||||
}
|
||||
type Result<T> = anyhow::Result<T, Error>;
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum MemberType {
|
||||
Request,
|
||||
Response,
|
||||
Error,
|
||||
Cap,
|
||||
}
|
||||
|
||||
pub trait Ptm {
|
||||
// Get Member Type
|
||||
fn get_member_type(&self) -> MemberType;
|
||||
|
||||
// Set Member Type
|
||||
fn set_member_type(&mut self, mem: MemberType);
|
||||
|
||||
// Convert PTM Request to bytes to be sent to tpm
|
||||
fn ptm_to_request(&self) -> Vec<u8>;
|
||||
|
||||
// Update PTM from tpm's reponse
|
||||
fn update_ptm_with_response(&mut self, buf: &[u8]) -> Result<()>;
|
||||
|
||||
// Update tpm result
|
||||
fn set_result_code(&mut self, res: u32);
|
||||
|
||||
fn get_result_code(&self) -> u32;
|
||||
}
|
||||
|
||||
/*
|
||||
* Every response for a tpm Control Command execution must hold tpm return
|
||||
* code (PtmResult) as its first element.
|
||||
* Based on the type of input Control Command additional data could be
|
||||
* appended to the response.
|
||||
*/
|
||||
pub type PtmResult = u32;
|
||||
|
||||
impl Ptm for PtmResult {
|
||||
fn ptm_to_request(&self) -> Vec<u8> {
|
||||
let buf: Vec<u8> = Vec::<u8>::new();
|
||||
buf
|
||||
}
|
||||
|
||||
fn get_member_type(&self) -> MemberType {
|
||||
MemberType::Response
|
||||
}
|
||||
|
||||
fn update_ptm_with_response(&mut self, buf: &[u8]) -> Result<()> {
|
||||
if buf.len() < 4 {
|
||||
return Err(Error::ConvertToPtm(anyhow!(
|
||||
"PtmRes buffer is of insufficient length. Buffer length should be atleast 4"
|
||||
)));
|
||||
}
|
||||
|
||||
*self = u32::from_be_bytes(buf[0..4].try_into().unwrap());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_member_type(&mut self, _mem: MemberType) {}
|
||||
|
||||
fn set_result_code(&mut self, res: u32) {
|
||||
*self = res;
|
||||
}
|
||||
|
||||
fn get_result_code(&self) -> u32 {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
/* GET_CAPABILITY Response */
|
||||
pub type PtmCap = u64;
|
||||
impl Ptm for PtmCap {
|
||||
fn ptm_to_request(&self) -> Vec<u8> {
|
||||
// tpm's GetCapability call doesn't need any supporting message
|
||||
// return an empty Buffer
|
||||
let buf: Vec<u8> = Vec::<u8>::new();
|
||||
buf
|
||||
}
|
||||
|
||||
fn get_member_type(&self) -> MemberType {
|
||||
MemberType::Cap
|
||||
}
|
||||
|
||||
fn update_ptm_with_response(&mut self, mut buf: &[u8]) -> Result<()> {
|
||||
let buf_len = buf.len();
|
||||
if buf_len != 8 {
|
||||
return Err(Error::ConvertToPtm(anyhow!(
|
||||
"Response for GetCapability cmd is of incorrect length: {:?}. Response buffer should be 8 bytes long",
|
||||
buf_len)));
|
||||
}
|
||||
*self = buf.read_u64::<BigEndian>().unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_member_type(&mut self, _mem: MemberType) {}
|
||||
|
||||
fn set_result_code(&mut self, _res: u32) {}
|
||||
|
||||
fn get_result_code(&self) -> u32 {
|
||||
((*self) >> 32) as u32
|
||||
}
|
||||
}
|
||||
|
||||
/* GET_TPMESTABLISHED Reponse */
|
||||
#[derive(Debug)]
|
||||
pub struct PtmEstResp {
|
||||
pub bit: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PtmEst {
|
||||
member: MemberType,
|
||||
pub resp: PtmEstResp,
|
||||
pub result_code: PtmResult,
|
||||
}
|
||||
|
||||
impl PtmEst {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
member: MemberType::Response,
|
||||
result_code: 0,
|
||||
resp: PtmEstResp { bit: 0 },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PtmEst {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Ptm for PtmEst {
|
||||
fn ptm_to_request(&self) -> Vec<u8> {
|
||||
// tpm's GetTpmEstablished call doesn't need any supporting message
|
||||
// return an empty Buffer
|
||||
let buf: Vec<u8> = Vec::<u8>::new();
|
||||
buf
|
||||
}
|
||||
|
||||
fn get_member_type(&self) -> MemberType {
|
||||
self.member
|
||||
}
|
||||
|
||||
fn update_ptm_with_response(&mut self, buf: &[u8]) -> Result<()> {
|
||||
if buf.len() < 5 {
|
||||
return Err(Error::ConvertToPtm(anyhow!(
|
||||
"Response for GetTpmEstablished cmd is of incorrect length. Response buffer should be 5 bytes long"
|
||||
)));
|
||||
}
|
||||
let mut res = &buf[0..4];
|
||||
self.set_result_code(res.read_u32::<BigEndian>().unwrap());
|
||||
let bit = &buf[4];
|
||||
self.resp.bit = *bit;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_member_type(&mut self, _mem: MemberType) {}
|
||||
|
||||
fn set_result_code(&mut self, res: u32) {
|
||||
self.result_code = res
|
||||
}
|
||||
|
||||
fn get_result_code(&self) -> u32 {
|
||||
self.result_code
|
||||
}
|
||||
}
|
||||
|
||||
/* INIT Response */
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PtmInit {
|
||||
pub member: MemberType,
|
||||
/* request */
|
||||
pub init_flags: u32,
|
||||
/* response */
|
||||
pub result_code: PtmResult,
|
||||
}
|
||||
|
||||
impl Default for PtmInit {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl PtmInit {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
member: MemberType::Request,
|
||||
init_flags: 0,
|
||||
result_code: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ptm for PtmInit {
|
||||
fn ptm_to_request(&self) -> Vec<u8> {
|
||||
let mut buf: Vec<u8> = Vec::<u8>::new();
|
||||
buf.extend_from_slice(&self.init_flags.to_be_bytes());
|
||||
buf
|
||||
}
|
||||
|
||||
fn get_member_type(&self) -> MemberType {
|
||||
self.member
|
||||
}
|
||||
|
||||
fn update_ptm_with_response(&mut self, buf: &[u8]) -> Result<()> {
|
||||
if buf.len() != 4 {
|
||||
return Err(Error::ConvertToPtm(anyhow!(
|
||||
"Response for Init cmd is of incorrect length. Response buffer should be 4 bytes long"
|
||||
)));
|
||||
}
|
||||
self.set_member_type(MemberType::Response);
|
||||
let mut res = &buf[0..4];
|
||||
self.set_result_code(res.read_u32::<BigEndian>().unwrap());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_member_type(&mut self, mem: MemberType) {
|
||||
self.member = mem
|
||||
}
|
||||
|
||||
fn set_result_code(&mut self, res: u32) {
|
||||
self.result_code = res
|
||||
}
|
||||
|
||||
fn get_result_code(&self) -> u32 {
|
||||
self.result_code
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* PTM_SET_BUFFERSIZE: Set the buffer size to be used by the tpm.
|
||||
* A 0 on input queries for the current buffer size. Any other
|
||||
* number will try to set the buffer size. The returned number is
|
||||
* the buffer size that will be used, which can be larger than the
|
||||
* requested one, if it was below the minimum, or smaller than the
|
||||
* requested one, if it was above the maximum.
|
||||
*
|
||||
* SET_BUFFERSIZE Response
|
||||
*/
|
||||
#[derive(Debug)]
|
||||
pub struct PtmSBSReq {
|
||||
buffersize: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PtmSBSResp {
|
||||
bufsize: u32,
|
||||
minsize: u32,
|
||||
maxsize: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PtmSetBufferSize {
|
||||
pub mem: MemberType,
|
||||
/* request */
|
||||
pub req: PtmSBSReq,
|
||||
/* response */
|
||||
pub resp: PtmSBSResp,
|
||||
pub result_code: PtmResult,
|
||||
}
|
||||
|
||||
impl PtmSetBufferSize {
|
||||
pub fn new(req_buffsize: u32) -> Self {
|
||||
Self {
|
||||
mem: MemberType::Request,
|
||||
req: PtmSBSReq {
|
||||
buffersize: req_buffsize,
|
||||
},
|
||||
resp: PtmSBSResp {
|
||||
bufsize: 0,
|
||||
minsize: 0,
|
||||
maxsize: 0,
|
||||
},
|
||||
result_code: 0,
|
||||
}
|
||||
}
|
||||
pub fn get_bufsize(&self) -> u32 {
|
||||
self.resp.bufsize
|
||||
}
|
||||
}
|
||||
|
||||
impl Ptm for PtmSetBufferSize {
|
||||
fn ptm_to_request(&self) -> Vec<u8> {
|
||||
let mut buf: Vec<u8> = Vec::<u8>::new();
|
||||
buf.extend_from_slice(&self.req.buffersize.to_be_bytes());
|
||||
buf
|
||||
}
|
||||
|
||||
fn get_member_type(&self) -> MemberType {
|
||||
self.mem
|
||||
}
|
||||
|
||||
fn update_ptm_with_response(&mut self, buf: &[u8]) -> Result<()> {
|
||||
if buf.len() != 16 {
|
||||
return Err(Error::ConvertToPtm(anyhow!(
|
||||
"Response for CmdSetBufferSize cmd is of incorrect length. Response buffer should be 16 bytes long"
|
||||
)));
|
||||
}
|
||||
self.set_member_type(MemberType::Response);
|
||||
let mut res = &buf[0..4];
|
||||
self.set_result_code(res.read_u32::<BigEndian>().unwrap());
|
||||
|
||||
let mut bufsize = &buf[4..8];
|
||||
self.resp.bufsize = bufsize.read_u32::<BigEndian>().unwrap();
|
||||
|
||||
let mut minsize = &buf[8..12];
|
||||
self.resp.minsize = minsize.read_u32::<BigEndian>().unwrap();
|
||||
|
||||
let mut maxsize = &buf[12..16];
|
||||
self.resp.maxsize = maxsize.read_u32::<BigEndian>().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_member_type(&mut self, mem: MemberType) {
|
||||
self.mem = mem
|
||||
}
|
||||
|
||||
fn set_result_code(&mut self, res: u32) {
|
||||
self.result_code = res
|
||||
}
|
||||
|
||||
fn get_result_code(&self) -> u32 {
|
||||
self.result_code
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_ptmresult() -> Result<()> {
|
||||
let buf: &[u8] = &[0, 0, 0, 1];
|
||||
let mut result_code: PtmResult = 0;
|
||||
result_code.update_ptm_with_response(buf)?;
|
||||
assert_eq!(result_code.get_result_code(), 0x1);
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn test_ptmcap() -> Result<()> {
|
||||
let mut cap: PtmCap = 0x0;
|
||||
let buf: &[u8] = &[0, 0, 0, 0xE, 0, 0, 0xFF, 0xFF];
|
||||
cap.update_ptm_with_response(buf)?;
|
||||
assert_eq!(cap.get_result_code(), 0xE);
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn test_ptmest() -> Result<()> {
|
||||
let mut est: PtmEst = PtmEst::new();
|
||||
let buf: &[u8] = &[0, 0, 0xE, 0, 0xC, 0, 1, 1];
|
||||
est.update_ptm_with_response(buf)?;
|
||||
assert_eq!(est.get_result_code(), 0xE00);
|
||||
assert_eq!(est.resp.bit, 0xC);
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
/*PtmInit Testing */
|
||||
fn test_ptminit() -> Result<()> {
|
||||
let mut init: PtmInit = PtmInit::new();
|
||||
init.init_flags = 0x1;
|
||||
let buf = init.ptm_to_request();
|
||||
assert_eq!(buf, [0x0, 0x0, 0x0, 0x1]);
|
||||
let response_buf: &[u8] = &[0, 0, 0xE, 0];
|
||||
init.update_ptm_with_response(response_buf)?;
|
||||
assert_eq!(init.get_result_code(), 0xE00);
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
/* PtmSetBufferSize Testing */
|
||||
fn test_ptmsetbuffersize() -> Result<()> {
|
||||
let mut psbs: PtmSetBufferSize = PtmSetBufferSize::new(1024);
|
||||
// Member type should be Request after initialization
|
||||
assert_eq!(psbs.get_member_type(), MemberType::Request);
|
||||
let buf: &[u8] = &[
|
||||
0, 0x12, 0x34, 0x56, 0, 0, 0, 0xA, 0, 0, 0, 0xB, 0, 0, 0, 0xC,
|
||||
];
|
||||
psbs.update_ptm_with_response(buf)?;
|
||||
assert_eq!(psbs.get_member_type(), MemberType::Response);
|
||||
assert_eq!(psbs.get_result_code(), 0x123456);
|
||||
assert_eq!(psbs.resp.bufsize, 0xA);
|
||||
assert_eq!(psbs.resp.minsize, 0xB);
|
||||
assert_eq!(psbs.resp.maxsize, 0xC);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
144
tpm/src/socket.rs
Normal file
144
tpm/src/socket.rs
Normal file
@@ -0,0 +1,144 @@
|
||||
// Copyright © 2022, Microsoft Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use crate::TPM_CRB_BUFFER_MAX;
|
||||
use anyhow::anyhow;
|
||||
use std::io::Read;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use thiserror::Error;
|
||||
use vmm_sys_util::sock_ctrl_msg::ScmSocket;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Cannot connect to tpm Socket: {0}")]
|
||||
ConnectToSocket(#[source] anyhow::Error),
|
||||
#[error("Failed to read from socket: {0}")]
|
||||
ReadFromSocket(#[source] anyhow::Error),
|
||||
#[error("Failed to write to socket: {0}")]
|
||||
WriteToSocket(#[source] anyhow::Error),
|
||||
}
|
||||
type Result<T> = anyhow::Result<T, Error>;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum SocketDevState {
|
||||
Disconnected,
|
||||
Connecting,
|
||||
Connected,
|
||||
}
|
||||
|
||||
pub struct SocketDev {
|
||||
state: SocketDevState,
|
||||
stream: Option<UnixStream>,
|
||||
// Fd sent to swtpm process for Data Channel
|
||||
write_msgfd: RawFd,
|
||||
// Data Channel used by Cloud-Hypervisor
|
||||
data_fd: RawFd,
|
||||
// Control Channel used by Cloud-Hypervisor
|
||||
control_fd: RawFd,
|
||||
}
|
||||
|
||||
impl Default for SocketDev {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl SocketDev {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
state: SocketDevState::Disconnected,
|
||||
stream: None,
|
||||
write_msgfd: -1,
|
||||
control_fd: -1,
|
||||
data_fd: -1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(&mut self, path: String) -> Result<()> {
|
||||
self.connect(&path)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn connect(&mut self, socket_path: &str) -> Result<()> {
|
||||
self.state = SocketDevState::Connecting;
|
||||
|
||||
let s = UnixStream::connect(socket_path).map_err(|e| {
|
||||
Error::ConnectToSocket(anyhow!("Failed to connect to tpm Socket. Error: {:?}", e))
|
||||
})?;
|
||||
self.control_fd = s.as_raw_fd();
|
||||
self.stream = Some(s);
|
||||
self.state = SocketDevState::Connected;
|
||||
debug!("Connected to tpm socket path : {:?}", socket_path);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_datafd(&mut self, fd: RawFd) {
|
||||
self.data_fd = fd;
|
||||
}
|
||||
|
||||
pub fn set_msgfd(&mut self, fd: RawFd) {
|
||||
self.write_msgfd = fd;
|
||||
}
|
||||
|
||||
pub fn send_full(&self, buf: &[u8]) -> Result<usize> {
|
||||
let write_fd = self.write_msgfd;
|
||||
|
||||
let size = self
|
||||
.stream
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.send_with_fd(buf, write_fd)
|
||||
.map_err(|e| {
|
||||
Error::WriteToSocket(anyhow!("Failed to write to Socket. Error: {:?}", e))
|
||||
})?;
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
|
||||
pub fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
if self.stream.is_none() {
|
||||
return Err(Error::WriteToSocket(anyhow!(
|
||||
"Stream for tpm socket was not initialized"
|
||||
)));
|
||||
}
|
||||
|
||||
if matches!(self.state, SocketDevState::Connected) {
|
||||
let ret = self.send_full(buf)?;
|
||||
// swtpm will receive data Fd after a successful send
|
||||
// Reset cached write_msgfd after a successful send
|
||||
// Ideally, write_msgfd is reset after first Ctrl Command
|
||||
if ret > 0 && self.write_msgfd != 0 {
|
||||
self.write_msgfd = 0;
|
||||
}
|
||||
Ok(ret)
|
||||
} else {
|
||||
Err(Error::WriteToSocket(anyhow!(
|
||||
"TPM Socket was not in Connected State"
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
let newbuf: &mut [u8] = &mut [0; TPM_CRB_BUFFER_MAX];
|
||||
|
||||
if self.stream.is_none() {
|
||||
return Err(Error::ReadFromSocket(anyhow!(
|
||||
"Stream for tpm socket was not initialized"
|
||||
)));
|
||||
}
|
||||
let mut socket = self.stream.as_ref().unwrap();
|
||||
let size: usize = socket.read(newbuf).map_err(|e| {
|
||||
Error::ReadFromSocket(anyhow!("Failed to read from socket. Error Code {:?}", e))
|
||||
})?;
|
||||
if buf.len() < size {
|
||||
return Err(Error::ReadFromSocket(anyhow!(
|
||||
"Input buffer is of insufficient size"
|
||||
)));
|
||||
}
|
||||
buf[0..size].clone_from_slice(&newbuf[0..size]);
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,11 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.126"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
once_cell = "1.12.0"
|
||||
serde = { version = "1.0.145", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.81"
|
||||
once_cell = "1.16.0"
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.89"
|
||||
|
||||
[features]
|
||||
tracing = []
|
||||
|
||||
@@ -52,9 +52,10 @@ impl Tracer {
|
||||
}
|
||||
|
||||
fn add_event(&mut self, event: TraceEvent) {
|
||||
let thread_name = std::thread::current().name().unwrap_or("").to_string();
|
||||
let current = std::thread::current();
|
||||
let thread_name = current.name().unwrap_or("");
|
||||
let mut events = self.events.lock().unwrap();
|
||||
if let Some(thread_events) = events.get_mut(&thread_name) {
|
||||
if let Some(thread_events) = events.get_mut(thread_name) {
|
||||
thread_events.push(event);
|
||||
} else {
|
||||
events.insert(thread_name.to_string(), vec![event]);
|
||||
@@ -62,18 +63,20 @@ impl Tracer {
|
||||
}
|
||||
|
||||
fn increase_thread_depth(&mut self) {
|
||||
let thread_name = std::thread::current().name().unwrap_or("").to_string();
|
||||
if let Some(depth) = self.thread_depths.get_mut(&thread_name) {
|
||||
let current = std::thread::current();
|
||||
let thread_name = current.name().unwrap_or("");
|
||||
if let Some(depth) = self.thread_depths.get_mut(thread_name) {
|
||||
depth.fetch_add(1, Ordering::SeqCst);
|
||||
} else {
|
||||
self.thread_depths
|
||||
.insert(thread_name, Arc::new(AtomicU64::new(0)));
|
||||
.insert(thread_name.to_string(), Arc::new(AtomicU64::new(0)));
|
||||
}
|
||||
}
|
||||
|
||||
fn decrease_thread_depth(&mut self) {
|
||||
let thread_name = std::thread::current().name().unwrap_or("").to_string();
|
||||
if let Some(depth) = self.thread_depths.get_mut(&thread_name) {
|
||||
let current = std::thread::current();
|
||||
let thread_name = current.name().unwrap_or("");
|
||||
if let Some(depth) = self.thread_depths.get_mut(thread_name) {
|
||||
depth.fetch_sub(1, Ordering::SeqCst);
|
||||
} else {
|
||||
panic!("Unmatched decreas for thread: {}", thread_name);
|
||||
@@ -81,9 +84,10 @@ impl Tracer {
|
||||
}
|
||||
|
||||
fn thread_depth(&self) -> u64 {
|
||||
let thread_name = std::thread::current().name().unwrap_or("").to_string();
|
||||
let current = std::thread::current();
|
||||
let thread_name = current.name().unwrap_or("");
|
||||
self.thread_depths
|
||||
.get(&thread_name)
|
||||
.get(thread_name)
|
||||
.map(|v| v.load(Ordering::SeqCst))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -5,15 +5,13 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.133"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
serde = { version = "1.0.145", features = ["rc"] }
|
||||
serde_derive = "1.0.142"
|
||||
serde_json = "1.0.85"
|
||||
serde = { version = "1.0.150", features = ["rc"] }
|
||||
serde_derive = "1.0.150"
|
||||
serde_json = "1.0.89"
|
||||
thiserror = "1.0.37"
|
||||
vm-memory = { version = "0.9.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = "0.10.0"
|
||||
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main", features = ["fam-wrappers"] }
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
[dependencies.vfio-bindings]
|
||||
version = "0.3.1"
|
||||
features = ["fam-wrappers"]
|
||||
|
||||
@@ -8,8 +8,8 @@ license = "Apache-2.0"
|
||||
[dependencies]
|
||||
byteorder = "1.4.3"
|
||||
crc32c = "0.6.3"
|
||||
libc = "0.2.133"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
remain = "0.2.4"
|
||||
remain = "0.2.5"
|
||||
thiserror = "1.0.37"
|
||||
uuid = { version = "1.1.2", features = ["v4"] }
|
||||
uuid = { version = "1.2.2", features = ["v4"] }
|
||||
|
||||
@@ -6,19 +6,19 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
block_util = { path = "../block_util" }
|
||||
clap = { version = "3.2.22", features = ["wrap_help","cargo"] }
|
||||
env_logger = "0.9.1"
|
||||
clap = { version = "4.0.29", features = ["wrap_help","cargo"] }
|
||||
env_logger = "0.10.0"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.133"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
option_parser = { path = "../option_parser" }
|
||||
qcow = { path = "../qcow" }
|
||||
vhost = { version = "0.5.0", features = ["vhost-user-slave"] }
|
||||
vhost-user-backend = "0.7.0"
|
||||
vhost = { version = "0.6.0", features = ["vhost-user-slave"] }
|
||||
vhost-user-backend = "0.8.0"
|
||||
virtio-bindings = "0.1.0"
|
||||
virtio-queue = "0.6.1"
|
||||
vm-memory = "0.9.0"
|
||||
vmm-sys-util = "0.10.0"
|
||||
virtio-queue = "0.7.0"
|
||||
vm-memory = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.2.22", features = ["cargo"] }
|
||||
clap = { version = "4.0.29", features = ["cargo"] }
|
||||
|
||||
@@ -234,7 +234,7 @@ impl VhostUserBlkBackend {
|
||||
}
|
||||
};
|
||||
|
||||
let nsectors = (image.lock().unwrap().seek(SeekFrom::End(0)).unwrap() as u64) / SECTOR_SIZE;
|
||||
let nsectors = (image.lock().unwrap().seek(SeekFrom::End(0)).unwrap()) / SECTOR_SIZE;
|
||||
let config = VirtioBlockConfig {
|
||||
capacity: nsectors,
|
||||
blk_size: BLK_SIZE,
|
||||
@@ -305,7 +305,7 @@ impl VhostUserBackendMut<VringRwLock<GuestMemoryAtomic<GuestMemoryMmap>>, Atomic
|
||||
}
|
||||
|
||||
fn max_queue_size(&self) -> usize {
|
||||
self.queue_size as usize
|
||||
self.queue_size
|
||||
}
|
||||
|
||||
fn features(&self) -> u64 {
|
||||
|
||||
@@ -26,11 +26,11 @@ fn main() {
|
||||
Arg::new("block-backend")
|
||||
.long("block-backend")
|
||||
.help(vhost_user_block::SYNTAX)
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.required(true),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let backend_command = cmd_arguments.value_of("block-backend").unwrap();
|
||||
let backend_command = cmd_arguments.get_one::<String>("block-backend").unwrap();
|
||||
start_block_backend(backend_command);
|
||||
}
|
||||
|
||||
@@ -5,18 +5,18 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.2.22", features = ["wrap_help","cargo"] }
|
||||
env_logger = "0.9.1"
|
||||
clap = { version = "4.0.29", features = ["wrap_help","cargo"] }
|
||||
env_logger = "0.10.0"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.133"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
net_util = { path = "../net_util" }
|
||||
option_parser = { path = "../option_parser" }
|
||||
vhost = { version = "0.5.0", features = ["vhost-user-slave"] }
|
||||
vhost-user-backend = "0.7.0"
|
||||
vhost = { version = "0.6.0", features = ["vhost-user-slave"] }
|
||||
vhost-user-backend = "0.8.0"
|
||||
virtio-bindings = "0.1.0"
|
||||
vm-memory = "0.9.0"
|
||||
vmm-sys-util = "0.10.0"
|
||||
vm-memory = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.2.22", features = ["cargo"] }
|
||||
clap = { version = "4.0.29", features = ["cargo"] }
|
||||
|
||||
@@ -23,11 +23,11 @@ fn main() {
|
||||
Arg::new("net-backend")
|
||||
.long("net-backend")
|
||||
.help(vhost_user_net::SYNTAX)
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.required(true),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let backend_command = cmd_arguments.value_of("net-backend").unwrap();
|
||||
let backend_command = cmd_arguments.get_one::<String>("net-backend").unwrap();
|
||||
start_net_backend(backend_command);
|
||||
}
|
||||
|
||||
@@ -8,32 +8,32 @@ edition = "2021"
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.65"
|
||||
anyhow = "1.0.66"
|
||||
arc-swap = "1.5.1"
|
||||
block_util = { path = "../block_util" }
|
||||
byteorder = "1.4.3"
|
||||
epoll = "4.3.1"
|
||||
event_monitor = { path = "../event_monitor" }
|
||||
io-uring = "0.5.6"
|
||||
libc = "0.2.133"
|
||||
io-uring = "0.5.9"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
net_gen = { path = "../net_gen" }
|
||||
net_util = { path = "../net_util" }
|
||||
pci = { path = "../pci" }
|
||||
rate_limiter = { path = "../rate_limiter" }
|
||||
seccompiler = "0.3.0"
|
||||
serde = { version = "1.0.145", features = ["derive"] }
|
||||
serde_json = "1.0.85"
|
||||
serde = { version = "1.0.150", features = ["derive"] }
|
||||
serde_json = "1.0.89"
|
||||
serial_buffer = { path = "../serial_buffer" }
|
||||
thiserror = "1.0.37"
|
||||
versionize = "0.1.6"
|
||||
versionize = "0.1.9"
|
||||
versionize_derive = "0.1.4"
|
||||
vhost = { version = "0.5.0", features = ["vhost-user-master", "vhost-user-slave", "vhost-kern", "vhost-vdpa"] }
|
||||
vhost = { version = "0.6.0", features = ["vhost-user-master", "vhost-user-slave", "vhost-kern", "vhost-vdpa"] }
|
||||
virtio-bindings = { version = "0.1.0", features = ["virtio-v5_0_0"] }
|
||||
virtio-queue = "0.6.1"
|
||||
virtio-queue = "0.7.0"
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = { version = "0.9.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
@@ -62,9 +62,9 @@ const VIRTIO_BALLOON_F_REPORTING: u64 = 5;
|
||||
pub enum Error {
|
||||
#[error("Guest gave us bad memory addresses.: {0}")]
|
||||
GuestMemory(GuestMemoryError),
|
||||
#[error("Guest gave us a write only descriptor that protocol says to read from.")]
|
||||
#[error("Guest gave us a write only descriptor that protocol says to read from")]
|
||||
UnexpectedWriteOnlyDescriptor,
|
||||
#[error("Guest sent us invalid request.")]
|
||||
#[error("Guest sent us invalid request")]
|
||||
InvalidRequest,
|
||||
#[error("Fallocate fail.: {0}")]
|
||||
FallocateFail(std::io::Error),
|
||||
@@ -151,7 +151,7 @@ impl BalloonEpollHandler {
|
||||
libc::fallocate64(
|
||||
f_off.file().as_raw_fd(),
|
||||
libc::FALLOC_FL_PUNCH_HOLE | libc::FALLOC_FL_KEEP_SIZE,
|
||||
(offset as u64 + f_off.start()) as libc::off64_t,
|
||||
(offset + f_off.start()) as libc::off64_t,
|
||||
range_len as libc::off64_t,
|
||||
)
|
||||
};
|
||||
@@ -360,26 +360,39 @@ impl Balloon {
|
||||
free_page_reporting: bool,
|
||||
seccomp_action: SeccompAction,
|
||||
exit_evt: EventFd,
|
||||
state: Option<BalloonState>,
|
||||
) -> io::Result<Self> {
|
||||
let mut queue_sizes = vec![QUEUE_SIZE; MIN_NUM_QUEUES];
|
||||
let mut avail_features = 1u64 << VIRTIO_F_VERSION_1;
|
||||
if deflate_on_oom {
|
||||
avail_features |= 1u64 << VIRTIO_BALLOON_F_DEFLATE_ON_OOM;
|
||||
}
|
||||
|
||||
let (avail_features, acked_features, config) = if let Some(state) = state {
|
||||
info!("Restoring virtio-balloon {}", id);
|
||||
(state.avail_features, state.acked_features, state.config)
|
||||
} else {
|
||||
let mut avail_features = 1u64 << VIRTIO_F_VERSION_1;
|
||||
if deflate_on_oom {
|
||||
avail_features |= 1u64 << VIRTIO_BALLOON_F_DEFLATE_ON_OOM;
|
||||
}
|
||||
if free_page_reporting {
|
||||
avail_features |= 1u64 << VIRTIO_BALLOON_F_REPORTING;
|
||||
}
|
||||
|
||||
let config = VirtioBalloonConfig {
|
||||
num_pages: (size >> VIRTIO_BALLOON_PFN_SHIFT) as u32,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
(avail_features, 0, config)
|
||||
};
|
||||
|
||||
if free_page_reporting {
|
||||
avail_features |= 1u64 << VIRTIO_BALLOON_F_REPORTING;
|
||||
queue_sizes.push(REPORTING_QUEUE_SIZE);
|
||||
}
|
||||
|
||||
let config = VirtioBalloonConfig {
|
||||
num_pages: (size >> VIRTIO_BALLOON_PFN_SHIFT) as u32,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(Balloon {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Balloon as u32,
|
||||
avail_features,
|
||||
acked_features,
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
queue_sizes,
|
||||
min_queues: MIN_NUM_QUEUES as u16,
|
||||
@@ -418,12 +431,6 @@ impl Balloon {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &BalloonState) {
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
self.config = state.config;
|
||||
}
|
||||
|
||||
#[cfg(fuzzing)]
|
||||
pub fn wait_for_epoll_threads(&mut self) {
|
||||
self.common.wait_for_epoll_threads();
|
||||
@@ -573,11 +580,6 @@ impl Snapshottable for Balloon {
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
Snapshot::new_from_versioned_state(&self.id(), &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for Balloon {}
|
||||
impl Migratable for Balloon {}
|
||||
|
||||
@@ -103,6 +103,7 @@ struct BlockEpollHandler {
|
||||
request_list: HashMap<u16, Request>,
|
||||
rate_limiter: Option<RateLimiter>,
|
||||
access_platform: Option<Arc<dyn AccessPlatform>>,
|
||||
read_only: bool,
|
||||
}
|
||||
|
||||
impl BlockEpollHandler {
|
||||
@@ -115,6 +116,27 @@ impl BlockEpollHandler {
|
||||
let mut request = Request::parse(&mut desc_chain, self.access_platform.as_ref())
|
||||
.map_err(Error::RequestParsing)?;
|
||||
|
||||
// For virtio spec compliance
|
||||
// "A device MUST set the status byte to VIRTIO_BLK_S_IOERR for a write request
|
||||
// if the VIRTIO_BLK_F_RO feature if offered, and MUST NOT write any data."
|
||||
if self.read_only
|
||||
&& (request.request_type == RequestType::Out
|
||||
|| request.request_type == RequestType::Flush)
|
||||
{
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_obj(VIRTIO_BLK_S_IOERR, request.status_addr)
|
||||
.map_err(Error::RequestStatus)?;
|
||||
|
||||
// If no asynchronous operation has been submitted, we can
|
||||
// simply return the used descriptor.
|
||||
queue
|
||||
.add_used(desc_chain.memory(), desc_chain.head_index(), 0)
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
used_descs = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(rate_limiter) = &mut self.rate_limiter {
|
||||
// If limiter.consume() fails it means there is no more TokenType::Ops
|
||||
// budget and rate limiting is in effect.
|
||||
@@ -219,7 +241,8 @@ impl BlockEpollHandler {
|
||||
(VIRTIO_BLK_S_OK, result as u32)
|
||||
} else {
|
||||
error!(
|
||||
"Request failed: {:?}",
|
||||
"Request failed: {:x?} {:?}",
|
||||
request,
|
||||
io::Error::from_raw_os_error(-result)
|
||||
);
|
||||
return Err(Error::AsyncRequestFailure);
|
||||
@@ -229,7 +252,7 @@ impl BlockEpollHandler {
|
||||
.map_err(Error::RequestStatus)?;
|
||||
|
||||
queue
|
||||
.add_used(mem.deref(), desc_index as u16, len)
|
||||
.add_used(mem.deref(), desc_index, len)
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
used_descs = true;
|
||||
}
|
||||
@@ -389,6 +412,7 @@ pub struct Block {
|
||||
seccomp_action: SeccompAction,
|
||||
rate_limiter_config: Option<RateLimiterConfig>,
|
||||
exit_evt: EventFd,
|
||||
read_only: bool,
|
||||
}
|
||||
|
||||
#[derive(Versionize)]
|
||||
@@ -409,79 +433,93 @@ impl Block {
|
||||
id: String,
|
||||
mut disk_image: Box<dyn DiskFile>,
|
||||
disk_path: PathBuf,
|
||||
is_disk_read_only: bool,
|
||||
read_only: bool,
|
||||
iommu: bool,
|
||||
num_queues: usize,
|
||||
queue_size: u16,
|
||||
seccomp_action: SeccompAction,
|
||||
rate_limiter_config: Option<RateLimiterConfig>,
|
||||
exit_evt: EventFd,
|
||||
state: Option<BlockState>,
|
||||
) -> io::Result<Self> {
|
||||
let disk_size = disk_image.size().map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("Failed getting disk size: {}", e),
|
||||
let (disk_nsectors, avail_features, acked_features, config) = if let Some(state) = state {
|
||||
info!("Restoring virtio-block {}", id);
|
||||
(
|
||||
state.disk_nsectors,
|
||||
state.avail_features,
|
||||
state.acked_features,
|
||||
state.config,
|
||||
)
|
||||
})?;
|
||||
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)
|
||||
| (1u64 << VIRTIO_BLK_F_BLK_SIZE)
|
||||
| (1u64 << VIRTIO_BLK_F_TOPOLOGY);
|
||||
|
||||
if iommu {
|
||||
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
|
||||
}
|
||||
|
||||
if is_disk_read_only {
|
||||
avail_features |= 1u64 << VIRTIO_BLK_F_RO;
|
||||
}
|
||||
|
||||
let topology = disk_image.topology();
|
||||
info!("Disk topology: {:?}", topology);
|
||||
|
||||
let logical_block_size = if topology.logical_block_size > 512 {
|
||||
topology.logical_block_size
|
||||
} else {
|
||||
512
|
||||
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 {}; \
|
||||
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)
|
||||
| (1u64 << VIRTIO_BLK_F_BLK_SIZE)
|
||||
| (1u64 << VIRTIO_BLK_F_TOPOLOGY);
|
||||
|
||||
if iommu {
|
||||
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
|
||||
}
|
||||
|
||||
if read_only {
|
||||
avail_features |= 1u64 << VIRTIO_BLK_F_RO;
|
||||
}
|
||||
|
||||
let topology = disk_image.topology();
|
||||
info!("Disk topology: {:?}", topology);
|
||||
|
||||
let logical_block_size = if topology.logical_block_size > 512 {
|
||||
topology.logical_block_size
|
||||
} else {
|
||||
512
|
||||
};
|
||||
|
||||
// Calculate the exponent that maps physical block to logical block
|
||||
let mut physical_block_exp = 0;
|
||||
let mut size = logical_block_size;
|
||||
while size < topology.physical_block_size {
|
||||
physical_block_exp += 1;
|
||||
size <<= 1;
|
||||
}
|
||||
|
||||
let disk_nsectors = disk_size / SECTOR_SIZE;
|
||||
let mut config = VirtioBlockConfig {
|
||||
capacity: disk_nsectors,
|
||||
writeback: 1,
|
||||
blk_size: topology.logical_block_size as u32,
|
||||
physical_block_exp,
|
||||
min_io_size: (topology.minimum_io_size / logical_block_size) as u16,
|
||||
opt_io_size: (topology.optimal_io_size / logical_block_size) as u32,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if num_queues > 1 {
|
||||
avail_features |= 1u64 << VIRTIO_BLK_F_MQ;
|
||||
config.num_queues = num_queues as u16;
|
||||
}
|
||||
|
||||
(disk_nsectors, avail_features, 0, config)
|
||||
};
|
||||
|
||||
// Calculate the exponent that maps physical block to logical block
|
||||
let mut physical_block_exp = 0;
|
||||
let mut size = logical_block_size;
|
||||
while size < topology.physical_block_size {
|
||||
physical_block_exp += 1;
|
||||
size <<= 1;
|
||||
}
|
||||
|
||||
let disk_nsectors = disk_size / SECTOR_SIZE;
|
||||
let mut config = VirtioBlockConfig {
|
||||
capacity: disk_nsectors,
|
||||
writeback: 1,
|
||||
blk_size: topology.logical_block_size as u32,
|
||||
physical_block_exp,
|
||||
min_io_size: (topology.minimum_io_size / logical_block_size) as u16,
|
||||
opt_io_size: (topology.optimal_io_size / logical_block_size) as u32,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if num_queues > 1 {
|
||||
avail_features |= 1u64 << VIRTIO_BLK_F_MQ;
|
||||
config.num_queues = num_queues as u16;
|
||||
}
|
||||
|
||||
Ok(Block {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Block as u32,
|
||||
avail_features,
|
||||
acked_features,
|
||||
paused_sync: Some(Arc::new(Barrier::new(num_queues + 1))),
|
||||
queue_sizes: vec![queue_size; num_queues],
|
||||
min_queues: 1,
|
||||
@@ -497,6 +535,7 @@ impl Block {
|
||||
seccomp_action,
|
||||
rate_limiter_config,
|
||||
exit_evt,
|
||||
read_only,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -510,14 +549,6 @@ impl Block {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &BlockState) {
|
||||
self.disk_path = state.disk_path.clone().into();
|
||||
self.disk_nsectors = state.disk_nsectors;
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
self.config = state.config;
|
||||
}
|
||||
|
||||
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()) {
|
||||
@@ -637,6 +668,7 @@ impl VirtioDevice for Block {
|
||||
request_list: HashMap::with_capacity(queue_size.into()),
|
||||
rate_limiter,
|
||||
access_platform: self.common.access_platform.clone(),
|
||||
read_only: self.read_only,
|
||||
};
|
||||
|
||||
let paused = self.common.paused.clone();
|
||||
@@ -710,11 +742,6 @@ impl Snapshottable for Block {
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
Snapshot::new_from_versioned_state(&self.id(), &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for Block {}
|
||||
impl Migratable for Block {}
|
||||
|
||||
@@ -24,9 +24,10 @@ use std::os::unix::io::AsRawFd;
|
||||
use std::result;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use thiserror::Error;
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use virtio_queue::{Queue, QueueOwnedT, QueueT};
|
||||
use virtio_queue::{Queue, QueueT};
|
||||
use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic};
|
||||
use vm_migration::VersionMapped;
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||
@@ -40,18 +41,30 @@ const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES];
|
||||
// New descriptors are pending on the virtio queue.
|
||||
const INPUT_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
|
||||
const OUTPUT_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
|
||||
// Some input from the VMM is ready to be injected into the VM.
|
||||
const INPUT_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 3;
|
||||
// Console configuration change event is triggered.
|
||||
const CONFIG_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 4;
|
||||
const CONFIG_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 3;
|
||||
// File written to (input ready)
|
||||
const FILE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 5;
|
||||
const FILE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 4;
|
||||
// Console resized
|
||||
const RESIZE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 6;
|
||||
const RESIZE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 5;
|
||||
|
||||
//Console size feature bit
|
||||
const VIRTIO_CONSOLE_F_SIZE: u64 = 0;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum Error {
|
||||
#[error("Descriptor chain too short")]
|
||||
DescriptorChainTooShort,
|
||||
#[error("Failed to read from guest memory: {0}")]
|
||||
GuestMemoryRead(vm_memory::guest_memory::Error),
|
||||
#[error("Failed to write to guest memory: {0}")]
|
||||
GuestMemoryWrite(vm_memory::guest_memory::Error),
|
||||
#[error("Failed to flush output: {0}")]
|
||||
OutputFlush(io::Error),
|
||||
#[error("Failed to add used index: {0}")]
|
||||
QueueAddUsed(virtio_queue::Error),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Versionize)]
|
||||
#[repr(C, packed)]
|
||||
pub struct VirtioConsoleConfig {
|
||||
@@ -77,14 +90,14 @@ unsafe impl ByteValued for VirtioConsoleConfig {}
|
||||
|
||||
struct ConsoleEpollHandler {
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
queues: Vec<Queue>,
|
||||
input_queue: Queue,
|
||||
output_queue: Queue,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
in_buffer: Arc<Mutex<VecDeque<u8>>>,
|
||||
resizer: Arc<ConsoleResizer>,
|
||||
endpoint: Endpoint,
|
||||
input_queue_evt: EventFd,
|
||||
output_queue_evt: EventFd,
|
||||
input_evt: EventFd,
|
||||
config_evt: EventFd,
|
||||
resize_pipe: Option<File>,
|
||||
kill_evt: EventFd,
|
||||
@@ -145,14 +158,14 @@ impl ConsoleEpollHandler {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn new(
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
queues: Vec<Queue>,
|
||||
input_queue: Queue,
|
||||
output_queue: Queue,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
in_buffer: Arc<Mutex<VecDeque<u8>>>,
|
||||
resizer: Arc<ConsoleResizer>,
|
||||
endpoint: Endpoint,
|
||||
input_queue_evt: EventFd,
|
||||
output_queue_evt: EventFd,
|
||||
input_evt: EventFd,
|
||||
config_evt: EventFd,
|
||||
resize_pipe: Option<File>,
|
||||
kill_evt: EventFd,
|
||||
@@ -176,14 +189,14 @@ impl ConsoleEpollHandler {
|
||||
|
||||
ConsoleEpollHandler {
|
||||
mem,
|
||||
queues,
|
||||
input_queue,
|
||||
output_queue,
|
||||
interrupt_cb,
|
||||
in_buffer,
|
||||
resizer,
|
||||
endpoint,
|
||||
input_queue_evt,
|
||||
output_queue_evt,
|
||||
input_evt,
|
||||
config_evt,
|
||||
resize_pipe,
|
||||
kill_evt,
|
||||
@@ -201,33 +214,32 @@ impl ConsoleEpollHandler {
|
||||
* driver in the receive queue for incoming data. Here,
|
||||
* we place the input data to these empty buffers.
|
||||
*/
|
||||
fn process_input_queue(&mut self) -> bool {
|
||||
fn process_input_queue(&mut self) -> Result<bool, Error> {
|
||||
let mut in_buffer = self.in_buffer.lock().unwrap();
|
||||
let recv_queue = &mut self.queues[0]; //receiveq
|
||||
let recv_queue = &mut self.input_queue; //receiveq
|
||||
let mut used_descs = false;
|
||||
|
||||
if in_buffer.is_empty() {
|
||||
return false;
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
while let Some(mut desc_chain) = recv_queue.pop_descriptor_chain(self.mem.memory()) {
|
||||
let desc = desc_chain.next().unwrap();
|
||||
let len = cmp::min(desc.len() as u32, in_buffer.len() as u32);
|
||||
let desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
|
||||
let len = cmp::min(desc.len(), in_buffer.len() as u32);
|
||||
let source_slice = in_buffer.drain(..len as usize).collect::<Vec<u8>>();
|
||||
|
||||
if let Err(e) = desc_chain.memory().write_slice(
|
||||
&source_slice[..],
|
||||
desc.addr()
|
||||
.translate_gva(self.access_platform.as_ref(), desc.len() as usize),
|
||||
) {
|
||||
error!("Failed to write slice: {:?}", e);
|
||||
recv_queue.go_to_previous_position();
|
||||
break;
|
||||
}
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_slice(
|
||||
&source_slice[..],
|
||||
desc.addr()
|
||||
.translate_gva(self.access_platform.as_ref(), desc.len() as usize),
|
||||
)
|
||||
.map_err(Error::GuestMemoryWrite)?;
|
||||
|
||||
recv_queue
|
||||
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
|
||||
.unwrap();
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
used_descs = true;
|
||||
|
||||
if in_buffer.is_empty() {
|
||||
@@ -235,7 +247,7 @@ impl ConsoleEpollHandler {
|
||||
}
|
||||
}
|
||||
|
||||
used_descs
|
||||
Ok(used_descs)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -245,28 +257,31 @@ impl ConsoleEpollHandler {
|
||||
* we read data from the transmit queue and flush them
|
||||
* to the referenced address.
|
||||
*/
|
||||
fn process_output_queue(&mut self) -> bool {
|
||||
let trans_queue = &mut self.queues[1]; //transmitq
|
||||
fn process_output_queue(&mut self) -> Result<bool, Error> {
|
||||
let trans_queue = &mut self.output_queue; //transmitq
|
||||
let mut used_descs = false;
|
||||
|
||||
while let Some(mut desc_chain) = trans_queue.pop_descriptor_chain(self.mem.memory()) {
|
||||
let desc = desc_chain.next().unwrap();
|
||||
let desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
|
||||
if let Some(out) = &mut self.out {
|
||||
let _ = desc_chain.memory().write_to(
|
||||
desc.addr()
|
||||
.translate_gva(self.access_platform.as_ref(), desc.len() as usize),
|
||||
out,
|
||||
desc.len() as usize,
|
||||
);
|
||||
let _ = out.flush();
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_to(
|
||||
desc.addr()
|
||||
.translate_gva(self.access_platform.as_ref(), desc.len() as usize),
|
||||
out,
|
||||
desc.len() as usize,
|
||||
)
|
||||
.map_err(Error::GuestMemoryRead)?;
|
||||
out.flush().map_err(Error::OutputFlush)?;
|
||||
}
|
||||
trans_queue
|
||||
.add_used(desc_chain.memory(), desc_chain.head_index(), desc.len())
|
||||
.unwrap();
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
used_descs = true;
|
||||
}
|
||||
|
||||
used_descs
|
||||
Ok(used_descs)
|
||||
}
|
||||
|
||||
fn signal_used_queue(&self, queue_index: u16) -> result::Result<(), DeviceError> {
|
||||
@@ -286,7 +301,6 @@ impl ConsoleEpollHandler {
|
||||
let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?;
|
||||
helper.add_event(self.input_queue_evt.as_raw_fd(), INPUT_QUEUE_EVENT)?;
|
||||
helper.add_event(self.output_queue_evt.as_raw_fd(), OUTPUT_QUEUE_EVENT)?;
|
||||
helper.add_event(self.input_evt.as_raw_fd(), INPUT_EVENT)?;
|
||||
helper.add_event(self.config_evt.as_raw_fd(), CONFIG_EVENT)?;
|
||||
if let Some(resize_pipe) = self.resize_pipe.as_ref() {
|
||||
helper.add_event(resize_pipe.as_raw_fd(), RESIZE_EVENT)?;
|
||||
@@ -366,7 +380,13 @@ impl EpollHelperHandler for ConsoleEpollHandler {
|
||||
self.input_queue_evt.read().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
|
||||
})?;
|
||||
if self.process_input_queue() {
|
||||
let needs_notification = self.process_input_queue().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to process input queue : {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
if needs_notification {
|
||||
self.signal_used_queue(0).map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal used queue: {:?}",
|
||||
@@ -379,21 +399,14 @@ impl EpollHelperHandler for ConsoleEpollHandler {
|
||||
self.output_queue_evt.read().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
|
||||
})?;
|
||||
if self.process_output_queue() {
|
||||
self.signal_used_queue(1).map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal used queue: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
}
|
||||
INPUT_EVENT => {
|
||||
self.input_evt.read().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!("Failed to get input event: {:?}", e))
|
||||
let needs_notification = self.process_output_queue().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to process output queue : {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
if self.process_input_queue() {
|
||||
self.signal_used_queue(0).map_err(|e| {
|
||||
if needs_notification {
|
||||
self.signal_used_queue(1).map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal used queue: {:?}",
|
||||
e
|
||||
@@ -436,7 +449,13 @@ impl EpollHelperHandler for ConsoleEpollHandler {
|
||||
in_buffer.extend(&input[..count]);
|
||||
}
|
||||
|
||||
if self.process_input_queue() {
|
||||
let needs_notification = self.process_input_queue().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to process input queue : {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
if needs_notification {
|
||||
self.signal_used_queue(0).map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal used queue: {:?}",
|
||||
@@ -598,7 +617,7 @@ fn get_win_size(tty: &dyn AsRawFd) -> (u16, u16) {
|
||||
impl VersionMapped for ConsoleState {}
|
||||
|
||||
impl Console {
|
||||
/// Create a new virtio console device that gets random data from /dev/urandom.
|
||||
/// Create a new virtio console device
|
||||
pub fn new(
|
||||
id: String,
|
||||
endpoint: Endpoint,
|
||||
@@ -606,20 +625,37 @@ impl Console {
|
||||
iommu: bool,
|
||||
seccomp_action: SeccompAction,
|
||||
exit_evt: EventFd,
|
||||
state: Option<ConsoleState>,
|
||||
) -> io::Result<(Console, Arc<ConsoleResizer>)> {
|
||||
let mut avail_features = 1u64 << VIRTIO_F_VERSION_1 | 1u64 << VIRTIO_CONSOLE_F_SIZE;
|
||||
let (avail_features, acked_features, config, in_buffer) = if let Some(state) = state {
|
||||
info!("Restoring virtio-console {}", id);
|
||||
(
|
||||
state.avail_features,
|
||||
state.acked_features,
|
||||
state.config,
|
||||
state.in_buffer.into(),
|
||||
)
|
||||
} else {
|
||||
let mut avail_features = 1u64 << VIRTIO_F_VERSION_1 | 1u64 << VIRTIO_CONSOLE_F_SIZE;
|
||||
if iommu {
|
||||
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
|
||||
}
|
||||
|
||||
if iommu {
|
||||
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
|
||||
}
|
||||
(
|
||||
avail_features,
|
||||
0,
|
||||
VirtioConsoleConfig::default(),
|
||||
VecDeque::new(),
|
||||
)
|
||||
};
|
||||
|
||||
let config_evt = EventFd::new(EFD_NONBLOCK).unwrap();
|
||||
let console_config = Arc::new(Mutex::new(VirtioConsoleConfig::default()));
|
||||
let console_config = Arc::new(Mutex::new(config));
|
||||
let resizer = Arc::new(ConsoleResizer {
|
||||
config_evt,
|
||||
config: console_config.clone(),
|
||||
tty: endpoint.out_file().as_ref().map(|t| t.try_clone().unwrap()),
|
||||
acked_features: AtomicU64::new(0),
|
||||
acked_features: AtomicU64::new(acked_features),
|
||||
});
|
||||
|
||||
resizer.update_console_size();
|
||||
@@ -630,6 +666,7 @@ impl Console {
|
||||
device_type: VirtioDeviceType::Console as u32,
|
||||
queue_sizes: QUEUE_SIZES.to_vec(),
|
||||
avail_features,
|
||||
acked_features,
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
min_queues: NUM_QUEUES as u16,
|
||||
..Default::default()
|
||||
@@ -640,7 +677,7 @@ impl Console {
|
||||
resize_pipe,
|
||||
endpoint,
|
||||
seccomp_action,
|
||||
in_buffer: Arc::new(Mutex::new(VecDeque::new())),
|
||||
in_buffer: Arc::new(Mutex::new(in_buffer)),
|
||||
exit_evt,
|
||||
},
|
||||
resizer,
|
||||
@@ -656,11 +693,9 @@ impl Console {
|
||||
}
|
||||
}
|
||||
|
||||
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.in_buffer.lock().unwrap()) = state.in_buffer.clone().into();
|
||||
#[cfg(fuzzing)]
|
||||
pub fn wait_for_epoll_threads(&mut self) {
|
||||
self.common.wait_for_epoll_threads();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -712,26 +747,20 @@ impl VirtioDevice for Console {
|
||||
}
|
||||
|
||||
let (kill_evt, pause_evt) = self.common.dup_eventfds();
|
||||
let input_evt = EventFd::new(EFD_NONBLOCK).unwrap();
|
||||
|
||||
let mut virtqueues = Vec::new();
|
||||
let (_, queue, queue_evt) = queues.remove(0);
|
||||
virtqueues.push(queue);
|
||||
let input_queue_evt = queue_evt;
|
||||
let (_, queue, queue_evt) = queues.remove(0);
|
||||
virtqueues.push(queue);
|
||||
let output_queue_evt = queue_evt;
|
||||
let (_, input_queue, input_queue_evt) = queues.remove(0);
|
||||
let (_, output_queue, output_queue_evt) = queues.remove(0);
|
||||
|
||||
let mut handler = ConsoleEpollHandler::new(
|
||||
mem,
|
||||
virtqueues,
|
||||
input_queue,
|
||||
output_queue,
|
||||
interrupt_cb,
|
||||
self.in_buffer.clone(),
|
||||
Arc::clone(&self.resizer),
|
||||
self.endpoint.clone(),
|
||||
input_queue_evt,
|
||||
output_queue_evt,
|
||||
input_evt,
|
||||
self.resizer.config_evt.try_clone().unwrap(),
|
||||
self.resize_pipe.as_ref().map(|p| p.try_clone().unwrap()),
|
||||
kill_evt,
|
||||
@@ -787,11 +816,6 @@ impl Snapshottable for Console {
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
Snapshot::new_from_versioned_state(&self.id, &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for Console {}
|
||||
impl Migratable for Console {}
|
||||
|
||||
@@ -282,7 +282,6 @@ impl VirtioCommon {
|
||||
}
|
||||
|
||||
// Wait for the worker thread to finish and return
|
||||
#[cfg(fuzzing)]
|
||||
pub fn wait_for_epoll_threads(&mut self) {
|
||||
if let Some(mut threads) = self.epoll_threads.take() {
|
||||
for t in threads.drain(..) {
|
||||
|
||||
@@ -148,7 +148,6 @@ impl EpollHelper {
|
||||
.map_err(EpollHelperError::Ctl)
|
||||
}
|
||||
|
||||
#[cfg(not(fuzzing))]
|
||||
pub fn run(
|
||||
&mut self,
|
||||
paused: Arc<AtomicBool>,
|
||||
@@ -158,6 +157,7 @@ impl EpollHelper {
|
||||
self.run_with_timeout(paused, paused_sync, handler, -1, false)
|
||||
}
|
||||
|
||||
#[cfg(not(fuzzing))]
|
||||
pub fn run_with_timeout(
|
||||
&mut self,
|
||||
paused: Arc<AtomicBool>,
|
||||
@@ -245,11 +245,13 @@ impl EpollHelper {
|
||||
#[cfg(fuzzing)]
|
||||
// Require to have a 'queue_evt' being kicked before calling
|
||||
// and return when no epoll events are active
|
||||
pub fn run(
|
||||
pub fn run_with_timeout(
|
||||
&mut self,
|
||||
paused: Arc<AtomicBool>,
|
||||
paused_sync: Arc<Barrier>,
|
||||
handler: &mut dyn EpollHelperHandler,
|
||||
_timeout: i32,
|
||||
_enable_event_list: bool,
|
||||
) -> std::result::Result<(), EpollHelperError> {
|
||||
const EPOLL_EVENTS_LEN: usize = 100;
|
||||
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN];
|
||||
|
||||
@@ -14,7 +14,6 @@ use crate::{DmaRemapping, VirtioInterrupt, VirtioInterruptType};
|
||||
use anyhow::anyhow;
|
||||
use seccompiler::SeccompAction;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::{self, Display};
|
||||
use std::io;
|
||||
use std::mem::size_of;
|
||||
use std::ops::Bound::Included;
|
||||
@@ -22,6 +21,7 @@ use std::os::unix::io::AsRawFd;
|
||||
use std::result;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Barrier, Mutex, RwLock};
|
||||
use thiserror::Error;
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use virtio_queue::{DescriptorChain, Queue, QueueT};
|
||||
@@ -47,7 +47,7 @@ const REQUEST_Q_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
|
||||
/// New descriptors are pending on the event queue.
|
||||
/// "eventq" lets the device report any fault or other asynchronous event to
|
||||
/// the guest driver.
|
||||
const EVENT_Q_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
|
||||
const _EVENT_Q_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
|
||||
|
||||
/// PROBE properties size.
|
||||
/// This is the minimal size to provide at least one RESV_MEM property.
|
||||
@@ -282,76 +282,44 @@ unsafe impl ByteValued for VirtioIommuProbeProperty {}
|
||||
unsafe impl ByteValued for VirtioIommuProbeResvMem {}
|
||||
unsafe impl ByteValued for VirtioIommuFault {}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
enum Error {
|
||||
/// Guest gave us bad memory addresses.
|
||||
#[error("Guest gave us bad memory addresses: {0}")]
|
||||
GuestMemory(GuestMemoryError),
|
||||
/// Guest gave us a write only descriptor that protocol says to read from.
|
||||
#[error("Guest gave us a write only descriptor that protocol says to read from")]
|
||||
UnexpectedWriteOnlyDescriptor,
|
||||
/// Guest gave us a read only descriptor that protocol says to write to.
|
||||
#[error("Guest gave us a read only descriptor that protocol says to write to")]
|
||||
UnexpectedReadOnlyDescriptor,
|
||||
/// Guest gave us too few descriptors in a descriptor chain.
|
||||
#[error("Guest gave us too few descriptors in a descriptor chain")]
|
||||
DescriptorChainTooShort,
|
||||
/// Guest gave us a buffer that was too short to use.
|
||||
#[error("Guest gave us a buffer that was too short to use")]
|
||||
BufferLengthTooSmall,
|
||||
/// Guest sent us invalid request.
|
||||
#[error("Guest sent us invalid request")]
|
||||
InvalidRequest,
|
||||
/// Guest sent us invalid ATTACH request.
|
||||
#[error("Guest sent us invalid ATTACH request")]
|
||||
InvalidAttachRequest,
|
||||
/// Guest sent us invalid DETACH request.
|
||||
#[error("Guest sent us invalid DETACH request")]
|
||||
InvalidDetachRequest,
|
||||
/// Guest sent us invalid MAP request.
|
||||
#[error("Guest sent us invalid MAP request")]
|
||||
InvalidMapRequest,
|
||||
/// Invalid to map because the domain is in bypass mode.
|
||||
#[error("Invalid to map because the domain is in bypass mode")]
|
||||
InvalidMapRequestBypassDomain,
|
||||
/// Invalid to map because the domain is missing.
|
||||
#[error("Invalid to map because the domain is missing")]
|
||||
InvalidMapRequestMissingDomain,
|
||||
/// Guest sent us invalid UNMAP request.
|
||||
#[error("Guest sent us invalid UNMAP request")]
|
||||
InvalidUnmapRequest,
|
||||
/// Invalid to unmap because the domain is in bypass mode.
|
||||
#[error("Invalid to unmap because the domain is in bypass mode")]
|
||||
InvalidUnmapRequestBypassDomain,
|
||||
/// Invalid to unmap because the domain is missing.
|
||||
#[error("Invalid to unmap because the domain is missing")]
|
||||
InvalidUnmapRequestMissingDomain,
|
||||
/// Guest sent us invalid PROBE request.
|
||||
#[error("Guest sent us invalid PROBE request")]
|
||||
InvalidProbeRequest,
|
||||
/// Failed to performing external mapping.
|
||||
#[error("Failed to performing external mapping: {0}")]
|
||||
ExternalMapping(io::Error),
|
||||
/// Failed to performing external unmapping.
|
||||
#[error("Failed to performing external unmapping: {0}")]
|
||||
ExternalUnmapping(io::Error),
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::Error::*;
|
||||
|
||||
match self {
|
||||
BufferLengthTooSmall => write!(f, "buffer length too small"),
|
||||
DescriptorChainTooShort => write!(f, "descriptor chain too short"),
|
||||
GuestMemory(e) => write!(f, "bad guest memory address: {}", e),
|
||||
InvalidRequest => write!(f, "invalid request"),
|
||||
InvalidAttachRequest => write!(f, "invalid attach request"),
|
||||
InvalidDetachRequest => write!(f, "invalid detach request"),
|
||||
InvalidMapRequest => write!(f, "invalid map request"),
|
||||
InvalidMapRequestBypassDomain => {
|
||||
write!(f, "invalid map request because domain in bypass mode")
|
||||
}
|
||||
InvalidMapRequestMissingDomain => {
|
||||
write!(f, "invalid map request because missing domain")
|
||||
}
|
||||
InvalidUnmapRequest => write!(f, "invalid unmap request"),
|
||||
InvalidUnmapRequestBypassDomain => {
|
||||
write!(f, "invalid unmap request because domain in bypass mode")
|
||||
}
|
||||
InvalidUnmapRequestMissingDomain => {
|
||||
write!(f, "invalid unmap request because missing domain")
|
||||
}
|
||||
InvalidProbeRequest => write!(f, "invalid probe request"),
|
||||
UnexpectedReadOnlyDescriptor => write!(f, "unexpected read-only descriptor"),
|
||||
UnexpectedWriteOnlyDescriptor => write!(f, "unexpected write-only descriptor"),
|
||||
ExternalMapping(e) => write!(f, "failed performing external mapping: {}", e),
|
||||
ExternalUnmapping(e) => write!(f, "failed performing external unmapping: {}", e),
|
||||
}
|
||||
}
|
||||
#[error("Failed adding used index: {0}")]
|
||||
QueueAddUsed(virtio_queue::Error),
|
||||
}
|
||||
|
||||
struct Request {}
|
||||
@@ -662,9 +630,11 @@ impl Request {
|
||||
|
||||
struct IommuEpollHandler {
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
queues: Vec<Queue>,
|
||||
request_queue: Queue,
|
||||
_event_queue: Queue,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
queue_evts: Vec<EventFd>,
|
||||
request_queue_evt: EventFd,
|
||||
_event_queue_evt: EventFd,
|
||||
kill_evt: EventFd,
|
||||
pause_evt: EventFd,
|
||||
mapping: Arc<IommuMapping>,
|
||||
@@ -673,33 +643,25 @@ struct IommuEpollHandler {
|
||||
}
|
||||
|
||||
impl IommuEpollHandler {
|
||||
fn request_queue(&mut self) -> bool {
|
||||
fn request_queue(&mut self) -> Result<bool, Error> {
|
||||
let mut used_descs = false;
|
||||
while let Some(mut desc_chain) = self.queues[0].pop_descriptor_chain(self.mem.memory()) {
|
||||
let len = match Request::parse(
|
||||
while let Some(mut desc_chain) = self.request_queue.pop_descriptor_chain(self.mem.memory())
|
||||
{
|
||||
let len = Request::parse(
|
||||
&mut desc_chain,
|
||||
&self.mapping,
|
||||
&self.ext_mapping.lock().unwrap(),
|
||||
self.msi_iova_space,
|
||||
) {
|
||||
Ok(len) => len as u32,
|
||||
Err(e) => {
|
||||
error!("failed parsing descriptor: {}", e);
|
||||
0
|
||||
}
|
||||
};
|
||||
)?;
|
||||
|
||||
self.request_queue
|
||||
.add_used(desc_chain.memory(), desc_chain.head_index(), len as u32)
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
|
||||
self.queues[0]
|
||||
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
|
||||
.unwrap();
|
||||
used_descs = true;
|
||||
}
|
||||
|
||||
used_descs
|
||||
}
|
||||
|
||||
fn event_queue(&mut self) -> bool {
|
||||
false
|
||||
Ok(used_descs)
|
||||
}
|
||||
|
||||
fn signal_used_queue(&self, queue_index: u16) -> result::Result<(), DeviceError> {
|
||||
@@ -717,8 +679,7 @@ impl IommuEpollHandler {
|
||||
paused_sync: Arc<Barrier>,
|
||||
) -> result::Result<(), EpollHelperError> {
|
||||
let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?;
|
||||
helper.add_event(self.queue_evts[0].as_raw_fd(), REQUEST_Q_EVENT)?;
|
||||
helper.add_event(self.queue_evts[1].as_raw_fd(), EVENT_Q_EVENT)?;
|
||||
helper.add_event(self.request_queue_evt.as_raw_fd(), REQUEST_Q_EVENT)?;
|
||||
helper.run(paused, paused_sync, self)?;
|
||||
|
||||
Ok(())
|
||||
@@ -734,11 +695,17 @@ impl EpollHelperHandler for IommuEpollHandler {
|
||||
let ev_type = event.data as u16;
|
||||
match ev_type {
|
||||
REQUEST_Q_EVENT => {
|
||||
self.queue_evts[0].read().map_err(|e| {
|
||||
self.request_queue_evt.read().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
|
||||
})?;
|
||||
|
||||
if self.request_queue() {
|
||||
let needs_notification = self.request_queue().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to process request queue : {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
if needs_notification {
|
||||
self.signal_used_queue(0).map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal used queue: {:?}",
|
||||
@@ -747,20 +714,6 @@ impl EpollHelperHandler for IommuEpollHandler {
|
||||
})?;
|
||||
}
|
||||
}
|
||||
EVENT_Q_EVENT => {
|
||||
self.queue_evts[1].read().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
|
||||
})?;
|
||||
|
||||
if self.event_queue() {
|
||||
self.signal_used_queue(1).map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal used queue: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(EpollHelperError::HandleEvent(anyhow!(
|
||||
"Unexpected event: {}",
|
||||
@@ -897,7 +850,7 @@ type EndpointsState = Vec<(u32, u32)>;
|
||||
type DomainsState = Vec<(u32, (Vec<(u64, Mapping)>, bool))>;
|
||||
|
||||
#[derive(Versionize)]
|
||||
struct IommuState {
|
||||
pub struct IommuState {
|
||||
avail_features: u64,
|
||||
acked_features: u64,
|
||||
endpoints: EndpointsState,
|
||||
@@ -912,7 +865,37 @@ impl Iommu {
|
||||
seccomp_action: SeccompAction,
|
||||
exit_evt: EventFd,
|
||||
msi_iova_space: (u64, u64),
|
||||
state: Option<IommuState>,
|
||||
) -> io::Result<(Self, Arc<IommuMapping>)> {
|
||||
let (avail_features, acked_features, endpoints, domains) = if let Some(state) = state {
|
||||
info!("Restoring virtio-iommu {}", id);
|
||||
(
|
||||
state.avail_features,
|
||||
state.acked_features,
|
||||
state.endpoints.into_iter().collect(),
|
||||
state
|
||||
.domains
|
||||
.into_iter()
|
||||
.map(|(k, v)| {
|
||||
(
|
||||
k,
|
||||
Domain {
|
||||
mappings: v.0.into_iter().collect(),
|
||||
bypass: v.1,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
} else {
|
||||
let avail_features = 1u64 << VIRTIO_F_VERSION_1
|
||||
| 1u64 << VIRTIO_IOMMU_F_MAP_UNMAP
|
||||
| 1u64 << VIRTIO_IOMMU_F_PROBE
|
||||
| 1u64 << VIRTIO_IOMMU_F_BYPASS_CONFIG;
|
||||
|
||||
(avail_features, 0, BTreeMap::new(), BTreeMap::new())
|
||||
};
|
||||
|
||||
let config = VirtioIommuConfig {
|
||||
page_size_mask: VIRTIO_IOMMU_PAGE_SIZE_MASK,
|
||||
probe_size: PROBE_PROP_SIZE,
|
||||
@@ -920,8 +903,8 @@ impl Iommu {
|
||||
};
|
||||
|
||||
let mapping = Arc::new(IommuMapping {
|
||||
endpoints: Arc::new(RwLock::new(BTreeMap::new())),
|
||||
domains: Arc::new(RwLock::new(BTreeMap::new())),
|
||||
endpoints: Arc::new(RwLock::new(endpoints)),
|
||||
domains: Arc::new(RwLock::new(domains)),
|
||||
bypass: AtomicBool::new(true),
|
||||
});
|
||||
|
||||
@@ -931,11 +914,10 @@ impl Iommu {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Iommu as u32,
|
||||
queue_sizes: QUEUE_SIZES.to_vec(),
|
||||
avail_features: 1u64 << VIRTIO_F_VERSION_1
|
||||
| 1u64 << VIRTIO_IOMMU_F_MAP_UNMAP
|
||||
| 1u64 << VIRTIO_IOMMU_F_PROBE
|
||||
| 1u64 << VIRTIO_IOMMU_F_BYPASS_CONFIG,
|
||||
avail_features,
|
||||
acked_features,
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
min_queues: NUM_QUEUES as u16,
|
||||
..Default::default()
|
||||
},
|
||||
config,
|
||||
@@ -973,26 +955,6 @@ impl Iommu {
|
||||
}
|
||||
}
|
||||
|
||||
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().into_iter().collect();
|
||||
*(self.mapping.domains.write().unwrap()) = state
|
||||
.domains
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|(k, v)| {
|
||||
(
|
||||
k,
|
||||
Domain {
|
||||
mappings: v.0.into_iter().collect(),
|
||||
bypass: v.1,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
fn update_bypass(&mut self) {
|
||||
// Use bypass from config if VIRTIO_IOMMU_F_BYPASS_CONFIG has been negotiated
|
||||
if !self
|
||||
@@ -1010,6 +972,11 @@ impl Iommu {
|
||||
pub fn add_external_mapping(&mut self, device_id: u32, mapping: Arc<dyn ExternalDmaMapping>) {
|
||||
self.ext_mapping.lock().unwrap().insert(device_id, mapping);
|
||||
}
|
||||
|
||||
#[cfg(fuzzing)]
|
||||
pub fn wait_for_epoll_threads(&mut self) {
|
||||
self.common.wait_for_epoll_threads();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Iommu {
|
||||
@@ -1064,23 +1031,21 @@ impl VirtioDevice for Iommu {
|
||||
&mut self,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
queues: Vec<(usize, Queue, EventFd)>,
|
||||
mut queues: Vec<(usize, Queue, EventFd)>,
|
||||
) -> ActivateResult {
|
||||
self.common.activate(&queues, &interrupt_cb)?;
|
||||
let (kill_evt, pause_evt) = self.common.dup_eventfds();
|
||||
|
||||
let mut virtqueues = Vec::new();
|
||||
let mut queue_evts = Vec::new();
|
||||
for (_, queue, queue_evt) in queues {
|
||||
virtqueues.push(queue);
|
||||
queue_evts.push(queue_evt);
|
||||
}
|
||||
let (_, request_queue, request_queue_evt) = queues.remove(0);
|
||||
let (_, _event_queue, _event_queue_evt) = queues.remove(0);
|
||||
|
||||
let mut handler = IommuEpollHandler {
|
||||
mem,
|
||||
queues: virtqueues,
|
||||
request_queue,
|
||||
_event_queue,
|
||||
interrupt_cb,
|
||||
queue_evts,
|
||||
request_queue_evt,
|
||||
_event_queue_evt,
|
||||
kill_evt,
|
||||
pause_evt,
|
||||
mapping: self.mapping.clone(),
|
||||
@@ -1131,11 +1096,6 @@ impl Snapshottable for Iommu {
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
Snapshot::new_from_versioned_state(&self.id, &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for Iommu {}
|
||||
impl Migratable for Iommu {}
|
||||
|
||||
@@ -80,7 +80,7 @@ const VIRTIO_F_NOTIFICATION_DATA: u32 = 38;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ActivateError {
|
||||
#[error("Failed to activate virtio device.")]
|
||||
#[error("Failed to activate virtio device")]
|
||||
BadActivate,
|
||||
#[error("Failed to clone exit event fd: {0}")]
|
||||
CloneExitEventFd(std::io::Error),
|
||||
@@ -112,7 +112,7 @@ pub enum Error {
|
||||
VhostUserUpdateMemory(vhost_user::Error),
|
||||
#[error("Failed to add memory region vhost-user: {0}")]
|
||||
VhostUserAddMemoryRegion(vhost_user::Error),
|
||||
#[error("Failed to set shared memory region.")]
|
||||
#[error("Failed to set shared memory region")]
|
||||
SetShmRegionsNotSupported,
|
||||
#[error("Failed to process net queue: {0}")]
|
||||
NetQueuePair(::net_util::NetQueuePairError),
|
||||
|
||||
@@ -105,15 +105,15 @@ const VIRTIO_MEM_F_ACPI_PXM: u8 = 0;
|
||||
pub enum Error {
|
||||
#[error("Guest gave us bad memory addresses: {0}")]
|
||||
GuestMemory(GuestMemoryError),
|
||||
#[error("Guest gave us a write only descriptor that protocol says to read from.")]
|
||||
#[error("Guest gave us a write only descriptor that protocol says to read from")]
|
||||
UnexpectedWriteOnlyDescriptor,
|
||||
#[error("Guest gave us a read only descriptor that protocol says to write to.")]
|
||||
#[error("Guest gave us a read only descriptor that protocol says to write to")]
|
||||
UnexpectedReadOnlyDescriptor,
|
||||
#[error("Guest gave us too few descriptors in a descriptor chain.")]
|
||||
#[error("Guest gave us too few descriptors in a descriptor chain")]
|
||||
DescriptorChainTooShort,
|
||||
#[error("Guest gave us a buffer that was too short to use.")]
|
||||
#[error("Guest gave us a buffer that was too short to use")]
|
||||
BufferLengthTooSmall,
|
||||
#[error("Guest sent us invalid request.")]
|
||||
#[error("Guest sent us invalid request")]
|
||||
InvalidRequest,
|
||||
#[error("Failed to EventFd write: {0}")]
|
||||
EventFdWriteFail(std::io::Error),
|
||||
@@ -133,10 +133,14 @@ pub enum Error {
|
||||
DmaMap(std::io::Error),
|
||||
#[error("Failed DMA unmapping: {0}")]
|
||||
DmaUnmap(std::io::Error),
|
||||
#[error("Invalid DMA mapping handler.")]
|
||||
#[error("Invalid DMA mapping handler")]
|
||||
InvalidDmaMappingHandler,
|
||||
#[error("Not activated by the guest.")]
|
||||
#[error("Not activated by the guest")]
|
||||
NotActivatedByGuest,
|
||||
#[error("Unknown request type: {0}")]
|
||||
UnkownRequestType(u16),
|
||||
#[error("Failed adding used index: {0}")]
|
||||
QueueAddUsed(virtio_queue::Error),
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
@@ -242,7 +246,7 @@ impl VirtioMemConfig {
|
||||
size,
|
||||
self.region_size
|
||||
)));
|
||||
} else if size % (self.block_size as u64) != 0 {
|
||||
} else if size % self.block_size != 0 {
|
||||
return Err(Error::ResizeError(anyhow!(
|
||||
"new size 0x{:x} is not aligned on block_size 0x{:x}",
|
||||
size,
|
||||
@@ -256,6 +260,12 @@ impl VirtioMemConfig {
|
||||
}
|
||||
|
||||
fn is_valid_range(&self, addr: u64, size: u64) -> bool {
|
||||
// Ensure no overflow from adding 'addr' and 'size' whose value are both
|
||||
// controlled by the guest driver
|
||||
if addr.checked_add(size).is_none() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -309,19 +319,20 @@ impl Request {
|
||||
})
|
||||
}
|
||||
|
||||
fn send_response(&self, mem: &GuestMemoryMmap, resp_type: u16, state: u16) -> u32 {
|
||||
fn send_response(
|
||||
&self,
|
||||
mem: &GuestMemoryMmap,
|
||||
resp_type: u16,
|
||||
state: u16,
|
||||
) -> Result<u32, Error> {
|
||||
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
|
||||
}
|
||||
}
|
||||
mem.write_obj(resp, self.status_addr)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
Ok(size_of::<VirtioMemResp>() as u32)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -588,49 +599,34 @@ impl MemEpollHandler {
|
||||
})
|
||||
}
|
||||
|
||||
fn process_queue(&mut self) -> bool {
|
||||
fn process_queue(&mut self) -> Result<bool, Error> {
|
||||
let mut used_descs = false;
|
||||
|
||||
while let Some(mut desc_chain) = self.queue.pop_descriptor_chain(self.mem.memory()) {
|
||||
let len = match Request::parse(&mut desc_chain) {
|
||||
Err(e) => {
|
||||
error!("failed parse VirtioMemReq: {:?}", e);
|
||||
0
|
||||
let r = Request::parse(&mut desc_chain)?;
|
||||
let (resp_type, resp_state) = match r.req.req_type {
|
||||
VIRTIO_MEM_REQ_PLUG => (
|
||||
self.state_change_request(r.req.addr, r.req.nb_blocks, true),
|
||||
0u16,
|
||||
),
|
||||
VIRTIO_MEM_REQ_UNPLUG => (
|
||||
self.state_change_request(r.req.addr, r.req.nb_blocks, false),
|
||||
0u16,
|
||||
),
|
||||
VIRTIO_MEM_REQ_UNPLUG_ALL => (self.unplug_all(), 0u16),
|
||||
VIRTIO_MEM_REQ_STATE => self.state_request(r.req.addr, r.req.nb_blocks),
|
||||
_ => {
|
||||
return Err(Error::UnkownRequestType(r.req.req_type));
|
||||
}
|
||||
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(desc_chain.memory(), 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(desc_chain.memory(), resp_type, 0u16)
|
||||
}
|
||||
VIRTIO_MEM_REQ_UNPLUG_ALL => {
|
||||
let resp_type = self.unplug_all();
|
||||
r.send_response(desc_chain.memory(), 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(desc_chain.memory(), resp_type, resp_state)
|
||||
}
|
||||
_ => {
|
||||
error!("VirtioMemReq unknown request type {:?}", r.req.req_type);
|
||||
0
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let len = r.send_response(desc_chain.memory(), resp_type, resp_state)?;
|
||||
self.queue
|
||||
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
|
||||
.unwrap();
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
used_descs = true;
|
||||
}
|
||||
|
||||
used_descs
|
||||
Ok(used_descs)
|
||||
}
|
||||
|
||||
fn run(
|
||||
@@ -659,7 +655,10 @@ impl EpollHelperHandler for MemEpollHandler {
|
||||
EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
|
||||
})?;
|
||||
|
||||
if self.process_queue() {
|
||||
let needs_notification = self.process_queue().map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!("Failed to process queue : {:?}", e))
|
||||
})?;
|
||||
if needs_notification {
|
||||
self.signal(VirtioInterruptType::Queue(0)).map_err(|e| {
|
||||
EpollHelperError::HandleEvent(anyhow!(
|
||||
"Failed to signal used queue: {:?}",
|
||||
@@ -721,6 +720,7 @@ impl Mem {
|
||||
hugepages: bool,
|
||||
exit_evt: EventFd,
|
||||
blocks_state: Arc<Mutex<BlocksState>>,
|
||||
state: Option<MemState>,
|
||||
) -> io::Result<Mem> {
|
||||
let region_len = region.len();
|
||||
|
||||
@@ -734,43 +734,51 @@ impl Mem {
|
||||
));
|
||||
}
|
||||
|
||||
let mut avail_features = 1u64 << VIRTIO_F_VERSION_1;
|
||||
let (avail_features, acked_features, config) = if let Some(state) = state {
|
||||
info!("Restoring virtio-mem {}", id);
|
||||
*(blocks_state.lock().unwrap()) = state.blocks_state.clone();
|
||||
(state.avail_features, state.acked_features, state.config)
|
||||
} else {
|
||||
let mut avail_features = 1u64 << VIRTIO_F_VERSION_1;
|
||||
|
||||
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()
|
||||
};
|
||||
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 {
|
||||
config.resize(initial_size).map_err(|e| {
|
||||
if initial_size != 0 {
|
||||
config.resize(initial_size).map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!(
|
||||
"Failed to resize virtio-mem configuration to {}: {:?}",
|
||||
initial_size, e
|
||||
),
|
||||
)
|
||||
})?;
|
||||
}
|
||||
|
||||
if let Some(node_id) = numa_node_id {
|
||||
avail_features |= 1u64 << VIRTIO_MEM_F_ACPI_PXM;
|
||||
config.node_id = node_id;
|
||||
}
|
||||
|
||||
// Make sure the virtio-mem configuration complies with the
|
||||
// specification.
|
||||
config.validate().map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!(
|
||||
"Failed to resize virtio-mem configuration to {}: {:?}",
|
||||
initial_size, e
|
||||
),
|
||||
format!("Invalid virtio-mem configuration: {:?}", e),
|
||||
)
|
||||
})?;
|
||||
}
|
||||
|
||||
if let Some(node_id) = numa_node_id {
|
||||
avail_features |= 1u64 << VIRTIO_MEM_F_ACPI_PXM;
|
||||
config.node_id = node_id;
|
||||
}
|
||||
|
||||
// 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),
|
||||
)
|
||||
})?;
|
||||
(avail_features, 0, config)
|
||||
};
|
||||
|
||||
let host_fd = region
|
||||
.file_offset()
|
||||
@@ -780,6 +788,7 @@ impl Mem {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Mem as u32,
|
||||
avail_features,
|
||||
acked_features,
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
queue_sizes: QUEUE_SIZES.to_vec(),
|
||||
min_queues: 1,
|
||||
@@ -877,13 +886,6 @@ impl Mem {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &MemState) {
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
*(self.config.lock().unwrap()) = state.config;
|
||||
*(self.blocks_state.lock().unwrap()) = state.blocks_state.clone();
|
||||
}
|
||||
|
||||
#[cfg(fuzzing)]
|
||||
pub fn wait_for_epoll_threads(&mut self) {
|
||||
self.common.wait_for_epoll_threads();
|
||||
@@ -1006,11 +1008,6 @@ impl Snapshottable for Mem {
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
Snapshot::new_from_versioned_state(&self.id(), &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for Mem {}
|
||||
impl Migratable for Mem {}
|
||||
|
||||
@@ -173,8 +173,8 @@ struct NetEpollHandler {
|
||||
kill_evt: EventFd,
|
||||
pause_evt: EventFd,
|
||||
queue_index_base: u16,
|
||||
queue_pair: Vec<Queue>,
|
||||
queue_evt_pair: Vec<EventFd>,
|
||||
queue_pair: (Queue, Queue),
|
||||
queue_evt_pair: (EventFd, EventFd),
|
||||
// Always generate interrupts until the driver has signalled to the device.
|
||||
// This mitigates a problem with interrupts from tap events being "lost" upon
|
||||
// a restore as the vCPU thread isn't ready to handle the interrupt. This causes
|
||||
@@ -193,7 +193,7 @@ impl NetEpollHandler {
|
||||
}
|
||||
|
||||
fn handle_rx_event(&mut self) -> result::Result<(), DeviceError> {
|
||||
let queue_evt = &self.queue_evt_pair[0];
|
||||
let queue_evt = &self.queue_evt_pair.0;
|
||||
if let Err(e) = queue_evt.read() {
|
||||
error!("Failed to get rx queue event: {:?}", e);
|
||||
}
|
||||
@@ -224,7 +224,7 @@ impl NetEpollHandler {
|
||||
fn process_tx(&mut self) -> result::Result<(), DeviceError> {
|
||||
if self
|
||||
.net
|
||||
.process_tx(&self.mem.memory(), &mut self.queue_pair[1])
|
||||
.process_tx(&self.mem.memory(), &mut self.queue_pair.1)
|
||||
.map_err(DeviceError::NetQueuePair)?
|
||||
|| !self.driver_awake
|
||||
{
|
||||
@@ -253,7 +253,7 @@ impl NetEpollHandler {
|
||||
fn handle_rx_tap_event(&mut self) -> result::Result<(), DeviceError> {
|
||||
if self
|
||||
.net
|
||||
.process_rx(&self.mem.memory(), &mut self.queue_pair[0])
|
||||
.process_rx(&self.mem.memory(), &mut self.queue_pair.0)
|
||||
.map_err(DeviceError::NetQueuePair)?
|
||||
|| !self.driver_awake
|
||||
{
|
||||
@@ -271,8 +271,8 @@ impl NetEpollHandler {
|
||||
paused_sync: Arc<Barrier>,
|
||||
) -> result::Result<(), EpollHelperError> {
|
||||
let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?;
|
||||
helper.add_event(self.queue_evt_pair[0].as_raw_fd(), RX_QUEUE_EVENT)?;
|
||||
helper.add_event(self.queue_evt_pair[1].as_raw_fd(), TX_QUEUE_EVENT)?;
|
||||
helper.add_event(self.queue_evt_pair.0.as_raw_fd(), RX_QUEUE_EVENT)?;
|
||||
helper.add_event(self.queue_evt_pair.1.as_raw_fd(), TX_QUEUE_EVENT)?;
|
||||
if let Some(rate_limiter) = &self.net.rx_rate_limiter {
|
||||
helper.add_event(rate_limiter.as_raw_fd(), RX_RATE_LIMITER_EVENT)?;
|
||||
}
|
||||
@@ -283,10 +283,14 @@ impl NetEpollHandler {
|
||||
let mem = self.mem.memory();
|
||||
// If there are some already available descriptors on the RX queue,
|
||||
// then we can start the thread while listening onto the TAP.
|
||||
if self.queue_pair[0]
|
||||
if self
|
||||
.queue_pair
|
||||
.0
|
||||
.used_idx(mem.deref(), Ordering::Acquire)
|
||||
.map_err(EpollHelperError::QueueRingIndex)?
|
||||
< self.queue_pair[0]
|
||||
< self
|
||||
.queue_pair
|
||||
.0
|
||||
.avail_idx(mem.deref(), Ordering::Acquire)
|
||||
.map_err(EpollHelperError::QueueRingIndex)?
|
||||
{
|
||||
@@ -319,7 +323,7 @@ impl EpollHelperHandler for NetEpollHandler {
|
||||
})?;
|
||||
}
|
||||
TX_QUEUE_EVENT => {
|
||||
let queue_evt = &self.queue_evt_pair[1];
|
||||
let queue_evt = &self.queue_evt_pair.1;
|
||||
if let Err(e) = queue_evt.read() {
|
||||
error!("Failed to get tx queue event: {:?}", e);
|
||||
}
|
||||
@@ -441,45 +445,70 @@ impl Net {
|
||||
seccomp_action: SeccompAction,
|
||||
rate_limiter_config: Option<RateLimiterConfig>,
|
||||
exit_evt: EventFd,
|
||||
state: Option<NetState>,
|
||||
) -> Result<Self> {
|
||||
assert!(!taps.is_empty());
|
||||
|
||||
let mtu = taps[0].mtu().map_err(Error::TapError)? as u16;
|
||||
|
||||
let mut avail_features = 1 << VIRTIO_NET_F_CSUM
|
||||
| 1 << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS
|
||||
| 1 << VIRTIO_NET_F_GUEST_CSUM
|
||||
| 1 << VIRTIO_NET_F_GUEST_ECN
|
||||
| 1 << VIRTIO_NET_F_GUEST_TSO4
|
||||
| 1 << VIRTIO_NET_F_GUEST_TSO6
|
||||
| 1 << VIRTIO_NET_F_GUEST_UFO
|
||||
| 1 << VIRTIO_NET_F_HOST_ECN
|
||||
| 1 << VIRTIO_NET_F_HOST_TSO4
|
||||
| 1 << VIRTIO_NET_F_HOST_TSO6
|
||||
| 1 << VIRTIO_NET_F_HOST_UFO
|
||||
| 1 << VIRTIO_NET_F_MTU
|
||||
| 1 << VIRTIO_RING_F_EVENT_IDX
|
||||
| 1 << VIRTIO_F_VERSION_1;
|
||||
|
||||
if iommu {
|
||||
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
|
||||
}
|
||||
|
||||
avail_features |= 1 << VIRTIO_NET_F_CTRL_VQ;
|
||||
let queue_num = num_queues + 1;
|
||||
|
||||
let mut config = VirtioNetConfig::default();
|
||||
if let Some(mac) = guest_mac {
|
||||
build_net_config_space(&mut config, mac, num_queues, Some(mtu), &mut avail_features);
|
||||
let (avail_features, acked_features, config, queue_sizes) = if let Some(state) = state {
|
||||
info!("Restoring virtio-net {}", id);
|
||||
(
|
||||
state.avail_features,
|
||||
state.acked_features,
|
||||
state.config,
|
||||
state.queue_size,
|
||||
)
|
||||
} else {
|
||||
build_net_config_space_with_mq(&mut config, num_queues, Some(mtu), &mut avail_features);
|
||||
}
|
||||
let mut avail_features = 1 << VIRTIO_NET_F_CSUM
|
||||
| 1 << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS
|
||||
| 1 << VIRTIO_NET_F_GUEST_CSUM
|
||||
| 1 << VIRTIO_NET_F_GUEST_ECN
|
||||
| 1 << VIRTIO_NET_F_GUEST_TSO4
|
||||
| 1 << VIRTIO_NET_F_GUEST_TSO6
|
||||
| 1 << VIRTIO_NET_F_GUEST_UFO
|
||||
| 1 << VIRTIO_NET_F_HOST_ECN
|
||||
| 1 << VIRTIO_NET_F_HOST_TSO4
|
||||
| 1 << VIRTIO_NET_F_HOST_TSO6
|
||||
| 1 << VIRTIO_NET_F_HOST_UFO
|
||||
| 1 << VIRTIO_NET_F_MTU
|
||||
| 1 << VIRTIO_RING_F_EVENT_IDX
|
||||
| 1 << VIRTIO_F_VERSION_1;
|
||||
|
||||
if iommu {
|
||||
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
|
||||
}
|
||||
|
||||
avail_features |= 1 << VIRTIO_NET_F_CTRL_VQ;
|
||||
let queue_num = num_queues + 1;
|
||||
|
||||
let mut config = VirtioNetConfig::default();
|
||||
if let Some(mac) = guest_mac {
|
||||
build_net_config_space(
|
||||
&mut config,
|
||||
mac,
|
||||
num_queues,
|
||||
Some(mtu),
|
||||
&mut avail_features,
|
||||
);
|
||||
} else {
|
||||
build_net_config_space_with_mq(
|
||||
&mut config,
|
||||
num_queues,
|
||||
Some(mtu),
|
||||
&mut avail_features,
|
||||
);
|
||||
}
|
||||
|
||||
(avail_features, 0, config, vec![queue_size; queue_num])
|
||||
};
|
||||
|
||||
Ok(Net {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Net as u32,
|
||||
avail_features,
|
||||
queue_sizes: vec![queue_size; queue_num],
|
||||
acked_features,
|
||||
queue_sizes,
|
||||
paused_sync: Some(Arc::new(Barrier::new((num_queues / 2) + 1))),
|
||||
min_queues: 2,
|
||||
..Default::default()
|
||||
@@ -512,6 +541,7 @@ impl Net {
|
||||
seccomp_action: SeccompAction,
|
||||
rate_limiter_config: Option<RateLimiterConfig>,
|
||||
exit_evt: EventFd,
|
||||
state: Option<NetState>,
|
||||
) -> Result<Self> {
|
||||
let taps = open_tap(
|
||||
if_name,
|
||||
@@ -534,6 +564,7 @@ impl Net {
|
||||
seccomp_action,
|
||||
rate_limiter_config,
|
||||
exit_evt,
|
||||
state,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -548,6 +579,7 @@ impl Net {
|
||||
seccomp_action: SeccompAction,
|
||||
rate_limiter_config: Option<RateLimiterConfig>,
|
||||
exit_evt: EventFd,
|
||||
state: Option<NetState>,
|
||||
) -> Result<Self> {
|
||||
let mut taps: Vec<Tap> = Vec::new();
|
||||
let num_queue_pairs = fds.len();
|
||||
@@ -579,6 +611,7 @@ impl Net {
|
||||
seccomp_action,
|
||||
rate_limiter_config,
|
||||
exit_evt,
|
||||
state,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -590,13 +623,6 @@ impl Net {
|
||||
queue_size: self.common.queue_sizes.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Net {
|
||||
@@ -605,6 +631,8 @@ impl Drop for Net {
|
||||
// Ignore the result because there is nothing we can do about it.
|
||||
let _ = kill_evt.write(1);
|
||||
}
|
||||
// Needed to ensure all references to tap FDs are dropped (#4868)
|
||||
self.common.wait_for_epoll_threads();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -686,11 +714,11 @@ impl VirtioDevice for Net {
|
||||
|
||||
let (_, queue_0, queue_evt_0) = queues.remove(0);
|
||||
let (_, queue_1, queue_evt_1) = queues.remove(0);
|
||||
let mut queue_pair = vec![queue_0, queue_1];
|
||||
queue_pair[0].set_event_idx(event_idx);
|
||||
queue_pair[1].set_event_idx(event_idx);
|
||||
let mut queue_pair = (queue_0, queue_1);
|
||||
queue_pair.0.set_event_idx(event_idx);
|
||||
queue_pair.1.set_event_idx(event_idx);
|
||||
|
||||
let queue_evt_pair = vec![queue_evt_0, queue_evt_1];
|
||||
let queue_evt_pair = (queue_evt_0, queue_evt_1);
|
||||
|
||||
let (kill_evt, pause_evt) = self.common.dup_eventfds();
|
||||
|
||||
@@ -816,11 +844,6 @@ impl Snapshottable for Net {
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
Snapshot::new_from_versioned_state(&self.id, &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for Net {}
|
||||
impl Migratable for Net {}
|
||||
|
||||
@@ -299,24 +299,32 @@ impl Pmem {
|
||||
iommu: bool,
|
||||
seccomp_action: SeccompAction,
|
||||
exit_evt: EventFd,
|
||||
state: Option<PmemState>,
|
||||
) -> io::Result<Pmem> {
|
||||
let config = VirtioPmemConfig {
|
||||
start: addr.raw_value().to_le(),
|
||||
size: (_region.size() as u64).to_le(),
|
||||
let (avail_features, acked_features, config) = if let Some(state) = state {
|
||||
info!("Restoring virtio-pmem {}", id);
|
||||
(state.avail_features, state.acked_features, state.config)
|
||||
} else {
|
||||
let config = VirtioPmemConfig {
|
||||
start: addr.raw_value().to_le(),
|
||||
size: (_region.size() as u64).to_le(),
|
||||
};
|
||||
|
||||
let mut avail_features = 1u64 << VIRTIO_F_VERSION_1;
|
||||
|
||||
if iommu {
|
||||
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
|
||||
}
|
||||
(avail_features, 0, config)
|
||||
};
|
||||
|
||||
let mut avail_features = 1u64 << VIRTIO_F_VERSION_1;
|
||||
|
||||
if iommu {
|
||||
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
|
||||
}
|
||||
|
||||
Ok(Pmem {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Pmem as u32,
|
||||
queue_sizes: QUEUE_SIZES.to_vec(),
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
avail_features,
|
||||
acked_features,
|
||||
min_queues: 1,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -338,12 +346,6 @@ impl Pmem {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#[cfg(fuzzing)]
|
||||
pub fn wait_for_epoll_threads(&mut self) {
|
||||
self.common.wait_for_epoll_threads();
|
||||
@@ -461,11 +463,6 @@ impl Snapshottable for Pmem {
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
Snapshot::new_from_versioned_state(&self.id, &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Transportable for Pmem {}
|
||||
|
||||
@@ -42,8 +42,8 @@ enum Error {
|
||||
DescriptorChainTooShort,
|
||||
#[error("Invalid descriptor")]
|
||||
InvalidDescriptor,
|
||||
#[error("Failed read from guest memory: {0}")]
|
||||
GuestMemoryRead(vm_memory::guest_memory::Error),
|
||||
#[error("Failed to write to guest memory: {0}")]
|
||||
GuestMemoryWrite(vm_memory::guest_memory::Error),
|
||||
#[error("Failed adding used index: {0}")]
|
||||
QueueAddUsed(virtio_queue::Error),
|
||||
}
|
||||
@@ -81,7 +81,7 @@ impl RngEpollHandler {
|
||||
&mut self.random_file,
|
||||
desc.len() as usize,
|
||||
)
|
||||
.map_err(Error::GuestMemoryRead)?;
|
||||
.map_err(Error::GuestMemoryWrite)?;
|
||||
|
||||
queue
|
||||
.add_used(desc_chain.memory(), desc_chain.head_index(), len as u32)
|
||||
@@ -174,13 +174,22 @@ impl Rng {
|
||||
iommu: bool,
|
||||
seccomp_action: SeccompAction,
|
||||
exit_evt: EventFd,
|
||||
state: Option<RngState>,
|
||||
) -> io::Result<Rng> {
|
||||
let random_file = File::open(path)?;
|
||||
let mut avail_features = 1u64 << VIRTIO_F_VERSION_1;
|
||||
|
||||
if iommu {
|
||||
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
|
||||
}
|
||||
let (avail_features, acked_features) = if let Some(state) = state {
|
||||
info!("Restoring virtio-rng {}", id);
|
||||
(state.avail_features, state.acked_features)
|
||||
} else {
|
||||
let mut avail_features = 1u64 << VIRTIO_F_VERSION_1;
|
||||
|
||||
if iommu {
|
||||
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
|
||||
}
|
||||
|
||||
(avail_features, 0)
|
||||
};
|
||||
|
||||
Ok(Rng {
|
||||
common: VirtioCommon {
|
||||
@@ -188,6 +197,7 @@ impl Rng {
|
||||
queue_sizes: QUEUE_SIZES.to_vec(),
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
avail_features,
|
||||
acked_features,
|
||||
min_queues: 1,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -205,11 +215,6 @@ impl Rng {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &RngState) {
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
}
|
||||
|
||||
#[cfg(fuzzing)]
|
||||
pub fn wait_for_epoll_threads(&mut self) {
|
||||
self.common.wait_for_epoll_threads();
|
||||
@@ -319,11 +324,6 @@ impl Snapshottable for Rng {
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
Snapshot::new_from_versioned_state(&self.id, &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Transportable for Rng {}
|
||||
|
||||
@@ -88,7 +88,6 @@ fn virtio_block_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
||||
(libc::SYS_getrandom, vec![]),
|
||||
(libc::SYS_io_uring_enter, vec![]),
|
||||
(libc::SYS_lseek, vec![]),
|
||||
(libc::SYS_mprotect, vec![]),
|
||||
(libc::SYS_prctl, vec![]),
|
||||
(libc::SYS_pread64, vec![]),
|
||||
(libc::SYS_preadv, vec![]),
|
||||
@@ -103,7 +102,6 @@ fn virtio_block_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
||||
fn virtio_console_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
||||
vec![
|
||||
(libc::SYS_ioctl, create_virtio_console_ioctl_seccomp_rule()),
|
||||
(libc::SYS_mprotect, vec![]),
|
||||
(libc::SYS_prctl, vec![]),
|
||||
(libc::SYS_sched_getaffinity, vec![]),
|
||||
(libc::SYS_set_robust_list, vec![]),
|
||||
@@ -111,10 +109,7 @@ fn virtio_console_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
||||
}
|
||||
|
||||
fn virtio_iommu_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
||||
vec![
|
||||
(libc::SYS_ioctl, create_virtio_iommu_ioctl_seccomp_rule()),
|
||||
(libc::SYS_mprotect, vec![]),
|
||||
]
|
||||
vec![(libc::SYS_ioctl, create_virtio_iommu_ioctl_seccomp_rule())]
|
||||
}
|
||||
|
||||
fn virtio_mem_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
||||
@@ -148,7 +143,6 @@ fn virtio_pmem_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
||||
|
||||
fn virtio_rng_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
||||
vec![
|
||||
(libc::SYS_mprotect, vec![]),
|
||||
(libc::SYS_prctl, vec![]),
|
||||
(libc::SYS_sched_getaffinity, vec![]),
|
||||
(libc::SYS_set_robust_list, vec![]),
|
||||
@@ -209,7 +203,6 @@ fn virtio_vsock_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
||||
|
||||
fn virtio_watchdog_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
||||
vec![
|
||||
(libc::SYS_mprotect, vec![]),
|
||||
(libc::SYS_prctl, vec![]),
|
||||
(libc::SYS_sched_getaffinity, vec![]),
|
||||
(libc::SYS_set_robust_list, vec![]),
|
||||
@@ -254,6 +247,7 @@ fn virtio_thread_common() -> Vec<(i64, Vec<SeccompRule>)> {
|
||||
(libc::SYS_futex, vec![]),
|
||||
(libc::SYS_madvise, vec![]),
|
||||
(libc::SYS_mmap, vec![]),
|
||||
(libc::SYS_mprotect, vec![]),
|
||||
(libc::SYS_munmap, vec![]),
|
||||
(libc::SYS_openat, vec![]),
|
||||
(libc::SYS_read, vec![]),
|
||||
|
||||
@@ -36,7 +36,7 @@ use vm_device::dma_mapping::ExternalDmaMapping;
|
||||
use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig,
|
||||
};
|
||||
use vm_device::{BusDevice, Resource};
|
||||
use vm_device::{BusDevice, PciBarType, Resource};
|
||||
use vm_memory::{Address, ByteValued, GuestAddress, GuestAddressSpace, GuestMemoryAtomic, Le32};
|
||||
use vm_migration::{
|
||||
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, VersionMapped,
|
||||
@@ -882,11 +882,22 @@ impl PciDevice for VirtioPciDevice {
|
||||
let device = device_clone.lock().unwrap();
|
||||
|
||||
let mut settings_bar_addr = None;
|
||||
if let Some(resources) = &resources {
|
||||
let mut use_64bit_bar = self.use_64bit_bar;
|
||||
if let Some(resources) = resources {
|
||||
for resource in resources {
|
||||
if let Resource::PciBar { index, base, .. } = resource {
|
||||
if *index == VIRTIO_COMMON_BAR_INDEX {
|
||||
settings_bar_addr = Some(GuestAddress(*base));
|
||||
if let Resource::PciBar {
|
||||
index, base, type_, ..
|
||||
} = resource
|
||||
{
|
||||
if index == VIRTIO_COMMON_BAR_INDEX {
|
||||
settings_bar_addr = Some(GuestAddress(base));
|
||||
use_64bit_bar = match type_ {
|
||||
PciBarType::Io => {
|
||||
return Err(PciDeviceError::InvalidResource(resource))
|
||||
}
|
||||
PciBarType::Mmio32 => false,
|
||||
PciBarType::Mmio64 => true,
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -899,7 +910,7 @@ impl PciDevice for VirtioPciDevice {
|
||||
|
||||
// Allocate the virtio-pci capability BAR.
|
||||
// See http://docs.oasis-open.org/virtio/virtio/v1.0/cs04/virtio-v1.0-cs04.html#x1-740004
|
||||
let (virtio_pci_bar_addr, region_type) = if self.use_64bit_bar {
|
||||
let (virtio_pci_bar_addr, region_type) = if use_64bit_bar {
|
||||
let region_type = PciBarRegionType::Memory64BitRegion;
|
||||
let addr = mmio_allocator
|
||||
.allocate(
|
||||
|
||||
@@ -8,20 +8,27 @@ use crate::{
|
||||
VirtioInterruptType, DEVICE_ACKNOWLEDGE, DEVICE_DRIVER, DEVICE_DRIVER_OK, DEVICE_FEATURES_OK,
|
||||
VIRTIO_F_IOMMU_PLATFORM,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
io, result,
|
||||
sync::{atomic::Ordering, Arc, Mutex},
|
||||
};
|
||||
use thiserror::Error;
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use vhost::{
|
||||
vdpa::{VhostVdpa, VhostVdpaIovaRange},
|
||||
vhost_kern::vdpa::VhostKernVdpa,
|
||||
vhost_kern::VhostKernFeatures,
|
||||
vhost_kern::{vdpa::VhostKernVdpa, vhost_binding::VHOST_BACKEND_F_SUSPEND},
|
||||
VhostBackend, VringConfigData,
|
||||
};
|
||||
use virtio_queue::{Descriptor, Queue, QueueT};
|
||||
use vm_device::dma_mapping::ExternalDmaMapping;
|
||||
use vm_memory::{GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic};
|
||||
use vm_migration::{
|
||||
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, VersionMapped,
|
||||
};
|
||||
use vm_virtio::{AccessPlatform, Translatable};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
@@ -37,6 +44,8 @@ pub enum Error {
|
||||
GetAddressRange,
|
||||
#[error("Failed to get the available index from the virtio queue: {0}")]
|
||||
GetAvailableIndex(virtio_queue::Error),
|
||||
#[error("Get virtio configuration size: {0}")]
|
||||
GetConfigSize(vhost::Error),
|
||||
#[error("Get virtio device identifier: {0}")]
|
||||
GetDeviceId(vhost::Error),
|
||||
#[error("Failed to get backend specific features: {0}")]
|
||||
@@ -55,6 +64,8 @@ pub enum Error {
|
||||
ResetOwner(vhost::Error),
|
||||
#[error("Failed to set backend specific features: {0}")]
|
||||
SetBackendFeatures(vhost::Error),
|
||||
#[error("Failed to set backend configuration: {0}")]
|
||||
SetConfig(vhost::Error),
|
||||
#[error("Failed to set eventfd notifying about a configuration change: {0}")]
|
||||
SetConfigCall(vhost::Error),
|
||||
#[error("Failed to set virtio features: {0}")]
|
||||
@@ -81,13 +92,28 @@ pub enum Error {
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Versionize)]
|
||||
pub struct VdpaState {
|
||||
pub avail_features: u64,
|
||||
pub acked_features: u64,
|
||||
pub device_type: u32,
|
||||
pub iova_range_first: u64,
|
||||
pub iova_range_last: u64,
|
||||
pub config: Vec<u8>,
|
||||
pub queue_sizes: Vec<u16>,
|
||||
pub backend_features: u64,
|
||||
}
|
||||
|
||||
impl VersionMapped for VdpaState {}
|
||||
|
||||
pub struct Vdpa {
|
||||
common: VirtioCommon,
|
||||
id: String,
|
||||
vhost: VhostKernVdpa<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
vhost: Option<VhostKernVdpa<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
iova_range: VhostVdpaIovaRange,
|
||||
enabled_num_queues: Option<usize>,
|
||||
enabled_queues: BTreeMap<usize, bool>,
|
||||
backend_features: u64,
|
||||
migrating: bool,
|
||||
}
|
||||
|
||||
impl Vdpa {
|
||||
@@ -96,47 +122,93 @@ impl Vdpa {
|
||||
device_path: &str,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
num_queues: u16,
|
||||
state: Option<VdpaState>,
|
||||
) -> Result<Self> {
|
||||
let mut vhost = VhostKernVdpa::new(device_path, mem).map_err(Error::CreateVhostVdpa)?;
|
||||
vhost.set_owner().map_err(Error::SetOwner)?;
|
||||
let device_type = vhost.get_device_id().map_err(Error::GetDeviceId)?;
|
||||
let queue_size = vhost.get_vring_num().map_err(Error::GetVringNum)?;
|
||||
let avail_features = vhost.get_features().map_err(Error::GetFeatures)?;
|
||||
let backend_features = vhost
|
||||
.get_backend_features()
|
||||
.map_err(Error::GetBackendFeatures)?;
|
||||
vhost.set_backend_features_acked(backend_features);
|
||||
|
||||
let iova_range = vhost.get_iova_range().map_err(Error::GetIovaRange)?;
|
||||
let (
|
||||
device_type,
|
||||
avail_features,
|
||||
acked_features,
|
||||
queue_sizes,
|
||||
iova_range,
|
||||
backend_features,
|
||||
) = if let Some(state) = state {
|
||||
info!("Restoring vDPA {}", id);
|
||||
|
||||
if avail_features & (1u64 << VIRTIO_F_IOMMU_PLATFORM) == 0 {
|
||||
return Err(Error::MissingAccessPlatformVirtioFeature);
|
||||
}
|
||||
vhost.set_backend_features_acked(state.backend_features);
|
||||
vhost
|
||||
.set_config(0, state.config.as_slice())
|
||||
.map_err(Error::SetConfig)?;
|
||||
|
||||
(
|
||||
state.device_type,
|
||||
state.avail_features,
|
||||
state.acked_features,
|
||||
state.queue_sizes,
|
||||
VhostVdpaIovaRange {
|
||||
first: state.iova_range_first,
|
||||
last: state.iova_range_last,
|
||||
},
|
||||
state.backend_features,
|
||||
)
|
||||
} else {
|
||||
let device_type = vhost.get_device_id().map_err(Error::GetDeviceId)?;
|
||||
let queue_size = vhost.get_vring_num().map_err(Error::GetVringNum)?;
|
||||
let avail_features = vhost.get_features().map_err(Error::GetFeatures)?;
|
||||
let backend_features = vhost
|
||||
.get_backend_features()
|
||||
.map_err(Error::GetBackendFeatures)?;
|
||||
vhost.set_backend_features_acked(backend_features);
|
||||
|
||||
let iova_range = vhost.get_iova_range().map_err(Error::GetIovaRange)?;
|
||||
|
||||
if avail_features & (1u64 << VIRTIO_F_IOMMU_PLATFORM) == 0 {
|
||||
return Err(Error::MissingAccessPlatformVirtioFeature);
|
||||
}
|
||||
|
||||
(
|
||||
device_type,
|
||||
avail_features,
|
||||
0,
|
||||
vec![queue_size; num_queues as usize],
|
||||
iova_range,
|
||||
backend_features,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(Vdpa {
|
||||
common: VirtioCommon {
|
||||
device_type,
|
||||
queue_sizes: vec![queue_size; num_queues as usize],
|
||||
queue_sizes,
|
||||
avail_features,
|
||||
acked_features,
|
||||
min_queues: num_queues,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
vhost,
|
||||
vhost: Some(vhost),
|
||||
iova_range,
|
||||
enabled_num_queues: None,
|
||||
enabled_queues: BTreeMap::new(),
|
||||
backend_features,
|
||||
migrating: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn enable_vrings(&mut self, num_queues: usize, enable: bool) -> Result<()> {
|
||||
for queue_index in 0..num_queues {
|
||||
self.vhost
|
||||
.set_vring_enable(queue_index, enable)
|
||||
.map_err(Error::SetVringEnable)?;
|
||||
}
|
||||
fn enable_vrings(&mut self, enable: bool) -> Result<()> {
|
||||
assert!(self.vhost.is_some());
|
||||
|
||||
self.enabled_num_queues = if enable { Some(num_queues) } else { None };
|
||||
for (queue_index, enabled) in self.enabled_queues.iter_mut() {
|
||||
if *enabled != enable {
|
||||
self.vhost
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_vring_enable(*queue_index, enable)
|
||||
.map_err(Error::SetVringEnable)?;
|
||||
*enabled = enable;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -147,10 +219,15 @@ impl Vdpa {
|
||||
virtio_interrupt: &Arc<dyn VirtioInterrupt>,
|
||||
queues: Vec<(usize, Queue, EventFd)>,
|
||||
) -> Result<()> {
|
||||
assert!(self.vhost.is_some());
|
||||
self.vhost
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_features(self.common.acked_features)
|
||||
.map_err(Error::SetFeatures)?;
|
||||
self.vhost
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.set_backend_features(self.backend_features)
|
||||
.map_err(Error::SetBackendFeatures)?;
|
||||
|
||||
@@ -158,6 +235,8 @@ impl Vdpa {
|
||||
let queue_max_size = queue.max_size();
|
||||
let queue_size = queue.size();
|
||||
self.vhost
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_vring_num(*queue_index, queue_size)
|
||||
.map_err(Error::SetVringNum)?;
|
||||
|
||||
@@ -181,9 +260,13 @@ impl Vdpa {
|
||||
};
|
||||
|
||||
self.vhost
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_vring_addr(*queue_index, &config_data)
|
||||
.map_err(Error::SetVringAddr)?;
|
||||
self.vhost
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_vring_base(
|
||||
*queue_index,
|
||||
queue
|
||||
@@ -197,25 +280,35 @@ impl Vdpa {
|
||||
virtio_interrupt.notifier(VirtioInterruptType::Queue(*queue_index as u16))
|
||||
{
|
||||
self.vhost
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_vring_call(*queue_index, &eventfd)
|
||||
.map_err(Error::SetVringCall)?;
|
||||
}
|
||||
|
||||
self.vhost
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_vring_kick(*queue_index, queue_evt)
|
||||
.map_err(Error::SetVringKick)?;
|
||||
|
||||
self.enabled_queues.insert(*queue_index, false);
|
||||
}
|
||||
|
||||
// Setup the config eventfd if there is one
|
||||
if let Some(eventfd) = virtio_interrupt.notifier(VirtioInterruptType::Config) {
|
||||
self.vhost
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_config_call(&eventfd)
|
||||
.map_err(Error::SetConfigCall)?;
|
||||
}
|
||||
|
||||
self.enable_vrings(queues.len(), true)?;
|
||||
self.enable_vrings(true)?;
|
||||
|
||||
self.vhost
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_status(
|
||||
(DEVICE_ACKNOWLEDGE | DEVICE_DRIVER | DEVICE_DRIVER_OK | DEVICE_FEATURES_OK) as u8,
|
||||
)
|
||||
@@ -223,20 +316,32 @@ impl Vdpa {
|
||||
}
|
||||
|
||||
fn reset_vdpa(&mut self) -> Result<()> {
|
||||
if let Some(num_queues) = self.enabled_num_queues {
|
||||
self.enable_vrings(num_queues, false)?;
|
||||
}
|
||||
self.enable_vrings(false)?;
|
||||
|
||||
self.vhost.set_status(0).map_err(Error::SetStatus)
|
||||
assert!(self.vhost.is_some());
|
||||
self.vhost
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_status(0)
|
||||
.map_err(Error::SetStatus)
|
||||
}
|
||||
|
||||
fn dma_map(&self, iova: u64, size: u64, host_vaddr: *const u8, readonly: bool) -> Result<()> {
|
||||
fn dma_map(
|
||||
&mut self,
|
||||
iova: u64,
|
||||
size: u64,
|
||||
host_vaddr: *const u8,
|
||||
readonly: bool,
|
||||
) -> Result<()> {
|
||||
let iova_last = iova + size - 1;
|
||||
if iova < self.iova_range.first || iova_last > self.iova_range.last {
|
||||
return Err(Error::InvalidIovaRange(iova, iova_last));
|
||||
}
|
||||
|
||||
assert!(self.vhost.is_some());
|
||||
self.vhost
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.dma_map(iova, size, host_vaddr, readonly)
|
||||
.map_err(Error::DmaMap)
|
||||
}
|
||||
@@ -247,7 +352,35 @@ impl Vdpa {
|
||||
return Err(Error::InvalidIovaRange(iova, iova_last));
|
||||
}
|
||||
|
||||
self.vhost.dma_unmap(iova, size).map_err(Error::DmaUnmap)
|
||||
assert!(self.vhost.is_some());
|
||||
self.vhost
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.dma_unmap(iova, size)
|
||||
.map_err(Error::DmaUnmap)
|
||||
}
|
||||
|
||||
fn state(&self) -> Result<VdpaState> {
|
||||
assert!(self.vhost.is_some());
|
||||
let config_size = self
|
||||
.vhost
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get_config_size()
|
||||
.map_err(Error::GetConfigSize)?;
|
||||
let mut config = vec![0; config_size as usize];
|
||||
self.read_config(0, config.as_mut_slice());
|
||||
|
||||
Ok(VdpaState {
|
||||
avail_features: self.common.avail_features,
|
||||
acked_features: self.common.acked_features,
|
||||
device_type: self.common.device_type,
|
||||
queue_sizes: self.common.queue_sizes.clone(),
|
||||
iova_range_first: self.iova_range.first,
|
||||
iova_range_last: self.iova_range.last,
|
||||
config,
|
||||
backend_features: self.backend_features,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,13 +402,15 @@ impl VirtioDevice for Vdpa {
|
||||
}
|
||||
|
||||
fn read_config(&self, offset: u64, data: &mut [u8]) {
|
||||
if let Err(e) = self.vhost.get_config(offset as u32, data) {
|
||||
assert!(self.vhost.is_some());
|
||||
if let Err(e) = self.vhost.as_ref().unwrap().get_config(offset as u32, data) {
|
||||
error!("Failed reading virtio config: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_config(&mut self, offset: u64, data: &[u8]) {
|
||||
if let Err(e) = self.vhost.set_config(offset as u32, data) {
|
||||
assert!(self.vhost.is_some());
|
||||
if let Err(e) = self.vhost.as_ref().unwrap().set_config(offset as u32, data) {
|
||||
error!("Failed writing virtio config: {}", e);
|
||||
}
|
||||
}
|
||||
@@ -313,6 +448,81 @@ impl VirtioDevice for Vdpa {
|
||||
}
|
||||
}
|
||||
|
||||
impl Pausable for Vdpa {
|
||||
fn pause(&mut self) -> std::result::Result<(), MigratableError> {
|
||||
if !self.migrating {
|
||||
Err(MigratableError::Pause(anyhow!(
|
||||
"Can't pause a vDPA device outside live migration"
|
||||
)))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn resume(&mut self) -> std::result::Result<(), MigratableError> {
|
||||
if !self.migrating {
|
||||
Err(MigratableError::Resume(anyhow!(
|
||||
"Can't resume a vDPA device outside live migration"
|
||||
)))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Snapshottable for Vdpa {
|
||||
fn id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
if !self.migrating {
|
||||
return Err(MigratableError::Snapshot(anyhow!(
|
||||
"Can't snapshot a vDPA device outside live migration"
|
||||
)));
|
||||
}
|
||||
|
||||
let snapshot = Snapshot::new_from_versioned_state(
|
||||
&self.id(),
|
||||
&self.state().map_err(|e| {
|
||||
MigratableError::Snapshot(anyhow!("Error snapshotting vDPA device: {:?}", e))
|
||||
})?,
|
||||
)?;
|
||||
|
||||
// Force the vhost handler to be dropped in order to close the vDPA
|
||||
// file. This will ensure the device can be accessed if the VM is
|
||||
// migrated on the same host machine.
|
||||
self.vhost.take();
|
||||
|
||||
Ok(snapshot)
|
||||
}
|
||||
}
|
||||
|
||||
impl Transportable for Vdpa {}
|
||||
|
||||
impl Migratable for Vdpa {
|
||||
fn start_migration(&mut self) -> std::result::Result<(), MigratableError> {
|
||||
self.migrating = true;
|
||||
// Given there's no way to track dirty pages, we must suspend the
|
||||
// device as soon as the migration process starts.
|
||||
if self.backend_features & (1 << VHOST_BACKEND_F_SUSPEND) != 0 {
|
||||
assert!(self.vhost.is_some());
|
||||
self.vhost.as_ref().unwrap().suspend().map_err(|e| {
|
||||
MigratableError::StartMigration(anyhow!("Error suspending vDPA device: {:?}", e))
|
||||
})
|
||||
} else {
|
||||
Err(MigratableError::StartMigration(anyhow!(
|
||||
"vDPA device can't be suspended"
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
fn complete_migration(&mut self) -> std::result::Result<(), MigratableError> {
|
||||
self.migrating = false;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VdpaDmaMapping<M: GuestAddressSpace> {
|
||||
device: Arc<Mutex<Vdpa>>,
|
||||
memory: Arc<M>,
|
||||
|
||||
@@ -69,112 +69,113 @@ impl Blk {
|
||||
pub fn new(
|
||||
id: String,
|
||||
vu_cfg: VhostUserConfig,
|
||||
restoring: bool,
|
||||
seccomp_action: SeccompAction,
|
||||
exit_evt: EventFd,
|
||||
iommu: bool,
|
||||
state: Option<State>,
|
||||
) -> Result<Blk> {
|
||||
let num_queues = vu_cfg.num_queues;
|
||||
|
||||
if restoring {
|
||||
// We need 'queue_sizes' to report a number of queues that will be
|
||||
// enough to handle all the potential queues. VirtioPciDevice::new()
|
||||
// will create the actual queues based on this information.
|
||||
return Ok(Blk {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Block as u32,
|
||||
queue_sizes: vec![vu_cfg.queue_size; num_queues],
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
min_queues: DEFAULT_QUEUE_NUMBER as u16,
|
||||
..Default::default()
|
||||
},
|
||||
vu_common: VhostUserCommon {
|
||||
socket_path: vu_cfg.socket,
|
||||
vu_num_queues: num_queues,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
config: VirtioBlockConfig::default(),
|
||||
guest_memory: None,
|
||||
epoll_thread: None,
|
||||
seccomp_action,
|
||||
exit_evt,
|
||||
iommu,
|
||||
});
|
||||
}
|
||||
|
||||
let mut vu =
|
||||
VhostUserHandle::connect_vhost_user(false, &vu_cfg.socket, num_queues as u64, false)?;
|
||||
|
||||
// Filling device and vring features VMM supports.
|
||||
let mut avail_features = 1 << VIRTIO_BLK_F_SIZE_MAX
|
||||
| 1 << VIRTIO_BLK_F_SEG_MAX
|
||||
| 1 << VIRTIO_BLK_F_GEOMETRY
|
||||
| 1 << VIRTIO_BLK_F_RO
|
||||
| 1 << VIRTIO_BLK_F_BLK_SIZE
|
||||
| 1 << VIRTIO_BLK_F_FLUSH
|
||||
| 1 << VIRTIO_BLK_F_TOPOLOGY
|
||||
| 1 << VIRTIO_BLK_F_CONFIG_WCE
|
||||
| 1 << VIRTIO_BLK_F_DISCARD
|
||||
| 1 << VIRTIO_BLK_F_WRITE_ZEROES
|
||||
| DEFAULT_VIRTIO_FEATURES;
|
||||
let (avail_features, acked_features, acked_protocol_features, vu_num_queues, config) =
|
||||
if let Some(state) = state {
|
||||
info!("Restoring vhost-user-block {}", id);
|
||||
|
||||
if num_queues > 1 {
|
||||
avail_features |= 1 << VIRTIO_BLK_F_MQ;
|
||||
}
|
||||
vu.set_protocol_features_vhost_user(
|
||||
state.acked_features,
|
||||
state.acked_protocol_features,
|
||||
)?;
|
||||
|
||||
let avail_protocol_features = VhostUserProtocolFeatures::CONFIG
|
||||
| VhostUserProtocolFeatures::MQ
|
||||
| VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
|
||||
| VhostUserProtocolFeatures::REPLY_ACK
|
||||
| VhostUserProtocolFeatures::INFLIGHT_SHMFD
|
||||
| VhostUserProtocolFeatures::LOG_SHMFD;
|
||||
|
||||
let (acked_features, acked_protocol_features) =
|
||||
vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?;
|
||||
|
||||
let backend_num_queues =
|
||||
if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 {
|
||||
vu.socket_handle()
|
||||
.get_queue_num()
|
||||
.map_err(Error::VhostUserGetQueueMaxNum)? as usize
|
||||
(
|
||||
state.avail_features,
|
||||
state.acked_features,
|
||||
state.acked_protocol_features,
|
||||
state.vu_num_queues,
|
||||
state.config,
|
||||
)
|
||||
} else {
|
||||
DEFAULT_QUEUE_NUMBER
|
||||
};
|
||||
// Filling device and vring features VMM supports.
|
||||
let mut avail_features = 1 << VIRTIO_BLK_F_SIZE_MAX
|
||||
| 1 << VIRTIO_BLK_F_SEG_MAX
|
||||
| 1 << VIRTIO_BLK_F_GEOMETRY
|
||||
| 1 << VIRTIO_BLK_F_RO
|
||||
| 1 << VIRTIO_BLK_F_BLK_SIZE
|
||||
| 1 << VIRTIO_BLK_F_FLUSH
|
||||
| 1 << VIRTIO_BLK_F_TOPOLOGY
|
||||
| 1 << VIRTIO_BLK_F_CONFIG_WCE
|
||||
| 1 << VIRTIO_BLK_F_DISCARD
|
||||
| 1 << VIRTIO_BLK_F_WRITE_ZEROES
|
||||
| DEFAULT_VIRTIO_FEATURES;
|
||||
|
||||
if num_queues > backend_num_queues {
|
||||
error!("vhost-user-blk requested too many queues ({}) since the backend only supports {}\n",
|
||||
if num_queues > 1 {
|
||||
avail_features |= 1 << VIRTIO_BLK_F_MQ;
|
||||
}
|
||||
|
||||
let avail_protocol_features = VhostUserProtocolFeatures::CONFIG
|
||||
| VhostUserProtocolFeatures::MQ
|
||||
| VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
|
||||
| VhostUserProtocolFeatures::REPLY_ACK
|
||||
| VhostUserProtocolFeatures::INFLIGHT_SHMFD
|
||||
| VhostUserProtocolFeatures::LOG_SHMFD;
|
||||
|
||||
let (acked_features, acked_protocol_features) =
|
||||
vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?;
|
||||
|
||||
let backend_num_queues =
|
||||
if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 {
|
||||
vu.socket_handle()
|
||||
.get_queue_num()
|
||||
.map_err(Error::VhostUserGetQueueMaxNum)?
|
||||
as usize
|
||||
} else {
|
||||
DEFAULT_QUEUE_NUMBER
|
||||
};
|
||||
|
||||
if num_queues > backend_num_queues {
|
||||
error!("vhost-user-blk requested too many queues ({}) since the backend only supports {}\n",
|
||||
num_queues, backend_num_queues);
|
||||
return Err(Error::BadQueueNum);
|
||||
}
|
||||
return Err(Error::BadQueueNum);
|
||||
}
|
||||
|
||||
let config_len = mem::size_of::<VirtioBlockConfig>();
|
||||
let config_space: Vec<u8> = vec![0u8; config_len as usize];
|
||||
let (_, config_space) = vu
|
||||
.socket_handle()
|
||||
.get_config(
|
||||
VHOST_USER_CONFIG_OFFSET,
|
||||
config_len as u32,
|
||||
VhostUserConfigFlags::WRITABLE,
|
||||
config_space.as_slice(),
|
||||
)
|
||||
.map_err(Error::VhostUserGetConfig)?;
|
||||
let mut config = VirtioBlockConfig::default();
|
||||
if let Some(backend_config) = VirtioBlockConfig::from_slice(config_space.as_slice()) {
|
||||
config = *backend_config;
|
||||
config.num_queues = num_queues as u16;
|
||||
}
|
||||
let config_len = mem::size_of::<VirtioBlockConfig>();
|
||||
let config_space: Vec<u8> = vec![0u8; config_len];
|
||||
let (_, config_space) = vu
|
||||
.socket_handle()
|
||||
.get_config(
|
||||
VHOST_USER_CONFIG_OFFSET,
|
||||
config_len as u32,
|
||||
VhostUserConfigFlags::WRITABLE,
|
||||
config_space.as_slice(),
|
||||
)
|
||||
.map_err(Error::VhostUserGetConfig)?;
|
||||
let mut config = VirtioBlockConfig::default();
|
||||
if let Some(backend_config) = VirtioBlockConfig::from_slice(config_space.as_slice())
|
||||
{
|
||||
config = *backend_config;
|
||||
config.num_queues = num_queues as u16;
|
||||
}
|
||||
|
||||
(
|
||||
acked_features,
|
||||
// If part of the available features that have been acked,
|
||||
// the PROTOCOL_FEATURES bit must be already set through
|
||||
// the VIRTIO acked features as we know the guest would
|
||||
// never ack it, thus the feature would be lost.
|
||||
acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(),
|
||||
acked_protocol_features,
|
||||
num_queues,
|
||||
config,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(Blk {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Block as u32,
|
||||
queue_sizes: vec![vu_cfg.queue_size; num_queues],
|
||||
avail_features: acked_features,
|
||||
// If part of the available features that have been acked, the
|
||||
// PROTOCOL_FEATURES bit must be already set through the VIRTIO
|
||||
// acked features as we know the guest would never ack it, thus
|
||||
// the feature would be lost.
|
||||
acked_features: acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(),
|
||||
avail_features,
|
||||
acked_features,
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
min_queues: DEFAULT_QUEUE_NUMBER as u16,
|
||||
..Default::default()
|
||||
@@ -183,7 +184,7 @@ impl Blk {
|
||||
vu: Some(Arc::new(Mutex::new(vu))),
|
||||
acked_protocol_features,
|
||||
socket_path: vu_cfg.socket,
|
||||
vu_num_queues: num_queues,
|
||||
vu_num_queues,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
@@ -205,24 +206,6 @@ impl Blk {
|
||||
vu_num_queues: self.vu_common.vu_num_queues,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &State) {
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
self.config = state.config;
|
||||
self.vu_common.acked_protocol_features = state.acked_protocol_features;
|
||||
self.vu_common.vu_num_queues = state.vu_num_queues;
|
||||
|
||||
if let Err(e) = self
|
||||
.vu_common
|
||||
.restore_backend_connection(self.common.acked_features)
|
||||
{
|
||||
error!(
|
||||
"Failed restoring connection with vhost-user backend: {:?}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Blk {
|
||||
@@ -392,11 +375,6 @@ impl Snapshottable for Blk {
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
self.vu_common.snapshot(&self.id(), &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for Blk {}
|
||||
|
||||
|
||||
@@ -232,20 +232,13 @@ impl VhostUserMasterReqHandler for SlaveReqHandler {
|
||||
pwrite64(
|
||||
fd.as_raw_fd(),
|
||||
ptr as *const c_void,
|
||||
len as usize,
|
||||
len,
|
||||
foffset as off64_t,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
debug!("read: foffset={}, len={}", foffset, len);
|
||||
unsafe {
|
||||
pread64(
|
||||
fd.as_raw_fd(),
|
||||
ptr as *mut c_void,
|
||||
len as usize,
|
||||
foffset as off64_t,
|
||||
)
|
||||
}
|
||||
unsafe { pread64(fd.as_raw_fd(), ptr as *mut c_void, len, foffset as off64_t) }
|
||||
};
|
||||
|
||||
if ret < 0 {
|
||||
@@ -315,102 +308,107 @@ impl Fs {
|
||||
queue_size: u16,
|
||||
cache: Option<(VirtioSharedMemoryList, MmapRegion)>,
|
||||
seccomp_action: SeccompAction,
|
||||
restoring: bool,
|
||||
exit_evt: EventFd,
|
||||
iommu: bool,
|
||||
state: Option<State>,
|
||||
) -> Result<Fs> {
|
||||
let mut slave_req_support = false;
|
||||
|
||||
// Calculate the actual number of queues needed.
|
||||
let num_queues = NUM_QUEUE_OFFSET + req_num_queues;
|
||||
|
||||
if restoring {
|
||||
// We need 'queue_sizes' to report a number of queues that will be
|
||||
// enough to handle all the potential queues. VirtioPciDevice::new()
|
||||
// will create the actual queues based on this information.
|
||||
return Ok(Fs {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Fs as u32,
|
||||
queue_sizes: vec![queue_size; num_queues],
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
min_queues: 1,
|
||||
..Default::default()
|
||||
},
|
||||
vu_common: VhostUserCommon {
|
||||
socket_path: path.to_string(),
|
||||
vu_num_queues: num_queues,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
config: VirtioFsConfig::default(),
|
||||
cache,
|
||||
slave_req_support,
|
||||
seccomp_action,
|
||||
guest_memory: None,
|
||||
epoll_thread: None,
|
||||
exit_evt,
|
||||
iommu,
|
||||
});
|
||||
}
|
||||
|
||||
// Connect to the vhost-user socket.
|
||||
let mut vu = VhostUserHandle::connect_vhost_user(false, path, num_queues as u64, false)?;
|
||||
|
||||
// Filling device and vring features VMM supports.
|
||||
let avail_features = DEFAULT_VIRTIO_FEATURES;
|
||||
let (
|
||||
avail_features,
|
||||
acked_features,
|
||||
acked_protocol_features,
|
||||
vu_num_queues,
|
||||
config,
|
||||
slave_req_support,
|
||||
) = if let Some(state) = state {
|
||||
info!("Restoring vhost-user-fs {}", id);
|
||||
|
||||
let mut avail_protocol_features = VhostUserProtocolFeatures::MQ
|
||||
| VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
|
||||
| VhostUserProtocolFeatures::REPLY_ACK
|
||||
| VhostUserProtocolFeatures::INFLIGHT_SHMFD
|
||||
| VhostUserProtocolFeatures::LOG_SHMFD;
|
||||
let slave_protocol_features =
|
||||
VhostUserProtocolFeatures::SLAVE_REQ | VhostUserProtocolFeatures::SLAVE_SEND_FD;
|
||||
if cache.is_some() {
|
||||
avail_protocol_features |= slave_protocol_features;
|
||||
}
|
||||
vu.set_protocol_features_vhost_user(
|
||||
state.acked_features,
|
||||
state.acked_protocol_features,
|
||||
)?;
|
||||
|
||||
let (acked_features, acked_protocol_features) =
|
||||
vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?;
|
||||
(
|
||||
state.avail_features,
|
||||
state.acked_features,
|
||||
state.acked_protocol_features,
|
||||
state.vu_num_queues,
|
||||
state.config,
|
||||
state.slave_req_support,
|
||||
)
|
||||
} else {
|
||||
// Filling device and vring features VMM supports.
|
||||
let avail_features = DEFAULT_VIRTIO_FEATURES;
|
||||
|
||||
let backend_num_queues =
|
||||
if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 {
|
||||
vu.socket_handle()
|
||||
.get_queue_num()
|
||||
.map_err(Error::VhostUserGetQueueMaxNum)? as usize
|
||||
} else {
|
||||
DEFAULT_QUEUE_NUMBER
|
||||
};
|
||||
let mut avail_protocol_features = VhostUserProtocolFeatures::MQ
|
||||
| VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
|
||||
| VhostUserProtocolFeatures::REPLY_ACK
|
||||
| VhostUserProtocolFeatures::INFLIGHT_SHMFD
|
||||
| VhostUserProtocolFeatures::LOG_SHMFD;
|
||||
let slave_protocol_features =
|
||||
VhostUserProtocolFeatures::SLAVE_REQ | VhostUserProtocolFeatures::SLAVE_SEND_FD;
|
||||
if cache.is_some() {
|
||||
avail_protocol_features |= slave_protocol_features;
|
||||
}
|
||||
|
||||
if num_queues > backend_num_queues {
|
||||
error!(
|
||||
let (acked_features, acked_protocol_features) =
|
||||
vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?;
|
||||
|
||||
let backend_num_queues =
|
||||
if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 {
|
||||
vu.socket_handle()
|
||||
.get_queue_num()
|
||||
.map_err(Error::VhostUserGetQueueMaxNum)? as usize
|
||||
} else {
|
||||
DEFAULT_QUEUE_NUMBER
|
||||
};
|
||||
|
||||
if num_queues > backend_num_queues {
|
||||
error!(
|
||||
"vhost-user-fs requested too many queues ({}) since the backend only supports {}\n",
|
||||
num_queues, backend_num_queues
|
||||
);
|
||||
return Err(Error::BadQueueNum);
|
||||
}
|
||||
return Err(Error::BadQueueNum);
|
||||
}
|
||||
|
||||
if acked_protocol_features & slave_protocol_features.bits()
|
||||
== slave_protocol_features.bits()
|
||||
{
|
||||
slave_req_support = true;
|
||||
}
|
||||
if acked_protocol_features & slave_protocol_features.bits()
|
||||
== slave_protocol_features.bits()
|
||||
{
|
||||
slave_req_support = true;
|
||||
}
|
||||
|
||||
// Create virtio-fs device configuration.
|
||||
let mut config = VirtioFsConfig::default();
|
||||
let tag_bytes_vec = tag.to_string().into_bytes();
|
||||
config.tag[..tag_bytes_vec.len()].copy_from_slice(tag_bytes_vec.as_slice());
|
||||
config.num_request_queues = req_num_queues as u32;
|
||||
// Create virtio-fs device configuration.
|
||||
let mut config = VirtioFsConfig::default();
|
||||
let tag_bytes_vec = tag.to_string().into_bytes();
|
||||
config.tag[..tag_bytes_vec.len()].copy_from_slice(tag_bytes_vec.as_slice());
|
||||
config.num_request_queues = req_num_queues as u32;
|
||||
|
||||
Ok(Fs {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Fs as u32,
|
||||
avail_features: acked_features,
|
||||
(
|
||||
acked_features,
|
||||
// If part of the available features that have been acked, the
|
||||
// PROTOCOL_FEATURES bit must be already set through the VIRTIO
|
||||
// acked features as we know the guest would never ack it, thus
|
||||
// the feature would be lost.
|
||||
acked_features: acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(),
|
||||
acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(),
|
||||
acked_protocol_features,
|
||||
num_queues,
|
||||
config,
|
||||
slave_req_support,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(Fs {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Fs as u32,
|
||||
avail_features,
|
||||
acked_features,
|
||||
queue_sizes: vec![queue_size; num_queues],
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
min_queues: 1,
|
||||
@@ -420,7 +418,7 @@ impl Fs {
|
||||
vu: Some(Arc::new(Mutex::new(vu))),
|
||||
acked_protocol_features,
|
||||
socket_path: path.to_string(),
|
||||
vu_num_queues: num_queues,
|
||||
vu_num_queues,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
@@ -445,25 +443,6 @@ impl Fs {
|
||||
slave_req_support: self.slave_req_support,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &State) {
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
self.config = state.config;
|
||||
self.vu_common.acked_protocol_features = state.acked_protocol_features;
|
||||
self.vu_common.vu_num_queues = state.vu_num_queues;
|
||||
self.slave_req_support = state.slave_req_support;
|
||||
|
||||
if let Err(e) = self
|
||||
.vu_common
|
||||
.restore_backend_connection(self.common.acked_features)
|
||||
{
|
||||
error!(
|
||||
"Failed restoring connection with vhost-user backend: {:?}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Fs {
|
||||
@@ -663,11 +642,6 @@ impl Snapshottable for Fs {
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
self.vu_common.snapshot(&self.id(), &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for Fs {}
|
||||
|
||||
|
||||
@@ -43,9 +43,9 @@ pub use self::vu_common_ctrl::VhostUserConfig;
|
||||
pub enum Error {
|
||||
#[error("Failed accepting connection: {0}")]
|
||||
AcceptConnection(io::Error),
|
||||
#[error("Invalid available address.")]
|
||||
#[error("Invalid available address")]
|
||||
AvailAddress,
|
||||
#[error("Queue number is not correct.")]
|
||||
#[error("Queue number is not correct")]
|
||||
BadQueueNum,
|
||||
#[error("Failed binding vhost-user socket: {0}")]
|
||||
BindSocket(io::Error),
|
||||
@@ -53,7 +53,7 @@ pub enum Error {
|
||||
CreateKillEventFd(io::Error),
|
||||
#[error("Cloning kill eventfd failed: {0}")]
|
||||
CloneKillEventFd(io::Error),
|
||||
#[error("Invalid descriptor table address.")]
|
||||
#[error("Invalid descriptor table address")]
|
||||
DescriptorTableAddress,
|
||||
#[error("Signal used queue failed: {0}")]
|
||||
FailedSignalingUsedQueue(io::Error),
|
||||
@@ -65,7 +65,7 @@ pub enum Error {
|
||||
VhostUserCreateMaster(VhostError),
|
||||
#[error("Failed to open vhost device: {0}")]
|
||||
VhostUserOpen(VhostError),
|
||||
#[error("Connection to socket failed.")]
|
||||
#[error("Connection to socket failed")]
|
||||
VhostUserConnect,
|
||||
#[error("Get features failed: {0}")]
|
||||
VhostUserGetFeatures(VhostError),
|
||||
@@ -75,7 +75,7 @@ pub enum Error {
|
||||
VhostUserGetProtocolFeatures(VhostError),
|
||||
#[error("Get vring base failed: {0}")]
|
||||
VhostUserGetVringBase(VhostError),
|
||||
#[error("Vhost-user Backend not support vhost-user protocol.")]
|
||||
#[error("Vhost-user Backend not support vhost-user protocol")]
|
||||
VhostUserProtocolNotSupport,
|
||||
#[error("Set owner failed: {0}")]
|
||||
VhostUserSetOwner(VhostError),
|
||||
@@ -121,17 +121,17 @@ pub enum Error {
|
||||
VhostUserSetInflight(VhostError),
|
||||
#[error("Failed setting the log base: {0}")]
|
||||
VhostUserSetLogBase(VhostError),
|
||||
#[error("Invalid used address.")]
|
||||
#[error("Invalid used address")]
|
||||
UsedAddress,
|
||||
#[error("Invalid features provided from vhost-user backe.")]
|
||||
#[error("Invalid features provided from vhost-user backe")]
|
||||
InvalidFeatures,
|
||||
#[error("Missing file descriptor for the region.")]
|
||||
#[error("Missing file descriptor for the region")]
|
||||
MissingRegionFd,
|
||||
#[error("Missing IrqFd.")]
|
||||
#[error("Missing IrqFd")]
|
||||
MissingIrqFd,
|
||||
#[error("Failed getting the available index: {0}")]
|
||||
GetAvailableIndex(QueueError),
|
||||
#[error("Migration is not supported by this vhost-user device.")]
|
||||
#[error("Migration is not supported by this vhost-user device")]
|
||||
MigrationNotSupported,
|
||||
#[error("Failed creating memfd: {0}")]
|
||||
MemfdCreate(io::Error),
|
||||
@@ -141,7 +141,7 @@ pub enum Error {
|
||||
SetSeals(io::Error),
|
||||
#[error("Failed creating new mmap region: {0}")]
|
||||
NewMmapRegion(MmapRegionError),
|
||||
#[error("Could not find the shm log region.")]
|
||||
#[error("Could not find the shm log region")]
|
||||
MissingShmLogRegion,
|
||||
}
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
@@ -74,115 +74,123 @@ impl Net {
|
||||
vu_cfg: VhostUserConfig,
|
||||
server: bool,
|
||||
seccomp_action: SeccompAction,
|
||||
restoring: bool,
|
||||
exit_evt: EventFd,
|
||||
iommu: bool,
|
||||
state: Option<State>,
|
||||
) -> Result<Net> {
|
||||
let mut num_queues = vu_cfg.num_queues;
|
||||
|
||||
if restoring {
|
||||
// We need 'queue_sizes' to report a number of queues that will be
|
||||
// enough to handle all the potential queues. Including the control
|
||||
// queue (with +1) will guarantee that. VirtioPciDevice::new() will
|
||||
// create the actual queues based on this information.
|
||||
return Ok(Net {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Net as u32,
|
||||
queue_sizes: vec![vu_cfg.queue_size; num_queues + 1],
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
min_queues: DEFAULT_QUEUE_NUMBER as u16,
|
||||
..Default::default()
|
||||
},
|
||||
vu_common: VhostUserCommon {
|
||||
socket_path: vu_cfg.socket,
|
||||
vu_num_queues: num_queues,
|
||||
server,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
config: VirtioNetConfig::default(),
|
||||
guest_memory: None,
|
||||
ctrl_queue_epoll_thread: None,
|
||||
epoll_thread: None,
|
||||
seccomp_action,
|
||||
exit_evt,
|
||||
iommu,
|
||||
});
|
||||
}
|
||||
|
||||
// Filling device and vring features VMM supports.
|
||||
let mut avail_features = 1 << VIRTIO_NET_F_CSUM
|
||||
| 1 << VIRTIO_NET_F_GUEST_CSUM
|
||||
| 1 << VIRTIO_NET_F_GUEST_TSO4
|
||||
| 1 << VIRTIO_NET_F_GUEST_TSO6
|
||||
| 1 << VIRTIO_NET_F_GUEST_ECN
|
||||
| 1 << VIRTIO_NET_F_GUEST_UFO
|
||||
| 1 << VIRTIO_NET_F_HOST_TSO4
|
||||
| 1 << VIRTIO_NET_F_HOST_TSO6
|
||||
| 1 << VIRTIO_NET_F_HOST_ECN
|
||||
| 1 << VIRTIO_NET_F_HOST_UFO
|
||||
| 1 << VIRTIO_NET_F_MRG_RXBUF
|
||||
| 1 << VIRTIO_NET_F_CTRL_VQ
|
||||
| 1 << VIRTIO_F_RING_EVENT_IDX
|
||||
| 1 << VIRTIO_F_VERSION_1
|
||||
| VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
|
||||
|
||||
if mtu.is_some() {
|
||||
avail_features |= 1u64 << VIRTIO_NET_F_MTU;
|
||||
}
|
||||
|
||||
let mut config = VirtioNetConfig::default();
|
||||
build_net_config_space(&mut config, mac_addr, num_queues, mtu, &mut avail_features);
|
||||
|
||||
let mut vu =
|
||||
VhostUserHandle::connect_vhost_user(server, &vu_cfg.socket, num_queues as u64, false)?;
|
||||
|
||||
let avail_protocol_features = VhostUserProtocolFeatures::MQ
|
||||
| VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
|
||||
| VhostUserProtocolFeatures::REPLY_ACK
|
||||
| VhostUserProtocolFeatures::INFLIGHT_SHMFD
|
||||
| VhostUserProtocolFeatures::LOG_SHMFD;
|
||||
let (avail_features, acked_features, acked_protocol_features, vu_num_queues, config) =
|
||||
if let Some(state) = state {
|
||||
info!("Restoring vhost-user-net {}", id);
|
||||
|
||||
let (mut acked_features, acked_protocol_features) =
|
||||
vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?;
|
||||
// The backend acknowledged features must not contain
|
||||
// VIRTIO_NET_F_MAC since we don't expect the backend
|
||||
// to handle it.
|
||||
let backend_acked_features = state.acked_features & !(1 << VIRTIO_NET_F_MAC);
|
||||
|
||||
let backend_num_queues =
|
||||
if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 {
|
||||
vu.socket_handle()
|
||||
.get_queue_num()
|
||||
.map_err(Error::VhostUserGetQueueMaxNum)? as usize
|
||||
vu.set_protocol_features_vhost_user(
|
||||
backend_acked_features,
|
||||
state.acked_protocol_features,
|
||||
)?;
|
||||
|
||||
// If the control queue feature has been negotiated, let's
|
||||
// increase the number of queues.
|
||||
if state.acked_features & (1 << VIRTIO_NET_F_CTRL_VQ) != 0 {
|
||||
num_queues += 1;
|
||||
}
|
||||
|
||||
(
|
||||
state.avail_features,
|
||||
state.acked_features,
|
||||
state.acked_protocol_features,
|
||||
state.vu_num_queues,
|
||||
state.config,
|
||||
)
|
||||
} else {
|
||||
DEFAULT_QUEUE_NUMBER
|
||||
};
|
||||
// Filling device and vring features VMM supports.
|
||||
let mut avail_features = 1 << VIRTIO_NET_F_CSUM
|
||||
| 1 << VIRTIO_NET_F_GUEST_CSUM
|
||||
| 1 << VIRTIO_NET_F_GUEST_TSO4
|
||||
| 1 << VIRTIO_NET_F_GUEST_TSO6
|
||||
| 1 << VIRTIO_NET_F_GUEST_ECN
|
||||
| 1 << VIRTIO_NET_F_GUEST_UFO
|
||||
| 1 << VIRTIO_NET_F_HOST_TSO4
|
||||
| 1 << VIRTIO_NET_F_HOST_TSO6
|
||||
| 1 << VIRTIO_NET_F_HOST_ECN
|
||||
| 1 << VIRTIO_NET_F_HOST_UFO
|
||||
| 1 << VIRTIO_NET_F_MRG_RXBUF
|
||||
| 1 << VIRTIO_NET_F_CTRL_VQ
|
||||
| 1 << VIRTIO_F_RING_EVENT_IDX
|
||||
| 1 << VIRTIO_F_VERSION_1
|
||||
| VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
|
||||
|
||||
if num_queues > backend_num_queues {
|
||||
error!("vhost-user-net requested too many queues ({}) since the backend only supports {}\n",
|
||||
if mtu.is_some() {
|
||||
avail_features |= 1u64 << VIRTIO_NET_F_MTU;
|
||||
}
|
||||
|
||||
let mut config = VirtioNetConfig::default();
|
||||
build_net_config_space(&mut config, mac_addr, num_queues, mtu, &mut avail_features);
|
||||
|
||||
let avail_protocol_features = VhostUserProtocolFeatures::MQ
|
||||
| VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
|
||||
| VhostUserProtocolFeatures::REPLY_ACK
|
||||
| VhostUserProtocolFeatures::INFLIGHT_SHMFD
|
||||
| VhostUserProtocolFeatures::LOG_SHMFD;
|
||||
|
||||
let (mut acked_features, acked_protocol_features) =
|
||||
vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?;
|
||||
|
||||
let backend_num_queues =
|
||||
if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 {
|
||||
vu.socket_handle()
|
||||
.get_queue_num()
|
||||
.map_err(Error::VhostUserGetQueueMaxNum)?
|
||||
as usize
|
||||
} else {
|
||||
DEFAULT_QUEUE_NUMBER
|
||||
};
|
||||
|
||||
if num_queues > backend_num_queues {
|
||||
error!("vhost-user-net requested too many queues ({}) since the backend only supports {}\n",
|
||||
num_queues, backend_num_queues);
|
||||
return Err(Error::BadQueueNum);
|
||||
}
|
||||
return Err(Error::BadQueueNum);
|
||||
}
|
||||
|
||||
// If the control queue feature has been negotiated, let's increase
|
||||
// the number of queues.
|
||||
let vu_num_queues = num_queues;
|
||||
if acked_features & (1 << VIRTIO_NET_F_CTRL_VQ) != 0 {
|
||||
num_queues += 1;
|
||||
}
|
||||
// If the control queue feature has been negotiated, let's increase
|
||||
// the number of queues.
|
||||
let vu_num_queues = num_queues;
|
||||
if acked_features & (1 << VIRTIO_NET_F_CTRL_VQ) != 0 {
|
||||
num_queues += 1;
|
||||
}
|
||||
|
||||
// Make sure the virtio feature to set the MAC address is exposed to
|
||||
// the guest, even if it hasn't been negotiated with the backend.
|
||||
acked_features |= 1 << VIRTIO_NET_F_MAC;
|
||||
// Make sure the virtio feature to set the MAC address is exposed to
|
||||
// the guest, even if it hasn't been negotiated with the backend.
|
||||
acked_features |= 1 << VIRTIO_NET_F_MAC;
|
||||
|
||||
(
|
||||
acked_features,
|
||||
// If part of the available features that have been acked,
|
||||
// the PROTOCOL_FEATURES bit must be already set through
|
||||
// the VIRTIO acked features as we know the guest would
|
||||
// never ack it, thus the feature would be lost.
|
||||
acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(),
|
||||
acked_protocol_features,
|
||||
vu_num_queues,
|
||||
config,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(Net {
|
||||
id,
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Net as u32,
|
||||
queue_sizes: vec![vu_cfg.queue_size; num_queues],
|
||||
avail_features: acked_features,
|
||||
// If part of the available features that have been acked, the
|
||||
// PROTOCOL_FEATURES bit must be already set through the VIRTIO
|
||||
// acked features as we know the guest would never ack it, thus
|
||||
// the feature would be lost.
|
||||
acked_features: acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(),
|
||||
avail_features,
|
||||
acked_features,
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
min_queues: DEFAULT_QUEUE_NUMBER as u16,
|
||||
..Default::default()
|
||||
@@ -214,28 +222,6 @@ impl Net {
|
||||
vu_num_queues: self.vu_common.vu_num_queues,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &State) {
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
self.config = state.config;
|
||||
self.vu_common.acked_protocol_features = state.acked_protocol_features;
|
||||
self.vu_common.vu_num_queues = state.vu_num_queues;
|
||||
|
||||
// The backend acknowledged features must not contain VIRTIO_NET_F_MAC
|
||||
// since we don't expect the backend to handle it.
|
||||
let backend_acked_features = self.common.acked_features & !(1 << VIRTIO_NET_F_MAC);
|
||||
|
||||
if let Err(e) = self
|
||||
.vu_common
|
||||
.restore_backend_connection(backend_acked_features)
|
||||
{
|
||||
error!(
|
||||
"Failed restoring connection with vhost-user backend: {:?}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Net {
|
||||
@@ -425,11 +411,6 @@ impl Snapshottable for Net {
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
self.vu_common.snapshot(&self.id(), &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for Net {}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ impl VhostUserHandle {
|
||||
|
||||
let vhost_user_net_reg = VhostUserMemoryRegionInfo {
|
||||
guest_phys_addr: region.start_addr().raw_value(),
|
||||
memory_size: region.len() as u64,
|
||||
memory_size: region.len(),
|
||||
userspace_addr: region.as_ptr() as u64,
|
||||
mmap_offset,
|
||||
mmap_handle,
|
||||
@@ -92,7 +92,7 @@ impl VhostUserHandle {
|
||||
|
||||
let region = VhostUserMemoryRegionInfo {
|
||||
guest_phys_addr: region.start_addr().raw_value(),
|
||||
memory_size: region.len() as u64,
|
||||
memory_size: region.len(),
|
||||
userspace_addr: region.as_ptr() as u64,
|
||||
mmap_offset,
|
||||
mmap_handle,
|
||||
|
||||
@@ -647,7 +647,7 @@ where
|
||||
/// buffer.
|
||||
///
|
||||
fn peer_avail_credit(&self) -> usize {
|
||||
(Wrapping(self.peer_buf_alloc as u32) - (self.rx_cnt - self.peer_fwd_cnt)).0 as usize
|
||||
(Wrapping(self.peer_buf_alloc) - (self.rx_cnt - self.peer_fwd_cnt)).0 as usize
|
||||
}
|
||||
|
||||
/// Prepare a packet header for transmission to our peer.
|
||||
@@ -1118,7 +1118,7 @@ mod tests {
|
||||
// CONN_TX_BUF_SIZE - CONN_CREDIT_UPDATE_THRESHOLD, we initialize
|
||||
// fwd_cnt at 6 bytes below the threshold.
|
||||
let initial_fwd_cnt =
|
||||
csm_defs::CONN_TX_BUF_SIZE as u32 - csm_defs::CONN_CREDIT_UPDATE_THRESHOLD as u32 - 6;
|
||||
csm_defs::CONN_TX_BUF_SIZE - csm_defs::CONN_CREDIT_UPDATE_THRESHOLD - 6;
|
||||
ctx.conn.fwd_cnt = Wrapping(initial_fwd_cnt);
|
||||
|
||||
// Use a 4-byte packet for triggering the credit update threshold.
|
||||
|
||||
@@ -337,6 +337,7 @@ where
|
||||
{
|
||||
/// Create a new virtio-vsock device with the given VM CID and vsock
|
||||
/// backend.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
id: String,
|
||||
cid: u64,
|
||||
@@ -345,17 +346,25 @@ where
|
||||
iommu: bool,
|
||||
seccomp_action: SeccompAction,
|
||||
exit_evt: EventFd,
|
||||
state: Option<VsockState>,
|
||||
) -> io::Result<Vsock<B>> {
|
||||
let mut avail_features = 1u64 << VIRTIO_F_VERSION_1 | 1u64 << VIRTIO_F_IN_ORDER;
|
||||
let (avail_features, acked_features) = if let Some(state) = state {
|
||||
info!("Restoring virtio-vsock {}", id);
|
||||
(state.avail_features, state.acked_features)
|
||||
} else {
|
||||
let mut avail_features = 1u64 << VIRTIO_F_VERSION_1 | 1u64 << VIRTIO_F_IN_ORDER;
|
||||
|
||||
if iommu {
|
||||
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
|
||||
}
|
||||
if iommu {
|
||||
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
|
||||
}
|
||||
(avail_features, 0)
|
||||
};
|
||||
|
||||
Ok(Vsock {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Vsock as u32,
|
||||
avail_features,
|
||||
acked_features,
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
queue_sizes: QUEUE_SIZES.to_vec(),
|
||||
min_queues: NUM_QUEUES as u16,
|
||||
@@ -376,11 +385,6 @@ where
|
||||
acked_features: self.common.acked_features,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &VsockState) {
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> Drop for Vsock<B>
|
||||
@@ -515,11 +519,6 @@ where
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
Snapshot::new_from_versioned_state(&self.id, &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl<B> Transportable for Vsock<B> where B: VsockBackend + Sync + 'static {}
|
||||
impl<B> Migratable for Vsock<B> where B: VsockBackend + Sync + 'static {}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user