mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
514 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9a39cf5b5 | ||
|
|
f6326df68b | ||
|
|
7571e93a69 | ||
|
|
ab6e1bd2d8 | ||
|
|
517ea00bd9 | ||
|
|
ea6d5a04fa | ||
|
|
799336459d | ||
|
|
f934e142ba | ||
|
|
0cde3df44a | ||
|
|
7585e16f9d | ||
|
|
ab575a54b9 | ||
|
|
d2ca7b0e87 | ||
|
|
dd9bce31e8 | ||
|
|
a6370b74d9 | ||
|
|
d172a5dddb | ||
|
|
d46517559b | ||
|
|
0463f4f156 | ||
|
|
fff62d9302 | ||
|
|
5db92f79bf | ||
|
|
06f3049d24 | ||
|
|
28e0a95450 | ||
|
|
a615c809eb | ||
|
|
a3dcaedf7e | ||
|
|
eb0b14f70e | ||
|
|
80e66657cc | ||
|
|
fd5cfb75f3 | ||
|
|
8696bc6604 | ||
|
|
93b599e59e | ||
|
|
01761c2596 | ||
|
|
a212343908 | ||
|
|
d1a406143d | ||
|
|
38380198e1 | ||
|
|
0e40a50407 | ||
|
|
262984f8fc | ||
|
|
dce82a34d0 | ||
|
|
1454d39b28 | ||
|
|
2588fc9e42 | ||
|
|
67793ca375 | ||
|
|
96deca9dc9 | ||
|
|
78b0f68b21 | ||
|
|
a007b750ff | ||
|
|
b2993fb2fa | ||
|
|
8d11bf7979 | ||
|
|
d4718a9bc8 | ||
|
|
05968f5c2c | ||
|
|
71a36e0c69 | ||
|
|
2da5e10689 | ||
|
|
a647d7863c | ||
|
|
a23d4b7cf2 | ||
|
|
f3209e4f78 | ||
|
|
596d6453c5 | ||
|
|
1307d31ede | ||
|
|
2f21827430 | ||
|
|
8da7c13e26 | ||
|
|
aaf86ef209 | ||
|
|
59f98a2edc | ||
|
|
cb52cf91df | ||
|
|
5814193722 | ||
|
|
10ee003d66 | ||
|
|
8f402687ce | ||
|
|
f72f16ee50 | ||
|
|
034aa514d7 | ||
|
|
4e48f429eb | ||
|
|
50b0493371 | ||
|
|
df418a2153 | ||
|
|
a072b9a356 | ||
|
|
691fe0ca68 | ||
|
|
aaa3a114dd | ||
|
|
77e042237d | ||
|
|
f16d45e86e | ||
|
|
3eb6b69dd2 | ||
|
|
fa2b5ca12b | ||
|
|
eac44e6af0 | ||
|
|
40055595ff | ||
|
|
149c08981b | ||
|
|
f1f6814774 | ||
|
|
58f71b0c44 | ||
|
|
1968805ba2 | ||
|
|
d0225fe68f | ||
|
|
87007a288f | ||
|
|
f67484a714 | ||
|
|
3c63779302 | ||
|
|
1c22c4a57b | ||
|
|
4af98f4cb2 | ||
|
|
c58c686f3e | ||
|
|
9b13d63f28 | ||
|
|
b5aeb1f63c | ||
|
|
af2ce3e0cc | ||
|
|
7fd1b9a284 | ||
|
|
e69acd1dc3 | ||
|
|
de764456ce | ||
|
|
0095556847 | ||
|
|
a0065452d8 | ||
|
|
56ca26e72c | ||
|
|
21b9806cad | ||
|
|
d374101f38 | ||
|
|
461e31e6d8 | ||
|
|
d22e7e2638 | ||
|
|
960d702255 | ||
|
|
1105243aca | ||
|
|
317f8002d7 | ||
|
|
278b57ba49 | ||
|
|
d7f87425cd | ||
|
|
573868c035 | ||
|
|
2798286278 | ||
|
|
bcc314eb8b | ||
|
|
af28569611 | ||
|
|
1a5dcc5e70 | ||
|
|
29b089296d | ||
|
|
6e4bf84383 | ||
|
|
226ecf47bb | ||
|
|
0e3733e938 | ||
|
|
f811e36443 | ||
|
|
a64ba04e78 | ||
|
|
b686a5bb24 | ||
|
|
ea4693a091 | ||
|
|
3698b8e74c | ||
|
|
6bb33601d0 | ||
|
|
3d88996e5b | ||
|
|
3899cd0e30 | ||
|
|
9d93df4c8f | ||
|
|
4c27572b87 | ||
|
|
19fea1ad88 | ||
|
|
9a96ea44be | ||
|
|
e11b9d6449 | ||
|
|
ce5fe7f89d | ||
|
|
0dd0364bf8 | ||
|
|
3081d01fc3 | ||
|
|
fa58b725cb | ||
|
|
a38b4c7f17 | ||
|
|
5aa1540c5d | ||
|
|
7da8ae9c0f | ||
|
|
108e8f9dff | ||
|
|
5ab2feae70 | ||
|
|
c99660a8f9 | ||
|
|
08c1bb4b00 | ||
|
|
5bd177dde8 | ||
|
|
52ed1082fb | ||
|
|
03a23309b9 | ||
|
|
fa0965a11a | ||
|
|
524f26abef | ||
|
|
846d0976e3 | ||
|
|
4718dc1b72 | ||
|
|
0a9895e009 | ||
|
|
297b41d615 | ||
|
|
396aba7a52 | ||
|
|
35aaa1333a | ||
|
|
6768a13d95 | ||
|
|
0d50d0b257 | ||
|
|
f5a2f8473b | ||
|
|
03bb59db69 | ||
|
|
9e1fb3bfa1 | ||
|
|
4de422ad69 | ||
|
|
c441bb2968 | ||
|
|
5cb5115456 | ||
|
|
0fbba66b21 | ||
|
|
5388fa1ced | ||
|
|
655d512523 | ||
|
|
7d45473ff7 | ||
|
|
294d5fbb08 | ||
|
|
8c796e6d5d | ||
|
|
630f5c1f14 | ||
|
|
5cbf907200 | ||
|
|
bfeab76059 | ||
|
|
12143ca72f | ||
|
|
ba05bdd085 | ||
|
|
171b28ce52 | ||
|
|
5b929cb277 | ||
|
|
ee0b0d43d8 | ||
|
|
061482340e | ||
|
|
c43ae1dc9d | ||
|
|
7db3002e1d | ||
|
|
07b7457c2e | ||
|
|
dc9142f86c | ||
|
|
6fa7c84d2e | ||
|
|
cc734e2e44 | ||
|
|
e3e83362d6 | ||
|
|
730cf1e944 | ||
|
|
1caa69d0cc | ||
|
|
abcbf43433 | ||
|
|
af74de2779 | ||
|
|
dfebc8929d | ||
|
|
9988379f6b | ||
|
|
4d12192490 | ||
|
|
9f9cfeb5be | ||
|
|
3fa1e77adc | ||
|
|
b8424ea506 | ||
|
|
0c2fe29f61 | ||
|
|
b445d219ca | ||
|
|
6de283f42c | ||
|
|
f892789481 | ||
|
|
f96554cbe9 | ||
|
|
3509b5bf4a | ||
|
|
cf463b88b7 | ||
|
|
ca8eda5399 | ||
|
|
45b770ea9f | ||
|
|
d2c4ceb99a | ||
|
|
caf7cae2fa | ||
|
|
81eca69b40 | ||
|
|
8c07294691 | ||
|
|
2f9436bc12 | ||
|
|
62fade97d1 | ||
|
|
5a8df3622c | ||
|
|
27fda753e1 | ||
|
|
74ca38f7a9 | ||
|
|
fa686fdfc7 | ||
|
|
c827a1c009 | ||
|
|
eefd7fcaaf | ||
|
|
62303d8c33 | ||
|
|
293cf90e3d | ||
|
|
8f98fabd0b | ||
|
|
07821c08c0 | ||
|
|
0cb2c86ff4 | ||
|
|
d359c8cdce | ||
|
|
c801929fd1 | ||
|
|
66da3b9970 | ||
|
|
6ddbd60d9d | ||
|
|
2fc4de6c65 | ||
|
|
03eeb36b74 | ||
|
|
2fe7f54ece | ||
|
|
1cc540c880 | ||
|
|
a1af4238ae | ||
|
|
d2e798944a | ||
|
|
d99f294281 | ||
|
|
44c9ce598d | ||
|
|
5b42aa0bcf | ||
|
|
703e0cab04 | ||
|
|
abb88cf47f | ||
|
|
2e22b8bc3a | ||
|
|
778c05d678 | ||
|
|
5b29eba636 | ||
|
|
e6e78e5986 | ||
|
|
a2df4d7660 | ||
|
|
bc4a1fd16c | ||
|
|
463c9b8e56 | ||
|
|
835847bea5 | ||
|
|
3e4ed5621e | ||
|
|
2624f17ffe | ||
|
|
21f05ebb4f | ||
|
|
1485e17268 | ||
|
|
b57cc3d79f | ||
|
|
a322e2d6f4 | ||
|
|
dd0b95ba5c | ||
|
|
b6667f948e | ||
|
|
4f22f67307 | ||
|
|
eeae63b459 | ||
|
|
abf5748f64 | ||
|
|
23b5f7b1c5 | ||
|
|
a66fef407c | ||
|
|
7c39f37855 | ||
|
|
fe24a7a24f | ||
|
|
73e1451a12 | ||
|
|
1180f757b3 | ||
|
|
2932658acd | ||
|
|
1f7b809619 | ||
|
|
32482f6634 | ||
|
|
6fd5b0f696 | ||
|
|
ef88b2778e | ||
|
|
8b55d1e2d4 | ||
|
|
2df285effd | ||
|
|
4ea4391f45 | ||
|
|
8f8d3c0139 | ||
|
|
408467a05f | ||
|
|
f27b028c18 | ||
|
|
eaa2194699 | ||
|
|
9973d28849 | ||
|
|
1cdb91a40f | ||
|
|
57e331db0e | ||
|
|
090fcebfec | ||
|
|
b2f40afc69 | ||
|
|
72452707ee | ||
|
|
49a389ff12 | ||
|
|
2a9b60dcce | ||
|
|
bbd8d3b71d | ||
|
|
b9163bf431 | ||
|
|
909e1bc3f0 | ||
|
|
4f18476cca | ||
|
|
3d7661664d | ||
|
|
50bac1694f | ||
|
|
dd8a5a7de8 | ||
|
|
dba92c28f7 | ||
|
|
4e298d1abf | ||
|
|
149c342867 | ||
|
|
4054a49e2d | ||
|
|
1001d807ff | ||
|
|
e4a5219f53 | ||
|
|
a458351d8b | ||
|
|
5a27bf878c | ||
|
|
310dafb4d7 | ||
|
|
78895dcc37 | ||
|
|
1757d83db3 | ||
|
|
9cef779cc7 | ||
|
|
a10d1ed78e | ||
|
|
dc7f0a42bc | ||
|
|
53098ae40f | ||
|
|
51cf346828 | ||
|
|
0042447fb9 | ||
|
|
e8c330e220 | ||
|
|
ba78e331c6 | ||
|
|
b6d2ac2c2e | ||
|
|
f6fb442fa3 | ||
|
|
0c2f2d3ec1 | ||
|
|
584c088094 | ||
|
|
5b715f483d | ||
|
|
1e602bd9a6 | ||
|
|
6b57f301db | ||
|
|
cd0db3146f | ||
|
|
ac3ef0849e | ||
|
|
bd8db86b0c | ||
|
|
7b5f06788a | ||
|
|
9a7f278716 | ||
|
|
fb457954bb | ||
|
|
c4063d26be | ||
|
|
9006013c60 | ||
|
|
30cf1eed5e | ||
|
|
c6b023e58d | ||
|
|
46f0e0d14a | ||
|
|
261bfac4d4 | ||
|
|
ec9b9ab542 | ||
|
|
8c9b34c9f1 | ||
|
|
5b822191c0 | ||
|
|
ab7b294688 | ||
|
|
7e419784cd | ||
|
|
48bed965c0 | ||
|
|
0d6cef4521 | ||
|
|
81f8a27ef6 | ||
|
|
e3bd5e9b35 | ||
|
|
80b2c98a68 | ||
|
|
3c05626ad1 | ||
|
|
906580ee92 | ||
|
|
c6889388a3 | ||
|
|
f38fc94115 | ||
|
|
6ecca29aa6 | ||
|
|
2ef04671be | ||
|
|
453bc31994 | ||
|
|
6e6966df8f | ||
|
|
f32c2638f6 | ||
|
|
fbe1cd6404 | ||
|
|
778a72f52f | ||
|
|
76256230d6 | ||
|
|
8cd80ea36b | ||
|
|
5e937c8b88 | ||
|
|
710535343b | ||
|
|
59c5b0a1cd | ||
|
|
c13019d5b5 | ||
|
|
c5774685f4 | ||
|
|
2df8d2fa1e | ||
|
|
dd00bd6ef2 | ||
|
|
2752149bb0 | ||
|
|
1bc74e43f8 | ||
|
|
8acaeb5c82 | ||
|
|
5eaf510f90 | ||
|
|
df1d6eaaee | ||
|
|
a6a3d247da | ||
|
|
838a857311 | ||
|
|
95229b24ab | ||
|
|
71b0eb3c21 | ||
|
|
337cbf3d33 | ||
|
|
675140f627 | ||
|
|
894a4dee6e | ||
|
|
f57d00cd66 | ||
|
|
59a1719b6e | ||
|
|
39ab10244b | ||
|
|
b5d856b31a | ||
|
|
7322f33368 | ||
|
|
58850d2679 | ||
|
|
b264aebcde | ||
|
|
329e3d0dad | ||
|
|
56ff396eef | ||
|
|
cc5e463ee3 | ||
|
|
7e6326b3c5 | ||
|
|
4168f8a14c | ||
|
|
f0dd0819d2 | ||
|
|
34dc97f74f | ||
|
|
b9bd80faa0 | ||
|
|
9b14ee830a | ||
|
|
166a005b76 | ||
|
|
107bfe1075 | ||
|
|
36b59a6d2b | ||
|
|
bac762e472 | ||
|
|
7a637fe1f4 | ||
|
|
ebfc8df9f0 | ||
|
|
0e209edb1d | ||
|
|
b708db393d | ||
|
|
416fe5eaac | ||
|
|
0aab960bf1 | ||
|
|
86315adb23 | ||
|
|
b41daddce1 | ||
|
|
6164aa0885 | ||
|
|
67fb786c54 | ||
|
|
2fa4dc6338 | ||
|
|
fc0d808205 | ||
|
|
caf15f0acb | ||
|
|
b1547c4ccb | ||
|
|
d2d3ba4ebf | ||
|
|
c162494867 | ||
|
|
92e48f7cf6 | ||
|
|
81db2f0233 | ||
|
|
79ccb25f78 | ||
|
|
ac97690848 | ||
|
|
78ea83d753 | ||
|
|
c891dcb947 | ||
|
|
0dc3634b7b | ||
|
|
297236a7c0 | ||
|
|
83bcf2a1ff | ||
|
|
c4baae079c | ||
|
|
19d36c765f | ||
|
|
8036a2c3de | ||
|
|
d1c9002c48 | ||
|
|
fbb21038cb | ||
|
|
d242ec6879 | ||
|
|
966db37f63 | ||
|
|
ab12e7c294 | ||
|
|
33c15ca273 | ||
|
|
61e57e1cb1 | ||
|
|
9013d8b4ca | ||
|
|
f041c940a7 | ||
|
|
13cf7a1315 | ||
|
|
88a9f79944 | ||
|
|
6e4aefe66f | ||
|
|
cc9899e09d | ||
|
|
7eb70730d1 | ||
|
|
0a3ad6153a | ||
|
|
9f02839448 | ||
|
|
eabbeac9c0 | ||
|
|
f436231cba | ||
|
|
5a70d7ec69 | ||
|
|
ea4be45bb0 | ||
|
|
3d73509338 | ||
|
|
a0ae3ad1df | ||
|
|
d90fa96bb7 | ||
|
|
a9b30a4c4e | ||
|
|
0f6cd3b030 | ||
|
|
287887c99c | ||
|
|
a5df86698b | ||
|
|
bc251fdf46 | ||
|
|
d1214acf2a | ||
|
|
938ff2eeb7 | ||
|
|
e810be62cd | ||
|
|
9f08aa6dc2 | ||
|
|
d9ee760f3b | ||
|
|
9ca88047a8 | ||
|
|
88c0f268c2 | ||
|
|
e8697735d1 | ||
|
|
2668dbbd8b | ||
|
|
e9f137dc4a | ||
|
|
be5fc769e9 | ||
|
|
faf11d7e9d | ||
|
|
60165bbcfa | ||
|
|
990ba0666d | ||
|
|
4644f8a643 | ||
|
|
4d45e89d24 | ||
|
|
fe9626e07a | ||
|
|
2ad8fac624 | ||
|
|
1103d531b7 | ||
|
|
0277974a36 | ||
|
|
cbc940d2e2 | ||
|
|
aba5fa8846 | ||
|
|
a87be264c8 | ||
|
|
4ad44caa52 | ||
|
|
9f53553860 | ||
|
|
d10f6ca714 | ||
|
|
1b816a358d | ||
|
|
f79ebd2afc | ||
|
|
4e64df1005 | ||
|
|
37756c5d32 | ||
|
|
d20cff95e1 | ||
|
|
a10e9e6cd5 | ||
|
|
e47677020e | ||
|
|
60c8a72e29 | ||
|
|
349f5fdd4e | ||
|
|
60aed1bd81 | ||
|
|
68468b8519 | ||
|
|
e508d2a3cd | ||
|
|
bbd72d6453 | ||
|
|
8e5c7a37b9 | ||
|
|
8fb8da8eb1 | ||
|
|
139802829a | ||
|
|
d2a364c5c0 | ||
|
|
2049b2c377 | ||
|
|
4bfeba967b | ||
|
|
6956306604 | ||
|
|
a79a3f6599 | ||
|
|
3dc0f9cfec | ||
|
|
8fd0310db9 | ||
|
|
486c61da5e | ||
|
|
c93e1aa12e | ||
|
|
ba262e45a4 | ||
|
|
a987c3d0fc | ||
|
|
cd0cdac0ed | ||
|
|
a4cf175b8e | ||
|
|
c5e5cd22db | ||
|
|
b256623d08 | ||
|
|
0a5e024765 | ||
|
|
4299815a67 | ||
|
|
f63c2d896f | ||
|
|
c3a83d2b6a | ||
|
|
8c146db313 | ||
|
|
3c4be55b3b | ||
|
|
a43d46edb0 | ||
|
|
ca88d4449e | ||
|
|
ce49a6f4b8 | ||
|
|
7f99ef2a92 | ||
|
|
3c9f2f6914 | ||
|
|
5f814308d6 | ||
|
|
5b58854ae9 | ||
|
|
4c99aea6c4 | ||
|
|
8f3bd4d9ec | ||
|
|
feb0a36067 | ||
|
|
3645654c39 | ||
|
|
a229afbc24 | ||
|
|
fd561f4034 | ||
|
|
b9c885b5b8 | ||
|
|
4ee3f2bde9 |
22
.github/workflows/build.yaml
vendored
22
.github/workflows/build.yaml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
- stable
|
- stable
|
||||||
- beta
|
- beta
|
||||||
- nightly
|
- nightly
|
||||||
- "1.77.0"
|
- "1.83.0"
|
||||||
target:
|
target:
|
||||||
- x86_64-unknown-linux-gnu
|
- x86_64-unknown-linux-gnu
|
||||||
- x86_64-unknown-linux-musl
|
- x86_64-unknown-linux-musl
|
||||||
@@ -35,34 +35,34 @@ jobs:
|
|||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Build (default features)
|
- name: Build (default features)
|
||||||
run: cargo rustc --locked --bin cloud-hypervisor -- -D warnings -D clippy::undocumented_unsafe_blocks
|
run: cargo rustc --locked --bin cloud-hypervisor -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Build (kvm)
|
- name: Build (kvm)
|
||||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Build (default features + tdx)
|
- name: Build (default features + tdx)
|
||||||
run: cargo rustc --locked --bin cloud-hypervisor --features "tdx" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
run: cargo rustc --locked --bin cloud-hypervisor --features "tdx" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Build (default features + dbus_api)
|
- name: Build (default features + dbus_api)
|
||||||
run: cargo rustc --locked --bin cloud-hypervisor --features "dbus_api" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
run: cargo rustc --locked --bin cloud-hypervisor --features "dbus_api" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Build (default features + guest_debug)
|
- name: Build (default features + guest_debug)
|
||||||
run: cargo rustc --locked --bin cloud-hypervisor --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
run: cargo rustc --locked --bin cloud-hypervisor --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Build (default features + pvmemcontrol)
|
- name: Build (default features + pvmemcontrol)
|
||||||
run: cargo rustc --locked --bin cloud-hypervisor --features "pvmemcontrol" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
run: cargo rustc --locked --bin cloud-hypervisor --features "pvmemcontrol" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Build (mshv)
|
- name: Build (mshv)
|
||||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Build (sev_snp)
|
- name: Build (sev_snp)
|
||||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "sev_snp" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "sev_snp" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Build (igvm)
|
- name: Build (igvm)
|
||||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "igvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "igvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Build (mshv + kvm)
|
- name: Build (mshv + kvm)
|
||||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Release Build (default features)
|
- name: Release Build (default features)
|
||||||
run: cargo build --locked --all --release --target=${{ matrix.target }}
|
run: cargo build --locked --all --release --target=${{ matrix.target }}
|
||||||
|
|||||||
14
.github/workflows/docker-image.yaml
vendored
14
.github/workflows/docker-image.yaml
vendored
@@ -21,13 +21,13 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Login to ghcr
|
- name: Login to ghcr
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -36,17 +36,17 @@ jobs:
|
|||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
# generate Docker tags based on the following events/attributes
|
# generate Docker tags based on the following events/attributes
|
||||||
tags: |
|
tags: |
|
||||||
type=raw,value=20240507-0
|
type=raw,value=20250412-0
|
||||||
type=sha
|
type=sha
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
if: ${{ github.event_name == 'push' }}
|
if: ${{ github.event_name == 'push' }}
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
file: ./resources/Dockerfile
|
file: ./resources/Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
@@ -55,7 +55,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build only
|
- name: Build only
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
file: ./resources/Dockerfile
|
file: ./resources/Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
|||||||
32
.github/workflows/formatting.yaml
vendored
Normal file
32
.github/workflows/formatting.yaml
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
name: Cloud Hypervisor Code Formatting
|
||||||
|
on: [pull_request, merge_group]
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Code Formatting
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
rust:
|
||||||
|
- nightly
|
||||||
|
target:
|
||||||
|
- x86_64-unknown-linux-gnu
|
||||||
|
- aarch64-unknown-linux-musl
|
||||||
|
env:
|
||||||
|
RUSTFLAGS: -D warnings
|
||||||
|
steps:
|
||||||
|
- name: Code checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install Rust toolchain (${{ matrix.rust }})
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.rust }}
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
components: rustfmt
|
||||||
|
- name: Formatting (rustfmt)
|
||||||
|
run: cargo fmt --all -- --check
|
||||||
|
- name: Formatting (fuzz) (rustfmt)
|
||||||
|
run: cargo fmt --all --manifest-path fuzz/Cargo.toml -- --check
|
||||||
10
.github/workflows/integration-arm64.yaml
vendored
10
.github/workflows/integration-arm64.yaml
vendored
@@ -6,12 +6,12 @@ concurrency:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
timeout-minutes: 60
|
timeout-minutes: 120
|
||||||
name: Tests (ARM64)
|
name: Tests (ARM64)
|
||||||
runs-on: focal-arm64
|
runs-on: bookworm-arm64
|
||||||
steps:
|
steps:
|
||||||
- name: Fix workspace permissions
|
- name: Fix workspace permissions
|
||||||
run: sudo chown -R github-runner:github-runner ${GITHUB_WORKSPACE}
|
run: sudo chown -R runner:runner ${GITHUB_WORKSPACE}
|
||||||
- name: Code checkout
|
- name: Code checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -21,14 +21,14 @@ jobs:
|
|||||||
- name: Load openvswitch module
|
- name: Load openvswitch module
|
||||||
run: sudo modprobe openvswitch
|
run: sudo modprobe openvswitch
|
||||||
- name: Run integration tests (musl)
|
- name: Run integration tests (musl)
|
||||||
timeout-minutes: 30
|
timeout-minutes: 60
|
||||||
run: scripts/dev_cli.sh tests --integration --libc musl
|
run: scripts/dev_cli.sh tests --integration --libc musl
|
||||||
- name: Install Azure CLI
|
- name: Install Azure CLI
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
run: |
|
run: |
|
||||||
sudo apt install -y ca-certificates curl apt-transport-https lsb-release gnupg
|
sudo apt install -y ca-certificates curl apt-transport-https lsb-release gnupg
|
||||||
curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null
|
curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null
|
||||||
echo "deb [arch=arm64] https://packages.microsoft.com/repos/azure-cli/ focal main" | sudo tee /etc/apt/sources.list.d/azure-cli.list
|
echo "deb [arch=arm64] https://packages.microsoft.com/repos/azure-cli/ bookworm main" | sudo tee /etc/apt/sources.list.d/azure-cli.list
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install -y azure-cli
|
sudo apt install -y azure-cli
|
||||||
- name: Download Windows image
|
- name: Download Windows image
|
||||||
|
|||||||
2
.github/workflows/integration-metrics.yaml
vendored
2
.github/workflows/integration-metrics.yaml
vendored
@@ -7,7 +7,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Tests (Metrics)
|
name: Tests (Metrics)
|
||||||
runs-on: jammy-metrics
|
runs-on: bare-metal-9950x
|
||||||
env:
|
env:
|
||||||
METRICS_PUBLISH_KEY: ${{ secrets.METRICS_PUBLISH_KEY }}
|
METRICS_PUBLISH_KEY: ${{ secrets.METRICS_PUBLISH_KEY }}
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -7,13 +7,10 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Tests (Rate-Limiter)
|
name: Tests (Rate-Limiter)
|
||||||
runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'jammy-rate-limiter' }}
|
runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'bare-metal-9950x' }}
|
||||||
env:
|
env:
|
||||||
AUTH_DOWNLOAD_TOKEN: ${{ secrets.AUTH_DOWNLOAD_TOKEN }}
|
AUTH_DOWNLOAD_TOKEN: ${{ secrets.AUTH_DOWNLOAD_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
- name: Fix workspace permissions
|
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
|
||||||
run: sudo chown -R github-runner:github-runner ${GITHUB_WORKSPACE}
|
|
||||||
- name: Code checkout
|
- name: Code checkout
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -21,7 +18,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Run rate-limiter integration tests
|
- name: Run rate-limiter integration tests
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
timeout-minutes: 10
|
timeout-minutes: 20
|
||||||
run: scripts/dev_cli.sh tests --integration-rate-limiter
|
run: scripts/dev_cli.sh tests --integration-rate-limiter
|
||||||
- name: Skipping build for PR
|
- name: Skipping build for PR
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
|
|||||||
32
.github/workflows/integration-sgx.yaml
vendored
32
.github/workflows/integration-sgx.yaml
vendored
@@ -1,32 +0,0 @@
|
|||||||
name: Cloud Hypervisor Tests (SGX)
|
|
||||||
on: [merge_group, pull_request]
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Tests (SGX)
|
|
||||||
runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'jammy-sgx' }}
|
|
||||||
env:
|
|
||||||
AUTH_DOWNLOAD_TOKEN: ${{ secrets.AUTH_DOWNLOAD_TOKEN }}
|
|
||||||
steps:
|
|
||||||
- name: Fix workspace permissions
|
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
|
||||||
run: sudo chown -R github-runner:github-runner ${GITHUB_WORKSPACE}
|
|
||||||
- name: Code checkout
|
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Run SGX integration tests
|
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
|
||||||
timeout-minutes: 10
|
|
||||||
run: scripts/dev_cli.sh tests --integration-sgx
|
|
||||||
- name: Run SGX integration tests for musl
|
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
|
||||||
timeout-minutes: 10
|
|
||||||
run: scripts/dev_cli.sh tests --integration-sgx --libc musl
|
|
||||||
- name: Skipping build for PR
|
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
|
||||||
run: echo "Skipping build for PR"
|
|
||||||
13
.github/workflows/integration-vfio.yaml
vendored
13
.github/workflows/integration-vfio.yaml
vendored
@@ -7,13 +7,13 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Tests (VFIO)
|
name: Tests (VFIO)
|
||||||
runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'jammy-vfio' }}
|
runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'vfio-nvidia' }}
|
||||||
env:
|
env:
|
||||||
AUTH_DOWNLOAD_TOKEN: ${{ secrets.AUTH_DOWNLOAD_TOKEN }}
|
AUTH_DOWNLOAD_TOKEN: ${{ secrets.AUTH_DOWNLOAD_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
- name: Fix workspace permissions
|
- name: Fix workspace permissions
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
run: sudo chown -R github-runner:github-runner ${GITHUB_WORKSPACE}
|
run: sudo chown -R runner:runner ${GITHUB_WORKSPACE}
|
||||||
- name: Code checkout
|
- name: Code checkout
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -23,10 +23,11 @@ jobs:
|
|||||||
if: ${{ github.event_name != 'pull_request' }}
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
run: scripts/dev_cli.sh tests --integration-vfio
|
run: scripts/dev_cli.sh tests --integration-vfio
|
||||||
- name: Run VFIO integration tests for musl
|
# Most tests are failing with musl see #6790
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
# - name: Run VFIO integration tests for musl
|
||||||
timeout-minutes: 15
|
# if: ${{ github.event_name != 'pull_request' }}
|
||||||
run: scripts/dev_cli.sh tests --integration-vfio --libc musl
|
# timeout-minutes: 15
|
||||||
|
# run: scripts/dev_cli.sh tests --integration-vfio --libc musl
|
||||||
- name: Skipping build for PR
|
- name: Skipping build for PR
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
run: echo "Skipping build for PR"
|
run: echo "Skipping build for PR"
|
||||||
|
|||||||
16
.github/workflows/lychee.yaml
vendored
Normal file
16
.github/workflows/lychee.yaml
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
name: Link Check (lychee)
|
||||||
|
on:
|
||||||
|
pull_request
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
link_check:
|
||||||
|
name: Link Check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Code checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Link Availability Check
|
||||||
|
uses: lycheeverse/lychee-action@master
|
||||||
|
with:
|
||||||
|
args: --verbose --config .lychee.toml .
|
||||||
32
.github/workflows/package-consistency.yaml
vendored
Normal file
32
.github/workflows/package-consistency.yaml
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
name: Cloud Hypervisor Consistency
|
||||||
|
on: [pull_request, merge_group]
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Rust VMM Consistency Check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Code checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt install -y python3
|
||||||
|
|
||||||
|
- name: Install Rust toolchain stable
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
|
||||||
|
- name: Check Rust VMM Package Consistency of root Workspace
|
||||||
|
run: python3 scripts/package-consistency-check.py github.com/rust-vmm
|
||||||
|
|
||||||
|
- name: Check Rust VMM Package Consistency of fuzz Workspace
|
||||||
|
run: |
|
||||||
|
pushd fuzz
|
||||||
|
python3 ../scripts/package-consistency-check.py github.com/rust-vmm
|
||||||
|
popd
|
||||||
39
.github/workflows/preview-riscv64.yaml
vendored
Normal file
39
.github/workflows/preview-riscv64.yaml
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
name: Cloud Hypervisor RISC-V 64-bit Preview
|
||||||
|
on: [pull_request, merge_group]
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Cargo
|
||||||
|
runs-on: riscv64-qemu-host
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
module:
|
||||||
|
- hypervisor
|
||||||
|
- arch
|
||||||
|
- vm-allocator
|
||||||
|
- devices
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Code checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
run: /opt/scripts/exec-in-qemu.sh rustup default 1.83.0
|
||||||
|
|
||||||
|
- name: Build ${{ matrix.module }} Module (kvm)
|
||||||
|
run: /opt/scripts/exec-in-qemu.sh cargo rustc --locked -p ${{ matrix.module }} --no-default-features --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
|
- name: Clippy ${{ matrix.module }} Module (kvm)
|
||||||
|
run: /opt/scripts/exec-in-qemu.sh cargo clippy --locked -p ${{ matrix.module }} --no-default-features --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
|
- name: Test ${{ matrix.module }} Module (kvm)
|
||||||
|
run: /opt/scripts/exec-in-qemu.sh cargo test --locked -p ${{ matrix.module }} --no-default-features --features "kvm"
|
||||||
|
|
||||||
|
- name: Check no files were modified
|
||||||
|
run: test -z "$(git status --porcelain)"
|
||||||
61
.github/workflows/quality.yaml
vendored
61
.github/workflows/quality.yaml
vendored
@@ -6,13 +6,14 @@ concurrency:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Quality (clippy, rustfmt)
|
name: Quality (clippy)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
continue-on-error: ${{ matrix.experimental }}
|
continue-on-error: ${{ matrix.experimental }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
rust:
|
rust:
|
||||||
|
- beta
|
||||||
- stable
|
- stable
|
||||||
target:
|
target:
|
||||||
- aarch64-unknown-linux-gnu
|
- aarch64-unknown-linux-gnu
|
||||||
@@ -20,20 +21,12 @@ jobs:
|
|||||||
- x86_64-unknown-linux-gnu
|
- x86_64-unknown-linux-gnu
|
||||||
- x86_64-unknown-linux-musl
|
- x86_64-unknown-linux-musl
|
||||||
|
|
||||||
experimental: [false]
|
|
||||||
include:
|
include:
|
||||||
- rust: beta
|
- rust: beta
|
||||||
target: aarch64-unknown-linux-gnu
|
|
||||||
experimental: true
|
|
||||||
- rust: beta
|
|
||||||
target: aarch64-unknown-linux-musl
|
|
||||||
experimental: true
|
|
||||||
- rust: beta
|
|
||||||
target: x86_64-unknown-linux-gnu
|
|
||||||
experimental: true
|
|
||||||
- rust: beta
|
|
||||||
target: x86_64-unknown-linux-musl
|
|
||||||
experimental: true
|
experimental: true
|
||||||
|
- rust: stable
|
||||||
|
experimental: false
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Code checkout
|
- name: Code checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -46,7 +39,7 @@ jobs:
|
|||||||
toolchain: ${{ matrix.rust }}
|
toolchain: ${{ matrix.rust }}
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
override: true
|
override: true
|
||||||
components: rustfmt, clippy
|
components: clippy
|
||||||
|
|
||||||
- name: Bisectability Check (default features)
|
- name: Bisectability Check (default features)
|
||||||
if: ${{ github.event_name == 'pull_request' && matrix.target == 'x86_64-unknown-linux-gnu' }}
|
if: ${{ github.event_name == 'pull_request' && matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||||
@@ -56,83 +49,89 @@ jobs:
|
|||||||
for commit in $commits; do git checkout $commit; cargo check --tests --examples --all --target=${{ matrix.target }}; done
|
for commit in $commits; do git checkout $commit; cargo check --tests --examples --all --target=${{ matrix.target }}; done
|
||||||
git checkout ${{ github.sha }}
|
git checkout ${{ github.sha }}
|
||||||
|
|
||||||
- name: Formatting (rustfmt)
|
|
||||||
run: cargo fmt -- --check
|
|
||||||
|
|
||||||
- name: Clippy (kvm)
|
- name: Clippy (kvm)
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
|
- name: Clippy (mshv)
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
|
command: clippy
|
||||||
|
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
|
- name: Clippy (mshv + kvm)
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
|
command: clippy
|
||||||
|
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "mshv,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Clippy (default features)
|
- name: Clippy (default features)
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples -- -D warnings -D clippy::undocumented_unsafe_blocks
|
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Clippy (default features + guest_debug)
|
- name: Clippy (default features + guest_debug)
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Clippy (default features + pvmemcontrol)
|
- name: Clippy (default features + pvmemcontrol)
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "pvmemcontrol" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "pvmemcontrol" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Clippy (default features + tracing)
|
- name: Clippy (default features + tracing)
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "tracing" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "tracing" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Clippy (mshv)
|
- name: Clippy (mshv)
|
||||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Clippy (mshv + kvm)
|
- name: Clippy (mshv + kvm)
|
||||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "mshv,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "mshv,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Clippy (sev_snp)
|
- name: Clippy (sev_snp)
|
||||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "sev_snp" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "sev_snp" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Clippy (igvm)
|
- name: Clippy (igvm)
|
||||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "igvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "igvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Clippy (kvm + tdx)
|
- name: Clippy (kvm + tdx)
|
||||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "tdx,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "tdx,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||||
|
|
||||||
- name: Check build did not modify any files
|
- name: Check build did not modify any files
|
||||||
run: test -z "$(git status --porcelain)"
|
run: test -z "$(git status --porcelain)"
|
||||||
|
|||||||
6
.github/workflows/release.yaml
vendored
6
.github/workflows/release.yaml
vendored
@@ -45,7 +45,7 @@ jobs:
|
|||||||
target: ${{ matrix.platform.target }}
|
target: ${{ matrix.platform.target }}
|
||||||
args: ${{ matrix.platform.args }}
|
args: ${{ matrix.platform.args }}
|
||||||
strip: true
|
strip: true
|
||||||
toolchain: "1.77.0"
|
toolchain: "1.83.0"
|
||||||
- name: Copy Release Binaries
|
- name: Copy Release Binaries
|
||||||
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -54,7 +54,7 @@ jobs:
|
|||||||
cp target/${{ matrix.platform.target }}/release/ch-remote ./${{ matrix.platform.name_ch_remote }}
|
cp target/${{ matrix.platform.target }}/release/ch-remote ./${{ matrix.platform.name_ch_remote }}
|
||||||
- name: Upload Release Artifacts
|
- name: Upload Release Artifacts
|
||||||
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Artifacts for ${{ matrix.platform.target }}
|
name: Artifacts for ${{ matrix.platform.target }}
|
||||||
path: |
|
path: |
|
||||||
@@ -80,7 +80,7 @@ jobs:
|
|||||||
github.event_name == 'create' && github.event.ref_type == 'tag' &&
|
github.event_name == 'create' && github.event.ref_type == 'tag' &&
|
||||||
matrix.platform.target == 'x86_64-unknown-linux-gnu'
|
matrix.platform.target == 'x86_64-unknown-linux-gnu'
|
||||||
id: upload-release-cloud-hypervisor-vendored-sources
|
id: upload-release-cloud-hypervisor-vendored-sources
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: cloud-hypervisor-${{ github.event.ref }}.tar.xz
|
path: cloud-hypervisor-${{ github.event.ref }}.tar.xz
|
||||||
name: cloud-hypervisor-${{ github.event.ref }}.tar.xz
|
name: cloud-hypervisor-${{ github.event.ref }}.tar.xz
|
||||||
|
|||||||
6
.gitlint
6
.gitlint
@@ -1,6 +1,7 @@
|
|||||||
[general]
|
[general]
|
||||||
extra-path=scripts/gitlint/rules.py
|
extra-path=scripts/gitlint/rules
|
||||||
regex-style-search=true
|
regex-style-search=true
|
||||||
|
ignore=body-max-line-length
|
||||||
|
|
||||||
[ignore-by-author-name]
|
[ignore-by-author-name]
|
||||||
regex=dependabot
|
regex=dependabot
|
||||||
@@ -10,6 +11,3 @@ ignore=all
|
|||||||
[title-max-length]
|
[title-max-length]
|
||||||
line-length=72
|
line-length=72
|
||||||
|
|
||||||
# default 80
|
|
||||||
[body-max-line-length]
|
|
||||||
line-length=72
|
|
||||||
|
|||||||
21
.lychee.toml
Normal file
21
.lychee.toml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
verbose = "info"
|
||||||
|
|
||||||
|
exclude = [
|
||||||
|
# Availability of links below should be manually verified.
|
||||||
|
# Page for intel SGX support, returns 403 while querying.
|
||||||
|
'^https://www.intel.com/content/www/us/en/developer/tools/software-guard-extensions/linux-overview.html',
|
||||||
|
# Page for intel TDX support, returns 403 while querying.
|
||||||
|
'^https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/overview.html',
|
||||||
|
# Page for TPM, returns 403 while querying.
|
||||||
|
'^https://trustedcomputinggroup.org/wp-content/uploads/PC-Client-Specific-Platform-TPM-Profile-for-TPM-2p0-v1p05p_r14_pub.pdf',
|
||||||
|
|
||||||
|
# GitHub user smibarber referenced in `CREDITS.md` no longer exist
|
||||||
|
'^https://github.com/smibarber',
|
||||||
|
|
||||||
|
# OSDev has added bot protection and accesses my result in 403 Forbidden.
|
||||||
|
'^https://wiki.osdev.org',
|
||||||
|
]
|
||||||
|
|
||||||
|
max_retries = 3
|
||||||
|
|
||||||
|
retry_wait_time = 5
|
||||||
@@ -1 +1,4 @@
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
group_imports="StdExternalCrate"
|
||||||
|
imports_granularity="Module"
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ EXTINT = "EXTINT"
|
|||||||
INOUT = "INOUT"
|
INOUT = "INOUT"
|
||||||
liness = "liness"
|
liness = "liness"
|
||||||
outout = "outout"
|
outout = "outout"
|
||||||
|
TRANSLATER = "TRANSLATER"
|
||||||
|
|
||||||
[default.extend-identifiers]
|
[default.extend-identifiers]
|
||||||
fo = "fo"
|
fo = "fo"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ License](https://opensource.org/licenses/Apache-2.0).
|
|||||||
|
|
||||||
## Coding Style
|
## Coding Style
|
||||||
|
|
||||||
We follow the [Rust Style](https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/guide.md)
|
We follow the [Rust Style](https://github.com/rust-lang/rust/tree/HEAD/src/doc/style-guide/src)
|
||||||
convention and enforce it through the Continuous Integration (CI) process calling into `rustfmt`
|
convention and enforce it through the Continuous Integration (CI) process calling into `rustfmt`
|
||||||
for each submitted Pull Request (PR).
|
for each submitted Pull Request (PR).
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ commit you make.
|
|||||||
|
|
||||||
## Certificate of Origin
|
## Certificate of Origin
|
||||||
|
|
||||||
In order to get a clear contribution chain of trust we use the [signed-off-by language](https://01.org/community/signed-process)
|
In order to get a clear contribution chain of trust we use the [signed-off-by language](https://web.archive.org/web/20230406041855/https://01.org/community/signed-process)
|
||||||
used by the Linux kernel project.
|
used by the Linux kernel project.
|
||||||
|
|
||||||
## Patch format
|
## Patch format
|
||||||
@@ -79,12 +79,12 @@ you want to merge your changes to `cloud-hypervisor`:
|
|||||||
|
|
||||||
1. Fork the [cloud-hypervisor](https://github.com/cloud-hypervisor/cloud-hypervisor) project
|
1. Fork the [cloud-hypervisor](https://github.com/cloud-hypervisor/cloud-hypervisor) project
|
||||||
into your github organization.
|
into your github organization.
|
||||||
2. Within your fork, create a branch for your contribution.
|
1. Within your fork, create a branch for your contribution.
|
||||||
3. [Create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)
|
1. [Create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)
|
||||||
against the main branch of the Cloud Hypervisor repository.
|
against the main branch of the Cloud Hypervisor repository.
|
||||||
4. To update your pull request amend existing commits whenever applicable and
|
1. To update your pull request amend existing commits whenever applicable and
|
||||||
then push the new changes to your pull request branch.
|
then push the new changes to your pull request branch.
|
||||||
5. Once the pull request is approved it can be integrated.
|
1. Once the pull request is approved it can be integrated.
|
||||||
|
|
||||||
## Issue tracking
|
## Issue tracking
|
||||||
|
|
||||||
|
|||||||
855
Cargo.lock
generated
855
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
60
Cargo.toml
60
Cargo.toml
@@ -7,15 +7,15 @@ edition = "2021"
|
|||||||
homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
||||||
license = "Apache-2.0 AND BSD-3-Clause"
|
license = "Apache-2.0 AND BSD-3-Clause"
|
||||||
name = "cloud-hypervisor"
|
name = "cloud-hypervisor"
|
||||||
version = "41.0.0"
|
version = "46.0.0"
|
||||||
# Minimum buildable version:
|
# Minimum buildable version:
|
||||||
# Keep in sync with version in .github/workflows/build.yaml
|
# Keep in sync with version in .github/workflows/build.yaml
|
||||||
# Policy on MSRV (see #4318):
|
# Policy on MSRV (see #4318):
|
||||||
# Can only be bumped by:
|
# Can only be bumped if satisfying any of the following:
|
||||||
# a.) A dependency requires it,
|
# a.) A dependency requires it,
|
||||||
# b.) If we want to use a new feature and that MSRV is at least 6 months old,
|
# b.) If we want to use a new feature and that MSRV is at least 6 months old,
|
||||||
# c.) There is a security issue that is addressed by the toolchain update.
|
# c.) There is a security issue that is addressed by the toolchain update.
|
||||||
rust-version = "1.77.0"
|
rust-version = "1.83.0"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
@@ -29,32 +29,32 @@ inherits = "release"
|
|||||||
strip = false
|
strip = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.94"
|
||||||
api_client = { path = "api_client" }
|
api_client = { path = "api_client" }
|
||||||
clap = { version = "4.5.4", features = ["string"] }
|
clap = { version = "4.5.13", features = ["string"] }
|
||||||
dhat = { version = "0.3.3", optional = true }
|
dhat = { version = "0.3.3", optional = true }
|
||||||
epoll = "4.3.3"
|
epoll = "4.3.3"
|
||||||
event_monitor = { path = "event_monitor" }
|
event_monitor = { path = "event_monitor" }
|
||||||
hypervisor = { path = "hypervisor" }
|
hypervisor = { path = "hypervisor" }
|
||||||
libc = "0.2.155"
|
libc = "0.2.167"
|
||||||
log = { version = "0.4.22", features = ["std"] }
|
log = { version = "0.4.22", features = ["std"] }
|
||||||
option_parser = { path = "option_parser" }
|
option_parser = { path = "option_parser" }
|
||||||
seccompiler = "0.4.0"
|
seccompiler = { workspace = true }
|
||||||
serde_json = "1.0.120"
|
serde_json = { workspace = true }
|
||||||
signal-hook = "0.3.17"
|
signal-hook = "0.3.18"
|
||||||
thiserror = "1.0.62"
|
thiserror = "2.0.6"
|
||||||
tpm = { path = "tpm" }
|
tpm = { path = "tpm" }
|
||||||
tracer = { path = "tracer" }
|
tracer = { path = "tracer" }
|
||||||
vm-memory = "0.14.1"
|
vm-memory = { workspace = true }
|
||||||
vmm = { path = "vmm" }
|
vmm = { path = "vmm" }
|
||||||
vmm-sys-util = "0.12.1"
|
vmm-sys-util = { workspace = true }
|
||||||
zbus = { version = "4.1.2", optional = true }
|
zbus = { version = "4.4.0", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
dirs = "5.0.1"
|
dirs = "6.0.0"
|
||||||
net_util = { path = "net_util" }
|
net_util = { path = "net_util" }
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.20.2"
|
||||||
serde_json = "1.0.120"
|
serde_json = { workspace = true }
|
||||||
test_infra = { path = "test_infra" }
|
test_infra = { path = "test_infra" }
|
||||||
wait-timeout = "0.2.0"
|
wait-timeout = "0.2.0"
|
||||||
|
|
||||||
@@ -100,3 +100,31 @@ members = [
|
|||||||
"vm-virtio",
|
"vm-virtio",
|
||||||
"vmm",
|
"vmm",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
# rust-vmm crates
|
||||||
|
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
|
||||||
|
kvm-bindings = "0.10.0"
|
||||||
|
kvm-ioctls = "0.19.1"
|
||||||
|
linux-loader = "0.13.0"
|
||||||
|
mshv-bindings = "0.5.1"
|
||||||
|
mshv-ioctls = "0.5.1"
|
||||||
|
seccompiler = "0.5.0"
|
||||||
|
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main" }
|
||||||
|
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||||
|
vfio_user = { git = "https://github.com/rust-vmm/vfio-user", branch = "main" }
|
||||||
|
vhost = { git = "https://github.com/rust-vmm/vhost", rev = "d983ae0" }
|
||||||
|
vhost-user-backend = { git = "https://github.com/rust-vmm/vhost", rev = "d983ae0" }
|
||||||
|
virtio-bindings = "0.2.4"
|
||||||
|
virtio-queue = "0.14.0"
|
||||||
|
vm-fdt = { git = "https://github.com/rust-vmm/vm-fdt", branch = "main" }
|
||||||
|
vm-memory = "0.16.1"
|
||||||
|
vmm-sys-util = "0.12.1"
|
||||||
|
|
||||||
|
# igvm crates
|
||||||
|
# TODO: bump to 0.3.5 release
|
||||||
|
igvm = { git = "https://github.com/microsoft/igvm", branch = "main" }
|
||||||
|
igvm_defs = { git = "https://github.com/microsoft/igvm", branch = "main" }
|
||||||
|
|
||||||
|
# serde crates
|
||||||
|
serde_json = "1.0.120"
|
||||||
|
|||||||
24
README.md
24
README.md
@@ -59,9 +59,13 @@ based on the [Rust VMM](https://github.com/rust-vmm) crates.
|
|||||||
|
|
||||||
### Architectures
|
### Architectures
|
||||||
|
|
||||||
Cloud Hypervisor supports the `x86-64` and `AArch64` architectures. There are
|
Cloud Hypervisor supports the `x86-64`, `AArch64` and `riscv64`
|
||||||
minor differences in functionality between the two architectures
|
architectures, with functionality varying across these platforms. The
|
||||||
(see [#1125](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/1125)).
|
functionality differences between `x86-64` and `AArch64` are documented
|
||||||
|
in [#1125](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/1125).
|
||||||
|
The `riscv64` architecture support is experimental and offers limited
|
||||||
|
functionality. For more details and instructions, please refer to [riscv
|
||||||
|
documentation](docs/riscv.md).
|
||||||
|
|
||||||
### Guest OS
|
### Guest OS
|
||||||
|
|
||||||
@@ -181,14 +185,9 @@ To build the kernel:
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
# Clone the Cloud Hypervisor Linux branch
|
# Clone the Cloud Hypervisor Linux branch
|
||||||
$ git clone --depth 1 https://github.com/cloud-hypervisor/linux.git -b ch-6.2 linux-cloud-hypervisor
|
$ git clone --depth 1 https://github.com/cloud-hypervisor/linux.git -b ch-6.12.8 linux-cloud-hypervisor
|
||||||
$ pushd linux-cloud-hypervisor
|
$ pushd linux-cloud-hypervisor
|
||||||
# Use the x86-64 cloud-hypervisor kernel config to build your kernel for x86-64
|
$ make ch_defconfig
|
||||||
$ wget https://raw.githubusercontent.com/cloud-hypervisor/cloud-hypervisor/main/resources/linux-config-x86_64
|
|
||||||
# Use the AArch64 cloud-hypervisor kernel config to build your kernel for AArch64
|
|
||||||
$ wget https://raw.githubusercontent.com/cloud-hypervisor/cloud-hypervisor/main/resources/linux-config-aarch64
|
|
||||||
$ cp linux-config-x86_64 .config # x86-64
|
|
||||||
$ cp linux-config-aarch64 .config # AArch64
|
|
||||||
# Do native build of the x86-64 kernel
|
# Do native build of the x86-64 kernel
|
||||||
$ KCFLAGS="-Wa,-mx86-used-note=no" make bzImage -j `nproc`
|
$ KCFLAGS="-Wa,-mx86-used-note=no" make bzImage -j `nproc`
|
||||||
# Do native build of the AArch64 kernel
|
# Do native build of the AArch64 kernel
|
||||||
@@ -302,8 +301,9 @@ Further details can be found in the [release documentation](docs/releases.md).
|
|||||||
As of 2023-01-03, the following cloud images are supported:
|
As of 2023-01-03, the following cloud images are supported:
|
||||||
|
|
||||||
- [Ubuntu Focal](https://cloud-images.ubuntu.com/focal/current/) (focal-server-cloudimg-{amd64,arm64}.img)
|
- [Ubuntu Focal](https://cloud-images.ubuntu.com/focal/current/) (focal-server-cloudimg-{amd64,arm64}.img)
|
||||||
- [Ubuntu Jammy](https://cloud-images.ubuntu.com/jammy/current/) (jammy-server-cloudimg-{amd64,arm64}.img )
|
- [Ubuntu Jammy](https://cloud-images.ubuntu.com/jammy/current/) (jammy-server-cloudimg-{amd64,arm64}.img)
|
||||||
- [Fedora 36](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/) ([Fedora-Cloud-Base-36-1.5.x86_64.raw.xz](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/x86_64/images/) / [Fedora-Cloud-Base-36-1.5.aarch64.raw.xz](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/aarch64/images/))
|
- [Ubuntu Noble](https://cloud-images.ubuntu.com/noble/current/) (noble-server-cloudimg-{amd64,arm64}.img)
|
||||||
|
- [Fedora 36](https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/36/Cloud/) ([Fedora-Cloud-Base-36-1.5.x86_64.raw.xz](https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/36/Cloud/x86_64/images/) / [Fedora-Cloud-Base-36-1.5.aarch64.raw.xz](https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/36/Cloud/aarch64/images/))
|
||||||
|
|
||||||
Direct kernel boot to userspace should work with a rootfs from most
|
Direct kernel boot to userspace should work with a rootfs from most
|
||||||
distributions although you may need to enable exotic filesystem types in the
|
distributions although you may need to enable exotic filesystem types in the
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ name = "api_client"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror = "1.0.62"
|
thiserror = "2.0.6"
|
||||||
vmm-sys-util = "0.12.1"
|
vmm-sys-util = { workspace = true }
|
||||||
|
|||||||
@@ -5,22 +5,23 @@
|
|||||||
|
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use vmm_sys_util::sock_ctrl_msg::ScmSocket;
|
use vmm_sys_util::sock_ctrl_msg::ScmSocket;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("Error writing to or reading from HTTP socket: {0}")]
|
#[error("Error writing to or reading from HTTP socket: {0}")]
|
||||||
Socket(std::io::Error),
|
Socket(#[source] std::io::Error),
|
||||||
#[error("Error sending file descriptors: {0}")]
|
#[error("Error sending file descriptors: {0}")]
|
||||||
SocketSendFds(vmm_sys_util::errno::Error),
|
SocketSendFds(#[source] vmm_sys_util::errno::Error),
|
||||||
#[error("Error parsing HTTP status code: {0}")]
|
#[error("Error parsing HTTP status code: {0}")]
|
||||||
StatusCodeParsing(std::num::ParseIntError),
|
StatusCodeParsing(#[source] std::num::ParseIntError),
|
||||||
#[error("HTTP output is missing protocol statement")]
|
#[error("HTTP output is missing protocol statement")]
|
||||||
MissingProtocol,
|
MissingProtocol,
|
||||||
#[error("Error parsing HTTP Content-Length field: {0}")]
|
#[error("Error parsing HTTP Content-Length field: {0}")]
|
||||||
ContentLengthParsing(std::num::ParseIntError),
|
ContentLengthParsing(#[source] std::num::ParseIntError),
|
||||||
#[error("Server responded with an error: {0:?}")]
|
#[error("Server responded with an error: {0:?}: {1:?}")]
|
||||||
ServerResponse(StatusCode, Option<String>),
|
ServerResponse(StatusCode, Option<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ pub enum StatusCode {
|
|||||||
NoContent,
|
NoContent,
|
||||||
BadRequest,
|
BadRequest,
|
||||||
NotFound,
|
NotFound,
|
||||||
|
TooManyRequests,
|
||||||
InternalServerError,
|
InternalServerError,
|
||||||
NotImplemented,
|
NotImplemented,
|
||||||
Unknown,
|
Unknown,
|
||||||
@@ -44,6 +46,7 @@ impl StatusCode {
|
|||||||
204 => StatusCode::NoContent,
|
204 => StatusCode::NoContent,
|
||||||
400 => StatusCode::BadRequest,
|
400 => StatusCode::BadRequest,
|
||||||
404 => StatusCode::NotFound,
|
404 => StatusCode::NotFound,
|
||||||
|
429 => StatusCode::TooManyRequests,
|
||||||
500 => StatusCode::InternalServerError,
|
500 => StatusCode::InternalServerError,
|
||||||
501 => StatusCode::NotImplemented,
|
501 => StatusCode::NotImplemented,
|
||||||
_ => StatusCode::Unknown,
|
_ => StatusCode::Unknown,
|
||||||
|
|||||||
@@ -6,27 +6,24 @@ version = "0.1.0"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
kvm = []
|
kvm = ["hypervisor/kvm"]
|
||||||
sev_snp = []
|
sev_snp = []
|
||||||
tdx = []
|
tdx = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.94"
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
libc = "0.2.155"
|
libc = "0.2.167"
|
||||||
linux-loader = { version = "0.11.0", features = ["bzimage", "elf", "pe"] }
|
linux-loader = { workspace = true, features = ["bzimage", "elf", "pe"] }
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
serde = { version = "1.0.197", features = ["derive", "rc"] }
|
serde = { version = "1.0.208", features = ["derive", "rc"] }
|
||||||
thiserror = "1.0.62"
|
thiserror = "2.0.6"
|
||||||
uuid = "1.8.0"
|
uuid = "1.12.1"
|
||||||
vm-memory = { version = "0.14.1", features = [
|
vm-memory = { workspace = true, features = ["backend-bitmap", "backend-mmap"] }
|
||||||
"backend-bitmap",
|
|
||||||
"backend-mmap",
|
|
||||||
] }
|
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
vmm-sys-util = { version = "0.12.1", features = ["with-serde"] }
|
vmm-sys-util = { workspace = true, features = ["with-serde"] }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
[target.'cfg(any(target_arch = "aarch64", target_arch = "riscv64"))'.dependencies]
|
||||||
fdt_parser = { version = "0.1.5", package = "fdt" }
|
fdt_parser = { version = "0.1.5", package = "fdt" }
|
||||||
vm-fdt = { git = "https://github.com/rust-vmm/vm-fdt", branch = "main" }
|
vm-fdt = { workspace = true }
|
||||||
|
|||||||
@@ -6,30 +6,30 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the THIRD-PARTY file.
|
// found in the THIRD-PARTY file.
|
||||||
|
|
||||||
use crate::{NumaNodes, PciSpaceInfo};
|
|
||||||
use byteorder::{BigEndian, ByteOrder};
|
|
||||||
use hypervisor::arch::aarch64::gic::Vgic;
|
|
||||||
use std::cmp;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::result;
|
|
||||||
use std::str;
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use super::super::DeviceType;
|
|
||||||
use super::super::GuestMemoryMmap;
|
|
||||||
use super::super::InitramfsConfig;
|
|
||||||
use super::layout::{
|
|
||||||
IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_PCI_IO_SIZE, MEM_PCI_IO_START,
|
|
||||||
PCI_HIGH_BASE, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
|
|
||||||
};
|
|
||||||
use std::fs;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::{cmp, fs, result, str};
|
||||||
|
|
||||||
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
|
use hypervisor::arch::aarch64::gic::Vgic;
|
||||||
|
use hypervisor::arch::aarch64::regs::{
|
||||||
|
AARCH64_ARCH_TIMER_HYP_IRQ, AARCH64_ARCH_TIMER_PHYS_NONSECURE_IRQ,
|
||||||
|
AARCH64_ARCH_TIMER_PHYS_SECURE_IRQ, AARCH64_ARCH_TIMER_VIRT_IRQ, AARCH64_PMU_IRQ,
|
||||||
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use vm_fdt::{FdtWriter, FdtWriterResult};
|
use vm_fdt::{FdtWriter, FdtWriterResult};
|
||||||
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryRegion};
|
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryRegion};
|
||||||
|
|
||||||
|
use super::super::{DeviceType, GuestMemoryMmap, InitramfsConfig};
|
||||||
|
use super::layout::{
|
||||||
|
GIC_V2M_COMPATIBLE, IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_PCI_IO_SIZE,
|
||||||
|
MEM_PCI_IO_START, PCI_HIGH_BASE, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT, SPI_BASE, SPI_NUM,
|
||||||
|
};
|
||||||
|
use crate::{NumaNodes, PciSpaceInfo};
|
||||||
|
|
||||||
// This is a value for uniquely identifying the FDT node declaring the interrupt controller.
|
// This is a value for uniquely identifying the FDT node declaring the interrupt controller.
|
||||||
const GIC_PHANDLE: u32 = 1;
|
const GIC_PHANDLE: u32 = 1;
|
||||||
// This is a value for uniquely identifying the FDT node declaring the MSI controller.
|
// This is a value for uniquely identifying the FDT node declaring the MSI controller.
|
||||||
@@ -63,9 +63,6 @@ const GIC_FDT_IRQ_TYPE_PPI: u32 = 1;
|
|||||||
const IRQ_TYPE_EDGE_RISING: u32 = 1;
|
const IRQ_TYPE_EDGE_RISING: u32 = 1;
|
||||||
const IRQ_TYPE_LEVEL_HI: u32 = 4;
|
const IRQ_TYPE_LEVEL_HI: u32 = 4;
|
||||||
|
|
||||||
// PMU PPI interrupt number
|
|
||||||
pub const AARCH64_PMU_IRQ: u32 = 7;
|
|
||||||
|
|
||||||
// Keys and Buttons
|
// Keys and Buttons
|
||||||
// System Power Down
|
// System Power Down
|
||||||
const KEY_POWER: u32 = 116;
|
const KEY_POWER: u32 = 116;
|
||||||
@@ -85,7 +82,7 @@ pub trait DeviceInfoForFdt {
|
|||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Failure in writing FDT in memory.
|
/// Failure in writing FDT in memory.
|
||||||
#[error("Failure in writing FDT in memory: {0}")]
|
#[error("Failure in writing FDT in memory: {0}")]
|
||||||
WriteFdtToMemory(GuestMemoryError),
|
WriteFdtToMemory(#[source] GuestMemoryError),
|
||||||
}
|
}
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
@@ -589,15 +586,14 @@ fn create_memory_node(
|
|||||||
&& (first_region_end <= &mem_32bit_reserved_start))
|
&& (first_region_end <= &mem_32bit_reserved_start))
|
||||||
{
|
{
|
||||||
panic!(
|
panic!(
|
||||||
"Unexpected first memory region layout: (start: 0x{:08x}, end: 0x{:08x}).
|
"Unexpected first memory region layout: (start: 0x{first_region_start:08x}, end: 0x{first_region_end:08x}).
|
||||||
ram_start: 0x{:08x}, mem_32bit_reserved_start: 0x{:08x}",
|
ram_start: 0x{ram_start:08x}, mem_32bit_reserved_start: 0x{mem_32bit_reserved_start:08x}"
|
||||||
first_region_start, first_region_end, ram_start, mem_32bit_reserved_start
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mem_size = first_region_end - ram_start;
|
let mem_size = first_region_end - ram_start;
|
||||||
let mem_reg_prop = [ram_start, mem_size];
|
let mem_reg_prop = [ram_start, mem_size];
|
||||||
let memory_node_name = format!("memory@{:x}", ram_start);
|
let memory_node_name = format!("memory@{ram_start:x}");
|
||||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||||
fdt.property_string("device_type", "memory")?;
|
fdt.property_string("device_type", "memory")?;
|
||||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||||
@@ -610,14 +606,13 @@ fn create_memory_node(
|
|||||||
|
|
||||||
if second_region_start != &ram_64bit_start {
|
if second_region_start != &ram_64bit_start {
|
||||||
panic!(
|
panic!(
|
||||||
"Unexpected second memory region layout: start: 0x{:08x}, ram_64bit_start: 0x{:08x}",
|
"Unexpected second memory region layout: start: 0x{second_region_start:08x}, ram_64bit_start: 0x{ram_64bit_start:08x}"
|
||||||
second_region_start, ram_64bit_start
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mem_size = second_region_end - ram_64bit_start;
|
let mem_size = second_region_end - ram_64bit_start;
|
||||||
let mem_reg_prop = [ram_64bit_start, mem_size];
|
let mem_reg_prop = [ram_64bit_start, mem_size];
|
||||||
let memory_node_name = format!("memory@{:x}", ram_64bit_start);
|
let memory_node_name = format!("memory@{ram_64bit_start:x}");
|
||||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||||
fdt.property_string("device_type", "memory")?;
|
fdt.property_string("device_type", "memory")?;
|
||||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||||
@@ -674,11 +669,19 @@ fn create_gic_node(fdt: &mut FdtWriter, gic_device: &Arc<Mutex<dyn Vgic>>) -> Fd
|
|||||||
|
|
||||||
if gic_device.lock().unwrap().msi_compatible() {
|
if gic_device.lock().unwrap().msi_compatible() {
|
||||||
let msic_node = fdt.begin_node("msic")?;
|
let msic_node = fdt.begin_node("msic")?;
|
||||||
fdt.property_string("compatible", gic_device.lock().unwrap().msi_compatibility())?;
|
let msi_compatibility = gic_device.lock().unwrap().msi_compatibility().to_string();
|
||||||
|
|
||||||
|
fdt.property_string("compatible", msi_compatibility.as_str())?;
|
||||||
fdt.property_null("msi-controller")?;
|
fdt.property_null("msi-controller")?;
|
||||||
fdt.property_u32("phandle", MSI_PHANDLE)?;
|
fdt.property_u32("phandle", MSI_PHANDLE)?;
|
||||||
let msi_reg_prop = gic_device.lock().unwrap().msi_properties();
|
let msi_reg_prop = gic_device.lock().unwrap().msi_properties();
|
||||||
fdt.property_array_u64("reg", &msi_reg_prop)?;
|
fdt.property_array_u64("reg", &msi_reg_prop)?;
|
||||||
|
|
||||||
|
if msi_compatibility == GIC_V2M_COMPATIBLE {
|
||||||
|
fdt.property_u32("arm,msi-base-spi", SPI_BASE)?;
|
||||||
|
fdt.property_u32("arm,msi-num-spis", SPI_NUM)?;
|
||||||
|
}
|
||||||
|
|
||||||
fdt.end_node(msic_node)?;
|
fdt.end_node(msic_node)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -705,9 +708,14 @@ fn create_clock_node(fdt: &mut FdtWriter) -> FdtWriterResult<()> {
|
|||||||
|
|
||||||
fn create_timer_node(fdt: &mut FdtWriter) -> FdtWriterResult<()> {
|
fn create_timer_node(fdt: &mut FdtWriter) -> FdtWriterResult<()> {
|
||||||
// See
|
// See
|
||||||
// https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/interrupt-controller/arch_timer.txt
|
// https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/timer/arm%2Carch_timer.yaml
|
||||||
// These are fixed interrupt numbers for the timer device.
|
// These are fixed interrupt numbers for the timer device.
|
||||||
let irqs = [13, 14, 11, 10];
|
let irqs = [
|
||||||
|
AARCH64_ARCH_TIMER_PHYS_SECURE_IRQ,
|
||||||
|
AARCH64_ARCH_TIMER_PHYS_NONSECURE_IRQ,
|
||||||
|
AARCH64_ARCH_TIMER_VIRT_IRQ,
|
||||||
|
AARCH64_ARCH_TIMER_HYP_IRQ,
|
||||||
|
];
|
||||||
let compatible = "arm,armv8-timer";
|
let compatible = "arm,armv8-timer";
|
||||||
|
|
||||||
let mut timer_reg_cells: Vec<u32> = Vec::new();
|
let mut timer_reg_cells: Vec<u32> = Vec::new();
|
||||||
@@ -1113,10 +1121,7 @@ fn print_node(node: fdt_parser::node::FdtNode<'_, '_>, n_spaces: usize) {
|
|||||||
// - At first, try to convert it to CStr and print,
|
// - At first, try to convert it to CStr and print,
|
||||||
// - If failed, print it as u32 array.
|
// - If failed, print it as u32 array.
|
||||||
let value_result = match CStr::from_bytes_with_nul(value) {
|
let value_result = match CStr::from_bytes_with_nul(value) {
|
||||||
Ok(value_cstr) => match value_cstr.to_str() {
|
Ok(value_cstr) => value_cstr.to_str().ok(),
|
||||||
Ok(value_str) => Some(value_str),
|
|
||||||
Err(_e) => None,
|
|
||||||
},
|
|
||||||
Err(_e) => None,
|
Err(_e) => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -138,3 +138,12 @@ pub const IRQ_BASE: u32 = 32;
|
|||||||
|
|
||||||
/// Number of supported interrupts
|
/// Number of supported interrupts
|
||||||
pub const IRQ_NUM: u32 = 256;
|
pub const IRQ_NUM: u32 = 256;
|
||||||
|
|
||||||
|
/// Base SPI interrupt number
|
||||||
|
pub const SPI_BASE: u32 = 32;
|
||||||
|
|
||||||
|
/// Total number of SPIs
|
||||||
|
pub const SPI_NUM: u32 = 64;
|
||||||
|
|
||||||
|
/// GICv2M compatible string
|
||||||
|
pub const GIC_V2M_COMPATIBLE: &str = "arm,gic-v2m-frame";
|
||||||
|
|||||||
@@ -6,21 +6,22 @@
|
|||||||
pub mod fdt;
|
pub mod fdt;
|
||||||
/// Layout for this aarch64 system.
|
/// Layout for this aarch64 system.
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
/// Module for system registers definition
|
|
||||||
pub mod regs;
|
|
||||||
/// Module for loading UEFI binary.
|
/// Module for loading UEFI binary.
|
||||||
pub mod uefi;
|
pub mod uefi;
|
||||||
|
|
||||||
pub use self::fdt::DeviceInfoForFdt;
|
|
||||||
use crate::{DeviceType, GuestMemoryMmap, NumaNodes, PciSpaceInfo, RegionType};
|
|
||||||
use hypervisor::arch::aarch64::gic::Vgic;
|
|
||||||
use log::{log_enabled, Level};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use hypervisor::arch::aarch64::gic::Vgic;
|
||||||
|
use hypervisor::arch::aarch64::regs::MPIDR_EL1;
|
||||||
|
use log::{log_enabled, Level};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic};
|
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic};
|
||||||
|
|
||||||
|
pub use self::fdt::DeviceInfoForFdt;
|
||||||
|
use crate::{DeviceType, GuestMemoryMmap, NumaNodes, PciSpaceInfo, RegionType};
|
||||||
|
|
||||||
pub const _NSIG: i32 = 65;
|
pub const _NSIG: i32 = 65;
|
||||||
|
|
||||||
/// Errors thrown while configuring aarch64 system.
|
/// Errors thrown while configuring aarch64 system.
|
||||||
@@ -32,7 +33,7 @@ pub enum Error {
|
|||||||
|
|
||||||
/// Failed to write FDT to memory.
|
/// Failed to write FDT to memory.
|
||||||
#[error("Failed to write FDT to memory: {0}")]
|
#[error("Failed to write FDT to memory: {0}")]
|
||||||
WriteFdtToMemory(fdt::Error),
|
WriteFdtToMemory(#[source] fdt::Error),
|
||||||
|
|
||||||
/// Failed to create a GIC.
|
/// Failed to create a GIC.
|
||||||
#[error("Failed to create a GIC")]
|
#[error("Failed to create a GIC")]
|
||||||
@@ -44,11 +45,11 @@ pub enum Error {
|
|||||||
|
|
||||||
/// Error configuring the general purpose registers
|
/// Error configuring the general purpose registers
|
||||||
#[error("Error configuring the general purpose registers: {0}")]
|
#[error("Error configuring the general purpose registers: {0}")]
|
||||||
RegsConfiguration(hypervisor::HypervisorCpuError),
|
RegsConfiguration(#[source] hypervisor::HypervisorCpuError),
|
||||||
|
|
||||||
/// Error configuring the MPIDR register
|
/// Error configuring the MPIDR register
|
||||||
#[error("Error configuring the MPIDR register: {0}")]
|
#[error("Error configuring the MPIDR register: {0}")]
|
||||||
VcpuRegMpidr(hypervisor::HypervisorCpuError),
|
VcpuRegMpidr(#[source] hypervisor::HypervisorCpuError),
|
||||||
|
|
||||||
/// Error initializing PMU for vcpu
|
/// Error initializing PMU for vcpu
|
||||||
#[error("Error initializing PMU for vcpu")]
|
#[error("Error initializing PMU for vcpu")]
|
||||||
@@ -84,9 +85,7 @@ pub fn configure_vcpu(
|
|||||||
.map_err(Error::RegsConfiguration)?;
|
.map_err(Error::RegsConfiguration)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mpidr = vcpu
|
let mpidr = vcpu.get_sys_reg(MPIDR_EL1).map_err(Error::VcpuRegMpidr)?;
|
||||||
.get_sys_reg(regs::MPIDR_EL1)
|
|
||||||
.map_err(Error::VcpuRegMpidr)?;
|
|
||||||
Ok(mpidr)
|
Ok(mpidr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// AArch64 system register encoding:
|
|
||||||
// See https://developer.arm.com/documentation/ddi0487 (chapter D12)
|
|
||||||
//
|
|
||||||
// 31 22 21 20 19 18 16 15 12 11 8 7 5 4 0
|
|
||||||
// +----------+---+-----+-----+-----+-----+-----+----+
|
|
||||||
// |1101010100| L | op0 | op1 | CRn | CRm | op2 | Rt |
|
|
||||||
// +----------+---+-----+-----+-----+-----+-----+----+
|
|
||||||
//
|
|
||||||
// Notes:
|
|
||||||
// - L and Rt are reserved as implementation defined fields, ignored.
|
|
||||||
|
|
||||||
const SYSREG_HEAD: u32 = 0b1101010100u32 << 22;
|
|
||||||
const SYSREG_OP0_SHIFT: u32 = 19;
|
|
||||||
const SYSREG_OP0_MASK: u32 = 0b11u32 << 19;
|
|
||||||
const SYSREG_OP1_SHIFT: u32 = 16;
|
|
||||||
const SYSREG_OP1_MASK: u32 = 0b111u32 << 16;
|
|
||||||
const SYSREG_CRN_SHIFT: u32 = 12;
|
|
||||||
const SYSREG_CRN_MASK: u32 = 0b1111u32 << 12;
|
|
||||||
const SYSREG_CRM_SHIFT: u32 = 8;
|
|
||||||
const SYSREG_CRM_MASK: u32 = 0b1111u32 << 8;
|
|
||||||
const SYSREG_OP2_SHIFT: u32 = 5;
|
|
||||||
const SYSREG_OP2_MASK: u32 = 0b111u32 << 5;
|
|
||||||
|
|
||||||
/// Define the ID of system registers
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! arm64_sys_reg {
|
|
||||||
($name: tt, $op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => {
|
|
||||||
pub const $name: u32 = SYSREG_HEAD
|
|
||||||
| ((($op0 as u32) << SYSREG_OP0_SHIFT) & SYSREG_OP0_MASK as u32)
|
|
||||||
| ((($op1 as u32) << SYSREG_OP1_SHIFT) & SYSREG_OP1_MASK as u32)
|
|
||||||
| ((($crn as u32) << SYSREG_CRN_SHIFT) & SYSREG_CRN_MASK as u32)
|
|
||||||
| ((($crm as u32) << SYSREG_CRM_SHIFT) & SYSREG_CRM_MASK as u32)
|
|
||||||
| ((($op2 as u32) << SYSREG_OP2_SHIFT) & SYSREG_OP2_MASK as u32);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
arm64_sys_reg!(MPIDR_EL1, 3, 0, 0, 0, 5);
|
|
||||||
arm64_sys_reg!(ID_AA64MMFR0_EL1, 3, 0, 0, 7, 0);
|
|
||||||
arm64_sys_reg!(TTBR1_EL1, 3, 0, 2, 0, 1);
|
|
||||||
arm64_sys_reg!(TCR_EL1, 3, 0, 2, 0, 2);
|
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
use std::io::{Read, Seek, SeekFrom};
|
use std::io::{Read, Seek, SeekFrom};
|
||||||
use std::os::fd::AsFd;
|
use std::os::fd::AsFd;
|
||||||
use std::result;
|
use std::result;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use vm_memory::{GuestAddress, GuestMemory};
|
use vm_memory::{GuestAddress, GuestMemory};
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||||
// Copyright 2020 Arm Limited (or its affiliates). All rights reserved.
|
// Copyright 2020 Arm Limited (or its affiliates). All rights reserved.
|
||||||
// Copyright © 2020, Oracle and/or its affiliates.
|
// Copyright © 2020, Oracle and/or its affiliates.
|
||||||
//
|
//
|
||||||
@@ -5,19 +6,20 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
//! Implements platform specific functionality.
|
//! Implements platform specific functionality.
|
||||||
//! Supported platforms: x86_64, aarch64.
|
//! Supported platforms: x86_64, aarch64, riscv64.
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::{fmt, result};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::x86_64::SgxEpcSection;
|
use crate::x86_64::SgxEpcSection;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::fmt;
|
|
||||||
use std::result;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<vm_memory::bitmap::AtomicBitmap>;
|
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<vm_memory::bitmap::AtomicBitmap>;
|
||||||
type GuestRegionMmap = vm_memory::GuestRegionMmap<vm_memory::bitmap::AtomicBitmap>;
|
type GuestRegionMmap = vm_memory::GuestRegionMmap<vm_memory::bitmap::AtomicBitmap>;
|
||||||
@@ -26,11 +28,14 @@ type GuestRegionMmap = vm_memory::GuestRegionMmap<vm_memory::bitmap::AtomicBitma
|
|||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
#[error("Platform specific error (x86_64): {0:?}")]
|
#[error("Platform specific error (x86_64): {0}")]
|
||||||
PlatformSpecific(x86_64::Error),
|
PlatformSpecific(#[source] x86_64::Error),
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
#[error("Platform specific error (aarch64): {0:?}")]
|
#[error("Platform specific error (aarch64): {0:?}")]
|
||||||
PlatformSpecific(aarch64::Error),
|
PlatformSpecific(#[source] aarch64::Error),
|
||||||
|
#[cfg(target_arch = "riscv64")]
|
||||||
|
#[error("Platform specific error (riscv64): {0:?}")]
|
||||||
|
PlatformSpecific(#[source] riscv64::Error),
|
||||||
#[error("The memory map table extends past the end of guest memory")]
|
#[error("The memory map table extends past the end of guest memory")]
|
||||||
MemmapTablePastRamEnd,
|
MemmapTablePastRamEnd,
|
||||||
#[error("Error writing memory map table to guest memory")]
|
#[error("Error writing memory map table to guest memory")]
|
||||||
@@ -84,6 +89,17 @@ pub use aarch64::{
|
|||||||
layout::IRQ_BASE, uefi, EntryPoint, _NSIG,
|
layout::IRQ_BASE, uefi, EntryPoint, _NSIG,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Module for riscv64 related functionality.
|
||||||
|
#[cfg(target_arch = "riscv64")]
|
||||||
|
pub mod riscv64;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "riscv64")]
|
||||||
|
pub use riscv64::{
|
||||||
|
arch_memory_regions, configure_system, configure_vcpu, fdt::DeviceInfoForFdt,
|
||||||
|
get_host_cpu_phys_bits, initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE,
|
||||||
|
layout::IRQ_BASE, EntryPoint, _NSIG,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub mod x86_64;
|
pub mod x86_64;
|
||||||
|
|
||||||
@@ -131,7 +147,7 @@ pub enum DeviceType {
|
|||||||
/// Device Type: Virtio.
|
/// Device Type: Virtio.
|
||||||
Virtio(u32),
|
Virtio(u32),
|
||||||
/// Device Type: Serial.
|
/// Device Type: Serial.
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||||
Serial,
|
Serial,
|
||||||
/// Device Type: RTC.
|
/// Device Type: RTC.
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
@@ -152,7 +168,7 @@ impl fmt::Display for DeviceType {
|
|||||||
|
|
||||||
/// Structure to describe MMIO device information
|
/// Structure to describe MMIO device information
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||||
pub struct MmioDeviceInfo {
|
pub struct MmioDeviceInfo {
|
||||||
pub addr: u64,
|
pub addr: u64,
|
||||||
pub len: u64,
|
pub len: u64,
|
||||||
@@ -161,7 +177,7 @@ pub struct MmioDeviceInfo {
|
|||||||
|
|
||||||
/// Structure to describe PCI space information
|
/// Structure to describe PCI space information
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||||
pub struct PciSpaceInfo {
|
pub struct PciSpaceInfo {
|
||||||
pub pci_segment_id: u16,
|
pub pci_segment_id: u16,
|
||||||
pub mmio_config_address: u64,
|
pub mmio_config_address: u64,
|
||||||
@@ -169,7 +185,7 @@ pub struct PciSpaceInfo {
|
|||||||
pub pci_device_space_size: u64,
|
pub pci_device_space_size: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||||
impl DeviceInfoForFdt for MmioDeviceInfo {
|
impl DeviceInfoForFdt for MmioDeviceInfo {
|
||||||
fn addr(&self) -> u64 {
|
fn addr(&self) -> u64 {
|
||||||
self.addr
|
self.addr
|
||||||
|
|||||||
484
arch/src/riscv64/fdt.rs
Normal file
484
arch/src/riscv64/fdt.rs
Normal file
@@ -0,0 +1,484 @@
|
|||||||
|
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||||
|
// Copyright 2020 Arm Limited (or its affiliates). All rights reserved.
|
||||||
|
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the THIRD-PARTY file.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::{cmp, result, str};
|
||||||
|
|
||||||
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
|
use hypervisor::arch::riscv64::aia::Vaia;
|
||||||
|
use thiserror::Error;
|
||||||
|
use vm_fdt::{FdtWriter, FdtWriterResult};
|
||||||
|
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryRegion};
|
||||||
|
|
||||||
|
use super::super::{DeviceType, GuestMemoryMmap, InitramfsConfig};
|
||||||
|
use super::layout::{
|
||||||
|
IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_PCI_IO_SIZE, MEM_PCI_IO_START,
|
||||||
|
PCI_HIGH_BASE, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
|
||||||
|
};
|
||||||
|
use crate::PciSpaceInfo;
|
||||||
|
|
||||||
|
const AIA_APLIC_PHANDLE: u32 = 1;
|
||||||
|
const AIA_IMSIC_PHANDLE: u32 = 2;
|
||||||
|
const CPU_INTC_BASE_PHANDLE: u32 = 3;
|
||||||
|
const CPU_BASE_PHANDLE: u32 = 256 + CPU_INTC_BASE_PHANDLE;
|
||||||
|
// Read the documentation specified when appending the root node to the FDT.
|
||||||
|
const ADDRESS_CELLS: u32 = 0x2;
|
||||||
|
const SIZE_CELLS: u32 = 0x2;
|
||||||
|
|
||||||
|
// From https://elixir.bootlin.com/linux/v6.10/source/include/dt-bindings/interrupt-controller/irq.h#L14
|
||||||
|
const _IRQ_TYPE_EDGE_RISING: u32 = 1;
|
||||||
|
const IRQ_TYPE_LEVEL_HI: u32 = 4;
|
||||||
|
|
||||||
|
const S_MODE_EXT_IRQ: u32 = 9;
|
||||||
|
|
||||||
|
/// Trait for devices to be added to the Flattened Device Tree.
|
||||||
|
pub trait DeviceInfoForFdt {
|
||||||
|
/// Returns the address where this device will be loaded.
|
||||||
|
fn addr(&self) -> u64;
|
||||||
|
/// Returns the associated interrupt for this device.
|
||||||
|
fn irq(&self) -> u32;
|
||||||
|
/// Returns the amount of memory that needs to be reserved for this device.
|
||||||
|
fn length(&self) -> u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Errors thrown while configuring the Flattened Device Tree for riscv64.
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Failure in writing FDT in memory.
|
||||||
|
#[error("Failure in writing FDT in memory: {0}")]
|
||||||
|
WriteFdtToMemory(#[source] GuestMemoryError),
|
||||||
|
}
|
||||||
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
|
/// Creates the flattened device tree for this riscv64 VM.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
|
||||||
|
guest_mem: &GuestMemoryMmap,
|
||||||
|
cmdline: &str,
|
||||||
|
num_vcpu: u32,
|
||||||
|
device_info: &HashMap<(DeviceType, String), T, S>,
|
||||||
|
aia_device: &Arc<Mutex<dyn Vaia>>,
|
||||||
|
initrd: &Option<InitramfsConfig>,
|
||||||
|
pci_space_info: &[PciSpaceInfo],
|
||||||
|
) -> FdtWriterResult<Vec<u8>> {
|
||||||
|
// Allocate stuff necessary for the holding the blob.
|
||||||
|
let mut fdt = FdtWriter::new()?;
|
||||||
|
|
||||||
|
// For an explanation why these nodes were introduced in the blob take a look at
|
||||||
|
// https://github.com/devicetree-org/devicetree-specification/releases/tag/v0.4
|
||||||
|
// In chapter 3.
|
||||||
|
|
||||||
|
// Header or the root node as per above mentioned documentation.
|
||||||
|
let root_node = fdt.begin_node("")?;
|
||||||
|
fdt.property_string("compatible", "linux,dummy-virt")?;
|
||||||
|
// For info on #address-cells and size-cells resort to Table 3.1 Root Node
|
||||||
|
// Properties
|
||||||
|
fdt.property_u32("#address-cells", ADDRESS_CELLS)?;
|
||||||
|
fdt.property_u32("#size-cells", SIZE_CELLS)?;
|
||||||
|
create_cpu_nodes(&mut fdt, num_vcpu)?;
|
||||||
|
create_memory_node(&mut fdt, guest_mem)?;
|
||||||
|
create_chosen_node(&mut fdt, cmdline, initrd)?;
|
||||||
|
create_aia_node(&mut fdt, aia_device)?;
|
||||||
|
create_devices_node(&mut fdt, device_info)?;
|
||||||
|
create_pci_nodes(&mut fdt, pci_space_info)?;
|
||||||
|
|
||||||
|
// End Header node.
|
||||||
|
fdt.end_node(root_node)?;
|
||||||
|
|
||||||
|
let fdt_final = fdt.finish()?;
|
||||||
|
|
||||||
|
Ok(fdt_final)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_fdt_to_memory(fdt_final: Vec<u8>, guest_mem: &GuestMemoryMmap) -> Result<()> {
|
||||||
|
// Write FDT to memory.
|
||||||
|
guest_mem
|
||||||
|
.write_slice(fdt_final.as_slice(), super::layout::FDT_START)
|
||||||
|
.map_err(Error::WriteFdtToMemory)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Following are the auxiliary function for creating the different nodes that we append to our FDT.
|
||||||
|
fn create_cpu_nodes(fdt: &mut FdtWriter, num_cpus: u32) -> FdtWriterResult<()> {
|
||||||
|
// See https://elixir.bootlin.com/linux/v6.10/source/Documentation/devicetree/bindings/riscv/cpus.yaml
|
||||||
|
let cpus = fdt.begin_node("cpus")?;
|
||||||
|
// As per documentation, on RISC-V 64-bit systems value should be set to 1.
|
||||||
|
fdt.property_u32("#address-cells", 0x01)?;
|
||||||
|
fdt.property_u32("#size-cells", 0x0)?;
|
||||||
|
// TODO: Retrieve CPU frequency from cpu timer regs
|
||||||
|
let timebase_frequency: u32 = 0x989680;
|
||||||
|
fdt.property_u32("timebase-frequency", timebase_frequency)?;
|
||||||
|
|
||||||
|
for cpu_index in 0..num_cpus {
|
||||||
|
let cpu = fdt.begin_node(&format!("cpu@{:x}", cpu_index))?;
|
||||||
|
fdt.property_string("device_type", "cpu")?;
|
||||||
|
fdt.property_string("compatible", "riscv")?;
|
||||||
|
fdt.property_string("mmu-type", "sv48")?;
|
||||||
|
fdt.property_string("riscv,isa", "rv64imafdc_smaia_ssaia")?;
|
||||||
|
fdt.property_string("status", "okay")?;
|
||||||
|
fdt.property_u32("reg", cpu_index)?;
|
||||||
|
fdt.property_u32("phandle", CPU_BASE_PHANDLE + cpu_index)?;
|
||||||
|
|
||||||
|
// interrupt controller node
|
||||||
|
let intc_node = fdt.begin_node("interrupt-controller")?;
|
||||||
|
fdt.property_string("compatible", "riscv,cpu-intc")?;
|
||||||
|
fdt.property_u32("#interrupt-cells", 1u32)?;
|
||||||
|
fdt.property_null("interrupt-controller")?;
|
||||||
|
fdt.property_u32("phandle", CPU_INTC_BASE_PHANDLE + cpu_index)?;
|
||||||
|
fdt.end_node(intc_node)?;
|
||||||
|
|
||||||
|
fdt.end_node(cpu)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdt.end_node(cpus)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_memory_node(fdt: &mut FdtWriter, guest_mem: &GuestMemoryMmap) -> FdtWriterResult<()> {
|
||||||
|
// Note: memory regions from "GuestMemory" are sorted and non-zero sized.
|
||||||
|
let ram_regions = {
|
||||||
|
let mut ram_regions = Vec::new();
|
||||||
|
let mut current_start = guest_mem
|
||||||
|
.iter()
|
||||||
|
.next()
|
||||||
|
.map(GuestMemoryRegion::start_addr)
|
||||||
|
.expect("GuestMemory must have one memory region at least")
|
||||||
|
.raw_value();
|
||||||
|
let mut current_end = current_start;
|
||||||
|
|
||||||
|
for (start, size) in guest_mem
|
||||||
|
.iter()
|
||||||
|
.map(|m| (m.start_addr().raw_value(), m.len()))
|
||||||
|
{
|
||||||
|
if current_end == start {
|
||||||
|
// This zone is continuous with the previous one.
|
||||||
|
current_end += size;
|
||||||
|
} else {
|
||||||
|
ram_regions.push((current_start, current_end));
|
||||||
|
|
||||||
|
current_start = start;
|
||||||
|
current_end = start + size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ram_regions.push((current_start, current_end));
|
||||||
|
|
||||||
|
ram_regions
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut mem_reg_property = Vec::new();
|
||||||
|
for region in ram_regions {
|
||||||
|
let mem_size = region.1 - region.0;
|
||||||
|
mem_reg_property.push(region.0);
|
||||||
|
mem_reg_property.push(mem_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ram_start = super::layout::RAM_START.raw_value();
|
||||||
|
let memory_node_name = format!("memory@{:x}", ram_start);
|
||||||
|
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||||
|
fdt.property_string("device_type", "memory")?;
|
||||||
|
fdt.property_array_u64("reg", &mem_reg_property)?;
|
||||||
|
fdt.end_node(memory_node)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_chosen_node(
|
||||||
|
fdt: &mut FdtWriter,
|
||||||
|
cmdline: &str,
|
||||||
|
initrd: &Option<InitramfsConfig>,
|
||||||
|
) -> FdtWriterResult<()> {
|
||||||
|
let chosen_node = fdt.begin_node("chosen")?;
|
||||||
|
fdt.property_string("bootargs", cmdline)?;
|
||||||
|
|
||||||
|
if let Some(initrd_config) = initrd {
|
||||||
|
let initrd_start = initrd_config.address.raw_value();
|
||||||
|
let initrd_end = initrd_config.address.raw_value() + initrd_config.size as u64;
|
||||||
|
fdt.property_u64("linux,initrd-start", initrd_start)?;
|
||||||
|
fdt.property_u64("linux,initrd-end", initrd_end)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdt.end_node(chosen_node)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_aia_node(fdt: &mut FdtWriter, aia_device: &Arc<Mutex<dyn Vaia>>) -> FdtWriterResult<()> {
|
||||||
|
// IMSIC
|
||||||
|
if aia_device.lock().unwrap().msi_compatible() {
|
||||||
|
use super::layout::IMSIC_START;
|
||||||
|
let imsic_name = format!("imsics@{:x}", IMSIC_START.0);
|
||||||
|
let imsic_node = fdt.begin_node(&imsic_name)?;
|
||||||
|
|
||||||
|
fdt.property_string(
|
||||||
|
"compatible",
|
||||||
|
aia_device.lock().unwrap().imsic_compatibility(),
|
||||||
|
)?;
|
||||||
|
let imsic_reg_prop = aia_device.lock().unwrap().imsic_properties();
|
||||||
|
fdt.property_array_u32("reg", &imsic_reg_prop)?;
|
||||||
|
fdt.property_u32("#interrupt-cells", 0u32)?;
|
||||||
|
fdt.property_null("interrupt-controller")?;
|
||||||
|
fdt.property_null("msi-controller")?;
|
||||||
|
// TODO complete num-ids
|
||||||
|
fdt.property_u32("riscv,num-ids", 2047u32)?;
|
||||||
|
fdt.property_u32("phandle", AIA_IMSIC_PHANDLE)?;
|
||||||
|
|
||||||
|
let mut irq_cells = Vec::new();
|
||||||
|
let num_cpus = aia_device.lock().unwrap().vcpu_count();
|
||||||
|
for i in 0..num_cpus {
|
||||||
|
irq_cells.push(CPU_INTC_BASE_PHANDLE + i);
|
||||||
|
irq_cells.push(S_MODE_EXT_IRQ);
|
||||||
|
}
|
||||||
|
fdt.property_array_u32("interrupts-extended", &irq_cells)?;
|
||||||
|
|
||||||
|
fdt.end_node(imsic_node)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// APLIC
|
||||||
|
use super::layout::APLIC_START;
|
||||||
|
let aplic_name = format!("aplic@{:x}", APLIC_START.0);
|
||||||
|
let aplic_node = fdt.begin_node(&aplic_name)?;
|
||||||
|
|
||||||
|
fdt.property_string(
|
||||||
|
"compatible",
|
||||||
|
aia_device.lock().unwrap().aplic_compatibility(),
|
||||||
|
)?;
|
||||||
|
let reg_cells = aia_device.lock().unwrap().aplic_properties();
|
||||||
|
fdt.property_array_u32("reg", ®_cells)?;
|
||||||
|
fdt.property_u32("#interrupt-cells", 2u32)?;
|
||||||
|
fdt.property_null("interrupt-controller")?;
|
||||||
|
// TODO complete num-srcs
|
||||||
|
fdt.property_u32("riscv,num-sources", 96u32)?;
|
||||||
|
fdt.property_u32("phandle", AIA_APLIC_PHANDLE)?;
|
||||||
|
fdt.property_u32("msi-parent", AIA_IMSIC_PHANDLE)?;
|
||||||
|
|
||||||
|
fdt.end_node(aplic_node)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_serial_node<T: DeviceInfoForFdt + Clone + Debug>(
|
||||||
|
fdt: &mut FdtWriter,
|
||||||
|
dev_info: &T,
|
||||||
|
) -> FdtWriterResult<()> {
|
||||||
|
let serial_reg_prop = [dev_info.addr(), dev_info.length()];
|
||||||
|
let irq = [dev_info.irq() - IRQ_BASE, IRQ_TYPE_LEVEL_HI];
|
||||||
|
|
||||||
|
let serial_node = fdt.begin_node(&format!("serial@{:x}", dev_info.addr()))?;
|
||||||
|
fdt.property_string("compatible", "ns16550a")?;
|
||||||
|
fdt.property_array_u64("reg", &serial_reg_prop)?;
|
||||||
|
fdt.property_u32("clock-frequency", 3686400)?;
|
||||||
|
fdt.property_u32("interrupt-parent", AIA_APLIC_PHANDLE)?;
|
||||||
|
fdt.property_array_u32("interrupts", &irq)?;
|
||||||
|
fdt.end_node(serial_node)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_devices_node<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
|
||||||
|
fdt: &mut FdtWriter,
|
||||||
|
dev_info: &HashMap<(DeviceType, String), T, S>,
|
||||||
|
) -> FdtWriterResult<()> {
|
||||||
|
for ((device_type, _device_id), info) in dev_info {
|
||||||
|
match device_type {
|
||||||
|
DeviceType::Serial => create_serial_node(fdt, info)?,
|
||||||
|
DeviceType::Virtio(_) => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_pci_nodes(fdt: &mut FdtWriter, pci_device_info: &[PciSpaceInfo]) -> FdtWriterResult<()> {
|
||||||
|
// Add node for PCIe controller.
|
||||||
|
// See Documentation/devicetree/bindings/pci/host-generic-pci.txt in the kernel
|
||||||
|
// and https://elinux.org/Device_Tree_Usage.
|
||||||
|
// In multiple PCI segments setup, each PCI segment needs a PCI node.
|
||||||
|
for pci_device_info_elem in pci_device_info.iter() {
|
||||||
|
// EDK2 requires the PCIe high space above 4G address.
|
||||||
|
// The actual space in CLH follows the RAM. If the RAM space is small, the PCIe high space
|
||||||
|
// could fall below 4G.
|
||||||
|
// Here we cut off PCI device space below 8G in FDT to workaround the EDK2 check.
|
||||||
|
// But the address written in ACPI is not impacted.
|
||||||
|
let (pci_device_base_64bit, pci_device_size_64bit) =
|
||||||
|
if pci_device_info_elem.pci_device_space_start < PCI_HIGH_BASE.raw_value() {
|
||||||
|
(
|
||||||
|
PCI_HIGH_BASE.raw_value(),
|
||||||
|
pci_device_info_elem.pci_device_space_size
|
||||||
|
- (PCI_HIGH_BASE.raw_value() - pci_device_info_elem.pci_device_space_start),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
pci_device_info_elem.pci_device_space_start,
|
||||||
|
pci_device_info_elem.pci_device_space_size,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
// There is no specific requirement of the 32bit MMIO range, and
|
||||||
|
// therefore at least we can make these ranges 4K aligned.
|
||||||
|
let pci_device_size_32bit: u64 =
|
||||||
|
MEM_32BIT_DEVICES_SIZE / ((1 << 12) * pci_device_info.len() as u64) * (1 << 12);
|
||||||
|
let pci_device_base_32bit: u64 = MEM_32BIT_DEVICES_START.0
|
||||||
|
+ pci_device_size_32bit * pci_device_info_elem.pci_segment_id as u64;
|
||||||
|
|
||||||
|
let ranges = [
|
||||||
|
// io addresses. Since AArch64 will not use IO address,
|
||||||
|
// we can set the same IO address range for every segment.
|
||||||
|
0x1000000,
|
||||||
|
0_u32,
|
||||||
|
0_u32,
|
||||||
|
(MEM_PCI_IO_START.0 >> 32) as u32,
|
||||||
|
MEM_PCI_IO_START.0 as u32,
|
||||||
|
(MEM_PCI_IO_SIZE >> 32) as u32,
|
||||||
|
MEM_PCI_IO_SIZE as u32,
|
||||||
|
// mmio addresses
|
||||||
|
0x2000000, // (ss = 10: 32-bit memory space)
|
||||||
|
(pci_device_base_32bit >> 32) as u32, // PCI address
|
||||||
|
pci_device_base_32bit as u32,
|
||||||
|
(pci_device_base_32bit >> 32) as u32, // CPU address
|
||||||
|
pci_device_base_32bit as u32,
|
||||||
|
(pci_device_size_32bit >> 32) as u32, // size
|
||||||
|
pci_device_size_32bit as u32,
|
||||||
|
// device addresses
|
||||||
|
0x3000000, // (ss = 11: 64-bit memory space)
|
||||||
|
(pci_device_base_64bit >> 32) as u32, // PCI address
|
||||||
|
pci_device_base_64bit as u32,
|
||||||
|
(pci_device_base_64bit >> 32) as u32, // CPU address
|
||||||
|
pci_device_base_64bit as u32,
|
||||||
|
(pci_device_size_64bit >> 32) as u32, // size
|
||||||
|
pci_device_size_64bit as u32,
|
||||||
|
];
|
||||||
|
let bus_range = [0, 0]; // Only bus 0
|
||||||
|
let reg = [
|
||||||
|
pci_device_info_elem.mmio_config_address,
|
||||||
|
PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
|
||||||
|
];
|
||||||
|
// See kernel document Documentation/devicetree/bindings/pci/pci-msi.txt
|
||||||
|
let msi_map = [
|
||||||
|
// rid-base: A single cell describing the first RID matched by the entry.
|
||||||
|
0x0,
|
||||||
|
// msi-controller: A single phandle to an MSI controller.
|
||||||
|
AIA_IMSIC_PHANDLE,
|
||||||
|
// msi-base: An msi-specifier describing the msi-specifier produced for the
|
||||||
|
// first RID matched by the entry.
|
||||||
|
(pci_device_info_elem.pci_segment_id as u32) << 8,
|
||||||
|
// length: A single cell describing how many consecutive RIDs are matched
|
||||||
|
// following the rid-base.
|
||||||
|
0x100,
|
||||||
|
];
|
||||||
|
|
||||||
|
let pci_node_name = format!("pci@{:x}", pci_device_info_elem.mmio_config_address);
|
||||||
|
let pci_node = fdt.begin_node(&pci_node_name)?;
|
||||||
|
|
||||||
|
fdt.property_string("compatible", "pci-host-ecam-generic")?;
|
||||||
|
fdt.property_string("device_type", "pci")?;
|
||||||
|
fdt.property_array_u32("ranges", &ranges)?;
|
||||||
|
fdt.property_array_u32("bus-range", &bus_range)?;
|
||||||
|
fdt.property_u32(
|
||||||
|
"linux,pci-domain",
|
||||||
|
pci_device_info_elem.pci_segment_id as u32,
|
||||||
|
)?;
|
||||||
|
fdt.property_u32("#address-cells", 3)?;
|
||||||
|
fdt.property_u32("#size-cells", 2)?;
|
||||||
|
fdt.property_array_u64("reg", ®)?;
|
||||||
|
fdt.property_u32("#interrupt-cells", 1)?;
|
||||||
|
fdt.property_null("interrupt-map")?;
|
||||||
|
fdt.property_null("interrupt-map-mask")?;
|
||||||
|
fdt.property_null("dma-coherent")?;
|
||||||
|
fdt.property_array_u32("msi-map", &msi_map)?;
|
||||||
|
fdt.property_u32("msi-parent", AIA_IMSIC_PHANDLE)?;
|
||||||
|
|
||||||
|
fdt.end_node(pci_node)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the DTB binary and print for debugging
|
||||||
|
pub fn print_fdt(dtb: &[u8]) {
|
||||||
|
match fdt_parser::Fdt::new(dtb) {
|
||||||
|
Ok(fdt) => {
|
||||||
|
if let Some(root) = fdt.find_node("/") {
|
||||||
|
debug!("Printing the FDT:");
|
||||||
|
print_node(root, 0);
|
||||||
|
} else {
|
||||||
|
debug!("Failed to find root node in FDT for debugging.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => debug!("Failed to parse FDT for debugging."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_node(node: fdt_parser::node::FdtNode<'_, '_>, n_spaces: usize) {
|
||||||
|
debug!("{:indent$}{}/", "", node.name, indent = n_spaces);
|
||||||
|
for property in node.properties() {
|
||||||
|
let name = property.name;
|
||||||
|
|
||||||
|
// If the property is 'compatible', its value requires special handling.
|
||||||
|
// The u8 array could contain multiple null-terminated strings.
|
||||||
|
// We copy the original array and simply replace all 'null' characters with spaces.
|
||||||
|
let value = if name == "compatible" {
|
||||||
|
let mut compatible = vec![0u8; 256];
|
||||||
|
let handled_value = property
|
||||||
|
.value
|
||||||
|
.iter()
|
||||||
|
.map(|&c| if c == 0 { b' ' } else { c })
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let len = cmp::min(255, handled_value.len());
|
||||||
|
compatible[..len].copy_from_slice(&handled_value[..len]);
|
||||||
|
compatible[..(len + 1)].to_vec()
|
||||||
|
} else {
|
||||||
|
property.value.to_vec()
|
||||||
|
};
|
||||||
|
let value = &value;
|
||||||
|
|
||||||
|
// Now the value can be either:
|
||||||
|
// - A null-terminated C string, or
|
||||||
|
// - Binary data
|
||||||
|
// We follow a very simple logic to present the value:
|
||||||
|
// - At first, try to convert it to CStr and print,
|
||||||
|
// - If failed, print it as u32 array.
|
||||||
|
let value_result = match CStr::from_bytes_with_nul(value) {
|
||||||
|
Ok(value_cstr) => match value_cstr.to_str() {
|
||||||
|
Ok(value_str) => Some(value_str),
|
||||||
|
Err(_e) => None,
|
||||||
|
},
|
||||||
|
Err(_e) => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(value_str) = value_result {
|
||||||
|
debug!(
|
||||||
|
"{:indent$}{} : {:#?}",
|
||||||
|
"",
|
||||||
|
name,
|
||||||
|
value_str,
|
||||||
|
indent = (n_spaces + 2)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let mut array = Vec::with_capacity(256);
|
||||||
|
array.resize(value.len() / 4, 0u32);
|
||||||
|
BigEndian::read_u32_into(value, &mut array);
|
||||||
|
debug!(
|
||||||
|
"{:indent$}{} : {:X?}",
|
||||||
|
"",
|
||||||
|
name,
|
||||||
|
array,
|
||||||
|
indent = (n_spaces + 2)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print children nodes if there is any
|
||||||
|
for child in node.children() {
|
||||||
|
print_node(child, n_spaces + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
105
arch/src/riscv64/layout.rs
Normal file
105
arch/src/riscv64/layout.rs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||||
|
// Copyright 2020 Arm Limited (or its affiliates). All rights reserved.
|
||||||
|
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
//
|
||||||
|
// Memory layout of RISC-V 64-bit guest:
|
||||||
|
//
|
||||||
|
// Physical +---------------------------------------------------------------+
|
||||||
|
// address | |
|
||||||
|
// end | |
|
||||||
|
// ~ ~ ~ ~
|
||||||
|
// | |
|
||||||
|
// | Highmem PCI MMIO space |
|
||||||
|
// | |
|
||||||
|
// RAM end +---------------------------------------------------------------+
|
||||||
|
// (dynamic, | |
|
||||||
|
// including | |
|
||||||
|
// hotplug ~ ~ ~ ~
|
||||||
|
// memory) | |
|
||||||
|
// | DRAM |
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// 1 GB +---------------------------------------------------------------+
|
||||||
|
// | |
|
||||||
|
// | PCI MMCONFIG space |
|
||||||
|
// | |
|
||||||
|
// 768 MB +---------------------------------------------------------------+
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// | PCI MMIO space |
|
||||||
|
// | |
|
||||||
|
// 256 MB +---------------------------------------------------------------|
|
||||||
|
// | |
|
||||||
|
// | Legacy devices space |
|
||||||
|
// | |
|
||||||
|
// 128 MB +---------------------------------------------------------------|
|
||||||
|
// | |
|
||||||
|
// | IMSICs |
|
||||||
|
// | |
|
||||||
|
// 64 MB +---------------------------------------------------------------+
|
||||||
|
// | |
|
||||||
|
// | APLICs |
|
||||||
|
// | |
|
||||||
|
// 0 GB +---------------------------------------------------------------+
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
use vm_memory::GuestAddress;
|
||||||
|
|
||||||
|
/// AIA related devices
|
||||||
|
/// See https://elixir.bootlin.com/linux/v6.10/source/arch/riscv/include/uapi/asm/kvm.h
|
||||||
|
/// 0x0 ~ 0x0400_0000 (64 MiB) resides APLICs
|
||||||
|
pub const APLIC_START: GuestAddress = GuestAddress(0);
|
||||||
|
pub const APLIC_SIZE: u64 = 0x4000;
|
||||||
|
|
||||||
|
/// 0x0400_0000 ~ 0x0800_0000 (64 MiB) resides IMSICs
|
||||||
|
pub const IMSIC_START: GuestAddress = GuestAddress(0x0400_0000);
|
||||||
|
pub const IMSIC_SIZE: u64 = 0x1000;
|
||||||
|
|
||||||
|
/// Below this address will reside the AIA, above this address will reside the MMIO devices.
|
||||||
|
const MAPPED_IO_START: GuestAddress = GuestAddress(0x0800_0000);
|
||||||
|
|
||||||
|
/// Space 0x0800_0000 ~ 0x1000_0000 is reserved for legacy devices.
|
||||||
|
pub const LEGACY_SERIAL_MAPPED_IO_START: GuestAddress = MAPPED_IO_START;
|
||||||
|
|
||||||
|
/// Space 0x0905_0000 ~ 0x0906_0000 is reserved for pcie io address
|
||||||
|
pub const MEM_PCI_IO_START: GuestAddress = GuestAddress(0x0905_0000);
|
||||||
|
pub const MEM_PCI_IO_SIZE: u64 = 0x1_0000;
|
||||||
|
|
||||||
|
/// Starting from 0x1000_0000 (256MiB) to 0x3000_0000 (768MiB) is used for PCIE MMIO
|
||||||
|
pub const MEM_32BIT_DEVICES_START: GuestAddress = GuestAddress(0x1000_0000);
|
||||||
|
pub const MEM_32BIT_DEVICES_SIZE: u64 = 0x2000_0000;
|
||||||
|
|
||||||
|
/// PCI MMCONFIG space (start: after the device space at 768MiB, length: 256MiB)
|
||||||
|
pub const PCI_MMCONFIG_START: GuestAddress = GuestAddress(0x3000_0000);
|
||||||
|
pub const PCI_MMCONFIG_SIZE: u64 = 256 << 20;
|
||||||
|
// One bus with potentially 256 devices (32 slots x 8 functions).
|
||||||
|
pub const PCI_MMIO_CONFIG_SIZE_PER_SEGMENT: u64 = 4096 * 256;
|
||||||
|
|
||||||
|
/// Start of RAM.
|
||||||
|
pub const RAM_START: GuestAddress = GuestAddress(0x4000_0000);
|
||||||
|
|
||||||
|
/// Kernel command line maximum size on RISC-V.
|
||||||
|
/// See https://elixir.bootlin.com/linux/v6.10/source/arch/riscv/include/uapi/asm/setup.h
|
||||||
|
pub const CMDLINE_MAX_SIZE: usize = 1024;
|
||||||
|
|
||||||
|
/// FDT is at the beginning of RAM.
|
||||||
|
pub const FDT_START: GuestAddress = RAM_START;
|
||||||
|
pub const FDT_MAX_SIZE: u64 = 0x1_0000;
|
||||||
|
|
||||||
|
/// Kernel start after FDT
|
||||||
|
pub const KERNEL_START: GuestAddress = GuestAddress(RAM_START.0 + FDT_MAX_SIZE);
|
||||||
|
|
||||||
|
/// Pci high memory base
|
||||||
|
pub const PCI_HIGH_BASE: GuestAddress = GuestAddress(0x2_0000_0000);
|
||||||
|
|
||||||
|
/// First usable interrupt on riscv64
|
||||||
|
pub const IRQ_BASE: u32 = 0;
|
||||||
|
|
||||||
|
// As per https://elixir.bootlin.com/linux/v6.10/source/arch/riscv/include/asm/kvm_host.h#L31
|
||||||
|
/// Number of supported interrupts
|
||||||
|
pub const IRQ_NUM: u32 = 1023;
|
||||||
173
arch/src/riscv64/mod.rs
Normal file
173
arch/src/riscv64/mod.rs
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||||
|
// Copyright 2020 Arm Limited (or its affiliates). All rights reserved.
|
||||||
|
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
/// Module for the flattened device tree.
|
||||||
|
pub mod fdt;
|
||||||
|
/// Layout for this riscv64 system.
|
||||||
|
pub mod layout;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use hypervisor::arch::riscv64::aia::Vaia;
|
||||||
|
use log::{log_enabled, Level};
|
||||||
|
use thiserror::Error;
|
||||||
|
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic};
|
||||||
|
|
||||||
|
pub use self::fdt::DeviceInfoForFdt;
|
||||||
|
use crate::{DeviceType, GuestMemoryMmap, PciSpaceInfo, RegionType};
|
||||||
|
|
||||||
|
pub const _NSIG: i32 = 65;
|
||||||
|
|
||||||
|
/// Errors thrown while configuring riscv64 system.
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Failed to create a FDT.
|
||||||
|
#[error("Failed to create a FDT")]
|
||||||
|
SetupFdt,
|
||||||
|
|
||||||
|
/// Failed to write FDT to memory.
|
||||||
|
#[error("Failed to write FDT to memory: {0}")]
|
||||||
|
WriteFdtToMemory(#[source] fdt::Error),
|
||||||
|
|
||||||
|
/// Failed to create a AIA.
|
||||||
|
#[error("Failed to create a AIA")]
|
||||||
|
SetupAia,
|
||||||
|
|
||||||
|
/// Failed to compute the initramfs address.
|
||||||
|
#[error("Failed to compute the initramfs address")]
|
||||||
|
InitramfsAddress,
|
||||||
|
|
||||||
|
/// Error configuring the general purpose registers
|
||||||
|
#[error("Error configuring the general purpose registers: {0}")]
|
||||||
|
RegsConfiguration(#[source] hypervisor::HypervisorCpuError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Error> for super::Error {
|
||||||
|
fn from(e: Error) -> super::Error {
|
||||||
|
super::Error::PlatformSpecific(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
/// Specifies the entry point address where the guest must start
|
||||||
|
/// executing code.
|
||||||
|
pub struct EntryPoint {
|
||||||
|
/// Address in guest memory where the guest must start execution
|
||||||
|
pub entry_addr: GuestAddress,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the specified VCPU, and return its MPIDR.
|
||||||
|
pub fn configure_vcpu(
|
||||||
|
vcpu: &Arc<dyn hypervisor::Vcpu>,
|
||||||
|
id: u8,
|
||||||
|
boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>,
|
||||||
|
) -> super::Result<()> {
|
||||||
|
if let Some((kernel_entry_point, _guest_memory)) = boot_setup {
|
||||||
|
vcpu.setup_regs(
|
||||||
|
id,
|
||||||
|
kernel_entry_point.entry_addr.raw_value(),
|
||||||
|
layout::FDT_START.raw_value(),
|
||||||
|
)
|
||||||
|
.map_err(Error::RegsConfiguration)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> {
|
||||||
|
vec![
|
||||||
|
// 0 MiB ~ 256 MiB: AIA and legacy devices
|
||||||
|
(
|
||||||
|
GuestAddress(0),
|
||||||
|
layout::MEM_32BIT_DEVICES_START.0 as usize,
|
||||||
|
RegionType::Reserved,
|
||||||
|
),
|
||||||
|
// 256 MiB ~ 768 MiB: MMIO space
|
||||||
|
(
|
||||||
|
layout::MEM_32BIT_DEVICES_START,
|
||||||
|
layout::MEM_32BIT_DEVICES_SIZE as usize,
|
||||||
|
RegionType::SubRegion,
|
||||||
|
),
|
||||||
|
// 768 MiB ~ 1 GiB: reserved. The leading 256M for PCIe MMCONFIG space
|
||||||
|
(
|
||||||
|
layout::PCI_MMCONFIG_START,
|
||||||
|
layout::PCI_MMCONFIG_SIZE as usize,
|
||||||
|
RegionType::Reserved,
|
||||||
|
),
|
||||||
|
// 1GiB ~ inf: RAM
|
||||||
|
(layout::RAM_START, usize::MAX, RegionType::Ram),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configures the system and should be called once per vm before starting vcpu threads.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
|
||||||
|
guest_mem: &GuestMemoryMmap,
|
||||||
|
cmdline: &str,
|
||||||
|
num_vcpu: u32,
|
||||||
|
device_info: &HashMap<(DeviceType, String), T, S>,
|
||||||
|
initrd: &Option<super::InitramfsConfig>,
|
||||||
|
pci_space_info: &[PciSpaceInfo],
|
||||||
|
aia_device: &Arc<Mutex<dyn Vaia>>,
|
||||||
|
) -> super::Result<()> {
|
||||||
|
let fdt_final = fdt::create_fdt(
|
||||||
|
guest_mem,
|
||||||
|
cmdline,
|
||||||
|
num_vcpu,
|
||||||
|
device_info,
|
||||||
|
aia_device,
|
||||||
|
initrd,
|
||||||
|
pci_space_info,
|
||||||
|
)
|
||||||
|
.map_err(|_| Error::SetupFdt)?;
|
||||||
|
|
||||||
|
if log_enabled!(Level::Debug) {
|
||||||
|
fdt::print_fdt(&fdt_final);
|
||||||
|
}
|
||||||
|
|
||||||
|
fdt::write_fdt_to_memory(fdt_final, guest_mem).map_err(Error::WriteFdtToMemory)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the memory address where the initramfs could be loaded.
|
||||||
|
pub fn initramfs_load_addr(
|
||||||
|
guest_mem: &GuestMemoryMmap,
|
||||||
|
initramfs_size: usize,
|
||||||
|
) -> super::Result<u64> {
|
||||||
|
let round_to_pagesize = |size| (size + (super::PAGE_SIZE - 1)) & !(super::PAGE_SIZE - 1);
|
||||||
|
match guest_mem
|
||||||
|
.last_addr()
|
||||||
|
.checked_sub(round_to_pagesize(initramfs_size) as u64 - 1)
|
||||||
|
{
|
||||||
|
Some(offset) => {
|
||||||
|
if guest_mem.address_in_range(offset) {
|
||||||
|
Ok(offset.raw_value())
|
||||||
|
} else {
|
||||||
|
Err(super::Error::PlatformSpecific(Error::InitramfsAddress))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => Err(super::Error::PlatformSpecific(Error::InitramfsAddress)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_host_cpu_phys_bits(_hypervisor: &Arc<dyn hypervisor::Hypervisor>) -> u8 {
|
||||||
|
40
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_arch_memory_regions_dram() {
|
||||||
|
let regions = arch_memory_regions();
|
||||||
|
assert_eq!(4, regions.len());
|
||||||
|
assert_eq!(layout::RAM_START, regions[3].0);
|
||||||
|
assert_eq!(RegionType::Ram, regions[3].2);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,22 +12,22 @@ pub mod layout;
|
|||||||
mod mpspec;
|
mod mpspec;
|
||||||
mod mptable;
|
mod mptable;
|
||||||
pub mod regs;
|
pub mod regs;
|
||||||
use crate::GuestMemoryMmap;
|
use std::collections::BTreeMap;
|
||||||
use crate::InitramfsConfig;
|
use std::mem;
|
||||||
use crate::RegionType;
|
|
||||||
use hypervisor::arch::x86::{CpuIdEntry, CPUID_FLAG_VALID_INDEX};
|
use hypervisor::arch::x86::{CpuIdEntry, CPUID_FLAG_VALID_INDEX};
|
||||||
use hypervisor::{CpuVendor, HypervisorCpuError, HypervisorError};
|
use hypervisor::{CpuVendor, HypervisorCpuError, HypervisorError};
|
||||||
use linux_loader::loader::bootparam::{boot_params, setup_header};
|
use linux_loader::loader::bootparam::{boot_params, setup_header};
|
||||||
use linux_loader::loader::elf::start_info::{
|
use linux_loader::loader::elf::start_info::{
|
||||||
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
|
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
|
||||||
};
|
};
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::mem;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use vm_memory::{
|
use vm_memory::{
|
||||||
Address, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
|
Address, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
|
||||||
GuestMemoryRegion, GuestUsize,
|
GuestMemoryRegion, GuestUsize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::{GuestMemoryMmap, InitramfsConfig, RegionType};
|
||||||
mod smbios;
|
mod smbios;
|
||||||
use std::arch::x86_64;
|
use std::arch::x86_64;
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
@@ -133,35 +133,35 @@ pub struct CpuidConfig {
|
|||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Error writing MP table to memory.
|
/// Error writing MP table to memory.
|
||||||
#[error("Error writing MP table to memory: {0}")]
|
#[error("Error writing MP table to memory: {0}")]
|
||||||
MpTableSetup(mptable::Error),
|
MpTableSetup(#[source] mptable::Error),
|
||||||
|
|
||||||
/// Error configuring the general purpose registers
|
/// Error configuring the general purpose registers
|
||||||
#[error("Error configuring the general purpose registers: {0}")]
|
#[error("Error configuring the general purpose registers: {0}")]
|
||||||
RegsConfiguration(regs::Error),
|
RegsConfiguration(#[source] regs::Error),
|
||||||
|
|
||||||
/// Error configuring the special registers
|
/// Error configuring the special registers
|
||||||
#[error("Error configuring the special registers: {0}")]
|
#[error("Error configuring the special registers: {0}")]
|
||||||
SregsConfiguration(regs::Error),
|
SregsConfiguration(#[source] regs::Error),
|
||||||
|
|
||||||
/// Error configuring the floating point related registers
|
/// Error configuring the floating point related registers
|
||||||
#[error("Error configuring the floating point related registers: {0}")]
|
#[error("Error configuring the floating point related registers: {0}")]
|
||||||
FpuConfiguration(regs::Error),
|
FpuConfiguration(#[source] regs::Error),
|
||||||
|
|
||||||
/// Error configuring the MSR registers
|
/// Error configuring the MSR registers
|
||||||
#[error("Error configuring the MSR registers: {0}")]
|
#[error("Error configuring the MSR registers: {0}")]
|
||||||
MsrsConfiguration(regs::Error),
|
MsrsConfiguration(#[source] regs::Error),
|
||||||
|
|
||||||
/// Failed to set supported CPUs.
|
/// Failed to set supported CPUs.
|
||||||
#[error("Failed to set supported CPUs: {0}")]
|
#[error("Failed to set supported CPUs: {0}")]
|
||||||
SetSupportedCpusFailed(anyhow::Error),
|
SetSupportedCpusFailed(#[source] anyhow::Error),
|
||||||
|
|
||||||
/// Cannot set the local interruption due to bad configuration.
|
/// Cannot set the local interruption due to bad configuration.
|
||||||
#[error("Cannot set the local interruption due to bad configuration: {0}")]
|
#[error("Cannot set the local interruption due to bad configuration: {0}")]
|
||||||
LocalIntConfiguration(anyhow::Error),
|
LocalIntConfiguration(#[source] anyhow::Error),
|
||||||
|
|
||||||
/// Error setting up SMBIOS table
|
/// Error setting up SMBIOS table
|
||||||
#[error("Error setting up SMBIOS table: {0}")]
|
#[error("Error setting up SMBIOS table: {0}")]
|
||||||
SmbiosSetup(smbios::Error),
|
SmbiosSetup(#[source] smbios::Error),
|
||||||
|
|
||||||
/// Could not find any SGX EPC section
|
/// Could not find any SGX EPC section
|
||||||
#[error("Could not find any SGX EPC section")]
|
#[error("Could not find any SGX EPC section")]
|
||||||
@@ -177,15 +177,15 @@ pub enum Error {
|
|||||||
|
|
||||||
/// Error getting supported CPUID through the hypervisor (kvm/mshv) API
|
/// Error getting supported CPUID through the hypervisor (kvm/mshv) API
|
||||||
#[error("Error getting supported CPUID through the hypervisor API: {0}")]
|
#[error("Error getting supported CPUID through the hypervisor API: {0}")]
|
||||||
CpuidGetSupported(HypervisorError),
|
CpuidGetSupported(#[source] HypervisorError),
|
||||||
|
|
||||||
/// Error populating CPUID with KVM HyperV emulation details
|
/// Error populating CPUID with KVM HyperV emulation details
|
||||||
#[error("Error populating CPUID with KVM HyperV emulation details: {0}")]
|
#[error("Error populating CPUID with KVM HyperV emulation details: {0}")]
|
||||||
CpuidKvmHyperV(vmm_sys_util::fam::Error),
|
CpuidKvmHyperV(#[source] vmm_sys_util::fam::Error),
|
||||||
|
|
||||||
/// Error populating CPUID with CPU identification
|
/// Error populating CPUID with CPU identification
|
||||||
#[error("Error populating CPUID with CPU identification: {0}")]
|
#[error("Error populating CPUID with CPU identification: {0}")]
|
||||||
CpuidIdentification(vmm_sys_util::fam::Error),
|
CpuidIdentification(#[source] vmm_sys_util::fam::Error),
|
||||||
|
|
||||||
/// Error checking CPUID compatibility
|
/// Error checking CPUID compatibility
|
||||||
#[error("Error checking CPUID compatibility")]
|
#[error("Error checking CPUID compatibility")]
|
||||||
@@ -193,16 +193,16 @@ pub enum Error {
|
|||||||
|
|
||||||
// Error writing EBDA address
|
// Error writing EBDA address
|
||||||
#[error("Error writing EBDA address: {0}")]
|
#[error("Error writing EBDA address: {0}")]
|
||||||
EbdaSetup(vm_memory::GuestMemoryError),
|
EbdaSetup(#[source] vm_memory::GuestMemoryError),
|
||||||
|
|
||||||
// Error getting CPU TSC frequency
|
// Error getting CPU TSC frequency
|
||||||
#[error("Error getting CPU TSC frequency: {0}")]
|
#[error("Error getting CPU TSC frequency: {0}")]
|
||||||
GetTscFrequency(HypervisorCpuError),
|
GetTscFrequency(#[source] HypervisorCpuError),
|
||||||
|
|
||||||
/// Error retrieving TDX capabilities through the hypervisor (kvm/mshv) API
|
/// Error retrieving TDX capabilities through the hypervisor (kvm/mshv) API
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
#[error("Error retrieving TDX capabilities through the hypervisor API: {0}")]
|
#[error("Error retrieving TDX capabilities through the hypervisor API: {0}")]
|
||||||
TdxCapabilities(HypervisorError),
|
TdxCapabilities(#[source] HypervisorError),
|
||||||
|
|
||||||
/// Failed to configure E820 map for bzImage
|
/// Failed to configure E820 map for bzImage
|
||||||
#[error("Failed to configure E820 map for bzImage")]
|
#[error("Failed to configure E820 map for bzImage")]
|
||||||
@@ -601,7 +601,7 @@ pub fn generate_common_cpuid(
|
|||||||
config: &CpuidConfig,
|
config: &CpuidConfig,
|
||||||
) -> super::Result<Vec<CpuIdEntry>> {
|
) -> super::Result<Vec<CpuIdEntry>> {
|
||||||
// SAFETY: cpuid called with valid leaves
|
// SAFETY: cpuid called with valid leaves
|
||||||
if unsafe { x86_64::__cpuid(1) }.ecx & 1 << HYPERVISOR_ECX_BIT == 1 << HYPERVISOR_ECX_BIT {
|
if unsafe { x86_64::__cpuid(1) }.ecx & (1 << HYPERVISOR_ECX_BIT) == 1 << HYPERVISOR_ECX_BIT {
|
||||||
// SAFETY: cpuid called with valid leaves
|
// SAFETY: cpuid called with valid leaves
|
||||||
let hypervisor_cpuid = unsafe { x86_64::__cpuid(0x4000_0000) };
|
let hypervisor_cpuid = unsafe { x86_64::__cpuid(0x4000_0000) };
|
||||||
|
|
||||||
@@ -689,7 +689,7 @@ pub fn generate_common_cpuid(
|
|||||||
// Clear AMX related bits if the AMX feature is not enabled
|
// Clear AMX related bits if the AMX feature is not enabled
|
||||||
0x7 => {
|
0x7 => {
|
||||||
if !config.amx && entry.index == 0 {
|
if !config.amx && entry.index == 0 {
|
||||||
entry.edx &= !(1 << AMX_BF16 | 1 << AMX_TILE | 1 << AMX_INT8)
|
entry.edx &= !((1 << AMX_BF16) | (1 << AMX_TILE) | (1 << AMX_INT8))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0xd =>
|
0xd =>
|
||||||
@@ -747,12 +747,12 @@ pub fn generate_common_cpuid(
|
|||||||
// These features are not supported by TDX
|
// These features are not supported by TDX
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
if config.tdx {
|
if config.tdx {
|
||||||
entry.eax &= !(1 << KVM_FEATURE_CLOCKSOURCE_BIT
|
entry.eax &= !((1 << KVM_FEATURE_CLOCKSOURCE_BIT)
|
||||||
| 1 << KVM_FEATURE_CLOCKSOURCE2_BIT
|
| (1 << KVM_FEATURE_CLOCKSOURCE2_BIT)
|
||||||
| 1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT
|
| (1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT)
|
||||||
| 1 << KVM_FEATURE_ASYNC_PF_BIT
|
| (1 << KVM_FEATURE_ASYNC_PF_BIT)
|
||||||
| 1 << KVM_FEATURE_ASYNC_PF_VMEXIT_BIT
|
| (1 << KVM_FEATURE_ASYNC_PF_VMEXIT_BIT)
|
||||||
| 1 << KVM_FEATURE_STEAL_TIME_BIT)
|
| (1 << KVM_FEATURE_STEAL_TIME_BIT))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@@ -801,10 +801,10 @@ pub fn generate_common_cpuid(
|
|||||||
});
|
});
|
||||||
cpuid.push(CpuIdEntry {
|
cpuid.push(CpuIdEntry {
|
||||||
function: 0x4000_0003,
|
function: 0x4000_0003,
|
||||||
eax: 1 << 1 // AccessPartitionReferenceCounter
|
eax: (1 << 1) // AccessPartitionReferenceCounter
|
||||||
| 1 << 2 // AccessSynicRegs
|
| (1 << 2) // AccessSynicRegs
|
||||||
| 1 << 3 // AccessSyntheticTimerRegs
|
| (1 << 3) // AccessSyntheticTimerRegs
|
||||||
| 1 << 9, // AccessPartitionReferenceTsc
|
| (1 << 9), // AccessPartitionReferenceTsc
|
||||||
edx: 1 << 3, // CPU dynamic partitioning
|
edx: 1 << 3, // CPU dynamic partitioning
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
@@ -887,6 +887,10 @@ pub fn configure_vcpu(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for c in &cpuid {
|
||||||
|
info!("{}", c);
|
||||||
|
}
|
||||||
|
|
||||||
vcpu.set_cpuid2(&cpuid)
|
vcpu.set_cpuid2(&cpuid)
|
||||||
.map_err(|e| Error::SetSupportedCpusFailed(e.into()))?;
|
.map_err(|e| Error::SetSupportedCpusFailed(e.into()))?;
|
||||||
|
|
||||||
@@ -905,6 +909,7 @@ pub fn configure_vcpu(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a Vec of the valid memory addresses.
|
/// Returns a Vec of the valid memory addresses.
|
||||||
|
///
|
||||||
/// These should be used to configure the GuestMemory structure for the platform.
|
/// These should be used to configure the GuestMemory structure for the platform.
|
||||||
/// For x86_64 all addresses are valid from the start of the kernel except a
|
/// For x86_64 all addresses are valid from the start of the kernel except a
|
||||||
/// carve out at the end of 32bit address space.
|
/// carve out at the end of 32bit address space.
|
||||||
@@ -1379,7 +1384,7 @@ fn update_cpuid_topology(
|
|||||||
|
|
||||||
let mut cpu_ebx = CpuidPatch::get_cpuid_reg(cpuid, 0x1, None, CpuidReg::EBX).unwrap_or(0);
|
let mut cpu_ebx = CpuidPatch::get_cpuid_reg(cpuid, 0x1, None, CpuidReg::EBX).unwrap_or(0);
|
||||||
cpu_ebx |= ((dies_per_package as u32) * (cores_per_die as u32) * (threads_per_core as u32))
|
cpu_ebx |= ((dies_per_package as u32) * (cores_per_die as u32) * (threads_per_core as u32))
|
||||||
& 0xff << 16;
|
& (0xff << 16);
|
||||||
CpuidPatch::set_cpuid_reg(cpuid, 0x1, None, CpuidReg::EBX, cpu_ebx);
|
CpuidPatch::set_cpuid_reg(cpuid, 0x1, None, CpuidReg::EBX, cpu_ebx);
|
||||||
|
|
||||||
let mut cpu_edx = CpuidPatch::get_cpuid_reg(cpuid, 0x1, None, CpuidReg::EDX).unwrap_or(0);
|
let mut cpu_edx = CpuidPatch::get_cpuid_reg(cpuid, 0x1, None, CpuidReg::EDX).unwrap_or(0);
|
||||||
@@ -1406,6 +1411,7 @@ fn update_cpuid_topology(
|
|||||||
u32::from(dies_per_package * cores_per_die * threads_per_core),
|
u32::from(dies_per_package * cores_per_die * threads_per_core),
|
||||||
);
|
);
|
||||||
CpuidPatch::set_cpuid_reg(cpuid, 0xb, Some(1), CpuidReg::ECX, 2 << 8);
|
CpuidPatch::set_cpuid_reg(cpuid, 0xb, Some(1), CpuidReg::ECX, 2 << 8);
|
||||||
|
CpuidPatch::set_cpuid_reg(cpuid, 0xb, Some(1), CpuidReg::EDX, x2apic_id);
|
||||||
|
|
||||||
// CPU Topology leaf 0x1f
|
// CPU Topology leaf 0x1f
|
||||||
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(0), CpuidReg::EAX, thread_width);
|
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(0), CpuidReg::EAX, thread_width);
|
||||||
@@ -1550,9 +1556,10 @@ fn update_cpuid_sgx(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
|
||||||
use linux_loader::loader::bootparam::boot_e820_entry;
|
use linux_loader::loader::bootparam::boot_e820_entry;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn regions_base_addr() {
|
fn regions_base_addr() {
|
||||||
let regions = arch_memory_regions();
|
let regions = arch_memory_regions();
|
||||||
@@ -1579,7 +1586,7 @@ mod tests {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
assert!(config_err.is_err());
|
config_err.unwrap_err();
|
||||||
|
|
||||||
// Now assigning some memory that falls before the 32bit memory hole.
|
// Now assigning some memory that falls before the 32bit memory hole.
|
||||||
let arch_mem_regions = arch_memory_regions();
|
let arch_mem_regions = arch_memory_regions();
|
||||||
@@ -1684,13 +1691,13 @@ mod tests {
|
|||||||
// Exercise the scenario where the field storing the length of the e820 entry table is
|
// Exercise the scenario where the field storing the length of the e820 entry table is
|
||||||
// is bigger than the allocated memory.
|
// is bigger than the allocated memory.
|
||||||
params.e820_entries = params.e820_table.len() as u8 + 1;
|
params.e820_entries = params.e820_table.len() as u8 + 1;
|
||||||
assert!(add_e820_entry(
|
add_e820_entry(
|
||||||
&mut params,
|
&mut params,
|
||||||
e820_table[0].addr,
|
e820_table[0].addr,
|
||||||
e820_table[0].size,
|
e820_table[0].size,
|
||||||
e820_table[0].type_
|
e820_table[0].type_,
|
||||||
)
|
)
|
||||||
.is_err());
|
.unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -5,15 +5,15 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE-BSD-3-Clause file.
|
// found in the LICENSE-BSD-3-Clause file.
|
||||||
|
|
||||||
|
use std::{mem, result, slice};
|
||||||
|
|
||||||
|
use libc::c_uchar;
|
||||||
|
use thiserror::Error;
|
||||||
|
use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryError};
|
||||||
|
|
||||||
use crate::layout::{APIC_START, HIGH_RAM_START, IOAPIC_START};
|
use crate::layout::{APIC_START, HIGH_RAM_START, IOAPIC_START};
|
||||||
use crate::x86_64::{get_x2apic_id, mpspec};
|
use crate::x86_64::{get_x2apic_id, mpspec};
|
||||||
use crate::GuestMemoryMmap;
|
use crate::GuestMemoryMmap;
|
||||||
use libc::c_uchar;
|
|
||||||
use std::mem;
|
|
||||||
use std::result;
|
|
||||||
use std::slice;
|
|
||||||
use thiserror::Error;
|
|
||||||
use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryError};
|
|
||||||
|
|
||||||
// This is a workaround to the Rust enforcement specifying that any implementation of a foreign
|
// This is a workaround to the Rust enforcement specifying that any implementation of a foreign
|
||||||
// trait (in this case `ByteValued`) where:
|
// trait (in this case `ByteValued`) where:
|
||||||
@@ -60,31 +60,31 @@ pub enum Error {
|
|||||||
AddressOverflow,
|
AddressOverflow,
|
||||||
/// Failure while zeroing out the memory for the MP table.
|
/// Failure while zeroing out the memory for the MP table.
|
||||||
#[error("Failure while zeroing out the memory for the MP table: {0}")]
|
#[error("Failure while zeroing out the memory for the MP table: {0}")]
|
||||||
Clear(GuestMemoryError),
|
Clear(#[source] GuestMemoryError),
|
||||||
/// Number of CPUs exceeds the maximum supported CPUs
|
/// Number of CPUs exceeds the maximum supported CPUs
|
||||||
#[error("Number of CPUs exceeds the maximum supported CPUs")]
|
#[error("Number of CPUs exceeds the maximum supported CPUs")]
|
||||||
TooManyCpus,
|
TooManyCpus,
|
||||||
/// Failure to write the MP floating pointer.
|
/// Failure to write the MP floating pointer.
|
||||||
#[error("Failure to write the MP floating pointer: {0}")]
|
#[error("Failure to write the MP floating pointer: {0}")]
|
||||||
WriteMpfIntel(GuestMemoryError),
|
WriteMpfIntel(#[source] GuestMemoryError),
|
||||||
/// Failure to write MP CPU entry.
|
/// Failure to write MP CPU entry.
|
||||||
#[error("Failure to write MP CPU entry: {0}")]
|
#[error("Failure to write MP CPU entry: {0}")]
|
||||||
WriteMpcCpu(GuestMemoryError),
|
WriteMpcCpu(#[source] GuestMemoryError),
|
||||||
/// Failure to write MP ioapic entry.
|
/// Failure to write MP ioapic entry.
|
||||||
#[error("Failure to write MP ioapic entry: {0}")]
|
#[error("Failure to write MP ioapic entry: {0}")]
|
||||||
WriteMpcIoapic(GuestMemoryError),
|
WriteMpcIoapic(#[source] GuestMemoryError),
|
||||||
/// Failure to write MP bus entry.
|
/// Failure to write MP bus entry.
|
||||||
#[error("Failure to write MP bus entry: {0}")]
|
#[error("Failure to write MP bus entry: {0}")]
|
||||||
WriteMpcBus(GuestMemoryError),
|
WriteMpcBus(#[source] GuestMemoryError),
|
||||||
/// Failure to write MP interrupt source entry.
|
/// Failure to write MP interrupt source entry.
|
||||||
#[error("Failure to write MP interrupt source entry: {0}")]
|
#[error("Failure to write MP interrupt source entry: {0}")]
|
||||||
WriteMpcIntsrc(GuestMemoryError),
|
WriteMpcIntsrc(#[source] GuestMemoryError),
|
||||||
/// Failure to write MP local interrupt source entry.
|
/// Failure to write MP local interrupt source entry.
|
||||||
#[error("Failure to write MP local interrupt source entry: {0}")]
|
#[error("Failure to write MP local interrupt source entry: {0}")]
|
||||||
WriteMpcLintsrc(GuestMemoryError),
|
WriteMpcLintsrc(#[source] GuestMemoryError),
|
||||||
/// Failure to write MP table header.
|
/// Failure to write MP table header.
|
||||||
#[error("Failure to write MP table header: {0}")]
|
#[error("Failure to write MP table header: {0}")]
|
||||||
WriteMpcTable(GuestMemoryError),
|
WriteMpcTable(#[source] GuestMemoryError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
@@ -304,11 +304,11 @@ pub fn setup_mptable(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use vm_memory::bitmap::BitmapSlice;
|
||||||
|
use vm_memory::{GuestUsize, VolatileMemoryError, VolatileSlice, WriteVolatile};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::layout::MPTABLE_START;
|
use crate::layout::MPTABLE_START;
|
||||||
use vm_memory::{
|
|
||||||
bitmap::BitmapSlice, GuestUsize, VolatileMemoryError, VolatileSlice, WriteVolatile,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn table_entry_size(type_: u8) -> usize {
|
fn table_entry_size(type_: u8) -> usize {
|
||||||
match type_ as u32 {
|
match type_ as u32 {
|
||||||
@@ -336,7 +336,7 @@ mod tests {
|
|||||||
let mem = GuestMemoryMmap::from_ranges(&[(MPTABLE_START, compute_mp_size(num_cpus) - 1)])
|
let mem = GuestMemoryMmap::from_ranges(&[(MPTABLE_START, compute_mp_size(num_cpus) - 1)])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert!(setup_mptable(MPTABLE_START, &mem, num_cpus, None).is_err());
|
setup_mptable(MPTABLE_START, &mem, num_cpus, None).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -433,6 +433,6 @@ mod tests {
|
|||||||
GuestMemoryMmap::from_ranges(&[(MPTABLE_START, compute_mp_size(cpus as u8))]).unwrap();
|
GuestMemoryMmap::from_ranges(&[(MPTABLE_START, compute_mp_size(cpus as u8))]).unwrap();
|
||||||
|
|
||||||
let result = setup_mptable(MPTABLE_START, &mem, cpus as u8, None);
|
let result = setup_mptable(MPTABLE_START, &mem, cpus as u8, None);
|
||||||
assert!(result.is_err());
|
result.unwrap_err();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,56 +6,58 @@
|
|||||||
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE-BSD-3-Clause file.
|
// found in the LICENSE-BSD-3-Clause file.
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::{mem, result};
|
||||||
|
|
||||||
|
use hypervisor::arch::x86::gdt::{gdt_entry, segment_from_gdt};
|
||||||
|
use hypervisor::arch::x86::regs::CR0_PE;
|
||||||
|
use hypervisor::arch::x86::{FpuState, SpecialRegisters};
|
||||||
|
use thiserror::Error;
|
||||||
|
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError};
|
||||||
|
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
BOOT_GDT_START, BOOT_IDT_START, BOOT_STACK_POINTER, PVH_INFO_START, ZERO_PAGE_START,
|
BOOT_GDT_START, BOOT_IDT_START, BOOT_STACK_POINTER, PVH_INFO_START, ZERO_PAGE_START,
|
||||||
};
|
};
|
||||||
use crate::{EntryPoint, GuestMemoryMmap};
|
use crate::{EntryPoint, GuestMemoryMmap};
|
||||||
use hypervisor::arch::x86::gdt::{gdt_entry, segment_from_gdt};
|
|
||||||
use hypervisor::arch::x86::regs::CR0_PE;
|
|
||||||
use hypervisor::arch::x86::{FpuState, SpecialRegisters, StandardRegisters};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::{mem, result};
|
|
||||||
use thiserror::Error;
|
|
||||||
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError};
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Failed to get SREGs for this CPU.
|
/// Failed to get SREGs for this CPU.
|
||||||
#[error("Failed to get SREGs for this CPU: {0}")]
|
#[error("Failed to get SREGs for this CPU: {0}")]
|
||||||
GetStatusRegisters(hypervisor::HypervisorCpuError),
|
GetStatusRegisters(#[source] hypervisor::HypervisorCpuError),
|
||||||
/// Failed to set base registers for this CPU.
|
/// Failed to set base registers for this CPU.
|
||||||
#[error("Failed to set base registers for this CPU: {0}")]
|
#[error("Failed to set base registers for this CPU: {0}")]
|
||||||
SetBaseRegisters(hypervisor::HypervisorCpuError),
|
SetBaseRegisters(#[source] hypervisor::HypervisorCpuError),
|
||||||
/// Failed to configure the FPU.
|
/// Failed to configure the FPU.
|
||||||
#[error("Failed to configure the FPU: {0}")]
|
#[error("Failed to configure the FPU: {0}")]
|
||||||
SetFpuRegisters(hypervisor::HypervisorCpuError),
|
SetFpuRegisters(#[source] hypervisor::HypervisorCpuError),
|
||||||
/// Setting up MSRs failed.
|
/// Setting up MSRs failed.
|
||||||
#[error("Setting up MSRs failed: {0}")]
|
#[error("Setting up MSRs failed: {0}")]
|
||||||
SetModelSpecificRegisters(hypervisor::HypervisorCpuError),
|
SetModelSpecificRegisters(#[source] hypervisor::HypervisorCpuError),
|
||||||
/// Failed to set SREGs for this CPU.
|
/// Failed to set SREGs for this CPU.
|
||||||
#[error("Failed to set SREGs for this CPU: {0}")]
|
#[error("Failed to set SREGs for this CPU: {0}")]
|
||||||
SetStatusRegisters(hypervisor::HypervisorCpuError),
|
SetStatusRegisters(#[source] hypervisor::HypervisorCpuError),
|
||||||
/// Checking the GDT address failed.
|
/// Checking the GDT address failed.
|
||||||
#[error("Checking the GDT address failed")]
|
#[error("Checking the GDT address failed")]
|
||||||
CheckGdtAddr,
|
CheckGdtAddr,
|
||||||
/// Writing the GDT to RAM failed.
|
/// Writing the GDT to RAM failed.
|
||||||
#[error("Writing the GDT to RAM failed: {0}")]
|
#[error("Writing the GDT to RAM failed: {0}")]
|
||||||
WriteGdt(GuestMemoryError),
|
WriteGdt(#[source] GuestMemoryError),
|
||||||
/// Writing the IDT to RAM failed.
|
/// Writing the IDT to RAM failed.
|
||||||
#[error("Writing the IDT to RAM failed: {0}")]
|
#[error("Writing the IDT to RAM failed: {0}")]
|
||||||
WriteIdt(GuestMemoryError),
|
WriteIdt(#[source] GuestMemoryError),
|
||||||
/// Writing PDPTE to RAM failed.
|
/// Writing PDPTE to RAM failed.
|
||||||
#[error("Writing PDPTE to RAM failed: {0}")]
|
#[error("Writing PDPTE to RAM failed: {0}")]
|
||||||
WritePdpteAddress(GuestMemoryError),
|
WritePdpteAddress(#[source] GuestMemoryError),
|
||||||
/// Writing PDE to RAM failed.
|
/// Writing PDE to RAM failed.
|
||||||
#[error("Writing PDE to RAM failed: {0}")]
|
#[error("Writing PDE to RAM failed: {0}")]
|
||||||
WritePdeAddress(GuestMemoryError),
|
WritePdeAddress(#[source] GuestMemoryError),
|
||||||
/// Writing PML4 to RAM failed.
|
/// Writing PML4 to RAM failed.
|
||||||
#[error("Writing PML4 to RAM failed: {0}")]
|
#[error("Writing PML4 to RAM failed: {0}")]
|
||||||
WritePml4Address(GuestMemoryError),
|
WritePml4Address(#[source] GuestMemoryError),
|
||||||
/// Writing PML5 to RAM failed.
|
/// Writing PML5 to RAM failed.
|
||||||
#[error("Writing PML5 to RAM failed: {0}")]
|
#[error("Writing PML5 to RAM failed: {0}")]
|
||||||
WritePml5Address(GuestMemoryError),
|
WritePml5Address(#[source] GuestMemoryError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
@@ -94,20 +96,19 @@ pub fn setup_msrs(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
|
|||||||
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
||||||
/// * `entry_point` - Description of the boot entry to set up.
|
/// * `entry_point` - Description of the boot entry to set up.
|
||||||
pub fn setup_regs(vcpu: &Arc<dyn hypervisor::Vcpu>, entry_point: EntryPoint) -> Result<()> {
|
pub fn setup_regs(vcpu: &Arc<dyn hypervisor::Vcpu>, entry_point: EntryPoint) -> Result<()> {
|
||||||
let regs = match entry_point.setup_header {
|
let mut regs = vcpu.create_standard_regs();
|
||||||
None => StandardRegisters {
|
match entry_point.setup_header {
|
||||||
rflags: 0x0000000000000002u64,
|
None => {
|
||||||
rip: entry_point.entry_addr.raw_value(),
|
regs.set_rflags(0x0000000000000002u64);
|
||||||
rbx: PVH_INFO_START.raw_value(),
|
regs.set_rip(entry_point.entry_addr.raw_value());
|
||||||
..Default::default()
|
regs.set_rbx(PVH_INFO_START.raw_value());
|
||||||
},
|
}
|
||||||
Some(_) => StandardRegisters {
|
Some(_) => {
|
||||||
rflags: 0x0000000000000002u64,
|
regs.set_rflags(0x0000000000000002u64);
|
||||||
rip: entry_point.entry_addr.raw_value(),
|
regs.set_rip(entry_point.entry_addr.raw_value());
|
||||||
rsp: BOOT_STACK_POINTER.raw_value(),
|
regs.set_rsp(BOOT_STACK_POINTER.raw_value());
|
||||||
rsi: ZERO_PAGE_START.raw_value(),
|
regs.set_rsi(ZERO_PAGE_START.raw_value());
|
||||||
..Default::default()
|
}
|
||||||
},
|
|
||||||
};
|
};
|
||||||
vcpu.set_regs(®s).map_err(Error::SetBaseRegisters)
|
vcpu.set_regs(®s).map_err(Error::SetBaseRegisters)
|
||||||
}
|
}
|
||||||
@@ -187,9 +188,10 @@ pub fn configure_segments_and_sregs(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
|
||||||
use vm_memory::GuestAddress;
|
use vm_memory::GuestAddress;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
fn create_guest_mem() -> GuestMemoryMmap {
|
fn create_guest_mem() -> GuestMemoryMmap {
|
||||||
GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap()
|
GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,14 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
use crate::layout::SMBIOS_START;
|
use std::{mem, result, slice};
|
||||||
use crate::GuestMemoryMmap;
|
|
||||||
use std::mem;
|
|
||||||
use std::result;
|
|
||||||
use std::slice;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use vm_memory::ByteValued;
|
use vm_memory::{Address, ByteValued, Bytes, GuestAddress};
|
||||||
use vm_memory::{Address, Bytes, GuestAddress};
|
|
||||||
|
use crate::layout::SMBIOS_START;
|
||||||
|
use crate::GuestMemoryMmap;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@@ -35,7 +34,7 @@ pub enum Error {
|
|||||||
WriteData,
|
WriteData,
|
||||||
/// Failure to parse uuid, uuid format may be error
|
/// Failure to parse uuid, uuid format may be error
|
||||||
#[error("Failure to parse uuid: {0}")]
|
#[error("Failure to parse uuid: {0}")]
|
||||||
ParseUuid(uuid::Error),
|
ParseUuid(#[source] uuid::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
// Copyright © 2021 Intel Corporation
|
// Copyright © 2021 Intel Corporation
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
use crate::GuestMemoryMmap;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Seek, SeekFrom};
|
use std::io::{Read, Seek, SeekFrom};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemoryError};
|
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemoryError};
|
||||||
|
|
||||||
|
use crate::GuestMemoryMmap;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum TdvfError {
|
pub enum TdvfError {
|
||||||
#[error("Failed read TDVF descriptor: {0}")]
|
#[error("Failed read TDVF descriptor: {0}")]
|
||||||
@@ -33,7 +35,7 @@ const TABLE_FOOTER_GUID: &str = "96b582de-1fb2-45f7-baea-a366c55a082d";
|
|||||||
const TDVF_METADATA_OFFSET_GUID: &str = "e47a6535-984a-4798-865e-4685a7bf8ec2";
|
const TDVF_METADATA_OFFSET_GUID: &str = "e47a6535-984a-4798-865e-4685a7bf8ec2";
|
||||||
|
|
||||||
// TDVF_DESCRIPTOR
|
// TDVF_DESCRIPTOR
|
||||||
#[repr(packed)]
|
#[repr(C, packed)]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct TdvfDescriptor {
|
pub struct TdvfDescriptor {
|
||||||
signature: [u8; 4],
|
signature: [u8; 4],
|
||||||
@@ -43,7 +45,7 @@ pub struct TdvfDescriptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TDVF_SECTION
|
// TDVF_SECTION
|
||||||
#[repr(packed)]
|
#[repr(C, packed)]
|
||||||
#[derive(Clone, Copy, Default, Debug)]
|
#[derive(Clone, Copy, Default, Debug)]
|
||||||
pub struct TdvfSection {
|
pub struct TdvfSection {
|
||||||
pub data_offset: u32,
|
pub data_offset: u32,
|
||||||
@@ -207,7 +209,7 @@ enum HobType {
|
|||||||
EndOfHobList = 0xffff,
|
EndOfHobList = 0xffff,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C, packed)]
|
||||||
#[derive(Copy, Clone, Default, Debug)]
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
struct HobHeader {
|
struct HobHeader {
|
||||||
r#type: HobType,
|
r#type: HobType,
|
||||||
@@ -215,7 +217,7 @@ struct HobHeader {
|
|||||||
reserved: u32,
|
reserved: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C, packed)]
|
||||||
#[derive(Copy, Clone, Default, Debug)]
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
struct HobHandoffInfoTable {
|
struct HobHandoffInfoTable {
|
||||||
header: HobHeader,
|
header: HobHeader,
|
||||||
@@ -228,7 +230,7 @@ struct HobHandoffInfoTable {
|
|||||||
efi_end_of_hob_list: u64,
|
efi_end_of_hob_list: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C, packed)]
|
||||||
#[derive(Copy, Clone, Default, Debug)]
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
struct EfiGuid {
|
struct EfiGuid {
|
||||||
data1: u32,
|
data1: u32,
|
||||||
@@ -237,7 +239,7 @@ struct EfiGuid {
|
|||||||
data4: [u8; 8],
|
data4: [u8; 8],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C, packed)]
|
||||||
#[derive(Copy, Clone, Default, Debug)]
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
struct HobResourceDescriptor {
|
struct HobResourceDescriptor {
|
||||||
header: HobHeader,
|
header: HobHeader,
|
||||||
@@ -248,7 +250,7 @@ struct HobResourceDescriptor {
|
|||||||
resource_length: u64,
|
resource_length: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C, packed)]
|
||||||
#[derive(Copy, Clone, Default, Debug)]
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
struct HobGuidType {
|
struct HobGuidType {
|
||||||
header: HobHeader,
|
header: HobHeader,
|
||||||
@@ -264,14 +266,14 @@ pub enum PayloadImageType {
|
|||||||
RawVmLinux,
|
RawVmLinux,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C, packed)]
|
||||||
#[derive(Copy, Clone, Default, Debug)]
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
pub struct PayloadInfo {
|
pub struct PayloadInfo {
|
||||||
pub image_type: PayloadImageType,
|
pub image_type: PayloadImageType,
|
||||||
pub entry_point: u64,
|
pub entry_point: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C, packed)]
|
||||||
#[derive(Copy, Clone, Default, Debug)]
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
struct TdPayload {
|
struct TdPayload {
|
||||||
guid_type: HobGuidType,
|
guid_type: HobGuidType,
|
||||||
@@ -297,7 +299,7 @@ pub struct TdHob {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn align_hob(v: u64) -> u64 {
|
fn align_hob(v: u64) -> u64 {
|
||||||
(v + 7) / 8 * 8
|
v.div_ceil(8) * 8
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TdHob {
|
impl TdHob {
|
||||||
|
|||||||
@@ -11,20 +11,20 @@ io_uring = ["dep:io-uring"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
crc-any = "2.4.4"
|
crc-any = "2.4.4"
|
||||||
io-uring = { version = "0.6.3", optional = true }
|
io-uring = { version = "0.6.4", optional = true }
|
||||||
libc = "0.2.155"
|
libc = "0.2.167"
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
remain = "0.2.14"
|
remain = "0.2.14"
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.208", features = ["derive"] }
|
||||||
smallvec = "1.13.2"
|
smallvec = "1.13.2"
|
||||||
thiserror = "1.0.62"
|
thiserror = "2.0.6"
|
||||||
uuid = { version = "1.8.0", features = ["v4"] }
|
uuid = { version = "1.12.1", features = ["v4"] }
|
||||||
virtio-bindings = { version = "0.2.2", features = ["virtio-v5_0_0"] }
|
virtio-bindings = { workspace = true, features = ["virtio-v5_0_0"] }
|
||||||
virtio-queue = "0.12.0"
|
virtio-queue = { workspace = true }
|
||||||
vm-memory = { version = "0.14.1", features = [
|
vm-memory = { workspace = true, features = [
|
||||||
"backend-atomic",
|
"backend-atomic",
|
||||||
"backend-bitmap",
|
"backend-bitmap",
|
||||||
"backend-mmap",
|
"backend-mmap",
|
||||||
] }
|
] }
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
vmm-sys-util = "0.12.1"
|
vmm-sys-util = { workspace = true }
|
||||||
|
|||||||
@@ -2,10 +2,14 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
use crate::DiskTopology;
|
use std::marker::PhantomData;
|
||||||
|
use std::os::fd::{AsRawFd, OwnedFd, RawFd};
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
use crate::DiskTopology;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum DiskFileError {
|
pub enum DiskFileError {
|
||||||
/// Failed getting disk file size.
|
/// Failed getting disk file size.
|
||||||
@@ -18,12 +22,50 @@ pub enum DiskFileError {
|
|||||||
|
|
||||||
pub type DiskFileResult<T> = std::result::Result<T, DiskFileError>;
|
pub type DiskFileResult<T> = std::result::Result<T, DiskFileError>;
|
||||||
|
|
||||||
|
/// A wrapper for [`RawFd`] capturing the lifetime of a corresponding [`DiskFile`].
|
||||||
|
///
|
||||||
|
/// This fulfills the same role as [`BorrowedFd`] but is tailored to the limitations
|
||||||
|
/// by some implementations of [`DiskFile`], which wrap the effective [`File`]
|
||||||
|
/// in an `Arc<Mutex<T>>`, making the use of [`BorrowedFd`] impossible.
|
||||||
|
///
|
||||||
|
/// [`BorrowedFd`]: std::os::fd::BorrowedFd
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct BorrowedDiskFd<'fd> {
|
||||||
|
raw_fd: RawFd,
|
||||||
|
_lifetime: PhantomData<&'fd OwnedFd>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BorrowedDiskFd<'_> {
|
||||||
|
pub(super) fn new(raw_fd: RawFd) -> Self {
|
||||||
|
Self {
|
||||||
|
raw_fd,
|
||||||
|
_lifetime: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for BorrowedDiskFd<'_> {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.raw_fd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Abstraction over the effective [`File`] backing up a block device,
|
||||||
|
/// with support for synchronous and asynchronous I/O.
|
||||||
|
///
|
||||||
|
/// This allows abstracting over raw image formats as well as structured
|
||||||
|
/// image formats.
|
||||||
pub trait DiskFile: Send {
|
pub trait DiskFile: Send {
|
||||||
fn size(&mut self) -> DiskFileResult<u64>;
|
fn size(&mut self) -> DiskFileResult<u64>;
|
||||||
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>>;
|
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>>;
|
||||||
fn topology(&mut self) -> DiskTopology {
|
fn topology(&mut self) -> DiskTopology {
|
||||||
DiskTopology::default()
|
DiskTopology::default()
|
||||||
}
|
}
|
||||||
|
/// Returns the file descriptor of the underlying disk image file.
|
||||||
|
///
|
||||||
|
/// The file descriptor is supposed to be used for `fcntl()` calls but no
|
||||||
|
/// other operation.
|
||||||
|
fn fd(&mut self) -> BorrowedDiskFd;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
|||||||
173
block/src/fcntl.rs
Normal file
173
block/src/fcntl.rs
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
// Copyright © 2025 Cyberus Technology GmbH
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
//! Helpers for advisory file locking.
|
||||||
|
//!
|
||||||
|
//! Under the hood, the implementation uses OFD locks for the entire file,
|
||||||
|
//! as described in [[0]]. The advantage over `F_SETLKW` (currently used by
|
||||||
|
//! Rust std: `File::try_lock()`) is that only the very last `close()` on a
|
||||||
|
//! file descriptor releases the lock. This prevents mistakes and unexpected
|
||||||
|
//! behavior.
|
||||||
|
//!
|
||||||
|
//! [0]: <https://apenwarr.ca/log/20101213>.
|
||||||
|
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::io;
|
||||||
|
use std::os::fd::{AsRawFd, RawFd};
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Errors that can happen when working with file locks.
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum LockError {
|
||||||
|
/// The file is already locked.
|
||||||
|
///
|
||||||
|
/// A call to [`get_lock_state`] can help to identify the reason.
|
||||||
|
#[error("The file is already locked")]
|
||||||
|
AlreadyLocked,
|
||||||
|
/// IO error.
|
||||||
|
#[error("The lock state could not be checked or set: {0}")]
|
||||||
|
Io(#[source] io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Commands for use with [`fcntl`].
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
enum FcntlArg<'a> {
|
||||||
|
/// Set an OFD lock from the given lock description.
|
||||||
|
F_OFD_SETLK(&'a libc::flock),
|
||||||
|
/// Get the first OFD lock for the given lock description.
|
||||||
|
F_OFD_GETLK(&'a mut libc::flock),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper for [`libc::fcntl`] that properly sets the function arguments.
|
||||||
|
fn fcntl(fd: RawFd, arg: FcntlArg) -> libc::c_int {
|
||||||
|
// SAFETY: We use a valid FD.
|
||||||
|
unsafe {
|
||||||
|
match arg {
|
||||||
|
FcntlArg::F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock),
|
||||||
|
FcntlArg::F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes the type of lock you want to set.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum LockType {
|
||||||
|
/// Clear a lock.
|
||||||
|
Unlock,
|
||||||
|
/// Set a write lock (exclusive).
|
||||||
|
Write,
|
||||||
|
/// Set a read lock (shared).
|
||||||
|
Read,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LockType {
|
||||||
|
pub const fn to_libc_val(self) -> libc::c_int {
|
||||||
|
match self {
|
||||||
|
Self::Unlock => libc::F_UNLCK as libc::c_int,
|
||||||
|
Self::Write => libc::F_WRLCK as libc::c_int,
|
||||||
|
Self::Read => libc::F_RDLCK as libc::c_int,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes the current state of a lock.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum LockState {
|
||||||
|
/// No lock set.
|
||||||
|
Unlocked,
|
||||||
|
/// Locked for reading (non-exclusive).
|
||||||
|
SharedRead,
|
||||||
|
/// Locked for writing (exclusive mode).
|
||||||
|
ExclusiveWrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LockState {
|
||||||
|
fn new(value: libc::c_int) -> Self {
|
||||||
|
const F_UNLCK: libc::c_int = libc::F_UNLCK as libc::c_int;
|
||||||
|
const F_WRLCK: libc::c_int = libc::F_WRLCK as libc::c_int;
|
||||||
|
const F_RDLCK: libc::c_int = libc::F_RDLCK as libc::c_int;
|
||||||
|
match value {
|
||||||
|
F_UNLCK => Self::Unlocked,
|
||||||
|
F_WRLCK => Self::ExclusiveWrite,
|
||||||
|
F_RDLCK => Self::SharedRead,
|
||||||
|
// This is so unlikely that we want to avoid the complexity of
|
||||||
|
// coping with this error case. Can only fail if either Linux
|
||||||
|
// is broken or memory is messed up.
|
||||||
|
other => panic!("Unexpected lock state: {other}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [`struct@libc::flock`] structure for the whole file.
|
||||||
|
const fn get_flock(lock_type: LockType) -> libc::flock {
|
||||||
|
libc::flock {
|
||||||
|
l_type: lock_type.to_libc_val() as libc::c_short,
|
||||||
|
l_whence: libc::SEEK_SET as libc::c_short,
|
||||||
|
l_start: 0,
|
||||||
|
l_len: 0, /* EOF */
|
||||||
|
l_pid: 0, /* filled by callee */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to acquire a lock using [`fcntl`] with respect to the given
|
||||||
|
/// parameters.
|
||||||
|
///
|
||||||
|
/// Please note that `fcntl()` OFD locks are **advisory locks**, which do not
|
||||||
|
/// prevent to `open()` a file if a lock is already placed.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `file`: The file to acquire a lock for [`LockType`]. The file's state will
|
||||||
|
/// be logically mutated, but not technically.
|
||||||
|
/// - `lock_type`: The [`LockType`]
|
||||||
|
pub fn try_acquire_lock<Fd: AsRawFd>(file: Fd, lock_type: LockType) -> Result<(), LockError> {
|
||||||
|
let flock = get_flock(lock_type);
|
||||||
|
|
||||||
|
let res = fcntl(file.as_raw_fd(), FcntlArg::F_OFD_SETLK(&flock));
|
||||||
|
match res {
|
||||||
|
0 => Ok(()),
|
||||||
|
-1 => {
|
||||||
|
let io_error = io::Error::last_os_error();
|
||||||
|
let errno = io_error.raw_os_error().unwrap();
|
||||||
|
match errno {
|
||||||
|
// See man page for error code:
|
||||||
|
// <https://man7.org/linux/man-pages/man2/fcntl.2.html>
|
||||||
|
libc::EAGAIN | libc::EACCES => Err(LockError::AlreadyLocked),
|
||||||
|
_ => Err(LockError::Io(io_error)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val => panic!("Unexpected return value from fcntl(): {val}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears a lock.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `file`: The file to clear all locks for [`LockType`].
|
||||||
|
pub fn clear_lock<Fd: AsRawFd>(file: Fd) -> Result<(), LockError> {
|
||||||
|
try_acquire_lock(file, LockType::Unlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current lock state using [`fcntl`] with respect to the given
|
||||||
|
/// parameters.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `file`: The file for which to get the lock state.
|
||||||
|
pub fn get_lock_state<Fd: AsRawFd>(file: Fd) -> Result<LockState, LockError> {
|
||||||
|
let mut flock = get_flock(LockType::Write);
|
||||||
|
let res = fcntl(file.as_raw_fd(), FcntlArg::F_OFD_GETLK(&mut flock));
|
||||||
|
match res {
|
||||||
|
0 => {
|
||||||
|
let state = flock.l_type as libc::c_int;
|
||||||
|
let state = LockState::new(state);
|
||||||
|
Ok(state)
|
||||||
|
}
|
||||||
|
-1 => {
|
||||||
|
let io_error = io::Error::last_os_error();
|
||||||
|
Err(LockError::Io(io_error))
|
||||||
|
}
|
||||||
|
val => panic!("Unexpected return value from fcntl(): {val}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,12 +2,13 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::vhd::VhdFooter;
|
|
||||||
use crate::BlockBackend;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Seek, SeekFrom, Write};
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
|
|
||||||
|
use crate::vhd::VhdFooter;
|
||||||
|
use crate::BlockBackend;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FixedVhd {
|
pub struct FixedVhd {
|
||||||
file: File,
|
file: File,
|
||||||
|
|||||||
@@ -2,15 +2,17 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use std::fs::File;
|
||||||
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
|
|
||||||
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
use crate::async_io::{
|
use crate::async_io::{
|
||||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
|
||||||
};
|
};
|
||||||
use crate::fixed_vhd::FixedVhd;
|
use crate::fixed_vhd::FixedVhd;
|
||||||
use crate::raw_async::RawFileAsync;
|
use crate::raw_async::RawFileAsync;
|
||||||
use crate::BlockBackend;
|
use crate::BlockBackend;
|
||||||
use std::fs::File;
|
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
|
||||||
|
|
||||||
pub struct FixedVhdDiskAsync(FixedVhd);
|
pub struct FixedVhdDiskAsync(FixedVhd);
|
||||||
|
|
||||||
@@ -31,6 +33,10 @@ impl DiskFile for FixedVhdDiskAsync {
|
|||||||
.map_err(DiskFileError::NewAsyncIo)?,
|
.map_err(DiskFileError::NewAsyncIo)?,
|
||||||
) as Box<dyn AsyncIo>)
|
) as Box<dyn AsyncIo>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fd(&mut self) -> BorrowedDiskFd {
|
||||||
|
BorrowedDiskFd::new(self.0.as_raw_fd())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FixedVhdAsync {
|
pub struct FixedVhdAsync {
|
||||||
|
|||||||
@@ -2,15 +2,17 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use std::fs::File;
|
||||||
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
|
|
||||||
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
use crate::async_io::{
|
use crate::async_io::{
|
||||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
|
||||||
};
|
};
|
||||||
use crate::fixed_vhd::FixedVhd;
|
use crate::fixed_vhd::FixedVhd;
|
||||||
use crate::raw_sync::RawFileSync;
|
use crate::raw_sync::RawFileSync;
|
||||||
use crate::BlockBackend;
|
use crate::BlockBackend;
|
||||||
use std::fs::File;
|
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
|
||||||
|
|
||||||
pub struct FixedVhdDiskSync(FixedVhd);
|
pub struct FixedVhdDiskSync(FixedVhd);
|
||||||
|
|
||||||
@@ -31,6 +33,10 @@ impl DiskFile for FixedVhdDiskSync {
|
|||||||
.map_err(DiskFileError::NewAsyncIo)?,
|
.map_err(DiskFileError::NewAsyncIo)?,
|
||||||
) as Box<dyn AsyncIo>)
|
) as Box<dyn AsyncIo>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fd(&mut self) -> BorrowedDiskFd {
|
||||||
|
BorrowedDiskFd::new(self.0.as_raw_fd())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FixedVhdSync {
|
pub struct FixedVhdSync {
|
||||||
|
|||||||
125
block/src/lib.rs
125
block/src/lib.rs
@@ -12,6 +12,7 @@
|
|||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
pub mod async_io;
|
pub mod async_io;
|
||||||
|
pub mod fcntl;
|
||||||
pub mod fixed_vhd;
|
pub mod fixed_vhd;
|
||||||
#[cfg(feature = "io_uring")]
|
#[cfg(feature = "io_uring")]
|
||||||
/// Enabled with the `"io_uring"` feature
|
/// Enabled with the `"io_uring"` feature
|
||||||
@@ -30,17 +31,7 @@ pub mod vhd;
|
|||||||
pub mod vhdx;
|
pub mod vhdx;
|
||||||
pub mod vhdx_sync;
|
pub mod vhdx_sync;
|
||||||
|
|
||||||
use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult};
|
|
||||||
use crate::fixed_vhd::FixedVhd;
|
|
||||||
use crate::qcow::{QcowFile, RawFile};
|
|
||||||
use crate::vhdx::{Vhdx, VhdxError};
|
|
||||||
#[cfg(feature = "io_uring")]
|
|
||||||
use io_uring::{opcode, IoUring, Probe};
|
|
||||||
use libc::{ioctl, S_IFBLK, S_IFMT};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
use std::alloc::{alloc_zeroed, dealloc, Layout};
|
use std::alloc::{alloc_zeroed, dealloc, Layout};
|
||||||
use std::cmp;
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@@ -48,21 +39,28 @@ use std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
|
|||||||
use std::os::linux::fs::MetadataExt;
|
use std::os::linux::fs::MetadataExt;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::result;
|
use std::sync::{Arc, MutexGuard};
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::MutexGuard;
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
use std::{cmp, result};
|
||||||
|
|
||||||
|
#[cfg(feature = "io_uring")]
|
||||||
|
use io_uring::{opcode, IoUring, Probe};
|
||||||
|
use libc::{ioctl, S_IFBLK, S_IFMT};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use smallvec::SmallVec;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use virtio_bindings::virtio_blk::*;
|
use virtio_bindings::virtio_blk::*;
|
||||||
use virtio_queue::DescriptorChain;
|
use virtio_queue::DescriptorChain;
|
||||||
|
use vm_memory::bitmap::Bitmap;
|
||||||
use vm_memory::{
|
use vm_memory::{
|
||||||
bitmap::Bitmap, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryError,
|
ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryError, GuestMemoryLoadGuard,
|
||||||
GuestMemoryLoadGuard,
|
|
||||||
};
|
};
|
||||||
use vm_virtio::{AccessPlatform, Translatable};
|
use vm_virtio::{AccessPlatform, Translatable};
|
||||||
use vmm_sys_util::aio;
|
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr};
|
use vmm_sys_util::{aio, ioctl_io_nr, ioctl_ioc_nr};
|
||||||
|
|
||||||
|
use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult};
|
||||||
|
use crate::vhdx::VhdxError;
|
||||||
|
|
||||||
const SECTOR_SHIFT: u8 = 9;
|
const SECTOR_SHIFT: u8 = 9;
|
||||||
pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
|
pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
|
||||||
@@ -70,9 +68,9 @@ pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
|
|||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("Guest gave us bad memory addresses")]
|
#[error("Guest gave us bad memory addresses")]
|
||||||
GuestMemory(GuestMemoryError),
|
GuestMemory(#[source] 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),
|
CheckedOffset(GuestAddress, usize /* sector offset */),
|
||||||
#[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,
|
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")]
|
||||||
@@ -82,21 +80,21 @@ pub enum Error {
|
|||||||
#[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,
|
DescriptorLengthTooSmall,
|
||||||
#[error("Failed to detect image type: {0}")]
|
#[error("Failed to detect image type: {0}")]
|
||||||
DetectImageType(std::io::Error),
|
DetectImageType(#[source] std::io::Error),
|
||||||
#[error("Failure in fixed vhd: {0}")]
|
#[error("Failure in fixed vhd: {0}")]
|
||||||
FixedVhdError(std::io::Error),
|
FixedVhdError(#[source] std::io::Error),
|
||||||
#[error("Getting a block's metadata fails for any reason")]
|
#[error("Getting a block's metadata fails for any reason")]
|
||||||
GetFileMetadata,
|
GetFileMetadata,
|
||||||
#[error("The requested operation would cause a seek beyond disk end")]
|
#[error("The requested operation would cause a seek beyond disk end")]
|
||||||
InvalidOffset,
|
InvalidOffset,
|
||||||
#[error("Failure in qcow: {0}")]
|
#[error("Failure in qcow: {0}")]
|
||||||
QcowError(qcow::Error),
|
QcowError(#[source] qcow::Error),
|
||||||
#[error("Failure in raw file: {0}")]
|
#[error("Failure in raw file: {0}")]
|
||||||
RawFileError(std::io::Error),
|
RawFileError(#[source] std::io::Error),
|
||||||
#[error("The requested operation does not support multiple descriptors")]
|
#[error("The requested operation does not support multiple descriptors")]
|
||||||
TooManyDescriptors,
|
TooManyDescriptors,
|
||||||
#[error("Failure in vhdx: {0}")]
|
#[error("Failure in vhdx: {0}")]
|
||||||
VhdxError(VhdxError),
|
VhdxError(#[source] VhdxError),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_device_id(disk_path: &Path) -> result::Result<String, Error> {
|
fn build_device_id(disk_path: &Path) -> result::Result<String, Error> {
|
||||||
@@ -134,33 +132,33 @@ pub fn build_serial(disk_path: &Path) -> Vec<u8> {
|
|||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ExecuteError {
|
pub enum ExecuteError {
|
||||||
#[error("Bad request: {0}")]
|
#[error("Bad request: {0}")]
|
||||||
BadRequest(Error),
|
BadRequest(#[source] Error),
|
||||||
#[error("Failed to flush: {0}")]
|
#[error("Failed to flush: {0}")]
|
||||||
Flush(io::Error),
|
Flush(#[source] io::Error),
|
||||||
#[error("Failed to read: {0}")]
|
#[error("Failed to read: {0}")]
|
||||||
Read(GuestMemoryError),
|
Read(#[source] GuestMemoryError),
|
||||||
#[error("Failed to read_exact: {0}")]
|
#[error("Failed to read_exact: {0}")]
|
||||||
ReadExact(io::Error),
|
ReadExact(#[source] io::Error),
|
||||||
#[error("Failed to seek: {0}")]
|
#[error("Failed to seek: {0}")]
|
||||||
Seek(io::Error),
|
Seek(#[source] io::Error),
|
||||||
#[error("Failed to write: {0}")]
|
#[error("Failed to write: {0}")]
|
||||||
Write(GuestMemoryError),
|
Write(#[source] GuestMemoryError),
|
||||||
#[error("Failed to write_all: {0}")]
|
#[error("Failed to write_all: {0}")]
|
||||||
WriteAll(io::Error),
|
WriteAll(#[source] io::Error),
|
||||||
#[error("Unsupported request: {0}")]
|
#[error("Unsupported request: {0}")]
|
||||||
Unsupported(u32),
|
Unsupported(u32),
|
||||||
#[error("Failed to submit io uring: {0}")]
|
#[error("Failed to submit io uring: {0}")]
|
||||||
SubmitIoUring(io::Error),
|
SubmitIoUring(#[source] io::Error),
|
||||||
#[error("Failed to get guest address: {0}")]
|
#[error("Failed to get guest address: {0}")]
|
||||||
GetHostAddress(GuestMemoryError),
|
GetHostAddress(#[source] GuestMemoryError),
|
||||||
#[error("Failed to async read: {0}")]
|
#[error("Failed to async read: {0}")]
|
||||||
AsyncRead(AsyncIoError),
|
AsyncRead(#[source] AsyncIoError),
|
||||||
#[error("Failed to async write: {0}")]
|
#[error("Failed to async write: {0}")]
|
||||||
AsyncWrite(AsyncIoError),
|
AsyncWrite(#[source] AsyncIoError),
|
||||||
#[error("failed to async flush: {0}")]
|
#[error("failed to async flush: {0}")]
|
||||||
AsyncFlush(AsyncIoError),
|
AsyncFlush(#[source] AsyncIoError),
|
||||||
#[error("Failed allocating a temporary buffer: {0}")]
|
#[error("Failed allocating a temporary buffer: {0}")]
|
||||||
TemporaryBufferAllocation(io::Error),
|
TemporaryBufferAllocation(#[source] io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExecuteError {
|
impl ExecuteError {
|
||||||
@@ -221,6 +219,8 @@ fn sector<B: Bitmap + 'static>(
|
|||||||
mem.read_obj(addr).map_err(Error::GuestMemory)
|
mem.read_obj(addr).map_err(Error::GuestMemory)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_DESCRIPTOR_VEC_SIZE: usize = 32;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AlignedOperation {
|
pub struct AlignedOperation {
|
||||||
origin_ptr: u64,
|
origin_ptr: u64,
|
||||||
@@ -233,10 +233,10 @@ pub struct AlignedOperation {
|
|||||||
pub struct Request {
|
pub struct Request {
|
||||||
pub request_type: RequestType,
|
pub request_type: RequestType,
|
||||||
pub sector: u64,
|
pub sector: u64,
|
||||||
pub data_descriptors: SmallVec<[(GuestAddress, u32); 1]>,
|
pub data_descriptors: SmallVec<[(GuestAddress, u32); DEFAULT_DESCRIPTOR_VEC_SIZE]>,
|
||||||
pub status_addr: GuestAddress,
|
pub status_addr: GuestAddress,
|
||||||
pub writeback: bool,
|
pub writeback: bool,
|
||||||
pub aligned_operations: SmallVec<[AlignedOperation; 1]>,
|
pub aligned_operations: SmallVec<[AlignedOperation; DEFAULT_DESCRIPTOR_VEC_SIZE]>,
|
||||||
pub start: Instant,
|
pub start: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,10 +264,10 @@ impl Request {
|
|||||||
let mut req = Request {
|
let mut req = Request {
|
||||||
request_type: request_type(desc_chain.memory(), hdr_desc_addr)?,
|
request_type: request_type(desc_chain.memory(), hdr_desc_addr)?,
|
||||||
sector: sector(desc_chain.memory(), hdr_desc_addr)?,
|
sector: sector(desc_chain.memory(), hdr_desc_addr)?,
|
||||||
data_descriptors: SmallVec::with_capacity(1),
|
data_descriptors: SmallVec::with_capacity(DEFAULT_DESCRIPTOR_VEC_SIZE),
|
||||||
status_addr: GuestAddress(0),
|
status_addr: GuestAddress(0),
|
||||||
writeback: true,
|
writeback: true,
|
||||||
aligned_operations: SmallVec::with_capacity(1),
|
aligned_operations: SmallVec::with_capacity(DEFAULT_DESCRIPTOR_VEC_SIZE),
|
||||||
start: Instant::now(),
|
start: Instant::now(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -399,7 +399,7 @@ impl Request {
|
|||||||
let request_type = self.request_type;
|
let request_type = self.request_type;
|
||||||
let offset = (sector << SECTOR_SHIFT) as libc::off_t;
|
let offset = (sector << SECTOR_SHIFT) as libc::off_t;
|
||||||
|
|
||||||
let mut iovecs: SmallVec<[libc::iovec; 1]> =
|
let mut iovecs: SmallVec<[libc::iovec; DEFAULT_DESCRIPTOR_VEC_SIZE]> =
|
||||||
SmallVec::with_capacity(self.data_descriptors.len());
|
SmallVec::with_capacity(self.data_descriptors.len());
|
||||||
for (data_addr, data_len) in &self.data_descriptors {
|
for (data_addr, data_len) in &self.data_descriptors {
|
||||||
if *data_len == 0 {
|
if *data_len == 0 {
|
||||||
@@ -654,7 +654,8 @@ where
|
|||||||
completion_list: &mut VecDeque<(u64, i32)>,
|
completion_list: &mut VecDeque<(u64, i32)>,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
// Convert libc::iovec into IoSliceMut
|
// Convert libc::iovec into IoSliceMut
|
||||||
let mut slices: SmallVec<[IoSliceMut; 1]> = SmallVec::with_capacity(iovecs.len());
|
let mut slices: SmallVec<[IoSliceMut; DEFAULT_DESCRIPTOR_VEC_SIZE]> =
|
||||||
|
SmallVec::with_capacity(iovecs.len());
|
||||||
for iovec in iovecs.iter() {
|
for iovec in iovecs.iter() {
|
||||||
// SAFETY: on Linux IoSliceMut wraps around libc::iovec
|
// SAFETY: on Linux IoSliceMut wraps around libc::iovec
|
||||||
slices.push(IoSliceMut::new(unsafe {
|
slices.push(IoSliceMut::new(unsafe {
|
||||||
@@ -669,9 +670,11 @@ where
|
|||||||
file.seek(SeekFrom::Start(offset as u64))
|
file.seek(SeekFrom::Start(offset as u64))
|
||||||
.map_err(AsyncIoError::ReadVectored)?;
|
.map_err(AsyncIoError::ReadVectored)?;
|
||||||
|
|
||||||
// Read vectored
|
let mut r = 0;
|
||||||
file.read_vectored(slices.as_mut_slice())
|
for b in slices.iter_mut() {
|
||||||
.map_err(AsyncIoError::ReadVectored)?
|
r += file.read(b).map_err(AsyncIoError::ReadVectored)?;
|
||||||
|
}
|
||||||
|
r
|
||||||
};
|
};
|
||||||
|
|
||||||
completion_list.push_back((user_data, result as i32));
|
completion_list.push_back((user_data, result as i32));
|
||||||
@@ -689,7 +692,8 @@ where
|
|||||||
completion_list: &mut VecDeque<(u64, i32)>,
|
completion_list: &mut VecDeque<(u64, i32)>,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
// Convert libc::iovec into IoSlice
|
// Convert libc::iovec into IoSlice
|
||||||
let mut slices: SmallVec<[IoSlice; 1]> = SmallVec::with_capacity(iovecs.len());
|
let mut slices: SmallVec<[IoSlice; DEFAULT_DESCRIPTOR_VEC_SIZE]> =
|
||||||
|
SmallVec::with_capacity(iovecs.len());
|
||||||
for iovec in iovecs.iter() {
|
for iovec in iovecs.iter() {
|
||||||
// SAFETY: on Linux IoSlice wraps around libc::iovec
|
// SAFETY: on Linux IoSlice wraps around libc::iovec
|
||||||
slices.push(IoSlice::new(unsafe {
|
slices.push(IoSlice::new(unsafe {
|
||||||
@@ -704,9 +708,11 @@ where
|
|||||||
file.seek(SeekFrom::Start(offset as u64))
|
file.seek(SeekFrom::Start(offset as u64))
|
||||||
.map_err(AsyncIoError::WriteVectored)?;
|
.map_err(AsyncIoError::WriteVectored)?;
|
||||||
|
|
||||||
// Write vectored
|
let mut r = 0;
|
||||||
file.write_vectored(slices.as_slice())
|
for b in slices.iter() {
|
||||||
.map_err(AsyncIoError::WriteVectored)?
|
r += file.write(b).map_err(AsyncIoError::WriteVectored)?;
|
||||||
|
}
|
||||||
|
r
|
||||||
};
|
};
|
||||||
|
|
||||||
completion_list.push_back((user_data, result as i32));
|
completion_list.push_back((user_data, result as i32));
|
||||||
@@ -790,25 +796,6 @@ pub trait BlockBackend: Read + Write + Seek + Send + Debug {
|
|||||||
fn size(&self) -> Result<u64, Error>;
|
fn size(&self) -> Result<u64, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inspect the image file type and create an appropriate disk file to match it.
|
|
||||||
pub fn create_disk_file(mut file: File, direct_io: bool) -> Result<Box<dyn BlockBackend>, Error> {
|
|
||||||
let image_type = detect_image_type(&mut file).map_err(Error::DetectImageType)?;
|
|
||||||
|
|
||||||
Ok(match image_type {
|
|
||||||
ImageType::Qcow2 => {
|
|
||||||
Box::new(QcowFile::from(RawFile::new(file, direct_io)).map_err(Error::QcowError)?)
|
|
||||||
as Box<dyn BlockBackend>
|
|
||||||
}
|
|
||||||
ImageType::FixedVhd => {
|
|
||||||
Box::new(FixedVhd::new(file).map_err(Error::FixedVhdError)?) as Box<dyn BlockBackend>
|
|
||||||
}
|
|
||||||
ImageType::Vhdx => {
|
|
||||||
Box::new(Vhdx::new(file).map_err(Error::VhdxError)?) as Box<dyn BlockBackend>
|
|
||||||
}
|
|
||||||
ImageType::Raw => Box::new(RawFile::new(file, direct_io)) as Box<dyn BlockBackend>,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DiskTopology {
|
pub struct DiskTopology {
|
||||||
pub logical_block_size: u64,
|
pub logical_block_size: u64,
|
||||||
|
|||||||
@@ -9,27 +9,26 @@ mod raw_file;
|
|||||||
mod refcount;
|
mod refcount;
|
||||||
mod vec_cache;
|
mod vec_cache;
|
||||||
|
|
||||||
use crate::qcow::{
|
|
||||||
qcow_raw_file::QcowRawFile,
|
|
||||||
refcount::RefCount,
|
|
||||||
vec_cache::{CacheMap, Cacheable, VecCache},
|
|
||||||
};
|
|
||||||
use crate::BlockBackend;
|
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
|
||||||
use libc::{EINVAL, ENOSPC, ENOTSUP};
|
|
||||||
use remain::sorted;
|
|
||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::{self, Read, Seek, SeekFrom, Write};
|
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
use std::os::fd::{AsRawFd, RawFd};
|
||||||
use std::str;
|
use std::str;
|
||||||
use thiserror::Error;
|
|
||||||
use vmm_sys_util::{
|
|
||||||
file_traits::FileSetLen, file_traits::FileSync, seek_hole::SeekHole, write_zeroes::PunchHole,
|
|
||||||
write_zeroes::WriteZeroesAt,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
use libc::{EINVAL, ENOSPC, ENOTSUP};
|
||||||
|
use remain::sorted;
|
||||||
|
use thiserror::Error;
|
||||||
|
use vmm_sys_util::file_traits::{FileSetLen, FileSync};
|
||||||
|
use vmm_sys_util::seek_hole::SeekHole;
|
||||||
|
use vmm_sys_util::write_zeroes::{PunchHole, WriteZeroesAt};
|
||||||
|
|
||||||
|
use crate::qcow::qcow_raw_file::QcowRawFile;
|
||||||
pub use crate::qcow::raw_file::RawFile;
|
pub use crate::qcow::raw_file::RawFile;
|
||||||
|
use crate::qcow::refcount::RefCount;
|
||||||
|
use crate::qcow::vec_cache::{CacheMap, Cacheable, VecCache};
|
||||||
|
use crate::BlockBackend;
|
||||||
|
|
||||||
/// Nesting depth limit for disk formats that can open other disk files.
|
/// Nesting depth limit for disk formats that can open other disk files.
|
||||||
const MAX_NESTING_DEPTH: u32 = 10;
|
const MAX_NESTING_DEPTH: u32 = 10;
|
||||||
@@ -38,23 +37,23 @@ const MAX_NESTING_DEPTH: u32 = 10;
|
|||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("Backing file io error: {0}")]
|
#[error("Backing file io error: {0}")]
|
||||||
BackingFileIo(io::Error),
|
BackingFileIo(#[source] io::Error),
|
||||||
#[error("Backing file open error: {0}")]
|
#[error("Backing file open error: {0}")]
|
||||||
BackingFileOpen(Box<Error>),
|
BackingFileOpen(#[source] Box<Error>),
|
||||||
#[error("Backing file name is too long: {0} bytes over")]
|
#[error("Backing file name is too long: {0} bytes over")]
|
||||||
BackingFileTooLong(usize),
|
BackingFileTooLong(usize),
|
||||||
#[error("Compressed blocks not supported")]
|
#[error("Compressed blocks not supported")]
|
||||||
CompressedBlocksNotSupported,
|
CompressedBlocksNotSupported,
|
||||||
#[error("Failed to evict cache: {0}")]
|
#[error("Failed to evict cache: {0}")]
|
||||||
EvictingCache(io::Error),
|
EvictingCache(#[source] io::Error),
|
||||||
#[error("File larger than max of {}: {0}", MAX_QCOW_FILE_SIZE)]
|
#[error("File larger than max of {MAX_QCOW_FILE_SIZE}: {0}")]
|
||||||
FileTooBig(u64),
|
FileTooBig(u64),
|
||||||
#[error("Failed to get file size: {0}")]
|
#[error("Failed to get file size: {0}")]
|
||||||
GettingFileSize(io::Error),
|
GettingFileSize(#[source] io::Error),
|
||||||
#[error("Failed to get refcount: {0}")]
|
#[error("Failed to get refcount: {0}")]
|
||||||
GettingRefcount(refcount::Error),
|
GettingRefcount(#[source] refcount::Error),
|
||||||
#[error("Failed to parse filename: {0}")]
|
#[error("Failed to parse filename: {0}")]
|
||||||
InvalidBackingFileName(str::Utf8Error),
|
InvalidBackingFileName(#[source] str::Utf8Error),
|
||||||
#[error("Invalid cluster index")]
|
#[error("Invalid cluster index")]
|
||||||
InvalidClusterIndex,
|
InvalidClusterIndex,
|
||||||
#[error("Invalid cluster size")]
|
#[error("Invalid cluster size")]
|
||||||
@@ -82,29 +81,29 @@ pub enum Error {
|
|||||||
#[error("Not enough space for refcounts")]
|
#[error("Not enough space for refcounts")]
|
||||||
NotEnoughSpaceForRefcounts,
|
NotEnoughSpaceForRefcounts,
|
||||||
#[error("Failed to open file {0}")]
|
#[error("Failed to open file {0}")]
|
||||||
OpeningFile(io::Error),
|
OpeningFile(#[source] io::Error),
|
||||||
#[error("Failed to read data: {0}")]
|
#[error("Failed to read data: {0}")]
|
||||||
ReadingData(io::Error),
|
ReadingData(#[source] io::Error),
|
||||||
#[error("Failed to read header: {0}")]
|
#[error("Failed to read header: {0}")]
|
||||||
ReadingHeader(io::Error),
|
ReadingHeader(#[source] io::Error),
|
||||||
#[error("Failed to read pointers: {0}")]
|
#[error("Failed to read pointers: {0}")]
|
||||||
ReadingPointers(io::Error),
|
ReadingPointers(#[source] io::Error),
|
||||||
#[error("Failed to read ref count block: {0}")]
|
#[error("Failed to read ref count block: {0}")]
|
||||||
ReadingRefCountBlock(refcount::Error),
|
ReadingRefCountBlock(#[source] refcount::Error),
|
||||||
#[error("Failed to read ref counts: {0}")]
|
#[error("Failed to read ref counts: {0}")]
|
||||||
ReadingRefCounts(io::Error),
|
ReadingRefCounts(#[source] io::Error),
|
||||||
#[error("Failed to rebuild ref counts: {0}")]
|
#[error("Failed to rebuild ref counts: {0}")]
|
||||||
RebuildingRefCounts(io::Error),
|
RebuildingRefCounts(#[source] io::Error),
|
||||||
#[error("Refcount table offset past file end")]
|
#[error("Refcount table offset past file end")]
|
||||||
RefcountTableOffEnd,
|
RefcountTableOffEnd,
|
||||||
#[error("Too many clusters specified for refcount")]
|
#[error("Too many clusters specified for refcount")]
|
||||||
RefcountTableTooLarge,
|
RefcountTableTooLarge,
|
||||||
#[error("Failed to seek file: {0}")]
|
#[error("Failed to seek file: {0}")]
|
||||||
SeekingFile(io::Error),
|
SeekingFile(#[source] io::Error),
|
||||||
#[error("Failed to set file size: {0}")]
|
#[error("Failed to set file size: {0}")]
|
||||||
SettingFileSize(io::Error),
|
SettingFileSize(#[source] io::Error),
|
||||||
#[error("Failed to set refcount refcount: {0}")]
|
#[error("Failed to set refcount refcount: {0}")]
|
||||||
SettingRefcountRefcount(io::Error),
|
SettingRefcountRefcount(#[source] io::Error),
|
||||||
#[error("Size too small for number of clusters")]
|
#[error("Size too small for number of clusters")]
|
||||||
SizeTooSmallForNumberOfClusters,
|
SizeTooSmallForNumberOfClusters,
|
||||||
#[error("L1 entry table too large: {0}")]
|
#[error("L1 entry table too large: {0}")]
|
||||||
@@ -116,9 +115,9 @@ pub enum Error {
|
|||||||
#[error("Unsupported version: {0}")]
|
#[error("Unsupported version: {0}")]
|
||||||
UnsupportedVersion(u32),
|
UnsupportedVersion(u32),
|
||||||
#[error("Failed to write data: {0}")]
|
#[error("Failed to write data: {0}")]
|
||||||
WritingData(io::Error),
|
WritingData(#[source] io::Error),
|
||||||
#[error("Failed to write header: {0}")]
|
#[error("Failed to write header: {0}")]
|
||||||
WritingHeader(io::Error),
|
WritingHeader(#[source] io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
@@ -380,7 +379,7 @@ impl QcowHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(backing_file_path) = self.backing_file_path.as_ref() {
|
if let Some(backing_file_path) = self.backing_file_path.as_ref() {
|
||||||
write!(file, "{}", backing_file_path).map_err(Error::WritingHeader)?;
|
write!(file, "{backing_file_path}").map_err(Error::WritingHeader)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the file length by seeking and writing a zero to the last byte. This avoids needing
|
// Set the file length by seeking and writing a zero to the last byte. This avoids needing
|
||||||
@@ -504,7 +503,7 @@ impl QcowFile {
|
|||||||
if refcount_bits != 16 {
|
if refcount_bits != 16 {
|
||||||
return Err(Error::UnsupportedRefcountOrder);
|
return Err(Error::UnsupportedRefcountOrder);
|
||||||
}
|
}
|
||||||
let refcount_bytes = (refcount_bits + 7) / 8;
|
let refcount_bytes = refcount_bits.div_ceil(8);
|
||||||
|
|
||||||
// Need at least one refcount cluster
|
// Need at least one refcount cluster
|
||||||
if header.refcount_table_clusters == 0 {
|
if header.refcount_table_clusters == 0 {
|
||||||
@@ -1518,6 +1517,12 @@ impl QcowFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for QcowFile {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.raw_file.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Drop for QcowFile {
|
impl Drop for QcowFile {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let _ = self.sync_caches();
|
let _ = self.sync_caches();
|
||||||
@@ -1627,8 +1632,7 @@ impl FileSync for QcowFile {
|
|||||||
|
|
||||||
impl FileSetLen for QcowFile {
|
impl FileSetLen for QcowFile {
|
||||||
fn set_len(&self, _len: u64) -> std::io::Result<()> {
|
fn set_len(&self, _len: u64) -> std::io::Result<()> {
|
||||||
Err(std::io::Error::new(
|
Err(std::io::Error::other(
|
||||||
std::io::ErrorKind::Other,
|
|
||||||
"set_len() not supported for QcowFile",
|
"set_len() not supported for QcowFile",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -1837,13 +1841,15 @@ pub fn detect_image_type(file: &mut RawFile) -> Result<ImageType> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use vmm_sys_util::tempdir::TempDir;
|
use vmm_sys_util::tempdir::TempDir;
|
||||||
use vmm_sys_util::tempfile::TempFile;
|
use vmm_sys_util::tempfile::TempFile;
|
||||||
use vmm_sys_util::write_zeroes::WriteZeroes;
|
use vmm_sys_util::write_zeroes::WriteZeroes;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
fn valid_header_v3() -> Vec<u8> {
|
fn valid_header_v3() -> Vec<u8> {
|
||||||
vec![
|
vec![
|
||||||
0x51u8, 0x46, 0x49, 0xfb, // magic
|
0x51u8, 0x46, 0x49, 0xfb, // magic
|
||||||
@@ -2051,8 +2057,7 @@ mod tests {
|
|||||||
.expect("Failed to write header to shm.");
|
.expect("Failed to write header to shm.");
|
||||||
disk_file.rewind().unwrap();
|
disk_file.rewind().unwrap();
|
||||||
// The maximum nesting depth is 0, which means backing file is not allowed.
|
// The maximum nesting depth is 0, which means backing file is not allowed.
|
||||||
let res = QcowFile::from_with_nesting_depth(disk_file, 0);
|
QcowFile::from_with_nesting_depth(disk_file, 0).unwrap();
|
||||||
assert!(res.is_ok());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -2068,7 +2073,6 @@ mod tests {
|
|||||||
disk_file.rewind().unwrap();
|
disk_file.rewind().unwrap();
|
||||||
// The maximum nesting depth is 0, which means backing file is not allowed.
|
// The maximum nesting depth is 0, which means backing file is not allowed.
|
||||||
let res = QcowFile::from_with_nesting_depth(disk_file, 0);
|
let res = QcowFile::from_with_nesting_depth(disk_file, 0);
|
||||||
assert!(res.is_err());
|
|
||||||
assert!(matches!(res.unwrap_err(), Error::MaxNestingDepthExceeded));
|
assert!(matches!(res.unwrap_err(), Error::MaxNestingDepthExceeded));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,15 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
use super::RawFile;
|
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
|
||||||
use std::io::{self, BufWriter, Seek, SeekFrom, Write};
|
use std::io::{self, BufWriter, Seek, SeekFrom, Write};
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
use std::os::fd::{AsRawFd, RawFd};
|
||||||
|
|
||||||
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use vmm_sys_util::write_zeroes::WriteZeroes;
|
use vmm_sys_util::write_zeroes::WriteZeroes;
|
||||||
|
|
||||||
|
use super::RawFile;
|
||||||
|
|
||||||
/// A qcow file. Allows reading/writing clusters and appending clusters.
|
/// A qcow file. Allows reading/writing clusters and appending clusters.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct QcowRawFile {
|
pub struct QcowRawFile {
|
||||||
@@ -22,7 +25,7 @@ impl QcowRawFile {
|
|||||||
/// Creates a `QcowRawFile` from the given `File`, `None` is returned if `cluster_size` is not
|
/// Creates a `QcowRawFile` from the given `File`, `None` is returned if `cluster_size` is not
|
||||||
/// a power of two.
|
/// a power of two.
|
||||||
pub fn from(file: RawFile, cluster_size: u64) -> Option<Self> {
|
pub fn from(file: RawFile, cluster_size: u64) -> Option<Self> {
|
||||||
if cluster_size.count_ones() != 1 {
|
if !cluster_size.is_power_of_two() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(QcowRawFile {
|
Some(QcowRawFile {
|
||||||
@@ -157,3 +160,9 @@ impl Clone for QcowRawFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for QcowRawFile {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.file.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,14 +8,17 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
use crate::BlockBackend;
|
|
||||||
use libc::c_void;
|
|
||||||
use std::alloc::{alloc_zeroed, dealloc, Layout};
|
use std::alloc::{alloc_zeroed, dealloc, Layout};
|
||||||
use std::fs::{File, Metadata};
|
use std::fs::{File, Metadata};
|
||||||
use std::io::{self, Read, Seek, SeekFrom, Write};
|
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use vmm_sys_util::{seek_hole::SeekHole, write_zeroes::PunchHole, write_zeroes::WriteZeroesAt};
|
|
||||||
|
use libc::c_void;
|
||||||
|
use vmm_sys_util::seek_hole::SeekHole;
|
||||||
|
use vmm_sys_util::write_zeroes::{PunchHole, WriteZeroesAt};
|
||||||
|
|
||||||
|
use crate::BlockBackend;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RawFile {
|
pub struct RawFile {
|
||||||
@@ -71,7 +74,7 @@ impl RawFile {
|
|||||||
|
|
||||||
fn round_up(&self, offset: u64) -> u64 {
|
fn round_up(&self, offset: u64) -> u64 {
|
||||||
let align: u64 = self.alignment.try_into().unwrap();
|
let align: u64 = self.alignment.try_into().unwrap();
|
||||||
((offset + align - 1) / align) * align
|
offset.div_ceil(align) * align
|
||||||
}
|
}
|
||||||
|
|
||||||
fn round_down(&self, offset: u64) -> u64 {
|
fn round_down(&self, offset: u64) -> u64 {
|
||||||
@@ -366,3 +369,9 @@ impl Clone for RawFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for RawFile {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.file.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,16 +9,14 @@ use std::io;
|
|||||||
use libc::EINVAL;
|
use libc::EINVAL;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::qcow::{
|
use crate::qcow::qcow_raw_file::QcowRawFile;
|
||||||
qcow_raw_file::QcowRawFile,
|
use crate::qcow::vec_cache::{CacheMap, Cacheable, VecCache};
|
||||||
vec_cache::{CacheMap, Cacheable, VecCache},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// `EvictingCache` - Error writing a refblock from the cache to disk.
|
/// `EvictingCache` - Error writing a refblock from the cache to disk.
|
||||||
#[error("Failed to write a refblock from the cache to disk: {0}")]
|
#[error("Failed to write a refblock from the cache to disk: {0}")]
|
||||||
EvictingRefCounts(io::Error),
|
EvictingRefCounts(#[source] io::Error),
|
||||||
/// `InvalidIndex` - Address requested isn't within the range of the disk.
|
/// `InvalidIndex` - Address requested isn't within the range of the disk.
|
||||||
#[error("Address requested is not within the range of the disk")]
|
#[error("Address requested is not within the range of the disk")]
|
||||||
InvalidIndex,
|
InvalidIndex,
|
||||||
@@ -30,7 +28,7 @@ pub enum Error {
|
|||||||
NeedNewCluster,
|
NeedNewCluster,
|
||||||
/// `ReadingRefCounts` - Error reading the file into the refcount cache.
|
/// `ReadingRefCounts` - Error reading the file into the refcount cache.
|
||||||
#[error("Failed to read the file into the refcount cache: {0}")]
|
#[error("Failed to read the file into the refcount cache: {0}")]
|
||||||
ReadingRefCounts(io::Error),
|
ReadingRefCounts(#[source] io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|||||||
@@ -2,15 +2,20 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult};
|
|
||||||
use crate::qcow::{QcowFile, RawFile, Result as QcowResult};
|
|
||||||
use crate::AsyncAdaptor;
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Seek, SeekFrom};
|
use std::io::{Seek, SeekFrom};
|
||||||
|
use std::os::fd::AsRawFd;
|
||||||
use std::sync::{Arc, Mutex, MutexGuard};
|
use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
|
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
use crate::async_io::{
|
||||||
|
AsyncIo, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
|
||||||
|
};
|
||||||
|
use crate::qcow::{QcowFile, RawFile, Result as QcowResult};
|
||||||
|
use crate::AsyncAdaptor;
|
||||||
|
|
||||||
pub struct QcowDiskSync {
|
pub struct QcowDiskSync {
|
||||||
qcow_file: Arc<Mutex<QcowFile>>,
|
qcow_file: Arc<Mutex<QcowFile>>,
|
||||||
}
|
}
|
||||||
@@ -33,6 +38,11 @@ impl DiskFile for QcowDiskSync {
|
|||||||
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||||
Ok(Box::new(QcowSync::new(self.qcow_file.clone())) as Box<dyn AsyncIo>)
|
Ok(Box::new(QcowSync::new(self.qcow_file.clone())) as Box<dyn AsyncIo>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fd(&mut self) -> BorrowedDiskFd {
|
||||||
|
let lock = self.qcow_file.lock().unwrap();
|
||||||
|
BorrowedDiskFd::new(lock.as_raw_fd())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct QcowSync {
|
pub struct QcowSync {
|
||||||
|
|||||||
@@ -2,15 +2,17 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Error, Seek, SeekFrom};
|
||||||
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
|
|
||||||
|
use io_uring::{opcode, types, IoUring};
|
||||||
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
use crate::async_io::{
|
use crate::async_io::{
|
||||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
|
||||||
};
|
};
|
||||||
use crate::DiskTopology;
|
use crate::DiskTopology;
|
||||||
use io_uring::{opcode, squeue, types, IoUring};
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{Seek, SeekFrom};
|
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
|
||||||
|
|
||||||
pub struct RawFileDisk {
|
pub struct RawFileDisk {
|
||||||
file: File,
|
file: File,
|
||||||
@@ -44,6 +46,10 @@ impl DiskFile for RawFileDisk {
|
|||||||
DiskTopology::default()
|
DiskTopology::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fd(&mut self) -> BorrowedDiskFd {
|
||||||
|
BorrowedDiskFd::new(self.file.as_raw_fd())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RawFileAsync {
|
pub struct RawFileAsync {
|
||||||
@@ -84,14 +90,14 @@ impl AsyncIo for RawFileAsync {
|
|||||||
|
|
||||||
// SAFETY: we know the file descriptor is valid and we
|
// SAFETY: we know the file descriptor is valid and we
|
||||||
// relied on vm-memory to provide the buffer address.
|
// relied on vm-memory to provide the buffer address.
|
||||||
let _ = unsafe {
|
unsafe {
|
||||||
sq.push(
|
sq.push(
|
||||||
&opcode::Readv::new(types::Fd(self.fd), iovecs.as_ptr(), iovecs.len() as u32)
|
&opcode::Readv::new(types::Fd(self.fd), iovecs.as_ptr(), iovecs.len() as u32)
|
||||||
.offset(offset.try_into().unwrap())
|
.offset(offset.try_into().unwrap())
|
||||||
.build()
|
.build()
|
||||||
.flags(squeue::Flags::ASYNC)
|
|
||||||
.user_data(user_data),
|
.user_data(user_data),
|
||||||
)
|
)
|
||||||
|
.map_err(|_| AsyncIoError::ReadVectored(Error::other("Submission queue is full")))?
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update the submission queue and submit new operations to the
|
// Update the submission queue and submit new operations to the
|
||||||
@@ -112,14 +118,14 @@ impl AsyncIo for RawFileAsync {
|
|||||||
|
|
||||||
// SAFETY: we know the file descriptor is valid and we
|
// SAFETY: we know the file descriptor is valid and we
|
||||||
// relied on vm-memory to provide the buffer address.
|
// relied on vm-memory to provide the buffer address.
|
||||||
let _ = unsafe {
|
unsafe {
|
||||||
sq.push(
|
sq.push(
|
||||||
&opcode::Writev::new(types::Fd(self.fd), iovecs.as_ptr(), iovecs.len() as u32)
|
&opcode::Writev::new(types::Fd(self.fd), iovecs.as_ptr(), iovecs.len() as u32)
|
||||||
.offset(offset.try_into().unwrap())
|
.offset(offset.try_into().unwrap())
|
||||||
.build()
|
.build()
|
||||||
.flags(squeue::Flags::ASYNC)
|
|
||||||
.user_data(user_data),
|
.user_data(user_data),
|
||||||
)
|
)
|
||||||
|
.map_err(|_| AsyncIoError::WriteVectored(Error::other("Submission queue is full")))?
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update the submission queue and submit new operations to the
|
// Update the submission queue and submit new operations to the
|
||||||
@@ -135,13 +141,13 @@ impl AsyncIo for RawFileAsync {
|
|||||||
let (submitter, mut sq, _) = self.io_uring.split();
|
let (submitter, mut sq, _) = self.io_uring.split();
|
||||||
|
|
||||||
// SAFETY: we know the file descriptor is valid.
|
// SAFETY: we know the file descriptor is valid.
|
||||||
let _ = unsafe {
|
unsafe {
|
||||||
sq.push(
|
sq.push(
|
||||||
&opcode::Fsync::new(types::Fd(self.fd))
|
&opcode::Fsync::new(types::Fd(self.fd))
|
||||||
.build()
|
.build()
|
||||||
.flags(squeue::Flags::ASYNC)
|
|
||||||
.user_data(user_data),
|
.user_data(user_data),
|
||||||
)
|
)
|
||||||
|
.map_err(|_| AsyncIoError::Fsync(Error::other("Submission queue is full")))?
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update the submission queue and submit new operations to the
|
// Update the submission queue and submit new operations to the
|
||||||
|
|||||||
@@ -5,16 +5,18 @@
|
|||||||
// Copyright © 2023 Crusoe Energy Systems LLC
|
// Copyright © 2023 Crusoe Energy Systems LLC
|
||||||
//
|
//
|
||||||
|
|
||||||
use crate::async_io::{
|
|
||||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
|
||||||
};
|
|
||||||
use crate::DiskTopology;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Seek, SeekFrom};
|
use std::io::{Seek, SeekFrom};
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
|
|
||||||
use vmm_sys_util::aio;
|
use vmm_sys_util::aio;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
use crate::async_io::{
|
||||||
|
AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
|
||||||
|
};
|
||||||
|
use crate::DiskTopology;
|
||||||
|
|
||||||
pub struct RawFileDiskAio {
|
pub struct RawFileDiskAio {
|
||||||
file: File,
|
file: File,
|
||||||
}
|
}
|
||||||
@@ -47,6 +49,10 @@ impl DiskFile for RawFileDiskAio {
|
|||||||
DiskTopology::default()
|
DiskTopology::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fd(&mut self) -> BorrowedDiskFd {
|
||||||
|
BorrowedDiskFd::new(self.file.as_raw_fd())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RawFileAsyncAio {
|
pub struct RawFileAsyncAio {
|
||||||
|
|||||||
@@ -2,16 +2,18 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
use crate::async_io::{
|
|
||||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
|
||||||
};
|
|
||||||
use crate::DiskTopology;
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Seek, SeekFrom};
|
use std::io::{Seek, SeekFrom};
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
|
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
use crate::async_io::{
|
||||||
|
AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
|
||||||
|
};
|
||||||
|
use crate::DiskTopology;
|
||||||
|
|
||||||
pub struct RawFileDiskSync {
|
pub struct RawFileDiskSync {
|
||||||
file: File,
|
file: File,
|
||||||
}
|
}
|
||||||
@@ -41,6 +43,10 @@ impl DiskFile for RawFileDiskSync {
|
|||||||
DiskTopology::default()
|
DiskTopology::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fd(&mut self) -> BorrowedDiskFd {
|
||||||
|
BorrowedDiskFd::new(self.file.as_raw_fd())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RawFileSync {
|
pub struct RawFileSync {
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::{read_aligned_block_size, DiskTopology};
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Seek, SeekFrom};
|
use std::io::{Seek, SeekFrom};
|
||||||
|
|
||||||
|
use crate::{read_aligned_block_size, DiskTopology};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct VhdFooter {
|
pub struct VhdFooter {
|
||||||
cookie: u64,
|
cookie: u64,
|
||||||
@@ -117,11 +118,13 @@ pub fn is_fixed_vhd(f: &mut File) -> std::io::Result<bool> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{is_fixed_vhd, VhdFooter};
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Seek, SeekFrom, Write};
|
use std::io::{Seek, SeekFrom, Write};
|
||||||
|
|
||||||
use vmm_sys_util::tempfile::TempFile;
|
use vmm_sys_util::tempfile::TempFile;
|
||||||
|
|
||||||
|
use super::{is_fixed_vhd, VhdFooter};
|
||||||
|
|
||||||
fn valid_fixed_vhd_footer() -> Vec<u8> {
|
fn valid_fixed_vhd_footer() -> Vec<u8> {
|
||||||
vec![
|
vec![
|
||||||
0x63, 0x6f, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x78, // cookie
|
0x63, 0x6f, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x78, // cookie
|
||||||
|
|||||||
@@ -2,26 +2,21 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::vhdx::{
|
|
||||||
vhdx_bat::{BatEntry, VhdxBatError},
|
|
||||||
vhdx_header::{RegionInfo, RegionTableEntry, VhdxHeader, VhdxHeaderError},
|
|
||||||
vhdx_io::VhdxIoError,
|
|
||||||
vhdx_metadata::{DiskSpec, VhdxMetadataError},
|
|
||||||
};
|
|
||||||
use crate::BlockBackend;
|
|
||||||
use byteorder::{BigEndian, ByteOrder};
|
|
||||||
use remain::sorted;
|
|
||||||
use std::collections::btree_map::BTreeMap;
|
use std::collections::btree_map::BTreeMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Seek, SeekFrom, Write};
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
|
use std::os::fd::{AsRawFd, RawFd};
|
||||||
|
|
||||||
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
|
use remain::sorted;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
macro_rules! div_round_up {
|
use crate::vhdx::vhdx_bat::{BatEntry, VhdxBatError};
|
||||||
($n:expr,$d:expr) => {
|
use crate::vhdx::vhdx_header::{RegionInfo, RegionTableEntry, VhdxHeader, VhdxHeaderError};
|
||||||
($n + $d - 1) / $d
|
use crate::vhdx::vhdx_io::VhdxIoError;
|
||||||
};
|
use crate::vhdx::vhdx_metadata::{DiskSpec, VhdxMetadataError};
|
||||||
}
|
use crate::BlockBackend;
|
||||||
|
|
||||||
mod vhdx_bat;
|
mod vhdx_bat;
|
||||||
mod vhdx_header;
|
mod vhdx_header;
|
||||||
@@ -31,19 +26,19 @@ mod vhdx_metadata;
|
|||||||
#[sorted]
|
#[sorted]
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum VhdxError {
|
pub enum VhdxError {
|
||||||
#[error("Not a VHDx file {0}")]
|
#[error("Not a VHDx file: {0}")]
|
||||||
NotVhdx(#[source] VhdxHeaderError),
|
NotVhdx(#[source] VhdxHeaderError),
|
||||||
#[error("Failed to parse VHDx header {0}")]
|
#[error("Failed to parse VHDx header: {0}")]
|
||||||
ParseVhdxHeader(#[source] VhdxHeaderError),
|
ParseVhdxHeader(#[source] VhdxHeaderError),
|
||||||
#[error("Failed to parse VHDx metadata {0}")]
|
#[error("Failed to parse VHDx metadata: {0}")]
|
||||||
ParseVhdxMetadata(#[source] VhdxMetadataError),
|
ParseVhdxMetadata(#[source] VhdxMetadataError),
|
||||||
#[error("Failed to parse VHDx region entries {0}")]
|
#[error("Failed to parse VHDx region entries: {0}")]
|
||||||
ParseVhdxRegionEntry(#[source] VhdxHeaderError),
|
ParseVhdxRegionEntry(#[source] VhdxHeaderError),
|
||||||
#[error("Failed reading metadata {0}")]
|
#[error("Failed reading metadata: {0}")]
|
||||||
ReadBatEntry(#[source] VhdxBatError),
|
ReadBatEntry(#[source] VhdxBatError),
|
||||||
#[error("Failed reading sector from disk {0}")]
|
#[error("Failed reading sector from disk: {0}")]
|
||||||
ReadFailed(#[source] VhdxIoError),
|
ReadFailed(#[source] VhdxIoError),
|
||||||
#[error("Failed writing to sector on disk {0}")]
|
#[error("Failed writing to sector on disk: {0}")]
|
||||||
WriteFailed(#[source] VhdxIoError),
|
WriteFailed(#[source] VhdxIoError),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,11 +100,10 @@ impl Read for Vhdx {
|
|||||||
/// Wrapper function to satisfy Read trait implementation for VHDx disk.
|
/// Wrapper function to satisfy Read trait implementation for VHDx disk.
|
||||||
/// Convert the offset to sector index and buffer length to sector count.
|
/// Convert the offset to sector index and buffer length to sector count.
|
||||||
fn read(&mut self, buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {
|
fn read(&mut self, buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {
|
||||||
let sector_count =
|
let sector_count = (buf.len() as u64).div_ceil(self.disk_spec.logical_sector_size as u64);
|
||||||
div_round_up!(buf.len() as u64, self.disk_spec.logical_sector_size as u64);
|
|
||||||
let sector_index = self.current_offset / self.disk_spec.logical_sector_size as u64;
|
let sector_index = self.current_offset / self.disk_spec.logical_sector_size as u64;
|
||||||
|
|
||||||
vhdx_io::read(
|
let result = vhdx_io::read(
|
||||||
&mut self.file,
|
&mut self.file,
|
||||||
buf,
|
buf,
|
||||||
&self.disk_spec,
|
&self.disk_spec,
|
||||||
@@ -118,13 +112,14 @@ impl Read for Vhdx {
|
|||||||
sector_count,
|
sector_count,
|
||||||
)
|
)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
std::io::Error::new(
|
std::io::Error::other(format!(
|
||||||
std::io::ErrorKind::Other,
|
"Failed reading {sector_count} sectors from VHDx at index {sector_index}: {e}"
|
||||||
format!(
|
))
|
||||||
"Failed reading {sector_count} sectors from VHDx at index {sector_index}: {e}"
|
})?;
|
||||||
),
|
|
||||||
)
|
self.current_offset = self.current_offset.checked_add(result as u64).unwrap();
|
||||||
})
|
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,21 +131,17 @@ impl Write for Vhdx {
|
|||||||
/// Wrapper function to satisfy Write trait implementation for VHDx disk.
|
/// Wrapper function to satisfy Write trait implementation for VHDx disk.
|
||||||
/// Convert the offset to sector index and buffer length to sector count.
|
/// Convert the offset to sector index and buffer length to sector count.
|
||||||
fn write(&mut self, buf: &[u8]) -> std::result::Result<usize, std::io::Error> {
|
fn write(&mut self, buf: &[u8]) -> std::result::Result<usize, std::io::Error> {
|
||||||
let sector_count =
|
let sector_count = (buf.len() as u64).div_ceil(self.disk_spec.logical_sector_size as u64);
|
||||||
div_round_up!(buf.len() as u64, self.disk_spec.logical_sector_size as u64);
|
|
||||||
let sector_index = self.current_offset / self.disk_spec.logical_sector_size as u64;
|
let sector_index = self.current_offset / self.disk_spec.logical_sector_size as u64;
|
||||||
|
|
||||||
if self.first_write {
|
if self.first_write {
|
||||||
self.first_write = false;
|
self.first_write = false;
|
||||||
self.vhdx_header.update(&mut self.file).map_err(|e| {
|
self.vhdx_header
|
||||||
std::io::Error::new(
|
.update(&mut self.file)
|
||||||
std::io::ErrorKind::Other,
|
.map_err(|e| std::io::Error::other(format!("Failed to update VHDx header: {e}")))?;
|
||||||
format!("Failed to update VHDx header: {e}"),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vhdx_io::write(
|
let result = vhdx_io::write(
|
||||||
&mut self.file,
|
&mut self.file,
|
||||||
buf,
|
buf,
|
||||||
&mut self.disk_spec,
|
&mut self.disk_spec,
|
||||||
@@ -160,13 +151,14 @@ impl Write for Vhdx {
|
|||||||
sector_count,
|
sector_count,
|
||||||
)
|
)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
std::io::Error::new(
|
std::io::Error::other(format!(
|
||||||
std::io::ErrorKind::Other,
|
"Failed writing {sector_count} sectors on VHDx at index {sector_index}: {e}"
|
||||||
format!(
|
))
|
||||||
"Failed writing {sector_count} sectors on VHDx at index {sector_index}: {e}"
|
})?;
|
||||||
),
|
|
||||||
)
|
self.current_offset = self.current_offset.checked_add(result as u64).unwrap();
|
||||||
})
|
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,6 +223,12 @@ impl Clone for Vhdx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for Vhdx {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.file.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn uuid_from_guid(buf: &[u8]) -> Uuid {
|
pub(crate) fn uuid_from_guid(buf: &[u8]) -> Uuid {
|
||||||
// The first 3 fields of UUID are stored in Big Endian format, and
|
// The first 3 fields of UUID are stored in Big Endian format, and
|
||||||
// the last 8 bytes are stored as byte array. Therefore, we read the
|
// the last 8 bytes are stored as byte array. Therefore, we read the
|
||||||
|
|||||||
@@ -2,14 +2,17 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::vhdx::{vhdx_header::RegionTableEntry, vhdx_metadata::DiskSpec};
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
|
||||||
use remain::sorted;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, Seek, SeekFrom};
|
use std::io::{self, Seek, SeekFrom};
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
use remain::sorted;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::vhdx::vhdx_header::RegionTableEntry;
|
||||||
|
use crate::vhdx::vhdx_metadata::DiskSpec;
|
||||||
|
|
||||||
// Payload BAT Entry States
|
// Payload BAT Entry States
|
||||||
pub const PAYLOAD_BLOCK_NOT_PRESENT: u64 = 0;
|
pub const PAYLOAD_BLOCK_NOT_PRESENT: u64 = 0;
|
||||||
pub const PAYLOAD_BLOCK_UNDEFINED: u64 = 1;
|
pub const PAYLOAD_BLOCK_UNDEFINED: u64 = 1;
|
||||||
@@ -30,9 +33,9 @@ pub enum VhdxBatError {
|
|||||||
InvalidBatEntry,
|
InvalidBatEntry,
|
||||||
#[error("Invalid BAT entry count")]
|
#[error("Invalid BAT entry count")]
|
||||||
InvalidEntryCount,
|
InvalidEntryCount,
|
||||||
#[error("Failed to read BAT entry {0}")]
|
#[error("Failed to read BAT entry: {0}")]
|
||||||
ReadBat(#[source] io::Error),
|
ReadBat(#[source] io::Error),
|
||||||
#[error("Failed to write BAT entry {0}")]
|
#[error("Failed to write BAT entry: {0}")]
|
||||||
WriteBat(#[source] io::Error),
|
WriteBat(#[source] io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +78,7 @@ impl BatEntry {
|
|||||||
|
|
||||||
// Calculate the number of entries in the BAT
|
// Calculate the number of entries in the BAT
|
||||||
fn calculate_entries(block_size: u32, virtual_disk_size: u64, chunk_ratio: u64) -> u64 {
|
fn calculate_entries(block_size: u32, virtual_disk_size: u64, chunk_ratio: u64) -> u64 {
|
||||||
let data_blocks_count = div_round_up!(virtual_disk_size, block_size as u64);
|
let data_blocks_count = virtual_disk_size.div_ceil(block_size as u64);
|
||||||
data_blocks_count + (data_blocks_count - 1) / chunk_ratio
|
data_blocks_count + (data_blocks_count - 1) / chunk_ratio
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,13 @@
|
|||||||
|
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
use byteorder::{ByteOrder, LittleEndian, ReadBytesExt};
|
|
||||||
use remain::sorted;
|
|
||||||
use std::collections::btree_map::BTreeMap;
|
use std::collections::btree_map::BTreeMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, Read, Seek, SeekFrom, Write};
|
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
use byteorder::{ByteOrder, LittleEndian, ReadBytesExt};
|
||||||
|
use remain::sorted;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@@ -109,7 +110,7 @@ impl FileTypeIdentifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(C, packed)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Header {
|
pub struct Header {
|
||||||
pub signature: u32,
|
pub signature: u32,
|
||||||
@@ -141,7 +142,7 @@ impl Header {
|
|||||||
return Err(VhdxHeaderError::InvalidHeaderSign);
|
return Err(VhdxHeaderError::InvalidHeaderSign);
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_checksum = calculate_checksum(&mut buffer, size_of::<u32>())?;
|
let new_checksum = calculate_checksum(&mut buffer, size_of::<u32>());
|
||||||
if header.checksum != new_checksum {
|
if header.checksum != new_checksum {
|
||||||
return Err(VhdxHeaderError::InvalidChecksum(String::from("Header")));
|
return Err(VhdxHeaderError::InvalidChecksum(String::from("Header")));
|
||||||
}
|
}
|
||||||
@@ -150,7 +151,7 @@ impl Header {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the header structure into a buffer
|
/// Converts the header structure into a buffer
|
||||||
fn get_header_as_buffer(&self, buffer: &mut [u8; HEADER_SIZE as usize]) {
|
fn write_to_buffer(&self, buffer: &mut [u8; HEADER_SIZE as usize]) {
|
||||||
// SAFETY: self is a valid header.
|
// SAFETY: self is a valid header.
|
||||||
let reference = unsafe {
|
let reference = unsafe {
|
||||||
std::slice::from_raw_parts(self as *const Header as *const u8, HEADER_SIZE as usize)
|
std::slice::from_raw_parts(self as *const Header as *const u8, HEADER_SIZE as usize)
|
||||||
@@ -159,7 +160,7 @@ impl Header {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates and returns new updated header from the provided current header
|
/// Creates and returns new updated header from the provided current header
|
||||||
pub fn update_header(
|
fn update_header(
|
||||||
f: &mut File,
|
f: &mut File,
|
||||||
current_header: &Header,
|
current_header: &Header,
|
||||||
change_data_guid: bool,
|
change_data_guid: bool,
|
||||||
@@ -190,11 +191,9 @@ impl Header {
|
|||||||
log_offset: current_header.log_offset,
|
log_offset: current_header.log_offset,
|
||||||
};
|
};
|
||||||
|
|
||||||
new_header.get_header_as_buffer(&mut buffer);
|
new_header.write_to_buffer(&mut buffer);
|
||||||
let mut crc = crc_any::CRC::crc32c();
|
new_header.checksum = calculate_checksum(&mut buffer, size_of::<u32>());
|
||||||
crc.digest(&buffer);
|
new_header.write_to_buffer(&mut buffer);
|
||||||
new_header.checksum = crc.get_crc() as u32;
|
|
||||||
new_header.get_header_as_buffer(&mut buffer);
|
|
||||||
|
|
||||||
f.seek(SeekFrom::Start(start))
|
f.seek(SeekFrom::Start(start))
|
||||||
.map_err(VhdxHeaderError::SeekHeader)?;
|
.map_err(VhdxHeaderError::SeekHeader)?;
|
||||||
@@ -204,7 +203,7 @@ impl Header {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(C, packed)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
struct RegionTableHeader {
|
struct RegionTableHeader {
|
||||||
pub signature: u32,
|
pub signature: u32,
|
||||||
@@ -230,7 +229,7 @@ impl RegionTableHeader {
|
|||||||
return Err(VhdxHeaderError::InvalidRegionSign);
|
return Err(VhdxHeaderError::InvalidRegionSign);
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_checksum = calculate_checksum(&mut buffer, size_of::<u32>())?;
|
let new_checksum = calculate_checksum(&mut buffer, size_of::<u32>());
|
||||||
if region_table_header.checksum != new_checksum {
|
if region_table_header.checksum != new_checksum {
|
||||||
return Err(VhdxHeaderError::InvalidChecksum(String::from("Region")));
|
return Err(VhdxHeaderError::InvalidChecksum(String::from("Region")));
|
||||||
}
|
}
|
||||||
@@ -329,7 +328,7 @@ impl RegionInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(C, packed)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct RegionTableEntry {
|
pub struct RegionTableEntry {
|
||||||
pub guid: Uuid,
|
pub guid: Uuid,
|
||||||
@@ -370,22 +369,10 @@ pub struct VhdxHeader {
|
|||||||
impl VhdxHeader {
|
impl VhdxHeader {
|
||||||
/// Creates a VhdxHeader from a reference to a file
|
/// Creates a VhdxHeader from a reference to a file
|
||||||
pub fn new(f: &mut File) -> Result<VhdxHeader> {
|
pub fn new(f: &mut File) -> Result<VhdxHeader> {
|
||||||
let _file_type_identifier: FileTypeIdentifier = FileTypeIdentifier::new(f)?;
|
|
||||||
let header_1 = Header::new(f, HEADER_1_START);
|
|
||||||
let header_2 = Header::new(f, HEADER_2_START);
|
|
||||||
|
|
||||||
let mut file_write_guid: u128 = 0;
|
|
||||||
let metadata = f.metadata().map_err(VhdxHeaderError::ReadMetadata)?;
|
|
||||||
if !metadata.permissions().readonly() {
|
|
||||||
file_write_guid = Uuid::new_v4().as_u128();
|
|
||||||
}
|
|
||||||
|
|
||||||
let (header_1, header_2) =
|
|
||||||
VhdxHeader::update_headers(f, header_1, header_2, file_write_guid)?;
|
|
||||||
Ok(VhdxHeader {
|
Ok(VhdxHeader {
|
||||||
_file_type_identifier,
|
_file_type_identifier: FileTypeIdentifier::new(f)?,
|
||||||
header_1,
|
header_1: Header::new(f, HEADER_1_START)?,
|
||||||
header_2,
|
header_2: Header::new(f, HEADER_2_START)?,
|
||||||
region_table_1: RegionTableHeader::new(f, REGION_TABLE_1_START)?,
|
region_table_1: RegionTableHeader::new(f, REGION_TABLE_1_START)?,
|
||||||
_region_table_2: RegionTableHeader::new(f, REGION_TABLE_2_START)?,
|
_region_table_2: RegionTableHeader::new(f, REGION_TABLE_2_START)?,
|
||||||
})
|
})
|
||||||
@@ -467,13 +454,11 @@ impl VhdxHeader {
|
|||||||
/// Therefore, before calculating, the existing checksum is retrieved and the
|
/// Therefore, before calculating, the existing checksum is retrieved and the
|
||||||
/// corresponding field is made zero. After the calculation, the existing checksum
|
/// corresponding field is made zero. After the calculation, the existing checksum
|
||||||
/// is put back to the buffer.
|
/// is put back to the buffer.
|
||||||
pub fn calculate_checksum(buffer: &mut [u8], csum_offset: usize) -> Result<u32> {
|
fn calculate_checksum(buffer: &mut [u8], csum_offset: usize) -> u32 {
|
||||||
// Read the checksum into a mutable slice
|
// Read the original checksum from the buffer
|
||||||
let csum_buf = &mut buffer[csum_offset..csum_offset + 4];
|
let orig_csum = LittleEndian::read_u32(&buffer[csum_offset..csum_offset + 4]);
|
||||||
// Convert the checksum chunk into a u32 integer
|
|
||||||
let orig_csum = LittleEndian::read_u32(csum_buf);
|
|
||||||
// Zero the checksum in the buffer
|
// Zero the checksum in the buffer
|
||||||
LittleEndian::write_u32(csum_buf, 0);
|
LittleEndian::write_u32(&mut buffer[csum_offset..csum_offset + 4], 0);
|
||||||
// Calculate the checksum on the resulting buffer
|
// Calculate the checksum on the resulting buffer
|
||||||
let mut crc = crc_any::CRC::crc32c();
|
let mut crc = crc_any::CRC::crc32c();
|
||||||
crc.digest(&buffer);
|
crc.digest(&buffer);
|
||||||
@@ -482,5 +467,5 @@ pub fn calculate_checksum(buffer: &mut [u8], csum_offset: usize) -> Result<u32>
|
|||||||
// Put back the original checksum in the buffer
|
// Put back the original checksum in the buffer
|
||||||
LittleEndian::write_u32(&mut buffer[csum_offset..csum_offset + 4], orig_csum);
|
LittleEndian::write_u32(&mut buffer[csum_offset..csum_offset + 4], orig_csum);
|
||||||
|
|
||||||
Ok(new_csum)
|
new_csum
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,15 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::vhdx::{
|
|
||||||
vhdx_bat::{self, BatEntry, VhdxBatError},
|
|
||||||
vhdx_metadata::{self, DiskSpec},
|
|
||||||
};
|
|
||||||
use remain::sorted;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, Read, Seek, SeekFrom, Write};
|
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||||
|
|
||||||
|
use remain::sorted;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::vhdx::vhdx_bat::{self, BatEntry, VhdxBatError};
|
||||||
|
use crate::vhdx::vhdx_metadata::{self, DiskSpec};
|
||||||
|
|
||||||
const SECTOR_SIZE: u64 = 512;
|
const SECTOR_SIZE: u64 = 512;
|
||||||
|
|
||||||
#[sorted]
|
#[sorted]
|
||||||
@@ -36,7 +36,7 @@ pub type Result<T> = std::result::Result<T, VhdxIoError>;
|
|||||||
|
|
||||||
macro_rules! align {
|
macro_rules! align {
|
||||||
($n:expr, $align:expr) => {{
|
($n:expr, $align:expr) => {{
|
||||||
(($n + $align - 1) / $align) * $align
|
$n.div_ceil($align) * $align
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,46 +95,45 @@ pub fn read(
|
|||||||
mut sector_index: u64,
|
mut sector_index: u64,
|
||||||
mut sector_count: u64,
|
mut sector_count: u64,
|
||||||
) -> Result<usize> {
|
) -> Result<usize> {
|
||||||
|
if disk_spec.has_parent {
|
||||||
|
return Err(VhdxIoError::UnsupportedMode);
|
||||||
|
}
|
||||||
|
|
||||||
let mut read_count: usize = 0;
|
let mut read_count: usize = 0;
|
||||||
|
|
||||||
while sector_count > 0 {
|
while sector_count > 0 {
|
||||||
if disk_spec.has_parent {
|
let sector = Sector::new(disk_spec, bat, sector_index, sector_count)?;
|
||||||
return Err(VhdxIoError::UnsupportedMode);
|
|
||||||
} else {
|
|
||||||
let sector = Sector::new(disk_spec, bat, sector_index, sector_count)?;
|
|
||||||
|
|
||||||
let bat_entry = match bat.get(sector.bat_index as usize) {
|
let bat_entry = match bat.get(sector.bat_index as usize) {
|
||||||
Some(entry) => entry.0,
|
Some(entry) => entry.0,
|
||||||
None => {
|
None => {
|
||||||
return Err(VhdxIoError::InvalidBatIndex);
|
return Err(VhdxIoError::InvalidBatIndex);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
match bat_entry & vhdx_bat::BAT_STATE_BIT_MASK {
|
|
||||||
vhdx_bat::PAYLOAD_BLOCK_NOT_PRESENT
|
|
||||||
| vhdx_bat::PAYLOAD_BLOCK_UNDEFINED
|
|
||||||
| vhdx_bat::PAYLOAD_BLOCK_UNMAPPED
|
|
||||||
| vhdx_bat::PAYLOAD_BLOCK_ZERO => {}
|
|
||||||
vhdx_bat::PAYLOAD_BLOCK_FULLY_PRESENT => {
|
|
||||||
f.seek(SeekFrom::Start(sector.file_offset))
|
|
||||||
.map_err(VhdxIoError::ReadSectorBlock)?;
|
|
||||||
f.read_exact(
|
|
||||||
&mut buf[read_count
|
|
||||||
..(read_count + (sector.free_sectors * SECTOR_SIZE) as usize)],
|
|
||||||
)
|
|
||||||
.map_err(VhdxIoError::ReadSectorBlock)?;
|
|
||||||
}
|
|
||||||
vhdx_bat::PAYLOAD_BLOCK_PARTIALLY_PRESENT => {
|
|
||||||
return Err(VhdxIoError::UnsupportedMode);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(VhdxIoError::InvalidBatEntryState);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
sector_count -= sector.free_sectors;
|
|
||||||
sector_index += sector.free_sectors;
|
|
||||||
read_count = sector.free_bytes as usize;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
match bat_entry & vhdx_bat::BAT_STATE_BIT_MASK {
|
||||||
|
vhdx_bat::PAYLOAD_BLOCK_NOT_PRESENT
|
||||||
|
| vhdx_bat::PAYLOAD_BLOCK_UNDEFINED
|
||||||
|
| vhdx_bat::PAYLOAD_BLOCK_UNMAPPED
|
||||||
|
| vhdx_bat::PAYLOAD_BLOCK_ZERO => {}
|
||||||
|
vhdx_bat::PAYLOAD_BLOCK_FULLY_PRESENT => {
|
||||||
|
f.seek(SeekFrom::Start(sector.file_offset))
|
||||||
|
.map_err(VhdxIoError::ReadSectorBlock)?;
|
||||||
|
f.read_exact(
|
||||||
|
&mut buf
|
||||||
|
[read_count..(read_count + (sector.free_sectors * SECTOR_SIZE) as usize)],
|
||||||
|
)
|
||||||
|
.map_err(VhdxIoError::ReadSectorBlock)?;
|
||||||
|
}
|
||||||
|
vhdx_bat::PAYLOAD_BLOCK_PARTIALLY_PRESENT => {
|
||||||
|
return Err(VhdxIoError::UnsupportedMode);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(VhdxIoError::InvalidBatEntryState);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
sector_count -= sector.free_sectors;
|
||||||
|
sector_index += sector.free_sectors;
|
||||||
|
read_count += sector.free_bytes as usize;
|
||||||
}
|
}
|
||||||
Ok(read_count)
|
Ok(read_count)
|
||||||
}
|
}
|
||||||
@@ -150,77 +149,73 @@ pub fn write(
|
|||||||
mut sector_index: u64,
|
mut sector_index: u64,
|
||||||
mut sector_count: u64,
|
mut sector_count: u64,
|
||||||
) -> Result<usize> {
|
) -> Result<usize> {
|
||||||
|
if disk_spec.has_parent {
|
||||||
|
return Err(VhdxIoError::UnsupportedMode);
|
||||||
|
}
|
||||||
|
|
||||||
let mut write_count: usize = 0;
|
let mut write_count: usize = 0;
|
||||||
|
|
||||||
while sector_count > 0 {
|
while sector_count > 0 {
|
||||||
if disk_spec.has_parent {
|
let sector = Sector::new(disk_spec, bat, sector_index, sector_count)?;
|
||||||
return Err(VhdxIoError::UnsupportedMode);
|
|
||||||
} else {
|
|
||||||
let sector = Sector::new(disk_spec, bat, sector_index, sector_count)?;
|
|
||||||
|
|
||||||
let bat_entry = match bat.get(sector.bat_index as usize) {
|
let bat_entry = match bat.get(sector.bat_index as usize) {
|
||||||
Some(entry) => entry.0,
|
Some(entry) => entry.0,
|
||||||
None => {
|
None => {
|
||||||
return Err(VhdxIoError::InvalidBatIndex);
|
return Err(VhdxIoError::InvalidBatIndex);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
match bat_entry & vhdx_bat::BAT_STATE_BIT_MASK {
|
|
||||||
vhdx_bat::PAYLOAD_BLOCK_NOT_PRESENT
|
|
||||||
| vhdx_bat::PAYLOAD_BLOCK_UNDEFINED
|
|
||||||
| vhdx_bat::PAYLOAD_BLOCK_UNMAPPED
|
|
||||||
| vhdx_bat::PAYLOAD_BLOCK_ZERO => {
|
|
||||||
let file_offset =
|
|
||||||
align!(disk_spec.image_size, vhdx_metadata::BLOCK_SIZE_MIN as u64);
|
|
||||||
let new_size = file_offset
|
|
||||||
.checked_add(disk_spec.block_size as u64)
|
|
||||||
.ok_or(VhdxIoError::InvalidDiskSize)?;
|
|
||||||
|
|
||||||
f.set_len(new_size).map_err(VhdxIoError::ResizeFile)?;
|
|
||||||
disk_spec.image_size = new_size;
|
|
||||||
|
|
||||||
let new_bat_entry = file_offset
|
|
||||||
| (vhdx_bat::PAYLOAD_BLOCK_FULLY_PRESENT & vhdx_bat::BAT_STATE_BIT_MASK);
|
|
||||||
bat[sector.bat_index as usize] = BatEntry(new_bat_entry);
|
|
||||||
BatEntry::write_bat_entries(f, bat_offset, bat)
|
|
||||||
.map_err(VhdxIoError::WriteBat)?;
|
|
||||||
|
|
||||||
if file_offset < vhdx_metadata::BLOCK_SIZE_MIN as u64 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
f.seek(SeekFrom::Start(file_offset))
|
|
||||||
.map_err(VhdxIoError::ReadSectorBlock)?;
|
|
||||||
f.write_all(
|
|
||||||
&buf[write_count
|
|
||||||
..(write_count + (sector.free_sectors * SECTOR_SIZE) as usize)],
|
|
||||||
)
|
|
||||||
.map_err(VhdxIoError::ReadSectorBlock)?;
|
|
||||||
}
|
|
||||||
vhdx_bat::PAYLOAD_BLOCK_FULLY_PRESENT => {
|
|
||||||
if sector.file_offset < vhdx_metadata::BLOCK_SIZE_MIN as u64 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
f.seek(SeekFrom::Start(sector.file_offset))
|
|
||||||
.map_err(VhdxIoError::ReadSectorBlock)?;
|
|
||||||
f.write_all(
|
|
||||||
&buf[write_count
|
|
||||||
..(write_count + (sector.free_sectors * SECTOR_SIZE) as usize)],
|
|
||||||
)
|
|
||||||
.map_err(VhdxIoError::ReadSectorBlock)?;
|
|
||||||
}
|
|
||||||
vhdx_bat::PAYLOAD_BLOCK_PARTIALLY_PRESENT => {
|
|
||||||
return Err(VhdxIoError::UnsupportedMode);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(VhdxIoError::InvalidBatEntryState);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
sector_count -= sector.free_sectors;
|
|
||||||
sector_index += sector.free_sectors;
|
|
||||||
write_count = sector.free_bytes as usize;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
match bat_entry & vhdx_bat::BAT_STATE_BIT_MASK {
|
||||||
|
vhdx_bat::PAYLOAD_BLOCK_NOT_PRESENT
|
||||||
|
| vhdx_bat::PAYLOAD_BLOCK_UNDEFINED
|
||||||
|
| vhdx_bat::PAYLOAD_BLOCK_UNMAPPED
|
||||||
|
| vhdx_bat::PAYLOAD_BLOCK_ZERO => {
|
||||||
|
let file_offset =
|
||||||
|
align!(disk_spec.image_size, vhdx_metadata::BLOCK_SIZE_MIN as u64);
|
||||||
|
let new_size = file_offset
|
||||||
|
.checked_add(disk_spec.block_size as u64)
|
||||||
|
.ok_or(VhdxIoError::InvalidDiskSize)?;
|
||||||
|
|
||||||
|
f.set_len(new_size).map_err(VhdxIoError::ResizeFile)?;
|
||||||
|
disk_spec.image_size = new_size;
|
||||||
|
|
||||||
|
let new_bat_entry = file_offset
|
||||||
|
| (vhdx_bat::PAYLOAD_BLOCK_FULLY_PRESENT & vhdx_bat::BAT_STATE_BIT_MASK);
|
||||||
|
bat[sector.bat_index as usize] = BatEntry(new_bat_entry);
|
||||||
|
BatEntry::write_bat_entries(f, bat_offset, bat).map_err(VhdxIoError::WriteBat)?;
|
||||||
|
|
||||||
|
if file_offset < vhdx_metadata::BLOCK_SIZE_MIN as u64 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
f.seek(SeekFrom::Start(file_offset))
|
||||||
|
.map_err(VhdxIoError::ReadSectorBlock)?;
|
||||||
|
f.write_all(
|
||||||
|
&buf[write_count..(write_count + (sector.free_sectors * SECTOR_SIZE) as usize)],
|
||||||
|
)
|
||||||
|
.map_err(VhdxIoError::ReadSectorBlock)?;
|
||||||
|
}
|
||||||
|
vhdx_bat::PAYLOAD_BLOCK_FULLY_PRESENT => {
|
||||||
|
if sector.file_offset < vhdx_metadata::BLOCK_SIZE_MIN as u64 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
f.seek(SeekFrom::Start(sector.file_offset))
|
||||||
|
.map_err(VhdxIoError::ReadSectorBlock)?;
|
||||||
|
f.write_all(
|
||||||
|
&buf[write_count..(write_count + (sector.free_sectors * SECTOR_SIZE) as usize)],
|
||||||
|
)
|
||||||
|
.map_err(VhdxIoError::ReadSectorBlock)?;
|
||||||
|
}
|
||||||
|
vhdx_bat::PAYLOAD_BLOCK_PARTIALLY_PRESENT => {
|
||||||
|
return Err(VhdxIoError::UnsupportedMode);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(VhdxIoError::InvalidBatEntryState);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
sector_count -= sector.free_sectors;
|
||||||
|
sector_index += sector.free_sectors;
|
||||||
|
write_count += sector.free_bytes as usize;
|
||||||
}
|
}
|
||||||
Ok(write_count)
|
Ok(write_count)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,17 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::vhdx::vhdx_header::RegionTableEntry;
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt};
|
|
||||||
use remain::sorted;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, Read, Seek, SeekFrom};
|
use std::io::{self, Read, Seek, SeekFrom};
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
use byteorder::{LittleEndian, ReadBytesExt};
|
||||||
|
use remain::sorted;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::vhdx::vhdx_header::RegionTableEntry;
|
||||||
|
|
||||||
const METADATA_SIGN: u64 = 0x6174_6164_6174_656D;
|
const METADATA_SIGN: u64 = 0x6174_6164_6174_656D;
|
||||||
const METADATA_ENTRY_SIZE: usize = 32;
|
const METADATA_ENTRY_SIZE: usize = 32;
|
||||||
const METADATA_MAX_ENTRIES: u16 = 2047;
|
const METADATA_MAX_ENTRIES: u16 = 2047;
|
||||||
@@ -53,6 +55,8 @@ const METADATA_LENGTH_MAX: u32 = 1 << 20; // 1 MiB
|
|||||||
pub enum VhdxMetadataError {
|
pub enum VhdxMetadataError {
|
||||||
#[error("Invalid block size count")]
|
#[error("Invalid block size count")]
|
||||||
InvalidBlockSize,
|
InvalidBlockSize,
|
||||||
|
#[error("Invalid disk size {0}")]
|
||||||
|
InvalidDiskSize(u64),
|
||||||
#[error("Invalid metadata entry count")]
|
#[error("Invalid metadata entry count")]
|
||||||
InvalidEntryCount,
|
InvalidEntryCount,
|
||||||
#[error("Invalid logical sector size")]
|
#[error("Invalid logical sector size")]
|
||||||
@@ -212,6 +216,14 @@ impl DiskSpec {
|
|||||||
if metadata_presence != METADATA_ALL_PRESENT {
|
if metadata_presence != METADATA_ALL_PRESENT {
|
||||||
return Err(VhdxMetadataError::MissingMetadata);
|
return Err(VhdxMetadataError::MissingMetadata);
|
||||||
}
|
}
|
||||||
|
// Make sure virtual disk size is not zero
|
||||||
|
if (metadata_presence & METADATA_VIRTUAL_DISK_SIZE_PRESENT != 0)
|
||||||
|
&& disk_spec.virtual_disk_size == 0
|
||||||
|
{
|
||||||
|
return Err(VhdxMetadataError::InvalidDiskSize(
|
||||||
|
disk_spec.virtual_disk_size,
|
||||||
|
));
|
||||||
|
}
|
||||||
// Check if the virtual disk size is a multiple of the logical sector
|
// Check if the virtual disk size is a multiple of the logical sector
|
||||||
// size.
|
// size.
|
||||||
if ((metadata_presence & METADATA_LOGICAL_SECTOR_SIZE_PRESENT) != 0)
|
if ((metadata_presence & METADATA_LOGICAL_SECTOR_SIZE_PRESENT) != 0)
|
||||||
@@ -255,7 +267,7 @@ impl DiskSpec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(C, packed)]
|
||||||
#[derive(Default, Debug, Clone, Copy)]
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
struct MetadataTableHeader {
|
struct MetadataTableHeader {
|
||||||
signature: u64,
|
signature: u64,
|
||||||
@@ -286,7 +298,7 @@ impl MetadataTableHeader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(C, packed)]
|
||||||
#[derive(Default, Debug, Clone, Copy)]
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
pub struct MetadataTableEntry {
|
pub struct MetadataTableEntry {
|
||||||
item_id: Uuid,
|
item_id: Uuid,
|
||||||
|
|||||||
@@ -2,14 +2,19 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult};
|
|
||||||
use crate::vhdx::{Result as VhdxResult, Vhdx};
|
|
||||||
use crate::AsyncAdaptor;
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use std::os::fd::AsRawFd;
|
||||||
use std::sync::{Arc, Mutex, MutexGuard};
|
use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
|
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
use crate::async_io::{
|
||||||
|
AsyncIo, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
|
||||||
|
};
|
||||||
|
use crate::vhdx::{Result as VhdxResult, Vhdx};
|
||||||
|
use crate::AsyncAdaptor;
|
||||||
|
|
||||||
pub struct VhdxDiskSync {
|
pub struct VhdxDiskSync {
|
||||||
vhdx_file: Arc<Mutex<Vhdx>>,
|
vhdx_file: Arc<Mutex<Vhdx>>,
|
||||||
}
|
}
|
||||||
@@ -33,6 +38,11 @@ impl DiskFile for VhdxDiskSync {
|
|||||||
as Box<dyn AsyncIo>,
|
as Box<dyn AsyncIo>,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fd(&mut self) -> BorrowedDiskFd {
|
||||||
|
let lock = self.vhdx_file.lock().unwrap();
|
||||||
|
BorrowedDiskFd::new(lock.as_raw_fd())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct VhdxSync {
|
pub struct VhdxSync {
|
||||||
|
|||||||
9
build.rs
9
build.rs
@@ -3,6 +3,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
|
use std::env;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -12,10 +13,18 @@ fn main() {
|
|||||||
if git_out.status.success() {
|
if git_out.status.success() {
|
||||||
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {
|
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {
|
||||||
version = git_out_str;
|
version = git_out_str;
|
||||||
|
// Pop the trailing newline.
|
||||||
|
version.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Append CH_EXTRA_VERSION to version if it is set.
|
||||||
|
if let Ok(extra_version) = env::var("CH_EXTRA_VERSION") {
|
||||||
|
println!("cargo:rerun-if-env-changed=CH_EXTRA_VERSION");
|
||||||
|
version.push_str(&format!("-{extra_version}"));
|
||||||
|
}
|
||||||
|
|
||||||
// This println!() has a special behavior, as it will set the environment
|
// This println!() has a special behavior, as it will set the environment
|
||||||
// variable BUILD_VERSION, so that it can be reused from the binary.
|
// variable BUILD_VERSION, so that it can be reused from the binary.
|
||||||
// Particularly, this is used from src/main.rs to display the exact
|
// Particularly, this is used from src/main.rs to display the exact
|
||||||
|
|||||||
@@ -5,33 +5,34 @@ name = "devices"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
|
acpi_tables = { workspace = true }
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.94"
|
||||||
arch = { path = "../arch" }
|
arch = { path = "../arch" }
|
||||||
bitflags = "2.6.0"
|
bitflags = "2.9.0"
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
event_monitor = { path = "../event_monitor" }
|
event_monitor = { path = "../event_monitor" }
|
||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
libc = "0.2.155"
|
libc = "0.2.167"
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
num_enum = "0.7.2"
|
num_enum = "0.7.2"
|
||||||
pci = { path = "../pci" }
|
pci = { path = "../pci" }
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.208", features = ["derive"] }
|
||||||
thiserror = "1.0.62"
|
thiserror = "2.0.6"
|
||||||
tpm = { path = "../tpm" }
|
tpm = { path = "../tpm" }
|
||||||
vm-allocator = { path = "../vm-allocator" }
|
vm-allocator = { path = "../vm-allocator" }
|
||||||
vm-device = { path = "../vm-device" }
|
vm-device = { path = "../vm-device" }
|
||||||
vm-memory = { version = "0.14.1", features = [
|
vm-memory = { workspace = true, features = [
|
||||||
"backend-atomic",
|
"backend-atomic",
|
||||||
"backend-bitmap",
|
"backend-bitmap",
|
||||||
"backend-mmap",
|
"backend-mmap",
|
||||||
] }
|
] }
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
vmm-sys-util = "0.12.1"
|
vmm-sys-util = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
[target.'cfg(any(target_arch = "aarch64", target_arch = "riscv64"))'.dependencies]
|
||||||
arch = { path = "../arch" }
|
arch = { path = "../arch" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
kvm = ["arch/kvm"]
|
||||||
pvmemcontrol = []
|
pvmemcontrol = []
|
||||||
|
|||||||
@@ -3,17 +3,19 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
use super::AcpiNotificationFlags;
|
|
||||||
use acpi_tables::{aml, Aml, AmlSink};
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use acpi_tables::{aml, Aml, AmlSink};
|
||||||
use vm_device::interrupt::InterruptSourceGroup;
|
use vm_device::interrupt::InterruptSourceGroup;
|
||||||
use vm_device::BusDevice;
|
use vm_device::BusDevice;
|
||||||
use vm_memory::GuestAddress;
|
use vm_memory::GuestAddress;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
use super::AcpiNotificationFlags;
|
||||||
|
|
||||||
pub const GED_DEVICE_ACPI_SIZE: usize = 0x1;
|
pub const GED_DEVICE_ACPI_SIZE: usize = 0x1;
|
||||||
|
|
||||||
/// A device for handling ACPI shutdown and reboot
|
/// A device for handling ACPI shutdown and reboot
|
||||||
|
|||||||
144
devices/src/aia.rs
Normal file
144
devices/src/aia.rs
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||||
|
// Copyright 2020, ARM Limited.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
|
use super::interrupt_controller::{Error, InterruptController};
|
||||||
|
extern crate arch;
|
||||||
|
use std::result;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use arch::layout;
|
||||||
|
use hypervisor::arch::riscv64::aia::{Vaia, VaiaConfig};
|
||||||
|
use hypervisor::{AiaState, CpuState};
|
||||||
|
use vm_device::interrupt::{
|
||||||
|
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
|
||||||
|
LegacyIrqSourceConfig, MsiIrqGroupConfig,
|
||||||
|
};
|
||||||
|
use vm_memory::address::Address;
|
||||||
|
use vm_migration::{Migratable, Pausable, Snapshottable, Transportable};
|
||||||
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
|
// Reserve 32 IRQs for legacy devices.
|
||||||
|
pub const IRQ_LEGACY_BASE: usize = layout::IRQ_BASE as usize;
|
||||||
|
pub const IRQ_LEGACY_COUNT: usize = 32;
|
||||||
|
// TODO: AIA snapshotting is not yet completed.
|
||||||
|
pub const _AIA_SNAPSHOT_ID: &str = "";
|
||||||
|
|
||||||
|
// Aia (Advance Interrupt Architecture) struct provides all the functionality of a
|
||||||
|
// AIA device. It wraps a hypervisor-emulated AIA device (Vaia) provided by the
|
||||||
|
// `hypervisor` crate.
|
||||||
|
// Aia struct also implements InterruptController to provide interrupt delivery
|
||||||
|
// service.
|
||||||
|
pub struct Aia {
|
||||||
|
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
|
||||||
|
// The hypervisor agnostic virtual AIA
|
||||||
|
vaia: Arc<Mutex<dyn Vaia>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Aia {
|
||||||
|
pub fn new(
|
||||||
|
vcpu_count: u8,
|
||||||
|
interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||||
|
vm: Arc<dyn hypervisor::Vm>,
|
||||||
|
) -> Result<Aia> {
|
||||||
|
let interrupt_source_group = interrupt_manager
|
||||||
|
.create_group(MsiIrqGroupConfig {
|
||||||
|
base: IRQ_LEGACY_BASE as InterruptIndex,
|
||||||
|
count: IRQ_LEGACY_COUNT as InterruptIndex,
|
||||||
|
})
|
||||||
|
.map_err(Error::CreateInterruptSourceGroup)?;
|
||||||
|
|
||||||
|
let vaia = vm
|
||||||
|
.create_vaia(Aia::create_default_config(vcpu_count as u64))
|
||||||
|
.map_err(Error::CreateAia)?;
|
||||||
|
|
||||||
|
let aia = Aia {
|
||||||
|
interrupt_source_group,
|
||||||
|
vaia,
|
||||||
|
};
|
||||||
|
aia.enable()?;
|
||||||
|
|
||||||
|
Ok(aia)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restore_vaia(
|
||||||
|
&mut self,
|
||||||
|
state: Option<AiaState>,
|
||||||
|
_saved_vcpu_states: &[CpuState],
|
||||||
|
) -> Result<()> {
|
||||||
|
self.vaia
|
||||||
|
.clone()
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.set_state(&state.unwrap())
|
||||||
|
.map_err(Error::RestoreAia)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enable(&self) -> Result<()> {
|
||||||
|
// Set irqfd for legacy interrupts
|
||||||
|
self.interrupt_source_group
|
||||||
|
.enable()
|
||||||
|
.map_err(Error::EnableInterrupt)?;
|
||||||
|
|
||||||
|
// Set irq_routing for legacy interrupts.
|
||||||
|
// irqchip: Hardcode to 0 as we support only 1 APLIC
|
||||||
|
// pin: Use irq number as pin
|
||||||
|
for i in IRQ_LEGACY_BASE..(IRQ_LEGACY_BASE + IRQ_LEGACY_COUNT) {
|
||||||
|
let config = LegacyIrqSourceConfig {
|
||||||
|
irqchip: 0,
|
||||||
|
pin: (i - IRQ_LEGACY_BASE) as u32,
|
||||||
|
};
|
||||||
|
self.interrupt_source_group
|
||||||
|
.update(
|
||||||
|
i as InterruptIndex,
|
||||||
|
InterruptSourceConfig::LegacyIrq(config),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.map_err(Error::EnableInterrupt)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.interrupt_source_group
|
||||||
|
.set_gsi()
|
||||||
|
.map_err(Error::EnableInterrupt)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Default config implied by arch::layout
|
||||||
|
pub fn create_default_config(vcpu_count: u64) -> VaiaConfig {
|
||||||
|
VaiaConfig {
|
||||||
|
vcpu_count: vcpu_count as u32,
|
||||||
|
aplic_addr: layout::APLIC_START.raw_value(),
|
||||||
|
imsic_addr: layout::IMSIC_START.raw_value(),
|
||||||
|
nr_irqs: layout::IRQ_NUM,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_vaia(&mut self) -> Result<Arc<Mutex<dyn Vaia>>> {
|
||||||
|
Ok(self.vaia.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterruptController for Aia {
|
||||||
|
// This should be called anytime an interrupt needs to be injected into the
|
||||||
|
// running guest.
|
||||||
|
fn service_irq(&mut self, irq: usize) -> Result<()> {
|
||||||
|
self.interrupt_source_group
|
||||||
|
.trigger(irq as InterruptIndex)
|
||||||
|
.map_err(Error::TriggerInterrupt)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notifier(&self, irq: usize) -> Option<EventFd> {
|
||||||
|
self.interrupt_source_group.notifier(irq as InterruptIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Snapshottable for Aia {}
|
||||||
|
impl Pausable for Aia {}
|
||||||
|
impl Transportable for Aia {}
|
||||||
|
impl Migratable for Aia {}
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
|
|
||||||
use vm_device::BusDevice;
|
use vm_device::BusDevice;
|
||||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,13 @@
|
|||||||
|
|
||||||
use super::interrupt_controller::{Error, InterruptController};
|
use super::interrupt_controller::{Error, InterruptController};
|
||||||
extern crate arch;
|
extern crate arch;
|
||||||
use anyhow::anyhow;
|
|
||||||
use arch::layout;
|
|
||||||
use hypervisor::{
|
|
||||||
arch::aarch64::gic::{Vgic, VgicConfig},
|
|
||||||
CpuState, GicState,
|
|
||||||
};
|
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use arch::layout;
|
||||||
|
use hypervisor::arch::aarch64::gic::{GicState, Vgic, VgicConfig};
|
||||||
|
use hypervisor::CpuState;
|
||||||
use vm_device::interrupt::{
|
use vm_device::interrupt::{
|
||||||
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
|
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
|
||||||
LegacyIrqSourceConfig, MsiIrqGroupConfig,
|
LegacyIrqSourceConfig, MsiIrqGroupConfig,
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||||
// Copyright 2020, ARM Limited.
|
// Copyright 2020, ARM Limited.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
use std::io;
|
use std::{io, result};
|
||||||
use std::result;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
@@ -17,30 +18,38 @@ pub enum Error {
|
|||||||
InvalidDeliveryMode,
|
InvalidDeliveryMode,
|
||||||
/// Failed creating the interrupt source group.
|
/// Failed creating the interrupt source group.
|
||||||
#[error("Failed creating the interrupt source group: {0}")]
|
#[error("Failed creating the interrupt source group: {0}")]
|
||||||
CreateInterruptSourceGroup(io::Error),
|
CreateInterruptSourceGroup(#[source] io::Error),
|
||||||
/// Failed triggering the interrupt.
|
/// Failed triggering the interrupt.
|
||||||
#[error("Failed triggering the interrupt: {0}")]
|
#[error("Failed triggering the interrupt: {0}")]
|
||||||
TriggerInterrupt(io::Error),
|
TriggerInterrupt(#[source] io::Error),
|
||||||
/// Failed masking the interrupt.
|
/// Failed masking the interrupt.
|
||||||
#[error("Failed masking the interrupt: {0}")]
|
#[error("Failed masking the interrupt: {0}")]
|
||||||
MaskInterrupt(io::Error),
|
MaskInterrupt(#[source] io::Error),
|
||||||
/// Failed unmasking the interrupt.
|
/// Failed unmasking the interrupt.
|
||||||
#[error("Failed unmasking the interrupt: {0}")]
|
#[error("Failed unmasking the interrupt: {0}")]
|
||||||
UnmaskInterrupt(io::Error),
|
UnmaskInterrupt(#[source] io::Error),
|
||||||
/// Failed updating the interrupt.
|
/// Failed updating the interrupt.
|
||||||
#[error("Failed updating the interrupt: {0}")]
|
#[error("Failed updating the interrupt: {0}")]
|
||||||
UpdateInterrupt(io::Error),
|
UpdateInterrupt(#[source] io::Error),
|
||||||
/// Failed enabling the interrupt.
|
/// Failed enabling the interrupt.
|
||||||
#[error("Failed enabling the interrupt: {0}")]
|
#[error("Failed enabling the interrupt: {0}")]
|
||||||
EnableInterrupt(io::Error),
|
EnableInterrupt(#[source] io::Error),
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
/// Failed creating GIC device.
|
/// Failed creating GIC device.
|
||||||
#[error("Failed creating GIC device: {0}")]
|
#[error("Failed creating GIC device: {0}")]
|
||||||
CreateGic(hypervisor::HypervisorVmError),
|
CreateGic(#[source] hypervisor::HypervisorVmError),
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
/// Failed restoring GIC device.
|
/// Failed restoring GIC device.
|
||||||
#[error("Failed restoring GIC device: {0}")]
|
#[error("Failed restoring GIC device: {0}")]
|
||||||
RestoreGic(hypervisor::arch::aarch64::gic::Error),
|
RestoreGic(#[source] hypervisor::arch::aarch64::gic::Error),
|
||||||
|
#[cfg(target_arch = "riscv64")]
|
||||||
|
/// Failed creating AIA device.
|
||||||
|
#[error("Failed creating AIA device: {0}")]
|
||||||
|
CreateAia(#[source] hypervisor::HypervisorVmError),
|
||||||
|
#[cfg(target_arch = "riscv64")]
|
||||||
|
/// Failed restoring AIA device.
|
||||||
|
#[error("Failed restoring AIA device: {0}")]
|
||||||
|
RestoreAia(#[source] hypervisor::arch::riscv64::aia::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
@@ -67,7 +76,7 @@ pub struct MsiMessage {
|
|||||||
// Introduce trait InterruptController to uniform the interrupt
|
// Introduce trait InterruptController to uniform the interrupt
|
||||||
// service provided for devices.
|
// service provided for devices.
|
||||||
// Device manager uses this trait without caring whether it is a
|
// Device manager uses this trait without caring whether it is a
|
||||||
// IOAPIC (X86) or GIC (Arm).
|
// IOAPIC (X86), GIC (Arm) or AIA (RISC-V).
|
||||||
pub trait InterruptController: Send {
|
pub trait InterruptController: Send {
|
||||||
fn service_irq(&mut self, irq: usize) -> Result<()>;
|
fn service_irq(&mut self, irq: usize) -> Result<()>;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
|||||||
@@ -9,11 +9,11 @@
|
|||||||
// Implementation of an intel 82093AA Input/Output Advanced Programmable Interrupt Controller
|
// Implementation of an intel 82093AA Input/Output Advanced Programmable Interrupt Controller
|
||||||
// See https://pdos.csail.mit.edu/6.828/2016/readings/ia32/ioapic.pdf for a specification.
|
// See https://pdos.csail.mit.edu/6.828/2016/readings/ia32/ioapic.pdf for a specification.
|
||||||
|
|
||||||
use super::interrupt_controller::{Error, InterruptController};
|
|
||||||
use byteorder::{ByteOrder, LittleEndian};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
|
|
||||||
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use vm_device::interrupt::{
|
use vm_device::interrupt::{
|
||||||
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
|
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
|
||||||
MsiIrqGroupConfig, MsiIrqSourceConfig,
|
MsiIrqGroupConfig, MsiIrqSourceConfig,
|
||||||
@@ -23,6 +23,8 @@ use vm_memory::GuestAddress;
|
|||||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
use super::interrupt_controller::{Error, InterruptController};
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
// I/O REDIRECTION TABLE REGISTER
|
// I/O REDIRECTION TABLE REGISTER
|
||||||
@@ -344,9 +346,9 @@ impl Ioapic {
|
|||||||
|
|
||||||
// Generate MSI message address
|
// Generate MSI message address
|
||||||
let low_addr: u32 = self.apic_address.0 as u32
|
let low_addr: u32 = self.apic_address.0 as u32
|
||||||
| u32::from(destination_id) << 12
|
| (u32::from(destination_id) << 12)
|
||||||
| u32::from(redirection_hint) << 3
|
| (u32::from(redirection_hint) << 3)
|
||||||
| u32::from(destination_mode) << 2;
|
| (u32::from(destination_mode) << 2);
|
||||||
|
|
||||||
// Validate Trigger Mode value
|
// Validate Trigger Mode value
|
||||||
let trigger_mode = trigger_mode(entry);
|
let trigger_mode = trigger_mode(entry);
|
||||||
@@ -370,9 +372,9 @@ impl Ioapic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate MSI message data
|
// Generate MSI message data
|
||||||
let data: u32 = u32::from(trigger_mode) << 15
|
let data: u32 = (u32::from(trigger_mode) << 15)
|
||||||
| u32::from(remote_irr(entry)) << 14
|
| (u32::from(remote_irr(entry)) << 14)
|
||||||
| u32::from(delivery_mode) << 8
|
| (u32::from(delivery_mode) << 8)
|
||||||
| u32::from(vector(entry));
|
| u32::from(vector(entry));
|
||||||
|
|
||||||
let config = MsiIrqSourceConfig {
|
let config = MsiIrqSourceConfig {
|
||||||
@@ -416,7 +418,7 @@ impl InterruptController for Ioapic {
|
|||||||
self.interrupt_source_group
|
self.interrupt_source_group
|
||||||
.trigger(irq as InterruptIndex)
|
.trigger(irq as InterruptIndex)
|
||||||
.map_err(Error::TriggerInterrupt)?;
|
.map_err(Error::TriggerInterrupt)?;
|
||||||
debug!("Interrupt {irq} successfully delivered");
|
trace!("Interrupt {irq} successfully delivered");
|
||||||
|
|
||||||
// If trigger mode is level sensitive, set the Remote IRR bit.
|
// If trigger mode is level sensitive, set the Remote IRR bit.
|
||||||
// It will be cleared when the EOI is received.
|
// It will be cleared when the EOI is received.
|
||||||
|
|||||||
@@ -4,18 +4,17 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
use libc::{clock_gettime, gmtime_r, timespec, tm, CLOCK_REALTIME};
|
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::mem;
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
use std::thread;
|
use std::{mem, thread};
|
||||||
use vm_device::BusDevice;
|
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
|
||||||
|
|
||||||
// https://github.com/rust-lang/libc/issues/1848
|
// https://github.com/rust-lang/libc/issues/1848
|
||||||
#[cfg_attr(target_env = "musl", allow(deprecated))]
|
#[cfg_attr(target_env = "musl", allow(deprecated))]
|
||||||
use libc::time_t;
|
use libc::time_t;
|
||||||
|
use libc::{clock_gettime, gmtime_r, timespec, tm, CLOCK_REALTIME};
|
||||||
|
use vm_device::BusDevice;
|
||||||
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
const INDEX_MASK: u8 = 0x7f;
|
const INDEX_MASK: u8 = 0x7f;
|
||||||
const INDEX_OFFSET: u64 = 0x0;
|
const INDEX_OFFSET: u64 = 0x0;
|
||||||
@@ -153,7 +152,7 @@ impl BusDevice for Cmos {
|
|||||||
0x08 => to_bcd(month as u8),
|
0x08 => to_bcd(month as u8),
|
||||||
0x09 => to_bcd((year % 100) as u8),
|
0x09 => to_bcd((year % 100) as u8),
|
||||||
// Bit 5 for 32kHz clock. Bit 7 for Update in Progress
|
// Bit 5 for 32kHz clock. Bit 7 for Update in Progress
|
||||||
0x0a => 1 << 5 | (update_in_progress as u8) << 7,
|
0x0a => (1 << 5) | ((update_in_progress as u8) << 7),
|
||||||
// Bit 0-6 are reserved and must be 0.
|
// Bit 0-6 are reserved and must be 0.
|
||||||
// Bit 7 must be 1 (CMOS has power)
|
// Bit 7 must be 1 (CMOS has power)
|
||||||
0x0d => 1 << 7,
|
0x0d => 1 << 7,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use vm_device::BusDevice;
|
use vm_device::BusDevice;
|
||||||
|
|
||||||
/// Debug I/O port, see:
|
/// Debug I/O port, see:
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
|
|
||||||
use vm_device::BusDevice;
|
use vm_device::BusDevice;
|
||||||
|
|
||||||
/// Provides firmware debug output via I/O port controls
|
/// Provides firmware debug output via I/O port controls
|
||||||
|
|||||||
@@ -7,16 +7,17 @@
|
|||||||
//! This module implements an ARM PrimeCell General Purpose Input/Output(PL061) to support gracefully poweroff microvm from external.
|
//! This module implements an ARM PrimeCell General Purpose Input/Output(PL061) to support gracefully poweroff microvm from external.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use crate::{read_le_u32, write_le_u32};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::io;
|
|
||||||
use std::result;
|
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
|
use std::{io, result};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use vm_device::interrupt::InterruptSourceGroup;
|
use vm_device::interrupt::InterruptSourceGroup;
|
||||||
use vm_device::BusDevice;
|
use vm_device::BusDevice;
|
||||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||||
|
|
||||||
|
use crate::{read_le_u32, write_le_u32};
|
||||||
|
|
||||||
const OFS_DATA: u64 = 0x400; // Data Register
|
const OFS_DATA: u64 = 0x400; // Data Register
|
||||||
const GPIODIR: u64 = 0x400; // Direction Register
|
const GPIODIR: u64 = 0x400; // Direction Register
|
||||||
const GPIOIS: u64 = 0x404; // Interrupt Sense Register
|
const GPIOIS: u64 = 0x404; // Interrupt Sense Register
|
||||||
@@ -42,11 +43,11 @@ const N_GPIOS: u32 = 8;
|
|||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("Bad Write Offset: {0}")]
|
#[error("Bad Write Offset: {0}")]
|
||||||
BadWriteOffset(u64),
|
BadWriteOffset(u64),
|
||||||
#[error("GPIO interrupt disabled by guest driver.")]
|
#[error("GPIO interrupt disabled by guest driver")]
|
||||||
GpioInterruptDisabled,
|
GpioInterruptDisabled,
|
||||||
#[error("Could not trigger GPIO interrupt: {0}.")]
|
#[error("Could not trigger GPIO interrupt: {0}")]
|
||||||
GpioInterruptFailure(io::Error),
|
GpioInterruptFailure(#[source] io::Error),
|
||||||
#[error("Invalid GPIO Input key triggered: {0}.")]
|
#[error("Invalid GPIO Input key triggered: {0}")]
|
||||||
GpioTriggerKeyFailure(u32),
|
GpioTriggerKeyFailure(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,10 +324,11 @@ impl Migratable for Gpio {}
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
|
||||||
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
|
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
const GPIO_NAME: &str = "gpio";
|
const GPIO_NAME: &str = "gpio";
|
||||||
const LEGACY_GPIO_MAPPED_IO_START: u64 = 0x0902_0000;
|
const LEGACY_GPIO_MAPPED_IO_START: u64 = 0x0902_0000;
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,10 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
use std::sync::{
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
atomic::{AtomicBool, Ordering},
|
use std::sync::{Arc, Barrier};
|
||||||
Arc, Barrier,
|
|
||||||
};
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use vm_device::BusDevice;
|
use vm_device::BusDevice;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
|||||||
@@ -24,14 +24,13 @@ pub use self::cmos::Cmos;
|
|||||||
pub use self::debug_port::DebugPort;
|
pub use self::debug_port::DebugPort;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub use self::fwdebug::FwDebugDevice;
|
pub use self::fwdebug::FwDebugDevice;
|
||||||
pub use self::i8042::I8042Device;
|
|
||||||
pub use self::serial::Serial;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
pub use self::gpio_pl061::Error as GpioDeviceError;
|
pub use self::gpio_pl061::Error as GpioDeviceError;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
pub use self::gpio_pl061::Gpio;
|
pub use self::gpio_pl061::Gpio;
|
||||||
|
pub use self::i8042::I8042Device;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
pub use self::rtc_pl031::Rtc;
|
pub use self::rtc_pl031::Rtc;
|
||||||
|
pub use self::serial::Serial;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
pub use self::uart_pl011::Pl011;
|
pub use self::uart_pl011::Pl011;
|
||||||
|
|||||||
@@ -8,14 +8,16 @@
|
|||||||
//! This is achieved by generating an interrupt signal after counting for a programmed number of cycles of
|
//! This is achieved by generating an interrupt signal after counting for a programmed number of cycles of
|
||||||
//! a real-time clock input.
|
//! a real-time clock input.
|
||||||
//!
|
//!
|
||||||
use crate::{read_le_u32, write_le_u32};
|
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{io, result};
|
use std::{io, result};
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use vm_device::interrupt::InterruptSourceGroup;
|
use vm_device::interrupt::InterruptSourceGroup;
|
||||||
use vm_device::BusDevice;
|
use vm_device::BusDevice;
|
||||||
|
|
||||||
|
use crate::{read_le_u32, write_le_u32};
|
||||||
|
|
||||||
// As you can see in https://static.docs.arm.com/ddi0224/c/real_time_clock_pl031_r1p3_technical_reference_manual_DDI0224C.pdf
|
// As you can see in https://static.docs.arm.com/ddi0224/c/real_time_clock_pl031_r1p3_technical_reference_manual_DDI0224C.pdf
|
||||||
// at section 3.2 Summary of RTC registers, the total size occupied by this device is 0x000 -> 0xFFC + 4 = 0x1000.
|
// at section 3.2 Summary of RTC registers, the total size occupied by this device is 0x000 -> 0xFFC + 4 = 0x1000.
|
||||||
// From 0x0 to 0x1C we have following registers:
|
// From 0x0 to 0x1C we have following registers:
|
||||||
@@ -44,7 +46,7 @@ pub enum Error {
|
|||||||
#[error("Bad Write Offset: {0}")]
|
#[error("Bad Write Offset: {0}")]
|
||||||
BadWriteOffset(u64),
|
BadWriteOffset(u64),
|
||||||
#[error("Failed to trigger interrupt: {0}")]
|
#[error("Failed to trigger interrupt: {0}")]
|
||||||
InterruptFailure(io::Error),
|
InterruptFailure(#[source] io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
@@ -228,13 +230,14 @@ impl BusDevice for Rtc {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
|
||||||
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
read_be_u16, read_be_u32, read_le_i32, read_le_u16, read_le_u64, write_be_u16,
|
read_be_u16, read_be_u32, read_le_i32, read_le_u16, read_le_u64, write_be_u16,
|
||||||
write_be_u32, write_le_i32, write_le_u16, write_le_u64,
|
write_be_u32, write_le_i32, write_le_u16, write_le_u64,
|
||||||
};
|
};
|
||||||
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
|
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
|
||||||
|
|
||||||
const LEGACY_RTC_MAPPED_IO_START: u64 = 0x0901_0000;
|
const LEGACY_RTC_MAPPED_IO_START: u64 = 0x0901_0000;
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,11 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE-BSD-3-Clause file.
|
// found in the LICENSE-BSD-3-Clause file.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
use std::{io, result};
|
use std::{io, result};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use vm_device::interrupt::InterruptSourceGroup;
|
use vm_device::interrupt::InterruptSourceGroup;
|
||||||
use vm_device::BusDevice;
|
use vm_device::BusDevice;
|
||||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||||
@@ -340,11 +341,13 @@ impl Migratable for Serial {}
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
|
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
const SERIAL_NAME: &str = "serial";
|
const SERIAL_NAME: &str = "serial";
|
||||||
|
|
||||||
struct TestInterrupt {
|
struct TestInterrupt {
|
||||||
@@ -431,7 +434,7 @@ mod tests {
|
|||||||
|
|
||||||
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd
|
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd
|
||||||
// counter doesn't change (for 0 it blocks)
|
// counter doesn't change (for 0 it blocks)
|
||||||
assert!(intr_evt.write(1).is_ok());
|
intr_evt.write(1).unwrap();
|
||||||
serial.write(0, IER as u64, &[IER_RECV_BIT]);
|
serial.write(0, IER as u64, &[IER_RECV_BIT]);
|
||||||
serial.queue_input_bytes(b"abc").unwrap();
|
serial.queue_input_bytes(b"abc").unwrap();
|
||||||
|
|
||||||
@@ -468,7 +471,7 @@ mod tests {
|
|||||||
|
|
||||||
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd
|
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd
|
||||||
// counter doesn't change (for 0 it blocks)
|
// counter doesn't change (for 0 it blocks)
|
||||||
assert!(intr_evt.write(1).is_ok());
|
intr_evt.write(1).unwrap();
|
||||||
serial.write(0, IER as u64, &[IER_THR_BIT]);
|
serial.write(0, IER as u64, &[IER_THR_BIT]);
|
||||||
serial.write(0, DATA as u64, b"a");
|
serial.write(0, DATA as u64, b"a");
|
||||||
|
|
||||||
|
|||||||
@@ -6,17 +6,19 @@
|
|||||||
//! This module implements an ARM PrimeCell UART(PL011).
|
//! This module implements an ARM PrimeCell UART(PL011).
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use crate::{read_le_u32, write_le_u32};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{io, result};
|
use std::{io, result};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use vm_device::interrupt::InterruptSourceGroup;
|
use vm_device::interrupt::InterruptSourceGroup;
|
||||||
use vm_device::BusDevice;
|
use vm_device::BusDevice;
|
||||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||||
|
|
||||||
|
use crate::{read_le_u32, write_le_u32};
|
||||||
|
|
||||||
/* Registers */
|
/* Registers */
|
||||||
const UARTDR: u64 = 0;
|
const UARTDR: u64 = 0;
|
||||||
const UARTRSR_UARTECR: u64 = 1;
|
const UARTRSR_UARTECR: u64 = 1;
|
||||||
@@ -49,14 +51,14 @@ const AMBA_ID_HIGH: u64 = 0x401;
|
|||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("pl011_write: Bad Write Offset: {0}")]
|
#[error("pl011_write: Bad Write Offset: {0}")]
|
||||||
BadWriteOffset(u64),
|
BadWriteOffset(u64),
|
||||||
#[error("pl011: DMA not implemented.")]
|
#[error("pl011: DMA not implemented")]
|
||||||
DmaNotImplemented,
|
DmaNotImplemented,
|
||||||
#[error("Failed to trigger interrupt: {0}")]
|
#[error("Failed to trigger interrupt: {0}")]
|
||||||
InterruptFailure(io::Error),
|
InterruptFailure(#[source] io::Error),
|
||||||
#[error("Failed to write: {0}")]
|
#[error("Failed to write: {0}")]
|
||||||
WriteAllFailure(io::Error),
|
WriteAllFailure(#[source] io::Error),
|
||||||
#[error("Failed to flush: {0}")]
|
#[error("Failed to flush: {0}")]
|
||||||
FlushFailure(io::Error),
|
FlushFailure(#[source] io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
@@ -452,11 +454,13 @@ impl Migratable for Pl011 {}
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
|
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
const SERIAL_NAME: &str = "serial";
|
const SERIAL_NAME: &str = "serial";
|
||||||
|
|
||||||
struct TestInterrupt {
|
struct TestInterrupt {
|
||||||
@@ -545,7 +549,7 @@ mod tests {
|
|||||||
|
|
||||||
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd
|
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd
|
||||||
// counter doesn't change (for 0 it blocks)
|
// counter doesn't change (for 0 it blocks)
|
||||||
assert!(intr_evt.write(1).is_ok());
|
intr_evt.write(1).unwrap();
|
||||||
pl011.queue_input_bytes(b"abc").unwrap();
|
pl011.queue_input_bytes(b"abc").unwrap();
|
||||||
|
|
||||||
assert_eq!(intr_evt.read().unwrap(), 2);
|
assert_eq!(intr_evt.read().unwrap(), 2);
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ extern crate event_monitor;
|
|||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
pub mod acpi;
|
pub mod acpi;
|
||||||
|
#[cfg(target_arch = "riscv64")]
|
||||||
|
pub mod aia;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub mod debug_console;
|
pub mod debug_console;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
@@ -26,6 +28,8 @@ pub mod legacy;
|
|||||||
#[cfg(feature = "pvmemcontrol")]
|
#[cfg(feature = "pvmemcontrol")]
|
||||||
pub mod pvmemcontrol;
|
pub mod pvmemcontrol;
|
||||||
pub mod pvpanic;
|
pub mod pvpanic;
|
||||||
|
// TODO: TPM is not yet supported
|
||||||
|
#[cfg(not(target_arch = "riscv64"))]
|
||||||
pub mod tpm;
|
pub mod tpm;
|
||||||
|
|
||||||
pub use self::acpi::{AcpiGedDevice, AcpiPmTimerDevice, AcpiShutdownDevice};
|
pub use self::acpi::{AcpiGedDevice, AcpiPmTimerDevice, AcpiShutdownDevice};
|
||||||
|
|||||||
@@ -3,23 +3,24 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::sync::{Arc, Barrier, Mutex, RwLock};
|
||||||
|
use std::{io, result};
|
||||||
|
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
use pci::{
|
use pci::{
|
||||||
BarReprogrammingParams, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType,
|
BarReprogrammingParams, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType,
|
||||||
PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciSubclass,
|
PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciSubclass,
|
||||||
};
|
};
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
ffi::CString,
|
|
||||||
io, result,
|
|
||||||
sync::{Arc, Barrier, Mutex, RwLock},
|
|
||||||
};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use vm_allocator::{page_size::get_page_size, AddressAllocator, SystemAllocator};
|
use vm_allocator::page_size::get_page_size;
|
||||||
|
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||||
use vm_device::{BusDeviceSync, Resource};
|
use vm_device::{BusDeviceSync, Resource};
|
||||||
|
use vm_memory::bitmap::AtomicBitmap;
|
||||||
use vm_memory::{
|
use vm_memory::{
|
||||||
bitmap::AtomicBitmap, Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemory,
|
Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
|
||||||
GuestMemoryAtomic, GuestMemoryError, GuestMemoryMmap, Le32, Le64,
|
GuestMemoryError, GuestMemoryMmap, Le32, Le64,
|
||||||
};
|
};
|
||||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||||
|
|
||||||
@@ -483,7 +484,7 @@ impl PvmemcontrolBusDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_vma_anon_name(&self, addr: u64, length: u64, name: u64) -> result::Result<(), Error> {
|
fn set_vma_anon_name(&self, addr: u64, length: u64, name: u64) -> result::Result<(), Error> {
|
||||||
let name = (name != 0).then(|| CString::new(format!("pvmemcontrol-{}", name)).unwrap());
|
let name = (name != 0).then(|| CString::new(format!("pvmemcontrol-{name}")).unwrap());
|
||||||
let name_ptr = if let Some(name) = &name {
|
let name_ptr = if let Some(name) = &name {
|
||||||
name.as_ptr()
|
name.as_ptr()
|
||||||
} else {
|
} else {
|
||||||
@@ -697,17 +698,19 @@ impl PciDevice for PvmemcontrolPciDevice {
|
|||||||
reg_idx: usize,
|
reg_idx: usize,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
) -> Option<Arc<Barrier>> {
|
) -> (Vec<BarReprogrammingParams>, Option<Arc<Barrier>>) {
|
||||||
self.configuration
|
(
|
||||||
.write_config_register(reg_idx, offset, data);
|
self.configuration
|
||||||
None
|
.write_config_register(reg_idx, offset, data),
|
||||||
|
None,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
|
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
|
||||||
self.configuration.read_config_register(reg_idx)
|
self.configuration.read_config_register(reg_idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any(&mut self) -> &mut dyn std::any::Any {
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -715,14 +718,6 @@ impl PciDevice for PvmemcontrolPciDevice {
|
|||||||
Some(self.id.clone())
|
Some(self.id.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detect_bar_reprogramming(
|
|
||||||
&mut self,
|
|
||||||
reg_idx: usize,
|
|
||||||
data: &[u8],
|
|
||||||
) -> Option<BarReprogrammingParams> {
|
|
||||||
self.configuration.detect_bar_reprogramming(reg_idx, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allocate_bars(
|
fn allocate_bars(
|
||||||
&mut self,
|
&mut self,
|
||||||
_allocator: &Arc<Mutex<SystemAllocator>>,
|
_allocator: &Arc<Mutex<SystemAllocator>>,
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
|
use std::any::Any;
|
||||||
|
use std::result;
|
||||||
|
use std::sync::{Arc, Barrier, Mutex};
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use pci::{
|
use pci::{
|
||||||
BarReprogrammingParams, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType,
|
BarReprogrammingParams, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType,
|
||||||
@@ -10,9 +14,6 @@ use pci::{
|
|||||||
PCI_CONFIGURATION_ID,
|
PCI_CONFIGURATION_ID,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::any::Any;
|
|
||||||
use std::result;
|
|
||||||
use std::sync::{Arc, Barrier, Mutex};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||||
use vm_device::{BusDevice, Resource};
|
use vm_device::{BusDevice, Resource};
|
||||||
@@ -87,7 +88,11 @@ impl PvPanicDevice {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let command: [u8; 2] = [0x03, 0x01];
|
let command: [u8; 2] = [0x03, 0x01];
|
||||||
configuration.write_config_register(1, 0, &command);
|
let bar_reprogram = configuration.write_config_register(1, 0, &command);
|
||||||
|
assert!(
|
||||||
|
bar_reprogram.is_empty(),
|
||||||
|
"No bar reprogrammig is expected from writing to the COMMAND register"
|
||||||
|
);
|
||||||
|
|
||||||
let state: Option<PvPanicDeviceState> = snapshot
|
let state: Option<PvPanicDeviceState> = snapshot
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -155,24 +160,18 @@ impl PciDevice for PvPanicDevice {
|
|||||||
reg_idx: usize,
|
reg_idx: usize,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
) -> Option<Arc<Barrier>> {
|
) -> (Vec<BarReprogrammingParams>, Option<Arc<Barrier>>) {
|
||||||
self.configuration
|
(
|
||||||
.write_config_register(reg_idx, offset, data);
|
self.configuration
|
||||||
None
|
.write_config_register(reg_idx, offset, data),
|
||||||
|
None,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
|
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
|
||||||
self.configuration.read_reg(reg_idx)
|
self.configuration.read_reg(reg_idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detect_bar_reprogramming(
|
|
||||||
&mut self,
|
|
||||||
reg_idx: usize,
|
|
||||||
data: &[u8],
|
|
||||||
) -> Option<BarReprogrammingParams> {
|
|
||||||
self.configuration.detect_bar_reprogramming(reg_idx, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allocate_bars(
|
fn allocate_bars(
|
||||||
&mut self,
|
&mut self,
|
||||||
_allocator: &Arc<Mutex<SystemAllocator>>,
|
_allocator: &Arc<Mutex<SystemAllocator>>,
|
||||||
@@ -236,7 +235,7 @@ impl PciDevice for PvPanicDevice {
|
|||||||
data[0] = self.events;
|
data[0] = self.events;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any(&mut self) -> &mut dyn Any {
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
|
use std::cmp;
|
||||||
|
use std::sync::{Arc, Barrier};
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use arch::aarch64::layout::{TPM_SIZE, TPM_START};
|
use arch::aarch64::layout::{TPM_SIZE, TPM_START};
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use arch::x86_64::layout::{TPM_SIZE, TPM_START};
|
use arch::x86_64::layout::{TPM_SIZE, TPM_START};
|
||||||
use std::cmp;
|
|
||||||
use std::sync::{Arc, Barrier};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tpm::emulator::{BackendCmd, Emulator};
|
use tpm::emulator::{BackendCmd, Emulator};
|
||||||
use tpm::TPM_CRB_BUFFER_MAX;
|
use tpm::TPM_CRB_BUFFER_MAX;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# AMD SEV-SNP
|
# AMD SEV-SNP
|
||||||
|
|
||||||
### WARNING
|
### WARNING
|
||||||
|
|
||||||
This feature is only currently supported on MSHV.
|
This feature is only currently supported on MSHV.
|
||||||
|
|
||||||
AMD Secure Encrypted Virtualization & Secure Nested Paging (SEV-SNP) is an AMD
|
AMD Secure Encrypted Virtualization & Secure Nested Paging (SEV-SNP) is an AMD
|
||||||
@@ -9,8 +10,8 @@ malicious hypervisor-based attacks like data replay, memory-remapping and more
|
|||||||
in order to create an isolated execution environment. Here are some useful
|
in order to create an isolated execution environment. Here are some useful
|
||||||
links:
|
links:
|
||||||
|
|
||||||
* [SNP Homepage] (https://www.amd.com/en/processors/amd-secure-encrypted-virtualization)
|
- [SNP Homepage](https://www.amd.com/content/dam/amd/en/documents/epyc-business-docs/solution-briefs/amd-secure-encrypted-virtualization-solution-brief.pdf):
|
||||||
more information about SEV-SNP technical aspects, design and specification.
|
more information about SEV-SNP technical aspects, design and specification.
|
||||||
|
|
||||||
## Cloud Hypervisor support
|
## Cloud Hypervisor support
|
||||||
|
|
||||||
|
|||||||
13
docs/api.md
13
docs/api.md
@@ -50,17 +50,12 @@ for more details about the API payloads and responses.
|
|||||||
|
|
||||||
#### REST API Location and availability
|
#### REST API Location and availability
|
||||||
|
|
||||||
The REST API is available as soon as the Cloud Hypervisor binary is started,
|
The REST API, if enabled, is available as soon as the Cloud Hypervisor binary is started,
|
||||||
through a local UNIX socket.
|
through either a local UNIX socket as given in the Cloud Hypervisor option `--api-socket path=...`
|
||||||
By default, it is located at `/run/user/{user ID}/cloud-hypervisor.{Cloud Hypervisor PID}`.
|
or a fd with `--api-socket fd=...`.
|
||||||
For example, if you launched Cloud Hypervisor as user ID 1000 and its PID is
|
|
||||||
123456, the Cloud Hypervisor REST API will be available at `/run/user/1000/cloud-hypervisor.123456`.
|
|
||||||
|
|
||||||
The REST API default URL can be overridden through the Cloud Hypervisor
|
|
||||||
option `--api-socket`:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
$ ./target/debug/cloud-hypervisor --api-socket /tmp/cloud-hypervisor.sock
|
$ ./target/debug/cloud-hypervisor --api-socket path=/tmp/cloud-hypervisor.sock
|
||||||
Cloud Hypervisor Guest
|
Cloud Hypervisor Guest
|
||||||
API server: /tmp/cloud-hypervisor.sock
|
API server: /tmp/cloud-hypervisor.sock
|
||||||
vCPUs: 1
|
vCPUs: 1
|
||||||
|
|||||||
@@ -65,17 +65,17 @@ the data processing from within the container if you don't want to fight the
|
|||||||
tool chain.
|
tool chain.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# Get a shell
|
# Set env to enable code coverage
|
||||||
./scripts/dev_cli.sh shell
|
export RUSTFLAGS="-Cinstrument-coverage"
|
||||||
|
export LLVM_PROFILE_FILE="ch-%p-%m.profraw"
|
||||||
|
|
||||||
# Install llvm-tools-preview for llvm-profdata
|
# Run unit tests
|
||||||
rustup component add llvm-tools-preview
|
scripts/dev_cli.sh tests --unit --libc gnu
|
||||||
# Merge data files by using the following command
|
|
||||||
find . -name '*.profraw' -exec `rustc --print sysroot`/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-profdata merge -sparse {} -o coverage.profdata \;
|
|
||||||
|
|
||||||
# As of writing, the container has Rust 1.67.1. It is too old for grcov.
|
# Run integration tests
|
||||||
rustup install stable
|
scripts/dev_cli.sh tests --integration --libc gnu
|
||||||
cargo +stable install grcov
|
scripts/dev_cli.sh tests --integration-live-migration --libc gnu
|
||||||
# Run grcov as usual
|
|
||||||
grcov . --binary-path ./target/x86_64-unknown-linux-gnu/release -s . -t html --branch --ignore-not-existing -o coverage-html-output/
|
# Export code coverage report
|
||||||
|
scripts/dev_cli.sh tests --coverage -- -- html
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -4,151 +4,17 @@ In the context of adding more utilities to the Ubuntu cloud image being used
|
|||||||
for integration testing, this quick guide details how to achieve the proper
|
for integration testing, this quick guide details how to achieve the proper
|
||||||
modification of an official Ubuntu cloud image.
|
modification of an official Ubuntu cloud image.
|
||||||
|
|
||||||
## Create the image
|
## Image generation script
|
||||||
|
|
||||||
Let's go through the steps on how to extend an official Ubuntu image. These
|
This [script](../scripts/build-custom-image.sh) can be used to generate a custom image (needs to be modified per architecture/distribution image)
|
||||||
steps can be applied to other distributions (with a few changes regarding
|
|
||||||
package management).
|
|
||||||
|
|
||||||
### Get latest Ubuntu cloud image
|
|
||||||
|
|
||||||
```bash
|
|
||||||
wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
|
|
||||||
```
|
|
||||||
|
|
||||||
### Check the file format is QCOW2
|
|
||||||
|
|
||||||
```bash
|
|
||||||
file focal-server-cloudimg-amd64.img
|
|
||||||
focal-server-cloudimg-amd64.img: QEMU QCOW2 Image (v2), 2361393152 bytes
|
|
||||||
```
|
|
||||||
|
|
||||||
### Convert QCOW2 into RAW
|
|
||||||
|
|
||||||
```bash
|
|
||||||
qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-amd64.img focal-server-cloudimg-amd64.raw
|
|
||||||
```
|
|
||||||
|
|
||||||
### Identify the Linux partition
|
|
||||||
|
|
||||||
The goal is to mount the image rootfs so that it can be modified as needed.
|
|
||||||
That's why we need to identify where the Linux filesystem partition is located
|
|
||||||
in the image.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo fdisk -l focal-server-cloudimg-amd64.raw
|
|
||||||
Disk focal-server-cloudimg-amd64.raw: 2.2 GiB, 2361393152 bytes, 4612096 sectors
|
|
||||||
Units: sectors of 1 * 512 = 512 bytes
|
|
||||||
Sector size (logical/physical): 512 bytes / 512 bytes
|
|
||||||
I/O size (minimum/optimal): 512 bytes / 512 bytes
|
|
||||||
Disklabel type: gpt
|
|
||||||
Disk identifier: A1171ABA-2BEA-4218-A467-1B2B607E5953
|
|
||||||
|
|
||||||
Device Start End Sectors Size Type
|
|
||||||
focal-server-cloudimg-amd64.raw1 227328 4612062 4384735 2.1G Linux filesystem
|
|
||||||
focal-server-cloudimg-amd64.raw14 2048 10239 8192 4M BIOS boot
|
|
||||||
focal-server-cloudimg-amd64.raw15 10240 227327 217088 106M EFI System
|
|
||||||
|
|
||||||
Partition table entries are not in disk order.
|
|
||||||
```
|
|
||||||
|
|
||||||
### Mount the Linux partition
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p /mnt
|
|
||||||
sudo mount -o loop,offset=$((227328 * 512)) focal-server-cloudimg-amd64.raw /mnt
|
|
||||||
```
|
|
||||||
|
|
||||||
### Set up DNS
|
|
||||||
|
|
||||||
The next step describes changing the root directory to the rootfs contained by
|
|
||||||
the cloud image. For DNS to work in the root directory, you will need to first bind-mount
|
|
||||||
the host `/etc/resolv.conf` onto the mounted linux partition of the cloud image.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo mount -o bind /etc/resolv.conf /mnt/etc/resolv.conf
|
|
||||||
```
|
|
||||||
|
|
||||||
### Change root directory
|
|
||||||
|
|
||||||
Changing the root directory will allow us to install new packages to the rootfs
|
|
||||||
contained by the cloud image.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo chroot /mnt
|
|
||||||
mount -t proc proc /proc
|
|
||||||
mount -t devpts devpts /dev/pts
|
|
||||||
```
|
|
||||||
|
|
||||||
### Install needed packages
|
|
||||||
|
|
||||||
In the context Cloud Hypervisor's integration tests, we need several utilities.
|
|
||||||
Here is the way to install them for a Ubuntu image. This step is specific to
|
|
||||||
Ubuntu distributions.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
apt update
|
|
||||||
apt install fio iperf iperf3 socat stress cpuid tpm2-tools
|
|
||||||
```
|
|
||||||
|
|
||||||
### Remove counterproductive packages
|
|
||||||
|
|
||||||
* snapd:
|
|
||||||
|
|
||||||
This prevents snapd from trying to mount squashfs filesystem when the kernel
|
|
||||||
might not support it. This might be the case when the image is used with direct
|
|
||||||
kernel boot. This step is specific to Ubuntu distributions.
|
|
||||||
|
|
||||||
* pollinate:
|
|
||||||
|
|
||||||
Remove this package which can fail and lead to the SSH daemon failing to start.
|
|
||||||
See #2113 for details.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
apt remove --purge snapd pollinate
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Cleanup the image
|
|
||||||
|
|
||||||
Leave no trace in the image before unmounting its content.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
umount /dev/pts
|
|
||||||
umount /proc
|
|
||||||
history -c
|
|
||||||
exit
|
|
||||||
umount /mnt/etc/resolv.conf
|
|
||||||
umount /mnt
|
|
||||||
```
|
|
||||||
|
|
||||||
### Rename the image
|
|
||||||
|
|
||||||
Renaming is important to identify this is a modified image.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mv focal-server-cloudimg-amd64.raw focal-server-cloudimg-amd64-custom-$(date "+%Y%m%d")-0.raw
|
|
||||||
```
|
|
||||||
|
|
||||||
The `-0` is the revision and is only necessary to change if multiple images are
|
|
||||||
updated on the same day.
|
|
||||||
|
|
||||||
### Create QCOW2 from RAW
|
|
||||||
|
|
||||||
Last step is to create the QCOW2 image back from the modified image.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
qemu-img convert -p -f raw -O qcow2 focal-server-cloudimg-amd64-custom-$(date "+%Y%m%d")-0.raw focal-server-cloudimg-amd64-custom-$(date "+%Y%m%d")-0.qcow2
|
|
||||||
```
|
|
||||||
|
|
||||||
## Switch CI to use the new image
|
## Switch CI to use the new image
|
||||||
|
|
||||||
### Upload to Azure storage
|
### Upload to Azure storage
|
||||||
|
|
||||||
The next step is to update both images (QCOW2 and RAW) stored as part of the
|
A command like the following can be used to upload the image:
|
||||||
Azure storage account, replacing them with the newly created ones. This will
|
|
||||||
make these new images available from the integration tests. This is usually
|
`az storage blob upload --account-name cloudhypervisorstorages --container-name '$web' --name jammy-server-cloudimg-amd64-custom-20241017-0.qcow2 --file jammy-server-cloudimg-amd64-custom-20241017-0.qcow2 --sas-token <redacted>`
|
||||||
achieved through the web interface.
|
|
||||||
|
|
||||||
### Update integration tests
|
### Update integration tests
|
||||||
|
|
||||||
@@ -161,169 +27,4 @@ Update all references to the previous image name to the new one.
|
|||||||
|
|
||||||
## NVIDIA image for VFIO bare-metal CI
|
## NVIDIA image for VFIO bare-metal CI
|
||||||
|
|
||||||
Here we are going to describe how to create a cloud image that contains the
|
Uncomment "VFIO_CUSTOM_IMAGE" in the script listed above to generate the custom image used for the VFIO worker.
|
||||||
necessary NVIDIA drivers for our VFIO bare-metal CI.
|
|
||||||
|
|
||||||
### Download base image
|
|
||||||
|
|
||||||
We usually start from one of the custom cloud image we have previously created
|
|
||||||
but we can use a stock cloud image as well.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
wget https://ch-images.azureedge.net/jammy-server-cloudimg-amd64-custom-20230119-0.raw
|
|
||||||
mv jammy-server-cloudimg-amd64-custom-20230119-0.raw jammy-server-cloudimg-amd64-nvidia.raw
|
|
||||||
```
|
|
||||||
|
|
||||||
### Extend the image size
|
|
||||||
|
|
||||||
The NVIDIA drivers consume lots of space, which is why we must resize the image
|
|
||||||
before we proceed any further.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
qemu-img resize jammy-server-cloudimg-amd64-nvidia.raw 5G
|
|
||||||
```
|
|
||||||
|
|
||||||
### Resize the partition
|
|
||||||
|
|
||||||
We use `parted` for fixing the GPT after the image was resized, as well as for
|
|
||||||
resizing the `Linux` partition.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo parted jammy-server-cloudimg-amd64-nvidia.raw
|
|
||||||
|
|
||||||
(parted) print
|
|
||||||
Warning: Not all of the space available to jammy-server-cloudimg-amd64-nvidia.raw
|
|
||||||
appears to be used, you can fix the GPT to use all of the space (an extra 5873664
|
|
||||||
blocks) or continue with the current setting?
|
|
||||||
Fix/Ignore? Fix
|
|
||||||
Model: (file)
|
|
||||||
Disk jammy-server-cloudimg-amd64-nvidia.raw: 5369MB
|
|
||||||
Sector size (logical/physical): 512B/512B
|
|
||||||
Partition Table: gpt
|
|
||||||
Disk Flags:
|
|
||||||
|
|
||||||
Number Start End Size File system Name Flags
|
|
||||||
14 1049kB 5243kB 4194kB bios_grub
|
|
||||||
15 5243kB 116MB 111MB fat32 boot, esp
|
|
||||||
1 116MB 2361MB 2245MB ext4
|
|
||||||
|
|
||||||
(parted) resizepart 1 5369MB
|
|
||||||
(parted) print
|
|
||||||
Model: (file)
|
|
||||||
Disk jammy-server-cloudimg-amd64-nvidia.raw: 5369MB
|
|
||||||
Sector size (logical/physical): 512B/512B
|
|
||||||
Partition Table: gpt
|
|
||||||
Disk Flags:
|
|
||||||
|
|
||||||
Number Start End Size File system Name Flags
|
|
||||||
14 1049kB 5243kB 4194kB bios_grub
|
|
||||||
15 5243kB 116MB 111MB fat32 boot, esp
|
|
||||||
1 116MB 5369MB 5252MB ext4
|
|
||||||
|
|
||||||
(parted) quit
|
|
||||||
```
|
|
||||||
|
|
||||||
### Create a macvtap interface
|
|
||||||
|
|
||||||
Rely on the following [documentation](macvtap-bridge.md) to set up a
|
|
||||||
macvtap interface to provide your VM with proper connectivity.
|
|
||||||
|
|
||||||
### Boot the image
|
|
||||||
|
|
||||||
It is particularly important to boot with a `cloud-init` disk attached to the
|
|
||||||
VM as it will automatically resize the Linux `ext4` filesystem based on the
|
|
||||||
partition that we have previously resized.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./cloud-hypervisor \
|
|
||||||
--kernel hypervisor-fw \
|
|
||||||
--disk path=focal-server-cloudimg-amd64-nvidia.raw path=/tmp/ubuntu-cloudinit.img \
|
|
||||||
--cpus boot=4 \
|
|
||||||
--memory size=4G \
|
|
||||||
--net fd=3,mac=$mac 3<>$"$tapdevice"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Bring up connectivity
|
|
||||||
|
|
||||||
If your network has a DHCP server, run the following from your VM
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo dhclient
|
|
||||||
```
|
|
||||||
|
|
||||||
But if that's not the case, let's give it an IP manually (the IP addresses
|
|
||||||
depend on your actual network) and set the DNS server IP address as well.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo ip addr add 192.168.2.10/24 dev ens4
|
|
||||||
sudo ip link set up dev ens4
|
|
||||||
sudo ip route add default via 192.168.2.1
|
|
||||||
sudo resolvectl dns ens4 8.8.8.8
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Check connectivity and update the image
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt update
|
|
||||||
sudo apt upgrade
|
|
||||||
```
|
|
||||||
|
|
||||||
### Install NVIDIA drivers
|
|
||||||
|
|
||||||
The following steps and commands are referenced from the
|
|
||||||
[NVIDIA official documentation](https://docs.nvidia.com/datacenter/tesla/tesla-installation-notes/index.html#ubuntu-lts)
|
|
||||||
about Tesla compute cards.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
distribution=$(. /etc/os-release;echo $ID$VERSION_ID | sed -e 's/\.//g')
|
|
||||||
wget https://developer.download.nvidia.com/compute/cuda/repos/$distribution/x86_64/cuda-keyring_1.0-1_all.deb
|
|
||||||
sudo dpkg -i cuda-keyring_1.0-1_all.deb
|
|
||||||
sudo apt-key del 7fa2af80
|
|
||||||
sudo apt update
|
|
||||||
sudo apt -y install cuda-drivers
|
|
||||||
```
|
|
||||||
|
|
||||||
### Check the `nvidia-smi` tool
|
|
||||||
|
|
||||||
Quickly validate that you can find and run the `nvidia-smi` command from your
|
|
||||||
VM. At this point it should fail given no NVIDIA card has been passed through
|
|
||||||
the VM, therefore no NVIDIA driver is loaded.
|
|
||||||
|
|
||||||
### Workaround LA57 reboot issue
|
|
||||||
|
|
||||||
Add `reboot=a` to `GRUB_CMDLINE_LINUX` in `etc/default/grub` so that the VM
|
|
||||||
will be booted with the ACPI reboot type. This resolves a reboot issue when
|
|
||||||
running on 5-level paging systems.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo vim /etc/default/grub
|
|
||||||
sudo update-grub
|
|
||||||
sudo reboot
|
|
||||||
```
|
|
||||||
|
|
||||||
### Remove previous logins
|
|
||||||
|
|
||||||
Since our integration tests rely on past logins to count the number of reboots,
|
|
||||||
we must ensure to clear the list.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
>/var/log/lastlog
|
|
||||||
>/var/log/wtmp
|
|
||||||
>/var/log/btmp
|
|
||||||
```
|
|
||||||
|
|
||||||
### Clear history
|
|
||||||
|
|
||||||
```
|
|
||||||
history -c
|
|
||||||
rm /home/cloud/.bash_history
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reset cloud-init
|
|
||||||
|
|
||||||
This is mandatory as we want `cloud-init` provisioning to work again when a new
|
|
||||||
VM will be booted with this image.
|
|
||||||
|
|
||||||
```
|
|
||||||
sudo cloud-init clean
|
|
||||||
```
|
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
# `cloud-hypervisor` debug IO ports
|
# `cloud-hypervisor` debug IO ports
|
||||||
|
|
||||||
When running x86 guests, `cloud-hypervisor` provides different kinds of debug ports:
|
When running x86 guests, `cloud-hypervisor` provides different kinds of debug ports:
|
||||||
- [`0x80` debug port](https://www.intel.com/content/www/us/en/support/articles/000005500/boards-and-kits.html)
|
|
||||||
- Debug console (by default at `0xe9`).
|
- [`0x80` debug port](https://web.archive.org/web/20211028033025/https://www.intel.com/content/www/us/en/support/articles/000005500/boards-and-kits.html)
|
||||||
|
- Debug console (by default at `0xe9`).
|
||||||
- Firmware debug port at `0x402`.
|
- Firmware debug port at `0x402`.
|
||||||
|
|
||||||
All of them can be used to trace user-defined guest events and all of them can
|
All of them can be used to trace user-defined guest events and all of them can
|
||||||
@@ -25,13 +26,13 @@ for debugging specific components of the guest software stack. When logging a
|
|||||||
write of one of those codes to the debug port, `cloud-hypervisor` adds a
|
write of one of those codes to the debug port, `cloud-hypervisor` adds a
|
||||||
pre-defined string to the logs.
|
pre-defined string to the logs.
|
||||||
|
|
||||||
| Code Range | Component | Log string |
|
| Code Range | Component | Log string |
|
||||||
| ---------------- | ----------- | ------------ |
|
| ---------------- | ---------- | ------------ |
|
||||||
| `0x00` to `0x1f` | Firmware | `Firmware` |
|
| `0x00` to `0x1f` | Firmware | `Firmware` |
|
||||||
| `0x20` to `0x3f` | Bootloader | `Bootloader` |
|
| `0x20` to `0x3f` | Bootloader | `Bootloader` |
|
||||||
| `0x40` to `0x5f` | Kernel | `Kernel` |
|
| `0x40` to `0x5f` | Kernel | `Kernel` |
|
||||||
| `0x60` to `0x7f` | Userspace | `Userspace` |
|
| `0x60` to `0x7f` | Userspace | `Userspace` |
|
||||||
| `0x80` to `0xff` | Custom | `Custom` |
|
| `0x80` to `0xff` | Custom | `Custom` |
|
||||||
|
|
||||||
One typical use case is guest boot time measurement and tracing. By writing
|
One typical use case is guest boot time measurement and tracing. By writing
|
||||||
different values to the debug I/O port at different boot process steps, the
|
different values to the debug I/O port at different boot process steps, the
|
||||||
@@ -71,8 +72,8 @@ cloud-hypervisor: 403.499628ms: DEBUG:vmm/src/vm.rs:510 -- [Debug I/O port: Firm
|
|||||||
|
|
||||||
### Debug console port
|
### Debug console port
|
||||||
|
|
||||||
The debug console is inspired by QEMU and Bochs, which have a similar feature.
|
The debug console is inspired by QEMU and Bochs, which have a similar feature.
|
||||||
By default, the I/O port `0xe9` is used. This port can be configured like a
|
By default, the I/O port `0xe9` is used. This port can be configured like a
|
||||||
console. Thus, it can print to a tty, a file, or a pty, for example.
|
console. Thus, it can print to a tty, a file, or a pty, for example.
|
||||||
|
|
||||||
### Firmware debug port
|
### Firmware debug port
|
||||||
@@ -83,7 +84,7 @@ it. The firmware debug port only prints to stdout.
|
|||||||
## When do I need these ports?
|
## When do I need these ports?
|
||||||
|
|
||||||
The ports are on the one hand interesting for firmware or kernel developers, as
|
The ports are on the one hand interesting for firmware or kernel developers, as
|
||||||
they provide an easy way to print debug information from within a guest.
|
they provide an easy way to print debug information from within a guest.
|
||||||
Furthermore, you can patch "normal" software to measure certain events, such as
|
Furthermore, you can patch "normal" software to measure certain events, such as
|
||||||
the boot time of a guest.
|
the boot time of a guest.
|
||||||
|
|
||||||
@@ -91,6 +92,6 @@ the boot time of a guest.
|
|||||||
|
|
||||||
The `0x80` debug port and the port of the firmware debug device are always
|
The `0x80` debug port and the port of the firmware debug device are always
|
||||||
available. The debug console must be activated via the command line, but
|
available. The debug console must be activated via the command line, but
|
||||||
provides more configuration options.
|
provides more configuration options.
|
||||||
|
|
||||||
You can use different ports for different aspect of your logging messages.
|
You can use different ports for different aspect of your logging messages.
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ the host kernel. The required Linux and KVM changes can be found in the
|
|||||||
[KVM SGX Tree](https://github.com/intel/kvm-sgx).
|
[KVM SGX Tree](https://github.com/intel/kvm-sgx).
|
||||||
|
|
||||||
Utilizing SGX in the guest requires a kernel/OS with SGX support, e.g. a kernel
|
Utilizing SGX in the guest requires a kernel/OS with SGX support, e.g. a kernel
|
||||||
built using the [SGX Linux Development Tree](https://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-sgx.git)
|
since release 5.11, see
|
||||||
|
[here](https://www.intel.com/content/www/us/en/developer/tools/software-guard-extensions/linux-overview.html)
|
||||||
or the [KVM SGX Tree](https://github.com/intel/kvm-sgx). Running KVM SGX as the
|
or the [KVM SGX Tree](https://github.com/intel/kvm-sgx). Running KVM SGX as the
|
||||||
guest kernel allows nested virtualization of SGX.
|
guest kernel allows nested virtualization of SGX.
|
||||||
|
|
||||||
For more information about SGX, please refer to the [SGX Homepage](https://software.intel.com/sgx).
|
For more information about SGX, please refer to the [SGX Homepage](https://www.intel.com/content/www/us/en/developer/tools/software-guard-extensions/linux-overview.html).
|
||||||
|
|
||||||
For more information about SGX SDK and how to test SGX, please refer to the
|
For more information about SGX SDK and how to test SGX, please refer to the
|
||||||
following [instructions](https://github.com/intel/linux-sgx).
|
following [instructions](https://github.com/intel/linux-sgx).
|
||||||
|
|||||||
@@ -4,21 +4,21 @@ Intel® Trust Domain Extensions (Intel® TDX) is an Intel technology designed to
|
|||||||
isolate virtual machines from the VMM, hypervisor and any other software on the
|
isolate virtual machines from the VMM, hypervisor and any other software on the
|
||||||
host platform. Here are some useful links:
|
host platform. Here are some useful links:
|
||||||
|
|
||||||
* [TDX Homepage](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-trust-domain-extensions.html):
|
- [TDX Homepage](https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/overview.html):
|
||||||
more information about TDX technical aspects, design and specification
|
more information about TDX technical aspects, design and specification
|
||||||
|
|
||||||
* [KVM TDX tree](https://github.com/intel/tdx/tree/kvm): the required
|
- [KVM TDX tree](https://github.com/intel/tdx/tree/kvm): the required
|
||||||
Linux kernel changes for the host side
|
Linux kernel changes for the host side
|
||||||
|
|
||||||
* [Guest TDX tree](https://github.com/intel/tdx/tree/guest): the Linux
|
- [Guest TDX tree](https://github.com/intel/tdx/tree/guest): the Linux
|
||||||
kernel changes for the guest side
|
kernel changes for the guest side
|
||||||
|
|
||||||
* [EDK2 project](https://github.com/tianocore/edk2): the TDVF firmware
|
- [EDK2 project](https://github.com/tianocore/edk2): the TDVF firmware
|
||||||
|
|
||||||
* [Confidential Containers project](https://github.com/confidential-containers/td-shim):
|
- [Confidential Containers project](https://github.com/confidential-containers/td-shim):
|
||||||
the TDShim firmware
|
the TDShim firmware
|
||||||
|
|
||||||
* [TDX Tools](https://github.com/intel/tdx-tools): a collection of tools
|
- [TDX Linux](https://github.com/intel/tdx-linux): a collection of tools
|
||||||
and scripts to setup TDX environment for testing purpose (such as
|
and scripts to setup TDX environment for testing purpose (such as
|
||||||
installing required packages on the host, creating guest images, and
|
installing required packages on the host, creating guest images, and
|
||||||
building the custom Linux kernel for TDX host and guest)
|
building the custom Linux kernel for TDX host and guest)
|
||||||
@@ -27,17 +27,13 @@ more information about TDX technical aspects, design and specification
|
|||||||
|
|
||||||
It is required to use a machine with TDX enabled in hardware and
|
It is required to use a machine with TDX enabled in hardware and
|
||||||
with the host OS compiled from the [KVM TDX tree](https://github.com/intel/tdx/tree/kvm).
|
with the host OS compiled from the [KVM TDX tree](https://github.com/intel/tdx/tree/kvm).
|
||||||
The host environment can also be setup with the [TDX Tools](https://github.com/intel/tdx-tools).
|
The host environment can also be setup with the [TDX Linux](https://github.com/intel/tdx-linux).
|
||||||
|
|
||||||
Cloud Hypervisor can run TDX VM (Trust Domain) by loading a TD firmware ([TDVF](https://github.com/tianocore/edk2)),
|
Cloud Hypervisor can run TDX VM (Trust Domain) by loading a TD firmware ([TDVF](https://github.com/tianocore/edk2)),
|
||||||
which will then load the guest kernel from the image. The image must be custom
|
which will then load the guest kernel from the image. The image must be custom
|
||||||
as it must include a kernel built from the [Guest TDX tree](https://github.com/intel/tdx/tree/guest).
|
as it must include a kernel built from the [Guest TDX tree](https://github.com/intel/tdx/tree/guest).
|
||||||
Cloud Hypervisor can also boot a TDX VM with direct kernel boot using [TDshim](https://github.com/confidential-containers/td-shim).
|
Cloud Hypervisor can also boot a TDX VM with direct kernel boot using [TDshim](https://github.com/confidential-containers/td-shim).
|
||||||
The custom Linux kernel for the guest can be built with the [TDX Tools](https://github.com/intel/tdx-tools).
|
The custom Linux kernel for the guest can be built with the [TDX Linux](https://github.com/intel/tdx-linux).
|
||||||
|
|
||||||
> **Note**
|
|
||||||
> The latest version of custom host and guest kernel being tested is
|
|
||||||
> from [TDX Tools - 2023ww01](https://github.com/intel/tdx-tools/commits/2023ww01).
|
|
||||||
|
|
||||||
### TDVF
|
### TDVF
|
||||||
|
|
||||||
@@ -110,6 +106,7 @@ direct kernel boot, which is useful for containers use cases.
|
|||||||
|
|
||||||
To build TDShim from source, it is required to install `Rust`, `NASM`,
|
To build TDShim from source, it is required to install `Rust`, `NASM`,
|
||||||
and `LLVM` first. The TDshim can be build as follows:
|
and `LLVM` first. The TDshim can be build as follows:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/confidential-containers/td-shim
|
git clone https://github.com/confidential-containers/td-shim
|
||||||
cd td-shim
|
cd td-shim
|
||||||
@@ -126,13 +123,14 @@ cargo image --release
|
|||||||
|
|
||||||
If debug logs from the TDShim is needed, here are the alternative
|
If debug logs from the TDShim is needed, here are the alternative
|
||||||
commands:
|
commands:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo image
|
cargo image
|
||||||
```
|
```
|
||||||
|
|
||||||
And run a TDX VM by providing the firmware previously built, along with a guest
|
And run a TDX VM by providing the firmware previously built, along with a guest
|
||||||
kernel built from the [Guest TDX tree](https://github.com/intel/tdx/tree/guest)
|
kernel built from the [Guest TDX tree](https://github.com/intel/tdx/tree/guest)
|
||||||
or the [TDX Tools](https://github.com/intel/tdx-tools).
|
or the [TDX Linux](https://github.com/intel/tdx-linux).
|
||||||
The appropriate kernel boot options must be provided through the `--cmdline`
|
The appropriate kernel boot options must be provided through the `--cmdline`
|
||||||
option as well.
|
option as well.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# I/O Throttling
|
# I/O Throttling
|
||||||
|
|
||||||
Cloud Hypervisor now supports I/O throttling on virtio-block and virtio-net
|
Cloud Hypervisor now supports I/O throttling on virtio-block and virtio-net
|
||||||
devices. This support is based on the [`rate-limiter` module](https://github.com/firecracker-microvm/firecracker/tree/master/src/rate_limiter)
|
devices. This support is based on the [`rate-limiter` module](https://github.com/firecracker-microvm/firecracker/tree/7a1231b141e958d15d5b2c079dd5e0880528b4b0/src/rate_limiter)
|
||||||
from Firecracker. This document explains the user interface of this
|
from Firecracker. This document explains the user interface of this
|
||||||
feature, and highlights some internal implementations that can help users
|
feature, and highlights some internal implementations that can help users
|
||||||
better understand the expected behavior of I/O throttling in practice.
|
better understand the expected behavior of I/O throttling in practice.
|
||||||
@@ -44,10 +44,12 @@ generally advisable to keep `bw/ops_refill_time` larger than `100 ms`
|
|||||||
expectation ("refill-rate").
|
expectation ("refill-rate").
|
||||||
|
|
||||||
## Rate Limit Groups
|
## Rate Limit Groups
|
||||||
|
|
||||||
It is possible to throttle the aggregate bandwidth or operations
|
It is possible to throttle the aggregate bandwidth or operations
|
||||||
of multiple virtio-blk devices using a `rate_limit_group`. virtio-blk devices may be
|
of multiple virtio-blk devices using a `rate_limit_group`. virtio-blk devices may be
|
||||||
dynamically added and removed from a `rate_limit_group`. The following example
|
dynamically added and removed from a `rate_limit_group`. The following example
|
||||||
demonstrates how to throttle the aggregate bandwidth of two disks to 10 MiB/s.
|
demonstrates how to throttle the aggregate bandwidth of two disks to 10 MiB/s.
|
||||||
|
|
||||||
```
|
```
|
||||||
--disk path=disk0.raw,rate_limit_group=group0 \
|
--disk path=disk0.raw,rate_limit_group=group0 \
|
||||||
path=disk1.raw,rate_limit_group=group0 \
|
path=disk1.raw,rate_limit_group=group0 \
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
# Live Migration
|
# Live Migration
|
||||||
|
|
||||||
This document gives two examples of how to use the live migration
|
This document gives examples of how to use the live migration support
|
||||||
support in Cloud Hypervisor:
|
in Cloud Hypervisor:
|
||||||
|
|
||||||
1. local migration - migrating between two VMs running on the same
|
1. local migration - migrating a VM from one Cloud Hypervisor instance to another on the same machine;
|
||||||
machine;
|
1. remote migration - migrating a VM between two machines;
|
||||||
1. nested-vm migration - migrating between two nested VMs whose host VMs
|
|
||||||
are running on the same machine.
|
> :warning: These examples place sockets /tmp. This is done for
|
||||||
|
> simplicity and should not be done in production.
|
||||||
|
|
||||||
## Local Migration (Suitable for Live Upgrade of VMM)
|
## Local Migration (Suitable for Live Upgrade of VMM)
|
||||||
|
|
||||||
Launch the source VM (on the host machine):
|
Launch the source VM (on the host machine):
|
||||||
```bash
|
|
||||||
|
```console
|
||||||
$ target/release/cloud-hypervisor
|
$ target/release/cloud-hypervisor
|
||||||
--kernel ~/workloads/vmlinux \
|
--kernel ~/workloads/vmlinux \
|
||||||
--disk path=~/workloads/focal.raw \
|
--disk path=~/workloads/focal.raw \
|
||||||
@@ -20,17 +23,20 @@ $ target/release/cloud-hypervisor
|
|||||||
```
|
```
|
||||||
|
|
||||||
Launch the destination VM from the same directory (on the host machine):
|
Launch the destination VM from the same directory (on the host machine):
|
||||||
```bash
|
|
||||||
|
```console
|
||||||
$ target/release/cloud-hypervisor --api-socket=/tmp/api2
|
$ target/release/cloud-hypervisor --api-socket=/tmp/api2
|
||||||
```
|
```
|
||||||
|
|
||||||
Get ready for receiving migration for the destination VM (on the host machine):
|
Get ready for receiving migration for the destination VM (on the host machine):
|
||||||
```bash
|
|
||||||
|
```console
|
||||||
$ target/release/ch-remote --api-socket=/tmp/api2 receive-migration unix:/tmp/sock
|
$ target/release/ch-remote --api-socket=/tmp/api2 receive-migration unix:/tmp/sock
|
||||||
```
|
```
|
||||||
|
|
||||||
Start to send migration for the source VM (on the host machine):
|
Start to send migration for the source VM (on the host machine):
|
||||||
```bash
|
|
||||||
|
```console
|
||||||
$ target/release/ch-remote --api-socket=/tmp/api1 send-migration --local unix:/tmp/sock
|
$ target/release/ch-remote --api-socket=/tmp/api1 send-migration --local unix:/tmp/sock
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -38,102 +44,139 @@ When the above commands completed, the source VM should be successfully
|
|||||||
migrated to the destination VM. Now the destination VM is running while
|
migrated to the destination VM. Now the destination VM is running while
|
||||||
the source VM is terminated gracefully.
|
the source VM is terminated gracefully.
|
||||||
|
|
||||||
## Nested-VM Migration
|
## Remote Migration
|
||||||
|
|
||||||
Launch VM 1 (on the host machine) with an extra virtio-blk device for
|
In this example, we will migrate a VM from one machine (`src`) to
|
||||||
exposing a guest image for the nested source VM:
|
another (`dst`) across the network. To keep it simple, we will use a
|
||||||
> Note: the example below also attached an additional virtio-blk device
|
minimal VM setup without storage.
|
||||||
> with a dummy image for testing purpose (which is optional).
|
|
||||||
```bash
|
### Preparation
|
||||||
$ head -c 1M < /dev/urandom > tmp.img # create a dummy image for testing
|
|
||||||
$ sudo /target/release/cloud-hypervisor \
|
Make sure that `src` and `dst` can reach each other via the
|
||||||
|
network. You should be able to ping each machine. Also each machine
|
||||||
|
should have an open TCP port.
|
||||||
|
|
||||||
|
You will need a kernel and initramfs for a minimal Linux system. For
|
||||||
|
this example, we will use the Debian netboot image.
|
||||||
|
|
||||||
|
Place the kernel and initramfs into the _same directory_ on both
|
||||||
|
machines. This is important for the migration to succeed. We will use
|
||||||
|
`/var/images`:
|
||||||
|
|
||||||
|
```console
|
||||||
|
src $ export DEBIAN=https://ftp.debian.org/debian/dists/stable/main/installer-amd64/current/images/netboot/debian-installer/amd64
|
||||||
|
src $ mkdir -p /var/images
|
||||||
|
src $ curl $DEBIAN/linux > /var/images/linux
|
||||||
|
src $ curl $DEBIAN/initrd.gz > /var/images/initrd
|
||||||
|
```
|
||||||
|
|
||||||
|
Repeat the above steps on the destination host.
|
||||||
|
|
||||||
|
### Unix Socket Migration
|
||||||
|
|
||||||
|
If Unix socket is selected for migration, we can tunnel traffic through "socat".
|
||||||
|
|
||||||
|
#### Starting the Receiver VM
|
||||||
|
|
||||||
|
On the receiver side, we prepare an empty VM:
|
||||||
|
|
||||||
|
```console
|
||||||
|
dst $ cloud-hypervisor --api-socket /tmp/api
|
||||||
|
```
|
||||||
|
|
||||||
|
In a different terminal, configure the VM as a migration target:
|
||||||
|
|
||||||
|
```console
|
||||||
|
dst $ ch-remote --api-socket=/tmp/api receive-migration unix:/tmp/sock
|
||||||
|
```
|
||||||
|
|
||||||
|
In yet another terminal, forward TCP connections to the Unix domain socket:
|
||||||
|
|
||||||
|
```console
|
||||||
|
dst $ socat TCP-LISTEN:{port},reuseaddr UNIX-CLIENT:/tmp/sock
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Starting the Sender VM
|
||||||
|
|
||||||
|
Let's start the VM on the source machine:
|
||||||
|
|
||||||
|
```console
|
||||||
|
src $ cloud-hypervisor \
|
||||||
--serial tty --console off \
|
--serial tty --console off \
|
||||||
--cpus boot=1 --memory size=512M \
|
--cpus boot=2 --memory size=4G \
|
||||||
--kernel vmlinux \
|
--kernel /var/images/linux \
|
||||||
--cmdline "root=/dev/vda1 console=ttyS0" \
|
--initramfs /var/images/initrd \
|
||||||
--disk path=focal-1.raw path=focal-nested.raw path=tmp.img\
|
--cmdline "console=ttyS0" \
|
||||||
--net ip=192.168.101.1
|
--api-socket /tmp/api
|
||||||
```
|
```
|
||||||
|
|
||||||
Launch VM 2 (on the host machine) with an extra virtio-blk device for
|
After a few seconds the VM should be up and you can interact with it.
|
||||||
exposing the same guest image for the nested destination VM:
|
|
||||||
```bash
|
#### Performing the Migration
|
||||||
$ sudo /target/release/cloud-hypervisor \
|
|
||||||
|
First, we start `socat`:
|
||||||
|
|
||||||
|
```console
|
||||||
|
src $ socat UNIX-LISTEN:/tmp/sock,reuseaddr TCP:{dst}:{port}
|
||||||
|
```
|
||||||
|
|
||||||
|
> Replace {dst}:{port} with the actual IP address and port of your destination host.
|
||||||
|
|
||||||
|
Then we kick-off the migration itself:
|
||||||
|
|
||||||
|
```console
|
||||||
|
src $ ch-remote --api-socket=/tmp/api send-migration unix:/tmp/sock
|
||||||
|
```
|
||||||
|
|
||||||
|
When the above commands completed, the VM should be successfully
|
||||||
|
migrated to the destination machine without interrupting the workload.
|
||||||
|
|
||||||
|
### TCP Socket Migration
|
||||||
|
|
||||||
|
If TCP socket is selected for migration, we need to consider migrating
|
||||||
|
in a trusted network.
|
||||||
|
|
||||||
|
#### Starting the Receiver VM
|
||||||
|
|
||||||
|
On the receiver side, we prepare an empty VM:
|
||||||
|
|
||||||
|
```console
|
||||||
|
dst $ cloud-hypervisor --api-socket /tmp/api
|
||||||
|
```
|
||||||
|
|
||||||
|
In a different terminal, prepare to receive the migration:
|
||||||
|
|
||||||
|
```console
|
||||||
|
dst $ ch-remote --api-socket=/tmp/api receive-migration tcp:0.0.0.0:{port}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Starting the Sender VM
|
||||||
|
|
||||||
|
Let's start the VM on the source machine:
|
||||||
|
|
||||||
|
```console
|
||||||
|
src $ cloud-hypervisor \
|
||||||
--serial tty --console off \
|
--serial tty --console off \
|
||||||
--cpus boot=1 --memory size=512M \
|
--cpus boot=2 --memory size=4G \
|
||||||
--kernel vmlinux \
|
--kernel /var/images/linux \
|
||||||
--cmdline "root=/dev/vda1 console=ttyS0" \
|
--initramfs /var/images/initrd \
|
||||||
--disk path=focal-2.raw path=focal-nested.raw path=tmp.img\
|
--cmdline "console=ttyS0" \
|
||||||
--net ip=192.168.102.1
|
--api-socket /tmp/api
|
||||||
```
|
```
|
||||||
|
|
||||||
Launch the nested source VM (inside the guest OS of the VM 1) :
|
After a few seconds the VM should be up and you can interact with it.
|
||||||
```bash
|
|
||||||
vm-1:~$ sudo ./cloud-hypervisor \
|
|
||||||
--serial tty --console off \
|
|
||||||
--memory size=128M \
|
|
||||||
--kernel vmlinux \
|
|
||||||
--cmdline "console=ttyS0 root=/dev/vda1" \
|
|
||||||
--disk path=/dev/vdb path=/dev/vdc \
|
|
||||||
--api-socket=/tmp/api1 \
|
|
||||||
--net ip=192.168.100.1
|
|
||||||
vm-1:~$ # setup the guest network if needed
|
|
||||||
vm-1:~$ sudo ip addr add 192.168.101.2/24 dev ens4
|
|
||||||
vm-1:~$ sudo ip link set up dev ens4
|
|
||||||
vm-1:~$ sudo ip r add default via 192.168.101.1
|
|
||||||
```
|
|
||||||
Optional: Run the guest workload below (on the guest OS of the nested source VM),
|
|
||||||
which performs intensive virtio-blk operations. Now the console of the nested
|
|
||||||
source VM should repeatedly print `"equal"`, and our goal is migrating
|
|
||||||
this VM and the running workload without interruption.
|
|
||||||
```bash
|
|
||||||
#/bin/bash
|
|
||||||
|
|
||||||
# On the guest OS of the nested source VM
|
#### Performing the Migration
|
||||||
|
|
||||||
input="/dev/vdb"
|
Initiate the Migration over TCP:
|
||||||
result=$(md5sum $input)
|
|
||||||
tmp=$(md5sum $input)
|
|
||||||
|
|
||||||
while [[ "$result" == "$tmp" ]]
|
```console
|
||||||
do
|
src $ ch-remote --api-socket=/tmp/api send-migration tcp:{dst}:{port}
|
||||||
echo "equal"
|
|
||||||
tmp=$(md5sum $input)
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "not equal"
|
|
||||||
echo "result = $result"
|
|
||||||
echo "tmp = $tmp"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Launch the nested destination VM (inside the guest OS of the VM 2):
|
> Replace {dst}:{port} with the actual IP address and port of your destination host.
|
||||||
```bash
|
|
||||||
vm-2:~$ sudo ./cloud-hypervisor --api-socket=/tmp/api2
|
|
||||||
vm-2:~$ # setup the guest network with the following commands if needed
|
|
||||||
vm-2:~$ sudo ip addr add 192.168.102.2/24 dev ens4
|
|
||||||
vm-2:~$ sudo ip link set up dev ens4
|
|
||||||
vm-2:~$ sudo ip r add default via 192.168.102.1
|
|
||||||
vm-2:~$ ping 192.168.101.2 # This should succeed
|
|
||||||
```
|
|
||||||
> Note: If the above ping failed, please check the iptables rule on the
|
|
||||||
> host machine, e.g. whether the policy for the `FORWARD` chain is set
|
|
||||||
> to `DROP` (which is the default setting configured by Docker).
|
|
||||||
|
|
||||||
Get ready for receiving migration for the nested destination VM (inside
|
After completing the above commands, the source VM will be migrated to
|
||||||
the guest OS of the VM 2):
|
the destination host and continue running there. The source VM instance
|
||||||
```bash
|
will terminate normally. All ongoing processes and connections within
|
||||||
vm-2:~$ sudo ./ch-remote --api-socket=/tmp/api2 receive-migration unix:/tmp/sock2
|
the VM should remain intact after the migration.
|
||||||
vm-2:~$ sudo socat TCP-LISTEN:6000,reuseaddr UNIX-CLIENT:/tmp/sock2
|
|
||||||
```
|
|
||||||
|
|
||||||
Start to send migration for the nested source VM (inside the guest OS of
|
|
||||||
the VM 1):
|
|
||||||
```bash
|
|
||||||
vm-1:~$ sudo socat UNIX-LISTEN:/tmp/sock1,reuseaddr TCP:192.168.102.2:6000
|
|
||||||
vm-1:~$ sudo ./ch-remote --api-socket=/tmp/api1 send-migration unix:/tmp/sock1
|
|
||||||
```
|
|
||||||
|
|
||||||
When the above commands completed, the source VM should be successfully
|
|
||||||
migrated to the destination VM without interrupting our testing guest
|
|
||||||
workload. Now the destination VM is running the testing guest workload
|
|
||||||
while the source VM is terminated gracefully.
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
The target audience of this document is both:
|
The target audience of this document is both:
|
||||||
|
|
||||||
* Developers who want to understand what log level to use and when,
|
- Developers who want to understand what log level to use and when,
|
||||||
* Users who want to debug issues with running their workloads in Cloud Hypervisor
|
- Users who want to debug issues with running their workloads in Cloud Hypervisor
|
||||||
|
|
||||||
## Control
|
## Control
|
||||||
|
|
||||||
@@ -29,7 +29,6 @@ A typical example of where this level of message should be generated is during a
|
|||||||
|
|
||||||
The user should investigate the meaning of this warning and take steps to ensure the correct functionality.
|
The user should investigate the meaning of this warning and take steps to ensure the correct functionality.
|
||||||
|
|
||||||
|
|
||||||
### `info!()`
|
### `info!()`
|
||||||
|
|
||||||
Use `-v` to enable.
|
Use `-v` to enable.
|
||||||
@@ -40,4 +39,4 @@ This level is for the benefit of developers. It should be used for sporadic and
|
|||||||
|
|
||||||
Use `-vv` to enable.
|
Use `-vv` to enable.
|
||||||
|
|
||||||
For the most verbose of logging messages. It is acceptable to "spam" the log with repeated invocations of the same message. This level of logging would be combined with `--log-file`.
|
For the most verbose of logging messages. It is acceptable to "spam" the log with repeated invocations of the same message. This level of logging would be combined with `--log-file`.
|
||||||
|
|||||||
168
docs/riscv.md
Normal file
168
docs/riscv.md
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
# How to build and test Cloud Hypervisor on riscv64
|
||||||
|
|
||||||
|
This document introduces how to build and test Cloud Hypervisor on `riscv64`.
|
||||||
|
All instructions here are tested with Ubuntu 24.04.2 as the host OS.
|
||||||
|
|
||||||
|
## Hardware requirements
|
||||||
|
|
||||||
|
- riscv64 servers (recommended) or development boards equipped with the AIA
|
||||||
|
(Advance Interrupt Architecture) interrupt controller.
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
We create a folder to build and run Cloud Hypervisor at `$HOME/cloud-hypervisor`
|
||||||
|
|
||||||
|
```console
|
||||||
|
export CLOUDH=$HOME/cloud-hypervisor
|
||||||
|
mkdir $CLOUDH
|
||||||
|
```
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
You need to install some prerequisite packages to build and test Cloud Hypervisor.
|
||||||
|
|
||||||
|
### Tools
|
||||||
|
|
||||||
|
```console
|
||||||
|
# Install rust tool chain
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||||
|
# Install the tools used for building guest kernel, EDK2 and converting guest disk
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install git build-essential m4 bison flex uuid-dev qemu-utils
|
||||||
|
```
|
||||||
|
|
||||||
|
### Building Cloud Hypervisor
|
||||||
|
|
||||||
|
```console
|
||||||
|
pushd $CLOUDH
|
||||||
|
git clone https://github.com/cloud-hypervisor/cloud-hypervisor.git
|
||||||
|
cd cloud-hypervisor
|
||||||
|
cargo build
|
||||||
|
popd
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disk image
|
||||||
|
|
||||||
|
Download the Ubuntu cloud image and convert the image type.
|
||||||
|
|
||||||
|
```console
|
||||||
|
pushd $CLOUDH
|
||||||
|
wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-riscv64.img
|
||||||
|
qemu-img convert -p -f qcow2 -O raw jammy-server-cloudimg-riscv64.img jammy-server-cloudimg-riscv64.raw
|
||||||
|
popd
|
||||||
|
```
|
||||||
|
|
||||||
|
## Direct-kernel booting
|
||||||
|
|
||||||
|
### Building kernel
|
||||||
|
|
||||||
|
```console
|
||||||
|
pushd $CLOUDH
|
||||||
|
git clone --depth 1 "https://github.com/cloud-hypervisor/linux.git" -b ch-6.12.8
|
||||||
|
cd linux
|
||||||
|
make ch_defconfig
|
||||||
|
make -j `nproc`
|
||||||
|
popd
|
||||||
|
```
|
||||||
|
|
||||||
|
### Booting the guest VM
|
||||||
|
|
||||||
|
```console
|
||||||
|
pushd $CLOUDH
|
||||||
|
sudo $CLOUDH/cloud-hypervisor/target/debug/cloud-hypervisor \
|
||||||
|
--kernel $CLOUDH/linux/arch/riscv64/boot/Image \
|
||||||
|
--disk path=jammy-server-cloudimg-riscv64.raw \
|
||||||
|
--cmdline "console=hvc0 root=/dev/vda rw" \
|
||||||
|
--cpus boot=1 \
|
||||||
|
--memory size=1024M \
|
||||||
|
--seccomp false \
|
||||||
|
--log-file boot.log -vv
|
||||||
|
popd
|
||||||
|
```
|
||||||
|
|
||||||
|
## Virtualized Development Setup
|
||||||
|
|
||||||
|
Since there are few RISC-V development boards on the market and not
|
||||||
|
many details about the AIA interrupt controller featured in product listings,
|
||||||
|
QEMU is a popular and viable choice for creating a RISC-V development environment.
|
||||||
|
Below are the steps used to create a QEMU virtual machine that can be used for
|
||||||
|
cloud-hypervisor RISC-V development:
|
||||||
|
|
||||||
|
### Install Dependencies
|
||||||
|
|
||||||
|
```console
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install opensbi qemu-system-misc u-boot-qemu
|
||||||
|
```
|
||||||
|
|
||||||
|
### Download and Build QEMU (>=v9.2.0)
|
||||||
|
|
||||||
|
Older versions of QEMU may not have support for the AIA
|
||||||
|
interrupt controller.
|
||||||
|
|
||||||
|
```console
|
||||||
|
wget https://download.qemu.org/qemu-10.0.0.tar.xz
|
||||||
|
tar xvJf qemu-10.0.0.tar.xz
|
||||||
|
cd qemu-10.0.0
|
||||||
|
./configure --target-list=riscv64-softmmu
|
||||||
|
make -j $(nproc)
|
||||||
|
sudo make install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Download Ubuntu Server Image
|
||||||
|
|
||||||
|
At the time of writing, the best results have been seen with
|
||||||
|
the Ubuntu 24.10 (Oracular) server image. Ex:
|
||||||
|
|
||||||
|
```console
|
||||||
|
wget https://cdimage.ubuntu.com/releases/oracular/release/ubuntu-24.10-preinstalled-server-riscv64.img.xz
|
||||||
|
xz -dk ubuntu-24.10-preinstalled-server-riscv64.img.xz
|
||||||
|
```
|
||||||
|
|
||||||
|
### (Optional) Resize Disk
|
||||||
|
|
||||||
|
If you would like a larger disk, you can resize it now.
|
||||||
|
|
||||||
|
```console
|
||||||
|
qemu-img resize -f raw <ubuntu-image> +5G
|
||||||
|
```
|
||||||
|
|
||||||
|
### Boot VM
|
||||||
|
|
||||||
|
Note the inclusion of the AIA interrupt controller in the
|
||||||
|
invocation.
|
||||||
|
|
||||||
|
```console
|
||||||
|
qemu-system-riscv64 \
|
||||||
|
-machine virt,aia=aplic-imsic \
|
||||||
|
-nographic -m 1G -smp 8 \
|
||||||
|
-kernel /usr/lib/u-boot/qemu-riscv64_smode/uboot.elf \
|
||||||
|
-device virtio-rng-pci \
|
||||||
|
-device virtio-net-device,netdev=eth0 -netdev user,id=eth0 \
|
||||||
|
-drive file=<ubuntu-image>,format=raw,if=virtio
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install KVM Kernel Module Within VM
|
||||||
|
KVM is not enabled within the VM by default, so we must enable
|
||||||
|
it manually.
|
||||||
|
|
||||||
|
```console
|
||||||
|
sudo modprobe kvm
|
||||||
|
```
|
||||||
|
|
||||||
|
From this point, you can continue with the above steps from the beginning.
|
||||||
|
|
||||||
|
### Sources
|
||||||
|
|
||||||
|
https://risc-v-getting-started-guide.readthedocs.io/en/latest/linux-qemu.html
|
||||||
|
|
||||||
|
https://canonical-ubuntu-boards.readthedocs-hosted.com/en/latest/how-to/qemu-riscv/#using-the-live-server-image
|
||||||
|
|
||||||
|
https://www.qemu.org/docs/master/specs/riscv-aia.html
|
||||||
|
|
||||||
|
## Known limitations
|
||||||
|
|
||||||
|
- Direct kernel boot only
|
||||||
|
- `64-bit Linux` guest OS only
|
||||||
|
- For more details, see
|
||||||
|
[here](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/6978).
|
||||||
@@ -5,8 +5,8 @@ name = "event_monitor"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
flume = "0.11.0"
|
flume = "0.11.1"
|
||||||
libc = "0.2.155"
|
libc = "0.2.167"
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.20.2"
|
||||||
serde = { version = "1.0.197", features = ["derive", "rc"] }
|
serde = { version = "1.0.208", features = ["derive", "rc"] }
|
||||||
serde_json = "1.0.120"
|
serde_json = { workspace = true }
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use serde::Serialize;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@@ -13,6 +11,9 @@ use std::os::unix::io::AsRawFd;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
static MONITOR: OnceCell<MonitorHandle> = OnceCell::new();
|
static MONITOR: OnceCell<MonitorHandle> = OnceCell::new();
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
|||||||
442
fuzz/Cargo.lock
generated
442
fuzz/Cargo.lock
generated
@@ -1,13 +1,13 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "acpi_tables"
|
name = "acpi_tables"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#ca1a473fe73cdd8eb49c1449faad7aaac06f32c2"
|
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#e08a3f0b0a59b98859dbf59f5aa7fd4d2eb4018a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy",
|
"zerocopy 0.8.24",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -33,9 +33,9 @@ checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-parse"
|
name = "anstyle-parse"
|
||||||
version = "0.2.4"
|
version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
|
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"utf8parse",
|
"utf8parse",
|
||||||
]
|
]
|
||||||
@@ -61,15 +61,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.86"
|
version = "1.0.95"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arbitrary"
|
name = "arbitrary"
|
||||||
version = "1.3.2"
|
version = "1.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arc-swap"
|
name = "arc-swap"
|
||||||
@@ -89,7 +89,7 @@ dependencies = [
|
|||||||
"linux-loader",
|
"linux-loader",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror 2.0.9",
|
||||||
"uuid",
|
"uuid",
|
||||||
"vm-fdt",
|
"vm-fdt",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
@@ -103,6 +103,17 @@ version = "1.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitfield-struct"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2be5a46ba01b60005ae2c51a36a29cfe134bcacae2dd5cedcd4615fbaad1494b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
@@ -111,9 +122,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.6.0"
|
version = "2.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block"
|
name = "block"
|
||||||
@@ -126,7 +137,7 @@ dependencies = [
|
|||||||
"remain",
|
"remain",
|
||||||
"serde",
|
"serde",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror",
|
"thiserror 2.0.9",
|
||||||
"uuid",
|
"uuid",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
"virtio-queue",
|
"virtio-queue",
|
||||||
@@ -195,13 +206,16 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
|||||||
name = "cloud-hypervisor-fuzz"
|
name = "cloud-hypervisor-fuzz"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
"block",
|
"block",
|
||||||
"devices",
|
"devices",
|
||||||
"epoll",
|
"epoll",
|
||||||
|
"hypervisor",
|
||||||
"libc",
|
"libc",
|
||||||
"libfuzzer-sys",
|
"libfuzzer-sys",
|
||||||
"linux-loader",
|
"linux-loader",
|
||||||
"micro_http",
|
"micro_http",
|
||||||
|
"mshv-bindings",
|
||||||
"net_util",
|
"net_util",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"seccompiler",
|
"seccompiler",
|
||||||
@@ -221,6 +235,16 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "concat-idents"
|
||||||
|
version = "1.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f76990911f2267d837d9d0ad060aa63aaad170af40904b29461734c339030d4d"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc-any"
|
name = "crc-any"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
@@ -278,7 +302,7 @@ dependencies = [
|
|||||||
"acpi_tables",
|
"acpi_tables",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arch",
|
"arch",
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.0",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"event_monitor",
|
"event_monitor",
|
||||||
"hypervisor",
|
"hypervisor",
|
||||||
@@ -287,7 +311,7 @@ dependencies = [
|
|||||||
"num_enum",
|
"num_enum",
|
||||||
"pci",
|
"pci",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror 2.0.9",
|
||||||
"tpm",
|
"tpm",
|
||||||
"vm-allocator",
|
"vm-allocator",
|
||||||
"vm-device",
|
"vm-device",
|
||||||
@@ -322,7 +346,7 @@ version = "4.3.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79"
|
checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.0",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -351,9 +375,9 @@ checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flume"
|
name = "flume"
|
||||||
version = "0.11.0"
|
version = "0.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
@@ -381,11 +405,11 @@ checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gdbstub"
|
name = "gdbstub"
|
||||||
version = "0.7.1"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6341b3480afbb34eaefc7f92713bc92f2d83e338aaa1c44192f9c2956f4a4903"
|
checksum = "fbcc892208d6998fb57e7c3e05883def66f8130924bba066beb0cfe71566a9f6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.0",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"log",
|
"log",
|
||||||
"managed",
|
"managed",
|
||||||
@@ -395,9 +419,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gdbstub_arch"
|
name = "gdbstub_arch"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4e3b1357bd3203fc09a6601327ae0ab38865d14231d0b65d3143f5762cc7977d"
|
checksum = "328a9e9425db13770d0d11de6332a608854266e44c53d12776be7b4aa427e3de"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gdbstub",
|
"gdbstub",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@@ -412,10 +436,22 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi 0.13.3+wasi-0.2.2",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.5"
|
version = "0.14.5"
|
||||||
@@ -427,17 +463,35 @@ name = "hypervisor"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"arc-swap",
|
||||||
|
"bitfield-struct",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
"cfg-if",
|
||||||
|
"concat-idents",
|
||||||
|
"iced-x86",
|
||||||
"kvm-bindings",
|
"kvm-bindings",
|
||||||
"kvm-ioctls",
|
"kvm-ioctls",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
"mshv-bindings",
|
||||||
|
"open-enum",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"thiserror",
|
"thiserror 2.0.9",
|
||||||
"vfio-ioctls",
|
"vfio-ioctls",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
|
"zerocopy 0.8.24",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iced-x86"
|
||||||
|
version = "1.21.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c447cff8c7f384a7d4f741cfcff32f75f3ad02b406432e8d6c878d56b1edf6b"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -448,9 +502,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.3.0"
|
version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
|
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
@@ -488,22 +542,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kvm-bindings"
|
name = "kvm-bindings"
|
||||||
version = "0.8.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a82e7e8725a39a0015e511a46cc1f7d90cecc180db1610c4d0d4339a9e48bd21"
|
checksum = "fa4933174d0cc4b77b958578cd45784071cc5ae212c2d78fbd755aaaa6dfa71a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
"zerocopy",
|
"zerocopy 0.7.35",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kvm-ioctls"
|
name = "kvm-ioctls"
|
||||||
version = "0.17.0"
|
version = "0.19.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bedae2ca4a531bebe311abaf9691f5cc14eaa21475243caa2e39c43bb872947d"
|
checksum = "e013ae7fcd2c6a8f384104d16afe7ea02969301ea2bb2a56e44b011ebc907cab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.0",
|
||||||
"kvm-bindings",
|
"kvm-bindings",
|
||||||
"libc",
|
"libc",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
@@ -511,20 +565,26 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "landlock"
|
name = "landlock"
|
||||||
version = "0.4.0"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dafb8a4afee64f167eb2b52d32f0eea002e41a7a6450e68c799c8ec3a81a634c"
|
checksum = "18738c5d4c7fae6727a96adb94722ef7ce82f3eafea0a11777e258a93816537e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"enumflags2",
|
"enumflags2",
|
||||||
"libc",
|
"libc",
|
||||||
"thiserror",
|
"thiserror 1.0.64",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "lazy_static"
|
||||||
version = "0.2.155"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.169"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libfuzzer-sys"
|
name = "libfuzzer-sys"
|
||||||
@@ -539,9 +599,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-loader"
|
name = "linux-loader"
|
||||||
version = "0.11.0"
|
version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eb68dd3452f25a8defaf0ae593509cff0c777683e4d8924f59ac7c5f89267a83"
|
checksum = "870c3814345f050991f99869417779f6062542bcf4ed81db7a1b926ad1306638"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
]
|
]
|
||||||
@@ -570,26 +630,40 @@ checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.2"
|
version = "2.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "micro_http"
|
name = "micro_http"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#ef43cef7162a55a6790d528a5e76b4fe2da22de0"
|
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#4f621532e81ee2ad096a9c9592fdacc40d19de48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mshv-bindings"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "577073a0abbf515d17bfe96ca2ce49c44a68454d4179e95ce1244e858a9ebd4e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"num_enum",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"vmm-sys-util",
|
||||||
|
"zerocopy 0.8.24",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nanorand"
|
name = "nanorand"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.2.15",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -604,13 +678,13 @@ name = "net_util"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"epoll",
|
"epoll",
|
||||||
"getrandom",
|
"getrandom 0.3.1",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"net_gen",
|
"net_gen",
|
||||||
"rate_limiter",
|
"rate_limiter",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror 2.0.9",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
"virtio-queue",
|
"virtio-queue",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
@@ -650,9 +724,29 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.19.0"
|
version = "1.20.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "open-enum"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2eb2508143a400b3361812094d987dd5adc81f0f5294a46491be648d6c94cab5"
|
||||||
|
dependencies = [
|
||||||
|
"open-enum-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "open-enum-derive"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d1296fab5231654a5aec8bf9e87ba4e3938c502fc4c3c0425a00084c78944be"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "option_parser"
|
name = "option_parser"
|
||||||
@@ -674,7 +768,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror 2.0.9",
|
||||||
"vfio-bindings",
|
"vfio-bindings",
|
||||||
"vfio-ioctls",
|
"vfio-ioctls",
|
||||||
"vfio_user",
|
"vfio_user",
|
||||||
@@ -686,32 +780,71 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-crate"
|
name = "ppv-lite86"
|
||||||
version = "3.1.0"
|
version = "0.2.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284"
|
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy 0.7.35",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-crate"
|
||||||
|
version = "3.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"toml_edit",
|
"toml_edit",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.86"
|
version = "1.0.92"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.36"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||||
|
dependencies = [
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
"zerocopy 0.8.24",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rate_limiter"
|
name = "rate_limiter"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -719,7 +852,7 @@ dependencies = [
|
|||||||
"epoll",
|
"epoll",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"thiserror",
|
"thiserror 2.0.9",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -748,27 +881,27 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "seccompiler"
|
name = "seccompiler"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "345a3e4dddf721a478089d4697b83c6c0a8f5bf16086f6c13397e4534eb6e2e5"
|
checksum = "a4ae55de56877481d112a559bbc12667635fdaf5e005712fd4e2b2fa50ffc884"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.203"
|
version = "1.0.208"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.203"
|
version = "1.0.208"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -777,11 +910,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.120"
|
version = "1.0.128"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
|
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
|
"memchr",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@@ -861,9 +995,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.72"
|
version = "2.0.95"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
|
checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -872,18 +1006,38 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.63"
|
version = "1.0.64"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl 1.0.64",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl 2.0.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.63"
|
version = "1.0.64"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -898,9 +1052,9 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.21.1"
|
version = "0.22.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
|
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
@@ -916,7 +1070,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"net_gen",
|
"net_gen",
|
||||||
"thiserror",
|
"thiserror 2.0.9",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -933,9 +1087,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.12"
|
version = "1.0.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
@@ -945,17 +1099,30 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.10.0"
|
version = "1.15.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
|
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.3.1",
|
||||||
|
"rand",
|
||||||
|
"uuid-macro-internal",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid-macro-internal"
|
||||||
|
version = "1.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9521621447c21497fac206ffe6e9f642f977c4f82eeba9201055f64884d9cb01"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vfio-bindings"
|
name = "vfio-bindings"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#da8c5b67095fb70f5ef237ca63d316219888f015"
|
source = "git+https://github.com/rust-vmm/vfio?branch=main#b135b8305c2cc8ec333e0cf77a780445cc98dcee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
@@ -963,14 +1130,14 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "vfio-ioctls"
|
name = "vfio-ioctls"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#da8c5b67095fb70f5ef237ca63d316219888f015"
|
source = "git+https://github.com/rust-vmm/vfio?branch=main#b135b8305c2cc8ec333e0cf77a780445cc98dcee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"kvm-bindings",
|
"kvm-bindings",
|
||||||
"kvm-ioctls",
|
"kvm-ioctls",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"thiserror",
|
"thiserror 1.0.64",
|
||||||
"vfio-bindings",
|
"vfio-bindings",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
@@ -979,7 +1146,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "vfio_user"
|
name = "vfio_user"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/rust-vmm/vfio-user?branch=main#a1f6e52829e069b6d698b2cfeecac742e4653186"
|
source = "git+https://github.com/rust-vmm/vfio-user?branch=main#3febcdd3fa2531623865663ca1721e1962ed9979"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -987,7 +1154,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror 1.0.64",
|
||||||
"vfio-bindings",
|
"vfio-bindings",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
@@ -995,21 +1162,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vhost"
|
name = "vhost"
|
||||||
version = "0.11.0"
|
version = "0.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/rust-vmm/vhost?rev=d983ae0#d983ae07f78663b7d24059667376992460b571a2"
|
||||||
checksum = "6be08d1166d41a78861ad50212ab3f9eca0729c349ac3a7a8f557c62406b87cc"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.0",
|
||||||
"libc",
|
"libc",
|
||||||
|
"uuid",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "virtio-bindings"
|
name = "virtio-bindings"
|
||||||
version = "0.2.2"
|
version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "878bcb1b2812a10c30d53b0ed054999de3d98f25ece91fc173973f9c57aaae86"
|
checksum = "1711e61c00f8cb450bd15368152a1e37a12ef195008ddc7d0f4812f9e2b30a68"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "virtio-devices"
|
name = "virtio-devices"
|
||||||
@@ -1032,7 +1199,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"serial_buffer",
|
"serial_buffer",
|
||||||
"thiserror",
|
"thiserror 2.0.9",
|
||||||
"vhost",
|
"vhost",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
"virtio-queue",
|
"virtio-queue",
|
||||||
@@ -1046,9 +1213,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "virtio-queue"
|
name = "virtio-queue"
|
||||||
version = "0.12.0"
|
version = "0.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07d8406e7250c934462de585d8f2d2781c31819bca1fbb7c5e964ca6bbaabfe8"
|
checksum = "872e2f3fbd70a7e6f01689720cce3d5c2c5efe52b484dd07b674246ada0e9a8d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
@@ -1072,7 +1239,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"hypervisor",
|
"hypervisor",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror 2.0.9",
|
||||||
"vfio-ioctls",
|
"vfio-ioctls",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
@@ -1081,17 +1248,17 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "vm-fdt"
|
name = "vm-fdt"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#982fb8d9c8cd7f53520d7e304b39ff307fa3a641"
|
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#ef5bd734f5f66fb07722d766981adbc915f0d941"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vm-memory"
|
name = "vm-memory"
|
||||||
version = "0.14.1"
|
version = "0.16.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c3aba5064cc5f6f7740cddc8dae34d2d9a311cac69b60d942af7f3ab8fc49f4"
|
checksum = "f1720e7240cdc739f935456eb77f370d7e9b2a3909204da1e2b47bef1137a013"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"libc",
|
"libc",
|
||||||
"thiserror",
|
"thiserror 1.0.64",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1102,7 +1269,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror 2.0.9",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1123,7 +1290,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"arch",
|
"arch",
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.0",
|
||||||
"block",
|
"block",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -1149,7 +1316,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"serial_buffer",
|
"serial_buffer",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"thiserror",
|
"thiserror 2.0.9",
|
||||||
"tracer",
|
"tracer",
|
||||||
"uuid",
|
"uuid",
|
||||||
"vfio-ioctls",
|
"vfio-ioctls",
|
||||||
@@ -1162,7 +1329,7 @@ dependencies = [
|
|||||||
"vm-migration",
|
"vm-migration",
|
||||||
"vm-virtio",
|
"vm-virtio",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
"zerocopy",
|
"zerocopy 0.8.24",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1184,20 +1351,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasi"
|
||||||
version = "0.2.92"
|
version = "0.13.3+wasi-0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen-rt",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.93"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
"wasm-bindgen-macro",
|
"wasm-bindgen-macro",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-backend"
|
name = "wasm-bindgen-backend"
|
||||||
version = "0.2.92"
|
version = "0.2.93"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
|
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"log",
|
"log",
|
||||||
@@ -1210,9 +1387,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.92"
|
version = "0.2.93"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
|
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
@@ -1220,9 +1397,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.92"
|
version = "0.2.93"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -1233,9 +1410,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.92"
|
version = "0.2.93"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
@@ -1334,13 +1511,22 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.5.40"
|
version = "0.6.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
|
checksum = "c52ac009d615e79296318c1bcce2d422aaca15ad08515e344feeda07df67a587"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-rt"
|
||||||
|
version = "0.33.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.9.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.35"
|
version = "0.7.35"
|
||||||
@@ -1348,7 +1534,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"zerocopy-derive",
|
"zerocopy-derive 0.7.35",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive 0.8.24",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1361,3 +1556,14 @@ dependencies = [
|
|||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|||||||
@@ -9,24 +9,29 @@ version = "0.0.0"
|
|||||||
cargo-fuzz = true
|
cargo-fuzz = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["mshv_emulator"]
|
||||||
igvm = []
|
igvm = []
|
||||||
|
mshv_emulator = ["hypervisor/mshv_emulator"]
|
||||||
pvmemcontrol = []
|
pvmemcontrol = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
arbitrary = "1.4.1"
|
||||||
block = { path = "../block" }
|
block = { path = "../block" }
|
||||||
devices = { path = "../devices" }
|
devices = { path = "../devices" }
|
||||||
epoll = "4.3.3"
|
epoll = "4.3.3"
|
||||||
|
hypervisor = { path = "../hypervisor", features = ["mshv_emulator"] }
|
||||||
libc = "0.2.155"
|
libc = "0.2.155"
|
||||||
libfuzzer-sys = "0.4.7"
|
libfuzzer-sys = "0.4.7"
|
||||||
linux-loader = { version = "0.11.0", features = ["bzimage", "elf", "pe"] }
|
linux-loader = { version = "0.13.0", features = ["bzimage", "elf", "pe"] }
|
||||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||||
|
mshv-bindings = "0.5.0"
|
||||||
net_util = { path = "../net_util" }
|
net_util = { path = "../net_util" }
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
seccompiler = "0.4.0"
|
seccompiler = "0.5.0"
|
||||||
virtio-devices = { path = "../virtio-devices" }
|
virtio-devices = { path = "../virtio-devices" }
|
||||||
virtio-queue = "0.12.0"
|
virtio-queue = "0.14.0"
|
||||||
vm-device = { path = "../vm-device" }
|
vm-device = { path = "../vm-device" }
|
||||||
vm-memory = "0.14.1"
|
vm-memory = "0.16.0"
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
vmm = { path = "../vmm", features = ["guest_debug"] }
|
vmm = { path = "../vmm", features = ["guest_debug"] }
|
||||||
@@ -126,8 +131,21 @@ name = "vhdx"
|
|||||||
path = "fuzz_targets/vhdx.rs"
|
path = "fuzz_targets/vhdx.rs"
|
||||||
test = false
|
test = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
doc = false
|
||||||
|
name = "vsock"
|
||||||
|
path = "fuzz_targets/vsock.rs"
|
||||||
|
test = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
doc = false
|
doc = false
|
||||||
name = "watchdog"
|
name = "watchdog"
|
||||||
path = "fuzz_targets/watchdog.rs"
|
path = "fuzz_targets/watchdog.rs"
|
||||||
test = false
|
test = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
doc = false
|
||||||
|
name = "x86emul"
|
||||||
|
path = "fuzz_targets/x86emul.rs"
|
||||||
|
required-features = ["mshv_emulator"]
|
||||||
|
test = false
|
||||||
|
|||||||
@@ -4,13 +4,15 @@
|
|||||||
|
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
use libfuzzer_sys::fuzz_target;
|
|
||||||
use seccompiler::SeccompAction;
|
|
||||||
use std::os::unix::io::{AsRawFd, FromRawFd};
|
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use libfuzzer_sys::{fuzz_target, Corpus};
|
||||||
|
use seccompiler::SeccompAction;
|
||||||
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||||
use virtio_queue::{Queue, QueueT};
|
use virtio_queue::{Queue, QueueT};
|
||||||
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
use vm_memory::bitmap::AtomicBitmap;
|
||||||
|
use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic};
|
||||||
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||||
|
|
||||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||||
@@ -35,11 +37,11 @@ const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
|||||||
// Used ring size
|
// Used ring size
|
||||||
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
|
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
|
||||||
|
|
||||||
fuzz_target!(|bytes| {
|
fuzz_target!(|bytes: &[u8]| -> Corpus {
|
||||||
if bytes.len() < QUEUE_DATA_SIZE * QUEUE_NUM
|
if bytes.len() < QUEUE_DATA_SIZE * QUEUE_NUM
|
||||||
|| bytes.len() > (QUEUE_DATA_SIZE * QUEUE_NUM + MEM_SIZE)
|
|| bytes.len() > (QUEUE_DATA_SIZE * QUEUE_NUM + MEM_SIZE)
|
||||||
{
|
{
|
||||||
return;
|
return Corpus::Reject;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut balloon = virtio_devices::Balloon::new(
|
let mut balloon = virtio_devices::Balloon::new(
|
||||||
@@ -59,7 +61,7 @@ fuzz_target!(|bytes| {
|
|||||||
// Setup the guest memory with the input bytes
|
// Setup the guest memory with the input bytes
|
||||||
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
|
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
|
||||||
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
||||||
return;
|
return Corpus::Reject;
|
||||||
}
|
}
|
||||||
let guest_memory = GuestMemoryAtomic::new(mem);
|
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||||
|
|
||||||
@@ -106,6 +108,8 @@ fuzz_target!(|bytes| {
|
|||||||
|
|
||||||
// Wait for the events to finish and balloon device worker thread to return
|
// Wait for the events to finish and balloon device worker thread to return
|
||||||
balloon.wait_for_epoll_threads();
|
balloon.wait_for_epoll_threads();
|
||||||
|
|
||||||
|
Corpus::Keep
|
||||||
});
|
});
|
||||||
|
|
||||||
pub struct NoopVirtioInterrupt {}
|
pub struct NoopVirtioInterrupt {}
|
||||||
@@ -118,7 +122,7 @@ impl VirtioInterrupt for NoopVirtioInterrupt {
|
|||||||
|
|
||||||
macro_rules! align {
|
macro_rules! align {
|
||||||
($n:expr, $align:expr) => {{
|
($n:expr, $align:expr) => {{
|
||||||
(($n + $align - 1) / $align) * $align
|
$n.div_ceil($align) * $align
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,20 +8,22 @@
|
|||||||
|
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
use block::{async_io::DiskFile, raw_sync::RawFileDiskSync};
|
use std::collections::BTreeMap;
|
||||||
use libfuzzer_sys::fuzz_target;
|
|
||||||
use seccompiler::SeccompAction;
|
|
||||||
use std::ffi;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io;
|
|
||||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::{ffi, io};
|
||||||
|
|
||||||
|
use block::async_io::DiskFile;
|
||||||
|
use block::raw_sync::RawFileDiskSync;
|
||||||
|
use libfuzzer_sys::{fuzz_target, Corpus};
|
||||||
|
use seccompiler::SeccompAction;
|
||||||
use virtio_devices::{Block, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
use virtio_devices::{Block, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||||
use virtio_queue::{Queue, QueueT};
|
use virtio_queue::{Queue, QueueT};
|
||||||
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
use vm_memory::bitmap::AtomicBitmap;
|
||||||
|
use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic};
|
||||||
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||||
|
|
||||||
@@ -38,9 +40,9 @@ const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
|||||||
// Guest physical address for used ring (requires to 4-bytes aligned)
|
// Guest physical address for used ring (requires to 4-bytes aligned)
|
||||||
const USED_RING_ADDR: u64 = (AVAIL_RING_ADDR + AVAIL_RING_SIZE + 3) & !3_u64;
|
const USED_RING_ADDR: u64 = (AVAIL_RING_ADDR + AVAIL_RING_SIZE + 3) & !3_u64;
|
||||||
|
|
||||||
fuzz_target!(|bytes| {
|
fuzz_target!(|bytes: &[u8]| -> Corpus {
|
||||||
if bytes.len() < QUEUE_DATA_SIZE || bytes.len() > (QUEUE_DATA_SIZE + MEM_SIZE) {
|
if bytes.len() < QUEUE_DATA_SIZE || bytes.len() > (QUEUE_DATA_SIZE + MEM_SIZE) {
|
||||||
return;
|
return Corpus::Reject;
|
||||||
}
|
}
|
||||||
|
|
||||||
let queue_data = &bytes[..QUEUE_DATA_SIZE];
|
let queue_data = &bytes[..QUEUE_DATA_SIZE];
|
||||||
@@ -74,7 +76,7 @@ fuzz_target!(|bytes| {
|
|||||||
// Setup the guest memory with the input bytes
|
// Setup the guest memory with the input bytes
|
||||||
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
|
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
|
||||||
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
||||||
return;
|
return Corpus::Reject;
|
||||||
}
|
}
|
||||||
let guest_memory = GuestMemoryAtomic::new(mem);
|
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||||
|
|
||||||
@@ -94,6 +96,8 @@ fuzz_target!(|bytes| {
|
|||||||
|
|
||||||
// Wait for the events to finish and block device worker thread to return
|
// Wait for the events to finish and block device worker thread to return
|
||||||
block.wait_for_epoll_threads();
|
block.wait_for_epoll_threads();
|
||||||
|
|
||||||
|
Corpus::Keep
|
||||||
});
|
});
|
||||||
|
|
||||||
fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> {
|
fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> {
|
||||||
|
|||||||
@@ -5,14 +5,14 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
use devices::legacy::Cmos;
|
use devices::legacy::Cmos;
|
||||||
use libc::EFD_NONBLOCK;
|
use libc::EFD_NONBLOCK;
|
||||||
use libfuzzer_sys::fuzz_target;
|
use libfuzzer_sys::{fuzz_target, Corpus};
|
||||||
use vm_device::BusDevice;
|
use vm_device::BusDevice;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
fuzz_target!(|bytes| {
|
fuzz_target!(|bytes: &[u8]| -> Corpus {
|
||||||
// Need at least 16 bytes for the test
|
// Need at least 16 bytes for the test
|
||||||
if bytes.len() < 16 {
|
if bytes.len() < 16 {
|
||||||
return;
|
return Corpus::Reject;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut below_4g = [0u8; 8];
|
let mut below_4g = [0u8; 8];
|
||||||
@@ -46,4 +46,6 @@ fuzz_target!(|bytes| {
|
|||||||
cmos.write(0, offset, &data);
|
cmos.write(0, offset, &data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Corpus::Keep
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,22 +4,24 @@
|
|||||||
|
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
use libfuzzer_sys::fuzz_target;
|
|
||||||
use seccompiler::SeccompAction;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use libfuzzer_sys::{fuzz_target, Corpus};
|
||||||
|
use seccompiler::SeccompAction;
|
||||||
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||||
use virtio_queue::{Queue, QueueT};
|
use virtio_queue::{Queue, QueueT};
|
||||||
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
use vm_memory::bitmap::AtomicBitmap;
|
||||||
|
use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic};
|
||||||
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||||
|
|
||||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||||
|
|
||||||
macro_rules! align {
|
macro_rules! align {
|
||||||
($n:expr, $align:expr) => {{
|
($n:expr, $align:expr) => {{
|
||||||
(($n + $align - 1) / $align) * $align
|
$n.div_ceil($align) * $align
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,12 +54,12 @@ const QUEUE_BYTES_SIZE: usize = align!(
|
|||||||
DESC_TABLE_ALIGN_SIZE
|
DESC_TABLE_ALIGN_SIZE
|
||||||
) as usize;
|
) as usize;
|
||||||
|
|
||||||
fuzz_target!(|bytes| {
|
fuzz_target!(|bytes: &[u8]| -> Corpus {
|
||||||
if bytes.len() < (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + CONSOLE_INPUT_SIZE
|
if bytes.len() < (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + CONSOLE_INPUT_SIZE
|
||||||
|| bytes.len()
|
|| bytes.len()
|
||||||
> (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + CONSOLE_INPUT_SIZE + MEM_SIZE
|
> (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + CONSOLE_INPUT_SIZE + MEM_SIZE
|
||||||
{
|
{
|
||||||
return;
|
return Corpus::Reject;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (pipe_rx, mut pipe_tx) = create_pipe().unwrap();
|
let (pipe_rx, mut pipe_tx) = create_pipe().unwrap();
|
||||||
@@ -66,7 +68,7 @@ fuzz_target!(|bytes| {
|
|||||||
memfd_create(&std::ffi::CString::new("fuzz_console_output").unwrap()).unwrap(),
|
memfd_create(&std::ffi::CString::new("fuzz_console_output").unwrap()).unwrap(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let endpoint = virtio_devices::Endpoint::FilePair(output, pipe_rx);
|
let endpoint = virtio_devices::Endpoint::FilePair(Arc::new(output), Arc::new(pipe_rx));
|
||||||
|
|
||||||
let (mut console, _) = virtio_devices::Console::new(
|
let (mut console, _) = virtio_devices::Console::new(
|
||||||
"fuzzer_console".to_owned(),
|
"fuzzer_console".to_owned(),
|
||||||
@@ -106,10 +108,10 @@ fuzz_target!(|bytes| {
|
|||||||
.write_slice(queue_bytes, GuestAddress(BASE_VIRT_QUEUE_ADDR))
|
.write_slice(queue_bytes, GuestAddress(BASE_VIRT_QUEUE_ADDR))
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
return;
|
return Corpus::Reject;
|
||||||
}
|
}
|
||||||
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
||||||
return;
|
return Corpus::Reject;
|
||||||
}
|
}
|
||||||
let guest_memory = GuestMemoryAtomic::new(mem);
|
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||||
|
|
||||||
@@ -135,6 +137,8 @@ fuzz_target!(|bytes| {
|
|||||||
|
|
||||||
// Wait for the events to finish and console device worker thread to return
|
// Wait for the events to finish and console device worker thread to return
|
||||||
console.wait_for_epoll_threads();
|
console.wait_for_epoll_threads();
|
||||||
|
|
||||||
|
Corpus::Keep
|
||||||
});
|
});
|
||||||
|
|
||||||
pub struct NoopVirtioInterrupt {}
|
pub struct NoopVirtioInterrupt {}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user