mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
837 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f61c97e2b | ||
|
|
868e566c08 | ||
|
|
0f54fb1f5f | ||
|
|
758ef9604a | ||
|
|
8768a600ff | ||
|
|
5c3dad3efe | ||
|
|
4eb5af3100 | ||
|
|
3e9eff1a9a | ||
|
|
43c4b6f546 | ||
|
|
dc858ed96f | ||
|
|
42c0b4055a | ||
|
|
c6ca964ee6 | ||
|
|
42e8e2b933 | ||
|
|
902adabbbd | ||
|
|
5573faff56 | ||
|
|
7fc0776aac | ||
|
|
4f4eb3f6b8 | ||
|
|
8f4de45937 | ||
|
|
32dd4d10d0 | ||
|
|
27b5f8756f | ||
|
|
15358ef79d | ||
|
|
46672c384c | ||
|
|
a18b818227 | ||
|
|
0a5111b6c3 | ||
|
|
17b0f40154 | ||
|
|
8c88b10384 | ||
|
|
b0d245be70 | ||
|
|
fad29fdf1a | ||
|
|
c9374d87ac | ||
|
|
649e1fa1a6 | ||
|
|
c7725f921c | ||
|
|
82d06c0efa | ||
|
|
bc827ee3be | ||
|
|
ac25172176 | ||
|
|
c7977aa4a6 | ||
|
|
d93b7bb04e | ||
|
|
c4454f54bd | ||
|
|
580db833f0 | ||
|
|
e91409956d | ||
|
|
61ce4b8f31 | ||
|
|
154cca4170 | ||
|
|
6032e30807 | ||
|
|
7b7d720f37 | ||
|
|
d23c1abbf0 | ||
|
|
9b9015d907 | ||
|
|
60b44141b4 | ||
|
|
896a651b5c | ||
|
|
9075809494 | ||
|
|
6b23227e10 | ||
|
|
f68d63ef6c | ||
|
|
1bcb07b68b | ||
|
|
ef1ac5c670 | ||
|
|
5e7c9eaefd | ||
|
|
c4211686ab | ||
|
|
51cfffd24f | ||
|
|
e34c25cf6f | ||
|
|
366f058d61 | ||
|
|
2a312cd4fe | ||
|
|
0d7328da01 | ||
|
|
589dc77354 | ||
|
|
58d984f6b8 | ||
|
|
dd3c086a0a | ||
|
|
46953db3ca | ||
|
|
f855d9e237 | ||
|
|
ff0e92ab88 | ||
|
|
32af6f9723 | ||
|
|
9b3cab8c72 | ||
|
|
84630aa0b5 | ||
|
|
ad8ed80eb1 | ||
|
|
f035cff10f | ||
|
|
1a2b01888c | ||
|
|
94fcfa9f1f | ||
|
|
31b3871eee | ||
|
|
d8becc742c | ||
|
|
d2d6eb0591 | ||
|
|
8ee253cd3f | ||
|
|
6f49cb2860 | ||
|
|
03862314eb | ||
|
|
243b5bad65 | ||
|
|
80bbc47147 | ||
|
|
351a7b52c8 | ||
|
|
8c737793d6 | ||
|
|
419870ae45 | ||
|
|
66a2045148 | ||
|
|
5813ce0307 | ||
|
|
533e47f26b | ||
|
|
6221b6f8a1 | ||
|
|
57cc8bc6fe | ||
|
|
d3526823db | ||
|
|
608ad4894f | ||
|
|
e4dc776ea4 | ||
|
|
1fac67f648 | ||
|
|
592b8bcaaa | ||
|
|
e6e58e6d66 | ||
|
|
7c53896f11 | ||
|
|
a1f1dfddeb | ||
|
|
e2bd35f4bb | ||
|
|
b000b303e2 | ||
|
|
9260e3b273 | ||
|
|
cfdf643237 | ||
|
|
3480e69ff5 | ||
|
|
6a7a588268 | ||
|
|
932c8c9713 | ||
|
|
a4f5ad6076 | ||
|
|
be79a80361 | ||
|
|
34edb8960f | ||
|
|
83af9965fa | ||
|
|
f7e60434fa | ||
|
|
a4d8616676 | ||
|
|
38351f0d96 | ||
|
|
3587adcf5f | ||
|
|
611e71826d | ||
|
|
ed52e5c029 | ||
|
|
c8e3c1eed6 | ||
|
|
ad521fd4e4 | ||
|
|
e4cbafcd01 | ||
|
|
b81d758c41 | ||
|
|
184a3d1fb5 | ||
|
|
d3b2f2a4d9 | ||
|
|
a362e85539 | ||
|
|
9a583e6e9f | ||
|
|
a628854d9c | ||
|
|
7618cd2c2d | ||
|
|
eae4b315e4 | ||
|
|
c1a3b63112 | ||
|
|
751e76db08 | ||
|
|
d96d98d88e | ||
|
|
185f0c1bf3 | ||
|
|
e04cbb2ad4 | ||
|
|
986e43f899 | ||
|
|
a7786b4e0c | ||
|
|
def371e969 | ||
|
|
72d4d2c6f0 | ||
|
|
99467982aa | ||
|
|
6172b6aac5 | ||
|
|
9cddcc8766 | ||
|
|
d2319124f5 | ||
|
|
24fd54401d | ||
|
|
9256c41173 | ||
|
|
b1d4bcf952 | ||
|
|
8b7bfca9b0 | ||
|
|
7da69b59f7 | ||
|
|
50b6ee3947 | ||
|
|
c0ca6b5377 | ||
|
|
8c9f0e407f | ||
|
|
29f46ee470 | ||
|
|
f3e86bdf26 | ||
|
|
311dd7be50 | ||
|
|
bfd3deac7c | ||
|
|
1a35d5a017 | ||
|
|
95e63d5f44 | ||
|
|
f31d218479 | ||
|
|
ef216c37df | ||
|
|
242a7f7ab4 | ||
|
|
eec7634cd7 | ||
|
|
7ad83f1daa | ||
|
|
6c181061df | ||
|
|
99d4f77197 | ||
|
|
3c00a696b5 | ||
|
|
4f496e39a1 | ||
|
|
d92489e0a4 | ||
|
|
93647f0313 | ||
|
|
1877c723cd | ||
|
|
f51bad20bf | ||
|
|
f7da35857f | ||
|
|
5902ceb955 | ||
|
|
395929f1d4 | ||
|
|
4bd0ac6623 | ||
|
|
f917198f27 | ||
|
|
211ee1521d | ||
|
|
d0c3342c97 | ||
|
|
b4f3e1c2a1 | ||
|
|
b11a8a5ab5 | ||
|
|
436f3605e9 | ||
|
|
6cfa794f33 | ||
|
|
cc1db2ea13 | ||
|
|
fbdfc7a426 | ||
|
|
58d25b3ccc | ||
|
|
495d4eb896 | ||
|
|
6859a1694c | ||
|
|
960c7027c7 | ||
|
|
a2e02a8fff | ||
|
|
def98faf37 | ||
|
|
9d1a7e43a7 | ||
|
|
afe95e5a2a | ||
|
|
b8fee11822 | ||
|
|
e20be3e147 | ||
|
|
ec81f377b6 | ||
|
|
438be0dad5 | ||
|
|
1a5a89508b | ||
|
|
bad26133cb | ||
|
|
b881339866 | ||
|
|
0afa619d2c | ||
|
|
ae83e3b383 | ||
|
|
79e43ac534 | ||
|
|
a26ce353d3 | ||
|
|
cd9d1cf8fc | ||
|
|
a6456b50f3 | ||
|
|
7cfeefde57 | ||
|
|
f71f6da907 | ||
|
|
d4f7f42800 | ||
|
|
ca955a47ff | ||
|
|
88378d17a2 | ||
|
|
cf1c2bf0e8 | ||
|
|
e3d6e222a1 | ||
|
|
f8d9c073f0 | ||
|
|
e3c35a3579 | ||
|
|
7a4606f800 | ||
|
|
49f19e061b | ||
|
|
d33d254921 | ||
|
|
b8b0dab1ae | ||
|
|
c118d7d7d3 | ||
|
|
3059ba4305 | ||
|
|
080ce9b068 | ||
|
|
c886d71d29 | ||
|
|
4f5c179b9b | ||
|
|
fbb385834a | ||
|
|
b4fc02857f | ||
|
|
b55f009b8a | ||
|
|
b59f1d90dd | ||
|
|
a7fba8105f | ||
|
|
8b67298ad8 | ||
|
|
21706b02b8 | ||
|
|
bbc300b2cd | ||
|
|
1d095a929c | ||
|
|
5d14541924 | ||
|
|
f17addd36f | ||
|
|
b80df526e4 | ||
|
|
8c1176e33a | ||
|
|
ed5f25446a | ||
|
|
23f63262e7 | ||
|
|
455b0d12e9 | ||
|
|
3c7323230a | ||
|
|
374e1a2eca | ||
|
|
9d8315521c | ||
|
|
bc1e0361c0 | ||
|
|
8d6076653d | ||
|
|
57f063513c | ||
|
|
beb0c0707f | ||
|
|
1c2ab3e771 | ||
|
|
aaf253a2d7 | ||
|
|
bbc223b93f | ||
|
|
b5d5ffa969 | ||
|
|
93dce87cfe | ||
|
|
3dda3669b9 | ||
|
|
4c9a88db87 | ||
|
|
5dcc8cdf39 | ||
|
|
5963002cc5 | ||
|
|
f151a8602c | ||
|
|
0249e8641a | ||
|
|
7c19ae92b8 | ||
|
|
7f0e7d19a6 | ||
|
|
81b9705b10 | ||
|
|
27f7b2c2a1 | ||
|
|
76b6c62d07 | ||
|
|
7aff4decc6 | ||
|
|
bd51f3069c | ||
|
|
c8f108d2f0 | ||
|
|
e3feba287d | ||
|
|
e78c058420 | ||
|
|
70f9fea1c3 | ||
|
|
2f57d1c3f9 | ||
|
|
1311a7f178 | ||
|
|
7d7577007a | ||
|
|
84f0f332b3 | ||
|
|
e9ea9d63f8 | ||
|
|
48d4ccbfeb | ||
|
|
4ae8ee2376 | ||
|
|
8a56720b0f | ||
|
|
a3a0d26959 | ||
|
|
00bfb63607 | ||
|
|
fccd9c8829 | ||
|
|
feed0efc60 | ||
|
|
61e3f0f9e9 | ||
|
|
8df418358e | ||
|
|
2cccdc5ddd | ||
|
|
c3d6aceed1 | ||
|
|
2226207874 | ||
|
|
690bdc3448 | ||
|
|
c25bd447a1 | ||
|
|
d00eb4aa25 | ||
|
|
a1d8b63d86 | ||
|
|
0ce47c0538 | ||
|
|
828156db38 | ||
|
|
52df97e659 | ||
|
|
8e65d812cb | ||
|
|
610d694f1d | ||
|
|
25c6733feb | ||
|
|
39af0df279 | ||
|
|
5cc2876516 | ||
|
|
9009e785d9 | ||
|
|
a3231b1727 | ||
|
|
76a036e96d | ||
|
|
3e0bf235bb | ||
|
|
d0bafc72f8 | ||
|
|
2f3502eacc | ||
|
|
30cafccd23 | ||
|
|
88ebf565e0 | ||
|
|
0344805ed0 | ||
|
|
22873ae79e | ||
|
|
7dacfa3edc | ||
|
|
58d8206e2b | ||
|
|
1e1e61614c | ||
|
|
6a55768d94 | ||
|
|
5b177b205b | ||
|
|
f440976a7c | ||
|
|
0d573ae86c | ||
|
|
01420f5195 | ||
|
|
dfb1829f65 | ||
|
|
b5d11f72b3 | ||
|
|
00951f17d4 | ||
|
|
63c6c78c4e | ||
|
|
3fbdd7e16c | ||
|
|
455b0d5b62 | ||
|
|
bbefbe9273 | ||
|
|
84fc0e093d | ||
|
|
0eb78ab177 | ||
|
|
9aadbbe6d9 | ||
|
|
dc822be5e2 | ||
|
|
1a4747a20f | ||
|
|
83066cf58e | ||
|
|
495e444ca6 | ||
|
|
b99a3a7dc9 | ||
|
|
84a741a3fa | ||
|
|
db5d66747e | ||
|
|
dd6f07da23 | ||
|
|
8c135030d0 | ||
|
|
0468598fa3 | ||
|
|
7b7a3abfaa | ||
|
|
277ff84e55 | ||
|
|
a9f50dda08 | ||
|
|
73244aa6f2 | ||
|
|
6454c677d0 | ||
|
|
ca4857b5e6 | ||
|
|
1511cbda87 | ||
|
|
08021087ec | ||
|
|
90d79fa76b | ||
|
|
bebc5f957e | ||
|
|
5ac013df8b | ||
|
|
562a57012f | ||
|
|
93d19329f9 | ||
|
|
612dc544a8 | ||
|
|
59031531b6 | ||
|
|
2569621c4b | ||
|
|
1ea63f50a1 | ||
|
|
86f86c5348 | ||
|
|
0fb24ea3ae | ||
|
|
e390775bcb | ||
|
|
4450c44fbc | ||
|
|
a1caa6549a | ||
|
|
af3a59aa33 | ||
|
|
d7115ec656 | ||
|
|
7bbcc0f849 | ||
|
|
c4dc7a583d | ||
|
|
74485924b1 | ||
|
|
4889999277 | ||
|
|
eda0dc20d3 | ||
|
|
1e9c84af0f | ||
|
|
b50519651c | ||
|
|
08d859a7dd | ||
|
|
0dad7d9331 | ||
|
|
a496cecef1 | ||
|
|
a8f063db7c | ||
|
|
0066ddefe1 | ||
|
|
f9dd0aaf8a | ||
|
|
8b7880160e | ||
|
|
b7cb6257b5 | ||
|
|
6037c83585 | ||
|
|
b910a7922d | ||
|
|
ad6dfc5875 | ||
|
|
cb59976c68 | ||
|
|
520f54cb8d | ||
|
|
1a2d0e6dd8 | ||
|
|
7383087230 | ||
|
|
d72af85c42 | ||
|
|
d76d04e2f2 | ||
|
|
56c26b3d9c | ||
|
|
2280be825e | ||
|
|
73c822f64a | ||
|
|
43365ade2e | ||
|
|
e9d67dc405 | ||
|
|
fd4f32fa69 | ||
|
|
6fb88c3c5a | ||
|
|
d6c08d902b | ||
|
|
86afa38c64 | ||
|
|
ae68c802bd | ||
|
|
ccced2ebf4 | ||
|
|
d826b4fbdc | ||
|
|
0faa7afac2 | ||
|
|
da8eecc797 | ||
|
|
9dc9e224b9 | ||
|
|
b3fa56544c | ||
|
|
b30ddc0837 | ||
|
|
253c06d3ba | ||
|
|
46f6d9597d | ||
|
|
7b4f56e372 | ||
|
|
d6a2f48b32 | ||
|
|
c313bcbfd4 | ||
|
|
c741c01cc0 | ||
|
|
0aba41b3ee | ||
|
|
f67b3f79ea | ||
|
|
02173f42fc | ||
|
|
8ea0c542fd | ||
|
|
c1e896dddb | ||
|
|
a6040d7a30 | ||
|
|
a5d77cdee9 | ||
|
|
34f220edcd | ||
|
|
bcdac10149 | ||
|
|
ccda1a004e | ||
|
|
43f0dd6d25 | ||
|
|
a77160dca4 | ||
|
|
1d78812b63 | ||
|
|
8836715c2d | ||
|
|
330b5ea3be | ||
|
|
98bfd1e988 | ||
|
|
28382a1491 | ||
|
|
68e6a14deb | ||
|
|
f3778a7fc7 | ||
|
|
c6a110dd28 | ||
|
|
b0c7df2e59 | ||
|
|
1067059c4a | ||
|
|
8abe8c679b | ||
|
|
b6b686c71c | ||
|
|
171d12943d | ||
|
|
bdc44cd8bc | ||
|
|
33a55bac0f | ||
|
|
82ace6e327 | ||
|
|
2e56f0df77 | ||
|
|
a181b77bc8 | ||
|
|
9023412e31 | ||
|
|
977a893d86 | ||
|
|
2c4f8d2218 | ||
|
|
4f37a273d9 | ||
|
|
d64a77a5c6 | ||
|
|
e0d05683ab | ||
|
|
387753ae1d | ||
|
|
951ad3495e | ||
|
|
0dbb2683e3 | ||
|
|
579710d3e0 | ||
|
|
4d8a73735b | ||
|
|
687d646c60 | ||
|
|
54e523c302 | ||
|
|
4737679661 | ||
|
|
9c5b404415 | ||
|
|
6e9d3eccd6 | ||
|
|
10b954e954 | ||
|
|
e4abf2ce6c | ||
|
|
ad07a275c5 | ||
|
|
a20041ba68 | ||
|
|
eed0c927fd | ||
|
|
fb5cbd966e | ||
|
|
25798a97b3 | ||
|
|
2752b07762 | ||
|
|
a2e72774a9 | ||
|
|
584d5c5596 | ||
|
|
828dbd5227 | ||
|
|
b2e88967e4 | ||
|
|
ea396afdfa | ||
|
|
bfe0e90671 | ||
|
|
282484120d | ||
|
|
4d52a84ef7 | ||
|
|
1bf083bf00 | ||
|
|
c50051a686 | ||
|
|
e475b12cf7 | ||
|
|
64e217cf39 | ||
|
|
968902dfec | ||
|
|
df3b20f472 | ||
|
|
6c142e35f7 | ||
|
|
32da33eacd | ||
|
|
5d53648762 | ||
|
|
7549149bb5 | ||
|
|
c2144b5690 | ||
|
|
0d01eac1d4 | ||
|
|
46c60183cd | ||
|
|
7b80709595 | ||
|
|
a1a0bc8592 | ||
|
|
92a506fb06 | ||
|
|
72eb533f56 | ||
|
|
66f0b5b2b6 | ||
|
|
d92707afc5 | ||
|
|
9a634f07cb | ||
|
|
d475b953b8 | ||
|
|
c331d6b8cd | ||
|
|
4ef4a22e02 | ||
|
|
ae1ffeacec | ||
|
|
79486d1964 | ||
|
|
19ed8b329a | ||
|
|
b30a95f69a | ||
|
|
63637eba31 | ||
|
|
d74a219add | ||
|
|
4e5cbd27bb | ||
|
|
bc7ffdce95 | ||
|
|
22d7398139 | ||
|
|
a90260ffb6 | ||
|
|
f3793c08b5 | ||
|
|
8841e63e2d | ||
|
|
4d673de0fb | ||
|
|
e877718b29 | ||
|
|
744b5799a3 | ||
|
|
b0d8b50b36 | ||
|
|
2349b7e753 | ||
|
|
45d698ae72 | ||
|
|
f9098af4e4 | ||
|
|
47091bd02c | ||
|
|
8fae21c10c | ||
|
|
0bd0a65b3a | ||
|
|
b82bb55927 | ||
|
|
e89345c2c2 | ||
|
|
2b513ece00 | ||
|
|
e1f35628e2 | ||
|
|
001ee6794c | ||
|
|
f840335922 | ||
|
|
183605f9e1 | ||
|
|
e9a1b75ee6 | ||
|
|
e3487c0146 | ||
|
|
b075aada8c | ||
|
|
4d2a4e2805 | ||
|
|
6233f6f68e | ||
|
|
c913f7da2f | ||
|
|
760031103e | ||
|
|
25ed8722c7 | ||
|
|
8f6a5f979d | ||
|
|
b8b16c6eec | ||
|
|
a969d5016c | ||
|
|
c8df576933 | ||
|
|
f3035a0f7a | ||
|
|
6c00f5514f | ||
|
|
8f31688fa3 | ||
|
|
756dd236b9 | ||
|
|
780e606f12 | ||
|
|
db557916a5 | ||
|
|
722523f925 | ||
|
|
16f52914e0 | ||
|
|
b347e192d3 | ||
|
|
ddf5a848d8 | ||
|
|
d3170a926f | ||
|
|
2f2e870ad1 | ||
|
|
a37424c011 | ||
|
|
02aa95a098 | ||
|
|
2a470d9a30 | ||
|
|
e965007514 | ||
|
|
98fc38c465 | ||
|
|
1fa3a60b35 | ||
|
|
5db4dede28 | ||
|
|
452af9b17c | ||
|
|
fc24f39507 | ||
|
|
3778bf8a8e | ||
|
|
9aba1fdee6 | ||
|
|
864a5e4fe0 | ||
|
|
7d38a1848b | ||
|
|
2d2463ce04 | ||
|
|
08ac3405f5 | ||
|
|
c7f75f9d59 | ||
|
|
620aaf234e | ||
|
|
86b2c17135 | ||
|
|
ce52fc97b9 | ||
|
|
754ce37031 | ||
|
|
8f7ed5fed7 | ||
|
|
af3bdb452a | ||
|
|
9d35a10fd4 | ||
|
|
ea5a050341 | ||
|
|
0bad764462 | ||
|
|
e7893b9a97 | ||
|
|
d13eea3ba1 | ||
|
|
1240ef3261 | ||
|
|
ed53c74ca9 | ||
|
|
0b5c680d15 | ||
|
|
6f436f02dc | ||
|
|
53b2e19934 | ||
|
|
f99462add4 | ||
|
|
1d403ea5d6 | ||
|
|
c9cc97e9a0 | ||
|
|
bcae6c41e3 | ||
|
|
f3197c3833 | ||
|
|
5a0a4bc505 | ||
|
|
165364e08b | ||
|
|
20aa811de7 | ||
|
|
ba8d3f2c1c | ||
|
|
6d34ed03f7 | ||
|
|
4918c1ca7f | ||
|
|
d278e9f39b | ||
|
|
7e06d36dee | ||
|
|
4735cb8563 | ||
|
|
a636411522 | ||
|
|
71c7dff32b | ||
|
|
db444715fd | ||
|
|
c85aa6dfae | ||
|
|
5a83ebce64 | ||
|
|
f65538b08f | ||
|
|
152a3b98c9 | ||
|
|
a738808604 | ||
|
|
06729bb3ba | ||
|
|
2c54c30435 | ||
|
|
adae986233 | ||
|
|
1c3f8236e7 | ||
|
|
204be8611c | ||
|
|
5e74848ab4 | ||
|
|
7935430f6b | ||
|
|
d3b32b3d6d | ||
|
|
3efccd0fef | ||
|
|
b28063a7b4 | ||
|
|
9254b74c6d | ||
|
|
7fbec7113e | ||
|
|
2e236c53c8 | ||
|
|
77e147f333 | ||
|
|
980c1f7b19 | ||
|
|
db2159b563 | ||
|
|
5b0d4bb398 | ||
|
|
f7f2f25a57 | ||
|
|
b4f887ea80 | ||
|
|
91b05b6186 | ||
|
|
ed1b12f9d0 | ||
|
|
c6dd9918d5 | ||
|
|
db102a2547 | ||
|
|
27a285257e | ||
|
|
447c986916 | ||
|
|
7fb980f17b | ||
|
|
5c6139bbff | ||
|
|
0411064271 | ||
|
|
e9637d3733 | ||
|
|
9d88e0b417 | ||
|
|
b3f5630c27 | ||
|
|
61994cdb14 | ||
|
|
c1b962048c | ||
|
|
79425b6aa8 | ||
|
|
2a1e33ee35 | ||
|
|
b02df13af9 | ||
|
|
b2cc45e0a3 | ||
|
|
deb4ad5e23 | ||
|
|
3dff598fa2 | ||
|
|
cdfc177347 | ||
|
|
51ceae9131 | ||
|
|
1997152ee1 | ||
|
|
a5f4d79547 | ||
|
|
2ff193456d | ||
|
|
ecc8382ff0 | ||
|
|
521a11a110 | ||
|
|
60d054519e | ||
|
|
dc35dac306 | ||
|
|
ec1f7189da | ||
|
|
f5353c5b08 | ||
|
|
2a76a589c3 | ||
|
|
22275c3462 | ||
|
|
d27ea34a2d | ||
|
|
a0e48a87b8 | ||
|
|
349dbb9aac | ||
|
|
9e0f402e27 | ||
|
|
673c539f40 | ||
|
|
504ccc32a0 | ||
|
|
8fb53eb167 | ||
|
|
c7d3992bbc | ||
|
|
83c44a2411 | ||
|
|
c597655d06 | ||
|
|
902fe20d41 | ||
|
|
eabcbb5159 | ||
|
|
e51e410e04 | ||
|
|
8363a44b51 | ||
|
|
edc9f25efa | ||
|
|
62b8955245 | ||
|
|
461e6da1dc | ||
|
|
4993a1653b | ||
|
|
73eb1b1ba1 | ||
|
|
3baa0c3721 | ||
|
|
81895b9b40 | ||
|
|
6492496cc2 | ||
|
|
fdecba6958 | ||
|
|
4abcb0a33c | ||
|
|
ccafab6983 | ||
|
|
382b37f8d1 | ||
|
|
d4b8c8308c | ||
|
|
1da37c7bb4 | ||
|
|
12db6e5068 | ||
|
|
0852287fee | ||
|
|
dcc646f5b1 | ||
|
|
91bd4ee8cc | ||
|
|
6761868a69 | ||
|
|
9ccd23ebb0 | ||
|
|
b00a6a8519 | ||
|
|
e7c9954dc1 | ||
|
|
2723995cfa | ||
|
|
ca09638491 | ||
|
|
0835198ddd | ||
|
|
6d9c1eb638 | ||
|
|
b4c2772031 | ||
|
|
9106afae38 | ||
|
|
f063346de3 | ||
|
|
5e0d498582 | ||
|
|
a33280b8ad | ||
|
|
ec0a0adaab | ||
|
|
49c72beda5 | ||
|
|
bcd45efdd3 | ||
|
|
1b2e51d4d9 | ||
|
|
5bcbd5607e | ||
|
|
02f2ecee61 | ||
|
|
4c840f04e1 | ||
|
|
e6db599907 | ||
|
|
72092a9733 | ||
|
|
624af4f9af | ||
|
|
2b7b26374a | ||
|
|
48d1a31e08 | ||
|
|
2ecced376e | ||
|
|
da4cde70c3 | ||
|
|
9af2968a7d | ||
|
|
0ac4545c5b | ||
|
|
2aa43d3554 | ||
|
|
3e482c9c74 | ||
|
|
05f7651cf5 | ||
|
|
569be6e706 | ||
|
|
972e96ea68 | ||
|
|
d4316d0228 | ||
|
|
e481f97550 | ||
|
|
3937e03c02 | ||
|
|
d68c388cac | ||
|
|
e60078d939 | ||
|
|
39bc444db4 | ||
|
|
e8bafd8caa | ||
|
|
50e0869a17 | ||
|
|
867016757e | ||
|
|
1365388f0b | ||
|
|
73cc443213 | ||
|
|
4e759ab437 | ||
|
|
8c0f2ae87c | ||
|
|
2b1173acc3 | ||
|
|
26b46ec435 | ||
|
|
2100f97cbc | ||
|
|
607fd3a8e5 | ||
|
|
429928057d | ||
|
|
7f507dd77d | ||
|
|
ea0918e5e8 | ||
|
|
6b710209b1 | ||
|
|
3987026997 | ||
|
|
9aedabe11e | ||
|
|
4a3cec8c1f | ||
|
|
3229fcc900 | ||
|
|
910313cfde | ||
|
|
5b8eed0e8c | ||
|
|
6cd6bb2755 | ||
|
|
613f74ca1e | ||
|
|
5effa20a5b | ||
|
|
17c99ae00a | ||
|
|
9ec0c981f8 | ||
|
|
3d4e27fa0a | ||
|
|
c46441c937 | ||
|
|
5b6d424a77 | ||
|
|
76ac0f1555 | ||
|
|
74950d07dd | ||
|
|
894a46f30c | ||
|
|
8744162a0e | ||
|
|
4da3bdcd6e | ||
|
|
95ca4fb15e | ||
|
|
6dcf9f6588 | ||
|
|
4440671739 | ||
|
|
957d3deeea | ||
|
|
6e63df98ba | ||
|
|
1f2915bff0 | ||
|
|
63b8d2eb58 | ||
|
|
71bbaf556f | ||
|
|
8819bb0f21 | ||
|
|
215372fd78 | ||
|
|
97b3c9b7b6 | ||
|
|
43f1a32859 | ||
|
|
b45264af75 | ||
|
|
d9680c4c51 | ||
|
|
28b80ba42a | ||
|
|
f40e8ce6b7 | ||
|
|
d494d6b837 | ||
|
|
48326ce731 | ||
|
|
054c036e81 | ||
|
|
5e42e0f1f7 | ||
|
|
81ec47b283 | ||
|
|
c101b1f558 | ||
|
|
efb0e5a49f | ||
|
|
e214bb9eed | ||
|
|
5cd82cb2e2 | ||
|
|
cff190fa5f | ||
|
|
7ba3eeda98 | ||
|
|
239e39ddbc | ||
|
|
5768dcc320 | ||
|
|
1075209e2a | ||
|
|
54c552f90d | ||
|
|
d330d91e1a | ||
|
|
3613b4c096 | ||
|
|
45c4d1a06e | ||
|
|
d4d62fc9dc | ||
|
|
c4be0f4235 | ||
|
|
5825ab2dd4 | ||
|
|
585269ecb9 | ||
|
|
5432cbfaad | ||
|
|
4d25eaa24a | ||
|
|
58461ce250 | ||
|
|
57762d4328 | ||
|
|
652e30c97d | ||
|
|
49e061d15b | ||
|
|
28ad84c207 | ||
|
|
b546afd196 | ||
|
|
6974b378f3 | ||
|
|
4d36ecef9e | ||
|
|
8a02cb3378 | ||
|
|
15112ae1b6 | ||
|
|
c93b7037b4 | ||
|
|
56c3073da4 | ||
|
|
cee5508e9b | ||
|
|
b56e1217b6 | ||
|
|
fb2e8b6817 | ||
|
|
a94fa77621 | ||
|
|
f0af934891 | ||
|
|
dc15e44e2a | ||
|
|
a36ac96444 | ||
|
|
09c3ddd47d | ||
|
|
07f3075773 | ||
|
|
acc71c4eda | ||
|
|
4c22592c6f | ||
|
|
9708019312 | ||
|
|
14c0e8424b | ||
|
|
3c0f06c09c | ||
|
|
2c576ab312 | ||
|
|
3fed07419a | ||
|
|
aa27f0e743 | ||
|
|
a6fe4aa7e9 | ||
|
|
de7cfd61a0 | ||
|
|
1ff4d4e3ae | ||
|
|
744e9d06e5 | ||
|
|
428c637506 | ||
|
|
f67ae9b47e | ||
|
|
251708b908 | ||
|
|
40f73e4fa1 | ||
|
|
62e1721c52 | ||
|
|
57f70c0761 | ||
|
|
0699dc1000 | ||
|
|
2e3e64a4cf | ||
|
|
36d336841c | ||
|
|
bfd4aa2fed | ||
|
|
f060ecd705 | ||
|
|
5363cee847 | ||
|
|
31e4d80caf | ||
|
|
c7f69cc0df | ||
|
|
ee118152ff |
14
.github/workflows/build.yaml
vendored
14
.github/workflows/build.yaml
vendored
@@ -31,14 +31,26 @@ jobs:
|
|||||||
|
|
||||||
- name: Debug Check (default features)
|
- name: Debug Check (default features)
|
||||||
run: |
|
run: |
|
||||||
git rev-list origin/master..$GITHUB_SHA | xargs -t -I % sh -c 'git checkout %; cargo check --all --target=${{ matrix.target }}'
|
git rev-list origin/main..$GITHUB_SHA | xargs -t -I % sh -c 'git checkout %; cargo check --all --target=${{ matrix.target }}'
|
||||||
git checkout $GITHUB_SHA
|
git checkout $GITHUB_SHA
|
||||||
|
|
||||||
|
- name: Build (default + tdx)
|
||||||
|
run: cargo rustc --bin cloud-hypervisor --features "tdx" -- -D warnings
|
||||||
|
|
||||||
- name: Build (acpi,kvm)
|
- name: Build (acpi,kvm)
|
||||||
run: cargo rustc --bin cloud-hypervisor --no-default-features --features "acpi,kvm" -- -D warnings
|
run: cargo rustc --bin cloud-hypervisor --no-default-features --features "acpi,kvm" -- -D warnings
|
||||||
|
|
||||||
- name: Build (kvm)
|
- name: Build (kvm)
|
||||||
run: cargo rustc --bin cloud-hypervisor --no-default-features --features "kvm" -- -D warnings
|
run: cargo rustc --bin cloud-hypervisor --no-default-features --features "kvm" -- -D warnings
|
||||||
|
|
||||||
|
- name: Build (acpi,mshv)
|
||||||
|
run: cargo rustc --bin cloud-hypervisor --no-default-features --features "acpi,mshv" -- -D warnings
|
||||||
|
|
||||||
|
- name: Build (mshv)
|
||||||
|
run: cargo rustc --bin cloud-hypervisor --no-default-features --features "mshv" -- -D warnings
|
||||||
|
|
||||||
- name: Release Build (default features)
|
- name: Release Build (default features)
|
||||||
run: cargo build --all --release --target=${{ matrix.target }}
|
run: cargo build --all --release --target=${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Check build did not modify any files
|
||||||
|
run: test -z "$(git status --porcelain)"
|
||||||
|
|||||||
18
.github/workflows/dco.yaml
vendored
Normal file
18
.github/workflows/dco.yaml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
name: DCO
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python 3.x
|
||||||
|
uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
- name: Check DCO
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
pip3 install -U dco-check
|
||||||
|
dco-check -e "49699333+dependabot[bot]@users.noreply.github.com"
|
||||||
16
.github/workflows/docker-image.yaml
vendored
16
.github/workflows/docker-image.yaml
vendored
@@ -1,8 +1,10 @@
|
|||||||
name: Cloud-Hypervisor's Docker image update
|
name: Cloud Hypervisor's Docker image update
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: master
|
branches: main
|
||||||
|
paths: resources/Dockerfile
|
||||||
|
pull_request:
|
||||||
paths: resources/Dockerfile
|
paths: resources/Dockerfile
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -19,12 +21,14 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v1
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
|
if: ${{ github.event_name == 'push' }}
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v1
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
|
if: ${{ github.event_name == 'push' }}
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
file: ./resources/Dockerfile
|
file: ./resources/Dockerfile
|
||||||
@@ -32,5 +36,13 @@ jobs:
|
|||||||
push: true
|
push: true
|
||||||
tags: cloudhypervisor/dev:latest
|
tags: cloudhypervisor/dev:latest
|
||||||
|
|
||||||
|
- name: Build only
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
file: ./resources/Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
tags: cloudhypervisor/dev:latest
|
||||||
|
|
||||||
- name: Image digest
|
- name: Image digest
|
||||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||||
|
|||||||
4
.github/workflows/fuzz-build.yaml
vendored
4
.github/workflows/fuzz-build.yaml
vendored
@@ -22,6 +22,8 @@ jobs:
|
|||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
override: true
|
override: true
|
||||||
- name: Install Cargo fuzz
|
- name: Install Cargo fuzz
|
||||||
run: cargo install -f cargo-fuzz
|
# Temporary fix for cargo-fuzz on latest nightly: https://github.com/rust-fuzz/cargo-fuzz/issues/276
|
||||||
|
#run: cargo install cargo-fuzz
|
||||||
|
run: cargo install --git https://github.com/rust-fuzz/cargo-fuzz --rev b4df3e58f767b5cad8d1aa6753961003f56f3609
|
||||||
- name: Cargo Fuzz Build
|
- name: Cargo Fuzz Build
|
||||||
run: cargo fuzz build
|
run: cargo fuzz build
|
||||||
|
|||||||
24
.github/workflows/quality-aarch64.yaml
vendored
24
.github/workflows/quality-aarch64.yaml
vendored
@@ -31,9 +31,31 @@ jobs:
|
|||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
- name: Formatting (rustfmt)
|
- name: Formatting (rustfmt)
|
||||||
run: cargo fmt -- --check
|
run: cargo fmt -- --check
|
||||||
|
|
||||||
- name: Clippy (kvm)
|
- name: Clippy (kvm)
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
use-cross: true
|
use-cross: true
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --target=${{ matrix.target }} --no-default-features --features "kvm" -- -D warnings
|
args: --target=${{ matrix.target }} --all --no-default-features --features "kvm" -- -D warnings
|
||||||
|
|
||||||
|
- name: Clippy (kvm,acpi)
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: true
|
||||||
|
command: clippy
|
||||||
|
args: --target=${{ matrix.target }} --all --no-default-features --features "kvm,acpi" -- -D warnings
|
||||||
|
|
||||||
|
- name: Clippy (all features,kvm)
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: true
|
||||||
|
command: clippy
|
||||||
|
args: --target=${{ matrix.target }} --all --no-default-features --features "common,kvm" -- -D warnings
|
||||||
|
|
||||||
|
- name: Clippy (default)
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: true
|
||||||
|
command: clippy
|
||||||
|
args: --target=${{ matrix.target }} --all -- -D warnings
|
||||||
|
|||||||
3
.github/workflows/quality.yaml
vendored
3
.github/workflows/quality.yaml
vendored
@@ -56,3 +56,6 @@ jobs:
|
|||||||
|
|
||||||
- name: Clippy (integration tests)
|
- name: Clippy (integration tests)
|
||||||
run: cargo clippy --all --all-targets --tests --features "integration_tests" -- -D warnings
|
run: cargo clippy --all --all-targets --tests --features "integration_tests" -- -D warnings
|
||||||
|
|
||||||
|
- name: Check build did not modify any files
|
||||||
|
run: test -z "$(git status --porcelain)"
|
||||||
|
|||||||
54
.github/workflows/release.yaml
vendored
54
.github/workflows/release.yaml
vendored
@@ -9,6 +9,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Code checkout
|
- name: Code checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
- name: Create release directory
|
||||||
|
run: rsync -rv --exclude=.git . ../cloud-hypervisor-${{ github.event.ref }}
|
||||||
- name: Install Rust toolchain (x86_64-unknown-linux-gnu)
|
- name: Install Rust toolchain (x86_64-unknown-linux-gnu)
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
@@ -25,6 +27,25 @@ jobs:
|
|||||||
run: cargo build --all --release --target=x86_64-unknown-linux-musl
|
run: cargo build --all --release --target=x86_64-unknown-linux-musl
|
||||||
- name: Strip cloud-hypervisor binaries
|
- name: Strip cloud-hypervisor binaries
|
||||||
run: strip target/*/release/cloud-hypervisor
|
run: strip target/*/release/cloud-hypervisor
|
||||||
|
- name: Install Rust toolchain (aarch64-unknown-linux-musl)
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
target: aarch64-unknown-linux-musl
|
||||||
|
override: true
|
||||||
|
- name: Static Build (AArch64)
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: true
|
||||||
|
command: build
|
||||||
|
args: --all --release --target=aarch64-unknown-linux-musl
|
||||||
|
- name: Vendor
|
||||||
|
working-directory: ../cloud-hypervisor-${{ github.event.ref }}
|
||||||
|
run: |
|
||||||
|
mkdir ../vendor-cargo-home
|
||||||
|
export CARGO_HOME=$(realpath ../vendor-cargo-home)
|
||||||
|
mkdir .cargo
|
||||||
|
cargo vendor > .cargo/config.toml
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: actions/create-release@v1
|
uses: actions/create-release@v1
|
||||||
@@ -35,6 +56,19 @@ jobs:
|
|||||||
release_name: ${{ github.ref }}
|
release_name: ${{ github.ref }}
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: true
|
prerelease: true
|
||||||
|
- name: Create vendored source archive
|
||||||
|
working-directory: ../
|
||||||
|
run: tar cJf cloud-hypervisor-${{ github.event.ref }}.tar.xz cloud-hypervisor-${{ github.event.ref }}
|
||||||
|
- name: Upload cloud-hypervisor vendored source archive
|
||||||
|
id: upload-release-cloud-hypervisor-vendored-sources
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ../cloud-hypervisor-${{ github.event.ref }}.tar.xz
|
||||||
|
asset_name: cloud-hypervisor-${{ github.event.ref }}.tar.xz
|
||||||
|
asset_content_type: application/x-xz
|
||||||
- name: Upload cloud-hypervisor
|
- name: Upload cloud-hypervisor
|
||||||
id: upload-release-cloud-hypervisor
|
id: upload-release-cloud-hypervisor
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
@@ -65,3 +99,23 @@ jobs:
|
|||||||
asset_path: target/x86_64-unknown-linux-gnu/release/ch-remote
|
asset_path: target/x86_64-unknown-linux-gnu/release/ch-remote
|
||||||
asset_name: ch-remote
|
asset_name: ch-remote
|
||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
|
- name: Upload static AArch64 cloud-hypervisor
|
||||||
|
id: upload-release-static-aarch64-cloud-hypervisor
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: target/aarch64-unknown-linux-musl/release/cloud-hypervisor
|
||||||
|
asset_name: cloud-hypervisor-static-aarch64
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
- name: Upload static AArch64 ch-remote
|
||||||
|
id: upload-release-static-aarch64-ch-remote
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: target/aarch64-unknown-linux-musl/release/ch-remote
|
||||||
|
asset_name: ch-remote-static-aarch64
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
# Contributing to Cloud Hypervisor
|
# Contributing to Cloud Hypervisor
|
||||||
|
|
||||||
Cloud Hypervisor is an open source project licensed under the [Apache v2 License](https://opensource.org/licenses/Apache-2.0) and the [BSD 3 Clause](https://opensource.org/licenses/BSD-3-Clause) license.
|
Cloud Hypervisor is an open source project licensed under the [Apache v2
|
||||||
|
License](https://opensource.org/licenses/Apache-2.0) and the [BSD 3
|
||||||
|
Clause](https://opensource.org/licenses/BSD-3-Clause) license. Contributions
|
||||||
|
can be made under either license or both. Individual files contain details of
|
||||||
|
their licensing and changes to that file are under the same license unless the
|
||||||
|
contribution changes the license of the file. When importing code from a third
|
||||||
|
party project (e.g. Firecracker or CrosVM) please respect the license of those
|
||||||
|
projects.
|
||||||
|
|
||||||
## Coding Style
|
## Coding Style
|
||||||
|
|
||||||
@@ -55,7 +62,7 @@ you want to merge your changes to `cloud-hypervisor`:
|
|||||||
into your github organization.
|
into your github organization.
|
||||||
2. Within your fork, create a branch for your contribution.
|
2. Within your fork, create a branch for your contribution.
|
||||||
3. [Create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)
|
3. [Create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)
|
||||||
against the master branch of the Cloud Hypervisor repository.
|
against the main branch of the Cloud Hypervisor repository.
|
||||||
4. Add reviewers to your pull request and then work with your reviewers to address
|
4. Add reviewers to your pull request and then work with your reviewers to address
|
||||||
any comments and obtain minimum of 2 [maintainers](MAINTAINERS.md) approvals.
|
any comments and obtain minimum of 2 [maintainers](MAINTAINERS.md) approvals.
|
||||||
To update your pull request amend existing commits whenever applicable and
|
To update your pull request amend existing commits whenever applicable and
|
||||||
|
|||||||
380
Cargo.lock
generated
380
Cargo.lock
generated
@@ -11,9 +11,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
version = "0.15.2"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e7a2e47a1fbe209ee101dd6d61285226744c6c8d3c21c8dc878ba6cb9f467f3a"
|
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gimli",
|
"gimli",
|
||||||
]
|
]
|
||||||
@@ -35,28 +35,31 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ansi_term"
|
name = "ansi_term"
|
||||||
version = "0.11.0"
|
version = "0.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.40"
|
version = "1.0.51"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
|
checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "api_client"
|
name = "api_client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arc-swap"
|
name = "arc-swap"
|
||||||
version = "1.3.0"
|
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 = "e906254e445520903e7fc9da4f709886c84ae4bc4ddaf0e093188d66df4dc820"
|
checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arch"
|
name = "arch"
|
||||||
@@ -65,17 +68,20 @@ dependencies = [
|
|||||||
"acpi_tables",
|
"acpi_tables",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
"fdt",
|
||||||
"hypervisor",
|
"hypervisor",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-loader",
|
"linux-loader",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
"vm-fdt",
|
"vm-fdt",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vm-migration",
|
"vm-migration",
|
||||||
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -97,13 +103,13 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.60"
|
version = "0.3.63"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7815ea54e4d821e791162e078acbebfd6d8c8939cd559c9335dceb1c8ca7282"
|
checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr2line",
|
"addr2line",
|
||||||
"cc",
|
"cc",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
"object",
|
"object",
|
||||||
@@ -121,9 +127,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.2.1"
|
version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block_util"
|
name = "block_util"
|
||||||
@@ -136,7 +142,9 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
|
"vhdx",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
|
"virtio-queue",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vm-virtio",
|
"vm-virtio",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
@@ -150,15 +158,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.68"
|
version = "1.0.72"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787"
|
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "0.1.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@@ -168,9 +170,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "2.33.3"
|
version = "2.34.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"atty",
|
"atty",
|
||||||
@@ -184,13 +186,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cloud-hypervisor"
|
name = "cloud-hypervisor"
|
||||||
version = "16.0.0"
|
version = "20.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"api_client",
|
"api_client",
|
||||||
"clap",
|
"clap",
|
||||||
"credibility",
|
"credibility",
|
||||||
"dirs",
|
"dirs 4.0.0",
|
||||||
"epoll",
|
"epoll",
|
||||||
"event_monitor",
|
"event_monitor",
|
||||||
"hypervisor",
|
"hypervisor",
|
||||||
@@ -199,7 +201,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"net_util",
|
"net_util",
|
||||||
"option_parser",
|
"option_parser",
|
||||||
"seccomp",
|
"seccompiler",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"test_infra",
|
"test_infra",
|
||||||
@@ -211,12 +213,12 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cloudabi"
|
name = "crc32c"
|
||||||
version = "0.0.3"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
checksum = "210cdf933e6a81212bfabf90cd8762f471b5922e5f6b709547673ad8e04b9448"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"rustc_version",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -264,6 +266,15 @@ dependencies = [
|
|||||||
"dirs-sys",
|
"dirs-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs"
|
||||||
|
version = "4.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs-sys"
|
name = "dirs-sys"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
@@ -277,9 +288,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "env_logger"
|
name = "env_logger"
|
||||||
version = "0.8.3"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f"
|
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"humantime",
|
"humantime",
|
||||||
@@ -330,22 +341,28 @@ dependencies = [
|
|||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fdt"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b643857cf70949306b81d7e92cb9d47add673868edac9863c4a49c42feaf3f1e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.24.0"
|
version = "0.26.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189"
|
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
@@ -355,9 +372,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.18"
|
version = "0.1.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@@ -392,19 +409,28 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iced-x86"
|
name = "iced-x86"
|
||||||
version = "1.11.3"
|
version = "1.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d907dfc273d9ed0da940e8145ceb77759056a1fb2110b44b83d1b203cddb906"
|
checksum = "46e977036f7f5139d580c7f19ad62df9cb8ebd8410bb569e73585226be80a86f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-uring"
|
name = "instant"
|
||||||
version = "0.5.1"
|
version = "0.1.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fb82832e05cc4ca298f198a8db108837b4f7b7b1248e3cba8e48f151aece80cf"
|
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "io-uring"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d75829ed9377bab6c90039fe47b9d84caceb4b5063266142e21bcce6550cda8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -421,14 +447,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "0.4.7"
|
version = "0.4.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kvm-bindings"
|
name = "kvm-bindings"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.4.0#1a68725639283e622f4bb64584885b30bfe8be44"
|
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.5.0#9c497710ba9968d8efe15dbae03991b16cf82e23"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
@@ -437,9 +463,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kvm-ioctls"
|
name = "kvm-ioctls"
|
||||||
version = "0.9.0"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/rust-vmm/kvm-ioctls?branch=main#d22ef1f51852dfb055da38004e1a4fed81246f81"
|
||||||
checksum = "f2924454e22895c738e43331ae310459c74a11ded9c97dc250129ee10d2f9ca2"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"kvm-bindings",
|
"kvm-bindings",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -454,15 +479,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.96"
|
version = "0.2.108"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5600b4e6efc5421841a2138a6b082e07fe12f9aaa12783d50e5d13325b26b4fc"
|
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libssh2-sys"
|
name = "libssh2-sys"
|
||||||
version = "0.2.21"
|
version = "0.2.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e0186af0d8f171ae6b9c4c90ec51898bad5d08a2d5e470903a50d9ad8959cbee"
|
checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -486,18 +511,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-loader"
|
name = "linux-loader"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c819cc8275b0f2c1ed9feec455ca288b45d82932384a6a5f7a86812ee3427459"
|
checksum = "8a5e77493808403a6bd56a301a64ea6b9342e36ea845044bf0dfdf56fe52fa08"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.3.4"
|
version = "0.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
|
checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
]
|
]
|
||||||
@@ -508,19 +533,19 @@ version = "0.4.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.4.0"
|
version = "2.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
|
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||||
|
|
||||||
[[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#9b605a8b61df602cda62076d30d9f70307ea336f"
|
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#0a58eb1ece68e326e68365c4297d0a7c08ecd9bc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
@@ -539,7 +564,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "mshv-bindings"
|
name = "mshv-bindings"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/cloud-hypervisor/mshv?branch=master#1024e9a821d33079452c20477b6ecb976ff795ad"
|
source = "git+https://github.com/rust-vmm/mshv?branch=main#8770516859e7d05193c496b77ac5ddad3ca5943a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -551,7 +576,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "mshv-ioctls"
|
name = "mshv-ioctls"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/cloud-hypervisor/mshv?branch=master#1024e9a821d33079452c20477b6ecb976ff795ad"
|
source = "git+https://github.com/rust-vmm/mshv?branch=main#8770516859e7d05193c496b77ac5ddad3ca5943a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"mshv-bindings",
|
"mshv-bindings",
|
||||||
@@ -570,6 +595,7 @@ name = "net_util"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"epoll",
|
"epoll",
|
||||||
|
"getrandom",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
@@ -581,6 +607,7 @@ dependencies = [
|
|||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
|
"virtio-queue",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vm-virtio",
|
"vm-virtio",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
@@ -588,18 +615,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.25.2"
|
version = "0.27.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8bc1d42047cf336f0f939c99e97183cf31551bf0f2865a2ec9c8d91fd4ffb5e"
|
checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-sys"
|
name = "openssl-sys"
|
||||||
version = "0.9.63"
|
version = "0.9.71"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6b0d6fb7d80f877617dfcb014e605e2b5ab2fb0afdf27935219bb6bd984cb98"
|
checksum = "7df13d165e607909b363a4757a6f133f8a818a74e9d3a98d09c6128e15fa4c73"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"cc",
|
"cc",
|
||||||
@@ -614,24 +641,25 @@ version = "0.1.0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.10.2"
|
version = "0.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
|
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"instant",
|
||||||
"lock_api",
|
"lock_api",
|
||||||
"parking_lot_core",
|
"parking_lot_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot_core"
|
name = "parking_lot_core"
|
||||||
version = "0.7.2"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
|
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 0.1.10",
|
"cfg-if",
|
||||||
"cloudabi",
|
"instant",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall 0.1.57",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
@@ -645,10 +673,12 @@ dependencies = [
|
|||||||
"hypervisor",
|
"hypervisor",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
"thiserror",
|
||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
"vfio-bindings",
|
"vfio-bindings",
|
||||||
"vfio-ioctls",
|
"vfio-ioctls",
|
||||||
|
"vfio_user",
|
||||||
"vm-allocator",
|
"vm-allocator",
|
||||||
"vm-device",
|
"vm-device",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
@@ -658,9 +688,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.19"
|
version = "0.3.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pnet"
|
name = "pnet"
|
||||||
@@ -752,9 +782,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.27"
|
version = "1.0.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
|
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
@@ -772,9 +802,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.9"
|
version = "1.0.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
@@ -790,15 +820,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.1.57"
|
version = "0.2.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_syscall"
|
|
||||||
version = "0.2.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
@@ -810,7 +834,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
|
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"redox_syscall 0.2.8",
|
"redox_syscall",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -843,15 +867,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.19"
|
version = "0.1.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "410f7acf3cb3a44527c5d9546bad4bf4e6c460915d5f9f2fc524498bfe8f70ce"
|
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.5"
|
version = "1.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
@@ -860,24 +893,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "seccomp"
|
name = "seccompiler"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/firecracker-microvm/firecracker?tag=v0.24.4#8f44986a0e956a77f2b2324c12f73bec16130c82"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e01d1292a1131b22ccea49f30bd106f1238b5ddeec1a98d39268dcc31d540e68"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "semver"
|
||||||
version = "1.0.126"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
|
dependencies = [
|
||||||
|
"semver-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver-parser"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.130"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.126"
|
version = "1.0.130"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
|
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -886,9 +935,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.64"
|
version = "1.0.72"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
|
checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
@@ -897,9 +946,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook"
|
name = "signal-hook"
|
||||||
version = "0.3.9"
|
version = "0.3.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "470c5a6397076fae0094aaf06a08e6ba6f37acb77d3b1b91ea92b4d6c8650c39"
|
checksum = "9c98891d737e271a2954825ef19e46bd16bdb98e2746f2eec4f7a4ef7946efd1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
@@ -916,15 +965,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.6.1"
|
version = "1.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ssh2"
|
name = "ssh2"
|
||||||
version = "0.9.1"
|
version = "0.9.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d876d4d57f6bbf2245d43f7ec53759461f801a446d3693704aa6d27b257844d7"
|
checksum = "269343e64430067a14937ae0e3c4ec604c178fb896dde0964b1acd22b3e2eeb1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -946,9 +995,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.73"
|
version = "1.0.82"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7"
|
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -957,9 +1006,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "synstructure"
|
name = "synstructure"
|
||||||
version = "0.12.4"
|
version = "0.12.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
|
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -990,7 +1039,7 @@ dependencies = [
|
|||||||
name = "test_infra"
|
name = "test_infra"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs",
|
"dirs 3.0.2",
|
||||||
"epoll",
|
"epoll",
|
||||||
"libc",
|
"libc",
|
||||||
"ssh2",
|
"ssh2",
|
||||||
@@ -1010,18 +1059,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.25"
|
version = "1.0.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6"
|
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.25"
|
version = "1.0.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d"
|
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -1030,9 +1079,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.8"
|
version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
@@ -1045,12 +1094,15 @@ name = "uuid"
|
|||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.13"
|
version = "0.2.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "025ce40a007e1907e58d5bc1a594def78e5573bb0b1160bc389634e8f12e4faa"
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vec_map"
|
name = "vec_map"
|
||||||
@@ -1097,24 +1149,60 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "vfio-ioctls"
|
name = "vfio-ioctls"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/rust-vmm/vfio-ioctls?branch=master#0f7eb6f855b80d60a7e09ac6df43b46ba69f9e9e"
|
source = "git+https://github.com/rust-vmm/vfio-ioctls?branch=main#19e5b83ddfad430109ce3e8d51f4a24796099127"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"kvm-bindings",
|
"kvm-bindings",
|
||||||
"kvm-ioctls",
|
"kvm-ioctls",
|
||||||
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
"mshv-bindings",
|
||||||
|
"mshv-ioctls",
|
||||||
|
"thiserror",
|
||||||
"vfio-bindings",
|
"vfio-bindings",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vhost"
|
name = "vfio_user"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/rust-vmm/vhost?branch=master#30ba3e7bbe9e0542cf480f44bc6845a8dbd2a6ba"
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"vfio-bindings",
|
||||||
|
"vm-memory",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vhdx"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"crc32c",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"remain",
|
||||||
|
"thiserror",
|
||||||
|
"uuid",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vhost"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d23ddeb452fb4f837526c6298cc8a2f4948e5595b0328a3d61b5eebe51874d7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"libc",
|
"libc",
|
||||||
|
"vm-memory",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1127,6 +1215,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"vhost",
|
"vhost",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
|
"virtio-queue",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vm-virtio",
|
"vm-virtio",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
@@ -1138,6 +1227,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"block_util",
|
"block_util",
|
||||||
"clap",
|
"clap",
|
||||||
|
"env_logger",
|
||||||
"epoll",
|
"epoll",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
@@ -1155,6 +1245,7 @@ name = "vhost_user_net"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
|
"env_logger",
|
||||||
"epoll",
|
"epoll",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
@@ -1190,7 +1281,7 @@ dependencies = [
|
|||||||
"net_util",
|
"net_util",
|
||||||
"pci",
|
"pci",
|
||||||
"rate_limiter",
|
"rate_limiter",
|
||||||
"seccomp",
|
"seccompiler",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -1198,6 +1289,7 @@ dependencies = [
|
|||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
"vhost",
|
"vhost",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
|
"virtio-queue",
|
||||||
"vm-allocator",
|
"vm-allocator",
|
||||||
"vm-device",
|
"vm-device",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
@@ -1206,6 +1298,15 @@ dependencies = [
|
|||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "virtio-queue"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"vm-memory",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vm-allocator"
|
name = "vm-allocator"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -1231,13 +1332,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vm-fdt"
|
name = "vm-fdt"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/rust-vmm/vm-fdt?branch=master#2e4ebdeb27be5b450b51b79fdeeeee3750d42182"
|
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#9cfa0c8d7c3032f79589c7b01c8722a37f4cf44f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vm-memory"
|
name = "vm-memory"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "git+https://github.com/rust-vmm/vm-memory?rev=5bd7138758183a73ac0da27ce40c004d95f1a7e9#5bd7138758183a73ac0da27ce40c004d95f1a7e9"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0a8ebcb86ca457f9d6e14cf97009f679952eba42f0113de5db596e514cd0e43b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -1264,6 +1366,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
|
"virtio-queue",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1292,7 +1395,7 @@ dependencies = [
|
|||||||
"option_parser",
|
"option_parser",
|
||||||
"pci",
|
"pci",
|
||||||
"qcow",
|
"qcow",
|
||||||
"seccomp",
|
"seccompiler",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -1302,7 +1405,10 @@ dependencies = [
|
|||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
"vfio-ioctls",
|
"vfio-ioctls",
|
||||||
|
"vfio_user",
|
||||||
|
"vhdx",
|
||||||
"virtio-devices",
|
"virtio-devices",
|
||||||
|
"virtio-queue",
|
||||||
"vm-allocator",
|
"vm-allocator",
|
||||||
"vm-device",
|
"vm-device",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
@@ -1313,9 +1419,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vmm-sys-util"
|
name = "vmm-sys-util"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "01cf11afbc4ebc0d5c7a7748a77d19e2042677fc15faa2f4ccccb27c18a60605"
|
checksum = "733537bded03aaa93543f785ae997727b30d1d9f4a03b7861d23290474242e11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -1371,9 +1477,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.3.0"
|
version = "0.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6580539ad917b7c026220c4b3f2c08d52ce54d6ce0dc491e66002e35388fab46"
|
checksum = "332f188cc1bcf1fe1064b8c58d150f497e697f49774aa846f2dc949d9a25f236"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"zerocopy-derive",
|
"zerocopy-derive",
|
||||||
@@ -1381,9 +1487,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy-derive"
|
name = "zerocopy-derive"
|
||||||
version = "0.2.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 = "d498dbd1fd7beb83c86709ae1c33ca50942889473473d287d56ce4770a18edfb"
|
checksum = "a0fbc82b82efe24da867ee52e015e58178684bd9dd64c34e66bdf21da2582a9f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"syn",
|
"syn",
|
||||||
|
|||||||
35
Cargo.toml
35
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cloud-hypervisor"
|
name = "cloud-hypervisor"
|
||||||
version = "16.0.0"
|
version = "20.2.0"
|
||||||
authors = ["The Cloud Hypervisor Authors"]
|
authors = ["The Cloud Hypervisor Authors"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
default-run = "cloud-hypervisor"
|
default-run = "cloud-hypervisor"
|
||||||
@@ -13,43 +13,43 @@ homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
|||||||
lto = true
|
lto = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.40"
|
anyhow = "1.0.51"
|
||||||
api_client = { path = "api_client" }
|
api_client = { path = "api_client" }
|
||||||
clap = { version = "2.33.3", features = ["wrap_help"] }
|
clap = { version = "2.34.0", features = ["wrap_help"] }
|
||||||
epoll = "4.3.1"
|
epoll = "4.3.1"
|
||||||
event_monitor = { path = "event_monitor" }
|
event_monitor = { path = "event_monitor" }
|
||||||
hypervisor = { path = "hypervisor" }
|
hypervisor = { path = "hypervisor" }
|
||||||
libc = "0.2.96"
|
libc = "0.2.108"
|
||||||
log = { version = "0.4.14", features = ["std"] }
|
log = { version = "0.4.14", features = ["std"] }
|
||||||
option_parser = { path = "option_parser" }
|
option_parser = { path = "option_parser" }
|
||||||
seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v0.24.4" }
|
seccompiler = "0.2.0"
|
||||||
serde_json = "1.0.64"
|
serde_json = "1.0.72"
|
||||||
signal-hook = "0.3.9"
|
signal-hook = "0.3.10"
|
||||||
thiserror = "1.0.25"
|
thiserror = "1.0.30"
|
||||||
vmm = { path = "vmm" }
|
vmm = { path = "vmm" }
|
||||||
vmm-sys-util = "0.8.0"
|
vmm-sys-util = "0.9.0"
|
||||||
vm-memory = "0.5.0"
|
vm-memory = "0.6.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
clap = { version = "2.33.3", features = ["wrap_help"] }
|
clap = { version = "2.34.0", features = ["wrap_help"] }
|
||||||
|
|
||||||
# List of patched crates
|
# List of patched crates
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.4.0", features = ["with-serde", "fam-wrappers"] }
|
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0", features = ["with-serde", "fam-wrappers"] }
|
||||||
|
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "main" }
|
||||||
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
|
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
|
||||||
vm-memory = { git = "https://github.com/rust-vmm/vm-memory", rev = "5bd7138758183a73ac0da27ce40c004d95f1a7e9"}
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
credibility = "0.1.3"
|
credibility = "0.1.3"
|
||||||
dirs = "3.0.2"
|
dirs = "4.0.0"
|
||||||
lazy_static= "1.4.0"
|
lazy_static= "1.4.0"
|
||||||
net_util = { path = "net_util" }
|
net_util = { path = "net_util" }
|
||||||
serde_json = "1.0.64"
|
serde_json = "1.0.72"
|
||||||
test_infra = { path = "test_infra" }
|
test_infra = { path = "test_infra" }
|
||||||
wait-timeout = "0.2.0"
|
wait-timeout = "0.2.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["acpi", "cmos", "io_uring", "kvm"]
|
default = ["common", "kvm"]
|
||||||
# Common features for all hypervisors
|
# Common features for all hypervisors
|
||||||
common = ["acpi", "cmos", "fwdebug", "io_uring"]
|
common = ["acpi", "cmos", "fwdebug", "io_uring"]
|
||||||
acpi = ["vmm/acpi"]
|
acpi = ["vmm/acpi"]
|
||||||
@@ -78,10 +78,13 @@ members = [
|
|||||||
"pci",
|
"pci",
|
||||||
"qcow",
|
"qcow",
|
||||||
"rate_limiter",
|
"rate_limiter",
|
||||||
|
"vfio_user",
|
||||||
|
"vhdx",
|
||||||
"vhost_user_backend",
|
"vhost_user_backend",
|
||||||
"vhost_user_block",
|
"vhost_user_block",
|
||||||
"vhost_user_net",
|
"vhost_user_net",
|
||||||
"virtio-devices",
|
"virtio-devices",
|
||||||
|
"virtio-queue",
|
||||||
"vmm",
|
"vmm",
|
||||||
"vm-allocator",
|
"vm-allocator",
|
||||||
"vm-device",
|
"vm-device",
|
||||||
|
|||||||
66
Jenkinsfile
vendored
66
Jenkinsfile
vendored
@@ -2,7 +2,7 @@ pipeline{
|
|||||||
agent none
|
agent none
|
||||||
stages {
|
stages {
|
||||||
stage ('Early checks') {
|
stage ('Early checks') {
|
||||||
agent { node { label 'master' } }
|
agent { node { label 'built-in' } }
|
||||||
stages {
|
stages {
|
||||||
stage ('Check for RFC/WIP builds') {
|
stage ('Check for RFC/WIP builds') {
|
||||||
when {
|
when {
|
||||||
@@ -14,7 +14,7 @@ pipeline{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Cancel older builds') {
|
stage ('Cancel older builds') {
|
||||||
when { not { branch 'master' } }
|
when { not { branch 'main' } }
|
||||||
steps {
|
steps {
|
||||||
cancelPreviousBuilds()
|
cancelPreviousBuilds()
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ pipeline{
|
|||||||
stage ('Build') {
|
stage ('Build') {
|
||||||
parallel {
|
parallel {
|
||||||
stage ('Worker build') {
|
stage ('Worker build') {
|
||||||
agent { node { label 'groovy' } }
|
agent { node { label 'hirsute' } }
|
||||||
stages {
|
stages {
|
||||||
stage ('Checkout') {
|
stage ('Checkout') {
|
||||||
steps {
|
steps {
|
||||||
@@ -83,7 +83,7 @@ pipeline{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Worker build (musl)') {
|
stage ('Worker build (musl)') {
|
||||||
agent { node { label 'groovy' } }
|
agent { node { label 'hirsute' } }
|
||||||
stages {
|
stages {
|
||||||
stage ('Checkout') {
|
stage ('Checkout') {
|
||||||
steps {
|
steps {
|
||||||
@@ -110,7 +110,7 @@ pipeline{
|
|||||||
agent { node { label 'bionic-sgx' } }
|
agent { node { label 'bionic-sgx' } }
|
||||||
when {
|
when {
|
||||||
beforeAgent true
|
beforeAgent true
|
||||||
branch 'master'
|
branch 'main'
|
||||||
}
|
}
|
||||||
stages {
|
stages {
|
||||||
stage ('Checkout') {
|
stage ('Checkout') {
|
||||||
@@ -146,7 +146,7 @@ pipeline{
|
|||||||
agent { node { label 'bionic-vfio' } }
|
agent { node { label 'bionic-vfio' } }
|
||||||
when {
|
when {
|
||||||
beforeAgent true
|
beforeAgent true
|
||||||
branch 'master'
|
branch 'main'
|
||||||
}
|
}
|
||||||
stages {
|
stages {
|
||||||
stage ('Checkout') {
|
stage ('Checkout') {
|
||||||
@@ -179,7 +179,10 @@ pipeline{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Worker build - Windows guest') {
|
stage ('Worker build - Windows guest') {
|
||||||
agent { node { label 'groovy' } }
|
agent { node { label 'hirsute' } }
|
||||||
|
environment {
|
||||||
|
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
|
||||||
|
}
|
||||||
stages {
|
stages {
|
||||||
stage ('Checkout') {
|
stage ('Checkout') {
|
||||||
steps {
|
steps {
|
||||||
@@ -188,17 +191,10 @@ pipeline{
|
|||||||
}
|
}
|
||||||
stage ('Download assets') {
|
stage ('Download assets') {
|
||||||
steps {
|
steps {
|
||||||
|
sh "sudo apt install -y azure-cli"
|
||||||
sh "mkdir ${env.HOME}/workloads"
|
sh "mkdir ${env.HOME}/workloads"
|
||||||
azureDownload(storageCredentialId: 'ch-image-store',
|
sh 'az storage blob download --container-name private-images --file "$HOME/workloads/OVMF-4b47d0c6c8.fd" --name OVMF-4b47d0c6c8.fd --connection-string "$AZURE_CONNECTION_STRING"'
|
||||||
containerName: 'private-images',
|
sh 'az storage blob download --container-name private-images --file "$HOME/workloads/windows-server-2019.raw" --name windows-server-2019.raw --connection-string "$AZURE_CONNECTION_STRING"'
|
||||||
includeFilesPattern: 'OVMF-4b47d0c6c8.fd',
|
|
||||||
downloadType: 'container',
|
|
||||||
downloadDirLoc: "${env.HOME}/workloads")
|
|
||||||
azureDownload(storageCredentialId: 'ch-image-store',
|
|
||||||
containerName: 'private-images',
|
|
||||||
includeFilesPattern: 'windows-server-2019.raw',
|
|
||||||
downloadType: 'container',
|
|
||||||
downloadDirLoc: "${env.HOME}/workloads")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Run Windows guest integration tests') {
|
stage ('Run Windows guest integration tests') {
|
||||||
@@ -219,21 +215,49 @@ pipeline{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stage ('Worker build - Live Migration') {
|
||||||
|
agent { node { label 'hirsute-small' } }
|
||||||
|
stages {
|
||||||
|
stage ('Checkout') {
|
||||||
|
steps {
|
||||||
|
checkout scm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage ('Run live-migration integration tests') {
|
||||||
|
options {
|
||||||
|
timeout(time: 1, unit: 'HOURS')
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh "sudo modprobe openvswitch"
|
||||||
|
sh "scripts/dev_cli.sh tests --integration-live-migration"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage ('Run live-migration integration tests for musl') {
|
||||||
|
options {
|
||||||
|
timeout(time: 1, unit: 'HOURS')
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh "sudo modprobe openvswitch"
|
||||||
|
sh "scripts/dev_cli.sh tests --integration-live-migration --libc musl"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
post {
|
post {
|
||||||
regression {
|
regression {
|
||||||
script {
|
script {
|
||||||
if (env.BRANCH_NAME == 'master') {
|
if (env.BRANCH_NAME == 'main') {
|
||||||
slackSend (color: '#ff0000', message: '"master" branch build is now failing')
|
slackSend (color: '#ff0000', message: '"main" branch build is now failing')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fixed {
|
fixed {
|
||||||
script {
|
script {
|
||||||
if (env.BRANCH_NAME == 'master') {
|
if (env.BRANCH_NAME == 'main') {
|
||||||
slackSend (color: '#00ff00', message: '"master" branch build is now fixed')
|
slackSend (color: '#00ff00', message: '"main" branch build is now fixed')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Maintainers
|
# Maintainers
|
||||||
|
|
||||||
- Sebastien Boeuf <sebastien.boeuf@intel.com> @sboeuf
|
- Sebastien Boeuf - @sboeuf
|
||||||
- Robert Bradford <robert.bradford@intel.com> @rbradford
|
- Robert Bradford - @rbradford
|
||||||
- Samuel Ortiz <sameo@linux.intel.com> @sameo
|
- Samuel Ortiz - @sameo
|
||||||
- Chao P Peng <chao.p.peng@linux.intel.com> @chao-p
|
- Wei Liu - @liuw
|
||||||
|
- Michael Zhao - @michael2012z
|
||||||
|
|||||||
14
README.md
14
README.md
@@ -1,11 +1,10 @@
|
|||||||
[](https://travis-ci.com/cloud-hypervisor/cloud-hypervisor)
|
|
||||||
|
|
||||||
- [1. What is Cloud Hypervisor?](#1-what-is-cloud-hypervisor)
|
- [1. What is Cloud Hypervisor?](#1-what-is-cloud-hypervisor)
|
||||||
- [Objectives](#objectives)
|
- [Objectives](#objectives)
|
||||||
- [High Level](#high-level)
|
- [High Level](#high-level)
|
||||||
- [Architectures](#architectures)
|
- [Architectures](#architectures)
|
||||||
- [Guest OS](#guest-os)
|
- [Guest OS](#guest-os)
|
||||||
- [2. Getting Started](#2-getting-started)
|
- [2. Getting Started](#2-getting-started)
|
||||||
|
- [Install prerequisites](#install-prerequisites)
|
||||||
- [Clone and build](#clone-and-build)
|
- [Clone and build](#clone-and-build)
|
||||||
- [Containerized builds and tests](#containerized-builds-and-tests)
|
- [Containerized builds and tests](#containerized-builds-and-tests)
|
||||||
- [Run](#run)
|
- [Run](#run)
|
||||||
@@ -23,6 +22,7 @@
|
|||||||
- [5. Community](#5-community)
|
- [5. Community](#5-community)
|
||||||
- [Contribute](#contribute)
|
- [Contribute](#contribute)
|
||||||
- [Join us](#join-us)
|
- [Join us](#join-us)
|
||||||
|
- [Security issues](#security-issues)
|
||||||
|
|
||||||
# 1. What is Cloud Hypervisor?
|
# 1. What is Cloud Hypervisor?
|
||||||
|
|
||||||
@@ -58,6 +58,12 @@ Cloud Hypervisor supports `64-bit Linux` and Windows 10/Windows Server 2019.
|
|||||||
|
|
||||||
# 2. Getting Started
|
# 2. Getting Started
|
||||||
|
|
||||||
|
Below sections describe how to build and run Cloud Hypervisor on the `x86_64`
|
||||||
|
platform. For getting started on the `AArch64` platform, please refer to the
|
||||||
|
[Arm64 documentation](docs/arm64.md).
|
||||||
|
|
||||||
|
## Preparation
|
||||||
|
|
||||||
We create a folder to build and run `cloud-hypervisor` at `$HOME/cloud-hypervisor`
|
We create a folder to build and run `cloud-hypervisor` at `$HOME/cloud-hypervisor`
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@@ -149,7 +155,7 @@ We need to get the latest `rust-hypervisor-firmware` release and also a working
|
|||||||
$ pushd $CLOUDH
|
$ pushd $CLOUDH
|
||||||
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
|
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
|
||||||
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-amd64.img focal-server-cloudimg-amd64.raw
|
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-amd64.img focal-server-cloudimg-amd64.raw
|
||||||
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.3.1/hypervisor-fw
|
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.3.2/hypervisor-fw
|
||||||
$ popd
|
$ popd
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -180,7 +186,7 @@ To build the kernel:
|
|||||||
|
|
||||||
# Clone the Cloud Hypervisor Linux branch
|
# Clone the Cloud Hypervisor Linux branch
|
||||||
$ pushd $CLOUDH
|
$ pushd $CLOUDH
|
||||||
$ git clone --depth 1 https://github.com/cloud-hypervisor/linux.git -b ch-5.12 linux-cloud-hypervisor
|
$ git clone --depth 1 https://github.com/cloud-hypervisor/linux.git -b ch-5.14 linux-cloud-hypervisor
|
||||||
$ pushd linux-cloud-hypervisor
|
$ pushd linux-cloud-hypervisor
|
||||||
|
|
||||||
# Use the cloud-hypervisor kernel config to build your kernel
|
# Use the cloud-hypervisor kernel config to build your kernel
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ authors = ["The Cloud Hypervisor Authors"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
vm-memory = "0.5.0"
|
vm-memory = "0.6.0"
|
||||||
|
|||||||
@@ -6,15 +6,23 @@
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
pub trait Aml {
|
pub trait Aml {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8>;
|
fn append_aml_bytes(&self, _v: &mut Vec<u8>) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
|
let mut v = Vec::new();
|
||||||
|
self.append_aml_bytes(&mut v);
|
||||||
|
v
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const ZERO: Zero = Zero {};
|
pub const ZERO: Zero = Zero {};
|
||||||
pub struct Zero {}
|
pub struct Zero {}
|
||||||
|
|
||||||
impl Aml for Zero {
|
impl Aml for Zero {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, v: &mut Vec<u8>) {
|
||||||
vec![0u8]
|
v.push(0u8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,8 +30,8 @@ pub const ONE: One = One {};
|
|||||||
pub struct One {}
|
pub struct One {}
|
||||||
|
|
||||||
impl Aml for One {
|
impl Aml for One {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, v: &mut Vec<u8>) {
|
||||||
vec![1u8]
|
v.push(1u8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,8 +39,8 @@ pub const ONES: Ones = Ones {};
|
|||||||
pub struct Ones {}
|
pub struct Ones {}
|
||||||
|
|
||||||
impl Aml for Ones {
|
impl Aml for Ones {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, v: &mut Vec<u8>) {
|
||||||
vec![0xffu8]
|
v.push(0xffu8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,9 +50,7 @@ pub struct Path {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Aml for Path {
|
impl Aml for Path {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = Vec::new();
|
|
||||||
|
|
||||||
if self.root {
|
if self.root {
|
||||||
bytes.push(b'\\');
|
bytes.push(b'\\');
|
||||||
}
|
}
|
||||||
@@ -62,10 +68,8 @@ impl Aml for Path {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for part in self.name_parts.clone().iter_mut() {
|
for part in self.name_parts.clone().iter_mut() {
|
||||||
bytes.append(&mut part.to_vec());
|
bytes.extend_from_slice(&part.to_vec());
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,40 +98,36 @@ impl From<&str> for Path {
|
|||||||
pub type Byte = u8;
|
pub type Byte = u8;
|
||||||
|
|
||||||
impl Aml for Byte {
|
impl Aml for Byte {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![0x0a]; /* BytePrefix */
|
bytes.push(0x0a); /* BytePrefix */
|
||||||
bytes.push(*self);
|
bytes.push(*self);
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Word = u16;
|
pub type Word = u16;
|
||||||
|
|
||||||
impl Aml for Word {
|
impl Aml for Word {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![0x0bu8]; /* WordPrefix */
|
bytes.push(0x0b); /* WordPrefix */
|
||||||
bytes.append(&mut self.to_le_bytes().to_vec());
|
bytes.extend_from_slice(&self.to_le_bytes())
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type DWord = u32;
|
pub type DWord = u32;
|
||||||
|
|
||||||
impl Aml for DWord {
|
impl Aml for DWord {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![0x0c]; /* DWordPrefix */
|
bytes.push(0x0c); /* DWordPrefix */
|
||||||
bytes.append(&mut self.to_le_bytes().to_vec());
|
bytes.extend_from_slice(&self.to_le_bytes())
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type QWord = u64;
|
pub type QWord = u64;
|
||||||
|
|
||||||
impl Aml for QWord {
|
impl Aml for QWord {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![0x0e]; /* QWordPrefix */
|
bytes.push(0x0e); /* QWordPrefix */
|
||||||
bytes.append(&mut self.to_le_bytes().to_vec());
|
bytes.extend_from_slice(&self.to_le_bytes())
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,16 +136,18 @@ pub struct Name {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Aml for Name {
|
impl Aml for Name {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
self.bytes.clone()
|
// TODO: Refactor this to make more efficient but there are
|
||||||
|
// lifetime/ownership challenges.
|
||||||
|
bytes.extend_from_slice(&self.bytes.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Name {
|
impl Name {
|
||||||
pub fn new(path: Path, inner: &dyn Aml) -> Self {
|
pub fn new(path: Path, inner: &dyn Aml) -> Self {
|
||||||
let mut bytes = vec![0x08]; /* NameOp */
|
let mut bytes = vec![0x08]; /* NameOp */
|
||||||
bytes.append(&mut path.to_aml_bytes());
|
path.append_aml_bytes(&mut bytes);
|
||||||
bytes.append(&mut inner.to_aml_bytes());
|
inner.append_aml_bytes(&mut bytes);
|
||||||
Name { bytes }
|
Name { bytes }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,21 +157,17 @@ pub struct Package<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Aml for Package<'a> {
|
impl<'a> Aml for Package<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![self.children.len() as u8];
|
let mut tmp = vec![self.children.len() as u8];
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
bytes.append(&mut child.to_aml_bytes());
|
child.append_aml_bytes(&mut tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
let pkg_length = create_pkg_length(&tmp, true);
|
||||||
pkg_length.reverse();
|
|
||||||
for byte in pkg_length {
|
|
||||||
bytes.insert(0, byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes.insert(0, 0x12); /* PackageOp */
|
bytes.push(0x12); /* PackageOp */
|
||||||
|
bytes.extend_from_slice(&pkg_length);
|
||||||
bytes
|
bytes.extend_from_slice(&tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,51 +255,46 @@ impl EisaName {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Aml for EisaName {
|
impl Aml for EisaName {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
self.value.to_aml_bytes()
|
self.value.append_aml_bytes(bytes)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_integer(v: usize) -> Vec<u8> {
|
|
||||||
if v <= u8::max_value().into() {
|
|
||||||
(v as u8).to_aml_bytes()
|
|
||||||
} else if v <= u16::max_value().into() {
|
|
||||||
(v as u16).to_aml_bytes()
|
|
||||||
} else if v <= u32::max_value() as usize {
|
|
||||||
(v as u32).to_aml_bytes()
|
|
||||||
} else {
|
|
||||||
(v as u64).to_aml_bytes()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Usize = usize;
|
pub type Usize = usize;
|
||||||
|
|
||||||
impl Aml for Usize {
|
impl Aml for Usize {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
create_integer(*self)
|
if *self <= u8::max_value().into() {
|
||||||
|
(*self as u8).append_aml_bytes(bytes)
|
||||||
|
} else if *self <= u16::max_value().into() {
|
||||||
|
(*self as u16).append_aml_bytes(bytes)
|
||||||
|
} else if *self <= u32::max_value() as usize {
|
||||||
|
(*self as u32).append_aml_bytes(bytes)
|
||||||
|
} else {
|
||||||
|
(*self as u64).append_aml_bytes(bytes)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_aml_string(v: &str) -> Vec<u8> {
|
fn append_aml_string(v: &str, bytes: &mut Vec<u8>) {
|
||||||
let mut data = vec![0x0D]; /* String Op */
|
bytes.push(0x0D); /* String Op */
|
||||||
data.extend_from_slice(v.as_bytes());
|
bytes.extend_from_slice(v.as_bytes());
|
||||||
data.push(0x0); /* NullChar */
|
bytes.push(0x0); /* NullChar */
|
||||||
data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type AmlStr = &'static str;
|
pub type AmlStr = &'static str;
|
||||||
|
|
||||||
impl Aml for AmlStr {
|
impl Aml for AmlStr {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
create_aml_string(self)
|
append_aml_string(self, bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type AmlString = String;
|
pub type AmlString = String;
|
||||||
|
|
||||||
impl Aml for AmlString {
|
impl Aml for AmlString {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
create_aml_string(self)
|
append_aml_string(self, bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,36 +303,31 @@ pub struct ResourceTemplate<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Aml for ResourceTemplate<'a> {
|
impl<'a> Aml for ResourceTemplate<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = Vec::new();
|
let mut tmp = Vec::new();
|
||||||
|
|
||||||
// Add buffer data
|
// Add buffer data
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
bytes.append(&mut child.to_aml_bytes());
|
child.append_aml_bytes(&mut tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark with end and mark checksum as as always valid
|
// Mark with end and mark checksum as as always valid
|
||||||
bytes.push(0x79); /* EndTag */
|
tmp.push(0x79); /* EndTag */
|
||||||
bytes.push(0); /* zero checksum byte */
|
tmp.push(0); /* zero checksum byte */
|
||||||
|
|
||||||
// Buffer length is an encoded integer including buffer data
|
// Buffer length is an encoded integer including buffer data
|
||||||
// and EndTag and checksum byte
|
// and EndTag and checksum byte
|
||||||
let mut buffer_length = bytes.len().to_aml_bytes();
|
let mut buffer_length = tmp.len().to_aml_bytes();
|
||||||
buffer_length.reverse();
|
buffer_length.reverse();
|
||||||
for byte in buffer_length {
|
for byte in buffer_length {
|
||||||
bytes.insert(0, byte);
|
tmp.insert(0, byte);
|
||||||
}
|
}
|
||||||
|
|
||||||
// PkgLength is everything else
|
// PkgLength is everything else
|
||||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
let pkg_length = create_pkg_length(&tmp, true);
|
||||||
pkg_length.reverse();
|
|
||||||
for byte in pkg_length {
|
|
||||||
bytes.insert(0, byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes.insert(0, 0x11); /* BufferOp */
|
bytes.push(0x11); /* BufferOp */
|
||||||
|
bytes.extend_from_slice(&pkg_length);
|
||||||
bytes
|
bytes.extend_from_slice(&tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,15 +354,13 @@ impl Memory32Fixed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Aml for Memory32Fixed {
|
impl Aml for Memory32Fixed {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![0x86]; /* Memory32Fixed */
|
bytes.push(0x86); /* Memory32Fixed */
|
||||||
bytes.append(&mut 9u16.to_le_bytes().to_vec());
|
bytes.extend_from_slice(&9u16.to_le_bytes());
|
||||||
|
|
||||||
// 9 bytes of payload
|
// 9 bytes of payload
|
||||||
bytes.push(self.read_write as u8);
|
bytes.push(self.read_write as u8);
|
||||||
bytes.append(&mut self.base.to_le_bytes().to_vec());
|
bytes.extend_from_slice(&self.base.to_le_bytes());
|
||||||
bytes.append(&mut self.length.to_le_bytes().to_vec());
|
bytes.extend_from_slice(&self.length.to_le_bytes());
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,7 +416,7 @@ impl<T> AddressSpace<T> {
|
|||||||
|
|
||||||
fn push_header(&self, bytes: &mut Vec<u8>, descriptor: u8, length: usize) {
|
fn push_header(&self, bytes: &mut Vec<u8>, descriptor: u8, length: usize) {
|
||||||
bytes.push(descriptor); /* Word Address Space Descriptor */
|
bytes.push(descriptor); /* Word Address Space Descriptor */
|
||||||
bytes.append(&mut (length as u16).to_le_bytes().to_vec());
|
bytes.extend_from_slice(&(length as u16).to_le_bytes());
|
||||||
bytes.push(self.r#type as u8); /* type */
|
bytes.push(self.r#type as u8); /* type */
|
||||||
let generic_flags = 1 << 2 /* Min Fixed */ | 1 << 3; /* Max Fixed */
|
let generic_flags = 1 << 2 /* Min Fixed */ | 1 << 3; /* Max Fixed */
|
||||||
bytes.push(generic_flags);
|
bytes.push(generic_flags);
|
||||||
@@ -439,65 +425,53 @@ impl<T> AddressSpace<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Aml for AddressSpace<u16> {
|
impl Aml for AddressSpace<u16> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = Vec::new();
|
|
||||||
|
|
||||||
self.push_header(
|
self.push_header(
|
||||||
&mut bytes,
|
bytes,
|
||||||
0x88, /* Word Address Space Descriptor */
|
0x88, /* Word Address Space Descriptor */
|
||||||
3 + 5 * std::mem::size_of::<u16>(), /* 3 bytes of header + 5 u16 fields */
|
3 + 5 * std::mem::size_of::<u16>(), /* 3 bytes of header + 5 u16 fields */
|
||||||
);
|
);
|
||||||
|
|
||||||
bytes.append(&mut 0u16.to_le_bytes().to_vec()); /* Granularity */
|
bytes.extend_from_slice(&0u16.to_le_bytes()); /* Granularity */
|
||||||
bytes.append(&mut self.min.to_le_bytes().to_vec()); /* Min */
|
bytes.extend_from_slice(&self.min.to_le_bytes()); /* Min */
|
||||||
bytes.append(&mut self.max.to_le_bytes().to_vec()); /* Max */
|
bytes.extend_from_slice(&self.max.to_le_bytes()); /* Max */
|
||||||
bytes.append(&mut 0u16.to_le_bytes().to_vec()); /* Translation */
|
bytes.extend_from_slice(&0u16.to_le_bytes()); /* Translation */
|
||||||
let len = self.max - self.min + 1;
|
let len = self.max - self.min + 1;
|
||||||
bytes.append(&mut len.to_le_bytes().to_vec()); /* Length */
|
bytes.extend_from_slice(&len.to_le_bytes()); /* Length */
|
||||||
|
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Aml for AddressSpace<u32> {
|
impl Aml for AddressSpace<u32> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = Vec::new();
|
|
||||||
|
|
||||||
self.push_header(
|
self.push_header(
|
||||||
&mut bytes,
|
bytes,
|
||||||
0x87, /* DWord Address Space Descriptor */
|
0x87, /* DWord Address Space Descriptor */
|
||||||
3 + 5 * std::mem::size_of::<u32>(), /* 3 bytes of header + 5 u32 fields */
|
3 + 5 * std::mem::size_of::<u32>(), /* 3 bytes of header + 5 u32 fields */
|
||||||
);
|
);
|
||||||
|
|
||||||
bytes.append(&mut 0u32.to_le_bytes().to_vec()); /* Granularity */
|
bytes.extend_from_slice(&0u32.to_le_bytes()); /* Granularity */
|
||||||
bytes.append(&mut self.min.to_le_bytes().to_vec()); /* Min */
|
bytes.extend_from_slice(&self.min.to_le_bytes()); /* Min */
|
||||||
bytes.append(&mut self.max.to_le_bytes().to_vec()); /* Max */
|
bytes.extend_from_slice(&self.max.to_le_bytes()); /* Max */
|
||||||
bytes.append(&mut 0u32.to_le_bytes().to_vec()); /* Translation */
|
bytes.extend_from_slice(&0u32.to_le_bytes()); /* Translation */
|
||||||
let len = self.max - self.min + 1;
|
let len = self.max - self.min + 1;
|
||||||
bytes.append(&mut len.to_le_bytes().to_vec()); /* Length */
|
bytes.extend_from_slice(&len.to_le_bytes()); /* Length */
|
||||||
|
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Aml for AddressSpace<u64> {
|
impl Aml for AddressSpace<u64> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = Vec::new();
|
|
||||||
|
|
||||||
self.push_header(
|
self.push_header(
|
||||||
&mut bytes,
|
bytes,
|
||||||
0x8A, /* QWord Address Space Descriptor */
|
0x8A, /* QWord Address Space Descriptor */
|
||||||
3 + 5 * std::mem::size_of::<u64>(), /* 3 bytes of header + 5 u64 fields */
|
3 + 5 * std::mem::size_of::<u64>(), /* 3 bytes of header + 5 u64 fields */
|
||||||
);
|
);
|
||||||
|
|
||||||
bytes.append(&mut 0u64.to_le_bytes().to_vec()); /* Granularity */
|
bytes.extend_from_slice(&0u64.to_le_bytes()); /* Granularity */
|
||||||
bytes.append(&mut self.min.to_le_bytes().to_vec()); /* Min */
|
bytes.extend_from_slice(&self.min.to_le_bytes()); /* Min */
|
||||||
bytes.append(&mut self.max.to_le_bytes().to_vec()); /* Max */
|
bytes.extend_from_slice(&self.max.to_le_bytes()); /* Max */
|
||||||
bytes.append(&mut 0u64.to_le_bytes().to_vec()); /* Translation */
|
bytes.extend_from_slice(&0u64.to_le_bytes()); /* Translation */
|
||||||
let len = self.max - self.min + 1;
|
let len = self.max - self.min + 1;
|
||||||
bytes.append(&mut len.to_le_bytes().to_vec()); /* Length */
|
bytes.extend_from_slice(&len.to_le_bytes()); /* Length */
|
||||||
|
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -520,16 +494,13 @@ impl Io {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Aml for Io {
|
impl Aml for Io {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![0x47]; /* Io Port Descriptor */
|
bytes.push(0x47); /* Io Port Descriptor */
|
||||||
|
|
||||||
bytes.push(1); /* IODecode16 */
|
bytes.push(1); /* IODecode16 */
|
||||||
bytes.append(&mut self.min.to_le_bytes().to_vec());
|
bytes.extend_from_slice(&self.min.to_le_bytes());
|
||||||
bytes.append(&mut self.max.to_le_bytes().to_vec());
|
bytes.extend_from_slice(&self.max.to_le_bytes());
|
||||||
bytes.push(self.alignment);
|
bytes.push(self.alignment);
|
||||||
bytes.push(self.length);
|
bytes.push(self.length);
|
||||||
|
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -560,18 +531,16 @@ impl Interrupt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Aml for Interrupt {
|
impl Aml for Interrupt {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![0x89]; /* Extended IRQ Descriptor */
|
bytes.push(0x89); /* Extended IRQ Descriptor */
|
||||||
bytes.append(&mut 6u16.to_le_bytes().to_vec());
|
bytes.extend_from_slice(&6u16.to_le_bytes());
|
||||||
let flags = (self.shared as u8) << 3
|
let flags = (self.shared as u8) << 3
|
||||||
| (self.active_low as u8) << 2
|
| (self.active_low as u8) << 2
|
||||||
| (self.edge_triggered as u8) << 1
|
| (self.edge_triggered as u8) << 1
|
||||||
| self.consumer as u8;
|
| self.consumer as u8;
|
||||||
bytes.push(flags);
|
bytes.push(flags);
|
||||||
bytes.push(1u8); /* count */
|
bytes.push(1u8); /* count */
|
||||||
bytes.append(&mut self.number.to_le_bytes().to_vec());
|
bytes.extend_from_slice(&self.number.to_le_bytes());
|
||||||
|
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -581,22 +550,20 @@ pub struct Device<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Aml for Device<'a> {
|
impl<'a> Aml for Device<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = Vec::new();
|
let mut tmp = Vec::new();
|
||||||
bytes.append(&mut self.path.to_aml_bytes());
|
self.path.append_aml_bytes(&mut tmp);
|
||||||
|
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
bytes.append(&mut child.to_aml_bytes());
|
child.append_aml_bytes(&mut tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
let pkg_length = create_pkg_length(&tmp, true);
|
||||||
pkg_length.reverse();
|
|
||||||
for byte in pkg_length {
|
|
||||||
bytes.insert(0, byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes.insert(0, 0x82); /* DeviceOp */
|
bytes.push(0x5b); /* ExtOpPrefix */
|
||||||
bytes.insert(0, 0x5b); /* ExtOpPrefix */
|
bytes.push(0x82); /* DeviceOp */
|
||||||
bytes
|
bytes.extend_from_slice(&pkg_length);
|
||||||
|
bytes.extend_from_slice(&tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -612,21 +579,18 @@ pub struct Scope<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Aml for Scope<'a> {
|
impl<'a> Aml for Scope<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = Vec::new();
|
let mut tmp = Vec::new();
|
||||||
bytes.append(&mut self.path.to_aml_bytes());
|
self.path.append_aml_bytes(&mut tmp);
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
bytes.append(&mut child.to_aml_bytes());
|
child.append_aml_bytes(&mut tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
let pkg_length = create_pkg_length(&tmp, true);
|
||||||
pkg_length.reverse();
|
|
||||||
for byte in pkg_length {
|
|
||||||
bytes.insert(0, byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes.insert(0, 0x10); /* ScopeOp */
|
bytes.push(0x10); /* ScopeOp */
|
||||||
bytes
|
bytes.extend_from_slice(&pkg_length);
|
||||||
|
bytes.extend_from_slice(&tmp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -655,23 +619,20 @@ impl<'a> Method<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Aml for Method<'a> {
|
impl<'a> Aml for Method<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = Vec::new();
|
let mut tmp = Vec::new();
|
||||||
bytes.append(&mut self.path.to_aml_bytes());
|
self.path.append_aml_bytes(&mut tmp);
|
||||||
let flags: u8 = (self.args & 0x7) | (self.serialized as u8) << 3;
|
let flags: u8 = (self.args & 0x7) | (self.serialized as u8) << 3;
|
||||||
bytes.push(flags);
|
tmp.push(flags);
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
bytes.append(&mut child.to_aml_bytes());
|
child.append_aml_bytes(&mut tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
let pkg_length = create_pkg_length(&tmp, true);
|
||||||
pkg_length.reverse();
|
|
||||||
for byte in pkg_length {
|
|
||||||
bytes.insert(0, byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes.insert(0, 0x14); /* MethodOp */
|
bytes.push(0x14); /* MethodOp */
|
||||||
bytes
|
bytes.extend_from_slice(&pkg_length);
|
||||||
|
bytes.extend_from_slice(&tmp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,10 +647,9 @@ impl<'a> Return<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Aml for Return<'a> {
|
impl<'a> Aml for Return<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![0xa4]; /* ReturnOp */
|
bytes.push(0xa4); /* ReturnOp */
|
||||||
bytes.append(&mut self.value.to_aml_bytes());
|
self.value.append_aml_bytes(bytes);
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -740,35 +700,32 @@ impl Field {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Aml for Field {
|
impl Aml for Field {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = Vec::new();
|
let mut tmp = Vec::new();
|
||||||
bytes.append(&mut self.path.to_aml_bytes());
|
self.path.append_aml_bytes(&mut tmp);
|
||||||
|
|
||||||
let flags: u8 = self.access_type as u8 | (self.update_rule as u8) << 5;
|
let flags: u8 = self.access_type as u8 | (self.update_rule as u8) << 5;
|
||||||
bytes.push(flags);
|
tmp.push(flags);
|
||||||
|
|
||||||
for field in self.fields.iter() {
|
for field in self.fields.iter() {
|
||||||
match field {
|
match field {
|
||||||
FieldEntry::Named(name, length) => {
|
FieldEntry::Named(name, length) => {
|
||||||
bytes.extend_from_slice(name);
|
tmp.extend_from_slice(name);
|
||||||
bytes.append(&mut create_pkg_length(&vec![0; *length], false));
|
tmp.extend_from_slice(&create_pkg_length(&vec![0; *length], false));
|
||||||
}
|
}
|
||||||
FieldEntry::Reserved(length) => {
|
FieldEntry::Reserved(length) => {
|
||||||
bytes.push(0x0);
|
tmp.push(0x0);
|
||||||
bytes.append(&mut create_pkg_length(&vec![0; *length], false));
|
tmp.extend_from_slice(&create_pkg_length(&vec![0; *length], false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
let pkg_length = create_pkg_length(&tmp, true);
|
||||||
pkg_length.reverse();
|
|
||||||
for byte in pkg_length {
|
|
||||||
bytes.insert(0, byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes.insert(0, 0x81); /* FieldOp */
|
bytes.push(0x5b); /* ExtOpPrefix */
|
||||||
bytes.insert(0, 0x5b); /* ExtOpPrefix */
|
bytes.push(0x81); /* FieldOp */
|
||||||
bytes
|
bytes.extend_from_slice(&pkg_length);
|
||||||
|
bytes.extend_from_slice(&tmp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -805,15 +762,13 @@ impl OpRegion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Aml for OpRegion {
|
impl Aml for OpRegion {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = Vec::new();
|
bytes.push(0x5b); /* ExtOpPrefix */
|
||||||
bytes.append(&mut self.path.to_aml_bytes());
|
bytes.push(0x80); /* OpRegionOp */
|
||||||
|
self.path.append_aml_bytes(bytes);
|
||||||
bytes.push(self.space as u8);
|
bytes.push(self.space as u8);
|
||||||
bytes.extend_from_slice(&self.offset.to_aml_bytes()); /* RegionOffset */
|
self.offset.append_aml_bytes(bytes); /* RegionOffset */
|
||||||
bytes.extend_from_slice(&self.length.to_aml_bytes()); /* RegionLen */
|
self.length.append_aml_bytes(bytes); /* RegionLen */
|
||||||
bytes.insert(0, 0x80); /* OpRegionOp */
|
|
||||||
bytes.insert(0, 0x5b); /* ExtOpPrefix */
|
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -832,21 +787,18 @@ impl<'a> If<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Aml for If<'a> {
|
impl<'a> Aml for If<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = Vec::new();
|
let mut tmp = Vec::new();
|
||||||
bytes.extend_from_slice(&self.predicate.to_aml_bytes());
|
self.predicate.append_aml_bytes(&mut tmp);
|
||||||
for child in self.if_children.iter() {
|
for child in self.if_children.iter() {
|
||||||
bytes.extend_from_slice(&child.to_aml_bytes());
|
child.append_aml_bytes(&mut tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
let pkg_length = create_pkg_length(&tmp, true);
|
||||||
pkg_length.reverse();
|
|
||||||
for byte in pkg_length {
|
|
||||||
bytes.insert(0, byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes.insert(0, 0xa0); /* IfOp */
|
bytes.push(0xa0); /* IfOp */
|
||||||
bytes
|
bytes.extend_from_slice(&pkg_length);
|
||||||
|
bytes.extend_from_slice(&tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -862,11 +814,10 @@ impl<'a> Equal<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Aml for Equal<'a> {
|
impl<'a> Aml for Equal<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![0x93]; /* LEqualOp */
|
bytes.push(0x93); /* LEqualOp */
|
||||||
bytes.extend_from_slice(&self.left.to_aml_bytes());
|
self.left.append_aml_bytes(bytes);
|
||||||
bytes.extend_from_slice(&self.right.to_aml_bytes());
|
self.right.append_aml_bytes(bytes);
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -882,33 +833,28 @@ impl<'a> LessThan<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Aml for LessThan<'a> {
|
impl<'a> Aml for LessThan<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![0x95]; /* LLessOp */
|
bytes.push(0x95); /* LLessOp */
|
||||||
bytes.extend_from_slice(&self.left.to_aml_bytes());
|
self.left.append_aml_bytes(bytes);
|
||||||
bytes.extend_from_slice(&self.right.to_aml_bytes());
|
self.right.append_aml_bytes(bytes);
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Arg(pub u8);
|
pub struct Arg(pub u8);
|
||||||
|
|
||||||
impl Aml for Arg {
|
impl Aml for Arg {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = Vec::new();
|
|
||||||
assert!(self.0 <= 6);
|
assert!(self.0 <= 6);
|
||||||
bytes.push(0x68 + self.0); /* Arg0Op */
|
bytes.push(0x68 + self.0); /* Arg0Op */
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Local(pub u8);
|
pub struct Local(pub u8);
|
||||||
|
|
||||||
impl Aml for Local {
|
impl Aml for Local {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = Vec::new();
|
|
||||||
assert!(self.0 <= 7);
|
assert!(self.0 <= 7);
|
||||||
bytes.push(0x60 + self.0); /* Local0Op */
|
bytes.push(0x60 + self.0); /* Local0Op */
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -924,11 +870,10 @@ impl<'a> Store<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Aml for Store<'a> {
|
impl<'a> Aml for Store<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![0x70]; /* StoreOp */
|
bytes.push(0x70); /* StoreOp */
|
||||||
bytes.extend_from_slice(&self.value.to_aml_bytes());
|
self.value.append_aml_bytes(bytes);
|
||||||
bytes.extend_from_slice(&self.name.to_aml_bytes());
|
self.name.append_aml_bytes(bytes);
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -944,12 +889,11 @@ impl Mutex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Aml for Mutex {
|
impl Aml for Mutex {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![0x5b]; /* ExtOpPrefix */
|
bytes.push(0x5b); /* ExtOpPrefix */
|
||||||
bytes.push(0x01); /* MutexOp */
|
bytes.push(0x01); /* MutexOp */
|
||||||
bytes.extend_from_slice(&self.path.to_aml_bytes());
|
self.path.append_aml_bytes(bytes);
|
||||||
bytes.push(self.sync_level);
|
bytes.push(self.sync_level);
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -965,12 +909,11 @@ impl Acquire {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Aml for Acquire {
|
impl Aml for Acquire {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![0x5b]; /* ExtOpPrefix */
|
bytes.push(0x5b); /* ExtOpPrefix */
|
||||||
bytes.push(0x23); /* AcquireOp */
|
bytes.push(0x23); /* AcquireOp */
|
||||||
bytes.extend_from_slice(&self.mutex.to_aml_bytes());
|
self.mutex.append_aml_bytes(bytes);
|
||||||
bytes.extend_from_slice(&self.timeout.to_le_bytes());
|
bytes.extend_from_slice(&self.timeout.to_le_bytes());
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -985,11 +928,10 @@ impl Release {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Aml for Release {
|
impl Aml for Release {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![0x5b]; /* ExtOpPrefix */
|
bytes.push(0x5b); /* ExtOpPrefix */
|
||||||
bytes.push(0x27); /* ReleaseOp */
|
bytes.push(0x27); /* ReleaseOp */
|
||||||
bytes.extend_from_slice(&self.mutex.to_aml_bytes());
|
self.mutex.append_aml_bytes(bytes);
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1005,11 +947,10 @@ impl<'a> Notify<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Aml for Notify<'a> {
|
impl<'a> Aml for Notify<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![0x86]; /* NotifyOp */
|
bytes.push(0x86); /* NotifyOp */
|
||||||
bytes.extend_from_slice(&self.object.to_aml_bytes());
|
self.object.append_aml_bytes(bytes);
|
||||||
bytes.extend_from_slice(&self.value.to_aml_bytes());
|
self.value.append_aml_bytes(bytes);
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1028,21 +969,18 @@ impl<'a> While<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Aml for While<'a> {
|
impl<'a> Aml for While<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = Vec::new();
|
let mut tmp = Vec::new();
|
||||||
bytes.extend_from_slice(&self.predicate.to_aml_bytes());
|
self.predicate.append_aml_bytes(&mut tmp);
|
||||||
for child in self.while_children.iter() {
|
for child in self.while_children.iter() {
|
||||||
bytes.extend_from_slice(&child.to_aml_bytes());
|
child.append_aml_bytes(&mut tmp)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
let pkg_length = create_pkg_length(&tmp, true);
|
||||||
pkg_length.reverse();
|
|
||||||
for byte in pkg_length {
|
|
||||||
bytes.insert(0, byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes.insert(0, 0xa2); /* WhileOp */
|
bytes.push(0xa2); /* WhileOp */
|
||||||
bytes
|
bytes.extend_from_slice(&pkg_length);
|
||||||
|
bytes.extend_from_slice(&tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1061,12 +999,11 @@ macro_rules! binary_op {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Aml for $name<'a> {
|
impl<'a> Aml for $name<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![$opcode]; /* Op for the binary operator */
|
bytes.push($opcode); /* Op for the binary operator */
|
||||||
bytes.extend_from_slice(&self.a.to_aml_bytes());
|
self.a.append_aml_bytes(bytes);
|
||||||
bytes.extend_from_slice(&self.b.to_aml_bytes());
|
self.b.append_aml_bytes(bytes);
|
||||||
bytes.extend_from_slice(&self.target.to_aml_bytes());
|
self.target.append_aml_bytes(bytes);
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1101,13 +1038,11 @@ impl<'a> MethodCall<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Aml for MethodCall<'a> {
|
impl<'a> Aml for MethodCall<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = Vec::new();
|
self.name.append_aml_bytes(bytes);
|
||||||
bytes.extend_from_slice(&self.name.to_aml_bytes());
|
|
||||||
for arg in self.args.iter() {
|
for arg in self.args.iter() {
|
||||||
bytes.extend_from_slice(&arg.to_aml_bytes());
|
arg.append_aml_bytes(bytes);
|
||||||
}
|
}
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1122,20 +1057,16 @@ impl Buffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Aml for Buffer {
|
impl Aml for Buffer {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = Vec::new();
|
let mut tmp = Vec::new();
|
||||||
bytes.extend_from_slice(&self.data.len().to_aml_bytes());
|
self.data.len().append_aml_bytes(&mut tmp);
|
||||||
bytes.extend_from_slice(&self.data);
|
tmp.extend_from_slice(&self.data);
|
||||||
|
|
||||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
let pkg_length = create_pkg_length(&tmp, true);
|
||||||
pkg_length.reverse();
|
|
||||||
for byte in pkg_length {
|
|
||||||
bytes.insert(0, byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes.insert(0, 0x11); /* BufferOp */
|
bytes.push(0x11); /* BufferOp */
|
||||||
|
bytes.extend_from_slice(&pkg_length);
|
||||||
bytes
|
bytes.extend_from_slice(&tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1158,22 +1089,20 @@ impl<'a, T> CreateField<'a, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Aml for CreateField<'a, u64> {
|
impl<'a> Aml for CreateField<'a, u64> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![0x8f]; /* CreateQWordFieldOp */
|
bytes.push(0x8f); /* CreateQWordFieldOp */
|
||||||
bytes.extend_from_slice(&self.buffer.to_aml_bytes());
|
self.buffer.append_aml_bytes(bytes);
|
||||||
bytes.extend_from_slice(&self.offset.to_aml_bytes());
|
self.offset.append_aml_bytes(bytes);
|
||||||
bytes.extend_from_slice(&self.field.to_aml_bytes());
|
self.field.append_aml_bytes(bytes);
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Aml for CreateField<'a, u32> {
|
impl<'a> Aml for CreateField<'a, u32> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
let mut bytes = vec![0x8a]; /* CreateDWordFieldOp */
|
bytes.push(0x8a); /* CreateDWordFieldOp */
|
||||||
bytes.extend_from_slice(&self.buffer.to_aml_bytes());
|
self.buffer.append_aml_bytes(bytes);
|
||||||
bytes.extend_from_slice(&self.offset.to_aml_bytes());
|
self.offset.append_aml_bytes(bytes);
|
||||||
bytes.extend_from_slice(&self.field.to_aml_bytes());
|
self.field.append_aml_bytes(bytes);
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ pub struct Rsdp {
|
|||||||
_reserved: [u8; 3],
|
_reserved: [u8; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: Rsdp only contains a series of integers
|
||||||
unsafe impl ByteValued for Rsdp {}
|
unsafe impl ByteValued for Rsdp {}
|
||||||
|
|
||||||
impl Rsdp {
|
impl Rsdp {
|
||||||
@@ -36,7 +37,7 @@ impl Rsdp {
|
|||||||
};
|
};
|
||||||
|
|
||||||
rsdp.checksum = super::generate_checksum(&rsdp.as_slice()[0..19]);
|
rsdp.checksum = super::generate_checksum(&rsdp.as_slice()[0..19]);
|
||||||
rsdp.extended_checksum = super::generate_checksum(&rsdp.as_slice());
|
rsdp.extended_checksum = super::generate_checksum(rsdp.as_slice());
|
||||||
rsdp
|
rsdp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ impl Sdt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_slice(&self) -> &[u8] {
|
pub fn as_slice(&self) -> &[u8] {
|
||||||
&self.data.as_slice()
|
self.data.as_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn append<T>(&mut self, value: T) {
|
pub fn append<T>(&mut self, value: T) {
|
||||||
|
|||||||
@@ -3,3 +3,6 @@ name = "api_client"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["The Cloud Hypervisor Authors"]
|
authors = ["The Cloud Hypervisor Authors"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
vmm-sys-util = "0.9.0"
|
||||||
|
|||||||
@@ -5,10 +5,13 @@
|
|||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
use std::os::unix::io::RawFd;
|
||||||
|
use vmm_sys_util::sock_ctrl_msg::ScmSocket;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
Socket(std::io::Error),
|
Socket(std::io::Error),
|
||||||
|
SocketSendFds(vmm_sys_util::errno::Error),
|
||||||
StatusCodeParsing(std::num::ParseIntError),
|
StatusCodeParsing(std::num::ParseIntError),
|
||||||
MissingProtocol,
|
MissingProtocol,
|
||||||
ContentLengthParsing(std::num::ParseIntError),
|
ContentLengthParsing(std::num::ParseIntError),
|
||||||
@@ -20,6 +23,7 @@ impl fmt::Display for Error {
|
|||||||
use Error::*;
|
use Error::*;
|
||||||
match self {
|
match self {
|
||||||
Socket(e) => write!(f, "Error writing to or reading from HTTP socket: {}", e),
|
Socket(e) => write!(f, "Error writing to or reading from HTTP socket: {}", e),
|
||||||
|
SocketSendFds(e) => write!(f, "Error writing to or reading from HTTP socket: {}", e),
|
||||||
StatusCodeParsing(e) => write!(f, "Error parsing HTTP status code: {}", e),
|
StatusCodeParsing(e) => write!(f, "Error parsing HTTP status code: {}", e),
|
||||||
MissingProtocol => write!(f, "HTTP output is missing protocol statement"),
|
MissingProtocol => write!(f, "HTTP output is missing protocol statement"),
|
||||||
ContentLengthParsing(e) => write!(f, "Error parsing HTTP Content-Length field: {}", e),
|
ContentLengthParsing(e) => write!(f, "Error parsing HTTP Content-Length field: {}", e),
|
||||||
@@ -133,21 +137,23 @@ fn parse_http_response(socket: &mut dyn Read) -> Result<Option<String>, Error> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn simple_api_command<T: Read + Write>(
|
pub fn simple_api_command_with_fds<T: Read + Write + ScmSocket>(
|
||||||
socket: &mut T,
|
socket: &mut T,
|
||||||
method: &str,
|
method: &str,
|
||||||
c: &str,
|
c: &str,
|
||||||
request_body: Option<&str>,
|
request_body: Option<&str>,
|
||||||
|
request_fds: Vec<RawFd>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
socket
|
socket
|
||||||
.write_all(
|
.send_with_fds(
|
||||||
format!(
|
&[format!(
|
||||||
"{} /api/v1/vm.{} HTTP/1.1\r\nHost: localhost\r\nAccept: */*\r\n",
|
"{} /api/v1/vm.{} HTTP/1.1\r\nHost: localhost\r\nAccept: */*\r\n",
|
||||||
method, c
|
method, c
|
||||||
)
|
)
|
||||||
.as_bytes(),
|
.as_bytes()],
|
||||||
|
&request_fds,
|
||||||
)
|
)
|
||||||
.map_err(Error::Socket)?;
|
.map_err(Error::SocketSendFds)?;
|
||||||
|
|
||||||
if let Some(request_body) = request_body {
|
if let Some(request_body) = request_body {
|
||||||
socket
|
socket
|
||||||
@@ -170,3 +176,12 @@ pub fn simple_api_command<T: Read + Write>(
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn simple_api_command<T: Read + Write + ScmSocket>(
|
||||||
|
socket: &mut T,
|
||||||
|
method: &str,
|
||||||
|
c: &str,
|
||||||
|
request_body: Option<&str>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
simple_api_command_with_fds(socket, method, c, request_body, Vec::new())
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,17 +11,21 @@ tdx = []
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
acpi_tables = { path = "../acpi_tables", optional = true }
|
acpi_tables = { path = "../acpi_tables", optional = true }
|
||||||
anyhow = "1.0"
|
anyhow = "1.0.51"
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
libc = "0.2.96"
|
libc = "0.2.108"
|
||||||
linux-loader = { version = "0.3.0", features = ["elf", "bzimage", "pe"] }
|
linux-loader = { version = "0.4.0", features = ["elf", "bzimage", "pe"] }
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
serde = {version = ">=1.0.27", features = ["rc"] }
|
serde = { version = "1.0.130", features = ["rc"] }
|
||||||
thiserror = "1.0"
|
serde_derive = "1.0.130"
|
||||||
|
thiserror = "1.0.30"
|
||||||
versionize = "0.1.6"
|
versionize = "0.1.6"
|
||||||
versionize_derive = "0.1.4"
|
versionize_derive = "0.1.4"
|
||||||
vm-fdt = { git = "https://github.com/rust-vmm/vm-fdt", branch = "master" }
|
vm-memory = { version = "0.6.0", features = ["backend-mmap", "backend-bitmap"] }
|
||||||
vm-memory = { version = "0.5.0", features = ["backend-mmap", "backend-bitmap"] }
|
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
|
vmm-sys-util = { version = "0.9.0", features = ["with-serde"] }
|
||||||
|
|
||||||
|
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||||
|
fdt_parser = { version = "0.1.3", package = 'fdt'}
|
||||||
|
vm-fdt = { git = "https://github.com/rust-vmm/vm-fdt", branch = "main" }
|
||||||
|
|||||||
@@ -6,10 +6,14 @@
|
|||||||
// 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;
|
||||||
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
|
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::result;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
use super::super::DeviceType;
|
use super::super::DeviceType;
|
||||||
use super::super::GuestMemoryMmap;
|
use super::super::GuestMemoryMmap;
|
||||||
@@ -21,7 +25,7 @@ use super::layout::{
|
|||||||
PCI_HIGH_BASE, PCI_MMCONFIG_SIZE, PCI_MMCONFIG_START,
|
PCI_HIGH_BASE, PCI_MMCONFIG_SIZE, PCI_MMCONFIG_START,
|
||||||
};
|
};
|
||||||
use vm_fdt::{FdtWriter, FdtWriterResult};
|
use vm_fdt::{FdtWriter, FdtWriterResult};
|
||||||
use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestMemoryError};
|
use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestMemoryError, GuestMemoryRegion};
|
||||||
|
|
||||||
// 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;
|
||||||
@@ -31,6 +35,12 @@ const MSI_PHANDLE: u32 = 2;
|
|||||||
const CLOCK_PHANDLE: u32 = 3;
|
const CLOCK_PHANDLE: u32 = 3;
|
||||||
// This is a value for uniquely identifying the FDT node containing the gpio controller.
|
// This is a value for uniquely identifying the FDT node containing the gpio controller.
|
||||||
const GPIO_PHANDLE: u32 = 4;
|
const GPIO_PHANDLE: u32 = 4;
|
||||||
|
// This is a value for virtio-iommu. Now only one virtio-iommu device is supported.
|
||||||
|
const VIRTIO_IOMMU_PHANDLE: u32 = 5;
|
||||||
|
// NOTE: Keep FIRST_VCPU_PHANDLE the last PHANDLE defined.
|
||||||
|
// This is a value for uniquely identifying the FDT node containing the first vCPU.
|
||||||
|
// The last number of vCPU phandle depends on the number of vCPUs.
|
||||||
|
const FIRST_VCPU_PHANDLE: u32 = 6;
|
||||||
|
|
||||||
// Read the documentation specified when appending the root node to the FDT.
|
// Read the documentation specified when appending the root node to the FDT.
|
||||||
const ADDRESS_CELLS: u32 = 0x2;
|
const ADDRESS_CELLS: u32 = 0x2;
|
||||||
@@ -69,17 +79,21 @@ pub enum Error {
|
|||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
/// Creates the flattened device tree for this aarch64 VM.
|
/// Creates the flattened device tree for this aarch64 VM.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
|
pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
|
||||||
guest_mem: &GuestMemoryMmap,
|
guest_mem: &GuestMemoryMmap,
|
||||||
cmdline: &CStr,
|
cmdline: &str,
|
||||||
vcpu_mpidr: Vec<u64>,
|
vcpu_mpidr: Vec<u64>,
|
||||||
|
vcpu_topology: Option<(u8, u8, u8)>,
|
||||||
device_info: &HashMap<(DeviceType, String), T, S>,
|
device_info: &HashMap<(DeviceType, String), T, S>,
|
||||||
gic_device: &dyn GicDevice,
|
gic_device: &dyn GicDevice,
|
||||||
initrd: &Option<InitramfsConfig>,
|
initrd: &Option<InitramfsConfig>,
|
||||||
pci_space_address: &(u64, u64),
|
pci_space_address: &(u64, u64),
|
||||||
|
numa_nodes: &NumaNodes,
|
||||||
|
virtio_iommu_bdf: Option<u32>,
|
||||||
) -> FdtWriterResult<Vec<u8>> {
|
) -> FdtWriterResult<Vec<u8>> {
|
||||||
// Allocate stuff necessary for the holding the blob.
|
// Allocate stuff necessary for the holding the blob.
|
||||||
let mut fdt = FdtWriter::new(&[]).unwrap();
|
let mut fdt = FdtWriter::new().unwrap();
|
||||||
|
|
||||||
// For an explanation why these nodes were introduced in the blob take a look at
|
// For an explanation why these nodes were introduced in the blob take a look at
|
||||||
// https://github.com/torvalds/linux/blob/master/Documentation/devicetree/booting-without-of.txt#L845
|
// https://github.com/torvalds/linux/blob/master/Documentation/devicetree/booting-without-of.txt#L845
|
||||||
@@ -95,15 +109,23 @@ pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHash
|
|||||||
// This is not mandatory but we use it to point the root node to the node
|
// This is not mandatory but we use it to point the root node to the node
|
||||||
// containing description of the interrupt controller for this VM.
|
// containing description of the interrupt controller for this VM.
|
||||||
fdt.property_u32("interrupt-parent", GIC_PHANDLE)?;
|
fdt.property_u32("interrupt-parent", GIC_PHANDLE)?;
|
||||||
create_cpu_nodes(&mut fdt, &vcpu_mpidr)?;
|
create_cpu_nodes(&mut fdt, &vcpu_mpidr, vcpu_topology, numa_nodes)?;
|
||||||
create_memory_node(&mut fdt, guest_mem)?;
|
create_memory_node(&mut fdt, guest_mem, numa_nodes)?;
|
||||||
create_chosen_node(&mut fdt, cmdline.to_str().unwrap(), initrd)?;
|
create_chosen_node(&mut fdt, cmdline, initrd)?;
|
||||||
create_gic_node(&mut fdt, gic_device)?;
|
create_gic_node(&mut fdt, gic_device)?;
|
||||||
create_timer_node(&mut fdt)?;
|
create_timer_node(&mut fdt)?;
|
||||||
create_clock_node(&mut fdt)?;
|
create_clock_node(&mut fdt)?;
|
||||||
create_psci_node(&mut fdt)?;
|
create_psci_node(&mut fdt)?;
|
||||||
create_devices_node(&mut fdt, device_info)?;
|
create_devices_node(&mut fdt, device_info)?;
|
||||||
create_pci_nodes(&mut fdt, pci_space_address.0, pci_space_address.1)?;
|
create_pci_nodes(
|
||||||
|
&mut fdt,
|
||||||
|
pci_space_address.0,
|
||||||
|
pci_space_address.1,
|
||||||
|
virtio_iommu_bdf,
|
||||||
|
)?;
|
||||||
|
if numa_nodes.len() > 1 {
|
||||||
|
create_distance_map_node(&mut fdt, numa_nodes)?;
|
||||||
|
}
|
||||||
|
|
||||||
// End Header node.
|
// End Header node.
|
||||||
fdt.end_node(root_node)?;
|
fdt.end_node(root_node)?;
|
||||||
@@ -123,14 +145,23 @@ pub fn write_fdt_to_memory(fdt_final: Vec<u8>, guest_mem: &GuestMemoryMmap) -> R
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Following are the auxiliary function for creating the different nodes that we append to our FDT.
|
// Following are the auxiliary function for creating the different nodes that we append to our FDT.
|
||||||
fn create_cpu_nodes(fdt: &mut FdtWriter, vcpu_mpidr: &[u64]) -> FdtWriterResult<()> {
|
fn create_cpu_nodes(
|
||||||
|
fdt: &mut FdtWriter,
|
||||||
|
vcpu_mpidr: &[u64],
|
||||||
|
vcpu_topology: Option<(u8, u8, u8)>,
|
||||||
|
numa_nodes: &NumaNodes,
|
||||||
|
) -> FdtWriterResult<()> {
|
||||||
// See https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/arm/cpus.yaml.
|
// See https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/arm/cpus.yaml.
|
||||||
let cpus_node = fdt.begin_node("cpus")?;
|
let cpus_node = fdt.begin_node("cpus")?;
|
||||||
fdt.property_u32("#address-cells", 0x1)?;
|
fdt.property_u32("#address-cells", 0x1)?;
|
||||||
fdt.property_u32("#size-cells", 0x0)?;
|
fdt.property_u32("#size-cells", 0x0)?;
|
||||||
let num_cpus = vcpu_mpidr.len();
|
|
||||||
|
|
||||||
for cpu_id in 0..num_cpus {
|
let num_cpus = vcpu_mpidr.len();
|
||||||
|
let threads_per_core = vcpu_topology.unwrap_or_default().0 as u8;
|
||||||
|
let cores_per_package = vcpu_topology.unwrap_or_default().1 as u8;
|
||||||
|
let packages = vcpu_topology.unwrap_or_default().2 as u8;
|
||||||
|
|
||||||
|
for (cpu_id, mpidr) in vcpu_mpidr.iter().enumerate().take(num_cpus) {
|
||||||
let cpu_name = format!("cpu@{:x}", cpu_id);
|
let cpu_name = format!("cpu@{:x}", cpu_id);
|
||||||
let cpu_node = fdt.begin_node(&cpu_name)?;
|
let cpu_node = fdt.begin_node(&cpu_name)?;
|
||||||
fdt.property_string("device_type", "cpu")?;
|
fdt.property_string("device_type", "cpu")?;
|
||||||
@@ -139,23 +170,132 @@ fn create_cpu_nodes(fdt: &mut FdtWriter, vcpu_mpidr: &[u64]) -> FdtWriterResult<
|
|||||||
// This is required on armv8 64-bit. See aforementioned documentation.
|
// This is required on armv8 64-bit. See aforementioned documentation.
|
||||||
fdt.property_string("enable-method", "psci")?;
|
fdt.property_string("enable-method", "psci")?;
|
||||||
}
|
}
|
||||||
fdt.property_u32("reg", cpu_id as u32)?;
|
// Set the field to first 24 bits of the MPIDR - Multiprocessor Affinity Register.
|
||||||
|
// See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0488c/BABHBJCI.html.
|
||||||
|
fdt.property_u32("reg", (mpidr & 0x7FFFFF) as u32)?;
|
||||||
|
fdt.property_u32("phandle", cpu_id as u32 + FIRST_VCPU_PHANDLE)?;
|
||||||
|
|
||||||
|
// Add `numa-node-id` property if there is any numa config.
|
||||||
|
if numa_nodes.len() > 1 {
|
||||||
|
for numa_node_idx in 0..numa_nodes.len() {
|
||||||
|
let numa_node = numa_nodes.get(&(numa_node_idx as u32));
|
||||||
|
if numa_node.unwrap().cpus.contains(&(cpu_id as u8)) {
|
||||||
|
fdt.property_u32("numa-node-id", numa_node_idx as u32)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fdt.end_node(cpu_node)?;
|
fdt.end_node(cpu_node)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there is a valid cpu topology config, create the cpu-map node.
|
||||||
|
if (threads_per_core > 0)
|
||||||
|
&& (cores_per_package > 0)
|
||||||
|
&& (packages > 0)
|
||||||
|
&& (num_cpus as u8 == threads_per_core * cores_per_package * packages)
|
||||||
|
{
|
||||||
|
let cpu_map_node = fdt.begin_node("cpu-map")?;
|
||||||
|
// Create mappings between CPU index and cluster,core, and thread.
|
||||||
|
let mut cluster_core_thread_to_cpuidx = HashMap::new();
|
||||||
|
for cpu_idx in 0..num_cpus as u8 {
|
||||||
|
if threads_per_core > 1 {
|
||||||
|
cluster_core_thread_to_cpuidx.insert(
|
||||||
|
(
|
||||||
|
cpu_idx / (cores_per_package * threads_per_core),
|
||||||
|
(cpu_idx / threads_per_core) % cores_per_package,
|
||||||
|
cpu_idx % threads_per_core,
|
||||||
|
),
|
||||||
|
cpu_idx,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
cluster_core_thread_to_cpuidx.insert(
|
||||||
|
(cpu_idx / cores_per_package, cpu_idx % cores_per_package, 0),
|
||||||
|
cpu_idx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create device tree nodes with regard of above mapping.
|
||||||
|
for cluster_idx in 0..packages {
|
||||||
|
let cluster_name = format!("cluster{:x}", cluster_idx);
|
||||||
|
let cluster_node = fdt.begin_node(&cluster_name)?;
|
||||||
|
|
||||||
|
for core_idx in 0..cores_per_package {
|
||||||
|
let core_name = format!("core{:x}", core_idx);
|
||||||
|
let core_node = fdt.begin_node(&core_name)?;
|
||||||
|
|
||||||
|
if threads_per_core > 1 {
|
||||||
|
for thread_idx in 0..threads_per_core {
|
||||||
|
let thread_name = format!("thread{:x}", thread_idx);
|
||||||
|
let thread_node = fdt.begin_node(&thread_name)?;
|
||||||
|
let cpu_idx = cluster_core_thread_to_cpuidx
|
||||||
|
.get(&(cluster_idx, core_idx, thread_idx))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
fdt.property_u32("cpu", *cpu_idx as u32 + FIRST_VCPU_PHANDLE)?;
|
||||||
|
fdt.end_node(thread_node)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let cpu_idx = cluster_core_thread_to_cpuidx
|
||||||
|
.get(&(cluster_idx, core_idx, 0))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
fdt.property_u32("cpu", *cpu_idx as u32 + FIRST_VCPU_PHANDLE)?;
|
||||||
|
}
|
||||||
|
fdt.end_node(core_node)?;
|
||||||
|
}
|
||||||
|
fdt.end_node(cluster_node)?;
|
||||||
|
}
|
||||||
|
fdt.end_node(cpu_map_node)?;
|
||||||
|
} else {
|
||||||
|
debug!("Boot using device tree, CPU topology is not (correctly) specified");
|
||||||
|
}
|
||||||
|
|
||||||
fdt.end_node(cpus_node)?;
|
fdt.end_node(cpus_node)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_memory_node(fdt: &mut FdtWriter, guest_mem: &GuestMemoryMmap) -> FdtWriterResult<()> {
|
fn create_memory_node(
|
||||||
let mem_size = guest_mem.last_addr().raw_value() - super::layout::RAM_64BIT_START + 1;
|
fdt: &mut FdtWriter,
|
||||||
// See https://github.com/torvalds/linux/blob/master/Documentation/devicetree/booting-without-of.txt#L960
|
guest_mem: &GuestMemoryMmap,
|
||||||
// for an explanation of this.
|
numa_nodes: &NumaNodes,
|
||||||
let mem_reg_prop = [super::layout::RAM_64BIT_START as u64, mem_size as u64];
|
) -> FdtWriterResult<()> {
|
||||||
|
if numa_nodes.len() > 1 {
|
||||||
|
for numa_node_idx in 0..numa_nodes.len() {
|
||||||
|
let numa_node = numa_nodes.get(&(numa_node_idx as u32));
|
||||||
|
// Each memory zone of numa will have its own memory node, but
|
||||||
|
// different numa nodes should not share same memory zones.
|
||||||
|
for memory_region in numa_node.unwrap().memory_regions.iter() {
|
||||||
|
let memory_region_start_addr: u64 = memory_region.start_addr().raw_value();
|
||||||
|
let memory_region_size: u64 = memory_region.size() as u64;
|
||||||
|
let mem_reg_prop = [memory_region_start_addr, memory_region_size];
|
||||||
|
// With feature `acpi` enabled, RAM at 0-4M is for edk2 only
|
||||||
|
// and should be hidden to the guest.
|
||||||
|
#[cfg(feature = "acpi")]
|
||||||
|
if memory_region_start_addr == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let memory_node = fdt.begin_node("memory")?;
|
let memory_node_name = format!("memory@{:x}", memory_region_start_addr);
|
||||||
fdt.property_string("device_type", "memory")?;
|
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
fdt.property_string("device_type", "memory")?;
|
||||||
fdt.end_node(memory_node)?;
|
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||||
|
fdt.property_u32("numa-node-id", numa_node_idx as u32)?;
|
||||||
|
fdt.end_node(memory_node)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mem_size = guest_mem.last_addr().raw_value() - super::layout::RAM_64BIT_START + 1;
|
||||||
|
// See https://github.com/torvalds/linux/blob/master/Documentation/devicetree/booting-without-of.txt#L960
|
||||||
|
// for an explanation of this.
|
||||||
|
let mem_reg_prop = [super::layout::RAM_64BIT_START as u64, mem_size as u64];
|
||||||
|
let memory_node = fdt.begin_node("memory")?;
|
||||||
|
|
||||||
|
fdt.property_string("device_type", "memory")?;
|
||||||
|
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||||
|
fdt.end_node(memory_node)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -191,7 +331,7 @@ fn create_gic_node(fdt: &mut FdtWriter, gic_device: &dyn GicDevice) -> FdtWriter
|
|||||||
// interrupt source. The type shall be a <u32> and the value shall be 3 if no PPI affinity description
|
// interrupt source. The type shall be a <u32> and the value shall be 3 if no PPI affinity description
|
||||||
// is required.
|
// is required.
|
||||||
fdt.property_u32("#interrupt-cells", 3)?;
|
fdt.property_u32("#interrupt-cells", 3)?;
|
||||||
fdt.property_array_u64("reg", &gic_reg_prop)?;
|
fdt.property_array_u64("reg", gic_reg_prop)?;
|
||||||
fdt.property_u32("phandle", GIC_PHANDLE)?;
|
fdt.property_u32("phandle", GIC_PHANDLE)?;
|
||||||
fdt.property_u32("#address-cells", 2)?;
|
fdt.property_u32("#address-cells", 2)?;
|
||||||
fdt.property_u32("#size-cells", 2)?;
|
fdt.property_u32("#size-cells", 2)?;
|
||||||
@@ -210,7 +350,7 @@ fn create_gic_node(fdt: &mut FdtWriter, gic_device: &dyn GicDevice) -> FdtWriter
|
|||||||
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.msi_properties();
|
let msi_reg_prop = gic_device.msi_properties();
|
||||||
fdt.property_array_u64("reg", &msi_reg_prop)?;
|
fdt.property_array_u64("reg", msi_reg_prop)?;
|
||||||
fdt.end_node(msic_node)?;
|
fdt.end_node(msic_node)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,6 +549,7 @@ fn create_pci_nodes(
|
|||||||
fdt: &mut FdtWriter,
|
fdt: &mut FdtWriter,
|
||||||
pci_device_base: u64,
|
pci_device_base: u64,
|
||||||
pci_device_size: u64,
|
pci_device_size: u64,
|
||||||
|
virtio_iommu_bdf: Option<u32>,
|
||||||
) -> FdtWriterResult<()> {
|
) -> FdtWriterResult<()> {
|
||||||
// Add node for PCIe controller.
|
// Add node for PCIe controller.
|
||||||
// See Documentation/devicetree/bindings/pci/host-generic-pci.txt in the kernel
|
// See Documentation/devicetree/bindings/pci/host-generic-pci.txt in the kernel
|
||||||
@@ -472,7 +613,162 @@ fn create_pci_nodes(
|
|||||||
fdt.property_null("interrupt-map-mask")?;
|
fdt.property_null("interrupt-map-mask")?;
|
||||||
fdt.property_null("dma-coherent")?;
|
fdt.property_null("dma-coherent")?;
|
||||||
fdt.property_u32("msi-parent", MSI_PHANDLE)?;
|
fdt.property_u32("msi-parent", MSI_PHANDLE)?;
|
||||||
|
|
||||||
|
if let Some(virtio_iommu_bdf) = virtio_iommu_bdf {
|
||||||
|
// See kernel document Documentation/devicetree/bindings/pci/pci-iommu.txt
|
||||||
|
// for 'iommu-map' attribute setting.
|
||||||
|
let iommu_map = [
|
||||||
|
0_u32,
|
||||||
|
VIRTIO_IOMMU_PHANDLE,
|
||||||
|
0_u32,
|
||||||
|
virtio_iommu_bdf,
|
||||||
|
virtio_iommu_bdf + 1,
|
||||||
|
VIRTIO_IOMMU_PHANDLE,
|
||||||
|
virtio_iommu_bdf + 1,
|
||||||
|
0xffff - virtio_iommu_bdf,
|
||||||
|
];
|
||||||
|
fdt.property_array_u32("iommu-map", &iommu_map)?;
|
||||||
|
|
||||||
|
// See kernel document Documentation/devicetree/bindings/virtio/iommu.txt
|
||||||
|
// for virtio-iommu node settings.
|
||||||
|
let virtio_iommu_node_name = format!("virtio_iommu@{:x}", virtio_iommu_bdf);
|
||||||
|
let virtio_iommu_node = fdt.begin_node(&virtio_iommu_node_name)?;
|
||||||
|
fdt.property_u32("#iommu-cells", 1)?;
|
||||||
|
fdt.property_string("compatible", "virtio,pci-iommu")?;
|
||||||
|
|
||||||
|
// 'reg' is a five-cell address encoded as
|
||||||
|
// (phys.hi phys.mid phys.lo size.hi size.lo). phys.hi should contain the
|
||||||
|
// device's BDF as 0b00000000 bbbbbbbb dddddfff 00000000. The other cells
|
||||||
|
// should be zero.
|
||||||
|
let reg = [virtio_iommu_bdf << 8, 0_u32, 0_u32, 0_u32, 0_u32];
|
||||||
|
fdt.property_array_u32("reg", ®)?;
|
||||||
|
fdt.property_u32("phandle", VIRTIO_IOMMU_PHANDLE)?;
|
||||||
|
|
||||||
|
fdt.end_node(virtio_iommu_node)?;
|
||||||
|
}
|
||||||
|
|
||||||
fdt.end_node(pci_node)?;
|
fdt.end_node(pci_node)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_distance_map_node(fdt: &mut FdtWriter, numa_nodes: &NumaNodes) -> FdtWriterResult<()> {
|
||||||
|
let distance_map_node = fdt.begin_node("distance-map")?;
|
||||||
|
fdt.property_string("compatible", "numa-distance-map-v1")?;
|
||||||
|
// Construct the distance matrix.
|
||||||
|
// 1. We use the word entry to describe a distance from a node to
|
||||||
|
// its destination, e.g. 0 -> 1 = 20 is described as <0 1 20>.
|
||||||
|
// 2. Each entry represents distance from first node to second node.
|
||||||
|
// The distances are equal in either direction.
|
||||||
|
// 3. The distance from a node to self (local distance) is represented
|
||||||
|
// with value 10 and all internode distance should be represented with
|
||||||
|
// a value greater than 10.
|
||||||
|
// 4. distance-matrix should have entries in lexicographical ascending
|
||||||
|
// order of nodes.
|
||||||
|
let mut distance_matrix = Vec::new();
|
||||||
|
for numa_node_idx in 0..numa_nodes.len() {
|
||||||
|
let numa_node = numa_nodes.get(&(numa_node_idx as u32));
|
||||||
|
for dest_numa_node in 0..numa_node.unwrap().distances.len() + 1 {
|
||||||
|
if numa_node_idx == dest_numa_node {
|
||||||
|
distance_matrix.push(numa_node_idx as u32);
|
||||||
|
distance_matrix.push(dest_numa_node as u32);
|
||||||
|
distance_matrix.push(10_u32);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
distance_matrix.push(numa_node_idx as u32);
|
||||||
|
distance_matrix.push(dest_numa_node as u32);
|
||||||
|
distance_matrix.push(
|
||||||
|
*numa_node
|
||||||
|
.unwrap()
|
||||||
|
.distances
|
||||||
|
.get(&(dest_numa_node as u32))
|
||||||
|
.unwrap() as u32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fdt.property_array_u32("distance-matrix", distance_matrix.as_ref())?;
|
||||||
|
fdt.end_node(distance_map_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ pub fn set_dist_regs(gic: &Arc<dyn hypervisor::Device>, state: &[u32]) -> Result
|
|||||||
|
|
||||||
for dreg in VGIC_DIST_REGS {
|
for dreg in VGIC_DIST_REGS {
|
||||||
let mut base = dreg.base + REG_SIZE as u32 * dreg.bpi as u32;
|
let mut base = dreg.base + REG_SIZE as u32 * dreg.bpi as u32;
|
||||||
let end = compute_reg_len(gic, &dreg, base)?;
|
let end = compute_reg_len(gic, dreg, base)?;
|
||||||
|
|
||||||
while base < end {
|
while base < end {
|
||||||
let val = state[idx];
|
let val = state[idx];
|
||||||
@@ -169,7 +169,7 @@ pub fn get_dist_regs(gic: &Arc<dyn hypervisor::Device>) -> Result<Vec<u32>> {
|
|||||||
|
|
||||||
for dreg in VGIC_DIST_REGS {
|
for dreg in VGIC_DIST_REGS {
|
||||||
let mut base = dreg.base + REG_SIZE as u32 * dreg.bpi as u32;
|
let mut base = dreg.base + REG_SIZE as u32 * dreg.bpi as u32;
|
||||||
let end = compute_reg_len(gic, &dreg, base)?;
|
let end = compute_reg_len(gic, dreg, base)?;
|
||||||
|
|
||||||
while base < end {
|
while base < end {
|
||||||
let val: u32 = 0;
|
let val: u32 = 0;
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
// Copyright 2021 Arm Limited (or its affiliates). All rights reserved.
|
||||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// This file implements the GicV3 device.
|
||||||
|
|
||||||
pub mod kvm {
|
pub mod kvm {
|
||||||
use crate::aarch64::gic::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
use crate::aarch64::gic::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
||||||
@@ -50,7 +53,7 @@ pub mod kvm {
|
|||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
pub struct KvmGicV3 {
|
pub struct KvmGicV3 {
|
||||||
/// The hypervisor agnostic device
|
/// The hypervisor agnostic device for the GicV3
|
||||||
device: Arc<dyn hypervisor::Device>,
|
device: Arc<dyn hypervisor::Device>,
|
||||||
|
|
||||||
/// Vector holding values of GICR_TYPER for each vCPU
|
/// Vector holding values of GICR_TYPER for each vCPU
|
||||||
@@ -75,23 +78,17 @@ pub mod kvm {
|
|||||||
impl VersionMapped for Gicv3State {}
|
impl VersionMapped for Gicv3State {}
|
||||||
|
|
||||||
impl KvmGicV3 {
|
impl KvmGicV3 {
|
||||||
// Unfortunately bindgen omits defines that are based on other defines.
|
|
||||||
// See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel.
|
|
||||||
pub const SZ_64K: u64 = 0x0001_0000;
|
|
||||||
const KVM_VGIC_V3_DIST_SIZE: u64 = KvmGicV3::SZ_64K;
|
|
||||||
const KVM_VGIC_V3_REDIST_SIZE: u64 = (2 * KvmGicV3::SZ_64K);
|
|
||||||
|
|
||||||
// Device trees specific constants
|
// Device trees specific constants
|
||||||
pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
|
pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
|
||||||
|
|
||||||
/// Get the address of the GIC distributor.
|
/// Get the address of the GIC distributor.
|
||||||
pub fn get_dist_addr() -> u64 {
|
pub fn get_dist_addr() -> u64 {
|
||||||
layout::MAPPED_IO_START - KvmGicV3::KVM_VGIC_V3_DIST_SIZE
|
layout::GIC_V3_DIST_START
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the size of the GIC distributor.
|
/// Get the size of the GIC distributor.
|
||||||
pub fn get_dist_size() -> u64 {
|
pub fn get_dist_size() -> u64 {
|
||||||
KvmGicV3::KVM_VGIC_V3_DIST_SIZE
|
layout::GIC_V3_DIST_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the address of the GIC redistributors.
|
/// Get the address of the GIC redistributors.
|
||||||
@@ -101,25 +98,22 @@ pub mod kvm {
|
|||||||
|
|
||||||
/// Get the size of the GIC redistributors.
|
/// Get the size of the GIC redistributors.
|
||||||
pub fn get_redists_size(vcpu_count: u64) -> u64 {
|
pub fn get_redists_size(vcpu_count: u64) -> u64 {
|
||||||
vcpu_count * KvmGicV3::KVM_VGIC_V3_REDIST_SIZE
|
vcpu_count * layout::GIC_V3_REDIST_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save the state of GIC.
|
/// Save the state of GIC.
|
||||||
fn state(&self, gicr_typers: &[u64]) -> Result<Gicv3State> {
|
fn state(&self, gicr_typers: &[u64]) -> Result<Gicv3State> {
|
||||||
// Flush redistributors pending tables to guest RAM.
|
|
||||||
save_pending_tables(&self.device()).map_err(Error::SavePendingTables)?;
|
|
||||||
|
|
||||||
let gicd_ctlr =
|
let gicd_ctlr =
|
||||||
read_ctlr(&self.device()).map_err(Error::SaveDistributorCtrlRegisters)?;
|
read_ctlr(self.device()).map_err(Error::SaveDistributorCtrlRegisters)?;
|
||||||
|
|
||||||
let dist_state =
|
let dist_state =
|
||||||
get_dist_regs(&self.device()).map_err(Error::SaveDistributorRegisters)?;
|
get_dist_regs(self.device()).map_err(Error::SaveDistributorRegisters)?;
|
||||||
|
|
||||||
let rdist_state = get_redist_regs(&self.device(), &gicr_typers)
|
let rdist_state = get_redist_regs(self.device(), gicr_typers)
|
||||||
.map_err(Error::SaveRedistributorRegisters)?;
|
.map_err(Error::SaveRedistributorRegisters)?;
|
||||||
|
|
||||||
let icc_state =
|
let icc_state =
|
||||||
get_icc_regs(&self.device(), &gicr_typers).map_err(Error::SaveIccRegisters)?;
|
get_icc_regs(self.device(), gicr_typers).map_err(Error::SaveIccRegisters)?;
|
||||||
|
|
||||||
Ok(Gicv3State {
|
Ok(Gicv3State {
|
||||||
dist: dist_state,
|
dist: dist_state,
|
||||||
@@ -131,16 +125,16 @@ pub mod kvm {
|
|||||||
|
|
||||||
/// Restore the state of GIC.
|
/// Restore the state of GIC.
|
||||||
fn set_state(&mut self, gicr_typers: &[u64], state: &Gicv3State) -> Result<()> {
|
fn set_state(&mut self, gicr_typers: &[u64], state: &Gicv3State) -> Result<()> {
|
||||||
write_ctlr(&self.device(), state.gicd_ctlr)
|
write_ctlr(self.device(), state.gicd_ctlr)
|
||||||
.map_err(Error::RestoreDistributorCtrlRegisters)?;
|
.map_err(Error::RestoreDistributorCtrlRegisters)?;
|
||||||
|
|
||||||
set_dist_regs(&self.device(), &state.dist)
|
set_dist_regs(self.device(), &state.dist)
|
||||||
.map_err(Error::RestoreDistributorRegisters)?;
|
.map_err(Error::RestoreDistributorRegisters)?;
|
||||||
|
|
||||||
set_redist_regs(&self.device(), gicr_typers, &state.rdist)
|
set_redist_regs(self.device(), gicr_typers, &state.rdist)
|
||||||
.map_err(Error::RestoreRedistributorRegisters)?;
|
.map_err(Error::RestoreRedistributorRegisters)?;
|
||||||
|
|
||||||
set_icc_regs(&self.device(), &gicr_typers, &state.icc)
|
set_icc_regs(self.device(), gicr_typers, &state.icc)
|
||||||
.map_err(Error::RestoreIccRegisters)?;
|
.map_err(Error::RestoreIccRegisters)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -168,6 +162,8 @@ pub mod kvm {
|
|||||||
self.vcpu_count
|
self.vcpu_count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_its_device(&mut self, _its_device: Option<Arc<dyn hypervisor::Device>>) {}
|
||||||
|
|
||||||
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
|
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
|
||||||
let gicr_typers = construct_gicr_typers(vcpu_states);
|
let gicr_typers = construct_gicr_typers(vcpu_states);
|
||||||
self.gicr_typers = gicr_typers;
|
self.gicr_typers = gicr_typers;
|
||||||
@@ -202,7 +198,7 @@ pub mod kvm {
|
|||||||
|
|
||||||
fn init_device_attributes(
|
fn init_device_attributes(
|
||||||
_vm: &Arc<dyn hypervisor::Vm>,
|
_vm: &Arc<dyn hypervisor::Vm>,
|
||||||
gic_device: &dyn GicDevice,
|
gic_device: &mut dyn GicDevice,
|
||||||
) -> crate::aarch64::gic::Result<()> {
|
) -> crate::aarch64::gic::Result<()> {
|
||||||
/* Setting up the distributor attribute.
|
/* Setting up the distributor attribute.
|
||||||
We are placing the GIC below 1GB so we need to substract the size of the distributor.
|
We are placing the GIC below 1GB so we need to substract the size of the distributor.
|
||||||
@@ -250,7 +246,16 @@ pub mod kvm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pausable for KvmGicV3 {}
|
impl Pausable for KvmGicV3 {
|
||||||
|
fn pause(&mut self) -> std::result::Result<(), MigratableError> {
|
||||||
|
// Flush redistributors pending tables to guest RAM.
|
||||||
|
save_pending_tables(self.device()).map_err(|e| {
|
||||||
|
MigratableError::Pause(anyhow!("Could not save GICv3 GIC pending tables {:?}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Transportable for KvmGicV3 {}
|
impl Transportable for KvmGicV3 {}
|
||||||
impl Migratable for KvmGicV3 {}
|
impl Migratable for KvmGicV3 {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,157 @@
|
|||||||
// Copyright 2020 ARM Limited
|
// Copyright 2021 Arm Limited (or its affiliates). All rights reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// This file implements the GicV3 device with ITS (Virtual Interrupt Translation Service).
|
||||||
|
|
||||||
pub mod kvm {
|
pub mod kvm {
|
||||||
use std::any::Any;
|
use crate::aarch64::gic::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
||||||
use std::sync::Arc;
|
use crate::aarch64::gic::icc_regs::{get_icc_regs, set_icc_regs};
|
||||||
use std::{boxed::Box, result};
|
use crate::aarch64::gic::redist_regs::{
|
||||||
type Result<T> = result::Result<T, Error>;
|
construct_gicr_typers, get_redist_regs, set_redist_regs,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::aarch64::gic::gicv3::kvm::KvmGicV3;
|
use crate::aarch64::gic::gicv3::kvm::KvmGicV3;
|
||||||
use crate::aarch64::gic::kvm::KvmGicDevice;
|
use crate::aarch64::gic::kvm::{save_pending_tables, KvmGicDevice};
|
||||||
use crate::aarch64::gic::{Error, GicDevice};
|
use crate::aarch64::gic::GicDevice;
|
||||||
|
use crate::layout;
|
||||||
|
use anyhow::anyhow;
|
||||||
use hypervisor::kvm::kvm_bindings;
|
use hypervisor::kvm::kvm_bindings;
|
||||||
use hypervisor::CpuState;
|
use hypervisor::CpuState;
|
||||||
|
use std::any::Any;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::{boxed::Box, result};
|
||||||
|
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||||
|
use versionize_derive::Versionize;
|
||||||
|
use vm_migration::{
|
||||||
|
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable,
|
||||||
|
VersionMapped,
|
||||||
|
};
|
||||||
|
|
||||||
|
const GITS_CTLR: u32 = 0x0000;
|
||||||
|
const GITS_IIDR: u32 = 0x0004;
|
||||||
|
const GITS_CBASER: u32 = 0x0080;
|
||||||
|
const GITS_CWRITER: u32 = 0x0088;
|
||||||
|
const GITS_CREADR: u32 = 0x0090;
|
||||||
|
const GITS_BASER: u32 = 0x0100;
|
||||||
|
|
||||||
|
/// Errors thrown while saving/restoring the GICv3ITS.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Error in saving RDIST pending tables into guest RAM.
|
||||||
|
SavePendingTables(crate::aarch64::gic::Error),
|
||||||
|
/// Error in saving GIC distributor registers.
|
||||||
|
SaveDistributorRegisters(crate::aarch64::gic::Error),
|
||||||
|
/// Error in restoring GIC distributor registers.
|
||||||
|
RestoreDistributorRegisters(crate::aarch64::gic::Error),
|
||||||
|
/// Error in saving GIC distributor control registers.
|
||||||
|
SaveDistributorCtrlRegisters(crate::aarch64::gic::Error),
|
||||||
|
/// Error in restoring GIC distributor control registers.
|
||||||
|
RestoreDistributorCtrlRegisters(crate::aarch64::gic::Error),
|
||||||
|
/// Error in saving GIC redistributor registers.
|
||||||
|
SaveRedistributorRegisters(crate::aarch64::gic::Error),
|
||||||
|
/// Error in restoring GIC redistributor registers.
|
||||||
|
RestoreRedistributorRegisters(crate::aarch64::gic::Error),
|
||||||
|
/// Error in saving GIC CPU interface registers.
|
||||||
|
SaveIccRegisters(crate::aarch64::gic::Error),
|
||||||
|
/// Error in restoring GIC CPU interface registers.
|
||||||
|
RestoreIccRegisters(crate::aarch64::gic::Error),
|
||||||
|
/// Error in saving GICv3ITS IIDR register.
|
||||||
|
SaveITSIIDR(crate::aarch64::gic::Error),
|
||||||
|
/// Error in restoring GICv3ITS IIDR register.
|
||||||
|
RestoreITSIIDR(crate::aarch64::gic::Error),
|
||||||
|
/// Error in saving GICv3ITS CBASER register.
|
||||||
|
SaveITSCBASER(crate::aarch64::gic::Error),
|
||||||
|
/// Error in restoring GICv3ITS CBASER register.
|
||||||
|
RestoreITSCBASER(crate::aarch64::gic::Error),
|
||||||
|
/// Error in saving GICv3ITS CREADR register.
|
||||||
|
SaveITSCREADR(crate::aarch64::gic::Error),
|
||||||
|
/// Error in restoring GICv3ITS CREADR register.
|
||||||
|
RestoreITSCREADR(crate::aarch64::gic::Error),
|
||||||
|
/// Error in saving GICv3ITS CWRITER register.
|
||||||
|
SaveITSCWRITER(crate::aarch64::gic::Error),
|
||||||
|
/// Error in restoring GICv3ITS CWRITER register.
|
||||||
|
RestoreITSCWRITER(crate::aarch64::gic::Error),
|
||||||
|
/// Error in saving GICv3ITS BASER register.
|
||||||
|
SaveITSBASER(crate::aarch64::gic::Error),
|
||||||
|
/// Error in restoring GICv3ITS BASER register.
|
||||||
|
RestoreITSBASER(crate::aarch64::gic::Error),
|
||||||
|
/// Error in saving GICv3ITS CTLR register.
|
||||||
|
SaveITSCTLR(crate::aarch64::gic::Error),
|
||||||
|
/// Error in restoring GICv3ITS CTLR register.
|
||||||
|
RestoreITSCTLR(crate::aarch64::gic::Error),
|
||||||
|
/// Error in saving GICv3ITS restore tables.
|
||||||
|
SaveITSTables(crate::aarch64::gic::Error),
|
||||||
|
/// Error in restoring GICv3ITS restore tables.
|
||||||
|
RestoreITSTables(crate::aarch64::gic::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
|
/// Access an ITS device attribute.
|
||||||
|
///
|
||||||
|
/// This is a helper function to get/set the ITS device attribute depending
|
||||||
|
/// the bool parameter `set` provided.
|
||||||
|
pub fn gicv3_its_attr_access(
|
||||||
|
its_device: &Arc<dyn hypervisor::Device>,
|
||||||
|
group: u32,
|
||||||
|
attr: u32,
|
||||||
|
val: &u64,
|
||||||
|
set: bool,
|
||||||
|
) -> crate::aarch64::gic::Result<()> {
|
||||||
|
let mut gicv3_its_attr = kvm_bindings::kvm_device_attr {
|
||||||
|
group,
|
||||||
|
attr: attr as u64,
|
||||||
|
addr: val as *const u64 as u64,
|
||||||
|
flags: 0,
|
||||||
|
};
|
||||||
|
if set {
|
||||||
|
its_device
|
||||||
|
.set_device_attr(&gicv3_its_attr)
|
||||||
|
.map_err(crate::aarch64::gic::Error::SetDeviceAttribute)?;
|
||||||
|
} else {
|
||||||
|
its_device
|
||||||
|
.get_device_attr(&mut gicv3_its_attr)
|
||||||
|
.map_err(crate::aarch64::gic::Error::GetDeviceAttribute)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Function that saves/restores ITS tables into guest RAM.
|
||||||
|
///
|
||||||
|
/// The tables get flushed to guest RAM whenever the VM gets stopped.
|
||||||
|
pub fn gicv3_its_tables_access(
|
||||||
|
its_device: &Arc<dyn hypervisor::Device>,
|
||||||
|
save: bool,
|
||||||
|
) -> crate::aarch64::gic::Result<()> {
|
||||||
|
let attr: u64;
|
||||||
|
if save {
|
||||||
|
attr = u64::from(kvm_bindings::KVM_DEV_ARM_ITS_SAVE_TABLES);
|
||||||
|
} else {
|
||||||
|
attr = u64::from(kvm_bindings::KVM_DEV_ARM_ITS_RESTORE_TABLES);
|
||||||
|
}
|
||||||
|
|
||||||
|
let init_gic_attr = kvm_bindings::kvm_device_attr {
|
||||||
|
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||||
|
attr,
|
||||||
|
addr: 0,
|
||||||
|
flags: 0,
|
||||||
|
};
|
||||||
|
its_device
|
||||||
|
.set_device_attr(&init_gic_attr)
|
||||||
|
.map_err(crate::aarch64::gic::Error::SetDeviceAttribute)
|
||||||
|
}
|
||||||
|
|
||||||
pub struct KvmGicV3Its {
|
pub struct KvmGicV3Its {
|
||||||
/// The hypervisor agnostic device
|
/// The hypervisor agnostic device for the GicV3
|
||||||
device: Arc<dyn hypervisor::Device>,
|
device: Arc<dyn hypervisor::Device>,
|
||||||
|
|
||||||
|
/// The hypervisor agnostic device for the Its device
|
||||||
|
its_device: Option<Arc<dyn hypervisor::Device>>,
|
||||||
|
|
||||||
|
/// Vector holding values of GICR_TYPER for each vCPU
|
||||||
|
gicr_typers: Vec<u64>,
|
||||||
|
|
||||||
/// GIC device properties, to be used for setting up the fdt entry
|
/// GIC device properties, to be used for setting up the fdt entry
|
||||||
gic_properties: [u64; 4],
|
gic_properties: [u64; 4],
|
||||||
|
|
||||||
@@ -26,16 +162,199 @@ pub mod kvm {
|
|||||||
vcpu_count: u64,
|
vcpu_count: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KvmGicV3Its {
|
#[derive(Versionize)]
|
||||||
const KVM_VGIC_V3_ITS_SIZE: u64 = (2 * KvmGicV3::SZ_64K);
|
pub struct Gicv3ItsState {
|
||||||
|
dist: Vec<u32>,
|
||||||
|
rdist: Vec<u32>,
|
||||||
|
icc: Vec<u32>,
|
||||||
|
// special register that enables interrupts and affinity routing
|
||||||
|
gicd_ctlr: u32,
|
||||||
|
its_ctlr: u64,
|
||||||
|
its_iidr: u64,
|
||||||
|
its_cbaser: u64,
|
||||||
|
its_cwriter: u64,
|
||||||
|
its_creadr: u64,
|
||||||
|
its_baser: [u64; 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VersionMapped for Gicv3ItsState {}
|
||||||
|
|
||||||
|
impl KvmGicV3Its {
|
||||||
fn get_msi_size() -> u64 {
|
fn get_msi_size() -> u64 {
|
||||||
KvmGicV3Its::KVM_VGIC_V3_ITS_SIZE
|
layout::GIC_V3_ITS_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_msi_addr(vcpu_count: u64) -> u64 {
|
fn get_msi_addr(vcpu_count: u64) -> u64 {
|
||||||
KvmGicV3::get_redists_addr(vcpu_count) - KvmGicV3Its::get_msi_size()
|
KvmGicV3::get_redists_addr(vcpu_count) - KvmGicV3Its::get_msi_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Save the state of GICv3ITS.
|
||||||
|
fn state(&self, gicr_typers: &[u64]) -> Result<Gicv3ItsState> {
|
||||||
|
let gicd_ctlr =
|
||||||
|
read_ctlr(self.device()).map_err(Error::SaveDistributorCtrlRegisters)?;
|
||||||
|
|
||||||
|
let dist_state =
|
||||||
|
get_dist_regs(self.device()).map_err(Error::SaveDistributorRegisters)?;
|
||||||
|
|
||||||
|
let rdist_state = get_redist_regs(self.device(), gicr_typers)
|
||||||
|
.map_err(Error::SaveRedistributorRegisters)?;
|
||||||
|
|
||||||
|
let icc_state =
|
||||||
|
get_icc_regs(self.device(), gicr_typers).map_err(Error::SaveIccRegisters)?;
|
||||||
|
|
||||||
|
let its_baser_state: [u64; 8] = [0; 8];
|
||||||
|
for i in 0..8 {
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_BASER + i * 8,
|
||||||
|
&its_baser_state[i as usize],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.map_err(Error::SaveITSBASER)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let its_ctlr_state: u64 = 0;
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_CTLR,
|
||||||
|
&its_ctlr_state,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.map_err(Error::SaveITSCTLR)?;
|
||||||
|
|
||||||
|
let its_cbaser_state: u64 = 0;
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_CBASER,
|
||||||
|
&its_cbaser_state,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.map_err(Error::SaveITSCBASER)?;
|
||||||
|
|
||||||
|
let its_creadr_state: u64 = 0;
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_CREADR,
|
||||||
|
&its_creadr_state,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.map_err(Error::SaveITSCREADR)?;
|
||||||
|
|
||||||
|
let its_cwriter_state: u64 = 0;
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_CWRITER,
|
||||||
|
&its_cwriter_state,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.map_err(Error::SaveITSCWRITER)?;
|
||||||
|
|
||||||
|
let its_iidr_state: u64 = 0;
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_IIDR,
|
||||||
|
&its_iidr_state,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.map_err(Error::SaveITSIIDR)?;
|
||||||
|
|
||||||
|
Ok(Gicv3ItsState {
|
||||||
|
dist: dist_state,
|
||||||
|
rdist: rdist_state,
|
||||||
|
icc: icc_state,
|
||||||
|
gicd_ctlr,
|
||||||
|
its_ctlr: its_ctlr_state,
|
||||||
|
its_iidr: its_iidr_state,
|
||||||
|
its_cbaser: its_cbaser_state,
|
||||||
|
its_cwriter: its_cwriter_state,
|
||||||
|
its_creadr: its_creadr_state,
|
||||||
|
its_baser: its_baser_state,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Restore the state of GICv3ITS.
|
||||||
|
fn set_state(&mut self, gicr_typers: &[u64], state: &Gicv3ItsState) -> Result<()> {
|
||||||
|
write_ctlr(self.device(), state.gicd_ctlr)
|
||||||
|
.map_err(Error::RestoreDistributorCtrlRegisters)?;
|
||||||
|
|
||||||
|
set_dist_regs(self.device(), &state.dist)
|
||||||
|
.map_err(Error::RestoreDistributorRegisters)?;
|
||||||
|
|
||||||
|
set_redist_regs(self.device(), gicr_typers, &state.rdist)
|
||||||
|
.map_err(Error::RestoreRedistributorRegisters)?;
|
||||||
|
|
||||||
|
set_icc_regs(self.device(), gicr_typers, &state.icc)
|
||||||
|
.map_err(Error::RestoreIccRegisters)?;
|
||||||
|
|
||||||
|
//Restore GICv3ITS registers
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_IIDR,
|
||||||
|
&state.its_iidr,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.map_err(Error::RestoreITSIIDR)?;
|
||||||
|
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_CBASER,
|
||||||
|
&state.its_cbaser,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.map_err(Error::RestoreITSCBASER)?;
|
||||||
|
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_CREADR,
|
||||||
|
&state.its_creadr,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.map_err(Error::RestoreITSCREADR)?;
|
||||||
|
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_CWRITER,
|
||||||
|
&state.its_cwriter,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.map_err(Error::RestoreITSCWRITER)?;
|
||||||
|
|
||||||
|
for i in 0..8 {
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_BASER + i * 8,
|
||||||
|
&state.its_baser[i as usize],
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.map_err(Error::RestoreITSBASER)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore ITS tables
|
||||||
|
gicv3_its_tables_access(self.its_device().unwrap(), false)
|
||||||
|
.map_err(Error::RestoreITSTables)?;
|
||||||
|
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_CTLR,
|
||||||
|
&state.its_ctlr,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.map_err(Error::RestoreITSCTLR)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GicDevice for KvmGicV3Its {
|
impl GicDevice for KvmGicV3Its {
|
||||||
@@ -43,6 +362,10 @@ pub mod kvm {
|
|||||||
&self.device
|
&self.device
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn its_device(&self) -> Option<&Arc<dyn hypervisor::Device>> {
|
||||||
|
self.its_device.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn fdt_compatibility(&self) -> &str {
|
fn fdt_compatibility(&self) -> &str {
|
||||||
"arm,gic-v3"
|
"arm,gic-v3"
|
||||||
}
|
}
|
||||||
@@ -71,7 +394,14 @@ pub mod kvm {
|
|||||||
self.vcpu_count
|
self.vcpu_count
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_gicr_typers(&mut self, _vcpu_states: &[CpuState]) {}
|
fn set_its_device(&mut self, its_device: Option<Arc<dyn hypervisor::Device>>) {
|
||||||
|
self.its_device = its_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
|
||||||
|
let gicr_typers = construct_gicr_typers(vcpu_states);
|
||||||
|
self.gicr_typers = gicr_typers;
|
||||||
|
}
|
||||||
|
|
||||||
fn as_any_concrete_mut(&mut self) -> &mut dyn Any {
|
fn as_any_concrete_mut(&mut self) -> &mut dyn Any {
|
||||||
self
|
self
|
||||||
@@ -89,6 +419,8 @@ pub mod kvm {
|
|||||||
) -> Box<dyn GicDevice> {
|
) -> Box<dyn GicDevice> {
|
||||||
Box::new(KvmGicV3Its {
|
Box::new(KvmGicV3Its {
|
||||||
device,
|
device,
|
||||||
|
its_device: None,
|
||||||
|
gicr_typers: vec![0; vcpu_count.try_into().unwrap()],
|
||||||
gic_properties: [
|
gic_properties: [
|
||||||
KvmGicV3::get_dist_addr(),
|
KvmGicV3::get_dist_addr(),
|
||||||
KvmGicV3::get_dist_size(),
|
KvmGicV3::get_dist_size(),
|
||||||
@@ -105,8 +437,8 @@ pub mod kvm {
|
|||||||
|
|
||||||
fn init_device_attributes(
|
fn init_device_attributes(
|
||||||
vm: &Arc<dyn hypervisor::Vm>,
|
vm: &Arc<dyn hypervisor::Vm>,
|
||||||
gic_device: &dyn GicDevice,
|
gic_device: &mut dyn GicDevice,
|
||||||
) -> Result<()> {
|
) -> crate::aarch64::gic::Result<()> {
|
||||||
KvmGicV3::init_device_attributes(vm, gic_device)?;
|
KvmGicV3::init_device_attributes(vm, gic_device)?;
|
||||||
|
|
||||||
let mut its_device = kvm_bindings::kvm_create_device {
|
let mut its_device = kvm_bindings::kvm_create_device {
|
||||||
@@ -117,7 +449,7 @@ pub mod kvm {
|
|||||||
|
|
||||||
let its_fd = vm
|
let its_fd = vm
|
||||||
.create_device(&mut its_device)
|
.create_device(&mut its_device)
|
||||||
.map_err(Error::CreateGic)?;
|
.map_err(crate::aarch64::gic::Error::CreateGic)?;
|
||||||
|
|
||||||
Self::set_device_attribute(
|
Self::set_device_attribute(
|
||||||
&its_fd,
|
&its_fd,
|
||||||
@@ -135,7 +467,49 @@ pub mod kvm {
|
|||||||
0,
|
0,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
gic_device.set_its_device(Some(its_fd));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const GIC_V3_ITS_SNAPSHOT_ID: &str = "gic-v3-its";
|
||||||
|
impl Snapshottable for KvmGicV3Its {
|
||||||
|
fn id(&self) -> String {
|
||||||
|
GIC_V3_ITS_SNAPSHOT_ID.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||||
|
let gicr_typers = self.gicr_typers.clone();
|
||||||
|
Snapshot::new_from_versioned_state(&self.id(), &self.state(&gicr_typers).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||||
|
let gicr_typers = self.gicr_typers.clone();
|
||||||
|
self.set_state(&gicr_typers, &snapshot.to_versioned_state(&self.id())?)
|
||||||
|
.map_err(|e| {
|
||||||
|
MigratableError::Restore(anyhow!("Could not restore GICv3ITS state {:?}", e))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pausable for KvmGicV3Its {
|
||||||
|
fn pause(&mut self) -> std::result::Result<(), MigratableError> {
|
||||||
|
// Flush redistributors pending tables to guest RAM.
|
||||||
|
save_pending_tables(self.device()).map_err(|e| {
|
||||||
|
MigratableError::Pause(anyhow!(
|
||||||
|
"Could not save GICv3ITS GIC pending tables {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
// Flush ITS tables to guest RAM.
|
||||||
|
gicv3_its_tables_access(self.its_device().unwrap(), true).map_err(|e| {
|
||||||
|
MigratableError::Pause(anyhow!("Could not save GICv3ITS ITS tables {:?}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Transportable for KvmGicV3Its {}
|
||||||
|
impl Migratable for KvmGicV3Its {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Copyright 2021 Arm Limited (or its affiliates). All rights reserved.
|
||||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
@@ -31,6 +32,11 @@ pub trait GicDevice: Send {
|
|||||||
/// Returns the hypervisor agnostic Device of the GIC device
|
/// Returns the hypervisor agnostic Device of the GIC device
|
||||||
fn device(&self) -> &Arc<dyn hypervisor::Device>;
|
fn device(&self) -> &Arc<dyn hypervisor::Device>;
|
||||||
|
|
||||||
|
/// Returns the hypervisor agnostic Device of the ITS device
|
||||||
|
fn its_device(&self) -> Option<&Arc<dyn hypervisor::Device>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the fdt compatibility property of the device
|
/// Returns the fdt compatibility property of the device
|
||||||
fn fdt_compatibility(&self) -> &str;
|
fn fdt_compatibility(&self) -> &str;
|
||||||
|
|
||||||
@@ -58,6 +64,8 @@ pub trait GicDevice: Send {
|
|||||||
&[]
|
&[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_its_device(&mut self, its_device: Option<Arc<dyn hypervisor::Device>>);
|
||||||
|
|
||||||
/// Get the values of GICR_TYPER for each vCPU.
|
/// Get the values of GICR_TYPER for each vCPU.
|
||||||
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]);
|
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]);
|
||||||
|
|
||||||
@@ -88,7 +96,7 @@ pub mod kvm {
|
|||||||
/// Setup the device-specific attributes
|
/// Setup the device-specific attributes
|
||||||
fn init_device_attributes(
|
fn init_device_attributes(
|
||||||
vm: &Arc<dyn hypervisor::Vm>,
|
vm: &Arc<dyn hypervisor::Vm>,
|
||||||
gic_device: &dyn GicDevice,
|
gic_device: &mut dyn GicDevice,
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
|
|
||||||
/// Initialize a GIC device
|
/// Initialize a GIC device
|
||||||
@@ -179,9 +187,9 @@ pub mod kvm {
|
|||||||
fn new(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GicDevice>> {
|
fn new(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GicDevice>> {
|
||||||
let vgic_fd = Self::init_device(vm)?;
|
let vgic_fd = Self::init_device(vm)?;
|
||||||
|
|
||||||
let device = Self::create_device(vgic_fd, vcpu_count);
|
let mut device = Self::create_device(vgic_fd, vcpu_count);
|
||||||
|
|
||||||
Self::init_device_attributes(vm, &*device)?;
|
Self::init_device_attributes(vm, &mut *device)?;
|
||||||
|
|
||||||
Self::finalize_device(&*device)?;
|
Self::finalize_device(&*device)?;
|
||||||
|
|
||||||
|
|||||||
@@ -62,11 +62,11 @@ macro_rules! VGIC_RDIST_REG {
|
|||||||
|
|
||||||
// List with relevant distributor registers that we will be restoring.
|
// List with relevant distributor registers that we will be restoring.
|
||||||
static VGIC_RDIST_REGS: &'static [RdistReg] = &[
|
static VGIC_RDIST_REGS: &'static [RdistReg] = &[
|
||||||
VGIC_RDIST_REG!(GICR_CTLR, 4),
|
|
||||||
VGIC_RDIST_REG!(GICR_STATUSR, 4),
|
VGIC_RDIST_REG!(GICR_STATUSR, 4),
|
||||||
VGIC_RDIST_REG!(GICR_WAKER, 4),
|
VGIC_RDIST_REG!(GICR_WAKER, 4),
|
||||||
VGIC_RDIST_REG!(GICR_PROPBASER, 8),
|
VGIC_RDIST_REG!(GICR_PROPBASER, 8),
|
||||||
VGIC_RDIST_REG!(GICR_PENDBASER, 8),
|
VGIC_RDIST_REG!(GICR_PENDBASER, 8),
|
||||||
|
VGIC_RDIST_REG!(GICR_CTLR, 4),
|
||||||
];
|
];
|
||||||
|
|
||||||
// List with relevant distributor registers that we will be restoring.
|
// List with relevant distributor registers that we will be restoring.
|
||||||
@@ -142,14 +142,14 @@ pub fn get_redist_regs(gic: &Arc<dyn hypervisor::Device>, gicr_typer: &[u64]) ->
|
|||||||
let mut idx: usize = 0;
|
let mut idx: usize = 0;
|
||||||
access_redists_aux(
|
access_redists_aux(
|
||||||
gic,
|
gic,
|
||||||
&gicr_typer,
|
gicr_typer,
|
||||||
&mut state,
|
&mut state,
|
||||||
VGIC_RDIST_REGS,
|
VGIC_RDIST_REGS,
|
||||||
&mut idx,
|
&mut idx,
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
access_redists_aux(gic, &gicr_typer, &mut state, VGIC_SGI_REGS, &mut idx, false)?;
|
access_redists_aux(gic, gicr_typer, &mut state, VGIC_SGI_REGS, &mut idx, false)?;
|
||||||
Ok(state)
|
Ok(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,14 +46,26 @@
|
|||||||
|
|
||||||
use vm_memory::GuestAddress;
|
use vm_memory::GuestAddress;
|
||||||
|
|
||||||
/// 0x0 ~ 0x400_0000 is reserved to uefi
|
/// 0x0 ~ 0x40_0000 (4 MiB) is reserved to UEFI
|
||||||
|
/// UEFI binary size is required less than 3 MiB, reserving 4 MiB is enough.
|
||||||
pub const UEFI_START: u64 = 0x0;
|
pub const UEFI_START: u64 = 0x0;
|
||||||
pub const MEM_UEFI_START: GuestAddress = GuestAddress(0);
|
pub const MEM_UEFI_START: GuestAddress = GuestAddress(0);
|
||||||
pub const UEFI_SIZE: u64 = 0x0400_0000;
|
pub const UEFI_SIZE: u64 = 0x040_0000;
|
||||||
|
|
||||||
/// Below this address will reside the GIC, above this address will reside the MMIO devices.
|
/// Below this address will reside the GIC, above this address will reside the MMIO devices.
|
||||||
pub const MAPPED_IO_START: u64 = 0x0900_0000;
|
pub const MAPPED_IO_START: u64 = 0x0900_0000;
|
||||||
|
|
||||||
|
/// See kernel file arch/arm64/include/uapi/asm/kvm.h for the GIC related definitions.
|
||||||
|
/// 0x08ff_0000 ~ 0x0900_0000 is reserved for GICv3 Distributor
|
||||||
|
pub const GIC_V3_DIST_SIZE: u64 = 0x01_0000;
|
||||||
|
pub const GIC_V3_DIST_START: u64 = MAPPED_IO_START - GIC_V3_DIST_SIZE;
|
||||||
|
/// Below 0x08ff_0000 is reserved for GICv3 Redistributor.
|
||||||
|
/// The size defined here is for each vcpu.
|
||||||
|
/// The total size is 'number_of_vcpu * GIC_V3_REDIST_SIZE'
|
||||||
|
pub const GIC_V3_REDIST_SIZE: u64 = 0x02_0000;
|
||||||
|
/// Below Redistributor area is GICv3 ITS
|
||||||
|
pub const GIC_V3_ITS_SIZE: u64 = 0x02_0000;
|
||||||
|
|
||||||
/// Space 0x0900_0000 ~ 0x0905_0000 is reserved for legacy devices.
|
/// Space 0x0900_0000 ~ 0x0905_0000 is reserved for legacy devices.
|
||||||
pub const LEGACY_SERIAL_MAPPED_IO_START: u64 = 0x0900_0000;
|
pub const LEGACY_SERIAL_MAPPED_IO_START: u64 = 0x0900_0000;
|
||||||
pub const LEGACY_RTC_MAPPED_IO_START: u64 = 0x0901_0000;
|
pub const LEGACY_RTC_MAPPED_IO_START: u64 = 0x0901_0000;
|
||||||
@@ -70,6 +82,8 @@ pub const MEM_32BIT_DEVICES_SIZE: u64 = 0x2000_0000;
|
|||||||
/// PCI MMCONFIG space (start: after the device space at 1 GiB, length: 256MiB)
|
/// PCI MMCONFIG space (start: after the device space at 1 GiB, length: 256MiB)
|
||||||
pub const PCI_MMCONFIG_START: GuestAddress = GuestAddress(0x3000_0000);
|
pub const PCI_MMCONFIG_START: GuestAddress = GuestAddress(0x3000_0000);
|
||||||
pub const PCI_MMCONFIG_SIZE: u64 = 256 << 20;
|
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 on 64 bit ARM.
|
/// Start of RAM on 64 bit ARM.
|
||||||
pub const RAM_64BIT_START: u64 = 0x4000_0000;
|
pub const RAM_64BIT_START: u64 = 0x4000_0000;
|
||||||
|
|||||||
@@ -14,13 +14,11 @@ pub mod regs;
|
|||||||
pub mod uefi;
|
pub mod uefi;
|
||||||
|
|
||||||
pub use self::fdt::DeviceInfoForFdt;
|
pub use self::fdt::DeviceInfoForFdt;
|
||||||
use crate::DeviceType;
|
use crate::{DeviceType, GuestMemoryMmap, NumaNodes, RegionType};
|
||||||
use crate::GuestMemoryMmap;
|
|
||||||
use crate::RegionType;
|
|
||||||
use gic::GicDevice;
|
use gic::GicDevice;
|
||||||
|
use log::{log_enabled, Level};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use vm_memory::{
|
use vm_memory::{
|
||||||
@@ -87,7 +85,7 @@ pub fn configure_vcpu(
|
|||||||
pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> {
|
pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> {
|
||||||
// Normally UEFI should be loaded to a flash area at the beginning of memory.
|
// Normally UEFI should be loaded to a flash area at the beginning of memory.
|
||||||
// But now flash memory type is not supported.
|
// But now flash memory type is not supported.
|
||||||
// As a workaround, we take 64 MiB memory from the main RAM for UEFI.
|
// As a workaround, we take 4 MiB memory from the main RAM for UEFI.
|
||||||
// As a result, the RAM that the guest can see is less than what has been
|
// As a result, the RAM that the guest can see is less than what has been
|
||||||
// assigned in command line, when ACPI and UEFI is enabled.
|
// assigned in command line, when ACPI and UEFI is enabled.
|
||||||
let ram_deduction = if cfg!(feature = "acpi") {
|
let ram_deduction = if cfg!(feature = "acpi") {
|
||||||
@@ -97,7 +95,7 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
|
|||||||
};
|
};
|
||||||
|
|
||||||
vec![
|
vec![
|
||||||
// 0 ~ 64 MiB: Reserved for UEFI space
|
// 0 ~ 4 MiB: Reserved for UEFI space
|
||||||
#[cfg(feature = "acpi")]
|
#[cfg(feature = "acpi")]
|
||||||
(GuestAddress(0), layout::UEFI_SIZE as usize, RegionType::Ram),
|
(GuestAddress(0), layout::UEFI_SIZE as usize, RegionType::Ram),
|
||||||
#[cfg(not(feature = "acpi"))]
|
#[cfg(not(feature = "acpi"))]
|
||||||
@@ -106,7 +104,7 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
|
|||||||
layout::UEFI_SIZE as usize,
|
layout::UEFI_SIZE as usize,
|
||||||
RegionType::Reserved,
|
RegionType::Reserved,
|
||||||
),
|
),
|
||||||
// 64 MiB ~ 256 MiB: Gic and legacy devices
|
// 4 MiB ~ 256 MiB: Gic and legacy devices
|
||||||
(
|
(
|
||||||
GuestAddress(layout::UEFI_SIZE),
|
GuestAddress(layout::UEFI_SIZE),
|
||||||
(layout::MEM_32BIT_DEVICES_START.0 - layout::UEFI_SIZE) as usize,
|
(layout::MEM_32BIT_DEVICES_START.0 - layout::UEFI_SIZE) as usize,
|
||||||
@@ -134,26 +132,37 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Configures the system and should be called once per vm before starting vcpu threads.
|
/// 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>(
|
pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
|
||||||
guest_mem: &GuestMemoryMmap,
|
guest_mem: &GuestMemoryMmap,
|
||||||
cmdline_cstring: &CStr,
|
cmdline: &str,
|
||||||
vcpu_mpidr: Vec<u64>,
|
vcpu_mpidr: Vec<u64>,
|
||||||
|
vcpu_topology: Option<(u8, u8, u8)>,
|
||||||
device_info: &HashMap<(DeviceType, String), T, S>,
|
device_info: &HashMap<(DeviceType, String), T, S>,
|
||||||
initrd: &Option<super::InitramfsConfig>,
|
initrd: &Option<super::InitramfsConfig>,
|
||||||
pci_space_address: &(u64, u64),
|
pci_space_address: &(u64, u64),
|
||||||
|
virtio_iommu_bdf: Option<u32>,
|
||||||
gic_device: &dyn GicDevice,
|
gic_device: &dyn GicDevice,
|
||||||
|
numa_nodes: &NumaNodes,
|
||||||
) -> super::Result<()> {
|
) -> super::Result<()> {
|
||||||
let fdt_final = fdt::create_fdt(
|
let fdt_final = fdt::create_fdt(
|
||||||
guest_mem,
|
guest_mem,
|
||||||
cmdline_cstring,
|
cmdline,
|
||||||
vcpu_mpidr,
|
vcpu_mpidr,
|
||||||
|
vcpu_topology,
|
||||||
device_info,
|
device_info,
|
||||||
gic_device,
|
gic_device,
|
||||||
initrd,
|
initrd,
|
||||||
pci_space_address,
|
pci_space_address,
|
||||||
|
numa_nodes,
|
||||||
|
virtio_iommu_bdf,
|
||||||
)
|
)
|
||||||
.map_err(|_| Error::SetupFdt)?;
|
.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)?;
|
fdt::write_fdt_to_memory(fdt_final, guest_mem).map_err(Error::WriteFdtToMemory)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -10,11 +10,21 @@
|
|||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
use crate::x86_64::SgxEpcSection;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::result;
|
use std::result;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use versionize::{VersionMap, Versionize, VersionizeError, VersionizeResult};
|
||||||
|
use versionize_derive::Versionize;
|
||||||
|
use vm_migration::VersionMapped;
|
||||||
|
|
||||||
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 for returning error code.
|
/// Type for returning error code.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -49,7 +59,7 @@ pub enum Error {
|
|||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
/// Type for memory region types.
|
/// Type for memory region types.
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize, Versionize)]
|
||||||
pub enum RegionType {
|
pub enum RegionType {
|
||||||
/// RAM type
|
/// RAM type
|
||||||
Ram,
|
Ram,
|
||||||
@@ -67,6 +77,8 @@ pub enum RegionType {
|
|||||||
Reserved,
|
Reserved,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl VersionMapped for RegionType {}
|
||||||
|
|
||||||
/// Module for aarch64 related functionality.
|
/// Module for aarch64 related functionality.
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
pub mod aarch64;
|
pub mod aarch64;
|
||||||
@@ -83,9 +95,9 @@ pub mod x86_64;
|
|||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub use x86_64::{
|
pub use x86_64::{
|
||||||
arch_memory_regions, configure_system, configure_vcpu, get_host_cpu_phys_bits,
|
arch_memory_regions, configure_system, configure_vcpu, generate_common_cpuid,
|
||||||
initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START, regs, CpuidPatch,
|
get_host_cpu_phys_bits, initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE,
|
||||||
CpuidReg, EntryPoint,
|
layout::CMDLINE_START, regs, CpuidFeatureEntry, EntryPoint,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
|
/// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
|
||||||
@@ -96,6 +108,19 @@ fn pagesize() -> usize {
|
|||||||
unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
|
unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct NumaNode {
|
||||||
|
pub memory_regions: Vec<Arc<GuestRegionMmap>>,
|
||||||
|
pub hotplug_regions: Vec<Arc<GuestRegionMmap>>,
|
||||||
|
pub cpus: Vec<u8>,
|
||||||
|
pub distances: BTreeMap<u32, u8>,
|
||||||
|
pub memory_zones: Vec<String>,
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
pub sgx_epc_sections: Vec<SgxEpcSection>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type NumaNodes = BTreeMap<u32, NumaNode>;
|
||||||
|
|
||||||
/// Type for passing information about the initramfs in the guest memory.
|
/// Type for passing information about the initramfs in the guest memory.
|
||||||
pub struct InitramfsConfig {
|
pub struct InitramfsConfig {
|
||||||
/// Load address of initramfs in guest memory
|
/// Load address of initramfs in guest memory
|
||||||
|
|||||||
@@ -95,22 +95,4 @@ mod tests {
|
|||||||
let mut klapic = LapicState::default();
|
let mut klapic = LapicState::default();
|
||||||
set_klapic_reg(&mut klapic, reg_offset, 3);
|
set_klapic_reg(&mut klapic, reg_offset, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_apic_delivery_mode() {
|
|
||||||
let mut v: Vec<u32> = Vec::new();
|
|
||||||
v.resize(20, 0);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
assert_eq!(
|
|
||||||
libc::getrandom(v.as_mut_ptr() as *mut _ as *mut libc::c_void, 80, 0),
|
|
||||||
80
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
v.iter_mut()
|
|
||||||
.for_each(|x| *x = set_apic_delivery_mode(*x, 2));
|
|
||||||
let after: Vec<u32> = v.iter().map(|x| ((*x & !0x700) | ((2) << 8))).collect();
|
|
||||||
assert_eq!(v, after);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,9 +79,9 @@ pub const HIGH_RAM_START: GuestAddress = GuestAddress(0x100000);
|
|||||||
|
|
||||||
// == No fixed addresses in the "High RAM" range ==
|
// == No fixed addresses in the "High RAM" range ==
|
||||||
|
|
||||||
// ** 32-bit reserved area (start: 3GiB, length: 1GiB) **
|
// ** 32-bit reserved area (start: 3GiB, length: 896MiB) **
|
||||||
pub const MEM_32BIT_RESERVED_START: GuestAddress = GuestAddress(0xc000_0000);
|
pub const MEM_32BIT_RESERVED_START: GuestAddress = GuestAddress(0xc000_0000);
|
||||||
pub const MEM_32BIT_RESERVED_SIZE: u64 = 1024 << 20;
|
pub const MEM_32BIT_RESERVED_SIZE: u64 = PCI_MMCONFIG_SIZE + MEM_32BIT_DEVICES_SIZE;
|
||||||
|
|
||||||
// == Fixed constants within the "32-bit reserved" range ==
|
// == Fixed constants within the "32-bit reserved" range ==
|
||||||
|
|
||||||
@@ -93,6 +93,16 @@ pub const MEM_32BIT_DEVICES_SIZE: u64 = 640 << 20;
|
|||||||
pub const PCI_MMCONFIG_START: GuestAddress =
|
pub const PCI_MMCONFIG_START: GuestAddress =
|
||||||
GuestAddress(MEM_32BIT_DEVICES_START.0 + MEM_32BIT_DEVICES_SIZE);
|
GuestAddress(MEM_32BIT_DEVICES_START.0 + MEM_32BIT_DEVICES_SIZE);
|
||||||
pub const PCI_MMCONFIG_SIZE: u64 = 256 << 20;
|
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;
|
||||||
|
|
||||||
|
// TSS is 3 pages after the PCI MMCONFIG space
|
||||||
|
pub const KVM_TSS_START: GuestAddress = GuestAddress(PCI_MMCONFIG_START.0 + PCI_MMCONFIG_SIZE);
|
||||||
|
pub const KVM_TSS_SIZE: u64 = (3 * 4) << 10;
|
||||||
|
|
||||||
|
// Identity map is a one page region after the TSS
|
||||||
|
pub const KVM_IDENTITY_MAP_START: GuestAddress = GuestAddress(KVM_TSS_START.0 + KVM_TSS_SIZE);
|
||||||
|
pub const KVM_IDENTITY_MAP_SIZE: u64 = 4 << 10;
|
||||||
|
|
||||||
// IOAPIC
|
// IOAPIC
|
||||||
pub const IOAPIC_START: GuestAddress = GuestAddress(0xfec0_0000);
|
pub const IOAPIC_START: GuestAddress = GuestAddress(0xfec0_0000);
|
||||||
@@ -101,9 +111,6 @@ pub const IOAPIC_SIZE: u64 = 0x20;
|
|||||||
// APIC
|
// APIC
|
||||||
pub const APIC_START: GuestAddress = GuestAddress(0xfee0_0000);
|
pub const APIC_START: GuestAddress = GuestAddress(0xfee0_0000);
|
||||||
|
|
||||||
/// Address for the TSS setup.
|
|
||||||
pub const KVM_TSS_ADDRESS: GuestAddress = GuestAddress(0xfffb_d000);
|
|
||||||
|
|
||||||
// == End of "32-bit reserved" range. ==
|
// == End of "32-bit reserved" range. ==
|
||||||
|
|
||||||
// ** 64-bit RAM start (start: 4GiB, length: varies) **
|
// ** 64-bit RAM start (start: 4GiB, length: varies) **
|
||||||
|
|||||||
@@ -15,11 +15,12 @@ pub mod regs;
|
|||||||
use crate::GuestMemoryMmap;
|
use crate::GuestMemoryMmap;
|
||||||
use crate::InitramfsConfig;
|
use crate::InitramfsConfig;
|
||||||
use crate::RegionType;
|
use crate::RegionType;
|
||||||
use hypervisor::{CpuId, CpuIdEntry, CPUID_FLAG_VALID_INDEX};
|
use hypervisor::{CpuId, CpuIdEntry, HypervisorError, CPUID_FLAG_VALID_INDEX};
|
||||||
use linux_loader::loader::bootparam::boot_params;
|
use linux_loader::loader::bootparam::boot_params;
|
||||||
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 std::mem;
|
||||||
use vm_memory::{
|
use vm_memory::{
|
||||||
Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
|
Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
|
||||||
@@ -30,13 +31,33 @@ use std::arch::x86_64;
|
|||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
pub mod tdx;
|
pub mod tdx;
|
||||||
|
|
||||||
|
// CPUID feature bits
|
||||||
|
const TSC_DEADLINE_TIMER_ECX_BIT: u8 = 24; // tsc deadline timer ecx bit.
|
||||||
|
const HYPERVISOR_ECX_BIT: u8 = 31; // Hypervisor ecx bit.
|
||||||
|
const MTRR_EDX_BIT: u8 = 12; // Hypervisor ecx bit.
|
||||||
|
|
||||||
|
// KVM feature bits
|
||||||
|
const KVM_FEATURE_ASYNC_PF_INT_BIT: u8 = 14;
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
const KVM_FEATURE_CLOCKSOURCE_BIT: u8 = 0;
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
const KVM_FEATURE_CLOCKSOURCE2_BIT: u8 = 3;
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
const KVM_FEATURE_CLOCKSOURCE_STABLE_BIT: u8 = 24;
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
const KVM_FEATURE_ASYNC_PF_BIT: u8 = 4;
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
const KVM_FEATURE_ASYNC_PF_VMEXIT_BIT: u8 = 10;
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
const KVM_FEATURE_STEAL_TIME_BIT: u8 = 5;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
/// Specifies the entry point address where the guest must start
|
/// Specifies the entry point address where the guest must start
|
||||||
/// executing code, as well as which of the supported boot protocols
|
/// executing code, as well as which of the supported boot protocols
|
||||||
/// is to be used to configure the guest initial state.
|
/// is to be used to configure the guest initial state.
|
||||||
pub struct EntryPoint {
|
pub struct EntryPoint {
|
||||||
/// Address in guest memory where the guest must start execution
|
/// Address in guest memory where the guest must start execution
|
||||||
pub entry_addr: GuestAddress,
|
pub entry_addr: Option<GuestAddress>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const E820_RAM: u32 = 1;
|
const E820_RAM: u32 = 1;
|
||||||
@@ -64,7 +85,7 @@ impl SgxEpcSection {
|
|||||||
pub struct SgxEpcRegion {
|
pub struct SgxEpcRegion {
|
||||||
start: GuestAddress,
|
start: GuestAddress,
|
||||||
size: GuestUsize,
|
size: GuestUsize,
|
||||||
epc_sections: Vec<SgxEpcSection>,
|
epc_sections: BTreeMap<String, SgxEpcSection>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SgxEpcRegion {
|
impl SgxEpcRegion {
|
||||||
@@ -72,7 +93,7 @@ impl SgxEpcRegion {
|
|||||||
SgxEpcRegion {
|
SgxEpcRegion {
|
||||||
start,
|
start,
|
||||||
size,
|
size,
|
||||||
epc_sections: Vec::new(),
|
epc_sections: BTreeMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn start(&self) -> GuestAddress {
|
pub fn start(&self) -> GuestAddress {
|
||||||
@@ -81,11 +102,11 @@ impl SgxEpcRegion {
|
|||||||
pub fn size(&self) -> GuestUsize {
|
pub fn size(&self) -> GuestUsize {
|
||||||
self.size
|
self.size
|
||||||
}
|
}
|
||||||
pub fn epc_sections(&self) -> &Vec<SgxEpcSection> {
|
pub fn epc_sections(&self) -> &BTreeMap<String, SgxEpcSection> {
|
||||||
&self.epc_sections
|
&self.epc_sections
|
||||||
}
|
}
|
||||||
pub fn push(&mut self, epc_section: SgxEpcSection) {
|
pub fn insert(&mut self, id: String, epc_section: SgxEpcSection) {
|
||||||
self.epc_sections.push(epc_section);
|
self.epc_sections.insert(id, epc_section);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,17 +118,15 @@ impl SgxEpcRegion {
|
|||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
struct StartInfoWrapper(hvm_start_info);
|
struct StartInfoWrapper(hvm_start_info);
|
||||||
|
|
||||||
// It is safe to initialize StartInfoWrapper which is a wrapper over `hvm_start_info` (a series of ints).
|
|
||||||
unsafe impl ByteValued for StartInfoWrapper {}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
struct MemmapTableEntryWrapper(hvm_memmap_table_entry);
|
struct MemmapTableEntryWrapper(hvm_memmap_table_entry);
|
||||||
|
|
||||||
unsafe impl ByteValued for MemmapTableEntryWrapper {}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
struct ModlistEntryWrapper(hvm_modlist_entry);
|
struct ModlistEntryWrapper(hvm_modlist_entry);
|
||||||
|
|
||||||
|
// SAFETY: These data structures only contain a series of integers
|
||||||
|
unsafe impl ByteValued for StartInfoWrapper {}
|
||||||
|
unsafe impl ByteValued for MemmapTableEntryWrapper {}
|
||||||
unsafe impl ByteValued for ModlistEntryWrapper {}
|
unsafe impl ByteValued for ModlistEntryWrapper {}
|
||||||
|
|
||||||
// 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
|
||||||
@@ -118,7 +137,7 @@ unsafe impl ByteValued for ModlistEntryWrapper {}
|
|||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
struct BootParamsWrapper(boot_params);
|
struct BootParamsWrapper(boot_params);
|
||||||
|
|
||||||
// It is safe to initialize BootParamsWrap which is a wrapper over `boot_params` (a series of ints).
|
// SAFETY: BootParamsWrap is a wrapper over `boot_params` (a series of ints).
|
||||||
unsafe impl ByteValued for BootParamsWrapper {}
|
unsafe impl ByteValued for BootParamsWrapper {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -155,6 +174,18 @@ pub enum Error {
|
|||||||
|
|
||||||
/// Missing SGX_LC CPU feature
|
/// Missing SGX_LC CPU feature
|
||||||
MissingSgxLaunchControlFeature,
|
MissingSgxLaunchControlFeature,
|
||||||
|
|
||||||
|
/// Error getting supported CPUID through the hypervisor (kvm/mshv) API
|
||||||
|
CpuidGetSupported(HypervisorError),
|
||||||
|
|
||||||
|
/// Error populating CPUID with KVM HyperV emulation details
|
||||||
|
CpuidKvmHyperV(vmm_sys_util::fam::Error),
|
||||||
|
|
||||||
|
/// Error populating CPUID with CPU identification
|
||||||
|
CpuidIdentification(vmm_sys_util::fam::Error),
|
||||||
|
|
||||||
|
/// Error checking CPUID compatibility
|
||||||
|
CpuidCheckCompatibility,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Error> for super::Error {
|
impl From<Error> for super::Error {
|
||||||
@@ -164,7 +195,7 @@ impl From<Error> for super::Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code, clippy::upper_case_acronyms)]
|
#[allow(dead_code, clippy::upper_case_acronyms)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum CpuidReg {
|
pub enum CpuidReg {
|
||||||
EAX,
|
EAX,
|
||||||
EBX,
|
EBX,
|
||||||
@@ -308,6 +339,386 @@ impl CpuidPatch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum CpuidCompatibleCheck {
|
||||||
|
BitwiseSubset, // bitwise subset
|
||||||
|
Equal, // equal in value
|
||||||
|
NumNotGreater, // smaller or equal as a number
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CpuidFeatureEntry {
|
||||||
|
function: u32,
|
||||||
|
index: u32,
|
||||||
|
feature_reg: CpuidReg,
|
||||||
|
compatible_check: CpuidCompatibleCheck,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuidFeatureEntry {
|
||||||
|
fn checked_feature_entry_list() -> Vec<CpuidFeatureEntry> {
|
||||||
|
vec![
|
||||||
|
// The following list includes all hardware features bits from
|
||||||
|
// the CPUID Wiki Page: https://en.wikipedia.org/wiki/CPUID
|
||||||
|
// Leaf 0x1, ECX/EDX, feature bits
|
||||||
|
CpuidFeatureEntry {
|
||||||
|
function: 1,
|
||||||
|
index: 0,
|
||||||
|
feature_reg: CpuidReg::ECX,
|
||||||
|
compatible_check: CpuidCompatibleCheck::BitwiseSubset,
|
||||||
|
},
|
||||||
|
CpuidFeatureEntry {
|
||||||
|
function: 1,
|
||||||
|
index: 0,
|
||||||
|
feature_reg: CpuidReg::EDX,
|
||||||
|
compatible_check: CpuidCompatibleCheck::BitwiseSubset,
|
||||||
|
},
|
||||||
|
// Leaf 0x7, EAX/EBX/ECX/EDX, extended features
|
||||||
|
CpuidFeatureEntry {
|
||||||
|
function: 7,
|
||||||
|
index: 0,
|
||||||
|
feature_reg: CpuidReg::EAX,
|
||||||
|
compatible_check: CpuidCompatibleCheck::NumNotGreater,
|
||||||
|
},
|
||||||
|
CpuidFeatureEntry {
|
||||||
|
function: 7,
|
||||||
|
index: 0,
|
||||||
|
feature_reg: CpuidReg::EBX,
|
||||||
|
compatible_check: CpuidCompatibleCheck::BitwiseSubset,
|
||||||
|
},
|
||||||
|
CpuidFeatureEntry {
|
||||||
|
function: 7,
|
||||||
|
index: 0,
|
||||||
|
feature_reg: CpuidReg::ECX,
|
||||||
|
compatible_check: CpuidCompatibleCheck::BitwiseSubset,
|
||||||
|
},
|
||||||
|
CpuidFeatureEntry {
|
||||||
|
function: 7,
|
||||||
|
index: 0,
|
||||||
|
feature_reg: CpuidReg::EDX,
|
||||||
|
compatible_check: CpuidCompatibleCheck::BitwiseSubset,
|
||||||
|
},
|
||||||
|
// Leaf 0x7 subleaf 0x1, EAX, extended features
|
||||||
|
CpuidFeatureEntry {
|
||||||
|
function: 7,
|
||||||
|
index: 1,
|
||||||
|
feature_reg: CpuidReg::EAX,
|
||||||
|
compatible_check: CpuidCompatibleCheck::BitwiseSubset,
|
||||||
|
},
|
||||||
|
// Leaf 0x8000_0001, ECX/EDX, CPUID features bits
|
||||||
|
CpuidFeatureEntry {
|
||||||
|
function: 0x8000_0001,
|
||||||
|
index: 0,
|
||||||
|
feature_reg: CpuidReg::ECX,
|
||||||
|
compatible_check: CpuidCompatibleCheck::BitwiseSubset,
|
||||||
|
},
|
||||||
|
CpuidFeatureEntry {
|
||||||
|
function: 0x8000_0001,
|
||||||
|
index: 0,
|
||||||
|
feature_reg: CpuidReg::EDX,
|
||||||
|
compatible_check: CpuidCompatibleCheck::BitwiseSubset,
|
||||||
|
},
|
||||||
|
// KVM CPUID bits: https://www.kernel.org/doc/html/latest/virt/kvm/cpuid.html
|
||||||
|
// Leaf 0x4000_0000, EAX/EBX/ECX/EDX, KVM CPUID SIGNATURE
|
||||||
|
CpuidFeatureEntry {
|
||||||
|
function: 0x4000_0000,
|
||||||
|
index: 0,
|
||||||
|
feature_reg: CpuidReg::EAX,
|
||||||
|
compatible_check: CpuidCompatibleCheck::NumNotGreater,
|
||||||
|
},
|
||||||
|
CpuidFeatureEntry {
|
||||||
|
function: 0x4000_0000,
|
||||||
|
index: 0,
|
||||||
|
feature_reg: CpuidReg::EBX,
|
||||||
|
compatible_check: CpuidCompatibleCheck::Equal,
|
||||||
|
},
|
||||||
|
CpuidFeatureEntry {
|
||||||
|
function: 0x4000_0000,
|
||||||
|
index: 0,
|
||||||
|
feature_reg: CpuidReg::ECX,
|
||||||
|
compatible_check: CpuidCompatibleCheck::Equal,
|
||||||
|
},
|
||||||
|
CpuidFeatureEntry {
|
||||||
|
function: 0x4000_0000,
|
||||||
|
index: 0,
|
||||||
|
feature_reg: CpuidReg::EDX,
|
||||||
|
compatible_check: CpuidCompatibleCheck::Equal,
|
||||||
|
},
|
||||||
|
// Leaf 0x4000_0001, EAX/EBX/ECX/EDX, KVM CPUID features
|
||||||
|
CpuidFeatureEntry {
|
||||||
|
function: 0x4000_0001,
|
||||||
|
index: 0,
|
||||||
|
feature_reg: CpuidReg::EAX,
|
||||||
|
compatible_check: CpuidCompatibleCheck::BitwiseSubset,
|
||||||
|
},
|
||||||
|
CpuidFeatureEntry {
|
||||||
|
function: 0x4000_0001,
|
||||||
|
index: 0,
|
||||||
|
feature_reg: CpuidReg::EBX,
|
||||||
|
compatible_check: CpuidCompatibleCheck::BitwiseSubset,
|
||||||
|
},
|
||||||
|
CpuidFeatureEntry {
|
||||||
|
function: 0x4000_0001,
|
||||||
|
index: 0,
|
||||||
|
feature_reg: CpuidReg::ECX,
|
||||||
|
compatible_check: CpuidCompatibleCheck::BitwiseSubset,
|
||||||
|
},
|
||||||
|
CpuidFeatureEntry {
|
||||||
|
function: 0x4000_0001,
|
||||||
|
index: 0,
|
||||||
|
feature_reg: CpuidReg::EDX,
|
||||||
|
compatible_check: CpuidCompatibleCheck::BitwiseSubset,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_features_from_cpuid(
|
||||||
|
cpuid: &CpuId,
|
||||||
|
feature_entry_list: &[CpuidFeatureEntry],
|
||||||
|
) -> Vec<u32> {
|
||||||
|
let mut features = vec![0; feature_entry_list.len()];
|
||||||
|
for (i, feature_entry) in feature_entry_list.iter().enumerate() {
|
||||||
|
for cpuid_entry in cpuid.as_slice().iter() {
|
||||||
|
if cpuid_entry.function == feature_entry.function
|
||||||
|
&& cpuid_entry.index == feature_entry.index
|
||||||
|
{
|
||||||
|
match feature_entry.feature_reg {
|
||||||
|
CpuidReg::EAX => {
|
||||||
|
features[i] = cpuid_entry.eax;
|
||||||
|
}
|
||||||
|
CpuidReg::EBX => {
|
||||||
|
features[i] = cpuid_entry.ebx;
|
||||||
|
}
|
||||||
|
CpuidReg::ECX => {
|
||||||
|
features[i] = cpuid_entry.ecx;
|
||||||
|
}
|
||||||
|
CpuidReg::EDX => {
|
||||||
|
features[i] = cpuid_entry.edx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
features
|
||||||
|
}
|
||||||
|
|
||||||
|
// The function returns `Error` (a.k.a. "incompatible"), when the CPUID features from `src_vm_cpuid`
|
||||||
|
// is not a subset of those of the `dest_vm_cpuid`.
|
||||||
|
pub fn check_cpuid_compatibility(
|
||||||
|
src_vm_cpuid: &CpuId,
|
||||||
|
dest_vm_cpuid: &CpuId,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let feature_entry_list = &Self::checked_feature_entry_list();
|
||||||
|
let src_vm_features = Self::get_features_from_cpuid(src_vm_cpuid, feature_entry_list);
|
||||||
|
let dest_vm_features = Self::get_features_from_cpuid(dest_vm_cpuid, feature_entry_list);
|
||||||
|
|
||||||
|
// Loop on feature bit and check if the 'source vm' feature is a subset
|
||||||
|
// of those of the 'destination vm' feature
|
||||||
|
let mut compatible = true;
|
||||||
|
for (i, (src_vm_feature, dest_vm_feature)) in src_vm_features
|
||||||
|
.iter()
|
||||||
|
.zip(dest_vm_features.iter())
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
let entry = &feature_entry_list[i];
|
||||||
|
let entry_compatible;
|
||||||
|
match entry.compatible_check {
|
||||||
|
CpuidCompatibleCheck::BitwiseSubset => {
|
||||||
|
let different_feature_bits = src_vm_feature ^ dest_vm_feature;
|
||||||
|
let src_vm_feature_bits_only = different_feature_bits & src_vm_feature;
|
||||||
|
entry_compatible = src_vm_feature_bits_only == 0;
|
||||||
|
}
|
||||||
|
CpuidCompatibleCheck::Equal => {
|
||||||
|
entry_compatible = src_vm_feature == dest_vm_feature;
|
||||||
|
}
|
||||||
|
CpuidCompatibleCheck::NumNotGreater => {
|
||||||
|
entry_compatible = src_vm_feature <= dest_vm_feature;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if !entry_compatible {
|
||||||
|
error!(
|
||||||
|
"Detected incompatible CPUID entry: leaf={:#02x} (subleaf={:#02x}), register='{:?}', \
|
||||||
|
compatilbe_check='{:?}', source VM feature='{:#04x}', destination VM feature'{:#04x}'.",
|
||||||
|
entry.function, entry.index, entry.feature_reg,
|
||||||
|
entry.compatible_check, src_vm_feature, dest_vm_feature
|
||||||
|
);
|
||||||
|
|
||||||
|
compatible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if compatible {
|
||||||
|
info!("No CPU incompatibility detected.");
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::CpuidCheckCompatibility)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_common_cpuid(
|
||||||
|
hypervisor: Arc<dyn hypervisor::Hypervisor>,
|
||||||
|
topology: Option<(u8, u8, u8)>,
|
||||||
|
sgx_epc_sections: Option<Vec<SgxEpcSection>>,
|
||||||
|
phys_bits: u8,
|
||||||
|
kvm_hyperv: bool,
|
||||||
|
#[cfg(feature = "tdx")] tdx_enabled: bool,
|
||||||
|
) -> super::Result<CpuId> {
|
||||||
|
let cpuid_patches = vec![
|
||||||
|
// Patch tsc deadline timer bit
|
||||||
|
CpuidPatch {
|
||||||
|
function: 1,
|
||||||
|
index: 0,
|
||||||
|
flags_bit: None,
|
||||||
|
eax_bit: None,
|
||||||
|
ebx_bit: None,
|
||||||
|
ecx_bit: Some(TSC_DEADLINE_TIMER_ECX_BIT),
|
||||||
|
edx_bit: None,
|
||||||
|
},
|
||||||
|
// Patch hypervisor bit
|
||||||
|
CpuidPatch {
|
||||||
|
function: 1,
|
||||||
|
index: 0,
|
||||||
|
flags_bit: None,
|
||||||
|
eax_bit: None,
|
||||||
|
ebx_bit: None,
|
||||||
|
ecx_bit: Some(HYPERVISOR_ECX_BIT),
|
||||||
|
edx_bit: None,
|
||||||
|
},
|
||||||
|
// Enable MTRR feature
|
||||||
|
CpuidPatch {
|
||||||
|
function: 1,
|
||||||
|
index: 0,
|
||||||
|
flags_bit: None,
|
||||||
|
eax_bit: None,
|
||||||
|
ebx_bit: None,
|
||||||
|
ecx_bit: None,
|
||||||
|
edx_bit: Some(MTRR_EDX_BIT),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Supported CPUID
|
||||||
|
let mut cpuid = hypervisor.get_cpuid().map_err(Error::CpuidGetSupported)?;
|
||||||
|
|
||||||
|
CpuidPatch::patch_cpuid(&mut cpuid, cpuid_patches);
|
||||||
|
|
||||||
|
if let Some(t) = topology {
|
||||||
|
update_cpuid_topology(&mut cpuid, t.0, t.1, t.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(sgx_epc_sections) = sgx_epc_sections {
|
||||||
|
update_cpuid_sgx(&mut cpuid, sgx_epc_sections)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update some existing CPUID
|
||||||
|
for entry in cpuid.as_mut_slice().iter_mut() {
|
||||||
|
match entry.function {
|
||||||
|
// Set CPU physical bits
|
||||||
|
0x8000_0008 => {
|
||||||
|
entry.eax = (entry.eax & 0xffff_ff00) | (phys_bits as u32 & 0xff);
|
||||||
|
}
|
||||||
|
// Disable KVM_FEATURE_ASYNC_PF_INT
|
||||||
|
// This is required until we find out why the asynchronous page
|
||||||
|
// fault is generating unexpected behavior when using interrupt
|
||||||
|
// mechanism.
|
||||||
|
// TODO: Re-enable KVM_FEATURE_ASYNC_PF_INT (#2277)
|
||||||
|
0x4000_0001 => {
|
||||||
|
entry.eax &= !(1 << KVM_FEATURE_ASYNC_PF_INT_BIT);
|
||||||
|
|
||||||
|
// These features are not supported by TDX
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
if tdx_enabled {
|
||||||
|
entry.eax &= !(1 << KVM_FEATURE_CLOCKSOURCE_BIT
|
||||||
|
| 1 << KVM_FEATURE_CLOCKSOURCE2_BIT
|
||||||
|
| 1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT
|
||||||
|
| 1 << KVM_FEATURE_ASYNC_PF_BIT
|
||||||
|
| 1 << KVM_FEATURE_ASYNC_PF_VMEXIT_BIT
|
||||||
|
| 1 << KVM_FEATURE_STEAL_TIME_BIT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy CPU identification string
|
||||||
|
for i in 0x8000_0002..=0x8000_0004 {
|
||||||
|
cpuid.retain(|c| c.function != i);
|
||||||
|
let leaf = unsafe { std::arch::x86_64::__cpuid(i) };
|
||||||
|
cpuid
|
||||||
|
.push(CpuIdEntry {
|
||||||
|
function: i,
|
||||||
|
eax: leaf.eax,
|
||||||
|
ebx: leaf.ebx,
|
||||||
|
ecx: leaf.ecx,
|
||||||
|
edx: leaf.edx,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.map_err(Error::CpuidIdentification)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if kvm_hyperv {
|
||||||
|
// Remove conflicting entries
|
||||||
|
cpuid.retain(|c| c.function != 0x4000_0000);
|
||||||
|
cpuid.retain(|c| c.function != 0x4000_0001);
|
||||||
|
// See "Hypervisor Top Level Functional Specification" for details
|
||||||
|
// Compliance with "Hv#1" requires leaves up to 0x4000_000a
|
||||||
|
cpuid
|
||||||
|
.push(CpuIdEntry {
|
||||||
|
function: 0x40000000,
|
||||||
|
eax: 0x4000000a, // Maximum cpuid leaf
|
||||||
|
ebx: 0x756e694c, // "Linu"
|
||||||
|
ecx: 0x564b2078, // "x KV"
|
||||||
|
edx: 0x7648204d, // "M Hv"
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.map_err(Error::CpuidKvmHyperV)?;
|
||||||
|
cpuid
|
||||||
|
.push(CpuIdEntry {
|
||||||
|
function: 0x40000001,
|
||||||
|
eax: 0x31237648, // "Hv#1"
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.map_err(Error::CpuidKvmHyperV)?;
|
||||||
|
cpuid
|
||||||
|
.push(CpuIdEntry {
|
||||||
|
function: 0x40000002,
|
||||||
|
eax: 0x3839, // "Build number"
|
||||||
|
ebx: 0xa0000, // "Version"
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.map_err(Error::CpuidKvmHyperV)?;
|
||||||
|
cpuid
|
||||||
|
.push(CpuIdEntry {
|
||||||
|
function: 0x4000_0003,
|
||||||
|
eax: 1 << 1 // AccessPartitionReferenceCounter
|
||||||
|
| 1 << 2 // AccessSynicRegs
|
||||||
|
| 1 << 3 // AccessSyntheticTimerRegs
|
||||||
|
| 1 << 9, // AccessPartitionReferenceTsc
|
||||||
|
edx: 1 << 3, // CPU dynamic partitioning
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.map_err(Error::CpuidKvmHyperV)?;
|
||||||
|
cpuid
|
||||||
|
.push(CpuIdEntry {
|
||||||
|
function: 0x4000_0004,
|
||||||
|
eax: 1 << 5, // Recommend relaxed timing
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.map_err(Error::CpuidKvmHyperV)?;
|
||||||
|
for i in 0x4000_0005..=0x4000_000a {
|
||||||
|
cpuid
|
||||||
|
.push(CpuIdEntry {
|
||||||
|
function: i,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.map_err(Error::CpuidKvmHyperV)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(cpuid)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn configure_vcpu(
|
pub fn configure_vcpu(
|
||||||
fd: &Arc<dyn hypervisor::Vcpu>,
|
fd: &Arc<dyn hypervisor::Vcpu>,
|
||||||
id: u8,
|
id: u8,
|
||||||
@@ -316,7 +727,7 @@ pub fn configure_vcpu(
|
|||||||
cpuid: CpuId,
|
cpuid: CpuId,
|
||||||
kvm_hyperv: bool,
|
kvm_hyperv: bool,
|
||||||
) -> super::Result<()> {
|
) -> super::Result<()> {
|
||||||
// Per vCPU CPUID changes; common are handled via CpuManager::generate_common_cpuid()
|
// Per vCPU CPUID changes; common are handled via generate_common_cpuid()
|
||||||
let mut cpuid = cpuid;
|
let mut cpuid = cpuid;
|
||||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id));
|
CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id));
|
||||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1f, None, CpuidReg::EDX, u32::from(id));
|
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1f, None, CpuidReg::EDX, u32::from(id));
|
||||||
@@ -330,11 +741,12 @@ pub fn configure_vcpu(
|
|||||||
|
|
||||||
regs::setup_msrs(fd).map_err(Error::MsrsConfiguration)?;
|
regs::setup_msrs(fd).map_err(Error::MsrsConfiguration)?;
|
||||||
if let Some(kernel_entry_point) = kernel_entry_point {
|
if let Some(kernel_entry_point) = kernel_entry_point {
|
||||||
// Safe to unwrap because this method is called after the VM is configured
|
if let Some(entry_addr) = kernel_entry_point.entry_addr {
|
||||||
regs::setup_regs(fd, kernel_entry_point.entry_addr.raw_value())
|
// Safe to unwrap because this method is called after the VM is configured
|
||||||
.map_err(Error::RegsConfiguration)?;
|
regs::setup_regs(fd, entry_addr.raw_value()).map_err(Error::RegsConfiguration)?;
|
||||||
regs::setup_fpu(fd).map_err(Error::FpuConfiguration)?;
|
regs::setup_fpu(fd).map_err(Error::FpuConfiguration)?;
|
||||||
regs::setup_sregs(&vm_memory.memory(), fd).map_err(Error::SregsConfiguration)?;
|
regs::setup_sregs(&vm_memory.memory(), fd).map_err(Error::SregsConfiguration)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
interrupts::set_lint(fd).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
|
interrupts::set_lint(fd).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -613,7 +1025,7 @@ pub fn get_host_cpu_phys_bits() -> u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_cpuid_topology(
|
fn update_cpuid_topology(
|
||||||
cpuid: &mut CpuId,
|
cpuid: &mut CpuId,
|
||||||
threads_per_core: u8,
|
threads_per_core: u8,
|
||||||
cores_per_die: u8,
|
cores_per_die: u8,
|
||||||
@@ -678,7 +1090,7 @@ pub fn update_cpuid_topology(
|
|||||||
|
|
||||||
// The goal is to update the CPUID sub-leaves to reflect the number of EPC
|
// The goal is to update the CPUID sub-leaves to reflect the number of EPC
|
||||||
// sections exposed to the guest.
|
// sections exposed to the guest.
|
||||||
pub fn update_cpuid_sgx(cpuid: &mut CpuId, epc_sections: Vec<SgxEpcSection>) -> Result<(), Error> {
|
fn update_cpuid_sgx(cpuid: &mut CpuId, epc_sections: Vec<SgxEpcSection>) -> Result<(), Error> {
|
||||||
// Something's wrong if there's no EPC section.
|
// Something's wrong if there's no EPC section.
|
||||||
if epc_sections.is_empty() {
|
if epc_sections.is_empty() {
|
||||||
return Err(Error::NoSgxEpcSection);
|
return Err(Error::NoSgxEpcSection);
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ struct MpcLintsrcWrapper(mpspec::mpc_lintsrc);
|
|||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
struct MpfIntelWrapper(mpspec::mpf_intel);
|
struct MpfIntelWrapper(mpspec::mpf_intel);
|
||||||
|
|
||||||
// These `mpspec` wrapper types are only data, reading them from data is a safe initialization.
|
// SAFETY: These `mpspec` wrapper types are only data, reading them from data is a safe initialization.
|
||||||
unsafe impl ByteValued for MpcBusWrapper {}
|
unsafe impl ByteValued for MpcBusWrapper {}
|
||||||
unsafe impl ByteValued for MpcCpuWrapper {}
|
unsafe impl ByteValued for MpcCpuWrapper {}
|
||||||
unsafe impl ByteValued for MpcIntsrcWrapper {}
|
unsafe impl ByteValued for MpcIntsrcWrapper {}
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ pub struct Smbios30Entrypoint {
|
|||||||
pub max_size: u32,
|
pub max_size: u32,
|
||||||
pub physptr: u64,
|
pub physptr: u64,
|
||||||
}
|
}
|
||||||
unsafe impl ByteValued for Smbios30Entrypoint {}
|
|
||||||
|
|
||||||
impl Clone for Smbios30Entrypoint {
|
impl Clone for Smbios30Entrypoint {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
@@ -112,8 +111,6 @@ impl Clone for SmbiosBiosInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl ByteValued for SmbiosBiosInfo {}
|
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
#[derive(Default, Copy)]
|
#[derive(Default, Copy)]
|
||||||
pub struct SmbiosSysInfo {
|
pub struct SmbiosSysInfo {
|
||||||
@@ -136,6 +133,9 @@ impl Clone for SmbiosSysInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: These data structures only contain a series of integers
|
||||||
|
unsafe impl ByteValued for Smbios30Entrypoint {}
|
||||||
|
unsafe impl ByteValued for SmbiosBiosInfo {}
|
||||||
unsafe impl ByteValued for SmbiosSysInfo {}
|
unsafe impl ByteValued for SmbiosSysInfo {}
|
||||||
|
|
||||||
fn write_and_incr<T: ByteValued>(
|
fn write_and_incr<T: ByteValued>(
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ pub enum TdvfError {
|
|||||||
|
|
||||||
// TDVF_DESCRIPTOR
|
// TDVF_DESCRIPTOR
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
|
#[derive(Default)]
|
||||||
pub struct TdvfDescriptor {
|
pub struct TdvfDescriptor {
|
||||||
signature: [u8; 4],
|
signature: [u8; 4],
|
||||||
length: u32,
|
length: u32,
|
||||||
@@ -60,6 +61,57 @@ impl Default for TdvfSectionType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Default, Debug)]
|
||||||
|
pub struct TdVmmDataRegion {
|
||||||
|
pub start_address: u64,
|
||||||
|
pub length: u64,
|
||||||
|
pub region_type: TdVmmDataRegionType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u16)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum TdVmmDataRegionType {
|
||||||
|
Signature = 0x0000,
|
||||||
|
InterfaceVersion = 0x0001,
|
||||||
|
SystemUuid = 0x0002,
|
||||||
|
RamSize = 0x0003,
|
||||||
|
GraphicsEnabled = 0x0004,
|
||||||
|
SmpCpuCount = 0x0005,
|
||||||
|
MachineId = 0x0006,
|
||||||
|
KernelAddress = 0x0007,
|
||||||
|
KernelSize = 0x0008,
|
||||||
|
KernelCommandLine = 0x0009,
|
||||||
|
InitrdAddress = 0x000a,
|
||||||
|
InitrdSize = 0x000b,
|
||||||
|
BootDevice = 0x000c,
|
||||||
|
NumaData = 0x000d,
|
||||||
|
BootMenu = 0x000e,
|
||||||
|
MaximumCpuCount = 0x000f,
|
||||||
|
KernelEntry = 0x0010,
|
||||||
|
KernelData = 0x0011,
|
||||||
|
InitrdData = 0x0012,
|
||||||
|
CommandLineAddress = 0x0013,
|
||||||
|
CommandLineSize = 0x0014,
|
||||||
|
CommandLineData = 0x0015,
|
||||||
|
KernelSetupAddress = 0x0016,
|
||||||
|
KernelSetupSize = 0x0017,
|
||||||
|
KernelSetupData = 0x0018,
|
||||||
|
FileDir = 0x0019,
|
||||||
|
AcpiTables = 0x8000,
|
||||||
|
SmbiosTables = 0x8001,
|
||||||
|
Irq0Override = 0x8002,
|
||||||
|
E820Table = 0x8003,
|
||||||
|
HpetData = 0x8004,
|
||||||
|
Reserved = 0xffff,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TdVmmDataRegionType {
|
||||||
|
fn default() -> Self {
|
||||||
|
TdVmmDataRegionType::Reserved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_tdvf_sections(file: &mut File) -> Result<Vec<TdvfSection>, TdvfError> {
|
pub fn parse_tdvf_sections(file: &mut File) -> Result<Vec<TdvfSection>, TdvfError> {
|
||||||
// The 32-bit offset to the TDVF metadata is located 32 bytes from
|
// The 32-bit offset to the TDVF metadata is located 32 bytes from
|
||||||
// the end of the file.
|
// the end of the file.
|
||||||
@@ -75,7 +127,7 @@ pub fn parse_tdvf_sections(file: &mut File) -> Result<Vec<TdvfSection>, TdvfErro
|
|||||||
file.seek(SeekFrom::Start(descriptor_offset))
|
file.seek(SeekFrom::Start(descriptor_offset))
|
||||||
.map_err(TdvfError::ReadDescriptor)?;
|
.map_err(TdvfError::ReadDescriptor)?;
|
||||||
|
|
||||||
let mut descriptor: TdvfDescriptor = unsafe { std::mem::zeroed() };
|
let mut descriptor: TdvfDescriptor = Default::default();
|
||||||
// Safe as we read exactly the size of the descriptor header
|
// Safe as we read exactly the size of the descriptor header
|
||||||
file.read_exact(unsafe {
|
file.read_exact(unsafe {
|
||||||
std::slice::from_raw_parts_mut(
|
std::slice::from_raw_parts_mut(
|
||||||
@@ -120,6 +172,7 @@ pub fn parse_tdvf_sections(file: &mut File) -> Result<Vec<TdvfSection>, TdvfErro
|
|||||||
enum HobType {
|
enum HobType {
|
||||||
Handoff = 0x1,
|
Handoff = 0x1,
|
||||||
ResourceDescriptor = 0x3,
|
ResourceDescriptor = 0x3,
|
||||||
|
GuidExtension = 0x4,
|
||||||
Unused = 0xfffe,
|
Unused = 0xfffe,
|
||||||
EndOfHobList = 0xffff,
|
EndOfHobList = 0xffff,
|
||||||
}
|
}
|
||||||
@@ -137,20 +190,19 @@ struct HobHeader {
|
|||||||
length: u16,
|
length: u16,
|
||||||
reserved: u32,
|
reserved: u32,
|
||||||
}
|
}
|
||||||
unsafe impl ByteValued for HobHeader {}
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Default, Debug)]
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
struct HobHandoffInfoTable {
|
struct HobHandoffInfoTable {
|
||||||
header: HobHeader,
|
header: HobHeader,
|
||||||
version: u32,
|
version: u32,
|
||||||
|
boot_mode: u32,
|
||||||
efi_memory_top: u64,
|
efi_memory_top: u64,
|
||||||
efi_memory_bottom: u64,
|
efi_memory_bottom: u64,
|
||||||
efi_free_memory_top: u64,
|
efi_free_memory_top: u64,
|
||||||
efi_free_memory_bottom: u64,
|
efi_free_memory_bottom: u64,
|
||||||
efi_end_of_hob_list: u64,
|
efi_end_of_hob_list: u64,
|
||||||
}
|
}
|
||||||
unsafe impl ByteValued for HobHandoffInfoTable {}
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Default, Debug)]
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
@@ -171,7 +223,28 @@ struct HobResourceDescriptor {
|
|||||||
physical_start: u64,
|
physical_start: u64,
|
||||||
resource_length: u64,
|
resource_length: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
|
struct HobGuidType {
|
||||||
|
header: HobHeader,
|
||||||
|
name: EfiGuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
|
struct TdVmmData {
|
||||||
|
guid_type: HobGuidType,
|
||||||
|
region: TdVmmDataRegion,
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: These data structures only contain a series of integers
|
||||||
|
unsafe impl ByteValued for TdVmmDataRegion {}
|
||||||
|
unsafe impl ByteValued for HobHeader {}
|
||||||
|
unsafe impl ByteValued for HobHandoffInfoTable {}
|
||||||
unsafe impl ByteValued for HobResourceDescriptor {}
|
unsafe impl ByteValued for HobResourceDescriptor {}
|
||||||
|
unsafe impl ByteValued for HobGuidType {}
|
||||||
|
unsafe impl ByteValued for TdVmmData {}
|
||||||
|
|
||||||
pub struct TdHob {
|
pub struct TdHob {
|
||||||
start_offset: u64,
|
start_offset: u64,
|
||||||
@@ -218,6 +291,7 @@ impl TdHob {
|
|||||||
reserved: 0,
|
reserved: 0,
|
||||||
},
|
},
|
||||||
version: 0x9,
|
version: 0x9,
|
||||||
|
boot_mode: 0,
|
||||||
efi_memory_top: 0,
|
efi_memory_top: 0,
|
||||||
efi_memory_bottom: 0,
|
efi_memory_bottom: 0,
|
||||||
efi_free_memory_top: 0,
|
efi_free_memory_top: 0,
|
||||||
@@ -277,10 +351,10 @@ impl TdHob {
|
|||||||
},
|
},
|
||||||
/* TODO:
|
/* TODO:
|
||||||
* QEMU currently fills it in like this:
|
* QEMU currently fills it in like this:
|
||||||
* EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_TESTED
|
* EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_ENCRYPTED | EFI_RESOURCE_ATTRIBUTE_TESTED
|
||||||
* which differs from the spec (due to TDVF implementation issue?)
|
* which differs from the spec (due to TDVF implementation issue?)
|
||||||
*/
|
*/
|
||||||
0x7,
|
0x04000007,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,6 +375,38 @@ impl TdHob {
|
|||||||
0x403,
|
0x403,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_td_vmm_data(
|
||||||
|
&mut self,
|
||||||
|
mem: &GuestMemoryMmap,
|
||||||
|
region: TdVmmDataRegion,
|
||||||
|
) -> Result<(), TdvfError> {
|
||||||
|
let td_vmm_data = TdVmmData {
|
||||||
|
guid_type: HobGuidType {
|
||||||
|
header: HobHeader {
|
||||||
|
r#type: HobType::GuidExtension,
|
||||||
|
length: std::mem::size_of::<TdVmmData>() as u16,
|
||||||
|
reserved: 0,
|
||||||
|
},
|
||||||
|
// TD_VMM_DATA_GUID CF2643E4-C0D3-46FF-0000-72EE623DDE38
|
||||||
|
name: EfiGuid {
|
||||||
|
data1: 0xcf26_43e4,
|
||||||
|
data2: 0xc0d3,
|
||||||
|
data3: 0x46ff,
|
||||||
|
data4: [0x00, 0x00, 0x72, 0xee, 0x62, 0x3d, 0xde, 0x38],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
region,
|
||||||
|
};
|
||||||
|
info!(
|
||||||
|
"Writing HOB TD_VMM_DATA {:x} {:x?}",
|
||||||
|
self.current_offset, td_vmm_data
|
||||||
|
);
|
||||||
|
mem.write_obj(td_vmm_data, GuestAddress(self.current_offset))
|
||||||
|
.map_err(TdvfError::GuestMemoryWriteHob)?;
|
||||||
|
self.update_offset::<TdVmmData>();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -9,15 +9,17 @@ default = []
|
|||||||
io_uring = []
|
io_uring = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
io-uring = ">=0.4.0"
|
io-uring = "0.5.2"
|
||||||
libc = "0.2.96"
|
libc = "0.2.108"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
qcow = { path = "../qcow" }
|
qcow = { path = "../qcow" }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0.30"
|
||||||
versionize = "0.1.6"
|
versionize = "0.1.6"
|
||||||
versionize_derive = "0.1.4"
|
versionize_derive = "0.1.4"
|
||||||
virtio-bindings = { version = "0.1", features = ["virtio-v5_0_0"]}
|
vhdx = { path = "../vhdx" }
|
||||||
vm-memory = { version = "0.5.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
virtio-bindings = { version = "0.1.0", features = ["virtio-v5_0_0"] }
|
||||||
|
virtio-queue = { path = "../virtio-queue" }
|
||||||
|
vm-memory = { version = "0.6.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = "0.9.0"
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ pub mod qcow_sync;
|
|||||||
pub mod raw_async;
|
pub mod raw_async;
|
||||||
pub mod raw_sync;
|
pub mod raw_sync;
|
||||||
pub mod vhd;
|
pub mod vhd;
|
||||||
|
pub mod vhdx_sync;
|
||||||
|
|
||||||
use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult, DiskFileError, DiskFileResult};
|
use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult, DiskFileError, DiskFileResult};
|
||||||
#[cfg(feature = "io_uring")]
|
#[cfg(feature = "io_uring")]
|
||||||
@@ -27,19 +28,17 @@ use std::convert::TryInto;
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
|
use std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
|
||||||
use std::os::linux::fs::MetadataExt;
|
use std::os::linux::fs::MetadataExt;
|
||||||
#[cfg(feature = "io_uring")]
|
|
||||||
use std::os::unix::io::AsRawFd;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||||
use versionize_derive::Versionize;
|
use versionize_derive::Versionize;
|
||||||
use virtio_bindings::bindings::virtio_blk::*;
|
use virtio_bindings::bindings::virtio_blk::*;
|
||||||
|
use virtio_queue::DescriptorChain;
|
||||||
use vm_memory::{
|
use vm_memory::{
|
||||||
bitmap::AtomicBitmap, bitmap::Bitmap, ByteValued, Bytes, GuestAddress, GuestMemory,
|
bitmap::AtomicBitmap, bitmap::Bitmap, ByteValued, Bytes, GuestAddress, GuestMemory,
|
||||||
GuestMemoryError,
|
GuestMemoryAtomic, GuestMemoryError,
|
||||||
};
|
};
|
||||||
use vm_virtio::DescriptorChain;
|
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||||
@@ -178,25 +177,32 @@ pub struct Request {
|
|||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
pub fn parse(
|
pub fn parse(
|
||||||
avail_desc: &DescriptorChain,
|
desc_chain: &mut DescriptorChain<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||||
mem: &GuestMemoryMmap,
|
|
||||||
) -> result::Result<Request, Error> {
|
) -> result::Result<Request, Error> {
|
||||||
|
let hdr_desc = desc_chain
|
||||||
|
.next()
|
||||||
|
.ok_or(Error::DescriptorChainTooShort)
|
||||||
|
.map_err(|e| {
|
||||||
|
error!("Missing head descriptor");
|
||||||
|
e
|
||||||
|
})?;
|
||||||
|
|
||||||
// The head contains the request type which MUST be readable.
|
// The head contains the request type which MUST be readable.
|
||||||
if avail_desc.is_write_only() {
|
if hdr_desc.is_write_only() {
|
||||||
return Err(Error::UnexpectedWriteOnlyDescriptor);
|
return Err(Error::UnexpectedWriteOnlyDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut req = Request {
|
let mut req = Request {
|
||||||
request_type: request_type(&mem, avail_desc.addr)?,
|
request_type: request_type(desc_chain.memory(), hdr_desc.addr())?,
|
||||||
sector: sector(&mem, avail_desc.addr)?,
|
sector: sector(desc_chain.memory(), hdr_desc.addr())?,
|
||||||
data_descriptors: Vec::new(),
|
data_descriptors: Vec::new(),
|
||||||
status_addr: GuestAddress(0),
|
status_addr: GuestAddress(0),
|
||||||
writeback: true,
|
writeback: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let status_desc;
|
let status_desc;
|
||||||
let mut desc = avail_desc
|
let mut desc = desc_chain
|
||||||
.next_descriptor()
|
.next()
|
||||||
.ok_or(Error::DescriptorChainTooShort)
|
.ok_or(Error::DescriptorChainTooShort)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
error!("Only head descriptor present: request = {:?}", req);
|
error!("Only head descriptor present: request = {:?}", req);
|
||||||
@@ -221,9 +227,9 @@ impl Request {
|
|||||||
if !desc.is_write_only() && req.request_type == RequestType::GetDeviceId {
|
if !desc.is_write_only() && req.request_type == RequestType::GetDeviceId {
|
||||||
return Err(Error::UnexpectedReadOnlyDescriptor);
|
return Err(Error::UnexpectedReadOnlyDescriptor);
|
||||||
}
|
}
|
||||||
req.data_descriptors.push((desc.addr, desc.len));
|
req.data_descriptors.push((desc.addr(), desc.len()));
|
||||||
desc = desc
|
desc = desc_chain
|
||||||
.next_descriptor()
|
.next()
|
||||||
.ok_or(Error::DescriptorChainTooShort)
|
.ok_or(Error::DescriptorChainTooShort)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
error!("DescriptorChain corrupted: request = {:?}", req);
|
error!("DescriptorChain corrupted: request = {:?}", req);
|
||||||
@@ -238,11 +244,11 @@ impl Request {
|
|||||||
return Err(Error::UnexpectedReadOnlyDescriptor);
|
return Err(Error::UnexpectedReadOnlyDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if status_desc.len < 1 {
|
if status_desc.len() < 1 {
|
||||||
return Err(Error::DescriptorLengthTooSmall);
|
return Err(Error::DescriptorLengthTooSmall);
|
||||||
}
|
}
|
||||||
|
|
||||||
req.status_addr = status_desc.addr;
|
req.status_addr = status_desc.addr();
|
||||||
|
|
||||||
Ok(req)
|
Ok(req)
|
||||||
}
|
}
|
||||||
@@ -288,7 +294,7 @@ impl Request {
|
|||||||
if (*data_len as usize) < disk_id.len() {
|
if (*data_len as usize) < disk_id.len() {
|
||||||
return Err(ExecuteError::BadRequest(Error::InvalidOffset));
|
return Err(ExecuteError::BadRequest(Error::InvalidOffset));
|
||||||
}
|
}
|
||||||
mem.write_slice(&disk_id.as_slice(), *data_addr)
|
mem.write_slice(disk_id.as_slice(), *data_addr)
|
||||||
.map_err(ExecuteError::Write)?;
|
.map_err(ExecuteError::Write)?;
|
||||||
}
|
}
|
||||||
RequestType::Unsupported(t) => return Err(ExecuteError::Unsupported(t)),
|
RequestType::Unsupported(t) => return Err(ExecuteError::Unsupported(t)),
|
||||||
@@ -403,8 +409,6 @@ pub struct VirtioBlockConfig {
|
|||||||
pub write_zeroes_may_unmap: u8,
|
pub write_zeroes_may_unmap: u8,
|
||||||
pub unused1: [u8; 3],
|
pub unused1: [u8; 3],
|
||||||
}
|
}
|
||||||
unsafe impl ByteValued for VirtioBlockConfig {}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, Versionize)]
|
#[derive(Copy, Clone, Debug, Default, Versionize)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub struct VirtioBlockGeometry {
|
pub struct VirtioBlockGeometry {
|
||||||
@@ -412,6 +416,9 @@ pub struct VirtioBlockGeometry {
|
|||||||
pub heads: u8,
|
pub heads: u8,
|
||||||
pub sectors: u8,
|
pub sectors: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: these data structures only contain a series of integers
|
||||||
|
unsafe impl ByteValued for VirtioBlockConfig {}
|
||||||
unsafe impl ByteValued for VirtioBlockGeometry {}
|
unsafe impl ByteValued for VirtioBlockGeometry {}
|
||||||
|
|
||||||
/// Check if io_uring for block device can be used on the current system, as
|
/// Check if io_uring for block device can be used on the current system, as
|
||||||
@@ -432,25 +439,6 @@ pub fn block_io_uring_is_supported() -> bool {
|
|||||||
|
|
||||||
let submitter = io_uring.submitter();
|
let submitter = io_uring.submitter();
|
||||||
|
|
||||||
let event_fd = match EventFd::new(libc::EFD_NONBLOCK) {
|
|
||||||
Ok(fd) => fd,
|
|
||||||
Err(e) => {
|
|
||||||
info!("{} failed to create eventfd: {}", error_msg, e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check we can register an eventfd as this is going to be needed while
|
|
||||||
// using io_uring with the virtio block device. This also validates that
|
|
||||||
// io_uring_register() syscall is supported.
|
|
||||||
match submitter.register_eventfd(event_fd.as_raw_fd()) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
info!("{} failed to register eventfd: {}", error_msg, e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut probe = Probe::new();
|
let mut probe = Probe::new();
|
||||||
|
|
||||||
// Check we can register a probe to validate supported operations.
|
// Check we can register a probe to validate supported operations.
|
||||||
@@ -602,9 +590,11 @@ pub enum ImageType {
|
|||||||
FixedVhd,
|
FixedVhd,
|
||||||
Qcow2,
|
Qcow2,
|
||||||
Raw,
|
Raw,
|
||||||
|
Vhdx,
|
||||||
}
|
}
|
||||||
|
|
||||||
const QCOW_MAGIC: u32 = 0x5146_49fb;
|
const QCOW_MAGIC: u32 = 0x5146_49fb;
|
||||||
|
const VHDX_SIGN: u64 = 0x656C_6966_7864_6876;
|
||||||
|
|
||||||
/// Determine image type through file parsing.
|
/// Determine image type through file parsing.
|
||||||
pub fn detect_image_type(f: &mut File) -> std::io::Result<ImageType> {
|
pub fn detect_image_type(f: &mut File) -> std::io::Result<ImageType> {
|
||||||
@@ -623,6 +613,8 @@ pub fn detect_image_type(f: &mut File) -> std::io::Result<ImageType> {
|
|||||||
ImageType::Qcow2
|
ImageType::Qcow2
|
||||||
} else if vhd::is_fixed_vhd(f)? {
|
} else if vhd::is_fixed_vhd(f)? {
|
||||||
ImageType::FixedVhd
|
ImageType::FixedVhd
|
||||||
|
} else if u64::from_le_bytes(s.data[0..8].try_into().unwrap()) == VHDX_SIGN {
|
||||||
|
ImageType::Vhdx
|
||||||
} else {
|
} else {
|
||||||
ImageType::Raw
|
ImageType::Raw
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileResult};
|
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileResult};
|
||||||
use crate::{disk_size, fsync_sync, read_vectored_sync, write_vectored_sync};
|
use crate::{disk_size, fsync_sync, read_vectored_sync, write_vectored_sync};
|
||||||
use qcow::{QcowFile, RawFile};
|
use qcow::{QcowFile, RawFile, Result as QcowResult};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
@@ -15,12 +15,11 @@ pub struct QcowDiskSync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl QcowDiskSync {
|
impl QcowDiskSync {
|
||||||
pub fn new(file: File, direct_io: bool) -> Self {
|
pub fn new(file: File, direct_io: bool) -> QcowResult<Self> {
|
||||||
QcowDiskSync {
|
Ok(QcowDiskSync {
|
||||||
qcow_file: QcowFile::from(RawFile::new(file, direct_io))
|
qcow_file: QcowFile::from(RawFile::new(file, direct_io))?,
|
||||||
.expect("Failed creating QcowFile"),
|
|
||||||
semaphore: Arc::new(Mutex::new(())),
|
semaphore: Arc::new(Mutex::new(())),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ mod tests {
|
|||||||
let mut disk_file: File = TempFile::new().unwrap().into_file();
|
let mut disk_file: File = TempFile::new().unwrap().into_file();
|
||||||
disk_file.set_len(0x1000_0200).unwrap();
|
disk_file.set_len(0x1000_0200).unwrap();
|
||||||
disk_file.seek(SeekFrom::Start(0x1000_0000)).unwrap();
|
disk_file.seek(SeekFrom::Start(0x1000_0000)).unwrap();
|
||||||
disk_file.write_all(&footer).unwrap();
|
disk_file.write_all(footer).unwrap();
|
||||||
|
|
||||||
testfn(disk_file); // File closed when the function exits.
|
testfn(disk_file); // File closed when the function exits.
|
||||||
}
|
}
|
||||||
|
|||||||
113
block_util/src/vhdx_sync.rs
Normal file
113
block_util/src/vhdx_sync.rs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
// Copyright © 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult};
|
||||||
|
use crate::{fsync_sync, read_vectored_sync, write_vectored_sync};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::ops::DerefMut;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use vhdx::vhdx::{Result as VhdxResult, Vhdx};
|
||||||
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
pub struct VhdxDiskSync {
|
||||||
|
vhdx_file: Arc<Mutex<Vhdx>>,
|
||||||
|
semaphore: Arc<Mutex<()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VhdxDiskSync {
|
||||||
|
pub fn new(f: File) -> VhdxResult<Self> {
|
||||||
|
let vhdx = Vhdx::new(f)?;
|
||||||
|
let vhdx_file = Arc::new(Mutex::new(vhdx));
|
||||||
|
|
||||||
|
Ok(VhdxDiskSync {
|
||||||
|
vhdx_file,
|
||||||
|
semaphore: Arc::new(Mutex::new(())),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiskFile for VhdxDiskSync {
|
||||||
|
fn size(&mut self) -> DiskFileResult<u64> {
|
||||||
|
Ok(self.vhdx_file.lock().unwrap().virtual_disk_size())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||||
|
Ok(Box::new(
|
||||||
|
VhdxSync::new(self.vhdx_file.clone(), self.semaphore.clone())
|
||||||
|
.map_err(DiskFileError::NewAsyncIo)?,
|
||||||
|
) as Box<dyn AsyncIo>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VhdxSync {
|
||||||
|
vhdx_file: Arc<Mutex<Vhdx>>,
|
||||||
|
eventfd: EventFd,
|
||||||
|
completion_list: Vec<(u64, i32)>,
|
||||||
|
semaphore: Arc<Mutex<()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VhdxSync {
|
||||||
|
pub fn new(vhdx_file: Arc<Mutex<Vhdx>>, semaphore: Arc<Mutex<()>>) -> std::io::Result<Self> {
|
||||||
|
Ok(VhdxSync {
|
||||||
|
vhdx_file,
|
||||||
|
eventfd: EventFd::new(libc::EFD_NONBLOCK)?,
|
||||||
|
completion_list: Vec::new(),
|
||||||
|
semaphore,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncIo for VhdxSync {
|
||||||
|
fn notifier(&self) -> &EventFd {
|
||||||
|
&self.eventfd
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_vectored(
|
||||||
|
&mut self,
|
||||||
|
offset: libc::off_t,
|
||||||
|
iovecs: Vec<libc::iovec>,
|
||||||
|
user_data: u64,
|
||||||
|
) -> AsyncIoResult<()> {
|
||||||
|
read_vectored_sync(
|
||||||
|
offset,
|
||||||
|
iovecs,
|
||||||
|
user_data,
|
||||||
|
self.vhdx_file.lock().unwrap().deref_mut(),
|
||||||
|
&self.eventfd,
|
||||||
|
&mut self.completion_list,
|
||||||
|
&mut self.semaphore,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(
|
||||||
|
&mut self,
|
||||||
|
offset: libc::off_t,
|
||||||
|
iovecs: Vec<libc::iovec>,
|
||||||
|
user_data: u64,
|
||||||
|
) -> AsyncIoResult<()> {
|
||||||
|
write_vectored_sync(
|
||||||
|
offset,
|
||||||
|
iovecs,
|
||||||
|
user_data,
|
||||||
|
self.vhdx_file.lock().unwrap().deref_mut(),
|
||||||
|
&self.eventfd,
|
||||||
|
&mut self.completion_list,
|
||||||
|
&mut self.semaphore,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
|
||||||
|
fsync_sync(
|
||||||
|
user_data,
|
||||||
|
self.vhdx_file.lock().unwrap().deref_mut(),
|
||||||
|
&self.eventfd,
|
||||||
|
&mut self.completion_list,
|
||||||
|
&mut self.semaphore,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complete(&mut self) -> Vec<(u64, i32)> {
|
||||||
|
self.completion_list.drain(..).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,19 +6,19 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
acpi_tables = { path = "../acpi_tables", optional = true }
|
acpi_tables = { path = "../acpi_tables", optional = true }
|
||||||
anyhow = "1.0"
|
anyhow = "1.0.51"
|
||||||
arch = { path = "../arch" }
|
arch = { path = "../arch" }
|
||||||
bitflags = ">=1.2.1"
|
bitflags = "1.3.2"
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
epoll = ">=4.0.1"
|
epoll = "4.3.1"
|
||||||
libc = "0.2.96"
|
libc = "0.2.108"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
versionize = "0.1.6"
|
versionize = "0.1.6"
|
||||||
versionize_derive = "0.1.4"
|
versionize_derive = "0.1.4"
|
||||||
vm-device = { path = "../vm-device" }
|
vm-device = { path = "../vm-device" }
|
||||||
vm-memory = "0.5.0"
|
vm-memory = "0.6.0"
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = "0.9.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|||||||
@@ -34,14 +34,12 @@ impl AcpiShutdownDevice {
|
|||||||
impl BusDevice for AcpiShutdownDevice {
|
impl BusDevice for AcpiShutdownDevice {
|
||||||
// Spec has all fields as zero
|
// Spec has all fields as zero
|
||||||
fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
|
fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
|
||||||
for i in data.iter_mut() {
|
data.fill(0)
|
||||||
*i = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||||
if data[0] == 1 {
|
if data[0] == 1 {
|
||||||
debug!("ACPI Reboot signalled");
|
info!("ACPI Reboot signalled");
|
||||||
if let Err(e) = self.reset_evt.write(1) {
|
if let Err(e) = self.reset_evt.write(1) {
|
||||||
error!("Error triggering ACPI reset event: {}", e);
|
error!("Error triggering ACPI reset event: {}", e);
|
||||||
}
|
}
|
||||||
@@ -51,7 +49,7 @@ impl BusDevice for AcpiShutdownDevice {
|
|||||||
const SLEEP_STATUS_EN_BIT: u8 = 5;
|
const SLEEP_STATUS_EN_BIT: u8 = 5;
|
||||||
const SLEEP_VALUE_BIT: u8 = 2;
|
const SLEEP_VALUE_BIT: u8 = 2;
|
||||||
if data[0] == (S5_SLEEP_VALUE << SLEEP_VALUE_BIT) | (1 << SLEEP_STATUS_EN_BIT) {
|
if data[0] == (S5_SLEEP_VALUE << SLEEP_VALUE_BIT) | (1 << SLEEP_STATUS_EN_BIT) {
|
||||||
debug!("ACPI Shutdown signalled");
|
info!("ACPI Shutdown signalled");
|
||||||
if let Err(e) = self.exit_evt.write(1) {
|
if let Err(e) = self.exit_evt.write(1) {
|
||||||
error!("Error triggering ACPI shutdown event: {}", e);
|
error!("Error triggering ACPI shutdown event: {}", e);
|
||||||
}
|
}
|
||||||
@@ -62,7 +60,7 @@ impl BusDevice for AcpiShutdownDevice {
|
|||||||
|
|
||||||
/// A device for handling ACPI GED event generation
|
/// A device for handling ACPI GED event generation
|
||||||
pub struct AcpiGedDevice {
|
pub struct AcpiGedDevice {
|
||||||
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
|
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||||
notification_type: AcpiNotificationFlags,
|
notification_type: AcpiNotificationFlags,
|
||||||
ged_irq: u32,
|
ged_irq: u32,
|
||||||
address: GuestAddress,
|
address: GuestAddress,
|
||||||
@@ -70,7 +68,7 @@ pub struct AcpiGedDevice {
|
|||||||
|
|
||||||
impl AcpiGedDevice {
|
impl AcpiGedDevice {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
|
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||||
ged_irq: u32,
|
ged_irq: u32,
|
||||||
address: GuestAddress,
|
address: GuestAddress,
|
||||||
) -> AcpiGedDevice {
|
) -> AcpiGedDevice {
|
||||||
@@ -106,7 +104,7 @@ impl BusDevice for AcpiGedDevice {
|
|||||||
|
|
||||||
#[cfg(feature = "acpi")]
|
#[cfg(feature = "acpi")]
|
||||||
impl Aml for AcpiGedDevice {
|
impl Aml for AcpiGedDevice {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
aml::Device::new(
|
aml::Device::new(
|
||||||
"_SB_.GED_".into(),
|
"_SB_.GED_".into(),
|
||||||
vec![
|
vec![
|
||||||
@@ -153,7 +151,7 @@ impl Aml for AcpiGedDevice {
|
|||||||
&aml::And::new(&aml::Local(1), &aml::Local(0), &4usize),
|
&aml::And::new(&aml::Local(1), &aml::Local(0), &4usize),
|
||||||
&aml::If::new(
|
&aml::If::new(
|
||||||
&aml::Equal::new(&aml::Local(1), &4usize),
|
&aml::Equal::new(&aml::Local(1), &4usize),
|
||||||
vec![&aml::MethodCall::new("\\_SB_.PCI0.PCNT".into(), vec![])],
|
vec![&aml::MethodCall::new("\\_SB_.PHPR.PSCN".into(), vec![])],
|
||||||
),
|
),
|
||||||
&aml::And::new(&aml::Local(1), &aml::Local(0), &8usize),
|
&aml::And::new(&aml::Local(1), &aml::Local(0), &8usize),
|
||||||
&aml::If::new(
|
&aml::If::new(
|
||||||
@@ -167,7 +165,7 @@ impl Aml for AcpiGedDevice {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.to_aml_bytes()
|
.append_aml_bytes(bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,6 +189,10 @@ impl Default for AcpiPmTimerDevice {
|
|||||||
|
|
||||||
impl BusDevice for AcpiPmTimerDevice {
|
impl BusDevice for AcpiPmTimerDevice {
|
||||||
fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
|
fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
|
||||||
|
if data.len() != std::mem::size_of::<u32>() {
|
||||||
|
warn!("Invalid sized read of PM timer: {}", data.len());
|
||||||
|
return;
|
||||||
|
}
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let since = now.duration_since(self.start);
|
let since = now.duration_since(self.start);
|
||||||
let nanos = since.as_nanos();
|
let nanos = since.as_nanos();
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ pub const IRQ_LEGACY_COUNT: usize = 32;
|
|||||||
// 1. Move Gic*.rs from arch/ folder here.
|
// 1. Move Gic*.rs from arch/ folder here.
|
||||||
// 2. Move this file and ioapic.rs to arch/, as they are architecture specific.
|
// 2. Move this file and ioapic.rs to arch/, as they are architecture specific.
|
||||||
pub struct Gic {
|
pub struct Gic {
|
||||||
interrupt_source_group: Arc<Box<dyn InterruptSourceGroup>>,
|
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
|
||||||
gic_device: Option<Arc<Mutex<Box<dyn GicDevice>>>>,
|
gic_device: Option<Arc<Mutex<Box<dyn GicDevice>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ pub struct Ioapic {
|
|||||||
reg_entries: [RedirectionTableEntry; NUM_IOAPIC_PINS],
|
reg_entries: [RedirectionTableEntry; NUM_IOAPIC_PINS],
|
||||||
used_entries: [bool; NUM_IOAPIC_PINS],
|
used_entries: [bool; NUM_IOAPIC_PINS],
|
||||||
apic_address: GuestAddress,
|
apic_address: GuestAddress,
|
||||||
interrupt_source_group: Arc<Box<dyn InterruptSourceGroup>>,
|
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Versionize)]
|
#[derive(Versionize)]
|
||||||
@@ -149,7 +149,10 @@ impl VersionMapped for IoapicState {}
|
|||||||
|
|
||||||
impl BusDevice for Ioapic {
|
impl BusDevice for Ioapic {
|
||||||
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
|
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
|
||||||
assert!(data.len() == 4);
|
if data.len() != std::mem::size_of::<u32>() {
|
||||||
|
warn!("Invalid read size on IOAPIC: {}", data.len());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
debug!("IOAPIC_R @ offset 0x{:x}", offset);
|
debug!("IOAPIC_R @ offset 0x{:x}", offset);
|
||||||
|
|
||||||
@@ -166,7 +169,10 @@ impl BusDevice for Ioapic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||||
assert!(data.len() == 4);
|
if data.len() != std::mem::size_of::<u32>() {
|
||||||
|
warn!("Invalid write size on IOAPIC: {}", data.len());
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
debug!("IOAPIC_W @ offset 0x{:x}", offset);
|
debug!("IOAPIC_W @ offset 0x{:x}", offset);
|
||||||
|
|
||||||
@@ -226,6 +232,10 @@ impl Ioapic {
|
|||||||
IOAPIC_REG_ID => self.id_reg = (val >> 24) & 0xf,
|
IOAPIC_REG_ID => self.id_reg = (val >> 24) & 0xf,
|
||||||
IOWIN_OFF..=REG_MAX_OFFSET => {
|
IOWIN_OFF..=REG_MAX_OFFSET => {
|
||||||
let (index, is_high_bits) = decode_irq_from_selector(self.reg_sel as u8);
|
let (index, is_high_bits) = decode_irq_from_selector(self.reg_sel as u8);
|
||||||
|
if index > NUM_IOAPIC_PINS {
|
||||||
|
warn!("IOAPIC index out of range: {}", index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if is_high_bits {
|
if is_high_bits {
|
||||||
self.reg_entries[index] &= 0xffff_ffff;
|
self.reg_entries[index] &= 0xffff_ffff;
|
||||||
self.reg_entries[index] |= u64::from(val) << 32;
|
self.reg_entries[index] |= u64::from(val) << 32;
|
||||||
@@ -259,6 +269,10 @@ impl Ioapic {
|
|||||||
IOAPIC_REG_ID | IOAPIC_REG_ARBITRATION_ID => (self.id_reg & 0xf) << 24,
|
IOAPIC_REG_ID | IOAPIC_REG_ARBITRATION_ID => (self.id_reg & 0xf) << 24,
|
||||||
IOWIN_OFF..=REG_MAX_OFFSET => {
|
IOWIN_OFF..=REG_MAX_OFFSET => {
|
||||||
let (index, is_high_bits) = decode_irq_from_selector(self.reg_sel as u8);
|
let (index, is_high_bits) = decode_irq_from_selector(self.reg_sel as u8);
|
||||||
|
if index > NUM_IOAPIC_PINS {
|
||||||
|
warn!("IOAPIC index out of range: {}", index);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if is_high_bits {
|
if is_high_bits {
|
||||||
(self.reg_entries[index] >> 32) as u32
|
(self.reg_entries[index] >> 32) as u32
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -2,12 +2,16 @@
|
|||||||
// 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 file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
use libc::{clock_gettime, gmtime_r, time_t, timespec, tm, CLOCK_REALTIME};
|
use libc::{clock_gettime, gmtime_r, timespec, tm, CLOCK_REALTIME};
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
use vm_device::BusDevice;
|
use vm_device::BusDevice;
|
||||||
|
|
||||||
|
// https://github.com/rust-lang/libc/issues/1848
|
||||||
|
#[cfg_attr(target_env = "musl", allow(deprecated))]
|
||||||
|
use libc::time_t;
|
||||||
|
|
||||||
const INDEX_MASK: u8 = 0x7f;
|
const INDEX_MASK: u8 = 0x7f;
|
||||||
const INDEX_OFFSET: u64 = 0x0;
|
const INDEX_OFFSET: u64 = 0x0;
|
||||||
const DATA_OFFSET: u64 = 0x1;
|
const DATA_OFFSET: u64 = 0x1;
|
||||||
@@ -47,13 +51,14 @@ impl Cmos {
|
|||||||
impl BusDevice for Cmos {
|
impl BusDevice for Cmos {
|
||||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||||
if data.len() != 1 {
|
if data.len() != 1 {
|
||||||
|
warn!("Invalid write size on CMOS device: {}", data.len());
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
match offset {
|
match offset {
|
||||||
INDEX_OFFSET => self.index = data[0] & INDEX_MASK,
|
INDEX_OFFSET => self.index = data[0] & INDEX_MASK,
|
||||||
DATA_OFFSET => self.data[self.index as usize] = data[0],
|
DATA_OFFSET => self.data[self.index as usize] = data[0],
|
||||||
o => panic!("bad write offset on CMOS device: {}", o),
|
o => warn!("bad write offset on CMOS device: {}", o),
|
||||||
};
|
};
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -65,6 +70,7 @@ impl BusDevice for Cmos {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if data.len() != 1 {
|
if data.len() != 1 {
|
||||||
|
warn!("Invalid read size on CMOS device: {}", data.len());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,6 +91,8 @@ impl BusDevice for Cmos {
|
|||||||
let mut timespec: timespec = mem::zeroed();
|
let mut timespec: timespec = mem::zeroed();
|
||||||
clock_gettime(CLOCK_REALTIME, &mut timespec as *mut _);
|
clock_gettime(CLOCK_REALTIME, &mut timespec as *mut _);
|
||||||
|
|
||||||
|
// https://github.com/rust-lang/libc/issues/1848
|
||||||
|
#[cfg_attr(target_env = "musl", allow(deprecated))]
|
||||||
let now: time_t = timespec.tv_sec;
|
let now: time_t = timespec.tv_sec;
|
||||||
let mut tm: tm = mem::zeroed();
|
let mut tm: tm = mem::zeroed();
|
||||||
gmtime_r(&now, &mut tm as *mut _);
|
gmtime_r(&now, &mut tm as *mut _);
|
||||||
@@ -113,6 +121,9 @@ impl BusDevice for Cmos {
|
|||||||
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 7 must be 1 (CMOS has power)
|
||||||
|
0x0d => 1 << 7,
|
||||||
0x32 => to_bcd(((year + 1900) / 100) as u8),
|
0x32 => to_bcd(((year + 1900) / 100) as u8),
|
||||||
_ => {
|
_ => {
|
||||||
// self.index is always guaranteed to be in range via INDEX_MASK.
|
// self.index is always guaranteed to be in range via INDEX_MASK.
|
||||||
@@ -120,7 +131,10 @@ impl BusDevice for Cmos {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
o => panic!("bad read offset on CMOS device: {}", o),
|
o => {
|
||||||
|
warn!("bad read offset on CMOS device: {}", o);
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ pub struct Gpio {
|
|||||||
// Mode Control Select Register
|
// Mode Control Select Register
|
||||||
afsel: u32,
|
afsel: u32,
|
||||||
// GPIO irq_field
|
// GPIO irq_field
|
||||||
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
|
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Versionize)]
|
#[derive(Versionize)]
|
||||||
@@ -106,7 +106,7 @@ impl VersionMapped for GpioState {}
|
|||||||
|
|
||||||
impl Gpio {
|
impl Gpio {
|
||||||
/// Constructs an PL061 GPIO device.
|
/// Constructs an PL061 GPIO device.
|
||||||
pub fn new(id: String, interrupt: Arc<Box<dyn InterruptSourceGroup>>) -> Self {
|
pub fn new(id: String, interrupt: Arc<dyn InterruptSourceGroup>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
data: 0,
|
data: 0,
|
||||||
@@ -297,7 +297,7 @@ impl BusDevice for Gpio {
|
|||||||
|
|
||||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||||
if data.len() <= 4 {
|
if data.len() <= 4 {
|
||||||
let value = read_le_u32(&data);
|
let value = read_le_u32(data);
|
||||||
if let Err(e) = self.handle_write(offset, value) {
|
if let Err(e) = self.handle_write(offset, value) {
|
||||||
warn!("Failed to write to GPIO PL061 device: {}", e);
|
warn!("Failed to write to GPIO PL061 device: {}", e);
|
||||||
}
|
}
|
||||||
@@ -376,14 +376,14 @@ mod tests {
|
|||||||
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
|
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
|
||||||
let mut gpio = Gpio::new(
|
let mut gpio = Gpio::new(
|
||||||
String::from(GPIO_NAME),
|
String::from(GPIO_NAME),
|
||||||
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
|
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||||
);
|
);
|
||||||
let mut data = [0; 4];
|
let mut data = [0; 4];
|
||||||
|
|
||||||
// Read and write to the GPIODIR register.
|
// Read and write to the GPIODIR register.
|
||||||
// Set pin 0 output pin.
|
// Set pin 0 output pin.
|
||||||
write_le_u32(&mut data, 1);
|
write_le_u32(&mut data, 1);
|
||||||
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIODIR, &mut data);
|
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIODIR, &data);
|
||||||
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIODIR, &mut data);
|
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIODIR, &mut data);
|
||||||
let v = read_le_u32(&data);
|
let v = read_le_u32(&data);
|
||||||
assert_eq!(v, 1);
|
assert_eq!(v, 1);
|
||||||
@@ -391,8 +391,8 @@ mod tests {
|
|||||||
// Read and write to the GPIODATA register.
|
// Read and write to the GPIODATA register.
|
||||||
write_le_u32(&mut data, 1);
|
write_le_u32(&mut data, 1);
|
||||||
// Set pin 0 high.
|
// Set pin 0 high.
|
||||||
let offset = 0x00000004 as u64;
|
let offset = 0x00000004_u64;
|
||||||
gpio.write(LEGACY_GPIO_MAPPED_IO_START, offset, &mut data);
|
gpio.write(LEGACY_GPIO_MAPPED_IO_START, offset, &data);
|
||||||
gpio.read(LEGACY_GPIO_MAPPED_IO_START, offset, &mut data);
|
gpio.read(LEGACY_GPIO_MAPPED_IO_START, offset, &mut data);
|
||||||
let v = read_le_u32(&data);
|
let v = read_le_u32(&data);
|
||||||
assert_eq!(v, 1);
|
assert_eq!(v, 1);
|
||||||
@@ -400,7 +400,7 @@ mod tests {
|
|||||||
// Read and write to the GPIOIS register.
|
// Read and write to the GPIOIS register.
|
||||||
// Configure pin 0 detecting level interrupt.
|
// Configure pin 0 detecting level interrupt.
|
||||||
write_le_u32(&mut data, 1);
|
write_le_u32(&mut data, 1);
|
||||||
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIS, &mut data);
|
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIS, &data);
|
||||||
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIS, &mut data);
|
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIS, &mut data);
|
||||||
let v = read_le_u32(&data);
|
let v = read_le_u32(&data);
|
||||||
assert_eq!(v, 1);
|
assert_eq!(v, 1);
|
||||||
@@ -408,7 +408,7 @@ mod tests {
|
|||||||
// Read and write to the GPIOIBE register.
|
// Read and write to the GPIOIBE register.
|
||||||
// Configure pin 1 detecting both falling and rising edges.
|
// Configure pin 1 detecting both falling and rising edges.
|
||||||
write_le_u32(&mut data, 2);
|
write_le_u32(&mut data, 2);
|
||||||
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIBE, &mut data);
|
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIBE, &data);
|
||||||
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIBE, &mut data);
|
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIBE, &mut data);
|
||||||
let v = read_le_u32(&data);
|
let v = read_le_u32(&data);
|
||||||
assert_eq!(v, 2);
|
assert_eq!(v, 2);
|
||||||
@@ -416,7 +416,7 @@ mod tests {
|
|||||||
// Read and write to the GPIOIEV register.
|
// Read and write to the GPIOIEV register.
|
||||||
// Configure pin 2 detecting both falling and rising edges.
|
// Configure pin 2 detecting both falling and rising edges.
|
||||||
write_le_u32(&mut data, 4);
|
write_le_u32(&mut data, 4);
|
||||||
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIEV, &mut data);
|
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIEV, &data);
|
||||||
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIEV, &mut data);
|
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIEV, &mut data);
|
||||||
let v = read_le_u32(&data);
|
let v = read_le_u32(&data);
|
||||||
assert_eq!(v, 4);
|
assert_eq!(v, 4);
|
||||||
@@ -425,12 +425,12 @@ mod tests {
|
|||||||
// Configure pin 0...2 capable of triggering their individual interrupts
|
// Configure pin 0...2 capable of triggering their individual interrupts
|
||||||
// and then the combined GPIOINTR line.
|
// and then the combined GPIOINTR line.
|
||||||
write_le_u32(&mut data, 7);
|
write_le_u32(&mut data, 7);
|
||||||
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIE, &mut data);
|
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIE, &data);
|
||||||
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIE, &mut data);
|
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIE, &mut data);
|
||||||
let v = read_le_u32(&data);
|
let v = read_le_u32(&data);
|
||||||
assert_eq!(v, 7);
|
assert_eq!(v, 7);
|
||||||
|
|
||||||
let mask = 0x00000002 as u32;
|
let mask = 0x00000002_u32;
|
||||||
// emulate an rising pulse in pin 1.
|
// emulate an rising pulse in pin 1.
|
||||||
gpio.data |= !(gpio.data & mask) & mask;
|
gpio.data |= !(gpio.data & mask) & mask;
|
||||||
gpio.pl061_internal_update();
|
gpio.pl061_internal_update();
|
||||||
@@ -443,14 +443,14 @@ mod tests {
|
|||||||
// Read and Write to the GPIOIC register.
|
// Read and Write to the GPIOIC register.
|
||||||
// clear interrupt in pin 1.
|
// clear interrupt in pin 1.
|
||||||
write_le_u32(&mut data, 2);
|
write_le_u32(&mut data, 2);
|
||||||
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIC, &mut data);
|
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIC, &data);
|
||||||
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIC, &mut data);
|
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIC, &mut data);
|
||||||
let v = read_le_u32(&data);
|
let v = read_le_u32(&data);
|
||||||
assert_eq!(v, 2);
|
assert_eq!(v, 2);
|
||||||
|
|
||||||
// Attempts to write beyond the writable space.
|
// Attempts to write beyond the writable space.
|
||||||
write_le_u32(&mut data, 0);
|
write_le_u32(&mut data, 0);
|
||||||
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIO_ID_LOW, &mut data);
|
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIO_ID_LOW, &data);
|
||||||
|
|
||||||
let mut data = [0; 4];
|
let mut data = [0; 4];
|
||||||
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIO_ID_LOW, &mut data);
|
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIO_ID_LOW, &mut data);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ impl BusDevice for I8042Device {
|
|||||||
|
|
||||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||||
if data.len() == 1 && data[0] == 0xfe && offset == 3 {
|
if data.len() == 1 && data[0] == 0xfe && offset == 3 {
|
||||||
debug!("i8042 reset signalled");
|
info!("i8042 reset signalled");
|
||||||
if let Err(e) = self.reset_evt.write(1) {
|
if let Err(e) = self.reset_evt.write(1) {
|
||||||
error!("Error triggering i8042 reset event: {}", e);
|
error!("Error triggering i8042 reset event: {}", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -224,12 +224,12 @@ pub struct Rtc {
|
|||||||
load: u32,
|
load: u32,
|
||||||
imsc: u32,
|
imsc: u32,
|
||||||
ris: u32,
|
ris: u32,
|
||||||
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
|
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rtc {
|
impl Rtc {
|
||||||
/// Constructs an AMBA PL031 RTC device.
|
/// Constructs an AMBA PL031 RTC device.
|
||||||
pub fn new(interrupt: Arc<Box<dyn InterruptSourceGroup>>) -> Self {
|
pub fn new(interrupt: Arc<dyn InterruptSourceGroup>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
// This is used only for duration measuring purposes.
|
// This is used only for duration measuring purposes.
|
||||||
previous_now: Instant::now(),
|
previous_now: Instant::now(),
|
||||||
@@ -328,7 +328,7 @@ impl BusDevice for Rtc {
|
|||||||
|
|
||||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||||
if data.len() <= 4 {
|
if data.len() <= 4 {
|
||||||
let v = read_le_u32(&data);
|
let v = read_le_u32(data);
|
||||||
if let Err(e) = self.handle_write(offset, v) {
|
if let Err(e) = self.handle_write(offset, v) {
|
||||||
warn!("Failed to write to RTC PL031 device: {}", e);
|
warn!("Failed to write to RTC PL031 device: {}", e);
|
||||||
}
|
}
|
||||||
@@ -450,14 +450,12 @@ mod tests {
|
|||||||
fn test_rtc_read_write_and_event() {
|
fn test_rtc_read_write_and_event() {
|
||||||
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
|
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
|
||||||
|
|
||||||
let mut rtc = Rtc::new(Arc::new(Box::new(TestInterrupt::new(
|
let mut rtc = Rtc::new(Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())));
|
||||||
intr_evt.try_clone().unwrap(),
|
|
||||||
))));
|
|
||||||
let mut data = [0; 4];
|
let mut data = [0; 4];
|
||||||
|
|
||||||
// Read and write to the MR register.
|
// Read and write to the MR register.
|
||||||
write_le_u32(&mut data, 123);
|
write_le_u32(&mut data, 123);
|
||||||
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCMR, &mut data);
|
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCMR, &data);
|
||||||
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCMR, &mut data);
|
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCMR, &mut data);
|
||||||
let v = read_le_u32(&data);
|
let v = read_le_u32(&data);
|
||||||
assert_eq!(v, 123);
|
assert_eq!(v, 123);
|
||||||
@@ -466,7 +464,7 @@ mod tests {
|
|||||||
let v = get_time(ClockType::Real);
|
let v = get_time(ClockType::Real);
|
||||||
write_le_u32(&mut data, (v / NANOS_PER_SECOND) as u32);
|
write_le_u32(&mut data, (v / NANOS_PER_SECOND) as u32);
|
||||||
let previous_now_before = rtc.previous_now;
|
let previous_now_before = rtc.previous_now;
|
||||||
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCLR, &mut data);
|
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCLR, &data);
|
||||||
|
|
||||||
assert!(rtc.previous_now > previous_now_before);
|
assert!(rtc.previous_now > previous_now_before);
|
||||||
|
|
||||||
@@ -478,7 +476,7 @@ mod tests {
|
|||||||
// Test with non zero value.
|
// Test with non zero value.
|
||||||
let non_zero = 1;
|
let non_zero = 1;
|
||||||
write_le_u32(&mut data, non_zero);
|
write_le_u32(&mut data, non_zero);
|
||||||
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
|
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &data);
|
||||||
// The interrupt line should be on.
|
// The interrupt line should be on.
|
||||||
assert!(rtc.interrupt.notifier(0).unwrap().read().unwrap() == 1);
|
assert!(rtc.interrupt.notifier(0).unwrap().read().unwrap() == 1);
|
||||||
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
|
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
|
||||||
@@ -487,14 +485,14 @@ mod tests {
|
|||||||
|
|
||||||
// Now test with 0.
|
// Now test with 0.
|
||||||
write_le_u32(&mut data, 0);
|
write_le_u32(&mut data, 0);
|
||||||
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
|
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &data);
|
||||||
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
|
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
|
||||||
let v = read_le_u32(&data);
|
let v = read_le_u32(&data);
|
||||||
assert_eq!(0, v);
|
assert_eq!(0, v);
|
||||||
|
|
||||||
// Read and write to the ICR register.
|
// Read and write to the ICR register.
|
||||||
write_le_u32(&mut data, 1);
|
write_le_u32(&mut data, 1);
|
||||||
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCICR, &mut data);
|
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCICR, &data);
|
||||||
// The interrupt line should be on.
|
// The interrupt line should be on.
|
||||||
assert!(rtc.interrupt.notifier(0).unwrap().read().unwrap() > 1);
|
assert!(rtc.interrupt.notifier(0).unwrap().read().unwrap() > 1);
|
||||||
let v_before = read_le_u32(&data);
|
let v_before = read_le_u32(&data);
|
||||||
@@ -506,7 +504,7 @@ mod tests {
|
|||||||
|
|
||||||
// Attempts to turn off the RTC should not go through.
|
// Attempts to turn off the RTC should not go through.
|
||||||
write_le_u32(&mut data, 0);
|
write_le_u32(&mut data, 0);
|
||||||
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCCR, &mut data);
|
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCCR, &data);
|
||||||
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCCR, &mut data);
|
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCCR, &mut data);
|
||||||
let v = read_le_u32(&data);
|
let v = read_le_u32(&data);
|
||||||
assert_eq!(v, 1);
|
assert_eq!(v, 1);
|
||||||
@@ -514,7 +512,7 @@ mod tests {
|
|||||||
// Attempts to write beyond the writable space. Using here the space used to read
|
// Attempts to write beyond the writable space. Using here the space used to read
|
||||||
// the CID and PID from.
|
// the CID and PID from.
|
||||||
write_le_u32(&mut data, 0);
|
write_le_u32(&mut data, 0);
|
||||||
rtc.write(LEGACY_RTC_MAPPED_IO_START, AMBA_ID_LOW, &mut data);
|
rtc.write(LEGACY_RTC_MAPPED_IO_START, AMBA_ID_LOW, &data);
|
||||||
// However, reading from the AMBA_ID_LOW should succeed upon read.
|
// However, reading from the AMBA_ID_LOW should succeed upon read.
|
||||||
|
|
||||||
let mut data = [0; 4];
|
let mut data = [0; 4];
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ pub struct Serial {
|
|||||||
id: String,
|
id: String,
|
||||||
interrupt_enable: u8,
|
interrupt_enable: u8,
|
||||||
interrupt_identification: u8,
|
interrupt_identification: u8,
|
||||||
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
|
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||||
line_control: u8,
|
line_control: u8,
|
||||||
line_status: u8,
|
line_status: u8,
|
||||||
modem_control: u8,
|
modem_control: u8,
|
||||||
@@ -91,7 +91,7 @@ impl VersionMapped for SerialState {}
|
|||||||
impl Serial {
|
impl Serial {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
id: String,
|
id: String,
|
||||||
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
|
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||||
out: Option<Box<dyn io::Write + Send>>,
|
out: Option<Box<dyn io::Write + Send>>,
|
||||||
) -> Serial {
|
) -> Serial {
|
||||||
Serial {
|
Serial {
|
||||||
@@ -113,17 +113,21 @@ impl Serial {
|
|||||||
/// Constructs a Serial port ready for output.
|
/// Constructs a Serial port ready for output.
|
||||||
pub fn new_out(
|
pub fn new_out(
|
||||||
id: String,
|
id: String,
|
||||||
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
|
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||||
out: Box<dyn io::Write + Send>,
|
out: Box<dyn io::Write + Send>,
|
||||||
) -> Serial {
|
) -> Serial {
|
||||||
Self::new(id, interrupt, Some(out))
|
Self::new(id, interrupt, Some(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a Serial port with no connected output.
|
/// Constructs a Serial port with no connected output.
|
||||||
pub fn new_sink(id: String, interrupt: Arc<Box<dyn InterruptSourceGroup>>) -> Serial {
|
pub fn new_sink(id: String, interrupt: Arc<dyn InterruptSourceGroup>) -> Serial {
|
||||||
Self::new(id, interrupt, None)
|
Self::new(id, interrupt, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_out(&mut self, out: Box<dyn io::Write + Send>) {
|
||||||
|
self.out = Some(out);
|
||||||
|
}
|
||||||
|
|
||||||
/// Queues raw bytes for the guest to read and signals the interrupt if the line status would
|
/// Queues raw bytes for the guest to read and signals the interrupt if the line status would
|
||||||
/// change.
|
/// change.
|
||||||
pub fn queue_input_bytes(&mut self, c: &[u8]) -> Result<()> {
|
pub fn queue_input_bytes(&mut self, c: &[u8]) -> Result<()> {
|
||||||
@@ -134,6 +138,13 @@ impl Serial {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn flush_output(&mut self) -> result::Result<(), io::Error> {
|
||||||
|
if let Some(out) = self.out.as_mut() {
|
||||||
|
out.flush()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn is_dlab_set(&self) -> bool {
|
fn is_dlab_set(&self) -> bool {
|
||||||
(self.line_control & LCR_DLAB_BIT) != 0
|
(self.line_control & LCR_DLAB_BIT) != 0
|
||||||
}
|
}
|
||||||
@@ -370,7 +381,7 @@ mod tests {
|
|||||||
let serial_out = SharedBuffer::new();
|
let serial_out = SharedBuffer::new();
|
||||||
let mut serial = Serial::new_out(
|
let mut serial = Serial::new_out(
|
||||||
String::from(SERIAL_NAME),
|
String::from(SERIAL_NAME),
|
||||||
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
|
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||||
Box::new(serial_out.clone()),
|
Box::new(serial_out.clone()),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -390,7 +401,7 @@ mod tests {
|
|||||||
let serial_out = SharedBuffer::new();
|
let serial_out = SharedBuffer::new();
|
||||||
let mut serial = Serial::new_out(
|
let mut serial = Serial::new_out(
|
||||||
String::from(SERIAL_NAME),
|
String::from(SERIAL_NAME),
|
||||||
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
|
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||||
Box::new(serial_out),
|
Box::new(serial_out),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -427,7 +438,7 @@ mod tests {
|
|||||||
let intr_evt = EventFd::new(0).unwrap();
|
let intr_evt = EventFd::new(0).unwrap();
|
||||||
let mut serial = Serial::new_sink(
|
let mut serial = Serial::new_sink(
|
||||||
String::from(SERIAL_NAME),
|
String::from(SERIAL_NAME),
|
||||||
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
|
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 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
|
||||||
@@ -449,7 +460,7 @@ mod tests {
|
|||||||
let intr_evt = EventFd::new(0).unwrap();
|
let intr_evt = EventFd::new(0).unwrap();
|
||||||
let mut serial = Serial::new_sink(
|
let mut serial = Serial::new_sink(
|
||||||
String::from(SERIAL_NAME),
|
String::from(SERIAL_NAME),
|
||||||
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
|
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||||
);
|
);
|
||||||
|
|
||||||
serial.write(0, LCR as u64, &[LCR_DLAB_BIT]);
|
serial.write(0, LCR as u64, &[LCR_DLAB_BIT]);
|
||||||
@@ -470,7 +481,7 @@ mod tests {
|
|||||||
let intr_evt = EventFd::new(0).unwrap();
|
let intr_evt = EventFd::new(0).unwrap();
|
||||||
let mut serial = Serial::new_sink(
|
let mut serial = Serial::new_sink(
|
||||||
String::from(SERIAL_NAME),
|
String::from(SERIAL_NAME),
|
||||||
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
|
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||||
);
|
);
|
||||||
|
|
||||||
serial.write(0, MCR as u64, &[MCR_LOOP_BIT]);
|
serial.write(0, MCR as u64, &[MCR_LOOP_BIT]);
|
||||||
@@ -496,7 +507,7 @@ mod tests {
|
|||||||
let intr_evt = EventFd::new(0).unwrap();
|
let intr_evt = EventFd::new(0).unwrap();
|
||||||
let mut serial = Serial::new_sink(
|
let mut serial = Serial::new_sink(
|
||||||
String::from(SERIAL_NAME),
|
String::from(SERIAL_NAME),
|
||||||
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
|
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||||
);
|
);
|
||||||
|
|
||||||
serial.write(0, SCR as u64, &[0x12]);
|
serial.write(0, SCR as u64, &[0x12]);
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ pub struct Pl011 {
|
|||||||
ifl: u32,
|
ifl: u32,
|
||||||
read_count: u32,
|
read_count: u32,
|
||||||
read_trigger: u32,
|
read_trigger: u32,
|
||||||
irq: Arc<Box<dyn InterruptSourceGroup>>,
|
irq: Arc<dyn InterruptSourceGroup>,
|
||||||
out: Option<Box<dyn io::Write + Send>>,
|
out: Option<Box<dyn io::Write + Send>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ impl Pl011 {
|
|||||||
/// Constructs an AMBA PL011 UART device.
|
/// Constructs an AMBA PL011 UART device.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
id: String,
|
id: String,
|
||||||
irq: Arc<Box<dyn InterruptSourceGroup>>,
|
irq: Arc<dyn InterruptSourceGroup>,
|
||||||
out: Option<Box<dyn io::Write + Send>>,
|
out: Option<Box<dyn io::Write + Send>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -138,6 +138,10 @@ impl Pl011 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_out(&mut self, out: Box<dyn io::Write + Send>) {
|
||||||
|
self.out = Some(out);
|
||||||
|
}
|
||||||
|
|
||||||
fn state(&self) -> Pl011State {
|
fn state(&self) -> Pl011State {
|
||||||
Pl011State {
|
Pl011State {
|
||||||
flags: self.flags,
|
flags: self.flags,
|
||||||
@@ -192,6 +196,13 @@ impl Pl011 {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn flush_output(&mut self) -> result::Result<(), io::Error> {
|
||||||
|
if let Some(out) = self.out.as_mut() {
|
||||||
|
out.flush()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn pl011_get_baudrate(&self) -> u32 {
|
fn pl011_get_baudrate(&self) -> u32 {
|
||||||
if self.fbrd == 0 {
|
if self.fbrd == 0 {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -341,7 +352,7 @@ impl BusDevice for Pl011 {
|
|||||||
|
|
||||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||||
if data.len() <= 4 {
|
if data.len() <= 4 {
|
||||||
let v = read_le_u32(&data);
|
let v = read_le_u32(data);
|
||||||
if let Err(e) = self.handle_write(offset, v) {
|
if let Err(e) = self.handle_write(offset, v) {
|
||||||
warn!("Failed to write to PL011 device: {}", e);
|
warn!("Failed to write to PL011 device: {}", e);
|
||||||
}
|
}
|
||||||
@@ -440,7 +451,7 @@ mod tests {
|
|||||||
let pl011_out = SharedBuffer::new();
|
let pl011_out = SharedBuffer::new();
|
||||||
let mut pl011 = Pl011::new(
|
let mut pl011 = Pl011::new(
|
||||||
String::from(SERIAL_NAME),
|
String::from(SERIAL_NAME),
|
||||||
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
|
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||||
Some(Box::new(pl011_out.clone())),
|
Some(Box::new(pl011_out.clone())),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -460,7 +471,7 @@ mod tests {
|
|||||||
let pl011_out = SharedBuffer::new();
|
let pl011_out = SharedBuffer::new();
|
||||||
let mut pl011 = Pl011::new(
|
let mut pl011 = Pl011::new(
|
||||||
String::from(SERIAL_NAME),
|
String::from(SERIAL_NAME),
|
||||||
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
|
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||||
Some(Box::new(pl011_out)),
|
Some(Box::new(pl011_out)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
44
docs/api.md
44
docs/api.md
@@ -78,27 +78,29 @@ Shut the VMM down | `/vmm.shutdown` | N/A | N/A
|
|||||||
|
|
||||||
#### Virtual Machine (VM) Actions
|
#### Virtual Machine (VM) Actions
|
||||||
|
|
||||||
Action | Endpoint | Request Body | Response Body | Prerequisites
|
Action | Endpoint | Request Body | Response Body | Prerequisites
|
||||||
-----------------------------------|---------------------|---------------------------|--------------------------|---------------------------
|
-----------------------------------|----------------------|---------------------------|--------------------------|---------------------------
|
||||||
Create the VM | `/vm.create` | `/schemas/VmConfig` | N/A | The VM is not created yet
|
Create the VM | `/vm.create` | `/schemas/VmConfig` | N/A | The VM is not created yet
|
||||||
Delete the VM | `/vm.delete` | N/A | N/A | N/A
|
Delete the VM | `/vm.delete` | N/A | N/A | N/A
|
||||||
Boot the VM | `/vm.boot` | N/A | N/A | The VM is created but not booted
|
Boot the VM | `/vm.boot` | N/A | N/A | The VM is created but not booted
|
||||||
Shut the VM down | `/vm.shutdown` | N/A | N/A | The VM is booted
|
Shut the VM down | `/vm.shutdown` | N/A | N/A | The VM is booted
|
||||||
Reboot the VM | `/vm.reboot` | N/A | N/A | The VM is booted
|
Reboot the VM | `/vm.reboot` | N/A | N/A | The VM is booted
|
||||||
Pause the VM | `/vm.pause` | N/A | N/A | The VM is booted
|
Trigger power button of the VM | `/vm.power-button` | N/A | N/A | The VM is booted
|
||||||
Resume the VM | `/vm.resume` | N/A | N/A | The VM is paused
|
Pause the VM | `/vm.pause` | N/A | N/A | The VM is booted
|
||||||
Add/remove CPUs to/from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
|
Resume the VM | `/vm.resume` | N/A | N/A | The VM is paused
|
||||||
Add/remove memory from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
|
Add/remove CPUs to/from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
|
||||||
Add/remove memory from a zone | `/vm.resize-zone` | `/schemas/VmResizeZone` | N/A | The VM is booted
|
Add/remove memory from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
|
||||||
Dump the VM information | `/vm.info` | N/A | `/schemas/VmInfo` | The VM is created
|
Add/remove memory from a zone | `/vm.resize-zone` | `/schemas/VmResizeZone` | N/A | The VM is booted
|
||||||
Add VFIO PCI device to the VM | `/vm.add-device` | `/schemas/VmAddDevice` | `/schemas/PciDeviceInfo` | The VM is booted
|
Dump the VM information | `/vm.info` | N/A | `/schemas/VmInfo` | The VM is created
|
||||||
Add disk device to the VM | `/vm.add-disk` | `/schemas/DiskConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
Add VFIO PCI device to the VM | `/vm.add-device` | `/schemas/VmAddDevice` | `/schemas/PciDeviceInfo` | The VM is booted
|
||||||
Add fs device to the VM | `/vm.add-fs` | `/schemas/FsConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
Add disk device to the VM | `/vm.add-disk` | `/schemas/DiskConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
||||||
Add pmem device to the VM | `/vm.add-pmem` | `/schemas/PmemConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
Add fs device to the VM | `/vm.add-fs` | `/schemas/FsConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
||||||
Add network device to the VM | `/vm.add-net` | `/schemas/NetConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
Add pmem device to the VM | `/vm.add-pmem` | `/schemas/PmemConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
||||||
Add vsock device to the VM | `/vm.add-vsock` | `/schemas/VsockConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
Add network device to the VM | `/vm.add-net` | `/schemas/NetConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
||||||
Remove device from the VM | `/vm.remove-device` | `/schemas/VmRemoveDevice` | N/A | The VM is booted
|
Add userspace PCI device to the VM | `/vm.add-user-device`| `/schemas/VmAddUserDevice`| `/schemas/PciDeviceInfo` | The VM is booted
|
||||||
Dump the VM counters | `/vm.counters` | N/A | `/schemas/VmCounters` | The VM is booted
|
Add vsock device to the VM | `/vm.add-vsock` | `/schemas/VsockConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
||||||
|
Remove device from the VM | `/vm.remove-device` | `/schemas/VmRemoveDevice` | N/A | The VM is booted
|
||||||
|
Dump the VM counters | `/vm.counters` | N/A | `/schemas/VmCounters` | The VM is booted
|
||||||
|
|
||||||
### REST API Examples
|
### REST API Examples
|
||||||
|
|
||||||
|
|||||||
133
docs/arm64.md
133
docs/arm64.md
@@ -1,58 +1,141 @@
|
|||||||
# How to build and run Cloud-hypervisor on AArch64
|
# How to build and test Cloud Hypervisor on AArch64
|
||||||
|
|
||||||
Cloud-hypervisor is partially enabled on AArch64 architecture.
|
This document introduces how to build and test Cloud Hypervisor on AArch64
|
||||||
Although all features are not ready yet, you can begin to test Cloud-hypervisor on a AArch64 host by following this guide.
|
servers. Currently Cloud Hypervisor cannot be tested on Raspberry PI. Because
|
||||||
|
on AArch64, Cloud Hypervisor requires GICv3-ITS device for PCIe MSI interrupt
|
||||||
|
handling. But GICv3-ITS has not been equipped on any Raspberry PI product so
|
||||||
|
far.
|
||||||
|
|
||||||
|
Now Cloud Hypervisor supports 2 ways of booting on AArch64: UEFI booting and
|
||||||
|
direct-kernel booting. The document covers both of the ways.
|
||||||
|
|
||||||
|
All the steps are based on Ubuntu. We use the Ubuntu cloud image for guest VM
|
||||||
|
disk.
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
We create a folder to build and run Cloud Hypervisor at `$HOME/cloud-hypervisor`
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ export CLOUDH=$HOME/cloud-hypervisor
|
||||||
|
$ mkdir $CLOUDH
|
||||||
|
```
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
On AArch64 machines, Cloud-hypervisor depends on an external library `libfdt-dev` for generating Flattened Device Tree (FDT).
|
You need to install some prerequisite packages to build and test Cloud Hypervisor.
|
||||||
|
|
||||||
The long-term plan is to replace `libfdt-dev` with some pure-Rust component to get rid of such dependency.
|
### Tools
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt-get update
|
# Install rust tool chain
|
||||||
sudo apt-get install libfdt-dev
|
$ 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
|
||||||
```
|
```
|
||||||
|
|
||||||
## Build
|
### Building Cloud Hypervisor
|
||||||
|
|
||||||
Using PCI devices requires GICv3-ITS for MSI messaging. GICv3-ITS is very common in modern servers.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo build --no-default-features --features kvm
|
$ pushd $CLOUDH
|
||||||
|
$ git clone https://github.com/cloud-hypervisor/cloud-hypervisor.git
|
||||||
|
$ cd cloud-hypervisor
|
||||||
|
$ cargo build
|
||||||
|
$ popd
|
||||||
```
|
```
|
||||||
|
|
||||||
## Image
|
### Disk image
|
||||||
|
|
||||||
Download kernel binary and rootfs image from AWS.
|
Download the Ubuntu cloud image and convert the image type.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wget https://s3.amazonaws.com/spec.ccfc.min/img/aarch64/ubuntu_with_ssh/fsfiles/xenial.rootfs.ext4 -O rootfs.ext4
|
$ pushd $CLOUDH
|
||||||
wget https://s3.amazonaws.com/spec.ccfc.min/img/aarch64/ubuntu_with_ssh/kernel/vmlinux.bin -O kernel.bin
|
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-arm64.img
|
||||||
|
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-arm64.img focal-server-cloudimg-arm64.raw
|
||||||
|
$ popd
|
||||||
```
|
```
|
||||||
|
|
||||||
## Containerized build
|
## UEFI booting
|
||||||
|
|
||||||
If you want to build and test Cloud Hypervisor without having to install all the required dependencies, you can also turn to the development script: dev_cli.sh.
|
This part introduces how to build EDK2 firmware and boot Cloud Hypervisor with it.
|
||||||
|
|
||||||
To build the development container:
|
### Building EDK2
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./scripts/dev_cli.sh build-container
|
$ pushd $CLOUDH
|
||||||
|
|
||||||
|
# Clone source code repos
|
||||||
|
$ git clone --depth 1 https://github.com/tianocore/edk2.git -b master
|
||||||
|
$ cd edk2
|
||||||
|
$ git submodule update --init
|
||||||
|
$ cd ..
|
||||||
|
$ git clone --depth 1 https://github.com/tianocore/edk2-platforms.git -b master
|
||||||
|
$ git clone --depth 1 https://github.com/acpica/acpica.git -b master
|
||||||
|
|
||||||
|
# Build tools
|
||||||
|
$ export PACKAGES_PATH="$PWD/edk2:$PWD/edk2-platforms"
|
||||||
|
$ export IASL_PREFIX="$PWD/acpica/generate/unix/bin/"
|
||||||
|
$ make -C acpica
|
||||||
|
$ cd edk2/
|
||||||
|
$ . edksetup.sh
|
||||||
|
$ cd ..
|
||||||
|
$ make -C edk2/BaseTools
|
||||||
|
|
||||||
|
# Build EDK2
|
||||||
|
$ build -a AARCH64 -t GCC5 -p ArmVirtPkg/ArmVirtCloudHv.dsc -b RELEASE
|
||||||
|
|
||||||
|
$ popd
|
||||||
```
|
```
|
||||||
|
|
||||||
To build Cloud-hypervisor in the container:
|
If the build goes well, the EDK2 binary is available at
|
||||||
|
`edk2/Build/ArmVirtCloudHv-AARCH64/RELEASE_GCC5/FV/CLOUDHV_EFI.fd`.
|
||||||
|
|
||||||
|
### Booting the guest VM
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./scripts/dev_cli.sh build
|
$ pushd $CLOUDH
|
||||||
|
$ sudo RUST_BACKTRACE=1 $CLOUDH/cloud-hypervisor/target/debug/cloud-hypervisor \
|
||||||
|
--api-socket /tmp/cloud-hypervisor.sock \
|
||||||
|
--kernel $CLOUDH/edk2/Build/ArmVirtCloudHv-AARCH64/RELEASE_GCC5/FV/CLOUDHV_EFI.fd \
|
||||||
|
--disk path=$CLOUDH/focal-server-cloudimg-arm64.raw \
|
||||||
|
--cpus boot=4 \
|
||||||
|
--memory size=4096M \
|
||||||
|
--net tap=,mac=12:34:56:78:90:01,ip=192.168.1.1,mask=255.255.255.0 \
|
||||||
|
--serial tty \
|
||||||
|
--console off
|
||||||
|
$ popd
|
||||||
```
|
```
|
||||||
|
|
||||||
## Run
|
## Direct-kernel booting
|
||||||
|
|
||||||
Assuming you have built Cloud-hypervisor with the development container, a VM can be started with command:
|
Alternativelly, you can build your own kernel for guest VM. This way, UEFI is
|
||||||
|
not involved and ACPI cannot be enabled.
|
||||||
|
|
||||||
|
### Building kernel
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo build/cargo_target/aarch64-unknown-linux-gnu/debug/cloud-hypervisor --kernel kernel.bin --disk path=rootfs.ext4 --cmdline "keep_bootcon console=hvc0 reboot=k panic=1 root=/dev/vda rw" --cpus boot=4 --memory size=512M --serial file=serial.log --log-file log.log -vvv
|
$ pushd $CLOUDH
|
||||||
|
$ git clone --depth 1 "https://github.com/cloud-hypervisor/linux.git" -b ch-5.12
|
||||||
|
$ cd linux
|
||||||
|
$ cp $CLOUDH/cloud-hypervisor/resources/linux-config-aarch64 .config
|
||||||
|
$ make -j `nproc`
|
||||||
|
$ popd
|
||||||
```
|
```
|
||||||
|
|
||||||
If the build was done out of the container, replace the binary path with `target/debug/cloud-hypervisor`.
|
### Booting the guest VM
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ pushd $CLOUDH
|
||||||
|
$ sudo $CLOUDH/cloud-hypervisor/target/debug/cloud-hypervisor \
|
||||||
|
--api-socket /tmp/cloud-hypervisor.sock \
|
||||||
|
--kernel $CLOUDH/linux/arch/arm64/boot/Image \
|
||||||
|
--disk path=focal-server-cloudimg-arm64.raw \
|
||||||
|
--cmdline "keep_bootcon console=ttyAMA0 reboot=k panic=1 root=/dev/vda1 rw" \
|
||||||
|
--cpus boot=4 \
|
||||||
|
--memory size=4096M \
|
||||||
|
--net tap=,mac=12:34:56:78:90:01,ip=192.168.1.1,mask=255.255.255.0 \
|
||||||
|
--serial tty \
|
||||||
|
--console off
|
||||||
|
$ popd
|
||||||
|
```
|
||||||
|
|||||||
189
docs/cpu.md
Normal file
189
docs/cpu.md
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
# CPU
|
||||||
|
|
||||||
|
Cloud Hypervisor has many options when it comes to the creation of virtual
|
||||||
|
CPUs. This document aims to explain what Cloud Hypervisor is capable of and
|
||||||
|
how it can be used to meet the needs of very different use cases.
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
`CpusConfig` or what is known as `--cpus` from the CLI perspective is the way
|
||||||
|
to set vCPUs options for Cloud Hypervisor.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct CpusConfig {
|
||||||
|
boot_vcpus: u8,
|
||||||
|
max_vcpus: u8,
|
||||||
|
topology: Option<CpuTopology>,
|
||||||
|
kvm_hyperv: bool,
|
||||||
|
max_phys_bits: u8,
|
||||||
|
affinity: Option<Vec<CpuAffinity>>,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
--cpus boot=<boot_vcpus>,max=<max_vcpus>,topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>,kvm_hyperv=on|off,max_phys_bits=<maximum_number_of_physical_bits>,affinity=<list_of_vcpus_with_their_associated_cpuset>
|
||||||
|
```
|
||||||
|
|
||||||
|
### `boot`
|
||||||
|
|
||||||
|
Number of vCPUs present at boot time.
|
||||||
|
|
||||||
|
This option allows to define a specific number of vCPUs to be present at the
|
||||||
|
time the VM is started. This option is mandatory when using the `--cpus`
|
||||||
|
parameter. If `--cpus` is not specified, this option takes the default value
|
||||||
|
of `1`, starting the VM with a single vCPU.
|
||||||
|
|
||||||
|
Value is an unsigned integer of 8 bits.
|
||||||
|
|
||||||
|
_Example_
|
||||||
|
|
||||||
|
```
|
||||||
|
--cpus boot=2
|
||||||
|
```
|
||||||
|
|
||||||
|
### `max`
|
||||||
|
|
||||||
|
Maximum number of vCPUs.
|
||||||
|
|
||||||
|
This option defines the maximum number of vCPUs that can be assigned to the VM.
|
||||||
|
In particular, this option is used when looking for CPU hotplug as it lets the
|
||||||
|
provide an indication about how many vCPUs might be needed later during the
|
||||||
|
runtime of the VM.
|
||||||
|
For instance, if booting the VM with 2 vCPUs and a maximum of 6 vCPUs, it means
|
||||||
|
up to 4 vCPUs can be added later at runtime by resizing the VM.
|
||||||
|
|
||||||
|
The value must be greater than or equal to the number of boot vCPUs.
|
||||||
|
The value is an unsigned integer of 8 bits.
|
||||||
|
|
||||||
|
By default this option takes the value of `boot`, meaning vCPU hotplug is not
|
||||||
|
expected and can't be performed.
|
||||||
|
|
||||||
|
_Example_
|
||||||
|
|
||||||
|
```
|
||||||
|
--cpus max=3
|
||||||
|
```
|
||||||
|
|
||||||
|
### `topology`
|
||||||
|
|
||||||
|
Topology of the guest platform.
|
||||||
|
|
||||||
|
This option gives the user a way to describe the exact topology that should be
|
||||||
|
exposed to the guest. It can be useful to describe to the guest the same
|
||||||
|
topology found on the host as it allows for proper usage of the resources and
|
||||||
|
is a way to achieve better performances.
|
||||||
|
|
||||||
|
The topology is described through the following structure:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct CpuTopology {
|
||||||
|
threads_per_core: u8,
|
||||||
|
cores_per_die: u8,
|
||||||
|
dies_per_package: u8,
|
||||||
|
packages: u8,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
or the following syntax through the CLI:
|
||||||
|
|
||||||
|
```
|
||||||
|
topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>
|
||||||
|
```
|
||||||
|
|
||||||
|
By default the topology will be `1:1:1:1`.
|
||||||
|
|
||||||
|
_Example_
|
||||||
|
|
||||||
|
```
|
||||||
|
--cpus boot=2,topology=1:1:2:1
|
||||||
|
```
|
||||||
|
|
||||||
|
### `kvm_hyperv`
|
||||||
|
|
||||||
|
Enable KVM Hyper-V emulation.
|
||||||
|
|
||||||
|
When turned on, this option relies on KVM to emulate the synthetic interrupt
|
||||||
|
controller (SynIC) along with synthetic timers expected by a Windows guest.
|
||||||
|
A Windows guest usually runs on top of Microsoft Hyper-V, therefore expects
|
||||||
|
these synthetic devices to be present. That's why KVM provides a way to emulate
|
||||||
|
them and avoids failures running a Windows guest with Cloud Hypervisor.
|
||||||
|
|
||||||
|
By default this option is turned off.
|
||||||
|
|
||||||
|
_Example_
|
||||||
|
|
||||||
|
```
|
||||||
|
--cpus kvm_hyperv=on
|
||||||
|
```
|
||||||
|
|
||||||
|
### `max_phys_bits`
|
||||||
|
|
||||||
|
Maximum size for guest's addressable space.
|
||||||
|
|
||||||
|
This option defines the maximum number of physical bits for all vCPUs, which
|
||||||
|
sets a limit for the size of the guest's addressable space. This is mainly
|
||||||
|
useful for debug purpose.
|
||||||
|
|
||||||
|
The value is an unsigned integer of 8 bits.
|
||||||
|
|
||||||
|
_Example_
|
||||||
|
|
||||||
|
```
|
||||||
|
--cpus max_phys_bits=40
|
||||||
|
```
|
||||||
|
|
||||||
|
### `affinity`
|
||||||
|
|
||||||
|
Affinity of each vCPU.
|
||||||
|
|
||||||
|
This option gives the user a way to provide the host CPU set associated with
|
||||||
|
each vCPU. It is useful for achieving CPU pinning, ensuring multiple VMs won't
|
||||||
|
affect the performance of each other. It might also be used in the context of
|
||||||
|
NUMA as it is way of making sure the VM can run on a specific host NUMA node.
|
||||||
|
In general, this option is used to increase the performances of a VM depending
|
||||||
|
on the host platform and the type of workload running in the guest.
|
||||||
|
|
||||||
|
The affinity is described through the following structure:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct CpuAffinity {
|
||||||
|
vcpu: u8,
|
||||||
|
host_cpus: Vec<u8>,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
or the following syntax through the CLI:
|
||||||
|
|
||||||
|
```
|
||||||
|
affinity=[<vcpu_id1>@[<host_cpu_id1>, <host_cpu_id2>], <vcpu_id2>@[<host_cpu_id3>, <host_cpu_id4>]]
|
||||||
|
```
|
||||||
|
|
||||||
|
The outer brackets define the list of vCPUs. And for each vCPU, the inner
|
||||||
|
brackets attached to `@` define the list of host CPUs the vCPU is allowed to
|
||||||
|
run onto.
|
||||||
|
|
||||||
|
Multiple values can be provided to define each list. Each value is an unsigned
|
||||||
|
integer of 8 bits.
|
||||||
|
|
||||||
|
For instance, if one needs to run vCPU 0 on host CPUs from 0 to 4, the syntax
|
||||||
|
using `-` will help define a contiguous range with `affinity=0@[0-4]`. The
|
||||||
|
same example could also be described with `affinity=0@[0,1,2,3,4]`.
|
||||||
|
|
||||||
|
A combination of both `-` and `,` separators is useful when one might need to
|
||||||
|
describe a list containing host CPUs from 0 to 99 and the host CPU 255, as it
|
||||||
|
could simply be described with `affinity=0@[0-99,255]`.
|
||||||
|
|
||||||
|
As soon as one tries to describe a list of values, `[` and `]` must be used to
|
||||||
|
demarcate the list.
|
||||||
|
|
||||||
|
By default each vCPU runs on the entire host CPU set.
|
||||||
|
|
||||||
|
_Example_
|
||||||
|
|
||||||
|
```
|
||||||
|
--cpus boot=3,affinity=[0@[2,3],1@[0,1]]
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, assuming the host has 4 CPUs, vCPU 0 will run exclusively on
|
||||||
|
host CPUs 2 and 3, while vCPU 1 will run exclusively on host CPUs 0 and 1.
|
||||||
|
Because nothing is defined for vCPU 2, it can run on any of the 4 host CPUs.
|
||||||
@@ -72,7 +72,7 @@ mount -t devpts devpts /dev/pts
|
|||||||
|
|
||||||
### Install needed packages
|
### Install needed packages
|
||||||
|
|
||||||
In the context Cloud-Hypervisor's integration tests, we need several utilities.
|
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
|
Here is the way to install them for a Ubuntu image. This step is specific to
|
||||||
Ubuntu distributions.
|
Ubuntu distributions.
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ cargo install cargo-fuzz
|
|||||||
|
|
||||||
## Running the fuzzers
|
## Running the fuzzers
|
||||||
|
|
||||||
e.g. To run the `qcow` fuzzer using all available CPUs:
|
e.g. To run the `block` fuzzer using all available CPUs:
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo fuzz run qcow -j `nproc`
|
cargo fuzz run block -j `nproc`
|
||||||
```
|
```
|
||||||
|
|
||||||
## Adding a new fuzzer
|
## Adding a new fuzzer
|
||||||
@@ -32,4 +32,4 @@ cargo fuzz run qcow -j `nproc`
|
|||||||
cargo fuzz add <new_fuzzer>
|
cargo fuzz add <new_fuzzer>
|
||||||
```
|
```
|
||||||
|
|
||||||
Inspiration for fuzzers can be found in [crosvm](https://chromium.googlesource.com/chromiumos/platform/crosvm/+/refs/heads/master/fuzz/)
|
Inspiration for fuzzers can be found in [crosvm](https://chromium.googlesource.com/chromiumos/platform/crosvm/+/refs/heads/master/fuzz/)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Cloud Hypervisor Hot Plug
|
# Cloud Hypervisor Hot Plug
|
||||||
|
|
||||||
Currently Cloud Hypervisor only support hot plugging of CPU devices.
|
Currently Cloud Hypervisor only supports hot plugging of CPU devices.
|
||||||
|
|
||||||
## Kernel support
|
## Kernel support
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ or by using this kernel patch (available in 5.5-rc1 and later): https://git.kern
|
|||||||
|
|
||||||
## CPU Hot Plug
|
## CPU Hot Plug
|
||||||
|
|
||||||
Extra vCPUs can be added and removed from a running Cloud Hypervisor instance. This is controlled by two mechanisms:
|
Extra vCPUs can be added and removed from a running `cloud-hypervisor` instance. This is controlled by two mechanisms:
|
||||||
|
|
||||||
1. Specifying a number of maximum potential vCPUs that is greater than the number of default (boot) vCPUs.
|
1. Specifying a number of maximum potential vCPUs that is greater than the number of default (boot) vCPUs.
|
||||||
2. Making a HTTP API request to the VMM to ask for the additional vCPUs to be added.
|
2. Making a HTTP API request to the VMM to ask for the additional vCPUs to be added.
|
||||||
@@ -65,7 +65,7 @@ As per adding CPUs to the guest, after a reboot the VM will be running with the
|
|||||||
|
|
||||||
### ACPI method
|
### ACPI method
|
||||||
|
|
||||||
Extra memory can be added from a running Cloud Hypervisor instance. This is controlled by two mechanisms:
|
Extra memory can be added from a running `cloud-hypervisor` instance. This is controlled by two mechanisms:
|
||||||
|
|
||||||
1. Allocating some of the guest physical address space for hotplug memory.
|
1. Allocating some of the guest physical address space for hotplug memory.
|
||||||
2. Making a HTTP API request to the VMM to ask for a new amount of RAM to be assigned to the VM. In the case of expanding the memory for the VM the new memory will be hotplugged into the running VM, if reducing the size of the memory then change will take effect after the next reboot.
|
2. Making a HTTP API request to the VMM to ask for a new amount of RAM to be assigned to the VM. In the case of expanding the memory for the VM the new memory will be hotplugged into the running VM, if reducing the size of the memory then change will take effect after the next reboot.
|
||||||
@@ -157,7 +157,9 @@ The same API can also be used to reduce the desired RAM for a VM. It is importan
|
|||||||
|
|
||||||
## PCI Device Hot Plug
|
## PCI Device Hot Plug
|
||||||
|
|
||||||
Extra PCI devices can be added and removed from a running Cloud Hypervisor instance. This is controlled by making a HTTP API request to the VMM to ask for the additional device to be added, or for the existing device to be removed.
|
Extra PCI devices can be added and removed from a running `cloud-hypervisor` instance. This is controlled by making a HTTP API request to the VMM to ask for the additional device to be added, or for the existing device to be removed.
|
||||||
|
|
||||||
|
Note: On AArch64 platform, PCI device hotplug can only be achieved using ACPI. Please refer to the [documentation](arm64.md#uefi-booting) for more information.
|
||||||
|
|
||||||
To use PCI device hotplug start the VM with the HTTP server.
|
To use PCI device hotplug start the VM with the HTTP server.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Intel SGX
|
# Intel SGX
|
||||||
|
|
||||||
Intel® Software Guard Extensions (Intel® SGX) is an Intel technology designed
|
Intel® Software Guard Extensions (Intel® SGX) is an Intel technology designed
|
||||||
to increase the security of application code and data. Cloud-Hypervisor supports
|
to increase the security of application code and data. Cloud Hypervisor supports
|
||||||
SGX virtualization through KVM. Because SGX is built on hardware features that
|
SGX virtualization through KVM. Because SGX is built on hardware features that
|
||||||
cannot be emulated in software, virtualizing SGX requires support in KVM and in
|
cannot be emulated in software, virtualizing SGX requires support in KVM and in
|
||||||
the host kernel. The required Linux and KVM changes can be found in the
|
the host kernel. The required Linux and KVM changes can be found in the
|
||||||
@@ -17,12 +17,12 @@ For more information about SGX, please refer to the [SGX Homepage](https://softw
|
|||||||
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).
|
||||||
|
|
||||||
## Cloud-Hypervisor support
|
## Cloud Hypervisor support
|
||||||
|
|
||||||
Assuming the host exposes `/dev/sgx_vepc`, we can pass SGX enclaves through
|
Assuming the host exposes `/dev/sgx_vepc`, we can pass SGX enclaves through
|
||||||
the guest.
|
the guest.
|
||||||
|
|
||||||
In order to use SGX enclaves within a Cloud-Hypervisor VM, we must define one
|
In order to use SGX enclaves within a Cloud Hypervisor VM, we must define one
|
||||||
or several Enclave Page Cache (EPC) sections. Here is an example of a VM being
|
or several Enclave Page Cache (EPC) sections. Here is an example of a VM being
|
||||||
created with 2 EPC sections, the first one being 64MiB with pre-allocated
|
created with 2 EPC sections, the first one being 64MiB with pre-allocated
|
||||||
memory, the second one being 32MiB with no pre-allocated memory.
|
memory, the second one being 32MiB with no pre-allocated memory.
|
||||||
@@ -34,7 +34,7 @@ memory, the second one being 32MiB with no pre-allocated memory.
|
|||||||
--disk path=focal-server-cloudimg-amd64.raw \
|
--disk path=focal-server-cloudimg-amd64.raw \
|
||||||
--kernel vmlinux \
|
--kernel vmlinux \
|
||||||
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
|
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
|
||||||
--sgx-epc size=64M,prefault=on size=32M,prefault=off
|
--sgx-epc id=epc0,size=64M,prefault=on id=epc1,size=32M,prefault=off
|
||||||
```
|
```
|
||||||
|
|
||||||
Once booted, and assuming your guest kernel contains the patches from the
|
Once booted, and assuming your guest kernel contains the patches from the
|
||||||
|
|||||||
75
docs/intel_tdx.md
Normal file
75
docs/intel_tdx.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# Intel TDX
|
||||||
|
|
||||||
|
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
|
||||||
|
host platform.
|
||||||
|
|
||||||
|
For more information about TDX technical aspects, design and specification
|
||||||
|
please refer to the
|
||||||
|
[TDX Homepage](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-trust-domain-extensions.html).
|
||||||
|
|
||||||
|
The required Linux changes for the host side can be found in the
|
||||||
|
[KVM TDX tree](https://github.com/intel/tdx/tree/kvm) while the changes for
|
||||||
|
the guest side can be found in the [Guest TDX tree](https://github.com/intel/tdx/tree/guest).
|
||||||
|
|
||||||
|
The TDVF firmware can be found in the
|
||||||
|
[EDK2 staging project](https://github.com/tianocore/edk2-staging/tree/TDVF).
|
||||||
|
|
||||||
|
## Cloud Hypervisor support
|
||||||
|
|
||||||
|
First, you must be running on 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).
|
||||||
|
|
||||||
|
Cloud Hypervisor can run TDX VM (Trust Domain) by loading the TDVF firmware,
|
||||||
|
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).
|
||||||
|
The firmware can be built as follows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/tianocore/edk2-staging.git
|
||||||
|
cd edk2-staging
|
||||||
|
git checkout origin/TDVF
|
||||||
|
git submodule update --init --recursive
|
||||||
|
make -C BaseTools
|
||||||
|
source ./edksetup.sh
|
||||||
|
build -p OvmfPkg/OvmfCh.dsc -a X64 -t GCC5 -b RELEASE
|
||||||
|
```
|
||||||
|
|
||||||
|
If debug logs are needed, here is the alternative command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
build -p OvmfPkg/OvmfCh.dsc -a X64 -t GCC5 -D DEBUG_ON_SERIAL_PORT=TRUE
|
||||||
|
```
|
||||||
|
|
||||||
|
On the Cloud Hypervisor side, all you need is to build the project with the
|
||||||
|
`tdx` feature enabled:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo build --features tdx
|
||||||
|
```
|
||||||
|
|
||||||
|
And run a TDX VM by providing the firmware previously built, along with the
|
||||||
|
guest image containing the TDX enlightened kernel. Assuming the guest kernel
|
||||||
|
command line contains `console=hvc0` (printing to the `virtio-console` device),
|
||||||
|
run Cloud Hypervisor as follows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./cloud-hypervisor \
|
||||||
|
--tdx firmware=edk2-staging/Build/OvmfCh/RELEASE_GCC5/FV/OVMF.fd \
|
||||||
|
--cpus boot=1 \
|
||||||
|
--memory size=1G \
|
||||||
|
--disk path=tdx_guest_img
|
||||||
|
```
|
||||||
|
|
||||||
|
And here is the alternative command when looking for debug logs (assuming the
|
||||||
|
guest kernel command line contains `console=ttyS0`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./cloud-hypervisor \
|
||||||
|
--tdx firmware=edk2-staging/Build/OvmfCh/DEBUG_GCC5/FV/OVMF.fd \
|
||||||
|
--cpus boot=1 \
|
||||||
|
--memory size=1G \
|
||||||
|
--disk path=tdx_guest_img \
|
||||||
|
--serial tty \
|
||||||
|
--console off
|
||||||
|
```
|
||||||
@@ -121,6 +121,29 @@ lspci
|
|||||||
00:04.0 Unassigned class [ffff]: Red Hat, Inc. Virtio RNG
|
00:04.0 Unassigned class [ffff]: Red Hat, Inc. Virtio RNG
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Work with FDT on AArch64
|
||||||
|
|
||||||
|
On AArch64 architecture, the virtual IOMMU can still be used even if ACPI is not
|
||||||
|
enabled. But the effect is different with what the aforementioned test showed.
|
||||||
|
|
||||||
|
When ACPI is disabled, virtual IOMMU is supported through Flattened Device Tree
|
||||||
|
(FDT). In this case, the guest kernel can not tell which device should be
|
||||||
|
IOMMU-attached and which should not. No matter how many devices you attached to
|
||||||
|
the virtual IOMMU by setting `iommu=on` option, all the devices on the PCI bus
|
||||||
|
will be attached to the virtual IOMMU (except the IOMMU itself). Each of the
|
||||||
|
devices will be added into a IOMMU group.
|
||||||
|
|
||||||
|
As a result, the directory content of `/sys/kernel/iommu_groups` would be:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls /sys/kernel/iommu_groups/0/devices/
|
||||||
|
0000:00:02.0
|
||||||
|
ls /sys/kernel/iommu_groups/1/devices/
|
||||||
|
0000:00:03.0
|
||||||
|
ls /sys/kernel/iommu_groups/2/devices/
|
||||||
|
0000:00:04.0
|
||||||
|
```
|
||||||
|
|
||||||
## Faster mappings
|
## Faster mappings
|
||||||
|
|
||||||
By default, the guest memory is mapped with 4k pages and no huge pages, which
|
By default, the guest memory is mapped with 4k pages and no huge pages, which
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ $ target/release/ch-remote --api-socket=/tmp/api1 send-migration unix:/tmp/sock
|
|||||||
|
|
||||||
When the above commands completed, the source VM should be successfully
|
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 paused.
|
the source VM is terminated gracefully.
|
||||||
|
|
||||||
## Nested-VM Migration
|
## Nested-VM Migration
|
||||||
|
|
||||||
@@ -136,4 +136,4 @@ 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
|
When the above commands completed, the source VM should be successfully
|
||||||
migrated to the destination VM without interrupting our testing guest
|
migrated to the destination VM without interrupting our testing guest
|
||||||
workload. Now the destination VM is running the testing guest workload
|
workload. Now the destination VM is running the testing guest workload
|
||||||
while the source VM is paused.
|
while the source VM is terminated gracefully.
|
||||||
|
|||||||
232
docs/memory.md
232
docs/memory.md
@@ -1,30 +1,31 @@
|
|||||||
# Memory
|
# Memory
|
||||||
|
|
||||||
Cloud-Hypervisor has many ways to expose memory to the guest VM. This document
|
Cloud Hypervisor has many ways to expose memory to the guest VM. This document
|
||||||
aims to explain what Cloud-Hypervisor is capable of and how it can be used to
|
aims to explain what Cloud Hypervisor is capable of and how it can be used to
|
||||||
meet the needs of very different use cases.
|
meet the needs of very different use cases.
|
||||||
|
|
||||||
## Basic Parameters
|
## Basic Parameters
|
||||||
|
|
||||||
`MemoryConfig` or what is known as `--memory` from the CLI perspective is the
|
`MemoryConfig` or what is known as `--memory` from the CLI perspective is the
|
||||||
easiest way to get started with Cloud-Hypervisor.
|
easiest way to get started with Cloud Hypervisor.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
struct MemoryConfig {
|
struct MemoryConfig {
|
||||||
size: u64,
|
size: u64,
|
||||||
mergeable: bool,
|
mergeable: bool,
|
||||||
shared: bool,
|
|
||||||
hugepages: bool,
|
|
||||||
hugepage_size: Option<u64>,
|
|
||||||
hotplug_method: HotplugMethod,
|
hotplug_method: HotplugMethod,
|
||||||
hotplug_size: Option<u64>,
|
hotplug_size: Option<u64>,
|
||||||
hotplugged_size: Option<u64>,
|
hotplugged_size: Option<u64>,
|
||||||
|
shared: bool,
|
||||||
|
hugepages: bool,
|
||||||
|
hugepage_size: Option<u64>,
|
||||||
|
prefault: bool,
|
||||||
zones: Option<Vec<MemoryZoneConfig>>,
|
zones: Option<Vec<MemoryZoneConfig>>,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
--memory <memory> Memory parameters "size=<guest_memory_size>,mergeable=on|off,shared=on|off,hugepages=on|off,hotplug_method=acpi|virtio-mem,hotplug_size=<hotpluggable_memory_size>,hotplugged_size=<hotplugged_memory_size>"
|
--memory <memory> Memory parameters "size=<guest_memory_size>,mergeable=on|off,shared=on|off,hugepages=on|off,hugepage_size=<hugepage_size>,hotplug_method=acpi|virtio-mem,hotplug_size=<hotpluggable_memory_size>,hotplugged_size=<hotplugged_memory_size>,prefault=on|off" [default: size=512M]
|
||||||
```
|
```
|
||||||
|
|
||||||
### `size`
|
### `size`
|
||||||
@@ -59,44 +60,6 @@ _Example_
|
|||||||
--memory size=1G,mergeable=on
|
--memory size=1G,mergeable=on
|
||||||
```
|
```
|
||||||
|
|
||||||
### `shared`
|
|
||||||
|
|
||||||
Specifies if the memory must be `mmap(2)` with `MAP_SHARED` flag.
|
|
||||||
|
|
||||||
By sharing a memory mapping, one can share the guest RAM with other processes
|
|
||||||
running on the host. One can use this option when running vhost-user devices
|
|
||||||
as part of the VM device model, as they will be driven by standalone daemons
|
|
||||||
needing access to the guest RAM content.
|
|
||||||
|
|
||||||
By default this option is turned off, which results in performing `mmap(2)`
|
|
||||||
with `MAP_PRIVATE` flag.
|
|
||||||
|
|
||||||
_Example_
|
|
||||||
|
|
||||||
```
|
|
||||||
--memory size=1G,shared=on
|
|
||||||
```
|
|
||||||
|
|
||||||
### `hugepages` and `hugepage_size`
|
|
||||||
|
|
||||||
Specifies if the memory must be created and `mmap(2)` with `MAP_HUGETLB` and size
|
|
||||||
flags. This performs a memory mapping relying on the specified huge page size. If no huge page size is supplied the system's default huge page size is used.
|
|
||||||
|
|
||||||
By using hugepages, one can improve the overall performance of the VM, assuming
|
|
||||||
the guest will allocate hugepages as well. Another interesting use case is VFIO
|
|
||||||
as it speeds up the VM's boot time since the amount of IOMMU mappings are
|
|
||||||
reduced.
|
|
||||||
|
|
||||||
The user is responsible for ensuring there are sufficient huge pages of the specified size for the VMM to use. Failure to do so may result in strange VMM behaviour.
|
|
||||||
|
|
||||||
By default this option is turned off.
|
|
||||||
|
|
||||||
_Example_
|
|
||||||
|
|
||||||
```
|
|
||||||
--memory size=1G,hugepages=on,hugepage_size=2M
|
|
||||||
```
|
|
||||||
|
|
||||||
### `hotplug_method`
|
### `hotplug_method`
|
||||||
|
|
||||||
Selects the way of adding and/or removing memory to/from a booted VM.
|
Selects the way of adding and/or removing memory to/from a booted VM.
|
||||||
@@ -142,6 +105,72 @@ _Example_
|
|||||||
--memory size=1G,hotplug_method=virtio-mem,hotplug_size=1G,hotplugged_size=512M
|
--memory size=1G,hotplug_method=virtio-mem,hotplug_size=1G,hotplugged_size=512M
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `shared`
|
||||||
|
|
||||||
|
Specifies if the memory must be `mmap(2)` with `MAP_SHARED` flag.
|
||||||
|
|
||||||
|
By sharing a memory mapping, one can share the guest RAM with other processes
|
||||||
|
running on the host. One can use this option when running vhost-user devices
|
||||||
|
as part of the VM device model, as they will be driven by standalone daemons
|
||||||
|
needing access to the guest RAM content.
|
||||||
|
|
||||||
|
By default this option is turned off, which results in performing `mmap(2)`
|
||||||
|
with `MAP_PRIVATE` flag.
|
||||||
|
|
||||||
|
_Example_
|
||||||
|
|
||||||
|
```
|
||||||
|
--memory size=1G,shared=on
|
||||||
|
```
|
||||||
|
|
||||||
|
### `hugepages` and `hugepage_size`
|
||||||
|
|
||||||
|
Specifies if the memory must be created and `mmap(2)` with `MAP_HUGETLB` and size
|
||||||
|
flags. This performs a memory mapping relying on the specified huge page size.
|
||||||
|
If no huge page size is supplied the system's default huge page size is used.
|
||||||
|
|
||||||
|
By using hugepages, one can improve the overall performance of the VM, assuming
|
||||||
|
the guest will allocate hugepages as well. Another interesting use case is VFIO
|
||||||
|
as it speeds up the VM's boot time since the amount of IOMMU mappings are
|
||||||
|
reduced.
|
||||||
|
|
||||||
|
The user is responsible for ensuring there are sufficient huge pages of the
|
||||||
|
specified size for the VMM to use. Failure to do so may result in strange VMM
|
||||||
|
behaviour, e.g. error with `ReadKernelImage` is common. If there is a strange
|
||||||
|
error with `hugepages` enabled, just disable it or check whether there are enough
|
||||||
|
huge pages.
|
||||||
|
|
||||||
|
By default this option is turned off.
|
||||||
|
|
||||||
|
_Example_
|
||||||
|
|
||||||
|
```
|
||||||
|
--memory size=1G,hugepages=on,hugepage_size=2M
|
||||||
|
```
|
||||||
|
|
||||||
|
### `prefault`
|
||||||
|
|
||||||
|
Specifies if the memory must be `mmap(2)` with `MAP_POPULATE` flag.
|
||||||
|
|
||||||
|
By triggering prefault, one can allocate all required physical memory and create
|
||||||
|
its page tables while calling `mmap`. With physical memory allocated, the number
|
||||||
|
of page faults will decrease during running, and performance will also improve.
|
||||||
|
|
||||||
|
Note that boot of VM will be slower with `prefault` enabled because of allocating
|
||||||
|
physical memory and creating page tables in advance, and physical memory of the
|
||||||
|
specified size will be consumed quickly.
|
||||||
|
|
||||||
|
This option only takes effect at boot of VM. There is also a `prefault` option in
|
||||||
|
restore and its choice will overwrite `prefault` in memory.
|
||||||
|
|
||||||
|
By default this option is turned off.
|
||||||
|
|
||||||
|
_Example_
|
||||||
|
|
||||||
|
```
|
||||||
|
--memory size=1G,prefault=on
|
||||||
|
```
|
||||||
|
|
||||||
## Advanced Parameters
|
## Advanced Parameters
|
||||||
|
|
||||||
`MemoryZoneConfig` or what is known as `--memory-zone` from the CLI perspective
|
`MemoryZoneConfig` or what is known as `--memory-zone` from the CLI perspective
|
||||||
@@ -155,14 +184,16 @@ struct MemoryZoneConfig {
|
|||||||
file: Option<PathBuf>,
|
file: Option<PathBuf>,
|
||||||
shared: bool,
|
shared: bool,
|
||||||
hugepages: bool,
|
hugepages: bool,
|
||||||
|
hugepage_size: Option<u64>,
|
||||||
host_numa_node: Option<u32>,
|
host_numa_node: Option<u32>,
|
||||||
hotplug_size: Option<u64>,
|
hotplug_size: Option<u64>,
|
||||||
hotplugged_size: Option<u64>,
|
hotplugged_size: Option<u64>,
|
||||||
|
prefault: bool,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
--memory-zone <memory-zone> User defined memory zone parameters "size=<guest_memory_region_size>,file=<backing_file>,shared=on|off,hugepages=on|off,host_numa_node=<node_id>,id=<zone_identifier>,hotplug_size=<hotpluggable_memory_size>,hotplugged_size=<hotplugged_memory_size>"
|
--memory-zone <memory-zone> User defined memory zone parameters "size=<guest_memory_region_size>,file=<backing_file>,shared=on|off,hugepages=on|off,hugepage_size=<hugepage_size>,host_numa_node=<node_id>,id=<zone_identifier>,hotplug_size=<hotpluggable_memory_size>,hotplugged_size=<hotplugged_memory_size>,prefault=on|off"
|
||||||
```
|
```
|
||||||
|
|
||||||
This parameter expects one or more occurences, allowing for a list of memory
|
This parameter expects one or more occurences, allowing for a list of memory
|
||||||
@@ -250,24 +281,30 @@ _Example_
|
|||||||
--memory-zone id=mem0,size=1G,shared=on
|
--memory-zone id=mem0,size=1G,shared=on
|
||||||
```
|
```
|
||||||
|
|
||||||
### `hugepages`
|
### `hugepages` and `hugepage_size`
|
||||||
|
|
||||||
Specifies if the memory zone must be `mmap(2)` with `MAP_HUGETLB` and
|
Specifies if the memory must be created and `mmap(2)` with `MAP_HUGETLB` and size
|
||||||
`MAP_HUGE_2MB` flags. This performs a memory zone mapping relying on 2MiB
|
flags. This performs a memory mapping relying on the specified huge page size.
|
||||||
pages instead of the default 4kiB pages.
|
If no huge page size is supplied the system's default huge page size is used.
|
||||||
|
|
||||||
By using hugepages, one can improve the overall performance of the VM, assuming
|
By using hugepages, one can improve the overall performance of the VM, assuming
|
||||||
the guest will allocate hugepages as well. Another interesting use case is VFIO
|
the guest will allocate hugepages as well. Another interesting use case is VFIO
|
||||||
as it speeds up the VM's boot time since the amount of IOMMU mappings are
|
as it speeds up the VM's boot time since the amount of IOMMU mappings are
|
||||||
reduced.
|
reduced.
|
||||||
|
|
||||||
|
The user is responsible for ensuring there are sufficient huge pages of the
|
||||||
|
specified size for the VMM to use. Failure to do so may result in strange VMM
|
||||||
|
behaviour, e.g. error with `ReadKernelImage` is common. If there is a strange
|
||||||
|
error with `hugepages` enabled, just disable it or check whether there are enough
|
||||||
|
huge pages.
|
||||||
|
|
||||||
By default this option is turned off.
|
By default this option is turned off.
|
||||||
|
|
||||||
_Example_
|
_Example_
|
||||||
|
|
||||||
```
|
```
|
||||||
--memory size=0
|
--memory size=0
|
||||||
--memory-zone id=mem0,size=1G,hugepages=on
|
--memory-zone id=mem0,size=1G,hugepages=on,hugepage_size=2M
|
||||||
```
|
```
|
||||||
|
|
||||||
### `host_numa_node`
|
### `host_numa_node`
|
||||||
@@ -334,6 +371,30 @@ _Example_
|
|||||||
--memory-zone id=mem0,size=1G,hotplug_size=1G,hotplugged_size=512M
|
--memory-zone id=mem0,size=1G,hotplug_size=1G,hotplugged_size=512M
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `prefault`
|
||||||
|
|
||||||
|
Specifies if the memory must be `mmap(2)` with `MAP_POPULATE` flag.
|
||||||
|
|
||||||
|
By triggering prefault, one can allocate all required physical memory and create
|
||||||
|
its page tables while calling `mmap`. With physical memory allocated, the number
|
||||||
|
of page faults will decrease during running, and performance will also improve.
|
||||||
|
|
||||||
|
Note that boot of VM will be slower with `prefault` enabled because of allocating
|
||||||
|
physical memory and creating page tables in advance, and physical memory of the
|
||||||
|
specified size will be consumed quickly.
|
||||||
|
|
||||||
|
This option only takes effect at boot of VM. There is also a `prefault` option in
|
||||||
|
restore and its choice will overwrite `prefault` in memory.
|
||||||
|
|
||||||
|
By default this option is turned off.
|
||||||
|
|
||||||
|
_Example_
|
||||||
|
|
||||||
|
```
|
||||||
|
--memory size=0
|
||||||
|
--memory-zone id=mem0,size=1G,prefault=on
|
||||||
|
```
|
||||||
|
|
||||||
## NUMA settings
|
## NUMA settings
|
||||||
|
|
||||||
`NumaConfig` or what is known as `--numa` from the CLI perspective has been
|
`NumaConfig` or what is known as `--numa` from the CLI perspective has been
|
||||||
@@ -343,15 +404,16 @@ it allows for specifying the distance between each NUMA node.
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
struct NumaConfig {
|
struct NumaConfig {
|
||||||
id: u32,
|
guest_numa_id: u32,
|
||||||
cpus: Option<Vec<u8>>,
|
cpus: Option<Vec<u8>>,
|
||||||
distances: Option<Vec<NumaDistance>>,
|
distances: Option<Vec<NumaDistance>>,
|
||||||
memory_zones: Option<Vec<String>>,
|
memory_zones: Option<Vec<String>>,
|
||||||
|
sgx_epc_sections: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
--numa <numa> Settings related to a given NUMA node "id=<node_id>,cpus=<cpus_id>,distances=<list_of_distances_to_destination_nodes>,memory_zones=<list_of_memory_zones>"
|
--numa <numa> Settings related to a given NUMA node "guest_numa_id=<node_id>,cpus=<cpus_id>,distances=<list_of_distances_to_destination_nodes>,memory_zones=<list_of_memory_zones>,sgx_epc_sections=<list_of_sgx_epc_sections>"
|
||||||
```
|
```
|
||||||
|
|
||||||
### `guest_numa_id`
|
### `guest_numa_id`
|
||||||
@@ -384,18 +446,20 @@ integer of 8 bits.
|
|||||||
|
|
||||||
For instance, if one needs to attach all CPUs from 0 to 4 to a specific node,
|
For instance, if one needs to attach all CPUs from 0 to 4 to a specific node,
|
||||||
the syntax using `-` will help define a contiguous range with `cpus=0-4`. The
|
the syntax using `-` will help define a contiguous range with `cpus=0-4`. The
|
||||||
same example could also be described with `cpus=0:1:2:3:4`.
|
same example could also be described with `cpus=[0,1,2,3,4]`.
|
||||||
|
|
||||||
A combination of both `-` and `:` separators is useful when one might need to
|
A combination of both `-` and `,` separators is useful when one might need to
|
||||||
describe a list containing all CPUs from 0 to 99 and the CPU 255, as it could
|
describe a list containing all CPUs from 0 to 99 and the CPU 255, as it could
|
||||||
simply be described with `cpus=0-99:255`.
|
simply be described with `cpus=[0-99,255]`.
|
||||||
|
|
||||||
|
As soon as one tries to describe a list of values, `[` and `]` must be used to
|
||||||
|
demarcate the list.
|
||||||
|
|
||||||
_Example_
|
_Example_
|
||||||
|
|
||||||
```
|
```
|
||||||
--cpus boot=8
|
--cpus boot=8
|
||||||
--numa guest_numa_id=0,cpus=1-3:7
|
--numa guest_numa_id=0,cpus=[1-3,7] guest_numa_id=1,cpus=[0,4-6]
|
||||||
--numa guest_numa_id=1,cpus=0:4-6
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### `distances`
|
### `distances`
|
||||||
@@ -412,7 +476,10 @@ node. The second value is an unsigned integer of 8 bits as it represents the
|
|||||||
distance between the current NUMA node and the destination NUMA node. The two
|
distance between the current NUMA node and the destination NUMA node. The two
|
||||||
values are separated by `@` (`value1@value2`), meaning the destination NUMA
|
values are separated by `@` (`value1@value2`), meaning the destination NUMA
|
||||||
node `value1` is located at a distance of `value2`. Each tuple is separated
|
node `value1` is located at a distance of `value2`. Each tuple is separated
|
||||||
from the others with `:` separator.
|
from the others with `,` separator.
|
||||||
|
|
||||||
|
As soon as one tries to describe a list of values, `[` and `]` must be used to
|
||||||
|
demarcate the list.
|
||||||
|
|
||||||
For instance, if one wants to define 3 NUMA nodes, with each node located at
|
For instance, if one wants to define 3 NUMA nodes, with each node located at
|
||||||
different distances, it can be described with the following example.
|
different distances, it can be described with the following example.
|
||||||
@@ -420,9 +487,7 @@ different distances, it can be described with the following example.
|
|||||||
_Example_
|
_Example_
|
||||||
|
|
||||||
```
|
```
|
||||||
--numa guest_numa_id=0,distances=1@15:2@25
|
--numa guest_numa_id=0,distances=[1@15,2@25] guest_numa_id=1,distances=[0@15,2@20] guest_numa_id=2,distances=[0@25,1@20]
|
||||||
--numa guest_numa_id=1,distances=0@15:2@20
|
|
||||||
--numa guest_numa_id=2,distances=0@25:1@20
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### `memory_zones`
|
### `memory_zones`
|
||||||
@@ -439,15 +504,46 @@ workload run more efficiently.
|
|||||||
|
|
||||||
Multiple values can be provided to define the list. Each value is a string
|
Multiple values can be provided to define the list. Each value is a string
|
||||||
referring to an existing memory zone identifier. Values are separated from
|
referring to an existing memory zone identifier. Values are separated from
|
||||||
each other with the `:` separator.
|
each other with the `,` separator.
|
||||||
|
|
||||||
|
As soon as one tries to describe a list of values, `[` and `]` must be used to
|
||||||
|
demarcate the list.
|
||||||
|
|
||||||
|
Note that a memory zone must belong to a single NUMA node. The following
|
||||||
|
configuration is incorrect, therefore not allowed:
|
||||||
|
`--numa guest_numa_id=0,memory_zones=mem0 guest_numa_id=1,memory_zones=mem0`
|
||||||
|
|
||||||
_Example_
|
_Example_
|
||||||
|
|
||||||
```
|
```
|
||||||
--memory size=0
|
--memory size=0
|
||||||
--memory-zone id=mem0,size=1G
|
--memory-zone id=mem0,size=1G id=mem1,size=1G id=mem2,size=1G
|
||||||
--memory-zone id=mem1,size=1G
|
--numa guest_numa_id=0,memory_zones=[mem0,mem2] guest_numa_id=1,memory_zones=mem1
|
||||||
--memory-zone id=mem2,size=1G
|
|
||||||
--numa guest_numa_id=0,memory_zones=mem0:mem2
|
|
||||||
--numa guest_numa_id=1,memory_zones=mem1
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `sgx_epc_sections`
|
||||||
|
|
||||||
|
List of SGX EPC sections attached to the guest NUMA node identified by the
|
||||||
|
`guest_numa_id` option. This allows for describing a list of SGX EPC sections
|
||||||
|
which must be seen by the guest as belonging to the NUMA node `guest_numa_id`.
|
||||||
|
|
||||||
|
Multiple values can be provided to define the list. Each value is a string
|
||||||
|
referring to an existing SGX EPC section identifier. Values are separated from
|
||||||
|
each other with the `,` separator.
|
||||||
|
|
||||||
|
As soon as one tries to describe a list of values, `[` and `]` must be used to
|
||||||
|
demarcate the list.
|
||||||
|
|
||||||
|
_Example_
|
||||||
|
|
||||||
|
```
|
||||||
|
--sgx-epc id=epc0,size=32M id=epc1,size=64M id=epc2,size=32M
|
||||||
|
--numa guest_numa_id=0,sgx_epc_sections=epc1 guest_numa_id=1,sgx_epc_sections=[epc0,epc2]
|
||||||
|
```
|
||||||
|
|
||||||
|
### PCI bus
|
||||||
|
|
||||||
|
Cloud Hypervisor supports only one PCI bus, which is why it has been tied to
|
||||||
|
the NUMA node 0 by default. It is the user responsibility to organize the NUMA
|
||||||
|
nodes correctly so that vCPUs and guest RAM which should be located on the same
|
||||||
|
NUMA node as the PCI bus end up on the NUMA node 0.
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ before the snapshot was performed.
|
|||||||
This feature is important for the project as it establishes the first step
|
This feature is important for the project as it establishes the first step
|
||||||
towards the support for live migration.
|
towards the support for live migration.
|
||||||
|
|
||||||
## Snapshot a Cloud-Hypervisor VM
|
## Snapshot a Cloud Hypervisor VM
|
||||||
|
|
||||||
First thing, we must run a Cloud-Hypervisor VM:
|
First thing, we must run a Cloud Hypervisor VM:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./cloud-hypervisor \
|
./cloud-hypervisor \
|
||||||
@@ -54,7 +54,7 @@ drwxr-xr-x 47 foo bar 4096 Jul 22 11:47 ../
|
|||||||
In this particular example, we can observe that 2 memory region files were
|
In this particular example, we can observe that 2 memory region files were
|
||||||
created. That is explained by the size of the guest RAM, which is 4GiB in this
|
created. That is explained by the size of the guest RAM, which is 4GiB in this
|
||||||
case. Because it exceeds 3GiB (which is where we can find a ~1GiB memory hole),
|
case. Because it exceeds 3GiB (which is where we can find a ~1GiB memory hole),
|
||||||
Cloud-Hypervisor needs 2 distinct memory regions to be created. Each memory
|
Cloud Hypervisor needs 2 distinct memory regions to be created. Each memory
|
||||||
region's content is stored through a dedicated file, which explains why we end
|
region's content is stored through a dedicated file, which explains why we end
|
||||||
up with 2 different files, the first one containing the guest RAM range 0-3GiB
|
up with 2 different files, the first one containing the guest RAM range 0-3GiB
|
||||||
and the second one containing the guest RAM range 3-4GiB.
|
and the second one containing the guest RAM range 3-4GiB.
|
||||||
@@ -65,7 +65,7 @@ with the correct amount of CPUs, RAM, and other expected devices. The state
|
|||||||
bits are used to restore each component in the state it was left before the
|
bits are used to restore each component in the state it was left before the
|
||||||
snapshot occurred.
|
snapshot occurred.
|
||||||
|
|
||||||
## Restore a Cloud-Hypervisor VM
|
## Restore a Cloud Hypervisor VM
|
||||||
|
|
||||||
Given that one has access to an existing snapshot in `/home/foo/snapshot`,
|
Given that one has access to an existing snapshot in `/home/foo/snapshot`,
|
||||||
it is possible to create a new VM based on this snapshot with the following
|
it is possible to create a new VM based on this snapshot with the following
|
||||||
|
|||||||
@@ -41,19 +41,20 @@ CSM is a module that allows to boot legacy operating systems using the OVMF firm
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install uuid-dev nasm iasl build-essential python3-distutils git
|
sudo apt-get install uuid-dev nasm iasl build-essential python3-distutils git libncurses-dev
|
||||||
|
|
||||||
git checkout https://github.com/cloud-hypervisor/seabios -b ch
|
git clone https://github.com/cloud-hypervisor/seabios -b ch
|
||||||
cd seabios
|
cd seabios
|
||||||
make menuconfig
|
make menuconfig
|
||||||
# Enable `CONFIG_CSM` and `CONFIG_QEMU_HARDWARE`
|
# Enable `CONFIG_CSM` and `CONFIG_QEMU_HARDWARE`
|
||||||
|
# Please make sure `python` is in your path
|
||||||
make CONFIG_CSM=y CONFIG_QEMU_HARDWARE=y
|
make CONFIG_CSM=y CONFIG_QEMU_HARDWARE=y
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
git clone https://github.com/cloud-hypervisor/edk2 -b ch
|
git clone https://github.com/cloud-hypervisor/edk2 -b ch
|
||||||
cd edk2
|
cd edk2
|
||||||
. edksetup.sh
|
. edksetup.sh
|
||||||
git submodule update --init
|
git submodule update --init --recursive
|
||||||
cp ../seabios/out/Csm16.bin OvmfPkg/Csm/Csm16/
|
cp ../seabios/out/Csm16.bin OvmfPkg/Csm/Csm16/
|
||||||
|
|
||||||
echo "ACTIVE_PLATFORM=OvmfPkg/OvmfCh.dsc" >> Conf/target.txt
|
echo "ACTIVE_PLATFORM=OvmfPkg/OvmfCh.dsc" >> Conf/target.txt
|
||||||
@@ -61,7 +62,7 @@ echo "TARGET_ARCH=X64" >> Conf/target.txt
|
|||||||
echo "TOOL_CHAIN_TAG=GCC5" >> Conf/target.txt
|
echo "TOOL_CHAIN_TAG=GCC5" >> Conf/target.txt
|
||||||
|
|
||||||
make -C ./BaseTools
|
make -C ./BaseTools
|
||||||
build
|
build -DCSM_ENABLE
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
79
docs/vfio-user.md
Normal file
79
docs/vfio-user.md
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# Cloud Hypervisor VFIO-user HOWTO
|
||||||
|
|
||||||
|
VFIO-user is an *experimental* protocol for allowing devices to be implemented in another process and communicate over a socket; ie.e VFIO-user is to VFIO as virtio is to vhost-user.
|
||||||
|
|
||||||
|
The protocol is documented here: https://github.com/nutanix/libvfio-user/blob/master/docs/vfio-user.rst
|
||||||
|
|
||||||
|
The Cloud Hypervisor support for such devices is *experimental*. Not all Cloud Hypervisor functionality is supported in particular: virtio-mem and iommu are not supported.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The `--user-device socket=<path>` parameter is used to create a vfio-user device when creating the VM specifying the socket to connect to. The device can also be hotplugged with `ch-remote add-user-device socket=<path>`.
|
||||||
|
|
||||||
|
## Example (GPIO device)
|
||||||
|
|
||||||
|
There is a simple GPIO device included in the libvfio-user repository: https://github.com/nutanix/libvfio-user#gpio
|
||||||
|
|
||||||
|
Run the example from the libvfio-user repository:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
rm /tmp/vfio-user.sock
|
||||||
|
./build/dbg/samples/gpio-pci-idio-16 -v /tmp/vfio-user.sock &
|
||||||
|
```
|
||||||
|
|
||||||
|
Start Cloud Hypervisor:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
target/debug/cloud-hypervisor \
|
||||||
|
--memory size=1G,shared=on \
|
||||||
|
--disk path=~/images/focal-server-cloudimg-amd64.raw \
|
||||||
|
--kernel ~/src/linux/vmlinux \
|
||||||
|
--cmdline "root=/dev/vda1 console=hvc0" \
|
||||||
|
--user-device socket=/tmp/vfio-user.sock
|
||||||
|
```
|
||||||
|
|
||||||
|
Inside the VM you can test the device with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cat /sys/class/gpio/gpiochip480/base > /sys/class/gpio/export
|
||||||
|
for ((i=0;i<12;i++)); do cat /sys/class/gpio/OUT0/value; done
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example (NVMe device)
|
||||||
|
|
||||||
|
Use SPDK: https://github.com/spdk/spdk
|
||||||
|
|
||||||
|
Compile with `./configure --with-vfio-user`
|
||||||
|
|
||||||
|
Create an NVMe controller listening on a vfio-user socket with a simple AIO block device in spdk.
|
||||||
|
More details of configuring SPDK bdev can be viewed in [SPDK bdev](https://spdk.io/doc/bdev.html).
|
||||||
|
More details of setting SPDK NVMe-oF target can be viewed in [SDPK NVMe-oF tgt](https://spdk.io/doc/nvmf.html).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo scripts/setup.sh
|
||||||
|
rm ~/images/test-disk.raw
|
||||||
|
truncate ~/images/test-disk.raw -s 128M
|
||||||
|
mkfs.ext4 ~/images/test-disk.raw
|
||||||
|
sudo killall ./build/bin/nvmf_tgt
|
||||||
|
sudo ./build/bin/nvmf_tgt -i 0 -e 0xFFFF -m 0x1 &
|
||||||
|
sleep 2
|
||||||
|
sudo ./scripts/rpc.py nvmf_create_transport -t VFIOUSER
|
||||||
|
sudo rm -rf /tmp/nvme-vfio-user
|
||||||
|
sudo mkdir -p /tmp/nvme-vfio-user
|
||||||
|
sudo ./scripts/rpc.py bdev_aio_create ~/images/test-disk.raw test 512
|
||||||
|
sudo ./scripts/rpc.py nvmf_create_subsystem nqn.2019-07.io.spdk:cnode -a -s test
|
||||||
|
sudo ./scripts/rpc.py nvmf_subsystem_add_ns nqn.2019-07.io.spdk:cnode test
|
||||||
|
sudo ./scripts/rpc.py nvmf_subsystem_add_listener nqn.2019-07.io.spdk:cnode -t VFIOUSER -a /tmp/nvme-vfio-user -s 0
|
||||||
|
sudo chown $USER.$USER -R /tmp/nvme-vfio-user
|
||||||
|
```
|
||||||
|
|
||||||
|
Start Cloud Hypervisor:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
target/debug/cloud-hypervisor \
|
||||||
|
--memory size=1G,shared=on \
|
||||||
|
--disk path=~/images/focal-server-cloudimg-amd64.raw \
|
||||||
|
--kernel ~/src/linux/vmlinux \
|
||||||
|
--cmdline "root=/dev/vda1 console=hvc0" \
|
||||||
|
--user-device socket=/tmp/nvme-vfio-user/cntrl
|
||||||
|
```
|
||||||
54
docs/vfio.md
54
docs/vfio.md
@@ -36,14 +36,22 @@ Now that we have identified the device, we must unbind it from its native driver
|
|||||||
First we add VFIO support to the host:
|
First we add VFIO support to the host:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ sudo modprobe vfio_pci
|
# modprobe -r vfio_pci
|
||||||
$ sudo modprobe vfio_iommu_type1 allow_unsafe_interrupts
|
# modprobe -r vfio_iommu_type1
|
||||||
|
# modprobe vfio_iommu_type1 allow_unsafe_interrupts
|
||||||
|
# modprobe vfio_pci
|
||||||
|
```
|
||||||
|
|
||||||
|
In case the VFIO drivers are built-in, enable unsafe interrupts with:
|
||||||
|
|
||||||
|
```
|
||||||
|
# echo 1 > /sys/module/vfio_iommu_type1/parameters/allow_unsafe_interrupts
|
||||||
```
|
```
|
||||||
|
|
||||||
Then we unbind it from its native driver:
|
Then we unbind it from its native driver:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ echo 0000:01:00.0 > /sys/bus/pci/devices/0000\:01\:00.0/driver/unbind
|
# echo 0000:01:00.0 > /sys/bus/pci/devices/0000\:01\:00.0/driver/unbind
|
||||||
```
|
```
|
||||||
|
|
||||||
And finally we bind it to the VFIO driver. To do that we first need to get the
|
And finally we bind it to the VFIO driver. To do that we first need to get the
|
||||||
@@ -53,8 +61,14 @@ device's VID (Vendor ID) and PID (Product ID):
|
|||||||
$ lspci -n -s 01:00.0
|
$ lspci -n -s 01:00.0
|
||||||
01:00.0 ff00: 10ec:525a (rev 01)
|
01:00.0 ff00: 10ec:525a (rev 01)
|
||||||
|
|
||||||
$ echo 10ec 525a > /sys/bus/pci/drivers/vfio-pci/new_id
|
# echo 10ec 525a > /sys/bus/pci/drivers/vfio-pci/new_id
|
||||||
$ echo 0000:01:00.0 > /sys/bus/pci/drivers/vfio-pci/bind
|
```
|
||||||
|
|
||||||
|
If you have more than one device with the same `vendorID`/`deviceID`, starting
|
||||||
|
with the second device, the binding is performed as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
# echo 0000:02:00.0 > /sys/bus/pci/drivers/vfio-pci/bind
|
||||||
```
|
```
|
||||||
|
|
||||||
Now the device is managed by the VFIO framework.
|
Now the device is managed by the VFIO framework.
|
||||||
@@ -79,3 +93,33 @@ takes the device's sysfs path as an argument. In our example it is
|
|||||||
The guest kernel will then detect the card reader on its PCI bus and provided
|
The guest kernel will then detect the card reader on its PCI bus and provided
|
||||||
that support for this device is enabled, it will probe and enable it for the
|
that support for this device is enabled, it will probe and enable it for the
|
||||||
guest to use.
|
guest to use.
|
||||||
|
|
||||||
|
In case you want to pass multiple devices, here is the correct syntax:
|
||||||
|
|
||||||
|
```
|
||||||
|
--device path=/sys/bus/pci/devices/0000:01:00.0/ path=/sys/bus/pci/devices/0000:02:00.0/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiple devices in the same IOMMU group
|
||||||
|
|
||||||
|
There are cases where multiple devices can be found under the same IOMMU group.
|
||||||
|
This happens often with graphics card embedding an audio controller.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ lspci
|
||||||
|
[...]
|
||||||
|
01:00.0 VGA compatible controller: NVIDIA Corporation GK208B [GeForce GT 710] (rev a1)
|
||||||
|
01:00.1 Audio device: NVIDIA Corporation GK208 HDMI/DP Audio Controller (rev a1)
|
||||||
|
[...]
|
||||||
|
```
|
||||||
|
|
||||||
|
This is usually exposed as follows through `sysfs`:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ls /sys/kernel/iommu_groups/22/devices/
|
||||||
|
0000:01:00.0 0000:01:00.1
|
||||||
|
```
|
||||||
|
|
||||||
|
This means these two devices are under the same IOMMU group 22. In such case,
|
||||||
|
it is important to bind both devices to VFIO and pass them both through the
|
||||||
|
VM, otherwise this could cause some functional and security issues.
|
||||||
@@ -69,12 +69,16 @@ make
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Set the SPDK environment
|
### Set the SPDK environment
|
||||||
|
|
||||||
|
Run SPDK vhost target with 2 CPU cores, i.e., core 0 and 1.
|
||||||
```bash
|
```bash
|
||||||
sudo HUGEMEM=2048 scripts/setup.sh
|
sudo HUGEMEM=2048 scripts/setup.sh
|
||||||
sudo ./build/bin/vhost -S /var/tmp -s 1024 -m 0x3 &
|
sudo ./build/bin/vhost -S /var/tmp -s 1024 -m 0x3 &
|
||||||
```
|
```
|
||||||
|
|
||||||
### Create 512M block device
|
### Create 512M block device
|
||||||
|
|
||||||
|
Create a 512M (first parameter) block device with 512 bytes (second parameter) block size.
|
||||||
```bash
|
```bash
|
||||||
sudo scripts/rpc.py bdev_malloc_create 512 512 -b Malloc0
|
sudo scripts/rpc.py bdev_malloc_create 512 512 -b Malloc0
|
||||||
sudo scripts/rpc.py vhost_create_blk_controller --cpumask 0x1 vhost.1 Malloc0
|
sudo scripts/rpc.py vhost_create_blk_controller --cpumask 0x1 vhost.1 Malloc0
|
||||||
|
|||||||
@@ -30,15 +30,15 @@ This step currently requires QEMU to install Windows onto the guest. QEMU is onl
|
|||||||
Preparing several command parts as these will be used in the follow up sections as well.
|
Preparing several command parts as these will be used in the follow up sections as well.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
IMG_FILE=windows-disk.qcow
|
IMG_FILE=windows-disk.raw
|
||||||
WIN_ISO_FILE=en_windows_server_version_2004_updated_may_2020_x64_dvd_1e7f1cfa.iso
|
WIN_ISO_FILE=en_windows_server_version_2004_updated_may_2020_x64_dvd_1e7f1cfa.iso
|
||||||
VIRTIO_ISO_FILE=virtio-win-0.1.185.iso
|
VIRTIO_ISO_FILE=virtio-win-0.1.185.iso
|
||||||
OVMF_DIR=./FV
|
OVMF_DIR=./FV
|
||||||
```
|
```
|
||||||
|
|
||||||
Create an empty image file, `qcow` or `raw` is supported.
|
Create an empty image file, `raw` is supported.
|
||||||
```shell
|
```shell
|
||||||
qemu-img create -f qcow2 $IMG_FILE 30G
|
qemu-img create -f raw $IMG_FILE 30G
|
||||||
```
|
```
|
||||||
|
|
||||||
Begin the Windows installation process under QEMU
|
Begin the Windows installation process under QEMU
|
||||||
@@ -221,7 +221,7 @@ qemu-system-x86_64 \
|
|||||||
-m 4G \
|
-m 4G \
|
||||||
-cdrom ./$WIN_ISO_FILE \
|
-cdrom ./$WIN_ISO_FILE \
|
||||||
-drive file=./$VIRTIO_ISO_FILE,index=0,media=cdrom
|
-drive file=./$VIRTIO_ISO_FILE,index=0,media=cdrom
|
||||||
-drive if=none,id=root,file=./windbg-disk.qcow \
|
-drive if=none,id=root,file=./windbg-disk.raw \
|
||||||
-device virtio-blk-pci,drive=root,disable-legacy=on \
|
-device virtio-blk-pci,drive=root,disable-legacy=on \
|
||||||
-device virtio-net-pci,netdev=mynet0,disable-legacy=on \
|
-device virtio-net-pci,netdev=mynet0,disable-legacy=on \
|
||||||
-netdev user,id=mynet0,net=192.168.178.0/24,host=192.168.178.1,dhcpstart=192.168.178.64,hostname=windbg-host \
|
-netdev user,id=mynet0,net=192.168.178.0/24,host=192.168.178.1,dhcpstart=192.168.178.64,hostname=windbg-host \
|
||||||
@@ -280,7 +280,7 @@ qemu-system-x86_64 \
|
|||||||
-cpu host \
|
-cpu host \
|
||||||
-smp 1 \
|
-smp 1 \
|
||||||
-m 4G \
|
-m 4G \
|
||||||
-drive if=none,id=root,file=./windbg-disk.qcow \
|
-drive if=none,id=root,file=./windbg-disk.raw \
|
||||||
-device virtio-blk-pci,drive=root,disable-legacy=on \
|
-device virtio-blk-pci,drive=root,disable-legacy=on \
|
||||||
-serial tcp::4445,server,nowait \
|
-serial tcp::4445,server,nowait \
|
||||||
-device virtio-net-pci,netdev=mynet0,disable-legacy=on \
|
-device virtio-net-pci,netdev=mynet0,disable-legacy=on \
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ authors = ["The Cloud Hypervisor Authors"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "0.2.96"
|
libc = "0.2.108"
|
||||||
serde = {version = ">=1.0.27", features = ["rc"] }
|
serde = { version = "1.0.130", features = ["rc"] }
|
||||||
serde_derive = ">=1.0.27"
|
serde_derive = "1.0.130"
|
||||||
serde_json = ">=1.0.9"
|
serde_json = "1.0.72"
|
||||||
|
|||||||
273
fuzz/Cargo.lock
generated
273
fuzz/Cargo.lock
generated
@@ -11,34 +11,37 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ansi_term"
|
name = "ansi_term"
|
||||||
version = "0.11.0"
|
version = "0.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.40"
|
version = "1.0.51"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
|
checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "api_client"
|
name = "api_client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arbitrary"
|
name = "arbitrary"
|
||||||
version = "1.0.1"
|
version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "237430fd6ed3740afe94eefcc278ae21e050285be882804e0d6e8695f0c94691"
|
checksum = "510c76ecefdceada737ea728f4f9a84bd2e1ef29f1ba555e560940fe279954de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arc-swap"
|
name = "arc-swap"
|
||||||
version = "1.3.0"
|
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 = "e906254e445520903e7fc9da4f709886c84ae4bc4ddaf0e093188d66df4dc820"
|
checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arch"
|
name = "arch"
|
||||||
@@ -47,17 +50,20 @@ dependencies = [
|
|||||||
"acpi_tables",
|
"acpi_tables",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
"fdt",
|
||||||
"hypervisor",
|
"hypervisor",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-loader",
|
"linux-loader",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
"vm-fdt",
|
"vm-fdt",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vm-migration",
|
"vm-migration",
|
||||||
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -82,9 +88,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.2.1"
|
version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block_util"
|
name = "block_util"
|
||||||
@@ -97,7 +103,9 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
|
"vhdx",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
|
"virtio-queue",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vm-virtio",
|
"vm-virtio",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
@@ -111,9 +119,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.68"
|
version = "1.0.72"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787"
|
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@@ -123,9 +131,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "2.33.3"
|
version = "2.34.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"atty",
|
"atty",
|
||||||
@@ -139,7 +147,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cloud-hypervisor"
|
name = "cloud-hypervisor"
|
||||||
version = "15.0.0"
|
version = "20.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"api_client",
|
"api_client",
|
||||||
@@ -150,7 +158,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"option_parser",
|
"option_parser",
|
||||||
"seccomp",
|
"seccompiler",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@@ -168,13 +176,24 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"libfuzzer-sys",
|
"libfuzzer-sys",
|
||||||
"qcow",
|
"qcow",
|
||||||
"seccomp",
|
"seccompiler",
|
||||||
|
"vhdx",
|
||||||
"virtio-devices",
|
"virtio-devices",
|
||||||
|
"virtio-queue",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vm-virtio",
|
"vm-virtio",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32c"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "210cdf933e6a81212bfabf90cd8762f471b5922e5f6b709547673ad8e04b9448"
|
||||||
|
dependencies = [
|
||||||
|
"rustc_version",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc64"
|
name = "crc64"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -222,10 +241,27 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "fdt"
|
||||||
version = "0.1.18"
|
version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
checksum = "b643857cf70949306b81d7e92cb9d47add673868edac9863c4a49c42feaf3f1e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@@ -251,9 +287,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iced-x86"
|
name = "iced-x86"
|
||||||
version = "1.11.3"
|
version = "1.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d907dfc273d9ed0da940e8145ceb77759056a1fb2110b44b83d1b203cddb906"
|
checksum = "46e977036f7f5139d580c7f19ad62df9cb8ebd8410bb569e73585226be80a86f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
@@ -261,9 +297,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-uring"
|
name = "io-uring"
|
||||||
version = "0.5.1"
|
version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fb82832e05cc4ca298f198a8db108837b4f7b7b1248e3cba8e48f151aece80cf"
|
checksum = "8d75829ed9377bab6c90039fe47b9d84caceb4b5063266142e21bcce6550cda8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -271,14 +307,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "0.4.7"
|
version = "0.4.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kvm-bindings"
|
name = "kvm-bindings"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.4.0#1a68725639283e622f4bb64584885b30bfe8be44"
|
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.5.0#9c497710ba9968d8efe15dbae03991b16cf82e23"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
@@ -287,9 +323,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kvm-ioctls"
|
name = "kvm-ioctls"
|
||||||
version = "0.9.0"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/rust-vmm/kvm-ioctls?branch=main#d22ef1f51852dfb055da38004e1a4fed81246f81"
|
||||||
checksum = "f2924454e22895c738e43331ae310459c74a11ded9c97dc250129ee10d2f9ca2"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"kvm-bindings",
|
"kvm-bindings",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -304,9 +339,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.96"
|
version = "0.2.108"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5600b4e6efc5421841a2138a6b082e07fe12f9aaa12783d50e5d13325b26b4fc"
|
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libfuzzer-sys"
|
name = "libfuzzer-sys"
|
||||||
@@ -321,9 +356,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-loader"
|
name = "linux-loader"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c819cc8275b0f2c1ed9feec455ca288b45d82932384a6a5f7a86812ee3427459"
|
checksum = "8a5e77493808403a6bd56a301a64ea6b9342e36ea845044bf0dfdf56fe52fa08"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
]
|
]
|
||||||
@@ -340,7 +375,7 @@ dependencies = [
|
|||||||
[[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#9b605a8b61df602cda62076d30d9f70307ea336f"
|
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#9517a300370a158a7af0996b7eebf040d171e1a4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
@@ -358,6 +393,7 @@ name = "net_util"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"epoll",
|
"epoll",
|
||||||
|
"getrandom",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"net_gen",
|
"net_gen",
|
||||||
@@ -366,6 +402,7 @@ dependencies = [
|
|||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
|
"virtio-queue",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vm-virtio",
|
"vm-virtio",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
@@ -373,9 +410,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.7.2"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
|
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "option_parser"
|
name = "option_parser"
|
||||||
@@ -390,10 +427,12 @@ dependencies = [
|
|||||||
"hypervisor",
|
"hypervisor",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
"thiserror",
|
||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
"vfio-bindings",
|
"vfio-bindings",
|
||||||
"vfio-ioctls",
|
"vfio-ioctls",
|
||||||
|
"vfio_user",
|
||||||
"vm-allocator",
|
"vm-allocator",
|
||||||
"vm-device",
|
"vm-device",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
@@ -403,9 +442,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.27"
|
version = "1.0.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
|
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
@@ -423,9 +462,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.9"
|
version = "1.0.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
@@ -451,30 +490,55 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "rustc_version"
|
||||||
version = "1.0.5"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "seccomp"
|
name = "ryu"
|
||||||
version = "0.1.0"
|
version = "1.0.6"
|
||||||
source = "git+https://github.com/firecracker-microvm/firecracker?tag=v0.24.4#8f44986a0e956a77f2b2324c12f73bec16130c82"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "seccompiler"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e01d1292a1131b22ccea49f30bd106f1238b5ddeec1a98d39268dcc31d540e68"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "semver"
|
||||||
version = "1.0.126"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
|
dependencies = [
|
||||||
|
"semver-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver-parser"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.130"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.126"
|
version = "1.0.130"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
|
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -483,9 +547,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.64"
|
version = "1.0.72"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
|
checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
@@ -494,9 +558,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook"
|
name = "signal-hook"
|
||||||
version = "0.3.9"
|
version = "0.3.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "470c5a6397076fae0094aaf06a08e6ba6f37acb77d3b1b91ea92b4d6c8650c39"
|
checksum = "9c98891d737e271a2954825ef19e46bd16bdb98e2746f2eec4f7a4ef7946efd1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
@@ -525,9 +589,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.73"
|
version = "1.0.82"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7"
|
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -556,18 +620,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.25"
|
version = "1.0.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6"
|
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.25"
|
version = "1.0.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d"
|
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -576,9 +640,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.8"
|
version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
@@ -591,6 +655,9 @@ name = "uuid"
|
|||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vec_map"
|
name = "vec_map"
|
||||||
@@ -618,8 +685,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "versionize_derive"
|
name = "versionize_derive"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/cloud-hypervisor/versionize_derive?branch=ch#ae35ef7a3ddabd3371ab8ac0193a383aff6e4b1b"
|
||||||
checksum = "140aa9fd298f667ea50fa1cb0d8530076924079285c623b18b8f8a1c28386b4a"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -638,7 +704,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "vfio-ioctls"
|
name = "vfio-ioctls"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/rust-vmm/vfio-ioctls?branch=master#5cb8550556620546e74b9fe6d1174e0d1fc83888"
|
source = "git+https://github.com/rust-vmm/vfio-ioctls?branch=main#8f7f2210ebe71660938b05b2ed7eec9253478bc5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"kvm-bindings",
|
"kvm-bindings",
|
||||||
@@ -650,12 +716,44 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vhost"
|
name = "vfio_user"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/rust-vmm/vhost?branch=master#e5b930b73a47cbdd79858e2e68bcb57123d1c1f3"
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"vfio-bindings",
|
||||||
|
"vm-memory",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vhdx"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"crc32c",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"remain",
|
||||||
|
"thiserror",
|
||||||
|
"uuid",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vhost"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d23ddeb452fb4f837526c6298cc8a2f4948e5595b0328a3d61b5eebe51874d7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"libc",
|
"libc",
|
||||||
|
"vm-memory",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -682,7 +780,7 @@ dependencies = [
|
|||||||
"net_util",
|
"net_util",
|
||||||
"pci",
|
"pci",
|
||||||
"rate_limiter",
|
"rate_limiter",
|
||||||
"seccomp",
|
"seccompiler",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -690,6 +788,7 @@ dependencies = [
|
|||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
"vhost",
|
"vhost",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
|
"virtio-queue",
|
||||||
"vm-allocator",
|
"vm-allocator",
|
||||||
"vm-device",
|
"vm-device",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
@@ -698,6 +797,15 @@ dependencies = [
|
|||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "virtio-queue"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"vm-memory",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vm-allocator"
|
name = "vm-allocator"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -724,12 +832,13 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "vm-fdt"
|
name = "vm-fdt"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/rust-vmm/vm-fdt?branch=master#13ab882e2f8387753064351c69fbe35c422b448b"
|
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#720e48e435b791ec6cbe8d2b229448b71dcb1ab9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vm-memory"
|
name = "vm-memory"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "git+https://github.com/rust-vmm/vm-memory?rev=5bd7138758183a73ac0da27ce40c004d95f1a7e9#5bd7138758183a73ac0da27ce40c004d95f1a7e9"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0a8ebcb86ca457f9d6e14cf97009f679952eba42f0113de5db596e514cd0e43b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -756,6 +865,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
|
"virtio-queue",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -783,7 +893,7 @@ dependencies = [
|
|||||||
"option_parser",
|
"option_parser",
|
||||||
"pci",
|
"pci",
|
||||||
"qcow",
|
"qcow",
|
||||||
"seccomp",
|
"seccompiler",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -793,7 +903,10 @@ dependencies = [
|
|||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
"vfio-ioctls",
|
"vfio-ioctls",
|
||||||
|
"vfio_user",
|
||||||
|
"vhdx",
|
||||||
"virtio-devices",
|
"virtio-devices",
|
||||||
|
"virtio-queue",
|
||||||
"vm-allocator",
|
"vm-allocator",
|
||||||
"vm-device",
|
"vm-device",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
@@ -804,9 +917,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vmm-sys-util"
|
name = "vmm-sys-util"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "01cf11afbc4ebc0d5c7a7748a77d19e2042677fc15faa2f4ccccb27c18a60605"
|
checksum = "733537bded03aaa93543f785ae997727b30d1d9f4a03b7861d23290474242e11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -814,6 +927,12 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|||||||
@@ -10,21 +10,24 @@ cargo-fuzz = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
block_util = { path = "../block_util" }
|
block_util = { path = "../block_util" }
|
||||||
libc = "0.2.96"
|
libc = "0.2.108"
|
||||||
libfuzzer-sys = "0.4.2"
|
libfuzzer-sys = "0.4.2"
|
||||||
qcow = { path = "../qcow" }
|
qcow = { path = "../qcow" }
|
||||||
seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v0.24.4" }
|
seccompiler = "0.2.0"
|
||||||
|
vhdx = { path = "../vhdx" }
|
||||||
virtio-devices = { path = "../virtio-devices" }
|
virtio-devices = { path = "../virtio-devices" }
|
||||||
vmm-sys-util = "0.8.0"
|
virtio-queue = { path = "../virtio-queue" }
|
||||||
|
vmm-sys-util = "0.9.0"
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
vm-memory = "0.5.0"
|
vm-memory = "0.6.0"
|
||||||
|
|
||||||
[dependencies.cloud-hypervisor]
|
[dependencies.cloud-hypervisor]
|
||||||
path = ".."
|
path = ".."
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.4.0", features = ["with-serde", "fam-wrappers"] }
|
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0", features = ["with-serde", "fam-wrappers"] }
|
||||||
vm-memory = { git = "https://github.com/rust-vmm/vm-memory", rev = "5bd7138758183a73ac0da27ce40c004d95f1a7e9"}
|
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "main" }
|
||||||
|
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
|
||||||
|
|
||||||
# Prevent this from interfering with workspaces
|
# Prevent this from interfering with workspaces
|
||||||
[workspace]
|
[workspace]
|
||||||
@@ -41,3 +44,9 @@ name = "block"
|
|||||||
path = "fuzz_targets/block.rs"
|
path = "fuzz_targets/block.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "vhdx"
|
||||||
|
path = "fuzz_targets/vhdx.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
use block_util::{async_io::DiskFile, qcow_sync::QcowDiskSync};
|
use block_util::{async_io::DiskFile, raw_sync::RawFileDiskSync};
|
||||||
use libfuzzer_sys::fuzz_target;
|
use libfuzzer_sys::fuzz_target;
|
||||||
use seccomp::SeccompAction;
|
use seccompiler::SeccompAction;
|
||||||
use std::ffi;
|
use std::ffi;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, Cursor, Read, Seek, SeekFrom};
|
use std::io::{self, Cursor, Read, Seek, SeekFrom};
|
||||||
@@ -15,9 +15,11 @@ 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 virtio_devices::{Block, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
use virtio_devices::{Block, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||||
use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryMmap};
|
use virtio_queue::{Queue, QueueState};
|
||||||
use vm_virtio::Queue;
|
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||||
|
|
||||||
|
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||||
|
|
||||||
const MEM_SIZE: u64 = 256 * 1024 * 1024;
|
const MEM_SIZE: u64 = 256 * 1024 * 1024;
|
||||||
const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor.
|
const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor.
|
||||||
@@ -73,10 +75,14 @@ fuzz_target!(|bytes| {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut q = Queue::new(QUEUE_SIZE);
|
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||||
q.ready = true;
|
|
||||||
q.size = QUEUE_SIZE / 2;
|
let mut q = Queue::<
|
||||||
q.max_size = QUEUE_SIZE;
|
GuestMemoryAtomic<GuestMemoryMmap>,
|
||||||
|
QueueState<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||||
|
>::new(guest_memory.clone(), QUEUE_SIZE);
|
||||||
|
q.state.ready = true;
|
||||||
|
q.state.size = QUEUE_SIZE / 2;
|
||||||
|
|
||||||
let queue_evts: Vec<EventFd> = vec![EventFd::new(0).unwrap()];
|
let queue_evts: Vec<EventFd> = vec![EventFd::new(0).unwrap()];
|
||||||
let queue_fd = queue_evts[0].as_raw_fd();
|
let queue_fd = queue_evts[0].as_raw_fd();
|
||||||
@@ -84,7 +90,7 @@ fuzz_target!(|bytes| {
|
|||||||
|
|
||||||
let shm = memfd_create(&ffi::CString::new("fuzz").unwrap(), 0).unwrap();
|
let shm = memfd_create(&ffi::CString::new("fuzz").unwrap(), 0).unwrap();
|
||||||
let disk_file: File = unsafe { File::from_raw_fd(shm) };
|
let disk_file: File = unsafe { File::from_raw_fd(shm) };
|
||||||
let qcow_disk = Box::new(QcowDiskSync::new(disk_file, false)) as Box<dyn DiskFile>;
|
let qcow_disk = Box::new(RawFileDiskSync::new(disk_file)) as Box<dyn DiskFile>;
|
||||||
|
|
||||||
let mut block = Block::new(
|
let mut block = Block::new(
|
||||||
"tmp".to_owned(),
|
"tmp".to_owned(),
|
||||||
@@ -96,12 +102,13 @@ fuzz_target!(|bytes| {
|
|||||||
256,
|
256,
|
||||||
SeccompAction::Allow,
|
SeccompAction::Allow,
|
||||||
None,
|
None,
|
||||||
|
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
block
|
block
|
||||||
.activate(
|
.activate(
|
||||||
GuestMemoryAtomic::new(mem),
|
guest_memory,
|
||||||
Arc::new(NoopVirtioInterrupt {}),
|
Arc::new(NoopVirtioInterrupt {}),
|
||||||
vec![q],
|
vec![q],
|
||||||
queue_evts,
|
queue_evts,
|
||||||
@@ -133,7 +140,7 @@ impl VirtioInterrupt for NoopVirtioInterrupt {
|
|||||||
fn trigger(
|
fn trigger(
|
||||||
&self,
|
&self,
|
||||||
_int_type: &VirtioInterruptType,
|
_int_type: &VirtioInterruptType,
|
||||||
_queue: Option<&Queue>,
|
_queue: Option<&Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||||
) -> std::result::Result<(), std::io::Error> {
|
) -> std::result::Result<(), std::io::Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
53
fuzz/fuzz_targets/vhdx.rs
Normal file
53
fuzz/fuzz_targets/vhdx.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
// Copyright © 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use std::ffi;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||||
|
use std::os::unix::io::{FromRawFd, RawFd};
|
||||||
|
use vhdx::vhdx::Vhdx;
|
||||||
|
|
||||||
|
// Populate the corpus directory with a test file:
|
||||||
|
// truncate -s 16M /tmp/source
|
||||||
|
// qemu-img convert -O vhdx /tmp/source fuzz/corpus/vhdx/test.vhdx
|
||||||
|
// Run with:
|
||||||
|
// cargo fuzz run vhdx -j 32 -- -max_len=16777216
|
||||||
|
fuzz_target!(|bytes| {
|
||||||
|
let shm = memfd_create(&ffi::CString::new("fuzz").unwrap(), 0).unwrap();
|
||||||
|
let mut disk_file: File = unsafe { File::from_raw_fd(shm) };
|
||||||
|
disk_file.write_all(&bytes[..]).unwrap();
|
||||||
|
disk_file.seek(SeekFrom::Start(0)).unwrap();
|
||||||
|
|
||||||
|
if let Ok(mut vhdx) = Vhdx::new(disk_file) {
|
||||||
|
if vhdx.seek(SeekFrom::Start(0)).is_ok() {
|
||||||
|
let mut offset = 0;
|
||||||
|
while offset < bytes.len() {
|
||||||
|
let mut data = vec![0; 8192];
|
||||||
|
vhdx.read_exact(&mut data).ok();
|
||||||
|
offset += data.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if vhdx.seek(SeekFrom::Start(0)).is_ok() {
|
||||||
|
let mut offset = 0;
|
||||||
|
while offset < bytes.len() {
|
||||||
|
let data = vec![0; 8192];
|
||||||
|
vhdx.write_all(&data).ok();
|
||||||
|
offset += data.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> {
|
||||||
|
let res = unsafe { libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags) };
|
||||||
|
|
||||||
|
if res < 0 {
|
||||||
|
Err(io::Error::last_os_error())
|
||||||
|
} else {
|
||||||
|
Ok(res as RawFd)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,25 +11,25 @@ mshv = ["mshv-ioctls", "mshv-bindings"]
|
|||||||
tdx = []
|
tdx = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0.51"
|
||||||
epoll = ">=4.0.1"
|
epoll = "4.3.1"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0.30"
|
||||||
libc = "0.2.96"
|
libc = "0.2.108"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
kvm-ioctls = { version = "0.9.0", optional = true }
|
kvm-ioctls = { version = "0.11.0", optional = true }
|
||||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.4.0", features = ["with-serde", "fam-wrappers"], optional = true }
|
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||||
mshv-bindings = {git = "https://github.com/cloud-hypervisor/mshv", branch = "master", features = ["with-serde", "fam-wrappers"], optional = true }
|
mshv-bindings = {git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||||
mshv-ioctls = { git = "https://github.com/cloud-hypervisor/mshv", branch = "master", optional = true}
|
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", branch = "main", optional = true}
|
||||||
serde = {version = ">=1.0.27", features = ["rc"] }
|
serde = { version = "1.0.130", features = ["rc"] }
|
||||||
serde_derive = ">=1.0.27"
|
serde_derive = "1.0.130"
|
||||||
serde_json = ">=1.0.9"
|
serde_json = "1.0.72"
|
||||||
vm-memory = { version = "0.5.0", features = ["backend-mmap", "backend-atomic"] }
|
vm-memory = { version = "0.6.0", features = ["backend-mmap", "backend-atomic"] }
|
||||||
vmm-sys-util = { version = ">=0.5.0", features = ["with-serde"] }
|
vmm-sys-util = { version = "0.9.0", features = ["with-serde"] }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
||||||
version = "1.11"
|
version = "1.15.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]
|
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
env_logger = "0.8.3"
|
env_logger = "0.9.0"
|
||||||
|
|||||||
@@ -18,7 +18,18 @@ pub struct Exception<T: Debug> {
|
|||||||
|
|
||||||
impl<T: Debug> Display for Exception<T> {
|
impl<T: Debug> Display for Exception<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "Exception {:?} at IP {:#x}", self.vector, self.ip)
|
write!(
|
||||||
|
f,
|
||||||
|
"Exception {:?} at IP {:#x}{}{}",
|
||||||
|
self.vector,
|
||||||
|
self.ip,
|
||||||
|
self.error
|
||||||
|
.map(|e| format!(": error {:x}", e))
|
||||||
|
.unwrap_or_else(|| "".to_owned()),
|
||||||
|
self.payload
|
||||||
|
.map(|payload| format!(": payload {:x}", payload))
|
||||||
|
.unwrap_or_else(|| "".to_owned())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -653,8 +653,6 @@ mod mock_vmm {
|
|||||||
state: Arc<Mutex<CpuState>>,
|
state: Arc<Mutex<CpuState>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Sync for MockVmm {}
|
|
||||||
|
|
||||||
pub type MockResult = Result<(), EmulationError<Exception>>;
|
pub type MockResult = Result<(), EmulationError<Exception>>;
|
||||||
|
|
||||||
impl MockVmm {
|
impl MockVmm {
|
||||||
@@ -678,7 +676,7 @@ mod mock_vmm {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(mem) = memory {
|
if let Some(mem) = memory {
|
||||||
vmm.write_memory(mem.0, &mem.1).unwrap();
|
vmm.write_memory(mem.0, mem.1).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
vmm
|
vmm
|
||||||
@@ -693,7 +691,7 @@ mod mock_vmm {
|
|||||||
let ip = self.cpu_state(cpu_id).unwrap().ip();
|
let ip = self.cpu_state(cpu_id).unwrap().ip();
|
||||||
let mut emulator = Emulator::new(self);
|
let mut emulator = Emulator::new(self);
|
||||||
|
|
||||||
let new_state = emulator.emulate_insn_stream(cpu_id, &insn, num_insn)?;
|
let new_state = emulator.emulate_insn_stream(cpu_id, insn, num_insn)?;
|
||||||
if num_insn.is_none() {
|
if num_insn.is_none() {
|
||||||
assert_eq!(ip + insn.len() as u64, new_state.ip());
|
assert_eq!(ip + insn.len() as u64, new_state.ip());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ use crate::x86_64::{
|
|||||||
use crate::CpuState;
|
use crate::CpuState;
|
||||||
#[cfg(feature = "kvm")]
|
#[cfg(feature = "kvm")]
|
||||||
use crate::MpState;
|
use crate::MpState;
|
||||||
|
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||||
|
use crate::SuspendRegisters;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::Xsave;
|
use crate::Xsave;
|
||||||
#[cfg(feature = "mshv")]
|
#[cfg(feature = "mshv")]
|
||||||
@@ -42,6 +44,11 @@ pub enum HypervisorCpuError {
|
|||||||
#[error("Failed to get standard registers: {0}")]
|
#[error("Failed to get standard registers: {0}")]
|
||||||
GetStandardRegs(#[source] anyhow::Error),
|
GetStandardRegs(#[source] anyhow::Error),
|
||||||
///
|
///
|
||||||
|
/// Getting suspend registers error
|
||||||
|
///
|
||||||
|
#[error("Failed to get suspend registers: {0}")]
|
||||||
|
GetSuspendRegs(#[source] anyhow::Error),
|
||||||
|
///
|
||||||
/// Setting special register error
|
/// Setting special register error
|
||||||
///
|
///
|
||||||
#[error("Failed to set special registers: {0}")]
|
#[error("Failed to set special registers: {0}")]
|
||||||
@@ -167,6 +174,16 @@ pub enum HypervisorCpuError {
|
|||||||
#[error("Failed to get debug registers: {0}")]
|
#[error("Failed to get debug registers: {0}")]
|
||||||
GetDebugRegs(#[source] anyhow::Error),
|
GetDebugRegs(#[source] anyhow::Error),
|
||||||
///
|
///
|
||||||
|
/// Setting misc register error
|
||||||
|
///
|
||||||
|
#[error("Failed to set misc registers: {0}")]
|
||||||
|
SetMiscRegs(#[source] anyhow::Error),
|
||||||
|
///
|
||||||
|
/// Getting misc register error
|
||||||
|
///
|
||||||
|
#[error("Failed to get misc registers: {0}")]
|
||||||
|
GetMiscRegs(#[source] anyhow::Error),
|
||||||
|
///
|
||||||
/// Write to Guest Mem
|
/// Write to Guest Mem
|
||||||
///
|
///
|
||||||
#[error("Failed to write to Guest Mem at: {0}")]
|
#[error("Failed to write to Guest Mem at: {0}")]
|
||||||
@@ -421,4 +438,9 @@ pub trait Vcpu: Send + Sync {
|
|||||||
///
|
///
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
fn tdx_init(&self, hob_address: u64) -> Result<()>;
|
fn tdx_init(&self, hob_address: u64) -> Result<()>;
|
||||||
|
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||||
|
///
|
||||||
|
/// Return suspend registers(explicit and intercept suspend registers)
|
||||||
|
///
|
||||||
|
fn get_suspend_regs(&self) -> Result<SuspendRegisters>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,23 +26,33 @@ pub use kvm_bindings::{
|
|||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
pub use {kvm_ioctls::Cap, kvm_ioctls::Kvm};
|
pub use {kvm_ioctls::Cap, kvm_ioctls::Kvm};
|
||||||
|
|
||||||
// Following are macros that help with getting the ID of a aarch64 core register.
|
|
||||||
// The core register are represented by the user_pt_regs structure. Look for it in
|
|
||||||
// arch/arm64/include/uapi/asm/ptrace.h.
|
|
||||||
|
|
||||||
// This macro gets the offset of a structure (i.e `str`) member (i.e `field`) without having
|
// This macro gets the offset of a structure (i.e `str`) member (i.e `field`) without having
|
||||||
// an instance of that structure.
|
// an instance of that structure.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! offset__of {
|
macro_rules! offset__of {
|
||||||
($str:ty, $($field:ident)+) => ({
|
($str:ty, $($field:ident)+) => ({
|
||||||
let tmp: std::mem::MaybeUninit<$str> = std::mem::MaybeUninit::uninit();
|
let tmp: std::mem::MaybeUninit<$str> = std::mem::MaybeUninit::uninit();
|
||||||
let tmp = unsafe { tmp.assume_init() };
|
let base = tmp.as_ptr();
|
||||||
let base = &tmp as *const _ as usize;
|
|
||||||
let member = &tmp.$($field)* as *const _ as usize;
|
|
||||||
|
|
||||||
member - base
|
// Avoid warnings when nesting `unsafe` blocks.
|
||||||
|
#[allow(unused_unsafe)]
|
||||||
|
// SAFETY: The pointer is valid and aligned, just not initialised. Using `addr_of` ensures
|
||||||
|
// that we don't actually read from `base` (which would be UB) nor create an intermediate
|
||||||
|
// reference.
|
||||||
|
let member = unsafe { core::ptr::addr_of!((*base).$($field)*) } as *const u8;
|
||||||
|
|
||||||
|
// Avoid warnings when nesting `unsafe` blocks.
|
||||||
|
#[allow(unused_unsafe)]
|
||||||
|
// SAFETY: The two pointers are within the same allocated object `tmp`. All requirements
|
||||||
|
// from offset_from are upheld.
|
||||||
|
unsafe { member.offset_from(base as *const u8) as usize }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Following are macros that help with getting the ID of a aarch64 core register.
|
||||||
|
// The core register are represented by the user_pt_regs structure. Look for it in
|
||||||
|
// arch/arm64/include/uapi/asm/ptrace.h.
|
||||||
|
|
||||||
// Get the ID of a core register
|
// Get the ID of a core register
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! arm64_core_reg_id {
|
macro_rules! arm64_core_reg_id {
|
||||||
@@ -115,9 +125,13 @@ pub fn is_system_register(regid: u64) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let size = regid & KVM_REG_SIZE_MASK;
|
let size = regid & KVM_REG_SIZE_MASK;
|
||||||
if size != KVM_REG_SIZE_U32 && size != KVM_REG_SIZE_U64 {
|
|
||||||
panic!("Unexpected register size for system register {}", size);
|
assert!(
|
||||||
}
|
!(size != KVM_REG_SIZE_U32 && size != KVM_REG_SIZE_U64),
|
||||||
|
"Unexpected register size for system register {}",
|
||||||
|
size
|
||||||
|
);
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,15 +22,16 @@ use crate::vm::{self, VmmOps};
|
|||||||
use crate::{arm64_core_reg_id, offset__of};
|
use crate::{arm64_core_reg_id, offset__of};
|
||||||
use kvm_ioctls::{NoDatamatch, VcpuFd, VmFd};
|
use kvm_ioctls::{NoDatamatch, VcpuFd, VmFd};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
use std::fs::File;
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
use std::result;
|
use std::result;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, RwLock};
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
use vm_memory::Address;
|
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
// x86_64 dependencies
|
// x86_64 dependencies
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
@@ -44,9 +45,7 @@ use kvm_bindings::{
|
|||||||
kvm_enable_cap, kvm_msr_entry, MsrList, KVM_CAP_HYPERV_SYNIC, KVM_CAP_SPLIT_IRQCHIP,
|
kvm_enable_cap, kvm_msr_entry, MsrList, KVM_CAP_HYPERV_SYNIC, KVM_CAP_SPLIT_IRQCHIP,
|
||||||
};
|
};
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use x86_64::{
|
use x86_64::{check_required_kvm_extensions, FpuState, SpecialRegisters, StandardRegisters};
|
||||||
check_required_kvm_extensions, FpuState, SpecialRegisters, StandardRegisters, KVM_TSS_ADDRESS,
|
|
||||||
};
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub use x86_64::{
|
pub use x86_64::{
|
||||||
CpuId, CpuIdEntry, ExtendedControlRegisters, LapicState, MsrEntries, VcpuKvmState as CpuState,
|
CpuId, CpuIdEntry, ExtendedControlRegisters, LapicState, MsrEntries, VcpuKvmState as CpuState,
|
||||||
@@ -87,6 +86,9 @@ pub use {
|
|||||||
kvm_ioctls::VcpuExit,
|
kvm_ioctls::VcpuExit,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
const KVM_CAP_SGX_ATTRIBUTE: u32 = 196;
|
||||||
|
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
ioctl_iowr_nr!(KVM_MEMORY_ENCRYPT_OP, KVMIO, 0xba, std::os::raw::c_ulong);
|
ioctl_iowr_nr!(KVM_MEMORY_ENCRYPT_OP, KVMIO, 0xba, std::os::raw::c_ulong);
|
||||||
|
|
||||||
@@ -105,12 +107,21 @@ enum TdxCommand {
|
|||||||
pub struct KvmVmState {}
|
pub struct KvmVmState {}
|
||||||
|
|
||||||
pub use KvmVmState as VmState;
|
pub use KvmVmState as VmState;
|
||||||
|
|
||||||
|
struct KvmDirtyLogSlot {
|
||||||
|
slot: u32,
|
||||||
|
guest_phys_addr: u64,
|
||||||
|
memory_size: u64,
|
||||||
|
userspace_addr: u64,
|
||||||
|
}
|
||||||
|
|
||||||
/// Wrapper over KVM VM ioctls.
|
/// Wrapper over KVM VM ioctls.
|
||||||
pub struct KvmVm {
|
pub struct KvmVm {
|
||||||
fd: Arc<VmFd>,
|
fd: Arc<VmFd>,
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
msrs: MsrEntries,
|
msrs: MsrEntries,
|
||||||
state: KvmVmState,
|
state: KvmVmState,
|
||||||
|
dirty_log_slots: Arc<RwLock<HashMap<u32, KvmDirtyLogSlot>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -124,6 +135,15 @@ pub struct KvmVm {
|
|||||||
/// vm.set/get().unwrap()
|
/// vm.set/get().unwrap()
|
||||||
///
|
///
|
||||||
impl vm::Vm for KvmVm {
|
impl vm::Vm for KvmVm {
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
///
|
||||||
|
/// Sets the address of the one-page region in the VM's address space.
|
||||||
|
///
|
||||||
|
fn set_identity_map_address(&self, address: u64) -> vm::Result<()> {
|
||||||
|
self.fd
|
||||||
|
.set_identity_map_address(address)
|
||||||
|
.map_err(|e| vm::HypervisorVmError::SetIdentityMapAddress(e.into()))
|
||||||
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
/// Sets the address of the three-page region in the VM's address space.
|
/// Sets the address of the three-page region in the VM's address space.
|
||||||
@@ -163,7 +183,7 @@ impl vm::Vm for KvmVm {
|
|||||||
fn create_vcpu(
|
fn create_vcpu(
|
||||||
&self,
|
&self,
|
||||||
id: u8,
|
id: u8,
|
||||||
vmmops: Option<Arc<Box<dyn VmmOps>>>,
|
vmmops: Option<Arc<dyn VmmOps>>,
|
||||||
) -> vm::Result<Arc<dyn cpu::Vcpu>> {
|
) -> vm::Result<Arc<dyn cpu::Vcpu>> {
|
||||||
let vc = self
|
let vc = self
|
||||||
.fd
|
.fd
|
||||||
@@ -223,10 +243,13 @@ impl vm::Vm for KvmVm {
|
|||||||
irq_routing[0].nr = entries.len() as u32;
|
irq_routing[0].nr = entries.len() as u32;
|
||||||
irq_routing[0].flags = 0;
|
irq_routing[0].flags = 0;
|
||||||
|
|
||||||
|
// SAFETY: irq_routing initialized with entries.len() and now it is being turned into
|
||||||
|
// entries_slice with entries.len() again. It is guaranteed to be large enough to hold
|
||||||
|
// everything from entries.
|
||||||
unsafe {
|
unsafe {
|
||||||
let entries_slice: &mut [kvm_irq_routing_entry] =
|
let entries_slice: &mut [kvm_irq_routing_entry] =
|
||||||
irq_routing[0].entries.as_mut_slice(entries.len());
|
irq_routing[0].entries.as_mut_slice(entries.len());
|
||||||
entries_slice.copy_from_slice(&entries);
|
entries_slice.copy_from_slice(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.fd
|
self.fd
|
||||||
@@ -234,7 +257,7 @@ impl vm::Vm for KvmVm {
|
|||||||
.map_err(|e| vm::HypervisorVmError::SetGsiRouting(e.into()))
|
.map_err(|e| vm::HypervisorVmError::SetGsiRouting(e.into()))
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
/// Creates a memory region structure that can be used with set_user_memory_region
|
/// Creates a memory region structure that can be used with {create/remove}_user_memory_region
|
||||||
///
|
///
|
||||||
fn make_user_memory_region(
|
fn make_user_memory_region(
|
||||||
&self,
|
&self,
|
||||||
@@ -259,14 +282,57 @@ impl vm::Vm for KvmVm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
/// Creates/modifies a guest physical memory slot.
|
/// Creates a guest physical memory region.
|
||||||
///
|
///
|
||||||
fn set_user_memory_region(&self, user_memory_region: MemoryRegion) -> vm::Result<()> {
|
fn create_user_memory_region(&self, user_memory_region: MemoryRegion) -> vm::Result<()> {
|
||||||
// Safe because guest regions are guaranteed not to overlap.
|
let mut region = user_memory_region;
|
||||||
|
|
||||||
|
if (region.flags & KVM_MEM_LOG_DIRTY_PAGES) != 0 {
|
||||||
|
if (region.flags & KVM_MEM_READONLY) != 0 {
|
||||||
|
return Err(vm::HypervisorVmError::CreateUserMemory(anyhow!(
|
||||||
|
"Error creating regions with both 'dirty-pages-log' and 'read-only'."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep track of the regions that need dirty pages log
|
||||||
|
self.dirty_log_slots.write().unwrap().insert(
|
||||||
|
region.slot,
|
||||||
|
KvmDirtyLogSlot {
|
||||||
|
slot: region.slot,
|
||||||
|
guest_phys_addr: region.guest_phys_addr,
|
||||||
|
memory_size: region.memory_size,
|
||||||
|
userspace_addr: region.userspace_addr,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Always create guest physical memory region without `KVM_MEM_LOG_DIRTY_PAGES`.
|
||||||
|
// For regions that need this flag, dirty pages log will be turned on in `start_dirty_log`.
|
||||||
|
region.flags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: Safe because guest regions are guaranteed not to overlap.
|
||||||
unsafe {
|
unsafe {
|
||||||
self.fd
|
self.fd
|
||||||
.set_user_memory_region(user_memory_region)
|
.set_user_memory_region(region)
|
||||||
.map_err(|e| vm::HypervisorVmError::SetUserMemory(e.into()))
|
.map_err(|e| vm::HypervisorVmError::CreateUserMemory(e.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
///
|
||||||
|
/// Removes a guest physical memory region.
|
||||||
|
///
|
||||||
|
fn remove_user_memory_region(&self, user_memory_region: MemoryRegion) -> vm::Result<()> {
|
||||||
|
let mut region = user_memory_region;
|
||||||
|
|
||||||
|
// Remove the corresponding entry from "self.dirty_log_slots" if needed
|
||||||
|
self.dirty_log_slots.write().unwrap().remove(®ion.slot);
|
||||||
|
|
||||||
|
// Setting the size to 0 means "remove"
|
||||||
|
region.memory_size = 0;
|
||||||
|
// SAFETY: Safe because guest regions are guaranteed not to overlap.
|
||||||
|
unsafe {
|
||||||
|
self.fd
|
||||||
|
.set_user_memory_region(region)
|
||||||
|
.map_err(|e| vm::HypervisorVmError::RemoveUserMemory(e.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
@@ -292,10 +358,6 @@ impl vm::Vm for KvmVm {
|
|||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn enable_split_irq(&self) -> vm::Result<()> {
|
fn enable_split_irq(&self) -> vm::Result<()> {
|
||||||
// Set TSS
|
|
||||||
self.fd
|
|
||||||
.set_tss_address(KVM_TSS_ADDRESS.raw_value() as usize)
|
|
||||||
.map_err(|e| vm::HypervisorVmError::EnableSplitIrq(e.into()))?;
|
|
||||||
// Create split irqchip
|
// Create split irqchip
|
||||||
// Only the local APIC is emulated in kernel, both PICs and IOAPIC
|
// Only the local APIC is emulated in kernel, both PICs and IOAPIC
|
||||||
// are not.
|
// are not.
|
||||||
@@ -309,6 +371,18 @@ impl vm::Vm for KvmVm {
|
|||||||
.map_err(|e| vm::HypervisorVmError::EnableSplitIrq(e.into()))?;
|
.map_err(|e| vm::HypervisorVmError::EnableSplitIrq(e.into()))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
fn enable_sgx_attribute(&self, file: File) -> vm::Result<()> {
|
||||||
|
let mut cap = kvm_enable_cap {
|
||||||
|
cap: KVM_CAP_SGX_ATTRIBUTE,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
cap.args[0] = file.as_raw_fd() as u64;
|
||||||
|
self.fd
|
||||||
|
.enable_cap(&cap)
|
||||||
|
.map_err(|e| vm::HypervisorVmError::EnableSgxAttribute(e.into()))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
/// Retrieve guest clock.
|
/// Retrieve guest clock.
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn get_clock(&self) -> vm::Result<ClockData> {
|
fn get_clock(&self) -> vm::Result<ClockData> {
|
||||||
@@ -351,10 +425,58 @@ impl vm::Vm for KvmVm {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Start logging dirty pages
|
||||||
|
///
|
||||||
|
fn start_dirty_log(&self) -> vm::Result<()> {
|
||||||
|
let dirty_log_slots = self.dirty_log_slots.read().unwrap();
|
||||||
|
for (_, s) in dirty_log_slots.iter() {
|
||||||
|
let region = MemoryRegion {
|
||||||
|
slot: s.slot,
|
||||||
|
guest_phys_addr: s.guest_phys_addr,
|
||||||
|
memory_size: s.memory_size,
|
||||||
|
userspace_addr: s.userspace_addr,
|
||||||
|
flags: KVM_MEM_LOG_DIRTY_PAGES,
|
||||||
|
};
|
||||||
|
// SAFETY: Safe because guest regions are guaranteed not to overlap.
|
||||||
|
unsafe {
|
||||||
|
self.fd
|
||||||
|
.set_user_memory_region(region)
|
||||||
|
.map_err(|e| vm::HypervisorVmError::StartDirtyLog(e.into()))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Stop logging dirty pages
|
||||||
|
///
|
||||||
|
fn stop_dirty_log(&self) -> vm::Result<()> {
|
||||||
|
let dirty_log_slots = self.dirty_log_slots.read().unwrap();
|
||||||
|
for (_, s) in dirty_log_slots.iter() {
|
||||||
|
let region = MemoryRegion {
|
||||||
|
slot: s.slot,
|
||||||
|
guest_phys_addr: s.guest_phys_addr,
|
||||||
|
memory_size: s.memory_size,
|
||||||
|
userspace_addr: s.userspace_addr,
|
||||||
|
flags: 0,
|
||||||
|
};
|
||||||
|
// SAFETY: Safe because guest regions are guaranteed not to overlap.
|
||||||
|
unsafe {
|
||||||
|
self.fd
|
||||||
|
.set_user_memory_region(region)
|
||||||
|
.map_err(|e| vm::HypervisorVmError::StartDirtyLog(e.into()))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Get dirty pages bitmap (one bit per page)
|
/// Get dirty pages bitmap (one bit per page)
|
||||||
///
|
///
|
||||||
fn get_dirty_log(&self, slot: u32, memory_size: u64) -> vm::Result<Vec<u64>> {
|
fn get_dirty_log(&self, slot: u32, _base_gpa: u64, memory_size: u64) -> vm::Result<Vec<u64>> {
|
||||||
self.fd
|
self.fd
|
||||||
.get_dirty_log(slot, memory_size as usize)
|
.get_dirty_log(slot, memory_size as usize)
|
||||||
.map_err(|e| vm::HypervisorVmError::GetDirtyLog(e.into()))
|
.map_err(|e| vm::HypervisorVmError::GetDirtyLog(e.into()))
|
||||||
@@ -368,15 +490,23 @@ impl vm::Vm for KvmVm {
|
|||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct TdxInitVm {
|
struct TdxInitVm {
|
||||||
max_vcpus: u32,
|
max_vcpus: u32,
|
||||||
reserved: u32,
|
tsc_khz: u32,
|
||||||
attributes: u64,
|
attributes: u64,
|
||||||
cpuid: u64,
|
cpuid: u64,
|
||||||
|
mrconfigid: [u64; 6],
|
||||||
|
mrowner: [u64; 6],
|
||||||
|
mrownerconfig: [u64; 6],
|
||||||
|
reserved: [u64; 43],
|
||||||
}
|
}
|
||||||
let data = TdxInitVm {
|
let data = TdxInitVm {
|
||||||
max_vcpus,
|
max_vcpus,
|
||||||
reserved: 0,
|
tsc_khz: 0,
|
||||||
attributes: 1, // TDX1_TD_ATTRIBUTE_DEBUG,
|
attributes: 1, // TDX1_TD_ATTRIBUTE_DEBUG,
|
||||||
cpuid: cpuid.as_fam_struct_ptr() as u64,
|
cpuid: cpuid.as_fam_struct_ptr() as u64,
|
||||||
|
mrconfigid: [0; 6],
|
||||||
|
mrowner: [0; 6],
|
||||||
|
mrownerconfig: [0; 6],
|
||||||
|
reserved: [0; 43],
|
||||||
};
|
};
|
||||||
|
|
||||||
tdx_command(
|
tdx_command(
|
||||||
@@ -448,6 +578,7 @@ fn tdx_command(
|
|||||||
metadata,
|
metadata,
|
||||||
data,
|
data,
|
||||||
};
|
};
|
||||||
|
// SAFETY: FFI call. All input parameters are valid.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
ioctl_with_val(
|
ioctl_with_val(
|
||||||
fd,
|
fd,
|
||||||
@@ -539,6 +670,7 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
|||||||
fd: vm_fd,
|
fd: vm_fd,
|
||||||
msrs,
|
msrs,
|
||||||
state: VmState {},
|
state: VmState {},
|
||||||
|
dirty_log_slots: Arc::new(RwLock::new(HashMap::new())),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,6 +679,7 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
|||||||
Ok(Arc::new(KvmVm {
|
Ok(Arc::new(KvmVm {
|
||||||
fd: vm_fd,
|
fd: vm_fd,
|
||||||
state: VmState {},
|
state: VmState {},
|
||||||
|
dirty_log_slots: Arc::new(RwLock::new(HashMap::new())),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -611,7 +744,7 @@ pub struct KvmVcpu {
|
|||||||
fd: VcpuFd,
|
fd: VcpuFd,
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
msrs: MsrEntries,
|
msrs: MsrEntries,
|
||||||
vmmops: Option<Arc<Box<dyn vm::VmmOps>>>,
|
vmmops: Option<Arc<dyn vm::VmmOps>>,
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
hyperv_synic: AtomicBool,
|
hyperv_synic: AtomicBool,
|
||||||
}
|
}
|
||||||
@@ -801,7 +934,7 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
///
|
///
|
||||||
fn set_xcrs(&self, xcrs: &ExtendedControlRegisters) -> cpu::Result<()> {
|
fn set_xcrs(&self, xcrs: &ExtendedControlRegisters) -> cpu::Result<()> {
|
||||||
self.fd
|
self.fd
|
||||||
.set_xcrs(&xcrs)
|
.set_xcrs(xcrs)
|
||||||
.map_err(|e| cpu::HypervisorCpuError::SetXcsr(e.into()))
|
.map_err(|e| cpu::HypervisorCpuError::SetXcsr(e.into()))
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
@@ -1025,10 +1158,11 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
// https://elixir.free-electrons.com/linux/v4.9.62/source/arch/arm64/include/uapi/asm/kvm.h#L53
|
// https://elixir.free-electrons.com/linux/v4.9.62/source/arch/arm64/include/uapi/asm/kvm.h#L53
|
||||||
let mut off = offset__of!(kvm_regs, fp_regs) + offset__of!(user_fpsimd_state, vregs);
|
let mut off = offset__of!(kvm_regs, fp_regs) + offset__of!(user_fpsimd_state, vregs);
|
||||||
for i in 0..32 {
|
for i in 0..32 {
|
||||||
state.fp_regs.vregs[i][0] = self
|
state.fp_regs.vregs[i] = self
|
||||||
.fd
|
.fd
|
||||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U128, off))
|
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U128, off))
|
||||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?;
|
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?
|
||||||
|
.into();
|
||||||
off += mem::size_of::<u128>();
|
off += mem::size_of::<u128>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1105,7 +1239,7 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
self.fd
|
self.fd
|
||||||
.set_one_reg(
|
.set_one_reg(
|
||||||
arm64_core_reg_id!(KVM_REG_SIZE_U128, off),
|
arm64_core_reg_id!(KVM_REG_SIZE_U128, off),
|
||||||
state.fp_regs.vregs[i][0],
|
state.fp_regs.vregs[i] as u64,
|
||||||
)
|
)
|
||||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||||
off += mem::size_of::<u128>();
|
off += mem::size_of::<u128>();
|
||||||
@@ -1145,19 +1279,12 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
// call KVM_GET_ONE_REG on each id in order to save all of them. We carve out from the list
|
// call KVM_GET_ONE_REG on each id in order to save all of them. We carve out from the list
|
||||||
// the core registers which are represented in the kernel by kvm_regs structure and for which
|
// the core registers which are represented in the kernel by kvm_regs structure and for which
|
||||||
// we can calculate the id based on the offset in the structure.
|
// we can calculate the id based on the offset in the structure.
|
||||||
|
|
||||||
reg_list.retain(|regid| *regid != 0);
|
|
||||||
reg_list.as_slice().to_vec().sort_unstable();
|
|
||||||
|
|
||||||
reg_list.retain(|regid| is_system_register(*regid));
|
reg_list.retain(|regid| is_system_register(*regid));
|
||||||
|
|
||||||
// Now, for the rest of the registers left in the previously fetched register list, we are
|
// Now, for the rest of the registers left in the previously fetched register list, we are
|
||||||
// simply calling KVM_GET_ONE_REG.
|
// simply calling KVM_GET_ONE_REG.
|
||||||
let indices = reg_list.as_slice();
|
let indices = reg_list.as_slice();
|
||||||
for (_pos, index) in indices.iter().enumerate() {
|
for index in indices.iter() {
|
||||||
if _pos > 230 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
state.push(kvm_bindings::kvm_one_reg {
|
state.push(kvm_bindings::kvm_one_reg {
|
||||||
id: *index,
|
id: *index,
|
||||||
addr: self
|
addr: self
|
||||||
@@ -1252,8 +1379,8 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
0x40000020, 0x40000021, 0x40000080, 0x40000081, 0x40000082, 0x40000083, 0x40000084,
|
0x40000020, 0x40000021, 0x40000080, 0x40000081, 0x40000082, 0x40000083, 0x40000084,
|
||||||
0x40000090, 0x40000091, 0x40000092, 0x40000093, 0x40000094, 0x40000095, 0x40000096,
|
0x40000090, 0x40000091, 0x40000092, 0x40000093, 0x40000094, 0x40000095, 0x40000096,
|
||||||
0x40000097, 0x40000098, 0x40000099, 0x4000009a, 0x4000009b, 0x4000009c, 0x4000009d,
|
0x40000097, 0x40000098, 0x40000099, 0x4000009a, 0x4000009b, 0x4000009c, 0x4000009d,
|
||||||
0x4000009f, 0x400000b0, 0x400000b1, 0x400000b2, 0x400000b3, 0x400000b4, 0x400000b5,
|
0x4000009e, 0x4000009f, 0x400000b0, 0x400000b1, 0x400000b2, 0x400000b3, 0x400000b4,
|
||||||
0x400000b6, 0x400000b7,
|
0x400000b5, 0x400000b6, 0x400000b7,
|
||||||
];
|
];
|
||||||
for index in hyperv_synic_msrs {
|
for index in hyperv_synic_msrs {
|
||||||
let msr = kvm_msr_entry {
|
let msr = kvm_msr_entry {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
use crate::arch::x86::{msr_index, SegmentRegisterOps, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
use crate::arch::x86::{msr_index, SegmentRegisterOps, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
||||||
use crate::kvm::{Cap, Kvm, KvmError, KvmResult};
|
use crate::kvm::{Cap, Kvm, KvmError, KvmResult};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use vm_memory::GuestAddress;
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
|
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
|
||||||
@@ -92,8 +91,6 @@ impl SegmentRegisterOps for SegmentRegister {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const KVM_TSS_ADDRESS: GuestAddress = GuestAddress(0xfffb_d000);
|
|
||||||
|
|
||||||
pub fn boot_msr_entries() -> MsrEntries {
|
pub fn boot_msr_entries() -> MsrEntries {
|
||||||
MsrEntries::from_entries(&[
|
MsrEntries::from_entries(&[
|
||||||
msr!(msr_index::MSR_IA32_SYSENTER_CS),
|
msr!(msr_index::MSR_IA32_SYSENTER_CS),
|
||||||
@@ -127,6 +124,12 @@ pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
|
|||||||
if !kvm.check_extension(Cap::SplitIrqchip) {
|
if !kvm.check_extension(Cap::SplitIrqchip) {
|
||||||
return Err(KvmError::CapabilityMissing(Cap::SplitIrqchip));
|
return Err(KvmError::CapabilityMissing(Cap::SplitIrqchip));
|
||||||
}
|
}
|
||||||
|
if !kvm.check_extension(Cap::SetIdentityMapAddr) {
|
||||||
|
return Err(KvmError::CapabilityMissing(Cap::SetIdentityMapAddr));
|
||||||
|
}
|
||||||
|
if !kvm.check_extension(Cap::SetTssAddr) {
|
||||||
|
return Err(KvmError::CapabilityMissing(Cap::SetTssAddr));
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ pub use mshv_bindings::*;
|
|||||||
pub use mshv_ioctls::IoEventAddress;
|
pub use mshv_ioctls::IoEventAddress;
|
||||||
use mshv_ioctls::{set_registers_64, Mshv, NoDatamatch, VcpuFd, VmFd};
|
use mshv_ioctls::{set_registers_64, Mshv, NoDatamatch, VcpuFd, VmFd};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::sync::Arc;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
use vm::DataMatch;
|
use vm::DataMatch;
|
||||||
// x86_64 dependencies
|
// x86_64 dependencies
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
@@ -28,8 +29,21 @@ pub use x86_64::VcpuMshvState as CpuState;
|
|||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub use x86_64::*;
|
pub use x86_64::*;
|
||||||
|
|
||||||
use std::os::unix::io::AsRawFd;
|
#[cfg(target_arch = "x86_64")]
|
||||||
use std::sync::RwLock;
|
use std::fs::File;
|
||||||
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
|
|
||||||
|
const DIRTY_BITMAP_CLEAR_DIRTY: u64 = 0x4;
|
||||||
|
const DIRTY_BITMAP_SET_DIRTY: u64 = 0x8;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Export generically-named wrappers of mshv-bindings for Unix-based platforms
|
||||||
|
///
|
||||||
|
pub use {
|
||||||
|
mshv_bindings::mshv_create_device as CreateDevice,
|
||||||
|
mshv_bindings::mshv_device_attr as DeviceAttr,
|
||||||
|
mshv_bindings::mshv_msi_routing_entry as IrqRoutingEntry, mshv_ioctls::DeviceFd,
|
||||||
|
};
|
||||||
|
|
||||||
pub const PAGE_SHIFT: usize = 12;
|
pub const PAGE_SHIFT: usize = 12;
|
||||||
|
|
||||||
@@ -40,6 +54,11 @@ pub struct HvState {
|
|||||||
|
|
||||||
pub use HvState as VmState;
|
pub use HvState as VmState;
|
||||||
|
|
||||||
|
struct MshvDirtyLogSlot {
|
||||||
|
guest_pfn: u64,
|
||||||
|
memory_size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
/// Wrapper over mshv system ioctls.
|
/// Wrapper over mshv system ioctls.
|
||||||
pub struct MshvHypervisor {
|
pub struct MshvHypervisor {
|
||||||
mshv: Mshv,
|
mshv: Mshv,
|
||||||
@@ -104,6 +123,7 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
|||||||
msrs,
|
msrs,
|
||||||
hv_state: hv_state_init(),
|
hv_state: hv_state_init(),
|
||||||
vmmops: None,
|
vmmops: None,
|
||||||
|
dirty_log_slots: Arc::new(RwLock::new(HashMap::new())),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
@@ -131,7 +151,7 @@ pub struct MshvVcpu {
|
|||||||
cpuid: CpuId,
|
cpuid: CpuId,
|
||||||
msrs: MsrEntries,
|
msrs: MsrEntries,
|
||||||
hv_state: Arc<RwLock<HvState>>, // Mshv State
|
hv_state: Arc<RwLock<HvState>>, // Mshv State
|
||||||
vmmops: Option<Arc<Box<dyn vm::VmmOps>>>,
|
vmmops: Option<Arc<dyn vm::VmmOps>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of Vcpu trait for Microsoft Hypervisor
|
/// Implementation of Vcpu trait for Microsoft Hypervisor
|
||||||
@@ -235,7 +255,7 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
///
|
///
|
||||||
fn set_xcrs(&self, xcrs: &ExtendedControlRegisters) -> cpu::Result<()> {
|
fn set_xcrs(&self, xcrs: &ExtendedControlRegisters) -> cpu::Result<()> {
|
||||||
self.fd
|
self.fd
|
||||||
.set_xcrs(&xcrs)
|
.set_xcrs(xcrs)
|
||||||
.map_err(|e| cpu::HypervisorCpuError::SetXcsr(e.into()))
|
.map_err(|e| cpu::HypervisorCpuError::SetXcsr(e.into()))
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
@@ -268,9 +288,7 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
}
|
}
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
fn run(&self) -> std::result::Result<cpu::VmExit, cpu::HypervisorCpuError> {
|
fn run(&self) -> std::result::Result<cpu::VmExit, cpu::HypervisorCpuError> {
|
||||||
// Safe because this is just only done during initialization.
|
let hv_message: hv_message = hv_message::default();
|
||||||
// TODO don't zero it everytime we enter this function.
|
|
||||||
let hv_message: hv_message = unsafe { std::mem::zeroed() };
|
|
||||||
match self.fd.run(hv_message) {
|
match self.fd.run(hv_message) {
|
||||||
Ok(x) => match x.header.message_type {
|
Ok(x) => match x.header.message_type {
|
||||||
hv_message_type_HVMSG_X64_HALT => {
|
hv_message_type_HVMSG_X64_HALT => {
|
||||||
@@ -284,6 +302,7 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT => {
|
hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT => {
|
||||||
let info = x.to_ioport_info().unwrap();
|
let info = x.to_ioport_info().unwrap();
|
||||||
let access_info = info.access_info;
|
let access_info = info.access_info;
|
||||||
|
// SAFETY: access_info is valid, otherwise we won't be here
|
||||||
let len = unsafe { access_info.__bindgen_anon_1.access_size() } as usize;
|
let len = unsafe { access_info.__bindgen_anon_1.access_size() } as usize;
|
||||||
let is_write = info.header.intercept_access_type == 1;
|
let is_write = info.header.intercept_access_type == 1;
|
||||||
let port = info.port_number;
|
let port = info.port_number;
|
||||||
@@ -324,12 +343,15 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if unsafe { access_info.__bindgen_anon_1.string_op() } == 1 {
|
// SAFETY: access_info is valid, otherwise we won't be here
|
||||||
panic!("String IN/OUT not supported");
|
assert!(
|
||||||
}
|
!(unsafe { access_info.__bindgen_anon_1.string_op() } == 1),
|
||||||
if unsafe { access_info.__bindgen_anon_1.rep_prefix() } == 1 {
|
"String IN/OUT not supported"
|
||||||
panic!("Rep IN/OUT not supported");
|
);
|
||||||
}
|
assert!(
|
||||||
|
!(unsafe { access_info.__bindgen_anon_1.rep_prefix() } == 1),
|
||||||
|
"Rep IN/OUT not supported"
|
||||||
|
);
|
||||||
|
|
||||||
if is_write {
|
if is_write {
|
||||||
let data = (info.rax as u32).to_le_bytes();
|
let data = (info.rax as u32).to_le_bytes();
|
||||||
@@ -393,22 +415,22 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
}
|
}
|
||||||
hv_message_type_HVMSG_X64_CPUID_INTERCEPT => {
|
hv_message_type_HVMSG_X64_CPUID_INTERCEPT => {
|
||||||
let info = x.to_cpuid_info().unwrap();
|
let info = x.to_cpuid_info().unwrap();
|
||||||
debug!("cpuid eax: {:x}", info.rax);
|
debug!("cpuid eax: {:x}", { info.rax });
|
||||||
Ok(cpu::VmExit::Ignore)
|
Ok(cpu::VmExit::Ignore)
|
||||||
}
|
}
|
||||||
hv_message_type_HVMSG_X64_MSR_INTERCEPT => {
|
hv_message_type_HVMSG_X64_MSR_INTERCEPT => {
|
||||||
let info = x.to_msr_info().unwrap();
|
let info = x.to_msr_info().unwrap();
|
||||||
if info.header.intercept_access_type == 0 {
|
if info.header.intercept_access_type == 0 {
|
||||||
debug!("msr read: {:x}", info.msr_number);
|
debug!("msr read: {:x}", { info.msr_number });
|
||||||
} else {
|
} else {
|
||||||
debug!("msr write: {:x}", info.msr_number);
|
debug!("msr write: {:x}", { info.msr_number });
|
||||||
}
|
}
|
||||||
Ok(cpu::VmExit::Ignore)
|
Ok(cpu::VmExit::Ignore)
|
||||||
}
|
}
|
||||||
hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT => {
|
hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT => {
|
||||||
//TODO: Handler for VMCALL here.
|
//TODO: Handler for VMCALL here.
|
||||||
let info = x.to_exception_info().unwrap();
|
let info = x.to_exception_info().unwrap();
|
||||||
debug!("Exception Info {:?}", info.exception_vector);
|
debug!("Exception Info {:?}", { info.exception_vector });
|
||||||
Ok(cpu::VmExit::Ignore)
|
Ok(cpu::VmExit::Ignore)
|
||||||
}
|
}
|
||||||
exit => Err(cpu::HypervisorCpuError::RunVcpu(anyhow!(
|
exit => Err(cpu::HypervisorCpuError::RunVcpu(anyhow!(
|
||||||
@@ -488,6 +510,13 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
self.set_xcrs(&state.xcrs)?;
|
self.set_xcrs(&state.xcrs)?;
|
||||||
self.set_lapic(&state.lapic)?;
|
self.set_lapic(&state.lapic)?;
|
||||||
self.set_xsave(&state.xsave)?;
|
self.set_xsave(&state.xsave)?;
|
||||||
|
// These registers are global and needed to be set only for first VCPU
|
||||||
|
// as Microsoft Hypervisor allows setting this regsier for only one VCPU
|
||||||
|
if self.vp_index == 0 {
|
||||||
|
self.fd
|
||||||
|
.set_misc_regs(&state.misc)
|
||||||
|
.map_err(|e| cpu::HypervisorCpuError::SetMiscRegs(e.into()))?
|
||||||
|
}
|
||||||
self.fd
|
self.fd
|
||||||
.set_debug_regs(&state.dbg)
|
.set_debug_regs(&state.dbg)
|
||||||
.map_err(|e| cpu::HypervisorCpuError::SetDebugRegs(e.into()))?;
|
.map_err(|e| cpu::HypervisorCpuError::SetDebugRegs(e.into()))?;
|
||||||
@@ -506,10 +535,15 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
self.get_msrs(&mut msrs)?;
|
self.get_msrs(&mut msrs)?;
|
||||||
let lapic = self.get_lapic()?;
|
let lapic = self.get_lapic()?;
|
||||||
let xsave = self.get_xsave()?;
|
let xsave = self.get_xsave()?;
|
||||||
|
let misc = self
|
||||||
|
.fd
|
||||||
|
.get_misc_regs()
|
||||||
|
.map_err(|e| cpu::HypervisorCpuError::GetMiscRegs(e.into()))?;
|
||||||
let dbg = self
|
let dbg = self
|
||||||
.fd
|
.fd
|
||||||
.get_debug_regs()
|
.get_debug_regs()
|
||||||
.map_err(|e| cpu::HypervisorCpuError::GetDebugRegs(e.into()))?;
|
.map_err(|e| cpu::HypervisorCpuError::GetDebugRegs(e.into()))?;
|
||||||
|
|
||||||
Ok(CpuState {
|
Ok(CpuState {
|
||||||
msrs,
|
msrs,
|
||||||
vcpu_events,
|
vcpu_events,
|
||||||
@@ -520,6 +554,7 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
lapic,
|
lapic,
|
||||||
dbg,
|
dbg,
|
||||||
xsave,
|
xsave,
|
||||||
|
misc,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
@@ -534,6 +569,45 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
|
|
||||||
Ok(r)
|
Ok(r)
|
||||||
}
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
///
|
||||||
|
/// X86 specific call that returns the vcpu's current "suspend registers".
|
||||||
|
///
|
||||||
|
fn get_suspend_regs(&self) -> cpu::Result<SuspendRegisters> {
|
||||||
|
self.fd
|
||||||
|
.get_suspend_regs()
|
||||||
|
.map_err(|e| cpu::HypervisorCpuError::GetSuspendRegs(e.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Device struct for MSHV
|
||||||
|
pub struct MshvDevice {
|
||||||
|
fd: DeviceFd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl device::Device for MshvDevice {
|
||||||
|
///
|
||||||
|
/// Set device attribute
|
||||||
|
///
|
||||||
|
fn set_device_attr(&self, attr: &DeviceAttr) -> device::Result<()> {
|
||||||
|
self.fd
|
||||||
|
.set_device_attr(attr)
|
||||||
|
.map_err(|e| device::HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||||
|
}
|
||||||
|
///
|
||||||
|
/// Get device attribute
|
||||||
|
///
|
||||||
|
fn get_device_attr(&self, attr: &mut DeviceAttr) -> device::Result<()> {
|
||||||
|
self.fd
|
||||||
|
.get_device_attr(attr)
|
||||||
|
.map_err(|e| device::HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for MshvDevice {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.fd.as_raw_fd()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MshvEmulatorContext<'a> {
|
struct MshvEmulatorContext<'a> {
|
||||||
@@ -557,9 +631,10 @@ impl<'a> MshvEmulatorContext<'a> {
|
|||||||
.translate_gva(gva, flags.into())
|
.translate_gva(gva, flags.into())
|
||||||
.map_err(|e| PlatformError::TranslateVirtualAddress(anyhow!(e)))?;
|
.map_err(|e| PlatformError::TranslateVirtualAddress(anyhow!(e)))?;
|
||||||
|
|
||||||
|
// SAFETY: r is valid, otherwise this function will have returned
|
||||||
let result_code = unsafe { r.1.__bindgen_anon_1.result_code };
|
let result_code = unsafe { r.1.__bindgen_anon_1.result_code };
|
||||||
match result_code {
|
match result_code {
|
||||||
hv_translate_gva_result_code_HvTranslateGvaSuccess => Ok(r.0),
|
hv_translate_gva_result_code_HV_TRANSLATE_GVA_SUCCESS => Ok(r.0),
|
||||||
_ => Err(PlatformError::TranslateVirtualAddress(anyhow!(result_code))),
|
_ => Err(PlatformError::TranslateVirtualAddress(anyhow!(result_code))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -669,7 +744,8 @@ pub struct MshvVm {
|
|||||||
msrs: MsrEntries,
|
msrs: MsrEntries,
|
||||||
// Hypervisor State
|
// Hypervisor State
|
||||||
hv_state: Arc<RwLock<HvState>>,
|
hv_state: Arc<RwLock<HvState>>,
|
||||||
vmmops: Option<Arc<Box<dyn vm::VmmOps>>>,
|
vmmops: Option<Arc<dyn vm::VmmOps>>,
|
||||||
|
dirty_log_slots: Arc<RwLock<HashMap<u64, MshvDirtyLogSlot>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hv_state_init() -> Arc<RwLock<HvState>> {
|
fn hv_state_init() -> Arc<RwLock<HvState>> {
|
||||||
@@ -688,6 +764,13 @@ fn hv_state_init() -> Arc<RwLock<HvState>> {
|
|||||||
/// vm.set/get().unwrap()
|
/// vm.set/get().unwrap()
|
||||||
///
|
///
|
||||||
impl vm::Vm for MshvVm {
|
impl vm::Vm for MshvVm {
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
///
|
||||||
|
/// Sets the address of the one-page region in the VM's address space.
|
||||||
|
///
|
||||||
|
fn set_identity_map_address(&self, _address: u64) -> vm::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
/// Sets the address of the three-page region in the VM's address space.
|
/// Sets the address of the three-page region in the VM's address space.
|
||||||
@@ -708,7 +791,7 @@ impl vm::Vm for MshvVm {
|
|||||||
debug!("register_irqfd fd {} gsi {}", fd.as_raw_fd(), gsi);
|
debug!("register_irqfd fd {} gsi {}", fd.as_raw_fd(), gsi);
|
||||||
|
|
||||||
self.fd
|
self.fd
|
||||||
.register_irqfd(&fd, gsi)
|
.register_irqfd(fd, gsi)
|
||||||
.map_err(|e| vm::HypervisorVmError::RegisterIrqFd(e.into()))?;
|
.map_err(|e| vm::HypervisorVmError::RegisterIrqFd(e.into()))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -720,7 +803,7 @@ impl vm::Vm for MshvVm {
|
|||||||
debug!("unregister_irqfd fd {} gsi {}", fd.as_raw_fd(), gsi);
|
debug!("unregister_irqfd fd {} gsi {}", fd.as_raw_fd(), gsi);
|
||||||
|
|
||||||
self.fd
|
self.fd
|
||||||
.unregister_irqfd(&fd, gsi)
|
.unregister_irqfd(fd, gsi)
|
||||||
.map_err(|e| vm::HypervisorVmError::UnregisterIrqFd(e.into()))?;
|
.map_err(|e| vm::HypervisorVmError::UnregisterIrqFd(e.into()))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -731,7 +814,7 @@ impl vm::Vm for MshvVm {
|
|||||||
fn create_vcpu(
|
fn create_vcpu(
|
||||||
&self,
|
&self,
|
||||||
id: u8,
|
id: u8,
|
||||||
vmmops: Option<Arc<Box<dyn VmmOps>>>,
|
vmmops: Option<Arc<dyn VmmOps>>,
|
||||||
) -> vm::Result<Arc<dyn cpu::Vcpu>> {
|
) -> vm::Result<Arc<dyn cpu::Vcpu>> {
|
||||||
let vcpu_fd = self
|
let vcpu_fd = self
|
||||||
.fd
|
.fd
|
||||||
@@ -751,6 +834,10 @@ impl vm::Vm for MshvVm {
|
|||||||
fn enable_split_irq(&self) -> vm::Result<()> {
|
fn enable_split_irq(&self) -> vm::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
fn enable_sgx_attribute(&self, _file: File) -> vm::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
fn register_ioevent(
|
fn register_ioevent(
|
||||||
&self,
|
&self,
|
||||||
fd: &EventFd,
|
fd: &EventFd,
|
||||||
@@ -789,11 +876,36 @@ impl vm::Vm for MshvVm {
|
|||||||
.map_err(|e| vm::HypervisorVmError::UnregisterIoEvent(e.into()))
|
.map_err(|e| vm::HypervisorVmError::UnregisterIoEvent(e.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates/modifies a guest physical memory slot.
|
/// Creates a guest physical memory region.
|
||||||
fn set_user_memory_region(&self, user_memory_region: MemoryRegion) -> vm::Result<()> {
|
fn create_user_memory_region(&self, user_memory_region: MemoryRegion) -> vm::Result<()> {
|
||||||
|
// No matter read only or not we keep track the slots.
|
||||||
|
// For readonly hypervisor can enable the dirty bits,
|
||||||
|
// but a VM exit happens before setting the dirty bits
|
||||||
|
self.dirty_log_slots.write().unwrap().insert(
|
||||||
|
user_memory_region.guest_pfn,
|
||||||
|
MshvDirtyLogSlot {
|
||||||
|
guest_pfn: user_memory_region.guest_pfn,
|
||||||
|
memory_size: user_memory_region.size,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
self.fd
|
self.fd
|
||||||
.map_user_memory(user_memory_region)
|
.map_user_memory(user_memory_region)
|
||||||
.map_err(|e| vm::HypervisorVmError::SetUserMemory(e.into()))?;
|
.map_err(|e| vm::HypervisorVmError::CreateUserMemory(e.into()))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a guest physical memory region.
|
||||||
|
fn remove_user_memory_region(&self, user_memory_region: MemoryRegion) -> vm::Result<()> {
|
||||||
|
// Remove the corresponding entry from "self.dirty_log_slots" if needed
|
||||||
|
self.dirty_log_slots
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.remove(&user_memory_region.guest_pfn);
|
||||||
|
|
||||||
|
self.fd
|
||||||
|
.unmap_user_memory(user_memory_region)
|
||||||
|
.map_err(|e| vm::HypervisorVmError::RemoveUserMemory(e.into()))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -819,10 +931,28 @@ impl vm::Vm for MshvVm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Creates an in-kernel device.
|
||||||
|
///
|
||||||
|
/// See the documentation for `MSHV_CREATE_DEVICE`.
|
||||||
|
fn create_device(&self, device: &mut CreateDevice) -> vm::Result<Arc<dyn device::Device>> {
|
||||||
|
let fd = self
|
||||||
|
.fd
|
||||||
|
.create_device(device)
|
||||||
|
.map_err(|e| vm::HypervisorVmError::CreateDevice(e.into()))?;
|
||||||
|
let device = MshvDevice { fd };
|
||||||
|
Ok(Arc::new(device))
|
||||||
|
}
|
||||||
|
|
||||||
fn create_passthrough_device(&self) -> vm::Result<Arc<dyn device::Device>> {
|
fn create_passthrough_device(&self) -> vm::Result<Arc<dyn device::Device>> {
|
||||||
Err(vm::HypervisorVmError::CreatePassthroughDevice(anyhow!(
|
let mut vfio_dev = mshv_create_device {
|
||||||
"No passthrough support"
|
type_: mshv_device_type_MSHV_DEV_TYPE_VFIO,
|
||||||
)))
|
fd: 0,
|
||||||
|
flags: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.create_device(&mut vfio_dev)
|
||||||
|
.map_err(|e| vm::HypervisorVmError::CreatePassthroughDevice(e.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> vm::Result<()> {
|
fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> vm::Result<()> {
|
||||||
@@ -830,10 +960,13 @@ impl vm::Vm for MshvVm {
|
|||||||
vec_with_array_field::<mshv_msi_routing, mshv_msi_routing_entry>(entries.len());
|
vec_with_array_field::<mshv_msi_routing, mshv_msi_routing_entry>(entries.len());
|
||||||
msi_routing[0].nr = entries.len() as u32;
|
msi_routing[0].nr = entries.len() as u32;
|
||||||
|
|
||||||
|
// SAFETY: msi_routing initialized with entries.len() and now it is being turned into
|
||||||
|
// entries_slice with entries.len() again. It is guaranteed to be large enough to hold
|
||||||
|
// everything from entries.
|
||||||
unsafe {
|
unsafe {
|
||||||
let entries_slice: &mut [mshv_msi_routing_entry] =
|
let entries_slice: &mut [mshv_msi_routing_entry] =
|
||||||
msi_routing[0].entries.as_mut_slice(entries.len());
|
msi_routing[0].entries.as_mut_slice(entries.len());
|
||||||
entries_slice.copy_from_slice(&entries);
|
entries_slice.copy_from_slice(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.fd
|
self.fd
|
||||||
@@ -854,16 +987,44 @@ impl vm::Vm for MshvVm {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
|
/// Start logging dirty pages
|
||||||
|
///
|
||||||
|
fn start_dirty_log(&self) -> vm::Result<()> {
|
||||||
|
self.fd
|
||||||
|
.enable_dirty_page_tracking()
|
||||||
|
.map_err(|e| vm::HypervisorVmError::StartDirtyLog(e.into()))
|
||||||
|
}
|
||||||
|
///
|
||||||
|
/// Stop logging dirty pages
|
||||||
|
///
|
||||||
|
fn stop_dirty_log(&self) -> vm::Result<()> {
|
||||||
|
let dirty_log_slots = self.dirty_log_slots.read().unwrap();
|
||||||
|
// Before disabling the dirty page tracking we need
|
||||||
|
// to set the dirty bits in the Hypervisor
|
||||||
|
// This is a requirement from Microsoft Hypervisor
|
||||||
|
for (_, s) in dirty_log_slots.iter() {
|
||||||
|
self.fd
|
||||||
|
.get_dirty_log(s.guest_pfn, s.memory_size as usize, DIRTY_BITMAP_SET_DIRTY)
|
||||||
|
.map_err(|e| vm::HypervisorVmError::StartDirtyLog(e.into()))?;
|
||||||
|
}
|
||||||
|
self.fd
|
||||||
|
.disable_dirty_page_tracking()
|
||||||
|
.map_err(|e| vm::HypervisorVmError::StartDirtyLog(e.into()))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
///
|
||||||
/// Get dirty pages bitmap (one bit per page)
|
/// Get dirty pages bitmap (one bit per page)
|
||||||
///
|
///
|
||||||
fn get_dirty_log(&self, _slot: u32, _memory_size: u64) -> vm::Result<Vec<u64>> {
|
fn get_dirty_log(&self, _slot: u32, base_gpa: u64, memory_size: u64) -> vm::Result<Vec<u64>> {
|
||||||
Err(vm::HypervisorVmError::GetDirtyLog(anyhow!(
|
self.fd
|
||||||
"get_dirty_log not implemented"
|
.get_dirty_log(
|
||||||
)))
|
base_gpa >> PAGE_SHIFT,
|
||||||
|
memory_size as usize,
|
||||||
|
DIRTY_BITMAP_CLEAR_DIRTY,
|
||||||
|
)
|
||||||
|
.map_err(|e| vm::HypervisorVmError::GetDirtyLog(e.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub use hv_cpuid_entry as CpuIdEntry;
|
pub use hv_cpuid_entry as CpuIdEntry;
|
||||||
|
|
||||||
pub type IrqRoutingEntry = mshv_msi_routing_entry;
|
|
||||||
|
|
||||||
pub const CPUID_FLAG_VALID_INDEX: u32 = 0;
|
pub const CPUID_FLAG_VALID_INDEX: u32 = 0;
|
||||||
|
|||||||
@@ -7,9 +7,10 @@
|
|||||||
// Copyright 2018-2019 CrowdStrike, Inc.
|
// Copyright 2018-2019 CrowdStrike, Inc.
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
use crate::arch::x86::{msr_index, SegmentRegisterOps, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
||||||
use crate::arch::x86::{msr_index, SegmentRegisterOps};
|
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Export generically-named wrappers of mshv_bindings for Unix-based platforms
|
/// Export generically-named wrappers of mshv_bindings for Unix-based platforms
|
||||||
///
|
///
|
||||||
@@ -17,9 +18,10 @@ pub use {
|
|||||||
mshv_bindings::mshv_user_mem_region as MemoryRegion, mshv_bindings::msr_entry as MsrEntry,
|
mshv_bindings::mshv_user_mem_region as MemoryRegion, mshv_bindings::msr_entry as MsrEntry,
|
||||||
mshv_bindings::CpuId, mshv_bindings::DebugRegisters,
|
mshv_bindings::CpuId, mshv_bindings::DebugRegisters,
|
||||||
mshv_bindings::FloatingPointUnit as FpuState, mshv_bindings::LapicState,
|
mshv_bindings::FloatingPointUnit as FpuState, mshv_bindings::LapicState,
|
||||||
mshv_bindings::MsrList, mshv_bindings::Msrs as MsrEntries, mshv_bindings::Msrs,
|
mshv_bindings::MiscRegs as MiscRegisters, mshv_bindings::MsrList,
|
||||||
mshv_bindings::SegmentRegister, mshv_bindings::SpecialRegisters,
|
mshv_bindings::Msrs as MsrEntries, mshv_bindings::Msrs, mshv_bindings::SegmentRegister,
|
||||||
mshv_bindings::StandardRegisters, mshv_bindings::VcpuEvents, mshv_bindings::XSave as Xsave,
|
mshv_bindings::SpecialRegisters, mshv_bindings::StandardRegisters,
|
||||||
|
mshv_bindings::SuspendRegisters, mshv_bindings::VcpuEvents, mshv_bindings::XSave as Xsave,
|
||||||
mshv_bindings::Xcrs as ExtendedControlRegisters,
|
mshv_bindings::Xcrs as ExtendedControlRegisters,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -34,10 +36,33 @@ pub struct VcpuMshvState {
|
|||||||
pub lapic: LapicState,
|
pub lapic: LapicState,
|
||||||
pub dbg: DebugRegisters,
|
pub dbg: DebugRegisters,
|
||||||
pub xsave: Xsave,
|
pub xsave: Xsave,
|
||||||
|
pub misc: MiscRegisters,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for VcpuMshvState {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let expected_num_msrs = self.msrs.as_fam_struct_ref().nmsrs as usize;
|
||||||
|
let mut msr_entries = vec![vec![0; 2]; expected_num_msrs];
|
||||||
|
|
||||||
|
for (i, entry) in self.msrs.as_slice().iter().enumerate() {
|
||||||
|
msr_entries[i][1] = entry.data;
|
||||||
|
msr_entries[i][0] = entry.index as u64;
|
||||||
|
}
|
||||||
|
write!(f, "Number of MSRs: {}: MSRs: {:#010X?}, -- VCPU Events: {:?} -- Standard registers: {:?} Special Registers: {:?} ---- Floating Point Unit: {:?} --- Extended Control Register: {:?} --- Local APIC: {:?} --- DBG: {:?} --- Xsave: {:?}",
|
||||||
|
msr_entries.len(),
|
||||||
|
msr_entries,
|
||||||
|
self.vcpu_events,
|
||||||
|
self.regs,
|
||||||
|
self.sregs,
|
||||||
|
self.fpu,
|
||||||
|
self.xcrs,
|
||||||
|
self.lapic,
|
||||||
|
self.dbg,
|
||||||
|
self.xsave,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CreateDevice {}
|
|
||||||
pub struct DeviceAttr {}
|
|
||||||
pub struct IrqRouting {}
|
pub struct IrqRouting {}
|
||||||
pub enum VcpuExit {}
|
pub enum VcpuExit {}
|
||||||
pub struct MpState {}
|
pub struct MpState {}
|
||||||
@@ -118,6 +143,7 @@ pub fn boot_msr_entries() -> MsrEntries {
|
|||||||
msr!(msr_index::MSR_KERNEL_GS_BASE),
|
msr!(msr_index::MSR_KERNEL_GS_BASE),
|
||||||
msr!(msr_index::MSR_SYSCALL_MASK),
|
msr!(msr_index::MSR_SYSCALL_MASK),
|
||||||
msr!(msr_index::MSR_IA32_TSC),
|
msr!(msr_index::MSR_IA32_TSC),
|
||||||
|
msr_data!(msr_index::MSR_MTRRdefType, MTRR_ENABLE | MTRR_MEM_TYPE_WB),
|
||||||
])
|
])
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ use crate::device::Device;
|
|||||||
use crate::x86_64::CpuId;
|
use crate::x86_64::CpuId;
|
||||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||||
use crate::ClockData;
|
use crate::ClockData;
|
||||||
#[cfg(feature = "kvm")]
|
|
||||||
use crate::CreateDevice;
|
use crate::CreateDevice;
|
||||||
#[cfg(feature = "mshv")]
|
#[cfg(feature = "mshv")]
|
||||||
use crate::HvState as VmState;
|
use crate::HvState as VmState;
|
||||||
@@ -25,6 +24,8 @@ use crate::KvmVmState as VmState;
|
|||||||
use crate::{IoEventAddress, IrqRoutingEntry, MemoryRegion};
|
use crate::{IoEventAddress, IrqRoutingEntry, MemoryRegion};
|
||||||
#[cfg(feature = "kvm")]
|
#[cfg(feature = "kvm")]
|
||||||
use kvm_ioctls::Cap;
|
use kvm_ioctls::Cap;
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
use std::fs::File;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
@@ -57,6 +58,11 @@ pub enum HypervisorVmError {
|
|||||||
#[error("Failed to create Vcpu: {0}")]
|
#[error("Failed to create Vcpu: {0}")]
|
||||||
CreateVcpu(#[source] anyhow::Error),
|
CreateVcpu(#[source] anyhow::Error),
|
||||||
///
|
///
|
||||||
|
/// Identity map address error
|
||||||
|
///
|
||||||
|
#[error("Failed to set identity map address: {0}")]
|
||||||
|
SetIdentityMapAddress(#[source] anyhow::Error),
|
||||||
|
///
|
||||||
/// TSS address error
|
/// TSS address error
|
||||||
///
|
///
|
||||||
#[error("Failed to set TSS address: {0}")]
|
#[error("Failed to set TSS address: {0}")]
|
||||||
@@ -92,10 +98,15 @@ pub enum HypervisorVmError {
|
|||||||
#[error("Failed to set GSI routing: {0}")]
|
#[error("Failed to set GSI routing: {0}")]
|
||||||
SetGsiRouting(#[source] anyhow::Error),
|
SetGsiRouting(#[source] anyhow::Error),
|
||||||
///
|
///
|
||||||
/// Set user memory error
|
/// Create user memory error
|
||||||
///
|
///
|
||||||
#[error("Failed to set user memory: {0}")]
|
#[error("Failed to create user memory: {0}")]
|
||||||
SetUserMemory(#[source] anyhow::Error),
|
CreateUserMemory(#[source] anyhow::Error),
|
||||||
|
///
|
||||||
|
/// Remove user memory region error
|
||||||
|
///
|
||||||
|
#[error("Failed to remove user memory: {0}")]
|
||||||
|
RemoveUserMemory(#[source] anyhow::Error),
|
||||||
///
|
///
|
||||||
/// Create device error
|
/// Create device error
|
||||||
///
|
///
|
||||||
@@ -112,6 +123,11 @@ pub enum HypervisorVmError {
|
|||||||
#[error("Failed to enable split Irq: {0}")]
|
#[error("Failed to enable split Irq: {0}")]
|
||||||
EnableSplitIrq(#[source] anyhow::Error),
|
EnableSplitIrq(#[source] anyhow::Error),
|
||||||
///
|
///
|
||||||
|
/// Enable SGX attribute error
|
||||||
|
///
|
||||||
|
#[error("Failed to enable SGX attribute: {0}")]
|
||||||
|
EnableSgxAttribute(#[source] anyhow::Error),
|
||||||
|
///
|
||||||
/// Get clock error
|
/// Get clock error
|
||||||
///
|
///
|
||||||
#[error("Failed to get clock: {0}")]
|
#[error("Failed to get clock: {0}")]
|
||||||
@@ -156,6 +172,16 @@ pub enum HypervisorVmError {
|
|||||||
#[error("Failed to write to IO Bus: {0}")]
|
#[error("Failed to write to IO Bus: {0}")]
|
||||||
IoBusWrite(#[source] anyhow::Error),
|
IoBusWrite(#[source] anyhow::Error),
|
||||||
///
|
///
|
||||||
|
/// Start dirty log error
|
||||||
|
///
|
||||||
|
#[error("Failed to get dirty log: {0}")]
|
||||||
|
StartDirtyLog(#[source] anyhow::Error),
|
||||||
|
///
|
||||||
|
/// Stop dirty log error
|
||||||
|
///
|
||||||
|
#[error("Failed to get dirty log: {0}")]
|
||||||
|
StopDirtyLog(#[source] anyhow::Error),
|
||||||
|
///
|
||||||
/// Get dirty log error
|
/// Get dirty log error
|
||||||
///
|
///
|
||||||
#[error("Failed to get dirty log: {0}")]
|
#[error("Failed to get dirty log: {0}")]
|
||||||
@@ -196,6 +222,9 @@ pub type Result<T> = std::result::Result<T, HypervisorVmError>;
|
|||||||
/// This crate provides a hypervisor-agnostic interfaces for Vm
|
/// This crate provides a hypervisor-agnostic interfaces for Vm
|
||||||
///
|
///
|
||||||
pub trait Vm: Send + Sync {
|
pub trait Vm: Send + Sync {
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
/// Sets the address of the one-page region in the VM's address space.
|
||||||
|
fn set_identity_map_address(&self, address: u64) -> Result<()>;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
/// Sets the address of the three-page region in the VM's address space.
|
/// Sets the address of the three-page region in the VM's address space.
|
||||||
fn set_tss_address(&self, offset: usize) -> Result<()>;
|
fn set_tss_address(&self, offset: usize) -> Result<()>;
|
||||||
@@ -206,7 +235,7 @@ pub trait Vm: Send + Sync {
|
|||||||
/// Unregister an event that will, when signaled, trigger the `gsi` IRQ.
|
/// Unregister an event that will, when signaled, trigger the `gsi` IRQ.
|
||||||
fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>;
|
fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>;
|
||||||
/// Creates a new KVM vCPU file descriptor and maps the memory corresponding
|
/// Creates a new KVM vCPU file descriptor and maps the memory corresponding
|
||||||
fn create_vcpu(&self, id: u8, vmmops: Option<Arc<Box<dyn VmmOps>>>) -> Result<Arc<dyn Vcpu>>;
|
fn create_vcpu(&self, id: u8, vmmops: Option<Arc<dyn VmmOps>>) -> Result<Arc<dyn Vcpu>>;
|
||||||
/// Registers an event to be signaled whenever a certain address is written to.
|
/// Registers an event to be signaled whenever a certain address is written to.
|
||||||
fn register_ioevent(
|
fn register_ioevent(
|
||||||
&self,
|
&self,
|
||||||
@@ -218,7 +247,7 @@ pub trait Vm: Send + Sync {
|
|||||||
fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> Result<()>;
|
fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> Result<()>;
|
||||||
/// Sets the GSI routing table entries, overwriting any previously set
|
/// Sets the GSI routing table entries, overwriting any previously set
|
||||||
fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> Result<()>;
|
fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> Result<()>;
|
||||||
/// Creates a memory region structure that can be used with set_user_memory_region
|
/// Creates a memory region structure that can be used with {create/remove}_user_memory_region
|
||||||
fn make_user_memory_region(
|
fn make_user_memory_region(
|
||||||
&self,
|
&self,
|
||||||
slot: u32,
|
slot: u32,
|
||||||
@@ -228,9 +257,10 @@ pub trait Vm: Send + Sync {
|
|||||||
readonly: bool,
|
readonly: bool,
|
||||||
log_dirty_pages: bool,
|
log_dirty_pages: bool,
|
||||||
) -> MemoryRegion;
|
) -> MemoryRegion;
|
||||||
/// Creates/modifies a guest physical memory slot.
|
/// Creates a guest physical memory slot.
|
||||||
fn set_user_memory_region(&self, user_memory_region: MemoryRegion) -> Result<()>;
|
fn create_user_memory_region(&self, user_memory_region: MemoryRegion) -> Result<()>;
|
||||||
#[cfg(feature = "kvm")]
|
/// Removes a guest physical memory slot.
|
||||||
|
fn remove_user_memory_region(&self, user_memory_region: MemoryRegion) -> Result<()>;
|
||||||
/// Creates an emulated device in the kernel.
|
/// Creates an emulated device in the kernel.
|
||||||
fn create_device(&self, device: &mut CreateDevice) -> Result<Arc<dyn Device>>;
|
fn create_device(&self, device: &mut CreateDevice) -> Result<Arc<dyn Device>>;
|
||||||
/// Returns the preferred CPU target type which can be emulated by KVM on underlying host.
|
/// Returns the preferred CPU target type which can be emulated by KVM on underlying host.
|
||||||
@@ -239,6 +269,8 @@ pub trait Vm: Send + Sync {
|
|||||||
/// Enable split Irq capability
|
/// Enable split Irq capability
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn enable_split_irq(&self) -> Result<()>;
|
fn enable_split_irq(&self) -> Result<()>;
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
fn enable_sgx_attribute(&self, file: File) -> Result<()>;
|
||||||
/// Retrieve guest clock.
|
/// Retrieve guest clock.
|
||||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||||
fn get_clock(&self) -> Result<ClockData>;
|
fn get_clock(&self) -> Result<ClockData>;
|
||||||
@@ -254,8 +286,12 @@ pub trait Vm: Send + Sync {
|
|||||||
fn state(&self) -> Result<VmState>;
|
fn state(&self) -> Result<VmState>;
|
||||||
/// Set the VM state
|
/// Set the VM state
|
||||||
fn set_state(&self, state: VmState) -> Result<()>;
|
fn set_state(&self, state: VmState) -> Result<()>;
|
||||||
|
/// Start logging dirty pages
|
||||||
|
fn start_dirty_log(&self) -> Result<()>;
|
||||||
|
/// Stop logging dirty pages
|
||||||
|
fn stop_dirty_log(&self) -> Result<()>;
|
||||||
/// Get dirty pages bitmap
|
/// Get dirty pages bitmap
|
||||||
fn get_dirty_log(&self, slot: u32, memory_size: u64) -> Result<Vec<u64>>;
|
fn get_dirty_log(&self, slot: u32, base_gpa: u64, memory_size: u64) -> Result<Vec<u64>>;
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
/// Initalize TDX on this VM
|
/// Initalize TDX on this VM
|
||||||
fn tdx_init(&self, cpuid: &CpuId, max_vcpus: u32) -> Result<()>;
|
fn tdx_init(&self, cpuid: &CpuId, max_vcpus: u32) -> Result<()>;
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ authors = ["The Chromium OS Authors"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = "0.9.0"
|
||||||
|
|||||||
@@ -5,20 +5,22 @@ authors = ["The Chromium OS Authors"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
epoll = ">=4.0.1"
|
epoll = "4.3.1"
|
||||||
libc = "0.2.96"
|
getrandom = "0.2"
|
||||||
|
libc = "0.2.108"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
net_gen = { path = "../net_gen" }
|
net_gen = { path = "../net_gen" }
|
||||||
rate_limiter = { path = "../rate_limiter" }
|
rate_limiter = { path = "../rate_limiter" }
|
||||||
serde = "1.0.126"
|
serde = "1.0.130"
|
||||||
versionize = "0.1.6"
|
versionize = "0.1.6"
|
||||||
versionize_derive = "0.1.4"
|
versionize_derive = "0.1.4"
|
||||||
virtio-bindings = "0.1.0"
|
virtio-bindings = "0.1.0"
|
||||||
vm-memory = { version = "0.5.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
virtio-queue = { path = "../virtio-queue" }
|
||||||
|
vm-memory = { version = "0.6.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = "0.9.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
lazy_static = "1.3.0"
|
lazy_static = "1.4.0"
|
||||||
pnet = "0.28.0"
|
pnet = "0.28.0"
|
||||||
serde_json = "1.0.64"
|
serde_json = "1.0.72"
|
||||||
|
|||||||
@@ -12,17 +12,25 @@ use virtio_bindings::bindings::virtio_net::{
|
|||||||
VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6,
|
VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6,
|
||||||
VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_OK,
|
VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_OK,
|
||||||
};
|
};
|
||||||
use vm_memory::{ByteValued, Bytes, GuestMemoryError};
|
use virtio_queue::Queue;
|
||||||
use vm_virtio::Queue;
|
use vm_memory::{ByteValued, Bytes, GuestMemoryAtomic, GuestMemoryError};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Read queue failed.
|
/// Read queue failed.
|
||||||
GuestMemory(GuestMemoryError),
|
GuestMemory(GuestMemoryError),
|
||||||
/// No queue pairs number.
|
/// No control header descriptor
|
||||||
NoQueuePairsDescriptor,
|
NoControlHeaderDescriptor,
|
||||||
|
/// Missing the data descriptor in the chain.
|
||||||
|
NoDataDescriptor,
|
||||||
/// No status descriptor
|
/// No status descriptor
|
||||||
NoStatusDescriptor,
|
NoStatusDescriptor,
|
||||||
|
/// Failed adding used index
|
||||||
|
QueueAddUsed(virtio_queue::Error),
|
||||||
|
/// Failed creating an iterator over the queue
|
||||||
|
QueueIterator(virtio_queue::Error),
|
||||||
|
/// Failed enabling notification for the queue
|
||||||
|
QueueEnableNotification(virtio_queue::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Error>;
|
type Result<T> = std::result::Result<T, Error>;
|
||||||
@@ -34,6 +42,7 @@ pub struct ControlHeader {
|
|||||||
pub cmd: u8,
|
pub cmd: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: ControlHeader only contains a series of integers
|
||||||
unsafe impl ByteValued for ControlHeader {}
|
unsafe impl ByteValued for ControlHeader {}
|
||||||
|
|
||||||
pub struct CtrlQueue {
|
pub struct CtrlQueue {
|
||||||
@@ -45,22 +54,26 @@ impl CtrlQueue {
|
|||||||
CtrlQueue { taps }
|
CtrlQueue { taps }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process(&mut self, mem: &GuestMemoryMmap, queue: &mut Queue) -> Result<bool> {
|
pub fn process(
|
||||||
|
&mut self,
|
||||||
|
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||||
|
) -> Result<bool> {
|
||||||
let mut used_desc_heads = Vec::new();
|
let mut used_desc_heads = Vec::new();
|
||||||
for avail_desc in queue.iter(&mem) {
|
for mut desc_chain in queue.iter().map_err(Error::QueueIterator)? {
|
||||||
let ctrl_hdr: ControlHeader =
|
let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?;
|
||||||
mem.read_obj(avail_desc.addr).map_err(Error::GuestMemory)?;
|
|
||||||
let data_desc = avail_desc
|
let ctrl_hdr: ControlHeader = desc_chain
|
||||||
.next_descriptor()
|
.memory()
|
||||||
.ok_or(Error::NoQueuePairsDescriptor)?;
|
.read_obj(ctrl_desc.addr())
|
||||||
let status_desc = data_desc
|
.map_err(Error::GuestMemory)?;
|
||||||
.next_descriptor()
|
let data_desc = desc_chain.next().ok_or(Error::NoDataDescriptor)?;
|
||||||
.ok_or(Error::NoStatusDescriptor)?;
|
let status_desc = desc_chain.next().ok_or(Error::NoStatusDescriptor)?;
|
||||||
|
|
||||||
let ok = match u32::from(ctrl_hdr.class) {
|
let ok = match u32::from(ctrl_hdr.class) {
|
||||||
VIRTIO_NET_CTRL_MQ => {
|
VIRTIO_NET_CTRL_MQ => {
|
||||||
let queue_pairs = mem
|
let queue_pairs = desc_chain
|
||||||
.read_obj::<u16>(data_desc.addr)
|
.memory()
|
||||||
|
.read_obj::<u16>(data_desc.addr())
|
||||||
.map_err(Error::GuestMemory)?;
|
.map_err(Error::GuestMemory)?;
|
||||||
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET {
|
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET {
|
||||||
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
||||||
@@ -76,8 +89,9 @@ impl CtrlQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
VIRTIO_NET_CTRL_GUEST_OFFLOADS => {
|
VIRTIO_NET_CTRL_GUEST_OFFLOADS => {
|
||||||
let features = mem
|
let features = desc_chain
|
||||||
.read_obj::<u64>(data_desc.addr)
|
.memory()
|
||||||
|
.read_obj::<u64>(data_desc.addr())
|
||||||
.map_err(Error::GuestMemory)?;
|
.map_err(Error::GuestMemory)?;
|
||||||
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET {
|
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET {
|
||||||
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
||||||
@@ -102,17 +116,24 @@ impl CtrlQueue {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
mem.write_obj(
|
desc_chain
|
||||||
if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8,
|
.memory()
|
||||||
status_desc.addr,
|
.write_obj(
|
||||||
)
|
if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8,
|
||||||
.map_err(Error::GuestMemory)?;
|
status_desc.addr(),
|
||||||
used_desc_heads.push((avail_desc.index, avail_desc.len));
|
)
|
||||||
|
.map_err(Error::GuestMemory)?;
|
||||||
|
let len = ctrl_desc.len() + data_desc.len() + status_desc.len();
|
||||||
|
used_desc_heads.push((desc_chain.head_index(), len));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (desc_index, len) in used_desc_heads.iter() {
|
for (desc_index, len) in used_desc_heads.iter() {
|
||||||
queue.add_used(&mem, *desc_index, *len);
|
queue
|
||||||
queue.update_avail_event(&mem);
|
.add_used(*desc_index, *len)
|
||||||
|
.map_err(Error::QueueAddUsed)?;
|
||||||
|
queue
|
||||||
|
.enable_notification()
|
||||||
|
.map_err(Error::QueueEnableNotification)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(!used_desc_heads.is_empty())
|
Ok(!used_desc_heads.is_empty())
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ pub struct VirtioNetConfig {
|
|||||||
pub duplex: u8,
|
pub duplex: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe because it only has data and has no implicit padding.
|
// SAFETY: it only has data and has no implicit padding.
|
||||||
unsafe impl ByteValued for VirtioNetConfig {}
|
unsafe impl ByteValued for VirtioNetConfig {}
|
||||||
|
|
||||||
/// Create a sockaddr_in from an IPv4 address, and expose it as
|
/// Create a sockaddr_in from an IPv4 address, and expose it as
|
||||||
@@ -121,7 +121,7 @@ pub fn unregister_listener(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_net_config_space(
|
pub fn build_net_config_space(
|
||||||
mut config: &mut VirtioNetConfig,
|
config: &mut VirtioNetConfig,
|
||||||
mac: MacAddr,
|
mac: MacAddr,
|
||||||
num_queues: usize,
|
num_queues: usize,
|
||||||
avail_features: &mut u64,
|
avail_features: &mut u64,
|
||||||
@@ -129,7 +129,7 @@ pub fn build_net_config_space(
|
|||||||
config.mac.copy_from_slice(mac.get_bytes());
|
config.mac.copy_from_slice(mac.get_bytes());
|
||||||
*avail_features |= 1 << VIRTIO_NET_F_MAC;
|
*avail_features |= 1 << VIRTIO_NET_F_MAC;
|
||||||
|
|
||||||
build_net_config_space_with_mq(&mut config, num_queues, avail_features);
|
build_net_config_space_with_mq(config, num_queues, avail_features);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_net_config_space_with_mq(
|
pub fn build_net_config_space_with_mq(
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ impl MacAddr {
|
|||||||
// TODO: using something like std::mem::uninitialized could avoid the extra initialization,
|
// TODO: using something like std::mem::uninitialized could avoid the extra initialization,
|
||||||
// if this ever becomes a performance bottleneck.
|
// if this ever becomes a performance bottleneck.
|
||||||
let mut bytes = [0u8; MAC_ADDR_LEN];
|
let mut bytes = [0u8; MAC_ADDR_LEN];
|
||||||
bytes[..].copy_from_slice(&src);
|
bytes[..].copy_from_slice(src);
|
||||||
|
|
||||||
MacAddr { bytes }
|
MacAddr { bytes }
|
||||||
}
|
}
|
||||||
@@ -82,21 +82,12 @@ impl MacAddr {
|
|||||||
pub fn local_random() -> MacAddr {
|
pub fn local_random() -> MacAddr {
|
||||||
// Generate a fully random MAC
|
// Generate a fully random MAC
|
||||||
let mut random_bytes = [0u8; MAC_ADDR_LEN];
|
let mut random_bytes = [0u8; MAC_ADDR_LEN];
|
||||||
unsafe {
|
if let Err(e) = getrandom::getrandom(&mut random_bytes) {
|
||||||
// Man page says this function will not be interrupted by a signal
|
error!(
|
||||||
// for requests less than 256 bytes
|
"Error populating MAC address with random data: {}",
|
||||||
if libc::getrandom(
|
e.to_string()
|
||||||
random_bytes.as_mut_ptr() as *mut _ as *mut libc::c_void,
|
);
|
||||||
MAC_ADDR_LEN,
|
}
|
||||||
0,
|
|
||||||
) < 0
|
|
||||||
{
|
|
||||||
error!(
|
|
||||||
"Error populating MAC address with random data: {}",
|
|
||||||
std::io::Error::last_os_error()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set the first byte to make the OUI a locally administered OUI
|
// Set the first byte to make the OUI a locally administered OUI
|
||||||
random_bytes[0] = 0x2e;
|
random_bytes[0] = 0x2e;
|
||||||
@@ -170,7 +161,7 @@ mod tests {
|
|||||||
|
|
||||||
let mac = MacAddr::parse_str("12:34:56:78:9a:BC").unwrap();
|
let mac = MacAddr::parse_str("12:34:56:78:9a:BC").unwrap();
|
||||||
|
|
||||||
println!("parsed MAC address: {}", mac.to_string());
|
println!("parsed MAC address: {}", mac);
|
||||||
|
|
||||||
let bytes = mac.get_bytes();
|
let bytes = mac.get_bytes();
|
||||||
assert_eq!(bytes, [0x12u8, 0x34, 0x56, 0x78, 0x9a, 0xbc]);
|
assert_eq!(bytes, [0x12u8, 0x34, 0x56, 0x78, 0x9a, 0xbc]);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
use super::{unregister_listener, vnet_hdr_len, Tap};
|
use super::{register_listener, unregister_listener, vnet_hdr_len, Tap};
|
||||||
use crate::GuestMemoryMmap;
|
use crate::GuestMemoryMmap;
|
||||||
use rate_limiter::{RateLimiter, TokenType};
|
use rate_limiter::{RateLimiter, TokenType};
|
||||||
use std::io;
|
use std::io;
|
||||||
@@ -10,8 +10,8 @@ use std::num::Wrapping;
|
|||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use vm_memory::{Bytes, GuestAddressSpace, GuestMemory, GuestMemoryAtomic};
|
use virtio_queue::Queue;
|
||||||
use vm_virtio::Queue;
|
use vm_memory::{Bytes, GuestMemory, GuestMemoryAtomic};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TxVirtio {
|
pub struct TxVirtio {
|
||||||
@@ -35,84 +35,96 @@ impl TxVirtio {
|
|||||||
|
|
||||||
pub fn process_desc_chain(
|
pub fn process_desc_chain(
|
||||||
&mut self,
|
&mut self,
|
||||||
mem: &GuestMemoryMmap,
|
|
||||||
tap: &mut Tap,
|
tap: &mut Tap,
|
||||||
queue: &mut Queue,
|
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||||
rate_limiter: &mut Option<RateLimiter>,
|
rate_limiter: &mut Option<RateLimiter>,
|
||||||
) -> Result<(), NetQueuePairError> {
|
) -> Result<bool, NetQueuePairError> {
|
||||||
while let Some(avail_desc) = queue.iter(&mem).next() {
|
let mut retry_write = false;
|
||||||
let head_index = avail_desc.index;
|
let mut rate_limit_reached = false;
|
||||||
let mut next_desc = Some(avail_desc);
|
|
||||||
|
|
||||||
if let Some(rate_limiter) = rate_limiter {
|
loop {
|
||||||
if !rate_limiter.consume(1, TokenType::Ops) {
|
let used_desc_head: (u16, u32);
|
||||||
queue.go_to_previous_position();
|
let mut avail_iter = queue
|
||||||
|
.iter()
|
||||||
|
.map_err(NetQueuePairError::QueueIteratorFailed)?;
|
||||||
|
|
||||||
|
if let Some(mut desc_chain) = avail_iter.next() {
|
||||||
|
if rate_limit_reached {
|
||||||
|
avail_iter.go_to_previous_position();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut bytes = Wrapping(0);
|
let mut next_desc = desc_chain.next();
|
||||||
let mut tmp_next_desc = next_desc.clone();
|
|
||||||
while let Some(desc) = tmp_next_desc {
|
let mut iovecs = Vec::new();
|
||||||
if !desc.is_write_only() {
|
while let Some(desc) = next_desc {
|
||||||
bytes += Wrapping(desc.len as u64);
|
if !desc.is_write_only() && desc.len() > 0 {
|
||||||
|
let buf = desc_chain
|
||||||
|
.memory()
|
||||||
|
.get_slice(desc.addr(), desc.len() as usize)
|
||||||
|
.map_err(NetQueuePairError::GuestMemory)?
|
||||||
|
.as_ptr();
|
||||||
|
let iovec = libc::iovec {
|
||||||
|
iov_base: buf as *mut libc::c_void,
|
||||||
|
iov_len: desc.len() as libc::size_t,
|
||||||
|
};
|
||||||
|
iovecs.push(iovec);
|
||||||
}
|
}
|
||||||
tmp_next_desc = desc.next_descriptor();
|
next_desc = desc_chain.next();
|
||||||
}
|
}
|
||||||
bytes -= Wrapping(vnet_hdr_len() as u64);
|
|
||||||
if !rate_limiter.consume(bytes.0, TokenType::Bytes) {
|
|
||||||
// Revert the OPS consume().
|
|
||||||
rate_limiter.manual_replenish(1, TokenType::Ops);
|
|
||||||
queue.go_to_previous_position();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut iovecs = Vec::new();
|
let len = if !iovecs.is_empty() {
|
||||||
while let Some(desc) = next_desc {
|
let result = unsafe {
|
||||||
if !desc.is_write_only() && desc.len > 0 {
|
libc::writev(
|
||||||
let buf = mem
|
tap.as_raw_fd() as libc::c_int,
|
||||||
.get_slice(desc.addr, desc.len as usize)
|
iovecs.as_ptr() as *const libc::iovec,
|
||||||
.map_err(NetQueuePairError::GuestMemory)?
|
iovecs.len() as libc::c_int,
|
||||||
.as_ptr();
|
)
|
||||||
let iovec = libc::iovec {
|
|
||||||
iov_base: buf as *mut libc::c_void,
|
|
||||||
iov_len: desc.len as libc::size_t,
|
|
||||||
};
|
};
|
||||||
iovecs.push(iovec);
|
|
||||||
}
|
|
||||||
next_desc = desc.next_descriptor();
|
|
||||||
}
|
|
||||||
|
|
||||||
if !iovecs.is_empty() {
|
if result < 0 {
|
||||||
let result = unsafe {
|
let e = std::io::Error::last_os_error();
|
||||||
libc::writev(
|
|
||||||
tap.as_raw_fd() as libc::c_int,
|
|
||||||
iovecs.as_ptr() as *const libc::iovec,
|
|
||||||
iovecs.len() as libc::c_int,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if result < 0 {
|
|
||||||
let e = std::io::Error::last_os_error();
|
|
||||||
queue.go_to_previous_position();
|
|
||||||
|
|
||||||
/* EAGAIN */
|
/* EAGAIN */
|
||||||
if e.kind() == std::io::ErrorKind::WouldBlock {
|
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||||
warn!("net: tx: (recoverable) failed writing to tap: {}", e);
|
avail_iter.go_to_previous_position();
|
||||||
break;
|
retry_write = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
error!("net: tx: failed writing to tap: {}", e);
|
||||||
|
return Err(NetQueuePairError::WriteTap(e));
|
||||||
}
|
}
|
||||||
error!("net: tx: failed writing to tap: {}", e);
|
|
||||||
return Err(NetQueuePairError::WriteTap(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
|
self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
|
||||||
self.counter_frames += Wrapping(1);
|
self.counter_frames += Wrapping(1);
|
||||||
|
|
||||||
|
result as u32
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
used_desc_head = (desc_chain.head_index(), len);
|
||||||
|
|
||||||
|
// For the sake of simplicity (similar to the RX rate limiting), we always
|
||||||
|
// let the 'last' descriptor chain go-through even if it was over the rate
|
||||||
|
// limit, and simply stop processing oncoming `avail_desc` if any.
|
||||||
|
if let Some(rate_limiter) = rate_limiter {
|
||||||
|
rate_limit_reached = !rate_limiter.consume(1, TokenType::Ops)
|
||||||
|
|| !rate_limiter.consume(len as u64, TokenType::Bytes);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.add_used(&mem, head_index, 0);
|
queue
|
||||||
queue.update_avail_event(&mem);
|
.add_used(used_desc_head.0, used_desc_head.1)
|
||||||
|
.map_err(NetQueuePairError::QueueAddUsed)?;
|
||||||
|
queue
|
||||||
|
.enable_notification()
|
||||||
|
.map_err(NetQueuePairError::QueueEnableNotification)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(retry_write)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,87 +150,106 @@ impl RxVirtio {
|
|||||||
|
|
||||||
pub fn process_desc_chain(
|
pub fn process_desc_chain(
|
||||||
&mut self,
|
&mut self,
|
||||||
mem: &GuestMemoryMmap,
|
|
||||||
tap: &mut Tap,
|
tap: &mut Tap,
|
||||||
queue: &mut Queue,
|
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||||
rate_limiter: &mut Option<RateLimiter>,
|
rate_limiter: &mut Option<RateLimiter>,
|
||||||
) -> Result<bool, NetQueuePairError> {
|
) -> Result<bool, NetQueuePairError> {
|
||||||
let mut exhausted_descs = true;
|
let mut exhausted_descs = true;
|
||||||
let mut rate_limit_reached = false;
|
let mut rate_limit_reached = false;
|
||||||
|
|
||||||
while let Some(avail_desc) = queue.iter(&mem).next() {
|
loop {
|
||||||
if rate_limit_reached {
|
let used_desc_head: (u16, u32);
|
||||||
exhausted_descs = false;
|
let mut avail_iter = queue
|
||||||
queue.go_to_previous_position();
|
.iter()
|
||||||
|
.map_err(NetQueuePairError::QueueIteratorFailed)?;
|
||||||
|
|
||||||
|
if let Some(mut desc_chain) = avail_iter.next() {
|
||||||
|
if rate_limit_reached {
|
||||||
|
exhausted_descs = false;
|
||||||
|
avail_iter.go_to_previous_position();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let desc = desc_chain
|
||||||
|
.next()
|
||||||
|
.ok_or(NetQueuePairError::DescriptorChainTooShort)?;
|
||||||
|
let num_buffers_addr = desc_chain.memory().checked_offset(desc.addr(), 10).unwrap();
|
||||||
|
let mut next_desc = Some(desc);
|
||||||
|
|
||||||
|
let mut iovecs = Vec::new();
|
||||||
|
while let Some(desc) = next_desc {
|
||||||
|
if desc.is_write_only() && desc.len() > 0 {
|
||||||
|
let buf = desc_chain
|
||||||
|
.memory()
|
||||||
|
.get_slice(desc.addr(), desc.len() as usize)
|
||||||
|
.map_err(NetQueuePairError::GuestMemory)?
|
||||||
|
.as_ptr();
|
||||||
|
let iovec = libc::iovec {
|
||||||
|
iov_base: buf as *mut libc::c_void,
|
||||||
|
iov_len: desc.len() as libc::size_t,
|
||||||
|
};
|
||||||
|
iovecs.push(iovec);
|
||||||
|
}
|
||||||
|
next_desc = desc_chain.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
let len = if !iovecs.is_empty() {
|
||||||
|
let result = unsafe {
|
||||||
|
libc::readv(
|
||||||
|
tap.as_raw_fd() as libc::c_int,
|
||||||
|
iovecs.as_ptr() as *const libc::iovec,
|
||||||
|
iovecs.len() as libc::c_int,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if result < 0 {
|
||||||
|
let e = std::io::Error::last_os_error();
|
||||||
|
exhausted_descs = false;
|
||||||
|
avail_iter.go_to_previous_position();
|
||||||
|
|
||||||
|
/* EAGAIN */
|
||||||
|
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
error!("net: rx: failed reading from tap: {}", e);
|
||||||
|
return Err(NetQueuePairError::ReadTap(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write num_buffers to guest memory. We simply write 1 as we
|
||||||
|
// never spread the frame over more than one descriptor chain.
|
||||||
|
desc_chain
|
||||||
|
.memory()
|
||||||
|
.write_obj(1u16, num_buffers_addr)
|
||||||
|
.map_err(NetQueuePairError::GuestMemory)?;
|
||||||
|
|
||||||
|
self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
|
||||||
|
self.counter_frames += Wrapping(1);
|
||||||
|
|
||||||
|
result as u32
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
used_desc_head = (desc_chain.head_index(), len);
|
||||||
|
|
||||||
|
// For the sake of simplicity (keeping the handling of RX_QUEUE_EVENT and
|
||||||
|
// RX_TAP_EVENT totally asynchronous), we always let the 'last' descriptor
|
||||||
|
// chain go-through even if it was over the rate limit, and simply stop
|
||||||
|
// processing oncoming `avail_desc` if any.
|
||||||
|
if let Some(rate_limiter) = rate_limiter {
|
||||||
|
rate_limit_reached = !rate_limiter.consume(1, TokenType::Ops)
|
||||||
|
|| !rate_limiter.consume(len as u64, TokenType::Bytes);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let head_index = avail_desc.index;
|
queue
|
||||||
let num_buffers_addr = mem.checked_offset(avail_desc.addr, 10).unwrap();
|
.add_used(used_desc_head.0, used_desc_head.1)
|
||||||
let mut next_desc = Some(avail_desc);
|
.map_err(NetQueuePairError::QueueAddUsed)?;
|
||||||
|
queue
|
||||||
let mut iovecs = Vec::new();
|
.enable_notification()
|
||||||
while let Some(desc) = next_desc {
|
.map_err(NetQueuePairError::QueueEnableNotification)?;
|
||||||
if desc.is_write_only() && desc.len > 0 {
|
|
||||||
let buf = mem
|
|
||||||
.get_slice(desc.addr, desc.len as usize)
|
|
||||||
.map_err(NetQueuePairError::GuestMemory)?
|
|
||||||
.as_ptr();
|
|
||||||
let iovec = libc::iovec {
|
|
||||||
iov_base: buf as *mut libc::c_void,
|
|
||||||
iov_len: desc.len as libc::size_t,
|
|
||||||
};
|
|
||||||
iovecs.push(iovec);
|
|
||||||
}
|
|
||||||
next_desc = desc.next_descriptor();
|
|
||||||
}
|
|
||||||
|
|
||||||
let len = if !iovecs.is_empty() {
|
|
||||||
let result = unsafe {
|
|
||||||
libc::readv(
|
|
||||||
tap.as_raw_fd() as libc::c_int,
|
|
||||||
iovecs.as_ptr() as *const libc::iovec,
|
|
||||||
iovecs.len() as libc::c_int,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if result < 0 {
|
|
||||||
let e = std::io::Error::last_os_error();
|
|
||||||
exhausted_descs = false;
|
|
||||||
queue.go_to_previous_position();
|
|
||||||
|
|
||||||
/* EAGAIN */
|
|
||||||
if e.kind() == std::io::ErrorKind::WouldBlock {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
error!("net: rx: failed reading from tap: {}", e);
|
|
||||||
return Err(NetQueuePairError::ReadTap(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write num_buffers to guest memory. We simply write 1 as we
|
|
||||||
// never spread the frame over more than one descriptor chain.
|
|
||||||
mem.write_obj(1u16, num_buffers_addr)
|
|
||||||
.map_err(NetQueuePairError::GuestMemory)?;
|
|
||||||
|
|
||||||
self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
|
|
||||||
self.counter_frames += Wrapping(1);
|
|
||||||
|
|
||||||
result as u32
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
queue.add_used(&mem, head_index, len);
|
|
||||||
queue.update_avail_event(&mem);
|
|
||||||
|
|
||||||
// For the sake of simplicity (keeping the handling of RX_QUEUE_EVENT and
|
|
||||||
// RX_TAP_EVENT totally asynchronous), we always let the 'last' descriptor
|
|
||||||
// chain go-through even if it was over the rate limit, and simply stop
|
|
||||||
// processing oncoming `avail_desc` if any.
|
|
||||||
if let Some(rate_limiter) = rate_limiter {
|
|
||||||
rate_limit_reached = !rate_limiter.consume(1, TokenType::Ops)
|
|
||||||
|| !rate_limiter.consume(len as u64, TokenType::Bytes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(exhausted_descs)
|
Ok(exhausted_descs)
|
||||||
@@ -247,32 +278,69 @@ pub enum NetQueuePairError {
|
|||||||
ReadTap(io::Error),
|
ReadTap(io::Error),
|
||||||
/// Error related to guest memory
|
/// Error related to guest memory
|
||||||
GuestMemory(vm_memory::GuestMemoryError),
|
GuestMemory(vm_memory::GuestMemoryError),
|
||||||
|
/// Returned an error while iterating through the queue
|
||||||
|
QueueIteratorFailed(virtio_queue::Error),
|
||||||
|
/// Descriptor chain is too short
|
||||||
|
DescriptorChainTooShort,
|
||||||
|
/// Failed to determine if queue needed notification
|
||||||
|
QueueNeedsNotification(virtio_queue::Error),
|
||||||
|
/// Failed to enable notification on the queue
|
||||||
|
QueueEnableNotification(virtio_queue::Error),
|
||||||
|
/// Failed to add used index to the queue
|
||||||
|
QueueAddUsed(virtio_queue::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NetQueuePair {
|
pub struct NetQueuePair {
|
||||||
pub mem: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
|
|
||||||
pub tap: Tap,
|
pub tap: Tap,
|
||||||
|
// With epoll each FD must be unique. So in order to filter the
|
||||||
|
// events we need to get a second FD responding to the original
|
||||||
|
// device so that we can send EPOLLOUT and EPOLLIN to separate
|
||||||
|
// events.
|
||||||
|
pub tap_for_write_epoll: Tap,
|
||||||
pub rx: RxVirtio,
|
pub rx: RxVirtio,
|
||||||
pub tx: TxVirtio,
|
pub tx: TxVirtio,
|
||||||
pub epoll_fd: Option<RawFd>,
|
pub epoll_fd: Option<RawFd>,
|
||||||
pub rx_tap_listening: bool,
|
pub rx_tap_listening: bool,
|
||||||
|
pub tx_tap_listening: bool,
|
||||||
pub counters: NetCounters,
|
pub counters: NetCounters,
|
||||||
pub tap_event_id: u16,
|
pub tap_rx_event_id: u16,
|
||||||
|
pub tap_tx_event_id: u16,
|
||||||
pub rx_desc_avail: bool,
|
pub rx_desc_avail: bool,
|
||||||
pub rx_rate_limiter: Option<RateLimiter>,
|
pub rx_rate_limiter: Option<RateLimiter>,
|
||||||
pub tx_rate_limiter: Option<RateLimiter>,
|
pub tx_rate_limiter: Option<RateLimiter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetQueuePair {
|
impl NetQueuePair {
|
||||||
pub fn process_tx(&mut self, mut queue: &mut Queue) -> Result<bool, NetQueuePairError> {
|
pub fn process_tx(
|
||||||
let mem = self
|
&mut self,
|
||||||
.mem
|
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||||
.as_ref()
|
) -> Result<bool, NetQueuePairError> {
|
||||||
.ok_or(NetQueuePairError::NoMemoryConfigured)
|
let tx_tap_retry =
|
||||||
.map(|m| m.memory())?;
|
self.tx
|
||||||
|
.process_desc_chain(&mut self.tap, queue, &mut self.tx_rate_limiter)?;
|
||||||
|
|
||||||
self.tx
|
// We got told to try again when writing to the tap. Wait for the TAP to be writable
|
||||||
.process_desc_chain(&mem, &mut self.tap, &mut queue, &mut self.tx_rate_limiter)?;
|
if tx_tap_retry && !self.tx_tap_listening {
|
||||||
|
register_listener(
|
||||||
|
self.epoll_fd.unwrap(),
|
||||||
|
self.tap_for_write_epoll.as_raw_fd(),
|
||||||
|
epoll::Events::EPOLLOUT,
|
||||||
|
u64::from(self.tap_tx_event_id),
|
||||||
|
)
|
||||||
|
.map_err(NetQueuePairError::RegisterListener)?;
|
||||||
|
self.tx_tap_listening = true;
|
||||||
|
info!("Writing to TAP returned EAGAIN. Listening for TAP to become writable.");
|
||||||
|
} else if !tx_tap_retry && self.tx_tap_listening {
|
||||||
|
unregister_listener(
|
||||||
|
self.epoll_fd.unwrap(),
|
||||||
|
self.tap_for_write_epoll.as_raw_fd(),
|
||||||
|
epoll::Events::EPOLLOUT,
|
||||||
|
u64::from(self.tap_tx_event_id),
|
||||||
|
)
|
||||||
|
.map_err(NetQueuePairError::UnregisterListener)?;
|
||||||
|
self.tx_tap_listening = false;
|
||||||
|
info!("Writing to TAP succeeded. No longer listening for TAP to become writable.");
|
||||||
|
}
|
||||||
|
|
||||||
self.counters
|
self.counters
|
||||||
.tx_bytes
|
.tx_bytes
|
||||||
@@ -283,22 +351,19 @@ impl NetQueuePair {
|
|||||||
self.tx.counter_bytes = Wrapping(0);
|
self.tx.counter_bytes = Wrapping(0);
|
||||||
self.tx.counter_frames = Wrapping(0);
|
self.tx.counter_frames = Wrapping(0);
|
||||||
|
|
||||||
Ok(queue.needs_notification(&mem, queue.next_used))
|
queue
|
||||||
|
.needs_notification()
|
||||||
|
.map_err(NetQueuePairError::QueueNeedsNotification)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_rx(&mut self, mut queue: &mut Queue) -> Result<bool, NetQueuePairError> {
|
pub fn process_rx(
|
||||||
let mem = self
|
&mut self,
|
||||||
.mem
|
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||||
.as_ref()
|
) -> Result<bool, NetQueuePairError> {
|
||||||
.ok_or(NetQueuePairError::NoMemoryConfigured)
|
self.rx_desc_avail =
|
||||||
.map(|m| m.memory())?;
|
!self
|
||||||
|
.rx
|
||||||
self.rx_desc_avail = !self.rx.process_desc_chain(
|
.process_desc_chain(&mut self.tap, queue, &mut self.rx_rate_limiter)?;
|
||||||
&mem,
|
|
||||||
&mut self.tap,
|
|
||||||
&mut queue,
|
|
||||||
&mut self.rx_rate_limiter,
|
|
||||||
)?;
|
|
||||||
let rate_limit_reached = self
|
let rate_limit_reached = self
|
||||||
.rx_rate_limiter
|
.rx_rate_limiter
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -312,7 +377,7 @@ impl NetQueuePair {
|
|||||||
self.epoll_fd.unwrap(),
|
self.epoll_fd.unwrap(),
|
||||||
self.tap.as_raw_fd(),
|
self.tap.as_raw_fd(),
|
||||||
epoll::Events::EPOLLIN,
|
epoll::Events::EPOLLIN,
|
||||||
u64::from(self.tap_event_id),
|
u64::from(self.tap_rx_event_id),
|
||||||
)
|
)
|
||||||
.map_err(NetQueuePairError::UnregisterListener)?;
|
.map_err(NetQueuePairError::UnregisterListener)?;
|
||||||
self.rx_tap_listening = false;
|
self.rx_tap_listening = false;
|
||||||
@@ -327,6 +392,8 @@ impl NetQueuePair {
|
|||||||
self.rx.counter_bytes = Wrapping(0);
|
self.rx.counter_bytes = Wrapping(0);
|
||||||
self.rx.counter_frames = Wrapping(0);
|
self.rx.counter_frames = Wrapping(0);
|
||||||
|
|
||||||
Ok(queue.needs_notification(&mem, queue.next_used))
|
queue
|
||||||
|
.needs_notification()
|
||||||
|
.map_err(NetQueuePairError::QueueNeedsNotification)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ impl Read for Tap {
|
|||||||
|
|
||||||
impl Write for Tap {
|
impl Write for Tap {
|
||||||
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
|
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
|
||||||
self.tap_file.write(&buf)
|
self.tap_file.write(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) -> IoResult<()> {
|
fn flush(&mut self) -> IoResult<()> {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::num::ParseIntError;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -37,6 +38,32 @@ impl fmt::Display for OptionParserError {
|
|||||||
}
|
}
|
||||||
type OptionParserResult<T> = std::result::Result<T, OptionParserError>;
|
type OptionParserResult<T> = std::result::Result<T, OptionParserError>;
|
||||||
|
|
||||||
|
fn split_commas_outside_brackets(s: &str) -> OptionParserResult<Vec<String>> {
|
||||||
|
let mut list: Vec<String> = Vec::new();
|
||||||
|
let mut opened_brackets: usize = 0;
|
||||||
|
for element in s.trim().split(',') {
|
||||||
|
if opened_brackets > 0 {
|
||||||
|
if let Some(last) = list.last_mut() {
|
||||||
|
*last = format!("{},{}", last, element);
|
||||||
|
} else {
|
||||||
|
return Err(OptionParserError::InvalidSyntax(s.to_owned()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
list.push(element.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
opened_brackets += element.matches('[').count();
|
||||||
|
let closing_brackets = element.matches(']').count();
|
||||||
|
if closing_brackets > opened_brackets {
|
||||||
|
return Err(OptionParserError::InvalidSyntax(s.to_owned()));
|
||||||
|
} else {
|
||||||
|
opened_brackets -= closing_brackets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(list)
|
||||||
|
}
|
||||||
|
|
||||||
impl OptionParser {
|
impl OptionParser {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -49,9 +76,7 @@ impl OptionParser {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let options_list: Vec<&str> = input.trim().split(',').collect();
|
for option in split_commas_outside_brackets(input)?.iter() {
|
||||||
|
|
||||||
for option in options_list.iter() {
|
|
||||||
let parts: Vec<&str> = option.split('=').collect();
|
let parts: Vec<&str> = option.split('=').collect();
|
||||||
|
|
||||||
match self.options.get_mut(parts[0]) {
|
match self.options.get_mut(parts[0]) {
|
||||||
@@ -183,7 +208,11 @@ impl FromStr for IntegerList {
|
|||||||
|
|
||||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||||
let mut integer_list = Vec::new();
|
let mut integer_list = Vec::new();
|
||||||
let ranges_list: Vec<&str> = s.trim().split(':').collect();
|
let ranges_list: Vec<&str> = s
|
||||||
|
.trim()
|
||||||
|
.trim_matches(|c| c == '[' || c == ']')
|
||||||
|
.split(',')
|
||||||
|
.collect();
|
||||||
|
|
||||||
for range in ranges_list.iter() {
|
for range in ranges_list.iter() {
|
||||||
let items: Vec<&str> = range.split('-').collect();
|
let items: Vec<&str> = range.split('-').collect();
|
||||||
@@ -216,39 +245,71 @@ impl FromStr for IntegerList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TupleTwoIntegers(pub Vec<(u64, u64)>);
|
pub trait TupleValue {
|
||||||
|
fn parse_value(input: &str) -> Result<Self, TupleError>
|
||||||
pub enum TupleTwoIntegersParseError {
|
where
|
||||||
InvalidValue(String),
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for TupleTwoIntegers {
|
impl TupleValue for u64 {
|
||||||
type Err = TupleTwoIntegersParseError;
|
fn parse_value(input: &str) -> Result<Self, TupleError> {
|
||||||
|
input.parse::<u64>().map_err(TupleError::InvalidInteger)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TupleValue for Vec<u8> {
|
||||||
|
fn parse_value(input: &str) -> Result<Self, TupleError> {
|
||||||
|
Ok(IntegerList::from_str(input)
|
||||||
|
.map_err(TupleError::InvalidIntegerList)?
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.map(|v| *v as u8)
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TupleValue for Vec<u64> {
|
||||||
|
fn parse_value(input: &str) -> Result<Self, TupleError> {
|
||||||
|
Ok(IntegerList::from_str(input)
|
||||||
|
.map_err(TupleError::InvalidIntegerList)?
|
||||||
|
.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Tuple<S, T>(pub Vec<(S, T)>);
|
||||||
|
|
||||||
|
pub enum TupleError {
|
||||||
|
InvalidValue(String),
|
||||||
|
SplitOutsideBrackets(OptionParserError),
|
||||||
|
InvalidIntegerList(IntegerListParseError),
|
||||||
|
InvalidInteger(ParseIntError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: FromStr, T: TupleValue> FromStr for Tuple<S, T> {
|
||||||
|
type Err = TupleError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||||
let mut list = Vec::new();
|
let mut list: Vec<(S, T)> = Vec::new();
|
||||||
let tuples_list: Vec<&str> = s.trim().split(':').collect();
|
|
||||||
|
|
||||||
|
let tuples_list =
|
||||||
|
split_commas_outside_brackets(s.trim().trim_matches(|c| c == '[' || c == ']'))
|
||||||
|
.map_err(TupleError::SplitOutsideBrackets)?;
|
||||||
for tuple in tuples_list.iter() {
|
for tuple in tuples_list.iter() {
|
||||||
let items: Vec<&str> = tuple.split('@').collect();
|
let items: Vec<&str> = tuple.split('@').collect();
|
||||||
|
|
||||||
if items.len() != 2 {
|
if items.len() != 2 {
|
||||||
return Err(TupleTwoIntegersParseError::InvalidValue(
|
return Err(TupleError::InvalidValue((*tuple).to_string()));
|
||||||
(*tuple).to_string(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let item1 = items[0]
|
let item1 = items[0]
|
||||||
.parse::<u64>()
|
.parse::<S>()
|
||||||
.map_err(|_| TupleTwoIntegersParseError::InvalidValue(items[0].to_owned()))?;
|
.map_err(|_| TupleError::InvalidValue(items[0].to_owned()))?;
|
||||||
let item2 = items[1]
|
let item2 = TupleValue::parse_value(items[1])?;
|
||||||
.parse::<u64>()
|
|
||||||
.map_err(|_| TupleTwoIntegersParseError::InvalidValue(items[1].to_owned()))?;
|
|
||||||
|
|
||||||
list.push((item1, item2));
|
list.push((item1, item2));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(TupleTwoIntegers(list))
|
Ok(Tuple(list))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,7 +323,12 @@ impl FromStr for StringList {
|
|||||||
type Err = StringListParseError;
|
type Err = StringListParseError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||||
let string_list: Vec<String> = s.trim().split(':').map(|e| e.to_owned()).collect();
|
let string_list: Vec<String> = s
|
||||||
|
.trim()
|
||||||
|
.trim_matches(|c| c == '[' || c == ']')
|
||||||
|
.split(',')
|
||||||
|
.map(|e| e.to_owned())
|
||||||
|
.collect();
|
||||||
|
|
||||||
Ok(StringList(string_list))
|
Ok(StringList(string_list))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,19 +4,26 @@ version = "0.1.0"
|
|||||||
authors = ["Samuel Ortiz <sameo@linux.intel.com>"]
|
authors = ["Samuel Ortiz <sameo@linux.intel.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
kvm = ["vfio-ioctls/kvm"]
|
||||||
|
mshv = ["vfio-ioctls/mshv"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0.51"
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio-ioctls", branch = "master" }
|
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio-ioctls", branch = "main", default-features = false }
|
||||||
vmm-sys-util = ">=0.3.1"
|
vfio_user = { path = "../vfio_user" }
|
||||||
libc = "0.2.96"
|
vmm-sys-util = "0.9.0"
|
||||||
|
libc = "0.2.108"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
|
thiserror = "1.0.30"
|
||||||
versionize = "0.1.6"
|
versionize = "0.1.6"
|
||||||
versionize_derive = "0.1.4"
|
versionize_derive = "0.1.4"
|
||||||
vm-allocator = { path = "../vm-allocator" }
|
vm-allocator = { path = "../vm-allocator" }
|
||||||
vm-device = { path = "../vm-device" }
|
vm-device = { path = "../vm-device" }
|
||||||
vm-memory = "0.5.0"
|
vm-memory = "0.6.0"
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
|
|
||||||
[dependencies.vfio-bindings]
|
[dependencies.vfio-bindings]
|
||||||
|
|||||||
@@ -140,12 +140,8 @@ impl PciBus {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_device(
|
pub fn add_device(&mut self, device_id: u32, device: Arc<Mutex<dyn PciDevice>>) -> Result<()> {
|
||||||
&mut self,
|
self.devices.insert(device_id, device);
|
||||||
pci_device_bdf: u32,
|
|
||||||
device: Arc<Mutex<dyn PciDevice>>,
|
|
||||||
) -> Result<()> {
|
|
||||||
self.devices.insert(pci_device_bdf >> 3, device);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,8 +193,8 @@ pub struct PciConfigIo {
|
|||||||
impl PciConfigIo {
|
impl PciConfigIo {
|
||||||
pub fn new(pci_bus: Arc<Mutex<PciBus>>) -> Self {
|
pub fn new(pci_bus: Arc<Mutex<PciBus>>) -> Self {
|
||||||
PciConfigIo {
|
PciConfigIo {
|
||||||
pci_bus,
|
|
||||||
config_address: 0,
|
config_address: 0,
|
||||||
|
pci_bus,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,6 +218,7 @@ impl PciConfigIo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.pci_bus
|
self.pci_bus
|
||||||
|
.as_ref()
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.devices
|
.devices
|
||||||
@@ -249,7 +246,7 @@ impl PciConfigIo {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pci_bus = self.pci_bus.lock().unwrap();
|
let pci_bus = self.pci_bus.as_ref().lock().unwrap();
|
||||||
if let Some(d) = pci_bus.devices.get(&(device as u32)) {
|
if let Some(d) = pci_bus.devices.get(&(device as u32)) {
|
||||||
let mut device = d.lock().unwrap();
|
let mut device = d.lock().unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ pub struct PciConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// See pci_regs.h in kernel
|
/// See pci_regs.h in kernel
|
||||||
#[derive(Copy, Clone, PartialEq, Versionize)]
|
#[derive(Copy, Clone, PartialEq, Versionize, Debug)]
|
||||||
pub enum PciBarRegionType {
|
pub enum PciBarRegionType {
|
||||||
Memory32BitRegion = 0,
|
Memory32BitRegion = 0,
|
||||||
IoRegion = 0x01,
|
IoRegion = 0x01,
|
||||||
@@ -794,9 +794,9 @@ impl PciConfiguration {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(
|
info!(
|
||||||
"DETECT BAR REPROG: current 0x{:x}, new 0x{:x}",
|
"Detected BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}",
|
||||||
self.registers[reg_idx], value
|
reg_idx, self.registers[reg_idx], value
|
||||||
);
|
);
|
||||||
let old_base = u64::from(self.bars[bar_idx].addr & mask);
|
let old_base = u64::from(self.bars[bar_idx].addr & mask);
|
||||||
let new_base = u64::from(value & mask);
|
let new_base = u64::from(value & mask);
|
||||||
@@ -820,9 +820,9 @@ impl PciConfiguration {
|
|||||||
!= (self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1])
|
!= (self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1])
|
||||||
|| (value & mask) != (self.bars[bar_idx].addr & mask))
|
|| (value & mask) != (self.bars[bar_idx].addr & mask))
|
||||||
{
|
{
|
||||||
debug!(
|
info!(
|
||||||
"DETECT BAR REPROG: current 0x{:x}, new 0x{:x}",
|
"Detected BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}",
|
||||||
self.registers[reg_idx], value
|
reg_idx, self.registers[reg_idx], value
|
||||||
);
|
);
|
||||||
let old_base = u64::from(self.bars[bar_idx].addr & mask) << 32
|
let old_base = u64::from(self.bars[bar_idx].addr & mask) << 32
|
||||||
| u64::from(self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1]);
|
| u64::from(self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1]);
|
||||||
@@ -850,9 +850,9 @@ impl PciConfiguration {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(
|
info!(
|
||||||
"DETECT ROM BAR REPROG: current 0x{:x}, new 0x{:x}",
|
"Detected ROM BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}",
|
||||||
self.registers[reg_idx], value
|
reg_idx, self.registers[reg_idx], value
|
||||||
);
|
);
|
||||||
let old_base = u64::from(self.rom_bar_addr & mask);
|
let old_base = u64::from(self.rom_bar_addr & mask);
|
||||||
let new_base = u64::from(value & mask);
|
let new_base = u64::from(value & mask);
|
||||||
@@ -961,7 +961,7 @@ mod tests {
|
|||||||
foo: u8,
|
foo: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
// It is safe to implement BytesValued; all members are simple numbers and any value is valid.
|
// SAFETY: All members are simple numbers and any value is valid.
|
||||||
unsafe impl ByteValued for TestCap {}
|
unsafe impl ByteValued for TestCap {}
|
||||||
|
|
||||||
impl PciCapability for TestCap {
|
impl PciCapability for TestCap {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use std::any::Any;
|
|||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
use std::{self, io, result};
|
use std::{self, io, result};
|
||||||
use vm_allocator::SystemAllocator;
|
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||||
use vm_device::BusDevice;
|
use vm_device::BusDevice;
|
||||||
use vm_memory::{GuestAddress, GuestUsize};
|
use vm_memory::{GuestAddress, GuestUsize};
|
||||||
|
|
||||||
@@ -52,12 +52,17 @@ pub trait PciDevice: BusDevice {
|
|||||||
fn allocate_bars(
|
fn allocate_bars(
|
||||||
&mut self,
|
&mut self,
|
||||||
_allocator: &mut SystemAllocator,
|
_allocator: &mut SystemAllocator,
|
||||||
|
_mmio_allocator: &mut AddressAllocator,
|
||||||
) -> Result<Vec<(GuestAddress, GuestUsize, PciBarRegionType)>> {
|
) -> Result<Vec<(GuestAddress, GuestUsize, PciBarRegionType)>> {
|
||||||
Ok(Vec::new())
|
Ok(Vec::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Frees the PCI BARs previously allocated with a call to allocate_bars().
|
/// Frees the PCI BARs previously allocated with a call to allocate_bars().
|
||||||
fn free_bars(&mut self, _allocator: &mut SystemAllocator) -> Result<()> {
|
fn free_bars(
|
||||||
|
&mut self,
|
||||||
|
_allocator: &mut SystemAllocator,
|
||||||
|
_mmio_allocator: &mut AddressAllocator,
|
||||||
|
) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ mod device;
|
|||||||
mod msi;
|
mod msi;
|
||||||
mod msix;
|
mod msix;
|
||||||
mod vfio;
|
mod vfio;
|
||||||
|
mod vfio_user;
|
||||||
|
|
||||||
pub use self::bus::{PciBus, PciConfigIo, PciConfigMmio, PciRoot, PciRootError};
|
pub use self::bus::{PciBus, PciConfigIo, PciConfigMmio, PciRoot, PciRootError};
|
||||||
pub use self::configuration::{
|
pub use self::configuration::{
|
||||||
@@ -25,6 +26,8 @@ pub use self::device::{
|
|||||||
pub use self::msi::{msi_num_enabled_vectors, MsiCap, MsiConfig};
|
pub use self::msi::{msi_num_enabled_vectors, MsiCap, MsiConfig};
|
||||||
pub use self::msix::{MsixCap, MsixConfig, MsixTableEntry, MSIX_TABLE_ENTRY_SIZE};
|
pub use self::msix::{MsixCap, MsixConfig, MsixTableEntry, MSIX_TABLE_ENTRY_SIZE};
|
||||||
pub use self::vfio::{VfioPciDevice, VfioPciError};
|
pub use self::vfio::{VfioPciDevice, VfioPciError};
|
||||||
|
pub use self::vfio_user::{VfioUserDmaMapping, VfioUserPciDevice, VfioUserPciDeviceError};
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
/// PCI has four interrupt pins A->D.
|
/// PCI has four interrupt pins A->D.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
@@ -40,3 +43,81 @@ impl PciInterruptPin {
|
|||||||
self as u32
|
self as u32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
pub const PCI_CONFIG_IO_PORT: u64 = 0xcf8;
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
pub const PCI_CONFIG_IO_PORT_SIZE: u64 = 0x8;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||||
|
pub struct PciBdf(u32);
|
||||||
|
|
||||||
|
impl PciBdf {
|
||||||
|
pub fn segment(&self) -> u16 {
|
||||||
|
((self.0 >> 16) & 0xffff) as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bus(&self) -> u8 {
|
||||||
|
((self.0 >> 8) & 0xff) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn device(&self) -> u8 {
|
||||||
|
((self.0 >> 3) & 0x1f) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn function(&self) -> u8 {
|
||||||
|
(self.0 & 0x7) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(segment: u16, bus: u8, device: u8, function: u8) -> Self {
|
||||||
|
Self(
|
||||||
|
(segment as u32) << 16
|
||||||
|
| (bus as u32) << 8
|
||||||
|
| ((device & 0x1f) as u32) << 3
|
||||||
|
| (function & 0x7) as u32,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for PciBdf {
|
||||||
|
fn from(bdf: u32) -> Self {
|
||||||
|
Self(bdf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PciBdf> for u32 {
|
||||||
|
fn from(bdf: PciBdf) -> Self {
|
||||||
|
bdf.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&PciBdf> for u32 {
|
||||||
|
fn from(bdf: &PciBdf) -> Self {
|
||||||
|
bdf.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PciBdf> for u16 {
|
||||||
|
fn from(bdf: PciBdf) -> Self {
|
||||||
|
(bdf.0 & 0xffff) as u16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&PciBdf> for u16 {
|
||||||
|
fn from(bdf: &PciBdf) -> Self {
|
||||||
|
(bdf.0 & 0xffff) as u16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for PciBdf {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{:04x}:{:02x}:{:02x}.{:01x}",
|
||||||
|
self.segment(),
|
||||||
|
self.bus(),
|
||||||
|
self.device(),
|
||||||
|
self.function()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -159,11 +159,11 @@ impl MsiCap {
|
|||||||
|
|
||||||
pub struct MsiConfig {
|
pub struct MsiConfig {
|
||||||
cap: MsiCap,
|
cap: MsiCap,
|
||||||
interrupt_source_group: Arc<Box<dyn InterruptSourceGroup>>,
|
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MsiConfig {
|
impl MsiConfig {
|
||||||
pub fn new(msg_ctl: u16, interrupt_source_group: Arc<Box<dyn InterruptSourceGroup>>) -> Self {
|
pub fn new(msg_ctl: u16, interrupt_source_group: Arc<dyn InterruptSourceGroup>) -> Self {
|
||||||
let cap = MsiCap {
|
let cap = MsiCap {
|
||||||
msg_ctl,
|
msg_ctl,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ pub struct MsixConfig {
|
|||||||
pub table_entries: Vec<MsixTableEntry>,
|
pub table_entries: Vec<MsixTableEntry>,
|
||||||
pub pba_entries: Vec<u64>,
|
pub pba_entries: Vec<u64>,
|
||||||
pub devid: u32,
|
pub devid: u32,
|
||||||
interrupt_source_group: Arc<Box<dyn InterruptSourceGroup>>,
|
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
|
||||||
masked: bool,
|
masked: bool,
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ pub struct MsixConfig {
|
|||||||
impl MsixConfig {
|
impl MsixConfig {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
msix_vectors: u16,
|
msix_vectors: u16,
|
||||||
interrupt_source_group: Arc<Box<dyn InterruptSourceGroup>>,
|
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
|
||||||
devid: u32,
|
devid: u32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
assert!(msix_vectors <= MAX_MSIX_VECTORS_PER_DEVICE);
|
assert!(msix_vectors <= MAX_MSIX_VECTORS_PER_DEVICE);
|
||||||
@@ -468,7 +468,7 @@ pub struct MsixCap {
|
|||||||
pub pba: u32,
|
pub pba: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
// It is safe to implement ByteValued. All members are simple numbers and any value is valid.
|
// SAFETY: All members are simple numbers and any value is valid.
|
||||||
unsafe impl ByteValued for MsixCap {}
|
unsafe impl ByteValued for MsixCap {}
|
||||||
|
|
||||||
impl PciCapability for MsixCap {
|
impl PciCapability for MsixCap {
|
||||||
@@ -533,4 +533,16 @@ impl MsixCap {
|
|||||||
pub fn table_size(&self) -> u16 {
|
pub fn table_size(&self) -> u16 {
|
||||||
(self.msg_ctl & 0x7ff) + 1
|
(self.msg_ctl & 0x7ff) + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn table_range(&self) -> (u64, u64) {
|
||||||
|
// The table takes 16 bytes per entry.
|
||||||
|
let size = self.table_size() as u64 * 16;
|
||||||
|
(self.table_offset() as u64, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pba_range(&self) -> (u64, u64) {
|
||||||
|
// The table takes 1 bit per entry modulo 8 bytes.
|
||||||
|
let size = ((self.table_size() as u64 / 64) + 1) * 8;
|
||||||
|
(self.pba_offset() as u64, size)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1656
pci/src/vfio.rs
1656
pci/src/vfio.rs
File diff suppressed because it is too large
Load Diff
563
pci/src/vfio_user.rs
Normal file
563
pci/src/vfio_user.rs
Normal file
@@ -0,0 +1,563 @@
|
|||||||
|
// Copyright © 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use crate::vfio::{Interrupt, Vfio, VfioCommon, VfioError};
|
||||||
|
use crate::{BarReprogrammingParams, PciBarRegionType, VfioPciError};
|
||||||
|
use crate::{
|
||||||
|
PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciSubclass,
|
||||||
|
};
|
||||||
|
use hypervisor::HypervisorVmError;
|
||||||
|
use std::any::Any;
|
||||||
|
use std::os::unix::prelude::AsRawFd;
|
||||||
|
use std::ptr::null_mut;
|
||||||
|
use std::sync::{Arc, Barrier, Mutex};
|
||||||
|
use std::u32;
|
||||||
|
use thiserror::Error;
|
||||||
|
use vfio_bindings::bindings::vfio::*;
|
||||||
|
use vfio_ioctls::VfioIrq;
|
||||||
|
use vfio_user::{Client, Error as VfioUserError};
|
||||||
|
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||||
|
use vm_device::dma_mapping::ExternalDmaMapping;
|
||||||
|
use vm_device::interrupt::{InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig};
|
||||||
|
use vm_device::BusDevice;
|
||||||
|
use vm_memory::bitmap::AtomicBitmap;
|
||||||
|
use vm_memory::{
|
||||||
|
Address, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryRegion, GuestRegionMmap,
|
||||||
|
GuestUsize,
|
||||||
|
};
|
||||||
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
pub struct VfioUserPciDevice {
|
||||||
|
vm: Arc<dyn hypervisor::Vm>,
|
||||||
|
client: Arc<Mutex<Client>>,
|
||||||
|
vfio_wrapper: VfioUserClientWrapper,
|
||||||
|
common: VfioCommon,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum VfioUserPciDeviceError {
|
||||||
|
#[error("Client error: {0}")]
|
||||||
|
Client(#[source] VfioUserError),
|
||||||
|
#[error("Failed to map VFIO PCI region into guest: {0}")]
|
||||||
|
MapRegionGuest(#[source] HypervisorVmError),
|
||||||
|
#[error("Failed to DMA map: {0}")]
|
||||||
|
DmaMap(#[source] VfioUserError),
|
||||||
|
#[error("Failed to DMA unmap: {0}")]
|
||||||
|
DmaUnmap(#[source] VfioUserError),
|
||||||
|
#[error("Failed to initialize legacy interrupts: {0}")]
|
||||||
|
InitializeLegacyInterrupts(#[source] VfioPciError),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
enum PciVfioUserSubclass {
|
||||||
|
VfioUserSubclass = 0xff,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PciSubclass for PciVfioUserSubclass {
|
||||||
|
fn get_register_value(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VfioUserPciDevice {
|
||||||
|
pub fn new(
|
||||||
|
vm: &Arc<dyn hypervisor::Vm>,
|
||||||
|
client: Arc<Mutex<Client>>,
|
||||||
|
msi_interrupt_manager: &Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||||
|
legacy_interrupt_group: Option<Arc<dyn InterruptSourceGroup>>,
|
||||||
|
) -> Result<Self, VfioUserPciDeviceError> {
|
||||||
|
// This is used for the BAR and capabilities only
|
||||||
|
let configuration = PciConfiguration::new(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
PciClassCode::Other,
|
||||||
|
&PciVfioUserSubclass::VfioUserSubclass,
|
||||||
|
None,
|
||||||
|
PciHeaderType::Device,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let resettable = client.lock().unwrap().resettable();
|
||||||
|
if resettable {
|
||||||
|
client
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.reset()
|
||||||
|
.map_err(VfioUserPciDeviceError::Client)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let vfio_wrapper = VfioUserClientWrapper {
|
||||||
|
client: client.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut common = VfioCommon {
|
||||||
|
mmio_regions: Vec::new(),
|
||||||
|
configuration,
|
||||||
|
interrupt: Interrupt {
|
||||||
|
intx: None,
|
||||||
|
msi: None,
|
||||||
|
msix: None,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
common.parse_capabilities(msi_interrupt_manager, &vfio_wrapper);
|
||||||
|
common
|
||||||
|
.initialize_legacy_interrupt(legacy_interrupt_group, &vfio_wrapper)
|
||||||
|
.map_err(VfioUserPciDeviceError::InitializeLegacyInterrupts)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
vm: vm.clone(),
|
||||||
|
client,
|
||||||
|
vfio_wrapper,
|
||||||
|
common,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_mmio_regions<F>(
|
||||||
|
&mut self,
|
||||||
|
vm: &Arc<dyn hypervisor::Vm>,
|
||||||
|
mem_slot: F,
|
||||||
|
) -> Result<(), VfioUserPciDeviceError>
|
||||||
|
where
|
||||||
|
F: Fn() -> u32,
|
||||||
|
{
|
||||||
|
for mmio_region in &mut self.common.mmio_regions {
|
||||||
|
let region_flags = self
|
||||||
|
.client
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.region(mmio_region.index)
|
||||||
|
.unwrap()
|
||||||
|
.flags;
|
||||||
|
let file_offset = self
|
||||||
|
.client
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.region(mmio_region.index)
|
||||||
|
.unwrap()
|
||||||
|
.file_offset
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
if region_flags & VFIO_REGION_INFO_FLAG_MMAP != 0 {
|
||||||
|
let mut prot = 0;
|
||||||
|
if region_flags & VFIO_REGION_INFO_FLAG_READ != 0 {
|
||||||
|
prot |= libc::PROT_READ;
|
||||||
|
}
|
||||||
|
if region_flags & VFIO_REGION_INFO_FLAG_WRITE != 0 {
|
||||||
|
prot |= libc::PROT_WRITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
let host_addr = unsafe {
|
||||||
|
libc::mmap(
|
||||||
|
null_mut(),
|
||||||
|
mmio_region.length as usize,
|
||||||
|
prot,
|
||||||
|
libc::MAP_SHARED,
|
||||||
|
file_offset.as_ref().unwrap().file().as_raw_fd(),
|
||||||
|
file_offset.as_ref().unwrap().start() as libc::off_t,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if host_addr == libc::MAP_FAILED {
|
||||||
|
error!(
|
||||||
|
"Could not mmap regions, error:{}",
|
||||||
|
std::io::Error::last_os_error()
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let slot = mem_slot();
|
||||||
|
let mem_region = vm.make_user_memory_region(
|
||||||
|
slot,
|
||||||
|
mmio_region.start.0,
|
||||||
|
mmio_region.length as u64,
|
||||||
|
host_addr as u64,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
vm.create_user_memory_region(mem_region)
|
||||||
|
.map_err(VfioUserPciDeviceError::MapRegionGuest)?;
|
||||||
|
|
||||||
|
mmio_region.mem_slot = Some(slot);
|
||||||
|
mmio_region.host_addr = Some(host_addr as u64);
|
||||||
|
mmio_region.mmap_size = Some(mmio_region.length as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unmap_mmio_regions(&mut self) {
|
||||||
|
for mmio_region in self.common.mmio_regions.iter() {
|
||||||
|
if let (Some(host_addr), Some(mmap_size), Some(mem_slot)) = (
|
||||||
|
mmio_region.host_addr,
|
||||||
|
mmio_region.mmap_size,
|
||||||
|
mmio_region.mem_slot,
|
||||||
|
) {
|
||||||
|
// Remove region
|
||||||
|
let r = self.vm.make_user_memory_region(
|
||||||
|
mem_slot,
|
||||||
|
mmio_region.start.raw_value(),
|
||||||
|
mmap_size as u64,
|
||||||
|
host_addr as u64,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Err(e) = self.vm.remove_user_memory_region(r) {
|
||||||
|
error!("Could not remove the userspace memory region: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = unsafe { libc::munmap(host_addr as *mut libc::c_void, mmap_size) };
|
||||||
|
if ret != 0 {
|
||||||
|
error!(
|
||||||
|
"Could not unmap region {}, error:{}",
|
||||||
|
mmio_region.index,
|
||||||
|
std::io::Error::last_os_error()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dma_map(
|
||||||
|
&mut self,
|
||||||
|
region: &GuestRegionMmap<AtomicBitmap>,
|
||||||
|
) -> Result<(), VfioUserPciDeviceError> {
|
||||||
|
let (fd, offset) = match region.file_offset() {
|
||||||
|
Some(_file_offset) => (_file_offset.file().as_raw_fd(), _file_offset.start()),
|
||||||
|
None => return Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.client
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.dma_map(
|
||||||
|
offset,
|
||||||
|
region.start_addr().raw_value(),
|
||||||
|
region.len() as u64,
|
||||||
|
fd,
|
||||||
|
)
|
||||||
|
.map_err(VfioUserPciDeviceError::DmaMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dma_unmap(
|
||||||
|
&mut self,
|
||||||
|
region: &GuestRegionMmap<AtomicBitmap>,
|
||||||
|
) -> Result<(), VfioUserPciDeviceError> {
|
||||||
|
self.client
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.dma_unmap(region.start_addr().raw_value(), region.len() as u64)
|
||||||
|
.map_err(VfioUserPciDeviceError::DmaUnmap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BusDevice for VfioUserPciDevice {
|
||||||
|
fn read(&mut self, base: u64, offset: u64, data: &mut [u8]) {
|
||||||
|
self.read_bar(base, offset, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||||
|
self.write_bar(base, offset, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
enum Regions {
|
||||||
|
Bar0,
|
||||||
|
Bar1,
|
||||||
|
Bar2,
|
||||||
|
Bar3,
|
||||||
|
Bar4,
|
||||||
|
Bar5,
|
||||||
|
Rom,
|
||||||
|
Config,
|
||||||
|
Vga,
|
||||||
|
Migration,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VfioUserClientWrapper {
|
||||||
|
client: Arc<Mutex<Client>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vfio for VfioUserClientWrapper {
|
||||||
|
fn region_read(&self, index: u32, offset: u64, data: &mut [u8]) {
|
||||||
|
self.client
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.region_read(index, offset, data)
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn region_write(&self, index: u32, offset: u64, data: &[u8]) {
|
||||||
|
self.client
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.region_write(index, offset, data)
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_irq_info(&self, irq_index: u32) -> Option<VfioIrq> {
|
||||||
|
self.client
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.get_irq_info(irq_index)
|
||||||
|
.ok()
|
||||||
|
.map(|i| VfioIrq {
|
||||||
|
index: i.index,
|
||||||
|
flags: i.flags,
|
||||||
|
count: i.count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enable_irq(&self, irq_index: u32, event_fds: Vec<&EventFd>) -> Result<(), VfioError> {
|
||||||
|
info!(
|
||||||
|
"Enabling IRQ {:x} number of fds = {:?}",
|
||||||
|
irq_index,
|
||||||
|
event_fds.len()
|
||||||
|
);
|
||||||
|
let fds: Vec<i32> = event_fds.iter().map(|e| e.as_raw_fd()).collect();
|
||||||
|
|
||||||
|
// Batch into blocks of 16 fds as sendmsg() has a size limit
|
||||||
|
let mut sent_fds = 0;
|
||||||
|
let num_fds = event_fds.len() as u32;
|
||||||
|
while sent_fds < num_fds {
|
||||||
|
let remaining_fds = num_fds - sent_fds;
|
||||||
|
let count = if remaining_fds > 16 {
|
||||||
|
16
|
||||||
|
} else {
|
||||||
|
remaining_fds
|
||||||
|
};
|
||||||
|
|
||||||
|
self.client
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.set_irqs(
|
||||||
|
irq_index,
|
||||||
|
VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER,
|
||||||
|
sent_fds,
|
||||||
|
count,
|
||||||
|
&fds[sent_fds as usize..(sent_fds + count) as usize],
|
||||||
|
)
|
||||||
|
.map_err(VfioError::VfioUser)?;
|
||||||
|
|
||||||
|
sent_fds += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disable_irq(&self, irq_index: u32) -> Result<(), VfioError> {
|
||||||
|
info!("Disabling IRQ {:x}", irq_index);
|
||||||
|
self.client
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.set_irqs(
|
||||||
|
irq_index,
|
||||||
|
VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
.map_err(VfioError::VfioUser)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unmask_irq(&self, irq_index: u32) -> Result<(), VfioError> {
|
||||||
|
info!("Unmasking IRQ {:x}", irq_index);
|
||||||
|
self.client
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.set_irqs(
|
||||||
|
irq_index,
|
||||||
|
VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
.map_err(VfioError::VfioUser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PciDevice for VfioUserPciDevice {
|
||||||
|
fn allocate_bars(
|
||||||
|
&mut self,
|
||||||
|
allocator: &mut SystemAllocator,
|
||||||
|
mmio_allocator: &mut AddressAllocator,
|
||||||
|
) -> Result<Vec<(GuestAddress, GuestUsize, PciBarRegionType)>, PciDeviceError> {
|
||||||
|
self.common
|
||||||
|
.allocate_bars(allocator, mmio_allocator, &self.vfio_wrapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn free_bars(
|
||||||
|
&mut self,
|
||||||
|
allocator: &mut SystemAllocator,
|
||||||
|
mmio_allocator: &mut AddressAllocator,
|
||||||
|
) -> Result<(), PciDeviceError> {
|
||||||
|
self.common.free_bars(allocator, mmio_allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&mut self) -> &mut dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detect_bar_reprogramming(
|
||||||
|
&mut self,
|
||||||
|
reg_idx: usize,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Option<BarReprogrammingParams> {
|
||||||
|
self.common
|
||||||
|
.configuration
|
||||||
|
.detect_bar_reprogramming(reg_idx, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_config_register(
|
||||||
|
&mut self,
|
||||||
|
reg_idx: usize,
|
||||||
|
offset: u64,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Option<Arc<Barrier>> {
|
||||||
|
self.common
|
||||||
|
.write_config_register(reg_idx, offset, data, &self.vfio_wrapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
|
||||||
|
self.common
|
||||||
|
.read_config_register(reg_idx, &self.vfio_wrapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_bar(&mut self, base: u64, offset: u64, data: &mut [u8]) {
|
||||||
|
self.common.read_bar(base, offset, data, &self.vfio_wrapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_bar(&mut self, base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||||
|
self.common
|
||||||
|
.write_bar(base, offset, data, &self.vfio_wrapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_bar(&mut self, old_base: u64, new_base: u64) -> Result<(), std::io::Error> {
|
||||||
|
info!("Moving BAR 0x{:x} -> 0x{:x}", old_base, new_base);
|
||||||
|
for mmio_region in self.common.mmio_regions.iter_mut() {
|
||||||
|
if mmio_region.start.raw_value() == old_base {
|
||||||
|
mmio_region.start = GuestAddress(new_base);
|
||||||
|
|
||||||
|
if let Some(mem_slot) = mmio_region.mem_slot {
|
||||||
|
if let Some(host_addr) = mmio_region.host_addr {
|
||||||
|
// Remove original region
|
||||||
|
let old_region = self.vm.make_user_memory_region(
|
||||||
|
mem_slot,
|
||||||
|
old_base,
|
||||||
|
mmio_region.length as u64,
|
||||||
|
host_addr as u64,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.vm
|
||||||
|
.remove_user_memory_region(old_region)
|
||||||
|
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||||
|
|
||||||
|
let new_region = self.vm.make_user_memory_region(
|
||||||
|
mem_slot,
|
||||||
|
new_base,
|
||||||
|
mmio_region.length as u64,
|
||||||
|
host_addr as u64,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.vm
|
||||||
|
.create_user_memory_region(new_region)
|
||||||
|
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info!("Moved bar 0x{:x} -> 0x{:x}", old_base, new_base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for VfioUserPciDevice {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.unmap_mmio_regions();
|
||||||
|
|
||||||
|
if let Some(msix) = &self.common.interrupt.msix {
|
||||||
|
if msix.bar.enabled() {
|
||||||
|
self.common.disable_msix(&self.vfio_wrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(msi) = &self.common.interrupt.msi {
|
||||||
|
if msi.cfg.enabled() {
|
||||||
|
self.common.disable_msi(&self.vfio_wrapper)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.common.interrupt.intx_in_use() {
|
||||||
|
self.common.disable_intx(&self.vfio_wrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VfioUserDmaMapping<M: GuestAddressSpace> {
|
||||||
|
client: Arc<Mutex<Client>>,
|
||||||
|
memory: Arc<M>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M: GuestAddressSpace> VfioUserDmaMapping<M> {
|
||||||
|
pub fn new(client: Arc<Mutex<Client>>, memory: Arc<M>) -> Self {
|
||||||
|
Self { client, memory }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M: GuestAddressSpace + Sync + Send> ExternalDmaMapping for VfioUserDmaMapping<M> {
|
||||||
|
fn map(&self, iova: u64, gpa: u64, size: u64) -> std::result::Result<(), std::io::Error> {
|
||||||
|
let mem = self.memory.memory();
|
||||||
|
let guest_addr = GuestAddress(gpa);
|
||||||
|
let region = mem.find_region(guest_addr);
|
||||||
|
|
||||||
|
if let Some(region) = region {
|
||||||
|
let file_offset = region.file_offset().unwrap();
|
||||||
|
let offset = (GuestAddress(gpa).checked_offset_from(region.start_addr())).unwrap()
|
||||||
|
+ file_offset.start();
|
||||||
|
|
||||||
|
self.client
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.dma_map(offset, iova, size, file_offset.file().as_raw_fd())
|
||||||
|
.map_err(|e| {
|
||||||
|
std::io::Error::new(
|
||||||
|
std::io::ErrorKind::Other,
|
||||||
|
format!("Error mapping region: {}", e),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::Other,
|
||||||
|
format!("Region not found for 0x{:x}", gpa),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unmap(&self, iova: u64, size: u64) -> std::result::Result<(), std::io::Error> {
|
||||||
|
self.client
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.dma_unmap(iova, size)
|
||||||
|
.map_err(|e| {
|
||||||
|
std::io::Error::new(
|
||||||
|
std::io::ErrorKind::Other,
|
||||||
|
format!("Error unmapping region: {}", e),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ path = "src/qcow.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
libc = "0.2.96"
|
libc = "0.2.108"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
remain = "0.2.2"
|
remain = "0.2.2"
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = "0.9.0"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user