mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
838 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ba6a9bfcf | ||
|
|
362f95c825 | ||
|
|
a02387862d | ||
|
|
ee5c2d2765 | ||
|
|
cd0208fe0a | ||
|
|
eea9bcea38 | ||
|
|
45fa8be97a | ||
|
|
f0c55f5245 | ||
|
|
4779265453 | ||
|
|
0b9b46bbc8 | ||
|
|
06eb82d239 | ||
|
|
b8503b5f45 | ||
|
|
3bf3cca70a | ||
|
|
903c08f8a1 | ||
|
|
44d66ec531 | ||
|
|
ac77d0923f | ||
|
|
ba3debaa27 | ||
|
|
1ff26fbb8a | ||
|
|
4aa5d5a2c1 | ||
|
|
023c77bdf3 | ||
|
|
e2a7972c59 | ||
|
|
a4791263c9 | ||
|
|
b2d1dd65f3 | ||
|
|
36e26f004c | ||
|
|
66c092e69b | ||
|
|
76903e5fd0 | ||
|
|
cf31a303cf | ||
|
|
ef603fde4c | ||
|
|
7a0bfd2e71 | ||
|
|
98aab37b00 | ||
|
|
194b59f44b | ||
|
|
c98bd2fd4a | ||
|
|
e53419ecfc | ||
|
|
5e811a6823 | ||
|
|
1202b9a07a | ||
|
|
b35e6f3018 | ||
|
|
56f163170c | ||
|
|
98c461e932 | ||
|
|
76dbf85b79 | ||
|
|
d95220108a | ||
|
|
5a9fe9855a | ||
|
|
2d0ba3637a | ||
|
|
fdb4b32876 | ||
|
|
f38056fc9e | ||
|
|
430bfd38be | ||
|
|
2b53de58b3 | ||
|
|
d4d0780b89 | ||
|
|
a375e230b8 | ||
|
|
f32487f8e8 | ||
|
|
ab0b3f1b7b | ||
|
|
e1b483fc88 | ||
|
|
f815fcbb5d | ||
|
|
6a67675442 | ||
|
|
50c3aa775b | ||
|
|
14865a58a9 | ||
|
|
7d5f53439c | ||
|
|
5123d0b1e1 | ||
|
|
beee45a1ce | ||
|
|
5f4a6b42c0 | ||
|
|
134e555c42 | ||
|
|
18f1be0fac | ||
|
|
001f10d614 | ||
|
|
1a2cd44e44 | ||
|
|
1849ffff31 | ||
|
|
0e52be0909 | ||
|
|
19b006eb84 | ||
|
|
a5f655a032 | ||
|
|
a4f781e142 | ||
|
|
1c1bff93a1 | ||
|
|
ec384af29e | ||
|
|
f7a013da92 | ||
|
|
5dfd64e9e2 | ||
|
|
77bc86c8d3 | ||
|
|
3e16c2e257 | ||
|
|
9fe1643089 | ||
|
|
cb00dbe6ec | ||
|
|
253d16b4c9 | ||
|
|
2656b9b925 | ||
|
|
5aab82ccd2 | ||
|
|
43c4270289 | ||
|
|
651506bfe5 | ||
|
|
d6013904a8 | ||
|
|
91b8b00f95 | ||
|
|
67a89f4538 | ||
|
|
1d3bb63ec8 | ||
|
|
c8fe101b6f | ||
|
|
ef1983ee60 | ||
|
|
d5219525d8 | ||
|
|
af0a01c2fb | ||
|
|
652aa39934 | ||
|
|
a15b641c61 | ||
|
|
d8a6725995 | ||
|
|
ecaff8ff1b | ||
|
|
06d7796190 | ||
|
|
4e589d2e13 | ||
|
|
bf638a45de | ||
|
|
1d29c31e6b | ||
|
|
b84f41eac4 | ||
|
|
e5287d27b2 | ||
|
|
522e32219c | ||
|
|
4cc3bdba9f | ||
|
|
9d3ecefc00 | ||
|
|
561af4fd1a | ||
|
|
e0634c48d0 | ||
|
|
e23319426d | ||
|
|
df6f1ea40c | ||
|
|
b25f640ef5 | ||
|
|
f37d07c724 | ||
|
|
92713f7c5e | ||
|
|
60f3bd43f5 | ||
|
|
e145b823a5 | ||
|
|
ff3e9eb668 | ||
|
|
e3d7824f09 | ||
|
|
d5481cddcf | ||
|
|
4e91199ff8 | ||
|
|
1817224fe9 | ||
|
|
180251f057 | ||
|
|
fae801cd70 | ||
|
|
2a222b2692 | ||
|
|
1435a58c1e | ||
|
|
d6f8411635 | ||
|
|
52d1b5f753 | ||
|
|
3b5c2b3718 | ||
|
|
62681a776e | ||
|
|
3793ffe888 | ||
|
|
b05bd28321 | ||
|
|
514ec79368 | ||
|
|
39c1102c60 | ||
|
|
4465bbaf38 | ||
|
|
b3bef3adda | ||
|
|
8e630710f9 | ||
|
|
688602f3fc | ||
|
|
7e4341d9e3 | ||
|
|
817fa0dc81 | ||
|
|
0322a43312 | ||
|
|
75e4ee5362 | ||
|
|
3c6014554b | ||
|
|
6cf4ea74fc | ||
|
|
742d6858f7 | ||
|
|
dc2365bfd0 | ||
|
|
41e22a7d03 | ||
|
|
8d68e78957 | ||
|
|
33d9ed9a66 | ||
|
|
fb5c792da6 | ||
|
|
7b9e826c2c | ||
|
|
1d164abba4 | ||
|
|
784a3aaf3c | ||
|
|
a832033531 | ||
|
|
3a19573c69 | ||
|
|
b65639fad3 | ||
|
|
9b1452f258 | ||
|
|
2054c8699a | ||
|
|
c38d167523 | ||
|
|
b04baf6017 | ||
|
|
6cb214f15c | ||
|
|
a9924df2b8 | ||
|
|
616ec530a8 | ||
|
|
850c0c8319 | ||
|
|
8c02648ac9 | ||
|
|
a391bce781 | ||
|
|
9d2e835d14 | ||
|
|
a940f525a8 | ||
|
|
63462fd8ab | ||
|
|
2306291720 | ||
|
|
e833679428 | ||
|
|
956907df97 | ||
|
|
79be8b6216 | ||
|
|
0d5b68d79c | ||
|
|
31df1bcac0 | ||
|
|
0bcb6ff061 | ||
|
|
d66d64c325 | ||
|
|
223b1f6c51 | ||
|
|
3126b351c1 | ||
|
|
bfe5aca254 | ||
|
|
fba95cbc4c | ||
|
|
86dc1b50e3 | ||
|
|
3e6b0a5eab | ||
|
|
88bbf7989a | ||
|
|
c798b958f3 | ||
|
|
575458ded4 | ||
|
|
0522e40933 | ||
|
|
5febdec81a | ||
|
|
baa58ad8bf | ||
|
|
fdc8546eef | ||
|
|
cdcd4d259e | ||
|
|
1ee41a98de | ||
|
|
b4fe41ad0c | ||
|
|
0b15e378c7 | ||
|
|
7f51357812 | ||
|
|
6fefca39e6 | ||
|
|
0b7d2caf76 | ||
|
|
396f9ce2c6 | ||
|
|
b49f8b9248 | ||
|
|
31dfe3d02a | ||
|
|
476e30f5cb | ||
|
|
ccc88ccfd2 | ||
|
|
ed9e54d6c7 | ||
|
|
282a1001ef | ||
|
|
0d682e185f | ||
|
|
8ec5a248cd | ||
|
|
763ea7da42 | ||
|
|
2856074d12 | ||
|
|
485900eeb4 | ||
|
|
6988da79d2 | ||
|
|
80f7d62e1c | ||
|
|
4c5c7fb77c | ||
|
|
1b8b2fc1af | ||
|
|
7a911db4fa | ||
|
|
8f3462b3e3 | ||
|
|
1f70d16c12 | ||
|
|
25f815e7d1 | ||
|
|
76bde33fc4 | ||
|
|
9591e225e6 | ||
|
|
df5b803a63 | ||
|
|
8c38992143 | ||
|
|
068ea9a4db | ||
|
|
b1752994d5 | ||
|
|
a7f3620564 | ||
|
|
54c484397e | ||
|
|
f9b36a3412 | ||
|
|
1793c1cb39 | ||
|
|
b67755be17 | ||
|
|
4a2539cb92 | ||
|
|
f39b08f21f | ||
|
|
6a1e637a46 | ||
|
|
041acf2a65 | ||
|
|
2ee7c663c2 | ||
|
|
b865b059aa | ||
|
|
1012530bdd | ||
|
|
5b60e5372b | ||
|
|
dc540a7549 | ||
|
|
6c492d8512 | ||
|
|
944d09208e | ||
|
|
c591a96ecb | ||
|
|
eb0ce4b4d6 | ||
|
|
c59b5e18a8 | ||
|
|
98f949d35d | ||
|
|
0b182be65e | ||
|
|
fbec4a070d | ||
|
|
5ba3b80e83 | ||
|
|
1fda830961 | ||
|
|
8c22c03e1e | ||
|
|
51fdc48817 | ||
|
|
999bc2baca | ||
|
|
072af95609 | ||
|
|
6fda567ab8 | ||
|
|
cef51a9de0 | ||
|
|
5810e3bee8 | ||
|
|
6d452fad52 | ||
|
|
561e6a5b6e | ||
|
|
3d9c9d720f | ||
|
|
dde2eb531b | ||
|
|
2cc21d00e2 | ||
|
|
4e31206403 | ||
|
|
00c1b4a95b | ||
|
|
f42c6d567d | ||
|
|
ac0aa8aa5e | ||
|
|
8a7f3425d8 | ||
|
|
c5fdc47918 | ||
|
|
6bc46ba9c1 | ||
|
|
3c26e9b741 | ||
|
|
226a21811e | ||
|
|
79cf3952f3 | ||
|
|
e45e3df64d | ||
|
|
9f89e0a4e0 | ||
|
|
19957d4fa4 | ||
|
|
ea58d2f68a | ||
|
|
71a7d5d85e | ||
|
|
531621474f | ||
|
|
46b790b582 | ||
|
|
9990439829 | ||
|
|
d295de4cd5 | ||
|
|
868d1f6902 | ||
|
|
62f1b6bc61 | ||
|
|
2afd2f9f52 | ||
|
|
a85d27bd52 | ||
|
|
53aecf9341 | ||
|
|
57e9b80123 | ||
|
|
964985bb5c | ||
|
|
cb18edb535 | ||
|
|
05e0daa411 | ||
|
|
a62b611659 | ||
|
|
dc75519372 | ||
|
|
d9ee5c2640 | ||
|
|
ec4a55b860 | ||
|
|
3df8c6fd3b | ||
|
|
474125db12 | ||
|
|
a3bb459bff | ||
|
|
3d947fafe5 | ||
|
|
7d9a704474 | ||
|
|
47f9ee418d | ||
|
|
38c25ea5df | ||
|
|
21da11cee7 | ||
|
|
bb444f3e7c | ||
|
|
3b0f6247b7 | ||
|
|
9b4940f154 | ||
|
|
4bf01fa366 | ||
|
|
4052a89268 | ||
|
|
73efcc1f73 | ||
|
|
b3b252a5d1 | ||
|
|
65f42c1f62 | ||
|
|
bc3a276b43 | ||
|
|
3abc1e1e51 | ||
|
|
147e89f413 | ||
|
|
1327350b4e | ||
|
|
9d31856c08 | ||
|
|
9f457887df | ||
|
|
4269fb1366 | ||
|
|
d889bf625d | ||
|
|
00b4357da3 | ||
|
|
579d0d56e3 | ||
|
|
64ac6ae1a3 | ||
|
|
8ca7318046 | ||
|
|
ec16e14b2f | ||
|
|
af08f6bca5 | ||
|
|
1867ee3ddd | ||
|
|
4e0b41ce77 | ||
|
|
b731c8863f | ||
|
|
8d86cffc87 | ||
|
|
1a09381d1e | ||
|
|
103494f441 | ||
|
|
1125fd2667 | ||
|
|
e5155bab62 | ||
|
|
eb056d374a | ||
|
|
487458c9f8 | ||
|
|
4d74525bdc | ||
|
|
9524a8ffe8 | ||
|
|
fa0565234c | ||
|
|
755eb80c49 | ||
|
|
ac3ab19443 | ||
|
|
8585d1a355 | ||
|
|
5f539e429a | ||
|
|
6835de0f8c | ||
|
|
bf662522dd | ||
|
|
575221cba9 | ||
|
|
9b7af90b4c | ||
|
|
b4ed9f867e | ||
|
|
af958c9448 | ||
|
|
a4859ffe85 | ||
|
|
87f57f7c1e | ||
|
|
a423bf13ad | ||
|
|
7199119bb2 | ||
|
|
5b54dc60aa | ||
|
|
ecb66b5e94 | ||
|
|
cd7f36a713 | ||
|
|
f7b6d99c2d | ||
|
|
e1a70f676a | ||
|
|
bec47ebcc9 | ||
|
|
ddc9004471 | ||
|
|
0c9b4fefc0 | ||
|
|
df10dab952 | ||
|
|
57ee561c39 | ||
|
|
a330c531b0 | ||
|
|
e4211272ad | ||
|
|
f4e2198767 | ||
|
|
f8f0b40e94 | ||
|
|
87c0791d53 | ||
|
|
c46a9592c0 | ||
|
|
561791ed22 | ||
|
|
857edc71a9 | ||
|
|
0e29379bcf | ||
|
|
23352c4945 | ||
|
|
160528b6f5 | ||
|
|
00200296ec | ||
|
|
c612886150 | ||
|
|
a749182777 | ||
|
|
2e8eb96ef6 | ||
|
|
686e6d5082 | ||
|
|
b1a87cb698 | ||
|
|
869e96ed10 | ||
|
|
bcaa299c55 | ||
|
|
454964cc90 | ||
|
|
2afd0e626a | ||
|
|
e885c35a54 | ||
|
|
ff7773044c | ||
|
|
5aaa647639 | ||
|
|
7b99bd9496 | ||
|
|
bb19c3d2b7 | ||
|
|
aa66526ea0 | ||
|
|
c3ce5aa5b1 | ||
|
|
ad33f7c5e6 | ||
|
|
9fc3379e8d | ||
|
|
d56263706d | ||
|
|
4a00371fe8 | ||
|
|
ae7f74d0a8 | ||
|
|
cb6a14dec9 | ||
|
|
e931c40e96 | ||
|
|
a96a5d7816 | ||
|
|
5e2c70b87f | ||
|
|
eca6609dbb | ||
|
|
c5e966c972 | ||
|
|
0856ebbd42 | ||
|
|
422bf89d4d | ||
|
|
500d91311f | ||
|
|
b5270e0b45 | ||
|
|
f84ddedb1a | ||
|
|
b5d10eb28b | ||
|
|
de14db7320 | ||
|
|
269b78ca0f | ||
|
|
5f98710471 | ||
|
|
613c60fc6f | ||
|
|
3f62a172b2 | ||
|
|
423c54eafe | ||
|
|
e1a63822fa | ||
|
|
3083da154e | ||
|
|
5c20869dd5 | ||
|
|
ec27e0ee82 | ||
|
|
0bb509e5d2 | ||
|
|
a161e56bef | ||
|
|
777ba6d577 | ||
|
|
b0258c7b13 | ||
|
|
55c9562c4d | ||
|
|
7419a021cd | ||
|
|
cc9908efe2 | ||
|
|
474e55724f | ||
|
|
b440cb7d23 | ||
|
|
f21fc1dcb6 | ||
|
|
4d2cc3778f | ||
|
|
563919fc4a | ||
|
|
05e5106b9b | ||
|
|
d461daa7fa | ||
|
|
58dbf07453 | ||
|
|
c8d9a43072 | ||
|
|
6a8c0fc887 | ||
|
|
08135fa085 | ||
|
|
45fbf840db | ||
|
|
9fdfdf25fb | ||
|
|
f8f2f09a19 | ||
|
|
edf6dda705 | ||
|
|
f1ab86fecb | ||
|
|
d2b194c4f1 | ||
|
|
75797827d5 | ||
|
|
8b7781e267 | ||
|
|
d20f647b32 | ||
|
|
2de7121680 | ||
|
|
709958be34 | ||
|
|
4201bf4011 | ||
|
|
beb4f86b82 | ||
|
|
72d552e5e1 | ||
|
|
dd592e86fd | ||
|
|
f9f0a60dcf | ||
|
|
9810ed4496 | ||
|
|
02866fccb0 | ||
|
|
a455917db5 | ||
|
|
4a1eab11b4 | ||
|
|
f0ad7fc74f | ||
|
|
2d8635f04a | ||
|
|
c445513976 | ||
|
|
fc2cc53d51 | ||
|
|
0e8769d76a | ||
|
|
47b5581c50 | ||
|
|
3710932248 | ||
|
|
ddad5f3510 | ||
|
|
28683b8471 | ||
|
|
e1cf889dbd | ||
|
|
5894b5370c | ||
|
|
fabc940b65 | ||
|
|
6b9c5d21c4 | ||
|
|
c140fd6220 | ||
|
|
7063c41811 | ||
|
|
92f9866e2f | ||
|
|
23935ad96b | ||
|
|
335fd465fe | ||
|
|
b8e5c2ecca | ||
|
|
a99c33ccfa | ||
|
|
b70f2c5385 | ||
|
|
84bbaf06d1 | ||
|
|
121729a3b0 | ||
|
|
e300c3dcf7 | ||
|
|
b132cd2145 | ||
|
|
a650b8645f | ||
|
|
f94a5fb4e3 | ||
|
|
00800b8499 | ||
|
|
0b311f6c4c | ||
|
|
8063c00e67 | ||
|
|
4f611c8654 | ||
|
|
d291b87288 | ||
|
|
51787d30da | ||
|
|
63579341e1 | ||
|
|
9c0872def7 | ||
|
|
5dfc49c9fa | ||
|
|
65f8c3682f | ||
|
|
c87466eea2 | ||
|
|
6c365617ee | ||
|
|
30da293d8f | ||
|
|
67bfd4e234 | ||
|
|
46bbe0e612 | ||
|
|
216f330f4e | ||
|
|
bd075872d4 | ||
|
|
7fd5f41623 | ||
|
|
93237f0106 | ||
|
|
0c82e7593d | ||
|
|
1999ab5707 | ||
|
|
5c22f02f32 | ||
|
|
c7afb44c5a | ||
|
|
b0afeeeb65 | ||
|
|
cb53a8b40c | ||
|
|
adf5881757 | ||
|
|
44eca31642 | ||
|
|
b57d7b258d | ||
|
|
d7decfd525 | ||
|
|
2716bc3311 | ||
|
|
9cf0dee264 | ||
|
|
d8b3d07825 | ||
|
|
3d66e86604 | ||
|
|
a2164873ac | ||
|
|
7f8cf4f8e7 | ||
|
|
aedbd7ae27 | ||
|
|
bb4461e951 | ||
|
|
54fe5fb9f5 | ||
|
|
5a9e7ee7fe | ||
|
|
2e664dca64 | ||
|
|
a26f86f8ad | ||
|
|
fde13889f4 | ||
|
|
65ec6631fb | ||
|
|
6ac8dc8745 | ||
|
|
2f514523dd | ||
|
|
92f559f6f2 | ||
|
|
3972e38371 | ||
|
|
a1a016526f | ||
|
|
a5692a95c1 | ||
|
|
0fd25b3246 | ||
|
|
e6127df703 | ||
|
|
559657eecc | ||
|
|
66f1ec0063 | ||
|
|
13d081ac47 | ||
|
|
b12f4ccade | ||
|
|
1f8bca4840 | ||
|
|
833eb2ebe6 | ||
|
|
7c7f0f1f5f | ||
|
|
d445ec7633 | ||
|
|
039e758f15 | ||
|
|
eb55d0809e | ||
|
|
ecde44617b | ||
|
|
58e567b309 | ||
|
|
fa0140a183 | ||
|
|
c6c5e4ee87 | ||
|
|
a64bdcf3f5 | ||
|
|
439823e7ea | ||
|
|
9019086625 | ||
|
|
bccd7c7e48 | ||
|
|
f1dc7f442a | ||
|
|
32b855df3a | ||
|
|
8fa1098629 | ||
|
|
41cec53bb8 | ||
|
|
fd9f14893b | ||
|
|
7c8719b4ea | ||
|
|
335a4e1cc0 | ||
|
|
6dbbd0363f | ||
|
|
40e1ad3f62 | ||
|
|
094625242d | ||
|
|
b3569f084d | ||
|
|
3800212def | ||
|
|
e9f5052fcd | ||
|
|
a583b055f5 | ||
|
|
2b33e66bb4 | ||
|
|
92ceef52c8 | ||
|
|
d7baa58e11 | ||
|
|
2d39b6ed06 | ||
|
|
ed31ca9041 | ||
|
|
06b880e99f | ||
|
|
742ca68070 | ||
|
|
f950cb1fdb | ||
|
|
c6c5dca651 | ||
|
|
d646e10079 | ||
|
|
efe0429a9a | ||
|
|
1f3321c50e | ||
|
|
c0d51eecf1 | ||
|
|
1b9dc80469 | ||
|
|
43d32a7288 | ||
|
|
27e04e41bf | ||
|
|
41ac48d332 | ||
|
|
2afaed9111 | ||
|
|
f1ea69474d | ||
|
|
cac42301f8 | ||
|
|
ada85f68f6 | ||
|
|
64b4b7205e | ||
|
|
4a4352f9d9 | ||
|
|
81ba70a497 | ||
|
|
7df7061610 | ||
|
|
c021dda267 | ||
|
|
f48b05eee6 | ||
|
|
3b59e57001 | ||
|
|
49069d8474 | ||
|
|
8eaefa6e8e | ||
|
|
a1b996ac37 | ||
|
|
4ddc6ffd73 | ||
|
|
94fb9f817d | ||
|
|
133a5a858a | ||
|
|
3a18860326 | ||
|
|
0d0013c46e | ||
|
|
86d243938e | ||
|
|
a7a15d56dd | ||
|
|
65dc1c83a9 | ||
|
|
3edaa8adb6 | ||
|
|
9260c3816e | ||
|
|
5d45d6d0fb | ||
|
|
957d3a7443 | ||
|
|
04949755c0 | ||
|
|
b8dbb26647 | ||
|
|
3fe7d61a02 | ||
|
|
c2862b6947 | ||
|
|
03b01c02cd | ||
|
|
435d769511 | ||
|
|
9ba6767a90 | ||
|
|
3742cc42ae | ||
|
|
e0dd2e41c2 | ||
|
|
f1495b5767 | ||
|
|
6bd4bdfa26 | ||
|
|
ade3a9c8f6 | ||
|
|
15e2763753 | ||
|
|
8b87b752b5 | ||
|
|
7864f91a2b | ||
|
|
6ab93a08ec | ||
|
|
fd41892065 | ||
|
|
dbeb922882 | ||
|
|
0bb2060ca7 | ||
|
|
8b585b96c1 | ||
|
|
ccb604e1e1 | ||
|
|
0e65ca4a6c | ||
|
|
7e280b6f70 | ||
|
|
90034fd6ba | ||
|
|
642309f141 | ||
|
|
5da8a1c08e | ||
|
|
465db7f08c | ||
|
|
55c5961f43 | ||
|
|
7c3582b4a8 | ||
|
|
979797786d | ||
|
|
62120f9afe | ||
|
|
56c11600db | ||
|
|
f2336413be | ||
|
|
0fd6521759 | ||
|
|
4a2d3cb4f5 | ||
|
|
3fe20cc09a | ||
|
|
fd581b1afb | ||
|
|
1e732bf532 | ||
|
|
ad8295e11d | ||
|
|
fe1f0ef49e | ||
|
|
f9fcc3722f | ||
|
|
c0cbaff519 | ||
|
|
ebaea6fe0f | ||
|
|
0150de55d2 | ||
|
|
75e3a3d982 | ||
|
|
3b10699743 | ||
|
|
d0798aad59 | ||
|
|
e52175c2ab | ||
|
|
ffd1411e7c | ||
|
|
a14ebbb758 | ||
|
|
02358d3091 | ||
|
|
025447e8cc | ||
|
|
d582121143 | ||
|
|
38b8e387ea | ||
|
|
3538afc1fe | ||
|
|
d96614fe42 | ||
|
|
7076684124 | ||
|
|
fa07d83565 | ||
|
|
80f6dda932 | ||
|
|
f160572f9d | ||
|
|
9626288d4f | ||
|
|
e2f9c4e756 | ||
|
|
e674577ea9 | ||
|
|
49db713124 | ||
|
|
f0aa70c78b | ||
|
|
db3bd6bb16 | ||
|
|
5e7a77e5f9 | ||
|
|
ea753b4f32 | ||
|
|
5587245bf2 | ||
|
|
bc5f70d1ed | ||
|
|
0e16ffbcff | ||
|
|
3a0429c998 | ||
|
|
7f29cef5d2 | ||
|
|
4a32f17aad | ||
|
|
0c452ac895 | ||
|
|
32353fa389 | ||
|
|
33e2a7121e | ||
|
|
2eb984b45d | ||
|
|
3bcd3e7f4c | ||
|
|
58966e952d | ||
|
|
f809f44c02 | ||
|
|
c0cc5cb01d | ||
|
|
da4fdeb017 | ||
|
|
f5f63130bf | ||
|
|
cb8075e53b | ||
|
|
30c5aeff23 | ||
|
|
86d09f6caa | ||
|
|
7db7410920 | ||
|
|
64c76693c4 | ||
|
|
c622b43f85 | ||
|
|
1651253777 | ||
|
|
3824f17516 | ||
|
|
8ab3aaee6a | ||
|
|
12f11b5619 | ||
|
|
16a9882153 | ||
|
|
2caf5e3b2b | ||
|
|
218be2642e | ||
|
|
cd0df05808 | ||
|
|
4ff0686d37 | ||
|
|
288cea917e | ||
|
|
d3f66f8702 | ||
|
|
8e1ba99e8d | ||
|
|
3ffc105f83 | ||
|
|
b1bd87df19 | ||
|
|
3f9e8d676a | ||
|
|
fe82322727 | ||
|
|
e89e5c9beb | ||
|
|
e85ac2695f | ||
|
|
c2c813599d | ||
|
|
3c0817a1b7 | ||
|
|
387d56879b | ||
|
|
f24f35b65d | ||
|
|
28926c7454 | ||
|
|
24543e7076 | ||
|
|
21474e280d | ||
|
|
5f722d0d3f | ||
|
|
06f57abdf9 | ||
|
|
42c19e14c5 | ||
|
|
058a61148c | ||
|
|
425902b296 | ||
|
|
54f39aa8cb | ||
|
|
1108bd1967 | ||
|
|
707cea2182 | ||
|
|
c47e3b8689 | ||
|
|
5bd7a1f03c | ||
|
|
c614dcb9f7 | ||
|
|
7fe399598d | ||
|
|
25a38b25c4 | ||
|
|
b9fb849b83 | ||
|
|
0c00da1dd8 | ||
|
|
28fb2d3235 | ||
|
|
bf39146caa | ||
|
|
1dfe4eda5c | ||
|
|
5530795898 | ||
|
|
db06ef78ba | ||
|
|
95c6ee3e6a | ||
|
|
d4168056be | ||
|
|
592a65f52e | ||
|
|
6e101f479c | ||
|
|
6d4862245d | ||
|
|
78abbfa604 | ||
|
|
664c9e3eb3 | ||
|
|
1f71c28656 | ||
|
|
30e06eaaba | ||
|
|
0ed196820e | ||
|
|
bde2370b18 | ||
|
|
960a9e0ff7 | ||
|
|
3277c27e0d | ||
|
|
dea10b5500 | ||
|
|
691d9b16db | ||
|
|
400a351bb0 | ||
|
|
fa9fb26952 | ||
|
|
938b0bfcec | ||
|
|
a5a2e591c9 | ||
|
|
677c8831af | ||
|
|
634c53ea50 | ||
|
|
657660e5c2 | ||
|
|
b60e6f7cb1 | ||
|
|
4a7c858fdc | ||
|
|
85a72efed7 | ||
|
|
8afdcb98d4 | ||
|
|
6c36c60330 | ||
|
|
6239b8ad7a | ||
|
|
4fed2d4ed7 | ||
|
|
ec0c1b01c4 | ||
|
|
f77ea5a2ce | ||
|
|
f17aa3755f | ||
|
|
744a049007 | ||
|
|
e70bd069b3 | ||
|
|
bfeb3120f5 | ||
|
|
ce6d88d187 | ||
|
|
56fe4c61af | ||
|
|
1d1a087fc5 | ||
|
|
f1276c58d2 | ||
|
|
da33eb5e8c | ||
|
|
fdeb4f7c46 | ||
|
|
7388f71228 | ||
|
|
eebf4f10e3 | ||
|
|
3d1b2eb4e5 | ||
|
|
16740b4fcd | ||
|
|
e97fe7a43e | ||
|
|
87eed369cd | ||
|
|
79f4c2db01 | ||
|
|
6df8f0bbf3 | ||
|
|
bbd0667b98 | ||
|
|
b40633f92c | ||
|
|
2f47bea809 | ||
|
|
f38360deb6 | ||
|
|
fc8facddee | ||
|
|
bf9f79081a | ||
|
|
ec1679406e | ||
|
|
17107c59ac | ||
|
|
62f17ccf8c | ||
|
|
cb03540ffd | ||
|
|
0270d697ab | ||
|
|
47529796d0 | ||
|
|
1c786610b7 | ||
|
|
4a99d3dbaf | ||
|
|
e6aa792c01 | ||
|
|
eb6daa2fc3 | ||
|
|
f767e97fa5 | ||
|
|
f076819d81 | ||
|
|
3c8e280bf1 | ||
|
|
c1ecfd23be | ||
|
|
80de77a6e6 | ||
|
|
adb3dcdc13 | ||
|
|
e972eb7c74 | ||
|
|
203dfdc156 | ||
|
|
6ccf037972 | ||
|
|
4a04d1f8f2 | ||
|
|
c274ce4d49 | ||
|
|
047432a81b | ||
|
|
8358d100cc | ||
|
|
82aa0e1222 | ||
|
|
edca82c4bd | ||
|
|
7e9c61ab18 | ||
|
|
f81220ae2d | ||
|
|
4ca066f077 | ||
|
|
88ed85247e | ||
|
|
de1af55e22 | ||
|
|
bf059914a6 | ||
|
|
3952cc6629 | ||
|
|
e15cac55aa | ||
|
|
824c0c962d | ||
|
|
37521ddff7 | ||
|
|
11e9f43305 | ||
|
|
89218b6d1e | ||
|
|
da95c0d784 | ||
|
|
1795afadb8 | ||
|
|
a87d1bbaa1 | ||
|
|
babece08c6 | ||
|
|
5a9779f88c | ||
|
|
45d3db48cf | ||
|
|
f03258bb2a | ||
|
|
5264d545dd | ||
|
|
6175cc0977 | ||
|
|
0c34846ef6 | ||
|
|
4f172ae4b6 | ||
|
|
0f12fe9b3b | ||
|
|
6e084572d4 |
35
.github/workflows/build.yaml
vendored
35
.github/workflows/build.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
- stable
|
- stable
|
||||||
- beta
|
- beta
|
||||||
- nightly
|
- nightly
|
||||||
- 1.56
|
- "1.60"
|
||||||
target:
|
target:
|
||||||
- x86_64-unknown-linux-gnu
|
- x86_64-unknown-linux-gnu
|
||||||
- x86_64-unknown-linux-musl
|
- x86_64-unknown-linux-musl
|
||||||
@@ -29,35 +29,30 @@ jobs:
|
|||||||
- name: Install Rust toolchain (${{ matrix.rust }})
|
- name: Install Rust toolchain (${{ matrix.rust }})
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ matrix.rust }}
|
toolchain: ${{ matrix.rust }}
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
override: true
|
override: true
|
||||||
|
|
||||||
- name: Debug Check (default features)
|
|
||||||
run: |
|
|
||||||
git rev-list origin/main..$GITHUB_SHA | xargs -t -I % sh -c 'git checkout %; cargo check --tests --all --target=${{ matrix.target }}'
|
|
||||||
git checkout $GITHUB_SHA
|
|
||||||
|
|
||||||
- name: Build (default features)
|
- name: Build (default features)
|
||||||
run: cargo rustc --bin cloud-hypervisor -- -D warnings
|
run: cargo rustc --locked --bin cloud-hypervisor -- -D warnings
|
||||||
|
|
||||||
- name: Build (common + kvm)
|
- name: Build (kvm)
|
||||||
run: cargo rustc --bin cloud-hypervisor --no-default-features --features "common,kvm" -- -D warnings
|
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "kvm" -- -D warnings
|
||||||
|
|
||||||
- name: Build (default features + tdx)
|
- name: Build (default features + tdx)
|
||||||
run: cargo rustc --bin cloud-hypervisor --features "tdx" -- -D warnings
|
run: cargo rustc --locked --bin cloud-hypervisor --features "tdx" -- -D warnings
|
||||||
|
|
||||||
- name: Build (default features + amx)
|
- name: Build (default features + guest_debug)
|
||||||
run: cargo rustc --bin cloud-hypervisor --features "amx" -- -D warnings
|
run: cargo rustc --locked --bin cloud-hypervisor --features "guest_debug" -- -D warnings
|
||||||
|
|
||||||
- name: Build (default features + gdb)
|
- name: Build (mshv)
|
||||||
run: cargo rustc --bin cloud-hypervisor --features "gdb" -- -D warnings
|
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv" -- -D warnings
|
||||||
|
|
||||||
- name: Build (common + mshv)
|
- name: Build (mshv + kvm)
|
||||||
run: cargo rustc --bin cloud-hypervisor --no-default-features --features "common,mshv" -- -D warnings
|
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv,kvm" -- -D warnings
|
||||||
|
|
||||||
- name: Release Build (default features)
|
- name: Release Build (default features)
|
||||||
run: cargo build --all --release --target=${{ matrix.target }}
|
run: cargo build --locked --all --release --target=${{ matrix.target }}
|
||||||
|
|
||||||
- name: Check build did not modify any files
|
- name: Check build did not modify any files
|
||||||
run: test -z "$(git status --porcelain)"
|
run: test -z "$(git status --porcelain)"
|
||||||
|
|||||||
47
.github/workflows/quality-aarch64.yaml
vendored
47
.github/workflows/quality-aarch64.yaml
vendored
@@ -1,47 +0,0 @@
|
|||||||
name: Cloud Hypervisor Quality Checks
|
|
||||||
on: [pull_request, create]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
name: Quality (clippy, rustfmt)
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
continue-on-error: ${{ matrix.experimental }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
rust:
|
|
||||||
- stable
|
|
||||||
target:
|
|
||||||
- aarch64-unknown-linux-gnu
|
|
||||||
experimental: [false]
|
|
||||||
include:
|
|
||||||
- rust: beta
|
|
||||||
target: aarch64-unknown-linux-gnu
|
|
||||||
experimental: true
|
|
||||||
steps:
|
|
||||||
- name: Code checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Install Rust toolchain (${{ matrix.rust }})
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: ${{ matrix.rust }}
|
|
||||||
target: ${{ matrix.target }}
|
|
||||||
override: true
|
|
||||||
components: rustfmt, clippy
|
|
||||||
- name: Formatting (rustfmt)
|
|
||||||
run: cargo fmt -- --check
|
|
||||||
|
|
||||||
- name: Clippy (common + kvm)
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
use-cross: true
|
|
||||||
command: clippy
|
|
||||||
args: --target=${{ matrix.target }} --tests --all --no-default-features --features "common,kvm" -- -D warnings
|
|
||||||
|
|
||||||
- name: Clippy (default features)
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
use-cross: true
|
|
||||||
command: clippy
|
|
||||||
args: --target=${{ matrix.target }} --tests --all -- -D warnings
|
|
||||||
72
.github/workflows/quality.yaml
vendored
72
.github/workflows/quality.yaml
vendored
@@ -14,14 +14,21 @@ jobs:
|
|||||||
- stable
|
- stable
|
||||||
target:
|
target:
|
||||||
- x86_64-unknown-linux-gnu
|
- x86_64-unknown-linux-gnu
|
||||||
|
- aarch64-unknown-linux-gnu
|
||||||
experimental: [false]
|
experimental: [false]
|
||||||
include:
|
include:
|
||||||
- rust: beta
|
- rust: beta
|
||||||
target: x86_64-unknown-linux-gnu
|
target: x86_64-unknown-linux-gnu
|
||||||
experimental: true
|
experimental: true
|
||||||
|
- rust: beta
|
||||||
|
target: aarch64-unknown-linux-gnu
|
||||||
|
experimental: true
|
||||||
steps:
|
steps:
|
||||||
- name: Code checkout
|
- name: Code checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Install Rust toolchain (${{ matrix.rust }})
|
- name: Install Rust toolchain (${{ matrix.rust }})
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
@@ -30,23 +37,68 @@ jobs:
|
|||||||
override: true
|
override: true
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
|
|
||||||
|
- name: Debug Check (default features)
|
||||||
|
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
commits=$(git rev-list origin/${{ github.base_ref }}..${{ github.sha }})
|
||||||
|
for commit in $commits; do git checkout $commit; cargo check --tests --all --target=${{ matrix.target }}; done
|
||||||
|
git checkout ${{ github.sha }}
|
||||||
|
|
||||||
- name: Formatting (rustfmt)
|
- name: Formatting (rustfmt)
|
||||||
run: cargo fmt -- --check
|
run: cargo fmt -- --check
|
||||||
|
|
||||||
- name: Clippy (common + kvm)
|
- name: Clippy (kvm)
|
||||||
run: cargo clippy --all --all-targets --no-default-features --tests --features "common,kvm" -- -D warnings
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
|
command: clippy
|
||||||
|
args: --locked --all --all-targets --no-default-features --tests --features "kvm" -- -D warnings
|
||||||
|
|
||||||
- name: Clippy (default features)
|
- name: Clippy (default features)
|
||||||
run: cargo clippy --all --all-targets --tests -- -D warnings
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
|
command: clippy
|
||||||
|
args: --locked --all --all-targets --tests -- -D warnings
|
||||||
|
|
||||||
- name: Clippy (default features + amx)
|
- name: Clippy (default features + guest_debug)
|
||||||
run: cargo clippy --all --all-targets --tests --features "amx" -- -D warnings
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
|
command: clippy
|
||||||
|
args: --locked --all --all-targets --tests --features "guest_debug" -- -D warnings
|
||||||
|
|
||||||
- name: Clippy (default features + gdb)
|
- name: Clippy (default features + tracing)
|
||||||
run: cargo clippy --all --all-targets --tests --features "gdb" -- -D warnings
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
|
command: clippy
|
||||||
|
args: --locked --all --all-targets --tests --features "tracing" -- -D warnings
|
||||||
|
|
||||||
- name: Clippy (common + mshv)
|
- name: Clippy (mshv)
|
||||||
run: cargo clippy --all --all-targets --no-default-features --tests --features "common,mshv" -- -D warnings
|
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
|
command: clippy
|
||||||
|
args: --locked --all --all-targets --no-default-features --tests --features "mshv" -- -D warnings
|
||||||
|
|
||||||
|
- name: Clippy (mshv + kvm)
|
||||||
|
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
|
command: clippy
|
||||||
|
args: --locked --all --all-targets --no-default-features --tests --features "mshv,kvm" -- -D warnings
|
||||||
|
|
||||||
|
- name: Clippy (kvm + tdx)
|
||||||
|
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
|
command: clippy
|
||||||
|
args: --locked --all --all-targets --no-default-features --tests --features "tdx,kvm" -- -D warnings
|
||||||
|
|
||||||
- name: Check build did not modify any files
|
- name: Check build did not modify any files
|
||||||
run: test -z "$(git status --porcelain)"
|
run: test -z "$(git status --porcelain)"
|
||||||
|
|||||||
10
.github/workflows/release.yaml
vendored
10
.github/workflows/release.yaml
vendored
@@ -16,23 +16,23 @@ jobs:
|
|||||||
- name: Install Rust toolchain (x86_64-unknown-linux-gnu)
|
- name: Install Rust toolchain (x86_64-unknown-linux-gnu)
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: 1.58
|
toolchain: "1.62"
|
||||||
target: x86_64-unknown-linux-gnu
|
target: x86_64-unknown-linux-gnu
|
||||||
- name: Install Rust toolchain (x86_64-unknown-linux-musl)
|
- name: Install Rust toolchain (x86_64-unknown-linux-musl)
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: 1.58
|
toolchain: "1.62"
|
||||||
target: x86_64-unknown-linux-musl
|
target: x86_64-unknown-linux-musl
|
||||||
- name: Build
|
- name: Build
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
toolchain: 1.58
|
toolchain: "1.62"
|
||||||
command: build
|
command: build
|
||||||
args: --all --release --target=x86_64-unknown-linux-gnu
|
args: --all --release --target=x86_64-unknown-linux-gnu
|
||||||
- name: Static Build
|
- name: Static Build
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
toolchain: 1.58
|
toolchain: "1.62"
|
||||||
command: build
|
command: build
|
||||||
args: --all --release --target=x86_64-unknown-linux-musl
|
args: --all --release --target=x86_64-unknown-linux-musl
|
||||||
- name: Strip cloud-hypervisor binaries
|
- name: Strip cloud-hypervisor binaries
|
||||||
@@ -40,7 +40,7 @@ jobs:
|
|||||||
- name: Install Rust toolchain (aarch64-unknown-linux-musl)
|
- name: Install Rust toolchain (aarch64-unknown-linux-musl)
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: 1.58
|
toolchain: "1.62"
|
||||||
target: aarch64-unknown-linux-musl
|
target: aarch64-unknown-linux-musl
|
||||||
override: true
|
override: true
|
||||||
- name: Static Build (AArch64)
|
- name: Static Build (AArch64)
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,3 +4,4 @@
|
|||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
**/Cargo.lock
|
**/Cargo.lock
|
||||||
**/rusty-tags.vi
|
**/rusty-tags.vi
|
||||||
|
/rpm/SOURCES
|
||||||
|
|||||||
2
CODEOWNERS
Normal file
2
CODEOWNERS
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Add the list of code owners here (using their GitHub username)
|
||||||
|
* @cloud-hypervisor/cloud-hypervisor-reviewers
|
||||||
@@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
Cloud Hypervisor is an open source project licensed under the [Apache v2
|
Cloud Hypervisor is an open source project licensed under the [Apache v2
|
||||||
License](https://opensource.org/licenses/Apache-2.0) and the [BSD 3
|
License](https://opensource.org/licenses/Apache-2.0) and the [BSD 3
|
||||||
Clause](https://opensource.org/licenses/BSD-3-Clause) license. Contributions
|
Clause](https://opensource.org/licenses/BSD-3-Clause) license. Individual files
|
||||||
can be made under either license or both. Individual files contain details of
|
contain details of their licensing and changes to that file are under the same
|
||||||
their licensing and changes to that file are under the same license unless the
|
license unless the contribution changes the license of the file. When importing
|
||||||
contribution changes the license of the file. When importing code from a third
|
code from a third party project (e.g. Firecracker or CrosVM) please respect the
|
||||||
party project (e.g. Firecracker or CrosVM) please respect the license of those
|
license of those projects.
|
||||||
projects.
|
|
||||||
|
New code should be under the [Apache v2
|
||||||
|
License](https://opensource.org/licenses/Apache-2.0).
|
||||||
|
|
||||||
## Coding Style
|
## Coding Style
|
||||||
|
|
||||||
@@ -15,6 +17,23 @@ We follow the [Rust Style](https://github.com/rust-dev-tools/fmt-rfcs/blob/maste
|
|||||||
convention and enforce it through the Continuous Integration (CI) process calling into `rustfmt`
|
convention and enforce it through the Continuous Integration (CI) process calling into `rustfmt`
|
||||||
for each submitted Pull Request (PR).
|
for each submitted Pull Request (PR).
|
||||||
|
|
||||||
|
## Basic Checks
|
||||||
|
|
||||||
|
Please consider creating the following hook as `.git/hooks/pre-commit` in order
|
||||||
|
to ensure basic correctness of your code. You can extend this further if you
|
||||||
|
have specific features that you regularly develop against.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
cargo fmt -- --check || exit 1
|
||||||
|
cargo check --locked --all --all-targets --tests || exit 1
|
||||||
|
cargo clippy --locked --all --all-targets --tests -- -D warnings || exit 1
|
||||||
|
```
|
||||||
|
|
||||||
|
You will need to `chmod +x .git/hooks/pre-commit` to have it run on every
|
||||||
|
commit you make.
|
||||||
|
|
||||||
## Certificate of Origin
|
## Certificate of Origin
|
||||||
|
|
||||||
In order to get a clear contribution chain of trust we use the [signed-off-by language](https://01.org/community/signed-process)
|
In order to get a clear contribution chain of trust we use the [signed-off-by language](https://01.org/community/signed-process)
|
||||||
@@ -63,11 +82,9 @@ you want to merge your changes to `cloud-hypervisor`:
|
|||||||
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 main 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. To update your pull request amend existing commits whenever applicable and
|
||||||
any comments and obtain minimum of 2 [maintainers](MAINTAINERS.md) approvals.
|
|
||||||
To update your pull request amend existing commits whenever applicable and
|
|
||||||
then push the new changes to your pull request branch.
|
then push the new changes to your pull request branch.
|
||||||
5. Once the pull request is approved, one of the maintainers will merge it.
|
5. Once the pull request is approved it can be integrated.
|
||||||
|
|
||||||
## Issue tracking
|
## Issue tracking
|
||||||
|
|
||||||
|
|||||||
539
Cargo.lock
generated
539
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
45
Cargo.toml
45
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cloud-hypervisor"
|
name = "cloud-hypervisor"
|
||||||
version = "23.0.0"
|
version = "27.0.0"
|
||||||
authors = ["The Cloud Hypervisor Authors"]
|
authors = ["The Cloud Hypervisor Authors"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
default-run = "cloud-hypervisor"
|
default-run = "cloud-hypervisor"
|
||||||
@@ -10,31 +10,34 @@ description = "Open source Virtual Machine Monitor (VMM) that runs on top of KVM
|
|||||||
homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
||||||
# Minimum buildable version:
|
# Minimum buildable version:
|
||||||
# Keep in sync with version in .github/workflows/build.yaml
|
# Keep in sync with version in .github/workflows/build.yaml
|
||||||
rust-version = "1.56"
|
rust-version = "1.60"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
opt-level = "s"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.56"
|
anyhow = "1.0.65"
|
||||||
api_client = { path = "api_client" }
|
api_client = { path = "api_client" }
|
||||||
clap = { version = "3.1.8", features = ["wrap_help","cargo"] }
|
clap = { version = "3.2.22", features = ["wrap_help","cargo"] }
|
||||||
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.123"
|
libc = "0.2.133"
|
||||||
log = { version = "0.4.16", features = ["std"] }
|
log = { version = "0.4.17", features = ["std"] }
|
||||||
option_parser = { path = "option_parser" }
|
option_parser = { path = "option_parser" }
|
||||||
seccompiler = "0.2.0"
|
seccompiler = "0.3.0"
|
||||||
serde_json = "1.0.79"
|
serde_json = "1.0.85"
|
||||||
signal-hook = "0.3.13"
|
signal-hook = "0.3.14"
|
||||||
thiserror = "1.0.30"
|
thiserror = "1.0.37"
|
||||||
|
tracer = { path = "tracer" }
|
||||||
vmm = { path = "vmm" }
|
vmm = { path = "vmm" }
|
||||||
vmm-sys-util = "0.9.0"
|
vmm-sys-util = "0.10.0"
|
||||||
vm-memory = "0.7.0"
|
vm-memory = "0.9.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
clap = { version = "3.1.8", features = ["cargo"] }
|
clap = { version = "3.2.22", features = ["cargo"] }
|
||||||
|
|
||||||
# List of patched crates
|
# List of patched crates
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
@@ -44,23 +47,19 @@ versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_deri
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
dirs = "4.0.0"
|
dirs = "4.0.0"
|
||||||
lazy_static= "1.4.0"
|
|
||||||
net_util = { path = "net_util" }
|
net_util = { path = "net_util" }
|
||||||
serde_json = "1.0.79"
|
once_cell = "1.15.0"
|
||||||
|
serde_json = "1.0.85"
|
||||||
test_infra = { path = "test_infra" }
|
test_infra = { path = "test_infra" }
|
||||||
wait-timeout = "0.2.0"
|
wait-timeout = "0.2.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["common", "kvm"]
|
default = ["kvm"]
|
||||||
# Common features for all hypervisors
|
guest_debug = ["vmm/guest_debug"]
|
||||||
common = ["fwdebug"]
|
|
||||||
amx = ["vmm/amx"]
|
|
||||||
cmos = ["vmm/cmos"]
|
|
||||||
fwdebug = ["vmm/fwdebug"]
|
|
||||||
gdb = ["vmm/gdb"]
|
|
||||||
kvm = ["vmm/kvm"]
|
kvm = ["vmm/kvm"]
|
||||||
mshv = ["vmm/mshv"]
|
mshv = ["vmm/mshv"]
|
||||||
tdx = ["vmm/tdx"]
|
tdx = ["vmm/tdx"]
|
||||||
|
tracing = ["vmm/tracing", "tracer/tracing"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
@@ -78,7 +77,9 @@ members = [
|
|||||||
"performance-metrics",
|
"performance-metrics",
|
||||||
"qcow",
|
"qcow",
|
||||||
"rate_limiter",
|
"rate_limiter",
|
||||||
|
"serial_buffer",
|
||||||
"test_infra",
|
"test_infra",
|
||||||
|
"tracer",
|
||||||
"vfio_user",
|
"vfio_user",
|
||||||
"vhdx",
|
"vhdx",
|
||||||
"vhost_user_block",
|
"vhost_user_block",
|
||||||
|
|||||||
196
Jenkinsfile
vendored
196
Jenkinsfile
vendored
@@ -23,6 +23,19 @@ pipeline{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stage ('Check for fuzzer files only changes') {
|
||||||
|
when {
|
||||||
|
expression {
|
||||||
|
return fuzzFileOnly()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
runWorkers = false
|
||||||
|
echo "Fuzzer cargo files only changes, no need to run the CI"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
stage ('Check for RFC/WIP builds') {
|
stage ('Check for RFC/WIP builds') {
|
||||||
when {
|
when {
|
||||||
changeRequest comparator: 'REGEXP', title: '.*(rfc|RFC|wip|WIP).*'
|
changeRequest comparator: 'REGEXP', title: '.*(rfc|RFC|wip|WIP).*'
|
||||||
@@ -43,7 +56,7 @@ pipeline{
|
|||||||
stage ('Build') {
|
stage ('Build') {
|
||||||
parallel {
|
parallel {
|
||||||
stage ('Worker build') {
|
stage ('Worker build') {
|
||||||
agent { node { label 'focal' } }
|
agent { node { label 'jammy' } }
|
||||||
when {
|
when {
|
||||||
beforeAgent true
|
beforeAgent true
|
||||||
expression {
|
expression {
|
||||||
@@ -90,6 +103,9 @@ pipeline{
|
|||||||
return runWorkers
|
return runWorkers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
environment {
|
||||||
|
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
|
||||||
|
}
|
||||||
stages {
|
stages {
|
||||||
stage ('Checkout') {
|
stage ('Checkout') {
|
||||||
steps {
|
steps {
|
||||||
@@ -110,6 +126,42 @@ pipeline{
|
|||||||
sh "scripts/dev_cli.sh tests --integration --libc musl"
|
sh "scripts/dev_cli.sh tests --integration --libc musl"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stage ('Install azure-cli') {
|
||||||
|
steps {
|
||||||
|
installAzureCli("bionic", "arm64")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage ('Download Windows image') {
|
||||||
|
steps {
|
||||||
|
sh '''#!/bin/bash -x
|
||||||
|
IMG_BASENAME=windows-11-iot-enterprise-aarch64.raw
|
||||||
|
IMG_PATH=$HOME/workloads/$IMG_BASENAME
|
||||||
|
IMG_GZ_PATH=$HOME/workloads/$IMG_BASENAME.gz
|
||||||
|
IMG_GZ_BLOB_NAME=windows-11-iot-enterprise-aarch64-9-min.raw.gz
|
||||||
|
cp "scripts/$IMG_BASENAME.sha1" "$HOME/workloads/"
|
||||||
|
pushd "$HOME/workloads"
|
||||||
|
if sha1sum "$IMG_BASENAME.sha1" --check; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
popd
|
||||||
|
mkdir -p "$HOME/workloads"
|
||||||
|
az storage blob download \
|
||||||
|
--container-name private-images \
|
||||||
|
--file "$IMG_GZ_PATH" \
|
||||||
|
--name "$IMG_GZ_BLOB_NAME" \
|
||||||
|
--connection-string "$AZURE_CONNECTION_STRING"
|
||||||
|
gzip -d $IMG_GZ_PATH
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage ('Run Windows guest integration tests') {
|
||||||
|
options {
|
||||||
|
timeout(time: 1, unit: 'HOURS')
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh "scripts/dev_cli.sh tests --integration-windows --libc musl"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
post {
|
post {
|
||||||
always {
|
always {
|
||||||
@@ -119,7 +171,7 @@ pipeline{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Worker build (musl)') {
|
stage ('Worker build (musl)') {
|
||||||
agent { node { label 'focal' } }
|
agent { node { label 'jammy' } }
|
||||||
when {
|
when {
|
||||||
beforeAgent true
|
beforeAgent true
|
||||||
expression {
|
expression {
|
||||||
@@ -153,90 +205,8 @@ pipeline{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Worker build SGX') {
|
|
||||||
agent { node { label 'bionic-sgx' } }
|
|
||||||
when {
|
|
||||||
beforeAgent true
|
|
||||||
allOf {
|
|
||||||
branch 'main'
|
|
||||||
expression {
|
|
||||||
return runWorkers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stages {
|
|
||||||
stage ('Checkout') {
|
|
||||||
steps {
|
|
||||||
checkout scm
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage ('Run SGX integration tests') {
|
|
||||||
options {
|
|
||||||
timeout(time: 1, unit: 'HOURS')
|
|
||||||
}
|
|
||||||
steps {
|
|
||||||
sh "scripts/dev_cli.sh tests --integration-sgx"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage ('Run SGX integration tests for musl') {
|
|
||||||
options {
|
|
||||||
timeout(time: 1, unit: 'HOURS')
|
|
||||||
}
|
|
||||||
steps {
|
|
||||||
sh "scripts/dev_cli.sh tests --integration-sgx --libc musl"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
post {
|
|
||||||
always {
|
|
||||||
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
|
||||||
deleteDir()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage ('Worker build VFIO') {
|
|
||||||
agent { node { label 'bionic-vfio' } }
|
|
||||||
when {
|
|
||||||
beforeAgent true
|
|
||||||
allOf {
|
|
||||||
branch 'main'
|
|
||||||
expression {
|
|
||||||
return runWorkers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stages {
|
|
||||||
stage ('Checkout') {
|
|
||||||
steps {
|
|
||||||
checkout scm
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage ('Run VFIO integration tests') {
|
|
||||||
options {
|
|
||||||
timeout(time: 1, unit: 'HOURS')
|
|
||||||
}
|
|
||||||
steps {
|
|
||||||
sh "scripts/dev_cli.sh tests --integration-vfio"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage ('Run VFIO integration tests for musl') {
|
|
||||||
options {
|
|
||||||
timeout(time: 1, unit: 'HOURS')
|
|
||||||
}
|
|
||||||
steps {
|
|
||||||
sh "scripts/dev_cli.sh tests --integration-vfio --libc musl"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
post {
|
|
||||||
always {
|
|
||||||
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
|
||||||
deleteDir()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage ('Worker build - Windows guest') {
|
stage ('Worker build - Windows guest') {
|
||||||
agent { node { label 'focal' } }
|
agent { node { label 'jammy' } }
|
||||||
when {
|
when {
|
||||||
beforeAgent true
|
beforeAgent true
|
||||||
expression {
|
expression {
|
||||||
@@ -254,7 +224,7 @@ pipeline{
|
|||||||
}
|
}
|
||||||
stage ('Install azure-cli') {
|
stage ('Install azure-cli') {
|
||||||
steps {
|
steps {
|
||||||
installAzureCli()
|
installAzureCli("jammy", "amd64")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Download assets') {
|
stage ('Download assets') {
|
||||||
@@ -282,7 +252,7 @@ pipeline{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Worker build - Live Migration') {
|
stage ('Worker build - Live Migration') {
|
||||||
agent { node { label 'focal-small' } }
|
agent { node { label 'jammy' } }
|
||||||
when {
|
when {
|
||||||
beforeAgent true
|
beforeAgent true
|
||||||
expression {
|
expression {
|
||||||
@@ -315,39 +285,6 @@ pipeline{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Worker build - Metrics') {
|
|
||||||
agent { node { label 'focal-metrics' } }
|
|
||||||
when {
|
|
||||||
branch 'main'
|
|
||||||
beforeAgent true
|
|
||||||
expression {
|
|
||||||
return runWorkers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
environment {
|
|
||||||
METRICS_PUBLISH_KEY = credentials('52e0945f-ce7a-43d1-87af-67d1d87cc40f')
|
|
||||||
}
|
|
||||||
stages {
|
|
||||||
stage ('Checkout') {
|
|
||||||
steps {
|
|
||||||
checkout scm
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage ('Run metrics tests') {
|
|
||||||
options {
|
|
||||||
timeout(time: 1, unit: 'HOURS')
|
|
||||||
}
|
|
||||||
steps {
|
|
||||||
sh 'scripts/dev_cli.sh tests --metrics -- -- --report-file /root/workloads/metrics.json'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage ('Upload metrics report') {
|
|
||||||
steps {
|
|
||||||
sh 'curl -X PUT https://cloud-hypervisor-metrics.azurewebsites.net/api/publishmetrics -H "x-functions-key: $METRICS_PUBLISH_KEY" -T ~/workloads/metrics.json'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -355,14 +292,14 @@ pipeline{
|
|||||||
regression {
|
regression {
|
||||||
script {
|
script {
|
||||||
if (env.BRANCH_NAME == 'main') {
|
if (env.BRANCH_NAME == 'main') {
|
||||||
slackSend (color: '#ff0000', message: '"main" branch build is now failing')
|
slackSend (color: '#ff0000', message: '"main" branch build is now failing', channel: '#jenkins-ci')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fixed {
|
fixed {
|
||||||
script {
|
script {
|
||||||
if (env.BRANCH_NAME == 'main') {
|
if (env.BRANCH_NAME == 'main') {
|
||||||
slackSend (color: '#00ff00', message: '"main" branch build is now fixed')
|
slackSend (color: '#00ff00', message: '"main" branch build is now fixed', channel: '#jenkins-ci')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -384,10 +321,10 @@ def cancelPreviousBuilds() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def installAzureCli() {
|
def installAzureCli(distro, arch) {
|
||||||
sh "sudo apt install -y ca-certificates curl apt-transport-https lsb-release gnupg"
|
sh "sudo apt install -y ca-certificates curl apt-transport-https lsb-release gnupg"
|
||||||
sh "curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null"
|
sh "curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null"
|
||||||
sh "echo \"deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ focal main\" | sudo tee /etc/apt/sources.list.d/azure-cli.list"
|
sh "echo \"deb [arch=${arch}] https://packages.microsoft.com/repos/azure-cli/ ${distro} main\" | sudo tee /etc/apt/sources.list.d/azure-cli.list"
|
||||||
sh "sudo apt update"
|
sh "sudo apt update"
|
||||||
sh "sudo apt install -y azure-cli"
|
sh "sudo apt install -y azure-cli"
|
||||||
}
|
}
|
||||||
@@ -402,3 +339,14 @@ def boolean docsFileOnly() {
|
|||||||
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v '\\.md'"
|
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v '\\.md'"
|
||||||
) != 0
|
) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def boolean fuzzFileOnly() {
|
||||||
|
if (env.CHANGE_TARGET == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sh(
|
||||||
|
returnStatus: true,
|
||||||
|
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v -E 'fuzz/'"
|
||||||
|
) != 0
|
||||||
|
}
|
||||||
|
|||||||
44
README.md
44
README.md
@@ -4,10 +4,12 @@
|
|||||||
- [Architectures](#architectures)
|
- [Architectures](#architectures)
|
||||||
- [Guest OS](#guest-os)
|
- [Guest OS](#guest-os)
|
||||||
- [2. Getting Started](#2-getting-started)
|
- [2. Getting Started](#2-getting-started)
|
||||||
|
- [Host OS](#host-os)
|
||||||
- [Preparation](#preparation)
|
- [Preparation](#preparation)
|
||||||
- [Install prerequisites](#install-prerequisites)
|
- [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)
|
||||||
|
- [Use Prebuilt Binaries](#use-prebuilt-binaries)
|
||||||
- [Run](#run)
|
- [Run](#run)
|
||||||
- [Cloud image](#cloud-image)
|
- [Cloud image](#cloud-image)
|
||||||
- [Custom kernel and disk image](#custom-kernel-and-disk-image)
|
- [Custom kernel and disk image](#custom-kernel-and-disk-image)
|
||||||
@@ -18,11 +20,13 @@
|
|||||||
- [Hot Plug](#hot-plug)
|
- [Hot Plug](#hot-plug)
|
||||||
- [Device Model](#device-model)
|
- [Device Model](#device-model)
|
||||||
- [TODO](#todo)
|
- [TODO](#todo)
|
||||||
|
- [Roadmap](#roadmap)
|
||||||
- [4. `rust-vmm` project dependency](#4-rust-vmm-project-dependency)
|
- [4. `rust-vmm` project dependency](#4-rust-vmm-project-dependency)
|
||||||
- [Firecracker and crosvm](#firecracker-and-crosvm)
|
- [Firecracker and crosvm](#firecracker-and-crosvm)
|
||||||
- [5. Community](#5-community)
|
- [5. Community](#5-community)
|
||||||
- [Contribute](#contribute)
|
- [Contribute](#contribute)
|
||||||
- [Join us](#join-us)
|
- [Slack](#slack)
|
||||||
|
- [Mailing list](#mailing-list)
|
||||||
- [Security issues](#security-issues)
|
- [Security issues](#security-issues)
|
||||||
|
|
||||||
# 1. What is Cloud Hypervisor?
|
# 1. What is Cloud Hypervisor?
|
||||||
@@ -72,6 +76,12 @@ 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
|
platform. For getting started on the `AArch64` platform, please refer to the
|
||||||
[Arm64 documentation](docs/arm64.md).
|
[Arm64 documentation](docs/arm64.md).
|
||||||
|
|
||||||
|
## Host OS
|
||||||
|
|
||||||
|
Based on required KVM functionality the minimum host kernel version is 4.11.
|
||||||
|
For adequate peformance the minimum recommended host kernel vesion is 5.6. The
|
||||||
|
majority of the CI currently tests with kernel version 5.15.
|
||||||
|
|
||||||
## Preparation
|
## 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`
|
||||||
@@ -88,12 +98,10 @@ Hypervisor. Here, all the steps are based on Ubuntu, for other Linux
|
|||||||
distributions please replace the package manager and package name.
|
distributions please replace the package manager and package name.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# Install git
|
# Install build-essential, git, and qemu-utils
|
||||||
$ sudo apt install git
|
$ sudo apt install git build-essential qemu-utils
|
||||||
# Install rust tool chain
|
# Install rust tool chain
|
||||||
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||||
# Install build-essential
|
|
||||||
$ sudo apt install build-essential
|
|
||||||
# If you want to build statically linked binary please add musl target
|
# If you want to build statically linked binary please add musl target
|
||||||
$ rustup target add x86_64-unknown-linux-musl
|
$ rustup target add x86_64-unknown-linux-musl
|
||||||
```
|
```
|
||||||
@@ -147,6 +155,15 @@ $ ./scripts/dev_cli.sh tests --unit
|
|||||||
Run the `./scripts/dev_cli.sh --help` command to view all the supported
|
Run the `./scripts/dev_cli.sh --help` command to view all the supported
|
||||||
development script commands and their related options.
|
development script commands and their related options.
|
||||||
|
|
||||||
|
## Use Prebuilt Binaries
|
||||||
|
|
||||||
|
Cloud Hypervisor packages targeting some popular Linux distributions are available
|
||||||
|
thanks to the [Open Build Service](https://build.opensuse.org). The
|
||||||
|
[OBS README](https://github.com/cloud-hypervisor/obs-packaging) explains how to
|
||||||
|
enable the repository in a supported Linux distribution and install Cloud Hypervisor
|
||||||
|
and accompanying packages. Please report any packaging issues in the
|
||||||
|
[obs-packaging](https://github.com/cloud-hypervisor/obs-packaging) repository.
|
||||||
|
|
||||||
## Run
|
## Run
|
||||||
|
|
||||||
You can run a guest VM by either using an existing cloud image or booting into
|
You can run a guest VM by either using an existing cloud image or booting into
|
||||||
@@ -168,7 +185,7 @@ cloud image. Here we will use a Ubuntu image:
|
|||||||
$ 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.2/hypervisor-fw
|
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.4.1/hypervisor-fw
|
||||||
$ popd
|
$ popd
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -291,6 +308,8 @@ As of 2022-04-05, the following cloud images are supported:
|
|||||||
Direct kernel boot to userspace should work with a rootfs from most
|
Direct kernel boot to userspace should work with a rootfs from most
|
||||||
distributions.
|
distributions.
|
||||||
|
|
||||||
|
Further details can be found in the [release documentation](docs/releases.md).
|
||||||
|
|
||||||
## Hot Plug
|
## Hot Plug
|
||||||
|
|
||||||
Cloud Hypervisor supports hotplug of CPUs, passthrough devices (VFIO),
|
Cloud Hypervisor supports hotplug of CPUs, passthrough devices (VFIO),
|
||||||
@@ -309,6 +328,10 @@ file but through
|
|||||||
[github issues](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/new)
|
[github issues](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/new)
|
||||||
instead.
|
instead.
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
The project roadmap is tracked through a [GitHub project](https://github.com/orgs/cloud-hypervisor/projects/6).
|
||||||
|
|
||||||
# 4. `rust-vmm` project dependency
|
# 4. `rust-vmm` project dependency
|
||||||
|
|
||||||
In order to satisfy the design goal of having a high-performance,
|
In order to satisfy the design goal of having a high-performance,
|
||||||
@@ -370,11 +393,18 @@ reviews, bug reports, feature requests, project improvement suggestions, etc,
|
|||||||
are all equal and welcome means of contribution. See the
|
are all equal and welcome means of contribution. See the
|
||||||
[CONTRIBUTING](CONTRIBUTING.md) document for more details.
|
[CONTRIBUTING](CONTRIBUTING.md) document for more details.
|
||||||
|
|
||||||
## Join us
|
## Slack
|
||||||
|
|
||||||
Get an [invite to our Slack channel](https://join.slack.com/t/cloud-hypervisor/shared_invite/enQtNjY3MTE3MDkwNDQ4LWQ1MTA1ZDVmODkwMWQ1MTRhYzk4ZGNlN2UwNTI3ZmFlODU0OTcwOWZjMTkwZDExYWE3YjFmNzgzY2FmNDAyMjI)
|
Get an [invite to our Slack channel](https://join.slack.com/t/cloud-hypervisor/shared_invite/enQtNjY3MTE3MDkwNDQ4LWQ1MTA1ZDVmODkwMWQ1MTRhYzk4ZGNlN2UwNTI3ZmFlODU0OTcwOWZjMTkwZDExYWE3YjFmNzgzY2FmNDAyMjI)
|
||||||
and [join us on Slack](https://cloud-hypervisor.slack.com/).
|
and [join us on Slack](https://cloud-hypervisor.slack.com/).
|
||||||
|
|
||||||
|
## Mailing list
|
||||||
|
|
||||||
|
Please report bugs using the [GitHub issue
|
||||||
|
tracker](https://github.com/cloud-hypervisor/cloud-hypervisor/issues) but for
|
||||||
|
broader community discussions you may use our [mailing
|
||||||
|
list](https://lists.cloudhypervisor.org/g/dev/).
|
||||||
|
|
||||||
## Security issues
|
## Security issues
|
||||||
|
|
||||||
Please contact the maintainers listed in the MAINTAINERS.md file with security issues.
|
Please contact the maintainers listed in the MAINTAINERS.md file with security issues.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ authors = ["The Cloud Hypervisor Authors"]
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
vm-memory = "0.7.0"
|
vm-memory = "0.9.0"
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ impl Aml for Path {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for part in self.name_parts.clone().iter_mut() {
|
for part in self.name_parts.clone().iter_mut() {
|
||||||
bytes.extend_from_slice(&part.to_vec());
|
bytes.extend_from_slice(part.as_ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
pub struct GenericAddress {
|
pub struct GenericAddress {
|
||||||
pub address_space_id: u8,
|
pub address_space_id: u8,
|
||||||
pub register_bit_width: u8,
|
pub register_bit_width: u8,
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ authors = ["The Cloud Hypervisor Authors"]
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
vmm-sys-util = "0.9.0"
|
vmm-sys-util = "0.10.0"
|
||||||
|
|||||||
@@ -101,6 +101,10 @@ fn parse_http_response(socket: &mut dyn Read) -> Result<Option<String>, Error> {
|
|||||||
loop {
|
loop {
|
||||||
let mut bytes = vec![0; 256];
|
let mut bytes = vec![0; 256];
|
||||||
let count = socket.read(&mut bytes).map_err(Error::Socket)?;
|
let count = socket.read(&mut bytes).map_err(Error::Socket)?;
|
||||||
|
// If the return value is 0, the peer has performed an orderly shutdown.
|
||||||
|
if count == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
res.push_str(std::str::from_utf8(&bytes[0..count]).unwrap());
|
res.push_str(std::str::from_utf8(&bytes[0..count]).unwrap());
|
||||||
|
|
||||||
// End of headers
|
// End of headers
|
||||||
@@ -127,7 +131,7 @@ fn parse_http_response(socket: &mut dyn Read) -> Result<Option<String>, Error> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let body_string = content_length.and(Some(String::from(&res[body_offset.unwrap()..])));
|
let body_string = content_length.and(body_offset.map(|o| String::from(&res[o..])));
|
||||||
let status_code = get_status_code(&res)?;
|
let status_code = get_status_code(&res)?;
|
||||||
|
|
||||||
if status_code.is_server_error() {
|
if status_code.is_server_error() {
|
||||||
@@ -137,18 +141,20 @@ fn parse_http_response(socket: &mut dyn Read) -> Result<Option<String>, Error> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn simple_api_command_with_fds<T: Read + Write + ScmSocket>(
|
/// Make an API request using the fully qualified command name.
|
||||||
|
/// For example, full_command could be "vm.create" or "vmm.ping".
|
||||||
|
pub fn simple_api_full_command_with_fds_and_response<T: Read + Write + ScmSocket>(
|
||||||
socket: &mut T,
|
socket: &mut T,
|
||||||
method: &str,
|
method: &str,
|
||||||
c: &str,
|
full_command: &str,
|
||||||
request_body: Option<&str>,
|
request_body: Option<&str>,
|
||||||
request_fds: Vec<RawFd>,
|
request_fds: Vec<RawFd>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<Option<String>, Error> {
|
||||||
socket
|
socket
|
||||||
.send_with_fds(
|
.send_with_fds(
|
||||||
&[format!(
|
&[format!(
|
||||||
"{} /api/v1/vm.{} HTTP/1.1\r\nHost: localhost\r\nAccept: */*\r\n",
|
"{} /api/v1/{} HTTP/1.1\r\nHost: localhost\r\nAccept: */*\r\n",
|
||||||
method, c
|
method, full_command
|
||||||
)
|
)
|
||||||
.as_bytes()],
|
.as_bytes()],
|
||||||
&request_fds,
|
&request_fds,
|
||||||
@@ -171,12 +177,69 @@ pub fn simple_api_command_with_fds<T: Read + Write + ScmSocket>(
|
|||||||
|
|
||||||
socket.flush().map_err(Error::Socket)?;
|
socket.flush().map_err(Error::Socket)?;
|
||||||
|
|
||||||
if let Some(body) = parse_http_response(socket)? {
|
parse_http_response(socket)
|
||||||
println!("{}", body);
|
}
|
||||||
|
|
||||||
|
pub fn simple_api_full_command_with_fds<T: Read + Write + ScmSocket>(
|
||||||
|
socket: &mut T,
|
||||||
|
method: &str,
|
||||||
|
full_command: &str,
|
||||||
|
request_body: Option<&str>,
|
||||||
|
request_fds: Vec<RawFd>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let response = simple_api_full_command_with_fds_and_response(
|
||||||
|
socket,
|
||||||
|
method,
|
||||||
|
full_command,
|
||||||
|
request_body,
|
||||||
|
request_fds,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if response.is_some() {
|
||||||
|
println!("{}", response.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn simple_api_full_command<T: Read + Write + ScmSocket>(
|
||||||
|
socket: &mut T,
|
||||||
|
method: &str,
|
||||||
|
full_command: &str,
|
||||||
|
request_body: Option<&str>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
simple_api_full_command_with_fds(socket, method, full_command, request_body, Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn simple_api_full_command_and_response<T: Read + Write + ScmSocket>(
|
||||||
|
socket: &mut T,
|
||||||
|
method: &str,
|
||||||
|
full_command: &str,
|
||||||
|
request_body: Option<&str>,
|
||||||
|
) -> Result<Option<String>, Error> {
|
||||||
|
simple_api_full_command_with_fds_and_response(
|
||||||
|
socket,
|
||||||
|
method,
|
||||||
|
full_command,
|
||||||
|
request_body,
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn simple_api_command_with_fds<T: Read + Write + ScmSocket>(
|
||||||
|
socket: &mut T,
|
||||||
|
method: &str,
|
||||||
|
c: &str,
|
||||||
|
request_body: Option<&str>,
|
||||||
|
request_fds: Vec<RawFd>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// Create the full VM command. For VMM commands, use
|
||||||
|
// simple_api_full_command().
|
||||||
|
let full_command = format!("vm.{}", c);
|
||||||
|
|
||||||
|
simple_api_full_command_with_fds(socket, method, &full_command, request_body, request_fds)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn simple_api_command<T: Read + Write + ScmSocket>(
|
pub fn simple_api_command<T: Read + Write + ScmSocket>(
|
||||||
socket: &mut T,
|
socket: &mut T,
|
||||||
method: &str,
|
method: &str,
|
||||||
|
|||||||
@@ -9,22 +9,21 @@ default = []
|
|||||||
tdx = []
|
tdx = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
acpi_tables = { path = "../acpi_tables" }
|
anyhow = "1.0.65"
|
||||||
anyhow = "1.0.56"
|
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
libc = "0.2.123"
|
libc = "0.2.133"
|
||||||
linux-loader = { version = "0.4.0", features = ["elf", "bzimage", "pe"] }
|
linux-loader = { version = "0.6.0", features = ["elf", "bzimage", "pe"] }
|
||||||
log = "0.4.16"
|
log = "0.4.17"
|
||||||
serde = { version = "1.0.136", features = ["rc"] }
|
serde = { version = "1.0.145", features = ["rc", "derive"] }
|
||||||
serde_derive = "1.0.136"
|
thiserror = "1.0.37"
|
||||||
thiserror = "1.0.30"
|
uuid = "1.1.2"
|
||||||
versionize = "0.1.6"
|
versionize = "0.1.6"
|
||||||
versionize_derive = "0.1.4"
|
versionize_derive = "0.1.4"
|
||||||
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-bitmap"] }
|
vm-memory = { version = "0.9.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"] }
|
vmm-sys-util = { version = "0.10.0", features = ["with-serde"] }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||||
fdt_parser = { version = "0.1.3", package = 'fdt'}
|
fdt_parser = { version = "0.1.4", package = "fdt" }
|
||||||
vm-fdt = { git = "https://github.com/rust-vmm/vm-fdt", branch = "main" }
|
vm-fdt = { git = "https://github.com/rust-vmm/vm-fdt", branch = "main" }
|
||||||
|
|||||||
@@ -8,17 +8,18 @@
|
|||||||
|
|
||||||
use crate::{NumaNodes, PciSpaceInfo};
|
use crate::{NumaNodes, PciSpaceInfo};
|
||||||
use byteorder::{BigEndian, ByteOrder};
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
|
use hypervisor::arch::aarch64::gic::Vgic;
|
||||||
use std::cmp;
|
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 std::str;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use super::super::DeviceType;
|
use super::super::DeviceType;
|
||||||
use super::super::GuestMemoryMmap;
|
use super::super::GuestMemoryMmap;
|
||||||
use super::super::InitramfsConfig;
|
use super::super::InitramfsConfig;
|
||||||
use super::gic::GicDevice;
|
|
||||||
use super::layout::{
|
use super::layout::{
|
||||||
IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_PCI_IO_SIZE, MEM_PCI_IO_START,
|
IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_PCI_IO_SIZE, MEM_PCI_IO_START,
|
||||||
PCI_HIGH_BASE, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
|
PCI_HIGH_BASE, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
|
||||||
@@ -90,7 +91,7 @@ pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHash
|
|||||||
vcpu_mpidr: Vec<u64>,
|
vcpu_mpidr: Vec<u64>,
|
||||||
vcpu_topology: Option<(u8, u8, u8)>,
|
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: &Arc<Mutex<dyn Vgic>>,
|
||||||
initrd: &Option<InitramfsConfig>,
|
initrd: &Option<InitramfsConfig>,
|
||||||
pci_space_info: &[PciSpaceInfo],
|
pci_space_info: &[PciSpaceInfo],
|
||||||
numa_nodes: &NumaNodes,
|
numa_nodes: &NumaNodes,
|
||||||
@@ -315,18 +316,18 @@ fn create_chosen_node(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_gic_node(fdt: &mut FdtWriter, gic_device: &dyn GicDevice) -> FdtWriterResult<()> {
|
fn create_gic_node(fdt: &mut FdtWriter, gic_device: &Arc<Mutex<dyn Vgic>>) -> FdtWriterResult<()> {
|
||||||
let gic_reg_prop = gic_device.device_properties();
|
let gic_reg_prop = gic_device.lock().unwrap().device_properties();
|
||||||
|
|
||||||
let intc_node = fdt.begin_node("intc")?;
|
let intc_node = fdt.begin_node("intc")?;
|
||||||
|
|
||||||
fdt.property_string("compatible", gic_device.fdt_compatibility())?;
|
fdt.property_string("compatible", gic_device.lock().unwrap().fdt_compatibility())?;
|
||||||
fdt.property_null("interrupt-controller")?;
|
fdt.property_null("interrupt-controller")?;
|
||||||
// "interrupt-cells" field specifies the number of cells needed to encode an
|
// "interrupt-cells" field specifies the number of cells needed to encode an
|
||||||
// interrupt source. The type shall be a <u32> and the value shall be 3 if no PPI affinity description
|
// 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)?;
|
||||||
@@ -334,18 +335,18 @@ fn create_gic_node(fdt: &mut FdtWriter, gic_device: &dyn GicDevice) -> FdtWriter
|
|||||||
|
|
||||||
let gic_intr_prop = [
|
let gic_intr_prop = [
|
||||||
GIC_FDT_IRQ_TYPE_PPI,
|
GIC_FDT_IRQ_TYPE_PPI,
|
||||||
gic_device.fdt_maint_irq(),
|
gic_device.lock().unwrap().fdt_maint_irq(),
|
||||||
IRQ_TYPE_LEVEL_HI,
|
IRQ_TYPE_LEVEL_HI,
|
||||||
];
|
];
|
||||||
fdt.property_array_u32("interrupts", &gic_intr_prop)?;
|
fdt.property_array_u32("interrupts", &gic_intr_prop)?;
|
||||||
|
|
||||||
if gic_device.msi_compatible() {
|
if gic_device.lock().unwrap().msi_compatible() {
|
||||||
let msic_node = fdt.begin_node("msic")?;
|
let msic_node = fdt.begin_node("msic")?;
|
||||||
fdt.property_string("compatible", gic_device.msi_compatibility())?;
|
fdt.property_string("compatible", gic_device.lock().unwrap().msi_compatibility())?;
|
||||||
fdt.property_null("msi-controller")?;
|
fdt.property_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.lock().unwrap().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)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,262 +0,0 @@
|
|||||||
// Copyright 2021 Arm Limited (or its affiliates). All rights reserved.
|
|
||||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
// This file implements the GicV3 device.
|
|
||||||
|
|
||||||
pub mod kvm {
|
|
||||||
use crate::aarch64::gic::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
|
||||||
use crate::aarch64::gic::icc_regs::{get_icc_regs, set_icc_regs};
|
|
||||||
use crate::aarch64::gic::kvm::{save_pending_tables, KvmGicDevice};
|
|
||||||
use crate::aarch64::gic::redist_regs::{
|
|
||||||
construct_gicr_typers, get_redist_regs, set_redist_regs,
|
|
||||||
};
|
|
||||||
use crate::aarch64::gic::GicDevice;
|
|
||||||
use crate::layout;
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use hypervisor::kvm::kvm_bindings;
|
|
||||||
use hypervisor::CpuState;
|
|
||||||
use std::any::Any;
|
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::{boxed::Box, result};
|
|
||||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
|
||||||
use versionize_derive::Versionize;
|
|
||||||
use vm_memory::Address;
|
|
||||||
use vm_migration::{
|
|
||||||
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable,
|
|
||||||
VersionMapped,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Errors thrown while saving/restoring the GICv3.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
/// Error in saving RDIST pending tables into guest RAM.
|
|
||||||
SavePendingTables(crate::aarch64::gic::Error),
|
|
||||||
/// Error in saving GIC distributor registers.
|
|
||||||
SaveDistributorRegisters(crate::aarch64::gic::Error),
|
|
||||||
/// Error in restoring GIC distributor registers.
|
|
||||||
RestoreDistributorRegisters(crate::aarch64::gic::Error),
|
|
||||||
/// Error in saving GIC distributor control registers.
|
|
||||||
SaveDistributorCtrlRegisters(crate::aarch64::gic::Error),
|
|
||||||
/// Error in restoring GIC distributor control registers.
|
|
||||||
RestoreDistributorCtrlRegisters(crate::aarch64::gic::Error),
|
|
||||||
/// Error in saving GIC redistributor registers.
|
|
||||||
SaveRedistributorRegisters(crate::aarch64::gic::Error),
|
|
||||||
/// Error in restoring GIC redistributor registers.
|
|
||||||
RestoreRedistributorRegisters(crate::aarch64::gic::Error),
|
|
||||||
/// Error in saving GIC CPU interface registers.
|
|
||||||
SaveIccRegisters(crate::aarch64::gic::Error),
|
|
||||||
/// Error in restoring GIC CPU interface registers.
|
|
||||||
RestoreIccRegisters(crate::aarch64::gic::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
|
||||||
|
|
||||||
pub struct KvmGicV3 {
|
|
||||||
/// The hypervisor agnostic device for the GicV3
|
|
||||||
device: Arc<dyn hypervisor::Device>,
|
|
||||||
|
|
||||||
/// Vector holding values of GICR_TYPER for each vCPU
|
|
||||||
gicr_typers: Vec<u64>,
|
|
||||||
|
|
||||||
/// GIC device properties, to be used for setting up the fdt entry
|
|
||||||
properties: [u64; 4],
|
|
||||||
|
|
||||||
/// Number of CPUs handled by the device
|
|
||||||
vcpu_count: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Versionize)]
|
|
||||||
pub struct Gicv3State {
|
|
||||||
dist: Vec<u32>,
|
|
||||||
rdist: Vec<u32>,
|
|
||||||
icc: Vec<u32>,
|
|
||||||
// special register that enables interrupts and affinity routing
|
|
||||||
gicd_ctlr: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VersionMapped for Gicv3State {}
|
|
||||||
|
|
||||||
impl KvmGicV3 {
|
|
||||||
// Device trees specific constants
|
|
||||||
pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
|
|
||||||
|
|
||||||
/// Get the address of the GIC distributor.
|
|
||||||
pub fn get_dist_addr() -> u64 {
|
|
||||||
layout::GIC_V3_DIST_START.raw_value()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the size of the GIC distributor.
|
|
||||||
pub fn get_dist_size() -> u64 {
|
|
||||||
layout::GIC_V3_DIST_SIZE
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the address of the GIC redistributors.
|
|
||||||
pub fn get_redists_addr(vcpu_count: u64) -> u64 {
|
|
||||||
KvmGicV3::get_dist_addr() - KvmGicV3::get_redists_size(vcpu_count)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the size of the GIC redistributors.
|
|
||||||
pub fn get_redists_size(vcpu_count: u64) -> u64 {
|
|
||||||
vcpu_count * layout::GIC_V3_REDIST_SIZE
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Save the state of GIC.
|
|
||||||
fn state(&self, gicr_typers: &[u64]) -> Result<Gicv3State> {
|
|
||||||
let gicd_ctlr =
|
|
||||||
read_ctlr(self.device()).map_err(Error::SaveDistributorCtrlRegisters)?;
|
|
||||||
|
|
||||||
let dist_state =
|
|
||||||
get_dist_regs(self.device()).map_err(Error::SaveDistributorRegisters)?;
|
|
||||||
|
|
||||||
let rdist_state = get_redist_regs(self.device(), gicr_typers)
|
|
||||||
.map_err(Error::SaveRedistributorRegisters)?;
|
|
||||||
|
|
||||||
let icc_state =
|
|
||||||
get_icc_regs(self.device(), gicr_typers).map_err(Error::SaveIccRegisters)?;
|
|
||||||
|
|
||||||
Ok(Gicv3State {
|
|
||||||
dist: dist_state,
|
|
||||||
rdist: rdist_state,
|
|
||||||
icc: icc_state,
|
|
||||||
gicd_ctlr,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Restore the state of GIC.
|
|
||||||
fn set_state(&mut self, gicr_typers: &[u64], state: &Gicv3State) -> Result<()> {
|
|
||||||
write_ctlr(self.device(), state.gicd_ctlr)
|
|
||||||
.map_err(Error::RestoreDistributorCtrlRegisters)?;
|
|
||||||
|
|
||||||
set_dist_regs(self.device(), &state.dist)
|
|
||||||
.map_err(Error::RestoreDistributorRegisters)?;
|
|
||||||
|
|
||||||
set_redist_regs(self.device(), gicr_typers, &state.rdist)
|
|
||||||
.map_err(Error::RestoreRedistributorRegisters)?;
|
|
||||||
|
|
||||||
set_icc_regs(self.device(), gicr_typers, &state.icc)
|
|
||||||
.map_err(Error::RestoreIccRegisters)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GicDevice for KvmGicV3 {
|
|
||||||
fn device(&self) -> &Arc<dyn hypervisor::Device> {
|
|
||||||
&self.device
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fdt_compatibility(&self) -> &str {
|
|
||||||
"arm,gic-v3"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fdt_maint_irq(&self) -> u32 {
|
|
||||||
KvmGicV3::ARCH_GIC_V3_MAINT_IRQ
|
|
||||||
}
|
|
||||||
|
|
||||||
fn device_properties(&self) -> &[u64] {
|
|
||||||
&self.properties
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vcpu_count(&self) -> u64 {
|
|
||||||
self.vcpu_count
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_its_device(&mut self, _its_device: Option<Arc<dyn hypervisor::Device>>) {}
|
|
||||||
|
|
||||||
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
|
|
||||||
let gicr_typers = construct_gicr_typers(vcpu_states);
|
|
||||||
self.gicr_typers = gicr_typers;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any_concrete_mut(&mut self) -> &mut dyn Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KvmGicDevice for KvmGicV3 {
|
|
||||||
fn version() -> u32 {
|
|
||||||
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_device(
|
|
||||||
device: Arc<dyn hypervisor::Device>,
|
|
||||||
vcpu_count: u64,
|
|
||||||
) -> Box<dyn GicDevice> {
|
|
||||||
Box::new(KvmGicV3 {
|
|
||||||
device,
|
|
||||||
gicr_typers: vec![0; vcpu_count.try_into().unwrap()],
|
|
||||||
properties: [
|
|
||||||
KvmGicV3::get_dist_addr(),
|
|
||||||
KvmGicV3::get_dist_size(),
|
|
||||||
KvmGicV3::get_redists_addr(vcpu_count),
|
|
||||||
KvmGicV3::get_redists_size(vcpu_count),
|
|
||||||
],
|
|
||||||
vcpu_count,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_device_attributes(
|
|
||||||
_vm: &Arc<dyn hypervisor::Vm>,
|
|
||||||
gic_device: &mut dyn GicDevice,
|
|
||||||
) -> crate::aarch64::gic::Result<()> {
|
|
||||||
/* Setting up the distributor attribute.
|
|
||||||
We are placing the GIC below 1GB so we need to substract the size of the distributor.
|
|
||||||
*/
|
|
||||||
Self::set_device_attribute(
|
|
||||||
gic_device.device(),
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
|
||||||
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST),
|
|
||||||
&KvmGicV3::get_dist_addr() as *const u64 as u64,
|
|
||||||
0,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
/* Setting up the redistributors' attribute.
|
|
||||||
We are calculating here the start of the redistributors address. We have one per CPU.
|
|
||||||
*/
|
|
||||||
Self::set_device_attribute(
|
|
||||||
gic_device.device(),
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
|
||||||
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST),
|
|
||||||
&KvmGicV3::get_redists_addr(gic_device.vcpu_count()) as *const u64 as u64,
|
|
||||||
0,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const GIC_V3_SNAPSHOT_ID: &str = "gic-v3";
|
|
||||||
impl Snapshottable for KvmGicV3 {
|
|
||||||
fn id(&self) -> String {
|
|
||||||
GIC_V3_SNAPSHOT_ID.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
|
||||||
let gicr_typers = self.gicr_typers.clone();
|
|
||||||
Snapshot::new_from_versioned_state(&self.id(), &self.state(&gicr_typers).unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
|
||||||
let gicr_typers = self.gicr_typers.clone();
|
|
||||||
self.set_state(&gicr_typers, &snapshot.to_versioned_state(&self.id())?)
|
|
||||||
.map_err(|e| {
|
|
||||||
MigratableError::Restore(anyhow!("Could not restore GICv3 state {:?}", e))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Pausable for KvmGicV3 {
|
|
||||||
fn pause(&mut self) -> std::result::Result<(), MigratableError> {
|
|
||||||
// Flush redistributors pending tables to guest RAM.
|
|
||||||
save_pending_tables(self.device()).map_err(|e| {
|
|
||||||
MigratableError::Pause(anyhow!("Could not save GICv3 GIC pending tables {:?}", e))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Transportable for KvmGicV3 {}
|
|
||||||
impl Migratable for KvmGicV3 {}
|
|
||||||
}
|
|
||||||
@@ -1,514 +0,0 @@
|
|||||||
// Copyright 2021 Arm Limited (or its affiliates). All rights reserved.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
// This file implements the GicV3 device with ITS (Virtual Interrupt Translation Service).
|
|
||||||
|
|
||||||
pub mod kvm {
|
|
||||||
use crate::aarch64::gic::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
|
||||||
use crate::aarch64::gic::icc_regs::{get_icc_regs, set_icc_regs};
|
|
||||||
use crate::aarch64::gic::redist_regs::{
|
|
||||||
construct_gicr_typers, get_redist_regs, set_redist_regs,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::aarch64::gic::gicv3::kvm::KvmGicV3;
|
|
||||||
use crate::aarch64::gic::kvm::{save_pending_tables, KvmGicDevice};
|
|
||||||
use crate::aarch64::gic::GicDevice;
|
|
||||||
use crate::layout;
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use hypervisor::kvm::kvm_bindings;
|
|
||||||
use hypervisor::CpuState;
|
|
||||||
use std::any::Any;
|
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::{boxed::Box, result};
|
|
||||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
|
||||||
use versionize_derive::Versionize;
|
|
||||||
use vm_migration::{
|
|
||||||
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable,
|
|
||||||
VersionMapped,
|
|
||||||
};
|
|
||||||
|
|
||||||
const GITS_CTLR: u32 = 0x0000;
|
|
||||||
const GITS_IIDR: u32 = 0x0004;
|
|
||||||
const GITS_CBASER: u32 = 0x0080;
|
|
||||||
const GITS_CWRITER: u32 = 0x0088;
|
|
||||||
const GITS_CREADR: u32 = 0x0090;
|
|
||||||
const GITS_BASER: u32 = 0x0100;
|
|
||||||
|
|
||||||
/// Errors thrown while saving/restoring the GICv3ITS.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
/// Error in saving RDIST pending tables into guest RAM.
|
|
||||||
SavePendingTables(crate::aarch64::gic::Error),
|
|
||||||
/// Error in saving GIC distributor registers.
|
|
||||||
SaveDistributorRegisters(crate::aarch64::gic::Error),
|
|
||||||
/// Error in restoring GIC distributor registers.
|
|
||||||
RestoreDistributorRegisters(crate::aarch64::gic::Error),
|
|
||||||
/// Error in saving GIC distributor control registers.
|
|
||||||
SaveDistributorCtrlRegisters(crate::aarch64::gic::Error),
|
|
||||||
/// Error in restoring GIC distributor control registers.
|
|
||||||
RestoreDistributorCtrlRegisters(crate::aarch64::gic::Error),
|
|
||||||
/// Error in saving GIC redistributor registers.
|
|
||||||
SaveRedistributorRegisters(crate::aarch64::gic::Error),
|
|
||||||
/// Error in restoring GIC redistributor registers.
|
|
||||||
RestoreRedistributorRegisters(crate::aarch64::gic::Error),
|
|
||||||
/// Error in saving GIC CPU interface registers.
|
|
||||||
SaveIccRegisters(crate::aarch64::gic::Error),
|
|
||||||
/// Error in restoring GIC CPU interface registers.
|
|
||||||
RestoreIccRegisters(crate::aarch64::gic::Error),
|
|
||||||
/// Error in saving GICv3ITS IIDR register.
|
|
||||||
SaveITSIIDR(crate::aarch64::gic::Error),
|
|
||||||
/// Error in restoring GICv3ITS IIDR register.
|
|
||||||
RestoreITSIIDR(crate::aarch64::gic::Error),
|
|
||||||
/// Error in saving GICv3ITS CBASER register.
|
|
||||||
SaveITSCBASER(crate::aarch64::gic::Error),
|
|
||||||
/// Error in restoring GICv3ITS CBASER register.
|
|
||||||
RestoreITSCBASER(crate::aarch64::gic::Error),
|
|
||||||
/// Error in saving GICv3ITS CREADR register.
|
|
||||||
SaveITSCREADR(crate::aarch64::gic::Error),
|
|
||||||
/// Error in restoring GICv3ITS CREADR register.
|
|
||||||
RestoreITSCREADR(crate::aarch64::gic::Error),
|
|
||||||
/// Error in saving GICv3ITS CWRITER register.
|
|
||||||
SaveITSCWRITER(crate::aarch64::gic::Error),
|
|
||||||
/// Error in restoring GICv3ITS CWRITER register.
|
|
||||||
RestoreITSCWRITER(crate::aarch64::gic::Error),
|
|
||||||
/// Error in saving GICv3ITS BASER register.
|
|
||||||
SaveITSBASER(crate::aarch64::gic::Error),
|
|
||||||
/// Error in restoring GICv3ITS BASER register.
|
|
||||||
RestoreITSBASER(crate::aarch64::gic::Error),
|
|
||||||
/// Error in saving GICv3ITS CTLR register.
|
|
||||||
SaveITSCTLR(crate::aarch64::gic::Error),
|
|
||||||
/// Error in restoring GICv3ITS CTLR register.
|
|
||||||
RestoreITSCTLR(crate::aarch64::gic::Error),
|
|
||||||
/// Error in saving GICv3ITS restore tables.
|
|
||||||
SaveITSTables(crate::aarch64::gic::Error),
|
|
||||||
/// Error in restoring GICv3ITS restore tables.
|
|
||||||
RestoreITSTables(crate::aarch64::gic::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
|
||||||
|
|
||||||
/// Access an ITS device attribute.
|
|
||||||
///
|
|
||||||
/// This is a helper function to get/set the ITS device attribute depending
|
|
||||||
/// the bool parameter `set` provided.
|
|
||||||
pub fn gicv3_its_attr_access(
|
|
||||||
its_device: &Arc<dyn hypervisor::Device>,
|
|
||||||
group: u32,
|
|
||||||
attr: u32,
|
|
||||||
val: &u64,
|
|
||||||
set: bool,
|
|
||||||
) -> crate::aarch64::gic::Result<()> {
|
|
||||||
let mut gicv3_its_attr = kvm_bindings::kvm_device_attr {
|
|
||||||
group,
|
|
||||||
attr: attr as u64,
|
|
||||||
addr: val as *const u64 as u64,
|
|
||||||
flags: 0,
|
|
||||||
};
|
|
||||||
if set {
|
|
||||||
its_device
|
|
||||||
.set_device_attr(&gicv3_its_attr)
|
|
||||||
.map_err(crate::aarch64::gic::Error::SetDeviceAttribute)?;
|
|
||||||
} else {
|
|
||||||
its_device
|
|
||||||
.get_device_attr(&mut gicv3_its_attr)
|
|
||||||
.map_err(crate::aarch64::gic::Error::GetDeviceAttribute)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Function that saves/restores ITS tables into guest RAM.
|
|
||||||
///
|
|
||||||
/// The tables get flushed to guest RAM whenever the VM gets stopped.
|
|
||||||
pub fn gicv3_its_tables_access(
|
|
||||||
its_device: &Arc<dyn hypervisor::Device>,
|
|
||||||
save: bool,
|
|
||||||
) -> crate::aarch64::gic::Result<()> {
|
|
||||||
let attr = if save {
|
|
||||||
u64::from(kvm_bindings::KVM_DEV_ARM_ITS_SAVE_TABLES)
|
|
||||||
} else {
|
|
||||||
u64::from(kvm_bindings::KVM_DEV_ARM_ITS_RESTORE_TABLES)
|
|
||||||
};
|
|
||||||
|
|
||||||
let init_gic_attr = kvm_bindings::kvm_device_attr {
|
|
||||||
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
|
||||||
attr,
|
|
||||||
addr: 0,
|
|
||||||
flags: 0,
|
|
||||||
};
|
|
||||||
its_device
|
|
||||||
.set_device_attr(&init_gic_attr)
|
|
||||||
.map_err(crate::aarch64::gic::Error::SetDeviceAttribute)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct KvmGicV3Its {
|
|
||||||
/// The hypervisor agnostic device for the GicV3
|
|
||||||
device: Arc<dyn hypervisor::Device>,
|
|
||||||
|
|
||||||
/// The hypervisor agnostic device for the Its device
|
|
||||||
its_device: Option<Arc<dyn hypervisor::Device>>,
|
|
||||||
|
|
||||||
/// Vector holding values of GICR_TYPER for each vCPU
|
|
||||||
gicr_typers: Vec<u64>,
|
|
||||||
|
|
||||||
/// GIC device properties, to be used for setting up the fdt entry
|
|
||||||
gic_properties: [u64; 4],
|
|
||||||
|
|
||||||
/// MSI device properties, to be used for setting up the fdt entry
|
|
||||||
msi_properties: [u64; 2],
|
|
||||||
|
|
||||||
/// Number of CPUs handled by the device
|
|
||||||
vcpu_count: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Versionize)]
|
|
||||||
pub struct Gicv3ItsState {
|
|
||||||
dist: Vec<u32>,
|
|
||||||
rdist: Vec<u32>,
|
|
||||||
icc: Vec<u32>,
|
|
||||||
// special register that enables interrupts and affinity routing
|
|
||||||
gicd_ctlr: u32,
|
|
||||||
its_ctlr: u64,
|
|
||||||
its_iidr: u64,
|
|
||||||
its_cbaser: u64,
|
|
||||||
its_cwriter: u64,
|
|
||||||
its_creadr: u64,
|
|
||||||
its_baser: [u64; 8],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VersionMapped for Gicv3ItsState {}
|
|
||||||
|
|
||||||
impl KvmGicV3Its {
|
|
||||||
fn get_msi_size() -> u64 {
|
|
||||||
layout::GIC_V3_ITS_SIZE
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_msi_addr(vcpu_count: u64) -> u64 {
|
|
||||||
KvmGicV3::get_redists_addr(vcpu_count) - KvmGicV3Its::get_msi_size()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Save the state of GICv3ITS.
|
|
||||||
fn state(&self, gicr_typers: &[u64]) -> Result<Gicv3ItsState> {
|
|
||||||
let gicd_ctlr =
|
|
||||||
read_ctlr(self.device()).map_err(Error::SaveDistributorCtrlRegisters)?;
|
|
||||||
|
|
||||||
let dist_state =
|
|
||||||
get_dist_regs(self.device()).map_err(Error::SaveDistributorRegisters)?;
|
|
||||||
|
|
||||||
let rdist_state = get_redist_regs(self.device(), gicr_typers)
|
|
||||||
.map_err(Error::SaveRedistributorRegisters)?;
|
|
||||||
|
|
||||||
let icc_state =
|
|
||||||
get_icc_regs(self.device(), gicr_typers).map_err(Error::SaveIccRegisters)?;
|
|
||||||
|
|
||||||
let its_baser_state: [u64; 8] = [0; 8];
|
|
||||||
for i in 0..8 {
|
|
||||||
gicv3_its_attr_access(
|
|
||||||
self.its_device().unwrap(),
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
|
||||||
GITS_BASER + i * 8,
|
|
||||||
&its_baser_state[i as usize],
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.map_err(Error::SaveITSBASER)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let its_ctlr_state: u64 = 0;
|
|
||||||
gicv3_its_attr_access(
|
|
||||||
self.its_device().unwrap(),
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
|
||||||
GITS_CTLR,
|
|
||||||
&its_ctlr_state,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.map_err(Error::SaveITSCTLR)?;
|
|
||||||
|
|
||||||
let its_cbaser_state: u64 = 0;
|
|
||||||
gicv3_its_attr_access(
|
|
||||||
self.its_device().unwrap(),
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
|
||||||
GITS_CBASER,
|
|
||||||
&its_cbaser_state,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.map_err(Error::SaveITSCBASER)?;
|
|
||||||
|
|
||||||
let its_creadr_state: u64 = 0;
|
|
||||||
gicv3_its_attr_access(
|
|
||||||
self.its_device().unwrap(),
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
|
||||||
GITS_CREADR,
|
|
||||||
&its_creadr_state,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.map_err(Error::SaveITSCREADR)?;
|
|
||||||
|
|
||||||
let its_cwriter_state: u64 = 0;
|
|
||||||
gicv3_its_attr_access(
|
|
||||||
self.its_device().unwrap(),
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
|
||||||
GITS_CWRITER,
|
|
||||||
&its_cwriter_state,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.map_err(Error::SaveITSCWRITER)?;
|
|
||||||
|
|
||||||
let its_iidr_state: u64 = 0;
|
|
||||||
gicv3_its_attr_access(
|
|
||||||
self.its_device().unwrap(),
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
|
||||||
GITS_IIDR,
|
|
||||||
&its_iidr_state,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.map_err(Error::SaveITSIIDR)?;
|
|
||||||
|
|
||||||
Ok(Gicv3ItsState {
|
|
||||||
dist: dist_state,
|
|
||||||
rdist: rdist_state,
|
|
||||||
icc: icc_state,
|
|
||||||
gicd_ctlr,
|
|
||||||
its_ctlr: its_ctlr_state,
|
|
||||||
its_iidr: its_iidr_state,
|
|
||||||
its_cbaser: its_cbaser_state,
|
|
||||||
its_cwriter: its_cwriter_state,
|
|
||||||
its_creadr: its_creadr_state,
|
|
||||||
its_baser: its_baser_state,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Restore the state of GICv3ITS.
|
|
||||||
fn set_state(&mut self, gicr_typers: &[u64], state: &Gicv3ItsState) -> Result<()> {
|
|
||||||
write_ctlr(self.device(), state.gicd_ctlr)
|
|
||||||
.map_err(Error::RestoreDistributorCtrlRegisters)?;
|
|
||||||
|
|
||||||
set_dist_regs(self.device(), &state.dist)
|
|
||||||
.map_err(Error::RestoreDistributorRegisters)?;
|
|
||||||
|
|
||||||
set_redist_regs(self.device(), gicr_typers, &state.rdist)
|
|
||||||
.map_err(Error::RestoreRedistributorRegisters)?;
|
|
||||||
|
|
||||||
set_icc_regs(self.device(), gicr_typers, &state.icc)
|
|
||||||
.map_err(Error::RestoreIccRegisters)?;
|
|
||||||
|
|
||||||
//Restore GICv3ITS registers
|
|
||||||
gicv3_its_attr_access(
|
|
||||||
self.its_device().unwrap(),
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
|
||||||
GITS_IIDR,
|
|
||||||
&state.its_iidr,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.map_err(Error::RestoreITSIIDR)?;
|
|
||||||
|
|
||||||
gicv3_its_attr_access(
|
|
||||||
self.its_device().unwrap(),
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
|
||||||
GITS_CBASER,
|
|
||||||
&state.its_cbaser,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.map_err(Error::RestoreITSCBASER)?;
|
|
||||||
|
|
||||||
gicv3_its_attr_access(
|
|
||||||
self.its_device().unwrap(),
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
|
||||||
GITS_CREADR,
|
|
||||||
&state.its_creadr,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.map_err(Error::RestoreITSCREADR)?;
|
|
||||||
|
|
||||||
gicv3_its_attr_access(
|
|
||||||
self.its_device().unwrap(),
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
|
||||||
GITS_CWRITER,
|
|
||||||
&state.its_cwriter,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.map_err(Error::RestoreITSCWRITER)?;
|
|
||||||
|
|
||||||
for i in 0..8 {
|
|
||||||
gicv3_its_attr_access(
|
|
||||||
self.its_device().unwrap(),
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
|
||||||
GITS_BASER + i * 8,
|
|
||||||
&state.its_baser[i as usize],
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.map_err(Error::RestoreITSBASER)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore ITS tables
|
|
||||||
gicv3_its_tables_access(self.its_device().unwrap(), false)
|
|
||||||
.map_err(Error::RestoreITSTables)?;
|
|
||||||
|
|
||||||
gicv3_its_attr_access(
|
|
||||||
self.its_device().unwrap(),
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
|
||||||
GITS_CTLR,
|
|
||||||
&state.its_ctlr,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.map_err(Error::RestoreITSCTLR)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GicDevice for KvmGicV3Its {
|
|
||||||
fn device(&self) -> &Arc<dyn hypervisor::Device> {
|
|
||||||
&self.device
|
|
||||||
}
|
|
||||||
|
|
||||||
fn its_device(&self) -> Option<&Arc<dyn hypervisor::Device>> {
|
|
||||||
self.its_device.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fdt_compatibility(&self) -> &str {
|
|
||||||
"arm,gic-v3"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn msi_compatible(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn msi_compatibility(&self) -> &str {
|
|
||||||
"arm,gic-v3-its"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fdt_maint_irq(&self) -> u32 {
|
|
||||||
KvmGicV3::ARCH_GIC_V3_MAINT_IRQ
|
|
||||||
}
|
|
||||||
|
|
||||||
fn msi_properties(&self) -> &[u64] {
|
|
||||||
&self.msi_properties
|
|
||||||
}
|
|
||||||
|
|
||||||
fn device_properties(&self) -> &[u64] {
|
|
||||||
&self.gic_properties
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vcpu_count(&self) -> u64 {
|
|
||||||
self.vcpu_count
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_its_device(&mut self, its_device: Option<Arc<dyn hypervisor::Device>>) {
|
|
||||||
self.its_device = its_device;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
|
|
||||||
let gicr_typers = construct_gicr_typers(vcpu_states);
|
|
||||||
self.gicr_typers = gicr_typers;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any_concrete_mut(&mut self) -> &mut dyn Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KvmGicDevice for KvmGicV3Its {
|
|
||||||
fn version() -> u32 {
|
|
||||||
KvmGicV3::version()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_device(
|
|
||||||
device: Arc<dyn hypervisor::Device>,
|
|
||||||
vcpu_count: u64,
|
|
||||||
) -> Box<dyn GicDevice> {
|
|
||||||
Box::new(KvmGicV3Its {
|
|
||||||
device,
|
|
||||||
its_device: None,
|
|
||||||
gicr_typers: vec![0; vcpu_count.try_into().unwrap()],
|
|
||||||
gic_properties: [
|
|
||||||
KvmGicV3::get_dist_addr(),
|
|
||||||
KvmGicV3::get_dist_size(),
|
|
||||||
KvmGicV3::get_redists_addr(vcpu_count),
|
|
||||||
KvmGicV3::get_redists_size(vcpu_count),
|
|
||||||
],
|
|
||||||
msi_properties: [
|
|
||||||
KvmGicV3Its::get_msi_addr(vcpu_count),
|
|
||||||
KvmGicV3Its::get_msi_size(),
|
|
||||||
],
|
|
||||||
vcpu_count,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_device_attributes(
|
|
||||||
vm: &Arc<dyn hypervisor::Vm>,
|
|
||||||
gic_device: &mut dyn GicDevice,
|
|
||||||
) -> crate::aarch64::gic::Result<()> {
|
|
||||||
KvmGicV3::init_device_attributes(vm, gic_device)?;
|
|
||||||
|
|
||||||
let mut its_device = kvm_bindings::kvm_create_device {
|
|
||||||
type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS,
|
|
||||||
fd: 0,
|
|
||||||
flags: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let its_fd = vm
|
|
||||||
.create_device(&mut its_device)
|
|
||||||
.map_err(crate::aarch64::gic::Error::CreateGic)?;
|
|
||||||
|
|
||||||
Self::set_device_attribute(
|
|
||||||
&its_fd,
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
|
||||||
u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE),
|
|
||||||
&KvmGicV3Its::get_msi_addr(gic_device.vcpu_count()) as *const u64 as u64,
|
|
||||||
0,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Self::set_device_attribute(
|
|
||||||
&its_fd,
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
|
||||||
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
gic_device.set_its_device(Some(its_fd));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const GIC_V3_ITS_SNAPSHOT_ID: &str = "gic-v3-its";
|
|
||||||
impl Snapshottable for KvmGicV3Its {
|
|
||||||
fn id(&self) -> String {
|
|
||||||
GIC_V3_ITS_SNAPSHOT_ID.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
|
||||||
let gicr_typers = self.gicr_typers.clone();
|
|
||||||
Snapshot::new_from_versioned_state(&self.id(), &self.state(&gicr_typers).unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
|
||||||
let gicr_typers = self.gicr_typers.clone();
|
|
||||||
self.set_state(&gicr_typers, &snapshot.to_versioned_state(&self.id())?)
|
|
||||||
.map_err(|e| {
|
|
||||||
MigratableError::Restore(anyhow!("Could not restore GICv3ITS state {:?}", e))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Pausable for KvmGicV3Its {
|
|
||||||
fn pause(&mut self) -> std::result::Result<(), MigratableError> {
|
|
||||||
// Flush redistributors pending tables to guest RAM.
|
|
||||||
save_pending_tables(self.device()).map_err(|e| {
|
|
||||||
MigratableError::Pause(anyhow!(
|
|
||||||
"Could not save GICv3ITS GIC pending tables {:?}",
|
|
||||||
e
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
// Flush ITS tables to guest RAM.
|
|
||||||
gicv3_its_tables_access(self.its_device().unwrap(), true).map_err(|e| {
|
|
||||||
MigratableError::Pause(anyhow!("Could not save GICv3ITS ITS tables {:?}", e))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Transportable for KvmGicV3Its {}
|
|
||||||
impl Migratable for KvmGicV3Its {}
|
|
||||||
}
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
// Copyright 2021 Arm Limited (or its affiliates). All rights reserved.
|
|
||||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
pub mod dist_regs;
|
|
||||||
pub mod gicv3;
|
|
||||||
pub mod gicv3_its;
|
|
||||||
pub mod icc_regs;
|
|
||||||
pub mod redist_regs;
|
|
||||||
|
|
||||||
pub use self::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
|
||||||
pub use self::icc_regs::{get_icc_regs, set_icc_regs};
|
|
||||||
pub use self::redist_regs::{get_redist_regs, set_redist_regs};
|
|
||||||
use hypervisor::CpuState;
|
|
||||||
use std::any::Any;
|
|
||||||
use std::result;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
/// Errors thrown while setting up the GIC.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
/// Error while calling KVM ioctl for setting up the global interrupt controller.
|
|
||||||
CreateGic(hypervisor::HypervisorVmError),
|
|
||||||
/// Error while setting device attributes for the GIC.
|
|
||||||
SetDeviceAttribute(hypervisor::HypervisorDeviceError),
|
|
||||||
/// Error while getting device attributes for the GIC.
|
|
||||||
GetDeviceAttribute(hypervisor::HypervisorDeviceError),
|
|
||||||
}
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
|
||||||
|
|
||||||
pub trait GicDevice: Send {
|
|
||||||
/// Returns the hypervisor agnostic Device of the GIC device
|
|
||||||
fn device(&self) -> &Arc<dyn hypervisor::Device>;
|
|
||||||
|
|
||||||
/// Returns the hypervisor agnostic Device of the ITS device
|
|
||||||
fn its_device(&self) -> Option<&Arc<dyn hypervisor::Device>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the fdt compatibility property of the device
|
|
||||||
fn fdt_compatibility(&self) -> &str;
|
|
||||||
|
|
||||||
/// Returns the maint_irq fdt property of the device
|
|
||||||
fn fdt_maint_irq(&self) -> u32;
|
|
||||||
|
|
||||||
/// Returns an array with GIC device properties
|
|
||||||
fn device_properties(&self) -> &[u64];
|
|
||||||
|
|
||||||
/// Returns the number of vCPUs this GIC handles
|
|
||||||
fn vcpu_count(&self) -> u64;
|
|
||||||
|
|
||||||
/// Returns whether the GIC device is MSI compatible or not
|
|
||||||
fn msi_compatible(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the MSI compatibility property of the device
|
|
||||||
fn msi_compatibility(&self) -> &str {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the MSI reg property of the device
|
|
||||||
fn msi_properties(&self) -> &[u64] {
|
|
||||||
&[]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_its_device(&mut self, its_device: Option<Arc<dyn hypervisor::Device>>);
|
|
||||||
|
|
||||||
/// Get the values of GICR_TYPER for each vCPU.
|
|
||||||
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]);
|
|
||||||
|
|
||||||
/// Downcast the trait object to its concrete type.
|
|
||||||
fn as_any_concrete_mut(&mut self) -> &mut dyn Any;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod kvm {
|
|
||||||
use super::GicDevice;
|
|
||||||
use super::Result;
|
|
||||||
use crate::aarch64::gic::gicv3_its::kvm::KvmGicV3Its;
|
|
||||||
use crate::layout;
|
|
||||||
use hypervisor::kvm::kvm_bindings;
|
|
||||||
use std::boxed::Box;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
/// Trait for GIC devices.
|
|
||||||
pub trait KvmGicDevice: Send + Sync + GicDevice {
|
|
||||||
/// Returns the GIC version of the device
|
|
||||||
fn version() -> u32;
|
|
||||||
|
|
||||||
/// Create the GIC device object
|
|
||||||
fn create_device(
|
|
||||||
device: Arc<dyn hypervisor::Device>,
|
|
||||||
vcpu_count: u64,
|
|
||||||
) -> Box<dyn GicDevice>;
|
|
||||||
|
|
||||||
/// Setup the device-specific attributes
|
|
||||||
fn init_device_attributes(
|
|
||||||
vm: &Arc<dyn hypervisor::Vm>,
|
|
||||||
gic_device: &mut dyn GicDevice,
|
|
||||||
) -> Result<()>;
|
|
||||||
|
|
||||||
/// Initialize a GIC device
|
|
||||||
fn init_device(vm: &Arc<dyn hypervisor::Vm>) -> Result<Arc<dyn hypervisor::Device>> {
|
|
||||||
let mut gic_device = kvm_bindings::kvm_create_device {
|
|
||||||
type_: Self::version(),
|
|
||||||
fd: 0,
|
|
||||||
flags: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
vm.create_device(&mut gic_device)
|
|
||||||
.map_err(super::Error::CreateGic)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set a GIC device attribute
|
|
||||||
fn set_device_attribute(
|
|
||||||
device: &Arc<dyn hypervisor::Device>,
|
|
||||||
group: u32,
|
|
||||||
attr: u64,
|
|
||||||
addr: u64,
|
|
||||||
flags: u32,
|
|
||||||
) -> Result<()> {
|
|
||||||
let attr = kvm_bindings::kvm_device_attr {
|
|
||||||
flags,
|
|
||||||
group,
|
|
||||||
attr,
|
|
||||||
addr,
|
|
||||||
};
|
|
||||||
device
|
|
||||||
.set_device_attr(&attr)
|
|
||||||
.map_err(super::Error::SetDeviceAttribute)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a GIC device attribute
|
|
||||||
fn get_device_attribute(
|
|
||||||
device: &Arc<dyn hypervisor::Device>,
|
|
||||||
group: u32,
|
|
||||||
attr: u64,
|
|
||||||
addr: u64,
|
|
||||||
flags: u32,
|
|
||||||
) -> Result<()> {
|
|
||||||
let mut attr = kvm_bindings::kvm_device_attr {
|
|
||||||
flags,
|
|
||||||
group,
|
|
||||||
attr,
|
|
||||||
addr,
|
|
||||||
};
|
|
||||||
device
|
|
||||||
.get_device_attr(&mut attr)
|
|
||||||
.map_err(super::Error::GetDeviceAttribute)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finalize the setup of a GIC device
|
|
||||||
fn finalize_device(gic_device: &dyn GicDevice) -> Result<()> {
|
|
||||||
/* We need to tell the kernel how many irqs to support with this vgic.
|
|
||||||
* See the `layout` module for details.
|
|
||||||
*/
|
|
||||||
let nr_irqs: u32 = layout::IRQ_NUM;
|
|
||||||
let nr_irqs_ptr = &nr_irqs as *const u32;
|
|
||||||
Self::set_device_attribute(
|
|
||||||
gic_device.device(),
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
|
|
||||||
0,
|
|
||||||
nr_irqs_ptr as u64,
|
|
||||||
0,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
/* Finalize the GIC.
|
|
||||||
* See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
|
|
||||||
*/
|
|
||||||
Self::set_device_attribute(
|
|
||||||
gic_device.device(),
|
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
|
||||||
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Method to initialize the GIC device
|
|
||||||
#[allow(clippy::new_ret_no_self)]
|
|
||||||
fn new(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GicDevice>> {
|
|
||||||
let vgic_fd = Self::init_device(vm)?;
|
|
||||||
|
|
||||||
let mut device = Self::create_device(vgic_fd, vcpu_count);
|
|
||||||
|
|
||||||
Self::init_device_attributes(vm, &mut *device)?;
|
|
||||||
|
|
||||||
Self::finalize_device(&*device)?;
|
|
||||||
|
|
||||||
Ok(device)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a GICv3-ITS device.
|
|
||||||
///
|
|
||||||
pub fn create_gic(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GicDevice>> {
|
|
||||||
debug!("creating a GICv3-ITS");
|
|
||||||
KvmGicV3Its::new(vm, vcpu_count)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Function that saves RDIST pending tables into guest RAM.
|
|
||||||
///
|
|
||||||
/// The tables get flushed to guest RAM whenever the VM gets stopped.
|
|
||||||
pub fn save_pending_tables(gic: &Arc<dyn hypervisor::Device>) -> Result<()> {
|
|
||||||
let init_gic_attr = kvm_bindings::kvm_device_attr {
|
|
||||||
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
|
||||||
attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES),
|
|
||||||
addr: 0,
|
|
||||||
flags: 0,
|
|
||||||
};
|
|
||||||
gic.set_device_attr(&init_gic_attr)
|
|
||||||
.map_err(super::Error::SetDeviceAttribute)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -59,7 +59,7 @@ pub const UEFI_START: GuestAddress = GuestAddress(0);
|
|||||||
pub const UEFI_SIZE: u64 = 0x040_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: GuestAddress = GuestAddress(0x0900_0000);
|
const MAPPED_IO_START: GuestAddress = GuestAddress(0x0900_0000);
|
||||||
|
|
||||||
/// See kernel file arch/arm64/include/uapi/asm/kvm.h for the GIC related definitions.
|
/// See kernel file arch/arm64/include/uapi/asm/kvm.h for the GIC related definitions.
|
||||||
/// 0x08ff_0000 ~ 0x0900_0000 is reserved for GICv3 Distributor
|
/// 0x08ff_0000 ~ 0x0900_0000 is reserved for GICv3 Distributor
|
||||||
@@ -73,7 +73,7 @@ pub const GIC_V3_REDIST_SIZE: u64 = 0x02_0000;
|
|||||||
pub const GIC_V3_ITS_SIZE: u64 = 0x02_0000;
|
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: GuestAddress = GuestAddress(0x0900_0000);
|
pub const LEGACY_SERIAL_MAPPED_IO_START: GuestAddress = MAPPED_IO_START;
|
||||||
pub const LEGACY_RTC_MAPPED_IO_START: GuestAddress = GuestAddress(0x0901_0000);
|
pub const LEGACY_RTC_MAPPED_IO_START: GuestAddress = GuestAddress(0x0901_0000);
|
||||||
pub const LEGACY_GPIO_MAPPED_IO_START: GuestAddress = GuestAddress(0x0902_0000);
|
pub const LEGACY_GPIO_MAPPED_IO_START: GuestAddress = GuestAddress(0x0902_0000);
|
||||||
|
|
||||||
|
|||||||
@@ -4,23 +4,21 @@
|
|||||||
|
|
||||||
/// Module for the flattened device tree.
|
/// Module for the flattened device tree.
|
||||||
pub mod fdt;
|
pub mod fdt;
|
||||||
/// Module for the global interrupt controller configuration.
|
|
||||||
pub mod gic;
|
|
||||||
/// Layout for this aarch64 system.
|
/// Layout for this aarch64 system.
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
/// Logic for configuring aarch64 registers.
|
/// Module for system registers definition
|
||||||
pub mod regs;
|
pub mod regs;
|
||||||
/// Module for loading UEFI binary.
|
/// Module for loading UEFI binary.
|
||||||
pub mod uefi;
|
pub mod uefi;
|
||||||
|
|
||||||
pub use self::fdt::DeviceInfoForFdt;
|
pub use self::fdt::DeviceInfoForFdt;
|
||||||
use crate::{DeviceType, GuestMemoryMmap, NumaNodes, PciSpaceInfo, RegionType};
|
use crate::{DeviceType, GuestMemoryMmap, NumaNodes, PciSpaceInfo, RegionType};
|
||||||
use gic::GicDevice;
|
use hypervisor::arch::aarch64::gic::Vgic;
|
||||||
use log::{log_enabled, Level};
|
use log::{log_enabled, Level};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, Mutex};
|
||||||
use vm_memory::{Address, GuestAddress, GuestMemory, GuestUsize};
|
use vm_memory::{Address, GuestAddress, GuestMemory, GuestUsize};
|
||||||
|
|
||||||
/// Errors thrown while configuring aarch64 system.
|
/// Errors thrown while configuring aarch64 system.
|
||||||
@@ -33,13 +31,13 @@ pub enum Error {
|
|||||||
WriteFdtToMemory(fdt::Error),
|
WriteFdtToMemory(fdt::Error),
|
||||||
|
|
||||||
/// Failed to create a GIC.
|
/// Failed to create a GIC.
|
||||||
SetupGic(gic::Error),
|
SetupGic,
|
||||||
|
|
||||||
/// Failed to compute the initramfs address.
|
/// Failed to compute the initramfs address.
|
||||||
InitramfsAddress,
|
InitramfsAddress,
|
||||||
|
|
||||||
/// Error configuring the general purpose registers
|
/// Error configuring the general purpose registers
|
||||||
RegsConfiguration(regs::Error),
|
RegsConfiguration(hypervisor::HypervisorCpuError),
|
||||||
|
|
||||||
/// Error configuring the MPIDR register
|
/// Error configuring the MPIDR register
|
||||||
VcpuRegMpidr(hypervisor::HypervisorCpuError),
|
VcpuRegMpidr(hypervisor::HypervisorCpuError),
|
||||||
@@ -50,7 +48,7 @@ pub enum Error {
|
|||||||
|
|
||||||
impl From<Error> for super::Error {
|
impl From<Error> for super::Error {
|
||||||
fn from(e: Error) -> super::Error {
|
fn from(e: Error) -> super::Error {
|
||||||
super::Error::AArch64Setup(e)
|
super::Error::PlatformSpecific(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,16 +62,22 @@ pub struct EntryPoint {
|
|||||||
|
|
||||||
/// Configure the specified VCPU, and return its MPIDR.
|
/// Configure the specified VCPU, and return its MPIDR.
|
||||||
pub fn configure_vcpu(
|
pub fn configure_vcpu(
|
||||||
fd: &Arc<dyn hypervisor::Vcpu>,
|
vcpu: &Arc<dyn hypervisor::Vcpu>,
|
||||||
id: u8,
|
id: u8,
|
||||||
kernel_entry_point: Option<EntryPoint>,
|
kernel_entry_point: Option<EntryPoint>,
|
||||||
) -> super::Result<u64> {
|
) -> super::Result<u64> {
|
||||||
if let Some(kernel_entry_point) = kernel_entry_point {
|
if let Some(kernel_entry_point) = kernel_entry_point {
|
||||||
regs::setup_regs(fd, id, kernel_entry_point.entry_addr.raw_value())
|
vcpu.setup_regs(
|
||||||
.map_err(Error::RegsConfiguration)?;
|
id,
|
||||||
|
kernel_entry_point.entry_addr.raw_value(),
|
||||||
|
super::layout::FDT_START.raw_value(),
|
||||||
|
)
|
||||||
|
.map_err(Error::RegsConfiguration)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mpidr = fd.read_mpidr().map_err(Error::VcpuRegMpidr)?;
|
let mpidr = vcpu
|
||||||
|
.get_sys_reg(regs::MPIDR_EL1)
|
||||||
|
.map_err(Error::VcpuRegMpidr)?;
|
||||||
Ok(mpidr)
|
Ok(mpidr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +147,7 @@ pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::Bui
|
|||||||
initrd: &Option<super::InitramfsConfig>,
|
initrd: &Option<super::InitramfsConfig>,
|
||||||
pci_space_info: &[PciSpaceInfo],
|
pci_space_info: &[PciSpaceInfo],
|
||||||
virtio_iommu_bdf: Option<u32>,
|
virtio_iommu_bdf: Option<u32>,
|
||||||
gic_device: &dyn GicDevice,
|
gic_device: &Arc<Mutex<dyn Vgic>>,
|
||||||
numa_nodes: &NumaNodes,
|
numa_nodes: &NumaNodes,
|
||||||
pmu_supported: bool,
|
pmu_supported: bool,
|
||||||
) -> super::Result<()> {
|
) -> super::Result<()> {
|
||||||
@@ -185,10 +189,10 @@ pub fn initramfs_load_addr(
|
|||||||
if guest_mem.address_in_range(offset) {
|
if guest_mem.address_in_range(offset) {
|
||||||
Ok(offset.raw_value())
|
Ok(offset.raw_value())
|
||||||
} else {
|
} else {
|
||||||
Err(super::Error::AArch64Setup(Error::InitramfsAddress))
|
Err(super::Error::PlatformSpecific(Error::InitramfsAddress))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => Err(super::Error::AArch64Setup(Error::InitramfsAddress)),
|
None => Err(super::Error::PlatformSpecific(Error::InitramfsAddress)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,75 +1,44 @@
|
|||||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
|
||||||
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the THIRD-PARTY file.
|
|
||||||
|
|
||||||
use hypervisor::kvm::kvm_bindings::{
|
|
||||||
kvm_regs, user_pt_regs, KVM_REG_ARM64, KVM_REG_ARM_CORE, KVM_REG_SIZE_U64,
|
|
||||||
};
|
|
||||||
use hypervisor::{arm64_core_reg_id, offset__of};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::{mem, result};
|
|
||||||
use vm_memory::Address;
|
|
||||||
|
|
||||||
/// Errors thrown while setting aarch64 registers.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
/// Failed to set core register (PC, PSTATE or general purpose ones).
|
|
||||||
SetCoreRegister(hypervisor::HypervisorCpuError),
|
|
||||||
/// Failed to get a system register.
|
|
||||||
GetSysRegister(hypervisor::HypervisorCpuError),
|
|
||||||
}
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
|
||||||
|
|
||||||
#[allow(non_upper_case_globals)]
|
|
||||||
// PSR (Processor State Register) bits.
|
|
||||||
// Taken from arch/arm64/include/uapi/asm/ptrace.h.
|
|
||||||
const PSR_MODE_EL1h: u64 = 0x0000_0005;
|
|
||||||
const PSR_F_BIT: u64 = 0x0000_0040;
|
|
||||||
const PSR_I_BIT: u64 = 0x0000_0080;
|
|
||||||
const PSR_A_BIT: u64 = 0x0000_0100;
|
|
||||||
const PSR_D_BIT: u64 = 0x0000_0200;
|
|
||||||
// Taken from arch/arm64/kvm/inject_fault.c.
|
|
||||||
const PSTATE_FAULT_BITS_64: u64 = PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT;
|
|
||||||
|
|
||||||
/// Configure core registers for a given CPU.
|
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// AArch64 system register encoding:
|
||||||
|
/// See https://developer.arm.com/documentation/ddi0487 (chapter D12)
|
||||||
///
|
///
|
||||||
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
/// 31 22 21 20 19 18 16 15 12 11 8 7 5 4 0
|
||||||
/// * `cpu_id` - Index of current vcpu.
|
/// +----------+---+-----+-----+-----+-----+-----+----+
|
||||||
/// * `boot_ip` - Starting instruction pointer.
|
/// |1101010100| L | op0 | op1 | CRn | CRm | op2 | Rt |
|
||||||
/// * `mem` - Reserved DRAM for current VM.
|
/// +----------+---+-----+-----+-----+-----+-----+----+
|
||||||
pub fn setup_regs(vcpu: &Arc<dyn hypervisor::Vcpu>, cpu_id: u8, boot_ip: u64) -> Result<()> {
|
///
|
||||||
let kreg_off = offset__of!(kvm_regs, regs);
|
/// Notes:
|
||||||
|
/// - L and Rt are reserved as implementation defined fields, ignored.
|
||||||
|
///
|
||||||
|
const SYSREG_HEAD: u32 = 0b1101010100u32 << 22;
|
||||||
|
const SYSREG_OP0_SHIFT: u32 = 19;
|
||||||
|
const SYSREG_OP0_MASK: u32 = 0b11u32 << 19;
|
||||||
|
const SYSREG_OP1_SHIFT: u32 = 16;
|
||||||
|
const SYSREG_OP1_MASK: u32 = 0b111u32 << 16;
|
||||||
|
const SYSREG_CRN_SHIFT: u32 = 12;
|
||||||
|
const SYSREG_CRN_MASK: u32 = 0b1111u32 << 12;
|
||||||
|
const SYSREG_CRM_SHIFT: u32 = 8;
|
||||||
|
const SYSREG_CRM_MASK: u32 = 0b1111u32 << 8;
|
||||||
|
const SYSREG_OP2_SHIFT: u32 = 5;
|
||||||
|
const SYSREG_OP2_MASK: u32 = 0b111u32 << 5;
|
||||||
|
|
||||||
// Get the register index of the PSTATE (Processor State) register.
|
/// Define the ID of system registers
|
||||||
let pstate = offset__of!(user_pt_regs, pstate) + kreg_off;
|
#[macro_export]
|
||||||
vcpu.set_reg(
|
macro_rules! arm64_sys_reg {
|
||||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, pstate),
|
($name: tt, $op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => {
|
||||||
PSTATE_FAULT_BITS_64,
|
pub const $name: u32 = SYSREG_HEAD
|
||||||
)
|
| ((($op0 as u32) << SYSREG_OP0_SHIFT) & SYSREG_OP0_MASK as u32)
|
||||||
.map_err(Error::SetCoreRegister)?;
|
| ((($op1 as u32) << SYSREG_OP1_SHIFT) & SYSREG_OP1_MASK as u32)
|
||||||
|
| ((($crn as u32) << SYSREG_CRN_SHIFT) & SYSREG_CRN_MASK as u32)
|
||||||
// Other vCPUs are powered off initially awaiting PSCI wakeup.
|
| ((($crm as u32) << SYSREG_CRM_SHIFT) & SYSREG_CRM_MASK as u32)
|
||||||
if cpu_id == 0 {
|
| ((($op2 as u32) << SYSREG_OP2_SHIFT) & SYSREG_OP2_MASK as u32);
|
||||||
// Setting the PC (Processor Counter) to the current program address (kernel address).
|
};
|
||||||
let pc = offset__of!(user_pt_regs, pc) + kreg_off;
|
|
||||||
vcpu.set_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, pc), boot_ip as u64)
|
|
||||||
.map_err(Error::SetCoreRegister)?;
|
|
||||||
|
|
||||||
// Last mandatory thing to set -> the address pointing to the FDT (also called DTB).
|
|
||||||
// "The device tree blob (dtb) must be placed on an 8-byte boundary and must
|
|
||||||
// not exceed 2 megabytes in size." -> https://www.kernel.org/doc/Documentation/arm64/booting.txt.
|
|
||||||
// We are choosing to place it the end of DRAM. See `get_fdt_addr`.
|
|
||||||
let regs0 = offset__of!(user_pt_regs, regs) + kreg_off;
|
|
||||||
vcpu.set_reg(
|
|
||||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, regs0),
|
|
||||||
super::layout::FDT_START.raw_value(),
|
|
||||||
)
|
|
||||||
.map_err(Error::SetCoreRegister)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
arm64_sys_reg!(MPIDR_EL1, 3, 0, 0, 0, 5);
|
||||||
|
arm64_sys_reg!(ID_AA64MMFR0_EL1, 3, 0, 0, 7, 0);
|
||||||
|
arm64_sys_reg!(TTBR1_EL1, 3, 0, 2, 0, 1);
|
||||||
|
arm64_sys_reg!(TCR_EL1, 3, 0, 2, 0, 2);
|
||||||
|
|||||||
@@ -9,15 +9,15 @@
|
|||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
#[macro_use]
|
|
||||||
extern crate serde_derive;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::x86_64::SgxEpcSection;
|
use crate::x86_64::SgxEpcSection;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use thiserror::Error;
|
||||||
use versionize::{VersionMap, Versionize, VersionizeError, VersionizeResult};
|
use versionize::{VersionMap, Versionize, VersionizeError, VersionizeResult};
|
||||||
use versionize_derive::Versionize;
|
use versionize_derive::Versionize;
|
||||||
use vm_migration::VersionMapped;
|
use vm_migration::VersionMapped;
|
||||||
@@ -26,31 +26,27 @@ type GuestMemoryMmap = vm_memory::GuestMemoryMmap<vm_memory::bitmap::AtomicBitma
|
|||||||
type GuestRegionMmap = vm_memory::GuestRegionMmap<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, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
/// X86_64 specific error triggered during system configuration.
|
#[error("Platform specific error (x86_64): {0:?}")]
|
||||||
X86_64Setup(x86_64::Error),
|
PlatformSpecific(x86_64::Error),
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
/// AArch64 specific error triggered during system configuration.
|
#[error("Platform specific error (aarch64): {0:?}")]
|
||||||
AArch64Setup(aarch64::Error),
|
PlatformSpecific(aarch64::Error),
|
||||||
/// The zero page extends past the end of guest_mem.
|
#[error("The memory map table extends past the end of guest memory")]
|
||||||
ZeroPagePastRamEnd,
|
|
||||||
/// Error writing the zero page of guest memory.
|
|
||||||
ZeroPageSetup(vm_memory::GuestMemoryError),
|
|
||||||
/// The memory map table extends past the end of guest memory.
|
|
||||||
MemmapTablePastRamEnd,
|
MemmapTablePastRamEnd,
|
||||||
/// Error writing memory map table to guest memory.
|
#[error("Error writing memory map table to guest memory")]
|
||||||
MemmapTableSetup,
|
MemmapTableSetup,
|
||||||
/// The hvm_start_info structure extends past the end of guest memory.
|
#[error("The hvm_start_info structure extends past the end of guest memory")]
|
||||||
StartInfoPastRamEnd,
|
StartInfoPastRamEnd,
|
||||||
/// Error writing hvm_start_info to guest memory.
|
#[error("Error writing hvm_start_info to guest memory")]
|
||||||
StartInfoSetup,
|
StartInfoSetup,
|
||||||
/// Failed to compute initramfs address.
|
#[error("Failed to compute initramfs address")]
|
||||||
InitramfsAddress,
|
InitramfsAddress,
|
||||||
/// Error writing module entry to guest memory.
|
#[error("Error writing module entry to guest memory: {0}")]
|
||||||
ModlistSetup(vm_memory::GuestMemoryError),
|
ModlistSetup(#[source] vm_memory::GuestMemoryError),
|
||||||
/// RSDP Beyond Guest Memory
|
#[error("RSDP extends past the end of guest memory")]
|
||||||
RsdpPastRamEnd,
|
RsdpPastRamEnd,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +54,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(Clone, Copy, PartialEq, Debug, Serialize, Deserialize, Versionize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize, Versionize)]
|
||||||
pub enum RegionType {
|
pub enum RegionType {
|
||||||
/// RAM type
|
/// RAM type
|
||||||
Ram,
|
Ram,
|
||||||
|
|||||||
@@ -5,10 +5,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE-BSD-3-Clause file.
|
// found in the LICENSE-BSD-3-Clause file.
|
||||||
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
|
||||||
use hypervisor::x86_64::LapicState;
|
|
||||||
use std::io::Cursor;
|
|
||||||
use std::mem;
|
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -20,32 +16,6 @@ pub const APIC_LVT1: usize = 0x360;
|
|||||||
pub const APIC_MODE_NMI: u32 = 0x4;
|
pub const APIC_MODE_NMI: u32 = 0x4;
|
||||||
pub const APIC_MODE_EXTINT: u32 = 0x7;
|
pub const APIC_MODE_EXTINT: u32 = 0x7;
|
||||||
|
|
||||||
pub fn get_klapic_reg(klapic: &LapicState, reg_offset: usize) -> u32 {
|
|
||||||
let sliceu8 = unsafe {
|
|
||||||
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
|
|
||||||
// Cursors are only readable on arrays of u8, not i8(c_char).
|
|
||||||
mem::transmute::<&[i8], &[u8]>(&klapic.regs[reg_offset..])
|
|
||||||
};
|
|
||||||
let mut reader = Cursor::new(sliceu8);
|
|
||||||
// Following call can't fail if the offsets defined above are correct.
|
|
||||||
reader
|
|
||||||
.read_u32::<LittleEndian>()
|
|
||||||
.expect("Failed to read klapic register")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_klapic_reg(klapic: &mut LapicState, reg_offset: usize, value: u32) {
|
|
||||||
let sliceu8 = unsafe {
|
|
||||||
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
|
|
||||||
// Cursors are only readable on arrays of u8, not i8(c_char).
|
|
||||||
mem::transmute::<&mut [i8], &mut [u8]>(&mut klapic.regs[reg_offset..])
|
|
||||||
};
|
|
||||||
let mut writer = Cursor::new(sliceu8);
|
|
||||||
// Following call can't fail if the offsets defined above are correct.
|
|
||||||
writer
|
|
||||||
.write_u32::<LittleEndian>(value)
|
|
||||||
.expect("Failed to write klapic register")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 {
|
pub fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 {
|
||||||
((reg) & !0x700) | ((mode) << 8)
|
((reg) & !0x700) | ((mode) << 8)
|
||||||
}
|
}
|
||||||
@@ -57,42 +27,13 @@ pub fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 {
|
|||||||
pub fn set_lint(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
|
pub fn set_lint(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
|
||||||
let mut klapic = vcpu.get_lapic()?;
|
let mut klapic = vcpu.get_lapic()?;
|
||||||
|
|
||||||
let lvt_lint0 = get_klapic_reg(&klapic, APIC_LVT0);
|
let lvt_lint0 = klapic.get_klapic_reg(APIC_LVT0);
|
||||||
set_klapic_reg(
|
klapic.set_klapic_reg(
|
||||||
&mut klapic,
|
|
||||||
APIC_LVT0,
|
APIC_LVT0,
|
||||||
set_apic_delivery_mode(lvt_lint0, APIC_MODE_EXTINT),
|
set_apic_delivery_mode(lvt_lint0, APIC_MODE_EXTINT),
|
||||||
);
|
);
|
||||||
let lvt_lint1 = get_klapic_reg(&klapic, APIC_LVT1);
|
let lvt_lint1 = klapic.get_klapic_reg(APIC_LVT1);
|
||||||
set_klapic_reg(
|
klapic.set_klapic_reg(APIC_LVT1, set_apic_delivery_mode(lvt_lint1, APIC_MODE_NMI));
|
||||||
&mut klapic,
|
|
||||||
APIC_LVT1,
|
|
||||||
set_apic_delivery_mode(lvt_lint1, APIC_MODE_NMI),
|
|
||||||
);
|
|
||||||
|
|
||||||
vcpu.set_lapic(&klapic)
|
vcpu.set_lapic(&klapic)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
const KVM_APIC_REG_SIZE: usize = 0x400;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_set_and_get_klapic_reg() {
|
|
||||||
let reg_offset = 0x340;
|
|
||||||
let mut klapic = LapicState::default();
|
|
||||||
set_klapic_reg(&mut klapic, reg_offset, 3);
|
|
||||||
let value = get_klapic_reg(&klapic, reg_offset);
|
|
||||||
assert_eq!(value, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn test_set_and_get_klapic_out_of_bounds() {
|
|
||||||
let reg_offset = KVM_APIC_REG_SIZE + 10;
|
|
||||||
let mut klapic = LapicState::default();
|
|
||||||
set_klapic_reg(&mut klapic, reg_offset, 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ 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, HypervisorError, CPUID_FLAG_VALID_INDEX};
|
use hypervisor::arch::x86::{CpuIdEntry, CPUID_FLAG_VALID_INDEX};
|
||||||
|
use hypervisor::HypervisorError;
|
||||||
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,
|
||||||
@@ -197,11 +198,11 @@ pub enum Error {
|
|||||||
|
|
||||||
impl From<Error> for super::Error {
|
impl From<Error> for super::Error {
|
||||||
fn from(e: Error) -> super::Error {
|
fn from(e: Error) -> super::Error {
|
||||||
super::Error::X86_64Setup(e)
|
super::Error::PlatformSpecific(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code, clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum CpuidReg {
|
pub enum CpuidReg {
|
||||||
EAX,
|
EAX,
|
||||||
@@ -222,17 +223,15 @@ pub struct CpuidPatch {
|
|||||||
|
|
||||||
impl CpuidPatch {
|
impl CpuidPatch {
|
||||||
pub fn set_cpuid_reg(
|
pub fn set_cpuid_reg(
|
||||||
cpuid: &mut CpuId,
|
cpuid: &mut Vec<CpuIdEntry>,
|
||||||
function: u32,
|
function: u32,
|
||||||
index: Option<u32>,
|
index: Option<u32>,
|
||||||
reg: CpuidReg,
|
reg: CpuidReg,
|
||||||
value: u32,
|
value: u32,
|
||||||
) {
|
) {
|
||||||
let entries = cpuid.as_mut_slice();
|
|
||||||
|
|
||||||
let mut entry_found = false;
|
let mut entry_found = false;
|
||||||
for entry in entries.iter_mut() {
|
for entry in cpuid.iter_mut() {
|
||||||
if entry.function == function && (index == None || index.unwrap() == entry.index) {
|
if entry.function == function && (index.is_none() || index.unwrap() == entry.index) {
|
||||||
entry_found = true;
|
entry_found = true;
|
||||||
match reg {
|
match reg {
|
||||||
CpuidReg::EAX => {
|
CpuidReg::EAX => {
|
||||||
@@ -278,16 +277,12 @@ impl CpuidPatch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = cpuid.push(entry) {
|
cpuid.push(entry);
|
||||||
error!("Failed adding new CPUID entry: {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn patch_cpuid(cpuid: &mut CpuId, patches: Vec<CpuidPatch>) {
|
pub fn patch_cpuid(cpuid: &mut [CpuIdEntry], patches: Vec<CpuidPatch>) {
|
||||||
let entries = cpuid.as_mut_slice();
|
for entry in cpuid {
|
||||||
|
|
||||||
for entry in entries.iter_mut() {
|
|
||||||
for patch in patches.iter() {
|
for patch in patches.iter() {
|
||||||
if entry.function == patch.function && entry.index == patch.index {
|
if entry.function == patch.function && entry.index == patch.index {
|
||||||
if let Some(flags_bit) = patch.flags_bit {
|
if let Some(flags_bit) = patch.flags_bit {
|
||||||
@@ -311,16 +306,15 @@ impl CpuidPatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_feature_enabled(
|
pub fn is_feature_enabled(
|
||||||
cpuid: &CpuId,
|
cpuid: &[CpuIdEntry],
|
||||||
function: u32,
|
function: u32,
|
||||||
index: u32,
|
index: u32,
|
||||||
reg: CpuidReg,
|
reg: CpuidReg,
|
||||||
feature_bit: usize,
|
feature_bit: usize,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let entries = cpuid.as_slice();
|
|
||||||
let mask = 1 << feature_bit;
|
let mask = 1 << feature_bit;
|
||||||
|
|
||||||
for entry in entries.iter() {
|
for entry in cpuid {
|
||||||
if entry.function == function && entry.index == index {
|
if entry.function == function && entry.index == index {
|
||||||
let reg_val = match reg {
|
let reg_val = match reg {
|
||||||
CpuidReg::EAX => entry.eax,
|
CpuidReg::EAX => entry.eax,
|
||||||
@@ -469,12 +463,12 @@ impl CpuidFeatureEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_features_from_cpuid(
|
fn get_features_from_cpuid(
|
||||||
cpuid: &CpuId,
|
cpuid: &[CpuIdEntry],
|
||||||
feature_entry_list: &[CpuidFeatureEntry],
|
feature_entry_list: &[CpuidFeatureEntry],
|
||||||
) -> Vec<u32> {
|
) -> Vec<u32> {
|
||||||
let mut features = vec![0; feature_entry_list.len()];
|
let mut features = vec![0; feature_entry_list.len()];
|
||||||
for (i, feature_entry) in feature_entry_list.iter().enumerate() {
|
for (i, feature_entry) in feature_entry_list.iter().enumerate() {
|
||||||
for cpuid_entry in cpuid.as_slice().iter() {
|
for cpuid_entry in cpuid {
|
||||||
if cpuid_entry.function == feature_entry.function
|
if cpuid_entry.function == feature_entry.function
|
||||||
&& cpuid_entry.index == feature_entry.index
|
&& cpuid_entry.index == feature_entry.index
|
||||||
{
|
{
|
||||||
@@ -504,8 +498,8 @@ impl CpuidFeatureEntry {
|
|||||||
// The function returns `Error` (a.k.a. "incompatible"), when the CPUID features from `src_vm_cpuid`
|
// 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`.
|
// is not a subset of those of the `dest_vm_cpuid`.
|
||||||
pub fn check_cpuid_compatibility(
|
pub fn check_cpuid_compatibility(
|
||||||
src_vm_cpuid: &CpuId,
|
src_vm_cpuid: &[CpuIdEntry],
|
||||||
dest_vm_cpuid: &CpuId,
|
dest_vm_cpuid: &[CpuIdEntry],
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let feature_entry_list = &Self::checked_feature_entry_list();
|
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 src_vm_features = Self::get_features_from_cpuid(src_vm_cpuid, feature_entry_list);
|
||||||
@@ -557,7 +551,7 @@ pub fn generate_common_cpuid(
|
|||||||
phys_bits: u8,
|
phys_bits: u8,
|
||||||
kvm_hyperv: bool,
|
kvm_hyperv: bool,
|
||||||
#[cfg(feature = "tdx")] tdx_enabled: bool,
|
#[cfg(feature = "tdx")] tdx_enabled: bool,
|
||||||
) -> super::Result<CpuId> {
|
) -> super::Result<Vec<CpuIdEntry>> {
|
||||||
let cpuid_patches = vec![
|
let cpuid_patches = vec![
|
||||||
// Patch tsc deadline timer bit
|
// Patch tsc deadline timer bit
|
||||||
CpuidPatch {
|
CpuidPatch {
|
||||||
@@ -668,16 +662,14 @@ pub fn generate_common_cpuid(
|
|||||||
for i in 0x8000_0002..=0x8000_0004 {
|
for i in 0x8000_0002..=0x8000_0004 {
|
||||||
cpuid.retain(|c| c.function != i);
|
cpuid.retain(|c| c.function != i);
|
||||||
let leaf = unsafe { std::arch::x86_64::__cpuid(i) };
|
let leaf = unsafe { std::arch::x86_64::__cpuid(i) };
|
||||||
cpuid
|
cpuid.push(CpuIdEntry {
|
||||||
.push(CpuIdEntry {
|
function: i,
|
||||||
function: i,
|
eax: leaf.eax,
|
||||||
eax: leaf.eax,
|
ebx: leaf.ebx,
|
||||||
ebx: leaf.ebx,
|
ecx: leaf.ecx,
|
||||||
ecx: leaf.ecx,
|
edx: leaf.edx,
|
||||||
edx: leaf.edx,
|
..Default::default()
|
||||||
..Default::default()
|
});
|
||||||
})
|
|
||||||
.map_err(Error::CpuidIdentification)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if kvm_hyperv {
|
if kvm_hyperv {
|
||||||
@@ -686,56 +678,44 @@ pub fn generate_common_cpuid(
|
|||||||
cpuid.retain(|c| c.function != 0x4000_0001);
|
cpuid.retain(|c| c.function != 0x4000_0001);
|
||||||
// See "Hypervisor Top Level Functional Specification" for details
|
// See "Hypervisor Top Level Functional Specification" for details
|
||||||
// Compliance with "Hv#1" requires leaves up to 0x4000_000a
|
// Compliance with "Hv#1" requires leaves up to 0x4000_000a
|
||||||
cpuid
|
cpuid.push(CpuIdEntry {
|
||||||
.push(CpuIdEntry {
|
function: 0x40000000,
|
||||||
function: 0x40000000,
|
eax: 0x4000000a, // Maximum cpuid leaf
|
||||||
eax: 0x4000000a, // Maximum cpuid leaf
|
ebx: 0x756e694c, // "Linu"
|
||||||
ebx: 0x756e694c, // "Linu"
|
ecx: 0x564b2078, // "x KV"
|
||||||
ecx: 0x564b2078, // "x KV"
|
edx: 0x7648204d, // "M Hv"
|
||||||
edx: 0x7648204d, // "M Hv"
|
..Default::default()
|
||||||
..Default::default()
|
});
|
||||||
})
|
cpuid.push(CpuIdEntry {
|
||||||
.map_err(Error::CpuidKvmHyperV)?;
|
function: 0x40000001,
|
||||||
cpuid
|
eax: 0x31237648, // "Hv#1"
|
||||||
.push(CpuIdEntry {
|
..Default::default()
|
||||||
function: 0x40000001,
|
});
|
||||||
eax: 0x31237648, // "Hv#1"
|
cpuid.push(CpuIdEntry {
|
||||||
..Default::default()
|
function: 0x40000002,
|
||||||
})
|
eax: 0x3839, // "Build number"
|
||||||
.map_err(Error::CpuidKvmHyperV)?;
|
ebx: 0xa0000, // "Version"
|
||||||
cpuid
|
..Default::default()
|
||||||
.push(CpuIdEntry {
|
});
|
||||||
function: 0x40000002,
|
cpuid.push(CpuIdEntry {
|
||||||
eax: 0x3839, // "Build number"
|
function: 0x4000_0003,
|
||||||
ebx: 0xa0000, // "Version"
|
eax: 1 << 1 // AccessPartitionReferenceCounter
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.map_err(Error::CpuidKvmHyperV)?;
|
|
||||||
cpuid
|
|
||||||
.push(CpuIdEntry {
|
|
||||||
function: 0x4000_0003,
|
|
||||||
eax: 1 << 1 // AccessPartitionReferenceCounter
|
|
||||||
| 1 << 2 // AccessSynicRegs
|
| 1 << 2 // AccessSynicRegs
|
||||||
| 1 << 3 // AccessSyntheticTimerRegs
|
| 1 << 3 // AccessSyntheticTimerRegs
|
||||||
| 1 << 9, // AccessPartitionReferenceTsc
|
| 1 << 9, // AccessPartitionReferenceTsc
|
||||||
edx: 1 << 3, // CPU dynamic partitioning
|
edx: 1 << 3, // CPU dynamic partitioning
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
});
|
||||||
.map_err(Error::CpuidKvmHyperV)?;
|
cpuid.push(CpuIdEntry {
|
||||||
cpuid
|
function: 0x4000_0004,
|
||||||
.push(CpuIdEntry {
|
eax: 1 << 5, // Recommend relaxed timing
|
||||||
function: 0x4000_0004,
|
..Default::default()
|
||||||
eax: 1 << 5, // Recommend relaxed timing
|
});
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.map_err(Error::CpuidKvmHyperV)?;
|
|
||||||
for i in 0x4000_0005..=0x4000_000a {
|
for i in 0x4000_0005..=0x4000_000a {
|
||||||
cpuid
|
cpuid.push(CpuIdEntry {
|
||||||
.push(CpuIdEntry {
|
function: i,
|
||||||
function: i,
|
..Default::default()
|
||||||
..Default::default()
|
});
|
||||||
})
|
|
||||||
.map_err(Error::CpuidKvmHyperV)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -743,11 +723,11 @@ pub fn generate_common_cpuid(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn configure_vcpu(
|
pub fn configure_vcpu(
|
||||||
fd: &Arc<dyn hypervisor::Vcpu>,
|
vcpu: &Arc<dyn hypervisor::Vcpu>,
|
||||||
id: u8,
|
id: u8,
|
||||||
kernel_entry_point: Option<EntryPoint>,
|
kernel_entry_point: Option<EntryPoint>,
|
||||||
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
|
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
|
||||||
cpuid: CpuId,
|
cpuid: Vec<CpuIdEntry>,
|
||||||
kvm_hyperv: bool,
|
kvm_hyperv: bool,
|
||||||
) -> super::Result<()> {
|
) -> super::Result<()> {
|
||||||
// Per vCPU CPUID changes; common are handled via generate_common_cpuid()
|
// Per vCPU CPUID changes; common are handled via generate_common_cpuid()
|
||||||
@@ -755,23 +735,23 @@ pub fn configure_vcpu(
|
|||||||
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));
|
||||||
|
|
||||||
fd.set_cpuid2(&cpuid)
|
vcpu.set_cpuid2(&cpuid)
|
||||||
.map_err(|e| Error::SetSupportedCpusFailed(e.into()))?;
|
.map_err(|e| Error::SetSupportedCpusFailed(e.into()))?;
|
||||||
|
|
||||||
if kvm_hyperv {
|
if kvm_hyperv {
|
||||||
fd.enable_hyperv_synic().unwrap();
|
vcpu.enable_hyperv_synic().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
regs::setup_msrs(fd).map_err(Error::MsrsConfiguration)?;
|
regs::setup_msrs(vcpu).map_err(Error::MsrsConfiguration)?;
|
||||||
if let Some(kernel_entry_point) = kernel_entry_point {
|
if let Some(kernel_entry_point) = kernel_entry_point {
|
||||||
if let Some(entry_addr) = kernel_entry_point.entry_addr {
|
if let Some(entry_addr) = kernel_entry_point.entry_addr {
|
||||||
// Safe to unwrap because this method is called after the VM is configured
|
// Safe to unwrap because this method is called after the VM is configured
|
||||||
regs::setup_regs(fd, entry_addr.raw_value()).map_err(Error::RegsConfiguration)?;
|
regs::setup_regs(vcpu, entry_addr.raw_value()).map_err(Error::RegsConfiguration)?;
|
||||||
regs::setup_fpu(fd).map_err(Error::FpuConfiguration)?;
|
regs::setup_fpu(vcpu).map_err(Error::FpuConfiguration)?;
|
||||||
regs::setup_sregs(&vm_memory.memory(), fd).map_err(Error::SregsConfiguration)?;
|
regs::setup_sregs(&vm_memory.memory(), vcpu).map_err(Error::SregsConfiguration)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
interrupts::set_lint(fd).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
|
interrupts::set_lint(vcpu).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -838,13 +818,17 @@ pub fn configure_system(
|
|||||||
_num_cpus: u8,
|
_num_cpus: u8,
|
||||||
rsdp_addr: Option<GuestAddress>,
|
rsdp_addr: Option<GuestAddress>,
|
||||||
sgx_epc_region: Option<SgxEpcRegion>,
|
sgx_epc_region: Option<SgxEpcRegion>,
|
||||||
|
serial_number: Option<&str>,
|
||||||
|
uuid: Option<&str>,
|
||||||
|
oem_strings: Option<&[&str]>,
|
||||||
) -> super::Result<()> {
|
) -> super::Result<()> {
|
||||||
// Write EBDA address to location where ACPICA expects to find it
|
// Write EBDA address to location where ACPICA expects to find it
|
||||||
guest_mem
|
guest_mem
|
||||||
.write_obj((layout::EBDA_START.0 >> 4) as u16, layout::EBDA_POINTER)
|
.write_obj((layout::EBDA_START.0 >> 4) as u16, layout::EBDA_POINTER)
|
||||||
.map_err(Error::EbdaSetup)?;
|
.map_err(Error::EbdaSetup)?;
|
||||||
|
|
||||||
let size = smbios::setup_smbios(guest_mem).map_err(Error::SmbiosSetup)?;
|
let size = smbios::setup_smbios(guest_mem, serial_number, uuid, oem_strings)
|
||||||
|
.map_err(Error::SmbiosSetup)?;
|
||||||
|
|
||||||
// Place the MP table after the SMIOS table aligned to 16 bytes
|
// Place the MP table after the SMIOS table aligned to 16 bytes
|
||||||
let offset = GuestAddress(layout::SMBIOS_START).unchecked_add(size);
|
let offset = GuestAddress(layout::SMBIOS_START).unchecked_add(size);
|
||||||
@@ -1054,7 +1038,7 @@ pub fn get_host_cpu_phys_bits() -> u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update_cpuid_topology(
|
fn update_cpuid_topology(
|
||||||
cpuid: &mut CpuId,
|
cpuid: &mut Vec<CpuIdEntry>,
|
||||||
threads_per_core: u8,
|
threads_per_core: u8,
|
||||||
cores_per_die: u8,
|
cores_per_die: u8,
|
||||||
dies_per_package: u8,
|
dies_per_package: u8,
|
||||||
@@ -1118,7 +1102,10 @@ 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.
|
||||||
fn update_cpuid_sgx(cpuid: &mut CpuId, epc_sections: Vec<SgxEpcSection>) -> Result<(), Error> {
|
fn update_cpuid_sgx(
|
||||||
|
cpuid: &mut Vec<CpuIdEntry>,
|
||||||
|
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);
|
||||||
@@ -1193,6 +1180,9 @@ mod tests {
|
|||||||
1,
|
1,
|
||||||
Some(layout::RSDP_POINTER),
|
Some(layout::RSDP_POINTER),
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
assert!(config_err.is_err());
|
assert!(config_err.is_err());
|
||||||
|
|
||||||
@@ -1206,7 +1196,18 @@ mod tests {
|
|||||||
.collect();
|
.collect();
|
||||||
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
||||||
|
|
||||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None).unwrap();
|
configure_system(
|
||||||
|
&gm,
|
||||||
|
GuestAddress(0),
|
||||||
|
&None,
|
||||||
|
no_vcpus,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Now assigning some memory that is equal to the start of the 32bit memory hole.
|
// Now assigning some memory that is equal to the start of the 32bit memory hole.
|
||||||
let mem_size = 3328 << 20;
|
let mem_size = 3328 << 20;
|
||||||
@@ -1217,9 +1218,31 @@ mod tests {
|
|||||||
.map(|r| (r.0, r.1))
|
.map(|r| (r.0, r.1))
|
||||||
.collect();
|
.collect();
|
||||||
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
||||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None).unwrap();
|
configure_system(
|
||||||
|
&gm,
|
||||||
|
GuestAddress(0),
|
||||||
|
&None,
|
||||||
|
no_vcpus,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None).unwrap();
|
configure_system(
|
||||||
|
&gm,
|
||||||
|
GuestAddress(0),
|
||||||
|
&None,
|
||||||
|
no_vcpus,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Now assigning some memory that falls after the 32bit memory hole.
|
// Now assigning some memory that falls after the 32bit memory hole.
|
||||||
let mem_size = 3330 << 20;
|
let mem_size = 3330 << 20;
|
||||||
@@ -1230,9 +1253,31 @@ mod tests {
|
|||||||
.map(|r| (r.0, r.1))
|
.map(|r| (r.0, r.1))
|
||||||
.collect();
|
.collect();
|
||||||
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
||||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None).unwrap();
|
configure_system(
|
||||||
|
&gm,
|
||||||
|
GuestAddress(0),
|
||||||
|
&None,
|
||||||
|
no_vcpus,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None).unwrap();
|
configure_system(
|
||||||
|
&gm,
|
||||||
|
GuestAddress(0),
|
||||||
|
&None,
|
||||||
|
no_vcpus,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
use crate::layout::{BOOT_GDT_START, BOOT_IDT_START, PVH_INFO_START};
|
use crate::layout::{BOOT_GDT_START, BOOT_IDT_START, PVH_INFO_START};
|
||||||
use crate::GuestMemoryMmap;
|
use crate::GuestMemoryMmap;
|
||||||
use hypervisor::arch::x86::gdt::{gdt_entry, segment_from_gdt};
|
use hypervisor::arch::x86::gdt::{gdt_entry, segment_from_gdt};
|
||||||
use hypervisor::arch::x86::regs::*;
|
use hypervisor::arch::x86::regs::CR0_PE;
|
||||||
use hypervisor::x86_64::{FpuState, SpecialRegisters, StandardRegisters};
|
use hypervisor::arch::x86::{FpuState, SpecialRegisters, StandardRegisters};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{mem, result};
|
use std::{mem, result};
|
||||||
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError};
|
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError};
|
||||||
@@ -66,7 +66,7 @@ pub fn setup_fpu(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
|
|||||||
///
|
///
|
||||||
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
||||||
pub fn setup_msrs(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
|
pub fn setup_msrs(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
|
||||||
vcpu.set_msrs(&hypervisor::x86_64::boot_msr_entries())
|
vcpu.set_msrs(&vcpu.boot_msr_entries())
|
||||||
.map_err(Error::SetModelSpecificRegisters)?;
|
.map_err(Error::SetModelSpecificRegisters)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ use std::fmt::{self, Display};
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
use uuid::Uuid;
|
||||||
use vm_memory::ByteValued;
|
use vm_memory::ByteValued;
|
||||||
use vm_memory::{Address, Bytes, GuestAddress};
|
use vm_memory::{Address, Bytes, GuestAddress};
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// There was too little guest memory to store the entire SMBIOS table.
|
/// There was too little guest memory to store the entire SMBIOS table.
|
||||||
@@ -28,6 +28,8 @@ pub enum Error {
|
|||||||
WriteSmbiosEp,
|
WriteSmbiosEp,
|
||||||
/// Failure to write additional data to memory
|
/// Failure to write additional data to memory
|
||||||
WriteData,
|
WriteData,
|
||||||
|
/// Failure to parse uuid, uuid format may be error
|
||||||
|
ParseUuid(uuid::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for Error {}
|
impl std::error::Error for Error {}
|
||||||
@@ -37,11 +39,16 @@ impl Display for Error {
|
|||||||
use self::Error::*;
|
use self::Error::*;
|
||||||
|
|
||||||
let description = match self {
|
let description = match self {
|
||||||
NotEnoughMemory => "There was too little guest memory to store the SMBIOS table",
|
NotEnoughMemory => {
|
||||||
AddressOverflow => "The SMBIOS table has too little address space to be stored",
|
"There was too little guest memory to store the SMBIOS table".to_string()
|
||||||
Clear => "Failure while zeroing out the memory for the SMBIOS table",
|
}
|
||||||
WriteSmbiosEp => "Failure to write SMBIOS entrypoint structure",
|
AddressOverflow => {
|
||||||
WriteData => "Failure to write additional data to memory",
|
"The SMBIOS table has too little address space to be stored".to_string()
|
||||||
|
}
|
||||||
|
Clear => "Failure while zeroing out the memory for the SMBIOS table".to_string(),
|
||||||
|
WriteSmbiosEp => "Failure to write SMBIOS entrypoint structure".to_string(),
|
||||||
|
WriteData => "Failure to write additional data to memory".to_string(),
|
||||||
|
ParseUuid(e) => format!("Failure to parse uuid: {}", e),
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(f, "SMBIOS error: {}", description)
|
write!(f, "SMBIOS error: {}", description)
|
||||||
@@ -54,6 +61,7 @@ pub type Result<T> = result::Result<T, Error>;
|
|||||||
const SM3_MAGIC_IDENT: &[u8; 5usize] = b"_SM3_";
|
const SM3_MAGIC_IDENT: &[u8; 5usize] = b"_SM3_";
|
||||||
const BIOS_INFORMATION: u8 = 0;
|
const BIOS_INFORMATION: u8 = 0;
|
||||||
const SYSTEM_INFORMATION: u8 = 1;
|
const SYSTEM_INFORMATION: u8 = 1;
|
||||||
|
const OEM_STRINGS: u8 = 11;
|
||||||
const END_OF_TABLE: u8 = 127;
|
const END_OF_TABLE: u8 = 127;
|
||||||
const PCI_SUPPORTED: u64 = 1 << 7;
|
const PCI_SUPPORTED: u64 = 1 << 7;
|
||||||
const IS_VIRTUAL_MACHINE: u8 = 1 << 4;
|
const IS_VIRTUAL_MACHINE: u8 = 1 << 4;
|
||||||
@@ -68,75 +76,81 @@ fn compute_checksum<T: Copy>(v: &T) -> u8 {
|
|||||||
(!checksum).wrapping_add(1)
|
(!checksum).wrapping_add(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
#[derive(Default, Copy)]
|
#[derive(Default, Copy, Clone)]
|
||||||
pub struct Smbios30Entrypoint {
|
struct Smbios30Entrypoint {
|
||||||
pub signature: [u8; 5usize],
|
signature: [u8; 5usize],
|
||||||
pub checksum: u8,
|
checksum: u8,
|
||||||
pub length: u8,
|
length: u8,
|
||||||
pub majorver: u8,
|
majorver: u8,
|
||||||
pub minorver: u8,
|
minorver: u8,
|
||||||
pub docrev: u8,
|
docrev: u8,
|
||||||
pub revision: u8,
|
revision: u8,
|
||||||
pub reserved: u8,
|
reserved: u8,
|
||||||
pub max_size: u32,
|
max_size: u32,
|
||||||
pub physptr: u64,
|
physptr: u64,
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for Smbios30Entrypoint {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
#[derive(Default, Copy)]
|
#[derive(Default, Copy, Clone)]
|
||||||
pub struct SmbiosBiosInfo {
|
struct SmbiosBiosInfo {
|
||||||
pub typ: u8,
|
r#type: u8,
|
||||||
pub length: u8,
|
length: u8,
|
||||||
pub handle: u16,
|
handle: u16,
|
||||||
pub vendor: u8,
|
vendor: u8,
|
||||||
pub version: u8,
|
version: u8,
|
||||||
pub start_addr: u16,
|
start_addr: u16,
|
||||||
pub release_date: u8,
|
release_date: u8,
|
||||||
pub rom_size: u8,
|
rom_size: u8,
|
||||||
pub characteristics: u64,
|
characteristics: u64,
|
||||||
pub characteristics_ext1: u8,
|
characteristics_ext1: u8,
|
||||||
pub characteristics_ext2: u8,
|
characteristics_ext2: u8,
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for SmbiosBiosInfo {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
#[derive(Default, Copy)]
|
#[derive(Default, Copy, Clone)]
|
||||||
pub struct SmbiosSysInfo {
|
struct SmbiosSysInfo {
|
||||||
pub typ: u8,
|
r#type: u8,
|
||||||
pub length: u8,
|
length: u8,
|
||||||
pub handle: u16,
|
handle: u16,
|
||||||
pub manufacturer: u8,
|
manufacturer: u8,
|
||||||
pub product_name: u8,
|
product_name: u8,
|
||||||
pub version: u8,
|
version: u8,
|
||||||
pub serial_number: u8,
|
serial_number: u8,
|
||||||
pub uuid: [u8; 16usize],
|
uuid: [u8; 16usize],
|
||||||
pub wake_up_type: u8,
|
wake_up_type: u8,
|
||||||
pub sku: u8,
|
sku: u8,
|
||||||
pub family: u8,
|
family: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for SmbiosSysInfo {
|
#[repr(C)]
|
||||||
fn clone(&self) -> Self {
|
#[repr(packed)]
|
||||||
*self
|
#[derive(Default, Copy, Clone)]
|
||||||
}
|
struct SmbiosOemStrings {
|
||||||
|
r#type: u8,
|
||||||
|
length: u8,
|
||||||
|
handle: u16,
|
||||||
|
count: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[repr(packed)]
|
||||||
|
#[derive(Default, Copy, Clone)]
|
||||||
|
struct SmbiosEndOfTable {
|
||||||
|
r#type: u8,
|
||||||
|
length: u8,
|
||||||
|
handle: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: These data structures only contain a series of integers
|
// SAFETY: These data structures only contain a series of integers
|
||||||
unsafe impl ByteValued for Smbios30Entrypoint {}
|
unsafe impl ByteValued for Smbios30Entrypoint {}
|
||||||
unsafe impl ByteValued for SmbiosBiosInfo {}
|
unsafe impl ByteValued for SmbiosBiosInfo {}
|
||||||
unsafe impl ByteValued for SmbiosSysInfo {}
|
unsafe impl ByteValued for SmbiosSysInfo {}
|
||||||
|
unsafe impl ByteValued for SmbiosOemStrings {}
|
||||||
|
unsafe impl ByteValued for SmbiosEndOfTable {}
|
||||||
|
|
||||||
fn write_and_incr<T: ByteValued>(
|
fn write_and_incr<T: ByteValued>(
|
||||||
mem: &GuestMemoryMmap,
|
mem: &GuestMemoryMmap,
|
||||||
@@ -162,7 +176,12 @@ fn write_string(
|
|||||||
Ok(curptr)
|
Ok(curptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_smbios(mem: &GuestMemoryMmap) -> Result<u64> {
|
pub fn setup_smbios(
|
||||||
|
mem: &GuestMemoryMmap,
|
||||||
|
serial_number: Option<&str>,
|
||||||
|
uuid: Option<&str>,
|
||||||
|
oem_strings: Option<&[&str]>,
|
||||||
|
) -> Result<u64> {
|
||||||
let physptr = GuestAddress(SMBIOS_START)
|
let physptr = GuestAddress(SMBIOS_START)
|
||||||
.checked_add(mem::size_of::<Smbios30Entrypoint>() as u64)
|
.checked_add(mem::size_of::<Smbios30Entrypoint>() as u64)
|
||||||
.ok_or(Error::NotEnoughMemory)?;
|
.ok_or(Error::NotEnoughMemory)?;
|
||||||
@@ -172,7 +191,7 @@ pub fn setup_smbios(mem: &GuestMemoryMmap) -> Result<u64> {
|
|||||||
{
|
{
|
||||||
handle += 1;
|
handle += 1;
|
||||||
let smbios_biosinfo = SmbiosBiosInfo {
|
let smbios_biosinfo = SmbiosBiosInfo {
|
||||||
typ: BIOS_INFORMATION,
|
r#type: BIOS_INFORMATION,
|
||||||
length: mem::size_of::<SmbiosBiosInfo>() as u8,
|
length: mem::size_of::<SmbiosBiosInfo>() as u8,
|
||||||
handle,
|
handle,
|
||||||
vendor: 1, // First string written in this section
|
vendor: 1, // First string written in this section
|
||||||
@@ -189,29 +208,59 @@ pub fn setup_smbios(mem: &GuestMemoryMmap) -> Result<u64> {
|
|||||||
|
|
||||||
{
|
{
|
||||||
handle += 1;
|
handle += 1;
|
||||||
|
|
||||||
|
let uuid_number = uuid
|
||||||
|
.map(Uuid::parse_str)
|
||||||
|
.transpose()
|
||||||
|
.map_err(Error::ParseUuid)?
|
||||||
|
.unwrap_or(Uuid::nil());
|
||||||
let smbios_sysinfo = SmbiosSysInfo {
|
let smbios_sysinfo = SmbiosSysInfo {
|
||||||
typ: SYSTEM_INFORMATION,
|
r#type: SYSTEM_INFORMATION,
|
||||||
length: mem::size_of::<SmbiosSysInfo>() as u8,
|
length: mem::size_of::<SmbiosSysInfo>() as u8,
|
||||||
handle,
|
handle,
|
||||||
manufacturer: 1, // First string written in this section
|
manufacturer: 1, // First string written in this section
|
||||||
product_name: 2, // Second string written in this section
|
product_name: 2, // Second string written in this section
|
||||||
|
serial_number: serial_number.map(|_| 3).unwrap_or_default(), // 3rd string
|
||||||
|
uuid: uuid_number.to_bytes_le(), // set uuid
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
|
curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
|
||||||
curptr = write_string(mem, "Cloud Hypervisor", curptr)?;
|
curptr = write_string(mem, "Cloud Hypervisor", curptr)?;
|
||||||
curptr = write_string(mem, "cloud-hypervisor", curptr)?;
|
curptr = write_string(mem, "cloud-hypervisor", curptr)?;
|
||||||
|
if let Some(serial_number) = serial_number {
|
||||||
|
curptr = write_string(mem, serial_number, curptr)?;
|
||||||
|
}
|
||||||
|
curptr = write_and_incr(mem, 0u8, curptr)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(oem_strings) = oem_strings {
|
||||||
|
handle += 1;
|
||||||
|
|
||||||
|
let smbios_oemstrings = SmbiosOemStrings {
|
||||||
|
r#type: OEM_STRINGS,
|
||||||
|
length: mem::size_of::<SmbiosOemStrings>() as u8,
|
||||||
|
handle,
|
||||||
|
count: oem_strings.len() as u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
curptr = write_and_incr(mem, smbios_oemstrings, curptr)?;
|
||||||
|
|
||||||
|
for s in oem_strings {
|
||||||
|
curptr = write_string(mem, s, curptr)?;
|
||||||
|
}
|
||||||
|
|
||||||
curptr = write_and_incr(mem, 0u8, curptr)?;
|
curptr = write_and_incr(mem, 0u8, curptr)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
handle += 1;
|
handle += 1;
|
||||||
let smbios_sysinfo = SmbiosSysInfo {
|
let smbios_end = SmbiosEndOfTable {
|
||||||
typ: END_OF_TABLE,
|
r#type: END_OF_TABLE,
|
||||||
length: mem::size_of::<SmbiosSysInfo>() as u8,
|
length: mem::size_of::<SmbiosEndOfTable>() as u8,
|
||||||
handle,
|
handle,
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
|
curptr = write_and_incr(mem, smbios_end, curptr)?;
|
||||||
|
curptr = write_and_incr(mem, 0u8, curptr)?;
|
||||||
curptr = write_and_incr(mem, 0u8, curptr)?;
|
curptr = write_and_incr(mem, 0u8, curptr)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +282,7 @@ pub fn setup_smbios(mem: &GuestMemoryMmap) -> Result<u64> {
|
|||||||
.map_err(|_| Error::WriteSmbiosEp)?;
|
.map_err(|_| Error::WriteSmbiosEp)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(curptr.unchecked_offset_from(physptr))
|
Ok(curptr.unchecked_offset_from(physptr) + std::mem::size_of::<Smbios30Entrypoint>() as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -263,7 +312,7 @@ mod tests {
|
|||||||
fn entrypoint_checksum() {
|
fn entrypoint_checksum() {
|
||||||
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap();
|
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap();
|
||||||
|
|
||||||
setup_smbios(&mem).unwrap();
|
setup_smbios(&mem, None, None, None).unwrap();
|
||||||
|
|
||||||
let smbios_ep: Smbios30Entrypoint = mem.read_obj(GuestAddress(SMBIOS_START)).unwrap();
|
let smbios_ep: Smbios30Entrypoint = mem.read_obj(GuestAddress(SMBIOS_START)).unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -8,17 +8,17 @@ edition = "2021"
|
|||||||
default = []
|
default = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
io-uring = "0.5.2"
|
io-uring = "0.5.6"
|
||||||
libc = "0.2.123"
|
libc = "0.2.133"
|
||||||
log = "0.4.16"
|
log = "0.4.17"
|
||||||
qcow = { path = "../qcow" }
|
qcow = { path = "../qcow" }
|
||||||
thiserror = "1.0.30"
|
thiserror = "1.0.37"
|
||||||
versionize = "0.1.6"
|
versionize = "0.1.6"
|
||||||
versionize_derive = "0.1.4"
|
versionize_derive = "0.1.4"
|
||||||
vhdx = { path = "../vhdx" }
|
vhdx = { path = "../vhdx" }
|
||||||
virtio-bindings = { version = "0.1.0", features = ["virtio-v5_0_0"] }
|
virtio-bindings = { version = "0.1.0", features = ["virtio-v5_0_0"] }
|
||||||
virtio-queue = "0.2.0"
|
virtio-queue = "0.6.1"
|
||||||
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
vm-memory = { version = "0.9.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
vmm-sys-util = "0.9.0"
|
vmm-sys-util = "0.10.0"
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use std::fs::File;
|
|||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
use vmm_sys_util::{ioctl_expr, ioctl_io_nr, ioctl_ioc_nr};
|
use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum DiskFileError {
|
pub enum DiskFileError {
|
||||||
@@ -104,7 +104,7 @@ impl DiskTopology {
|
|||||||
|
|
||||||
pub type DiskFileResult<T> = std::result::Result<T, DiskFileError>;
|
pub type DiskFileResult<T> = std::result::Result<T, DiskFileError>;
|
||||||
|
|
||||||
pub trait DiskFile: Send + Sync {
|
pub trait DiskFile: Send {
|
||||||
fn size(&mut self) -> DiskFileResult<u64>;
|
fn size(&mut self) -> DiskFileResult<u64>;
|
||||||
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>>;
|
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>>;
|
||||||
fn topology(&mut self) -> DiskTopology {
|
fn topology(&mut self) -> DiskTopology {
|
||||||
@@ -127,7 +127,7 @@ pub enum AsyncIoError {
|
|||||||
|
|
||||||
pub type AsyncIoResult<T> = std::result::Result<T, AsyncIoError>;
|
pub type AsyncIoResult<T> = std::result::Result<T, AsyncIoError>;
|
||||||
|
|
||||||
pub trait AsyncIo: Send + Sync {
|
pub trait AsyncIo: Send {
|
||||||
fn notifier(&self) -> &EventFd;
|
fn notifier(&self) -> &EventFd;
|
||||||
fn read_vectored(
|
fn read_vectored(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ use std::path::Path;
|
|||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::MutexGuard;
|
use std::sync::MutexGuard;
|
||||||
|
use thiserror::Error;
|
||||||
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::*;
|
||||||
@@ -48,25 +49,25 @@ type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
|||||||
const SECTOR_SHIFT: u8 = 9;
|
const SECTOR_SHIFT: u8 = 9;
|
||||||
pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
|
pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Guest gave us bad memory addresses.
|
#[error("Guest gave us bad memory addresses.")]
|
||||||
GuestMemory(GuestMemoryError),
|
GuestMemory(GuestMemoryError),
|
||||||
/// Guest gave us offsets that would have overflowed a usize.
|
#[error("Guest gave us offsets that would have overflowed a usize.")]
|
||||||
CheckedOffset(GuestAddress, usize),
|
CheckedOffset(GuestAddress, usize),
|
||||||
/// Guest gave us a write only descriptor that protocol says to read from.
|
#[error("Guest gave us a write only descriptor that protocol says to read from.")]
|
||||||
UnexpectedWriteOnlyDescriptor,
|
UnexpectedWriteOnlyDescriptor,
|
||||||
/// Guest gave us a read only descriptor that protocol says to write to.
|
#[error("Guest gave us a read only descriptor that protocol says to write to.")]
|
||||||
UnexpectedReadOnlyDescriptor,
|
UnexpectedReadOnlyDescriptor,
|
||||||
/// Guest gave us too few descriptors in a descriptor chain.
|
#[error("Guest gave us too few descriptors in a descriptor chain.")]
|
||||||
DescriptorChainTooShort,
|
DescriptorChainTooShort,
|
||||||
/// Guest gave us a descriptor that was too short to use.
|
#[error("Guest gave us a descriptor that was too short to use.")]
|
||||||
DescriptorLengthTooSmall,
|
DescriptorLengthTooSmall,
|
||||||
/// Getting a block's metadata fails for any reason.
|
#[error("Getting a block's metadata fails for any reason.")]
|
||||||
GetFileMetadata,
|
GetFileMetadata,
|
||||||
/// The requested operation would cause a seek beyond disk end.
|
#[error("The requested operation would cause a seek beyond disk end.")]
|
||||||
InvalidOffset,
|
InvalidOffset,
|
||||||
/// The requested operation does not support multiple descriptors.
|
#[error("The requested operation does not support multiple descriptors.")]
|
||||||
TooManyDescriptors,
|
TooManyDescriptors,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,20 +103,31 @@ pub fn build_disk_image_id(disk_path: &Path) -> Vec<u8> {
|
|||||||
default_disk_image_id
|
default_disk_image_id
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ExecuteError {
|
pub enum ExecuteError {
|
||||||
|
#[error("Bad request: {0}")]
|
||||||
BadRequest(Error),
|
BadRequest(Error),
|
||||||
|
#[error("Falied to flush: {0}")]
|
||||||
Flush(io::Error),
|
Flush(io::Error),
|
||||||
|
#[error("Failed to read: {0}")]
|
||||||
Read(GuestMemoryError),
|
Read(GuestMemoryError),
|
||||||
|
#[error("Failed to seek: {0}")]
|
||||||
Seek(io::Error),
|
Seek(io::Error),
|
||||||
|
#[error("Failed to write: {0}")]
|
||||||
Write(GuestMemoryError),
|
Write(GuestMemoryError),
|
||||||
|
#[error("Unsupported request: {0}")]
|
||||||
Unsupported(u32),
|
Unsupported(u32),
|
||||||
|
#[error("Failed to submit io uring: {0}")]
|
||||||
SubmitIoUring(io::Error),
|
SubmitIoUring(io::Error),
|
||||||
|
#[error("Failed to get guest address: {0}")]
|
||||||
GetHostAddress(GuestMemoryError),
|
GetHostAddress(GuestMemoryError),
|
||||||
|
#[error("Failed to async read: {0}")]
|
||||||
AsyncRead(AsyncIoError),
|
AsyncRead(AsyncIoError),
|
||||||
|
#[error("Failed to async write: {0}")]
|
||||||
AsyncWrite(AsyncIoError),
|
AsyncWrite(AsyncIoError),
|
||||||
|
#[error("failed to async flush: {0}")]
|
||||||
AsyncFlush(AsyncIoError),
|
AsyncFlush(AsyncIoError),
|
||||||
/// Failed allocating a temporary buffer.
|
#[error("Failed allocating a temporary buffer: {0}")]
|
||||||
TemporaryBufferAllocation(io::Error),
|
TemporaryBufferAllocation(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +150,7 @@ impl ExecuteError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum RequestType {
|
pub enum RequestType {
|
||||||
In,
|
In,
|
||||||
Out,
|
Out,
|
||||||
|
|||||||
2
build.rs
2
build.rs
@@ -11,7 +11,7 @@ use std::process::Command;
|
|||||||
fn main() {
|
fn main() {
|
||||||
let mut version = "v".to_owned() + crate_version!();
|
let mut version = "v".to_owned() + crate_version!();
|
||||||
|
|
||||||
if let Ok(git_out) = Command::new("git").args(&["describe", "--dirty"]).output() {
|
if let Ok(git_out) = Command::new("git").args(["describe", "--dirty"]).output() {
|
||||||
if git_out.status.success() {
|
if git_out.status.success() {
|
||||||
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {
|
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {
|
||||||
version = git_out_str;
|
version = git_out_str;
|
||||||
|
|||||||
@@ -6,21 +6,22 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
acpi_tables = { path = "../acpi_tables" }
|
acpi_tables = { path = "../acpi_tables" }
|
||||||
anyhow = "1.0.56"
|
anyhow = "1.0.65"
|
||||||
arch = { path = "../arch" }
|
arch = { path = "../arch" }
|
||||||
bitflags = "1.3.2"
|
bitflags = "1.3.2"
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
epoll = "4.3.1"
|
hypervisor = { path = "../hypervisor" }
|
||||||
libc = "0.2.123"
|
libc = "0.2.133"
|
||||||
log = "0.4.16"
|
log = "0.4.17"
|
||||||
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.7.0"
|
vm-memory = "0.9.0"
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
vmm-sys-util = "0.9.0"
|
vmm-sys-util = "0.10.0"
|
||||||
|
|
||||||
|
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||||
|
arch = { path = "../arch" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
cmos = []
|
|
||||||
fwdebug = []
|
|
||||||
|
|||||||
@@ -105,18 +105,17 @@ impl BusDevice for AcpiGedDevice {
|
|||||||
impl Aml for AcpiGedDevice {
|
impl Aml for AcpiGedDevice {
|
||||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||||
aml::Device::new(
|
aml::Device::new(
|
||||||
"_SB_.GED_".into(),
|
"_SB_.GEC_".into(),
|
||||||
vec![
|
vec![
|
||||||
&aml::Name::new("_HID".into(), &"ACPI0013"),
|
&aml::Name::new("_HID".into(), &aml::EisaName::new("PNP0A06")),
|
||||||
&aml::Name::new("_UID".into(), &aml::ZERO),
|
&aml::Name::new("_UID".into(), &"Generic Event Controller"),
|
||||||
&aml::Name::new(
|
&aml::Name::new(
|
||||||
"_CRS".into(),
|
"_CRS".into(),
|
||||||
&aml::ResourceTemplate::new(vec![&aml::Interrupt::new(
|
&aml::ResourceTemplate::new(vec![&aml::AddressSpace::new_memory(
|
||||||
|
aml::AddressSpaceCachable::NotCacheable,
|
||||||
true,
|
true,
|
||||||
true,
|
self.address.0 as u64,
|
||||||
false,
|
self.address.0 + GED_DEVICE_ACPI_SIZE as u64 - 1,
|
||||||
false,
|
|
||||||
self.ged_irq,
|
|
||||||
)]),
|
)]),
|
||||||
),
|
),
|
||||||
&aml::OpRegion::new(
|
&aml::OpRegion::new(
|
||||||
@@ -132,8 +131,8 @@ impl Aml for AcpiGedDevice {
|
|||||||
vec![aml::FieldEntry::Named(*b"GDAT", 8)],
|
vec![aml::FieldEntry::Named(*b"GDAT", 8)],
|
||||||
),
|
),
|
||||||
&aml::Method::new(
|
&aml::Method::new(
|
||||||
"_EVT".into(),
|
"ESCN".into(),
|
||||||
1,
|
0,
|
||||||
true,
|
true,
|
||||||
vec![
|
vec![
|
||||||
&aml::Store::new(&aml::Local(0), &aml::Path::new("GDAT")),
|
&aml::Store::new(&aml::Local(0), &aml::Path::new("GDAT")),
|
||||||
@@ -164,6 +163,30 @@ impl Aml for AcpiGedDevice {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
.append_aml_bytes(bytes);
|
||||||
|
aml::Device::new(
|
||||||
|
"_SB_.GED_".into(),
|
||||||
|
vec![
|
||||||
|
&aml::Name::new("_HID".into(), &"ACPI0013"),
|
||||||
|
&aml::Name::new("_UID".into(), &aml::ZERO),
|
||||||
|
&aml::Name::new(
|
||||||
|
"_CRS".into(),
|
||||||
|
&aml::ResourceTemplate::new(vec![&aml::Interrupt::new(
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
self.ged_irq,
|
||||||
|
)]),
|
||||||
|
),
|
||||||
|
&aml::Method::new(
|
||||||
|
"_EVT".into(),
|
||||||
|
1,
|
||||||
|
true,
|
||||||
|
vec![&aml::MethodCall::new("\\_SB_.GEC_.ESCN".into(), vec![])],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
.append_aml_bytes(bytes)
|
.append_aml_bytes(bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,30 +4,37 @@
|
|||||||
|
|
||||||
use super::interrupt_controller::{Error, InterruptController};
|
use super::interrupt_controller::{Error, InterruptController};
|
||||||
extern crate arch;
|
extern crate arch;
|
||||||
use arch::aarch64::gic::GicDevice;
|
use anyhow::anyhow;
|
||||||
|
use arch::layout;
|
||||||
|
use hypervisor::{
|
||||||
|
arch::aarch64::gic::{Vgic, VgicConfig},
|
||||||
|
CpuState,
|
||||||
|
};
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use vm_device::interrupt::{
|
use vm_device::interrupt::{
|
||||||
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
|
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
|
||||||
LegacyIrqSourceConfig, MsiIrqGroupConfig,
|
LegacyIrqSourceConfig, MsiIrqGroupConfig,
|
||||||
};
|
};
|
||||||
|
use vm_memory::address::Address;
|
||||||
|
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
// Reserve 32 IRQs for legacy device.
|
// Reserve 32 IRQs for legacy devices.
|
||||||
pub const IRQ_LEGACY_BASE: usize = arch::layout::IRQ_BASE as usize;
|
pub const IRQ_LEGACY_BASE: usize = layout::IRQ_BASE as usize;
|
||||||
pub const IRQ_LEGACY_COUNT: usize = 32;
|
pub const IRQ_LEGACY_COUNT: usize = 32;
|
||||||
|
|
||||||
// This Gic struct implements InterruptController to provide interrupt delivery service.
|
// Gic (Generic Interupt Controller) struct provides all the functionality of a
|
||||||
// The Gic source files in arch/ folder maintain the Aarch64 specific Gic device.
|
// GIC device. It wraps a hypervisor-emulated GIC device (Vgic) provided by the
|
||||||
// The 2 Gic instances could be merged together.
|
// `hypervisor` crate.
|
||||||
// Leave this refactoring to future. Two options may be considered:
|
// Gic struct also implements InterruptController to provide interrupt delivery
|
||||||
// 1. Move Gic*.rs from arch/ folder here.
|
// service.
|
||||||
// 2. Move this file and ioapic.rs to arch/, as they are architecture specific.
|
|
||||||
pub struct Gic {
|
pub struct Gic {
|
||||||
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
|
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
|
||||||
gic_device: Option<Arc<Mutex<Box<dyn GicDevice>>>>,
|
// The hypervisor agnostic virtual GIC
|
||||||
|
vgic: Option<Arc<Mutex<dyn Vgic>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gic {
|
impl Gic {
|
||||||
@@ -44,16 +51,39 @@ impl Gic {
|
|||||||
|
|
||||||
Ok(Gic {
|
Ok(Gic {
|
||||||
interrupt_source_group,
|
interrupt_source_group,
|
||||||
gic_device: None,
|
vgic: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_gic_device(&mut self, gic_device: Arc<Mutex<Box<dyn GicDevice>>>) {
|
/// Default config implied by arch::layout
|
||||||
self.gic_device = Some(gic_device);
|
pub fn create_default_config(vcpu_count: u64) -> VgicConfig {
|
||||||
|
let redists_size = layout::GIC_V3_REDIST_SIZE * vcpu_count;
|
||||||
|
let redists_addr = layout::GIC_V3_DIST_START.raw_value() - redists_size;
|
||||||
|
VgicConfig {
|
||||||
|
vcpu_count,
|
||||||
|
dist_addr: layout::GIC_V3_DIST_START.raw_value(),
|
||||||
|
dist_size: layout::GIC_V3_DIST_SIZE,
|
||||||
|
redists_addr,
|
||||||
|
redists_size,
|
||||||
|
msi_addr: redists_addr - layout::GIC_V3_ITS_SIZE,
|
||||||
|
msi_size: layout::GIC_V3_ITS_SIZE,
|
||||||
|
nr_irqs: layout::IRQ_NUM,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_gic_device(&self) -> Option<&Arc<Mutex<Box<dyn GicDevice>>>> {
|
pub fn create_vgic(
|
||||||
self.gic_device.as_ref()
|
&mut self,
|
||||||
|
vm: &Arc<dyn hypervisor::Vm>,
|
||||||
|
config: VgicConfig,
|
||||||
|
) -> Result<Arc<Mutex<dyn Vgic>>> {
|
||||||
|
let vgic = vm.create_vgic(config).map_err(Error::CreateGic)?;
|
||||||
|
self.vgic = Some(vgic.clone());
|
||||||
|
Ok(vgic.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
|
||||||
|
let vgic = self.vgic.as_ref().unwrap().clone();
|
||||||
|
vgic.lock().unwrap().set_gicr_typers(vcpu_states);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,3 +127,43 @@ impl InterruptController for Gic {
|
|||||||
self.interrupt_source_group.notifier(irq as InterruptIndex)
|
self.interrupt_source_group.notifier(irq as InterruptIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const GIC_V3_ITS_SNAPSHOT_ID: &str = "gic-v3-its";
|
||||||
|
impl Snapshottable for Gic {
|
||||||
|
fn id(&self) -> String {
|
||||||
|
GIC_V3_ITS_SNAPSHOT_ID.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||||
|
let vgic = self.vgic.as_ref().unwrap().clone();
|
||||||
|
let state = vgic.lock().unwrap().state().unwrap();
|
||||||
|
Snapshot::new_from_state(&self.id(), &state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||||
|
let vgic = self.vgic.as_ref().unwrap().clone();
|
||||||
|
vgic.lock()
|
||||||
|
.unwrap()
|
||||||
|
.set_state(&snapshot.to_state(&self.id())?)
|
||||||
|
.map_err(|e| {
|
||||||
|
MigratableError::Restore(anyhow!("Could not restore GICv3ITS state {:?}", e))
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pausable for Gic {
|
||||||
|
fn pause(&mut self) -> std::result::Result<(), MigratableError> {
|
||||||
|
// Flush tables to guest RAM
|
||||||
|
let vgic = self.vgic.as_ref().unwrap().clone();
|
||||||
|
vgic.lock().unwrap().save_data_tables().map_err(|e| {
|
||||||
|
MigratableError::Pause(anyhow!(
|
||||||
|
"Could not save GICv3ITS GIC pending tables {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Transportable for Gic {}
|
||||||
|
impl Migratable for Gic {}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ pub enum Error {
|
|||||||
UpdateInterrupt(io::Error),
|
UpdateInterrupt(io::Error),
|
||||||
/// Failed enabling the interrupt.
|
/// Failed enabling the interrupt.
|
||||||
EnableInterrupt(io::Error),
|
EnableInterrupt(io::Error),
|
||||||
|
/// Failed creating GIC device.
|
||||||
|
CreateGic(hypervisor::HypervisorVmError),
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|||||||
86
devices/src/legacy/debug_port.rs
Normal file
86
devices/src/legacy/debug_port.rs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
// Copyright © 2022 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use std::time::Instant;
|
||||||
|
use vm_device::BusDevice;
|
||||||
|
|
||||||
|
/// Debug I/O port, see:
|
||||||
|
/// https://www.intel.com/content/www/us/en/support/articles/000005500/boards-and-kits.html
|
||||||
|
///
|
||||||
|
/// Since we're not a physical platform, we can freely assign code ranges for
|
||||||
|
/// debugging specific parts of our virtual platform.
|
||||||
|
pub enum DebugIoPortRange {
|
||||||
|
Firmware,
|
||||||
|
Bootloader,
|
||||||
|
Kernel,
|
||||||
|
Userspace,
|
||||||
|
Custom,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
const DEBUG_IOPORT_PREFIX: &str = "Debug I/O port";
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
impl DebugIoPortRange {
|
||||||
|
fn from_u8(value: u8) -> DebugIoPortRange {
|
||||||
|
match value {
|
||||||
|
0x00..=0x1f => DebugIoPortRange::Firmware,
|
||||||
|
0x20..=0x3f => DebugIoPortRange::Bootloader,
|
||||||
|
0x40..=0x5f => DebugIoPortRange::Kernel,
|
||||||
|
0x60..=0x7f => DebugIoPortRange::Userspace,
|
||||||
|
_ => DebugIoPortRange::Custom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
impl fmt::Display for DebugIoPortRange {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
DebugIoPortRange::Firmware => write!(f, "{}: Firmware", DEBUG_IOPORT_PREFIX),
|
||||||
|
DebugIoPortRange::Bootloader => write!(f, "{}: Bootloader", DEBUG_IOPORT_PREFIX),
|
||||||
|
DebugIoPortRange::Kernel => write!(f, "{}: Kernel", DEBUG_IOPORT_PREFIX),
|
||||||
|
DebugIoPortRange::Userspace => write!(f, "{}: Userspace", DEBUG_IOPORT_PREFIX),
|
||||||
|
DebugIoPortRange::Custom => write!(f, "{}: Custom", DEBUG_IOPORT_PREFIX),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DebugPort {
|
||||||
|
timestamp: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DebugPort {
|
||||||
|
pub fn new(timestamp: Instant) -> Self {
|
||||||
|
Self { timestamp }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BusDevice for DebugPort {
|
||||||
|
fn read(&mut self, _base: u64, _offset: u64, _data: &mut [u8]) {
|
||||||
|
error!("Invalid read to debug port")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(
|
||||||
|
&mut self,
|
||||||
|
_base: u64,
|
||||||
|
_offset: u64,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Option<std::sync::Arc<std::sync::Barrier>> {
|
||||||
|
let elapsed = self.timestamp.elapsed();
|
||||||
|
|
||||||
|
let code = data[0];
|
||||||
|
warn!(
|
||||||
|
"[{} code 0x{:x}] {}.{:>06} seconds",
|
||||||
|
DebugIoPortRange::from_u8(code),
|
||||||
|
code,
|
||||||
|
elapsed.as_secs(),
|
||||||
|
elapsed.as_micros()
|
||||||
|
);
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,9 @@
|
|||||||
// found in the LICENSE-BSD-3-Clause file.
|
// found in the LICENSE-BSD-3-Clause file.
|
||||||
|
|
||||||
mod cmos;
|
mod cmos;
|
||||||
#[cfg(feature = "fwdebug")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
mod debug_port;
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
mod fwdebug;
|
mod fwdebug;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
mod gpio_pl061;
|
mod gpio_pl061;
|
||||||
@@ -18,7 +20,9 @@ mod serial;
|
|||||||
mod uart_pl011;
|
mod uart_pl011;
|
||||||
|
|
||||||
pub use self::cmos::Cmos;
|
pub use self::cmos::Cmos;
|
||||||
#[cfg(feature = "fwdebug")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
pub use self::debug_port::DebugPort;
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub use self::fwdebug::FwDebugDevice;
|
pub use self::fwdebug::FwDebugDevice;
|
||||||
pub use self::i8042::I8042Device;
|
pub use self::i8042::I8042Device;
|
||||||
pub use self::serial::Serial;
|
pub use self::serial::Serial;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use crate::{read_le_u32, write_le_u32};
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
|
use std::time::Instant;
|
||||||
use std::{io, result};
|
use std::{io, result};
|
||||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||||
use versionize_derive::Versionize;
|
use versionize_derive::Versionize;
|
||||||
@@ -120,6 +121,7 @@ impl Pl011 {
|
|||||||
id: String,
|
id: String,
|
||||||
irq: Arc<dyn InterruptSourceGroup>,
|
irq: Arc<dyn InterruptSourceGroup>,
|
||||||
out: Option<Box<dyn io::Write + Send>>,
|
out: Option<Box<dyn io::Write + Send>>,
|
||||||
|
timestamp: Instant,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
@@ -140,7 +142,7 @@ impl Pl011 {
|
|||||||
read_trigger: 1u32,
|
read_trigger: 1u32,
|
||||||
irq,
|
irq,
|
||||||
out,
|
out,
|
||||||
timestamp: std::time::Instant::now(),
|
timestamp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,25 +306,25 @@ impl Pl011 {
|
|||||||
let elapsed = self.timestamp.elapsed();
|
let elapsed = self.timestamp.elapsed();
|
||||||
|
|
||||||
match self.debug {
|
match self.debug {
|
||||||
0x00..=0x1f => info!(
|
0x00..=0x1f => warn!(
|
||||||
"[Debug I/O port: Firmware code: 0x{:x}] {}.{:>06} seconds",
|
"[Debug I/O port: Firmware code: 0x{:x}] {}.{:>06} seconds",
|
||||||
self.debug,
|
self.debug,
|
||||||
elapsed.as_secs(),
|
elapsed.as_secs(),
|
||||||
elapsed.as_micros()
|
elapsed.as_micros()
|
||||||
),
|
),
|
||||||
0x20..=0x3f => info!(
|
0x20..=0x3f => warn!(
|
||||||
"[Debug I/O port: Bootloader code: 0x{:x}] {}.{:>06} seconds",
|
"[Debug I/O port: Bootloader code: 0x{:x}] {}.{:>06} seconds",
|
||||||
self.debug,
|
self.debug,
|
||||||
elapsed.as_secs(),
|
elapsed.as_secs(),
|
||||||
elapsed.as_micros()
|
elapsed.as_micros()
|
||||||
),
|
),
|
||||||
0x40..=0x5f => info!(
|
0x40..=0x5f => warn!(
|
||||||
"[Debug I/O port: Kernel code: 0x{:x}] {}.{:>06} seconds",
|
"[Debug I/O port: Kernel code: 0x{:x}] {}.{:>06} seconds",
|
||||||
self.debug,
|
self.debug,
|
||||||
elapsed.as_secs(),
|
elapsed.as_secs(),
|
||||||
elapsed.as_micros()
|
elapsed.as_micros()
|
||||||
),
|
),
|
||||||
0x60..=0x7f => info!(
|
0x60..=0x7f => warn!(
|
||||||
"[Debug I/O port: Userspace code: 0x{:x}] {}.{:>06} seconds",
|
"[Debug I/O port: Userspace code: 0x{:x}] {}.{:>06} seconds",
|
||||||
self.debug,
|
self.debug,
|
||||||
elapsed.as_secs(),
|
elapsed.as_secs(),
|
||||||
@@ -370,7 +372,7 @@ impl BusDevice for Pl011 {
|
|||||||
UARTIFLS => self.ifl,
|
UARTIFLS => self.ifl,
|
||||||
UARTIMSC => self.int_enabled,
|
UARTIMSC => self.int_enabled,
|
||||||
UARTRIS => self.int_level,
|
UARTRIS => self.int_level,
|
||||||
UARTMIS => (self.int_level & self.int_enabled),
|
UARTMIS => self.int_level & self.int_enabled,
|
||||||
UARTDMACR => self.dmacr,
|
UARTDMACR => self.dmacr,
|
||||||
UARTDEBUG => self.debug,
|
UARTDEBUG => self.debug,
|
||||||
_ => {
|
_ => {
|
||||||
@@ -495,6 +497,7 @@ mod tests {
|
|||||||
String::from(SERIAL_NAME),
|
String::from(SERIAL_NAME),
|
||||||
Arc::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())),
|
||||||
|
Instant::now(),
|
||||||
);
|
);
|
||||||
|
|
||||||
pl011.write(0, UARTDR as u64, &[b'x', b'y']);
|
pl011.write(0, UARTDR as u64, &[b'x', b'y']);
|
||||||
@@ -515,6 +518,7 @@ mod tests {
|
|||||||
String::from(SERIAL_NAME),
|
String::from(SERIAL_NAME),
|
||||||
Arc::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)),
|
||||||
|
Instant::now(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 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
|
||||||
|
|||||||
101
docs/api.md
101
docs/api.md
@@ -1,21 +1,21 @@
|
|||||||
- [Cloud Hypervisor API](#cloud-hypervisor-api)
|
- [Cloud Hypervisor API](#cloud-hypervisor-api)
|
||||||
* [External API](#external-api)
|
- [External API](#external-api)
|
||||||
+ [REST API](#rest-api)
|
- [REST API](#rest-api)
|
||||||
- [Location and availability](#location-and-availability)
|
- [Location and availability](#location-and-availability)
|
||||||
- [Endpoints](#endpoints)
|
- [Endpoints](#endpoints)
|
||||||
* [Virtual Machine Manager (VMM) Actions](#virtual-machine-manager-vmm-actions)
|
- [Virtual Machine Manager (VMM) Actions](#virtual-machine-manager-vmm-actions)
|
||||||
* [Virtual Machine (VM) Actions](#virtual-machine-vm-actions)
|
- [Virtual Machine (VM) Actions](#virtual-machine-vm-actions)
|
||||||
- [REST API Examples](#rest-api-examples)
|
- [REST API Examples](#rest-api-examples)
|
||||||
* [Create a Virtual Machine](#create-a-virtual-machine)
|
- [Create a Virtual Machine](#create-a-virtual-machine)
|
||||||
* [Boot a Virtual Machine](#boot-a-virtual-machine)
|
- [Boot a Virtual Machine](#boot-a-virtual-machine)
|
||||||
* [Dump a Virtual Machine Information](#dump-a-virtual-machine-information)
|
- [Dump a Virtual Machine Information](#dump-a-virtual-machine-information)
|
||||||
* [Reboot a Virtual Machine](#reboot-a-virtual-machine)
|
- [Reboot a Virtual Machine](#reboot-a-virtual-machine)
|
||||||
* [Shut a Virtual Machine Down](#shut-a-virtual-machine-down)
|
- [Shut a Virtual Machine Down](#shut-a-virtual-machine-down)
|
||||||
+ [Command Line Interface](#command-line-interface)
|
- [Command Line Interface](#command-line-interface)
|
||||||
+ [REST API and CLI Architectural Relationship](#rest-api-and-cli-architectural-relationship)
|
- [REST API and CLI Architectural Relationship](#rest-api-and-cli-architectural-relationship)
|
||||||
* [Internal API](#internal-api)
|
- [Internal API](#internal-api)
|
||||||
+ [Goals and Design](#goals-and-design)
|
- [Goals and Design](#goals-and-design)
|
||||||
* [End to End Example](#end-to-end-example)
|
- [End to End Example](#end-to-end-example)
|
||||||
|
|
||||||
# Cloud Hypervisor API
|
# Cloud Hypervisor API
|
||||||
|
|
||||||
@@ -71,39 +71,40 @@ The Cloud Hypervisor API exposes the following actions through its endpoints:
|
|||||||
|
|
||||||
#### Virtual Machine Manager (VMM) Actions
|
#### Virtual Machine Manager (VMM) Actions
|
||||||
|
|
||||||
Action | Endpoint | Request Body | Response Body | Prerequisites
|
| Action | Endpoint | Request Body | Response Body | Prerequisites |
|
||||||
------------------------------------|-----------------|--------------|----------------------------|---------------------------
|
| ----------------------------------- | --------------- | ------------ | -------------------------- | ------------------ |
|
||||||
Check for the REST API availability | `/vmm.ping` | N/A | `/schemas/VmmPingResponse` | N/A
|
| Check for the REST API availability | `/vmm.ping` | N/A | `/schemas/VmmPingResponse` | N/A |
|
||||||
Shut the VMM down | `/vmm.shutdown` | N/A | N/A | The VMM is running
|
| Shut the VMM down | `/vmm.shutdown` | N/A | N/A | The VMM is running |
|
||||||
|
|
||||||
#### 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 |
|
||||||
Trigger power button of the VM | `/vm.power-button` | 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 |
|
||||||
Pause the VM | `/vm.pause` | N/A | N/A | The VM is booted
|
| Pause the VM | `/vm.pause` | N/A | N/A | The VM is booted |
|
||||||
Resume the VM | `/vm.resume` | N/A | N/A | The VM is paused
|
| Resume the VM | `/vm.resume` | N/A | N/A | The VM is paused |
|
||||||
Task a snapshot of the VM | `/vm.snapshot` | `/schemas/VmSnapshotConfig`| N/A | The VM is paused
|
| Task a snapshot of the VM | `/vm.snapshot` | `/schemas/VmSnapshotConfig` | N/A | The VM is paused |
|
||||||
Restore the VM from a snapshot | `/vm.restore` | `/schemas/RestoreConfig` | N/A | The VM is created but not booted
|
| Perform a coredump of the VM | `/vm.coredump` | `/schemas/VmCoredumpData` | N/A | The VM is paused |
|
||||||
Add/remove CPUs to/from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
|
| Restore the VM from a snapshot | `/vm.restore` | `/schemas/RestoreConfig` | N/A | The VM is created but not booted |
|
||||||
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 userspace PCI device to the VM | `/vm.add-user-device`| `/schemas/VmAddUserDevice`| `/schemas/PciDeviceInfo` | The VM is booted
|
| Add network device to the VM | `/vm.add-net` | `/schemas/NetConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||||
Add vdpa device to the VM | `/vm.add-vdpa` | `/schemas/VdpaConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
| Add userspace PCI device to the VM | `/vm.add-user-device` | `/schemas/VmAddUserDevice` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||||
Add vsock device to the VM | `/vm.add-vsock` | `/schemas/VsockConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
| Add vdpa device to the VM | `/vm.add-vdpa` | `/schemas/VdpaConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||||
Remove device from the VM | `/vm.remove-device` | `/schemas/VmRemoveDevice` | N/A | The VM is booted
|
| Add vsock device to the VM | `/vm.add-vsock` | `/schemas/VsockConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||||
Dump the VM counters | `/vm.counters` | N/A | `/schemas/VmCounters` | 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
|
||||||
|
|
||||||
@@ -142,8 +143,7 @@ curl --unix-socket /tmp/cloud-hypervisor.sock -i \
|
|||||||
-H 'Content-Type: application/json' \
|
-H 'Content-Type: application/json' \
|
||||||
-d '{
|
-d '{
|
||||||
"cpus":{"boot_vcpus": 4, "max_vcpus": 4},
|
"cpus":{"boot_vcpus": 4, "max_vcpus": 4},
|
||||||
"kernel":{"path":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu"},
|
"payload":{"kernel":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu", "cmdline":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
|
||||||
"cmdline":{"args":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
|
|
||||||
"disks":[{"path":"/opt/clh/images/focal-server-cloudimg-amd64.raw"}],
|
"disks":[{"path":"/opt/clh/images/focal-server-cloudimg-amd64.raw"}],
|
||||||
"rng":{"src":"/dev/urandom"},
|
"rng":{"src":"/dev/urandom"},
|
||||||
"net":[{"ip":"192.168.10.10", "mask":"255.255.255.0", "mac":"12:34:56:78:90:01"}]
|
"net":[{"ip":"192.168.10.10", "mask":"255.255.255.0", "mac":"12:34:56:78:90:01"}]
|
||||||
@@ -310,8 +310,7 @@ APIs work together, let's look at a complete VM creation flow, from the
|
|||||||
-H 'Content-Type: application/json' \
|
-H 'Content-Type: application/json' \
|
||||||
-d '{
|
-d '{
|
||||||
"cpus":{"boot_vcpus": 4, "max_vcpus": 4},
|
"cpus":{"boot_vcpus": 4, "max_vcpus": 4},
|
||||||
"kernel":{"path":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu"},
|
"payload":{"kernel":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu", "cmdline":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
|
||||||
"cmdline":{"args":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
|
|
||||||
"disks":[{"path":"/opt/clh/images/focal-server-cloudimg-amd64.raw"}],
|
"disks":[{"path":"/opt/clh/images/focal-server-cloudimg-amd64.raw"}],
|
||||||
"rng":{"src":"/dev/urandom"},
|
"rng":{"src":"/dev/urandom"},
|
||||||
"net":[{"ip":"192.168.10.10", "mask":"255.255.255.0", "mac":"12:34:56:78:90:01"}]
|
"net":[{"ip":"192.168.10.10", "mask":"255.255.255.0", "mac":"12:34:56:78:90:01"}]
|
||||||
|
|||||||
@@ -59,6 +59,16 @@ mkdir -p /mnt
|
|||||||
sudo mount -o loop,offset=$((227328 * 512)) focal-server-cloudimg-amd64.raw /mnt
|
sudo mount -o loop,offset=$((227328 * 512)) focal-server-cloudimg-amd64.raw /mnt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Set up DNS
|
||||||
|
|
||||||
|
The next step describes changing the root directory to the rootfs contained by
|
||||||
|
the cloud image. For DNS to work in the root directory, you will need to first bind-mount
|
||||||
|
the host `/etc/resolv.conf` onto the mounted linux partition of the cloud image.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mount -o bind /etc/resolv.conf /mnt/etc/resolv.conf
|
||||||
|
```
|
||||||
|
|
||||||
### Change root directory
|
### Change root directory
|
||||||
|
|
||||||
Changing the root directory will allow us to install new packages to the rootfs
|
Changing the root directory will allow us to install new packages to the rootfs
|
||||||
@@ -108,6 +118,7 @@ umount /dev/pts
|
|||||||
umount /proc
|
umount /proc
|
||||||
history -c
|
history -c
|
||||||
exit
|
exit
|
||||||
|
umount /mnt/etc/resolv.conf
|
||||||
umount /mnt
|
umount /mnt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -6,22 +6,22 @@ This document describes the device model supported by `cloud-hypervisor`.
|
|||||||
|
|
||||||
| Device | Build configurable | Enabled by default | Runtime configurable |
|
| Device | Build configurable | Enabled by default | Runtime configurable |
|
||||||
| :----: | :----: | :----: | :----: |
|
| :----: | :----: | :----: | :----: |
|
||||||
| Serial port | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
| Serial port | :x: | :x: | :heavy_check_mark: |
|
||||||
| RTC/CMOS | :heavy_check_mark: | :heavy_check_mark: | :negative_squared_cross_mark: |
|
| RTC/CMOS | :heavy_check_mark: | :heavy_check_mark: | :x: |
|
||||||
| I/O APIC | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
| I/O APIC | :x: | :x: | :heavy_check_mark: |
|
||||||
| i8042 shutdown/reboot | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :negative_squared_cross_mark: |
|
| i8042 shutdown/reboot | :x: | :x: | :x: |
|
||||||
| ACPI shutdown/reboot | :negative_squared_cross_mark: | :heavy_check_mark: | :negative_squared_cross_mark: |
|
| ACPI shutdown/reboot | :x: | :heavy_check_mark: | :x: |
|
||||||
| virtio-blk | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
| virtio-blk | :x: | :x: | :heavy_check_mark: |
|
||||||
| virtio-console | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
| virtio-console | :x: | :x: | :heavy_check_mark: |
|
||||||
| virtio-iommu | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
| virtio-iommu | :x: | :x: | :heavy_check_mark: |
|
||||||
| virtio-net | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
| virtio-net | :x: | :x: | :heavy_check_mark: |
|
||||||
| virtio-pmem | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
| virtio-pmem | :x: | :x: | :heavy_check_mark: |
|
||||||
| virtio-rng | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
| virtio-rng | :x: | :x: | :heavy_check_mark: |
|
||||||
| virtio-vsock | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
| virtio-vsock | :x: | :x: | :heavy_check_mark: |
|
||||||
| vhost-user-blk | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
| vhost-user-blk | :x: | :x: | :heavy_check_mark: |
|
||||||
| vhost-user-fs | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
| vhost-user-fs | :x: | :x: | :heavy_check_mark: |
|
||||||
| vhost-user-net | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
| vhost-user-net | :x: | :x: | :heavy_check_mark: |
|
||||||
| VFIO | :heavy_check_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
|
| VFIO | :heavy_check_mark: | :x: | :heavy_check_mark: |
|
||||||
|
|
||||||
## Legacy devices
|
## Legacy devices
|
||||||
|
|
||||||
|
|||||||
11
docs/fs.md
11
docs/fs.md
@@ -29,10 +29,11 @@ mkdir /tmp/shared_dir
|
|||||||
_Run virtiofsd_
|
_Run virtiofsd_
|
||||||
```bash
|
```bash
|
||||||
./virtiofsd \
|
./virtiofsd \
|
||||||
-d \
|
--log-level debug \
|
||||||
--socket-path=/tmp/virtiofs \
|
--socket-path=/tmp/virtiofs \
|
||||||
--shared-dir=/tmp/shared_dir \
|
--shared-dir=/tmp/shared_dir \
|
||||||
--cache=never
|
--cache=never \
|
||||||
|
--thread-pool-size=$N
|
||||||
```
|
```
|
||||||
|
|
||||||
The `cache=never` option is the default when using `virtiofsd` with
|
The `cache=never` option is the default when using `virtiofsd` with
|
||||||
@@ -44,6 +45,10 @@ The `cache=always` option will allow the host page cache to be used, which can
|
|||||||
result in better performance for the guest's workload at the cost of increasing
|
result in better performance for the guest's workload at the cost of increasing
|
||||||
the footprint on host memory.
|
the footprint on host memory.
|
||||||
|
|
||||||
|
The `thread-pool-size` option controls how many IO threads are spawned. For
|
||||||
|
very fast storage like NVMe spawning enough worker threads is critical to
|
||||||
|
getting an acceptable performance compared to native.
|
||||||
|
|
||||||
### Kernel support
|
### Kernel support
|
||||||
|
|
||||||
Modern Linux kernels (at least v5.10) have support for virtio-fs. Use of older
|
Modern Linux kernels (at least v5.10) have support for virtio-fs. Use of older
|
||||||
@@ -90,4 +95,4 @@ Cloud Hypervisor command line, which happens to be `myfs` in this example.
|
|||||||
## DAX feature
|
## DAX feature
|
||||||
|
|
||||||
Given the DAX feature is not stable yet from a daemon standpoint, it is not
|
Given the DAX feature is not stable yet from a daemon standpoint, it is not
|
||||||
available in Cloud Hypervisor.
|
available in Cloud Hypervisor.
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
This feature allows remote guest debugging using GDB. Note that this feature is only supported on x86_64/KVM.
|
This feature allows remote guest debugging using GDB. Note that this feature is only supported on x86_64/KVM.
|
||||||
|
|
||||||
To enable debugging with GDB, build with the `gdb` feature enabled:
|
To enable debugging with GDB, build with the `guest_debug` feature enabled:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo build --features gdb
|
cargo build --features guest_debug
|
||||||
```
|
```
|
||||||
|
|
||||||
To use the `--gdb` option, specify the Unix Domain Socket with `--path` that Cloud Hypervisor will use to communicate with the host's GDB:
|
To use the `--gdb` option, specify the Unix Domain Socket with `--path` that Cloud Hypervisor will use to communicate with the host's GDB:
|
||||||
@@ -44,4 +44,4 @@ Continuing.
|
|||||||
|
|
||||||
Breakpoint 1, 0x00000000001121b7 in ?? ()
|
Breakpoint 1, 0x00000000001121b7 in ?? ()
|
||||||
(gdb)
|
(gdb)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ meaning it will be printing guest kernel logs to the `virtio-console` device.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
./cloud-hypervisor \
|
./cloud-hypervisor \
|
||||||
--tdx firmware=edk2-staging/Build/OvmfCh/RELEASE_GCC5/FV/OVMF.fd \
|
--platform tdx=on
|
||||||
|
--firmware edk2-staging/Build/OvmfCh/RELEASE_GCC5/FV/OVMF.fd \
|
||||||
--cpus boot=1 \
|
--cpus boot=1 \
|
||||||
--memory size=1G \
|
--memory size=1G \
|
||||||
--disk path=tdx_guest_img
|
--disk path=tdx_guest_img
|
||||||
@@ -72,7 +73,8 @@ firmware:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
./cloud-hypervisor \
|
./cloud-hypervisor \
|
||||||
--tdx firmware=edk2-staging/Build/OvmfCh/DEBUG_GCC5/FV/OVMF.fd \
|
--platform tdx=on
|
||||||
|
--firmware edk2-staging/Build/OvmfCh/DEBUG_GCC5/FV/OVMF.fd \
|
||||||
--cpus boot=1 \
|
--cpus boot=1 \
|
||||||
--memory size=1G \
|
--memory size=1G \
|
||||||
--disk path=tdx_guest_img \
|
--disk path=tdx_guest_img \
|
||||||
@@ -95,7 +97,8 @@ option as well.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
./cloud-hypervisor \
|
./cloud-hypervisor \
|
||||||
--tdx firmware=tdshim \
|
--platform tdx=on
|
||||||
|
--firmware tdshim \
|
||||||
--kernel bzImage \
|
--kernel bzImage \
|
||||||
--cmdline "root=/dev/vda3 console=hvc0 rw"
|
--cmdline "root=/dev/vda3 console=hvc0 rw"
|
||||||
--cpus boot=1 \
|
--cpus boot=1 \
|
||||||
@@ -103,8 +106,18 @@ option as well.
|
|||||||
--disk path=tdx_guest_img
|
--disk path=tdx_guest_img
|
||||||
```
|
```
|
||||||
|
|
||||||
### Guest kernel disables serial ports
|
### Guest kernel limitations
|
||||||
|
|
||||||
|
#### Serial ports disabled
|
||||||
|
|
||||||
The latest guest kernel that can be found in the latest image
|
The latest guest kernel that can be found in the latest image
|
||||||
`td-guest-rhel8.5.raw` disabled the support for serial ports. This means adding
|
`td-guest-rhel8.5.raw` disabled the support for serial ports. This means adding
|
||||||
`console=ttyS0` will have no effect and will not print any log from the guest.
|
`console=ttyS0` will have no effect and will not print any log from the guest.
|
||||||
|
|
||||||
|
#### PCI hotplug through ACPI
|
||||||
|
|
||||||
|
Unless you run the guest kernel with the parameter `tdx_disable_filter`, ACPI
|
||||||
|
devices responsible for handling PCI hotplug (PCI hotplug controller, PCI
|
||||||
|
Express Bus and Generic Event Device) will not be allowed, therefore the
|
||||||
|
corresponding drivers will not be loaded and the PCI hotplug feature will not
|
||||||
|
be supported.
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ bucket is unbounded in speed which allows for bursts bound in size by
|
|||||||
the amount of tokens available. Once the token bucket is empty,
|
the amount of tokens available. Once the token bucket is empty,
|
||||||
consumption speed is bound by the "refill-rate". Similarly, Cloud
|
consumption speed is bound by the "refill-rate". Similarly, Cloud
|
||||||
Hypervisor provides another three options for limiting I/O operations,
|
Hypervisor provides another three options for limiting I/O operations,
|
||||||
i.e., `ops_size` (I/O operations), `bw_one_time_burst` (I/O operations),
|
i.e., `ops_size` (I/O operations), `ops_one_time_burst` (I/O operations),
|
||||||
and `bw_refill_time` (ms).
|
and `ops_refill_time` (ms).
|
||||||
|
|
||||||
One caveat in the I/O throttling is that every-time the bucket gets
|
One caveat in the I/O throttling is that every-time the bucket gets
|
||||||
empty, it will stop I/O operations for a fixed amount of time
|
empty, it will stop I/O operations for a fixed amount of time
|
||||||
|
|||||||
@@ -43,3 +43,22 @@ $ perf report -g
|
|||||||
```
|
```
|
||||||
|
|
||||||
If profiling with a network device attached either the TAP device must be already created and configured or the profiling must be done as root so that the TAP device can be created.
|
If profiling with a network device attached either the TAP device must be already created and configured or the profiling must be done as root so that the TAP device can be created.
|
||||||
|
|
||||||
|
## Userspace only profiling with LBR
|
||||||
|
|
||||||
|
The use of LBR (Last Branch Record; available since Haswell) offers lower
|
||||||
|
overhead if only userspace profiling is required. This lower overhead can allow
|
||||||
|
a higher frequency of sampling. This also removes the requirement to compile
|
||||||
|
with custom `RUSTFLAGS` however debug symbols should still be included:
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ perf record --call-graph lbr --all-user --user-callchains -g target/release/cloud-hypervisor \
|
||||||
|
--kernel ~/src/linux/vmlinux \
|
||||||
|
--pmem file=~/workloads/focal.raw \
|
||||||
|
--cpus boot=1 --memory size=1G \
|
||||||
|
--cmdline "root=/dev/pmem0p1 console=ttyS0" \
|
||||||
|
--serial tty --console off \
|
||||||
|
--api-socket=/tmp/api1
|
||||||
|
```
|
||||||
|
|||||||
103
docs/releases.md
Executable file
103
docs/releases.md
Executable file
@@ -0,0 +1,103 @@
|
|||||||
|
# Release Documentation
|
||||||
|
|
||||||
|
## Abstract
|
||||||
|
|
||||||
|
This document provides guidance to users, downstream maintainers and
|
||||||
|
any other consumers of the Cloud Hypervisor project, this document
|
||||||
|
describes the release process, release cadence, stability expectations and
|
||||||
|
related topics.
|
||||||
|
|
||||||
|
## Basic Terms
|
||||||
|
|
||||||
|
### Stability
|
||||||
|
|
||||||
|
For Cloud Hypervisor the following areas are subject to stability guarantees:
|
||||||
|
|
||||||
|
- [REST API](api.md#rest-api)
|
||||||
|
- [Command line options](api.md#command-line-interface)
|
||||||
|
- [Device Model](device_model.md)
|
||||||
|
- Device tree, device list, ACPI, Hyper-V enlightenments and any other
|
||||||
|
features exposed to guest
|
||||||
|
- KVM compatibility
|
||||||
|
- Rust edition compatibility
|
||||||
|
|
||||||
|
This list is incomplete but this document serves as a best effort guide to stability
|
||||||
|
across releases.
|
||||||
|
|
||||||
|
### Experimental features
|
||||||
|
|
||||||
|
Experimental features are under active development and no guarantees are made about their stability.
|
||||||
|
|
||||||
|
List of experimental features:
|
||||||
|
|
||||||
|
- TDX
|
||||||
|
- vfio-user
|
||||||
|
- vDPA
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
Security fixes should be included in a new point release.
|
||||||
|
|
||||||
|
For security issues an advisory will be published via the GitHub security advisory process along with the release. Watching the project on GitHub will notify you of those issues.
|
||||||
|
|
||||||
|
## Releases
|
||||||
|
|
||||||
|
### Versioning
|
||||||
|
|
||||||
|
The versioning scheme uses `MAJOR.POINT` pattern:
|
||||||
|
|
||||||
|
- `MAJOR` can introduce incompatible changes along with support for new features. Changes to the [API](api.md#rest-api),
|
||||||
|
[CLI options](api.md#command-line-interface) and [device model](device_model.md)
|
||||||
|
require a notice at least 2 releases in advance for the actual change to take
|
||||||
|
place.
|
||||||
|
- `POINT` contains bug fixes and/or security fixes.
|
||||||
|
|
||||||
|
### Major Release Cadence
|
||||||
|
|
||||||
|
Cloud Hypervisor is under active development. A new major release is issued approximately
|
||||||
|
every 6 weeks. Point releases are issued on demand, when important bug fixes are in
|
||||||
|
the queue. A major release would receive bug fixes for the next two cycles (~12 weeks)
|
||||||
|
and then be considered EOL.
|
||||||
|
|
||||||
|
```
|
||||||
|
+ - Active release support
|
||||||
|
E - EOL
|
||||||
|
|
||||||
|
2021 2022 2023
|
||||||
|
| | | | | | | | |
|
||||||
|
18.0 | | | ++++++++E
|
||||||
|
19.0 | | | |++++++++E
|
||||||
|
20.0 | | | | ++++++++E
|
||||||
|
21.0 | | | | | ++++++++E
|
||||||
|
22.0 | | | | | +++++++++E
|
||||||
|
23.0 | | | | | | +++++++++E
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Major Release Stability Considerations
|
||||||
|
|
||||||
|
Snapshot/restore support is not compatible across `MAJOR` versions.
|
||||||
|
Live migration support is not compatible across `MAJOR` versions.
|
||||||
|
|
||||||
|
### LTS Release Cadence
|
||||||
|
|
||||||
|
A regular release is promoted to LTS every 12 months. An LTS release is supported for 18 months. This gives a 6 months window for users to move to the new LTS.
|
||||||
|
|
||||||
|
```
|
||||||
|
+ - Active release support
|
||||||
|
E - EOL
|
||||||
|
|
||||||
|
2022 2023 2024 2025 2026
|
||||||
|
| | | | | | | | | | | | | | | | |
|
||||||
|
23.0 | |+++++++++++++++++++++++++++++E
|
||||||
|
43.0 | | | | | |+++++++++++++++++++++++++++++E
|
||||||
|
63.0 | | | | | | | | | |+++++++++++++++++++++++++++++E
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### LTS Stablity Considerations
|
||||||
|
|
||||||
|
An LTS release is just a `MAJOR` release for which point releases are made for
|
||||||
|
longer following the same rules for what can be backported to a `POINT` release.
|
||||||
|
The focus lays on critical and security bug fixes which are pulled at the
|
||||||
|
maintainer's discretion.
|
||||||
42
docs/tracing.md
Normal file
42
docs/tracing.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Tracing
|
||||||
|
|
||||||
|
Cloud Hypervisor has a basic tracing infrastucture, particularly focussed on
|
||||||
|
the tracing of the initial VM setup.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To enabling tracing, build with "tracing" feature. When compiled without the
|
||||||
|
feature the tracing is compiled out.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo build --features "tracing"
|
||||||
|
```
|
||||||
|
|
||||||
|
And then run Cloud Hypervisor as you wish, the trace will be written to the current directory as `cloud-hypervisor-<pid>.trace`. This is JSON file which you can inspect yourself.
|
||||||
|
|
||||||
|
Alternatively you can use the provided script in
|
||||||
|
`scripts/ch-trace-visualiser.py` to generate an SVG:
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scripts/ch-trace-visualiser.py cloud-hypervisor-39466.trace output.svg
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tracing in the codebase
|
||||||
|
|
||||||
|
There are existing tracepoints in the code base; extra ones can be added for
|
||||||
|
more detailed tracing.
|
||||||
|
|
||||||
|
The `tracer::trace_scoped!()` macro is used to add the current existing scope
|
||||||
|
appears as a block in the trace. Other than providing a useful name for the
|
||||||
|
event nothing else is required from the developer.
|
||||||
|
|
||||||
|
The `tracer::start()` and `tracer::end()` functions are already in place for
|
||||||
|
generating traces of the boot. These can be relocated for focus tracing on a
|
||||||
|
narrow part of the code base.
|
||||||
|
|
||||||
|
A `tracer::trace_point!()` macro is also provided for an instantaneous trace
|
||||||
|
point however this is not in use in the code base currently nor is handled by
|
||||||
|
the visualisation script due to the difficulty in representation in the SVG.
|
||||||
|
|
||||||
@@ -75,8 +75,7 @@ Here is an example how to create a bridge and add two DPDK ports to it
|
|||||||
ovs-vsctl add-br ovsbr0 -- set bridge ovsbr0 datapath_type=netdev
|
ovs-vsctl add-br ovsbr0 -- set bridge ovsbr0 datapath_type=netdev
|
||||||
# create two DPDK ports and add them to the bridge
|
# create two DPDK ports and add them to the bridge
|
||||||
ovs-vsctl add-port ovsbr0 vhost-user1 -- set Interface vhost-user1 type=dpdkvhostuser
|
ovs-vsctl add-port ovsbr0 vhost-user1 -- set Interface vhost-user1 type=dpdkvhostuser
|
||||||
ovs-vsctl add-port ovsbr0 vhost-user2 -- set Interface vhost-user2
|
ovs-vsctl add-port ovsbr0 vhost-user2 -- set Interface vhost-user2 type=dpdkvhostuser
|
||||||
type=dpdkvhostuser
|
|
||||||
# set the number of rx queues
|
# set the number of rx queues
|
||||||
ovs-vsctl set Interface vhost-user1 options:n_rxq=2
|
ovs-vsctl set Interface vhost-user1 options:n_rxq=2
|
||||||
ovs-vsctl set Interface vhost-user2 options:n_rxq=2
|
ovs-vsctl set Interface vhost-user2 options:n_rxq=2
|
||||||
|
|||||||
84
docs/vsock.md
Normal file
84
docs/vsock.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# VSOCK support
|
||||||
|
|
||||||
|
VSOCK provides a way for guest and host to communicate through a socket. VSOCK sockets support both stream and datagram types.
|
||||||
|
|
||||||
|
The `virtio-vsock` is based on the [Firecracker](https://github.com/firecracker-microvm/firecracker/blob/main/docs/vsock.md) implementation, where additional details can be found.
|
||||||
|
|
||||||
|
## What is a CID?
|
||||||
|
|
||||||
|
CID is a 32-bit context identifier describing the source or destination. In combination with the port, the complete addressing can be achieved to describe multiple listeners running on the same machine.
|
||||||
|
|
||||||
|
The table below depicts the well known CID values:
|
||||||
|
|
||||||
|
| CID | Description |
|
||||||
|
|-----|-------------|
|
||||||
|
| -1 | Random CID |
|
||||||
|
| 0 | Hypervisor |
|
||||||
|
| 1 | Loopback |
|
||||||
|
| 2 | Host |
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
### Kernel Requirements
|
||||||
|
|
||||||
|
Host kernel: CONFIG_VHOST_VSOCK
|
||||||
|
|
||||||
|
Guest kernel: CONFIG_VIRTIO_VSOCKETS
|
||||||
|
|
||||||
|
### Nested VM support
|
||||||
|
|
||||||
|
Linux __v5.5__ or newer is required for the L1 VM.
|
||||||
|
|
||||||
|
### Loopback support
|
||||||
|
|
||||||
|
Linux __v5.6__ or newer is required.
|
||||||
|
|
||||||
|
## Establishing VSOCK Connection
|
||||||
|
|
||||||
|
VSOCK device becomes available with `--vsock` option passed by the VM start. Cloud Hypervisor can be invoked for instance as below:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cloud-hypervisor \
|
||||||
|
--cpus boot=1 \
|
||||||
|
--memory size=4G \
|
||||||
|
--firmware CLOUDHV.fd \
|
||||||
|
--disk path=jammy-server-cloudimg.raw \
|
||||||
|
--vsock cid=3,socket=/tmp/ch.vsock
|
||||||
|
```
|
||||||
|
|
||||||
|
The examples use __socat__ `>=1.7.4` to illustrate the VSOCK functionality. However, there are other tools supporting VSOCK, like [ncat](https://stefano-garzarella.github.io/posts/2019-11-08-kvmforum-2019-vsock/).
|
||||||
|
|
||||||
|
### Connecting from Host to Guest
|
||||||
|
|
||||||
|
The host starts to listen on the defined port:
|
||||||
|
|
||||||
|
`$ socat - VSOCK-LISTEN:1234`
|
||||||
|
|
||||||
|
Once the host is listening, the guest can send data:
|
||||||
|
|
||||||
|
`echo -e "CONNECT 1234\\nHello from host!" | socat - UNIX-CONNECT:/tmp/ch.vsock
|
||||||
|
|
||||||
|
Note the string `CONNECT <port>` prepended to the actual data. It is possible for the guest to start listening on different ports, thus the specific command is needed to instruct VSOCK to which listener the host wants to connect. It needs to be sent once per connection. Once the connection established, data transfers can take place directly.
|
||||||
|
|
||||||
|
### Connecting from Guest to Host
|
||||||
|
|
||||||
|
This first requires a listening UNIX socket on the host side. The UNIX socket path has to be constructed by using the socket path used at the VM launch time with appended `_` and the port number to be used on the guest side. As in the example above, if we'd intended to connect from the guest to the port `1234`, the Unix socket path on the host side would be `/tmp/ch.vsock_1234`.
|
||||||
|
|
||||||
|
Also note that the CID used on the guest side is the well known CID value `2`.
|
||||||
|
|
||||||
|
Listening on the host side:
|
||||||
|
|
||||||
|
`$ socat - UNIX-LISTEN:/tmp/ch.vsock_1234`
|
||||||
|
|
||||||
|
From the guest:
|
||||||
|
|
||||||
|
`$ echo -e "Hello from guest!" | socat - VSOCK-CONNECT:2:1234`
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
- [virtio-vsock in QEMU, Firecracker and Linux: Status, Performance and Challenges](https://kvmforum2019.sched.com/event/TmwK)
|
||||||
|
- [Leveraging virtio-vsock in the cloud and containers](https://archive.fosdem.org/2021/schedule/event/vai_virtio_vsock/)
|
||||||
|
- [VSOCK man page](https://manpages.ubuntu.com/manpages/focal/man7/vsock.7.html)
|
||||||
|
- [https://stefano-garzarella.github.io/posts/2020-02-20-vsock-nested-vms-loopback/](https://stefano-garzarella.github.io/posts/2020-02-20-vsock-nested-vms-loopback/)
|
||||||
|
- [https://github.com/firecracker-microvm/firecracker/blob/main/docs/vsock.md](https://github.com/firecracker-microvm/firecracker/blob/main/docs/vsock.md)
|
||||||
|
|
||||||
@@ -5,7 +5,6 @@ authors = ["The Cloud Hypervisor Authors"]
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "0.2.123"
|
libc = "0.2.133"
|
||||||
serde = { version = "1.0.136", features = ["rc"] }
|
serde = { version = "1.0.145", features = ["rc", "derive"] }
|
||||||
serde_derive = "1.0.136"
|
serde_json = "1.0.85"
|
||||||
serde_json = "1.0.79"
|
|
||||||
|
|||||||
@@ -3,12 +3,11 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
#[macro_use]
|
use serde::Serialize;
|
||||||
extern crate serde_derive;
|
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
@@ -50,6 +49,9 @@ pub fn event_log(source: &str, event: &str, properties: Option<&HashMap<Cow<str>
|
|||||||
properties,
|
properties,
|
||||||
};
|
};
|
||||||
serde_json::to_writer_pretty(file, &e).ok();
|
serde_json::to_writer_pretty(file, &e).ok();
|
||||||
|
|
||||||
|
let mut file = file;
|
||||||
|
file.write_all(b"\n\n").ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
466
fuzz/Cargo.lock
generated
466
fuzz/Cargo.lock
generated
@@ -11,9 +11,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.56"
|
version = "1.0.65"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
|
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "api_client"
|
name = "api_client"
|
||||||
@@ -24,21 +24,20 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arbitrary"
|
name = "arbitrary"
|
||||||
version = "1.1.0"
|
version = "1.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c38b6b6b79f671c25e1a3e785b7b82d7562ffc9cd3efdc98627e5668a2472490"
|
checksum = "f44124848854b941eafdb34f05b3bcf59472f643c7e151eba7c2b69daa469ed5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arc-swap"
|
name = "arc-swap"
|
||||||
version = "1.5.0"
|
version = "1.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f"
|
checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arch"
|
name = "arch"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"acpi_tables",
|
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"fdt",
|
"fdt",
|
||||||
@@ -47,8 +46,8 @@ dependencies = [
|
|||||||
"linux-loader",
|
"linux-loader",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"uuid",
|
||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
"vm-fdt",
|
"vm-fdt",
|
||||||
@@ -119,6 +118,9 @@ name = "cc"
|
|||||||
version = "1.0.73"
|
version = "1.0.73"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||||
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@@ -128,24 +130,33 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "3.1.8"
|
version = "3.2.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c"
|
checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
"clap_lex",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"lazy_static",
|
"once_cell",
|
||||||
"os_str_bytes",
|
|
||||||
"strsim",
|
"strsim",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
"terminal_size",
|
"terminal_size",
|
||||||
"textwrap",
|
"textwrap",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||||
|
dependencies = [
|
||||||
|
"os_str_bytes",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cloud-hypervisor"
|
name = "cloud-hypervisor"
|
||||||
version = "22.0.0"
|
version = "26.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"api_client",
|
"api_client",
|
||||||
@@ -160,6 +171,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tracer",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vmm",
|
"vmm",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
@@ -171,15 +183,21 @@ version = "0.0.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"block_util",
|
"block_util",
|
||||||
"cloud-hypervisor",
|
"cloud-hypervisor",
|
||||||
|
"devices",
|
||||||
|
"epoll",
|
||||||
"libc",
|
"libc",
|
||||||
"libfuzzer-sys",
|
"libfuzzer-sys",
|
||||||
|
"micro_http",
|
||||||
|
"once_cell",
|
||||||
"qcow",
|
"qcow",
|
||||||
"seccompiler",
|
"seccompiler",
|
||||||
"vhdx",
|
"vhdx",
|
||||||
"virtio-devices",
|
"virtio-devices",
|
||||||
"virtio-queue",
|
"virtio-queue",
|
||||||
|
"vm-device",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vm-virtio",
|
"vm-virtio",
|
||||||
|
"vmm",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -198,6 +216,41 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55626594feae15d266d52440b26ff77de0e22230cf0c113abe619084c1ddc910"
|
checksum = "55626594feae15d266d52440b26ff77de0e22230cf0c113abe619084c1ddc910"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.14.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"darling_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.14.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.14.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "devices"
|
name = "devices"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -207,7 +260,7 @@ dependencies = [
|
|||||||
"arch",
|
"arch",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"epoll",
|
"hypervisor",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"versionize",
|
"versionize",
|
||||||
@@ -228,51 +281,53 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
|
||||||
|
dependencies = [
|
||||||
|
"errno-dragonfly",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno-dragonfly"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "event_monitor"
|
name = "event_monitor"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
|
||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fdt"
|
name = "fdt"
|
||||||
version = "0.1.3"
|
version = "0.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b643857cf70949306b81d7e92cb9d47add673868edac9863c4a49c42feaf3f1e"
|
checksum = "964f5becd44d069dca0beea2b4bc05639ae7bf3b3f5369c295aff360bb57cca2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gdbstub"
|
name = "fnv"
|
||||||
version = "0.6.1"
|
version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fa2ca5d6b045de372cef3991f873389b421b4cabcfbe52f7787fae8b8b37906"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"cfg-if",
|
|
||||||
"log",
|
|
||||||
"managed",
|
|
||||||
"num-traits",
|
|
||||||
"paste",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gdbstub_arch"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "51dc4b5718ac76d21e8605c0966dd32d80273b89b11e6cfef467b04e45934d37"
|
|
||||||
dependencies = [
|
|
||||||
"gdbstub",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.6"
|
version = "0.2.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -281,9 +336,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.11.2"
|
version = "0.12.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
@@ -299,16 +354,16 @@ name = "hypervisor"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"epoll",
|
"byteorder",
|
||||||
"iced-x86",
|
"iced-x86",
|
||||||
"kvm-bindings",
|
"kvm-bindings",
|
||||||
"kvm-ioctls",
|
"kvm-ioctls",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_with",
|
||||||
"serde_json",
|
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"vfio-ioctls",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
@@ -324,20 +379,32 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "ident_case"
|
||||||
version = "1.8.1"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "1.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-uring"
|
name = "io-lifetimes"
|
||||||
version = "0.5.2"
|
version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8d75829ed9377bab6c90039fe47b9d84caceb4b5063266142e21bcce6550cda8"
|
checksum = "1ea37f355c05dde75b84bba2d767906ad522e97cd9e2eef2be7a4ab7fb442c06"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "io-uring"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a645fd8e7048c2f54050f7d0d4de8f624ada437e043bc7d72609bd08d70120b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -345,9 +412,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
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 = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kvm-bindings"
|
name = "kvm-bindings"
|
||||||
@@ -377,15 +453,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.123"
|
version = "0.2.133"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd"
|
checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libfuzzer-sys"
|
name = "libfuzzer-sys"
|
||||||
version = "0.4.3"
|
version = "0.4.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "336244aaeab6a12df46480dc585802aa743a72d66b11937844c61bbca84c991d"
|
checksum = "ae185684fe19814afd066da15a7cc41e126886c21282934225d9fc847582da58"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arbitrary",
|
"arbitrary",
|
||||||
"cc",
|
"cc",
|
||||||
@@ -394,38 +470,32 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-loader"
|
name = "linux-loader"
|
||||||
version = "0.4.0"
|
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 = "8a5e77493808403a6bd56a301a64ea6b9342e36ea845044bf0dfdf56fe52fa08"
|
checksum = "62a2f912deca034ec34b0a43a390059ea98daac40e440ebe8bea88f3315fe168"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.16"
|
version = "0.0.46"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
|
checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "managed"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memchr"
|
|
||||||
version = "2.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
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#9517a300370a158a7af0996b7eebf040d171e1a4"
|
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#863b0370ba7e57f7df5b908ada9e5b44809ccae9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
@@ -449,6 +519,7 @@ dependencies = [
|
|||||||
"net_gen",
|
"net_gen",
|
||||||
"rate_limiter",
|
"rate_limiter",
|
||||||
"serde",
|
"serde",
|
||||||
|
"thiserror",
|
||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
@@ -458,20 +529,11 @@ dependencies = [
|
|||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-traits"
|
|
||||||
version = "0.2.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.10.0"
|
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 = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
|
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "option_parser"
|
name = "option_parser"
|
||||||
@@ -479,18 +541,9 @@ version = "0.1.0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
version = "6.0.0"
|
version = "6.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
|
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "paste"
|
|
||||||
version = "1.0.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pci"
|
name = "pci"
|
||||||
@@ -502,7 +555,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
@@ -518,11 +570,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.37"
|
version = "1.0.45"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
|
checksum = "3edcd08cf4fea98d1ae6c9ddd3b8ccb1acac7c3693d62625969a7daa04a2ae36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -538,9 +590,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.18"
|
version = "1.0.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
@@ -556,9 +608,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "remain"
|
name = "remain"
|
||||||
version = "0.2.2"
|
version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "70ba1e78fa68412cb93ef642fd4d20b9a941be49ee9333875ebaf13112673ea7"
|
checksum = "06df529c0d271b27ac4a2c260f5ce2914b60bd44702cecec7b9f271adbdde23b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -575,37 +627,54 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "rustix"
|
||||||
version = "1.0.9"
|
version = "0.35.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
checksum = "fbb2fda4666def1433b1b05431ab402e42a1084285477222b72d6c564c417cef"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"io-lifetimes",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "seccompiler"
|
name = "seccompiler"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e01d1292a1131b22ccea49f30bd106f1238b5ddeec1a98d39268dcc31d540e68"
|
checksum = "6f6575e3c2b3a0fe2ef3e53855b6a8dead7c29f783da5e123d378c8c6a89017e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.7"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4"
|
checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.136"
|
version = "1.0.145"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
|
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.136"
|
version = "1.0.145"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
|
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -614,9 +683,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.79"
|
version = "1.0.85"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
|
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
@@ -624,10 +693,36 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook"
|
name = "serde_with"
|
||||||
version = "0.3.13"
|
version = "2.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d"
|
checksum = "368f2d60d049ea019a84dcd6687b0d1e0030fe663ae105039bdf967ed5e6a9a7"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_with_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with_macros"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ccadfacf6cf10faad22bbadf55986bdd0856edfb5d9210aa1dcf1f516e84e93"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serial_buffer"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
@@ -656,13 +751,13 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.91"
|
version = "1.0.101"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
|
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"unicode-xid",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -676,37 +771,37 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "terminal_size"
|
name = "terminal_size"
|
||||||
version = "0.1.17"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
|
checksum = "8440c860cf79def6164e4a0a983bcc2305d82419177a0e0c71930d049e3ac5a1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"rustix",
|
||||||
"winapi",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "textwrap"
|
name = "textwrap"
|
||||||
version = "0.15.0"
|
version = "0.15.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"terminal_size",
|
"terminal_size",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.30"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.30"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -714,16 +809,27 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "tracer"
|
||||||
version = "0.2.2"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "0.8.2"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
@@ -767,7 +873,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?branch=main#4630612f2ff300e78b6d55be324fb5481aa84661"
|
source = "git+https://github.com/rust-vmm/vfio?branch=main#bf7c9b997b4ad0ca1ab4bfe96dc1b10d4bc5be24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"kvm-bindings",
|
"kvm-bindings",
|
||||||
@@ -784,7 +890,6 @@ dependencies = [
|
|||||||
name = "vfio_user"
|
name = "vfio_user"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -807,14 +912,13 @@ dependencies = [
|
|||||||
"remain",
|
"remain",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"uuid",
|
"uuid",
|
||||||
"vmm-sys-util",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vhost"
|
name = "vhost"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "53567fd9ab820e4f3cc156f24146882fee3c365194c3e1dea74723265f27fc88"
|
checksum = "79243657c76e5c90dcbf60187c842614f6dfc7123972c55bb3bcc446792aca93"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -847,8 +951,8 @@ dependencies = [
|
|||||||
"rate_limiter",
|
"rate_limiter",
|
||||||
"seccompiler",
|
"seccompiler",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serial_buffer",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
@@ -865,11 +969,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "virtio-queue"
|
name = "virtio-queue"
|
||||||
version = "0.2.0"
|
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 = "3785325315e6496fa88673842ee6cd198b9658e88e8b0e1ad48a5dc818b221dc"
|
checksum = "6e2b7bea73248006630c9611a61ac3b8e44eece09342801fe1faec583b7a9f02"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
|
"virtio-bindings",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
@@ -888,9 +993,8 @@ name = "vm-device"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"hypervisor",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
|
||||||
"serde_json",
|
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"vfio-ioctls",
|
"vfio-ioctls",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
@@ -904,9 +1008,9 @@ source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#720e48e435b791ec6cb
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vm-memory"
|
name = "vm-memory"
|
||||||
version = "0.7.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 = "339d4349c126fdcd87e034631d7274370cf19eb0e87b33166bcd956589fc72c5"
|
checksum = "583f213899e8a5eea23d9c507252d4bed5bc88f0ecbe0783262f80034630744b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -919,7 +1023,6 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"versionize",
|
"versionize",
|
||||||
@@ -932,7 +1035,6 @@ name = "vm-virtio"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"virtio-bindings",
|
|
||||||
"virtio-queue",
|
"virtio-queue",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
]
|
]
|
||||||
@@ -951,24 +1053,23 @@ dependencies = [
|
|||||||
"devices",
|
"devices",
|
||||||
"epoll",
|
"epoll",
|
||||||
"event_monitor",
|
"event_monitor",
|
||||||
"gdbstub",
|
|
||||||
"gdbstub_arch",
|
|
||||||
"hypervisor",
|
"hypervisor",
|
||||||
"lazy_static",
|
|
||||||
"libc",
|
"libc",
|
||||||
"linux-loader",
|
"linux-loader",
|
||||||
"log",
|
"log",
|
||||||
"micro_http",
|
"micro_http",
|
||||||
"net_util",
|
"net_util",
|
||||||
|
"once_cell",
|
||||||
"option_parser",
|
"option_parser",
|
||||||
"pci",
|
"pci",
|
||||||
"qcow",
|
"qcow",
|
||||||
"seccompiler",
|
"seccompiler",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serial_buffer",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tracer",
|
||||||
"uuid",
|
"uuid",
|
||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
@@ -987,9 +1088,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vmm-sys-util"
|
name = "vmm-sys-util"
|
||||||
version = "0.9.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "733537bded03aaa93543f785ae997727b30d1d9f4a03b7861d23290474242e11"
|
checksum = "08604d7be03eb26e33b3cee3ed4aef2bf550b305d1cca60e84da5d28d3790b62"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -999,9 +1100,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.10.2+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
@@ -1033,3 +1134,46 @@ name = "winapi-x86_64-pc-windows-gnu"
|
|||||||
version = "0.4.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 = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||||
|
|||||||
@@ -10,16 +10,22 @@ cargo-fuzz = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
block_util = { path = "../block_util" }
|
block_util = { path = "../block_util" }
|
||||||
libc = "0.2.123"
|
devices = { path = "../devices" }
|
||||||
libfuzzer-sys = "0.4.3"
|
epoll = "4.3.1"
|
||||||
|
libc = "0.2.133"
|
||||||
|
libfuzzer-sys = "0.4.4"
|
||||||
|
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||||
|
once_cell = "1.15.0"
|
||||||
qcow = { path = "../qcow" }
|
qcow = { path = "../qcow" }
|
||||||
seccompiler = "0.2.0"
|
seccompiler = "0.3.0"
|
||||||
vhdx = { path = "../vhdx" }
|
vhdx = { path = "../vhdx" }
|
||||||
virtio-devices = { path = "../virtio-devices" }
|
virtio-devices = { path = "../virtio-devices" }
|
||||||
virtio-queue = "0.2.0"
|
virtio-queue = "0.6.1"
|
||||||
vmm-sys-util = "0.9.0"
|
vmm = { path = "../vmm" }
|
||||||
|
vmm-sys-util = "0.10.0"
|
||||||
|
vm-memory = "0.9.0"
|
||||||
|
vm-device = { path = "../vm-device" }
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
vm-memory = "0.7.0"
|
|
||||||
|
|
||||||
[dependencies.cloud-hypervisor]
|
[dependencies.cloud-hypervisor]
|
||||||
path = ".."
|
path = ".."
|
||||||
@@ -34,8 +40,8 @@ versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_deri
|
|||||||
members = ["."]
|
members = ["."]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "qcow"
|
name = "balloon"
|
||||||
path = "fuzz_targets/qcow.rs"
|
path = "fuzz_targets/balloon.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
doc = false
|
||||||
|
|
||||||
@@ -45,8 +51,50 @@ path = "fuzz_targets/block.rs"
|
|||||||
test = false
|
test = false
|
||||||
doc = false
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "cmos"
|
||||||
|
path = "fuzz_targets/cmos.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "http_api"
|
||||||
|
path = "fuzz_targets/http_api.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "pmem"
|
||||||
|
path = "fuzz_targets/pmem.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "qcow"
|
||||||
|
path = "fuzz_targets/qcow.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "rng"
|
||||||
|
path = "fuzz_targets/rng.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "serial"
|
||||||
|
path = "fuzz_targets/serial.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "vhdx"
|
name = "vhdx"
|
||||||
path = "fuzz_targets/vhdx.rs"
|
path = "fuzz_targets/vhdx.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "watchdog"
|
||||||
|
path = "fuzz_targets/watchdog.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|||||||
152
fuzz/fuzz_targets/balloon.rs
Normal file
152
fuzz/fuzz_targets/balloon.rs
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
// Copyright © 2022 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use seccompiler::SeccompAction;
|
||||||
|
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||||
|
use virtio_queue::{Queue, QueueT};
|
||||||
|
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||||
|
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||||
|
|
||||||
|
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||||
|
|
||||||
|
const QUEUE_DATA_SIZE: usize = 4;
|
||||||
|
const MEM_SIZE: usize = 1 * 1024 * 1024;
|
||||||
|
const BALLOON_SIZE: u64 = 512 * 1024;
|
||||||
|
// Number of queues
|
||||||
|
const QUEUE_NUM: usize = 3;
|
||||||
|
// Max entries in the queue.
|
||||||
|
const QUEUE_SIZE: u16 = 256;
|
||||||
|
// Descriptor table alignment
|
||||||
|
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||||
|
// Avalable ring alignment
|
||||||
|
const AVAIL_RING_ALIGN_SIZE: u64 = 2;
|
||||||
|
// Used ring alignment
|
||||||
|
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||||
|
// Descriptor table size
|
||||||
|
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
|
||||||
|
// Available ring size
|
||||||
|
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
||||||
|
// Used ring size
|
||||||
|
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
|
||||||
|
|
||||||
|
fuzz_target!(|bytes| {
|
||||||
|
if bytes.len() < QUEUE_DATA_SIZE * QUEUE_NUM
|
||||||
|
|| bytes.len() > (QUEUE_DATA_SIZE * QUEUE_NUM + MEM_SIZE)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut balloon = virtio_devices::Balloon::new(
|
||||||
|
"fuzzer_balloon".to_owned(),
|
||||||
|
BALLOON_SIZE,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
SeccompAction::Allow,
|
||||||
|
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let queue_data = &bytes[..QUEUE_DATA_SIZE * QUEUE_NUM];
|
||||||
|
let mem_bytes = &bytes[QUEUE_DATA_SIZE * QUEUE_NUM..];
|
||||||
|
|
||||||
|
// Setup the guest memory with the input bytes
|
||||||
|
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
|
||||||
|
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||||
|
|
||||||
|
// Setup the virt queues with the input bytes
|
||||||
|
let mut queues = setup_virt_queues(
|
||||||
|
&[
|
||||||
|
&queue_data[..QUEUE_DATA_SIZE].try_into().unwrap(),
|
||||||
|
&queue_data[QUEUE_DATA_SIZE..QUEUE_DATA_SIZE * 2]
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
&queue_data[QUEUE_DATA_SIZE * 2..QUEUE_DATA_SIZE * 3]
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
],
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let inflate_q = queues.remove(0);
|
||||||
|
let inflate_evt = EventFd::new(0).unwrap();
|
||||||
|
let inflate_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(inflate_evt.as_raw_fd())) };
|
||||||
|
let deflate_q = queues.remove(0);
|
||||||
|
let deflate_evt = EventFd::new(0).unwrap();
|
||||||
|
let deflate_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(deflate_evt.as_raw_fd())) };
|
||||||
|
let reporting_q = queues.remove(0);
|
||||||
|
let reporting_evt = EventFd::new(0).unwrap();
|
||||||
|
let reporting_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(reporting_evt.as_raw_fd())) };
|
||||||
|
|
||||||
|
// Kick the 'queue' events before activate the balloon device
|
||||||
|
inflate_queue_evt.write(1).unwrap();
|
||||||
|
deflate_queue_evt.write(1).unwrap();
|
||||||
|
reporting_queue_evt.write(1).unwrap();
|
||||||
|
|
||||||
|
balloon
|
||||||
|
.activate(
|
||||||
|
guest_memory,
|
||||||
|
Arc::new(NoopVirtioInterrupt {}),
|
||||||
|
vec![
|
||||||
|
(0, inflate_q, inflate_evt),
|
||||||
|
(1, deflate_q, deflate_evt),
|
||||||
|
(2, reporting_q, reporting_evt),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
// Wait for the events to finish and balloon device worker thread to return
|
||||||
|
balloon.wait_for_epoll_threads();
|
||||||
|
});
|
||||||
|
|
||||||
|
pub struct NoopVirtioInterrupt {}
|
||||||
|
|
||||||
|
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||||
|
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! align {
|
||||||
|
($n:expr, $align:expr) => {{
|
||||||
|
(($n + $align - 1) / $align) * $align
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_virt_queues(bytes: &[&[u8; QUEUE_DATA_SIZE]], base_addr: u64) -> Vec<Queue> {
|
||||||
|
let mut queues = Vec::new();
|
||||||
|
let mut base_addr = base_addr;
|
||||||
|
for b in bytes {
|
||||||
|
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||||
|
|
||||||
|
let desc_table_addr = align!(base_addr, DESC_TABLE_ALIGN_SIZE);
|
||||||
|
let avail_ring_addr = align!(desc_table_addr + DESC_TABLE_SIZE, AVAIL_RING_ALIGN_SIZE);
|
||||||
|
let used_ring_addr = align!(avail_ring_addr + AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE);
|
||||||
|
q.try_set_desc_table_address(GuestAddress(desc_table_addr))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_avail_ring_address(GuestAddress(avail_ring_addr))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_used_ring_address(GuestAddress(used_ring_addr))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
q.set_next_avail(b[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
|
||||||
|
q.set_next_used(b[1] as u16);
|
||||||
|
q.set_event_idx(b[2] % 2 != 0);
|
||||||
|
q.set_size(b[3] as u16 % QUEUE_SIZE);
|
||||||
|
|
||||||
|
q.set_ready(true);
|
||||||
|
queues.push(q);
|
||||||
|
|
||||||
|
base_addr = used_ring_addr + USED_RING_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
queues
|
||||||
|
}
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
// Copyright 2018 The Chromium OS Authors. All rights reserved.
|
// Copyright 2018 The Chromium OS Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
//
|
||||||
|
// Copyright © 2022 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
@@ -9,89 +13,42 @@ use libfuzzer_sys::fuzz_target;
|
|||||||
use seccompiler::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;
|
||||||
use std::mem::size_of;
|
|
||||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use virtio_devices::{Block, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
use virtio_devices::{Block, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||||
use virtio_queue::{Queue, QueueState};
|
use virtio_queue::{Queue, QueueT};
|
||||||
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||||
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||||
|
|
||||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||||
|
|
||||||
const MEM_SIZE: u64 = 256 * 1024 * 1024;
|
const QUEUE_DATA_SIZE: usize = 4;
|
||||||
const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor.
|
const MEM_SIZE: usize = 256 * 1024 * 1024;
|
||||||
const QUEUE_SIZE: u16 = 16; // Max entries in the queue.
|
// Max entries in the queue.
|
||||||
const CMD_SIZE: usize = 16; // Bytes in the command.
|
const QUEUE_SIZE: u16 = 256;
|
||||||
|
// Guest physical address for descriptor table.
|
||||||
|
const DESC_TABLE_ADDR: u64 = 0;
|
||||||
|
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
|
||||||
|
// Guest physical address for available ring
|
||||||
|
const AVAIL_RING_ADDR: u64 = DESC_TABLE_ADDR + DESC_TABLE_SIZE;
|
||||||
|
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
||||||
|
// Guest physical address for used ring (requires to 4-bytes aligned)
|
||||||
|
const USED_RING_ADDR: u64 = (AVAIL_RING_ADDR + AVAIL_RING_SIZE + 3) & !3_u64;
|
||||||
|
|
||||||
fuzz_target!(|bytes| {
|
fuzz_target!(|bytes| {
|
||||||
let size_u64 = size_of::<u64>();
|
if bytes.len() < QUEUE_DATA_SIZE || bytes.len() > (QUEUE_DATA_SIZE + MEM_SIZE) {
|
||||||
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE as usize)]).unwrap();
|
|
||||||
|
|
||||||
// The fuzz data is interpreted as:
|
|
||||||
// starting index 8 bytes
|
|
||||||
// command location 8 bytes
|
|
||||||
// command 16 bytes
|
|
||||||
// descriptors circular buffer 16 bytes * 3
|
|
||||||
if bytes.len() < 4 * size_u64 {
|
|
||||||
// Need an index to start.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut data_image = Cursor::new(bytes);
|
let queue_data = &bytes[..QUEUE_DATA_SIZE];
|
||||||
|
let mem_bytes = &bytes[QUEUE_DATA_SIZE..];
|
||||||
let first_index = read_u64(&mut data_image);
|
|
||||||
if first_index > MEM_SIZE / DESC_SIZE {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let first_offset = first_index * DESC_SIZE;
|
|
||||||
if first_offset as usize + size_u64 > bytes.len() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let command_addr = read_u64(&mut data_image);
|
|
||||||
if command_addr > MEM_SIZE - CMD_SIZE as u64 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if mem
|
|
||||||
.write_slice(
|
|
||||||
&bytes[2 * size_u64..(2 * size_u64) + CMD_SIZE],
|
|
||||||
GuestAddress(command_addr as u64),
|
|
||||||
)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
data_image.seek(SeekFrom::Start(first_offset)).unwrap();
|
|
||||||
let desc_table = read_u64(&mut data_image);
|
|
||||||
|
|
||||||
if mem
|
|
||||||
.write_slice(&bytes[32..], GuestAddress(desc_table as u64))
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let guest_memory = GuestMemoryAtomic::new(mem);
|
|
||||||
|
|
||||||
let mut q = Queue::<
|
|
||||||
GuestMemoryAtomic<GuestMemoryMmap>,
|
|
||||||
QueueState,
|
|
||||||
>::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_fd = queue_evts[0].as_raw_fd();
|
|
||||||
let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(queue_fd)) };
|
|
||||||
|
|
||||||
|
// Create a virtio-block device backed by a synchronous raw file
|
||||||
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(RawFileDiskSync::new(disk_file)) 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(),
|
||||||
qcow_disk,
|
qcow_disk,
|
||||||
@@ -106,24 +63,34 @@ fuzz_target!(|bytes| {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
// Setup the virt queue with the input bytes
|
||||||
|
let q = setup_virt_queue(queue_data.try_into().unwrap());
|
||||||
|
|
||||||
|
// Setup the guest memory with the input bytes
|
||||||
|
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
|
||||||
|
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||||
|
|
||||||
|
let evt = EventFd::new(0).unwrap();
|
||||||
|
let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) };
|
||||||
|
|
||||||
|
// Kick the 'queue' event before activate the block device
|
||||||
|
queue_evt.write(1).unwrap();
|
||||||
|
|
||||||
block
|
block
|
||||||
.activate(
|
.activate(
|
||||||
guest_memory,
|
guest_memory,
|
||||||
Arc::new(NoopVirtioInterrupt {}),
|
Arc::new(NoopVirtioInterrupt {}),
|
||||||
vec![q],
|
vec![(0, q, evt)],
|
||||||
queue_evts,
|
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
queue_evt.write(77).unwrap(); // Rings the doorbell, any byte will do.
|
// Wait for the events to finish and block device worker thread to return
|
||||||
|
block.wait_for_epoll_threads();
|
||||||
});
|
});
|
||||||
|
|
||||||
fn read_u64<T: Read>(readable: &mut T) -> u64 {
|
|
||||||
let mut buf = [0u8; size_of::<u64>()];
|
|
||||||
readable.read_exact(&mut buf[..]).unwrap();
|
|
||||||
u64::from_le_bytes(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> {
|
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) };
|
let res = unsafe { libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags) };
|
||||||
|
|
||||||
@@ -137,10 +104,25 @@ fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> {
|
|||||||
pub struct NoopVirtioInterrupt {}
|
pub struct NoopVirtioInterrupt {}
|
||||||
|
|
||||||
impl VirtioInterrupt for NoopVirtioInterrupt {
|
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||||
fn trigger(
|
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
|
||||||
&self,
|
|
||||||
_int_type: VirtioInterruptType,
|
|
||||||
) -> std::result::Result<(), std::io::Error> {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue {
|
||||||
|
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||||
|
q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
|
||||||
|
q.set_next_used(bytes[1] as u16);
|
||||||
|
q.set_event_idx(bytes[2] % 2 != 0);
|
||||||
|
q.set_size(bytes[3] as u16 % QUEUE_SIZE);
|
||||||
|
|
||||||
|
q.try_set_desc_table_address(GuestAddress(DESC_TABLE_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_avail_ring_address(GuestAddress(AVAIL_RING_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_used_ring_address(GuestAddress(USED_RING_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.set_ready(true);
|
||||||
|
|
||||||
|
q
|
||||||
|
}
|
||||||
|
|||||||
48
fuzz/fuzz_targets/cmos.rs
Normal file
48
fuzz/fuzz_targets/cmos.rs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// Copyright © 2022 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
use devices::legacy::Cmos;
|
||||||
|
use libc::EFD_NONBLOCK;
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use vm_device::BusDevice;
|
||||||
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
fuzz_target!(|bytes| {
|
||||||
|
// Need at least 16 bytes for the test
|
||||||
|
if bytes.len() < 16 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut below_4g = [0u8; 8];
|
||||||
|
let mut above_4g = [0u8; 8];
|
||||||
|
|
||||||
|
below_4g.copy_from_slice(&bytes[0..8]);
|
||||||
|
above_4g.copy_from_slice(&bytes[8..16]);
|
||||||
|
|
||||||
|
let mut cmos = Cmos::new(
|
||||||
|
u64::from_le_bytes(below_4g),
|
||||||
|
u64::from_le_bytes(above_4g),
|
||||||
|
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut i = 16;
|
||||||
|
while i < bytes.len() {
|
||||||
|
let read = bytes.get(i).unwrap_or(&0) % 2 == 0;
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
if read {
|
||||||
|
let offset = (bytes.get(i).unwrap_or(&0) % 2) as u64;
|
||||||
|
i += 1;
|
||||||
|
let mut out_bytes = vec![0];
|
||||||
|
cmos.read(0, offset, &mut out_bytes);
|
||||||
|
} else {
|
||||||
|
let offset = (bytes.get(i).unwrap_or(&0) % 2) as u64;
|
||||||
|
i += 1;
|
||||||
|
let data = vec![*bytes.get(i).unwrap_or(&0)];
|
||||||
|
i += 1;
|
||||||
|
cmos.write(0, offset, &data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
191
fuzz/fuzz_targets/http_api.rs
Normal file
191
fuzz/fuzz_targets/http_api.rs
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
// Copyright © 2022 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use micro_http::Request;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use std::sync::mpsc::{channel, Receiver};
|
||||||
|
use std::thread;
|
||||||
|
use vmm::api::{http::*, ApiRequest, ApiResponsePayload};
|
||||||
|
use vmm::{EpollContext, EpollDispatch};
|
||||||
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
// Need to be ordered for test case reproducibility
|
||||||
|
static ROUTES: Lazy<Vec<&Box<dyn EndpointHandler + Sync + Send>>> =
|
||||||
|
Lazy::new(|| HTTP_ROUTES.routes.values().collect());
|
||||||
|
|
||||||
|
fuzz_target!(|bytes| {
|
||||||
|
if bytes.len() < 2 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let route = ROUTES[bytes[0] as usize % ROUTES.len()];
|
||||||
|
if let Some(request) = generate_request(&bytes[1..]) {
|
||||||
|
let exit_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
|
||||||
|
let api_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
|
||||||
|
let (api_sender, api_receiver) = channel();
|
||||||
|
|
||||||
|
let http_receiver_thread = {
|
||||||
|
let exit_evt = exit_evt.try_clone().unwrap();
|
||||||
|
let api_evt = api_evt.try_clone().unwrap();
|
||||||
|
thread::Builder::new()
|
||||||
|
.name("http_receiver".to_string())
|
||||||
|
.spawn(move || {
|
||||||
|
http_receiver_stub(exit_evt, api_evt, api_receiver);
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
route.handle_request(&request, api_evt, api_sender);
|
||||||
|
exit_evt.write(1).ok();
|
||||||
|
http_receiver_thread.join().unwrap();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
fn generate_request(bytes: &[u8]) -> Option<Request> {
|
||||||
|
let req_method = match bytes[0] % 5 {
|
||||||
|
0 => "GET",
|
||||||
|
1 => "PUT",
|
||||||
|
2 => "PATCH",
|
||||||
|
3 => "POST",
|
||||||
|
_ => "INVALID",
|
||||||
|
};
|
||||||
|
let request_line = format!("{} http://localhost/home HTTP/1.1\r\n", req_method);
|
||||||
|
|
||||||
|
let req_body = &bytes[1..];
|
||||||
|
let request = if req_body.len() > 0 {
|
||||||
|
[
|
||||||
|
format!("{}Content-Length: {}\r\n", request_line, req_body.len()).as_bytes(),
|
||||||
|
req_body,
|
||||||
|
]
|
||||||
|
.concat()
|
||||||
|
} else {
|
||||||
|
format!("{}\r\n", request_line).as_bytes().to_vec()
|
||||||
|
};
|
||||||
|
|
||||||
|
Request::try_from(&request, None).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn http_receiver_stub(exit_evt: EventFd, api_evt: EventFd, api_receiver: Receiver<ApiRequest>) {
|
||||||
|
let mut epoll = EpollContext::new().unwrap();
|
||||||
|
epoll.add_event(&exit_evt, EpollDispatch::Exit).unwrap();
|
||||||
|
epoll.add_event(&api_evt, EpollDispatch::Api).unwrap();
|
||||||
|
|
||||||
|
let epoll_fd = epoll.as_raw_fd();
|
||||||
|
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); 2];
|
||||||
|
let num_events;
|
||||||
|
loop {
|
||||||
|
num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) {
|
||||||
|
Ok(num_events) => num_events,
|
||||||
|
Err(e) => match e.raw_os_error() {
|
||||||
|
Some(libc::EAGAIN) | Some(libc::EINTR) => continue,
|
||||||
|
_ => panic!("Unexpected epoll::wait error!"),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for event in events.iter().take(num_events) {
|
||||||
|
let dispatch_event: EpollDispatch = event.data.into();
|
||||||
|
match dispatch_event {
|
||||||
|
EpollDispatch::Exit => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
EpollDispatch::Api => {
|
||||||
|
for _ in 0..api_evt.read().unwrap() {
|
||||||
|
let api_request = api_receiver.recv().unwrap();
|
||||||
|
match api_request {
|
||||||
|
ApiRequest::VmCreate(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmDelete(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmBoot(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmShutdown(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmReboot(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmInfo(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmmPing(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmPause(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmResume(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmSnapshot(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmRestore(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmmShutdown(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmResize(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmResizeZone(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmAddDevice(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmAddUserDevice(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmRemoveDevice(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmAddDisk(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmAddFs(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmAddPmem(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmAddNet(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmAddVdpa(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmAddVsock(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmCounters(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmReceiveMigration(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmSendMigration(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmPowerButton(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("Unexpected Epoll event");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
150
fuzz/fuzz_targets/pmem.rs
Normal file
150
fuzz/fuzz_targets/pmem.rs
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
// Copyright © 2022 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use libc::{MAP_NORESERVE, MAP_PRIVATE, PROT_READ, PROT_WRITE};
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use seccompiler::SeccompAction;
|
||||||
|
use std::ffi;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io;
|
||||||
|
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use virtio_devices::{Pmem, UserspaceMapping, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||||
|
use virtio_queue::{Queue, QueueT};
|
||||||
|
use vm_memory::guest_memory::FileOffset;
|
||||||
|
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic, MmapRegion};
|
||||||
|
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||||
|
|
||||||
|
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||||
|
|
||||||
|
const QUEUE_DATA_SIZE: usize = 4;
|
||||||
|
const MEM_SIZE: usize = 256 * 1024 * 1024;
|
||||||
|
const PMEM_FILE_SIZE: usize = 128 * 1024 * 1024;
|
||||||
|
// Max entries in the queue.
|
||||||
|
const QUEUE_SIZE: u16 = 256;
|
||||||
|
// Guest physical address for descriptor table.
|
||||||
|
const DESC_TABLE_ADDR: u64 = 0;
|
||||||
|
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
|
||||||
|
// Guest physical address for available ring
|
||||||
|
const AVAIL_RING_ADDR: u64 = DESC_TABLE_ADDR + DESC_TABLE_SIZE;
|
||||||
|
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
||||||
|
// Guest physical address for used ring (requires to 4-bytes aligned)
|
||||||
|
const USED_RING_ADDR: u64 = (AVAIL_RING_ADDR + AVAIL_RING_SIZE + 3) & !3_u64;
|
||||||
|
|
||||||
|
fuzz_target!(|bytes| {
|
||||||
|
if bytes.len() < QUEUE_DATA_SIZE || bytes.len() > (QUEUE_DATA_SIZE + MEM_SIZE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut pmem = create_dummy_pmem();
|
||||||
|
let queue_data = &bytes[..QUEUE_DATA_SIZE];
|
||||||
|
let mem_bytes = &bytes[QUEUE_DATA_SIZE..];
|
||||||
|
|
||||||
|
// Setup the virt queue with the input bytes
|
||||||
|
let q = setup_virt_queue(queue_data.try_into().unwrap());
|
||||||
|
|
||||||
|
// Setup the guest memory with the input bytes
|
||||||
|
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
|
||||||
|
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||||
|
|
||||||
|
let evt = EventFd::new(0).unwrap();
|
||||||
|
let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) };
|
||||||
|
|
||||||
|
// Kick the 'queue' event before activate the pmem device
|
||||||
|
queue_evt.write(1).unwrap();
|
||||||
|
|
||||||
|
pmem.activate(
|
||||||
|
guest_memory,
|
||||||
|
Arc::new(NoopVirtioInterrupt {}),
|
||||||
|
vec![(0, q, evt)],
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
// Wait for the events to finish and pmem device worker thread to return
|
||||||
|
pmem.wait_for_epoll_threads();
|
||||||
|
});
|
||||||
|
|
||||||
|
fn memfd_create_with_size(name: &ffi::CStr, flags: u32, size: usize) -> Result<RawFd, io::Error> {
|
||||||
|
let fd = unsafe { libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags) };
|
||||||
|
if fd < 0 {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = unsafe { libc::syscall(libc::SYS_ftruncate, fd, size) };
|
||||||
|
if res < 0 {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(fd as RawFd)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NoopVirtioInterrupt {}
|
||||||
|
|
||||||
|
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||||
|
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a dummy virtio-pmem device for fuzzing purpose only
|
||||||
|
fn create_dummy_pmem() -> Pmem {
|
||||||
|
let shm =
|
||||||
|
memfd_create_with_size(&ffi::CString::new("fuzz").unwrap(), 0, PMEM_FILE_SIZE).unwrap();
|
||||||
|
let file: File = unsafe { File::from_raw_fd(shm) };
|
||||||
|
|
||||||
|
// The fuzzer is focusing on the virtio-pmem code that processes guest inputs (e.g. virt queues),
|
||||||
|
// so dummy mappings (both mmap and user mapping) does the job and is faster (using smaller amount of memory).
|
||||||
|
let dummy_mapping_size = 1;
|
||||||
|
let cloned_file = file.try_clone().unwrap();
|
||||||
|
let dummy_mmap_region = MmapRegion::build(
|
||||||
|
Some(FileOffset::new(cloned_file, 0)),
|
||||||
|
dummy_mapping_size,
|
||||||
|
PROT_READ | PROT_WRITE,
|
||||||
|
MAP_NORESERVE | MAP_PRIVATE,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let guest_addr = GuestAddress(0);
|
||||||
|
let dummy_user_mapping = UserspaceMapping {
|
||||||
|
host_addr: dummy_mmap_region.as_ptr() as u64,
|
||||||
|
mem_slot: 0,
|
||||||
|
addr: guest_addr,
|
||||||
|
len: dummy_mapping_size as u64,
|
||||||
|
mergeable: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
Pmem::new(
|
||||||
|
"tmp".to_owned(),
|
||||||
|
file,
|
||||||
|
guest_addr,
|
||||||
|
dummy_user_mapping,
|
||||||
|
dummy_mmap_region,
|
||||||
|
false,
|
||||||
|
SeccompAction::Allow,
|
||||||
|
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue {
|
||||||
|
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||||
|
q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
|
||||||
|
q.set_next_used(bytes[1] as u16);
|
||||||
|
q.set_event_idx(bytes[2] % 2 != 0);
|
||||||
|
q.set_size(bytes[3] as u16 % QUEUE_SIZE);
|
||||||
|
|
||||||
|
q.try_set_desc_table_address(GuestAddress(DESC_TABLE_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_avail_ring_address(GuestAddress(AVAIL_RING_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_used_ring_address(GuestAddress(USED_RING_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.set_ready(true);
|
||||||
|
|
||||||
|
q
|
||||||
|
}
|
||||||
134
fuzz/fuzz_targets/rng.rs
Normal file
134
fuzz/fuzz_targets/rng.rs
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
// Copyright © 2022 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use seccompiler::SeccompAction;
|
||||||
|
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||||
|
use virtio_queue::{Queue, QueueT};
|
||||||
|
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||||
|
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||||
|
|
||||||
|
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||||
|
|
||||||
|
macro_rules! align {
|
||||||
|
($n:expr, $align:expr) => {{
|
||||||
|
(($n + $align - 1) / $align) * $align
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
const QUEUE_DATA_SIZE: usize = 4;
|
||||||
|
const MEM_SIZE: usize = 1 * 1024 * 1024;
|
||||||
|
|
||||||
|
// Max entries in the queue.
|
||||||
|
const QUEUE_SIZE: u16 = 256;
|
||||||
|
// Descriptor table alignment
|
||||||
|
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||||
|
// Avalable ring alignment
|
||||||
|
const AVAIL_RING_ALIGN_SIZE: u64 = 2;
|
||||||
|
// Used ring alignment
|
||||||
|
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||||
|
// Descriptor table size
|
||||||
|
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
|
||||||
|
// Available ring size
|
||||||
|
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
||||||
|
// Used ring size
|
||||||
|
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
|
||||||
|
|
||||||
|
// Guest memory gap
|
||||||
|
const GUEST_MEM_GAP: u64 = 1 * 1024 * 1024;
|
||||||
|
// Guest physical address for descriptor table.
|
||||||
|
const DESC_TABLE_ADDR: u64 = align!(MEM_SIZE as u64 + GUEST_MEM_GAP, DESC_TABLE_ALIGN_SIZE);
|
||||||
|
// Guest physical address for available ring
|
||||||
|
const AVAIL_RING_ADDR: u64 = align!(DESC_TABLE_ADDR + DESC_TABLE_SIZE, AVAIL_RING_ALIGN_SIZE);
|
||||||
|
// Guest physical address for used ring
|
||||||
|
const USED_RING_ADDR: u64 = align!(AVAIL_RING_ADDR + AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE);
|
||||||
|
// Virtio-queue size in bytes
|
||||||
|
const QUEUE_BYTES_SIZE: usize = (USED_RING_ADDR + USED_RING_SIZE - DESC_TABLE_ADDR) as usize;
|
||||||
|
|
||||||
|
fuzz_target!(|bytes| {
|
||||||
|
if bytes.len() < (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE)
|
||||||
|
|| bytes.len() > (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE + MEM_SIZE)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut rng = virtio_devices::Rng::new(
|
||||||
|
"fuzzer_rng".to_owned(),
|
||||||
|
"/dev/urandom",
|
||||||
|
false,
|
||||||
|
SeccompAction::Allow,
|
||||||
|
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let queue_data = &bytes[..QUEUE_DATA_SIZE];
|
||||||
|
let queue_bytes = &bytes[QUEUE_DATA_SIZE..QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE];
|
||||||
|
let mem_bytes = &bytes[QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE..];
|
||||||
|
|
||||||
|
// Setup the virt queue with the input bytes
|
||||||
|
let q = setup_virt_queue(queue_data.try_into().unwrap());
|
||||||
|
|
||||||
|
// Setup the guest memory with the input bytes
|
||||||
|
let mem = GuestMemoryMmap::from_ranges(&[
|
||||||
|
(GuestAddress(0), MEM_SIZE),
|
||||||
|
(GuestAddress(DESC_TABLE_ADDR), QUEUE_BYTES_SIZE),
|
||||||
|
])
|
||||||
|
.unwrap();
|
||||||
|
if mem
|
||||||
|
.write_slice(queue_bytes, GuestAddress(DESC_TABLE_ADDR))
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||||
|
|
||||||
|
let evt = EventFd::new(0).unwrap();
|
||||||
|
let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) };
|
||||||
|
|
||||||
|
// Kick the 'queue' event before activate the rng device
|
||||||
|
queue_evt.write(1).unwrap();
|
||||||
|
|
||||||
|
rng.activate(
|
||||||
|
guest_memory,
|
||||||
|
Arc::new(NoopVirtioInterrupt {}),
|
||||||
|
vec![(0, q, evt)],
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
// Wait for the events to finish and rng device worker thread to return
|
||||||
|
rng.wait_for_epoll_threads();
|
||||||
|
});
|
||||||
|
|
||||||
|
pub struct NoopVirtioInterrupt {}
|
||||||
|
|
||||||
|
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||||
|
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue {
|
||||||
|
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||||
|
q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
|
||||||
|
q.set_next_used(bytes[1] as u16);
|
||||||
|
q.set_event_idx(bytes[2] % 2 != 0);
|
||||||
|
q.set_size(bytes[3] as u16 % QUEUE_SIZE);
|
||||||
|
|
||||||
|
q.try_set_desc_table_address(GuestAddress(DESC_TABLE_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_avail_ring_address(GuestAddress(AVAIL_RING_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_used_ring_address(GuestAddress(USED_RING_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.set_ready(true);
|
||||||
|
|
||||||
|
q
|
||||||
|
}
|
||||||
73
fuzz/fuzz_targets/serial.rs
Normal file
73
fuzz/fuzz_targets/serial.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
// Copyright © 2022 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
use devices::legacy::Serial;
|
||||||
|
use libc::EFD_NONBLOCK;
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig, InterruptSourceGroup};
|
||||||
|
use vm_device::BusDevice;
|
||||||
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
fuzz_target!(|bytes| {
|
||||||
|
let mut serial = Serial::new_sink(
|
||||||
|
"serial".into(),
|
||||||
|
Arc::new(TestInterrupt::new(EventFd::new(EFD_NONBLOCK).unwrap())),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i < bytes.len() {
|
||||||
|
let choice = bytes.get(i).unwrap_or(&0) % 3;
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
match choice {
|
||||||
|
0 => {
|
||||||
|
let offset = (bytes.get(i).unwrap_or(&0) % 8) as u64;
|
||||||
|
i += 1;
|
||||||
|
let mut out_bytes = vec![0];
|
||||||
|
serial.read(0, offset, &mut out_bytes);
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
let offset = (bytes.get(i).unwrap_or(&0) % 8) as u64;
|
||||||
|
i += 1;
|
||||||
|
let data = vec![*bytes.get(i).unwrap_or(&0)];
|
||||||
|
i += 1;
|
||||||
|
serial.write(0, offset, &data);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let data = vec![*bytes.get(i).unwrap_or(&0)];
|
||||||
|
i += 1;
|
||||||
|
serial.queue_input_bytes(&data).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
struct TestInterrupt {
|
||||||
|
event_fd: EventFd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterruptSourceGroup for TestInterrupt {
|
||||||
|
fn trigger(&self, _index: InterruptIndex) -> Result<(), std::io::Error> {
|
||||||
|
self.event_fd.write(1)
|
||||||
|
}
|
||||||
|
fn update(
|
||||||
|
&self,
|
||||||
|
_index: InterruptIndex,
|
||||||
|
_config: InterruptSourceConfig,
|
||||||
|
_masked: bool,
|
||||||
|
) -> Result<(), std::io::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
|
||||||
|
Some(self.event_fd.try_clone().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestInterrupt {
|
||||||
|
fn new(event_fd: EventFd) -> Self {
|
||||||
|
TestInterrupt { event_fd }
|
||||||
|
}
|
||||||
|
}
|
||||||
99
fuzz/fuzz_targets/watchdog.rs
Normal file
99
fuzz/fuzz_targets/watchdog.rs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
// Copyright © 2022 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use seccompiler::SeccompAction;
|
||||||
|
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||||
|
use virtio_queue::{Queue, QueueT};
|
||||||
|
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||||
|
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||||
|
|
||||||
|
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||||
|
|
||||||
|
const QUEUE_DATA_SIZE: usize = 4;
|
||||||
|
const MEM_SIZE: usize = 256 * 1024 * 1024;
|
||||||
|
// Max entries in the queue.
|
||||||
|
const QUEUE_SIZE: u16 = 256;
|
||||||
|
// Guest physical address for descriptor table.
|
||||||
|
const DESC_TABLE_ADDR: u64 = 0;
|
||||||
|
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
|
||||||
|
// Guest physical address for available ring
|
||||||
|
const AVAIL_RING_ADDR: u64 = DESC_TABLE_ADDR + DESC_TABLE_SIZE;
|
||||||
|
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
||||||
|
// Guest physical address for used ring (requires to 4-bytes aligned)
|
||||||
|
const USED_RING_ADDR: u64 = (AVAIL_RING_ADDR + AVAIL_RING_SIZE + 3) & !3_u64;
|
||||||
|
|
||||||
|
fuzz_target!(|bytes| {
|
||||||
|
if bytes.len() < QUEUE_DATA_SIZE || bytes.len() > (QUEUE_DATA_SIZE + MEM_SIZE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut watchdog = virtio_devices::Watchdog::new(
|
||||||
|
"fuzzer_watchdog".to_owned(),
|
||||||
|
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||||
|
SeccompAction::Allow,
|
||||||
|
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let queue_data = &bytes[..QUEUE_DATA_SIZE];
|
||||||
|
let mem_bytes = &bytes[QUEUE_DATA_SIZE..];
|
||||||
|
|
||||||
|
// Setup the virt queue with the input bytes
|
||||||
|
let q = setup_virt_queue(queue_data.try_into().unwrap());
|
||||||
|
|
||||||
|
// Setup the guest memory with the input bytes
|
||||||
|
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
|
||||||
|
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||||
|
|
||||||
|
let evt = EventFd::new(0).unwrap();
|
||||||
|
let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) };
|
||||||
|
|
||||||
|
// Kick the 'queue' event before activate the watchdog device
|
||||||
|
queue_evt.write(1).unwrap();
|
||||||
|
|
||||||
|
watchdog
|
||||||
|
.activate(
|
||||||
|
guest_memory,
|
||||||
|
Arc::new(NoopVirtioInterrupt {}),
|
||||||
|
vec![(0, q, evt)],
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
// Wait for the events to finish and watchdog device worker thread to return
|
||||||
|
watchdog.wait_for_epoll_threads();
|
||||||
|
});
|
||||||
|
|
||||||
|
pub struct NoopVirtioInterrupt {}
|
||||||
|
|
||||||
|
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||||
|
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue {
|
||||||
|
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||||
|
q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
|
||||||
|
q.set_next_used(bytes[1] as u16);
|
||||||
|
q.set_event_idx(bytes[2] % 2 != 0);
|
||||||
|
q.set_size(bytes[3] as u16 % QUEUE_SIZE);
|
||||||
|
|
||||||
|
q.try_set_desc_table_address(GuestAddress(DESC_TABLE_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_avail_ring_address(GuestAddress(AVAIL_RING_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_used_ring_address(GuestAddress(USED_RING_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.set_ready(true);
|
||||||
|
|
||||||
|
q
|
||||||
|
}
|
||||||
@@ -11,20 +11,20 @@ mshv = ["mshv-ioctls", "mshv-bindings"]
|
|||||||
tdx = []
|
tdx = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.56"
|
anyhow = "1.0.65"
|
||||||
epoll = "4.3.1"
|
byteorder = "1.4.3"
|
||||||
thiserror = "1.0.30"
|
thiserror = "1.0.37"
|
||||||
libc = "0.2.123"
|
libc = "0.2.133"
|
||||||
log = "0.4.16"
|
log = "0.4.17"
|
||||||
kvm-ioctls = { version = "0.11.0", optional = true }
|
kvm-ioctls = { version = "0.11.0", optional = true }
|
||||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0-tdx", features = ["with-serde", "fam-wrappers"], optional = true }
|
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0-tdx", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||||
mshv-bindings = {git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true }
|
mshv-bindings = { git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||||
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", branch = "main", optional = true}
|
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", branch = "main", optional = true}
|
||||||
serde = { version = "1.0.136", features = ["rc"] }
|
serde = { version = "1.0.145", features = ["rc", "derive"] }
|
||||||
serde_derive = "1.0.136"
|
serde_with = { version = "2.0.1", default-features = false, features = ["macros"] }
|
||||||
serde_json = "1.0.79"
|
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||||
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic"] }
|
vm-memory = { version = "0.9.0", features = ["backend-mmap", "backend-atomic"] }
|
||||||
vmm-sys-util = { version = "0.9.0", features = ["with-serde"] }
|
vmm-sys-util = { version = "0.10.0", features = ["with-serde"] }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
||||||
version = "1.17.0"
|
version = "1.17.0"
|
||||||
@@ -32,4 +32,4 @@ 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.9.0"
|
env_logger = "0.9.1"
|
||||||
|
|||||||
68
hypervisor/src/arch/aarch64/gic.rs
Normal file
68
hypervisor/src/arch/aarch64/gic.rs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||||
|
|
||||||
|
use crate::{CpuState, GicState, HypervisorDeviceError, HypervisorVmError};
|
||||||
|
use std::any::Any;
|
||||||
|
use std::result;
|
||||||
|
|
||||||
|
/// Errors thrown while setting up the VGIC.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Error while calling KVM ioctl for setting up the global interrupt controller.
|
||||||
|
CreateGic(HypervisorVmError),
|
||||||
|
/// Error while setting device attributes for the GIC.
|
||||||
|
SetDeviceAttribute(HypervisorDeviceError),
|
||||||
|
/// Error while getting device attributes for the GIC.
|
||||||
|
GetDeviceAttribute(HypervisorDeviceError),
|
||||||
|
}
|
||||||
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VgicConfig {
|
||||||
|
pub vcpu_count: u64,
|
||||||
|
pub dist_addr: u64,
|
||||||
|
pub dist_size: u64,
|
||||||
|
pub redists_addr: u64,
|
||||||
|
pub redists_size: u64,
|
||||||
|
pub msi_addr: u64,
|
||||||
|
pub msi_size: u64,
|
||||||
|
pub nr_irqs: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hypervisor agnostic interface for a virtualized GIC
|
||||||
|
pub trait Vgic: Send + Sync {
|
||||||
|
/// Returns the fdt compatibility property of the device
|
||||||
|
fn fdt_compatibility(&self) -> &str;
|
||||||
|
|
||||||
|
/// Returns the maint_irq fdt property of the device
|
||||||
|
fn fdt_maint_irq(&self) -> u32;
|
||||||
|
|
||||||
|
/// Returns an array with GIC device properties
|
||||||
|
fn device_properties(&self) -> [u64; 4];
|
||||||
|
|
||||||
|
/// Returns the number of vCPUs this GIC handles
|
||||||
|
fn vcpu_count(&self) -> u64;
|
||||||
|
|
||||||
|
/// Returns whether the GIC device is MSI compatible or not
|
||||||
|
fn msi_compatible(&self) -> bool;
|
||||||
|
|
||||||
|
/// Returns the MSI compatibility property of the device
|
||||||
|
fn msi_compatibility(&self) -> &str;
|
||||||
|
|
||||||
|
/// Returns the MSI reg property of the device
|
||||||
|
fn msi_properties(&self) -> [u64; 2];
|
||||||
|
|
||||||
|
/// Get the values of GICR_TYPER for each vCPU.
|
||||||
|
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]);
|
||||||
|
|
||||||
|
/// Downcast the trait object to its concrete type.
|
||||||
|
fn as_any_concrete_mut(&mut self) -> &mut dyn Any;
|
||||||
|
|
||||||
|
/// Save the state of GICv3ITS.
|
||||||
|
fn state(&self) -> Result<GicState>;
|
||||||
|
|
||||||
|
/// Restore the state of GICv3ITS.
|
||||||
|
fn set_state(&mut self, state: &GicState) -> Result<()>;
|
||||||
|
|
||||||
|
/// Saves GIC internal data tables into RAM.
|
||||||
|
fn save_data_tables(&self) -> Result<()>;
|
||||||
|
}
|
||||||
3
hypervisor/src/arch/aarch64/mod.rs
Normal file
3
hypervisor/src/arch/aarch64/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||||
|
|
||||||
|
pub mod gic;
|
||||||
@@ -99,7 +99,7 @@ pub enum EmulationError<T: Debug> {
|
|||||||
/// A CpuState is an architecture specific type, representing a CPU state.
|
/// A CpuState is an architecture specific type, representing a CPU state.
|
||||||
/// The emulator and its instruction handlers modify a given CPU state and
|
/// The emulator and its instruction handlers modify a given CPU state and
|
||||||
/// eventually ask the platform to commit it back through `set_cpu_state`.
|
/// eventually ask the platform to commit it back through `set_cpu_state`.
|
||||||
pub trait PlatformEmulator: Send + Sync {
|
pub trait PlatformEmulator {
|
||||||
type CpuState: Clone;
|
type CpuState: Clone;
|
||||||
|
|
||||||
/// Read guest memory into a u8 slice.
|
/// Read guest memory into a u8 slice.
|
||||||
|
|||||||
@@ -10,9 +10,14 @@
|
|||||||
//
|
//
|
||||||
// Copyright © 2020, Microsoft Corporation
|
// Copyright © 2020, Microsoft Corporation
|
||||||
//
|
//
|
||||||
|
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
pub mod emulator;
|
pub mod emulator;
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod x86;
|
pub mod x86;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
pub mod aarch64;
|
||||||
|
|||||||
@@ -38,11 +38,7 @@ fn calc_rflags_cpazso(op0: u64, op1: u64, op_size: usize) -> u64 {
|
|||||||
// AF cares about the lowest 4 bits (nibble). msb_shift is 3 in this case.
|
// AF cares about the lowest 4 bits (nibble). msb_shift is 3 in this case.
|
||||||
let af = ((cout >> 3) & 0x1) << AF_SHIFT;
|
let af = ((cout >> 3) & 0x1) << AF_SHIFT;
|
||||||
|
|
||||||
let zf = if result & (!0u64 >> (63 - msb_shift)) == 0 {
|
let zf = u64::from(result & (!0u64 >> (63 - msb_shift)) == 0) << ZF_SHIFT;
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
} << ZF_SHIFT;
|
|
||||||
|
|
||||||
let sf = ((result >> msb_shift) & 0x1) << SF_SHIFT;
|
let sf = ((result >> msb_shift) & 0x1) << SF_SHIFT;
|
||||||
|
|
||||||
|
|||||||
@@ -28,14 +28,11 @@ fn get_op<T: CpuStateManager>(
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
match op_size {
|
if !matches!(op_size, 1 | 2 | 4 | 8) {
|
||||||
1 | 2 | 4 | 8 => {}
|
return Err(PlatformError::InvalidOperand(anyhow!(
|
||||||
_ => {
|
"Invalid operand size {:?}",
|
||||||
return Err(PlatformError::InvalidOperand(anyhow!(
|
op_size
|
||||||
"Invalid operand size {:?}",
|
)));
|
||||||
op_size
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = match insn
|
let value = match insn
|
||||||
@@ -81,14 +78,11 @@ fn set_op<T: CpuStateManager>(
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
match op_size {
|
if !matches!(op_size, 1 | 2 | 4 | 8) {
|
||||||
1 | 2 | 4 | 8 => {}
|
return Err(PlatformError::InvalidOperand(anyhow!(
|
||||||
_ => {
|
"Invalid operand size {:?}",
|
||||||
return Err(PlatformError::InvalidOperand(anyhow!(
|
op_size
|
||||||
"Invalid operand size {:?}",
|
)));
|
||||||
op_size
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match insn
|
match insn
|
||||||
|
|||||||
@@ -6,10 +6,11 @@
|
|||||||
|
|
||||||
use crate::arch::emulator::{EmulationError, EmulationResult, PlatformEmulator, PlatformError};
|
use crate::arch::emulator::{EmulationError, EmulationResult, PlatformEmulator, PlatformError};
|
||||||
use crate::arch::x86::emulator::instructions::*;
|
use crate::arch::x86::emulator::instructions::*;
|
||||||
use crate::arch::x86::regs::*;
|
use crate::arch::x86::regs::{CR0_PE, EFER_LMA};
|
||||||
use crate::arch::x86::*;
|
use crate::arch::x86::{
|
||||||
use crate::arch::x86::{Exception, SegmentRegisterOps};
|
segment_type_expand_down, segment_type_ro, Exception, SegmentRegister, SpecialRegisters,
|
||||||
use crate::x86_64::{SegmentRegister, SpecialRegisters, StandardRegisters};
|
StandardRegisters,
|
||||||
|
};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use iced_x86::*;
|
use iced_x86::*;
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ use iced_x86::*;
|
|||||||
mod instructions;
|
mod instructions;
|
||||||
|
|
||||||
/// x86 CPU modes
|
/// x86 CPU modes
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum CpuMode {
|
pub enum CpuMode {
|
||||||
/// Real mode
|
/// Real mode
|
||||||
Real,
|
Real,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
// found in the LICENSE-BSD-3-Clause file.
|
// found in the LICENSE-BSD-3-Clause file.
|
||||||
|
|
||||||
// For GDT details see arch/x86/include/asm/segment.h
|
// For GDT details see arch/x86/include/asm/segment.h
|
||||||
use crate::x86_64::SegmentRegister;
|
use crate::arch::x86::SegmentRegister;
|
||||||
|
|
||||||
/// Constructor for a conventional segment GDT (or LDT) entry. Derived from the kernel's segment.h.
|
/// Constructor for a conventional segment GDT (or LDT) entry. Derived from the kernel's segment.h.
|
||||||
pub fn gdt_entry(flags: u16, base: u32, limit: u32) -> u64 {
|
pub fn gdt_entry(flags: u16, base: u32, limit: u32) -> u64 {
|
||||||
@@ -51,7 +51,7 @@ fn get_limit(entry: u64) -> u32 {
|
|||||||
// Perform manual limit scaling if G flag is set
|
// Perform manual limit scaling if G flag is set
|
||||||
match get_g(entry) {
|
match get_g(entry) {
|
||||||
0 => limit,
|
0 => limit,
|
||||||
_ => ((limit << 12) | 0xFFF), // G flag is either 0 or 1
|
_ => (limit << 12) | 0xFFF, // G flag is either 0 or 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +106,6 @@ pub fn segment_from_gdt(entry: u64, table_index: u8) -> SegmentRegister {
|
|||||||
l: get_l(entry),
|
l: get_l(entry),
|
||||||
g: get_g(entry),
|
g: get_g(entry),
|
||||||
avl: get_avl(entry),
|
avl: get_avl(entry),
|
||||||
padding: 0,
|
|
||||||
unusable: match get_p(entry) {
|
unusable: match get_p(entry) {
|
||||||
0 => 1,
|
0 => 1,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
pub mod emulator;
|
pub mod emulator;
|
||||||
pub mod gdt;
|
pub mod gdt;
|
||||||
#[allow(non_upper_case_globals)]
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
@@ -27,7 +26,7 @@ pub const MTRR_MEM_TYPE_WB: u64 = 0x6;
|
|||||||
pub const NUM_IOAPIC_PINS: usize = 24;
|
pub const NUM_IOAPIC_PINS: usize = 24;
|
||||||
|
|
||||||
// X86 Exceptions
|
// X86 Exceptions
|
||||||
#[allow(dead_code, clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Exception {
|
pub enum Exception {
|
||||||
DE = 0, // Divide Error
|
DE = 0, // Divide Error
|
||||||
@@ -53,40 +52,86 @@ pub enum Exception {
|
|||||||
|
|
||||||
pub mod regs;
|
pub mod regs;
|
||||||
|
|
||||||
// Abstracted segment register ops.
|
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
||||||
// Each x86 hypervisor should implement those.
|
#[cfg_attr(feature = "with-serde", derive(Deserialize, Serialize))]
|
||||||
pub trait SegmentRegisterOps {
|
pub struct SegmentRegister {
|
||||||
// Segment type
|
pub base: u64,
|
||||||
fn segment_type(&self) -> u8;
|
pub limit: u32,
|
||||||
fn set_segment_type(&mut self, val: u8);
|
pub selector: u16,
|
||||||
|
pub type_: u8,
|
||||||
|
pub present: u8,
|
||||||
|
pub dpl: u8,
|
||||||
|
pub db: u8,
|
||||||
|
pub s: u8,
|
||||||
|
pub l: u8,
|
||||||
|
pub g: u8,
|
||||||
|
pub avl: u8,
|
||||||
|
pub unusable: u8,
|
||||||
|
}
|
||||||
|
|
||||||
// Descriptor Privilege Level (DPL)
|
impl SegmentRegister {
|
||||||
fn dpl(&self) -> u8;
|
pub fn segment_type(&self) -> u8 {
|
||||||
fn set_dpl(&mut self, val: u8);
|
self.type_
|
||||||
|
}
|
||||||
|
pub fn set_segment_type(&mut self, val: u8) {
|
||||||
|
self.type_ = val;
|
||||||
|
}
|
||||||
|
|
||||||
// Granularity
|
pub fn dpl(&self) -> u8 {
|
||||||
fn granularity(&self) -> u8;
|
self.dpl
|
||||||
fn set_granularity(&mut self, val: u8);
|
}
|
||||||
|
|
||||||
// Memory Presence
|
pub fn set_dpl(&mut self, val: u8) {
|
||||||
fn present(&self) -> u8;
|
self.dpl = val;
|
||||||
fn set_present(&mut self, val: u8);
|
}
|
||||||
|
|
||||||
// Long mode
|
pub fn present(&self) -> u8 {
|
||||||
fn long(&self) -> u8;
|
self.present
|
||||||
fn set_long(&mut self, val: u8);
|
}
|
||||||
|
|
||||||
// Available for system use (AVL)
|
pub fn set_present(&mut self, val: u8) {
|
||||||
fn avl(&self) -> u8;
|
self.present = val;
|
||||||
fn set_avl(&mut self, val: u8);
|
}
|
||||||
|
|
||||||
// Descriptor type (System or code/data)
|
pub fn long(&self) -> u8 {
|
||||||
fn desc_type(&self) -> u8;
|
self.l
|
||||||
fn set_desc_type(&mut self, val: u8);
|
}
|
||||||
|
|
||||||
// D/B
|
pub fn set_long(&mut self, val: u8) {
|
||||||
fn db(&self) -> u8;
|
self.l = val;
|
||||||
fn set_db(&mut self, val: u8);
|
}
|
||||||
|
|
||||||
|
pub fn avl(&self) -> u8 {
|
||||||
|
self.avl
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_avl(&mut self, val: u8) {
|
||||||
|
self.avl = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn desc_type(&self) -> u8 {
|
||||||
|
self.s
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_desc_type(&mut self, val: u8) {
|
||||||
|
self.s = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn granularity(&self) -> u8 {
|
||||||
|
self.g
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_granularity(&mut self, val: u8) {
|
||||||
|
self.g = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn db(&self) -> u8 {
|
||||||
|
self.db
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_db(&mut self, val: u8) {
|
||||||
|
self.db = val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Code segment
|
// Code segment
|
||||||
@@ -115,7 +160,6 @@ macro_rules! msr {
|
|||||||
MsrEntry {
|
MsrEntry {
|
||||||
index: $msr,
|
index: $msr,
|
||||||
data: 0x0,
|
data: 0x0,
|
||||||
..Default::default()
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -125,7 +169,143 @@ macro_rules! msr_data {
|
|||||||
MsrEntry {
|
MsrEntry {
|
||||||
index: $msr,
|
index: $msr,
|
||||||
data: $data,
|
data: $data,
|
||||||
..Default::default()
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "with-serde", derive(Deserialize, Serialize))]
|
||||||
|
pub struct StandardRegisters {
|
||||||
|
pub rax: u64,
|
||||||
|
pub rbx: u64,
|
||||||
|
pub rcx: u64,
|
||||||
|
pub rdx: u64,
|
||||||
|
pub rsi: u64,
|
||||||
|
pub rdi: u64,
|
||||||
|
pub rsp: u64,
|
||||||
|
pub rbp: u64,
|
||||||
|
pub r8: u64,
|
||||||
|
pub r9: u64,
|
||||||
|
pub r10: u64,
|
||||||
|
pub r11: u64,
|
||||||
|
pub r12: u64,
|
||||||
|
pub r13: u64,
|
||||||
|
pub r14: u64,
|
||||||
|
pub r15: u64,
|
||||||
|
pub rip: u64,
|
||||||
|
pub rflags: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "with-serde", derive(Deserialize, Serialize))]
|
||||||
|
pub struct DescriptorTable {
|
||||||
|
pub base: u64,
|
||||||
|
pub limit: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "with-serde", derive(Deserialize, Serialize))]
|
||||||
|
pub struct SpecialRegisters {
|
||||||
|
pub cs: SegmentRegister,
|
||||||
|
pub ds: SegmentRegister,
|
||||||
|
pub es: SegmentRegister,
|
||||||
|
pub fs: SegmentRegister,
|
||||||
|
pub gs: SegmentRegister,
|
||||||
|
pub ss: SegmentRegister,
|
||||||
|
pub tr: SegmentRegister,
|
||||||
|
pub ldt: SegmentRegister,
|
||||||
|
pub gdt: DescriptorTable,
|
||||||
|
pub idt: DescriptorTable,
|
||||||
|
pub cr0: u64,
|
||||||
|
pub cr2: u64,
|
||||||
|
pub cr3: u64,
|
||||||
|
pub cr4: u64,
|
||||||
|
pub cr8: u64,
|
||||||
|
pub efer: u64,
|
||||||
|
pub apic_base: u64,
|
||||||
|
pub interrupt_bitmap: [u64; 4usize],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub struct CpuIdEntry {
|
||||||
|
pub function: u32,
|
||||||
|
pub index: u32,
|
||||||
|
pub flags: u32,
|
||||||
|
pub eax: u32,
|
||||||
|
pub ebx: u32,
|
||||||
|
pub ecx: u32,
|
||||||
|
pub edx: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const CPUID_FLAG_VALID_INDEX: u32 = 1;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub struct FpuState {
|
||||||
|
pub fpr: [[u8; 16usize]; 8usize],
|
||||||
|
pub fcw: u16,
|
||||||
|
pub fsw: u16,
|
||||||
|
pub ftwx: u8,
|
||||||
|
pub last_opcode: u16,
|
||||||
|
pub last_ip: u64,
|
||||||
|
pub last_dp: u64,
|
||||||
|
pub xmm: [[u8; 16usize]; 16usize],
|
||||||
|
pub mxcsr: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[serde_with::serde_as]
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct LapicState {
|
||||||
|
#[serde_as(as = "[_; 1024usize]")]
|
||||||
|
pub(crate) regs: [::std::os::raw::c_char; 1024usize],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for LapicState {
|
||||||
|
fn default() -> Self {
|
||||||
|
// SAFETY: this is plain old data structure
|
||||||
|
unsafe { ::std::mem::zeroed() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LapicState {
|
||||||
|
pub fn get_klapic_reg(&self, reg_offset: usize) -> u32 {
|
||||||
|
use byteorder::{LittleEndian, ReadBytesExt};
|
||||||
|
use std::io::Cursor;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
let sliceu8 = unsafe {
|
||||||
|
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
|
||||||
|
// Cursors are only readable on arrays of u8, not i8(c_char).
|
||||||
|
mem::transmute::<&[i8], &[u8]>(&self.regs[reg_offset..])
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut reader = Cursor::new(sliceu8);
|
||||||
|
// Following call can't fail if the offsets defined above are correct.
|
||||||
|
reader
|
||||||
|
.read_u32::<LittleEndian>()
|
||||||
|
.expect("Failed to read klapic register")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_klapic_reg(&mut self, reg_offset: usize, value: u32) {
|
||||||
|
use byteorder::{LittleEndian, WriteBytesExt};
|
||||||
|
use std::io::Cursor;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
let sliceu8 = unsafe {
|
||||||
|
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
|
||||||
|
// Cursors are only readable on arrays of u8, not i8(c_char).
|
||||||
|
mem::transmute::<&mut [i8], &mut [u8]>(&mut self.regs[reg_offset..])
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut writer = Cursor::new(sliceu8);
|
||||||
|
// Following call can't fail if the offsets defined above are correct.
|
||||||
|
writer
|
||||||
|
.write_u32::<LittleEndian>(value)
|
||||||
|
.expect("Failed to write klapic register")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub struct MsrEntry {
|
||||||
|
pub index: u32,
|
||||||
|
pub data: u64,
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,28 +9,16 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use crate::aarch64::VcpuInit;
|
use crate::aarch64::{RegList, StandardRegisters, VcpuInit};
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
use crate::aarch64::{RegList, Register, StandardRegisters};
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::x86_64::{CpuId, LapicState};
|
use crate::arch::x86::{
|
||||||
#[cfg(target_arch = "x86_64")]
|
CpuIdEntry, FpuState, LapicState, MsrEntry, SpecialRegisters, StandardRegisters,
|
||||||
use crate::x86_64::{
|
|
||||||
ExtendedControlRegisters, FpuState, MsrEntries, SpecialRegisters, StandardRegisters, VcpuEvents,
|
|
||||||
};
|
};
|
||||||
use crate::CpuState;
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
use crate::DeviceAttr;
|
|
||||||
#[cfg(feature = "kvm")]
|
|
||||||
use crate::MpState;
|
|
||||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
|
||||||
use crate::SuspendRegisters;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
use crate::Xsave;
|
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
use crate::{TdxExitDetails, TdxExitStatus};
|
use crate::kvm::{TdxExitDetails, TdxExitStatus};
|
||||||
|
use crate::CpuState;
|
||||||
|
use crate::MpState;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
|
||||||
use vm_memory::GuestAddress;
|
use vm_memory::GuestAddress;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
@@ -48,11 +36,6 @@ 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}")]
|
||||||
@@ -248,6 +231,12 @@ pub enum HypervisorCpuError {
|
|||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
#[error("Unknown TDX VM call")]
|
#[error("Unknown TDX VM call")]
|
||||||
UnknownTdxVmCall,
|
UnknownTdxVmCall,
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
///
|
||||||
|
/// Failed to intialize PMU
|
||||||
|
///
|
||||||
|
#[error("Failed to initialize PMU")]
|
||||||
|
InitializePmu,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -278,22 +267,10 @@ pub type Result<T> = anyhow::Result<T, HypervisorCpuError>;
|
|||||||
/// Trait to represent a generic Vcpu
|
/// Trait to represent a generic Vcpu
|
||||||
///
|
///
|
||||||
pub trait Vcpu: Send + Sync {
|
pub trait Vcpu: Send + Sync {
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
///
|
///
|
||||||
/// Returns the vCPU general purpose registers.
|
/// Returns the vCPU general purpose registers.
|
||||||
///
|
///
|
||||||
fn get_regs(&self) -> Result<StandardRegisters>;
|
fn get_regs(&self) -> Result<StandardRegisters>;
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
///
|
|
||||||
/// Sets vcpu attribute
|
|
||||||
///
|
|
||||||
fn set_vcpu_attr(&self, attr: &DeviceAttr) -> Result<()>;
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
///
|
|
||||||
/// Check if vcpu has attribute.
|
|
||||||
///
|
|
||||||
fn has_vcpu_attr(&self, attr: &DeviceAttr) -> Result<()>;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
///
|
///
|
||||||
/// Sets the vCPU general purpose registers.
|
/// Sets the vCPU general purpose registers.
|
||||||
///
|
///
|
||||||
@@ -322,7 +299,7 @@ pub trait Vcpu: Send + Sync {
|
|||||||
///
|
///
|
||||||
/// X86 specific call to setup the CPUID registers.
|
/// X86 specific call to setup the CPUID registers.
|
||||||
///
|
///
|
||||||
fn set_cpuid2(&self, cpuid: &CpuId) -> Result<()>;
|
fn set_cpuid2(&self, cpuid: &[CpuIdEntry]) -> Result<()>;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
/// X86 specific call to enable HyperV SynIC
|
/// X86 specific call to enable HyperV SynIC
|
||||||
@@ -332,7 +309,7 @@ pub trait Vcpu: Send + Sync {
|
|||||||
///
|
///
|
||||||
/// X86 specific call to retrieve the CPUID registers.
|
/// X86 specific call to retrieve the CPUID registers.
|
||||||
///
|
///
|
||||||
fn get_cpuid2(&self, num_entries: usize) -> Result<CpuId>;
|
fn get_cpuid2(&self, num_entries: usize) -> Result<Vec<CpuIdEntry>>;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
/// Returns the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
|
/// Returns the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
|
||||||
@@ -347,111 +324,65 @@ pub trait Vcpu: Send + Sync {
|
|||||||
///
|
///
|
||||||
/// Returns the model-specific registers (MSR) for this vCPU.
|
/// Returns the model-specific registers (MSR) for this vCPU.
|
||||||
///
|
///
|
||||||
fn get_msrs(&self, msrs: &mut MsrEntries) -> Result<usize>;
|
fn get_msrs(&self, msrs: &mut Vec<MsrEntry>) -> Result<usize>;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
/// Setup the model-specific registers (MSR) for this vCPU.
|
/// Setup the model-specific registers (MSR) for this vCPU.
|
||||||
///
|
///
|
||||||
fn set_msrs(&self, msrs: &MsrEntries) -> Result<usize>;
|
fn set_msrs(&self, msrs: &[MsrEntry]) -> Result<usize>;
|
||||||
#[cfg(feature = "kvm")]
|
|
||||||
///
|
///
|
||||||
/// Returns the vcpu's current "multiprocessing state".
|
/// Returns the vcpu's current "multiprocessing state".
|
||||||
///
|
///
|
||||||
fn get_mp_state(&self) -> Result<MpState>;
|
fn get_mp_state(&self) -> Result<MpState>;
|
||||||
#[cfg(feature = "kvm")]
|
|
||||||
///
|
///
|
||||||
/// Sets the vcpu's current "multiprocessing state".
|
/// Sets the vcpu's current "multiprocessing state".
|
||||||
///
|
///
|
||||||
fn set_mp_state(&self, mp_state: MpState) -> Result<()>;
|
fn set_mp_state(&self, mp_state: MpState) -> Result<()>;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
/// X86 specific call that returns the vcpu's current "xsave struct".
|
|
||||||
///
|
|
||||||
fn get_xsave(&self) -> Result<Xsave>;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
///
|
|
||||||
/// X86 specific call that sets the vcpu's current "xsave struct".
|
|
||||||
///
|
|
||||||
fn set_xsave(&self, xsave: &Xsave) -> Result<()>;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
///
|
|
||||||
/// X86 specific call that returns the vcpu's current "xcrs".
|
|
||||||
///
|
|
||||||
fn get_xcrs(&self) -> Result<ExtendedControlRegisters>;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
///
|
|
||||||
/// X86 specific call that sets the vcpu's current "xcrs".
|
|
||||||
///
|
|
||||||
fn set_xcrs(&self, xcrs: &ExtendedControlRegisters) -> Result<()>;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
///
|
|
||||||
/// Returns currently pending exceptions, interrupts, and NMIs as well as related
|
|
||||||
/// states of the vcpu.
|
|
||||||
///
|
|
||||||
fn get_vcpu_events(&self) -> Result<VcpuEvents>;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
///
|
|
||||||
/// Sets pending exceptions, interrupts, and NMIs as well as related states
|
|
||||||
/// of the vcpu.
|
|
||||||
///
|
|
||||||
fn set_vcpu_events(&self, events: &VcpuEvents) -> Result<()>;
|
|
||||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
|
||||||
///
|
|
||||||
/// Let the guest know that it has been paused, which prevents from
|
/// Let the guest know that it has been paused, which prevents from
|
||||||
/// potential soft lockups when being resumed.
|
/// potential soft lockups when being resumed.
|
||||||
///
|
///
|
||||||
fn notify_guest_clock_paused(&self) -> Result<()>;
|
fn notify_guest_clock_paused(&self) -> Result<()> {
|
||||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
Ok(())
|
||||||
|
}
|
||||||
///
|
///
|
||||||
/// Sets debug registers to set hardware breakpoints and/or enable single step.
|
/// Sets debug registers to set hardware breakpoints and/or enable single step.
|
||||||
///
|
///
|
||||||
fn set_guest_debug(&self, addrs: &[GuestAddress], singlestep: bool) -> Result<()>;
|
fn set_guest_debug(&self, _addrs: &[GuestAddress], _singlestep: bool) -> Result<()> {
|
||||||
|
Err(HypervisorCpuError::SetDebugRegs(anyhow!("unimplemented")))
|
||||||
|
}
|
||||||
///
|
///
|
||||||
/// Sets the type of CPU to be exposed to the guest and optional features.
|
/// Sets the type of CPU to be exposed to the guest and optional features.
|
||||||
///
|
///
|
||||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
#[cfg(target_arch = "aarch64")]
|
||||||
fn vcpu_init(&self, kvi: &VcpuInit) -> Result<()>;
|
fn vcpu_init(&self, kvi: &VcpuInit) -> Result<()>;
|
||||||
///
|
///
|
||||||
/// Sets the value of one register for this vCPU.
|
|
||||||
///
|
|
||||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
|
||||||
fn set_reg(&self, reg_id: u64, data: u64) -> Result<()>;
|
|
||||||
///
|
|
||||||
/// Sets the value of one register for this vCPU.
|
|
||||||
///
|
|
||||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
|
||||||
fn get_reg(&self, reg_id: u64) -> Result<u64>;
|
|
||||||
///
|
|
||||||
/// Gets a list of the guest registers that are supported for the
|
/// Gets a list of the guest registers that are supported for the
|
||||||
/// KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
|
/// KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
|
||||||
///
|
///
|
||||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
#[cfg(target_arch = "aarch64")]
|
||||||
fn get_reg_list(&self, reg_list: &mut RegList) -> Result<()>;
|
fn get_reg_list(&self, reg_list: &mut RegList) -> Result<()>;
|
||||||
///
|
///
|
||||||
/// Save the state of the core registers.
|
/// Gets the value of a system register
|
||||||
///
|
///
|
||||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
#[cfg(target_arch = "aarch64")]
|
||||||
fn core_registers(&self, state: &mut StandardRegisters) -> Result<()>;
|
fn get_sys_reg(&self, sys_reg: u32) -> Result<u64>;
|
||||||
///
|
///
|
||||||
/// Restore the state of the core registers.
|
/// Configure core registers for a given CPU.
|
||||||
///
|
///
|
||||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
#[cfg(target_arch = "aarch64")]
|
||||||
fn set_core_registers(&self, state: &StandardRegisters) -> Result<()>;
|
fn setup_regs(&self, cpu_id: u8, boot_ip: u64, fdt_start: u64) -> Result<()>;
|
||||||
///
|
///
|
||||||
/// Save the state of the system registers.
|
/// Check if the CPU supports PMU
|
||||||
///
|
///
|
||||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
#[cfg(target_arch = "aarch64")]
|
||||||
fn system_registers(&self, state: &mut Vec<Register>) -> Result<()>;
|
fn has_pmu_support(&self) -> bool;
|
||||||
///
|
///
|
||||||
/// Restore the state of the system registers.
|
/// Initialize PMU
|
||||||
///
|
///
|
||||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
#[cfg(target_arch = "aarch64")]
|
||||||
fn set_system_registers(&self, state: &[Register]) -> Result<()>;
|
fn init_pmu(&self, irq: u32) -> Result<()>;
|
||||||
///
|
|
||||||
/// Read the MPIDR - Multiprocessor Affinity Register.
|
|
||||||
///
|
|
||||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
|
||||||
fn read_mpidr(&self) -> Result<u64>;
|
|
||||||
///
|
///
|
||||||
/// Retrieve the vCPU state.
|
/// Retrieve the vCPU state.
|
||||||
/// This function is necessary to snapshot the VM
|
/// This function is necessary to snapshot the VM
|
||||||
@@ -475,25 +406,30 @@ pub trait Vcpu: Send + Sync {
|
|||||||
/// Initialize TDX support on the vCPU
|
/// Initialize TDX support on the vCPU
|
||||||
///
|
///
|
||||||
#[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"))]
|
unimplemented!()
|
||||||
///
|
}
|
||||||
/// Return suspend registers(explicit and intercept suspend registers)
|
|
||||||
///
|
|
||||||
fn get_suspend_regs(&self) -> Result<SuspendRegisters>;
|
|
||||||
#[cfg(feature = "kvm")]
|
|
||||||
///
|
///
|
||||||
/// Set the "immediate_exit" state
|
/// Set the "immediate_exit" state
|
||||||
///
|
///
|
||||||
fn set_immediate_exit(&self, exit: bool);
|
fn set_immediate_exit(&self, _exit: bool) {}
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
///
|
///
|
||||||
/// Returns the details about TDX exit reason
|
/// Returns the details about TDX exit reason
|
||||||
///
|
///
|
||||||
fn get_tdx_exit_details(&mut self) -> Result<TdxExitDetails>;
|
fn get_tdx_exit_details(&mut self) -> Result<TdxExitDetails> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
///
|
///
|
||||||
/// Set the status code for TDX exit
|
/// Set the status code for TDX exit
|
||||||
///
|
///
|
||||||
fn set_tdx_status(&mut self, status: TdxExitStatus);
|
fn set_tdx_status(&mut self, _status: TdxExitStatus) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
///
|
||||||
|
/// Return the list of initial MSR entries for a VCPU
|
||||||
|
///
|
||||||
|
fn boot_msr_entries(&self) -> Vec<MsrEntry>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,6 @@
|
|||||||
// Copyright 2020, ARM Limited
|
// Copyright 2020, ARM Limited
|
||||||
//
|
//
|
||||||
|
|
||||||
use crate::DeviceAttr;
|
|
||||||
use std::os::unix::io::AsRawFd;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
@@ -28,20 +26,3 @@ pub enum HypervisorDeviceError {
|
|||||||
#[error("Failed to get device attribute: {0}")]
|
#[error("Failed to get device attribute: {0}")]
|
||||||
GetDeviceAttribute(#[source] anyhow::Error),
|
GetDeviceAttribute(#[source] anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Result type for returning from a function
|
|
||||||
///
|
|
||||||
pub type Result<T> = std::result::Result<T, HypervisorDeviceError>;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Trait to represent a device
|
|
||||||
///
|
|
||||||
/// This crate provides a hypervisor-agnostic interfaces for device
|
|
||||||
///
|
|
||||||
pub trait Device: Send + Sync + AsRawFd {
|
|
||||||
/// Set device attribute.
|
|
||||||
fn set_device_attr(&self, attr: &DeviceAttr) -> Result<()>;
|
|
||||||
/// Get device attribute.
|
|
||||||
fn get_device_attr(&self, attr: &mut DeviceAttr) -> Result<()>;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,13 +7,12 @@
|
|||||||
// Copyright 2018-2019 CrowdStrike, Inc.
|
// Copyright 2018-2019 CrowdStrike, Inc.
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
use crate::vm::Vm;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::x86_64::CpuId;
|
use crate::arch::x86::CpuIdEntry;
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
use crate::x86_64::MsrList;
|
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
use crate::TdxCapabilities;
|
use crate::kvm::TdxCapabilities;
|
||||||
|
use crate::vm::Vm;
|
||||||
|
use crate::HypervisorType;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
@@ -21,6 +20,11 @@ use thiserror::Error;
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
pub enum HypervisorError {
|
pub enum HypervisorError {
|
||||||
|
///
|
||||||
|
/// Hypervisor availability check error
|
||||||
|
///
|
||||||
|
#[error("Failed to check availability of the hypervisor: {0}")]
|
||||||
|
HypervisorAvailableCheck(#[source] anyhow::Error),
|
||||||
///
|
///
|
||||||
/// hypervisor creation error
|
/// hypervisor creation error
|
||||||
///
|
///
|
||||||
@@ -66,6 +70,11 @@ pub enum HypervisorError {
|
|||||||
///
|
///
|
||||||
#[error("Failed to retrieve TDX capabilities:{0}")]
|
#[error("Failed to retrieve TDX capabilities:{0}")]
|
||||||
TdxCapabilities(#[source] anyhow::Error),
|
TdxCapabilities(#[source] anyhow::Error),
|
||||||
|
///
|
||||||
|
/// Failed to set partition property
|
||||||
|
///
|
||||||
|
#[error("Failed to set partition property:{0}")]
|
||||||
|
SetPartitionProperty(#[source] anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -79,6 +88,10 @@ pub type Result<T> = std::result::Result<T, HypervisorError>;
|
|||||||
/// This crate provides a hypervisor-agnostic interfaces
|
/// This crate provides a hypervisor-agnostic interfaces
|
||||||
///
|
///
|
||||||
pub trait Hypervisor: Send + Sync {
|
pub trait Hypervisor: Send + Sync {
|
||||||
|
///
|
||||||
|
/// Returns the type of the hypervisor
|
||||||
|
///
|
||||||
|
fn hypervisor_type(&self) -> HypervisorType;
|
||||||
///
|
///
|
||||||
/// Create a Vm using the underlying hypervisor
|
/// Create a Vm using the underlying hypervisor
|
||||||
/// Return a hypervisor-agnostic Vm trait object
|
/// Return a hypervisor-agnostic Vm trait object
|
||||||
@@ -95,26 +108,29 @@ pub trait Hypervisor: Send + Sync {
|
|||||||
///
|
///
|
||||||
/// Get the supported CpuID
|
/// Get the supported CpuID
|
||||||
///
|
///
|
||||||
fn get_cpuid(&self) -> Result<CpuId>;
|
fn get_cpuid(&self) -> Result<Vec<CpuIdEntry>>;
|
||||||
///
|
///
|
||||||
/// Check particular extensions if any
|
/// Check particular extensions if any
|
||||||
///
|
///
|
||||||
fn check_required_extensions(&self) -> Result<()> {
|
fn check_required_extensions(&self) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
///
|
|
||||||
/// Retrieve the list of MSRs supported by the hypervisor.
|
|
||||||
///
|
|
||||||
fn get_msr_list(&self) -> Result<MsrList>;
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
///
|
///
|
||||||
/// Retrieve AArch64 host maximum IPA size supported by KVM.
|
/// Retrieve AArch64 host maximum IPA size supported by KVM
|
||||||
///
|
///
|
||||||
fn get_host_ipa_limit(&self) -> i32;
|
fn get_host_ipa_limit(&self) -> i32;
|
||||||
///
|
///
|
||||||
/// Retrieve TDX capabilities
|
/// Retrieve TDX capabilities
|
||||||
///
|
///
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
fn tdx_capabilities(&self) -> Result<TdxCapabilities>;
|
fn tdx_capabilities(&self) -> Result<TdxCapabilities> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
///
|
||||||
|
/// Get the number of supported hardware breakpoints
|
||||||
|
///
|
||||||
|
fn get_guest_debug_hw_bps(&self) -> usize {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use super::{Error, Result};
|
use crate::arch::aarch64::gic::{Error, Result};
|
||||||
use crate::layout::IRQ_BASE;
|
use crate::device::HypervisorDeviceError;
|
||||||
use hypervisor::kvm::kvm_bindings::{
|
use crate::kvm::kvm_bindings::{
|
||||||
kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
|
kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use kvm_ioctls::DeviceFd;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Distributor registers as detailed at page 456 from
|
Distributor registers as detailed at page 456 from
|
||||||
@@ -77,12 +77,7 @@ static VGIC_DIST_REGS: &[DistReg] = &[
|
|||||||
VGIC_DIST_REG!(GICD_IPRIORITYR, 8, 0),
|
VGIC_DIST_REG!(GICD_IPRIORITYR, 8, 0),
|
||||||
];
|
];
|
||||||
|
|
||||||
fn dist_attr_access(
|
fn dist_attr_access(gic: &DeviceFd, offset: u32, val: &u32, set: bool) -> Result<()> {
|
||||||
gic: &Arc<dyn hypervisor::Device>,
|
|
||||||
offset: u32,
|
|
||||||
val: &u32,
|
|
||||||
set: bool,
|
|
||||||
) -> Result<()> {
|
|
||||||
let mut gic_dist_attr = kvm_device_attr {
|
let mut gic_dist_attr = kvm_device_attr {
|
||||||
group: KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
|
group: KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
|
||||||
attr: offset as u64,
|
attr: offset as u64,
|
||||||
@@ -90,28 +85,30 @@ fn dist_attr_access(
|
|||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
if set {
|
if set {
|
||||||
gic.set_device_attr(&gic_dist_attr)
|
gic.set_device_attr(&gic_dist_attr).map_err(|e| {
|
||||||
.map_err(Error::SetDeviceAttribute)?;
|
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||||
|
})?;
|
||||||
} else {
|
} else {
|
||||||
gic.get_device_attr(&mut gic_dist_attr)
|
gic.get_device_attr(&mut gic_dist_attr).map_err(|e| {
|
||||||
.map_err(Error::GetDeviceAttribute)?;
|
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the distributor control register.
|
/// Get the distributor control register.
|
||||||
pub fn read_ctlr(gic: &Arc<dyn hypervisor::Device>) -> Result<u32> {
|
pub fn read_ctlr(gic: &DeviceFd) -> Result<u32> {
|
||||||
let val: u32 = 0;
|
let val: u32 = 0;
|
||||||
dist_attr_access(gic, GICD_CTLR, &val, false)?;
|
dist_attr_access(gic, GICD_CTLR, &val, false)?;
|
||||||
Ok(val)
|
Ok(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the distributor control register.
|
/// Set the distributor control register.
|
||||||
pub fn write_ctlr(gic: &Arc<dyn hypervisor::Device>, val: u32) -> Result<()> {
|
pub fn write_ctlr(gic: &DeviceFd, val: u32) -> Result<()> {
|
||||||
dist_attr_access(gic, GICD_CTLR, &val, true)
|
dist_attr_access(gic, GICD_CTLR, &val, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_interrupts_num(gic: &Arc<dyn hypervisor::Device>) -> Result<u32> {
|
fn get_interrupts_num(gic: &DeviceFd) -> Result<u32> {
|
||||||
let num_irq = 0;
|
let num_irq = 0;
|
||||||
|
|
||||||
let mut nr_irqs_attr = kvm_device_attr {
|
let mut nr_irqs_attr = kvm_device_attr {
|
||||||
@@ -120,12 +117,18 @@ fn get_interrupts_num(gic: &Arc<dyn hypervisor::Device>) -> Result<u32> {
|
|||||||
addr: &num_irq as *const u32 as u64,
|
addr: &num_irq as *const u32 as u64,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
gic.get_device_attr(&mut nr_irqs_attr)
|
gic.get_device_attr(&mut nr_irqs_attr).map_err(|e| {
|
||||||
.map_err(Error::GetDeviceAttribute)?;
|
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||||
|
})?;
|
||||||
Ok(num_irq)
|
Ok(num_irq)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_reg_len(gic: &Arc<dyn hypervisor::Device>, reg: &DistReg, base: u32) -> Result<u32> {
|
fn compute_reg_len(gic: &DeviceFd, reg: &DistReg, base: u32) -> Result<u32> {
|
||||||
|
// FIXME:
|
||||||
|
// Redefine some GIC constants to avoid the dependency on `layout` crate.
|
||||||
|
// This is temporary solution, will be fixed in future refactoring.
|
||||||
|
const LAYOUT_IRQ_BASE: u32 = 32;
|
||||||
|
|
||||||
let mut end = base;
|
let mut end = base;
|
||||||
let num_irq = get_interrupts_num(gic)?;
|
let num_irq = get_interrupts_num(gic)?;
|
||||||
if reg.length > 0 {
|
if reg.length > 0 {
|
||||||
@@ -138,8 +141,8 @@ fn compute_reg_len(gic: &Arc<dyn hypervisor::Device>, reg: &DistReg, base: u32)
|
|||||||
// This is the type of register that takes into account the number of interrupts
|
// This is the type of register that takes into account the number of interrupts
|
||||||
// that the model has. It is also the type of register where
|
// that the model has. It is also the type of register where
|
||||||
// a register relates to multiple interrupts.
|
// a register relates to multiple interrupts.
|
||||||
end = base + (reg.bpi as u32 * (num_irq - IRQ_BASE) / 8);
|
end = base + (reg.bpi as u32 * (num_irq - LAYOUT_IRQ_BASE) / 8);
|
||||||
if reg.bpi as u32 * (num_irq - IRQ_BASE) % 8 > 0 {
|
if reg.bpi as u32 * (num_irq - LAYOUT_IRQ_BASE) % 8 > 0 {
|
||||||
end += REG_SIZE as u32;
|
end += REG_SIZE as u32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,7 +150,7 @@ fn compute_reg_len(gic: &Arc<dyn hypervisor::Device>, reg: &DistReg, base: u32)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set distributor registers of the GIC.
|
/// Set distributor registers of the GIC.
|
||||||
pub fn set_dist_regs(gic: &Arc<dyn hypervisor::Device>, state: &[u32]) -> Result<()> {
|
pub fn set_dist_regs(gic: &DeviceFd, state: &[u32]) -> Result<()> {
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
|
|
||||||
for dreg in VGIC_DIST_REGS {
|
for dreg in VGIC_DIST_REGS {
|
||||||
@@ -164,7 +167,7 @@ pub fn set_dist_regs(gic: &Arc<dyn hypervisor::Device>, state: &[u32]) -> Result
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
/// Get distributor registers of the GIC.
|
/// Get distributor registers of the GIC.
|
||||||
pub fn get_dist_regs(gic: &Arc<dyn hypervisor::Device>) -> Result<Vec<u32>> {
|
pub fn get_dist_regs(gic: &DeviceFd) -> Result<Vec<u32>> {
|
||||||
let mut state = Vec::new();
|
let mut state = Vec::new();
|
||||||
|
|
||||||
for dreg in VGIC_DIST_REGS {
|
for dreg in VGIC_DIST_REGS {
|
||||||
@@ -1,14 +1,16 @@
|
|||||||
|
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||||
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use super::{Error, Result};
|
use crate::arch::aarch64::gic::{Error, Result};
|
||||||
use hypervisor::kvm::kvm_bindings::{
|
use crate::device::HypervisorDeviceError;
|
||||||
|
use crate::kvm::kvm_bindings::{
|
||||||
kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, KVM_REG_ARM64_SYSREG_CRM_MASK,
|
kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, KVM_REG_ARM64_SYSREG_CRM_MASK,
|
||||||
KVM_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRN_MASK, KVM_REG_ARM64_SYSREG_CRN_SHIFT,
|
KVM_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRN_MASK, KVM_REG_ARM64_SYSREG_CRN_SHIFT,
|
||||||
KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP1_MASK,
|
KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP1_MASK,
|
||||||
KVM_REG_ARM64_SYSREG_OP1_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_SHIFT,
|
KVM_REG_ARM64_SYSREG_OP1_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_SHIFT,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use kvm_ioctls::DeviceFd;
|
||||||
|
|
||||||
const KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT: u32 = 32;
|
const KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT: u32 = 32;
|
||||||
const KVM_DEV_ARM_VGIC_V3_MPIDR_MASK: u64 = 0xffffffff << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT as u64;
|
const KVM_DEV_ARM_VGIC_V3_MPIDR_MASK: u64 = 0xffffffff << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT as u64;
|
||||||
@@ -77,13 +79,7 @@ static VGIC_ICC_REGS: &[u64] = &[
|
|||||||
SYS_ICC_AP1R3_EL1,
|
SYS_ICC_AP1R3_EL1,
|
||||||
];
|
];
|
||||||
|
|
||||||
fn icc_attr_access(
|
fn icc_attr_access(gic: &DeviceFd, offset: u64, typer: u64, val: &u32, set: bool) -> Result<()> {
|
||||||
gic: &Arc<dyn hypervisor::Device>,
|
|
||||||
offset: u64,
|
|
||||||
typer: u64,
|
|
||||||
val: &u32,
|
|
||||||
set: bool,
|
|
||||||
) -> Result<()> {
|
|
||||||
let mut gic_icc_attr = kvm_device_attr {
|
let mut gic_icc_attr = kvm_device_attr {
|
||||||
group: KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
|
group: KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
|
||||||
attr: ((typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | offset), // this needs the mpidr
|
attr: ((typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | offset), // this needs the mpidr
|
||||||
@@ -91,17 +87,19 @@ fn icc_attr_access(
|
|||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
if set {
|
if set {
|
||||||
gic.set_device_attr(&gic_icc_attr)
|
gic.set_device_attr(&gic_icc_attr).map_err(|e| {
|
||||||
.map_err(Error::SetDeviceAttribute)?;
|
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||||
|
})?;
|
||||||
} else {
|
} else {
|
||||||
gic.get_device_attr(&mut gic_icc_attr)
|
gic.get_device_attr(&mut gic_icc_attr).map_err(|e| {
|
||||||
.map_err(Error::GetDeviceAttribute)?;
|
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get ICC registers.
|
/// Get ICC registers.
|
||||||
pub fn get_icc_regs(gic: &Arc<dyn hypervisor::Device>, gicr_typer: &[u64]) -> Result<Vec<u32>> {
|
pub fn get_icc_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
|
||||||
let mut state: Vec<u32> = Vec::new();
|
let mut state: Vec<u32> = Vec::new();
|
||||||
// We need this for the ICC_AP<m>R<n>_EL1 registers.
|
// We need this for the ICC_AP<m>R<n>_EL1 registers.
|
||||||
let mut num_priority_bits = 0;
|
let mut num_priority_bits = 0;
|
||||||
@@ -154,11 +152,7 @@ pub fn get_icc_regs(gic: &Arc<dyn hypervisor::Device>, gicr_typer: &[u64]) -> Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set ICC registers.
|
/// Set ICC registers.
|
||||||
pub fn set_icc_regs(
|
pub fn set_icc_regs(gic: &DeviceFd, gicr_typer: &[u64], state: &[u32]) -> Result<()> {
|
||||||
gic: &Arc<dyn hypervisor::Device>,
|
|
||||||
gicr_typer: &[u64],
|
|
||||||
state: &[u32],
|
|
||||||
) -> Result<()> {
|
|
||||||
let mut num_priority_bits = 0;
|
let mut num_priority_bits = 0;
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
for ix in gicr_typer {
|
for ix in gicr_typer {
|
||||||
575
hypervisor/src/kvm/aarch64/gic/mod.rs
Normal file
575
hypervisor/src/kvm/aarch64/gic/mod.rs
Normal file
@@ -0,0 +1,575 @@
|
|||||||
|
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||||
|
|
||||||
|
mod dist_regs;
|
||||||
|
mod icc_regs;
|
||||||
|
mod redist_regs;
|
||||||
|
|
||||||
|
use crate::arch::aarch64::gic::{Error, Result, Vgic, VgicConfig};
|
||||||
|
use crate::device::HypervisorDeviceError;
|
||||||
|
use crate::kvm::{kvm_bindings, KvmVm};
|
||||||
|
use crate::{CpuState, Vm};
|
||||||
|
use dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
||||||
|
use icc_regs::{get_icc_regs, set_icc_regs};
|
||||||
|
use kvm_ioctls::DeviceFd;
|
||||||
|
use redist_regs::{construct_gicr_typers, get_redist_regs, set_redist_regs};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::any::Any;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
const GITS_CTLR: u32 = 0x0000;
|
||||||
|
const GITS_IIDR: u32 = 0x0004;
|
||||||
|
const GITS_CBASER: u32 = 0x0080;
|
||||||
|
const GITS_CWRITER: u32 = 0x0088;
|
||||||
|
const GITS_CREADR: u32 = 0x0090;
|
||||||
|
const GITS_BASER: u32 = 0x0100;
|
||||||
|
|
||||||
|
/// Access an ITS device attribute.
|
||||||
|
///
|
||||||
|
/// This is a helper function to get/set the ITS device attribute depending
|
||||||
|
/// the bool parameter `set` provided.
|
||||||
|
pub fn gicv3_its_attr_access(
|
||||||
|
its_device: &DeviceFd,
|
||||||
|
group: u32,
|
||||||
|
attr: u32,
|
||||||
|
val: &u64,
|
||||||
|
set: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut gicv3_its_attr = kvm_bindings::kvm_device_attr {
|
||||||
|
group,
|
||||||
|
attr: attr as u64,
|
||||||
|
addr: val as *const u64 as u64,
|
||||||
|
flags: 0,
|
||||||
|
};
|
||||||
|
if set {
|
||||||
|
its_device.set_device_attr(&gicv3_its_attr).map_err(|e| {
|
||||||
|
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
its_device
|
||||||
|
.get_device_attr(&mut gicv3_its_attr)
|
||||||
|
.map_err(|e| {
|
||||||
|
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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: &DeviceFd, save: bool) -> Result<()> {
|
||||||
|
let attr = if save {
|
||||||
|
u64::from(kvm_bindings::KVM_DEV_ARM_ITS_SAVE_TABLES)
|
||||||
|
} else {
|
||||||
|
u64::from(kvm_bindings::KVM_DEV_ARM_ITS_RESTORE_TABLES)
|
||||||
|
};
|
||||||
|
|
||||||
|
let init_gic_attr = kvm_bindings::kvm_device_attr {
|
||||||
|
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||||
|
attr,
|
||||||
|
addr: 0,
|
||||||
|
flags: 0,
|
||||||
|
};
|
||||||
|
its_device
|
||||||
|
.set_device_attr(&init_gic_attr)
|
||||||
|
.map_err(|e| Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into())))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct KvmGicV3Its {
|
||||||
|
/// The KVM device for the GicV3
|
||||||
|
device: DeviceFd,
|
||||||
|
|
||||||
|
/// The KVM device for the Its device
|
||||||
|
its_device: Option<DeviceFd>,
|
||||||
|
|
||||||
|
/// Vector holding values of GICR_TYPER for each vCPU
|
||||||
|
gicr_typers: Vec<u64>,
|
||||||
|
|
||||||
|
/// GIC distributor address
|
||||||
|
dist_addr: u64,
|
||||||
|
|
||||||
|
/// GIC distributor size
|
||||||
|
dist_size: u64,
|
||||||
|
|
||||||
|
/// GIC distributors address
|
||||||
|
redists_addr: u64,
|
||||||
|
|
||||||
|
/// GIC distributors size
|
||||||
|
redists_size: u64,
|
||||||
|
|
||||||
|
/// GIC MSI address
|
||||||
|
msi_addr: u64,
|
||||||
|
|
||||||
|
/// GIC MSI size
|
||||||
|
msi_size: u64,
|
||||||
|
|
||||||
|
/// Number of CPUs handled by the device
|
||||||
|
vcpu_count: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Serialize, Deserialize)]
|
||||||
|
pub struct Gicv3ItsState {
|
||||||
|
dist: Vec<u32>,
|
||||||
|
rdist: Vec<u32>,
|
||||||
|
icc: Vec<u32>,
|
||||||
|
// special register that enables interrupts and affinity routing
|
||||||
|
gicd_ctlr: u32,
|
||||||
|
its_ctlr: u64,
|
||||||
|
its_iidr: u64,
|
||||||
|
its_cbaser: u64,
|
||||||
|
its_cwriter: u64,
|
||||||
|
its_creadr: u64,
|
||||||
|
its_baser: [u64; 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KvmGicV3Its {
|
||||||
|
/// Device trees specific constants
|
||||||
|
pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
|
||||||
|
|
||||||
|
/// Returns the GIC version of the device
|
||||||
|
fn version() -> u32 {
|
||||||
|
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Setup the device-specific attributes
|
||||||
|
fn init_device_attributes(&mut self, vm: &KvmVm, nr_irqs: u32) -> Result<()> {
|
||||||
|
// GicV3 part attributes
|
||||||
|
/* Setting up the distributor attribute. */
|
||||||
|
Self::set_device_attribute(
|
||||||
|
&self.device,
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||||
|
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST),
|
||||||
|
&self.dist_addr as *const u64 as u64,
|
||||||
|
0,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
/* Setting up the redistributors' attribute. */
|
||||||
|
Self::set_device_attribute(
|
||||||
|
&self.device,
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||||
|
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST),
|
||||||
|
&self.redists_addr as *const u64 as u64,
|
||||||
|
0,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// ITS part attributes
|
||||||
|
let mut its_device = kvm_bindings::kvm_create_device {
|
||||||
|
type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS,
|
||||||
|
fd: 0,
|
||||||
|
flags: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let its_fd = vm
|
||||||
|
.create_device(&mut its_device)
|
||||||
|
.map_err(Error::CreateGic)?;
|
||||||
|
|
||||||
|
// We know vm is KvmVm
|
||||||
|
let its_fd = its_fd.to_kvm().unwrap();
|
||||||
|
|
||||||
|
Self::set_device_attribute(
|
||||||
|
&its_fd,
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||||
|
u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE),
|
||||||
|
&self.msi_addr as *const u64 as u64,
|
||||||
|
0,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Self::set_device_attribute(
|
||||||
|
&its_fd,
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||||
|
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
self.its_device = Some(its_fd);
|
||||||
|
|
||||||
|
/* We need to tell the kernel how many irqs to support with this vgic.
|
||||||
|
* See the `layout` module for details.
|
||||||
|
*/
|
||||||
|
let nr_irqs_ptr = &nr_irqs as *const u32;
|
||||||
|
Self::set_device_attribute(
|
||||||
|
&self.device,
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
|
||||||
|
0,
|
||||||
|
nr_irqs_ptr as u64,
|
||||||
|
0,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
/* Finalize the GIC.
|
||||||
|
* See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
|
||||||
|
*/
|
||||||
|
Self::set_device_attribute(
|
||||||
|
&self.device,
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||||
|
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a KVM Vgic device
|
||||||
|
fn create_device(vm: &KvmVm) -> Result<DeviceFd> {
|
||||||
|
let mut gic_device = kvm_bindings::kvm_create_device {
|
||||||
|
type_: Self::version(),
|
||||||
|
fd: 0,
|
||||||
|
flags: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let device_fd = vm
|
||||||
|
.create_device(&mut gic_device)
|
||||||
|
.map_err(Error::CreateGic)?;
|
||||||
|
|
||||||
|
// We know for sure this is a KVM fd
|
||||||
|
Ok(device_fd.to_kvm().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a GIC device attribute
|
||||||
|
fn set_device_attribute(
|
||||||
|
device: &DeviceFd,
|
||||||
|
group: u32,
|
||||||
|
attr: u64,
|
||||||
|
addr: u64,
|
||||||
|
flags: u32,
|
||||||
|
) -> Result<()> {
|
||||||
|
let attr = kvm_bindings::kvm_device_attr {
|
||||||
|
flags,
|
||||||
|
group,
|
||||||
|
attr,
|
||||||
|
addr,
|
||||||
|
};
|
||||||
|
device.set_device_attr(&attr).map_err(|e| {
|
||||||
|
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Method to initialize the GIC device
|
||||||
|
#[allow(clippy::new_ret_no_self)]
|
||||||
|
pub fn new(vm: &dyn Vm, config: VgicConfig) -> Result<KvmGicV3Its> {
|
||||||
|
// This is inside KVM module
|
||||||
|
let vm = vm.as_any().downcast_ref::<KvmVm>().expect("Wrong VM type?");
|
||||||
|
|
||||||
|
let vgic = Self::create_device(vm)?;
|
||||||
|
|
||||||
|
let mut gic_device = KvmGicV3Its {
|
||||||
|
device: vgic,
|
||||||
|
its_device: None,
|
||||||
|
gicr_typers: vec![0; config.vcpu_count.try_into().unwrap()],
|
||||||
|
dist_addr: config.dist_addr,
|
||||||
|
dist_size: config.dist_size,
|
||||||
|
redists_addr: config.redists_addr,
|
||||||
|
redists_size: config.redists_size,
|
||||||
|
msi_addr: config.msi_addr,
|
||||||
|
msi_size: config.msi_size,
|
||||||
|
vcpu_count: config.vcpu_count,
|
||||||
|
};
|
||||||
|
|
||||||
|
gic_device.init_device_attributes(vm, config.nr_irqs)?;
|
||||||
|
|
||||||
|
Ok(gic_device)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vgic for KvmGicV3Its {
|
||||||
|
fn fdt_compatibility(&self) -> &str {
|
||||||
|
"arm,gic-v3"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn msi_compatible(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn msi_compatibility(&self) -> &str {
|
||||||
|
"arm,gic-v3-its"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fdt_maint_irq(&self) -> u32 {
|
||||||
|
KvmGicV3Its::ARCH_GIC_V3_MAINT_IRQ
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vcpu_count(&self) -> u64 {
|
||||||
|
self.vcpu_count
|
||||||
|
}
|
||||||
|
|
||||||
|
fn device_properties(&self) -> [u64; 4] {
|
||||||
|
[
|
||||||
|
self.dist_addr,
|
||||||
|
self.dist_size,
|
||||||
|
self.redists_addr,
|
||||||
|
self.redists_size,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn msi_properties(&self) -> [u64; 2] {
|
||||||
|
[self.msi_addr, self.msi_size]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
|
||||||
|
let gicr_typers = construct_gicr_typers(vcpu_states);
|
||||||
|
self.gicr_typers = gicr_typers;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_concrete_mut(&mut self) -> &mut dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Save the state of GICv3ITS.
|
||||||
|
fn state(&self) -> Result<Gicv3ItsState> {
|
||||||
|
let gicr_typers = self.gicr_typers.clone();
|
||||||
|
|
||||||
|
let gicd_ctlr = read_ctlr(&self.device)?;
|
||||||
|
|
||||||
|
let dist_state = get_dist_regs(&self.device)?;
|
||||||
|
|
||||||
|
let rdist_state = get_redist_regs(&self.device, &gicr_typers)?;
|
||||||
|
|
||||||
|
let icc_state = get_icc_regs(&self.device, &gicr_typers)?;
|
||||||
|
|
||||||
|
let its_baser_state: [u64; 8] = [0; 8];
|
||||||
|
for i in 0..8 {
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device.as_ref().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_BASER + i * 8,
|
||||||
|
&its_baser_state[i as usize],
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let its_ctlr_state: u64 = 0;
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device.as_ref().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_CTLR,
|
||||||
|
&its_ctlr_state,
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let its_cbaser_state: u64 = 0;
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device.as_ref().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_CBASER,
|
||||||
|
&its_cbaser_state,
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let its_creadr_state: u64 = 0;
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device.as_ref().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_CREADR,
|
||||||
|
&its_creadr_state,
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let its_cwriter_state: u64 = 0;
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device.as_ref().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_CWRITER,
|
||||||
|
&its_cwriter_state,
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let its_iidr_state: u64 = 0;
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device.as_ref().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_IIDR,
|
||||||
|
&its_iidr_state,
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Gicv3ItsState {
|
||||||
|
dist: dist_state,
|
||||||
|
rdist: rdist_state,
|
||||||
|
icc: icc_state,
|
||||||
|
gicd_ctlr,
|
||||||
|
its_ctlr: its_ctlr_state,
|
||||||
|
its_iidr: its_iidr_state,
|
||||||
|
its_cbaser: its_cbaser_state,
|
||||||
|
its_cwriter: its_cwriter_state,
|
||||||
|
its_creadr: its_creadr_state,
|
||||||
|
its_baser: its_baser_state,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Restore the state of GICv3ITS.
|
||||||
|
fn set_state(&mut self, state: &Gicv3ItsState) -> Result<()> {
|
||||||
|
let gicr_typers = self.gicr_typers.clone();
|
||||||
|
|
||||||
|
write_ctlr(&self.device, state.gicd_ctlr)?;
|
||||||
|
|
||||||
|
set_dist_regs(&self.device, &state.dist)?;
|
||||||
|
|
||||||
|
set_redist_regs(&self.device, &gicr_typers, &state.rdist)?;
|
||||||
|
|
||||||
|
set_icc_regs(&self.device, &gicr_typers, &state.icc)?;
|
||||||
|
|
||||||
|
//Restore GICv3ITS registers
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device.as_ref().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_IIDR,
|
||||||
|
&state.its_iidr,
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device.as_ref().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_CBASER,
|
||||||
|
&state.its_cbaser,
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device.as_ref().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_CREADR,
|
||||||
|
&state.its_creadr,
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device.as_ref().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_CWRITER,
|
||||||
|
&state.its_cwriter,
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
for i in 0..8 {
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device.as_ref().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_BASER + i * 8,
|
||||||
|
&state.its_baser[i as usize],
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore ITS tables
|
||||||
|
gicv3_its_tables_access(self.its_device.as_ref().unwrap(), false)?;
|
||||||
|
|
||||||
|
gicv3_its_attr_access(
|
||||||
|
self.its_device.as_ref().unwrap(),
|
||||||
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
|
GITS_CTLR,
|
||||||
|
&state.its_ctlr,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Saves GIC internal data tables into RAM, including:
|
||||||
|
/// - RDIST pending tables
|
||||||
|
/// - ITS tables into guest RAM.
|
||||||
|
fn save_data_tables(&self) -> Result<()> {
|
||||||
|
// Flash RDIST pending tables
|
||||||
|
let init_gic_attr = kvm_bindings::kvm_device_attr {
|
||||||
|
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||||
|
attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES),
|
||||||
|
addr: 0,
|
||||||
|
flags: 0,
|
||||||
|
};
|
||||||
|
self.device.set_device_attr(&init_gic_attr).map_err(|e| {
|
||||||
|
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||||
|
})?;
|
||||||
|
// Flush ITS tables to guest RAM.
|
||||||
|
gicv3_its_tables_access(self.its_device.as_ref().unwrap(), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::aarch64::gic::{
|
||||||
|
get_dist_regs, get_icc_regs, get_redist_regs, set_dist_regs, set_icc_regs, set_redist_regs,
|
||||||
|
};
|
||||||
|
use crate::arch::aarch64::gic::VgicConfig;
|
||||||
|
use crate::kvm::KvmGicV3Its;
|
||||||
|
|
||||||
|
fn create_test_vgic_config() -> VgicConfig {
|
||||||
|
VgicConfig {
|
||||||
|
vcpu_count: 1,
|
||||||
|
dist_addr: 0x0900_0000 - 0x01_0000,
|
||||||
|
dist_size: 0x01_0000,
|
||||||
|
// dist_addr - redists_size
|
||||||
|
redists_addr: 0x0900_0000 - 0x01_0000 - 0x02_0000,
|
||||||
|
redists_size: 0x02_0000,
|
||||||
|
// redists_addr - msi_size
|
||||||
|
msi_addr: 0x0900_0000 - 0x01_0000 - 0x02_0000 - 0x02_0000,
|
||||||
|
msi_size: 0x02_0000,
|
||||||
|
nr_irqs: 256,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_gic() {
|
||||||
|
let hv = crate::new().unwrap();
|
||||||
|
let vm = hv.create_vm().unwrap();
|
||||||
|
|
||||||
|
assert!(KvmGicV3Its::new(&*vm, create_test_vgic_config()).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_set_dist_regs() {
|
||||||
|
let hv = crate::new().unwrap();
|
||||||
|
let vm = hv.create_vm().unwrap();
|
||||||
|
let _ = vm.create_vcpu(0, None).unwrap();
|
||||||
|
let gic = KvmGicV3Its::new(&*vm, create_test_vgic_config()).expect("Cannot create gic");
|
||||||
|
|
||||||
|
let res = get_dist_regs(&gic.device);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
let state = res.unwrap();
|
||||||
|
assert_eq!(state.len(), 568);
|
||||||
|
|
||||||
|
let res = set_dist_regs(&gic.device, &state);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_set_redist_regs() {
|
||||||
|
let hv = crate::new().unwrap();
|
||||||
|
let vm = hv.create_vm().unwrap();
|
||||||
|
let _ = vm.create_vcpu(0, None).unwrap();
|
||||||
|
let gic = KvmGicV3Its::new(&*vm, create_test_vgic_config()).expect("Cannot create gic");
|
||||||
|
|
||||||
|
let gicr_typer = vec![123];
|
||||||
|
let res = get_redist_regs(&gic.device, &gicr_typer);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
let state = res.unwrap();
|
||||||
|
println!("{}", state.len());
|
||||||
|
assert!(state.len() == 24);
|
||||||
|
|
||||||
|
assert!(set_redist_regs(&gic.device, &gicr_typer, &state).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_set_icc_regs() {
|
||||||
|
let hv = crate::new().unwrap();
|
||||||
|
let vm = hv.create_vm().unwrap();
|
||||||
|
let _ = vm.create_vcpu(0, None).unwrap();
|
||||||
|
let gic = KvmGicV3Its::new(&*vm, create_test_vgic_config()).expect("Cannot create gic");
|
||||||
|
|
||||||
|
let gicr_typer = vec![123];
|
||||||
|
let res = get_icc_regs(&gic.device, &gicr_typer);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
let state = res.unwrap();
|
||||||
|
println!("{}", state.len());
|
||||||
|
assert!(state.len() == 9);
|
||||||
|
|
||||||
|
assert!(set_icc_regs(&gic.device, &gicr_typer, &state).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_save_data_tables() {
|
||||||
|
let hv = crate::new().unwrap();
|
||||||
|
let vm = hv.create_vm().unwrap();
|
||||||
|
let _ = vm.create_vcpu(0, None).unwrap();
|
||||||
|
let gic = vm
|
||||||
|
.create_vgic(create_test_vgic_config())
|
||||||
|
.expect("Cannot create gic");
|
||||||
|
|
||||||
|
assert!(gic.lock().unwrap().save_data_tables().is_ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,18 @@
|
|||||||
|
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||||
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use super::{Error, Result};
|
use crate::arch::aarch64::gic::{Error, Result};
|
||||||
use hypervisor::kvm::kvm_bindings::{kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS};
|
use crate::device::HypervisorDeviceError;
|
||||||
use hypervisor::CpuState;
|
use crate::kvm::kvm_bindings::{
|
||||||
use std::sync::Arc;
|
kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, KVM_REG_ARM64, KVM_REG_ARM64_SYSREG,
|
||||||
|
KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK,
|
||||||
|
KVM_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_SIZE_U64,
|
||||||
|
};
|
||||||
|
use crate::kvm::Register;
|
||||||
|
use crate::kvm::VcpuKvmState;
|
||||||
|
use crate::CpuState;
|
||||||
|
use kvm_ioctls::DeviceFd;
|
||||||
|
|
||||||
// Relevant redistributor registers that we want to save/restore.
|
// Relevant redistributor registers that we want to save/restore.
|
||||||
const GICR_CTLR: u32 = 0x0000;
|
const GICR_CTLR: u32 = 0x0000;
|
||||||
@@ -31,6 +39,12 @@ const GICR_ICFGR0: u32 = GICR_SGI_OFFSET + 0x0C00;
|
|||||||
const KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT: u32 = 32;
|
const KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT: u32 = 32;
|
||||||
const KVM_DEV_ARM_VGIC_V3_MPIDR_MASK: u64 = 0xffffffff << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT as u64;
|
const KVM_DEV_ARM_VGIC_V3_MPIDR_MASK: u64 = 0xffffffff << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT as u64;
|
||||||
|
|
||||||
|
const KVM_ARM64_SYSREG_MPIDR_EL1: u64 = KVM_REG_ARM64 as u64
|
||||||
|
| KVM_REG_SIZE_U64 as u64
|
||||||
|
| KVM_REG_ARM64_SYSREG as u64
|
||||||
|
| (((3_u64) << KVM_REG_ARM64_SYSREG_OP0_SHIFT) & KVM_REG_ARM64_SYSREG_OP0_MASK as u64)
|
||||||
|
| (((5_u64) << KVM_REG_ARM64_SYSREG_OP2_SHIFT) & KVM_REG_ARM64_SYSREG_OP2_MASK as u64);
|
||||||
|
|
||||||
/// This is how we represent the registers of a distributor.
|
/// This is how we represent the registers of a distributor.
|
||||||
/// It is relrvant their offset from the base address of the
|
/// It is relrvant their offset from the base address of the
|
||||||
/// distributor.
|
/// distributor.
|
||||||
@@ -82,31 +96,27 @@ static VGIC_SGI_REGS: &[RdistReg] = &[
|
|||||||
VGIC_RDIST_REG!(GICR_IPRIORITYR0, 32),
|
VGIC_RDIST_REG!(GICR_IPRIORITYR0, 32),
|
||||||
];
|
];
|
||||||
|
|
||||||
fn redist_attr_access(
|
fn redist_attr_access(gic: &DeviceFd, offset: u32, typer: u64, val: &u32, set: bool) -> Result<()> {
|
||||||
gic: &Arc<dyn hypervisor::Device>,
|
let mut gic_redist_attr = kvm_device_attr {
|
||||||
offset: u32,
|
|
||||||
typer: u64,
|
|
||||||
val: &u32,
|
|
||||||
set: bool,
|
|
||||||
) -> Result<()> {
|
|
||||||
let mut gic_dist_attr = kvm_device_attr {
|
|
||||||
group: KVM_DEV_ARM_VGIC_GRP_REDIST_REGS,
|
group: KVM_DEV_ARM_VGIC_GRP_REDIST_REGS,
|
||||||
attr: (typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | (offset as u64), // this needs the mpidr
|
attr: (typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | (offset as u64), // this needs the mpidr
|
||||||
addr: val as *const u32 as u64,
|
addr: val as *const u32 as u64,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
if set {
|
if set {
|
||||||
gic.set_device_attr(&gic_dist_attr)
|
gic.set_device_attr(&gic_redist_attr).map_err(|e| {
|
||||||
.map_err(Error::SetDeviceAttribute)?;
|
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||||
|
})?;
|
||||||
} else {
|
} else {
|
||||||
gic.get_device_attr(&mut gic_dist_attr)
|
gic.get_device_attr(&mut gic_redist_attr).map_err(|e| {
|
||||||
.map_err(Error::GetDeviceAttribute)?;
|
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn access_redists_aux(
|
fn access_redists_aux(
|
||||||
gic: &Arc<dyn hypervisor::Device>,
|
gic: &DeviceFd,
|
||||||
gicr_typer: &[u64],
|
gicr_typer: &[u64],
|
||||||
state: &mut Vec<u32>,
|
state: &mut Vec<u32>,
|
||||||
reg_list: &[RdistReg],
|
reg_list: &[RdistReg],
|
||||||
@@ -136,7 +146,7 @@ fn access_redists_aux(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get redistributor registers.
|
/// Get redistributor registers.
|
||||||
pub fn get_redist_regs(gic: &Arc<dyn hypervisor::Device>, gicr_typer: &[u64]) -> Result<Vec<u32>> {
|
pub fn get_redist_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
|
||||||
let mut state = Vec::new();
|
let mut state = Vec::new();
|
||||||
let mut idx: usize = 0;
|
let mut idx: usize = 0;
|
||||||
access_redists_aux(
|
access_redists_aux(
|
||||||
@@ -153,11 +163,7 @@ pub fn get_redist_regs(gic: &Arc<dyn hypervisor::Device>, gicr_typer: &[u64]) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set redistributor registers.
|
/// Set redistributor registers.
|
||||||
pub fn set_redist_regs(
|
pub fn set_redist_regs(gic: &DeviceFd, gicr_typer: &[u64], state: &[u32]) -> Result<()> {
|
||||||
gic: &Arc<dyn hypervisor::Device>,
|
|
||||||
gicr_typer: &[u64],
|
|
||||||
state: &[u32],
|
|
||||||
) -> Result<()> {
|
|
||||||
let mut idx: usize = 0;
|
let mut idx: usize = 0;
|
||||||
let mut mut_state = state.to_owned();
|
let mut mut_state = state.to_owned();
|
||||||
access_redists_aux(
|
access_redists_aux(
|
||||||
@@ -193,15 +199,16 @@ pub fn construct_gicr_typers(vcpu_states: &[CpuState]) -> Vec<u64> {
|
|||||||
*/
|
*/
|
||||||
let mut gicr_typers: Vec<u64> = Vec::new();
|
let mut gicr_typers: Vec<u64> = Vec::new();
|
||||||
for (index, state) in vcpu_states.iter().enumerate() {
|
for (index, state) in vcpu_states.iter().enumerate() {
|
||||||
let last = {
|
let state: VcpuKvmState = state.clone().into();
|
||||||
if index == vcpu_states.len() - 1 {
|
let last = (index == vcpu_states.len() - 1) as u64;
|
||||||
1
|
// state.sys_regs is a big collection of system registers, including MIPDR_EL1
|
||||||
} else {
|
let mpidr: Vec<Register> = state
|
||||||
0
|
.sys_regs
|
||||||
}
|
.into_iter()
|
||||||
};
|
.filter(|reg| reg.id == KVM_ARM64_SYSREG_MPIDR_EL1)
|
||||||
|
.collect();
|
||||||
//calculate affinity
|
//calculate affinity
|
||||||
let mut cpu_affid = state.mpidr & 1095233437695;
|
let mut cpu_affid = mpidr[0].addr & 1095233437695;
|
||||||
cpu_affid = ((cpu_affid & 0xFF00000000) >> 8) | (cpu_affid & 0xFFFFFF);
|
cpu_affid = ((cpu_affid & 0xFF00000000) >> 8) | (cpu_affid & 0xFFFFFF);
|
||||||
gicr_typers.push((cpu_affid << 32) | (1 << 24) | (index as u64) << 8 | (last << 4));
|
gicr_typers.push((cpu_affid << 32) | (1 << 24) | (index as u64) << 8 | (last << 4));
|
||||||
}
|
}
|
||||||
@@ -8,22 +8,17 @@
|
|||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
///
|
pub mod gic;
|
||||||
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
|
|
||||||
///
|
|
||||||
use crate::kvm::{KvmError, KvmResult};
|
use crate::kvm::{KvmError, KvmResult};
|
||||||
use kvm_bindings::{
|
use kvm_bindings::{
|
||||||
kvm_mp_state, kvm_one_reg, kvm_regs, KVM_REG_ARM64, KVM_REG_ARM64_SYSREG,
|
kvm_mp_state, kvm_one_reg, kvm_regs, KVM_REG_ARM_COPROC_MASK, KVM_REG_ARM_CORE,
|
||||||
KVM_REG_ARM64_SYSREG_CRM_MASK, KVM_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRN_MASK,
|
KVM_REG_SIZE_MASK, KVM_REG_SIZE_U32, KVM_REG_SIZE_U64,
|
||||||
KVM_REG_ARM64_SYSREG_CRN_SHIFT, KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT,
|
|
||||||
KVM_REG_ARM64_SYSREG_OP1_MASK, KVM_REG_ARM64_SYSREG_OP1_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK,
|
|
||||||
KVM_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_ARM_COPROC_MASK, KVM_REG_ARM_CORE, KVM_REG_SIZE_MASK,
|
|
||||||
KVM_REG_SIZE_U32, KVM_REG_SIZE_U64,
|
|
||||||
};
|
};
|
||||||
pub use kvm_bindings::{
|
pub use kvm_bindings::{
|
||||||
kvm_one_reg as Register, kvm_regs as StandardRegisters, kvm_vcpu_init as VcpuInit, RegList,
|
kvm_one_reg as Register, kvm_regs as StandardRegisters, kvm_vcpu_init as VcpuInit, RegList,
|
||||||
};
|
};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
pub use {kvm_ioctls::Cap, kvm_ioctls::Kvm};
|
pub use {kvm_ioctls::Cap, kvm_ioctls::Kvm};
|
||||||
|
|
||||||
// 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
|
||||||
@@ -86,32 +81,6 @@ macro_rules! arm64_core_reg_id {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// This macro computes the ID of a specific ARM64 system register similar to how
|
|
||||||
// the kernel C macro does.
|
|
||||||
// https://elixir.bootlin.com/linux/v4.20.17/source/arch/arm64/include/uapi/asm/kvm.h#L203
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! arm64_sys_reg {
|
|
||||||
($name: tt, $op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => {
|
|
||||||
pub const $name: u64 = KVM_REG_ARM64 as u64
|
|
||||||
| KVM_REG_SIZE_U64 as u64
|
|
||||||
| KVM_REG_ARM64_SYSREG as u64
|
|
||||||
| ((($op0 as u64) << KVM_REG_ARM64_SYSREG_OP0_SHIFT)
|
|
||||||
& KVM_REG_ARM64_SYSREG_OP0_MASK as u64)
|
|
||||||
| ((($op1 as u64) << KVM_REG_ARM64_SYSREG_OP1_SHIFT)
|
|
||||||
& KVM_REG_ARM64_SYSREG_OP1_MASK as u64)
|
|
||||||
| ((($crn as u64) << KVM_REG_ARM64_SYSREG_CRN_SHIFT)
|
|
||||||
& KVM_REG_ARM64_SYSREG_CRN_MASK as u64)
|
|
||||||
| ((($crm as u64) << KVM_REG_ARM64_SYSREG_CRM_SHIFT)
|
|
||||||
& KVM_REG_ARM64_SYSREG_CRM_MASK as u64)
|
|
||||||
| ((($op2 as u64) << KVM_REG_ARM64_SYSREG_OP2_SHIFT)
|
|
||||||
& KVM_REG_ARM64_SYSREG_OP2_MASK as u64);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constant imported from the Linux kernel:
|
|
||||||
// https://elixir.bootlin.com/linux/v4.20.17/source/arch/arm64/include/asm/sysreg.h#L135
|
|
||||||
arm64_sys_reg!(MPIDR_EL1, 3, 0, 0, 0, 5);
|
|
||||||
|
|
||||||
/// Specifies whether a particular register is a system register or not.
|
/// Specifies whether a particular register is a system register or not.
|
||||||
/// The kernel splits the registers on aarch64 in core registers and system registers.
|
/// The kernel splits the registers on aarch64 in core registers and system registers.
|
||||||
/// So, below we get the system registers by checking that they are not core registers.
|
/// So, below we get the system registers by checking that they are not core registers.
|
||||||
@@ -150,8 +119,4 @@ pub struct VcpuKvmState {
|
|||||||
pub mp_state: kvm_mp_state,
|
pub mp_state: kvm_mp_state,
|
||||||
pub core_regs: kvm_regs,
|
pub core_regs: kvm_regs,
|
||||||
pub sys_regs: Vec<kvm_one_reg>,
|
pub sys_regs: Vec<kvm_one_reg>,
|
||||||
// We will be using the mpidr for passing it to the VmState.
|
|
||||||
// The VmState will give this away for saving restoring the icc and redistributor
|
|
||||||
// registers.
|
|
||||||
pub mpidr: u64,
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -8,109 +8,26 @@
|
|||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
use crate::arch::x86::{msr_index, SegmentRegisterOps, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
use crate::arch::x86::{
|
||||||
|
CpuIdEntry, DescriptorTable, FpuState, LapicState, MsrEntry, SegmentRegister, SpecialRegisters,
|
||||||
|
StandardRegisters, CPUID_FLAG_VALID_INDEX,
|
||||||
|
};
|
||||||
use crate::kvm::{Cap, Kvm, KvmError, KvmResult};
|
use crate::kvm::{Cap, Kvm, KvmError, KvmResult};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
|
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
|
||||||
///
|
///
|
||||||
pub use {
|
pub use {
|
||||||
kvm_bindings::kvm_cpuid_entry2 as CpuIdEntry, kvm_bindings::kvm_dtable as DescriptorTable,
|
kvm_bindings::kvm_cpuid_entry2, kvm_bindings::kvm_dtable, kvm_bindings::kvm_fpu,
|
||||||
kvm_bindings::kvm_fpu as FpuState, kvm_bindings::kvm_lapic_state as LapicState,
|
kvm_bindings::kvm_lapic_state, kvm_bindings::kvm_mp_state as MpState,
|
||||||
kvm_bindings::kvm_mp_state as MpState, kvm_bindings::kvm_msr_entry as MsrEntry,
|
kvm_bindings::kvm_msr_entry, kvm_bindings::kvm_regs, kvm_bindings::kvm_segment,
|
||||||
kvm_bindings::kvm_regs as StandardRegisters, kvm_bindings::kvm_segment as SegmentRegister,
|
kvm_bindings::kvm_sregs, kvm_bindings::kvm_vcpu_events as VcpuEvents,
|
||||||
kvm_bindings::kvm_sregs as SpecialRegisters, kvm_bindings::kvm_vcpu_events as VcpuEvents,
|
|
||||||
kvm_bindings::kvm_xcrs as ExtendedControlRegisters, kvm_bindings::kvm_xsave as Xsave,
|
kvm_bindings::kvm_xcrs as ExtendedControlRegisters, kvm_bindings::kvm_xsave as Xsave,
|
||||||
kvm_bindings::CpuId, kvm_bindings::MsrList, kvm_bindings::Msrs as MsrEntries,
|
kvm_bindings::CpuId, kvm_bindings::MsrList, kvm_bindings::Msrs as MsrEntries,
|
||||||
kvm_bindings::KVM_CPUID_FLAG_SIGNIFCANT_INDEX as CPUID_FLAG_VALID_INDEX,
|
kvm_bindings::KVM_CPUID_FLAG_SIGNIFCANT_INDEX,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl SegmentRegisterOps for SegmentRegister {
|
|
||||||
fn segment_type(&self) -> u8 {
|
|
||||||
self.type_
|
|
||||||
}
|
|
||||||
fn set_segment_type(&mut self, val: u8) {
|
|
||||||
self.type_ = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dpl(&self) -> u8 {
|
|
||||||
self.dpl
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_dpl(&mut self, val: u8) {
|
|
||||||
self.dpl = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn present(&self) -> u8 {
|
|
||||||
self.present
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_present(&mut self, val: u8) {
|
|
||||||
self.present = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn long(&self) -> u8 {
|
|
||||||
self.l
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_long(&mut self, val: u8) {
|
|
||||||
self.l = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn avl(&self) -> u8 {
|
|
||||||
self.avl
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_avl(&mut self, val: u8) {
|
|
||||||
self.avl = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn desc_type(&self) -> u8 {
|
|
||||||
self.s
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_desc_type(&mut self, val: u8) {
|
|
||||||
self.s = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn granularity(&self) -> u8 {
|
|
||||||
self.g
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_granularity(&mut self, val: u8) {
|
|
||||||
self.g = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn db(&self) -> u8 {
|
|
||||||
self.db
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_db(&mut self, val: u8) {
|
|
||||||
self.db = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn boot_msr_entries() -> MsrEntries {
|
|
||||||
MsrEntries::from_entries(&[
|
|
||||||
msr!(msr_index::MSR_IA32_SYSENTER_CS),
|
|
||||||
msr!(msr_index::MSR_IA32_SYSENTER_ESP),
|
|
||||||
msr!(msr_index::MSR_IA32_SYSENTER_EIP),
|
|
||||||
msr!(msr_index::MSR_STAR),
|
|
||||||
msr!(msr_index::MSR_CSTAR),
|
|
||||||
msr!(msr_index::MSR_LSTAR),
|
|
||||||
msr!(msr_index::MSR_KERNEL_GS_BASE),
|
|
||||||
msr!(msr_index::MSR_SYSCALL_MASK),
|
|
||||||
msr!(msr_index::MSR_IA32_TSC),
|
|
||||||
msr_data!(
|
|
||||||
msr_index::MSR_IA32_MISC_ENABLE,
|
|
||||||
msr_index::MSR_IA32_MISC_ENABLE_FAST_STRING as u64
|
|
||||||
),
|
|
||||||
msr_data!(msr_index::MSR_MTRRdefType, MTRR_ENABLE | MTRR_MEM_TYPE_WB),
|
|
||||||
])
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Check KVM extension for Linux
|
/// Check KVM extension for Linux
|
||||||
///
|
///
|
||||||
@@ -137,14 +54,275 @@ pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
|
|||||||
}
|
}
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct VcpuKvmState {
|
pub struct VcpuKvmState {
|
||||||
pub cpuid: CpuId,
|
pub cpuid: Vec<CpuIdEntry>,
|
||||||
pub msrs: MsrEntries,
|
pub msrs: Vec<MsrEntry>,
|
||||||
pub vcpu_events: VcpuEvents,
|
pub vcpu_events: VcpuEvents,
|
||||||
pub regs: StandardRegisters,
|
pub regs: kvm_regs,
|
||||||
pub sregs: SpecialRegisters,
|
pub sregs: kvm_sregs,
|
||||||
pub fpu: FpuState,
|
pub fpu: FpuState,
|
||||||
pub lapic_state: LapicState,
|
pub lapic_state: LapicState,
|
||||||
pub xsave: Xsave,
|
pub xsave: Xsave,
|
||||||
pub xcrs: ExtendedControlRegisters,
|
pub xcrs: ExtendedControlRegisters,
|
||||||
pub mp_state: MpState,
|
pub mp_state: MpState,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<StandardRegisters> for kvm_regs {
|
||||||
|
fn from(regs: StandardRegisters) -> Self {
|
||||||
|
Self {
|
||||||
|
rax: regs.rax,
|
||||||
|
rbx: regs.rbx,
|
||||||
|
rcx: regs.rcx,
|
||||||
|
rdx: regs.rdx,
|
||||||
|
rsi: regs.rsi,
|
||||||
|
rdi: regs.rdi,
|
||||||
|
rsp: regs.rsp,
|
||||||
|
rbp: regs.rbp,
|
||||||
|
r8: regs.r8,
|
||||||
|
r9: regs.r9,
|
||||||
|
r10: regs.r10,
|
||||||
|
r11: regs.r11,
|
||||||
|
r12: regs.r12,
|
||||||
|
r13: regs.r13,
|
||||||
|
r14: regs.r14,
|
||||||
|
r15: regs.r15,
|
||||||
|
rip: regs.rip,
|
||||||
|
rflags: regs.rflags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<kvm_regs> for StandardRegisters {
|
||||||
|
fn from(regs: kvm_regs) -> Self {
|
||||||
|
Self {
|
||||||
|
rax: regs.rax,
|
||||||
|
rbx: regs.rbx,
|
||||||
|
rcx: regs.rcx,
|
||||||
|
rdx: regs.rdx,
|
||||||
|
rsi: regs.rsi,
|
||||||
|
rdi: regs.rdi,
|
||||||
|
rsp: regs.rsp,
|
||||||
|
rbp: regs.rbp,
|
||||||
|
r8: regs.r8,
|
||||||
|
r9: regs.r9,
|
||||||
|
r10: regs.r10,
|
||||||
|
r11: regs.r11,
|
||||||
|
r12: regs.r12,
|
||||||
|
r13: regs.r13,
|
||||||
|
r14: regs.r14,
|
||||||
|
r15: regs.r15,
|
||||||
|
rip: regs.rip,
|
||||||
|
rflags: regs.rflags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SegmentRegister> for kvm_segment {
|
||||||
|
fn from(s: SegmentRegister) -> Self {
|
||||||
|
Self {
|
||||||
|
base: s.base,
|
||||||
|
limit: s.limit,
|
||||||
|
selector: s.selector,
|
||||||
|
type_: s.type_,
|
||||||
|
present: s.present,
|
||||||
|
dpl: s.dpl,
|
||||||
|
db: s.db,
|
||||||
|
s: s.s,
|
||||||
|
l: s.l,
|
||||||
|
g: s.g,
|
||||||
|
avl: s.avl,
|
||||||
|
unusable: s.unusable,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<kvm_segment> for SegmentRegister {
|
||||||
|
fn from(s: kvm_segment) -> Self {
|
||||||
|
Self {
|
||||||
|
base: s.base,
|
||||||
|
limit: s.limit,
|
||||||
|
selector: s.selector,
|
||||||
|
type_: s.type_,
|
||||||
|
present: s.present,
|
||||||
|
dpl: s.dpl,
|
||||||
|
db: s.db,
|
||||||
|
s: s.s,
|
||||||
|
l: s.l,
|
||||||
|
g: s.g,
|
||||||
|
avl: s.avl,
|
||||||
|
unusable: s.unusable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DescriptorTable> for kvm_dtable {
|
||||||
|
fn from(dt: DescriptorTable) -> Self {
|
||||||
|
Self {
|
||||||
|
base: dt.base,
|
||||||
|
limit: dt.limit,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<kvm_dtable> for DescriptorTable {
|
||||||
|
fn from(dt: kvm_dtable) -> Self {
|
||||||
|
Self {
|
||||||
|
base: dt.base,
|
||||||
|
limit: dt.limit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SpecialRegisters> for kvm_sregs {
|
||||||
|
fn from(s: SpecialRegisters) -> Self {
|
||||||
|
Self {
|
||||||
|
cs: s.cs.into(),
|
||||||
|
ds: s.ds.into(),
|
||||||
|
es: s.es.into(),
|
||||||
|
fs: s.fs.into(),
|
||||||
|
gs: s.gs.into(),
|
||||||
|
ss: s.ss.into(),
|
||||||
|
tr: s.tr.into(),
|
||||||
|
ldt: s.ldt.into(),
|
||||||
|
gdt: s.gdt.into(),
|
||||||
|
idt: s.idt.into(),
|
||||||
|
cr0: s.cr0,
|
||||||
|
cr2: s.cr2,
|
||||||
|
cr3: s.cr3,
|
||||||
|
cr4: s.cr4,
|
||||||
|
cr8: s.cr8,
|
||||||
|
efer: s.efer,
|
||||||
|
apic_base: s.apic_base,
|
||||||
|
interrupt_bitmap: s.interrupt_bitmap,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<kvm_sregs> for SpecialRegisters {
|
||||||
|
fn from(s: kvm_sregs) -> Self {
|
||||||
|
Self {
|
||||||
|
cs: s.cs.into(),
|
||||||
|
ds: s.ds.into(),
|
||||||
|
es: s.es.into(),
|
||||||
|
fs: s.fs.into(),
|
||||||
|
gs: s.gs.into(),
|
||||||
|
ss: s.ss.into(),
|
||||||
|
tr: s.tr.into(),
|
||||||
|
ldt: s.ldt.into(),
|
||||||
|
gdt: s.gdt.into(),
|
||||||
|
idt: s.idt.into(),
|
||||||
|
cr0: s.cr0,
|
||||||
|
cr2: s.cr2,
|
||||||
|
cr3: s.cr3,
|
||||||
|
cr4: s.cr4,
|
||||||
|
cr8: s.cr8,
|
||||||
|
efer: s.efer,
|
||||||
|
apic_base: s.apic_base,
|
||||||
|
interrupt_bitmap: s.interrupt_bitmap,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CpuIdEntry> for kvm_cpuid_entry2 {
|
||||||
|
fn from(e: CpuIdEntry) -> Self {
|
||||||
|
let flags = if e.flags & CPUID_FLAG_VALID_INDEX != 0 {
|
||||||
|
KVM_CPUID_FLAG_SIGNIFCANT_INDEX
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
function: e.function,
|
||||||
|
index: e.index,
|
||||||
|
flags,
|
||||||
|
eax: e.eax,
|
||||||
|
ebx: e.ebx,
|
||||||
|
ecx: e.ecx,
|
||||||
|
edx: e.edx,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<kvm_cpuid_entry2> for CpuIdEntry {
|
||||||
|
fn from(e: kvm_cpuid_entry2) -> Self {
|
||||||
|
let flags = if e.flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX != 0 {
|
||||||
|
CPUID_FLAG_VALID_INDEX
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
function: e.function,
|
||||||
|
index: e.index,
|
||||||
|
flags,
|
||||||
|
eax: e.eax,
|
||||||
|
ebx: e.ebx,
|
||||||
|
ecx: e.ecx,
|
||||||
|
edx: e.edx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<kvm_fpu> for FpuState {
|
||||||
|
fn from(s: kvm_fpu) -> Self {
|
||||||
|
Self {
|
||||||
|
fpr: s.fpr,
|
||||||
|
fcw: s.fcw,
|
||||||
|
fsw: s.fsw,
|
||||||
|
ftwx: s.ftwx,
|
||||||
|
last_opcode: s.last_opcode,
|
||||||
|
last_ip: s.last_ip,
|
||||||
|
last_dp: s.last_dp,
|
||||||
|
xmm: s.xmm,
|
||||||
|
mxcsr: s.mxcsr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FpuState> for kvm_fpu {
|
||||||
|
fn from(s: FpuState) -> Self {
|
||||||
|
Self {
|
||||||
|
fpr: s.fpr,
|
||||||
|
fcw: s.fcw,
|
||||||
|
fsw: s.fsw,
|
||||||
|
ftwx: s.ftwx,
|
||||||
|
last_opcode: s.last_opcode,
|
||||||
|
last_ip: s.last_ip,
|
||||||
|
last_dp: s.last_dp,
|
||||||
|
xmm: s.xmm,
|
||||||
|
mxcsr: s.mxcsr,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LapicState> for kvm_lapic_state {
|
||||||
|
fn from(s: LapicState) -> Self {
|
||||||
|
Self { regs: s.regs }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<kvm_lapic_state> for LapicState {
|
||||||
|
fn from(s: kvm_lapic_state) -> Self {
|
||||||
|
Self { regs: s.regs }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<kvm_msr_entry> for MsrEntry {
|
||||||
|
fn from(e: kvm_msr_entry) -> Self {
|
||||||
|
Self {
|
||||||
|
index: e.index,
|
||||||
|
data: e.data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MsrEntry> for kvm_msr_entry {
|
||||||
|
fn from(e: MsrEntry) -> Self {
|
||||||
|
Self {
|
||||||
|
index: e.index,
|
||||||
|
data: e.data,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
//! - arm64
|
//! - arm64
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
#![allow(clippy::significant_drop_in_scrutinee)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate anyhow;
|
extern crate anyhow;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
@@ -37,10 +39,10 @@ pub mod kvm;
|
|||||||
pub mod mshv;
|
pub mod mshv;
|
||||||
|
|
||||||
/// Hypevisor related module
|
/// Hypevisor related module
|
||||||
pub mod hypervisor;
|
mod hypervisor;
|
||||||
|
|
||||||
/// Vm related module
|
/// Vm related module
|
||||||
pub mod vm;
|
mod vm;
|
||||||
|
|
||||||
/// CPU related module
|
/// CPU related module
|
||||||
mod cpu;
|
mod cpu;
|
||||||
@@ -48,27 +50,37 @@ mod cpu;
|
|||||||
/// Device related module
|
/// Device related module
|
||||||
mod device;
|
mod device;
|
||||||
|
|
||||||
pub use crate::hypervisor::{Hypervisor, HypervisorError};
|
|
||||||
pub use cpu::{HypervisorCpuError, Vcpu, VmExit};
|
pub use cpu::{HypervisorCpuError, Vcpu, VmExit};
|
||||||
pub use device::{Device, HypervisorDeviceError};
|
pub use device::HypervisorDeviceError;
|
||||||
#[cfg(feature = "tdx")]
|
pub use hypervisor::{Hypervisor, HypervisorError};
|
||||||
pub use kvm::TdxCapabilities;
|
#[cfg(all(feature = "kvm", target_arch = "aarch64"))]
|
||||||
#[cfg(feature = "kvm")]
|
pub use kvm::{aarch64, GicState};
|
||||||
pub use kvm::*;
|
|
||||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
|
||||||
pub use mshv::*;
|
|
||||||
pub use vm::{DataMatch, HypervisorVmError, Vm};
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
pub use vm::{
|
||||||
|
DataMatch, HypervisorVmError, InterruptSourceConfig, LegacyIrqSourceConfig, MsiIrqSourceConfig,
|
||||||
|
Vm, VmOps,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum HypervisorType {
|
||||||
|
Kvm,
|
||||||
|
Mshv,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new() -> std::result::Result<Arc<dyn Hypervisor>, HypervisorError> {
|
pub fn new() -> std::result::Result<Arc<dyn Hypervisor>, HypervisorError> {
|
||||||
#[cfg(feature = "kvm")]
|
#[cfg(feature = "kvm")]
|
||||||
let hv = kvm::KvmHypervisor::new()?;
|
if kvm::KvmHypervisor::is_available()? {
|
||||||
|
return kvm::KvmHypervisor::new();
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "mshv")]
|
#[cfg(feature = "mshv")]
|
||||||
let hv = mshv::MshvHypervisor::new()?;
|
if mshv::MshvHypervisor::is_available()? {
|
||||||
|
return mshv::MshvHypervisor::new();
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Arc::new(hv))
|
Err(HypervisorError::HypervisorCreate(anyhow!(
|
||||||
|
"no supported hypervisor"
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a `Vec<T>` with a size in bytes at least as large as `size_in_bytes`.
|
// Returns a `Vec<T>` with a size in bytes at least as large as `size_in_bytes`.
|
||||||
@@ -100,3 +112,75 @@ pub fn vec_with_array_field<T: Default, F>(count: usize) -> Vec<T> {
|
|||||||
let vec_size_bytes = size_of::<T>() + element_space;
|
let vec_size_bytes = size_of::<T>() + element_space;
|
||||||
vec_with_size_in_bytes(vec_size_bytes)
|
vec_with_size_in_bytes(vec_size_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// User memory region structure
|
||||||
|
///
|
||||||
|
#[derive(Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct UserMemoryRegion {
|
||||||
|
pub slot: u32,
|
||||||
|
pub guest_phys_addr: u64,
|
||||||
|
pub memory_size: u64,
|
||||||
|
pub userspace_addr: u64,
|
||||||
|
pub flags: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Flags for user memory region
|
||||||
|
///
|
||||||
|
pub const USER_MEMORY_REGION_READ: u32 = 1;
|
||||||
|
pub const USER_MEMORY_REGION_WRITE: u32 = 1 << 1;
|
||||||
|
pub const USER_MEMORY_REGION_EXECUTE: u32 = 1 << 2;
|
||||||
|
pub const USER_MEMORY_REGION_LOG_DIRTY: u32 = 1 << 3;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum MpState {
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
|
Kvm(kvm_bindings::kvm_mp_state),
|
||||||
|
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||||
|
Mshv, /* MSHV does not supprt MpState yet */
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum IoEventAddress {
|
||||||
|
Pio(u64),
|
||||||
|
Mmio(u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
pub enum CpuState {
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
|
Kvm(kvm::VcpuKvmState),
|
||||||
|
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||||
|
Mshv(mshv::VcpuMshvState),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
pub enum ClockData {
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
|
Kvm(kvm_bindings::kvm_clock_data),
|
||||||
|
#[cfg(feature = "mshv")]
|
||||||
|
Mshv, /* MSHV does not supprt ClockData yet */
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
impl ClockData {
|
||||||
|
pub fn reset_flags(&mut self) {
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
|
ClockData::Kvm(s) => s.flags = 0,
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum IrqRoutingEntry {
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
|
Kvm(kvm_bindings::kvm_irq_routing_entry),
|
||||||
|
#[cfg(feature = "mshv")]
|
||||||
|
Mshv(mshv_bindings::mshv_msi_routing_entry),
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,27 +11,36 @@ use crate::cpu;
|
|||||||
use crate::cpu::Vcpu;
|
use crate::cpu::Vcpu;
|
||||||
use crate::hypervisor;
|
use crate::hypervisor;
|
||||||
use crate::vec_with_array_field;
|
use crate::vec_with_array_field;
|
||||||
use crate::vm::{self, VmmOps};
|
use crate::vm::{self, InterruptSourceConfig, VmOps};
|
||||||
|
use crate::HypervisorType;
|
||||||
pub use mshv_bindings::*;
|
pub use mshv_bindings::*;
|
||||||
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 std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
use vfio_ioctls::VfioDeviceFd;
|
||||||
use vm::DataMatch;
|
use vm::DataMatch;
|
||||||
// x86_64 dependencies
|
// x86_64 dependencies
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub mod x86_64;
|
pub mod x86_64;
|
||||||
use crate::device;
|
use crate::{
|
||||||
|
ClockData, CpuState, IoEventAddress, IrqRoutingEntry, MpState, UserMemoryRegion,
|
||||||
|
USER_MEMORY_REGION_EXECUTE, USER_MEMORY_REGION_READ, USER_MEMORY_REGION_WRITE,
|
||||||
|
};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub use x86_64::VcpuMshvState as CpuState;
|
pub use x86_64::VcpuMshvState;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub use x86_64::*;
|
pub use x86_64::*;
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::AsRawFd;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
use crate::arch::x86::{
|
||||||
|
CpuIdEntry, FpuState, LapicState, MsrEntry, SpecialRegisters, StandardRegisters,
|
||||||
|
};
|
||||||
|
|
||||||
const DIRTY_BITMAP_CLEAR_DIRTY: u64 = 0x4;
|
const DIRTY_BITMAP_CLEAR_DIRTY: u64 = 0x4;
|
||||||
const DIRTY_BITMAP_SET_DIRTY: u64 = 0x8;
|
const DIRTY_BITMAP_SET_DIRTY: u64 = 0x8;
|
||||||
@@ -41,18 +50,108 @@ const DIRTY_BITMAP_SET_DIRTY: u64 = 0x8;
|
|||||||
///
|
///
|
||||||
pub use {
|
pub use {
|
||||||
mshv_bindings::mshv_create_device as CreateDevice,
|
mshv_bindings::mshv_create_device as CreateDevice,
|
||||||
mshv_bindings::mshv_device_attr as DeviceAttr,
|
mshv_bindings::mshv_device_attr as DeviceAttr, mshv_ioctls::DeviceFd,
|
||||||
mshv_bindings::mshv_msi_routing_entry as IrqRoutingEntry, mshv_ioctls::DeviceFd,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const PAGE_SHIFT: usize = 12;
|
pub const PAGE_SHIFT: usize = 12;
|
||||||
|
|
||||||
#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)]
|
impl From<mshv_user_mem_region> for UserMemoryRegion {
|
||||||
pub struct HvState {
|
fn from(region: mshv_user_mem_region) -> Self {
|
||||||
hypercall_page: u64,
|
let mut flags: u32 = 0;
|
||||||
|
if region.flags & HV_MAP_GPA_READABLE != 0 {
|
||||||
|
flags |= USER_MEMORY_REGION_READ;
|
||||||
|
}
|
||||||
|
if region.flags & HV_MAP_GPA_WRITABLE != 0 {
|
||||||
|
flags |= USER_MEMORY_REGION_WRITE;
|
||||||
|
}
|
||||||
|
if region.flags & HV_MAP_GPA_EXECUTABLE != 0 {
|
||||||
|
flags |= USER_MEMORY_REGION_EXECUTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
UserMemoryRegion {
|
||||||
|
guest_phys_addr: (region.guest_pfn << PAGE_SHIFT as u64)
|
||||||
|
+ (region.userspace_addr & ((1 << PAGE_SHIFT) - 1)),
|
||||||
|
memory_size: region.size,
|
||||||
|
userspace_addr: region.userspace_addr,
|
||||||
|
flags,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use HvState as VmState;
|
impl From<UserMemoryRegion> for mshv_user_mem_region {
|
||||||
|
fn from(region: UserMemoryRegion) -> Self {
|
||||||
|
let mut flags: u32 = 0;
|
||||||
|
if region.flags & USER_MEMORY_REGION_READ != 0 {
|
||||||
|
flags |= HV_MAP_GPA_READABLE;
|
||||||
|
}
|
||||||
|
if region.flags & USER_MEMORY_REGION_WRITE != 0 {
|
||||||
|
flags |= HV_MAP_GPA_WRITABLE;
|
||||||
|
}
|
||||||
|
if region.flags & USER_MEMORY_REGION_EXECUTE != 0 {
|
||||||
|
flags |= HV_MAP_GPA_EXECUTABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
mshv_user_mem_region {
|
||||||
|
guest_pfn: region.guest_phys_addr >> PAGE_SHIFT,
|
||||||
|
size: region.memory_size,
|
||||||
|
userspace_addr: region.userspace_addr,
|
||||||
|
flags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<mshv_ioctls::IoEventAddress> for IoEventAddress {
|
||||||
|
fn from(a: mshv_ioctls::IoEventAddress) -> Self {
|
||||||
|
match a {
|
||||||
|
mshv_ioctls::IoEventAddress::Pio(x) => Self::Pio(x),
|
||||||
|
mshv_ioctls::IoEventAddress::Mmio(x) => Self::Mmio(x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<IoEventAddress> for mshv_ioctls::IoEventAddress {
|
||||||
|
fn from(a: IoEventAddress) -> Self {
|
||||||
|
match a {
|
||||||
|
IoEventAddress::Pio(x) => Self::Pio(x),
|
||||||
|
IoEventAddress::Mmio(x) => Self::Mmio(x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VcpuMshvState> for CpuState {
|
||||||
|
fn from(s: VcpuMshvState) -> Self {
|
||||||
|
CpuState::Mshv(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CpuState> for VcpuMshvState {
|
||||||
|
fn from(s: CpuState) -> Self {
|
||||||
|
match s {
|
||||||
|
CpuState::Mshv(s) => s,
|
||||||
|
/* Needed in case other hypervisors are enabled */
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
_ => panic!("CpuState is not valid"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<mshv_msi_routing_entry> for IrqRoutingEntry {
|
||||||
|
fn from(s: mshv_msi_routing_entry) -> Self {
|
||||||
|
IrqRoutingEntry::Mshv(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<IrqRoutingEntry> for mshv_msi_routing_entry {
|
||||||
|
fn from(e: IrqRoutingEntry) -> Self {
|
||||||
|
match e {
|
||||||
|
IrqRoutingEntry::Mshv(e) => e,
|
||||||
|
/* Needed in case other hypervisors are enabled */
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
_ => panic!("IrqRoutingEntry is not valid"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct MshvDirtyLogSlot {
|
struct MshvDirtyLogSlot {
|
||||||
guest_pfn: u64,
|
guest_pfn: u64,
|
||||||
@@ -64,12 +163,35 @@ pub struct MshvHypervisor {
|
|||||||
mshv: Mshv,
|
mshv: Mshv,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MshvHypervisor {
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
///
|
||||||
|
/// Retrieve the list of MSRs supported by MSHV.
|
||||||
|
///
|
||||||
|
fn get_msr_list(&self) -> hypervisor::Result<MsrList> {
|
||||||
|
self.mshv
|
||||||
|
.get_msr_index_list()
|
||||||
|
.map_err(|e| hypervisor::HypervisorError::GetMsrList(e.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl MshvHypervisor {
|
impl MshvHypervisor {
|
||||||
/// Create a hypervisor based on Mshv
|
/// Create a hypervisor based on Mshv
|
||||||
pub fn new() -> hypervisor::Result<MshvHypervisor> {
|
#[allow(clippy::new_ret_no_self)]
|
||||||
|
pub fn new() -> hypervisor::Result<Arc<dyn hypervisor::Hypervisor>> {
|
||||||
let mshv_obj =
|
let mshv_obj =
|
||||||
Mshv::new().map_err(|e| hypervisor::HypervisorError::HypervisorCreate(e.into()))?;
|
Mshv::new().map_err(|e| hypervisor::HypervisorError::HypervisorCreate(e.into()))?;
|
||||||
Ok(MshvHypervisor { mshv: mshv_obj })
|
Ok(Arc::new(MshvHypervisor { mshv: mshv_obj }))
|
||||||
|
}
|
||||||
|
/// Check if the hypervisor is available
|
||||||
|
pub fn is_available() -> hypervisor::Result<bool> {
|
||||||
|
match std::fs::metadata("/dev/mshv") {
|
||||||
|
Ok(_) => Ok(true),
|
||||||
|
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(false),
|
||||||
|
Err(err) => Err(hypervisor::HypervisorError::HypervisorAvailableCheck(
|
||||||
|
err.into(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Implementation of Hypervisor trait for Mshv
|
/// Implementation of Hypervisor trait for Mshv
|
||||||
@@ -81,6 +203,12 @@ impl MshvHypervisor {
|
|||||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||||
///
|
///
|
||||||
impl hypervisor::Hypervisor for MshvHypervisor {
|
impl hypervisor::Hypervisor for MshvHypervisor {
|
||||||
|
///
|
||||||
|
/// Returns the type of the hypervisor
|
||||||
|
///
|
||||||
|
fn hypervisor_type(&self) -> HypervisorType {
|
||||||
|
HypervisorType::Mshv
|
||||||
|
}
|
||||||
/// Create a mshv vm object and return the object as Vm trait object
|
/// Create a mshv vm object and return the object as Vm trait object
|
||||||
/// Example
|
/// Example
|
||||||
/// # extern crate hypervisor;
|
/// # extern crate hypervisor;
|
||||||
@@ -108,50 +236,52 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default Microsoft Hypervisor behavior for unimplemented MSR is to
|
||||||
|
// send a fault to the guest if it tries to access it. It is possible
|
||||||
|
// to override this behavior with a more suitable option i.e., ignore
|
||||||
|
// writes from the guest and return zero in attempt to read unimplemented
|
||||||
|
// MSR.
|
||||||
|
fd.set_partition_property(
|
||||||
|
hv_partition_property_code_HV_PARTITION_PROPERTY_UNIMPLEMENTED_MSR_ACTION,
|
||||||
|
hv_unimplemented_msr_action_HV_UNIMPLEMENTED_MSR_ACTION_IGNORE_WRITE_READ_ZERO as u64,
|
||||||
|
)
|
||||||
|
.map_err(|e| hypervisor::HypervisorError::SetPartitionProperty(e.into()))?;
|
||||||
|
|
||||||
let msr_list = self.get_msr_list()?;
|
let msr_list = self.get_msr_list()?;
|
||||||
let num_msrs = msr_list.as_fam_struct_ref().nmsrs as usize;
|
let num_msrs = msr_list.as_fam_struct_ref().nmsrs as usize;
|
||||||
let mut msrs = MsrEntries::new(num_msrs).unwrap();
|
let mut msrs: Vec<MsrEntry> = vec![
|
||||||
|
MsrEntry {
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
num_msrs
|
||||||
|
];
|
||||||
let indices = msr_list.as_slice();
|
let indices = msr_list.as_slice();
|
||||||
let msr_entries = msrs.as_mut_slice();
|
|
||||||
for (pos, index) in indices.iter().enumerate() {
|
for (pos, index) in indices.iter().enumerate() {
|
||||||
msr_entries[pos].index = *index;
|
msrs[pos].index = *index;
|
||||||
}
|
}
|
||||||
let vm_fd = Arc::new(fd);
|
let vm_fd = Arc::new(fd);
|
||||||
|
|
||||||
Ok(Arc::new(MshvVm {
|
Ok(Arc::new(MshvVm {
|
||||||
fd: vm_fd,
|
fd: vm_fd,
|
||||||
msrs,
|
msrs,
|
||||||
hv_state: hv_state_init(),
|
|
||||||
vmmops: None,
|
|
||||||
dirty_log_slots: Arc::new(RwLock::new(HashMap::new())),
|
dirty_log_slots: Arc::new(RwLock::new(HashMap::new())),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
/// Get the supported CpuID
|
/// Get the supported CpuID
|
||||||
///
|
///
|
||||||
fn get_cpuid(&self) -> hypervisor::Result<CpuId> {
|
fn get_cpuid(&self) -> hypervisor::Result<Vec<CpuIdEntry>> {
|
||||||
Ok(CpuId::new(1).unwrap())
|
Ok(Vec::new())
|
||||||
}
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
///
|
|
||||||
/// Retrieve the list of MSRs supported by MSHV.
|
|
||||||
///
|
|
||||||
fn get_msr_list(&self) -> hypervisor::Result<MsrList> {
|
|
||||||
self.mshv
|
|
||||||
.get_msr_index_list()
|
|
||||||
.map_err(|e| hypervisor::HypervisorError::GetMsrList(e.into()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
/// Vcpu struct for Microsoft Hypervisor
|
/// Vcpu struct for Microsoft Hypervisor
|
||||||
pub struct MshvVcpu {
|
pub struct MshvVcpu {
|
||||||
fd: VcpuFd,
|
fd: VcpuFd,
|
||||||
vp_index: u8,
|
vp_index: u8,
|
||||||
cpuid: CpuId,
|
cpuid: Vec<CpuIdEntry>,
|
||||||
msrs: MsrEntries,
|
msrs: Vec<MsrEntry>,
|
||||||
hv_state: Arc<RwLock<HvState>>, // Mshv State
|
vm_ops: Option<Arc<dyn vm::VmOps>>,
|
||||||
vmmops: Option<Arc<dyn vm::VmmOps>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of Vcpu trait for Microsoft Hypervisor
|
/// Implementation of Vcpu trait for Microsoft Hypervisor
|
||||||
@@ -170,17 +300,20 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
/// Returns the vCPU general purpose registers.
|
/// Returns the vCPU general purpose registers.
|
||||||
///
|
///
|
||||||
fn get_regs(&self) -> cpu::Result<StandardRegisters> {
|
fn get_regs(&self) -> cpu::Result<StandardRegisters> {
|
||||||
self.fd
|
Ok(self
|
||||||
|
.fd
|
||||||
.get_regs()
|
.get_regs()
|
||||||
.map_err(|e| cpu::HypervisorCpuError::GetStandardRegs(e.into()))
|
.map_err(|e| cpu::HypervisorCpuError::GetStandardRegs(e.into()))?
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
/// Sets the vCPU general purpose registers.
|
/// Sets the vCPU general purpose registers.
|
||||||
///
|
///
|
||||||
fn set_regs(&self, regs: &StandardRegisters) -> cpu::Result<()> {
|
fn set_regs(&self, regs: &StandardRegisters) -> cpu::Result<()> {
|
||||||
|
let regs = (*regs).into();
|
||||||
self.fd
|
self.fd
|
||||||
.set_regs(regs)
|
.set_regs(®s)
|
||||||
.map_err(|e| cpu::HypervisorCpuError::SetStandardRegs(e.into()))
|
.map_err(|e| cpu::HypervisorCpuError::SetStandardRegs(e.into()))
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
@@ -188,17 +321,20 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
/// Returns the vCPU special registers.
|
/// Returns the vCPU special registers.
|
||||||
///
|
///
|
||||||
fn get_sregs(&self) -> cpu::Result<SpecialRegisters> {
|
fn get_sregs(&self) -> cpu::Result<SpecialRegisters> {
|
||||||
self.fd
|
Ok(self
|
||||||
|
.fd
|
||||||
.get_sregs()
|
.get_sregs()
|
||||||
.map_err(|e| cpu::HypervisorCpuError::GetSpecialRegs(e.into()))
|
.map_err(|e| cpu::HypervisorCpuError::GetSpecialRegs(e.into()))?
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
/// Sets the vCPU special registers.
|
/// Sets the vCPU special registers.
|
||||||
///
|
///
|
||||||
fn set_sregs(&self, sregs: &SpecialRegisters) -> cpu::Result<()> {
|
fn set_sregs(&self, sregs: &SpecialRegisters) -> cpu::Result<()> {
|
||||||
|
let sregs = (*sregs).into();
|
||||||
self.fd
|
self.fd
|
||||||
.set_sregs(sregs)
|
.set_sregs(&sregs)
|
||||||
.map_err(|e| cpu::HypervisorCpuError::SetSpecialRegs(e.into()))
|
.map_err(|e| cpu::HypervisorCpuError::SetSpecialRegs(e.into()))
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
@@ -206,17 +342,20 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
/// Returns the floating point state (FPU) from the vCPU.
|
/// Returns the floating point state (FPU) from the vCPU.
|
||||||
///
|
///
|
||||||
fn get_fpu(&self) -> cpu::Result<FpuState> {
|
fn get_fpu(&self) -> cpu::Result<FpuState> {
|
||||||
self.fd
|
Ok(self
|
||||||
|
.fd
|
||||||
.get_fpu()
|
.get_fpu()
|
||||||
.map_err(|e| cpu::HypervisorCpuError::GetFloatingPointRegs(e.into()))
|
.map_err(|e| cpu::HypervisorCpuError::GetFloatingPointRegs(e.into()))?
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
/// Set the floating point state (FPU) of a vCPU.
|
/// Set the floating point state (FPU) of a vCPU.
|
||||||
///
|
///
|
||||||
fn set_fpu(&self, fpu: &FpuState) -> cpu::Result<()> {
|
fn set_fpu(&self, fpu: &FpuState) -> cpu::Result<()> {
|
||||||
|
let fpu: mshv_bindings::FloatingPointUnit = (*fpu).clone().into();
|
||||||
self.fd
|
self.fd
|
||||||
.set_fpu(fpu)
|
.set_fpu(&fpu)
|
||||||
.map_err(|e| cpu::HypervisorCpuError::SetFloatingPointRegs(e.into()))
|
.map_err(|e| cpu::HypervisorCpuError::SetFloatingPointRegs(e.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,60 +363,36 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
///
|
///
|
||||||
/// Returns the model-specific registers (MSR) for this vCPU.
|
/// Returns the model-specific registers (MSR) for this vCPU.
|
||||||
///
|
///
|
||||||
fn get_msrs(&self, msrs: &mut MsrEntries) -> cpu::Result<usize> {
|
fn get_msrs(&self, msrs: &mut Vec<MsrEntry>) -> cpu::Result<usize> {
|
||||||
self.fd
|
let mshv_msrs: Vec<msr_entry> = msrs.iter().map(|e| (*e).into()).collect();
|
||||||
.get_msrs(msrs)
|
let mut mshv_msrs = MsrEntries::from_entries(&mshv_msrs).unwrap();
|
||||||
.map_err(|e| cpu::HypervisorCpuError::GetMsrEntries(e.into()))
|
let succ = self
|
||||||
|
.fd
|
||||||
|
.get_msrs(&mut mshv_msrs)
|
||||||
|
.map_err(|e| cpu::HypervisorCpuError::GetMsrEntries(e.into()))?;
|
||||||
|
|
||||||
|
msrs[..succ].copy_from_slice(
|
||||||
|
&mshv_msrs.as_slice()[..succ]
|
||||||
|
.iter()
|
||||||
|
.map(|e| (*e).into())
|
||||||
|
.collect::<Vec<MsrEntry>>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(succ)
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
/// Setup the model-specific registers (MSR) for this vCPU.
|
/// Setup the model-specific registers (MSR) for this vCPU.
|
||||||
/// Returns the number of MSR entries actually written.
|
/// Returns the number of MSR entries actually written.
|
||||||
///
|
///
|
||||||
fn set_msrs(&self, msrs: &MsrEntries) -> cpu::Result<usize> {
|
fn set_msrs(&self, msrs: &[MsrEntry]) -> cpu::Result<usize> {
|
||||||
|
let mshv_msrs: Vec<msr_entry> = msrs.iter().map(|e| (*e).into()).collect();
|
||||||
|
let mshv_msrs = MsrEntries::from_entries(&mshv_msrs).unwrap();
|
||||||
self.fd
|
self.fd
|
||||||
.set_msrs(msrs)
|
.set_msrs(&mshv_msrs)
|
||||||
.map_err(|e| cpu::HypervisorCpuError::SetMsrEntries(e.into()))
|
.map_err(|e| cpu::HypervisorCpuError::SetMsrEntries(e.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
///
|
|
||||||
/// X86 specific call that returns the vcpu's current "xcrs".
|
|
||||||
///
|
|
||||||
fn get_xcrs(&self) -> cpu::Result<ExtendedControlRegisters> {
|
|
||||||
self.fd
|
|
||||||
.get_xcrs()
|
|
||||||
.map_err(|e| cpu::HypervisorCpuError::GetXcsr(e.into()))
|
|
||||||
}
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
///
|
|
||||||
/// X86 specific call that sets the vcpu's current "xcrs".
|
|
||||||
///
|
|
||||||
fn set_xcrs(&self, xcrs: &ExtendedControlRegisters) -> cpu::Result<()> {
|
|
||||||
self.fd
|
|
||||||
.set_xcrs(xcrs)
|
|
||||||
.map_err(|e| cpu::HypervisorCpuError::SetXcsr(e.into()))
|
|
||||||
}
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
///
|
|
||||||
/// Returns currently pending exceptions, interrupts, and NMIs as well as related
|
|
||||||
/// states of the vcpu.
|
|
||||||
///
|
|
||||||
fn get_vcpu_events(&self) -> cpu::Result<VcpuEvents> {
|
|
||||||
self.fd
|
|
||||||
.get_vcpu_events()
|
|
||||||
.map_err(|e| cpu::HypervisorCpuError::GetVcpuEvents(e.into()))
|
|
||||||
}
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
///
|
|
||||||
/// Sets pending exceptions, interrupts, and NMIs as well as related states
|
|
||||||
/// of the vcpu.
|
|
||||||
///
|
|
||||||
fn set_vcpu_events(&self, events: &VcpuEvents) -> cpu::Result<()> {
|
|
||||||
self.fd
|
|
||||||
.set_vcpu_events(events)
|
|
||||||
.map_err(|e| cpu::HypervisorCpuError::SetVcpuEvents(e.into()))
|
|
||||||
}
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
/// X86 specific call to enable HyperV SynIC
|
/// X86 specific call to enable HyperV SynIC
|
||||||
@@ -355,14 +470,14 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
|
|
||||||
if is_write {
|
if is_write {
|
||||||
let data = (info.rax as u32).to_le_bytes();
|
let data = (info.rax as u32).to_le_bytes();
|
||||||
if let Some(vmmops) = &self.vmmops {
|
if let Some(vm_ops) = &self.vm_ops {
|
||||||
vmmops
|
vm_ops
|
||||||
.pio_write(port.into(), &data[0..len])
|
.pio_write(port.into(), &data[0..len])
|
||||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
|
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let Some(vmmops) = &self.vmmops {
|
if let Some(vm_ops) = &self.vm_ops {
|
||||||
vmmops
|
vm_ops
|
||||||
.pio_read(port.into(), &mut data[0..len])
|
.pio_read(port.into(), &mut data[0..len])
|
||||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
|
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
|
||||||
}
|
}
|
||||||
@@ -452,14 +567,14 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
///
|
///
|
||||||
/// X86 specific call to setup the CPUID registers.
|
/// X86 specific call to setup the CPUID registers.
|
||||||
///
|
///
|
||||||
fn set_cpuid2(&self, _cpuid: &CpuId) -> cpu::Result<()> {
|
fn set_cpuid2(&self, _cpuid: &[CpuIdEntry]) -> cpu::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
/// X86 specific call to retrieve the CPUID registers.
|
/// X86 specific call to retrieve the CPUID registers.
|
||||||
///
|
///
|
||||||
fn get_cpuid2(&self, _num_entries: usize) -> cpu::Result<CpuId> {
|
fn get_cpuid2(&self, _num_entries: usize) -> cpu::Result<Vec<CpuIdEntry>> {
|
||||||
Ok(self.cpuid.clone())
|
Ok(self.cpuid.clone())
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
@@ -467,45 +582,43 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
/// Returns the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
|
/// Returns the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
|
||||||
///
|
///
|
||||||
fn get_lapic(&self) -> cpu::Result<LapicState> {
|
fn get_lapic(&self) -> cpu::Result<LapicState> {
|
||||||
self.fd
|
Ok(self
|
||||||
|
.fd
|
||||||
.get_lapic()
|
.get_lapic()
|
||||||
.map_err(|e| cpu::HypervisorCpuError::GetlapicState(e.into()))
|
.map_err(|e| cpu::HypervisorCpuError::GetlapicState(e.into()))?
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
/// Sets the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
|
/// Sets the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
|
||||||
///
|
///
|
||||||
fn set_lapic(&self, lapic: &LapicState) -> cpu::Result<()> {
|
fn set_lapic(&self, lapic: &LapicState) -> cpu::Result<()> {
|
||||||
|
let lapic: mshv_bindings::LapicState = (*lapic).clone().into();
|
||||||
self.fd
|
self.fd
|
||||||
.set_lapic(lapic)
|
.set_lapic(&lapic)
|
||||||
.map_err(|e| cpu::HypervisorCpuError::SetLapicState(e.into()))
|
.map_err(|e| cpu::HypervisorCpuError::SetLapicState(e.into()))
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
///
|
///
|
||||||
/// X86 specific call that returns the vcpu's current "xsave struct".
|
/// Returns the vcpu's current "multiprocessing state".
|
||||||
///
|
///
|
||||||
fn get_xsave(&self) -> cpu::Result<Xsave> {
|
fn get_mp_state(&self) -> cpu::Result<MpState> {
|
||||||
self.fd
|
Ok(MpState::Mshv)
|
||||||
.get_xsave()
|
|
||||||
.map_err(|e| cpu::HypervisorCpuError::GetXsaveState(e.into()))
|
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
///
|
///
|
||||||
/// X86 specific call that sets the vcpu's current "xsave struct".
|
/// Sets the vcpu's current "multiprocessing state".
|
||||||
///
|
///
|
||||||
fn set_xsave(&self, xsave: &Xsave) -> cpu::Result<()> {
|
fn set_mp_state(&self, _mp_state: MpState) -> cpu::Result<()> {
|
||||||
self.fd
|
Ok(())
|
||||||
.set_xsave(xsave)
|
|
||||||
.map_err(|e| cpu::HypervisorCpuError::SetXsaveState(e.into()))
|
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
/// Set CPU state
|
/// Set CPU state
|
||||||
///
|
///
|
||||||
fn set_state(&self, state: &CpuState) -> cpu::Result<()> {
|
fn set_state(&self, state: &CpuState) -> cpu::Result<()> {
|
||||||
|
let state: VcpuMshvState = state.clone().into();
|
||||||
self.set_msrs(&state.msrs)?;
|
self.set_msrs(&state.msrs)?;
|
||||||
self.set_vcpu_events(&state.vcpu_events)?;
|
self.set_vcpu_events(&state.vcpu_events)?;
|
||||||
self.set_regs(&state.regs)?;
|
self.set_regs(&state.regs.into())?;
|
||||||
self.set_sregs(&state.sregs)?;
|
self.set_sregs(&state.sregs.into())?;
|
||||||
self.set_fpu(&state.fpu)?;
|
self.set_fpu(&state.fpu)?;
|
||||||
self.set_xcrs(&state.xcrs)?;
|
self.set_xcrs(&state.xcrs)?;
|
||||||
self.set_lapic(&state.lapic)?;
|
self.set_lapic(&state.lapic)?;
|
||||||
@@ -544,18 +657,19 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
.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(VcpuMshvState {
|
||||||
msrs,
|
msrs,
|
||||||
vcpu_events,
|
vcpu_events,
|
||||||
regs,
|
regs: regs.into(),
|
||||||
sregs,
|
sregs: sregs.into(),
|
||||||
fpu,
|
fpu,
|
||||||
xcrs,
|
xcrs,
|
||||||
lapic,
|
lapic,
|
||||||
dbg,
|
dbg,
|
||||||
xsave,
|
xsave,
|
||||||
misc,
|
misc,
|
||||||
})
|
}
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
@@ -575,42 +689,83 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
/// X86 specific call that returns the vcpu's current "suspend registers".
|
/// Return the list of initial MSR entries for a VCPU
|
||||||
///
|
///
|
||||||
fn get_suspend_regs(&self) -> cpu::Result<SuspendRegisters> {
|
fn boot_msr_entries(&self) -> Vec<MsrEntry> {
|
||||||
self.fd
|
use crate::arch::x86::{msr_index, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
||||||
.get_suspend_regs()
|
|
||||||
.map_err(|e| cpu::HypervisorCpuError::GetSuspendRegs(e.into()))
|
[
|
||||||
|
msr!(msr_index::MSR_IA32_SYSENTER_CS),
|
||||||
|
msr!(msr_index::MSR_IA32_SYSENTER_ESP),
|
||||||
|
msr!(msr_index::MSR_IA32_SYSENTER_EIP),
|
||||||
|
msr!(msr_index::MSR_STAR),
|
||||||
|
msr!(msr_index::MSR_CSTAR),
|
||||||
|
msr!(msr_index::MSR_LSTAR),
|
||||||
|
msr!(msr_index::MSR_KERNEL_GS_BASE),
|
||||||
|
msr!(msr_index::MSR_SYSCALL_MASK),
|
||||||
|
msr!(msr_index::MSR_IA32_TSC),
|
||||||
|
msr_data!(msr_index::MSR_MTRRdefType, MTRR_ENABLE | MTRR_MEM_TYPE_WB),
|
||||||
|
]
|
||||||
|
.to_vec()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Device struct for MSHV
|
impl MshvVcpu {
|
||||||
pub struct MshvDevice {
|
#[cfg(target_arch = "x86_64")]
|
||||||
fd: DeviceFd,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl device::Device for MshvDevice {
|
|
||||||
///
|
///
|
||||||
/// Set device attribute
|
/// X86 specific call that returns the vcpu's current "xsave struct".
|
||||||
///
|
///
|
||||||
fn set_device_attr(&self, attr: &DeviceAttr) -> device::Result<()> {
|
fn get_xsave(&self) -> cpu::Result<Xsave> {
|
||||||
self.fd
|
self.fd
|
||||||
.set_device_attr(attr)
|
.get_xsave()
|
||||||
.map_err(|e| device::HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
.map_err(|e| cpu::HypervisorCpuError::GetXsaveState(e.into()))
|
||||||
}
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
/// Get device attribute
|
/// X86 specific call that sets the vcpu's current "xsave struct".
|
||||||
///
|
///
|
||||||
fn get_device_attr(&self, attr: &mut DeviceAttr) -> device::Result<()> {
|
fn set_xsave(&self, xsave: &Xsave) -> cpu::Result<()> {
|
||||||
self.fd
|
self.fd
|
||||||
.get_device_attr(attr)
|
.set_xsave(xsave)
|
||||||
.map_err(|e| device::HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
.map_err(|e| cpu::HypervisorCpuError::SetXsaveState(e.into()))
|
||||||
}
|
}
|
||||||
}
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
///
|
||||||
impl AsRawFd for MshvDevice {
|
/// X86 specific call that returns the vcpu's current "xcrs".
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
///
|
||||||
self.fd.as_raw_fd()
|
fn get_xcrs(&self) -> cpu::Result<ExtendedControlRegisters> {
|
||||||
|
self.fd
|
||||||
|
.get_xcrs()
|
||||||
|
.map_err(|e| cpu::HypervisorCpuError::GetXcsr(e.into()))
|
||||||
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
///
|
||||||
|
/// X86 specific call that sets the vcpu's current "xcrs".
|
||||||
|
///
|
||||||
|
fn set_xcrs(&self, xcrs: &ExtendedControlRegisters) -> cpu::Result<()> {
|
||||||
|
self.fd
|
||||||
|
.set_xcrs(xcrs)
|
||||||
|
.map_err(|e| cpu::HypervisorCpuError::SetXcsr(e.into()))
|
||||||
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
///
|
||||||
|
/// Returns currently pending exceptions, interrupts, and NMIs as well as related
|
||||||
|
/// states of the vcpu.
|
||||||
|
///
|
||||||
|
fn get_vcpu_events(&self) -> cpu::Result<VcpuEvents> {
|
||||||
|
self.fd
|
||||||
|
.get_vcpu_events()
|
||||||
|
.map_err(|e| cpu::HypervisorCpuError::GetVcpuEvents(e.into()))
|
||||||
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
///
|
||||||
|
/// Sets pending exceptions, interrupts, and NMIs as well as related states
|
||||||
|
/// of the vcpu.
|
||||||
|
///
|
||||||
|
fn set_vcpu_events(&self, events: &VcpuEvents) -> cpu::Result<()> {
|
||||||
|
self.fd
|
||||||
|
.set_vcpu_events(events)
|
||||||
|
.map_err(|e| cpu::HypervisorCpuError::SetVcpuEvents(e.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -655,9 +810,9 @@ impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
|||||||
gpa
|
gpa
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(vmmops) = &self.vcpu.vmmops {
|
if let Some(vm_ops) = &self.vcpu.vm_ops {
|
||||||
if vmmops.guest_mem_read(gpa, data).is_err() {
|
if vm_ops.guest_mem_read(gpa, data).is_err() {
|
||||||
vmmops
|
vm_ops
|
||||||
.mmio_read(gpa, data)
|
.mmio_read(gpa, data)
|
||||||
.map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
|
.map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
|
||||||
}
|
}
|
||||||
@@ -675,9 +830,9 @@ impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
|||||||
gpa
|
gpa
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(vmmops) = &self.vcpu.vmmops {
|
if let Some(vm_ops) = &self.vcpu.vm_ops {
|
||||||
if vmmops.guest_mem_write(gpa, data).is_err() {
|
if vm_ops.guest_mem_write(gpa, data).is_err() {
|
||||||
vmmops
|
vm_ops
|
||||||
.mmio_write(gpa, data)
|
.mmio_write(gpa, data)
|
||||||
.map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
|
.map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
|
||||||
}
|
}
|
||||||
@@ -739,19 +894,25 @@ impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
/// Wrapper over Mshv VM ioctls.
|
/// Wrapper over Mshv VM ioctls.
|
||||||
pub struct MshvVm {
|
pub struct MshvVm {
|
||||||
fd: Arc<VmFd>,
|
fd: Arc<VmFd>,
|
||||||
msrs: MsrEntries,
|
msrs: Vec<MsrEntry>,
|
||||||
// Hypervisor State
|
|
||||||
hv_state: Arc<RwLock<HvState>>,
|
|
||||||
vmmops: Option<Arc<dyn vm::VmmOps>>,
|
|
||||||
dirty_log_slots: Arc<RwLock<HashMap<u64, MshvDirtyLogSlot>>>,
|
dirty_log_slots: Arc<RwLock<HashMap<u64, MshvDirtyLogSlot>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hv_state_init() -> Arc<RwLock<HvState>> {
|
impl MshvVm {
|
||||||
Arc::new(RwLock::new(HvState { hypercall_page: 0 }))
|
///
|
||||||
|
/// Creates an in-kernel device.
|
||||||
|
///
|
||||||
|
/// See the documentation for `MSHV_CREATE_DEVICE`.
|
||||||
|
fn create_device(&self, device: &mut CreateDevice) -> vm::Result<VfioDeviceFd> {
|
||||||
|
let device_fd = self
|
||||||
|
.fd
|
||||||
|
.create_device(device)
|
||||||
|
.map_err(|e| vm::HypervisorVmError::CreateDevice(e.into()))?;
|
||||||
|
Ok(VfioDeviceFd::new_from_mshv(device_fd))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -816,7 +977,7 @@ impl vm::Vm for MshvVm {
|
|||||||
fn create_vcpu(
|
fn create_vcpu(
|
||||||
&self,
|
&self,
|
||||||
id: u8,
|
id: u8,
|
||||||
vmmops: Option<Arc<dyn VmmOps>>,
|
vm_ops: Option<Arc<dyn VmOps>>,
|
||||||
) -> vm::Result<Arc<dyn cpu::Vcpu>> {
|
) -> vm::Result<Arc<dyn cpu::Vcpu>> {
|
||||||
let vcpu_fd = self
|
let vcpu_fd = self
|
||||||
.fd
|
.fd
|
||||||
@@ -825,10 +986,9 @@ impl vm::Vm for MshvVm {
|
|||||||
let vcpu = MshvVcpu {
|
let vcpu = MshvVcpu {
|
||||||
fd: vcpu_fd,
|
fd: vcpu_fd,
|
||||||
vp_index: id,
|
vp_index: id,
|
||||||
cpuid: CpuId::new(1).unwrap(),
|
cpuid: Vec::new(),
|
||||||
msrs: self.msrs.clone(),
|
msrs: self.msrs.clone(),
|
||||||
hv_state: self.hv_state.clone(),
|
vm_ops,
|
||||||
vmmops,
|
|
||||||
};
|
};
|
||||||
Ok(Arc::new(vcpu))
|
Ok(Arc::new(vcpu))
|
||||||
}
|
}
|
||||||
@@ -846,6 +1006,7 @@ impl vm::Vm for MshvVm {
|
|||||||
addr: &IoEventAddress,
|
addr: &IoEventAddress,
|
||||||
datamatch: Option<DataMatch>,
|
datamatch: Option<DataMatch>,
|
||||||
) -> vm::Result<()> {
|
) -> vm::Result<()> {
|
||||||
|
let addr = &mshv_ioctls::IoEventAddress::from(*addr);
|
||||||
debug!(
|
debug!(
|
||||||
"register_ioevent fd {} addr {:x?} datamatch {:?}",
|
"register_ioevent fd {} addr {:x?} datamatch {:?}",
|
||||||
fd.as_raw_fd(),
|
fd.as_raw_fd(),
|
||||||
@@ -871,6 +1032,7 @@ impl vm::Vm for MshvVm {
|
|||||||
}
|
}
|
||||||
/// Unregister an event from a certain address it has been previously registered to.
|
/// Unregister an event from a certain address it has been previously registered to.
|
||||||
fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> vm::Result<()> {
|
fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> vm::Result<()> {
|
||||||
|
let addr = &mshv_ioctls::IoEventAddress::from(*addr);
|
||||||
debug!("unregister_ioevent fd {} addr {:x?}", fd.as_raw_fd(), addr);
|
debug!("unregister_ioevent fd {} addr {:x?}", fd.as_raw_fd(), addr);
|
||||||
|
|
||||||
self.fd
|
self.fd
|
||||||
@@ -879,7 +1041,8 @@ impl vm::Vm for MshvVm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a guest physical memory region.
|
/// Creates a guest physical memory region.
|
||||||
fn create_user_memory_region(&self, user_memory_region: MemoryRegion) -> vm::Result<()> {
|
fn create_user_memory_region(&self, user_memory_region: UserMemoryRegion) -> vm::Result<()> {
|
||||||
|
let user_memory_region: mshv_user_mem_region = user_memory_region.into();
|
||||||
// No matter read only or not we keep track the slots.
|
// No matter read only or not we keep track the slots.
|
||||||
// For readonly hypervisor can enable the dirty bits,
|
// For readonly hypervisor can enable the dirty bits,
|
||||||
// but a VM exit happens before setting the dirty bits
|
// but a VM exit happens before setting the dirty bits
|
||||||
@@ -898,7 +1061,8 @@ impl vm::Vm for MshvVm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a guest physical memory region.
|
/// Removes a guest physical memory region.
|
||||||
fn remove_user_memory_region(&self, user_memory_region: MemoryRegion) -> vm::Result<()> {
|
fn remove_user_memory_region(&self, user_memory_region: UserMemoryRegion) -> vm::Result<()> {
|
||||||
|
let user_memory_region: mshv_user_mem_region = user_memory_region.into();
|
||||||
// Remove the corresponding entry from "self.dirty_log_slots" if needed
|
// Remove the corresponding entry from "self.dirty_log_slots" if needed
|
||||||
self.dirty_log_slots
|
self.dirty_log_slots
|
||||||
.write()
|
.write()
|
||||||
@@ -919,7 +1083,7 @@ impl vm::Vm for MshvVm {
|
|||||||
userspace_addr: u64,
|
userspace_addr: u64,
|
||||||
readonly: bool,
|
readonly: bool,
|
||||||
_log_dirty_pages: bool,
|
_log_dirty_pages: bool,
|
||||||
) -> MemoryRegion {
|
) -> UserMemoryRegion {
|
||||||
let mut flags = HV_MAP_GPA_READABLE | HV_MAP_GPA_EXECUTABLE;
|
let mut flags = HV_MAP_GPA_READABLE | HV_MAP_GPA_EXECUTABLE;
|
||||||
if !readonly {
|
if !readonly {
|
||||||
flags |= HV_MAP_GPA_WRITABLE;
|
flags |= HV_MAP_GPA_WRITABLE;
|
||||||
@@ -931,22 +1095,10 @@ impl vm::Vm for MshvVm {
|
|||||||
size: memory_size,
|
size: memory_size,
|
||||||
userspace_addr: userspace_addr as u64,
|
userspace_addr: userspace_addr as u64,
|
||||||
}
|
}
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
fn create_passthrough_device(&self) -> vm::Result<VfioDeviceFd> {
|
||||||
/// 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>> {
|
|
||||||
let mut vfio_dev = mshv_create_device {
|
let mut vfio_dev = mshv_create_device {
|
||||||
type_: mshv_device_type_MSHV_DEV_TYPE_VFIO,
|
type_: mshv_device_type_MSHV_DEV_TYPE_VFIO,
|
||||||
fd: 0,
|
fd: 0,
|
||||||
@@ -957,18 +1109,45 @@ impl vm::Vm for MshvVm {
|
|||||||
.map_err(|e| vm::HypervisorVmError::CreatePassthroughDevice(e.into()))
|
.map_err(|e| vm::HypervisorVmError::CreatePassthroughDevice(e.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Constructs a routing entry
|
||||||
|
///
|
||||||
|
fn make_routing_entry(&self, gsi: u32, config: &InterruptSourceConfig) -> IrqRoutingEntry {
|
||||||
|
match config {
|
||||||
|
InterruptSourceConfig::MsiIrq(cfg) => mshv_msi_routing_entry {
|
||||||
|
gsi,
|
||||||
|
address_lo: cfg.low_addr,
|
||||||
|
address_hi: cfg.high_addr,
|
||||||
|
data: cfg.data,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
_ => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> vm::Result<()> {
|
fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> vm::Result<()> {
|
||||||
let mut msi_routing =
|
let mut msi_routing =
|
||||||
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;
|
||||||
|
|
||||||
|
let entries: Vec<mshv_msi_routing_entry> = entries
|
||||||
|
.iter()
|
||||||
|
.map(|entry| match entry {
|
||||||
|
IrqRoutingEntry::Mshv(e) => *e,
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
_ => panic!("IrqRoutingEntry type is wrong"),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
// SAFETY: msi_routing initialized with entries.len() and now it is being turned into
|
// 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
|
// entries_slice with entries.len() again. It is guaranteed to be large enough to hold
|
||||||
// everything from entries.
|
// 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
|
||||||
@@ -976,19 +1155,6 @@ impl vm::Vm for MshvVm {
|
|||||||
.map_err(|e| vm::HypervisorVmError::SetGsiRouting(e.into()))
|
.map_err(|e| vm::HypervisorVmError::SetGsiRouting(e.into()))
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
/// Get the Vm state. Return VM specific data
|
|
||||||
///
|
|
||||||
fn state(&self) -> vm::Result<VmState> {
|
|
||||||
Ok(*self.hv_state.read().unwrap())
|
|
||||||
}
|
|
||||||
///
|
|
||||||
/// Set the VM state
|
|
||||||
///
|
|
||||||
fn set_state(&self, state: VmState) -> vm::Result<()> {
|
|
||||||
self.hv_state.write().unwrap().hypercall_page = state.hypercall_page;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
///
|
|
||||||
/// Start logging dirty pages
|
/// Start logging dirty pages
|
||||||
///
|
///
|
||||||
fn start_dirty_log(&self) -> vm::Result<()> {
|
fn start_dirty_log(&self) -> vm::Result<()> {
|
||||||
@@ -1026,7 +1192,18 @@ impl vm::Vm for MshvVm {
|
|||||||
)
|
)
|
||||||
.map_err(|e| vm::HypervisorVmError::GetDirtyLog(e.into()))
|
.map_err(|e| vm::HypervisorVmError::GetDirtyLog(e.into()))
|
||||||
}
|
}
|
||||||
|
/// Retrieve guest clock.
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
fn get_clock(&self) -> vm::Result<ClockData> {
|
||||||
|
Ok(ClockData::Mshv)
|
||||||
|
}
|
||||||
|
/// Set guest clock.
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
fn set_clock(&self, _data: &ClockData) -> vm::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
/// Downcast to the underlying MshvVm type
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub use hv_cpuid_entry as CpuIdEntry;
|
|
||||||
|
|
||||||
pub const CPUID_FLAG_VALID_INDEX: u32 = 0;
|
|
||||||
|
|||||||
@@ -7,30 +7,35 @@
|
|||||||
// 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::{
|
||||||
use serde_derive::{Deserialize, Serialize};
|
CpuIdEntry, DescriptorTable, FpuState, LapicState, MsrEntry, SegmentRegister, SpecialRegisters,
|
||||||
|
StandardRegisters,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
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
|
||||||
///
|
///
|
||||||
pub use {
|
pub use {
|
||||||
mshv_bindings::mshv_user_mem_region as MemoryRegion, mshv_bindings::msr_entry as MsrEntry,
|
mshv_bindings::hv_cpuid_entry, mshv_bindings::mshv_user_mem_region as MemoryRegion,
|
||||||
mshv_bindings::CpuId, mshv_bindings::DebugRegisters,
|
mshv_bindings::msr_entry, mshv_bindings::CpuId, mshv_bindings::DebugRegisters,
|
||||||
mshv_bindings::FloatingPointUnit as FpuState, mshv_bindings::LapicState,
|
mshv_bindings::FloatingPointUnit, mshv_bindings::LapicState as MshvLapicState,
|
||||||
mshv_bindings::MiscRegs as MiscRegisters, mshv_bindings::MsrList,
|
mshv_bindings::MiscRegs as MiscRegisters, mshv_bindings::MsrList,
|
||||||
mshv_bindings::Msrs as MsrEntries, mshv_bindings::Msrs, mshv_bindings::SegmentRegister,
|
mshv_bindings::Msrs as MsrEntries, mshv_bindings::Msrs,
|
||||||
mshv_bindings::SpecialRegisters, mshv_bindings::StandardRegisters,
|
mshv_bindings::SegmentRegister as MshvSegmentRegister,
|
||||||
mshv_bindings::SuspendRegisters, mshv_bindings::VcpuEvents, mshv_bindings::XSave as Xsave,
|
mshv_bindings::SpecialRegisters as MshvSpecialRegisters,
|
||||||
|
mshv_bindings::StandardRegisters as MshvStandardRegisters, mshv_bindings::SuspendRegisters,
|
||||||
|
mshv_bindings::TableRegister, mshv_bindings::VcpuEvents, mshv_bindings::XSave as Xsave,
|
||||||
mshv_bindings::Xcrs as ExtendedControlRegisters,
|
mshv_bindings::Xcrs as ExtendedControlRegisters,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct VcpuMshvState {
|
pub struct VcpuMshvState {
|
||||||
pub msrs: MsrEntries,
|
pub msrs: Vec<MsrEntry>,
|
||||||
pub vcpu_events: VcpuEvents,
|
pub vcpu_events: VcpuEvents,
|
||||||
pub regs: StandardRegisters,
|
pub regs: MshvStandardRegisters,
|
||||||
pub sregs: SpecialRegisters,
|
pub sregs: MshvSpecialRegisters,
|
||||||
pub fpu: FpuState,
|
pub fpu: FpuState,
|
||||||
pub xcrs: ExtendedControlRegisters,
|
pub xcrs: ExtendedControlRegisters,
|
||||||
pub lapic: LapicState,
|
pub lapic: LapicState,
|
||||||
@@ -41,10 +46,10 @@ pub struct VcpuMshvState {
|
|||||||
|
|
||||||
impl fmt::Display for VcpuMshvState {
|
impl fmt::Display for VcpuMshvState {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let expected_num_msrs = self.msrs.as_fam_struct_ref().nmsrs as usize;
|
let expected_num_msrs = self.msrs.len();
|
||||||
let mut msr_entries = vec![vec![0; 2]; expected_num_msrs];
|
let mut msr_entries = vec![vec![0; 2]; expected_num_msrs];
|
||||||
|
|
||||||
for (i, entry) in self.msrs.as_slice().iter().enumerate() {
|
for (i, entry) in self.msrs.iter().enumerate() {
|
||||||
msr_entries[i][1] = entry.data;
|
msr_entries[i][1] = entry.data;
|
||||||
msr_entries[i][0] = entry.index as u64;
|
msr_entries[i][0] = entry.index as u64;
|
||||||
}
|
}
|
||||||
@@ -63,87 +68,252 @@ impl fmt::Display for VcpuMshvState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IrqRouting {}
|
impl From<StandardRegisters> for MshvStandardRegisters {
|
||||||
pub enum VcpuExit {}
|
fn from(regs: StandardRegisters) -> Self {
|
||||||
pub struct MpState {}
|
Self {
|
||||||
|
rax: regs.rax,
|
||||||
impl SegmentRegisterOps for SegmentRegister {
|
rbx: regs.rbx,
|
||||||
fn segment_type(&self) -> u8 {
|
rcx: regs.rcx,
|
||||||
self.type_
|
rdx: regs.rdx,
|
||||||
}
|
rsi: regs.rsi,
|
||||||
fn set_segment_type(&mut self, val: u8) {
|
rdi: regs.rdi,
|
||||||
self.type_ = val;
|
rsp: regs.rsp,
|
||||||
}
|
rbp: regs.rbp,
|
||||||
|
r8: regs.r8,
|
||||||
fn dpl(&self) -> u8 {
|
r9: regs.r9,
|
||||||
self.dpl
|
r10: regs.r10,
|
||||||
}
|
r11: regs.r11,
|
||||||
|
r12: regs.r12,
|
||||||
fn set_dpl(&mut self, val: u8) {
|
r13: regs.r13,
|
||||||
self.dpl = val;
|
r14: regs.r14,
|
||||||
}
|
r15: regs.r15,
|
||||||
|
rip: regs.rip,
|
||||||
fn present(&self) -> u8 {
|
rflags: regs.rflags,
|
||||||
self.present
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn set_present(&mut self, val: u8) {
|
|
||||||
self.present = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn long(&self) -> u8 {
|
|
||||||
self.l
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_long(&mut self, val: u8) {
|
|
||||||
self.l = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn avl(&self) -> u8 {
|
|
||||||
self.avl
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_avl(&mut self, val: u8) {
|
|
||||||
self.avl = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn desc_type(&self) -> u8 {
|
|
||||||
self.s
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_desc_type(&mut self, val: u8) {
|
|
||||||
self.s = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn granularity(&self) -> u8 {
|
|
||||||
self.g
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_granularity(&mut self, val: u8) {
|
|
||||||
self.g = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn db(&self) -> u8 {
|
|
||||||
self.db
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_db(&mut self, val: u8) {
|
|
||||||
self.db = val;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn boot_msr_entries() -> MsrEntries {
|
impl From<MshvStandardRegisters> for StandardRegisters {
|
||||||
MsrEntries::from_entries(&[
|
fn from(regs: MshvStandardRegisters) -> Self {
|
||||||
msr!(msr_index::MSR_IA32_SYSENTER_CS),
|
Self {
|
||||||
msr!(msr_index::MSR_IA32_SYSENTER_ESP),
|
rax: regs.rax,
|
||||||
msr!(msr_index::MSR_IA32_SYSENTER_EIP),
|
rbx: regs.rbx,
|
||||||
msr!(msr_index::MSR_STAR),
|
rcx: regs.rcx,
|
||||||
msr!(msr_index::MSR_CSTAR),
|
rdx: regs.rdx,
|
||||||
msr!(msr_index::MSR_LSTAR),
|
rsi: regs.rsi,
|
||||||
msr!(msr_index::MSR_KERNEL_GS_BASE),
|
rdi: regs.rdi,
|
||||||
msr!(msr_index::MSR_SYSCALL_MASK),
|
rsp: regs.rsp,
|
||||||
msr!(msr_index::MSR_IA32_TSC),
|
rbp: regs.rbp,
|
||||||
msr_data!(msr_index::MSR_MTRRdefType, MTRR_ENABLE | MTRR_MEM_TYPE_WB),
|
r8: regs.r8,
|
||||||
])
|
r9: regs.r9,
|
||||||
.unwrap()
|
r10: regs.r10,
|
||||||
|
r11: regs.r11,
|
||||||
|
r12: regs.r12,
|
||||||
|
r13: regs.r13,
|
||||||
|
r14: regs.r14,
|
||||||
|
r15: regs.r15,
|
||||||
|
rip: regs.rip,
|
||||||
|
rflags: regs.rflags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SegmentRegister> for MshvSegmentRegister {
|
||||||
|
fn from(s: SegmentRegister) -> Self {
|
||||||
|
Self {
|
||||||
|
base: s.base,
|
||||||
|
limit: s.limit,
|
||||||
|
selector: s.selector,
|
||||||
|
type_: s.type_,
|
||||||
|
present: s.present,
|
||||||
|
dpl: s.dpl,
|
||||||
|
db: s.db,
|
||||||
|
s: s.s,
|
||||||
|
l: s.l,
|
||||||
|
g: s.g,
|
||||||
|
avl: s.avl,
|
||||||
|
unusable: s.unusable,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MshvSegmentRegister> for SegmentRegister {
|
||||||
|
fn from(s: MshvSegmentRegister) -> Self {
|
||||||
|
Self {
|
||||||
|
base: s.base,
|
||||||
|
limit: s.limit,
|
||||||
|
selector: s.selector,
|
||||||
|
type_: s.type_,
|
||||||
|
present: s.present,
|
||||||
|
dpl: s.dpl,
|
||||||
|
db: s.db,
|
||||||
|
s: s.s,
|
||||||
|
l: s.l,
|
||||||
|
g: s.g,
|
||||||
|
avl: s.avl,
|
||||||
|
unusable: s.unusable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DescriptorTable> for TableRegister {
|
||||||
|
fn from(dt: DescriptorTable) -> Self {
|
||||||
|
Self {
|
||||||
|
base: dt.base,
|
||||||
|
limit: dt.limit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TableRegister> for DescriptorTable {
|
||||||
|
fn from(dt: TableRegister) -> Self {
|
||||||
|
Self {
|
||||||
|
base: dt.base,
|
||||||
|
limit: dt.limit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SpecialRegisters> for MshvSpecialRegisters {
|
||||||
|
fn from(s: SpecialRegisters) -> Self {
|
||||||
|
Self {
|
||||||
|
cs: s.cs.into(),
|
||||||
|
ds: s.ds.into(),
|
||||||
|
es: s.es.into(),
|
||||||
|
fs: s.fs.into(),
|
||||||
|
gs: s.gs.into(),
|
||||||
|
ss: s.ss.into(),
|
||||||
|
tr: s.tr.into(),
|
||||||
|
ldt: s.ldt.into(),
|
||||||
|
gdt: s.gdt.into(),
|
||||||
|
idt: s.idt.into(),
|
||||||
|
cr0: s.cr0,
|
||||||
|
cr2: s.cr2,
|
||||||
|
cr3: s.cr3,
|
||||||
|
cr4: s.cr4,
|
||||||
|
cr8: s.cr8,
|
||||||
|
efer: s.efer,
|
||||||
|
apic_base: s.apic_base,
|
||||||
|
interrupt_bitmap: s.interrupt_bitmap,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MshvSpecialRegisters> for SpecialRegisters {
|
||||||
|
fn from(s: MshvSpecialRegisters) -> Self {
|
||||||
|
Self {
|
||||||
|
cs: s.cs.into(),
|
||||||
|
ds: s.ds.into(),
|
||||||
|
es: s.es.into(),
|
||||||
|
fs: s.fs.into(),
|
||||||
|
gs: s.gs.into(),
|
||||||
|
ss: s.ss.into(),
|
||||||
|
tr: s.tr.into(),
|
||||||
|
ldt: s.ldt.into(),
|
||||||
|
gdt: s.gdt.into(),
|
||||||
|
idt: s.idt.into(),
|
||||||
|
cr0: s.cr0,
|
||||||
|
cr2: s.cr2,
|
||||||
|
cr3: s.cr3,
|
||||||
|
cr4: s.cr4,
|
||||||
|
cr8: s.cr8,
|
||||||
|
efer: s.efer,
|
||||||
|
apic_base: s.apic_base,
|
||||||
|
interrupt_bitmap: s.interrupt_bitmap,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CpuIdEntry> for hv_cpuid_entry {
|
||||||
|
fn from(e: CpuIdEntry) -> Self {
|
||||||
|
Self {
|
||||||
|
function: e.function,
|
||||||
|
index: e.index,
|
||||||
|
flags: e.flags,
|
||||||
|
eax: e.eax,
|
||||||
|
ebx: e.ebx,
|
||||||
|
ecx: e.ecx,
|
||||||
|
edx: e.edx,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<hv_cpuid_entry> for CpuIdEntry {
|
||||||
|
fn from(e: hv_cpuid_entry) -> Self {
|
||||||
|
Self {
|
||||||
|
function: e.function,
|
||||||
|
index: e.index,
|
||||||
|
flags: e.flags,
|
||||||
|
eax: e.eax,
|
||||||
|
ebx: e.ebx,
|
||||||
|
ecx: e.ecx,
|
||||||
|
edx: e.edx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FloatingPointUnit> for FpuState {
|
||||||
|
fn from(s: FloatingPointUnit) -> Self {
|
||||||
|
Self {
|
||||||
|
fpr: s.fpr,
|
||||||
|
fcw: s.fcw,
|
||||||
|
fsw: s.fsw,
|
||||||
|
ftwx: s.ftwx,
|
||||||
|
last_opcode: s.last_opcode,
|
||||||
|
last_ip: s.last_ip,
|
||||||
|
last_dp: s.last_dp,
|
||||||
|
xmm: s.xmm,
|
||||||
|
mxcsr: s.mxcsr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FpuState> for FloatingPointUnit {
|
||||||
|
fn from(s: FpuState) -> Self {
|
||||||
|
Self {
|
||||||
|
fpr: s.fpr,
|
||||||
|
fcw: s.fcw,
|
||||||
|
fsw: s.fsw,
|
||||||
|
ftwx: s.ftwx,
|
||||||
|
last_opcode: s.last_opcode,
|
||||||
|
last_ip: s.last_ip,
|
||||||
|
last_dp: s.last_dp,
|
||||||
|
xmm: s.xmm,
|
||||||
|
mxcsr: s.mxcsr,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LapicState> for MshvLapicState {
|
||||||
|
fn from(s: LapicState) -> Self {
|
||||||
|
Self { regs: s.regs }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MshvLapicState> for LapicState {
|
||||||
|
fn from(s: MshvLapicState) -> Self {
|
||||||
|
Self { regs: s.regs }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<msr_entry> for MsrEntry {
|
||||||
|
fn from(e: msr_entry) -> Self {
|
||||||
|
Self {
|
||||||
|
index: e.index,
|
||||||
|
data: e.data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MsrEntry> for msr_entry {
|
||||||
|
fn from(e: MsrEntry) -> Self {
|
||||||
|
Self {
|
||||||
|
index: e.index,
|
||||||
|
data: e.data,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,23 +10,21 @@
|
|||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use crate::aarch64::VcpuInit;
|
use crate::aarch64::VcpuInit;
|
||||||
use crate::cpu::Vcpu;
|
#[cfg(target_arch = "aarch64")]
|
||||||
use crate::device::Device;
|
use crate::arch::aarch64::gic::{Vgic, VgicConfig};
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
use crate::x86_64::CpuId;
|
use crate::arch::x86::CpuIdEntry;
|
||||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
use crate::cpu::Vcpu;
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::ClockData;
|
use crate::ClockData;
|
||||||
use crate::CreateDevice;
|
use crate::UserMemoryRegion;
|
||||||
#[cfg(feature = "mshv")]
|
use crate::{IoEventAddress, IrqRoutingEntry};
|
||||||
use crate::HvState as VmState;
|
use std::any::Any;
|
||||||
#[cfg(feature = "kvm")]
|
|
||||||
use crate::KvmVmState as VmState;
|
|
||||||
use crate::{IoEventAddress, IrqRoutingEntry, MemoryRegion};
|
|
||||||
#[cfg(feature = "kvm")]
|
|
||||||
use kvm_ioctls::Cap;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
use std::sync::Mutex;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
@@ -210,18 +208,56 @@ pub enum HypervisorVmError {
|
|||||||
///
|
///
|
||||||
#[error("Failed to initialize memory region TDX: {0}")]
|
#[error("Failed to initialize memory region TDX: {0}")]
|
||||||
InitMemRegionTdx(#[source] std::io::Error),
|
InitMemRegionTdx(#[source] std::io::Error),
|
||||||
|
///
|
||||||
|
/// Create Vgic error
|
||||||
|
///
|
||||||
|
#[error("Failed to create Vgic: {0}")]
|
||||||
|
CreateVgic(#[source] anyhow::Error),
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
/// Result type for returning from a function
|
/// Result type for returning from a function
|
||||||
///
|
///
|
||||||
pub type Result<T> = std::result::Result<T, HypervisorVmError>;
|
pub type Result<T> = std::result::Result<T, HypervisorVmError>;
|
||||||
|
|
||||||
|
/// Configuration data for legacy interrupts.
|
||||||
|
///
|
||||||
|
/// On x86 platforms, legacy interrupts means those interrupts routed through PICs or IOAPICs.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct LegacyIrqSourceConfig {
|
||||||
|
pub irqchip: u32,
|
||||||
|
pub pin: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configuration data for MSI/MSI-X interrupts.
|
||||||
|
///
|
||||||
|
/// On x86 platforms, these interrupts are vectors delivered directly to the LAPIC.
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
pub struct MsiIrqSourceConfig {
|
||||||
|
/// High address to delivery message signaled interrupt.
|
||||||
|
pub high_addr: u32,
|
||||||
|
/// Low address to delivery message signaled interrupt.
|
||||||
|
pub low_addr: u32,
|
||||||
|
/// Data to write to delivery message signaled interrupt.
|
||||||
|
pub data: u32,
|
||||||
|
/// Unique ID of the device to delivery message signaled interrupt.
|
||||||
|
pub devid: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configuration data for an interrupt source.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum InterruptSourceConfig {
|
||||||
|
/// Configuration data for Legacy interrupts.
|
||||||
|
LegacyIrq(LegacyIrqSourceConfig),
|
||||||
|
/// Configuration data for PciMsi, PciMsix and generic MSI interrupts.
|
||||||
|
MsiIrq(MsiIrqSourceConfig),
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Trait to represent a Vm
|
/// Trait to represent a Vm
|
||||||
///
|
///
|
||||||
/// 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 + Any {
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
/// Sets the address of the one-page region in the VM's address space.
|
/// Sets the address of the one-page region in the VM's address space.
|
||||||
fn set_identity_map_address(&self, address: u64) -> Result<()>;
|
fn set_identity_map_address(&self, address: u64) -> Result<()>;
|
||||||
@@ -235,7 +271,10 @@ 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<dyn VmmOps>>) -> Result<Arc<dyn Vcpu>>;
|
fn create_vcpu(&self, id: u8, vm_ops: Option<Arc<dyn VmOps>>) -> Result<Arc<dyn Vcpu>>;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
fn create_vgic(&self, config: VgicConfig) -> Result<Arc<Mutex<dyn Vgic>>>;
|
||||||
|
|
||||||
/// 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,
|
||||||
@@ -245,6 +284,8 @@ pub trait Vm: Send + Sync {
|
|||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
/// Unregister an event from a certain address it has been previously registered to.
|
/// Unregister an event from a certain address it has been previously registered to.
|
||||||
fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> Result<()>;
|
fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> Result<()>;
|
||||||
|
// Construct a routing entry
|
||||||
|
fn make_routing_entry(&self, gsi: u32, config: &InterruptSourceConfig) -> IrqRoutingEntry;
|
||||||
/// 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 {create/remove}_user_memory_region
|
/// Creates a memory region structure that can be used with {create/remove}_user_memory_region
|
||||||
@@ -256,15 +297,13 @@ pub trait Vm: Send + Sync {
|
|||||||
userspace_addr: u64,
|
userspace_addr: u64,
|
||||||
readonly: bool,
|
readonly: bool,
|
||||||
log_dirty_pages: bool,
|
log_dirty_pages: bool,
|
||||||
) -> MemoryRegion;
|
) -> UserMemoryRegion;
|
||||||
/// Creates a guest physical memory slot.
|
/// Creates a guest physical memory slot.
|
||||||
fn create_user_memory_region(&self, user_memory_region: MemoryRegion) -> Result<()>;
|
fn create_user_memory_region(&self, user_memory_region: UserMemoryRegion) -> Result<()>;
|
||||||
/// Removes a guest physical memory slot.
|
/// Removes a guest physical memory slot.
|
||||||
fn remove_user_memory_region(&self, user_memory_region: MemoryRegion) -> Result<()>;
|
fn remove_user_memory_region(&self, user_memory_region: UserMemoryRegion) -> Result<()>;
|
||||||
/// Creates an emulated device in the kernel.
|
|
||||||
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.
|
||||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
#[cfg(target_arch = "aarch64")]
|
||||||
fn get_preferred_target(&self, kvi: &mut VcpuInit) -> Result<()>;
|
fn get_preferred_target(&self, kvi: &mut VcpuInit) -> Result<()>;
|
||||||
/// Enable split Irq capability
|
/// Enable split Irq capability
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
@@ -272,20 +311,13 @@ pub trait Vm: Send + Sync {
|
|||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn enable_sgx_attribute(&self, file: File) -> Result<()>;
|
fn enable_sgx_attribute(&self, file: File) -> Result<()>;
|
||||||
/// Retrieve guest clock.
|
/// Retrieve guest clock.
|
||||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn get_clock(&self) -> Result<ClockData>;
|
fn get_clock(&self) -> Result<ClockData>;
|
||||||
/// Set guest clock.
|
/// Set guest clock.
|
||||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn set_clock(&self, data: &ClockData) -> Result<()>;
|
fn set_clock(&self, data: &ClockData) -> Result<()>;
|
||||||
#[cfg(feature = "kvm")]
|
|
||||||
/// Checks if a particular `Cap` is available.
|
|
||||||
fn check_extension(&self, c: Cap) -> bool;
|
|
||||||
/// Create a device that is used for passthrough
|
/// Create a device that is used for passthrough
|
||||||
fn create_passthrough_device(&self) -> Result<Arc<dyn Device>>;
|
fn create_passthrough_device(&self) -> Result<vfio_ioctls::VfioDeviceFd>;
|
||||||
/// Get the Vm state. Return VM specific data
|
|
||||||
fn state(&self) -> Result<VmState>;
|
|
||||||
/// Set the VM state
|
|
||||||
fn set_state(&self, state: VmState) -> Result<()>;
|
|
||||||
/// Start logging dirty pages
|
/// Start logging dirty pages
|
||||||
fn start_dirty_log(&self) -> Result<()>;
|
fn start_dirty_log(&self) -> Result<()>;
|
||||||
/// Stop logging dirty pages
|
/// Stop logging dirty pages
|
||||||
@@ -294,22 +326,30 @@ pub trait Vm: Send + Sync {
|
|||||||
fn get_dirty_log(&self, slot: u32, base_gpa: u64, 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: &[CpuIdEntry], _max_vcpus: u32) -> Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
/// Finalize the configuration of TDX on this VM
|
/// Finalize the configuration of TDX on this VM
|
||||||
fn tdx_finalize(&self) -> Result<()>;
|
fn tdx_finalize(&self) -> Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
/// Initalize a TDX memory region for this VM
|
/// Initalize a TDX memory region for this VM
|
||||||
fn tdx_init_memory_region(
|
fn tdx_init_memory_region(
|
||||||
&self,
|
&self,
|
||||||
host_address: u64,
|
_host_address: u64,
|
||||||
guest_address: u64,
|
_guest_address: u64,
|
||||||
size: u64,
|
_size: u64,
|
||||||
measure: bool,
|
_measure: bool,
|
||||||
) -> Result<()>;
|
) -> Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
/// Downcast to the underlying hypervisor VM type
|
||||||
|
fn as_any(&self) -> &dyn Any;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait VmmOps: Send + Sync {
|
pub trait VmOps: Send + Sync {
|
||||||
fn guest_mem_write(&self, gpa: u64, buf: &[u8]) -> Result<usize>;
|
fn guest_mem_write(&self, gpa: u64, buf: &[u8]) -> Result<usize>;
|
||||||
fn guest_mem_read(&self, gpa: u64, buf: &mut [u8]) -> Result<usize>;
|
fn guest_mem_read(&self, gpa: u64, buf: &mut [u8]) -> Result<usize>;
|
||||||
fn mmio_read(&self, gpa: u64, data: &mut [u8]) -> Result<()>;
|
fn mmio_read(&self, gpa: u64, data: &mut [u8]) -> Result<()>;
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ authors = ["The Chromium OS Authors"]
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
vmm-sys-util = "0.9.0"
|
vmm-sys-util = "0.10.0"
|
||||||
|
|||||||
@@ -6,21 +6,23 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
epoll = "4.3.1"
|
epoll = "4.3.1"
|
||||||
getrandom = "0.2"
|
getrandom = "0.2.7"
|
||||||
libc = "0.2.123"
|
libc = "0.2.133"
|
||||||
log = "0.4.16"
|
log = "0.4.17"
|
||||||
net_gen = { path = "../net_gen" }
|
net_gen = { path = "../net_gen" }
|
||||||
rate_limiter = { path = "../rate_limiter" }
|
rate_limiter = { path = "../rate_limiter" }
|
||||||
serde = "1.0.136"
|
serde = "1.0.145"
|
||||||
|
thiserror = "1.0.37"
|
||||||
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"
|
||||||
virtio-queue = "0.2.0"
|
virtio-queue = "0.6.1"
|
||||||
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
vm-memory = { version = "0.9.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
vmm-sys-util = "0.9.0"
|
vmm-sys-util = "0.10.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
lazy_static = "1.4.0"
|
once_cell = "1.15.0"
|
||||||
pnet = "0.29.0"
|
pnet = "0.31.0"
|
||||||
serde_json = "1.0.79"
|
pnet_datalink = "0.31.0"
|
||||||
|
serde_json = "1.0.85"
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ 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 virtio_queue::Queue;
|
use virtio_queue::{Queue, QueueT};
|
||||||
use vm_memory::{ByteValued, Bytes, GuestMemoryAtomic, GuestMemoryError};
|
use vm_memory::{ByteValued, Bytes, GuestMemoryError};
|
||||||
use vm_virtio::{AccessPlatform, Translatable};
|
use vm_virtio::{AccessPlatform, Translatable};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -58,98 +58,93 @@ impl CtrlQueue {
|
|||||||
|
|
||||||
pub fn process(
|
pub fn process(
|
||||||
&mut self,
|
&mut self,
|
||||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
mem: &GuestMemoryMmap,
|
||||||
|
queue: &mut Queue,
|
||||||
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut used_desc_heads = Vec::new();
|
while let Some(mut desc_chain) = queue.pop_descriptor_chain(mem) {
|
||||||
loop {
|
let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?;
|
||||||
for mut desc_chain in queue.iter().map_err(Error::QueueIterator)? {
|
|
||||||
let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?;
|
|
||||||
|
|
||||||
let ctrl_hdr: ControlHeader = desc_chain
|
let ctrl_hdr: ControlHeader = desc_chain
|
||||||
.memory()
|
.memory()
|
||||||
.read_obj(
|
.read_obj(
|
||||||
ctrl_desc
|
ctrl_desc
|
||||||
.addr()
|
.addr()
|
||||||
.translate_gva(access_platform, ctrl_desc.len() as usize),
|
.translate_gva(access_platform, ctrl_desc.len() as usize),
|
||||||
)
|
)
|
||||||
.map_err(Error::GuestMemory)?;
|
.map_err(Error::GuestMemory)?;
|
||||||
let data_desc = desc_chain.next().ok_or(Error::NoDataDescriptor)?;
|
let data_desc = desc_chain.next().ok_or(Error::NoDataDescriptor)?;
|
||||||
|
|
||||||
let data_desc_addr = data_desc
|
let data_desc_addr = data_desc
|
||||||
.addr()
|
.addr()
|
||||||
.translate_gva(access_platform, data_desc.len() as usize);
|
.translate_gva(access_platform, data_desc.len() as usize);
|
||||||
|
|
||||||
let status_desc = desc_chain.next().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 = desc_chain
|
let queue_pairs = desc_chain
|
||||||
.memory()
|
.memory()
|
||||||
.read_obj::<u16>(data_desc_addr)
|
.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);
|
||||||
false
|
|
||||||
} else if (queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN as u16)
|
|
||||||
|| (queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX as u16)
|
|
||||||
{
|
|
||||||
warn!("Number of MQ pairs out of range: {}", queue_pairs);
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
info!("Number of MQ pairs requested: {}", queue_pairs);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VIRTIO_NET_CTRL_GUEST_OFFLOADS => {
|
|
||||||
let features = desc_chain
|
|
||||||
.memory()
|
|
||||||
.read_obj::<u64>(data_desc_addr)
|
|
||||||
.map_err(Error::GuestMemory)?;
|
|
||||||
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET {
|
|
||||||
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
let mut ok = true;
|
|
||||||
for tap in self.taps.iter_mut() {
|
|
||||||
info!("Reprogramming tap offload with features: {}", features);
|
|
||||||
tap.set_offload(virtio_features_to_tap_offload(features))
|
|
||||||
.map_err(|e| {
|
|
||||||
error!("Error programming tap offload: {:?}", e);
|
|
||||||
ok = false
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
warn!("Unsupported command {:?}", ctrl_hdr);
|
|
||||||
false
|
false
|
||||||
|
} else if (queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN as u16)
|
||||||
|
|| (queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX as u16)
|
||||||
|
{
|
||||||
|
warn!("Number of MQ pairs out of range: {}", queue_pairs);
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
info!("Number of MQ pairs requested: {}", queue_pairs);
|
||||||
|
true
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
VIRTIO_NET_CTRL_GUEST_OFFLOADS => {
|
||||||
|
let features = desc_chain
|
||||||
|
.memory()
|
||||||
|
.read_obj::<u64>(data_desc_addr)
|
||||||
|
.map_err(Error::GuestMemory)?;
|
||||||
|
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET {
|
||||||
|
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
let mut ok = true;
|
||||||
|
for tap in self.taps.iter_mut() {
|
||||||
|
info!("Reprogramming tap offload with features: {}", features);
|
||||||
|
tap.set_offload(virtio_features_to_tap_offload(features))
|
||||||
|
.map_err(|e| {
|
||||||
|
error!("Error programming tap offload: {:?}", e);
|
||||||
|
ok = false
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
warn!("Unsupported command {:?}", ctrl_hdr);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
desc_chain
|
desc_chain
|
||||||
.memory()
|
.memory()
|
||||||
.write_obj(
|
.write_obj(
|
||||||
if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8,
|
if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8,
|
||||||
status_desc
|
status_desc
|
||||||
.addr()
|
.addr()
|
||||||
.translate_gva(access_platform, status_desc.len() as usize),
|
.translate_gva(access_platform, status_desc.len() as usize),
|
||||||
)
|
)
|
||||||
.map_err(Error::GuestMemory)?;
|
.map_err(Error::GuestMemory)?;
|
||||||
let len = ctrl_desc.len() + data_desc.len() + status_desc.len();
|
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() {
|
queue
|
||||||
queue
|
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
|
||||||
.add_used(*desc_index, *len)
|
.map_err(Error::QueueAddUsed)?;
|
||||||
.map_err(Error::QueueAddUsed)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !queue
|
if !queue
|
||||||
.enable_notification()
|
.enable_notification(mem)
|
||||||
.map_err(Error::QueueEnableNotification)?
|
.map_err(Error::QueueEnableNotification)?
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -5,11 +5,6 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
// This is only used by the tests module from tap.rs, but we cannot use #[macro_use] unless the
|
|
||||||
// reference to lazy_static is declared at the root level of the importing crate.
|
|
||||||
#[cfg(test)]
|
|
||||||
#[macro_use]
|
|
||||||
extern crate lazy_static;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
@@ -23,6 +18,7 @@ use std::io::Error as IoError;
|
|||||||
use std::os::raw::c_uint;
|
use std::os::raw::c_uint;
|
||||||
use std::os::unix::io::{FromRawFd, RawFd};
|
use std::os::unix::io::{FromRawFd, RawFd};
|
||||||
use std::{io, mem, net};
|
use std::{io, mem, net};
|
||||||
|
use thiserror::Error;
|
||||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||||
use versionize_derive::Versionize;
|
use versionize_derive::Versionize;
|
||||||
use virtio_bindings::bindings::virtio_net::{
|
use virtio_bindings::bindings::virtio_net::{
|
||||||
@@ -40,9 +36,9 @@ pub use open_tap::{open_tap, Error as OpenTapError};
|
|||||||
pub use queue_pair::{NetCounters, NetQueuePair, NetQueuePairError, RxVirtio, TxVirtio};
|
pub use queue_pair::{NetCounters, NetQueuePair, NetQueuePairError, RxVirtio, TxVirtio};
|
||||||
pub use tap::{Error as TapError, Tap};
|
pub use tap::{Error as TapError, Tap};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Failed to create a socket.
|
#[error("Failed to create a socket: {0}")]
|
||||||
CreateSocket(IoError),
|
CreateSocket(IoError),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,17 +131,19 @@ pub fn build_net_config_space(
|
|||||||
config: &mut VirtioNetConfig,
|
config: &mut VirtioNetConfig,
|
||||||
mac: MacAddr,
|
mac: MacAddr,
|
||||||
num_queues: usize,
|
num_queues: usize,
|
||||||
|
mtu: Option<u16>,
|
||||||
avail_features: &mut u64,
|
avail_features: &mut u64,
|
||||||
) {
|
) {
|
||||||
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(config, num_queues, avail_features);
|
build_net_config_space_with_mq(config, num_queues, mtu, avail_features);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_net_config_space_with_mq(
|
pub fn build_net_config_space_with_mq(
|
||||||
config: &mut VirtioNetConfig,
|
config: &mut VirtioNetConfig,
|
||||||
num_queues: usize,
|
num_queues: usize,
|
||||||
|
mtu: Option<u16>,
|
||||||
avail_features: &mut u64,
|
avail_features: &mut u64,
|
||||||
) {
|
) {
|
||||||
let num_queue_pairs = (num_queues / 2) as u16;
|
let num_queue_pairs = (num_queues / 2) as u16;
|
||||||
@@ -155,6 +153,9 @@ pub fn build_net_config_space_with_mq(
|
|||||||
config.max_virtqueue_pairs = num_queue_pairs;
|
config.max_virtqueue_pairs = num_queue_pairs;
|
||||||
*avail_features |= 1 << VIRTIO_NET_F_MQ;
|
*avail_features |= 1 << VIRTIO_NET_F_MQ;
|
||||||
}
|
}
|
||||||
|
if let Some(mtu) = mtu {
|
||||||
|
config.mtu = mtu;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn virtio_features_to_tap_offload(features: u64) -> c_uint {
|
pub fn virtio_features_to_tap_offload(features: u64) -> c_uint {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use serde::ser::{Serialize, Serializer};
|
|||||||
|
|
||||||
pub const MAC_ADDR_LEN: usize = 6;
|
pub const MAC_ADDR_LEN: usize = 6;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct MacAddr {
|
pub struct MacAddr {
|
||||||
bytes: [u8; MAC_ADDR_LEN],
|
bytes: [u8; MAC_ADDR_LEN],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,30 +6,33 @@ use super::{vnet_hdr_len, MacAddr, Tap, TapError};
|
|||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Failed to convert an hexadecimal string into an integer.
|
#[error("Failed to convert an hexadecimal string into an integer: {0}")]
|
||||||
ConvertHexStringToInt(std::num::ParseIntError),
|
ConvertHexStringToInt(std::num::ParseIntError),
|
||||||
/// Error related to the multiqueue support (no support TAP side).
|
#[error("Error related to the multiqueue support (no support TAP side).")]
|
||||||
MultiQueueNoTapSupport,
|
MultiQueueNoTapSupport,
|
||||||
/// Error related to the multiqueue support (no support device side).
|
#[error("Error related to the multiqueue support (no support device side).")]
|
||||||
MultiQueueNoDeviceSupport,
|
MultiQueueNoDeviceSupport,
|
||||||
/// Failed to read the TAP flags from sysfs.
|
#[error("Failed to read the TAP flags from sysfs: {0}")]
|
||||||
ReadSysfsTunFlags(io::Error),
|
ReadSysfsTunFlags(io::Error),
|
||||||
/// Open tap device failed.
|
#[error("Open tap device failed: {0}")]
|
||||||
TapOpen(TapError),
|
TapOpen(TapError),
|
||||||
/// Setting tap IP failed.
|
#[error("Setting tap IP failed: {0}")]
|
||||||
TapSetIp(TapError),
|
TapSetIp(TapError),
|
||||||
/// Setting tap netmask failed.
|
#[error("Setting tap netmask failed: {0}")]
|
||||||
TapSetNetmask(TapError),
|
TapSetNetmask(TapError),
|
||||||
/// Setting MAC address failed
|
#[error("Setting MAC address failed: {0}")]
|
||||||
TapSetMac(TapError),
|
TapSetMac(TapError),
|
||||||
/// Getting MAC address failed
|
#[error("Getting MAC address failed: {0}")]
|
||||||
TapGetMac(TapError),
|
TapGetMac(TapError),
|
||||||
/// Setting vnet header size failed.
|
#[error("Setting vnet header size failed: {0}")]
|
||||||
TapSetVnetHdrSize(TapError),
|
TapSetVnetHdrSize(TapError),
|
||||||
/// Enabling tap interface failed.
|
#[error("Setting MTU failed: {0}")]
|
||||||
|
TapSetMtu(TapError),
|
||||||
|
#[error("Enabling tap interface failed: {0}")]
|
||||||
TapEnable(TapError),
|
TapEnable(TapError),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +65,7 @@ pub fn open_tap(
|
|||||||
ip_addr: Option<Ipv4Addr>,
|
ip_addr: Option<Ipv4Addr>,
|
||||||
netmask: Option<Ipv4Addr>,
|
netmask: Option<Ipv4Addr>,
|
||||||
host_mac: &mut Option<MacAddr>,
|
host_mac: &mut Option<MacAddr>,
|
||||||
|
mtu: Option<u16>,
|
||||||
num_rx_q: usize,
|
num_rx_q: usize,
|
||||||
flags: Option<i32>,
|
flags: Option<i32>,
|
||||||
) -> Result<Vec<Tap>> {
|
) -> Result<Vec<Tap>> {
|
||||||
@@ -94,6 +98,9 @@ pub fn open_tap(
|
|||||||
} else {
|
} else {
|
||||||
*host_mac = Some(tap.get_mac_addr().map_err(Error::TapGetMac)?)
|
*host_mac = Some(tap.get_mac_addr().map_err(Error::TapGetMac)?)
|
||||||
}
|
}
|
||||||
|
if let Some(mtu) = mtu {
|
||||||
|
tap.set_mtu(mtu as i32).map_err(Error::TapSetMtu)?;
|
||||||
|
}
|
||||||
tap.enable().map_err(Error::TapEnable)?;
|
tap.enable().map_err(Error::TapEnable)?;
|
||||||
|
|
||||||
tap.set_vnet_hdr_size(vnet_hdr_size)
|
tap.set_vnet_hdr_size(vnet_hdr_size)
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ 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 virtio_queue::Queue;
|
use thiserror::Error;
|
||||||
use vm_memory::{Bytes, GuestMemory, GuestMemoryAtomic};
|
use virtio_queue::{Queue, QueueOwnedT, QueueT};
|
||||||
|
use vm_memory::{Bytes, GuestMemory};
|
||||||
use vm_virtio::{AccessPlatform, Translatable};
|
use vm_virtio::{AccessPlatform, Translatable};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -36,104 +37,95 @@ 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<GuestMemoryAtomic<GuestMemoryMmap>>,
|
queue: &mut Queue,
|
||||||
rate_limiter: &mut Option<RateLimiter>,
|
rate_limiter: &mut Option<RateLimiter>,
|
||||||
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
||||||
) -> Result<bool, NetQueuePairError> {
|
) -> Result<bool, NetQueuePairError> {
|
||||||
let mut retry_write = false;
|
let mut retry_write = false;
|
||||||
let mut rate_limit_reached = false;
|
let mut rate_limit_reached = false;
|
||||||
|
|
||||||
loop {
|
while let Some(mut desc_chain) = queue.pop_descriptor_chain(mem) {
|
||||||
let used_desc_head: (u16, u32);
|
if rate_limit_reached {
|
||||||
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 {
|
|
||||||
avail_iter.go_to_previous_position();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut next_desc = desc_chain.next();
|
|
||||||
|
|
||||||
let mut iovecs = Vec::new();
|
|
||||||
while let Some(desc) = next_desc {
|
|
||||||
let desc_addr = desc
|
|
||||||
.addr()
|
|
||||||
.translate_gva(access_platform, desc.len() as usize);
|
|
||||||
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);
|
|
||||||
} else {
|
|
||||||
error!(
|
|
||||||
"Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}",
|
|
||||||
desc_addr.0,
|
|
||||||
desc.len(),
|
|
||||||
desc.is_write_only()
|
|
||||||
);
|
|
||||||
return Err(NetQueuePairError::DescriptorChainInvalid);
|
|
||||||
}
|
|
||||||
next_desc = desc_chain.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
let len = if !iovecs.is_empty() {
|
|
||||||
let result = unsafe {
|
|
||||||
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();
|
|
||||||
|
|
||||||
/* EAGAIN */
|
|
||||||
if e.kind() == std::io::ErrorKind::WouldBlock {
|
|
||||||
avail_iter.go_to_previous_position();
|
|
||||||
retry_write = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
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_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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut next_desc = desc_chain.next();
|
||||||
|
|
||||||
|
let mut iovecs = Vec::new();
|
||||||
|
while let Some(desc) = next_desc {
|
||||||
|
let desc_addr = desc
|
||||||
|
.addr()
|
||||||
|
.translate_gva(access_platform, desc.len() as usize);
|
||||||
|
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);
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
"Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}",
|
||||||
|
desc_addr.0,
|
||||||
|
desc.len(),
|
||||||
|
desc.is_write_only()
|
||||||
|
);
|
||||||
|
return Err(NetQueuePairError::DescriptorChainInvalid);
|
||||||
|
}
|
||||||
|
next_desc = desc_chain.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
let len = if !iovecs.is_empty() {
|
||||||
|
let result = unsafe {
|
||||||
|
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();
|
||||||
|
|
||||||
|
/* EAGAIN */
|
||||||
|
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||||
|
queue.go_to_previous_position();
|
||||||
|
retry_write = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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_frames += Wrapping(1);
|
||||||
|
|
||||||
|
result as u32
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
queue
|
queue
|
||||||
.add_used(used_desc_head.0, used_desc_head.1)
|
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
|
||||||
.map_err(NetQueuePairError::QueueAddUsed)?;
|
.map_err(NetQueuePairError::QueueAddUsed)?;
|
||||||
|
|
||||||
if !queue
|
if !queue
|
||||||
.enable_notification()
|
.enable_notification(mem)
|
||||||
.map_err(NetQueuePairError::QueueEnableNotification)?
|
.map_err(NetQueuePairError::QueueEnableNotification)?
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
@@ -166,125 +158,116 @@ 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<GuestMemoryAtomic<GuestMemoryMmap>>,
|
queue: &mut Queue,
|
||||||
rate_limiter: &mut Option<RateLimiter>,
|
rate_limiter: &mut Option<RateLimiter>,
|
||||||
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
||||||
) -> 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;
|
||||||
|
|
||||||
loop {
|
while let Some(mut desc_chain) = queue.pop_descriptor_chain(mem) {
|
||||||
let used_desc_head: (u16, u32);
|
if rate_limit_reached {
|
||||||
let mut avail_iter = queue
|
exhausted_descs = false;
|
||||||
.iter()
|
queue.go_to_previous_position();
|
||||||
.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()
|
|
||||||
.translate_gva(access_platform, desc.len() as usize),
|
|
||||||
10,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let mut next_desc = Some(desc);
|
|
||||||
|
|
||||||
let mut iovecs = Vec::new();
|
|
||||||
while let Some(desc) = next_desc {
|
|
||||||
let desc_addr = desc
|
|
||||||
.addr()
|
|
||||||
.translate_gva(access_platform, desc.len() as usize);
|
|
||||||
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);
|
|
||||||
} else {
|
|
||||||
error!(
|
|
||||||
"Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}",
|
|
||||||
desc_addr.0,
|
|
||||||
desc.len(),
|
|
||||||
desc.is_write_only()
|
|
||||||
);
|
|
||||||
return Err(NetQueuePairError::DescriptorChainInvalid);
|
|
||||||
}
|
|
||||||
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 desc = desc_chain
|
||||||
|
.next()
|
||||||
|
.ok_or(NetQueuePairError::DescriptorChainTooShort)?;
|
||||||
|
|
||||||
|
let num_buffers_addr = desc_chain
|
||||||
|
.memory()
|
||||||
|
.checked_offset(
|
||||||
|
desc.addr()
|
||||||
|
.translate_gva(access_platform, desc.len() as usize),
|
||||||
|
10,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut next_desc = Some(desc);
|
||||||
|
|
||||||
|
let mut iovecs = Vec::new();
|
||||||
|
while let Some(desc) = next_desc {
|
||||||
|
let desc_addr = desc
|
||||||
|
.addr()
|
||||||
|
.translate_gva(access_platform, desc.len() as usize);
|
||||||
|
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);
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
"Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}",
|
||||||
|
desc_addr.0,
|
||||||
|
desc.len(),
|
||||||
|
desc.is_write_only()
|
||||||
|
);
|
||||||
|
return Err(NetQueuePairError::DescriptorChainInvalid);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
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.
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
queue
|
queue
|
||||||
.add_used(used_desc_head.0, used_desc_head.1)
|
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
|
||||||
.map_err(NetQueuePairError::QueueAddUsed)?;
|
.map_err(NetQueuePairError::QueueAddUsed)?;
|
||||||
|
|
||||||
if !queue
|
if !queue
|
||||||
.enable_notification()
|
.enable_notification(mem)
|
||||||
.map_err(NetQueuePairError::QueueEnableNotification)?
|
.map_err(NetQueuePairError::QueueEnableNotification)?
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
@@ -303,31 +286,31 @@ pub struct NetCounters {
|
|||||||
pub rx_frames: Arc<AtomicU64>,
|
pub rx_frames: Arc<AtomicU64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum NetQueuePairError {
|
pub enum NetQueuePairError {
|
||||||
/// No memory configured
|
#[error("No memory configured.")]
|
||||||
NoMemoryConfigured,
|
NoMemoryConfigured,
|
||||||
/// Error registering listener
|
#[error("Error registering listener: {0}")]
|
||||||
RegisterListener(io::Error),
|
RegisterListener(io::Error),
|
||||||
/// Error unregistering listener
|
#[error("Error unregistering listener: {0}")]
|
||||||
UnregisterListener(io::Error),
|
UnregisterListener(io::Error),
|
||||||
/// Error writing to the TAP device
|
#[error("Error writing to the TAP device: {0}")]
|
||||||
WriteTap(io::Error),
|
WriteTap(io::Error),
|
||||||
/// Error reading from the TAP device
|
#[error("Error reading from the TAP device: {0}")]
|
||||||
ReadTap(io::Error),
|
ReadTap(io::Error),
|
||||||
/// Error related to guest memory
|
#[error("Error related to guest memory: {0}")]
|
||||||
GuestMemory(vm_memory::GuestMemoryError),
|
GuestMemory(vm_memory::GuestMemoryError),
|
||||||
/// Returned an error while iterating through the queue
|
#[error("Returned an error while iterating through the queue: {0}")]
|
||||||
QueueIteratorFailed(virtio_queue::Error),
|
QueueIteratorFailed(virtio_queue::Error),
|
||||||
/// Descriptor chain is too short
|
#[error("Descriptor chain is too short.")]
|
||||||
DescriptorChainTooShort,
|
DescriptorChainTooShort,
|
||||||
/// Descriptor chain does not contain valid descriptors
|
#[error("Descriptor chain does not contain valid descriptors.")]
|
||||||
DescriptorChainInvalid,
|
DescriptorChainInvalid,
|
||||||
/// Failed to determine if queue needed notification
|
#[error("Failed to determine if queue needed notification: {0}")]
|
||||||
QueueNeedsNotification(virtio_queue::Error),
|
QueueNeedsNotification(virtio_queue::Error),
|
||||||
/// Failed to enable notification on the queue
|
#[error("Failed to enable notification on the queue: {0}")]
|
||||||
QueueEnableNotification(virtio_queue::Error),
|
QueueEnableNotification(virtio_queue::Error),
|
||||||
/// Failed to add used index to the queue
|
#[error("Failed to add used index to the queue: {0}")]
|
||||||
QueueAddUsed(virtio_queue::Error),
|
QueueAddUsed(virtio_queue::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,9 +338,11 @@ pub struct NetQueuePair {
|
|||||||
impl NetQueuePair {
|
impl NetQueuePair {
|
||||||
pub fn process_tx(
|
pub fn process_tx(
|
||||||
&mut self,
|
&mut self,
|
||||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
mem: &GuestMemoryMmap,
|
||||||
|
queue: &mut Queue,
|
||||||
) -> Result<bool, NetQueuePairError> {
|
) -> Result<bool, NetQueuePairError> {
|
||||||
let tx_tap_retry = self.tx.process_desc_chain(
|
let tx_tap_retry = self.tx.process_desc_chain(
|
||||||
|
mem,
|
||||||
&mut self.tap,
|
&mut self.tap,
|
||||||
queue,
|
queue,
|
||||||
&mut self.tx_rate_limiter,
|
&mut self.tx_rate_limiter,
|
||||||
@@ -397,15 +382,17 @@ impl NetQueuePair {
|
|||||||
self.tx.counter_frames = Wrapping(0);
|
self.tx.counter_frames = Wrapping(0);
|
||||||
|
|
||||||
queue
|
queue
|
||||||
.needs_notification()
|
.needs_notification(mem)
|
||||||
.map_err(NetQueuePairError::QueueNeedsNotification)
|
.map_err(NetQueuePairError::QueueNeedsNotification)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_rx(
|
pub fn process_rx(
|
||||||
&mut self,
|
&mut self,
|
||||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
mem: &GuestMemoryMmap,
|
||||||
|
queue: &mut Queue,
|
||||||
) -> Result<bool, NetQueuePairError> {
|
) -> Result<bool, NetQueuePairError> {
|
||||||
self.rx_desc_avail = !self.rx.process_desc_chain(
|
self.rx_desc_avail = !self.rx.process_desc_chain(
|
||||||
|
mem,
|
||||||
&mut self.tap,
|
&mut self.tap,
|
||||||
queue,
|
queue,
|
||||||
&mut self.rx_rate_limiter,
|
&mut self.rx_rate_limiter,
|
||||||
@@ -440,7 +427,7 @@ impl NetQueuePair {
|
|||||||
self.rx.counter_frames = Wrapping(0);
|
self.rx.counter_frames = Wrapping(0);
|
||||||
|
|
||||||
queue
|
queue
|
||||||
.needs_notification()
|
.needs_notification(mem)
|
||||||
.map_err(NetQueuePairError::QueueNeedsNotification)
|
.map_err(NetQueuePairError::QueueNeedsNotification)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,24 +15,26 @@ use std::io::{Error as IoError, Read, Result as IoResult, Write};
|
|||||||
use std::net;
|
use std::net;
|
||||||
use std::os::raw::*;
|
use std::os::raw::*;
|
||||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||||
|
use thiserror::Error;
|
||||||
use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val};
|
use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Couldn't open /dev/net/tun.
|
#[error("Couldn't open /dev/net/tun: {0}")]
|
||||||
OpenTun(IoError),
|
OpenTun(IoError),
|
||||||
/// Unable to configure tap interface.
|
#[error("Unable to configure tap interface: {0}")]
|
||||||
ConfigureTap(IoError),
|
ConfigureTap(IoError),
|
||||||
/// Unable to retrieve features.
|
#[error("Unable to retrieve features: {0}")]
|
||||||
GetFeatures(IoError),
|
GetFeatures(IoError),
|
||||||
/// Missing multiqueue support in the kernel.
|
#[error("Missing multiqueue support in the kernel.")]
|
||||||
MultiQueueKernelSupport,
|
MultiQueueKernelSupport,
|
||||||
/// ioctl failed.
|
#[error("ioctl failed: {0}")]
|
||||||
IoctlError(IoError),
|
IoctlError(IoError),
|
||||||
/// Failed to create a socket.
|
#[error("Failed to create a socket: {0}")]
|
||||||
NetUtil(NetUtilError),
|
NetUtil(NetUtilError),
|
||||||
|
#[error("Invalid interface name.")]
|
||||||
InvalidIfname,
|
InvalidIfname,
|
||||||
/// Error parsing MAC data
|
#[error("Error parsing MAC data: {0}")]
|
||||||
MacParsing(IoError),
|
MacParsing(IoError),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,6 +297,37 @@ impl Tap {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mtu(&self) -> Result<i32> {
|
||||||
|
let sock = create_unix_socket().map_err(Error::NetUtil)?;
|
||||||
|
|
||||||
|
let ifreq = self.get_ifreq();
|
||||||
|
|
||||||
|
// ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||||
|
let ret = unsafe { ioctl_with_ref(&sock, net_gen::sockios::SIOCGIFMTU as c_ulong, &ifreq) };
|
||||||
|
if ret < 0 {
|
||||||
|
return Err(Error::IoctlError(IoError::last_os_error()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mtu = unsafe { ifreq.ifr_ifru.ifru_mtu };
|
||||||
|
|
||||||
|
Ok(mtu)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_mtu(&self, mtu: i32) -> Result<()> {
|
||||||
|
let sock = create_unix_socket().map_err(Error::NetUtil)?;
|
||||||
|
|
||||||
|
let mut ifreq = self.get_ifreq();
|
||||||
|
ifreq.ifr_ifru.ifru_mtu = mtu;
|
||||||
|
|
||||||
|
// ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||||
|
let ret = unsafe { ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFMTU as c_ulong, &ifreq) };
|
||||||
|
if ret < 0 {
|
||||||
|
return Err(Error::IoctlError(IoError::last_os_error()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the offload flags for the tap interface.
|
/// Set the offload flags for the tap interface.
|
||||||
pub fn set_offload(&self, flags: c_uint) -> Result<()> {
|
pub fn set_offload(&self, flags: c_uint) -> Result<()> {
|
||||||
// ioctl is safe. Called with a valid tap fd, and we check the return.
|
// ioctl is safe. Called with a valid tap fd, and we check the return.
|
||||||
@@ -321,15 +354,13 @@ impl Tap {
|
|||||||
|
|
||||||
// If TAP device is already up don't try and enable it
|
// If TAP device is already up don't try and enable it
|
||||||
let ifru_flags = unsafe { ifreq.ifr_ifru.ifru_flags };
|
let ifru_flags = unsafe { ifreq.ifr_ifru.ifru_flags };
|
||||||
if ifru_flags
|
if ifru_flags & net_gen::net_device_flags_IFF_UP as i16
|
||||||
& (net_gen::net_device_flags_IFF_UP | net_gen::net_device_flags_IFF_RUNNING) as i16
|
== net_gen::net_device_flags_IFF_UP as i16
|
||||||
== (net_gen::net_device_flags_IFF_UP | net_gen::net_device_flags_IFF_RUNNING) as i16
|
|
||||||
{
|
{
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
ifreq.ifr_ifru.ifru_flags =
|
ifreq.ifr_ifru.ifru_flags = net_gen::net_device_flags_IFF_UP as i16;
|
||||||
(net_gen::net_device_flags_IFF_UP | net_gen::net_device_flags_IFF_RUNNING) as i16;
|
|
||||||
|
|
||||||
// ioctl is safe. Called with a valid sock fd, and we check the return.
|
// ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||||
let ret =
|
let ret =
|
||||||
@@ -395,38 +426,35 @@ impl AsRawFd for Tap {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
extern crate pnet;
|
|
||||||
|
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::sync::{mpsc, Mutex};
|
use std::sync::{mpsc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use self::pnet::datalink::Channel::Ethernet;
|
use once_cell::sync::Lazy;
|
||||||
use self::pnet::datalink::{self, DataLinkReceiver, DataLinkSender, NetworkInterface};
|
|
||||||
use self::pnet::packet::ethernet::{EtherTypes, EthernetPacket, MutableEthernetPacket};
|
use pnet::packet::ethernet::{EtherTypes, EthernetPacket, MutableEthernetPacket};
|
||||||
use self::pnet::packet::ip::IpNextHeaderProtocols;
|
use pnet::packet::ip::IpNextHeaderProtocols;
|
||||||
use self::pnet::packet::ipv4::{Ipv4Packet, MutableIpv4Packet};
|
use pnet::packet::ipv4::{Ipv4Packet, MutableIpv4Packet};
|
||||||
use self::pnet::packet::udp::{MutableUdpPacket, UdpPacket};
|
use pnet::packet::udp::{MutableUdpPacket, UdpPacket};
|
||||||
use self::pnet::packet::{MutablePacket, Packet};
|
use pnet::packet::{MutablePacket, Packet};
|
||||||
use self::pnet::util::MacAddr;
|
use pnet::util::MacAddr;
|
||||||
|
use pnet_datalink::Channel::Ethernet;
|
||||||
|
use pnet_datalink::{DataLinkReceiver, DataLinkSender, NetworkInterface};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
static DATA_STRING: &str = "test for tap";
|
static DATA_STRING: &str = "test for tap";
|
||||||
static SUBNET_MASK: &str = "255.255.255.0";
|
static SUBNET_MASK: &str = "255.255.255.0";
|
||||||
|
|
||||||
// We needed to have a mutex as a global variable, so we used the crate that provides the
|
// We needed to have a mutex as a global variable, so we used once_cell for testing. The main
|
||||||
// lazy_static! macro for testing. The main potential problem, caused by tests being run in
|
// potential problem, caused by tests being run in parallel by cargo, is creating different
|
||||||
// parallel by cargo, is creating different TAPs and trying to associate the same address,
|
// TAPs and trying to associate the same address, so we hide the IP address &str behind this
|
||||||
// so we hide the IP address &str behind this mutex, more as a convention to remember to lock
|
// mutex, more as a convention to remember to lock it at the very beginning of each function
|
||||||
// it at the very beginning of each function susceptible to this issue. Another variant is
|
// susceptible to this issue. Another variant is to use a different IP address per function,
|
||||||
// to use a different IP address per function, but we must remember to pick an unique one
|
// but we must remember to pick an unique one each time.
|
||||||
// each time.
|
static TAP_IP_LOCK: Lazy<Mutex<&'static str>> = Lazy::new(|| Mutex::new("192.168.241.1"));
|
||||||
lazy_static! {
|
|
||||||
static ref TAP_IP_LOCK: Mutex<&'static str> = Mutex::new("192.168.241.1");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Describes the outcomes we are currently interested in when parsing a packet (we use
|
// Describes the outcomes we are currently interested in when parsing a packet (we use
|
||||||
// an UDP packet for testing).
|
// an UDP packet for testing).
|
||||||
@@ -543,10 +571,10 @@ mod tests {
|
|||||||
let interface_name_matches = |iface: &NetworkInterface| iface.name == ifname;
|
let interface_name_matches = |iface: &NetworkInterface| iface.name == ifname;
|
||||||
|
|
||||||
// Find the network interface with the provided name.
|
// Find the network interface with the provided name.
|
||||||
let interfaces = datalink::interfaces();
|
let interfaces = pnet_datalink::interfaces();
|
||||||
let interface = interfaces.into_iter().find(interface_name_matches).unwrap();
|
let interface = interfaces.into_iter().find(interface_name_matches).unwrap();
|
||||||
|
|
||||||
if let Ok(Ethernet(tx, rx)) = datalink::channel(&interface, Default::default()) {
|
if let Ok(Ethernet(tx, rx)) = pnet_datalink::channel(&interface, Default::default()) {
|
||||||
(interface.mac.unwrap(), tx, rx)
|
(interface.mac.unwrap(), tx, rx)
|
||||||
} else {
|
} else {
|
||||||
panic!("datalink channel error or unhandled channel type");
|
panic!("datalink channel error or unhandled channel type");
|
||||||
|
|||||||
@@ -31,35 +31,49 @@ impl fmt::Display for OptionParserError {
|
|||||||
OptionParserError::UnknownOption(s) => write!(f, "unknown option: {}", s),
|
OptionParserError::UnknownOption(s) => write!(f, "unknown option: {}", s),
|
||||||
OptionParserError::InvalidSyntax(s) => write!(f, "invalid syntax:{}", s),
|
OptionParserError::InvalidSyntax(s) => write!(f, "invalid syntax:{}", s),
|
||||||
OptionParserError::Conversion(field, value) => {
|
OptionParserError::Conversion(field, value) => {
|
||||||
write!(f, "unable to parse {} for {}", value, field)
|
write!(f, "unable to convert {} for {}", value, field)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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>> {
|
fn split_commas(s: &str) -> OptionParserResult<Vec<String>> {
|
||||||
let mut list: Vec<String> = Vec::new();
|
let mut list: Vec<String> = Vec::new();
|
||||||
let mut opened_brackets: usize = 0;
|
let mut opened_brackets = 0;
|
||||||
for element in s.trim().split(',') {
|
let mut in_quotes = false;
|
||||||
if opened_brackets > 0 {
|
let mut current = String::new();
|
||||||
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();
|
for c in s.trim().chars() {
|
||||||
let closing_brackets = element.matches(']').count();
|
match c {
|
||||||
if closing_brackets > opened_brackets {
|
'[' => {
|
||||||
return Err(OptionParserError::InvalidSyntax(s.to_owned()));
|
opened_brackets += 1;
|
||||||
} else {
|
current.push('[');
|
||||||
opened_brackets -= closing_brackets;
|
}
|
||||||
|
']' => {
|
||||||
|
opened_brackets -= 1;
|
||||||
|
if opened_brackets < 0 {
|
||||||
|
return Err(OptionParserError::InvalidSyntax(s.to_owned()));
|
||||||
|
}
|
||||||
|
current.push(']');
|
||||||
|
}
|
||||||
|
'"' => in_quotes = !in_quotes,
|
||||||
|
',' => {
|
||||||
|
if opened_brackets > 0 || in_quotes {
|
||||||
|
current.push(',')
|
||||||
|
} else {
|
||||||
|
list.push(current);
|
||||||
|
current = String::new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c => current.push(c),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
list.push(current);
|
||||||
|
|
||||||
|
if opened_brackets != 0 || in_quotes {
|
||||||
|
return Err(OptionParserError::InvalidSyntax(s.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(list)
|
Ok(list)
|
||||||
}
|
}
|
||||||
@@ -76,8 +90,8 @@ impl OptionParser {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
for option in split_commas_outside_brackets(input)?.iter() {
|
for option in split_commas(input)?.iter() {
|
||||||
let parts: Vec<&str> = option.split('=').collect();
|
let parts: Vec<&str> = option.splitn(2, '=').collect();
|
||||||
|
|
||||||
match self.options.get_mut(parts[0]) {
|
match self.options.get_mut(parts[0]) {
|
||||||
None => return Err(OptionParserError::UnknownOption(parts[0].to_owned())),
|
None => return Err(OptionParserError::UnknownOption(parts[0].to_owned())),
|
||||||
@@ -291,9 +305,13 @@ impl<S: FromStr, T: TupleValue> FromStr for Tuple<S, T> {
|
|||||||
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<(S, T)> = Vec::new();
|
let mut list: Vec<(S, T)> = Vec::new();
|
||||||
|
|
||||||
let tuples_list =
|
let body = s
|
||||||
split_commas_outside_brackets(s.trim().trim_matches(|c| c == '[' || c == ']'))
|
.trim()
|
||||||
.map_err(TupleError::SplitOutsideBrackets)?;
|
.strip_prefix('[')
|
||||||
|
.and_then(|s| s.strip_suffix(']'))
|
||||||
|
.ok_or_else(|| TupleError::InvalidValue(s.to_string()))?;
|
||||||
|
|
||||||
|
let tuples_list = split_commas(body).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();
|
||||||
|
|
||||||
@@ -334,3 +352,58 @@ impl FromStr for StringList {
|
|||||||
Ok(StringList(string_list))
|
Ok(StringList(string_list))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_option_parser() {
|
||||||
|
let mut parser = OptionParser::new();
|
||||||
|
parser
|
||||||
|
.add("size")
|
||||||
|
.add("mergeable")
|
||||||
|
.add("hotplug_method")
|
||||||
|
.add("hotplug_size")
|
||||||
|
.add("topology")
|
||||||
|
.add("cmdline");
|
||||||
|
|
||||||
|
assert!(parser.parse("size=128M,hanging_param").is_err());
|
||||||
|
assert!(parser.parse("size=128M,too_many_equals=foo=bar").is_err());
|
||||||
|
assert!(parser.parse("size=128M,file=/dev/shm").is_err());
|
||||||
|
|
||||||
|
assert!(parser.parse("size=128M").is_ok());
|
||||||
|
assert_eq!(parser.get("size"), Some("128M".to_owned()));
|
||||||
|
assert!(!parser.is_set("mergeable"));
|
||||||
|
assert!(parser.is_set("size"));
|
||||||
|
|
||||||
|
assert!(parser.parse("size=128M,mergeable=on").is_ok());
|
||||||
|
assert_eq!(parser.get("size"), Some("128M".to_owned()));
|
||||||
|
assert_eq!(parser.get("mergeable"), Some("on".to_owned()));
|
||||||
|
|
||||||
|
assert!(parser
|
||||||
|
.parse("size=128M,mergeable=on,topology=[1,2]")
|
||||||
|
.is_ok());
|
||||||
|
assert_eq!(parser.get("size"), Some("128M".to_owned()));
|
||||||
|
assert_eq!(parser.get("mergeable"), Some("on".to_owned()));
|
||||||
|
assert_eq!(parser.get("topology"), Some("[1,2]".to_owned()));
|
||||||
|
|
||||||
|
assert!(parser
|
||||||
|
.parse("size=128M,mergeable=on,topology=[[1,2],[3,4]]")
|
||||||
|
.is_ok());
|
||||||
|
assert_eq!(parser.get("size"), Some("128M".to_owned()));
|
||||||
|
assert_eq!(parser.get("mergeable"), Some("on".to_owned()));
|
||||||
|
assert_eq!(parser.get("topology"), Some("[[1,2],[3,4]]".to_owned()));
|
||||||
|
|
||||||
|
assert!(parser.parse("topology=[").is_err());
|
||||||
|
assert!(parser.parse("topology=[[[]]]]").is_err());
|
||||||
|
|
||||||
|
assert!(parser.parse("cmdline=\"console=ttyS0,9600n8\"").is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
parser.get("cmdline"),
|
||||||
|
Some("console=ttyS0,9600n8".to_owned())
|
||||||
|
);
|
||||||
|
assert!(parser.parse("cmdline=\"").is_err());
|
||||||
|
assert!(parser.parse("cmdline=\"\"\"").is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,22 +10,21 @@ kvm = ["vfio-ioctls/kvm"]
|
|||||||
mshv = ["vfio-ioctls/mshv"]
|
mshv = ["vfio-ioctls/mshv"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.56"
|
anyhow = "1.0.65"
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||||
vfio_user = { path = "../vfio_user" }
|
vfio_user = { path = "../vfio_user" }
|
||||||
vmm-sys-util = "0.9.0"
|
vmm-sys-util = "0.10.0"
|
||||||
libc = "0.2.123"
|
libc = "0.2.133"
|
||||||
log = "0.4.16"
|
log = "0.4.17"
|
||||||
serde = "1.0.136"
|
serde = { version = "1.0.145", features = ["derive"] }
|
||||||
serde_derive = "1.0.136"
|
thiserror = "1.0.37"
|
||||||
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.7.0"
|
vm-memory = "0.9.0"
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
|
|
||||||
[dependencies.vfio-bindings]
|
[dependencies.vfio-bindings]
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ use crate::configuration::{
|
|||||||
PciBarRegionType, PciBridgeSubclass, PciClassCode, PciConfiguration, PciHeaderType,
|
PciBarRegionType, PciBridgeSubclass, PciClassCode, PciConfiguration, PciHeaderType,
|
||||||
};
|
};
|
||||||
use crate::device::{DeviceRelocation, Error as PciDeviceError, PciDevice};
|
use crate::device::{DeviceRelocation, Error as PciDeviceError, PciDevice};
|
||||||
|
use crate::PciBarConfiguration;
|
||||||
use byteorder::{ByteOrder, LittleEndian};
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use std::sync::{Arc, Barrier, Mutex};
|
use std::sync::{Arc, Barrier, Mutex};
|
||||||
use vm_device::{Bus, BusDevice};
|
use vm_device::{Bus, BusDevice};
|
||||||
use vm_memory::{Address, GuestAddress, GuestUsize};
|
|
||||||
|
|
||||||
const VENDOR_ID_INTEL: u16 = 0x8086;
|
const VENDOR_ID_INTEL: u16 = 0x8086;
|
||||||
const DEVICE_ID_INTEL_VIRT_PCIE_HOST: u16 = 0x0d57;
|
const DEVICE_ID_INTEL_VIRT_PCIE_HOST: u16 = 0x0d57;
|
||||||
@@ -88,6 +88,10 @@ impl PciDevice for PciRoot {
|
|||||||
fn as_any(&mut self) -> &mut dyn Any {
|
fn as_any(&mut self) -> &mut dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PciBus {
|
pub struct PciBus {
|
||||||
@@ -118,21 +122,21 @@ impl PciBus {
|
|||||||
dev: Arc<Mutex<dyn BusDevice>>,
|
dev: Arc<Mutex<dyn BusDevice>>,
|
||||||
#[cfg(target_arch = "x86_64")] io_bus: &Bus,
|
#[cfg(target_arch = "x86_64")] io_bus: &Bus,
|
||||||
mmio_bus: &Bus,
|
mmio_bus: &Bus,
|
||||||
bars: Vec<(GuestAddress, GuestUsize, PciBarRegionType)>,
|
bars: Vec<PciBarConfiguration>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
for (address, size, type_) in bars {
|
for bar in bars {
|
||||||
match type_ {
|
match bar.region_type() {
|
||||||
PciBarRegionType::IoRegion => {
|
PciBarRegionType::IoRegion => {
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
io_bus
|
io_bus
|
||||||
.insert(dev.clone(), address.raw_value(), size)
|
.insert(dev.clone(), bar.addr(), bar.size())
|
||||||
.map_err(PciRootError::PioInsert)?;
|
.map_err(PciRootError::PioInsert)?;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
error!("I/O region is not supported");
|
error!("I/O region is not supported");
|
||||||
}
|
}
|
||||||
PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => {
|
PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => {
|
||||||
mmio_bus
|
mmio_bus
|
||||||
.insert(dev.clone(), address.raw_value(), size)
|
.insert(dev.clone(), bar.addr(), bar.size())
|
||||||
.map_err(PciRootError::MmioInsert)?;
|
.map_err(PciRootError::MmioInsert)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use std::fmt::{self, Display};
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use versionize::{VersionMap, Versionize, VersionizeError, VersionizeResult};
|
use versionize::{VersionMap, Versionize, VersionizeError, VersionizeResult};
|
||||||
use versionize_derive::Versionize;
|
use versionize_derive::Versionize;
|
||||||
|
use vm_device::PciBarType;
|
||||||
use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable, VersionMapped};
|
use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable, VersionMapped};
|
||||||
|
|
||||||
// The number of 32bit registers in the config space, 4096 bytes.
|
// The number of 32bit registers in the config space, 4096 bytes.
|
||||||
@@ -18,6 +19,7 @@ const STATUS_REG: usize = 1;
|
|||||||
const STATUS_REG_CAPABILITIES_USED_MASK: u32 = 0x0010_0000;
|
const STATUS_REG_CAPABILITIES_USED_MASK: u32 = 0x0010_0000;
|
||||||
const BAR0_REG: usize = 4;
|
const BAR0_REG: usize = 4;
|
||||||
const ROM_BAR_REG: usize = 12;
|
const ROM_BAR_REG: usize = 12;
|
||||||
|
const ROM_BAR_IDX: usize = 6;
|
||||||
const BAR_IO_ADDR_MASK: u32 = 0xffff_fffc;
|
const BAR_IO_ADDR_MASK: u32 = 0xffff_fffc;
|
||||||
const BAR_MEM_ADDR_MASK: u32 = 0xffff_fff0;
|
const BAR_MEM_ADDR_MASK: u32 = 0xffff_fff0;
|
||||||
const ROM_BAR_ADDR_MASK: u32 = 0xffff_f800;
|
const ROM_BAR_ADDR_MASK: u32 = 0xffff_f800;
|
||||||
@@ -185,10 +187,10 @@ pub trait PciProgrammingInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Types of PCI capabilities.
|
/// Types of PCI capabilities.
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[repr(C)]
|
#[repr(u8)]
|
||||||
pub enum PciCapabilityId {
|
pub enum PciCapabilityId {
|
||||||
ListId = 0,
|
ListId = 0,
|
||||||
PowerManagement = 0x01,
|
PowerManagement = 0x01,
|
||||||
@@ -242,6 +244,118 @@ impl From<u8> for PciCapabilityId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Types of PCI Express capabilities.
|
||||||
|
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[repr(u16)]
|
||||||
|
pub enum PciExpressCapabilityId {
|
||||||
|
NullCapability = 0x0000,
|
||||||
|
AdvancedErrorReporting = 0x0001,
|
||||||
|
VirtualChannelMultiFunctionVirtualChannelNotPresent = 0x0002,
|
||||||
|
DeviceSerialNumber = 0x0003,
|
||||||
|
PowerBudgeting = 0x0004,
|
||||||
|
RootComplexLinkDeclaration = 0x0005,
|
||||||
|
RootComplexInternalLinkControl = 0x0006,
|
||||||
|
RootComplexEventCollectorEndpointAssociation = 0x0007,
|
||||||
|
MultiFunctionVirtualChannel = 0x0008,
|
||||||
|
VirtualChannelMultiFunctionVirtualChannelPresent = 0x0009,
|
||||||
|
RootComplexRegisterBlock = 0x000a,
|
||||||
|
VendorSpecificExtendedCapability = 0x000b,
|
||||||
|
ConfigurationAccessCorrelation = 0x000c,
|
||||||
|
AccessControlServices = 0x000d,
|
||||||
|
AlternativeRoutingIdentificationIntepretation = 0x000e,
|
||||||
|
AddressTranslationServices = 0x000f,
|
||||||
|
SingleRootIoVirtualization = 0x0010,
|
||||||
|
DeprecatedMultiRootIoVirtualzation = 0x0011,
|
||||||
|
Multicast = 0x0012,
|
||||||
|
PageRequestInterface = 0x0013,
|
||||||
|
ReservedForAmd = 0x0014,
|
||||||
|
ResizeableBar = 0x0015,
|
||||||
|
DynamicPowerAllocation = 0x0016,
|
||||||
|
ThpRequester = 0x0017,
|
||||||
|
LatencyToleranceReporting = 0x0018,
|
||||||
|
SecondaryPciExpress = 0x0019,
|
||||||
|
ProtocolMultiplexing = 0x001a,
|
||||||
|
ProcessAddressSpaceId = 0x001b,
|
||||||
|
LnRequestor = 0x001c,
|
||||||
|
DownstreamPortContainment = 0x001d,
|
||||||
|
L1PmSubstates = 0x001e,
|
||||||
|
PrecisionTimeMeasurement = 0x001f,
|
||||||
|
PciExpressOverMphy = 0x0020,
|
||||||
|
FRSQueueing = 0x0021,
|
||||||
|
ReadinessTimeReporting = 0x0022,
|
||||||
|
DesignatedVendorSpecificExtendedCapability = 0x0023,
|
||||||
|
VfResizeableBar = 0x0024,
|
||||||
|
DataLinkFeature = 0x0025,
|
||||||
|
PhysicalLayerSixteenGts = 0x0026,
|
||||||
|
LaneMargeningAtTheReceiver = 0x0027,
|
||||||
|
HierarchyId = 0x0028,
|
||||||
|
NativePcieEnclosureManagement = 0x0029,
|
||||||
|
PhysicalLayerThirtyTwoGts = 0x002a,
|
||||||
|
AlternateProtocol = 0x002b,
|
||||||
|
SystemFirmwareIntermediary = 0x002c,
|
||||||
|
ShadowFunctions = 0x002d,
|
||||||
|
DataObjectExchange = 0x002e,
|
||||||
|
Reserved = 0x002f,
|
||||||
|
ExtendedCapabilitiesAbsence = 0xffff,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u16> for PciExpressCapabilityId {
|
||||||
|
fn from(c: u16) -> Self {
|
||||||
|
match c {
|
||||||
|
0x0000 => PciExpressCapabilityId::NullCapability,
|
||||||
|
0x0001 => PciExpressCapabilityId::AdvancedErrorReporting,
|
||||||
|
0x0002 => PciExpressCapabilityId::VirtualChannelMultiFunctionVirtualChannelNotPresent,
|
||||||
|
0x0003 => PciExpressCapabilityId::DeviceSerialNumber,
|
||||||
|
0x0004 => PciExpressCapabilityId::PowerBudgeting,
|
||||||
|
0x0005 => PciExpressCapabilityId::RootComplexLinkDeclaration,
|
||||||
|
0x0006 => PciExpressCapabilityId::RootComplexInternalLinkControl,
|
||||||
|
0x0007 => PciExpressCapabilityId::RootComplexEventCollectorEndpointAssociation,
|
||||||
|
0x0008 => PciExpressCapabilityId::MultiFunctionVirtualChannel,
|
||||||
|
0x0009 => PciExpressCapabilityId::VirtualChannelMultiFunctionVirtualChannelPresent,
|
||||||
|
0x000a => PciExpressCapabilityId::RootComplexRegisterBlock,
|
||||||
|
0x000b => PciExpressCapabilityId::VendorSpecificExtendedCapability,
|
||||||
|
0x000c => PciExpressCapabilityId::ConfigurationAccessCorrelation,
|
||||||
|
0x000d => PciExpressCapabilityId::AccessControlServices,
|
||||||
|
0x000e => PciExpressCapabilityId::AlternativeRoutingIdentificationIntepretation,
|
||||||
|
0x000f => PciExpressCapabilityId::AddressTranslationServices,
|
||||||
|
0x0010 => PciExpressCapabilityId::SingleRootIoVirtualization,
|
||||||
|
0x0011 => PciExpressCapabilityId::DeprecatedMultiRootIoVirtualzation,
|
||||||
|
0x0012 => PciExpressCapabilityId::Multicast,
|
||||||
|
0x0013 => PciExpressCapabilityId::PageRequestInterface,
|
||||||
|
0x0014 => PciExpressCapabilityId::ReservedForAmd,
|
||||||
|
0x0015 => PciExpressCapabilityId::ResizeableBar,
|
||||||
|
0x0016 => PciExpressCapabilityId::DynamicPowerAllocation,
|
||||||
|
0x0017 => PciExpressCapabilityId::ThpRequester,
|
||||||
|
0x0018 => PciExpressCapabilityId::LatencyToleranceReporting,
|
||||||
|
0x0019 => PciExpressCapabilityId::SecondaryPciExpress,
|
||||||
|
0x001a => PciExpressCapabilityId::ProtocolMultiplexing,
|
||||||
|
0x001b => PciExpressCapabilityId::ProcessAddressSpaceId,
|
||||||
|
0x001c => PciExpressCapabilityId::LnRequestor,
|
||||||
|
0x001d => PciExpressCapabilityId::DownstreamPortContainment,
|
||||||
|
0x001e => PciExpressCapabilityId::L1PmSubstates,
|
||||||
|
0x001f => PciExpressCapabilityId::PrecisionTimeMeasurement,
|
||||||
|
0x0020 => PciExpressCapabilityId::PciExpressOverMphy,
|
||||||
|
0x0021 => PciExpressCapabilityId::FRSQueueing,
|
||||||
|
0x0022 => PciExpressCapabilityId::ReadinessTimeReporting,
|
||||||
|
0x0023 => PciExpressCapabilityId::DesignatedVendorSpecificExtendedCapability,
|
||||||
|
0x0024 => PciExpressCapabilityId::VfResizeableBar,
|
||||||
|
0x0025 => PciExpressCapabilityId::DataLinkFeature,
|
||||||
|
0x0026 => PciExpressCapabilityId::PhysicalLayerSixteenGts,
|
||||||
|
0x0027 => PciExpressCapabilityId::LaneMargeningAtTheReceiver,
|
||||||
|
0x0028 => PciExpressCapabilityId::HierarchyId,
|
||||||
|
0x0029 => PciExpressCapabilityId::NativePcieEnclosureManagement,
|
||||||
|
0x002a => PciExpressCapabilityId::PhysicalLayerThirtyTwoGts,
|
||||||
|
0x002b => PciExpressCapabilityId::AlternateProtocol,
|
||||||
|
0x002c => PciExpressCapabilityId::SystemFirmwareIntermediary,
|
||||||
|
0x002d => PciExpressCapabilityId::ShadowFunctions,
|
||||||
|
0x002e => PciExpressCapabilityId::DataObjectExchange,
|
||||||
|
0xffff => PciExpressCapabilityId::ExtendedCapabilitiesAbsence,
|
||||||
|
_ => PciExpressCapabilityId::Reserved,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A PCI capability list. Devices can optionally specify capabilities in their configuration space.
|
/// A PCI capability list. Devices can optionally specify capabilities in their configuration space.
|
||||||
pub trait PciCapability {
|
pub trait PciCapability {
|
||||||
fn bytes(&self) -> &[u8];
|
fn bytes(&self) -> &[u8];
|
||||||
@@ -319,24 +433,55 @@ pub struct PciConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// See pci_regs.h in kernel
|
/// See pci_regs.h in kernel
|
||||||
#[derive(Copy, Clone, PartialEq, Versionize, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Versionize, Debug)]
|
||||||
pub enum PciBarRegionType {
|
pub enum PciBarRegionType {
|
||||||
Memory32BitRegion = 0,
|
Memory32BitRegion = 0,
|
||||||
IoRegion = 0x01,
|
IoRegion = 0x01,
|
||||||
Memory64BitRegion = 0x04,
|
Memory64BitRegion = 0x04,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<PciBarType> for PciBarRegionType {
|
||||||
|
fn from(type_: PciBarType) -> Self {
|
||||||
|
match type_ {
|
||||||
|
PciBarType::Io => PciBarRegionType::IoRegion,
|
||||||
|
PciBarType::Mmio32 => PciBarRegionType::Memory32BitRegion,
|
||||||
|
PciBarType::Mmio64 => PciBarRegionType::Memory64BitRegion,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::from_over_into)]
|
||||||
|
impl Into<PciBarType> for PciBarRegionType {
|
||||||
|
fn into(self) -> PciBarType {
|
||||||
|
match self {
|
||||||
|
PciBarRegionType::IoRegion => PciBarType::Io,
|
||||||
|
PciBarRegionType::Memory32BitRegion => PciBarType::Mmio32,
|
||||||
|
PciBarRegionType::Memory64BitRegion => PciBarType::Mmio64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum PciBarPrefetchable {
|
pub enum PciBarPrefetchable {
|
||||||
NotPrefetchable = 0,
|
NotPrefetchable = 0,
|
||||||
Prefetchable = 0x08,
|
Prefetchable = 0x08,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::from_over_into)]
|
||||||
|
impl Into<bool> for PciBarPrefetchable {
|
||||||
|
fn into(self) -> bool {
|
||||||
|
match self {
|
||||||
|
PciBarPrefetchable::NotPrefetchable => false,
|
||||||
|
PciBarPrefetchable::Prefetchable => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct PciBarConfiguration {
|
pub struct PciBarConfiguration {
|
||||||
addr: u64,
|
addr: u64,
|
||||||
size: u64,
|
size: u64,
|
||||||
reg_idx: usize,
|
idx: usize,
|
||||||
region_type: PciBarRegionType,
|
region_type: PciBarRegionType,
|
||||||
prefetchable: PciBarPrefetchable,
|
prefetchable: PciBarPrefetchable,
|
||||||
}
|
}
|
||||||
@@ -556,22 +701,23 @@ impl PciConfiguration {
|
|||||||
|
|
||||||
/// Adds a region specified by `config`. Configures the specified BAR(s) to
|
/// Adds a region specified by `config`. Configures the specified BAR(s) to
|
||||||
/// report this region and size to the guest kernel. Enforces a few constraints
|
/// report this region and size to the guest kernel. Enforces a few constraints
|
||||||
/// (i.e, region size must be power of two, register not already used). Returns 'None' on
|
/// (i.e, region size must be power of two, register not already used).
|
||||||
/// failure all, `Some(BarIndex)` on success.
|
pub fn add_pci_bar(&mut self, config: &PciBarConfiguration) -> Result<()> {
|
||||||
pub fn add_pci_bar(&mut self, config: &PciBarConfiguration) -> Result<usize> {
|
let bar_idx = config.idx;
|
||||||
if self.bars[config.reg_idx].used {
|
let reg_idx = BAR0_REG + bar_idx;
|
||||||
return Err(Error::BarInUse(config.reg_idx));
|
|
||||||
|
if self.bars[bar_idx].used {
|
||||||
|
return Err(Error::BarInUse(bar_idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.size.count_ones() != 1 {
|
if config.size.count_ones() != 1 {
|
||||||
return Err(Error::BarSizeInvalid(config.size));
|
return Err(Error::BarSizeInvalid(config.size));
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.reg_idx >= NUM_BAR_REGS {
|
if bar_idx >= NUM_BAR_REGS {
|
||||||
return Err(Error::BarInvalid(config.reg_idx));
|
return Err(Error::BarInvalid(bar_idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
let bar_idx = BAR0_REG + config.reg_idx;
|
|
||||||
let end_addr = config
|
let end_addr = config
|
||||||
.addr
|
.addr
|
||||||
.checked_add(config.size - 1)
|
.checked_add(config.size - 1)
|
||||||
@@ -584,20 +730,20 @@ impl PciConfiguration {
|
|||||||
|
|
||||||
// Encode the BAR size as expected by the software running in
|
// Encode the BAR size as expected by the software running in
|
||||||
// the guest.
|
// the guest.
|
||||||
self.bars[config.reg_idx].size =
|
self.bars[bar_idx].size =
|
||||||
encode_32_bits_bar_size(config.size as u32).ok_or(Error::Encode32BarSize)?;
|
encode_32_bits_bar_size(config.size as u32).ok_or(Error::Encode32BarSize)?;
|
||||||
}
|
}
|
||||||
PciBarRegionType::Memory64BitRegion => {
|
PciBarRegionType::Memory64BitRegion => {
|
||||||
if config.reg_idx + 1 >= NUM_BAR_REGS {
|
if bar_idx + 1 >= NUM_BAR_REGS {
|
||||||
return Err(Error::BarInvalid64(config.reg_idx));
|
return Err(Error::BarInvalid64(bar_idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
if end_addr > u64::max_value() {
|
if end_addr > u64::max_value() {
|
||||||
return Err(Error::BarAddressInvalid(config.addr, config.size));
|
return Err(Error::BarAddressInvalid(config.addr, config.size));
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.bars[config.reg_idx + 1].used {
|
if self.bars[bar_idx + 1].used {
|
||||||
return Err(Error::BarInUse64(config.reg_idx));
|
return Err(Error::BarInUse64(bar_idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode the BAR size as expected by the software running in
|
// Encode the BAR size as expected by the software running in
|
||||||
@@ -605,12 +751,12 @@ impl PciConfiguration {
|
|||||||
let (bar_size_hi, bar_size_lo) =
|
let (bar_size_hi, bar_size_lo) =
|
||||||
encode_64_bits_bar_size(config.size).ok_or(Error::Encode64BarSize)?;
|
encode_64_bits_bar_size(config.size).ok_or(Error::Encode64BarSize)?;
|
||||||
|
|
||||||
self.registers[bar_idx + 1] = (config.addr >> 32) as u32;
|
self.registers[reg_idx + 1] = (config.addr >> 32) as u32;
|
||||||
self.writable_bits[bar_idx + 1] = 0xffff_ffff;
|
self.writable_bits[reg_idx + 1] = 0xffff_ffff;
|
||||||
self.bars[config.reg_idx + 1].addr = self.registers[bar_idx + 1];
|
self.bars[bar_idx + 1].addr = self.registers[reg_idx + 1];
|
||||||
self.bars[config.reg_idx].size = bar_size_lo;
|
self.bars[bar_idx].size = bar_size_lo;
|
||||||
self.bars[config.reg_idx + 1].size = bar_size_hi;
|
self.bars[bar_idx + 1].size = bar_size_hi;
|
||||||
self.bars[config.reg_idx + 1].used = true;
|
self.bars[bar_idx + 1].used = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -622,26 +768,30 @@ impl PciConfiguration {
|
|||||||
PciBarRegionType::IoRegion => (BAR_IO_ADDR_MASK, config.region_type as u32),
|
PciBarRegionType::IoRegion => (BAR_IO_ADDR_MASK, config.region_type as u32),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.registers[bar_idx] = ((config.addr as u32) & mask) | lower_bits;
|
self.registers[reg_idx] = ((config.addr as u32) & mask) | lower_bits;
|
||||||
self.writable_bits[bar_idx] = mask;
|
self.writable_bits[reg_idx] = mask;
|
||||||
self.bars[config.reg_idx].addr = self.registers[bar_idx];
|
self.bars[bar_idx].addr = self.registers[reg_idx];
|
||||||
self.bars[config.reg_idx].used = true;
|
self.bars[bar_idx].used = true;
|
||||||
self.bars[config.reg_idx].r#type = Some(config.region_type);
|
self.bars[bar_idx].r#type = Some(config.region_type);
|
||||||
Ok(config.reg_idx)
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds rom expansion BAR.
|
/// Adds rom expansion BAR.
|
||||||
pub fn add_pci_rom_bar(&mut self, config: &PciBarConfiguration, active: u32) -> Result<usize> {
|
pub fn add_pci_rom_bar(&mut self, config: &PciBarConfiguration, active: u32) -> Result<()> {
|
||||||
|
let bar_idx = config.idx;
|
||||||
|
let reg_idx = ROM_BAR_REG;
|
||||||
|
|
||||||
if self.rom_bar_used {
|
if self.rom_bar_used {
|
||||||
return Err(Error::RomBarInUse(config.reg_idx));
|
return Err(Error::RomBarInUse(bar_idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.size.count_ones() != 1 {
|
if config.size.count_ones() != 1 {
|
||||||
return Err(Error::RomBarSizeInvalid(config.size));
|
return Err(Error::RomBarSizeInvalid(config.size));
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.reg_idx != ROM_BAR_REG {
|
if bar_idx != ROM_BAR_IDX {
|
||||||
return Err(Error::RomBarInvalid(config.reg_idx));
|
return Err(Error::RomBarInvalid(bar_idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
let end_addr = config
|
let end_addr = config
|
||||||
@@ -653,13 +803,14 @@ impl PciConfiguration {
|
|||||||
return Err(Error::RomBarAddressInvalid(config.addr, config.size));
|
return Err(Error::RomBarAddressInvalid(config.addr, config.size));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.registers[config.reg_idx] = (config.addr as u32) | active;
|
self.registers[reg_idx] = (config.addr as u32) | active;
|
||||||
self.writable_bits[config.reg_idx] = ROM_BAR_ADDR_MASK;
|
self.writable_bits[reg_idx] = ROM_BAR_ADDR_MASK;
|
||||||
self.rom_bar_addr = self.registers[config.reg_idx];
|
self.rom_bar_addr = self.registers[reg_idx];
|
||||||
self.rom_bar_size =
|
self.rom_bar_size =
|
||||||
encode_32_bits_bar_size(config.size as u32).ok_or(Error::Encode32BarSize)?;
|
encode_32_bits_bar_size(config.size as u32).ok_or(Error::Encode32BarSize)?;
|
||||||
self.rom_bar_used = true;
|
self.rom_bar_used = true;
|
||||||
Ok(config.reg_idx)
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the address of the given BAR region.
|
/// Returns the address of the given BAR region.
|
||||||
@@ -911,7 +1062,7 @@ impl Snapshottable for PciConfiguration {
|
|||||||
impl Default for PciBarConfiguration {
|
impl Default for PciBarConfiguration {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
PciBarConfiguration {
|
PciBarConfiguration {
|
||||||
reg_idx: 0,
|
idx: 0,
|
||||||
addr: 0,
|
addr: 0,
|
||||||
size: 0,
|
size: 0,
|
||||||
region_type: PciBarRegionType::Memory64BitRegion,
|
region_type: PciBarRegionType::Memory64BitRegion,
|
||||||
@@ -922,13 +1073,13 @@ impl Default for PciBarConfiguration {
|
|||||||
|
|
||||||
impl PciBarConfiguration {
|
impl PciBarConfiguration {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
reg_idx: usize,
|
idx: usize,
|
||||||
size: u64,
|
size: u64,
|
||||||
region_type: PciBarRegionType,
|
region_type: PciBarRegionType,
|
||||||
prefetchable: PciBarPrefetchable,
|
prefetchable: PciBarPrefetchable,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
PciBarConfiguration {
|
PciBarConfiguration {
|
||||||
reg_idx,
|
idx,
|
||||||
addr: 0,
|
addr: 0,
|
||||||
size,
|
size,
|
||||||
region_type,
|
region_type,
|
||||||
@@ -937,8 +1088,8 @@ impl PciBarConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn set_register_index(mut self, reg_idx: usize) -> Self {
|
pub fn set_index(mut self, idx: usize) -> Self {
|
||||||
self.reg_idx = reg_idx;
|
self.idx = idx;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -954,15 +1105,37 @@ impl PciBarConfiguration {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_size(&self) -> u64 {
|
|
||||||
self.size
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn set_region_type(mut self, region_type: PciBarRegionType) -> Self {
|
pub fn set_region_type(mut self, region_type: PciBarRegionType) -> Self {
|
||||||
self.region_type = region_type;
|
self.region_type = region_type;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn set_prefetchable(mut self, prefetchable: PciBarPrefetchable) -> Self {
|
||||||
|
self.prefetchable = prefetchable;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn idx(&self) -> usize {
|
||||||
|
self.idx
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addr(&self) -> u64 {
|
||||||
|
self.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> u64 {
|
||||||
|
self.size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn region_type(&self) -> PciBarRegionType {
|
||||||
|
self.region_type
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prefetchable(&self) -> PciBarPrefetchable {
|
||||||
|
self.prefetchable
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user