mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
886 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e9172bf6f | ||
|
|
22cf8c97e5 | ||
|
|
9e6301be70 | ||
|
|
547230bb77 | ||
|
|
ef7e177df2 | ||
|
|
375e094c77 | ||
|
|
3df82337f1 | ||
|
|
0e273e8b0d | ||
|
|
ad6c0ee52b | ||
|
|
b1f6a59579 | ||
|
|
3494080e2f | ||
|
|
06e583c9ab | ||
|
|
7d8f795430 | ||
|
|
f8c6d7fe37 | ||
|
|
0dc122a9a9 | ||
|
|
e2ea9e2821 | ||
|
|
4e3bc20f2c | ||
|
|
e661139e1e | ||
|
|
c99a5fc7a5 | ||
|
|
5b2adb398a | ||
|
|
db5582f7d1 | ||
|
|
ce51755109 | ||
|
|
67ff0d7819 | ||
|
|
e7a51cde78 | ||
|
|
9ba06ce5f5 | ||
|
|
0e7d5d2761 | ||
|
|
ba9554389b | ||
|
|
c89b8e061f | ||
|
|
c2450bd055 | ||
|
|
bae9799259 | ||
|
|
3dd487c2cf | ||
|
|
eb87538100 | ||
|
|
9e8296b696 | ||
|
|
0dc3282ae0 | ||
|
|
67340c5295 | ||
|
|
160ea00d6b | ||
|
|
0389190c64 | ||
|
|
998fb48ff2 | ||
|
|
03d8a3ebcf | ||
|
|
08e4d1f481 | ||
|
|
d50a4cbb6b | ||
|
|
28eeb8a492 | ||
|
|
ec3520d8b2 | ||
|
|
19661d36af | ||
|
|
9747cd1059 | ||
|
|
0bdc7dcc1c | ||
|
|
e16817ea14 | ||
|
|
3de0a6d401 | ||
|
|
bb9c5e5faf | ||
|
|
0036302fc9 | ||
|
|
b4ec59cb0c | ||
|
|
8511603fdb | ||
|
|
df6e17840a | ||
|
|
fdb2d673d3 | ||
|
|
2d828121a6 | ||
|
|
55a8d3a0ea | ||
|
|
8e373f52a9 | ||
|
|
6eb0330bed | ||
|
|
c4c384bd1e | ||
|
|
180cc12b46 | ||
|
|
ccb76fc19f | ||
|
|
5668d7dfb7 | ||
|
|
7ca2a771c2 | ||
|
|
09abb5b709 | ||
|
|
7bac36bea4 | ||
|
|
0f5702f297 | ||
|
|
8914ce9da8 | ||
|
|
1f191d8130 | ||
|
|
129416de5c | ||
|
|
b3980f581b | ||
|
|
3a232ef31a | ||
|
|
cd83d258b8 | ||
|
|
1b0f35e42d | ||
|
|
85f818421f | ||
|
|
0031dacd7b | ||
|
|
2f855c9a67 | ||
|
|
b7e7455b49 | ||
|
|
795f2a5558 | ||
|
|
2b92a2778b | ||
|
|
cbe988d33e | ||
|
|
5e52729453 | ||
|
|
8b59316718 | ||
|
|
6e8972372e | ||
|
|
748ba1f50d | ||
|
|
a48d7c281e | ||
|
|
aea1f7743b | ||
|
|
ab8e559343 | ||
|
|
1b314cff5b | ||
|
|
683d116416 | ||
|
|
b93d50582d | ||
|
|
de4cd49c6d | ||
|
|
709545920e | ||
|
|
2b11966faa | ||
|
|
6835dfa5e9 | ||
|
|
d15e1f1a13 | ||
|
|
8ab15b9a98 | ||
|
|
287ec367d6 | ||
|
|
9fb0274479 | ||
|
|
51307dd509 | ||
|
|
de06bf4aed | ||
|
|
32ded2c72b | ||
|
|
8ecce8876e | ||
|
|
3931b99d4e | ||
|
|
4ae6b595d7 | ||
|
|
748018ace3 | ||
|
|
426ee39972 | ||
|
|
1d1043316b | ||
|
|
4517b76a23 | ||
|
|
1b32e2f8b2 | ||
|
|
5b3bcfa233 | ||
|
|
0489b6314e | ||
|
|
2c367bdde8 | ||
|
|
4f3f36fe5f | ||
|
|
4b08142117 | ||
|
|
b3e3a5fdd7 | ||
|
|
a1c6ef8385 | ||
|
|
16d0906dfd | ||
|
|
3e9ce6012c | ||
|
|
389264351e | ||
|
|
00becda899 | ||
|
|
fe5bde236a | ||
|
|
80cc2b6ef8 | ||
|
|
c37dadcc9a | ||
|
|
cefbf6b4a3 | ||
|
|
31209474b3 | ||
|
|
0e09b537e3 | ||
|
|
ca6d338ffa | ||
|
|
166b73e106 | ||
|
|
9398a0bd80 | ||
|
|
55f7df2e21 | ||
|
|
7fb1280666 | ||
|
|
bfa31f9c56 | ||
|
|
725e388684 | ||
|
|
c5eac2e822 | ||
|
|
c7b22156da | ||
|
|
e5e5a89e65 | ||
|
|
d98f2618bd | ||
|
|
2e01bf7f74 | ||
|
|
a4160c1fef | ||
|
|
d0c53a5357 | ||
|
|
3888f57600 | ||
|
|
7fd2022e8e | ||
|
|
1b6dd597b2 | ||
|
|
ad817f19b5 | ||
|
|
a8839c4a4e | ||
|
|
b173f6f654 | ||
|
|
def1d7cf86 | ||
|
|
e8c6d83f3f | ||
|
|
1c36065754 | ||
|
|
bccfa81368 | ||
|
|
a6959a7469 | ||
|
|
4487c8376b | ||
|
|
b62a40efae | ||
|
|
8f1e03fcf4 | ||
|
|
5ee68b1ee3 | ||
|
|
e2e02c8f69 | ||
|
|
ec94ae31ee | ||
|
|
83ab5ea528 | ||
|
|
30a7a8033e | ||
|
|
fc43a50f34 | ||
|
|
105c6cfc5a | ||
|
|
ee5792c0bb | ||
|
|
30e421d2e5 | ||
|
|
90b5014a50 | ||
|
|
cc3706afe1 | ||
|
|
1eac37bd5f | ||
|
|
d6bf1f5eb0 | ||
|
|
559faa272a | ||
|
|
6f8bd27cf7 | ||
|
|
47a7ebe434 | ||
|
|
1fd5384062 | ||
|
|
81862e8ed3 | ||
|
|
9fbf52b998 | ||
|
|
0bd910e8b0 | ||
|
|
ef92e55998 | ||
|
|
9f7ccb34cd | ||
|
|
e23f4e0783 | ||
|
|
6a29eba69e | ||
|
|
98e10062e8 | ||
|
|
c95b82d975 | ||
|
|
d83fc7f663 | ||
|
|
e3a5102f85 | ||
|
|
ea39fcca3f | ||
|
|
7e10b236d4 | ||
|
|
405b4a6829 | ||
|
|
a50b3784fe | ||
|
|
eae8043890 | ||
|
|
8868c9c950 | ||
|
|
7d16c74020 | ||
|
|
86e7f07485 | ||
|
|
578780ed0c | ||
|
|
ec01062ada | ||
|
|
d828fcd4ed | ||
|
|
d330f5389e | ||
|
|
e37ec26ccf | ||
|
|
4d9a2b17a7 | ||
|
|
7c3110e6d5 | ||
|
|
1c25d71127 | ||
|
|
8724ac1fe6 | ||
|
|
1ff0191b30 | ||
|
|
464ead57ec | ||
|
|
cee2d6627d | ||
|
|
bfeab9f69f | ||
|
|
3504947f0e | ||
|
|
d2442d39a8 | ||
|
|
1e87a409c7 | ||
|
|
1a2185ea96 | ||
|
|
8cea5db955 | ||
|
|
3ca6c29e28 | ||
|
|
d05586f520 | ||
|
|
d5f294b326 | ||
|
|
145df4b689 | ||
|
|
c45d24df16 | ||
|
|
f110029acf | ||
|
|
8a7f4b47cb | ||
|
|
c5bd8cabc4 | ||
|
|
d1b9d9d7f7 | ||
|
|
9806d83e7c | ||
|
|
f16b57716d | ||
|
|
3edf12accf | ||
|
|
7f2723d9c6 | ||
|
|
6c89c541da | ||
|
|
d272113d0c | ||
|
|
93883681be | ||
|
|
ca02a69fdf | ||
|
|
c4ffdad4bc | ||
|
|
2a5c248f32 | ||
|
|
926d149881 | ||
|
|
7f4b5dfae5 | ||
|
|
58b7057c7f | ||
|
|
b1efa5b26b | ||
|
|
b9ad3eda27 | ||
|
|
1d9050dbe3 | ||
|
|
3d08a0fba9 | ||
|
|
d274fe9cb8 | ||
|
|
cd24f470f4 | ||
|
|
2e22f950b7 | ||
|
|
66460765a3 | ||
|
|
b748b8987d | ||
|
|
c9483fb080 | ||
|
|
f52cd3bb85 | ||
|
|
57cd12fabc | ||
|
|
869c649e81 | ||
|
|
a9e30c88df | ||
|
|
1666375a31 | ||
|
|
9a0be7db3a | ||
|
|
324c5deb37 | ||
|
|
09e79a5e9b | ||
|
|
af261f231c | ||
|
|
7122e2989c | ||
|
|
d0b253d15f | ||
|
|
59baa29deb | ||
|
|
19fdf8bc79 | ||
|
|
58b902d036 | ||
|
|
e3213c8a79 | ||
|
|
2e2ce47271 | ||
|
|
d3a8332282 | ||
|
|
c64004b9a9 | ||
|
|
149e424b6e | ||
|
|
b07d471d4f | ||
|
|
345e65c9c2 | ||
|
|
f93aa42319 | ||
|
|
fa4bf92feb | ||
|
|
f30d460fa3 | ||
|
|
2c94773bdc | ||
|
|
9f1d2d34e8 | ||
|
|
57508a4b1c | ||
|
|
012f2572d5 | ||
|
|
b9dbe3a2f7 | ||
|
|
6230929d51 | ||
|
|
04d034a0bc | ||
|
|
f603afc46e | ||
|
|
b68add2d0d | ||
|
|
0f44db5da4 | ||
|
|
a7dccf94cf | ||
|
|
0e6e539d9b | ||
|
|
1cb1cff882 | ||
|
|
fefbc356a2 | ||
|
|
8b37448d28 | ||
|
|
92d083f593 | ||
|
|
58d8795b53 | ||
|
|
b37e2ed378 | ||
|
|
ef8fb9bd25 | ||
|
|
cfafc85b9c | ||
|
|
683491a955 | ||
|
|
2bbb08b2a4 | ||
|
|
b77eb63688 | ||
|
|
6722c303b0 | ||
|
|
6e0bd73c90 | ||
|
|
103fe1f48b | ||
|
|
f4e1b72477 | ||
|
|
65628e8d94 | ||
|
|
851de1e0b3 | ||
|
|
a9ec0f33c0 | ||
|
|
9266ea4995 | ||
|
|
6b8070de95 | ||
|
|
a5712641d2 | ||
|
|
aad4dc3b6b | ||
|
|
f4495de143 | ||
|
|
99d9a3d299 | ||
|
|
df7c728399 | ||
|
|
1e5a4e8d77 | ||
|
|
ff3fb91ba6 | ||
|
|
86c19816c6 | ||
|
|
ad1cb130a5 | ||
|
|
0c75e7f7b2 | ||
|
|
cb171d4a23 | ||
|
|
bc310bb173 | ||
|
|
76f4641004 | ||
|
|
dcb12c05a7 | ||
|
|
078c0408b3 | ||
|
|
b99b2bc990 | ||
|
|
0cd6d96874 | ||
|
|
a5d0ff7039 | ||
|
|
da1ab77848 | ||
|
|
989d3d3960 | ||
|
|
1559b7b684 | ||
|
|
e96474a5bd | ||
|
|
2a36a18be3 | ||
|
|
0e386581e0 | ||
|
|
1f0e5eb66a | ||
|
|
157db33d65 | ||
|
|
40df6c3787 | ||
|
|
b425c5f57c | ||
|
|
b4e3942708 | ||
|
|
802f489e4d | ||
|
|
fdecd94b20 | ||
|
|
2af2cc539f | ||
|
|
7d71319210 | ||
|
|
e710e21744 | ||
|
|
9c658e21a5 | ||
|
|
38620eaea8 | ||
|
|
a388d76228 | ||
|
|
710a860e9b | ||
|
|
83f22ac779 | ||
|
|
71b4aad3f1 | ||
|
|
d7afa3c47e | ||
|
|
073374e2b0 | ||
|
|
d4f40487e3 | ||
|
|
f63cf2ebc0 | ||
|
|
46031cb211 | ||
|
|
c52ccf3992 | ||
|
|
7b31871a36 | ||
|
|
1b58c63734 | ||
|
|
d1435a44af | ||
|
|
a75d71f2c8 | ||
|
|
96209e7a16 | ||
|
|
7a68689794 | ||
|
|
6e6b519a74 | ||
|
|
958ef69295 | ||
|
|
ffc222b26e | ||
|
|
5b706422e8 | ||
|
|
84105992b7 | ||
|
|
0235ed3388 | ||
|
|
58066e2da4 | ||
|
|
62d6553714 | ||
|
|
2a45e44bda | ||
|
|
099cdd2af8 | ||
|
|
340fd6571a | ||
|
|
02f951a9c3 | ||
|
|
22be5f9d0f | ||
|
|
a9ccb20e5a | ||
|
|
2b150ac2ea | ||
|
|
204bf2e3f5 | ||
|
|
2a40fe5541 | ||
|
|
7ae4e2b8fe | ||
|
|
f17f4079b4 | ||
|
|
342851c88c | ||
|
|
37c3b0429a | ||
|
|
af14044fb8 | ||
|
|
fa39d1e9b9 | ||
|
|
00714dc54a | ||
|
|
87fb9cedc9 | ||
|
|
ce2928d842 | ||
|
|
a18b08c682 | ||
|
|
43935c3b95 | ||
|
|
c3c550c454 | ||
|
|
7da632def6 | ||
|
|
f38ef20e3b | ||
|
|
a033b1ef67 | ||
|
|
ffa7d27b5e | ||
|
|
da725daca0 | ||
|
|
0fe0eb4c97 | ||
|
|
8877211d9e | ||
|
|
f907ff5d82 | ||
|
|
89a2562c5a | ||
|
|
29cf637f3f | ||
|
|
fd9fa2a681 | ||
|
|
756aebafda | ||
|
|
d4d63502be | ||
|
|
961793ba8c | ||
|
|
cbeed02a7c | ||
|
|
10eb687687 | ||
|
|
83cc554f90 | ||
|
|
7d8d27c1b4 | ||
|
|
d692dfb8e3 | ||
|
|
7ad58457b0 | ||
|
|
62d2189ade | ||
|
|
7e7855bc8f | ||
|
|
11cc35e6b5 | ||
|
|
89677c3181 | ||
|
|
307cfeba27 | ||
|
|
43bb09df50 | ||
|
|
81d570e271 | ||
|
|
31ca22d4b6 | ||
|
|
cf995451a2 | ||
|
|
4fea40f008 | ||
|
|
2daab89987 | ||
|
|
1bc63e7848 | ||
|
|
2115a41568 | ||
|
|
4b4354a52c | ||
|
|
67751d07d6 | ||
|
|
509fd8b03e | ||
|
|
1294f052b2 | ||
|
|
01b38b9c59 | ||
|
|
db51e9e209 | ||
|
|
57dfcc06d3 | ||
|
|
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 |
34
.github/workflows/build.yaml
vendored
34
.github/workflows/build.yaml
vendored
@@ -29,35 +29,27 @@ 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 --locked --bin cloud-hypervisor -- -D warnings
|
run: cargo rustc --locked --bin cloud-hypervisor -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||||
|
|
||||||
- name: Build (common + kvm)
|
- name: Build (kvm)
|
||||||
run: cargo rustc --locked --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 -D clippy::undocumented_unsafe_blocks
|
||||||
|
|
||||||
- name: Build (default features + tdx)
|
- name: Build (default features + tdx)
|
||||||
run: cargo rustc --locked --bin cloud-hypervisor --features "tdx" -- -D warnings
|
run: cargo rustc --locked --bin cloud-hypervisor --features "tdx" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||||
|
|
||||||
- name: Build (default features + amx)
|
|
||||||
run: cargo rustc --locked --bin cloud-hypervisor --features "amx" -- -D warnings
|
|
||||||
|
|
||||||
- name: Build (default features + gdb)
|
|
||||||
run: cargo rustc --locked --bin cloud-hypervisor --features "gdb" -- -D warnings
|
|
||||||
|
|
||||||
- name: Build (default features + guest_debug)
|
- name: Build (default features + guest_debug)
|
||||||
run: cargo rustc --locked --bin cloud-hypervisor --features "guest_debug" -- -D warnings
|
run: cargo rustc --locked --bin cloud-hypervisor --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||||
|
|
||||||
- name: Build (common + mshv)
|
- name: Build (mshv)
|
||||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "common,mshv" -- -D warnings
|
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||||
|
|
||||||
|
- name: Build (mshv + kvm)
|
||||||
|
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||||
|
|
||||||
- name: Release Build (default features)
|
- name: Release Build (default features)
|
||||||
run: cargo build --locked --all --release --target=${{ matrix.target }}
|
run: cargo build --locked --all --release --target=${{ matrix.target }}
|
||||||
|
|||||||
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
|
|
||||||
84
.github/workflows/quality.yaml
vendored
84
.github/workflows/quality.yaml
vendored
@@ -13,15 +13,31 @@ jobs:
|
|||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
target:
|
target:
|
||||||
|
- aarch64-unknown-linux-gnu
|
||||||
|
- aarch64-unknown-linux-musl
|
||||||
- x86_64-unknown-linux-gnu
|
- x86_64-unknown-linux-gnu
|
||||||
|
- x86_64-unknown-linux-musl
|
||||||
|
|
||||||
experimental: [false]
|
experimental: [false]
|
||||||
include:
|
include:
|
||||||
|
- rust: beta
|
||||||
|
target: aarch64-unknown-linux-gnu
|
||||||
|
experimental: true
|
||||||
|
- rust: beta
|
||||||
|
target: aarch64-unknown-linux-musl
|
||||||
|
experimental: true
|
||||||
- rust: beta
|
- rust: beta
|
||||||
target: x86_64-unknown-linux-gnu
|
target: x86_64-unknown-linux-gnu
|
||||||
experimental: true
|
experimental: true
|
||||||
|
- rust: beta
|
||||||
|
target: x86_64-unknown-linux-musl
|
||||||
|
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,26 +46,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 --locked --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: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||||
|
|
||||||
- name: Clippy (default features)
|
- name: Clippy (default features)
|
||||||
run: cargo clippy --locked --all --all-targets --tests -- -D warnings
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
- name: Clippy (default features + amx)
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
run: cargo clippy --locked --all --all-targets --tests --features "amx" -- -D warnings
|
command: clippy
|
||||||
|
args: --target=${{ matrix.target }} --locked --all --all-targets --tests -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||||
- name: Clippy (default features + gdb)
|
|
||||||
run: cargo clippy --locked --all --all-targets --tests --features "gdb" -- -D warnings
|
|
||||||
|
|
||||||
- name: Clippy (default features + guest_debug)
|
- name: Clippy (default features + guest_debug)
|
||||||
run: cargo clippy --locked --all --all-targets --tests --features "guest_debug" -- -D warnings
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
|
command: clippy
|
||||||
|
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||||
|
|
||||||
- name: Clippy (common + mshv)
|
- name: Clippy (default features + tracing)
|
||||||
run: cargo clippy --locked --all --all-targets --no-default-features --tests --features "common,mshv" -- -D warnings
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
|
command: clippy
|
||||||
|
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --features "tracing" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||||
|
|
||||||
|
- name: Clippy (mshv)
|
||||||
|
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: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||||
|
|
||||||
|
- 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: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --features "mshv,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||||
|
|
||||||
|
- 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: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --features "tdx,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||||
|
|
||||||
- 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)"
|
||||||
|
|||||||
95
.github/workflows/release.yaml
vendored
95
.github/workflows/release.yaml
vendored
@@ -1,9 +1,8 @@
|
|||||||
name: Cloud Hypervisor Release
|
name: Cloud Hypervisor Release
|
||||||
on: [create]
|
on: [pull_request, create]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
|
||||||
name: Release
|
name: Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -16,47 +15,33 @@ 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.62"
|
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.62"
|
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.62"
|
toolchain: "1.62"
|
||||||
command: build
|
command: build
|
||||||
args: --all --release --target=x86_64-unknown-linux-gnu
|
args: --all --release --no-default-features --features "kvm,mshv" --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.62"
|
toolchain: "1.62"
|
||||||
command: build
|
command: build
|
||||||
args: --all --release --target=x86_64-unknown-linux-musl
|
args: --all --release --no-default-features --features "kvm,mshv" --target=x86_64-unknown-linux-musl
|
||||||
- name: Strip cloud-hypervisor binaries
|
|
||||||
run: strip target/*/release/cloud-hypervisor
|
|
||||||
- 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.62"
|
toolchain: "1.62"
|
||||||
target: aarch64-unknown-linux-musl
|
target: aarch64-unknown-linux-musl
|
||||||
override: true
|
override: true
|
||||||
- name: Static Build (AArch64)
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
use-cross: true
|
|
||||||
command: build
|
|
||||||
args: --all --release --target=aarch64-unknown-linux-musl
|
|
||||||
- name: Vendor
|
|
||||||
working-directory: ../cloud-hypervisor-${{ github.event.ref }}
|
|
||||||
run: |
|
|
||||||
mkdir ../vendor-cargo-home
|
|
||||||
export CARGO_HOME=$(realpath ../vendor-cargo-home)
|
|
||||||
mkdir .cargo
|
|
||||||
cargo vendor > .cargo/config.toml
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
|
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: actions/create-release@v1
|
uses: actions/create-release@v1
|
||||||
env:
|
env:
|
||||||
@@ -66,20 +51,8 @@ jobs:
|
|||||||
release_name: ${{ github.ref }}
|
release_name: ${{ github.ref }}
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: true
|
prerelease: true
|
||||||
- name: Create vendored source archive
|
|
||||||
working-directory: ../
|
|
||||||
run: tar cJf cloud-hypervisor-${{ github.event.ref }}.tar.xz cloud-hypervisor-${{ github.event.ref }}
|
|
||||||
- name: Upload cloud-hypervisor vendored source archive
|
|
||||||
id: upload-release-cloud-hypervisor-vendored-sources
|
|
||||||
uses: actions/upload-release-asset@v1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
|
||||||
asset_path: ../cloud-hypervisor-${{ github.event.ref }}.tar.xz
|
|
||||||
asset_name: cloud-hypervisor-${{ github.event.ref }}.tar.xz
|
|
||||||
asset_content_type: application/x-xz
|
|
||||||
- name: Upload cloud-hypervisor
|
- name: Upload cloud-hypervisor
|
||||||
|
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||||
id: upload-release-cloud-hypervisor
|
id: upload-release-cloud-hypervisor
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
@@ -90,6 +63,7 @@ jobs:
|
|||||||
asset_name: cloud-hypervisor
|
asset_name: cloud-hypervisor
|
||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
- name: Upload static cloud-hypervisor
|
- name: Upload static cloud-hypervisor
|
||||||
|
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||||
id: upload-release-static-cloud-hypervisor
|
id: upload-release-static-cloud-hypervisor
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
@@ -100,6 +74,7 @@ jobs:
|
|||||||
asset_name: cloud-hypervisor-static
|
asset_name: cloud-hypervisor-static
|
||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
- name: Upload ch-remote
|
- name: Upload ch-remote
|
||||||
|
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||||
id: upload-release-ch-remote
|
id: upload-release-ch-remote
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
@@ -110,6 +85,7 @@ jobs:
|
|||||||
asset_name: ch-remote
|
asset_name: ch-remote
|
||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
- name: Upload static-ch-remote
|
- name: Upload static-ch-remote
|
||||||
|
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||||
id: upload-release-static-ch-remote
|
id: upload-release-static-ch-remote
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
@@ -119,7 +95,18 @@ jobs:
|
|||||||
asset_path: target/x86_64-unknown-linux-musl/release/ch-remote
|
asset_path: target/x86_64-unknown-linux-musl/release/ch-remote
|
||||||
asset_name: ch-remote-static
|
asset_name: ch-remote-static
|
||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
|
- name: Clean build tree ahead of cross build
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: clean
|
||||||
|
- name: Static Build (AArch64)
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: true
|
||||||
|
command: build
|
||||||
|
args: --all --release --target=aarch64-unknown-linux-musl
|
||||||
- name: Upload static AArch64 cloud-hypervisor
|
- name: Upload static AArch64 cloud-hypervisor
|
||||||
|
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||||
id: upload-release-static-aarch64-cloud-hypervisor
|
id: upload-release-static-aarch64-cloud-hypervisor
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
@@ -130,6 +117,7 @@ jobs:
|
|||||||
asset_name: cloud-hypervisor-static-aarch64
|
asset_name: cloud-hypervisor-static-aarch64
|
||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
- name: Upload static AArch64 ch-remote
|
- name: Upload static AArch64 ch-remote
|
||||||
|
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||||
id: upload-release-static-aarch64-ch-remote
|
id: upload-release-static-aarch64-ch-remote
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
@@ -139,3 +127,24 @@ jobs:
|
|||||||
asset_path: target/aarch64-unknown-linux-musl/release/ch-remote
|
asset_path: target/aarch64-unknown-linux-musl/release/ch-remote
|
||||||
asset_name: ch-remote-static-aarch64
|
asset_name: ch-remote-static-aarch64
|
||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
|
- name: Vendor
|
||||||
|
working-directory: ../cloud-hypervisor-${{ github.event.ref }}
|
||||||
|
run: |
|
||||||
|
mkdir ../vendor-cargo-home
|
||||||
|
export CARGO_HOME=$(realpath ../vendor-cargo-home)
|
||||||
|
mkdir .cargo
|
||||||
|
cargo vendor > .cargo/config.toml
|
||||||
|
- name: Create vendored source archive
|
||||||
|
working-directory: ../
|
||||||
|
run: tar cJf cloud-hypervisor-${{ github.event.ref }}.tar.xz cloud-hypervisor-${{ github.event.ref }}
|
||||||
|
- name: Upload cloud-hypervisor vendored source archive
|
||||||
|
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||||
|
id: upload-release-cloud-hypervisor-vendored-sources
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ../cloud-hypervisor-${{ github.event.ref }}.tar.xz
|
||||||
|
asset_name: cloud-hypervisor-${{ github.event.ref }}.tar.xz
|
||||||
|
asset_content_type: application/x-xz
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@
|
|||||||
**/Cargo.lock
|
**/Cargo.lock
|
||||||
**/rusty-tags.vi
|
**/rusty-tags.vi
|
||||||
/rpm/SOURCES
|
/rpm/SOURCES
|
||||||
|
/.vscode
|
||||||
|
|||||||
@@ -17,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)
|
||||||
|
|||||||
614
Cargo.lock
generated
614
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
49
Cargo.toml
49
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cloud-hypervisor"
|
name = "cloud-hypervisor"
|
||||||
version = "25.0.0"
|
version = "29.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,58 +10,59 @@ 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
|
||||||
|
# Policy on MSRV (see #4318):
|
||||||
|
# Can only be bumped by:
|
||||||
|
# a.) A dependency requires it,
|
||||||
|
# b.) If we want to use a new feature and that MSRV is at least 6 months old,
|
||||||
|
# c.) There is a security issue that is addressed by the toolchain update.
|
||||||
rust-version = "1.60"
|
rust-version = "1.60"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
opt-level = "s"
|
||||||
|
strip = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.58"
|
anyhow = "1.0.68"
|
||||||
api_client = { path = "api_client" }
|
api_client = { path = "api_client" }
|
||||||
clap = { version = "3.2.8", features = ["wrap_help","cargo"] }
|
clap = { version = "4.0.32", features = ["wrap_help","cargo","string"] }
|
||||||
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.126"
|
libc = "0.2.139"
|
||||||
log = { version = "0.4.17", 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.82"
|
serde_json = "1.0.89"
|
||||||
signal-hook = "0.3.14"
|
signal-hook = "0.3.14"
|
||||||
thiserror = "1.0.31"
|
thiserror = "1.0.38"
|
||||||
|
tpm = { path = "tpm"}
|
||||||
|
tracer = { path = "tracer" }
|
||||||
vmm = { path = "vmm" }
|
vmm = { path = "vmm" }
|
||||||
vmm-sys-util = "0.9.0"
|
vmm-sys-util = "0.11.0"
|
||||||
vm-memory = "0.8.0"
|
vm-memory = "0.10.0"
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
clap = { version = "3.2.8", features = ["cargo"] }
|
|
||||||
|
|
||||||
# List of patched crates
|
# List of patched crates
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0-tdx" }
|
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx" }
|
||||||
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "main" }
|
|
||||||
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
|
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
dirs = "4.0.0"
|
dirs = "4.0.0"
|
||||||
net_util = { path = "net_util" }
|
net_util = { path = "net_util" }
|
||||||
once_cell = "1.13.0"
|
once_cell = "1.16.0"
|
||||||
serde_json = "1.0.82"
|
serde_json = "1.0.89"
|
||||||
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
|
|
||||||
common = ["fwdebug"]
|
|
||||||
amx = ["vmm/amx"]
|
|
||||||
cmos = ["vmm/cmos"]
|
|
||||||
fwdebug = ["vmm/fwdebug"]
|
|
||||||
gdb = ["vmm/gdb"]
|
|
||||||
guest_debug = ["vmm/guest_debug"]
|
guest_debug = ["vmm/guest_debug"]
|
||||||
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 = [
|
||||||
@@ -79,7 +80,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",
|
||||||
|
|||||||
841
Jenkinsfile
vendored
841
Jenkinsfile
vendored
@@ -1,428 +1,449 @@
|
|||||||
def runWorkers = true
|
def runWorkers = true
|
||||||
pipeline{
|
pipeline {
|
||||||
agent none
|
agent none
|
||||||
stages {
|
stages {
|
||||||
stage ('Early checks') {
|
stage('Early checks') {
|
||||||
agent { node { label 'built-in' } }
|
agent { node { label 'built-in' } }
|
||||||
stages {
|
stages {
|
||||||
stage ('Checkout') {
|
stage('Checkout') {
|
||||||
steps {
|
steps {
|
||||||
checkout scm
|
checkout scm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Check for documentation only changes') {
|
stage('Check if worker build can be skipped') {
|
||||||
when {
|
when {
|
||||||
expression {
|
expression {
|
||||||
return docsFileOnly()
|
return skipWorkerBuild()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
runWorkers = false
|
runWorkers = false
|
||||||
echo "Documentation only changes, no need to run the CI"
|
echo 'No changes requring a build'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Check for fuzzer cargo files only changes') {
|
stage('Check for RFC/WIP builds') {
|
||||||
when {
|
when {
|
||||||
expression {
|
changeRequest comparator: 'REGEXP', title: '.*(rfc|RFC|wip|WIP).*'
|
||||||
return fuzzCargoFileOnly()
|
beforeAgent true
|
||||||
}
|
}
|
||||||
}
|
steps {
|
||||||
steps {
|
error('Failing as this is marked as a WIP or RFC PR.')
|
||||||
script {
|
}
|
||||||
runWorkers = false
|
}
|
||||||
echo "Fuzzer cargo files only changes, no need to run the CI"
|
stage('Cancel older builds') {
|
||||||
}
|
when { not { branch 'main' } }
|
||||||
}
|
steps {
|
||||||
}
|
cancelPreviousBuilds()
|
||||||
stage ('Check for RFC/WIP builds') {
|
}
|
||||||
when {
|
}
|
||||||
changeRequest comparator: 'REGEXP', title: '.*(rfc|RFC|wip|WIP).*'
|
}
|
||||||
beforeAgent true
|
}
|
||||||
}
|
stage('Build') {
|
||||||
steps {
|
parallel {
|
||||||
error("Failing as this is marked as a WIP or RFC PR.")
|
stage('Worker build') {
|
||||||
}
|
agent { node { label 'jammy' } }
|
||||||
}
|
when {
|
||||||
stage ('Cancel older builds') {
|
beforeAgent true
|
||||||
when { not { branch 'main' } }
|
expression {
|
||||||
steps {
|
return runWorkers
|
||||||
cancelPreviousBuilds()
|
}
|
||||||
}
|
}
|
||||||
}
|
stages {
|
||||||
}
|
stage('Checkout') {
|
||||||
}
|
steps {
|
||||||
stage ('Build') {
|
checkout scm
|
||||||
parallel {
|
}
|
||||||
stage ('Worker build') {
|
}
|
||||||
agent { node { label 'focal' } }
|
stage('Prepare environment') {
|
||||||
when {
|
steps {
|
||||||
beforeAgent true
|
sh 'scripts/prepare_vdpa.sh'
|
||||||
expression {
|
}
|
||||||
return runWorkers
|
}
|
||||||
}
|
stage('Run OpenAPI tests') {
|
||||||
}
|
steps {
|
||||||
stages {
|
sh 'scripts/run_openapi_tests.sh'
|
||||||
stage ('Checkout') {
|
}
|
||||||
steps {
|
}
|
||||||
checkout scm
|
stage('Run unit tests') {
|
||||||
}
|
steps {
|
||||||
}
|
sh 'scripts/dev_cli.sh tests --unit'
|
||||||
stage ('Prepare environment') {
|
}
|
||||||
steps {
|
}
|
||||||
sh "scripts/prepare_vdpa.sh"
|
stage('Run integration tests') {
|
||||||
}
|
options {
|
||||||
}
|
timeout(time: 1, unit: 'HOURS')
|
||||||
stage ('Run OpenAPI tests') {
|
}
|
||||||
steps {
|
steps {
|
||||||
sh "scripts/run_openapi_tests.sh"
|
sh 'sudo modprobe openvswitch'
|
||||||
}
|
sh 'scripts/dev_cli.sh tests --integration'
|
||||||
}
|
}
|
||||||
stage ('Run unit tests') {
|
}
|
||||||
steps {
|
stage('Run live-migration integration tests') {
|
||||||
sh "scripts/dev_cli.sh tests --unit"
|
options {
|
||||||
}
|
timeout(time: 1, unit: 'HOURS')
|
||||||
}
|
}
|
||||||
stage ('Run integration tests') {
|
steps {
|
||||||
options {
|
sh 'sudo modprobe openvswitch'
|
||||||
timeout(time: 1, unit: 'HOURS')
|
sh 'scripts/dev_cli.sh tests --integration-live-migration'
|
||||||
}
|
}
|
||||||
steps {
|
}
|
||||||
sh "sudo modprobe openvswitch"
|
stage('Run unit tests for musl') {
|
||||||
sh "scripts/dev_cli.sh tests --integration"
|
steps {
|
||||||
}
|
sh 'scripts/dev_cli.sh tests --unit --libc musl'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
stage('Run integration tests for musl') {
|
||||||
stage ('AArch64 worker build') {
|
options {
|
||||||
agent { node { label 'bionic-arm64' } }
|
timeout(time: 1, unit: 'HOURS')
|
||||||
when {
|
}
|
||||||
beforeAgent true
|
steps {
|
||||||
expression {
|
sh 'sudo modprobe openvswitch'
|
||||||
return runWorkers
|
sh 'scripts/dev_cli.sh tests --integration --libc musl'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stages {
|
stage('Run live-migration integration tests for musl') {
|
||||||
stage ('Checkout') {
|
options {
|
||||||
steps {
|
timeout(time: 1, unit: 'HOURS')
|
||||||
checkout scm
|
}
|
||||||
}
|
steps {
|
||||||
}
|
sh 'sudo modprobe openvswitch'
|
||||||
stage ('Run unit tests') {
|
sh 'scripts/dev_cli.sh tests --integration-live-migration --libc musl'
|
||||||
steps {
|
}
|
||||||
sh "scripts/dev_cli.sh tests --unit --libc musl"
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Run integration tests') {
|
stage('AArch64 worker build') {
|
||||||
options {
|
agent { node { label 'bionic-arm64' } }
|
||||||
timeout(time: 1, unit: 'HOURS')
|
when {
|
||||||
}
|
beforeAgent true
|
||||||
steps {
|
expression {
|
||||||
sh "sudo modprobe openvswitch"
|
return runWorkers
|
||||||
sh "scripts/dev_cli.sh tests --integration --libc musl"
|
}
|
||||||
}
|
}
|
||||||
}
|
environment {
|
||||||
}
|
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
|
||||||
post {
|
}
|
||||||
always {
|
stages {
|
||||||
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
stage('Checkout') {
|
||||||
deleteDir()
|
steps {
|
||||||
}
|
checkout scm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Worker build (musl)') {
|
stage('Run unit tests') {
|
||||||
agent { node { label 'focal' } }
|
steps {
|
||||||
when {
|
sh 'scripts/dev_cli.sh tests --unit --libc musl'
|
||||||
beforeAgent true
|
}
|
||||||
expression {
|
}
|
||||||
return runWorkers
|
stage('Run integration tests') {
|
||||||
}
|
options {
|
||||||
}
|
timeout(time: 1, unit: 'HOURS')
|
||||||
stages {
|
}
|
||||||
stage ('Checkout') {
|
steps {
|
||||||
steps {
|
sh 'sudo modprobe openvswitch'
|
||||||
checkout scm
|
sh 'scripts/dev_cli.sh tests --integration --libc musl'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Prepare environment') {
|
stage('Install azure-cli') {
|
||||||
steps {
|
steps {
|
||||||
sh "scripts/prepare_vdpa.sh"
|
installAzureCli('bionic', 'arm64')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Run unit tests for musl') {
|
stage('Download Windows image') {
|
||||||
steps {
|
steps {
|
||||||
sh "scripts/dev_cli.sh tests --unit --libc musl"
|
sh '''#!/bin/bash -x
|
||||||
}
|
IMG_BASENAME=windows-11-iot-enterprise-aarch64.raw
|
||||||
}
|
IMG_PATH=$HOME/workloads/$IMG_BASENAME
|
||||||
stage ('Run integration tests for musl') {
|
IMG_GZ_PATH=$HOME/workloads/$IMG_BASENAME.gz
|
||||||
options {
|
IMG_GZ_BLOB_NAME=windows-11-iot-enterprise-aarch64-9-min.raw.gz
|
||||||
timeout(time: 1, unit: 'HOURS')
|
cp "scripts/$IMG_BASENAME.sha1" "$HOME/workloads/"
|
||||||
}
|
pushd "$HOME/workloads"
|
||||||
steps {
|
if sha1sum "$IMG_BASENAME.sha1" --check; then
|
||||||
sh "sudo modprobe openvswitch"
|
exit
|
||||||
sh "scripts/dev_cli.sh tests --integration --libc musl"
|
fi
|
||||||
}
|
popd
|
||||||
}
|
mkdir -p "$HOME/workloads"
|
||||||
}
|
az storage blob download \
|
||||||
}
|
--container-name private-images \
|
||||||
stage ('Worker build SGX') {
|
--file "$IMG_GZ_PATH" \
|
||||||
agent { node { label 'bionic-sgx' } }
|
--name "$IMG_GZ_BLOB_NAME" \
|
||||||
when {
|
--connection-string "$AZURE_CONNECTION_STRING"
|
||||||
beforeAgent true
|
gzip -d $IMG_GZ_PATH
|
||||||
allOf {
|
'''
|
||||||
branch 'main'
|
}
|
||||||
expression {
|
}
|
||||||
return runWorkers
|
stage('Run Windows guest integration tests') {
|
||||||
}
|
options {
|
||||||
}
|
timeout(time: 1, unit: 'HOURS')
|
||||||
}
|
}
|
||||||
stages {
|
steps {
|
||||||
stage ('Checkout') {
|
sh 'scripts/dev_cli.sh tests --integration-windows --libc musl'
|
||||||
steps {
|
}
|
||||||
checkout scm
|
}
|
||||||
}
|
}
|
||||||
}
|
post {
|
||||||
stage ('Run SGX integration tests') {
|
always {
|
||||||
options {
|
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||||
timeout(time: 1, unit: 'HOURS')
|
deleteDir()
|
||||||
}
|
}
|
||||||
steps {
|
}
|
||||||
sh "scripts/dev_cli.sh tests --integration-sgx"
|
}
|
||||||
}
|
stage('Worker build - Windows guest') {
|
||||||
}
|
agent { node { label 'jammy' } }
|
||||||
stage ('Run SGX integration tests for musl') {
|
when {
|
||||||
options {
|
beforeAgent true
|
||||||
timeout(time: 1, unit: 'HOURS')
|
expression {
|
||||||
}
|
return runWorkers
|
||||||
steps {
|
}
|
||||||
sh "scripts/dev_cli.sh tests --integration-sgx --libc musl"
|
}
|
||||||
}
|
environment {
|
||||||
}
|
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
|
||||||
}
|
}
|
||||||
post {
|
stages {
|
||||||
always {
|
stage('Checkout') {
|
||||||
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
steps {
|
||||||
deleteDir()
|
checkout scm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
stage('Install azure-cli') {
|
||||||
stage ('Worker build VFIO') {
|
steps {
|
||||||
agent { node { label 'bionic-vfio' } }
|
installAzureCli('jammy', 'amd64')
|
||||||
when {
|
}
|
||||||
beforeAgent true
|
}
|
||||||
allOf {
|
stage('Download assets') {
|
||||||
branch 'main'
|
steps {
|
||||||
expression {
|
sh "mkdir ${env.HOME}/workloads"
|
||||||
return runWorkers
|
sh 'az storage blob download --container-name private-images --file "$HOME/workloads/windows-server-2019.raw" --name windows-server-2019.raw --connection-string "$AZURE_CONNECTION_STRING"'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
stage('Run Windows guest integration tests') {
|
||||||
stages {
|
options {
|
||||||
stage ('Checkout') {
|
timeout(time: 1, unit: 'HOURS')
|
||||||
steps {
|
}
|
||||||
checkout scm
|
steps {
|
||||||
}
|
sh 'scripts/dev_cli.sh tests --integration-windows'
|
||||||
}
|
}
|
||||||
stage ('Run VFIO integration tests') {
|
}
|
||||||
options {
|
stage('Run Windows guest integration tests for musl') {
|
||||||
timeout(time: 1, unit: 'HOURS')
|
options {
|
||||||
}
|
timeout(time: 1, unit: 'HOURS')
|
||||||
steps {
|
}
|
||||||
sh "scripts/dev_cli.sh tests --integration-vfio"
|
steps {
|
||||||
}
|
sh 'scripts/dev_cli.sh tests --integration-windows --libc musl'
|
||||||
}
|
}
|
||||||
stage ('Run VFIO integration tests for musl') {
|
}
|
||||||
options {
|
}
|
||||||
timeout(time: 1, unit: 'HOURS')
|
}
|
||||||
}
|
stage('Worker build - Metrics') {
|
||||||
steps {
|
agent { node { label 'jammy-metrics' } }
|
||||||
sh "scripts/dev_cli.sh tests --integration-vfio --libc musl"
|
when {
|
||||||
}
|
branch 'main'
|
||||||
}
|
beforeAgent true
|
||||||
}
|
expression {
|
||||||
post {
|
return runWorkers
|
||||||
always {
|
}
|
||||||
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
}
|
||||||
deleteDir()
|
environment {
|
||||||
}
|
METRICS_PUBLISH_KEY = credentials('52e0945f-ce7a-43d1-87af-67d1d87cc40f')
|
||||||
}
|
}
|
||||||
}
|
stages {
|
||||||
stage ('Worker build - Windows guest') {
|
stage('Checkout') {
|
||||||
agent { node { label 'focal' } }
|
steps {
|
||||||
when {
|
checkout scm
|
||||||
beforeAgent true
|
}
|
||||||
expression {
|
}
|
||||||
return runWorkers
|
stage('Run metrics tests') {
|
||||||
}
|
options {
|
||||||
}
|
timeout(time: 1, unit: 'HOURS')
|
||||||
environment {
|
}
|
||||||
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
|
steps {
|
||||||
}
|
sh 'scripts/dev_cli.sh tests --metrics -- -- --report-file /root/workloads/metrics.json'
|
||||||
stages {
|
}
|
||||||
stage ('Checkout') {
|
}
|
||||||
steps {
|
stage('Upload metrics report') {
|
||||||
checkout scm
|
steps {
|
||||||
}
|
sh 'curl -X PUT https://cloud-hypervisor-metrics.azurewebsites.net/api/publishmetrics -H "x-functions-key: $METRICS_PUBLISH_KEY" -T ~/workloads/metrics.json'
|
||||||
}
|
}
|
||||||
stage ('Install azure-cli') {
|
}
|
||||||
steps {
|
}
|
||||||
installAzureCli()
|
}
|
||||||
}
|
stage('Worker build - Rate Limiter') {
|
||||||
}
|
agent { node { label 'focal-metrics' } }
|
||||||
stage ('Download assets') {
|
when {
|
||||||
steps {
|
branch 'main'
|
||||||
sh "mkdir ${env.HOME}/workloads"
|
beforeAgent true
|
||||||
sh 'az storage blob download --container-name private-images --file "$HOME/workloads/windows-server-2019.raw" --name windows-server-2019.raw --connection-string "$AZURE_CONNECTION_STRING"'
|
expression {
|
||||||
}
|
return runWorkers
|
||||||
}
|
}
|
||||||
stage ('Run Windows guest integration tests') {
|
}
|
||||||
options {
|
stages {
|
||||||
timeout(time: 1, unit: 'HOURS')
|
stage('Checkout') {
|
||||||
}
|
steps {
|
||||||
steps {
|
checkout scm
|
||||||
sh "scripts/dev_cli.sh tests --integration-windows"
|
}
|
||||||
}
|
}
|
||||||
}
|
stage('Run rate-limiter integration tests') {
|
||||||
stage ('Run Windows guest integration tests for musl') {
|
options {
|
||||||
options {
|
timeout(time: 10, unit: 'MINUTES')
|
||||||
timeout(time: 1, unit: 'HOURS')
|
}
|
||||||
}
|
steps {
|
||||||
steps {
|
sh 'scripts/dev_cli.sh tests --integration-rate-limiter'
|
||||||
sh "scripts/dev_cli.sh tests --integration-windows --libc musl"
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
stage('Worker build - SGX') {
|
||||||
stage ('Worker build - Live Migration') {
|
agent { node { label 'jammy-sgx' } }
|
||||||
agent { node { label 'focal-small' } }
|
when {
|
||||||
when {
|
beforeAgent true
|
||||||
beforeAgent true
|
allOf {
|
||||||
expression {
|
branch 'main'
|
||||||
return runWorkers
|
expression {
|
||||||
}
|
return runWorkers
|
||||||
}
|
}
|
||||||
stages {
|
}
|
||||||
stage ('Checkout') {
|
}
|
||||||
steps {
|
stages {
|
||||||
checkout scm
|
stage('Checkout') {
|
||||||
}
|
steps {
|
||||||
}
|
checkout scm
|
||||||
stage ('Run live-migration integration tests') {
|
}
|
||||||
options {
|
}
|
||||||
timeout(time: 1, unit: 'HOURS')
|
stage('Run SGX integration tests') {
|
||||||
}
|
options {
|
||||||
steps {
|
timeout(time: 1, unit: 'HOURS')
|
||||||
sh "sudo modprobe openvswitch"
|
}
|
||||||
sh "scripts/dev_cli.sh tests --integration-live-migration"
|
steps {
|
||||||
}
|
sh 'scripts/dev_cli.sh tests --integration-sgx'
|
||||||
}
|
}
|
||||||
stage ('Run live-migration integration tests for musl') {
|
}
|
||||||
options {
|
stage('Run SGX integration tests for musl') {
|
||||||
timeout(time: 1, unit: 'HOURS')
|
options {
|
||||||
}
|
timeout(time: 1, unit: 'HOURS')
|
||||||
steps {
|
}
|
||||||
sh "sudo modprobe openvswitch"
|
steps {
|
||||||
sh "scripts/dev_cli.sh tests --integration-live-migration --libc musl"
|
sh 'scripts/dev_cli.sh tests --integration-sgx --libc musl'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
post {
|
||||||
stage ('Worker build - Metrics') {
|
always {
|
||||||
agent { node { label 'focal-metrics' } }
|
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||||
when {
|
deleteDir()
|
||||||
branch 'main'
|
}
|
||||||
beforeAgent true
|
}
|
||||||
expression {
|
}
|
||||||
return runWorkers
|
stage('Worker build - VFIO') {
|
||||||
}
|
agent { node { label 'jammy-vfio' } }
|
||||||
}
|
when {
|
||||||
environment {
|
beforeAgent true
|
||||||
METRICS_PUBLISH_KEY = credentials('52e0945f-ce7a-43d1-87af-67d1d87cc40f')
|
allOf {
|
||||||
}
|
branch 'main'
|
||||||
stages {
|
expression {
|
||||||
stage ('Checkout') {
|
return runWorkers
|
||||||
steps {
|
}
|
||||||
checkout scm
|
}
|
||||||
}
|
}
|
||||||
}
|
stages {
|
||||||
stage ('Run metrics tests') {
|
stage('Checkout') {
|
||||||
options {
|
steps {
|
||||||
timeout(time: 1, unit: 'HOURS')
|
checkout scm
|
||||||
}
|
}
|
||||||
steps {
|
}
|
||||||
sh 'scripts/dev_cli.sh tests --metrics -- -- --report-file /root/workloads/metrics.json'
|
stage('Run VFIO integration tests') {
|
||||||
}
|
options {
|
||||||
}
|
timeout(time: 1, unit: 'HOURS')
|
||||||
stage ('Upload metrics report') {
|
}
|
||||||
steps {
|
steps {
|
||||||
sh 'curl -X PUT https://cloud-hypervisor-metrics.azurewebsites.net/api/publishmetrics -H "x-functions-key: $METRICS_PUBLISH_KEY" -T ~/workloads/metrics.json'
|
sh 'scripts/dev_cli.sh tests --integration-vfio'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
stage('Run VFIO integration tests for musl') {
|
||||||
}
|
options {
|
||||||
}
|
timeout(time: 1, unit: 'HOURS')
|
||||||
}
|
}
|
||||||
}
|
steps {
|
||||||
post {
|
sh 'scripts/dev_cli.sh tests --integration-vfio --libc musl'
|
||||||
regression {
|
}
|
||||||
script {
|
}
|
||||||
if (env.BRANCH_NAME == 'main') {
|
}
|
||||||
slackSend (color: '#ff0000', message: '"main" branch build is now failing')
|
post {
|
||||||
}
|
always {
|
||||||
}
|
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||||
}
|
deleteDir()
|
||||||
fixed {
|
}
|
||||||
script {
|
}
|
||||||
if (env.BRANCH_NAME == 'main') {
|
}
|
||||||
slackSend (color: '#00ff00', message: '"main" branch build is now fixed')
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
post {
|
||||||
}
|
regression {
|
||||||
|
script {
|
||||||
|
if (env.BRANCH_NAME == 'main') {
|
||||||
|
slackSend(color: '#ff0000', message: '"main" branch build is now failing', channel: '#jenkins-ci')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fixed {
|
||||||
|
script {
|
||||||
|
if (env.BRANCH_NAME == 'main') {
|
||||||
|
slackSend(color: '#00ff00', message: '"main" branch build is now fixed', channel: '#jenkins-ci')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def cancelPreviousBuilds() {
|
def cancelPreviousBuilds() {
|
||||||
// Check for other instances of this particular build, cancel any that are older than the current one
|
// Check for other instances of this particular build, cancel any that are older than the current one
|
||||||
def jobName = env.JOB_NAME
|
def jobName = env.JOB_NAME
|
||||||
def currentBuildNumber = env.BUILD_NUMBER.toInteger()
|
def currentBuildNumber = env.BUILD_NUMBER.toInteger()
|
||||||
def currentJob = Jenkins.instance.getItemByFullName(jobName)
|
def currentJob = Jenkins.instance.getItemByFullName(jobName)
|
||||||
|
|
||||||
// Loop through all instances of this particular job/branch
|
// Loop through all instances of this particular job/branch
|
||||||
for (def build : currentJob.builds) {
|
for (def build : currentJob.builds) {
|
||||||
if (build.isBuilding() && (build.number.toInteger() < currentBuildNumber)) {
|
if (build.isBuilding() && (build.number.toInteger() < currentBuildNumber)) {
|
||||||
echo "Older build still queued. Sending kill signal to build number: ${build.number}"
|
echo "Older build still queued. Sending kill signal to build number: ${build.number}"
|
||||||
build.doStop()
|
build.doStop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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'
|
||||||
}
|
}
|
||||||
|
|
||||||
def boolean docsFileOnly() {
|
def boolean skipWorkerBuild() {
|
||||||
if (env.CHANGE_TARGET == null) {
|
if (env.CHANGE_TARGET == null) {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return sh(
|
if (sh(
|
||||||
returnStatus: true,
|
returnStatus: true,
|
||||||
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) {
|
||||||
}
|
return true
|
||||||
|
|
||||||
def boolean fuzzCargoFileOnly() {
|
|
||||||
if (env.CHANGE_TARGET == null) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sh(
|
if (sh(
|
||||||
returnStatus: true,
|
returnStatus: true,
|
||||||
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v -E 'fuzz\\/Cargo.(toml|lock)'"
|
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v -E 'fuzz/'"
|
||||||
) != 0
|
) != 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sh(
|
||||||
|
returnStatus: true,
|
||||||
|
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v -E '.github/'"
|
||||||
|
) != 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
399
README.md
399
README.md
@@ -4,42 +4,43 @@
|
|||||||
- [Architectures](#architectures)
|
- [Architectures](#architectures)
|
||||||
- [Guest OS](#guest-os)
|
- [Guest OS](#guest-os)
|
||||||
- [2. Getting Started](#2-getting-started)
|
- [2. Getting Started](#2-getting-started)
|
||||||
- [Preparation](#preparation)
|
- [Host OS](#host-os)
|
||||||
- [Install prerequisites](#install-prerequisites)
|
- [Use Pre-built Binaries](#use-pre-built-binaries)
|
||||||
- [Clone and build](#clone-and-build)
|
- [Packages](#packages)
|
||||||
- [Containerized builds and tests](#containerized-builds-and-tests)
|
- [Building from Source](#building-from-source)
|
||||||
- [Run](#run)
|
- [Booting Linux](#booting-linux)
|
||||||
- [Cloud image](#cloud-image)
|
- [Firmware Booting](#firmware-booting)
|
||||||
- [Custom kernel and disk image](#custom-kernel-and-disk-image)
|
- [Custom Kernel and Disk Image](#custom-kernel-and-disk-image)
|
||||||
- [Building your kernel](#building-your-kernel)
|
- [Building your Kernel](#building-your-kernel)
|
||||||
- [Disk image](#disk-image)
|
- [Disk image](#disk-image)
|
||||||
- [Booting the guest VM](#booting-the-guest-vm)
|
- [Booting the guest VM](#booting-the-guest-vm)
|
||||||
- [3. Status](#3-status)
|
- [3. Status](#3-status)
|
||||||
- [Hot Plug](#hot-plug)
|
- [Hot Plug](#hot-plug)
|
||||||
- [Device Model](#device-model)
|
- [Device Model](#device-model)
|
||||||
- [TODO](#todo)
|
- [Roadmap](#roadmap)
|
||||||
- [4. `rust-vmm` project dependency](#4-rust-vmm-project-dependency)
|
- [4. Relationship with _Rust VMM_ Project](#4-relationship-with-rust-vmm-project)
|
||||||
- [Firecracker and crosvm](#firecracker-and-crosvm)
|
- [Differences with Firecracker and crosvm](#differences-with-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?
|
||||||
|
|
||||||
Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on
|
Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on
|
||||||
top of [KVM](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt)
|
top of the [KVM](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt)
|
||||||
hypervisor and Microsoft Hypervisor (MSHV).
|
hypervisor and the Microsoft Hypervisor (MSHV).
|
||||||
|
|
||||||
The project focuses on exclusively running modern, cloud workloads, on top of
|
The project focuses on running modern, _Cloud Workloads_, on specific, common,
|
||||||
a limited set of hardware architectures and platforms. Cloud workloads refers
|
hardware architectures. In this case _Cloud Workloads_ refers to those that are
|
||||||
to those that are usually run by customers inside a cloud provider. For our
|
run by customers inside a Cloud Service Provider. This means modern operating
|
||||||
purposes this means modern operating systems with most I/O handled by
|
systems with most I/O handled by
|
||||||
paravirtualised devices (i.e. virtio), no requirement for legacy devices, and
|
paravirtualised devices (e.g. _virtio_), no requirement for legacy devices, and
|
||||||
64-bit CPUs.
|
64-bit CPUs.
|
||||||
|
|
||||||
Cloud Hypervisor is implemented in [Rust](https://www.rust-lang.org/) and is
|
Cloud Hypervisor is implemented in [Rust](https://www.rust-lang.org/) and is
|
||||||
based on the [rust-vmm](https://github.com/rust-vmm) crates.
|
based on the [Rust VMM](https://github.com/rust-vmm) crates.
|
||||||
|
|
||||||
## Objectives
|
## Objectives
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ based on the [rust-vmm](https://github.com/rust-vmm) crates.
|
|||||||
### Architectures
|
### Architectures
|
||||||
|
|
||||||
Cloud Hypervisor supports the `x86-64` and `AArch64` architectures. There are
|
Cloud Hypervisor supports the `x86-64` and `AArch64` architectures. There are
|
||||||
some small differences in functionality between the two architectures
|
minor differences in functionality between the two architectures
|
||||||
(see [#1125](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/1125)).
|
(see [#1125](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/1125)).
|
||||||
|
|
||||||
### Guest OS
|
### Guest OS
|
||||||
@@ -68,187 +69,186 @@ Cloud Hypervisor supports `64-bit Linux` and Windows 10/Windows Server 2019.
|
|||||||
|
|
||||||
# 2. Getting Started
|
# 2. Getting Started
|
||||||
|
|
||||||
Below sections describe how to build and run Cloud Hypervisor on the `x86_64`
|
The following sections describe how to build and run Cloud Hypervisor.
|
||||||
platform. For getting started on the `AArch64` platform, please refer to the
|
|
||||||
[Arm64 documentation](docs/arm64.md).
|
|
||||||
|
|
||||||
## Preparation
|
## Prerequisites for AArch64
|
||||||
|
|
||||||
We create a folder to build and run `cloud-hypervisor` at `$HOME/cloud-hypervisor`
|
- AArch64 servers (recommended) or development boards equipped with the GICv3
|
||||||
|
interrupt controller.
|
||||||
|
|
||||||
|
## Host OS
|
||||||
|
|
||||||
|
For required KVM functionality the minimum host kernel version is 4.11. For
|
||||||
|
adequate performance the minimum recommended host kernel version is 5.6. The
|
||||||
|
majority of the CI currently tests with kernel version 5.15.
|
||||||
|
|
||||||
|
## Use Pre-built Binaries
|
||||||
|
|
||||||
|
The recommended approach to getting started with Cloud Hypervisor is by using a
|
||||||
|
pre-built binary. Binaries are available for the [latest
|
||||||
|
release](https://github.com/cloud-hypervisor/cloud-hypervisor/releases/latest).
|
||||||
|
Use `cloud-hypervisor-static` for `x86-64` or `cloud-hypervisor-static-aarch64`
|
||||||
|
for `AArch64` platform.
|
||||||
|
|
||||||
|
## Packages
|
||||||
|
|
||||||
|
For convenience, packages are also available targeting some popular Linux
|
||||||
|
distributions. This is thanks to the [Open Build
|
||||||
|
Service](https://build.opensuse.org). The [OBS
|
||||||
|
README](https://github.com/cloud-hypervisor/obs-packaging) explains how to
|
||||||
|
enable the repository in a supported Linux distribution and install Cloud Hypervisor
|
||||||
|
and accompanying packages. Please report any packaging issues in the
|
||||||
|
[obs-packaging](https://github.com/cloud-hypervisor/obs-packaging) repository.
|
||||||
|
|
||||||
|
## Building from Source
|
||||||
|
|
||||||
|
Please see the [instructions for building from source](docs/building.md) if you
|
||||||
|
do not wish to use the pre-built binaries.
|
||||||
|
|
||||||
|
## Booting Linux
|
||||||
|
|
||||||
|
Cloud Hypervisor supports direct kernel boot (the x86-64 kernel requires the kernel
|
||||||
|
built with PVH support) or booting via a firmware (either [Rust Hypervisor
|
||||||
|
Firmware](https://github.com/cloud-hypervisor/rust-hypervisor-firmware) or an
|
||||||
|
edk2 UEFI firmware called `CLOUDHV` / `CLOUDHV_EFI`.)
|
||||||
|
|
||||||
|
Binary builds of the firmware files are available for the latest release of
|
||||||
|
[Rust Hyperivor
|
||||||
|
Firmware](https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/latest)
|
||||||
|
and [our edk2
|
||||||
|
repository](https://github.com/cloud-hypervisor/edk2/releases/latest)
|
||||||
|
|
||||||
|
The choice of firmware depends on your guest OS choice; some experimentation
|
||||||
|
may be required.
|
||||||
|
|
||||||
|
### Firmware Booting
|
||||||
|
|
||||||
|
Cloud Hypervisor supports booting disk images containing all needed components
|
||||||
|
to run cloud workloads, a.k.a. cloud images.
|
||||||
|
|
||||||
|
The following sample commands will download an Ubuntu Cloud image, converting
|
||||||
|
it into a format that Cloud Hypervisor can use and a firmware to boot the image
|
||||||
|
with.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ export CLOUDH=$HOME/cloud-hypervisor
|
|
||||||
$ mkdir $CLOUDH
|
|
||||||
```
|
|
||||||
|
|
||||||
## Install prerequisites
|
|
||||||
|
|
||||||
You need to install some prerequisite packages in order to build and test Cloud
|
|
||||||
Hypervisor. Here, all the steps are based on Ubuntu, for other Linux
|
|
||||||
distributions please replace the package manager and package name.
|
|
||||||
|
|
||||||
```shell
|
|
||||||
# Install build-essential, git, and qemu-img
|
|
||||||
$ sudo apt install git build-essential qemu-img
|
|
||||||
# Install rust tool chain
|
|
||||||
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
||||||
# If you want to build statically linked binary please add musl target
|
|
||||||
$ rustup target add x86_64-unknown-linux-musl
|
|
||||||
```
|
|
||||||
|
|
||||||
## Clone and build
|
|
||||||
|
|
||||||
First you need to clone and build the cloud-hypervisor repo:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ pushd $CLOUDH
|
|
||||||
$ git clone https://github.com/cloud-hypervisor/cloud-hypervisor.git
|
|
||||||
$ cd cloud-hypervisor
|
|
||||||
$ cargo build --release
|
|
||||||
|
|
||||||
# We need to give the cloud-hypervisor binary the NET_ADMIN capabilities for it to set TAP interfaces up on the host.
|
|
||||||
$ sudo setcap cap_net_admin+ep ./target/release/cloud-hypervisor
|
|
||||||
|
|
||||||
# If you want to build statically linked binary
|
|
||||||
$ cargo build --release --target=x86_64-unknown-linux-musl --all
|
|
||||||
$ popd
|
|
||||||
```
|
|
||||||
|
|
||||||
This will build a `cloud-hypervisor` binary under
|
|
||||||
`$CLOUDH/cloud-hypervisor/target/release/cloud-hypervisor`.
|
|
||||||
|
|
||||||
### Containerized builds and tests
|
|
||||||
|
|
||||||
If you want to build and test Cloud Hypervisor without having to install all the
|
|
||||||
required dependencies (The rust toolchain, cargo tools, etc), you can also use
|
|
||||||
Cloud Hypervisor's development script: `dev_cli.sh`. Please note that upon its
|
|
||||||
first invocation, this script will pull a fairly large container image.
|
|
||||||
|
|
||||||
For example, to build the Cloud Hypervisor release binary:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ pushd $CLOUDH
|
|
||||||
$ cd cloud-hypervisor
|
|
||||||
$ ./scripts/dev_cli.sh build --release
|
|
||||||
```
|
|
||||||
|
|
||||||
With `dev_cli.sh`, one can also run the Cloud Hypervisor CI locally. This can be
|
|
||||||
very convenient for debugging CI errors without having to fully rely on the
|
|
||||||
Cloud Hypervisor CI infrastructure.
|
|
||||||
|
|
||||||
For example, to run the Cloud Hypervisor unit tests:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ ./scripts/dev_cli.sh tests --unit
|
|
||||||
```
|
|
||||||
|
|
||||||
Run the `./scripts/dev_cli.sh --help` command to view all the supported
|
|
||||||
development script commands and their related options.
|
|
||||||
|
|
||||||
## Run
|
|
||||||
|
|
||||||
You can run a guest VM by either using an existing cloud image or booting into
|
|
||||||
your own kernel and disk image.
|
|
||||||
|
|
||||||
### Cloud image
|
|
||||||
|
|
||||||
Cloud Hypervisor supports booting disk images containing all needed
|
|
||||||
components to run cloud workloads, a.k.a. cloud images. To do that we rely on
|
|
||||||
the [Rust Hypervisor
|
|
||||||
Firmware](https://github.com/cloud-hypervisor/rust-hypervisor-firmware) project
|
|
||||||
to provide an ELF formatted KVM firmware for `cloud-hypervisor` to directly
|
|
||||||
boot into.
|
|
||||||
|
|
||||||
We need to get the latest `rust-hypervisor-firmware` release and also a working
|
|
||||||
cloud image. Here we will use a Ubuntu image:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ 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.4.0/hypervisor-fw
|
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.4.2/hypervisor-fw
|
||||||
$ popd
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The Ubuntu cloud images do not ship with a default password so it necessary to
|
||||||
|
use a `cloud-init` disk image to customise the image on the first boot. A basic
|
||||||
|
`cloud-init` image is generated by this [script](scripts/create-cloud-init.sh).
|
||||||
|
This seeds the image with a default username/password of `cloud/cloud123`. It
|
||||||
|
is only necessary to add this disk image on the first boot.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ pushd $CLOUDH
|
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor
|
||||||
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor/target/release/cloud-hypervisor
|
$ ./create-cloud-init.sh
|
||||||
$ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
$ ./cloud-hypervisor \
|
||||||
--kernel ./hypervisor-fw \
|
--kernel ./hypervisor-fw \
|
||||||
--disk path=focal-server-cloudimg-amd64.raw \
|
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||||
--cpus boot=4 \
|
--cpus boot=4 \
|
||||||
--memory size=1024M \
|
--memory size=1024M \
|
||||||
--net "tap=,mac=,ip=,mask="
|
--net "tap=,mac=,ip=,mask="
|
||||||
$ popd
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Multiple arguments can be given to the `--disk` parameter.
|
If access to the firmware messages or interaction with the boot loader (e.g.
|
||||||
|
GRUB) is required then it necessary to switch to the serial console instead of
|
||||||
|
`virtio-console`.
|
||||||
|
|
||||||
### Custom kernel and disk image
|
```shell
|
||||||
|
$ ./cloud-hypervisor \
|
||||||
|
--kernel ./hypervisor-fw \
|
||||||
|
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||||
|
--cpus boot=4 \
|
||||||
|
--memory size=1024M \
|
||||||
|
--net "tap=,mac=,ip=,mask=" \
|
||||||
|
--serial tty \
|
||||||
|
--console off
|
||||||
|
```
|
||||||
|
|
||||||
#### Building your kernel
|
### Custom Kernel and Disk Image
|
||||||
|
|
||||||
Cloud Hypervisor also supports direct kernel boot into a `vmlinux` ELF kernel.
|
#### Building your Kernel
|
||||||
In order to support virtio-watchdog we have our own development branch. You are
|
|
||||||
of course able to use your own kernel but these instructions will continue with
|
Cloud Hypervisor also supports direct kernel boot. For x86-64, a `vmlinux` ELF kernel (compiled with PVH support) is needed. In order to support development there is a custom branch; however provided the required options are enabled any recent kernel will suffice.
|
||||||
the version that we develop and test against.
|
|
||||||
|
|
||||||
To build the kernel:
|
To build the kernel:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
|
||||||
# Clone the Cloud Hypervisor Linux branch
|
# Clone the Cloud Hypervisor Linux branch
|
||||||
$ pushd $CLOUDH
|
|
||||||
$ git clone --depth 1 https://github.com/cloud-hypervisor/linux.git -b ch-5.15.12 linux-cloud-hypervisor
|
$ git clone --depth 1 https://github.com/cloud-hypervisor/linux.git -b ch-5.15.12 linux-cloud-hypervisor
|
||||||
$ pushd linux-cloud-hypervisor
|
$ pushd linux-cloud-hypervisor
|
||||||
|
# Use the x86-64 cloud-hypervisor kernel config to build your kernel for x86-64
|
||||||
# Use the cloud-hypervisor kernel config to build your kernel
|
$ wget https://raw.githubusercontent.com/cloud-hypervisor/cloud-hypervisor/main/resources/linux-config-x86_64
|
||||||
$ cp $CLOUDH/cloud-hypervisor/resources/linux-config-x86_64 .config
|
# Use the AArch64 cloud-hypervisor kernel config to build your kernel for AArch64
|
||||||
|
$ wget https://raw.githubusercontent.com/cloud-hypervisor/cloud-hypervisor/main/resources/linux-config-aarch64
|
||||||
|
$ cp linux-config-x86_64 .config # x86-64
|
||||||
|
$ cp linux-config-aarch64 .config # AArch64
|
||||||
|
# Do native build of the x86-64 kernel
|
||||||
$ KCFLAGS="-Wa,-mx86-used-note=no" make bzImage -j `nproc`
|
$ KCFLAGS="-Wa,-mx86-used-note=no" make bzImage -j `nproc`
|
||||||
|
# Do native build of the AArch64 kernel
|
||||||
|
$ make -j `nproc`
|
||||||
$ popd
|
$ popd
|
||||||
```
|
```
|
||||||
|
|
||||||
The `vmlinux` kernel image will then be located at
|
For x86-64, the `vmlinux` kernel image will then be located at
|
||||||
`linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin`.
|
`linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin`.
|
||||||
|
For AArch64, the `Image` kernel image will then be located at
|
||||||
|
`linux-cloud-hypervisor/arch/arm64/boot/Image`.
|
||||||
|
|
||||||
#### Disk image
|
#### Disk image
|
||||||
|
|
||||||
For the disk image, we will use a Ubuntu cloud image that contains a root
|
For the disk image the same Ubuntu image as before can be used. This contains
|
||||||
partition:
|
an `ext4` root filesystem.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ pushd $CLOUDH
|
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img # x86-64
|
||||||
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
|
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-arm64.img # AArch64
|
||||||
$ 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 # x86-64
|
||||||
$ popd
|
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-arm64.img focal-server-cloudimg-arm64.raw # AArch64
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Booting the guest VM
|
#### Booting the guest VM
|
||||||
|
|
||||||
Now we can directly boot into our custom kernel and make it use the Ubuntu root
|
These sample commands boot the disk image using the custom kernel whilst also
|
||||||
partition. If we want to have 4 vCPUs and 1024 MBytes of memory:
|
supplying the desired kernel command line.
|
||||||
|
|
||||||
|
- x86-64
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ pushd $CLOUDH
|
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor
|
||||||
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor/target/release/cloud-hypervisor
|
$ ./create-cloud-init.sh
|
||||||
$ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
$ ./cloud-hypervisor \
|
||||||
--kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
|
--kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
|
||||||
--disk path=focal-server-cloudimg-amd64.raw \
|
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||||
--cmdline "console=hvc0 root=/dev/vda1 rw" \
|
--cmdline "console=hvc0 root=/dev/vda1 rw" \
|
||||||
--cpus boot=4 \
|
--cpus boot=4 \
|
||||||
--memory size=1024M \
|
--memory size=1024M \
|
||||||
--net "tap=,mac=,ip=,mask="
|
--net "tap=,mac=,ip=,mask="
|
||||||
```
|
```
|
||||||
|
|
||||||
The above example use the `virtio-console` device as the guest console, and this
|
- AArch64
|
||||||
device may not be enabled soon enough by the guest kernel to get early kernel
|
|
||||||
debug messages.
|
|
||||||
|
|
||||||
When in need for earlier debug messages, using the legacy serial device based
|
|
||||||
console is preferred:
|
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor
|
||||||
|
$ ./create-cloud-init.sh
|
||||||
|
$ ./cloud-hypervisor \
|
||||||
|
--kernel ./linux-cloud-hypervisor/arch/arm64/boot/Image \
|
||||||
|
--disk path=focal-server-cloudimg-arm64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||||
|
--cmdline "console=hvc0 root=/dev/vda1 rw" \
|
||||||
|
--cpus boot=4 \
|
||||||
|
--memory size=1024M \
|
||||||
|
--net "tap=,mac=,ip=,mask="
|
||||||
```
|
```
|
||||||
$ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
|
||||||
|
If earlier kernel messages are required the serial console should be used instead of `virtio-console`.
|
||||||
|
|
||||||
|
- x86-64
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ ./cloud-hypervisor \
|
||||||
--kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
|
--kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
|
||||||
--console off \
|
--console off \
|
||||||
--serial tty \
|
--serial tty \
|
||||||
@@ -259,10 +259,24 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
|||||||
--net "tap=,mac=,ip=,mask="
|
--net "tap=,mac=,ip=,mask="
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- AArch64
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ ./cloud-hypervisor \
|
||||||
|
--kernel ./linux-cloud-hypervisor/arch/arm64/boot/Image \
|
||||||
|
--console off \
|
||||||
|
--serial tty \
|
||||||
|
--disk path=focal-server-cloudimg-arm64.raw \
|
||||||
|
--cmdline "console=ttyAMA0 root=/dev/vda1 rw" \
|
||||||
|
--cpus boot=4 \
|
||||||
|
--memory size=1024M \
|
||||||
|
--net "tap=,mac=,ip=,mask="
|
||||||
|
```
|
||||||
|
|
||||||
# 3. Status
|
# 3. Status
|
||||||
|
|
||||||
Cloud Hypervisor is under active development. The following stability guarantees
|
Cloud Hypervisor is under active development. The following stability
|
||||||
are currently made:
|
guarantees are currently made:
|
||||||
|
|
||||||
* The API (including command line options) will not be removed or changed in a
|
* The API (including command line options) will not be removed or changed in a
|
||||||
breaking way without a minimum of 2 major releases notice. Where possible
|
breaking way without a minimum of 2 major releases notice. Where possible
|
||||||
@@ -280,16 +294,17 @@ Currently the following items are **not** guaranteed across updates:
|
|||||||
* The following features are considered experimental and may change
|
* The following features are considered experimental and may change
|
||||||
substantially between releases: TDX, vfio-user, vDPA.
|
substantially between releases: TDX, vfio-user, vDPA.
|
||||||
|
|
||||||
As of 2022-04-05, the following cloud images are supported:
|
Further details can be found in the [release documentation](docs/releases.md).
|
||||||
|
|
||||||
- [Ubuntu Bionic](https://cloud-images.ubuntu.com/bionic/current/) (cloudimg)
|
As of 2023-01-03, the following cloud images are supported:
|
||||||
- [Ubuntu Focal](https://cloud-images.ubuntu.com/focal/current/) (cloudimg)
|
|
||||||
- [Ubuntu Jammy](https://cloud-images.ubuntu.com/jammy/current/) (cloudimg)
|
- [Ubuntu Focal](https://cloud-images.ubuntu.com/focal/current/) (focal-server-cloudimg-{amd64,arm64}.img)
|
||||||
|
- [Ubuntu Jammy](https://cloud-images.ubuntu.com/jammy/current/) (jammy-server-cloudimg-{amd64,arm64}.img )
|
||||||
|
- [Fedora 36](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/) ([Fedora-Cloud-Base-36-1.5.x86_64.raw.xz](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/x86_64/images/) / [Fedora-Cloud-Base-36-1.5.aarch64.raw.xz](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/aarch64/images/))
|
||||||
|
|
||||||
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 although you may need to enable exotic filesystem types in the
|
||||||
|
reference kernel configuration (e.g. XFS or btrfs.)
|
||||||
Further details can be found in the [release documentation](docs/releases.md).
|
|
||||||
|
|
||||||
## Hot Plug
|
## Hot Plug
|
||||||
|
|
||||||
@@ -302,14 +317,12 @@ Cloud Hypervisor supports hotplug of CPUs, passthrough devices (VFIO),
|
|||||||
Details of the device model can be found in this
|
Details of the device model can be found in this
|
||||||
[documentation](docs/device_model.md).
|
[documentation](docs/device_model.md).
|
||||||
|
|
||||||
## TODO
|
## Roadmap
|
||||||
|
|
||||||
We are not tracking the Cloud Hypervisor TODO list from a specific git tracked
|
The project roadmap is tracked through a [GitHub
|
||||||
file but through
|
project](https://github.com/orgs/cloud-hypervisor/projects/6).
|
||||||
[github issues](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/new)
|
|
||||||
instead.
|
|
||||||
|
|
||||||
# 4. `rust-vmm` project dependency
|
# 4. Relationship with _Rust VMM_ Project
|
||||||
|
|
||||||
In order to satisfy the design goal of having a high-performance,
|
In order to satisfy the design goal of having a high-performance,
|
||||||
security-focused hypervisor the decision was made to use the
|
security-focused hypervisor the decision was made to use the
|
||||||
@@ -318,39 +331,26 @@ focus on memory and thread safety makes it an ideal candidate for implementing
|
|||||||
VMMs.
|
VMMs.
|
||||||
|
|
||||||
Instead of implementing the VMM components from scratch, Cloud Hypervisor is
|
Instead of implementing the VMM components from scratch, Cloud Hypervisor is
|
||||||
importing the [rust-vmm](https://github.com/rust-vmm) crates, and sharing code
|
importing the [Rust VMM](https://github.com/rust-vmm) crates, and sharing code
|
||||||
and architecture together with other VMMs like e.g. Amazon's
|
and architecture together with other VMMs like e.g. Amazon's
|
||||||
[Firecracker](https://firecracker-microvm.github.io/) and Google's
|
[Firecracker](https://firecracker-microvm.github.io/) and Google's
|
||||||
[crosvm](https://chromium.googlesource.com/chromiumos/platform/crosvm/).
|
[crosvm](https://chromium.googlesource.com/chromiumos/platform/crosvm/).
|
||||||
|
|
||||||
Cloud Hypervisor embraces the rust-vmm project goals, which is to be able to
|
Cloud Hypervisor embraces the _Rust VMM_ project's goals, which is to be able
|
||||||
share and re-use as many virtualization crates as possible. As such, the Cloud
|
to share and re-use as many virtualization crates as possible.
|
||||||
Hypervisor relationship with the rust-vmm project is twofold:
|
|
||||||
|
|
||||||
1. It will use as much of the rust-vmm code as possible. Any new rust-vmm crate
|
## Differences with Firecracker and crosvm
|
||||||
that's relevant to the project goals will be integrated as soon as possible.
|
|
||||||
2. As it is likely that the rust-vmm project will lack some of the features that
|
|
||||||
Cloud Hypervisor needs (e.g. ACPI, VFIO, vhost-user, etc), we will be using
|
|
||||||
the Cloud Hypervisor VMM to implement and test them, and contribute them back
|
|
||||||
to the rust-vmm project.
|
|
||||||
|
|
||||||
## Firecracker and crosvm
|
|
||||||
|
|
||||||
A large part of the Cloud Hypervisor code is based on either the Firecracker or
|
A large part of the Cloud Hypervisor code is based on either the Firecracker or
|
||||||
the crosvm projects implementations. Both of these are VMMs written in Rust with
|
the crosvm project's implementations. Both of these are VMMs written in Rust
|
||||||
a focus on safety and security, like Cloud Hypervisor.
|
with a focus on safety and security, like Cloud Hypervisor.
|
||||||
|
|
||||||
However we want to emphasize that the Cloud Hypervisor project is neither a fork
|
The goal of the Cloud Hypervisor project differs from the aforementioned
|
||||||
nor a reimplementation of any of those projects. The goals and use cases we're
|
projects in that it aims to be a general purpose VMM for _Cloud Workloads_ and
|
||||||
trying to meet are different. We're aiming at supporting cloud workloads, i.e.
|
not limited to container/serverless or client workloads.
|
||||||
those modern, full Linux distribution images currently being run by Cloud
|
|
||||||
Service Provider (CSP) tenants.
|
|
||||||
|
|
||||||
Our primary target is not to support client or serverless use cases, and as such
|
The Cloud Hypervisor community thanks the communities of both the Firecracker
|
||||||
our code base already diverges from the crosvm and Firecracker ones. As we add
|
and crosvm projects for their excellent work.
|
||||||
more features to support our use cases, we believe that the divergence will
|
|
||||||
increase while at the same time sharing as much of the fundamental
|
|
||||||
virtualization code through the rust-vmm project crates as possible.
|
|
||||||
|
|
||||||
# 5. Community
|
# 5. Community
|
||||||
|
|
||||||
@@ -360,21 +360,28 @@ repository.
|
|||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
|
|
||||||
We are working on building a global, diverse and collaborative community around
|
The project strongly believes in building a global, diverse and collaborative
|
||||||
the Cloud Hypervisor project. Anyone who is interested in
|
community around the Cloud Hypervisor project. Anyone who is interested in
|
||||||
[contributing](CONTRIBUTING.md) to the project is welcome to participate.
|
[contributing](CONTRIBUTING.md) to the project is welcome to participate.
|
||||||
|
|
||||||
We believe that contributing to a open source project like Cloud Hypervisor
|
Contributing to a open source project like Cloud Hypervisor covers a lot more
|
||||||
covers a lot more than just sending code. Testing, documentation, pull request
|
than just sending code. Testing, documentation, pull request
|
||||||
reviews, bug reports, feature requests, project improvement suggestions, etc,
|
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.8.0"
|
vm-memory = "0.10.0"
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -37,7 +38,6 @@ pub struct Sdt {
|
|||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::len_without_is_empty)]
|
|
||||||
impl Sdt {
|
impl Sdt {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
signature: [u8; 4],
|
signature: [u8; 4],
|
||||||
@@ -97,6 +97,7 @@ impl Sdt {
|
|||||||
/// Write a value at the given offset
|
/// Write a value at the given offset
|
||||||
pub fn write<T>(&mut self, offset: usize, value: T) {
|
pub fn write<T>(&mut self, offset: usize, value: T) {
|
||||||
assert!((offset + (std::mem::size_of::<T>() - 1)) < self.data.len());
|
assert!((offset + (std::mem::size_of::<T>() - 1)) < self.data.len());
|
||||||
|
// SAFETY: The assertion above makes sure we don't do out of bounds write.
|
||||||
unsafe {
|
unsafe {
|
||||||
*(((self.data.as_mut_ptr() as usize) + offset) as *mut T) = value;
|
*(((self.data.as_mut_ptr() as usize) + offset) as *mut T) = value;
|
||||||
}
|
}
|
||||||
@@ -122,6 +123,10 @@ impl Sdt {
|
|||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.data.len()
|
self.data.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.data.is_empty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -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.11.0"
|
||||||
|
|||||||
@@ -22,16 +22,16 @@ impl fmt::Display for Error {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use Error::*;
|
use Error::*;
|
||||||
match self {
|
match self {
|
||||||
Socket(e) => write!(f, "Error writing to or reading from HTTP socket: {}", e),
|
Socket(e) => write!(f, "Error writing to or reading from HTTP socket: {e}"),
|
||||||
SocketSendFds(e) => write!(f, "Error writing to or reading from HTTP socket: {}", e),
|
SocketSendFds(e) => write!(f, "Error writing to or reading from HTTP socket: {e}"),
|
||||||
StatusCodeParsing(e) => write!(f, "Error parsing HTTP status code: {}", e),
|
StatusCodeParsing(e) => write!(f, "Error parsing HTTP status code: {e}"),
|
||||||
MissingProtocol => write!(f, "HTTP output is missing protocol statement"),
|
MissingProtocol => write!(f, "HTTP output is missing protocol statement"),
|
||||||
ContentLengthParsing(e) => write!(f, "Error parsing HTTP Content-Length field: {}", e),
|
ContentLengthParsing(e) => write!(f, "Error parsing HTTP Content-Length field: {e}"),
|
||||||
ServerResponse(s, o) => {
|
ServerResponse(s, o) => {
|
||||||
if let Some(o) = o {
|
if let Some(o) = o {
|
||||||
write!(f, "Server responded with an error: {:?}: {}", s, o)
|
write!(f, "Server responded with an error: {s:?}: {o}")
|
||||||
} else {
|
} else {
|
||||||
write!(f, "Server responded with an error: {:?}", s)
|
write!(f, "Server responded with an error: {s:?}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ impl StatusCode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_header<'a>(res: &'a str, header: &'a str) -> Option<&'a str> {
|
fn get_header<'a>(res: &'a str, header: &'a str) -> Option<&'a str> {
|
||||||
let header_str = format!("{}: ", header);
|
let header_str = format!("{header}: ");
|
||||||
res.find(&header_str)
|
res.find(&header_str)
|
||||||
.map(|o| &res[o + header_str.len()..o + res[o..].find('\r').unwrap()])
|
.map(|o| &res[o + header_str.len()..o + res[o..].find('\r').unwrap()])
|
||||||
}
|
}
|
||||||
@@ -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,19 @@ 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",
|
"{method} /api/v1/{full_command} HTTP/1.1\r\nHost: localhost\r\nAccept: */*\r\n"
|
||||||
method, c
|
|
||||||
)
|
)
|
||||||
.as_bytes()],
|
.as_bytes()],
|
||||||
&request_fds,
|
&request_fds,
|
||||||
@@ -171,12 +176,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,21 +9,21 @@ default = []
|
|||||||
tdx = []
|
tdx = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
acpi_tables = { path = "../acpi_tables" }
|
anyhow = "1.0.68"
|
||||||
anyhow = "1.0.58"
|
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
libc = "0.2.126"
|
libc = "0.2.139"
|
||||||
linux-loader = { version = "0.4.0", features = ["elf", "bzimage", "pe"] }
|
linux-loader = { version = "0.8.1", features = ["elf", "bzimage", "pe"] }
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
serde = { version = "1.0.138", features = ["rc", "derive"] }
|
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||||
thiserror = "1.0.31"
|
thiserror = "1.0.38"
|
||||||
versionize = "0.1.6"
|
uuid = "1.2.2"
|
||||||
|
versionize = "0.1.9"
|
||||||
versionize_derive = "0.1.4"
|
versionize_derive = "0.1.4"
|
||||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-bitmap"] }
|
vm-memory = { version = "0.10.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.11.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" }
|
||||||
|
|||||||
@@ -51,8 +51,6 @@ const SIZE_CELLS: u32 = 0x2;
|
|||||||
// Look for "The 1st cell..."
|
// Look for "The 1st cell..."
|
||||||
const GIC_FDT_IRQ_TYPE_SPI: u32 = 0;
|
const GIC_FDT_IRQ_TYPE_SPI: u32 = 0;
|
||||||
const GIC_FDT_IRQ_TYPE_PPI: u32 = 1;
|
const GIC_FDT_IRQ_TYPE_PPI: u32 = 1;
|
||||||
const GIC_FDT_IRQ_PPI_CPU_SHIFT: u32 = 8;
|
|
||||||
const GIC_FDT_IRQ_PPI_CPU_MASK: u32 = 0xff << GIC_FDT_IRQ_PPI_CPU_SHIFT;
|
|
||||||
|
|
||||||
// From https://elixir.bootlin.com/linux/v4.9.62/source/include/dt-bindings/interrupt-controller/irq.h#L17
|
// From https://elixir.bootlin.com/linux/v4.9.62/source/include/dt-bindings/interrupt-controller/irq.h#L17
|
||||||
const IRQ_TYPE_EDGE_RISING: u32 = 1;
|
const IRQ_TYPE_EDGE_RISING: u32 = 1;
|
||||||
@@ -121,7 +119,7 @@ pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHash
|
|||||||
create_gic_node(&mut fdt, gic_device)?;
|
create_gic_node(&mut fdt, gic_device)?;
|
||||||
create_timer_node(&mut fdt)?;
|
create_timer_node(&mut fdt)?;
|
||||||
if pmu_supported {
|
if pmu_supported {
|
||||||
create_pmu_node(&mut fdt, vcpu_mpidr.len())?;
|
create_pmu_node(&mut fdt)?;
|
||||||
}
|
}
|
||||||
create_clock_node(&mut fdt)?;
|
create_clock_node(&mut fdt)?;
|
||||||
create_psci_node(&mut fdt)?;
|
create_psci_node(&mut fdt)?;
|
||||||
@@ -162,7 +160,7 @@ fn create_cpu_nodes(
|
|||||||
let num_cpus = vcpu_mpidr.len();
|
let num_cpus = vcpu_mpidr.len();
|
||||||
|
|
||||||
for (cpu_id, mpidr) in vcpu_mpidr.iter().enumerate().take(num_cpus) {
|
for (cpu_id, mpidr) in vcpu_mpidr.iter().enumerate().take(num_cpus) {
|
||||||
let cpu_name = format!("cpu@{:x}", cpu_id);
|
let cpu_name = format!("cpu@{cpu_id:x}");
|
||||||
let cpu_node = fdt.begin_node(&cpu_name)?;
|
let cpu_node = fdt.begin_node(&cpu_name)?;
|
||||||
fdt.property_string("device_type", "cpu")?;
|
fdt.property_string("device_type", "cpu")?;
|
||||||
fdt.property_string("compatible", "arm,arm-v8")?;
|
fdt.property_string("compatible", "arm,arm-v8")?;
|
||||||
@@ -194,15 +192,15 @@ fn create_cpu_nodes(
|
|||||||
|
|
||||||
// Create device tree nodes with regard of above mapping.
|
// Create device tree nodes with regard of above mapping.
|
||||||
for cluster_idx in 0..packages {
|
for cluster_idx in 0..packages {
|
||||||
let cluster_name = format!("cluster{:x}", cluster_idx);
|
let cluster_name = format!("cluster{cluster_idx:x}");
|
||||||
let cluster_node = fdt.begin_node(&cluster_name)?;
|
let cluster_node = fdt.begin_node(&cluster_name)?;
|
||||||
|
|
||||||
for core_idx in 0..cores_per_package {
|
for core_idx in 0..cores_per_package {
|
||||||
let core_name = format!("core{:x}", core_idx);
|
let core_name = format!("core{core_idx:x}");
|
||||||
let core_node = fdt.begin_node(&core_name)?;
|
let core_node = fdt.begin_node(&core_name)?;
|
||||||
|
|
||||||
for thread_idx in 0..threads_per_core {
|
for thread_idx in 0..threads_per_core {
|
||||||
let thread_name = format!("thread{:x}", thread_idx);
|
let thread_name = format!("thread{thread_idx:x}");
|
||||||
let thread_node = fdt.begin_node(&thread_name)?;
|
let thread_node = fdt.begin_node(&thread_name)?;
|
||||||
let cpu_idx = threads_per_core * cores_per_package * cluster_idx
|
let cpu_idx = threads_per_core * cores_per_package * cluster_idx
|
||||||
+ threads_per_core * core_idx
|
+ threads_per_core * core_idx
|
||||||
@@ -249,7 +247,7 @@ fn create_memory_node(
|
|||||||
node_memory_addr = memory_region_start_addr;
|
node_memory_addr = memory_region_start_addr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let memory_node_name = format!("memory@{:x}", node_memory_addr);
|
let memory_node_name = format!("memory@{node_memory_addr:x}");
|
||||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||||
fdt.property_string("device_type", "memory")?;
|
fdt.property_string("device_type", "memory")?;
|
||||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||||
@@ -261,7 +259,7 @@ fn create_memory_node(
|
|||||||
if last_addr < super::layout::MEM_32BIT_RESERVED_START.raw_value() {
|
if last_addr < super::layout::MEM_32BIT_RESERVED_START.raw_value() {
|
||||||
// Case 1: all RAM is under the hole
|
// Case 1: all RAM is under the hole
|
||||||
let mem_size = last_addr - super::layout::RAM_START.raw_value() + 1;
|
let mem_size = last_addr - super::layout::RAM_START.raw_value() + 1;
|
||||||
let mem_reg_prop = [super::layout::RAM_START.raw_value() as u64, mem_size as u64];
|
let mem_reg_prop = [super::layout::RAM_START.raw_value(), mem_size];
|
||||||
let memory_node = fdt.begin_node("memory")?;
|
let memory_node = fdt.begin_node("memory")?;
|
||||||
fdt.property_string("device_type", "memory")?;
|
fdt.property_string("device_type", "memory")?;
|
||||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||||
@@ -271,7 +269,7 @@ fn create_memory_node(
|
|||||||
// Region 1: RAM before the hole
|
// Region 1: RAM before the hole
|
||||||
let mem_size = super::layout::MEM_32BIT_RESERVED_START.raw_value()
|
let mem_size = super::layout::MEM_32BIT_RESERVED_START.raw_value()
|
||||||
- super::layout::RAM_START.raw_value();
|
- super::layout::RAM_START.raw_value();
|
||||||
let mem_reg_prop = [super::layout::RAM_START.raw_value() as u64, mem_size as u64];
|
let mem_reg_prop = [super::layout::RAM_START.raw_value(), mem_size];
|
||||||
let memory_node_name = format!("memory@{:x}", super::layout::RAM_START.raw_value());
|
let memory_node_name = format!("memory@{:x}", super::layout::RAM_START.raw_value());
|
||||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||||
fdt.property_string("device_type", "memory")?;
|
fdt.property_string("device_type", "memory")?;
|
||||||
@@ -280,10 +278,7 @@ fn create_memory_node(
|
|||||||
|
|
||||||
// Region 2: RAM after the hole
|
// Region 2: RAM after the hole
|
||||||
let mem_size = last_addr - super::layout::RAM_64BIT_START.raw_value() + 1;
|
let mem_size = last_addr - super::layout::RAM_64BIT_START.raw_value() + 1;
|
||||||
let mem_reg_prop = [
|
let mem_reg_prop = [super::layout::RAM_64BIT_START.raw_value(), mem_size];
|
||||||
super::layout::RAM_64BIT_START.raw_value() as u64,
|
|
||||||
mem_size as u64,
|
|
||||||
];
|
|
||||||
let memory_node_name =
|
let memory_node_name =
|
||||||
format!("memory@{:x}", super::layout::RAM_64BIT_START.raw_value());
|
format!("memory@{:x}", super::layout::RAM_64BIT_START.raw_value());
|
||||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||||
@@ -305,7 +300,7 @@ fn create_chosen_node(
|
|||||||
fdt.property_string("bootargs", cmdline)?;
|
fdt.property_string("bootargs", cmdline)?;
|
||||||
|
|
||||||
if let Some(initrd_config) = initrd {
|
if let Some(initrd_config) = initrd {
|
||||||
let initrd_start = initrd_config.address.raw_value() as u64;
|
let initrd_start = initrd_config.address.raw_value();
|
||||||
let initrd_end = initrd_config.address.raw_value() + initrd_config.size as u64;
|
let initrd_end = initrd_config.address.raw_value() + initrd_config.size as u64;
|
||||||
fdt.property_u64("linux,initrd-start", initrd_start)?;
|
fdt.property_u64("linux,initrd-start", initrd_start)?;
|
||||||
fdt.property_u64("linux,initrd-end", initrd_end)?;
|
fdt.property_u64("linux,initrd-end", initrd_end)?;
|
||||||
@@ -541,16 +536,9 @@ fn create_devices_node<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::Buil
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_pmu_node(fdt: &mut FdtWriter, cpu_nums: usize) -> FdtWriterResult<()> {
|
fn create_pmu_node(fdt: &mut FdtWriter) -> FdtWriterResult<()> {
|
||||||
let num_cpus = cpu_nums as u64 as u32;
|
|
||||||
let compatible = "arm,armv8-pmuv3";
|
let compatible = "arm,armv8-pmuv3";
|
||||||
let cpu_mask: u32 =
|
let irq = [GIC_FDT_IRQ_TYPE_PPI, AARCH64_PMU_IRQ, IRQ_TYPE_LEVEL_HI];
|
||||||
(((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK;
|
|
||||||
let irq = [
|
|
||||||
GIC_FDT_IRQ_TYPE_PPI,
|
|
||||||
AARCH64_PMU_IRQ,
|
|
||||||
cpu_mask | IRQ_TYPE_LEVEL_HI,
|
|
||||||
];
|
|
||||||
|
|
||||||
let pmu_node = fdt.begin_node("pmu")?;
|
let pmu_node = fdt.begin_node("pmu")?;
|
||||||
fdt.property_string("compatible", compatible)?;
|
fdt.property_string("compatible", compatible)?;
|
||||||
@@ -679,7 +667,7 @@ fn create_pci_nodes(
|
|||||||
|
|
||||||
// See kernel document Documentation/devicetree/bindings/virtio/iommu.txt
|
// See kernel document Documentation/devicetree/bindings/virtio/iommu.txt
|
||||||
// for virtio-iommu node settings.
|
// for virtio-iommu node settings.
|
||||||
let virtio_iommu_node_name = format!("virtio_iommu@{:x}", virtio_iommu_bdf);
|
let virtio_iommu_node_name = format!("virtio_iommu@{virtio_iommu_bdf:x}");
|
||||||
let virtio_iommu_node = fdt.begin_node(&virtio_iommu_node_name)?;
|
let virtio_iommu_node = fdt.begin_node(&virtio_iommu_node_name)?;
|
||||||
fdt.property_u32("#iommu-cells", 1)?;
|
fdt.property_u32("#iommu-cells", 1)?;
|
||||||
fdt.property_string("compatible", "virtio,pci-iommu")?;
|
fdt.property_string("compatible", "virtio,pci-iommu")?;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
@@ -98,6 +98,11 @@ pub const RAM_START: GuestAddress = GuestAddress(0x4000_0000);
|
|||||||
pub const MEM_32BIT_RESERVED_START: GuestAddress = GuestAddress(0xfc00_0000);
|
pub const MEM_32BIT_RESERVED_START: GuestAddress = GuestAddress(0xfc00_0000);
|
||||||
pub const MEM_32BIT_RESERVED_SIZE: u64 = 0x0400_0000;
|
pub const MEM_32BIT_RESERVED_SIZE: u64 = 0x0400_0000;
|
||||||
|
|
||||||
|
/// TPM Address Range
|
||||||
|
/// This Address range is specific to CRB Interface
|
||||||
|
pub const TPM_START: GuestAddress = GuestAddress(0xfed4_0000);
|
||||||
|
pub const TPM_SIZE: u64 = 0x1000;
|
||||||
|
|
||||||
/// Start of 64-bit RAM.
|
/// Start of 64-bit RAM.
|
||||||
pub const RAM_64BIT_START: GuestAddress = GuestAddress(0x1_0000_0000);
|
pub const RAM_64BIT_START: GuestAddress = GuestAddress(0x1_0000_0000);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
pub mod fdt;
|
pub mod fdt;
|
||||||
/// Layout for this aarch64 system.
|
/// Layout for this aarch64 system.
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
|
/// Module for system registers definition
|
||||||
|
pub mod regs;
|
||||||
/// Module for loading UEFI binary.
|
/// Module for loading UEFI binary.
|
||||||
pub mod uefi;
|
pub mod uefi;
|
||||||
|
|
||||||
@@ -17,7 +19,7 @@ use std::collections::HashMap;
|
|||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use vm_memory::{Address, GuestAddress, GuestMemory, GuestUsize};
|
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic, GuestUsize};
|
||||||
|
|
||||||
/// Errors thrown while configuring aarch64 system.
|
/// Errors thrown while configuring aarch64 system.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -62,9 +64,9 @@ pub struct EntryPoint {
|
|||||||
pub fn configure_vcpu(
|
pub fn configure_vcpu(
|
||||||
vcpu: &Arc<dyn hypervisor::Vcpu>,
|
vcpu: &Arc<dyn hypervisor::Vcpu>,
|
||||||
id: u8,
|
id: u8,
|
||||||
kernel_entry_point: Option<EntryPoint>,
|
boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>,
|
||||||
) -> super::Result<u64> {
|
) -> super::Result<u64> {
|
||||||
if let Some(kernel_entry_point) = kernel_entry_point {
|
if let Some((kernel_entry_point, _guest_memory)) = boot_setup {
|
||||||
vcpu.setup_regs(
|
vcpu.setup_regs(
|
||||||
id,
|
id,
|
||||||
kernel_entry_point.entry_addr.raw_value(),
|
kernel_entry_point.entry_addr.raw_value(),
|
||||||
@@ -73,7 +75,9 @@ pub fn configure_vcpu(
|
|||||||
.map_err(Error::RegsConfiguration)?;
|
.map_err(Error::RegsConfiguration)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mpidr = vcpu.read_mpidr().map_err(Error::VcpuRegMpidr)?;
|
let mpidr = vcpu
|
||||||
|
.get_sys_reg(regs::MPIDR_EL1)
|
||||||
|
.map_err(Error::VcpuRegMpidr)?;
|
||||||
Ok(mpidr)
|
Ok(mpidr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +108,7 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
|
|||||||
|
|
||||||
// RAM space
|
// RAM space
|
||||||
// Case1: guest memory fits before the gap
|
// Case1: guest memory fits before the gap
|
||||||
if size as u64 <= ram_32bit_space_size {
|
if size <= ram_32bit_space_size {
|
||||||
regions.push((layout::RAM_START, size as usize, RegionType::Ram));
|
regions.push((layout::RAM_START, size as usize, RegionType::Ram));
|
||||||
// Case2: guest memory extends beyond the gap
|
// Case2: guest memory extends beyond the gap
|
||||||
} else {
|
} else {
|
||||||
@@ -227,7 +231,7 @@ mod tests {
|
|||||||
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START) as usize;
|
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START) as usize;
|
||||||
assert_eq!(6, regions.len());
|
assert_eq!(6, regions.len());
|
||||||
assert_eq!(layout::RAM_START, regions[3].0);
|
assert_eq!(layout::RAM_START, regions[3].0);
|
||||||
assert_eq!(ram_32bit_space_size as usize, regions[3].1);
|
assert_eq!(ram_32bit_space_size, regions[3].1);
|
||||||
assert_eq!(RegionType::Ram, regions[3].2);
|
assert_eq!(RegionType::Ram, regions[3].2);
|
||||||
assert_eq!(RegionType::Reserved, regions[5].2);
|
assert_eq!(RegionType::Reserved, regions[5].2);
|
||||||
assert_eq!(RegionType::Ram, regions[4].2);
|
assert_eq!(RegionType::Ram, regions[4].2);
|
||||||
|
|||||||
44
arch/src/aarch64/regs.rs
Normal file
44
arch/src/aarch64/regs.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
///
|
||||||
|
/// AArch64 system register encoding:
|
||||||
|
/// See https://developer.arm.com/documentation/ddi0487 (chapter D12)
|
||||||
|
///
|
||||||
|
/// 31 22 21 20 19 18 16 15 12 11 8 7 5 4 0
|
||||||
|
/// +----------+---+-----+-----+-----+-----+-----+----+
|
||||||
|
/// |1101010100| L | op0 | op1 | CRn | CRm | op2 | Rt |
|
||||||
|
/// +----------+---+-----+-----+-----+-----+-----+----+
|
||||||
|
///
|
||||||
|
/// Notes:
|
||||||
|
/// - L and Rt are reserved as implementation defined fields, ignored.
|
||||||
|
///
|
||||||
|
const SYSREG_HEAD: u32 = 0b1101010100u32 << 22;
|
||||||
|
const SYSREG_OP0_SHIFT: u32 = 19;
|
||||||
|
const SYSREG_OP0_MASK: u32 = 0b11u32 << 19;
|
||||||
|
const SYSREG_OP1_SHIFT: u32 = 16;
|
||||||
|
const SYSREG_OP1_MASK: u32 = 0b111u32 << 16;
|
||||||
|
const SYSREG_CRN_SHIFT: u32 = 12;
|
||||||
|
const SYSREG_CRN_MASK: u32 = 0b1111u32 << 12;
|
||||||
|
const SYSREG_CRM_SHIFT: u32 = 8;
|
||||||
|
const SYSREG_CRM_MASK: u32 = 0b1111u32 << 8;
|
||||||
|
const SYSREG_OP2_SHIFT: u32 = 5;
|
||||||
|
const SYSREG_OP2_MASK: u32 = 0b111u32 << 5;
|
||||||
|
|
||||||
|
/// Define the ID of system registers
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! arm64_sys_reg {
|
||||||
|
($name: tt, $op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => {
|
||||||
|
pub const $name: u32 = SYSREG_HEAD
|
||||||
|
| ((($op0 as u32) << SYSREG_OP0_SHIFT) & SYSREG_OP0_MASK as u32)
|
||||||
|
| ((($op1 as u32) << SYSREG_OP1_SHIFT) & SYSREG_OP1_MASK as u32)
|
||||||
|
| ((($crn as u32) << SYSREG_CRN_SHIFT) & SYSREG_CRN_MASK as u32)
|
||||||
|
| ((($crm as u32) << SYSREG_CRM_SHIFT) & SYSREG_CRM_MASK as u32)
|
||||||
|
| ((($op2 as u32) << SYSREG_OP2_SHIFT) & SYSREG_OP2_MASK as u32);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
arm64_sys_reg!(MPIDR_EL1, 3, 0, 0, 0, 5);
|
||||||
|
arm64_sys_reg!(ID_AA64MMFR0_EL1, 3, 0, 0, 7, 0);
|
||||||
|
arm64_sys_reg!(TTBR1_EL1, 3, 0, 2, 0, 1);
|
||||||
|
arm64_sys_reg!(TCR_EL1, 3, 0, 2, 0, 2);
|
||||||
@@ -34,9 +34,7 @@ where
|
|||||||
if uefi_size > 0x300000 {
|
if uefi_size > 0x300000 {
|
||||||
return Err(Error::UefiTooBig);
|
return Err(Error::UefiTooBig);
|
||||||
}
|
}
|
||||||
uefi_image
|
uefi_image.rewind().map_err(|_| Error::SeekUefiStart)?;
|
||||||
.seek(SeekFrom::Start(0))
|
|
||||||
.map_err(|_| Error::SeekUefiStart)?;
|
|
||||||
guest_mem
|
guest_mem
|
||||||
.read_exact_from(guest_addr, uefi_image, uefi_size)
|
.read_exact_from(guest_addr, uefi_image, uefi_size)
|
||||||
.map_err(|_| Error::ReadUefiImage)
|
.map_err(|_| Error::ReadUefiImage)
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ pub use x86_64::{
|
|||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn pagesize() -> usize {
|
fn pagesize() -> usize {
|
||||||
// Trivially safe
|
// SAFETY: Trivially safe
|
||||||
unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
|
unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ pub const PAGE_SIZE: usize = 4096;
|
|||||||
|
|
||||||
impl fmt::Display for DeviceType {
|
impl fmt::Display for DeviceType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{:?}", self)
|
write!(f, "{self:?}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -107,6 +107,11 @@ pub const KVM_TSS_SIZE: u64 = (3 * 4) << 10;
|
|||||||
pub const KVM_IDENTITY_MAP_START: GuestAddress = GuestAddress(KVM_TSS_START.0 + KVM_TSS_SIZE);
|
pub const KVM_IDENTITY_MAP_START: GuestAddress = GuestAddress(KVM_TSS_START.0 + KVM_TSS_SIZE);
|
||||||
pub const KVM_IDENTITY_MAP_SIZE: u64 = 4 << 10;
|
pub const KVM_IDENTITY_MAP_SIZE: u64 = 4 << 10;
|
||||||
|
|
||||||
|
/// TPM Address Range
|
||||||
|
/// This Address range is specific to CRB Interface
|
||||||
|
pub const TPM_START: GuestAddress = GuestAddress(0xfed4_0000);
|
||||||
|
pub const TPM_SIZE: u64 = 0x1000;
|
||||||
|
|
||||||
// IOAPIC
|
// IOAPIC
|
||||||
pub const IOAPIC_START: GuestAddress = GuestAddress(0xfec0_0000);
|
pub const IOAPIC_START: GuestAddress = GuestAddress(0xfec0_0000);
|
||||||
pub const IOAPIC_SIZE: u64 = 0x20;
|
pub const IOAPIC_SIZE: u64 = 0x20;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub mod regs;
|
|||||||
use crate::GuestMemoryMmap;
|
use crate::GuestMemoryMmap;
|
||||||
use crate::InitramfsConfig;
|
use crate::InitramfsConfig;
|
||||||
use crate::RegionType;
|
use crate::RegionType;
|
||||||
use hypervisor::x86_64::{CpuId, CpuIdEntry, CPUID_FLAG_VALID_INDEX};
|
use hypervisor::arch::x86::{CpuIdEntry, CPUID_FLAG_VALID_INDEX};
|
||||||
use hypervisor::HypervisorError;
|
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::{
|
||||||
@@ -125,9 +125,11 @@ struct MemmapTableEntryWrapper(hvm_memmap_table_entry);
|
|||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
struct ModlistEntryWrapper(hvm_modlist_entry);
|
struct ModlistEntryWrapper(hvm_modlist_entry);
|
||||||
|
|
||||||
// SAFETY: These data structures only contain a series of integers
|
// SAFETY: data structure only contain a series of integers
|
||||||
unsafe impl ByteValued for StartInfoWrapper {}
|
unsafe impl ByteValued for StartInfoWrapper {}
|
||||||
|
// SAFETY: data structure only contain a series of integers
|
||||||
unsafe impl ByteValued for MemmapTableEntryWrapper {}
|
unsafe impl ByteValued for MemmapTableEntryWrapper {}
|
||||||
|
// SAFETY: data structure only contain a series of integers
|
||||||
unsafe impl ByteValued for ModlistEntryWrapper {}
|
unsafe impl ByteValued for ModlistEntryWrapper {}
|
||||||
|
|
||||||
// This is a workaround to the Rust enforcement specifying that any implementation of a foreign
|
// This is a workaround to the Rust enforcement specifying that any implementation of a foreign
|
||||||
@@ -202,7 +204,7 @@ impl From<Error> for super::Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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,
|
||||||
@@ -223,17 +225,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 => {
|
||||||
@@ -279,16 +279,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 {
|
||||||
@@ -312,16 +308,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,
|
||||||
@@ -470,12 +465,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
|
||||||
{
|
{
|
||||||
@@ -505,8 +500,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);
|
||||||
@@ -552,13 +547,30 @@ impl CpuidFeatureEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_common_cpuid(
|
pub fn generate_common_cpuid(
|
||||||
hypervisor: Arc<dyn hypervisor::Hypervisor>,
|
hypervisor: &Arc<dyn hypervisor::Hypervisor>,
|
||||||
topology: Option<(u8, u8, u8)>,
|
topology: Option<(u8, u8, u8)>,
|
||||||
sgx_epc_sections: Option<Vec<SgxEpcSection>>,
|
sgx_epc_sections: Option<Vec<SgxEpcSection>>,
|
||||||
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>> {
|
||||||
|
// SAFETY: cpuid called with valid leaves
|
||||||
|
if unsafe { x86_64::__cpuid(1) }.ecx & 1 << HYPERVISOR_ECX_BIT == 1 << HYPERVISOR_ECX_BIT {
|
||||||
|
// SAFETY: cpuid called with valid leaves
|
||||||
|
let hypervisor_cpuid = unsafe { x86_64::__cpuid(0x4000_0000) };
|
||||||
|
|
||||||
|
let mut identifier: [u8; 12] = [0; 12];
|
||||||
|
identifier[0..4].copy_from_slice(&hypervisor_cpuid.ebx.to_le_bytes()[..]);
|
||||||
|
identifier[4..8].copy_from_slice(&hypervisor_cpuid.ecx.to_le_bytes()[..]);
|
||||||
|
identifier[8..12].copy_from_slice(&hypervisor_cpuid.edx.to_le_bytes()[..]);
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Running under nested virtualisation. Hypervisor string: {}",
|
||||||
|
String::from_utf8_lossy(&identifier)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Generating guest CPUID for with physical address size: {phys_bits}");
|
||||||
let cpuid_patches = vec![
|
let cpuid_patches = vec![
|
||||||
// Patch tsc deadline timer bit
|
// Patch tsc deadline timer bit
|
||||||
CpuidPatch {
|
CpuidPatch {
|
||||||
@@ -638,6 +650,20 @@ pub fn generate_common_cpuid(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Copy host L2 cache details if not populated by KVM
|
||||||
|
0x8000_0006 => {
|
||||||
|
if entry.eax == 0 && entry.ebx == 0 && entry.ecx == 0 && entry.edx == 0 {
|
||||||
|
// SAFETY: cpuid called with valid leaves
|
||||||
|
if unsafe { std::arch::x86_64::__cpuid(0x8000_0000).eax } >= 0x8000_0006 {
|
||||||
|
// SAFETY: cpuid called with valid leaves
|
||||||
|
let leaf = unsafe { std::arch::x86_64::__cpuid(0x8000_0006) };
|
||||||
|
entry.eax = leaf.eax;
|
||||||
|
entry.ebx = leaf.ebx;
|
||||||
|
entry.ecx = leaf.ecx;
|
||||||
|
entry.edx = leaf.edx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Set CPU physical bits
|
// Set CPU physical bits
|
||||||
0x8000_0008 => {
|
0x8000_0008 => {
|
||||||
entry.eax = (entry.eax & 0xffff_ff00) | (phys_bits as u32 & 0xff);
|
entry.eax = (entry.eax & 0xffff_ff00) | (phys_bits as u32 & 0xff);
|
||||||
@@ -668,17 +694,16 @@ pub fn generate_common_cpuid(
|
|||||||
// Copy CPU identification string
|
// Copy CPU identification string
|
||||||
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);
|
||||||
|
// SAFETY: call cpuid with valid leaves
|
||||||
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 {
|
||||||
@@ -687,56 +712,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)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -746,9 +759,8 @@ pub fn generate_common_cpuid(
|
|||||||
pub fn configure_vcpu(
|
pub fn configure_vcpu(
|
||||||
vcpu: &Arc<dyn hypervisor::Vcpu>,
|
vcpu: &Arc<dyn hypervisor::Vcpu>,
|
||||||
id: u8,
|
id: u8,
|
||||||
kernel_entry_point: Option<EntryPoint>,
|
boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>,
|
||||||
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
|
cpuid: Vec<CpuIdEntry>,
|
||||||
cpuid: CpuId,
|
|
||||||
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()
|
||||||
@@ -764,12 +776,12 @@ pub fn configure_vcpu(
|
|||||||
}
|
}
|
||||||
|
|
||||||
regs::setup_msrs(vcpu).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, guest_memory)) = boot_setup {
|
||||||
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(vcpu, entry_addr.raw_value()).map_err(Error::RegsConfiguration)?;
|
regs::setup_regs(vcpu, entry_addr.raw_value()).map_err(Error::RegsConfiguration)?;
|
||||||
regs::setup_fpu(vcpu).map_err(Error::FpuConfiguration)?;
|
regs::setup_fpu(vcpu).map_err(Error::FpuConfiguration)?;
|
||||||
regs::setup_sregs(&vm_memory.memory(), vcpu).map_err(Error::SregsConfiguration)?;
|
regs::setup_sregs(&guest_memory.memory(), vcpu).map_err(Error::SregsConfiguration)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
interrupts::set_lint(vcpu).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
|
interrupts::set_lint(vcpu).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
|
||||||
@@ -785,11 +797,11 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
|
|||||||
.checked_add(layout::MEM_32BIT_DEVICES_SIZE)
|
.checked_add(layout::MEM_32BIT_DEVICES_SIZE)
|
||||||
.expect("32-bit reserved region is too large");
|
.expect("32-bit reserved region is too large");
|
||||||
|
|
||||||
let requested_memory_size = GuestAddress(size as u64);
|
let requested_memory_size = GuestAddress(size);
|
||||||
let mut regions = Vec::new();
|
let mut regions = Vec::new();
|
||||||
|
|
||||||
// case1: guest memory fits before the gap
|
// case1: guest memory fits before the gap
|
||||||
if size as u64 <= layout::MEM_32BIT_RESERVED_START.raw_value() {
|
if size <= layout::MEM_32BIT_RESERVED_START.raw_value() {
|
||||||
regions.push((GuestAddress(0), size as usize, RegionType::Ram));
|
regions.push((GuestAddress(0), size as usize, RegionType::Ram));
|
||||||
// case2: guest memory extends beyond the gap
|
// case2: guest memory extends beyond the gap
|
||||||
} else {
|
} else {
|
||||||
@@ -840,13 +852,16 @@ pub fn configure_system(
|
|||||||
rsdp_addr: Option<GuestAddress>,
|
rsdp_addr: Option<GuestAddress>,
|
||||||
sgx_epc_region: Option<SgxEpcRegion>,
|
sgx_epc_region: Option<SgxEpcRegion>,
|
||||||
serial_number: Option<&str>,
|
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, serial_number).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);
|
||||||
@@ -883,7 +898,7 @@ fn configure_pvh(
|
|||||||
start_info.0.magic = XEN_HVM_START_MAGIC_VALUE;
|
start_info.0.magic = XEN_HVM_START_MAGIC_VALUE;
|
||||||
start_info.0.version = 1; // pvh has version 1
|
start_info.0.version = 1; // pvh has version 1
|
||||||
start_info.0.nr_modules = 0;
|
start_info.0.nr_modules = 0;
|
||||||
start_info.0.cmdline_paddr = cmdline_addr.raw_value() as u64;
|
start_info.0.cmdline_paddr = cmdline_addr.raw_value();
|
||||||
start_info.0.memmap_paddr = layout::MEMMAP_START.raw_value();
|
start_info.0.memmap_paddr = layout::MEMMAP_START.raw_value();
|
||||||
|
|
||||||
if let Some(rsdp_addr) = rsdp_addr {
|
if let Some(rsdp_addr) = rsdp_addr {
|
||||||
@@ -952,7 +967,7 @@ fn configure_pvh(
|
|||||||
add_memmap_entry(
|
add_memmap_entry(
|
||||||
&mut memmap,
|
&mut memmap,
|
||||||
sgx_epc_region.start().raw_value(),
|
sgx_epc_region.start().raw_value(),
|
||||||
sgx_epc_region.size() as u64,
|
sgx_epc_region.size(),
|
||||||
E820_RESERVED,
|
E820_RESERVED,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1029,6 +1044,7 @@ pub fn initramfs_load_addr(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_host_cpu_phys_bits() -> u8 {
|
pub fn get_host_cpu_phys_bits() -> u8 {
|
||||||
|
// SAFETY: call cpuid with valid leaves
|
||||||
unsafe {
|
unsafe {
|
||||||
let leaf = x86_64::__cpuid(0x8000_0000);
|
let leaf = x86_64::__cpuid(0x8000_0000);
|
||||||
|
|
||||||
@@ -1056,7 +1072,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,
|
||||||
@@ -1120,7 +1136,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);
|
||||||
@@ -1136,12 +1155,13 @@ fn update_cpuid_sgx(cpuid: &mut CpuId, epc_sections: Vec<SgxEpcSection>) -> Resu
|
|||||||
|
|
||||||
// Get host CPUID for leaf 0x12, subleaf 0x2. This is to retrieve EPC
|
// Get host CPUID for leaf 0x12, subleaf 0x2. This is to retrieve EPC
|
||||||
// properties such as confidentiality and integrity.
|
// properties such as confidentiality and integrity.
|
||||||
|
// SAFETY: call cpuid with valid leaves
|
||||||
let leaf = unsafe { std::arch::x86_64::__cpuid_count(0x12, 0x2) };
|
let leaf = unsafe { std::arch::x86_64::__cpuid_count(0x12, 0x2) };
|
||||||
|
|
||||||
for (i, epc_section) in epc_sections.iter().enumerate() {
|
for (i, epc_section) in epc_sections.iter().enumerate() {
|
||||||
let subleaf_idx = i + 2;
|
let subleaf_idx = i + 2;
|
||||||
let start = epc_section.start().raw_value();
|
let start = epc_section.start().raw_value();
|
||||||
let size = epc_section.size() as u64;
|
let size = epc_section.size();
|
||||||
let eax = (start & 0xffff_f000) as u32 | 0x1;
|
let eax = (start & 0xffff_f000) as u32 | 0x1;
|
||||||
let ebx = (start >> 32) as u32;
|
let ebx = (start >> 32) as u32;
|
||||||
let ecx = (size & 0xffff_f000) as u32 | (leaf.ecx & 0xf);
|
let ecx = (size & 0xffff_f000) as u32 | (leaf.ecx & 0xf);
|
||||||
@@ -1196,6 +1216,8 @@ mod tests {
|
|||||||
Some(layout::RSDP_POINTER),
|
Some(layout::RSDP_POINTER),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
assert!(config_err.is_err());
|
assert!(config_err.is_err());
|
||||||
|
|
||||||
@@ -1209,7 +1231,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, 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;
|
||||||
@@ -1220,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, 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, 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;
|
||||||
@@ -1233,9 +1288,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, 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, None).unwrap();
|
configure_system(
|
||||||
|
&gm,
|
||||||
|
GuestAddress(0),
|
||||||
|
&None,
|
||||||
|
no_vcpus,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1260,6 +1337,6 @@ mod tests {
|
|||||||
add_memmap_entry(&mut memmap, 0, 0x1000, E820_RAM);
|
add_memmap_entry(&mut memmap, 0, 0x1000, E820_RAM);
|
||||||
add_memmap_entry(&mut memmap, 0x10000, 0xa000, E820_RESERVED);
|
add_memmap_entry(&mut memmap, 0x10000, 0xa000, E820_RESERVED);
|
||||||
|
|
||||||
assert_eq!(format!("{:?}", memmap), format!("{:?}", expected_memmap));
|
assert_eq!(format!("{memmap:?}"), format!("{expected_memmap:?}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,11 +37,17 @@ struct MpfIntelWrapper(mpspec::mpf_intel);
|
|||||||
|
|
||||||
// SAFETY: These `mpspec` wrapper types are only data, reading them from data is a safe initialization.
|
// SAFETY: These `mpspec` wrapper types are only data, reading them from data is a safe initialization.
|
||||||
unsafe impl ByteValued for MpcBusWrapper {}
|
unsafe impl ByteValued for MpcBusWrapper {}
|
||||||
|
// SAFETY: see above
|
||||||
unsafe impl ByteValued for MpcCpuWrapper {}
|
unsafe impl ByteValued for MpcCpuWrapper {}
|
||||||
|
// SAFETY: see above
|
||||||
unsafe impl ByteValued for MpcIntsrcWrapper {}
|
unsafe impl ByteValued for MpcIntsrcWrapper {}
|
||||||
|
// SAFETY: see above
|
||||||
unsafe impl ByteValued for MpcIoapicWrapper {}
|
unsafe impl ByteValued for MpcIoapicWrapper {}
|
||||||
|
// SAFETY: see above
|
||||||
unsafe impl ByteValued for MpcTableWrapper {}
|
unsafe impl ByteValued for MpcTableWrapper {}
|
||||||
|
// SAFETY: see above
|
||||||
unsafe impl ByteValued for MpcLintsrcWrapper {}
|
unsafe impl ByteValued for MpcLintsrcWrapper {}
|
||||||
|
// SAFETY: see above
|
||||||
unsafe impl ByteValued for MpfIntelWrapper {}
|
unsafe impl ByteValued for MpfIntelWrapper {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -95,7 +101,7 @@ const CPU_FEATURE_APIC: u32 = 0x200;
|
|||||||
const CPU_FEATURE_FPU: u32 = 0x001;
|
const CPU_FEATURE_FPU: u32 = 0x001;
|
||||||
|
|
||||||
fn compute_checksum<T: Copy>(v: &T) -> u8 {
|
fn compute_checksum<T: Copy>(v: &T) -> u8 {
|
||||||
// Safe because we are only reading the bytes within the size of the `T` reference `v`.
|
// SAFETY: we are only reading the bytes within the size of the `T` reference `v`.
|
||||||
let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
|
let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
|
||||||
let mut checksum: u8 = 0;
|
let mut checksum: u8 = 0;
|
||||||
for i in v_slice.iter() {
|
for i in v_slice.iter() {
|
||||||
@@ -294,7 +300,7 @@ mod tests {
|
|||||||
mpspec::MP_IOAPIC => mem::size_of::<MpcIoapicWrapper>(),
|
mpspec::MP_IOAPIC => mem::size_of::<MpcIoapicWrapper>(),
|
||||||
mpspec::MP_INTSRC => mem::size_of::<MpcIntsrcWrapper>(),
|
mpspec::MP_INTSRC => mem::size_of::<MpcIntsrcWrapper>(),
|
||||||
mpspec::MP_LINTSRC => mem::size_of::<MpcLintsrcWrapper>(),
|
mpspec::MP_LINTSRC => mem::size_of::<MpcLintsrcWrapper>(),
|
||||||
_ => panic!("unrecognized mpc table entry type: {}", type_),
|
_ => panic!("unrecognized mpc table entry type: {type_}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ 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::CR0_PE;
|
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(())
|
||||||
@@ -124,7 +124,7 @@ pub fn configure_segments_and_sregs(
|
|||||||
mem: &GuestMemoryMmap,
|
mem: &GuestMemoryMmap,
|
||||||
sregs: &mut SpecialRegisters,
|
sregs: &mut SpecialRegisters,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let gdt_table: [u64; BOOT_GDT_MAX as usize] = {
|
let gdt_table: [u64; BOOT_GDT_MAX] = {
|
||||||
// Configure GDT entries as specified by PVH boot protocol
|
// Configure GDT entries as specified by PVH boot protocol
|
||||||
[
|
[
|
||||||
gdt_entry(0, 0, 0), // NULL
|
gdt_entry(0, 0, 0), // NULL
|
||||||
|
|||||||
@@ -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,14 +39,19 @@ 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,12 +61,13 @@ 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;
|
||||||
|
|
||||||
fn compute_checksum<T: Copy>(v: &T) -> u8 {
|
fn compute_checksum<T: Copy>(v: &T) -> u8 {
|
||||||
// Safe because we are only reading the bytes within the size of the `T` reference `v`.
|
// SAFETY: we are only reading the bytes within the size of the `T` reference `v`.
|
||||||
let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
|
let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
|
||||||
let mut checksum: u8 = 0;
|
let mut checksum: u8 = 0;
|
||||||
for i in v_slice.iter() {
|
for i in v_slice.iter() {
|
||||||
@@ -68,75 +76,85 @@ 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,
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: These data structures only contain a series of integers
|
#[repr(C)]
|
||||||
|
#[repr(packed)]
|
||||||
|
#[derive(Default, Copy, Clone)]
|
||||||
|
struct SmbiosEndOfTable {
|
||||||
|
r#type: u8,
|
||||||
|
length: u8,
|
||||||
|
handle: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: data structure only contain a series of integers
|
||||||
unsafe impl ByteValued for Smbios30Entrypoint {}
|
unsafe impl ByteValued for Smbios30Entrypoint {}
|
||||||
|
// SAFETY: data structure only contain a series of integers
|
||||||
unsafe impl ByteValued for SmbiosBiosInfo {}
|
unsafe impl ByteValued for SmbiosBiosInfo {}
|
||||||
|
// SAFETY: data structure only contain a series of integers
|
||||||
unsafe impl ByteValued for SmbiosSysInfo {}
|
unsafe impl ByteValued for SmbiosSysInfo {}
|
||||||
|
// SAFETY: data structure only contain a series of integers
|
||||||
|
unsafe impl ByteValued for SmbiosOemStrings {}
|
||||||
|
// SAFETY: data structure only contain a series of integers
|
||||||
|
unsafe impl ByteValued for SmbiosEndOfTable {}
|
||||||
|
|
||||||
fn write_and_incr<T: ByteValued>(
|
fn write_and_incr<T: ByteValued>(
|
||||||
mem: &GuestMemoryMmap,
|
mem: &GuestMemoryMmap,
|
||||||
@@ -162,7 +180,12 @@ fn write_string(
|
|||||||
Ok(curptr)
|
Ok(curptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_smbios(mem: &GuestMemoryMmap, serial_number: Option<&str>) -> 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 +195,7 @@ pub fn setup_smbios(mem: &GuestMemoryMmap, serial_number: Option<&str>) -> Resul
|
|||||||
{
|
{
|
||||||
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,13 +212,20 @@ pub fn setup_smbios(mem: &GuestMemoryMmap, serial_number: Option<&str>) -> Resul
|
|||||||
|
|
||||||
{
|
{
|
||||||
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
|
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)?;
|
||||||
@@ -207,15 +237,34 @@ pub fn setup_smbios(mem: &GuestMemoryMmap, serial_number: Option<&str>) -> Resul
|
|||||||
curptr = write_and_incr(mem, 0u8, 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)?;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
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)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +286,7 @@ pub fn setup_smbios(mem: &GuestMemoryMmap, serial_number: Option<&str>) -> Resul
|
|||||||
.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)]
|
||||||
@@ -267,7 +316,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, None).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();
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
use crate::GuestMemoryMmap;
|
use crate::GuestMemoryMmap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Seek, SeekFrom};
|
use std::io::{Read, Seek, SeekFrom};
|
||||||
|
use std::str::FromStr;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use uuid::Uuid;
|
||||||
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemoryError};
|
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemoryError};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
@@ -13,6 +15,8 @@ pub enum TdvfError {
|
|||||||
ReadDescriptor(#[source] std::io::Error),
|
ReadDescriptor(#[source] std::io::Error),
|
||||||
#[error("Failed read TDVF descriptor offset: {0}")]
|
#[error("Failed read TDVF descriptor offset: {0}")]
|
||||||
ReadDescriptorOffset(#[source] std::io::Error),
|
ReadDescriptorOffset(#[source] std::io::Error),
|
||||||
|
#[error("Failed read GUID table: {0}")]
|
||||||
|
ReadGuidTable(#[source] std::io::Error),
|
||||||
#[error("Invalid descriptor signature")]
|
#[error("Invalid descriptor signature")]
|
||||||
InvalidDescriptorSignature,
|
InvalidDescriptorSignature,
|
||||||
#[error("Invalid descriptor size")]
|
#[error("Invalid descriptor size")]
|
||||||
@@ -21,8 +25,13 @@ pub enum TdvfError {
|
|||||||
InvalidDescriptorVersion,
|
InvalidDescriptorVersion,
|
||||||
#[error("Failed to write HOB details to guest memory: {0}")]
|
#[error("Failed to write HOB details to guest memory: {0}")]
|
||||||
GuestMemoryWriteHob(#[source] GuestMemoryError),
|
GuestMemoryWriteHob(#[source] GuestMemoryError),
|
||||||
|
#[error("Failed to create Uuid: {0}")]
|
||||||
|
UuidCreation(#[source] uuid::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TABLE_FOOTER_GUID: &str = "96b582de-1fb2-45f7-baea-a366c55a082d";
|
||||||
|
const TDVF_METADATA_OFFSET_GUID: &str = "e47a6535-984a-4798-865e-4685a7bf8ec2";
|
||||||
|
|
||||||
// TDVF_DESCRIPTOR
|
// TDVF_DESCRIPTOR
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -64,7 +73,72 @@ impl Default for TdvfSectionType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_tdvf_sections(file: &mut File) -> Result<Vec<TdvfSection>, TdvfError> {
|
fn tdvf_descriptor_offset(file: &mut File) -> Result<(SeekFrom, bool), TdvfError> {
|
||||||
|
// Let's first try to identify the presence of the table footer GUID
|
||||||
|
file.seek(SeekFrom::End(-0x30))
|
||||||
|
.map_err(TdvfError::ReadGuidTable)?;
|
||||||
|
let mut table_footer_guid: [u8; 16] = [0; 16];
|
||||||
|
file.read_exact(&mut table_footer_guid)
|
||||||
|
.map_err(TdvfError::ReadGuidTable)?;
|
||||||
|
let uuid =
|
||||||
|
Uuid::from_slice_le(table_footer_guid.as_slice()).map_err(TdvfError::UuidCreation)?;
|
||||||
|
let expected_uuid = Uuid::from_str(TABLE_FOOTER_GUID).map_err(TdvfError::UuidCreation)?;
|
||||||
|
if uuid == expected_uuid {
|
||||||
|
// Retrieve the table size
|
||||||
|
file.seek(SeekFrom::End(-0x32))
|
||||||
|
.map_err(TdvfError::ReadGuidTable)?;
|
||||||
|
let mut table_size: [u8; 2] = [0; 2];
|
||||||
|
file.read_exact(&mut table_size)
|
||||||
|
.map_err(TdvfError::ReadGuidTable)?;
|
||||||
|
let table_size = u16::from_le_bytes(table_size) as usize;
|
||||||
|
let mut table: Vec<u8> = vec![0; table_size];
|
||||||
|
|
||||||
|
// Read the entire table
|
||||||
|
file.seek(SeekFrom::End(-(table_size as i64 + 0x20)))
|
||||||
|
.map_err(TdvfError::ReadGuidTable)?;
|
||||||
|
file.read_exact(table.as_mut_slice())
|
||||||
|
.map_err(TdvfError::ReadGuidTable)?;
|
||||||
|
|
||||||
|
// Let's start from the top and go backward down the table.
|
||||||
|
// We start after the footer GUID and the table length.
|
||||||
|
let mut offset = table_size - 18;
|
||||||
|
|
||||||
|
debug!("Parsing GUIDed structure");
|
||||||
|
while offset >= 18 {
|
||||||
|
let entry_uuid = Uuid::from_slice_le(&table[offset - 16..offset])
|
||||||
|
.map_err(TdvfError::UuidCreation)?;
|
||||||
|
let entry_size =
|
||||||
|
u16::from_le_bytes(table[offset - 18..offset - 16].try_into().unwrap()) as usize;
|
||||||
|
debug!(
|
||||||
|
"Entry GUID = {}, size = {}",
|
||||||
|
entry_uuid.hyphenated().to_string(),
|
||||||
|
entry_size
|
||||||
|
);
|
||||||
|
|
||||||
|
// Avoid going through an infinite loop if the entry size is 0
|
||||||
|
if entry_size == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset -= entry_size;
|
||||||
|
|
||||||
|
let expected_uuid =
|
||||||
|
Uuid::from_str(TDVF_METADATA_OFFSET_GUID).map_err(TdvfError::UuidCreation)?;
|
||||||
|
if entry_uuid == expected_uuid && entry_size == 22 {
|
||||||
|
return Ok((
|
||||||
|
SeekFrom::End(
|
||||||
|
-(u32::from_le_bytes(table[offset..offset + 4].try_into().unwrap()) as i64),
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we end up here, this means the firmware doesn't support the new way
|
||||||
|
// of exposing the TDVF descriptor offset through the table of GUIDs.
|
||||||
|
// That's why we fallback onto the deprecated method.
|
||||||
|
|
||||||
// The 32-bit offset to the TDVF metadata is located 32 bytes from
|
// The 32-bit offset to the TDVF metadata is located 32 bytes from
|
||||||
// the end of the file.
|
// the end of the file.
|
||||||
// See "TDVF Metadata Pointer" in "TDX Virtual Firmware Design Guide
|
// See "TDVF Metadata Pointer" in "TDX Virtual Firmware Design Guide
|
||||||
@@ -74,13 +148,21 @@ pub fn parse_tdvf_sections(file: &mut File) -> Result<Vec<TdvfSection>, TdvfErro
|
|||||||
let mut descriptor_offset: [u8; 4] = [0; 4];
|
let mut descriptor_offset: [u8; 4] = [0; 4];
|
||||||
file.read_exact(&mut descriptor_offset)
|
file.read_exact(&mut descriptor_offset)
|
||||||
.map_err(TdvfError::ReadDescriptorOffset)?;
|
.map_err(TdvfError::ReadDescriptorOffset)?;
|
||||||
let descriptor_offset = u32::from_le_bytes(descriptor_offset) as u64;
|
|
||||||
|
|
||||||
file.seek(SeekFrom::Start(descriptor_offset))
|
Ok((
|
||||||
|
SeekFrom::Start(u32::from_le_bytes(descriptor_offset) as u64),
|
||||||
|
false,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_tdvf_sections(file: &mut File) -> Result<(Vec<TdvfSection>, bool), TdvfError> {
|
||||||
|
let (descriptor_offset, guid_found) = tdvf_descriptor_offset(file)?;
|
||||||
|
|
||||||
|
file.seek(descriptor_offset)
|
||||||
.map_err(TdvfError::ReadDescriptor)?;
|
.map_err(TdvfError::ReadDescriptor)?;
|
||||||
|
|
||||||
let mut descriptor: TdvfDescriptor = Default::default();
|
let mut descriptor: TdvfDescriptor = Default::default();
|
||||||
// Safe as we read exactly the size of the descriptor header
|
// SAFETY: we read exactly the size of the descriptor header
|
||||||
file.read_exact(unsafe {
|
file.read_exact(unsafe {
|
||||||
std::slice::from_raw_parts_mut(
|
std::slice::from_raw_parts_mut(
|
||||||
&mut descriptor as *mut _ as *mut u8,
|
&mut descriptor as *mut _ as *mut u8,
|
||||||
@@ -107,7 +189,7 @@ pub fn parse_tdvf_sections(file: &mut File) -> Result<Vec<TdvfSection>, TdvfErro
|
|||||||
let mut sections = Vec::new();
|
let mut sections = Vec::new();
|
||||||
sections.resize_with(descriptor.num_sections as usize, TdvfSection::default);
|
sections.resize_with(descriptor.num_sections as usize, TdvfSection::default);
|
||||||
|
|
||||||
// Safe as we read exactly the advertised sections
|
// SAFETY: we read exactly the advertised sections
|
||||||
file.read_exact(unsafe {
|
file.read_exact(unsafe {
|
||||||
std::slice::from_raw_parts_mut(
|
std::slice::from_raw_parts_mut(
|
||||||
sections.as_mut_ptr() as *mut u8,
|
sections.as_mut_ptr() as *mut u8,
|
||||||
@@ -116,7 +198,7 @@ pub fn parse_tdvf_sections(file: &mut File) -> Result<Vec<TdvfSection>, TdvfErro
|
|||||||
})
|
})
|
||||||
.map_err(TdvfError::ReadDescriptor)?;
|
.map_err(TdvfError::ReadDescriptor)?;
|
||||||
|
|
||||||
Ok(sections)
|
Ok((sections, guid_found))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u16)]
|
#[repr(u16)]
|
||||||
@@ -211,12 +293,17 @@ struct TdPayload {
|
|||||||
payload_info: PayloadInfo,
|
payload_info: PayloadInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: These data structures only contain a series of integers
|
// SAFETY: data structure only contain a series of integers
|
||||||
unsafe impl ByteValued for HobHeader {}
|
unsafe impl ByteValued for HobHeader {}
|
||||||
|
// SAFETY: data structure only contain a series of integers
|
||||||
unsafe impl ByteValued for HobHandoffInfoTable {}
|
unsafe impl ByteValued for HobHandoffInfoTable {}
|
||||||
|
// SAFETY: data structure only contain a series of integers
|
||||||
unsafe impl ByteValued for HobResourceDescriptor {}
|
unsafe impl ByteValued for HobResourceDescriptor {}
|
||||||
|
// SAFETY: data structure only contain a series of integers
|
||||||
unsafe impl ByteValued for HobGuidType {}
|
unsafe impl ByteValued for HobGuidType {}
|
||||||
|
// SAFETY: data structure only contain a series of integers
|
||||||
unsafe impl ByteValued for PayloadInfo {}
|
unsafe impl ByteValued for PayloadInfo {}
|
||||||
|
// SAFETY: data structure only contain a series of integers
|
||||||
unsafe impl ByteValued for TdPayload {}
|
unsafe impl ByteValued for TdPayload {}
|
||||||
|
|
||||||
pub struct TdHob {
|
pub struct TdHob {
|
||||||
@@ -312,12 +399,19 @@ impl TdHob {
|
|||||||
physical_start: u64,
|
physical_start: u64,
|
||||||
resource_length: u64,
|
resource_length: u64,
|
||||||
ram: bool,
|
ram: bool,
|
||||||
|
guid_found: bool,
|
||||||
) -> Result<(), TdvfError> {
|
) -> Result<(), TdvfError> {
|
||||||
self.add_resource(
|
self.add_resource(
|
||||||
mem,
|
mem,
|
||||||
physical_start,
|
physical_start,
|
||||||
resource_length,
|
resource_length,
|
||||||
if ram {
|
if ram {
|
||||||
|
if guid_found {
|
||||||
|
0x7 /* EFI_RESOURCE_MEMORY_UNACCEPTED */
|
||||||
|
} else {
|
||||||
|
0 /* EFI_RESOURCE_SYSTEM_MEMORY */
|
||||||
|
}
|
||||||
|
} else if guid_found {
|
||||||
0 /* EFI_RESOURCE_SYSTEM_MEMORY */
|
0 /* EFI_RESOURCE_SYSTEM_MEMORY */
|
||||||
} else {
|
} else {
|
||||||
0x5 /*EFI_RESOURCE_MEMORY_RESERVED */
|
0x5 /*EFI_RESOURCE_MEMORY_RESERVED */
|
||||||
@@ -445,9 +539,9 @@ mod tests {
|
|||||||
#[ignore]
|
#[ignore]
|
||||||
fn test_parse_tdvf_sections() {
|
fn test_parse_tdvf_sections() {
|
||||||
let mut f = std::fs::File::open("tdvf.fd").unwrap();
|
let mut f = std::fs::File::open("tdvf.fd").unwrap();
|
||||||
let sections = parse_tdvf_sections(&mut f).unwrap();
|
let (sections, _) = parse_tdvf_sections(&mut f).unwrap();
|
||||||
for section in sections {
|
for section in sections {
|
||||||
eprintln!("{:x?}", section)
|
eprintln!("{section:x?}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,17 +8,18 @@ edition = "2021"
|
|||||||
default = []
|
default = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
io-uring = "0.5.2"
|
io-uring = "0.5.11"
|
||||||
libc = "0.2.126"
|
libc = "0.2.139"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
qcow = { path = "../qcow" }
|
qcow = { path = "../qcow" }
|
||||||
thiserror = "1.0.31"
|
smallvec = "1.10.0"
|
||||||
versionize = "0.1.6"
|
thiserror = "1.0.38"
|
||||||
|
versionize = "0.1.9"
|
||||||
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.4.0"
|
virtio-queue = "0.7.0"
|
||||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
vm-memory = { version = "0.10.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.11.0"
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,11 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
use libc::{ioctl, S_IFBLK, S_IFMT};
|
use libc::{ioctl, S_IFBLK, S_IFMT};
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::fs::File;
|
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 {
|
||||||
@@ -54,19 +53,21 @@ enum BlockSize {
|
|||||||
impl DiskTopology {
|
impl DiskTopology {
|
||||||
fn is_block_device(f: &mut File) -> std::io::Result<bool> {
|
fn is_block_device(f: &mut File) -> std::io::Result<bool> {
|
||||||
let mut stat = std::mem::MaybeUninit::<libc::stat>::uninit();
|
let mut stat = std::mem::MaybeUninit::<libc::stat>::uninit();
|
||||||
|
// SAFETY: FFI call with a valid fd and buffer
|
||||||
let ret = unsafe { libc::fstat(f.as_raw_fd(), stat.as_mut_ptr()) };
|
let ret = unsafe { libc::fstat(f.as_raw_fd(), stat.as_mut_ptr()) };
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
return Err(std::io::Error::last_os_error());
|
return Err(std::io::Error::last_os_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: stat is valid at this point
|
||||||
let is_block = unsafe { (*stat.as_ptr()).st_mode & S_IFMT == S_IFBLK };
|
let is_block = unsafe { (*stat.as_ptr()).st_mode & S_IFMT == S_IFBLK };
|
||||||
Ok(is_block)
|
Ok(is_block)
|
||||||
}
|
}
|
||||||
|
|
||||||
// libc::ioctl() takes different types on different architectures
|
// libc::ioctl() takes different types on different architectures
|
||||||
#[allow(clippy::useless_conversion)]
|
|
||||||
fn query_block_size(f: &mut File, block_size_type: BlockSize) -> std::io::Result<u64> {
|
fn query_block_size(f: &mut File, block_size_type: BlockSize) -> std::io::Result<u64> {
|
||||||
let mut block_size = 0;
|
let mut block_size = 0;
|
||||||
|
// SAFETY: FFI call with correct arguments
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
ioctl(
|
ioctl(
|
||||||
f.as_raw_fd(),
|
f.as_raw_fd(),
|
||||||
@@ -75,9 +76,7 @@ impl DiskTopology {
|
|||||||
BlockSize::PhysicalBlock => BLKPBSZGET(),
|
BlockSize::PhysicalBlock => BLKPBSZGET(),
|
||||||
BlockSize::MinimumIo => BLKIOMIN(),
|
BlockSize::MinimumIo => BLKIOMIN(),
|
||||||
BlockSize::OptimalIo => BLKIOOPT(),
|
BlockSize::OptimalIo => BLKIOOPT(),
|
||||||
}
|
} as _,
|
||||||
.try_into()
|
|
||||||
.unwrap(),
|
|
||||||
&mut block_size,
|
&mut block_size,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@@ -132,15 +131,15 @@ pub trait AsyncIo: Send {
|
|||||||
fn read_vectored(
|
fn read_vectored(
|
||||||
&mut self,
|
&mut self,
|
||||||
offset: libc::off_t,
|
offset: libc::off_t,
|
||||||
iovecs: Vec<libc::iovec>,
|
iovecs: &[libc::iovec],
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
) -> AsyncIoResult<()>;
|
) -> AsyncIoResult<()>;
|
||||||
fn write_vectored(
|
fn write_vectored(
|
||||||
&mut self,
|
&mut self,
|
||||||
offset: libc::off_t,
|
offset: libc::off_t,
|
||||||
iovecs: Vec<libc::iovec>,
|
iovecs: &[libc::iovec],
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
) -> AsyncIoResult<()>;
|
) -> AsyncIoResult<()>;
|
||||||
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()>;
|
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()>;
|
||||||
fn complete(&mut self) -> Vec<(u64, i32)>;
|
fn next_completed_request(&mut self) -> Option<(u64, i32)>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ impl AsyncIo for FixedVhdAsync {
|
|||||||
fn read_vectored(
|
fn read_vectored(
|
||||||
&mut self,
|
&mut self,
|
||||||
offset: libc::off_t,
|
offset: libc::off_t,
|
||||||
iovecs: Vec<libc::iovec>,
|
iovecs: &[libc::iovec],
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
if offset as u64 >= self.size {
|
if offset as u64 >= self.size {
|
||||||
@@ -83,7 +83,7 @@ impl AsyncIo for FixedVhdAsync {
|
|||||||
fn write_vectored(
|
fn write_vectored(
|
||||||
&mut self,
|
&mut self,
|
||||||
offset: libc::off_t,
|
offset: libc::off_t,
|
||||||
iovecs: Vec<libc::iovec>,
|
iovecs: &[libc::iovec],
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
if offset as u64 >= self.size {
|
if offset as u64 >= self.size {
|
||||||
@@ -104,7 +104,7 @@ impl AsyncIo for FixedVhdAsync {
|
|||||||
self.raw_file_async.fsync(user_data)
|
self.raw_file_async.fsync(user_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complete(&mut self) -> Vec<(u64, i32)> {
|
fn next_completed_request(&mut self) -> Option<(u64, i32)> {
|
||||||
self.raw_file_async.complete()
|
self.raw_file_async.next_completed_request()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ impl AsyncIo for FixedVhdSync {
|
|||||||
fn read_vectored(
|
fn read_vectored(
|
||||||
&mut self,
|
&mut self,
|
||||||
offset: libc::off_t,
|
offset: libc::off_t,
|
||||||
iovecs: Vec<libc::iovec>,
|
iovecs: &[libc::iovec],
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
if offset as u64 >= self.size {
|
if offset as u64 >= self.size {
|
||||||
@@ -81,7 +81,7 @@ impl AsyncIo for FixedVhdSync {
|
|||||||
fn write_vectored(
|
fn write_vectored(
|
||||||
&mut self,
|
&mut self,
|
||||||
offset: libc::off_t,
|
offset: libc::off_t,
|
||||||
iovecs: Vec<libc::iovec>,
|
iovecs: &[libc::iovec],
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
if offset as u64 >= self.size {
|
if offset as u64 >= self.size {
|
||||||
@@ -101,7 +101,7 @@ impl AsyncIo for FixedVhdSync {
|
|||||||
self.raw_file_sync.fsync(user_data)
|
self.raw_file_sync.fsync(user_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complete(&mut self) -> Vec<(u64, i32)> {
|
fn next_completed_request(&mut self) -> Option<(u64, i32)> {
|
||||||
self.raw_file_sync.complete()
|
self.raw_file_sync.next_completed_request()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,10 @@ pub mod vhdx_sync;
|
|||||||
|
|
||||||
use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult};
|
use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult};
|
||||||
use io_uring::{opcode, IoUring, Probe};
|
use io_uring::{opcode, IoUring, Probe};
|
||||||
|
use smallvec::SmallVec;
|
||||||
use std::alloc::{alloc_zeroed, dealloc, Layout};
|
use std::alloc::{alloc_zeroed, dealloc, Layout};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
use std::collections::VecDeque;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
|
use std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
|
||||||
@@ -32,6 +34,8 @@ 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 std::time::Instant;
|
||||||
|
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 +52,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 +106,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),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,10 +198,11 @@ pub struct AlignedOperation {
|
|||||||
pub struct Request {
|
pub struct Request {
|
||||||
pub request_type: RequestType,
|
pub request_type: RequestType,
|
||||||
pub sector: u64,
|
pub sector: u64,
|
||||||
pub data_descriptors: Vec<(GuestAddress, u32)>,
|
pub data_descriptors: SmallVec<[(GuestAddress, u32); 1]>,
|
||||||
pub status_addr: GuestAddress,
|
pub status_addr: GuestAddress,
|
||||||
pub writeback: bool,
|
pub writeback: bool,
|
||||||
pub aligned_operations: Vec<AlignedOperation>,
|
pub aligned_operations: Vec<AlignedOperation>,
|
||||||
|
pub start: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
@@ -214,10 +230,11 @@ impl Request {
|
|||||||
let mut req = Request {
|
let mut req = Request {
|
||||||
request_type: request_type(desc_chain.memory(), hdr_desc_addr)?,
|
request_type: request_type(desc_chain.memory(), hdr_desc_addr)?,
|
||||||
sector: sector(desc_chain.memory(), hdr_desc_addr)?,
|
sector: sector(desc_chain.memory(), hdr_desc_addr)?,
|
||||||
data_descriptors: Vec::new(),
|
data_descriptors: SmallVec::with_capacity(1),
|
||||||
status_addr: GuestAddress(0),
|
status_addr: GuestAddress(0),
|
||||||
writeback: true,
|
writeback: true,
|
||||||
aligned_operations: Vec::new(),
|
aligned_operations: Vec::new(),
|
||||||
|
start: Instant::now(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let status_desc;
|
let status_desc;
|
||||||
@@ -237,6 +254,7 @@ impl Request {
|
|||||||
return Err(Error::DescriptorChainTooShort);
|
return Err(Error::DescriptorChainTooShort);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
req.data_descriptors.reserve_exact(1);
|
||||||
while desc.has_next() {
|
while desc.has_next() {
|
||||||
if desc.is_write_only() && req.request_type == RequestType::Out {
|
if desc.is_write_only() && req.request_type == RequestType::Out {
|
||||||
return Err(Error::UnexpectedWriteOnlyDescriptor);
|
return Err(Error::UnexpectedWriteOnlyDescriptor);
|
||||||
@@ -341,7 +359,8 @@ impl Request {
|
|||||||
let request_type = self.request_type;
|
let request_type = self.request_type;
|
||||||
let offset = (sector << SECTOR_SHIFT) as libc::off_t;
|
let offset = (sector << SECTOR_SHIFT) as libc::off_t;
|
||||||
|
|
||||||
let mut iovecs = Vec::new();
|
let mut iovecs: SmallVec<[libc::iovec; 1]> =
|
||||||
|
SmallVec::with_capacity(self.data_descriptors.len());
|
||||||
for (data_addr, data_len) in &self.data_descriptors {
|
for (data_addr, data_len) in &self.data_descriptors {
|
||||||
if *data_len == 0 {
|
if *data_len == 0 {
|
||||||
continue;
|
continue;
|
||||||
@@ -369,7 +388,7 @@ impl Request {
|
|||||||
let iov_base = if (origin_ptr as u64) % SECTOR_SIZE != 0 {
|
let iov_base = if (origin_ptr as u64) % SECTOR_SIZE != 0 {
|
||||||
let layout =
|
let layout =
|
||||||
Layout::from_size_align(*data_len as usize, SECTOR_SIZE as usize).unwrap();
|
Layout::from_size_align(*data_len as usize, SECTOR_SIZE as usize).unwrap();
|
||||||
// Safe because layout has non-zero size
|
// SAFETY: layout has non-zero size
|
||||||
let aligned_ptr = unsafe { alloc_zeroed(layout) };
|
let aligned_ptr = unsafe { alloc_zeroed(layout) };
|
||||||
if aligned_ptr.is_null() {
|
if aligned_ptr.is_null() {
|
||||||
return Err(ExecuteError::TemporaryBufferAllocation(
|
return Err(ExecuteError::TemporaryBufferAllocation(
|
||||||
@@ -380,7 +399,7 @@ impl Request {
|
|||||||
// We need to perform the copy beforehand in case we're writing
|
// We need to perform the copy beforehand in case we're writing
|
||||||
// data out.
|
// data out.
|
||||||
if request_type == RequestType::Out {
|
if request_type == RequestType::Out {
|
||||||
// Safe because destination buffer has been allocated with
|
// SAFETY: destination buffer has been allocated with
|
||||||
// the proper size.
|
// the proper size.
|
||||||
unsafe {
|
unsafe {
|
||||||
std::ptr::copy(origin_ptr as *const u8, aligned_ptr, *data_len as usize)
|
std::ptr::copy(origin_ptr as *const u8, aligned_ptr, *data_len as usize)
|
||||||
@@ -418,12 +437,12 @@ impl Request {
|
|||||||
.mark_dirty(0, *data_len as usize);
|
.mark_dirty(0, *data_len as usize);
|
||||||
}
|
}
|
||||||
disk_image
|
disk_image
|
||||||
.read_vectored(offset, iovecs, user_data)
|
.read_vectored(offset, &iovecs, user_data)
|
||||||
.map_err(ExecuteError::AsyncRead)?;
|
.map_err(ExecuteError::AsyncRead)?;
|
||||||
}
|
}
|
||||||
RequestType::Out => {
|
RequestType::Out => {
|
||||||
disk_image
|
disk_image
|
||||||
.write_vectored(offset, iovecs, user_data)
|
.write_vectored(offset, &iovecs, user_data)
|
||||||
.map_err(ExecuteError::AsyncWrite)?;
|
.map_err(ExecuteError::AsyncWrite)?;
|
||||||
}
|
}
|
||||||
RequestType::Flush => {
|
RequestType::Flush => {
|
||||||
@@ -455,7 +474,7 @@ impl Request {
|
|||||||
// We need to perform the copy after the data has been read inside
|
// We need to perform the copy after the data has been read inside
|
||||||
// the aligned buffer in case we're reading data in.
|
// the aligned buffer in case we're reading data in.
|
||||||
if self.request_type == RequestType::In {
|
if self.request_type == RequestType::In {
|
||||||
// Safe because origin buffer has been allocated with the
|
// SAFETY: origin buffer has been allocated with the
|
||||||
// proper size.
|
// proper size.
|
||||||
unsafe {
|
unsafe {
|
||||||
std::ptr::copy(
|
std::ptr::copy(
|
||||||
@@ -467,7 +486,7 @@ impl Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Free the temporary aligned buffer.
|
// Free the temporary aligned buffer.
|
||||||
// Safe because aligned_ptr was allocated by alloc_zeroed with the same
|
// SAFETY: aligned_ptr was allocated by alloc_zeroed with the same
|
||||||
// layout
|
// layout
|
||||||
unsafe {
|
unsafe {
|
||||||
dealloc(
|
dealloc(
|
||||||
@@ -516,8 +535,9 @@ pub struct VirtioBlockGeometry {
|
|||||||
pub sectors: u8,
|
pub sectors: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: these data structures only contain a series of integers
|
// SAFETY: data structure only contain a series of integers
|
||||||
unsafe impl ByteValued for VirtioBlockConfig {}
|
unsafe impl ByteValued for VirtioBlockConfig {}
|
||||||
|
// SAFETY: data structure only contain a series of integers
|
||||||
unsafe impl ByteValued for VirtioBlockGeometry {}
|
unsafe impl ByteValued for VirtioBlockGeometry {}
|
||||||
|
|
||||||
/// Check if io_uring for block device can be used on the current system, as
|
/// Check if io_uring for block device can be used on the current system, as
|
||||||
@@ -554,15 +574,15 @@ pub fn block_io_uring_is_supported() -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check IORING_OP_READ is supported
|
// Check IORING_OP_READV is supported
|
||||||
if !probe.is_supported(opcode::Read::CODE) {
|
if !probe.is_supported(opcode::Readv::CODE) {
|
||||||
info!("{} IORING_OP_READ operation not supported", error_msg);
|
info!("{} IORING_OP_READV operation not supported", error_msg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check IORING_OP_WRITE is supported
|
// Check IORING_OP_WRITEV is supported
|
||||||
if !probe.is_supported(opcode::Write::CODE) {
|
if !probe.is_supported(opcode::Writev::CODE) {
|
||||||
info!("{} IORING_OP_WRITE operation not supported", error_msg);
|
info!("{} IORING_OP_WRITEV operation not supported", error_msg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -576,14 +596,15 @@ where
|
|||||||
fn read_vectored_sync(
|
fn read_vectored_sync(
|
||||||
&mut self,
|
&mut self,
|
||||||
offset: libc::off_t,
|
offset: libc::off_t,
|
||||||
iovecs: Vec<libc::iovec>,
|
iovecs: &[libc::iovec],
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
eventfd: &EventFd,
|
eventfd: &EventFd,
|
||||||
completion_list: &mut Vec<(u64, i32)>,
|
completion_list: &mut VecDeque<(u64, i32)>,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
// Convert libc::iovec into IoSliceMut
|
// Convert libc::iovec into IoSliceMut
|
||||||
let mut slices = Vec::new();
|
let mut slices = Vec::new();
|
||||||
for iovec in iovecs.iter() {
|
for iovec in iovecs.iter() {
|
||||||
|
// SAFETY: on Linux IoSliceMut wraps around libc::iovec
|
||||||
slices.push(IoSliceMut::new(unsafe { std::mem::transmute(*iovec) }));
|
slices.push(IoSliceMut::new(unsafe { std::mem::transmute(*iovec) }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -599,7 +620,7 @@ where
|
|||||||
.map_err(AsyncIoError::ReadVectored)?
|
.map_err(AsyncIoError::ReadVectored)?
|
||||||
};
|
};
|
||||||
|
|
||||||
completion_list.push((user_data, result as i32));
|
completion_list.push_back((user_data, result as i32));
|
||||||
eventfd.write(1).unwrap();
|
eventfd.write(1).unwrap();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -608,14 +629,15 @@ where
|
|||||||
fn write_vectored_sync(
|
fn write_vectored_sync(
|
||||||
&mut self,
|
&mut self,
|
||||||
offset: libc::off_t,
|
offset: libc::off_t,
|
||||||
iovecs: Vec<libc::iovec>,
|
iovecs: &[libc::iovec],
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
eventfd: &EventFd,
|
eventfd: &EventFd,
|
||||||
completion_list: &mut Vec<(u64, i32)>,
|
completion_list: &mut VecDeque<(u64, i32)>,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
// Convert libc::iovec into IoSlice
|
// Convert libc::iovec into IoSlice
|
||||||
let mut slices = Vec::new();
|
let mut slices = Vec::new();
|
||||||
for iovec in iovecs.iter() {
|
for iovec in iovecs.iter() {
|
||||||
|
// SAFETY: on Linux IoSliceMut wraps around libc::iovec
|
||||||
slices.push(IoSlice::new(unsafe { std::mem::transmute(*iovec) }));
|
slices.push(IoSlice::new(unsafe { std::mem::transmute(*iovec) }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -631,7 +653,7 @@ where
|
|||||||
.map_err(AsyncIoError::WriteVectored)?
|
.map_err(AsyncIoError::WriteVectored)?
|
||||||
};
|
};
|
||||||
|
|
||||||
completion_list.push((user_data, result as i32));
|
completion_list.push_back((user_data, result as i32));
|
||||||
eventfd.write(1).unwrap();
|
eventfd.write(1).unwrap();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -641,7 +663,7 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
user_data: Option<u64>,
|
user_data: Option<u64>,
|
||||||
eventfd: &EventFd,
|
eventfd: &EventFd,
|
||||||
completion_list: &mut Vec<(u64, i32)>,
|
completion_list: &mut VecDeque<(u64, i32)>,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
let result: i32 = {
|
let result: i32 = {
|
||||||
let mut file = self.file();
|
let mut file = self.file();
|
||||||
@@ -653,7 +675,7 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(user_data) = user_data {
|
if let Some(user_data) = user_data {
|
||||||
completion_list.push((user_data, result));
|
completion_list.push_back((user_data, result));
|
||||||
eventfd.write(1).unwrap();
|
eventfd.write(1).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult};
|
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult};
|
||||||
use crate::AsyncAdaptor;
|
use crate::AsyncAdaptor;
|
||||||
use qcow::{QcowFile, RawFile, Result as QcowResult};
|
use qcow::{QcowFile, RawFile, Result as QcowResult};
|
||||||
|
use std::collections::VecDeque;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Seek, SeekFrom};
|
use std::io::{Seek, SeekFrom};
|
||||||
use std::sync::{Arc, Mutex, MutexGuard};
|
use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
@@ -26,7 +27,7 @@ impl DiskFile for QcowDiskSync {
|
|||||||
fn size(&mut self) -> DiskFileResult<u64> {
|
fn size(&mut self) -> DiskFileResult<u64> {
|
||||||
let mut file = self.qcow_file.lock().unwrap();
|
let mut file = self.qcow_file.lock().unwrap();
|
||||||
|
|
||||||
Ok(file.seek(SeekFrom::End(0)).map_err(DiskFileError::Size)? as u64)
|
file.seek(SeekFrom::End(0)).map_err(DiskFileError::Size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||||
@@ -37,7 +38,7 @@ impl DiskFile for QcowDiskSync {
|
|||||||
pub struct QcowSync {
|
pub struct QcowSync {
|
||||||
qcow_file: Arc<Mutex<QcowFile>>,
|
qcow_file: Arc<Mutex<QcowFile>>,
|
||||||
eventfd: EventFd,
|
eventfd: EventFd,
|
||||||
completion_list: Vec<(u64, i32)>,
|
completion_list: VecDeque<(u64, i32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl QcowSync {
|
impl QcowSync {
|
||||||
@@ -46,7 +47,7 @@ impl QcowSync {
|
|||||||
qcow_file,
|
qcow_file,
|
||||||
eventfd: EventFd::new(libc::EFD_NONBLOCK)
|
eventfd: EventFd::new(libc::EFD_NONBLOCK)
|
||||||
.expect("Failed creating EventFd for QcowSync"),
|
.expect("Failed creating EventFd for QcowSync"),
|
||||||
completion_list: Vec::new(),
|
completion_list: VecDeque::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,7 +66,7 @@ impl AsyncIo for QcowSync {
|
|||||||
fn read_vectored(
|
fn read_vectored(
|
||||||
&mut self,
|
&mut self,
|
||||||
offset: libc::off_t,
|
offset: libc::off_t,
|
||||||
iovecs: Vec<libc::iovec>,
|
iovecs: &[libc::iovec],
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
self.qcow_file.read_vectored_sync(
|
self.qcow_file.read_vectored_sync(
|
||||||
@@ -80,7 +81,7 @@ impl AsyncIo for QcowSync {
|
|||||||
fn write_vectored(
|
fn write_vectored(
|
||||||
&mut self,
|
&mut self,
|
||||||
offset: libc::off_t,
|
offset: libc::off_t,
|
||||||
iovecs: Vec<libc::iovec>,
|
iovecs: &[libc::iovec],
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
self.qcow_file.write_vectored_sync(
|
self.qcow_file.write_vectored_sync(
|
||||||
@@ -97,7 +98,7 @@ impl AsyncIo for QcowSync {
|
|||||||
.fsync_sync(user_data, &self.eventfd, &mut self.completion_list)
|
.fsync_sync(user_data, &self.eventfd, &mut self.completion_list)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complete(&mut self) -> Vec<(u64, i32)> {
|
fn next_completed_request(&mut self) -> Option<(u64, i32)> {
|
||||||
self.completion_list.drain(..).collect()
|
self.completion_list.pop_front()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,10 +23,9 @@ impl RawFileDisk {
|
|||||||
|
|
||||||
impl DiskFile for RawFileDisk {
|
impl DiskFile for RawFileDisk {
|
||||||
fn size(&mut self) -> DiskFileResult<u64> {
|
fn size(&mut self) -> DiskFileResult<u64> {
|
||||||
Ok(self
|
self.file
|
||||||
.file
|
|
||||||
.seek(SeekFrom::End(0))
|
.seek(SeekFrom::End(0))
|
||||||
.map_err(DiskFileError::Size)? as u64)
|
.map_err(DiskFileError::Size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||||
@@ -77,12 +76,12 @@ impl AsyncIo for RawFileAsync {
|
|||||||
fn read_vectored(
|
fn read_vectored(
|
||||||
&mut self,
|
&mut self,
|
||||||
offset: libc::off_t,
|
offset: libc::off_t,
|
||||||
iovecs: Vec<libc::iovec>,
|
iovecs: &[libc::iovec],
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
let (submitter, mut sq, _) = self.io_uring.split();
|
let (submitter, mut sq, _) = self.io_uring.split();
|
||||||
|
|
||||||
// Safe because we know the file descriptor is valid and we
|
// SAFETY: we know the file descriptor is valid and we
|
||||||
// relied on vm-memory to provide the buffer address.
|
// relied on vm-memory to provide the buffer address.
|
||||||
let _ = unsafe {
|
let _ = unsafe {
|
||||||
sq.push(
|
sq.push(
|
||||||
@@ -105,12 +104,12 @@ impl AsyncIo for RawFileAsync {
|
|||||||
fn write_vectored(
|
fn write_vectored(
|
||||||
&mut self,
|
&mut self,
|
||||||
offset: libc::off_t,
|
offset: libc::off_t,
|
||||||
iovecs: Vec<libc::iovec>,
|
iovecs: &[libc::iovec],
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
let (submitter, mut sq, _) = self.io_uring.split();
|
let (submitter, mut sq, _) = self.io_uring.split();
|
||||||
|
|
||||||
// Safe because we know the file descriptor is valid and we
|
// SAFETY: we know the file descriptor is valid and we
|
||||||
// relied on vm-memory to provide the buffer address.
|
// relied on vm-memory to provide the buffer address.
|
||||||
let _ = unsafe {
|
let _ = unsafe {
|
||||||
sq.push(
|
sq.push(
|
||||||
@@ -134,7 +133,7 @@ impl AsyncIo for RawFileAsync {
|
|||||||
if let Some(user_data) = user_data {
|
if let Some(user_data) = user_data {
|
||||||
let (submitter, mut sq, _) = self.io_uring.split();
|
let (submitter, mut sq, _) = self.io_uring.split();
|
||||||
|
|
||||||
// Safe because we know the file descriptor is valid.
|
// SAFETY: we know the file descriptor is valid.
|
||||||
let _ = unsafe {
|
let _ = unsafe {
|
||||||
sq.push(
|
sq.push(
|
||||||
&opcode::Fsync::new(types::Fd(self.fd))
|
&opcode::Fsync::new(types::Fd(self.fd))
|
||||||
@@ -149,20 +148,17 @@ impl AsyncIo for RawFileAsync {
|
|||||||
sq.sync();
|
sq.sync();
|
||||||
submitter.submit().map_err(AsyncIoError::Fsync)?;
|
submitter.submit().map_err(AsyncIoError::Fsync)?;
|
||||||
} else {
|
} else {
|
||||||
|
// SAFETY: FFI call with a valid fd
|
||||||
unsafe { libc::fsync(self.fd) };
|
unsafe { libc::fsync(self.fd) };
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complete(&mut self) -> Vec<(u64, i32)> {
|
fn next_completed_request(&mut self) -> Option<(u64, i32)> {
|
||||||
let mut completion_list = Vec::new();
|
self.io_uring
|
||||||
|
.completion()
|
||||||
let cq = self.io_uring.completion();
|
.next()
|
||||||
for cq_entry in cq {
|
.map(|entry| (entry.user_data(), entry.result()))
|
||||||
completion_list.push((cq_entry.user_data(), cq_entry.result()));
|
|
||||||
}
|
|
||||||
|
|
||||||
completion_list
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
use crate::async_io::{
|
use crate::async_io::{
|
||||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult, DiskTopology,
|
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult, DiskTopology,
|
||||||
};
|
};
|
||||||
|
use std::collections::VecDeque;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Seek, SeekFrom};
|
use std::io::{Seek, SeekFrom};
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
@@ -22,10 +23,9 @@ impl RawFileDiskSync {
|
|||||||
|
|
||||||
impl DiskFile for RawFileDiskSync {
|
impl DiskFile for RawFileDiskSync {
|
||||||
fn size(&mut self) -> DiskFileResult<u64> {
|
fn size(&mut self) -> DiskFileResult<u64> {
|
||||||
Ok(self
|
self.file
|
||||||
.file
|
|
||||||
.seek(SeekFrom::End(0))
|
.seek(SeekFrom::End(0))
|
||||||
.map_err(DiskFileError::Size)? as u64)
|
.map_err(DiskFileError::Size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||||
@@ -45,7 +45,7 @@ impl DiskFile for RawFileDiskSync {
|
|||||||
pub struct RawFileSync {
|
pub struct RawFileSync {
|
||||||
fd: RawFd,
|
fd: RawFd,
|
||||||
eventfd: EventFd,
|
eventfd: EventFd,
|
||||||
completion_list: Vec<(u64, i32)>,
|
completion_list: VecDeque<(u64, i32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RawFileSync {
|
impl RawFileSync {
|
||||||
@@ -53,7 +53,7 @@ impl RawFileSync {
|
|||||||
RawFileSync {
|
RawFileSync {
|
||||||
fd,
|
fd,
|
||||||
eventfd: EventFd::new(libc::EFD_NONBLOCK).expect("Failed creating EventFd for RawFile"),
|
eventfd: EventFd::new(libc::EFD_NONBLOCK).expect("Failed creating EventFd for RawFile"),
|
||||||
completion_list: Vec::new(),
|
completion_list: VecDeque::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,9 +66,10 @@ impl AsyncIo for RawFileSync {
|
|||||||
fn read_vectored(
|
fn read_vectored(
|
||||||
&mut self,
|
&mut self,
|
||||||
offset: libc::off_t,
|
offset: libc::off_t,
|
||||||
iovecs: Vec<libc::iovec>,
|
iovecs: &[libc::iovec],
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
|
// SAFETY: FFI call with valid arguments
|
||||||
let result = unsafe {
|
let result = unsafe {
|
||||||
libc::preadv(
|
libc::preadv(
|
||||||
self.fd as libc::c_int,
|
self.fd as libc::c_int,
|
||||||
@@ -81,7 +82,7 @@ impl AsyncIo for RawFileSync {
|
|||||||
return Err(AsyncIoError::ReadVectored(std::io::Error::last_os_error()));
|
return Err(AsyncIoError::ReadVectored(std::io::Error::last_os_error()));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.completion_list.push((user_data, result as i32));
|
self.completion_list.push_back((user_data, result as i32));
|
||||||
self.eventfd.write(1).unwrap();
|
self.eventfd.write(1).unwrap();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -90,9 +91,10 @@ impl AsyncIo for RawFileSync {
|
|||||||
fn write_vectored(
|
fn write_vectored(
|
||||||
&mut self,
|
&mut self,
|
||||||
offset: libc::off_t,
|
offset: libc::off_t,
|
||||||
iovecs: Vec<libc::iovec>,
|
iovecs: &[libc::iovec],
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
|
// SAFETY: FFI call with valid arguments
|
||||||
let result = unsafe {
|
let result = unsafe {
|
||||||
libc::pwritev(
|
libc::pwritev(
|
||||||
self.fd as libc::c_int,
|
self.fd as libc::c_int,
|
||||||
@@ -105,27 +107,28 @@ impl AsyncIo for RawFileSync {
|
|||||||
return Err(AsyncIoError::WriteVectored(std::io::Error::last_os_error()));
|
return Err(AsyncIoError::WriteVectored(std::io::Error::last_os_error()));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.completion_list.push((user_data, result as i32));
|
self.completion_list.push_back((user_data, result as i32));
|
||||||
self.eventfd.write(1).unwrap();
|
self.eventfd.write(1).unwrap();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
|
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
|
||||||
|
// SAFETY: FFI call
|
||||||
let result = unsafe { libc::fsync(self.fd as libc::c_int) };
|
let result = unsafe { libc::fsync(self.fd as libc::c_int) };
|
||||||
if result < 0 {
|
if result < 0 {
|
||||||
return Err(AsyncIoError::Fsync(std::io::Error::last_os_error()));
|
return Err(AsyncIoError::Fsync(std::io::Error::last_os_error()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(user_data) = user_data {
|
if let Some(user_data) = user_data {
|
||||||
self.completion_list.push((user_data, result as i32));
|
self.completion_list.push_back((user_data, result));
|
||||||
self.eventfd.write(1).unwrap();
|
self.eventfd.write(1).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complete(&mut self) -> Vec<(u64, i32)> {
|
fn next_completed_request(&mut self) -> Option<(u64, i32)> {
|
||||||
self.completion_list.drain(..).collect()
|
self.completion_list.pop_front()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult};
|
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult};
|
||||||
use crate::AsyncAdaptor;
|
use crate::AsyncAdaptor;
|
||||||
|
use std::collections::VecDeque;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::sync::{Arc, Mutex, MutexGuard};
|
use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
use vhdx::vhdx::{Result as VhdxResult, Vhdx};
|
use vhdx::vhdx::{Result as VhdxResult, Vhdx};
|
||||||
@@ -37,7 +38,7 @@ impl DiskFile for VhdxDiskSync {
|
|||||||
pub struct VhdxSync {
|
pub struct VhdxSync {
|
||||||
vhdx_file: Arc<Mutex<Vhdx>>,
|
vhdx_file: Arc<Mutex<Vhdx>>,
|
||||||
eventfd: EventFd,
|
eventfd: EventFd,
|
||||||
completion_list: Vec<(u64, i32)>,
|
completion_list: VecDeque<(u64, i32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VhdxSync {
|
impl VhdxSync {
|
||||||
@@ -45,7 +46,7 @@ impl VhdxSync {
|
|||||||
Ok(VhdxSync {
|
Ok(VhdxSync {
|
||||||
vhdx_file,
|
vhdx_file,
|
||||||
eventfd: EventFd::new(libc::EFD_NONBLOCK)?,
|
eventfd: EventFd::new(libc::EFD_NONBLOCK)?,
|
||||||
completion_list: Vec::new(),
|
completion_list: VecDeque::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,7 +65,7 @@ impl AsyncIo for VhdxSync {
|
|||||||
fn read_vectored(
|
fn read_vectored(
|
||||||
&mut self,
|
&mut self,
|
||||||
offset: libc::off_t,
|
offset: libc::off_t,
|
||||||
iovecs: Vec<libc::iovec>,
|
iovecs: &[libc::iovec],
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
self.vhdx_file.read_vectored_sync(
|
self.vhdx_file.read_vectored_sync(
|
||||||
@@ -79,7 +80,7 @@ impl AsyncIo for VhdxSync {
|
|||||||
fn write_vectored(
|
fn write_vectored(
|
||||||
&mut self,
|
&mut self,
|
||||||
offset: libc::off_t,
|
offset: libc::off_t,
|
||||||
iovecs: Vec<libc::iovec>,
|
iovecs: &[libc::iovec],
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
self.vhdx_file.write_vectored_sync(
|
self.vhdx_file.write_vectored_sync(
|
||||||
@@ -96,7 +97,7 @@ impl AsyncIo for VhdxSync {
|
|||||||
.fsync_sync(user_data, &self.eventfd, &mut self.completion_list)
|
.fsync_sync(user_data, &self.eventfd, &mut self.completion_list)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complete(&mut self) -> Vec<(u64, i32)> {
|
fn next_completed_request(&mut self) -> Option<(u64, i32)> {
|
||||||
self.completion_list.drain(..).collect()
|
self.completion_list.pop_front()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
build.rs
9
build.rs
@@ -3,15 +3,12 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
#[macro_use(crate_version)]
|
|
||||||
extern crate clap;
|
|
||||||
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut version = "v".to_owned() + crate_version!();
|
let mut version = "v".to_owned() + env!("CARGO_PKG_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;
|
||||||
@@ -23,5 +20,5 @@ fn main() {
|
|||||||
// variable BUILT_VERSION, so that it can be reused from the binary.
|
// variable BUILT_VERSION, so that it can be reused from the binary.
|
||||||
// Particularly, this is used from src/main.rs to display the exact
|
// Particularly, this is used from src/main.rs to display the exact
|
||||||
// version.
|
// version.
|
||||||
println!("cargo:rustc-env=BUILT_VERSION={}", version);
|
println!("cargo:rustc-env=BUILT_VERSION={version}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,22 +6,25 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
acpi_tables = { path = "../acpi_tables" }
|
acpi_tables = { path = "../acpi_tables" }
|
||||||
anyhow = "1.0.58"
|
anyhow = "1.0.68"
|
||||||
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" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
libc = "0.2.126"
|
libc = "0.2.139"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
versionize = "0.1.6"
|
phf = { version = "0.11.1", features = ["macros"] }
|
||||||
|
thiserror = "1.0.38"
|
||||||
|
tpm = { path = "../tpm" }
|
||||||
|
versionize = "0.1.9"
|
||||||
versionize_derive = "0.1.4"
|
versionize_derive = "0.1.4"
|
||||||
vm-device = { path = "../vm-device" }
|
vm-device = { path = "../vm-device" }
|
||||||
vm-memory = "0.8.0"
|
vm-memory = "0.10.0"
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
vmm-sys-util = "0.9.0"
|
vmm-sys-util = "0.11.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,
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,17 @@ use super::interrupt_controller::{Error, InterruptController};
|
|||||||
extern crate arch;
|
extern crate arch;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use arch::layout;
|
use arch::layout;
|
||||||
use hypervisor::{arch::aarch64::gic::Vgic, CpuState};
|
use hypervisor::{
|
||||||
|
arch::aarch64::gic::{Vgic, VgicConfig},
|
||||||
|
CpuState, GicState,
|
||||||
|
};
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use vm_device::interrupt::{
|
use vm_device::interrupt::{
|
||||||
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
|
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
|
||||||
LegacyIrqSourceConfig, MsiIrqGroupConfig,
|
LegacyIrqSourceConfig, MsiIrqGroupConfig,
|
||||||
};
|
};
|
||||||
use vm_memory::Address;
|
use vm_memory::address::Address;
|
||||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
@@ -22,6 +25,7 @@ type Result<T> = result::Result<T, Error>;
|
|||||||
// Reserve 32 IRQs for legacy devices.
|
// Reserve 32 IRQs for legacy devices.
|
||||||
pub const IRQ_LEGACY_BASE: usize = 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;
|
||||||
|
pub const GIC_SNAPSHOT_ID: &str = "gic-v3-its";
|
||||||
|
|
||||||
// Gic (Generic Interupt Controller) struct provides all the functionality of a
|
// Gic (Generic Interupt Controller) struct provides all the functionality of a
|
||||||
// GIC device. It wraps a hypervisor-emulated GIC device (Vgic) provided by the
|
// GIC device. It wraps a hypervisor-emulated GIC device (Vgic) provided by the
|
||||||
@@ -36,8 +40,9 @@ pub struct Gic {
|
|||||||
|
|
||||||
impl Gic {
|
impl Gic {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
_vcpu_count: u8,
|
vcpu_count: u8,
|
||||||
interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||||
|
vm: Arc<dyn hypervisor::Vm>,
|
||||||
) -> Result<Gic> {
|
) -> Result<Gic> {
|
||||||
let interrupt_source_group = interrupt_manager
|
let interrupt_source_group = interrupt_manager
|
||||||
.create_group(MsiIrqGroupConfig {
|
.create_group(MsiIrqGroupConfig {
|
||||||
@@ -46,38 +51,34 @@ impl Gic {
|
|||||||
})
|
})
|
||||||
.map_err(Error::CreateInterruptSourceGroup)?;
|
.map_err(Error::CreateInterruptSourceGroup)?;
|
||||||
|
|
||||||
Ok(Gic {
|
|
||||||
interrupt_source_group,
|
|
||||||
vgic: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_vgic(
|
|
||||||
&mut self,
|
|
||||||
vm: &Arc<dyn hypervisor::Vm>,
|
|
||||||
vcpu_count: u64,
|
|
||||||
) -> Result<Arc<Mutex<dyn Vgic>>> {
|
|
||||||
let vgic = vm
|
let vgic = vm
|
||||||
.create_vgic(
|
.create_vgic(Gic::create_default_config(vcpu_count as u64))
|
||||||
vcpu_count,
|
|
||||||
layout::GIC_V3_DIST_START.raw_value(),
|
|
||||||
layout::GIC_V3_DIST_SIZE,
|
|
||||||
layout::GIC_V3_REDIST_SIZE,
|
|
||||||
layout::GIC_V3_ITS_SIZE,
|
|
||||||
layout::IRQ_NUM,
|
|
||||||
)
|
|
||||||
.map_err(Error::CreateGic)?;
|
.map_err(Error::CreateGic)?;
|
||||||
self.vgic = Some(vgic.clone());
|
|
||||||
Ok(vgic.clone())
|
let gic = Gic {
|
||||||
|
interrupt_source_group,
|
||||||
|
vgic: Some(vgic),
|
||||||
|
};
|
||||||
|
gic.enable()?;
|
||||||
|
|
||||||
|
Ok(gic)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
|
pub fn restore_vgic(
|
||||||
let vgic = self.vgic.as_ref().unwrap().clone();
|
&mut self,
|
||||||
vgic.lock().unwrap().set_gicr_typers(vcpu_states);
|
state: Option<GicState>,
|
||||||
|
saved_vcpu_states: &[CpuState],
|
||||||
|
) -> Result<()> {
|
||||||
|
self.set_gicr_typers(saved_vcpu_states);
|
||||||
|
self.vgic
|
||||||
|
.clone()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.set_state(&state.unwrap())
|
||||||
|
.map_err(Error::RestoreGic)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl InterruptController for Gic {
|
|
||||||
fn enable(&self) -> Result<()> {
|
fn enable(&self) -> Result<()> {
|
||||||
// Set irqfd for legacy interrupts
|
// Set irqfd for legacy interrupts
|
||||||
self.interrupt_source_group
|
self.interrupt_source_group
|
||||||
@@ -103,6 +104,33 @@ impl InterruptController for Gic {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Default config implied by arch::layout
|
||||||
|
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_vgic(&mut self) -> Result<Arc<Mutex<dyn Vgic>>> {
|
||||||
|
Ok(self.vgic.clone().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterruptController for Gic {
|
||||||
// This should be called anytime an interrupt needs to be injected into the
|
// This should be called anytime an interrupt needs to be injected into the
|
||||||
// running guest.
|
// running guest.
|
||||||
fn service_irq(&mut self, irq: usize) -> Result<()> {
|
fn service_irq(&mut self, irq: usize) -> Result<()> {
|
||||||
@@ -118,27 +146,15 @@ impl InterruptController for Gic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const GIC_V3_ITS_SNAPSHOT_ID: &str = "gic-v3-its";
|
|
||||||
impl Snapshottable for Gic {
|
impl Snapshottable for Gic {
|
||||||
fn id(&self) -> String {
|
fn id(&self) -> String {
|
||||||
GIC_V3_ITS_SNAPSHOT_ID.to_string()
|
GIC_SNAPSHOT_ID.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||||
let vgic = self.vgic.as_ref().unwrap().clone();
|
let vgic = self.vgic.as_ref().unwrap().clone();
|
||||||
let state = vgic.lock().unwrap().state().unwrap();
|
let state = vgic.lock().unwrap().state().unwrap();
|
||||||
Snapshot::new_from_state(&self.id(), &state)
|
Snapshot::new_from_state(&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(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,12 @@ pub enum Error {
|
|||||||
UpdateInterrupt(io::Error),
|
UpdateInterrupt(io::Error),
|
||||||
/// Failed enabling the interrupt.
|
/// Failed enabling the interrupt.
|
||||||
EnableInterrupt(io::Error),
|
EnableInterrupt(io::Error),
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
/// Failed creating GIC device.
|
/// Failed creating GIC device.
|
||||||
CreateGic(hypervisor::HypervisorVmError),
|
CreateGic(hypervisor::HypervisorVmError),
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
/// Failed restoring GIC device.
|
||||||
|
RestoreGic(hypervisor::arch::aarch64::gic::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
@@ -55,8 +59,6 @@ pub struct MsiMessage {
|
|||||||
// IOAPIC (X86) or GIC (Arm).
|
// IOAPIC (X86) or GIC (Arm).
|
||||||
pub trait InterruptController: Send {
|
pub trait InterruptController: Send {
|
||||||
fn service_irq(&mut self, irq: usize) -> Result<()>;
|
fn service_irq(&mut self, irq: usize) -> Result<()>;
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
fn enable(&self) -> Result<()>;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn end_of_interrupt(&mut self, vec: u8);
|
fn end_of_interrupt(&mut self, vec: u8);
|
||||||
fn notifier(&self, irq: usize) -> Option<EventFd>;
|
fn notifier(&self, irq: usize) -> Option<EventFd>;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
// See https://pdos.csail.mit.edu/6.828/2016/readings/ia32/ioapic.pdf for a specification.
|
// See https://pdos.csail.mit.edu/6.828/2016/readings/ia32/ioapic.pdf for a specification.
|
||||||
|
|
||||||
use super::interrupt_controller::{Error, InterruptController};
|
use super::interrupt_controller::{Error, InterruptController};
|
||||||
use anyhow::anyhow;
|
|
||||||
use byteorder::{ByteOrder, LittleEndian};
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
@@ -194,6 +193,7 @@ impl Ioapic {
|
|||||||
id: String,
|
id: String,
|
||||||
apic_address: GuestAddress,
|
apic_address: GuestAddress,
|
||||||
interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||||
|
state: Option<IoapicState>,
|
||||||
) -> Result<Ioapic> {
|
) -> Result<Ioapic> {
|
||||||
let interrupt_source_group = interrupt_manager
|
let interrupt_source_group = interrupt_manager
|
||||||
.create_group(MsiIrqGroupConfig {
|
.create_group(MsiIrqGroupConfig {
|
||||||
@@ -202,17 +202,47 @@ impl Ioapic {
|
|||||||
})
|
})
|
||||||
.map_err(Error::CreateInterruptSourceGroup)?;
|
.map_err(Error::CreateInterruptSourceGroup)?;
|
||||||
|
|
||||||
|
let (id_reg, reg_sel, reg_entries, used_entries, apic_address) = if let Some(state) = &state
|
||||||
|
{
|
||||||
|
(
|
||||||
|
state.id_reg,
|
||||||
|
state.reg_sel,
|
||||||
|
state.reg_entries,
|
||||||
|
state.used_entries,
|
||||||
|
GuestAddress(state.apic_address),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
[0x10000; NUM_IOAPIC_PINS],
|
||||||
|
[false; NUM_IOAPIC_PINS],
|
||||||
|
apic_address,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
// The IOAPIC is created with entries already masked. The guest will be
|
// The IOAPIC is created with entries already masked. The guest will be
|
||||||
// in charge of unmasking them if/when necessary.
|
// in charge of unmasking them if/when necessary.
|
||||||
Ok(Ioapic {
|
let ioapic = Ioapic {
|
||||||
id,
|
id,
|
||||||
id_reg: 0,
|
id_reg,
|
||||||
reg_sel: 0,
|
reg_sel,
|
||||||
reg_entries: [0x10000; NUM_IOAPIC_PINS],
|
reg_entries,
|
||||||
used_entries: [false; NUM_IOAPIC_PINS],
|
used_entries,
|
||||||
apic_address,
|
apic_address,
|
||||||
interrupt_source_group,
|
interrupt_source_group,
|
||||||
})
|
};
|
||||||
|
|
||||||
|
// When restoring the Ioapic, we must enable used entries.
|
||||||
|
if state.is_some() {
|
||||||
|
for (irq, entry) in ioapic.used_entries.iter().enumerate() {
|
||||||
|
if *entry {
|
||||||
|
ioapic.update_entry(irq)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ioapic)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ioapic_write(&mut self, val: u32) {
|
fn ioapic_write(&mut self, val: u32) {
|
||||||
@@ -299,21 +329,6 @@ impl Ioapic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_state(&mut self, state: &IoapicState) -> Result<()> {
|
|
||||||
self.id_reg = state.id_reg;
|
|
||||||
self.reg_sel = state.reg_sel;
|
|
||||||
self.reg_entries = state.reg_entries;
|
|
||||||
self.used_entries = state.used_entries;
|
|
||||||
self.apic_address = GuestAddress(state.apic_address);
|
|
||||||
for (irq, entry) in self.used_entries.iter().enumerate() {
|
|
||||||
if *entry {
|
|
||||||
self.update_entry(irq)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_entry(&self, irq: usize) -> Result<()> {
|
fn update_entry(&self, irq: usize) -> Result<()> {
|
||||||
let entry = self.reg_entries[irq];
|
let entry = self.reg_entries[irq];
|
||||||
|
|
||||||
@@ -423,18 +438,7 @@ impl Snapshottable for Ioapic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||||
Snapshot::new_from_versioned_state(&self.id, &self.state())
|
Snapshot::new_from_versioned_state(&self.state())
|
||||||
}
|
|
||||||
|
|
||||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
|
||||||
self.set_state(&snapshot.to_versioned_state(&self.id)?)
|
|
||||||
.map_err(|e| {
|
|
||||||
MigratableError::Restore(anyhow!(
|
|
||||||
"Could not restore state for {}: {:?}",
|
|
||||||
self.id,
|
|
||||||
e
|
|
||||||
))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ impl BusDevice for Cmos {
|
|||||||
let day;
|
let day;
|
||||||
let month;
|
let month;
|
||||||
let year;
|
let year;
|
||||||
// The clock_gettime and gmtime_r calls are safe as long as the structs they are
|
// SAFETY: The clock_gettime and gmtime_r calls are safe as long as the structs they are
|
||||||
// given are large enough, and neither of them fail. It is safe to zero initialize
|
// given are large enough, and neither of them fail. It is safe to zero initialize
|
||||||
// the tm and timespec struct because it contains only plain data.
|
// the tm and timespec struct because it contains only plain data.
|
||||||
let update_in_progress = unsafe {
|
let update_in_progress = unsafe {
|
||||||
|
|||||||
@@ -40,11 +40,11 @@ impl DebugIoPortRange {
|
|||||||
impl fmt::Display for DebugIoPortRange {
|
impl fmt::Display for DebugIoPortRange {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
DebugIoPortRange::Firmware => write!(f, "{}: Firmware", DEBUG_IOPORT_PREFIX),
|
DebugIoPortRange::Firmware => write!(f, "{DEBUG_IOPORT_PREFIX}: Firmware"),
|
||||||
DebugIoPortRange::Bootloader => write!(f, "{}: Bootloader", DEBUG_IOPORT_PREFIX),
|
DebugIoPortRange::Bootloader => write!(f, "{DEBUG_IOPORT_PREFIX}: Bootloader"),
|
||||||
DebugIoPortRange::Kernel => write!(f, "{}: Kernel", DEBUG_IOPORT_PREFIX),
|
DebugIoPortRange::Kernel => write!(f, "{DEBUG_IOPORT_PREFIX}: Kernel"),
|
||||||
DebugIoPortRange::Userspace => write!(f, "{}: Userspace", DEBUG_IOPORT_PREFIX),
|
DebugIoPortRange::Userspace => write!(f, "{DEBUG_IOPORT_PREFIX}: Userspace"),
|
||||||
DebugIoPortRange::Custom => write!(f, "{}: Custom", DEBUG_IOPORT_PREFIX),
|
DebugIoPortRange::Custom => write!(f, "{DEBUG_IOPORT_PREFIX}: Custom"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,13 +51,13 @@ pub enum Error {
|
|||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Error::BadWriteOffset(offset) => write!(f, "Bad Write Offset: {}", offset),
|
Error::BadWriteOffset(offset) => write!(f, "Bad Write Offset: {offset}"),
|
||||||
Error::GpioInterruptDisabled => write!(f, "GPIO interrupt disabled by guest driver.",),
|
Error::GpioInterruptDisabled => write!(f, "GPIO interrupt disabled by guest driver.",),
|
||||||
Error::GpioInterruptFailure(ref e) => {
|
Error::GpioInterruptFailure(ref e) => {
|
||||||
write!(f, "Could not trigger GPIO interrupt: {}.", e)
|
write!(f, "Could not trigger GPIO interrupt: {e}.")
|
||||||
}
|
}
|
||||||
Error::GpioTriggerKeyFailure(key) => {
|
Error::GpioTriggerKeyFailure(key) => {
|
||||||
write!(f, "Invalid GPIO Input key triggerd: {}.", key)
|
write!(f, "Invalid GPIO Input key triggerd: {key}.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,18 +106,39 @@ impl VersionMapped for GpioState {}
|
|||||||
|
|
||||||
impl Gpio {
|
impl Gpio {
|
||||||
/// Constructs an PL061 GPIO device.
|
/// Constructs an PL061 GPIO device.
|
||||||
pub fn new(id: String, interrupt: Arc<dyn InterruptSourceGroup>) -> Self {
|
pub fn new(
|
||||||
|
id: String,
|
||||||
|
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||||
|
state: Option<GpioState>,
|
||||||
|
) -> Self {
|
||||||
|
let (data, old_in_data, dir, isense, ibe, iev, im, istate, afsel) =
|
||||||
|
if let Some(state) = state {
|
||||||
|
(
|
||||||
|
state.data,
|
||||||
|
state.old_in_data,
|
||||||
|
state.dir,
|
||||||
|
state.isense,
|
||||||
|
state.ibe,
|
||||||
|
state.iev,
|
||||||
|
state.im,
|
||||||
|
state.istate,
|
||||||
|
state.afsel,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
data: 0,
|
data,
|
||||||
old_in_data: 0,
|
old_in_data,
|
||||||
dir: 0,
|
dir,
|
||||||
isense: 0,
|
isense,
|
||||||
ibe: 0,
|
ibe,
|
||||||
iev: 0,
|
iev,
|
||||||
im: 0,
|
im,
|
||||||
istate: 0,
|
istate,
|
||||||
afsel: 0,
|
afsel,
|
||||||
interrupt,
|
interrupt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,24 +157,12 @@ impl Gpio {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_state(&mut self, state: &GpioState) {
|
|
||||||
self.data = state.data;
|
|
||||||
self.old_in_data = state.old_in_data;
|
|
||||||
self.dir = state.dir;
|
|
||||||
self.isense = state.isense;
|
|
||||||
self.ibe = state.ibe;
|
|
||||||
self.iev = state.iev;
|
|
||||||
self.im = state.im;
|
|
||||||
self.istate = state.istate;
|
|
||||||
self.afsel = state.afsel;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pl061_internal_update(&mut self) {
|
fn pl061_internal_update(&mut self) {
|
||||||
// FIXME:
|
// FIXME:
|
||||||
// Missing Output Interrupt Emulation.
|
// Missing Output Interrupt Emulation.
|
||||||
|
|
||||||
// Input Edging Interrupt Emulation.
|
// Input Edging Interrupt Emulation.
|
||||||
let changed = ((self.old_in_data ^ self.data) & !self.dir) as u32;
|
let changed = (self.old_in_data ^ self.data) & !self.dir;
|
||||||
if changed > 0 {
|
if changed > 0 {
|
||||||
self.old_in_data = self.data;
|
self.old_in_data = self.data;
|
||||||
for i in 0..N_GPIOS {
|
for i in 0..N_GPIOS {
|
||||||
@@ -319,12 +328,7 @@ impl Snapshottable for Gpio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||||
Snapshot::new_from_versioned_state(&self.id, &self.state())
|
Snapshot::new_from_versioned_state(&self.state())
|
||||||
}
|
|
||||||
|
|
||||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
|
||||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,6 +382,7 @@ mod tests {
|
|||||||
let mut gpio = Gpio::new(
|
let mut gpio = Gpio::new(
|
||||||
String::from(GPIO_NAME),
|
String::from(GPIO_NAME),
|
||||||
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
let mut data = [0; 4];
|
let mut data = [0; 4];
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
mod cmos;
|
mod cmos;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
mod debug_port;
|
mod debug_port;
|
||||||
#[cfg(feature = "fwdebug")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
mod fwdebug;
|
mod fwdebug;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
mod gpio_pl061;
|
mod gpio_pl061;
|
||||||
@@ -22,7 +22,7 @@ mod uart_pl011;
|
|||||||
pub use self::cmos::Cmos;
|
pub use self::cmos::Cmos;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub use self::debug_port::DebugPort;
|
pub use self::debug_port::DebugPort;
|
||||||
#[cfg(feature = "fwdebug")]
|
#[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;
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ pub enum Error {
|
|||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Error::BadWriteOffset(offset) => write!(f, "Bad Write Offset: {}", offset),
|
Error::BadWriteOffset(offset) => write!(f, "Bad Write Offset: {offset}"),
|
||||||
Error::InterruptFailure(e) => write!(f, "Failed to trigger interrupt: {}", e),
|
Error::InterruptFailure(e) => write!(f, "Failed to trigger interrupt: {e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,12 +61,10 @@ pub enum ClockType {
|
|||||||
/// Equivalent to `libc::CLOCK_MONOTONIC`.
|
/// Equivalent to `libc::CLOCK_MONOTONIC`.
|
||||||
Monotonic,
|
Monotonic,
|
||||||
/// Equivalent to `libc::CLOCK_REALTIME`.
|
/// Equivalent to `libc::CLOCK_REALTIME`.
|
||||||
#[allow(dead_code)]
|
|
||||||
Real,
|
Real,
|
||||||
/// Equivalent to `libc::CLOCK_PROCESS_CPUTIME_ID`.
|
/// Equivalent to `libc::CLOCK_PROCESS_CPUTIME_ID`.
|
||||||
ProcessCpu,
|
ProcessCpu,
|
||||||
/// Equivalent to `libc::CLOCK_THREAD_CPUTIME_ID`.
|
/// Equivalent to `libc::CLOCK_THREAD_CPUTIME_ID`.
|
||||||
#[allow(dead_code)]
|
|
||||||
ThreadCpu,
|
ThreadCpu,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +99,7 @@ pub struct LocalTime {
|
|||||||
|
|
||||||
impl LocalTime {
|
impl LocalTime {
|
||||||
/// Returns the [LocalTime](struct.LocalTime.html) structure for the calling moment.
|
/// Returns the [LocalTime](struct.LocalTime.html) structure for the calling moment.
|
||||||
#[allow(dead_code)]
|
#[cfg(test)]
|
||||||
pub fn now() -> LocalTime {
|
pub fn now() -> LocalTime {
|
||||||
let mut timespec = libc::timespec {
|
let mut timespec = libc::timespec {
|
||||||
tv_sec: 0,
|
tv_sec: 0,
|
||||||
@@ -121,7 +119,7 @@ impl LocalTime {
|
|||||||
tm_zone: std::ptr::null(),
|
tm_zone: std::ptr::null(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Safe because the parameters are valid.
|
// SAFETY: the parameters are valid.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::clock_gettime(libc::CLOCK_REALTIME, &mut timespec);
|
libc::clock_gettime(libc::CLOCK_REALTIME, &mut timespec);
|
||||||
libc::localtime_r(×pec.tv_sec, &mut tm);
|
libc::localtime_r(×pec.tv_sec, &mut tm);
|
||||||
@@ -173,22 +171,6 @@ impl Default for TimestampUs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a timestamp in nanoseconds from a monotonic clock.
|
|
||||||
///
|
|
||||||
/// Uses `_rdstc` on `x86_64` and [`get_time`](fn.get_time.html) on other architectures.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn timestamp_cycles() -> u64 {
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
// Safe because there's nothing that can go wrong with this call.
|
|
||||||
unsafe {
|
|
||||||
std::arch::x86_64::_rdtsc() as u64
|
|
||||||
}
|
|
||||||
#[cfg(not(target_arch = "x86_64"))]
|
|
||||||
{
|
|
||||||
get_time(ClockType::Monotonic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a timestamp in nanoseconds based on the provided clock type.
|
/// Returns a timestamp in nanoseconds based on the provided clock type.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@@ -199,7 +181,7 @@ pub fn get_time(clock_type: ClockType) -> u64 {
|
|||||||
tv_sec: 0,
|
tv_sec: 0,
|
||||||
tv_nsec: 0,
|
tv_nsec: 0,
|
||||||
};
|
};
|
||||||
// Safe because the parameters are valid.
|
// SAFETY: the parameters are valid.
|
||||||
unsafe { libc::clock_gettime(clock_type.into(), &mut time_struct) };
|
unsafe { libc::clock_gettime(clock_type.into(), &mut time_struct) };
|
||||||
seconds_to_nanoseconds(time_struct.tv_sec).unwrap() as u64 + (time_struct.tv_nsec as u64)
|
seconds_to_nanoseconds(time_struct.tv_sec).unwrap() as u64 + (time_struct.tv_nsec as u64)
|
||||||
}
|
}
|
||||||
@@ -525,7 +507,6 @@ mod tests {
|
|||||||
($test_name: ident, $write_fn_name: ident, $read_fn_name: ident, $is_be: expr, $data_type: ty) => {
|
($test_name: ident, $write_fn_name: ident, $read_fn_name: ident, $is_be: expr, $data_type: ty) => {
|
||||||
#[test]
|
#[test]
|
||||||
fn $test_name() {
|
fn $test_name() {
|
||||||
#[allow(overflowing_literals)]
|
|
||||||
let test_cases = [
|
let test_cases = [
|
||||||
(
|
(
|
||||||
0x0123_4567_89AB_CDEF as u64,
|
0x0123_4567_89AB_CDEF as u64,
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ pub struct Serial {
|
|||||||
id: String,
|
id: String,
|
||||||
interrupt_enable: u8,
|
interrupt_enable: u8,
|
||||||
interrupt_identification: u8,
|
interrupt_identification: u8,
|
||||||
interrupt: Arc<dyn InterruptSourceGroup>,
|
|
||||||
line_control: u8,
|
line_control: u8,
|
||||||
line_status: u8,
|
line_status: u8,
|
||||||
modem_control: u8,
|
modem_control: u8,
|
||||||
@@ -71,6 +70,7 @@ pub struct Serial {
|
|||||||
scratch: u8,
|
scratch: u8,
|
||||||
baud_divisor: u16,
|
baud_divisor: u16,
|
||||||
in_buffer: VecDeque<u8>,
|
in_buffer: VecDeque<u8>,
|
||||||
|
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||||
out: Option<Box<dyn io::Write + Send>>,
|
out: Option<Box<dyn io::Write + Send>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,19 +93,56 @@ impl Serial {
|
|||||||
id: String,
|
id: String,
|
||||||
interrupt: Arc<dyn InterruptSourceGroup>,
|
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||||
out: Option<Box<dyn io::Write + Send>>,
|
out: Option<Box<dyn io::Write + Send>>,
|
||||||
|
state: Option<SerialState>,
|
||||||
) -> Serial {
|
) -> Serial {
|
||||||
|
let (
|
||||||
|
interrupt_enable,
|
||||||
|
interrupt_identification,
|
||||||
|
line_control,
|
||||||
|
line_status,
|
||||||
|
modem_control,
|
||||||
|
modem_status,
|
||||||
|
scratch,
|
||||||
|
baud_divisor,
|
||||||
|
in_buffer,
|
||||||
|
) = if let Some(state) = state {
|
||||||
|
(
|
||||||
|
state.interrupt_enable,
|
||||||
|
state.interrupt_identification,
|
||||||
|
state.line_control,
|
||||||
|
state.line_status,
|
||||||
|
state.modem_control,
|
||||||
|
state.modem_status,
|
||||||
|
state.scratch,
|
||||||
|
state.baud_divisor,
|
||||||
|
state.in_buffer.into(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
DEFAULT_INTERRUPT_IDENTIFICATION,
|
||||||
|
DEFAULT_LINE_CONTROL,
|
||||||
|
DEFAULT_LINE_STATUS,
|
||||||
|
DEFAULT_MODEM_CONTROL,
|
||||||
|
DEFAULT_MODEM_STATUS,
|
||||||
|
0,
|
||||||
|
DEFAULT_BAUD_DIVISOR,
|
||||||
|
VecDeque::new(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
Serial {
|
Serial {
|
||||||
id,
|
id,
|
||||||
interrupt_enable: 0,
|
interrupt_enable,
|
||||||
interrupt_identification: DEFAULT_INTERRUPT_IDENTIFICATION,
|
interrupt_identification,
|
||||||
|
line_control,
|
||||||
|
line_status,
|
||||||
|
modem_control,
|
||||||
|
modem_status,
|
||||||
|
scratch,
|
||||||
|
baud_divisor,
|
||||||
|
in_buffer,
|
||||||
interrupt,
|
interrupt,
|
||||||
line_control: DEFAULT_LINE_CONTROL,
|
|
||||||
line_status: DEFAULT_LINE_STATUS,
|
|
||||||
modem_control: DEFAULT_MODEM_CONTROL,
|
|
||||||
modem_status: DEFAULT_MODEM_STATUS,
|
|
||||||
scratch: 0,
|
|
||||||
baud_divisor: DEFAULT_BAUD_DIVISOR,
|
|
||||||
in_buffer: VecDeque::new(),
|
|
||||||
out,
|
out,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,13 +152,18 @@ impl Serial {
|
|||||||
id: String,
|
id: String,
|
||||||
interrupt: Arc<dyn InterruptSourceGroup>,
|
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||||
out: Box<dyn io::Write + Send>,
|
out: Box<dyn io::Write + Send>,
|
||||||
|
state: Option<SerialState>,
|
||||||
) -> Serial {
|
) -> Serial {
|
||||||
Self::new(id, interrupt, Some(out))
|
Self::new(id, interrupt, Some(out), state)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a Serial port with no connected output.
|
/// Constructs a Serial port with no connected output.
|
||||||
pub fn new_sink(id: String, interrupt: Arc<dyn InterruptSourceGroup>) -> Serial {
|
pub fn new_sink(
|
||||||
Self::new(id, interrupt, None)
|
id: String,
|
||||||
|
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||||
|
state: Option<SerialState>,
|
||||||
|
) -> Serial {
|
||||||
|
Self::new(id, interrupt, None, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_out(&mut self, out: Box<dyn io::Write + Send>) {
|
pub fn set_out(&mut self, out: Box<dyn io::Write + Send>) {
|
||||||
@@ -199,7 +241,7 @@ impl Serial {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_write(&mut self, offset: u8, v: u8) -> Result<()> {
|
fn handle_write(&mut self, offset: u8, v: u8) -> Result<()> {
|
||||||
match offset as u8 {
|
match offset {
|
||||||
DLAB_LOW if self.is_dlab_set() => {
|
DLAB_LOW if self.is_dlab_set() => {
|
||||||
self.baud_divisor = (self.baud_divisor & 0xff00) | u16::from(v)
|
self.baud_divisor = (self.baud_divisor & 0xff00) | u16::from(v)
|
||||||
}
|
}
|
||||||
@@ -242,18 +284,6 @@ impl Serial {
|
|||||||
in_buffer: self.in_buffer.clone().into(),
|
in_buffer: self.in_buffer.clone().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_state(&mut self, state: &SerialState) {
|
|
||||||
self.interrupt_enable = state.interrupt_enable;
|
|
||||||
self.interrupt_identification = state.interrupt_identification;
|
|
||||||
self.line_control = state.line_control;
|
|
||||||
self.line_status = state.line_status;
|
|
||||||
self.modem_control = state.modem_control;
|
|
||||||
self.modem_status = state.modem_status;
|
|
||||||
self.scratch = state.scratch;
|
|
||||||
self.baud_divisor = state.baud_divisor;
|
|
||||||
self.in_buffer = state.in_buffer.clone().into();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BusDevice for Serial {
|
impl BusDevice for Serial {
|
||||||
@@ -304,12 +334,7 @@ impl Snapshottable for Serial {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||||
Snapshot::new_from_versioned_state(&self.id, &self.state())
|
Snapshot::new_from_versioned_state(&self.state())
|
||||||
}
|
|
||||||
|
|
||||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
|
||||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,6 +409,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())),
|
||||||
Box::new(serial_out.clone()),
|
Box::new(serial_out.clone()),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
serial.write(0, DATA as u64, &[b'x', b'y']);
|
serial.write(0, DATA as u64, &[b'x', b'y']);
|
||||||
@@ -404,6 +430,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())),
|
||||||
Box::new(serial_out),
|
Box::new(serial_out),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 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
|
||||||
@@ -440,6 +467,7 @@ mod tests {
|
|||||||
let mut serial = Serial::new_sink(
|
let mut serial = Serial::new_sink(
|
||||||
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())),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 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
|
||||||
@@ -462,6 +490,7 @@ mod tests {
|
|||||||
let mut serial = Serial::new_sink(
|
let mut serial = Serial::new_sink(
|
||||||
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())),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
serial.write(0, LCR as u64, &[LCR_DLAB_BIT]);
|
serial.write(0, LCR as u64, &[LCR_DLAB_BIT]);
|
||||||
@@ -483,6 +512,7 @@ mod tests {
|
|||||||
let mut serial = Serial::new_sink(
|
let mut serial = Serial::new_sink(
|
||||||
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())),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
serial.write(0, MCR as u64, &[MCR_LOOP_BIT]);
|
serial.write(0, MCR as u64, &[MCR_LOOP_BIT]);
|
||||||
@@ -509,6 +539,7 @@ mod tests {
|
|||||||
let mut serial = Serial::new_sink(
|
let mut serial = Serial::new_sink(
|
||||||
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())),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
serial.write(0, SCR as u64, &[0x12]);
|
serial.write(0, SCR as u64, &[0x12]);
|
||||||
|
|||||||
@@ -60,11 +60,11 @@ pub enum Error {
|
|||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Error::BadWriteOffset(offset) => write!(f, "pl011_write: Bad Write Offset: {}", offset),
|
Error::BadWriteOffset(offset) => write!(f, "pl011_write: Bad Write Offset: {offset}"),
|
||||||
Error::DmaNotImplemented => write!(f, "pl011: DMA not implemented."),
|
Error::DmaNotImplemented => write!(f, "pl011: DMA not implemented."),
|
||||||
Error::InterruptFailure(e) => write!(f, "Failed to trigger interrupt: {}", e),
|
Error::InterruptFailure(e) => write!(f, "Failed to trigger interrupt: {e}"),
|
||||||
Error::WriteAllFailure(e) => write!(f, "Failed to write: {}", e),
|
Error::WriteAllFailure(e) => write!(f, "Failed to write: {e}"),
|
||||||
Error::FlushFailure(e) => write!(f, "Failed to flush: {}", e),
|
Error::FlushFailure(e) => write!(f, "Failed to flush: {e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,24 +122,79 @@ impl Pl011 {
|
|||||||
irq: Arc<dyn InterruptSourceGroup>,
|
irq: Arc<dyn InterruptSourceGroup>,
|
||||||
out: Option<Box<dyn io::Write + Send>>,
|
out: Option<Box<dyn io::Write + Send>>,
|
||||||
timestamp: Instant,
|
timestamp: Instant,
|
||||||
|
state: Option<Pl011State>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let (
|
||||||
|
flags,
|
||||||
|
lcr,
|
||||||
|
rsr,
|
||||||
|
cr,
|
||||||
|
dmacr,
|
||||||
|
debug,
|
||||||
|
int_enabled,
|
||||||
|
int_level,
|
||||||
|
read_fifo,
|
||||||
|
ilpr,
|
||||||
|
ibrd,
|
||||||
|
fbrd,
|
||||||
|
ifl,
|
||||||
|
read_count,
|
||||||
|
read_trigger,
|
||||||
|
) = if let Some(state) = state {
|
||||||
|
(
|
||||||
|
state.flags,
|
||||||
|
state.lcr,
|
||||||
|
state.rsr,
|
||||||
|
state.cr,
|
||||||
|
state.dmacr,
|
||||||
|
state.debug,
|
||||||
|
state.int_enabled,
|
||||||
|
state.int_level,
|
||||||
|
state.read_fifo.into(),
|
||||||
|
state.ilpr,
|
||||||
|
state.ibrd,
|
||||||
|
state.fbrd,
|
||||||
|
state.ifl,
|
||||||
|
state.read_count,
|
||||||
|
state.read_trigger,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
0x90,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0x300,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
VecDeque::new(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0x12,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
flags: 0x90u32,
|
flags,
|
||||||
lcr: 0u32,
|
lcr,
|
||||||
rsr: 0u32,
|
rsr,
|
||||||
cr: 0x300u32,
|
cr,
|
||||||
dmacr: 0u32,
|
dmacr,
|
||||||
debug: 0u32,
|
debug,
|
||||||
int_enabled: 0u32,
|
int_enabled,
|
||||||
int_level: 0u32,
|
int_level,
|
||||||
read_fifo: VecDeque::new(),
|
read_fifo,
|
||||||
ilpr: 0u32,
|
ilpr,
|
||||||
ibrd: 0u32,
|
ibrd,
|
||||||
fbrd: 0u32,
|
fbrd,
|
||||||
ifl: 0x12u32,
|
ifl,
|
||||||
read_count: 0u32,
|
read_count,
|
||||||
read_trigger: 1u32,
|
read_trigger,
|
||||||
irq,
|
irq,
|
||||||
out,
|
out,
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -170,24 +225,6 @@ impl Pl011 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_state(&mut self, state: &Pl011State) {
|
|
||||||
self.flags = state.flags;
|
|
||||||
self.lcr = state.lcr;
|
|
||||||
self.rsr = state.rsr;
|
|
||||||
self.cr = state.cr;
|
|
||||||
self.dmacr = state.dmacr;
|
|
||||||
self.debug = state.debug;
|
|
||||||
self.int_enabled = state.int_enabled;
|
|
||||||
self.int_level = state.int_level;
|
|
||||||
self.read_fifo = state.read_fifo.clone().into();
|
|
||||||
self.ilpr = state.ilpr;
|
|
||||||
self.ibrd = state.ibrd;
|
|
||||||
self.fbrd = state.fbrd;
|
|
||||||
self.ifl = state.ifl;
|
|
||||||
self.read_count = state.read_count;
|
|
||||||
self.read_trigger = state.read_trigger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Queues raw bytes for the guest to read and signals the interrupt
|
/// Queues raw bytes for the guest to read and signals the interrupt
|
||||||
pub fn queue_input_bytes(&mut self, c: &[u8]) -> vmm_sys_util::errno::Result<()> {
|
pub fn queue_input_bytes(&mut self, c: &[u8]) -> vmm_sys_util::errno::Result<()> {
|
||||||
self.read_fifo.extend(c);
|
self.read_fifo.extend(c);
|
||||||
@@ -306,25 +343,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(),
|
||||||
@@ -372,7 +409,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,
|
||||||
_ => {
|
_ => {
|
||||||
@@ -417,12 +454,7 @@ impl Snapshottable for Pl011 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||||
Snapshot::new_from_versioned_state(&self.id, &self.state())
|
Snapshot::new_from_versioned_state(&self.state())
|
||||||
}
|
|
||||||
|
|
||||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
|
||||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -498,12 +530,13 @@ mod tests {
|
|||||||
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(),
|
Instant::now(),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
pl011.write(0, UARTDR as u64, &[b'x', b'y']);
|
pl011.write(0, UARTDR, &[b'x', b'y']);
|
||||||
pl011.write(0, UARTDR as u64, &[b'a']);
|
pl011.write(0, UARTDR, &[b'a']);
|
||||||
pl011.write(0, UARTDR as u64, &[b'b']);
|
pl011.write(0, UARTDR, &[b'b']);
|
||||||
pl011.write(0, UARTDR as u64, &[b'c']);
|
pl011.write(0, UARTDR, &[b'c']);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pl011_out.buf.lock().unwrap().as_slice(),
|
pl011_out.buf.lock().unwrap().as_slice(),
|
||||||
&[b'x', b'a', b'b', b'c']
|
&[b'x', b'a', b'b', b'c']
|
||||||
@@ -519,6 +552,7 @@ mod tests {
|
|||||||
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(),
|
Instant::now(),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 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
|
||||||
@@ -529,11 +563,11 @@ mod tests {
|
|||||||
assert_eq!(intr_evt.read().unwrap(), 2);
|
assert_eq!(intr_evt.read().unwrap(), 2);
|
||||||
|
|
||||||
let mut data = [0u8];
|
let mut data = [0u8];
|
||||||
pl011.read(0, UARTDR as u64, &mut data);
|
pl011.read(0, UARTDR, &mut data);
|
||||||
assert_eq!(data[0], b'a');
|
assert_eq!(data[0], b'a');
|
||||||
pl011.read(0, UARTDR as u64, &mut data);
|
pl011.read(0, UARTDR, &mut data);
|
||||||
assert_eq!(data[0], b'b');
|
assert_eq!(data[0], b'b');
|
||||||
pl011.read(0, UARTDR as u64, &mut data);
|
pl011.read(0, UARTDR, &mut data);
|
||||||
assert_eq!(data[0], b'c');
|
assert_eq!(data[0], b'c');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ pub mod interrupt_controller;
|
|||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub mod ioapic;
|
pub mod ioapic;
|
||||||
pub mod legacy;
|
pub mod legacy;
|
||||||
|
pub mod tpm;
|
||||||
|
|
||||||
pub use self::acpi::{AcpiGedDevice, AcpiPmTimerDevice, AcpiShutdownDevice};
|
pub use self::acpi::{AcpiGedDevice, AcpiPmTimerDevice, AcpiShutdownDevice};
|
||||||
|
|
||||||
@@ -32,11 +33,9 @@ bitflags! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
macro_rules! generate_read_fn {
|
macro_rules! generate_read_fn {
|
||||||
($fn_name: ident, $data_type: ty, $byte_type: ty, $type_size: expr, $endian_type: ident) => {
|
($fn_name: ident, $data_type: ty, $byte_type: ty, $type_size: expr, $endian_type: ident) => {
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn $fn_name(input: &[$byte_type]) -> $data_type {
|
pub fn $fn_name(input: &[$byte_type]) -> $data_type {
|
||||||
assert!($type_size == std::mem::size_of::<$data_type>());
|
assert!($type_size == std::mem::size_of::<$data_type>());
|
||||||
let mut array = [0u8; $type_size];
|
let mut array = [0u8; $type_size];
|
||||||
@@ -48,11 +47,9 @@ macro_rules! generate_read_fn {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
macro_rules! generate_write_fn {
|
macro_rules! generate_write_fn {
|
||||||
($fn_name: ident, $data_type: ty, $byte_type: ty, $endian_type: ident) => {
|
($fn_name: ident, $data_type: ty, $byte_type: ty, $endian_type: ident) => {
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn $fn_name(buf: &mut [$byte_type], n: $data_type) {
|
pub fn $fn_name(buf: &mut [$byte_type], n: $data_type) {
|
||||||
for (byte, read) in buf
|
for (byte, read) in buf
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
|
|||||||
467
devices/src/tpm.rs
Normal file
467
devices/src/tpm.rs
Normal file
@@ -0,0 +1,467 @@
|
|||||||
|
// Copyright © 2022, Microsoft Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
use arch::aarch64::layout::{TPM_SIZE, TPM_START};
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
use arch::x86_64::layout::{TPM_SIZE, TPM_START};
|
||||||
|
use phf::phf_map;
|
||||||
|
use std::cmp;
|
||||||
|
use std::sync::{Arc, Barrier};
|
||||||
|
use thiserror::Error;
|
||||||
|
use tpm::emulator::{BackendCmd, Emulator};
|
||||||
|
use tpm::TPM_CRB_BUFFER_MAX;
|
||||||
|
use tpm::TPM_SUCCESS;
|
||||||
|
use vm_device::BusDevice;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("Emulator doesn't implement min required capabilities: {0}")]
|
||||||
|
CheckCaps(#[source] anyhow::Error),
|
||||||
|
#[error("Failed to initialize tpm: {0}")]
|
||||||
|
Init(#[source] anyhow::Error),
|
||||||
|
#[error("Failed to deliver tpm Command: {0}")]
|
||||||
|
DeliverRequest(#[source] anyhow::Error),
|
||||||
|
}
|
||||||
|
type Result<T> = anyhow::Result<T, Error>;
|
||||||
|
|
||||||
|
/* crb 32-bit registers */
|
||||||
|
const CRB_LOC_STATE: u32 = 0x0;
|
||||||
|
//Register Fields
|
||||||
|
// Field => (start, length)
|
||||||
|
// start: lowest bit in the bit field numbered from 0
|
||||||
|
// length: length of the bit field
|
||||||
|
const CRB_LOC_STATE_FIELDS: phf::Map<&str, [u32; 2]> = phf_map! {
|
||||||
|
"tpmEstablished" => [0, 1],
|
||||||
|
"locAssigned" => [1,1],
|
||||||
|
"activeLocality"=> [2, 3],
|
||||||
|
"reserved" => [5, 2],
|
||||||
|
"tpmRegValidSts" => [7, 1]
|
||||||
|
};
|
||||||
|
const CRB_LOC_CTRL: u32 = 0x08;
|
||||||
|
const CRB_LOC_CTRL_REQUEST_ACCESS: u32 = 1 << 0;
|
||||||
|
const CRB_LOC_CTRL_RELINQUISH: u32 = 1 << 1;
|
||||||
|
const CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT: u32 = 1 << 3;
|
||||||
|
const CRB_LOC_STS: u32 = 0x0C;
|
||||||
|
const CRB_LOC_STS_FIELDS: phf::Map<&str, [u32; 2]> = phf_map! {
|
||||||
|
"Granted" => [0, 1],
|
||||||
|
"beenSeized" => [1,1]
|
||||||
|
};
|
||||||
|
const CRB_INTF_ID: u32 = 0x30;
|
||||||
|
const CRB_INTF_ID_FIELDS: phf::Map<&str, [u32; 2]> = phf_map! {
|
||||||
|
"InterfaceType" => [0, 4],
|
||||||
|
"InterfaceVersion" => [4, 4],
|
||||||
|
"CapLocality" => [8, 1],
|
||||||
|
"CapCRBIdleBypass" => [9, 1],
|
||||||
|
"Reserved1" => [10, 1],
|
||||||
|
"CapDataXferSizeSupport" => [11, 2],
|
||||||
|
"CapFIFO" => [13, 1],
|
||||||
|
"CapCRB" => [14, 1],
|
||||||
|
"CapIFRes" => [15, 2],
|
||||||
|
"InterfaceSelector" => [17, 2],
|
||||||
|
"IntfSelLock" => [19, 1],
|
||||||
|
"Reserved2" => [20, 4],
|
||||||
|
"RID" => [24, 8]
|
||||||
|
};
|
||||||
|
const CRB_INTF_ID2: u32 = 0x34;
|
||||||
|
const CRB_INTF_ID2_FIELDS: phf::Map<&str, [u32; 2]> = phf_map! {
|
||||||
|
"VID" => [0, 16],
|
||||||
|
"DID" => [16, 16]
|
||||||
|
};
|
||||||
|
const CRB_CTRL_REQ: u32 = 0x40;
|
||||||
|
const CRB_CTRL_REQ_CMD_READY: u32 = 1 << 0;
|
||||||
|
const CRB_CTRL_REQ_GO_IDLE: u32 = 1 << 1;
|
||||||
|
const CRB_CTRL_STS: u32 = 0x44;
|
||||||
|
const CRB_CTRL_STS_FIELDS: phf::Map<&str, [u32; 2]> = phf_map! {
|
||||||
|
"tpmSts" => [0, 1],
|
||||||
|
"tpmIdle" => [1, 1]
|
||||||
|
};
|
||||||
|
const CRB_CTRL_CANCEL: u32 = 0x48;
|
||||||
|
const CRB_CANCEL_INVOKE: u32 = 1 << 0;
|
||||||
|
const CRB_CTRL_START: u32 = 0x4C;
|
||||||
|
const CRB_START_INVOKE: u32 = 1 << 0;
|
||||||
|
const CRB_CTRL_CMD_LADDR: u32 = 0x5C;
|
||||||
|
const CRB_CTRL_CMD_HADDR: u32 = 0x60;
|
||||||
|
const CRB_CTRL_RSP_SIZE: u32 = 0x64;
|
||||||
|
const CRB_CTRL_RSP_ADDR: u32 = 0x68;
|
||||||
|
const CRB_DATA_BUFFER: u32 = 0x80;
|
||||||
|
|
||||||
|
const TPM_CRB_NO_LOCALITY: u32 = 0xff;
|
||||||
|
|
||||||
|
const TPM_CRB_ADDR_BASE: u32 = TPM_START.0 as u32;
|
||||||
|
const TPM_CRB_ADDR_SIZE: usize = TPM_SIZE as usize;
|
||||||
|
|
||||||
|
const TPM_CRB_R_MAX: u32 = CRB_DATA_BUFFER;
|
||||||
|
|
||||||
|
// CRB Protocol details
|
||||||
|
const CRB_INTF_TYPE_CRB_ACTIVE: u32 = 0b1;
|
||||||
|
const CRB_INTF_VERSION_CRB: u32 = 0b1;
|
||||||
|
const CRB_INTF_CAP_LOCALITY_0_ONLY: u32 = 0b0;
|
||||||
|
const CRB_INTF_CAP_IDLE_FAST: u32 = 0b0;
|
||||||
|
const CRB_INTF_CAP_XFER_SIZE_64: u32 = 0b11;
|
||||||
|
const CRB_INTF_CAP_FIFO_NOT_SUPPORTED: u32 = 0b0;
|
||||||
|
const CRB_INTF_CAP_CRB_SUPPORTED: u32 = 0b1;
|
||||||
|
const CRB_INTF_IF_SELECTOR_CRB: u32 = 0b1;
|
||||||
|
const PCI_VENDOR_ID_IBM: u32 = 0x1014;
|
||||||
|
const CRB_CTRL_CMD_SIZE_REG: u32 = 0x58;
|
||||||
|
const CRB_CTRL_CMD_SIZE: usize = TPM_CRB_ADDR_SIZE - CRB_DATA_BUFFER as usize;
|
||||||
|
|
||||||
|
fn get_fields_map(reg: u32) -> phf::Map<&'static str, [u32; 2]> {
|
||||||
|
match reg {
|
||||||
|
CRB_LOC_STATE => CRB_LOC_STATE_FIELDS,
|
||||||
|
CRB_LOC_STS => CRB_LOC_STS_FIELDS,
|
||||||
|
CRB_INTF_ID => CRB_INTF_ID_FIELDS,
|
||||||
|
CRB_INTF_ID2 => CRB_INTF_ID2_FIELDS,
|
||||||
|
CRB_CTRL_STS => CRB_CTRL_STS_FIELDS,
|
||||||
|
_ => {
|
||||||
|
panic!("Fields in '{reg:?}' register were accessed which are Invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a particular field in a Register
|
||||||
|
fn set_reg_field(regs: &mut [u32; TPM_CRB_R_MAX as usize], reg: u32, field: &str, value: u32) {
|
||||||
|
let reg_fields = get_fields_map(reg);
|
||||||
|
if reg_fields.contains_key(field) {
|
||||||
|
let start = reg_fields.get(field).unwrap()[0];
|
||||||
|
let len = reg_fields.get(field).unwrap()[1];
|
||||||
|
let mask = (!(0_u32) >> (32 - len)) << start;
|
||||||
|
regs[reg as usize] = (regs[reg as usize] & !mask) | ((value << start) & mask);
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
"Failed to tpm Register. {:?} is not a valid field in Reg {:#X}",
|
||||||
|
field, reg
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the value of a particular field in a Register
|
||||||
|
fn get_reg_field(regs: &[u32; TPM_CRB_R_MAX as usize], reg: u32, field: &str) -> u32 {
|
||||||
|
let reg_fields = get_fields_map(reg);
|
||||||
|
if reg_fields.contains_key(field) {
|
||||||
|
let start = reg_fields.get(field).unwrap()[0];
|
||||||
|
let len = reg_fields.get(field).unwrap()[1];
|
||||||
|
let mask = (!(0_u32) >> (32 - len)) << start;
|
||||||
|
(regs[reg as usize] & mask) >> start
|
||||||
|
} else {
|
||||||
|
// TODO: Sensible return value if fields do not exist
|
||||||
|
0x0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn locality_from_addr(addr: u32) -> u8 {
|
||||||
|
(addr >> 12) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Tpm {
|
||||||
|
emulator: Emulator,
|
||||||
|
cmd: Option<BackendCmd>,
|
||||||
|
regs: [u32; TPM_CRB_R_MAX as usize],
|
||||||
|
backend_buff_size: usize,
|
||||||
|
data_buff: [u8; TPM_CRB_BUFFER_MAX],
|
||||||
|
data_buff_len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tpm {
|
||||||
|
pub fn new(path: String) -> Result<Self> {
|
||||||
|
let emulator = Emulator::new(path)
|
||||||
|
.map_err(|e| Error::Init(anyhow!("Failed while initializing tpm Emulator: {:?}", e)))?;
|
||||||
|
let mut tpm = Tpm {
|
||||||
|
emulator,
|
||||||
|
cmd: None,
|
||||||
|
regs: [0; TPM_CRB_R_MAX as usize],
|
||||||
|
backend_buff_size: TPM_CRB_BUFFER_MAX,
|
||||||
|
data_buff: [0; TPM_CRB_BUFFER_MAX],
|
||||||
|
data_buff_len: 0,
|
||||||
|
};
|
||||||
|
tpm.reset()?;
|
||||||
|
Ok(tpm)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_active_locality(&mut self) -> u32 {
|
||||||
|
if get_reg_field(&self.regs, CRB_LOC_STATE, "locAssigned") == 0 {
|
||||||
|
return TPM_CRB_NO_LOCALITY;
|
||||||
|
}
|
||||||
|
get_reg_field(&self.regs, CRB_LOC_STATE, "activeLocality")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_completed(&mut self, result: isize) {
|
||||||
|
self.regs[CRB_CTRL_START as usize] = !CRB_START_INVOKE;
|
||||||
|
if result != 0 {
|
||||||
|
set_reg_field(&mut self.regs, CRB_CTRL_STS, "tpmSts", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) -> Result<()> {
|
||||||
|
let cur_buff_size = self.emulator.get_buffer_size().unwrap();
|
||||||
|
self.regs = [0; TPM_CRB_R_MAX as usize];
|
||||||
|
set_reg_field(&mut self.regs, CRB_LOC_STATE, "tpmRegValidSts", 1);
|
||||||
|
set_reg_field(&mut self.regs, CRB_CTRL_STS, "tpmIdle", 1);
|
||||||
|
set_reg_field(
|
||||||
|
&mut self.regs,
|
||||||
|
CRB_INTF_ID,
|
||||||
|
"InterfaceType",
|
||||||
|
CRB_INTF_TYPE_CRB_ACTIVE,
|
||||||
|
);
|
||||||
|
set_reg_field(
|
||||||
|
&mut self.regs,
|
||||||
|
CRB_INTF_ID,
|
||||||
|
"InterfaceVersion",
|
||||||
|
CRB_INTF_VERSION_CRB,
|
||||||
|
);
|
||||||
|
set_reg_field(
|
||||||
|
&mut self.regs,
|
||||||
|
CRB_INTF_ID,
|
||||||
|
"CapLocality",
|
||||||
|
CRB_INTF_CAP_LOCALITY_0_ONLY,
|
||||||
|
);
|
||||||
|
set_reg_field(
|
||||||
|
&mut self.regs,
|
||||||
|
CRB_INTF_ID,
|
||||||
|
"CapCRBIdleBypass",
|
||||||
|
CRB_INTF_CAP_IDLE_FAST,
|
||||||
|
);
|
||||||
|
set_reg_field(
|
||||||
|
&mut self.regs,
|
||||||
|
CRB_INTF_ID,
|
||||||
|
"CapDataXferSizeSupport",
|
||||||
|
CRB_INTF_CAP_XFER_SIZE_64,
|
||||||
|
);
|
||||||
|
set_reg_field(
|
||||||
|
&mut self.regs,
|
||||||
|
CRB_INTF_ID,
|
||||||
|
"CapFIFO",
|
||||||
|
CRB_INTF_CAP_FIFO_NOT_SUPPORTED,
|
||||||
|
);
|
||||||
|
set_reg_field(
|
||||||
|
&mut self.regs,
|
||||||
|
CRB_INTF_ID,
|
||||||
|
"CapCRB",
|
||||||
|
CRB_INTF_CAP_CRB_SUPPORTED,
|
||||||
|
);
|
||||||
|
set_reg_field(
|
||||||
|
&mut self.regs,
|
||||||
|
CRB_INTF_ID,
|
||||||
|
"InterfaceSelector",
|
||||||
|
CRB_INTF_IF_SELECTOR_CRB,
|
||||||
|
);
|
||||||
|
set_reg_field(&mut self.regs, CRB_INTF_ID, "RID", 0b0000);
|
||||||
|
set_reg_field(&mut self.regs, CRB_INTF_ID2, "VID", PCI_VENDOR_ID_IBM);
|
||||||
|
|
||||||
|
self.regs[CRB_CTRL_CMD_SIZE_REG as usize] = CRB_CTRL_CMD_SIZE as u32;
|
||||||
|
self.regs[CRB_CTRL_CMD_LADDR as usize] = TPM_CRB_ADDR_BASE + CRB_DATA_BUFFER;
|
||||||
|
self.regs[CRB_CTRL_RSP_SIZE as usize] = CRB_CTRL_CMD_SIZE as u32;
|
||||||
|
self.regs[CRB_CTRL_RSP_ADDR as usize] = TPM_CRB_ADDR_BASE + CRB_DATA_BUFFER;
|
||||||
|
|
||||||
|
self.backend_buff_size = cmp::min(cur_buff_size, TPM_CRB_BUFFER_MAX);
|
||||||
|
|
||||||
|
if let Err(e) = self.emulator.startup_tpm(self.backend_buff_size) {
|
||||||
|
return Err(Error::Init(anyhow!(
|
||||||
|
"Failed while running Startup TPM. Error: {:?}",
|
||||||
|
e
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//impl BusDevice for TPM
|
||||||
|
impl BusDevice for Tpm {
|
||||||
|
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
|
||||||
|
let mut offset: u32 = offset as u32;
|
||||||
|
let read_len: usize = data.len();
|
||||||
|
|
||||||
|
if offset >= CRB_DATA_BUFFER
|
||||||
|
&& (offset + read_len as u32) < (CRB_DATA_BUFFER + self.data_buff.len() as u32)
|
||||||
|
{
|
||||||
|
// Read from Data Buffer
|
||||||
|
let start: usize = (offset as usize) - (CRB_DATA_BUFFER as usize);
|
||||||
|
let end: usize = start + read_len;
|
||||||
|
data[..].clone_from_slice(&self.data_buff[start..end]);
|
||||||
|
} else {
|
||||||
|
offset &= 0xff;
|
||||||
|
let mut val = self.regs[offset as usize];
|
||||||
|
|
||||||
|
if offset == CRB_LOC_STATE && !self.emulator.get_established_flag() {
|
||||||
|
val |= 0x1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.len() <= 4 {
|
||||||
|
data.clone_from_slice(val.to_ne_bytes()[0..read_len].as_ref());
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
"Invalid tpm read: offset {:#X}, data length {:?}",
|
||||||
|
offset,
|
||||||
|
data.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug!(
|
||||||
|
"MMIO Read: offset {:#X} len {:?} val = {:02X?} ",
|
||||||
|
offset,
|
||||||
|
data.len(),
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||||
|
debug!(
|
||||||
|
"MMIO Write: offset {:#X} len {:?} input data {:02X?}",
|
||||||
|
offset,
|
||||||
|
data.len(),
|
||||||
|
data
|
||||||
|
);
|
||||||
|
let mut offset: u32 = offset as u32;
|
||||||
|
if offset < CRB_DATA_BUFFER {
|
||||||
|
offset &= 0xff;
|
||||||
|
}
|
||||||
|
let locality = locality_from_addr(offset) as u32;
|
||||||
|
let write_len = data.len();
|
||||||
|
|
||||||
|
if offset >= CRB_DATA_BUFFER
|
||||||
|
&& (offset + write_len as u32) < (CRB_DATA_BUFFER + self.data_buff.len() as u32)
|
||||||
|
{
|
||||||
|
let start: usize = (offset as usize) - (CRB_DATA_BUFFER as usize);
|
||||||
|
if start == 0 {
|
||||||
|
// If filling data_buff at index 0, reset length to 0
|
||||||
|
self.data_buff_len = 0;
|
||||||
|
self.data_buff.fill(0);
|
||||||
|
}
|
||||||
|
let end: usize = start + data.len();
|
||||||
|
self.data_buff[start..end].clone_from_slice(data);
|
||||||
|
self.data_buff_len += data.len();
|
||||||
|
} else {
|
||||||
|
// Ctrl Commands that take more than 4 bytes as input are not yet supported
|
||||||
|
// CTRL_RSP_ADDR usually gets 8 byte write request. Last 4 bytes are zeros.
|
||||||
|
if write_len > 4 && offset != CRB_CTRL_RSP_ADDR {
|
||||||
|
error!(
|
||||||
|
"Invalid tpm write: offset {:#X}, data length {}",
|
||||||
|
offset,
|
||||||
|
data.len()
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut input: [u8; 4] = [0; 4];
|
||||||
|
input.copy_from_slice(&data[0..4]);
|
||||||
|
let v = u32::from_le_bytes(input);
|
||||||
|
|
||||||
|
match offset {
|
||||||
|
CRB_CTRL_CMD_SIZE_REG => {
|
||||||
|
self.regs[CRB_CTRL_CMD_SIZE_REG as usize] = v;
|
||||||
|
}
|
||||||
|
CRB_CTRL_CMD_LADDR => {
|
||||||
|
self.regs[CRB_CTRL_CMD_LADDR as usize] = v;
|
||||||
|
}
|
||||||
|
CRB_CTRL_CMD_HADDR => {
|
||||||
|
self.regs[CRB_CTRL_CMD_HADDR as usize] = v;
|
||||||
|
}
|
||||||
|
CRB_CTRL_RSP_SIZE => {
|
||||||
|
self.regs[CRB_CTRL_RSP_SIZE as usize] = v;
|
||||||
|
}
|
||||||
|
CRB_CTRL_RSP_ADDR => {
|
||||||
|
self.regs[CRB_CTRL_RSP_ADDR as usize] = v;
|
||||||
|
}
|
||||||
|
CRB_CTRL_REQ => match v {
|
||||||
|
CRB_CTRL_REQ_CMD_READY => {
|
||||||
|
set_reg_field(&mut self.regs, CRB_CTRL_STS, "tpmIdle", 0);
|
||||||
|
}
|
||||||
|
CRB_CTRL_REQ_GO_IDLE => {
|
||||||
|
set_reg_field(&mut self.regs, CRB_CTRL_STS, "tpmIdle", 1);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
error!("Invalid value passed to CRTL_REQ register");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CRB_CTRL_CANCEL => {
|
||||||
|
if v == CRB_CANCEL_INVOKE
|
||||||
|
&& (self.regs[CRB_CTRL_START as usize] & CRB_START_INVOKE != 0)
|
||||||
|
{
|
||||||
|
if let Err(e) = self.emulator.cancel_cmd() {
|
||||||
|
error!("Failed to run cancel command. Error: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CRB_CTRL_START => {
|
||||||
|
if v == CRB_START_INVOKE
|
||||||
|
&& ((self.regs[CRB_CTRL_START as usize] & CRB_START_INVOKE) == 0)
|
||||||
|
&& self.get_active_locality() == locality
|
||||||
|
{
|
||||||
|
self.regs[CRB_CTRL_START as usize] |= CRB_START_INVOKE;
|
||||||
|
|
||||||
|
self.cmd = Some(BackendCmd {
|
||||||
|
locality: locality as u8,
|
||||||
|
input: self.data_buff[0..self.data_buff_len].to_vec(),
|
||||||
|
input_len: cmp::min(self.data_buff_len, TPM_CRB_BUFFER_MAX),
|
||||||
|
output: self.data_buff.to_vec(),
|
||||||
|
output_len: TPM_CRB_BUFFER_MAX,
|
||||||
|
selftest_done: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut cmd = self.cmd.as_ref().unwrap().clone();
|
||||||
|
let output = self.emulator.deliver_request(&mut cmd).map_err(|e| {
|
||||||
|
Error::DeliverRequest(anyhow!(
|
||||||
|
"Failed to deliver tpm request. Error :{:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
});
|
||||||
|
//TODO: drop the copy here
|
||||||
|
self.data_buff.fill(0);
|
||||||
|
self.data_buff.clone_from_slice(output.unwrap().as_slice());
|
||||||
|
|
||||||
|
self.request_completed(TPM_SUCCESS as isize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CRB_LOC_CTRL => {
|
||||||
|
warn!(
|
||||||
|
"CRB_LOC_CTRL locality to write = {:?} val = {:?}",
|
||||||
|
locality, v
|
||||||
|
);
|
||||||
|
match v {
|
||||||
|
CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT => {}
|
||||||
|
CRB_LOC_CTRL_RELINQUISH => {
|
||||||
|
set_reg_field(&mut self.regs, CRB_LOC_STATE, "locAssigned", 0);
|
||||||
|
set_reg_field(&mut self.regs, CRB_LOC_STS, "Granted", 0);
|
||||||
|
}
|
||||||
|
CRB_LOC_CTRL_REQUEST_ACCESS => {
|
||||||
|
set_reg_field(&mut self.regs, CRB_LOC_STS, "Granted", 1);
|
||||||
|
set_reg_field(&mut self.regs, CRB_LOC_STS, "beenSeized", 0);
|
||||||
|
set_reg_field(&mut self.regs, CRB_LOC_STATE, "locAssigned", 1);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
error!("Invalid value to write in CRB_LOC_CTRL {:#X} ", v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
error!(
|
||||||
|
"Invalid tpm write: offset {:#X}, data length {:?}",
|
||||||
|
offset,
|
||||||
|
data.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_set_get_reg_field() {
|
||||||
|
let mut regs: [u32; TPM_CRB_R_MAX as usize] = [0; TPM_CRB_R_MAX as usize];
|
||||||
|
set_reg_field(&mut regs, CRB_INTF_ID, "RID", 0xAC);
|
||||||
|
assert_eq!(
|
||||||
|
get_reg_field(®s, CRB_INTF_ID, "RID"),
|
||||||
|
0xAC,
|
||||||
|
concat!("Test: ", stringify!(set_get_reg_field))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
102
docs/api.md
102
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,40 +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 |
|
||||||
Perform a coredump of the VM | `/vm.coredump` | `/schemas/VmCoredumpData` | N/A | The VM is paused
|
| Perform a coredump of the VM | `/vm.coredump` | `/schemas/VmCoredumpData` | 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
|
| Restore the VM from a snapshot | `/vm.restore` | `/schemas/RestoreConfig` | N/A | The VM is created but not booted |
|
||||||
Add/remove CPUs to/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 the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
|
| Add/remove memory 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 a zone | `/vm.resize-zone` | `/schemas/VmResizeZone` | N/A | The VM is booted |
|
||||||
Dump the VM information | `/vm.info` | N/A | `/schemas/VmInfo` | The VM is created
|
| Dump the VM information | `/vm.info` | N/A | `/schemas/VmInfo` | The VM is created |
|
||||||
Add VFIO PCI device to the VM | `/vm.add-device` | `/schemas/VmAddDevice` | `/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 disk device to the VM | `/vm.add-disk` | `/schemas/DiskConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
| Add disk device to the VM | `/vm.add-disk` | `/schemas/DiskConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||||
Add fs device to the VM | `/vm.add-fs` | `/schemas/FsConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
| Add fs device to the VM | `/vm.add-fs` | `/schemas/FsConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||||
Add pmem device to the VM | `/vm.add-pmem` | `/schemas/PmemConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
| Add pmem device to the VM | `/vm.add-pmem` | `/schemas/PmemConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||||
Add network device to the VM | `/vm.add-net` | `/schemas/NetConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
| Add network device to the VM | `/vm.add-net` | `/schemas/NetConfig` | `/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 userspace PCI device to the VM | `/vm.add-user-device` | `/schemas/VmAddUserDevice` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||||
Add vdpa device to the VM | `/vm.add-vdpa` | `/schemas/VdpaConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
| Add vdpa device to the VM | `/vm.add-vdpa` | `/schemas/VdpaConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||||
Add vsock device to the VM | `/vm.add-vsock` | `/schemas/VsockConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
| Add vsock device to the VM | `/vm.add-vsock` | `/schemas/VsockConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||||
Remove device from the VM | `/vm.remove-device` | `/schemas/VmRemoveDevice` | N/A | The VM is booted
|
| 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
|
| Dump the VM counters | `/vm.counters` | N/A | `/schemas/VmCounters` | The VM is booted |
|
||||||
|
|
||||||
### REST API Examples
|
### REST API Examples
|
||||||
|
|
||||||
@@ -143,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"}]
|
||||||
@@ -311,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"}]
|
||||||
|
|||||||
152
docs/arm64.md
152
docs/arm64.md
@@ -1,152 +0,0 @@
|
|||||||
# How to build and test Cloud Hypervisor on AArch64
|
|
||||||
|
|
||||||
This document introduces how to build and test Cloud Hypervisor on AArch64.
|
|
||||||
Currently, Cloud Hypervisor supports 2 methods of booting on AArch64: UEFI
|
|
||||||
booting and direct-kernel booting. The document covers both methods.
|
|
||||||
|
|
||||||
All the steps are based on Ubuntu. We use the Ubuntu cloud image for guest VM
|
|
||||||
disk.
|
|
||||||
|
|
||||||
## Hardware requirements
|
|
||||||
|
|
||||||
- AArch64 servers (recommended) or development boards equipped with the GICv3
|
|
||||||
interrupt controller.
|
|
||||||
|
|
||||||
- On development boards that have constrained RAM resources, if the creation of
|
|
||||||
a VM consumes a large portion of the free memory on the host, it may be required
|
|
||||||
to enable swap. For example, this was required on a board with 3 GB of RAM
|
|
||||||
booting a 2 GB VM at a point in time when 2.8 GB were free. Without enabling
|
|
||||||
swap the `cloud-hypervisor` process was terminated by the OOM killer. In this
|
|
||||||
situation memory was allocated for the virtual machine using memfd while the
|
|
||||||
page cache was filled, leading to a situation where the kernel could not even
|
|
||||||
drop caches. Making a small section of swap available (observably, 1 to 15 MB),
|
|
||||||
this situation can be resolved and the resulting memory footprint of
|
|
||||||
`cloud-hypervisor` is as expected.
|
|
||||||
|
|
||||||
## Getting started
|
|
||||||
|
|
||||||
We create a folder to build and run Cloud Hypervisor at `$HOME/cloud-hypervisor`
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ export CLOUDH=$HOME/cloud-hypervisor
|
|
||||||
$ mkdir $CLOUDH
|
|
||||||
```
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
You need to install some prerequisite packages to build and test Cloud Hypervisor.
|
|
||||||
|
|
||||||
### Tools
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install rust tool chain
|
|
||||||
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
||||||
# Install the tools used for building guest kernel, EDK2 and converting guest disk
|
|
||||||
$ sudo apt-get update
|
|
||||||
$ sudo apt-get install git build-essential m4 bison flex uuid-dev qemu-utils
|
|
||||||
```
|
|
||||||
|
|
||||||
### Building Cloud Hypervisor
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ pushd $CLOUDH
|
|
||||||
$ git clone https://github.com/cloud-hypervisor/cloud-hypervisor.git
|
|
||||||
$ cd cloud-hypervisor
|
|
||||||
$ cargo build
|
|
||||||
$ popd
|
|
||||||
```
|
|
||||||
|
|
||||||
### Disk image
|
|
||||||
|
|
||||||
Download the Ubuntu cloud image and convert the image type.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ pushd $CLOUDH
|
|
||||||
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-arm64.img
|
|
||||||
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-arm64.img focal-server-cloudimg-arm64.raw
|
|
||||||
$ popd
|
|
||||||
```
|
|
||||||
|
|
||||||
## UEFI booting
|
|
||||||
|
|
||||||
This part introduces how to build EDK2 firmware and boot Cloud Hypervisor with it.
|
|
||||||
|
|
||||||
### Building EDK2
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ pushd $CLOUDH
|
|
||||||
|
|
||||||
# Clone source code repos
|
|
||||||
$ git clone --depth 1 https://github.com/tianocore/edk2.git -b master
|
|
||||||
$ cd edk2
|
|
||||||
$ git submodule update --init
|
|
||||||
$ cd ..
|
|
||||||
$ git clone --depth 1 https://github.com/tianocore/edk2-platforms.git -b master
|
|
||||||
$ git clone --depth 1 https://github.com/acpica/acpica.git -b master
|
|
||||||
|
|
||||||
# Build tools
|
|
||||||
$ export PACKAGES_PATH="$PWD/edk2:$PWD/edk2-platforms"
|
|
||||||
$ export IASL_PREFIX="$PWD/acpica/generate/unix/bin/"
|
|
||||||
$ make -C acpica
|
|
||||||
$ cd edk2/
|
|
||||||
$ . edksetup.sh
|
|
||||||
$ cd ..
|
|
||||||
$ make -C edk2/BaseTools
|
|
||||||
|
|
||||||
# Build EDK2
|
|
||||||
$ build -a AARCH64 -t GCC5 -p ArmVirtPkg/ArmVirtCloudHv.dsc -b RELEASE
|
|
||||||
|
|
||||||
$ popd
|
|
||||||
```
|
|
||||||
|
|
||||||
If the build goes well, the EDK2 binary is available at
|
|
||||||
`edk2/Build/ArmVirtCloudHv-AARCH64/RELEASE_GCC5/FV/CLOUDHV_EFI.fd`.
|
|
||||||
|
|
||||||
### Booting the guest VM
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ pushd $CLOUDH
|
|
||||||
$ sudo RUST_BACKTRACE=1 $CLOUDH/cloud-hypervisor/target/debug/cloud-hypervisor \
|
|
||||||
--api-socket /tmp/cloud-hypervisor.sock \
|
|
||||||
--kernel $CLOUDH/edk2/Build/ArmVirtCloudHv-AARCH64/RELEASE_GCC5/FV/CLOUDHV_EFI.fd \
|
|
||||||
--disk path=$CLOUDH/focal-server-cloudimg-arm64.raw \
|
|
||||||
--cpus boot=4 \
|
|
||||||
--memory size=4096M \
|
|
||||||
--net tap=,mac=12:34:56:78:90:01,ip=192.168.1.1,mask=255.255.255.0 \
|
|
||||||
--serial tty \
|
|
||||||
--console off
|
|
||||||
$ popd
|
|
||||||
```
|
|
||||||
|
|
||||||
## Direct-kernel booting
|
|
||||||
|
|
||||||
Alternativelly, you can build your own kernel for guest VM. This way, UEFI is
|
|
||||||
not involved and ACPI cannot be enabled.
|
|
||||||
|
|
||||||
### Building kernel
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ pushd $CLOUDH
|
|
||||||
$ git clone --depth 1 "https://github.com/cloud-hypervisor/linux.git" -b ch-5.12
|
|
||||||
$ cd linux
|
|
||||||
$ cp $CLOUDH/cloud-hypervisor/resources/linux-config-aarch64 .config
|
|
||||||
$ make -j `nproc`
|
|
||||||
$ popd
|
|
||||||
```
|
|
||||||
|
|
||||||
### Booting the guest VM
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ pushd $CLOUDH
|
|
||||||
$ sudo $CLOUDH/cloud-hypervisor/target/debug/cloud-hypervisor \
|
|
||||||
--api-socket /tmp/cloud-hypervisor.sock \
|
|
||||||
--kernel $CLOUDH/linux/arch/arm64/boot/Image \
|
|
||||||
--disk path=focal-server-cloudimg-arm64.raw \
|
|
||||||
--cmdline "keep_bootcon console=ttyAMA0 reboot=k panic=1 root=/dev/vda1 rw" \
|
|
||||||
--cpus boot=4 \
|
|
||||||
--memory size=4096M \
|
|
||||||
--net tap=,mac=12:34:56:78:90:01,ip=192.168.1.1,mask=255.255.255.0 \
|
|
||||||
--serial tty \
|
|
||||||
--console off
|
|
||||||
$ popd
|
|
||||||
```
|
|
||||||
86
docs/building.md
Normal file
86
docs/building.md
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
- [Building Cloud Hypervisor](#building-cloud-hypervisor)
|
||||||
|
- [Preparation](#preparation)
|
||||||
|
- [Install prerequisites](#install-prerequisites)
|
||||||
|
- [Clone and build](#clone-and-build)
|
||||||
|
- [Containerized builds and tests](#containerized-builds-and-tests)
|
||||||
|
|
||||||
|
# Building Cloud Hypervisor
|
||||||
|
|
||||||
|
We recommend users use the pre-built binaries that are mentioned in the README.md file in the root of the repository. Building from source is only necessary if you wish to make modifications.
|
||||||
|
|
||||||
|
## Preparation
|
||||||
|
|
||||||
|
We create a folder to build and run `cloud-hypervisor` at `$HOME/cloud-hypervisor`
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ export CLOUDH=$HOME/cloud-hypervisor
|
||||||
|
$ mkdir $CLOUDH
|
||||||
|
```
|
||||||
|
|
||||||
|
## Install prerequisites
|
||||||
|
|
||||||
|
You need to install some prerequisite packages in order to build and test Cloud
|
||||||
|
Hypervisor. Here, all the steps are based on Ubuntu, for other Linux
|
||||||
|
distributions please replace the package manager and package name.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# Install basic packages needed. For a package list targeting for more
|
||||||
|
# functionalities for example the test, please see resources/Dockerfile.
|
||||||
|
$ sudo apt-get update
|
||||||
|
$ sudo apt install git build-essential m4 bison flex uuid-dev qemu-utils
|
||||||
|
# Install rust tool chain
|
||||||
|
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||||
|
# If you want to build statically linked binary please add musl target
|
||||||
|
$ rustup target add x86_64-unknown-linux-musl # x86-64
|
||||||
|
$ rustup target add aarch64-unknown-linux-musl # AArch64
|
||||||
|
```
|
||||||
|
|
||||||
|
## Clone and build
|
||||||
|
|
||||||
|
First you need to clone and build the Cloud Hypervisor repository:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ pushd $CLOUDH
|
||||||
|
$ git clone https://github.com/cloud-hypervisor/cloud-hypervisor.git
|
||||||
|
$ cd cloud-hypervisor
|
||||||
|
$ cargo build --release
|
||||||
|
|
||||||
|
# We need to give the cloud-hypervisor binary the NET_ADMIN capabilities for it to set TAP interfaces up on the host.
|
||||||
|
$ sudo setcap cap_net_admin+ep ./target/release/cloud-hypervisor
|
||||||
|
|
||||||
|
# If you want to build statically linked binary
|
||||||
|
$ cargo build --release --target=x86_64-unknown-linux-musl --all # x86-64
|
||||||
|
$ cargo build --release --target=aarch64-unknown-linux-musl --all # AArch64
|
||||||
|
$ popd
|
||||||
|
```
|
||||||
|
|
||||||
|
This will build a `cloud-hypervisor` binary under
|
||||||
|
`$CLOUDH/cloud-hypervisor/target/release/cloud-hypervisor`.
|
||||||
|
|
||||||
|
### Containerized builds and tests
|
||||||
|
|
||||||
|
If you want to build and test Cloud Hypervisor without having to install all the
|
||||||
|
required dependencies (The rust toolchain, cargo tools, etc), you can also use
|
||||||
|
Cloud Hypervisor's development script: `dev_cli.sh`. Please note that upon its
|
||||||
|
first invocation, this script will pull a fairly large container image.
|
||||||
|
|
||||||
|
For example, to build the Cloud Hypervisor release binary:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ pushd $CLOUDH
|
||||||
|
$ cd cloud-hypervisor
|
||||||
|
$ ./scripts/dev_cli.sh build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
With `dev_cli.sh`, one can also run the Cloud Hypervisor CI locally. This can be
|
||||||
|
very convenient for debugging CI errors without having to fully rely on the
|
||||||
|
Cloud Hypervisor CI infrastructure.
|
||||||
|
|
||||||
|
For example, to run the Cloud Hypervisor unit tests:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ ./scripts/dev_cli.sh tests --unit
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the `./scripts/dev_cli.sh --help` command to view all the supported
|
||||||
|
development script commands and their related options.
|
||||||
@@ -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
|
||||||
@@ -78,7 +88,7 @@ Ubuntu distributions.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
apt update
|
apt update
|
||||||
apt install fio iperf iperf3 socat stress
|
apt install fio iperf iperf3 socat stress cpuid
|
||||||
```
|
```
|
||||||
|
|
||||||
### Remove counterproductive packages
|
### Remove counterproductive packages
|
||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -147,3 +158,172 @@ as we might need to update the direct kernel boot command line, replacing
|
|||||||
`/dev/vda1` with the appropriate partition number.
|
`/dev/vda1` with the appropriate partition number.
|
||||||
|
|
||||||
Update all references to the previous image name to the new one.
|
Update all references to the previous image name to the new one.
|
||||||
|
|
||||||
|
## NVIDIA image for VFIO baremetal CI
|
||||||
|
|
||||||
|
Here we are going to describe how to create a cloud image that contains the
|
||||||
|
necessary NVIDIA drivers for our VFIO baremetal CI.
|
||||||
|
|
||||||
|
### Download base image
|
||||||
|
|
||||||
|
We usually start from one of the custom cloud image we have previously created
|
||||||
|
but we can use a stock cloud image as well.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://cloud-hypervisor.azureedge.net/jammy-server-cloudimg-amd64-custom-20221118-1.raw
|
||||||
|
mv jammy-server-cloudimg-amd64-custom-20221118-1.raw jammy-server-cloudimg-amd64-nvidia.raw
|
||||||
|
```
|
||||||
|
|
||||||
|
### Extend the image size
|
||||||
|
|
||||||
|
The NVIDIA drivers consume lots of space, which is why we must resize the image
|
||||||
|
before we proceed any further.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
qemu-img resize jammy-server-cloudimg-amd64-nvidia.raw 5G
|
||||||
|
```
|
||||||
|
|
||||||
|
### Resize the partition
|
||||||
|
|
||||||
|
We use `parted` for fixing the GPT after the image was resized, as well as for
|
||||||
|
resizing the `Linux` partition.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo parted jammy-server-cloudimg-amd64-nvidia.raw
|
||||||
|
|
||||||
|
(parted) print
|
||||||
|
Warning: Not all of the space available to jammy-server-cloudimg-amd64-nvidia.raw
|
||||||
|
appears to be used, you can fix the GPT to use all of the space (an extra 5873664
|
||||||
|
blocks) or continue with the current setting?
|
||||||
|
Fix/Ignore? Fix
|
||||||
|
Model: (file)
|
||||||
|
Disk jammy-server-cloudimg-amd64-nvidia.raw: 5369MB
|
||||||
|
Sector size (logical/physical): 512B/512B
|
||||||
|
Partition Table: gpt
|
||||||
|
Disk Flags:
|
||||||
|
|
||||||
|
Number Start End Size File system Name Flags
|
||||||
|
14 1049kB 5243kB 4194kB bios_grub
|
||||||
|
15 5243kB 116MB 111MB fat32 boot, esp
|
||||||
|
1 116MB 2361MB 2245MB ext4
|
||||||
|
|
||||||
|
(parted) resizepart 1 5369MB
|
||||||
|
(parted) print
|
||||||
|
Model: (file)
|
||||||
|
Disk jammy-server-cloudimg-amd64-nvidia.raw: 5369MB
|
||||||
|
Sector size (logical/physical): 512B/512B
|
||||||
|
Partition Table: gpt
|
||||||
|
Disk Flags:
|
||||||
|
|
||||||
|
Number Start End Size File system Name Flags
|
||||||
|
14 1049kB 5243kB 4194kB bios_grub
|
||||||
|
15 5243kB 116MB 111MB fat32 boot, esp
|
||||||
|
1 116MB 5369MB 5252MB ext4
|
||||||
|
|
||||||
|
(parted) quit
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create a macvtap interface
|
||||||
|
|
||||||
|
Rely on the following [documentation](docs/macvtap-bridge.md) to set up a
|
||||||
|
macvtap interface to provide your VM with proper connectivity.
|
||||||
|
|
||||||
|
### Boot the image
|
||||||
|
|
||||||
|
It is particularly important to boot with a `cloud-init` disk attached to the
|
||||||
|
VM as it will automatically resize the Linux `ext4` filesystem based on the
|
||||||
|
partition that we have previously resized.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./cloud-hypervisor \
|
||||||
|
--kernel hypervisor-fw \
|
||||||
|
--disk path=focal-server-cloudimg-amd64-nvidia.raw path=/tmp/ubuntu-cloudinit.img \
|
||||||
|
--cpus boot=4 \
|
||||||
|
--memory size=4G \
|
||||||
|
--net fd=3,mac=$mac 3<>$"$tapdevice"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bring up connectivity
|
||||||
|
|
||||||
|
If your network has a DHCP server, run the following from your VM
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo dhclient
|
||||||
|
```
|
||||||
|
|
||||||
|
But if that's not the case, let's give it an IP manually (the IP addresses
|
||||||
|
depend on your actual network) and set the DNS server IP address as well.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo ip addr add 192.168.2.10/24 dev ens4
|
||||||
|
sudo ip link set up dev ens4
|
||||||
|
sudo ip route add default via 192.168.2.1
|
||||||
|
sudo resolvectl dns ens4 8.8.8.8
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Check connectivity and update the image
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install NVIDIA drivers
|
||||||
|
|
||||||
|
The following steps and commands are referenced from the
|
||||||
|
[NVIDIA official documentation](https://docs.nvidia.com/datacenter/tesla/tesla-installation-notes/index.html#ubuntu-lts)
|
||||||
|
about Tesla compute cards.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
distribution=$(. /etc/os-release;echo $ID$VERSION_ID | sed -e 's/\.//g')
|
||||||
|
wget https://developer.download.nvidia.com/compute/cuda/repos/$distribution/x86_64/cuda-keyring_1.0-1_all.deb
|
||||||
|
sudo dpkg -i cuda-keyring_1.0-1_all.deb
|
||||||
|
sudo apt-key del 7fa2af80
|
||||||
|
sudo apt update
|
||||||
|
sudo apt -y install cuda-drivers
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check the `nvidia-smi` tool
|
||||||
|
|
||||||
|
Quickly validate that you can find and run the `nvidia-smi` command from your
|
||||||
|
VM. At this point it should fail given no NVIDIA card has been passed through
|
||||||
|
the VM, therefore no NVIDIA driver is loaded.
|
||||||
|
|
||||||
|
### Workaround LA57 reboot issue
|
||||||
|
|
||||||
|
Add `reboot=a` to `GRUB_CMDLINE_LINUX` in `etc/default/grub` so that the VM
|
||||||
|
will be booted with the ACPI reboot type. This resolves a reboot issue when
|
||||||
|
running on 5-level paging systems.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo vim /etc/default/grub
|
||||||
|
sudo update-grub
|
||||||
|
sudo reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
### Remove previous logins
|
||||||
|
|
||||||
|
Since our integration tests rely on past logins to count the number of reboots,
|
||||||
|
we must ensure to clear the list.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
>/var/log/lastlog
|
||||||
|
>/var/log/wtmp
|
||||||
|
>/var/log/btmp
|
||||||
|
```
|
||||||
|
|
||||||
|
### Clear history
|
||||||
|
|
||||||
|
```
|
||||||
|
history -c
|
||||||
|
rm /home/cloud/.bash_history
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reset cloud-init
|
||||||
|
|
||||||
|
This is mandatory as we want `cloud-init` provisioning to work again when a new
|
||||||
|
VM will be booted with this image.
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo cloud-init clean
|
||||||
|
```
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -20,12 +20,13 @@ struct MemoryConfig {
|
|||||||
hugepages: bool,
|
hugepages: bool,
|
||||||
hugepage_size: Option<u64>,
|
hugepage_size: Option<u64>,
|
||||||
prefault: bool,
|
prefault: bool,
|
||||||
|
thp: bool
|
||||||
zones: Option<Vec<MemoryZoneConfig>>,
|
zones: Option<Vec<MemoryZoneConfig>>,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
--memory <memory> Memory parameters "size=<guest_memory_size>,mergeable=on|off,shared=on|off,hugepages=on|off,hugepage_size=<hugepage_size>,hotplug_method=acpi|virtio-mem,hotplug_size=<hotpluggable_memory_size>,hotplugged_size=<hotplugged_memory_size>,prefault=on|off" [default: size=512M]
|
--memory <memory> Memory parameters "size=<guest_memory_size>,mergeable=on|off,shared=on|off,hugepages=on|off,hugepage_size=<hugepage_size>,hotplug_method=acpi|virtio-mem,hotplug_size=<hotpluggable_memory_size>,hotplugged_size=<hotplugged_memory_size>,prefault=on|off,thp=on|off" [default: size=512M,thp=on]
|
||||||
```
|
```
|
||||||
|
|
||||||
### `size`
|
### `size`
|
||||||
@@ -117,6 +118,9 @@ needing access to the guest RAM content.
|
|||||||
By default this option is turned off, which results in performing `mmap(2)`
|
By default this option is turned off, which results in performing `mmap(2)`
|
||||||
with `MAP_PRIVATE` flag.
|
with `MAP_PRIVATE` flag.
|
||||||
|
|
||||||
|
If `hugepages=on` then the value of this field is ignored as huge pages always
|
||||||
|
requires `MAP_SHARED`.
|
||||||
|
|
||||||
_Example_
|
_Example_
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -140,6 +144,9 @@ behaviour, e.g. error with `ReadKernelImage` is common. If there is a strange
|
|||||||
error with `hugepages` enabled, just disable it or check whether there are enough
|
error with `hugepages` enabled, just disable it or check whether there are enough
|
||||||
huge pages.
|
huge pages.
|
||||||
|
|
||||||
|
If `hugepages=on` then the value of `shared` is ignored as huge pages always
|
||||||
|
requires `MAP_SHARED`.
|
||||||
|
|
||||||
By default this option is turned off.
|
By default this option is turned off.
|
||||||
|
|
||||||
_Example_
|
_Example_
|
||||||
@@ -171,6 +178,25 @@ _Example_
|
|||||||
--memory size=1G,prefault=on
|
--memory size=1G,prefault=on
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `thp`
|
||||||
|
|
||||||
|
Specifies if private anonymous memory for the guest (i.e. `shared=off` and no
|
||||||
|
backing file) should be labelled `MADV_HUGEPAGE` with `madvise(2)` indicating
|
||||||
|
to the kernel that this memory may be backed with huge pages transparently.
|
||||||
|
|
||||||
|
The use of transparent huge pages can improve the performance of the guest as
|
||||||
|
there will fewer virtualisation related page faults. Unlike using
|
||||||
|
`hugepages=on` a specific number of huge pages do not need to be allocated by
|
||||||
|
the kernel.
|
||||||
|
|
||||||
|
By default this option is turned on.
|
||||||
|
|
||||||
|
_Example_
|
||||||
|
|
||||||
|
```
|
||||||
|
--memory size=1G,thp=on
|
||||||
|
```
|
||||||
|
|
||||||
## Advanced Parameters
|
## Advanced Parameters
|
||||||
|
|
||||||
`MemoryZoneConfig` or what is known as `--memory-zone` from the CLI perspective
|
`MemoryZoneConfig` or what is known as `--memory-zone` from the CLI perspective
|
||||||
@@ -271,6 +297,9 @@ other processes running on the host. One can use this option when running
|
|||||||
vhost-user devices as part of the VM device model, as they will be driven
|
vhost-user devices as part of the VM device model, as they will be driven
|
||||||
by standalone daemons needing access to the guest RAM content.
|
by standalone daemons needing access to the guest RAM content.
|
||||||
|
|
||||||
|
If `hugepages=on` then the value of this field is ignored as huge pages always
|
||||||
|
requires `MAP_SHARED`.
|
||||||
|
|
||||||
By default this option is turned off, which result in performing `mmap(2)`
|
By default this option is turned off, which result in performing `mmap(2)`
|
||||||
with `MAP_PRIVATE` flag.
|
with `MAP_PRIVATE` flag.
|
||||||
|
|
||||||
@@ -298,6 +327,9 @@ behaviour, e.g. error with `ReadKernelImage` is common. If there is a strange
|
|||||||
error with `hugepages` enabled, just disable it or check whether there are enough
|
error with `hugepages` enabled, just disable it or check whether there are enough
|
||||||
huge pages.
|
huge pages.
|
||||||
|
|
||||||
|
If `hugepages=on` then the value of `shared` is ignored as huge pages always
|
||||||
|
requires `MAP_SHARED`.
|
||||||
|
|
||||||
By default this option is turned off.
|
By default this option is turned off.
|
||||||
|
|
||||||
_Example_
|
_Example_
|
||||||
|
|||||||
@@ -64,5 +64,5 @@ it will log every system call issued by the process. It is important to use
|
|||||||
`-f` option in order to trace each and every thread belonging to the process.
|
`-f` option in order to trace each and every thread belonging to the process.
|
||||||
|
|
||||||
```
|
```
|
||||||
strace -f ./cloud-hypervisor ...
|
strace --decode-pids=comm -f ./cloud-hypervisor ...
|
||||||
```
|
```
|
||||||
|
|||||||
122
docs/tpm.md
Normal file
122
docs/tpm.md
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
# TPM
|
||||||
|
Tpm in Cloud-Hypervisor is emulated using `swtpm` as the backend. [swtpm](https://github.com/stefanberger/swtpm) is the link to swtpm project.
|
||||||
|
|
||||||
|
Current implementation only supports TPM `2.0` version. At the moment only
|
||||||
|
`CRB Interface` is implemented. This interface is described in
|
||||||
|
[TCG PC Client Platform TPM Profile Specification for TPM 2.0, Revision 01.05 v4](https://trustedcomputinggroup.org/wp-content/uploads/PC-Client-Specific-Platform-TPM-Profile-for-TPM-2p0-v1p05p_r14_pub.pdf).
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
`--tpm`, an optional argument, can be passed to enable tpm device.
|
||||||
|
This argument takes an UNIX domain Socket as a `socket` value.
|
||||||
|
|
||||||
|
_Example_
|
||||||
|
|
||||||
|
An Example invocation with `--tpm` argument:
|
||||||
|
|
||||||
|
```
|
||||||
|
./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||||
|
--kernel ./hypervisor-fw \
|
||||||
|
--disk path=focal-server-cloudimg-amd64.raw \
|
||||||
|
--cpus boot=4 \
|
||||||
|
--memory size=1024M \
|
||||||
|
--net "tap=,mac=,ip=,mask=" \
|
||||||
|
--tpm socket="/var/run/swtpm.socket"
|
||||||
|
```
|
||||||
|
|
||||||
|
## swtpm
|
||||||
|
Before invoking cloud-hypervisor with `--tpm` argument, a `swtpm`
|
||||||
|
process should be started to listen at the input socket. Below is an
|
||||||
|
example invocation of swtpm process.
|
||||||
|
|
||||||
|
```
|
||||||
|
swtpm socket --tpmstate dir=/var/run/swtpm \
|
||||||
|
--ctrl type=unixio,path="/var/run/swtpm.socket" \
|
||||||
|
--flags startup-clear \
|
||||||
|
--tpm2
|
||||||
|
```
|
||||||
|
|
||||||
|
## Guest
|
||||||
|
After starting a guest with the above commands, ensure below listed modules are
|
||||||
|
loaded in the guest:
|
||||||
|
|
||||||
|
```
|
||||||
|
# lsmod | grep tpm
|
||||||
|
tpm_crb 20480 0
|
||||||
|
tpm 81920 1 tpm_crb
|
||||||
|
```
|
||||||
|
|
||||||
|
Below is the IO Memory map configured in the guest:
|
||||||
|
|
||||||
|
```
|
||||||
|
# cat /proc/iomem | grep MSFT
|
||||||
|
fed40000-fed40fff : MSFT0101:00
|
||||||
|
fed40000-fed40fff : MSFT0101:00
|
||||||
|
```
|
||||||
|
Below are the devices created in the guest:
|
||||||
|
|
||||||
|
```
|
||||||
|
# ls /dev/tpm*
|
||||||
|
/dev/tpm0 /dev/tpmrm0
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Inside the guest install `tpm2-tools` package. This package provides some
|
||||||
|
commands to run against TPM that supports 2.0 version.
|
||||||
|
|
||||||
|
_Examples_
|
||||||
|
```
|
||||||
|
// Run Self Test
|
||||||
|
# tpm2_selftest -f
|
||||||
|
# echo $?
|
||||||
|
0
|
||||||
|
|
||||||
|
|
||||||
|
# echo "hello" > input.txt
|
||||||
|
// this command generates hash of the input file using all the algos supported by TPM
|
||||||
|
|
||||||
|
# tpm2_pcrevent input.txt
|
||||||
|
sha1: f572d396fae9206628714fb2ce00f72e94f2258f
|
||||||
|
sha256: 5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03
|
||||||
|
sha384: 1d0f284efe3edea4b9ca3bd514fa134b17eae361ccc7a1eefeff801b9bd6604e01f21f6bf249ef030599f0c
|
||||||
|
218f2ba8c
|
||||||
|
sha512: e7c22b994c59d9cf2b48e549b1e24666636045930d3da7c1acb299d1c3b7f931f94aae41edda2c2b207a36e
|
||||||
|
10f8bcb8d45223e54878f5b316e7ce3b6bc019629
|
||||||
|
|
||||||
|
// verify one of the hashes
|
||||||
|
# sha256sum input.txt
|
||||||
|
5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03 input.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bundled Functional Test
|
||||||
|
|
||||||
|
Build time dependencies for `tpm2-tss` are captured in [INSTALL](https://github.com/tpm2-software/tpm2-tss/blob/master/INSTALL.md).
|
||||||
|
|
||||||
|
```
|
||||||
|
# git clone https://github.com/tpm2-software/tpm2-tss.git
|
||||||
|
# cd tpm2-tss
|
||||||
|
# ./configure --enable-integration --with-devicetests="mandatory,optional" --with-device=/dev/tpm0
|
||||||
|
# sudo make check-device
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
============================================================================
|
||||||
|
Testsuite summary for tpm2-tss 3.2.0-74-ge03617d9
|
||||||
|
============================================================================
|
||||||
|
# TOTAL: 154
|
||||||
|
# PASS: 88
|
||||||
|
# SKIP: 7
|
||||||
|
# XFAIL: 0
|
||||||
|
# FAIL: 59
|
||||||
|
# XPASS: 0
|
||||||
|
# ERROR: 0
|
||||||
|
============================================================================
|
||||||
|
See ./test-suite.log
|
||||||
|
Please report to https://github.com/tpm2-software/tpm2-tss/issues
|
||||||
|
============================================================================
|
||||||
|
```
|
||||||
|
The same set of failures are noticed while running these tests on `Qemu` with
|
||||||
|
its TPM implementation.
|
||||||
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.
|
||||||
|
|
||||||
37
docs/uefi.md
37
docs/uefi.md
@@ -2,17 +2,19 @@
|
|||||||
|
|
||||||
Cloud Hypervisor supports UEFI boot through the utilization of the EDK II based UEFI firmware.
|
Cloud Hypervisor supports UEFI boot through the utilization of the EDK II based UEFI firmware.
|
||||||
|
|
||||||
## Building UEFI Firmware
|
## Building UEFI Firmware for x86-64
|
||||||
|
|
||||||
To avoid any unnecessary issues, it is recommended to use Ubuntu 18.04 and its default toolset. Any other compatible Linux distribution is otherwise suitable, however it is suggested to use a temporary Docker container with Ubuntu 18.04 for a quick build on an existing Linux machine.
|
To avoid any unnecessary issues, it is recommended to use Ubuntu 18.04 and its default toolset. Any other compatible Linux distribution is otherwise suitable, however it is suggested to use a temporary Docker container with Ubuntu 18.04 for a quick build on an existing Linux machine.
|
||||||
|
|
||||||
|
Please note that nasm-2.15 is required for the build to succeed.
|
||||||
|
|
||||||
The commands below will compile an OVMF firmware suitable for Cloud Hypervisor.
|
The commands below will compile an OVMF firmware suitable for Cloud Hypervisor.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install uuid-dev nasm iasl build-essential python3-distutils git
|
sudo apt-get install uuid-dev nasm iasl build-essential python3-distutils git
|
||||||
|
|
||||||
git clone https://github.com/cloud-hypervisor/edk2 -b ch
|
git clone https://github.com/tianocore/edk2
|
||||||
cd edk2
|
cd edk2
|
||||||
. edksetup.sh
|
. edksetup.sh
|
||||||
git submodule update --init
|
git submodule update --init
|
||||||
@@ -27,11 +29,40 @@ build
|
|||||||
|
|
||||||
After the successful build, the resulting firmware binaries are available under `Build/CloudHvX64/DEBUG_GCC5/FV` underneath the edk2 checkout.
|
After the successful build, the resulting firmware binaries are available under `Build/CloudHvX64/DEBUG_GCC5/FV` underneath the edk2 checkout.
|
||||||
|
|
||||||
|
## Building UEFI Firmware for AArch64
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# On an AArch64 machine:
|
||||||
|
$ sudo apt-get update
|
||||||
|
$ sudo apt-get install uuid-dev nasm iasl build-essential python3-distutils git
|
||||||
|
$ git clone --depth 1 https://github.com/tianocore/edk2.git -b master
|
||||||
|
$ cd edk2
|
||||||
|
$ git submodule update --init
|
||||||
|
$ cd ..
|
||||||
|
$ git clone --depth 1 https://github.com/tianocore/edk2-platforms.git -b master
|
||||||
|
$ git clone --depth 1 https://github.com/acpica/acpica.git -b master
|
||||||
|
|
||||||
|
# Build tools
|
||||||
|
$ export PACKAGES_PATH="$PWD/edk2:$PWD/edk2-platforms"
|
||||||
|
$ export IASL_PREFIX="$PWD/acpica/generate/unix/bin/"
|
||||||
|
$ make -C acpica
|
||||||
|
$ cd edk2/
|
||||||
|
$ . edksetup.sh
|
||||||
|
$ cd ..
|
||||||
|
$ make -C edk2/BaseTools
|
||||||
|
|
||||||
|
# Build EDK2
|
||||||
|
$ build -a AARCH64 -t GCC5 -p ArmVirtPkg/ArmVirtCloudHv.dsc -b RELEASE
|
||||||
|
```
|
||||||
|
|
||||||
|
If the build goes well, the EDK2 binary is available at
|
||||||
|
`edk2/Build/ArmVirtCloudHv-AARCH64/RELEASE_GCC5/FV/CLOUDHV_EFI.fd`.
|
||||||
|
|
||||||
## Using OVMF Binaries
|
## Using OVMF Binaries
|
||||||
|
|
||||||
Any UEFI capable image can be booted using the Cloud Hypervisor specific firmware. Windows guests under Cloud Hypervisor only support UEFI boot, therefore OVMF is mandatory there.
|
Any UEFI capable image can be booted using the Cloud Hypervisor specific firmware. Windows guests under Cloud Hypervisor only support UEFI boot, therefore OVMF is mandatory there.
|
||||||
|
|
||||||
To make Cloud Hypervisor use UEFI boot, pass the `CLOUDHV.fd` file path as an argument to the `--kernel` option. The firmware file will be opened in read only mode.
|
To make Cloud Hypervisor use UEFI boot, pass the `CLOUDHV.fd` (for x86-64) / `CLOUDHV_EFI.fd` (for AArch64) file path as an argument to the `--kernel` option. The firmware file will be opened in read only mode.
|
||||||
|
|
||||||
# Links
|
# Links
|
||||||
|
|
||||||
|
|||||||
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)
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ See also the [links](#Links) section for a more extended SAC documentation.
|
|||||||
|
|
||||||
## Network
|
## Network
|
||||||
|
|
||||||
This section illustrates the Windows specific corner points for the VM network configuration. For the extended networking guide, including bridging for multiple VMs, follow [networking.md](networking.md).
|
This section illustrates the Windows specific aspects of the VM network configuration.
|
||||||
|
|
||||||
### Basic Networking
|
### Basic Networking
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ authors = ["The Cloud Hypervisor Authors"]
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "0.2.126"
|
libc = "0.2.139"
|
||||||
serde = { version = "1.0.138", features = ["rc", "derive"] }
|
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||||
serde_json = "1.0.82"
|
serde_json = "1.0.89"
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ static mut MONITOR: Option<(File, Instant)> = None;
|
|||||||
/// This function must only be called once from the main process before any threads
|
/// This function must only be called once from the main process before any threads
|
||||||
/// are created to avoid race conditions
|
/// are created to avoid race conditions
|
||||||
pub fn set_monitor(file: File) -> Result<(), std::io::Error> {
|
pub fn set_monitor(file: File) -> Result<(), std::io::Error> {
|
||||||
|
// SAFETY: there is only one caller of this function, so MONITOR is written to only once
|
||||||
assert!(unsafe { MONITOR.is_none() });
|
assert!(unsafe { MONITOR.is_none() });
|
||||||
let fd = file.as_raw_fd();
|
let fd = file.as_raw_fd();
|
||||||
|
// SAFETY: FFI call to configure the fd
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
let mut flags = libc::fcntl(fd, libc::F_GETFL);
|
let mut flags = libc::fcntl(fd, libc::F_GETFL);
|
||||||
flags |= libc::O_NONBLOCK;
|
flags |= libc::O_NONBLOCK;
|
||||||
@@ -26,6 +28,7 @@ pub fn set_monitor(file: File) -> Result<(), std::io::Error> {
|
|||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return Err(std::io::Error::last_os_error());
|
return Err(std::io::Error::last_os_error());
|
||||||
}
|
}
|
||||||
|
// SAFETY: MONITOR is None. Nobody else can hold a reference to it.
|
||||||
unsafe {
|
unsafe {
|
||||||
MONITOR = Some((file, Instant::now()));
|
MONITOR = Some((file, Instant::now()));
|
||||||
};
|
};
|
||||||
@@ -41,6 +44,7 @@ struct Event<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn event_log(source: &str, event: &str, properties: Option<&HashMap<Cow<str>, Cow<str>>>) {
|
pub fn event_log(source: &str, event: &str, properties: Option<&HashMap<Cow<str>, Cow<str>>>) {
|
||||||
|
// SAFETY: MONITOR is always in a valid state (None or Some).
|
||||||
if let Some((file, start)) = unsafe { MONITOR.as_ref() } {
|
if let Some((file, start)) = unsafe { MONITOR.as_ref() } {
|
||||||
let e = Event {
|
let e = Event {
|
||||||
timestamp: start.elapsed(),
|
timestamp: start.elapsed(),
|
||||||
|
|||||||
549
fuzz/Cargo.lock
generated
549
fuzz/Cargo.lock
generated
@@ -11,9 +11,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.58"
|
version = "1.0.68"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
|
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "api_client"
|
name = "api_client"
|
||||||
@@ -24,21 +24,20 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arbitrary"
|
name = "arbitrary"
|
||||||
version = "1.1.3"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a7924531f38b1970ff630f03eb20a2fde69db5c590c93b0f3482e95dcc5fd60"
|
checksum = "29d47fbf90d5149a107494b15a7dc8d69b351be2db3bb9691740e88ec17fd880"
|
||||||
|
|
||||||
[[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",
|
||||||
@@ -48,6 +47,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"uuid",
|
||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
"vm-fdt",
|
"vm-fdt",
|
||||||
@@ -56,23 +56,6 @@ dependencies = [
|
|||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "atty"
|
|
||||||
version = "0.2.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
|
||||||
dependencies = [
|
|
||||||
"hermit-abi",
|
|
||||||
"libc",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bincode"
|
name = "bincode"
|
||||||
version = "1.3.3"
|
version = "1.3.3"
|
||||||
@@ -96,6 +79,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"qcow",
|
"qcow",
|
||||||
|
"smallvec",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
@@ -115,9 +99,12 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.73"
|
version = "1.0.78"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||||
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@@ -127,33 +114,31 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "3.2.8"
|
version = "4.0.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83"
|
checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
"indexmap",
|
"is-terminal",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"strsim",
|
"strsim",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
"terminal_size",
|
"terminal_size",
|
||||||
"textwrap",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.2.4"
|
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 = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"os_str_bytes",
|
"os_str_bytes",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cloud-hypervisor"
|
name = "cloud-hypervisor"
|
||||||
version = "24.0.0"
|
version = "28.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"api_client",
|
"api_client",
|
||||||
@@ -168,6 +153,8 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tpm",
|
||||||
|
"tracer",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vmm",
|
"vmm",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
@@ -179,15 +166,23 @@ version = "0.0.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"block_util",
|
"block_util",
|
||||||
"cloud-hypervisor",
|
"cloud-hypervisor",
|
||||||
|
"devices",
|
||||||
|
"epoll",
|
||||||
"libc",
|
"libc",
|
||||||
"libfuzzer-sys",
|
"libfuzzer-sys",
|
||||||
|
"linux-loader",
|
||||||
|
"micro_http",
|
||||||
|
"net_util",
|
||||||
|
"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",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -206,6 +201,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.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"darling_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.14.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.14.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "devices"
|
name = "devices"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -215,10 +245,12 @@ dependencies = [
|
|||||||
"arch",
|
"arch",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"epoll",
|
|
||||||
"hypervisor",
|
"hypervisor",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
"phf",
|
||||||
|
"thiserror",
|
||||||
|
"tpm",
|
||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
"vm-device",
|
"vm-device",
|
||||||
@@ -237,6 +269,27 @@ 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"
|
||||||
@@ -248,32 +301,32 @@ dependencies = [
|
|||||||
|
|
||||||
[[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]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.7"
|
version = "0.2.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hashbrown"
|
|
||||||
version = "0.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.20"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c7a30908dbce072eca83216eab939d2290080e00ca71611b96a09e5cdce5f3fa"
|
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@@ -283,59 +336,86 @@ 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_json",
|
"serde_with",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"vfio-ioctls",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iced-x86"
|
name = "iced-x86"
|
||||||
version = "1.17.0"
|
version = "1.18.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "158f5204401d08f91d19176112146d75e99b3cf745092e268fa7be33e09adcec"
|
checksum = "1dd04b950d75b3498320253b17fb92745b2cc79ead8814aede2f7c1bab858bec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"static_assertions",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "ident_case"
|
||||||
version = "1.9.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 = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "io-lifetimes"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"libc",
|
||||||
"hashbrown",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-uring"
|
name = "io-uring"
|
||||||
version = "0.5.2"
|
version = "0.5.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8d75829ed9377bab6c90039fe47b9d84caceb4b5063266142e21bcce6550cda8"
|
checksum = "c9ddf18110cedc09617f5a965bdb51adb0c0fb02c791e4ab22f7a21bb25269b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "is-terminal"
|
||||||
version = "1.0.2"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"io-lifetimes",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||||
|
|
||||||
|
[[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"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.5.0-tdx#52e56d0e8ef0f6ea32fc0492e6a175b73617a49f"
|
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.6.0-tdx#7d9ffb47e5b9b1989577258800a0f57c93f1445f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
@@ -344,8 +424,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kvm-ioctls"
|
name = "kvm-ioctls"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
source = "git+https://github.com/rust-vmm/kvm-ioctls?branch=main#1e03e29cdfbb0cb108a98de7a78045a5a517f18e"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3a321cabd827642499c77e27314f388dd83a717a5ca716b86476fb947f73ae4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"kvm-bindings",
|
"kvm-bindings",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -360,15 +441,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.126"
|
version = "0.2.139"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libfuzzer-sys"
|
name = "libfuzzer-sys"
|
||||||
version = "0.4.3"
|
version = "0.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "336244aaeab6a12df46480dc585802aa743a72d66b11937844c61bbca84c991d"
|
checksum = "c8fff891139ee62800da71b7fd5b508d570b9ad95e614a53c6f453ca08366038"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arbitrary",
|
"arbitrary",
|
||||||
"cc",
|
"cc",
|
||||||
@@ -377,13 +458,19 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-loader"
|
name = "linux-loader"
|
||||||
version = "0.4.0"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a5e77493808403a6bd56a301a64ea6b9342e36ea845044bf0dfdf56fe52fa08"
|
checksum = "b9259ddbfbb52cc918f6bbc60390004ddd0228cf1d85f402009ff2b3d95de83f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.17"
|
version = "0.4.17"
|
||||||
@@ -396,7 +483,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "micro_http"
|
name = "micro_http"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#9517a300370a158a7af0996b7eebf040d171e1a4"
|
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#fbef706e28c7ca7cce8dfec7ca073f3b359879d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
@@ -420,6 +507,7 @@ dependencies = [
|
|||||||
"net_gen",
|
"net_gen",
|
||||||
"rate_limiter",
|
"rate_limiter",
|
||||||
"serde",
|
"serde",
|
||||||
|
"thiserror",
|
||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
@@ -431,9 +519,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.13.0"
|
version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "option_parser"
|
name = "option_parser"
|
||||||
@@ -441,9 +529,9 @@ version = "0.1.0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
version = "6.1.0"
|
version = "6.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
|
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pci"
|
name = "pci"
|
||||||
@@ -469,10 +557,52 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "phf"
|
||||||
version = "1.0.40"
|
version = "0.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
|
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
|
||||||
|
dependencies = [
|
||||||
|
"phf_macros",
|
||||||
|
"phf_shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_generator"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
|
||||||
|
dependencies = [
|
||||||
|
"phf_shared",
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_macros"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66"
|
||||||
|
dependencies = [
|
||||||
|
"phf_generator",
|
||||||
|
"phf_shared",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_shared"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
|
||||||
|
dependencies = [
|
||||||
|
"siphasher",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.49"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -490,13 +620,28 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.20"
|
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 = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
|
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rate_limiter"
|
name = "rate_limiter"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -508,9 +653,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "remain"
|
name = "remain"
|
||||||
version = "0.2.3"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c35270ea384ac1762895831cc8acb96f171468e52cec82ed9186f9416209fa4"
|
checksum = "5704e2cda92fd54202f05430725317ba0ea7d0c96b246ca0a92e45177127ba3b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -527,40 +672,54 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "rustix"
|
||||||
version = "1.0.10"
|
version = "0.36.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
|
checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"io-lifetimes",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||||
|
|
||||||
[[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.12"
|
version = "1.0.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
|
checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.138"
|
version = "1.0.152"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47"
|
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.138"
|
version = "1.0.152"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c"
|
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -569,15 +728,41 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.82"
|
version = "1.0.91"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
|
checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25bf4a5a814902cd1014dbccfa4d4560fb8432c779471e96e035602519f82eef"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_with_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with_macros"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1966009f3c05f095697c537312f5415d1e3ed31ce0a56942bac4c771c5c335e"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serial_buffer"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook"
|
name = "signal-hook"
|
||||||
version = "0.3.14"
|
version = "0.3.14"
|
||||||
@@ -598,10 +783,16 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_assertions"
|
name = "siphasher"
|
||||||
version = "1.1.0"
|
version = "0.3.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
@@ -611,9 +802,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.98"
|
version = "1.0.107"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
|
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -631,63 +822,77 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "terminal_size"
|
name = "terminal_size"
|
||||||
version = "0.1.17"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
|
checksum = "cb20089a8ba2b69debd491f8d2d023761cbf196e999218c591fa1e7e15a21907"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"rustix",
|
||||||
"winapi",
|
"windows-sys",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "textwrap"
|
|
||||||
version = "0.15.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
|
||||||
dependencies = [
|
|
||||||
"terminal_size",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.31"
|
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 = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.31"
|
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 = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tpm"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"byteorder",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"thiserror",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracer"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.1"
|
version = "1.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
|
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.1.2"
|
version = "1.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
|
checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "versionize"
|
name = "versionize"
|
||||||
version = "0.1.6"
|
version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7429cf68de8f091b667d27323ed323afd39584a56d533995b12ddd748e5e6ca9"
|
checksum = "d6e2495726cf917e7ba7ec8bf0f0fceab543dd38d0a4195ed6bef331e38a290f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"crc64",
|
"crc64",
|
||||||
@@ -712,17 +917,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vfio-bindings"
|
name = "vfio-bindings"
|
||||||
version = "0.3.1"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/rust-vmm/vfio?branch=main#7ef33f6759ae6d37a54cd66f749aa47c1c81509c"
|
||||||
checksum = "43449b404c488f70507dca193debd4bea361fe8089869b947adc19720e464bce"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vfio-ioctls"
|
name = "vfio-ioctls"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#4630612f2ff300e78b6d55be324fb5481aa84661"
|
source = "git+https://github.com/rust-vmm/vfio?branch=main#7ef33f6759ae6d37a54cd66f749aa47c1c81509c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"kvm-bindings",
|
"kvm-bindings",
|
||||||
@@ -739,7 +943,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",
|
||||||
@@ -762,14 +965,13 @@ dependencies = [
|
|||||||
"remain",
|
"remain",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"uuid",
|
"uuid",
|
||||||
"vmm-sys-util",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vhost"
|
name = "vhost"
|
||||||
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 = "53567fd9ab820e4f3cc156f24146882fee3c365194c3e1dea74723265f27fc88"
|
checksum = "c9b791c5b0717a0558888a4cf7240cea836f39a99cb342e12ce633dcaa078072"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -803,6 +1005,7 @@ dependencies = [
|
|||||||
"seccompiler",
|
"seccompiler",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serial_buffer",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
@@ -819,11 +1022,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "virtio-queue"
|
name = "virtio-queue"
|
||||||
version = "0.3.0"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "88f2d73c184c18f8acc32dab77fcb6e3af92d53262538d3a68aa474810d6863c"
|
checksum = "19e927d93d54c365034fd7f31a5f458a1f540de4a37c52e892670dad9692173c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
|
"virtio-bindings",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
@@ -844,7 +1048,6 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"hypervisor",
|
"hypervisor",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"vfio-ioctls",
|
"vfio-ioctls",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
@@ -853,14 +1056,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vm-fdt"
|
name = "vm-fdt"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#720e48e435b791ec6cbe8d2b229448b71dcb1ab9"
|
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#c5a99ab71b130435927d19b50c85fcd5ce904a8c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vm-memory"
|
name = "vm-memory"
|
||||||
version = "0.8.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 = "767ed8aaebbff902e02e6d3749dc2baef55e46565f8a6414a065e5baee4b4a81"
|
checksum = "688a70366615b45575a424d9c665561c1b5ab2224d494f706b6a6812911a827c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -885,7 +1088,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",
|
||||||
]
|
]
|
||||||
@@ -917,8 +1119,10 @@ dependencies = [
|
|||||||
"seccompiler",
|
"seccompiler",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serial_buffer",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tracer",
|
||||||
"uuid",
|
"uuid",
|
||||||
"versionize",
|
"versionize",
|
||||||
"versionize_derive",
|
"versionize_derive",
|
||||||
@@ -937,9 +1141,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vmm-sys-util"
|
name = "vmm-sys-util"
|
||||||
version = "0.9.0"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "733537bded03aaa93543f785ae997727b30d1d9f4a03b7861d23290474242e11"
|
checksum = "cc06a16ee8ebf0d9269aed304030b0d20a866b8b3dd3d4ce532596ac567a0d24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -983,3 +1187,60 @@ 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.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||||
|
|||||||
105
fuzz/Cargo.toml
105
fuzz/Cargo.toml
@@ -10,23 +10,30 @@ cargo-fuzz = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
block_util = { path = "../block_util" }
|
block_util = { path = "../block_util" }
|
||||||
libc = "0.2.126"
|
devices = { path = "../devices" }
|
||||||
libfuzzer-sys = "0.4.3"
|
epoll = "4.3.1"
|
||||||
|
libc = "0.2.138"
|
||||||
|
libfuzzer-sys = "0.4.5"
|
||||||
|
linux-loader = { version = "0.8.1", features = ["elf", "bzimage", "pe"] }
|
||||||
|
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||||
|
net_util = { path = "../net_util" }
|
||||||
|
once_cell = "1.17.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.4.0"
|
virtio-queue = "0.7.0"
|
||||||
vmm-sys-util = "0.9.0"
|
vmm = { path = "../vmm" }
|
||||||
|
vmm-sys-util = "0.11.0"
|
||||||
|
vm-memory = "0.10.0"
|
||||||
|
vm-device = { path = "../vm-device" }
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
vm-memory = "0.8.0"
|
|
||||||
|
|
||||||
[dependencies.cloud-hypervisor]
|
[dependencies.cloud-hypervisor]
|
||||||
path = ".."
|
path = ".."
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0-tdx" }
|
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx" }
|
||||||
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "main" }
|
|
||||||
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
|
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
|
||||||
|
|
||||||
# Prevent this from interfering with workspaces
|
# Prevent this from interfering with workspaces
|
||||||
@@ -34,8 +41,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 +52,86 @@ 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 = "console"
|
||||||
|
path = "fuzz_targets/console.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "http_api"
|
||||||
|
path = "fuzz_targets/http_api.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "iommu"
|
||||||
|
path = "fuzz_targets/iommu.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "linux_loader"
|
||||||
|
path = "fuzz_targets/linux_loader.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "linux_loader_cmdline"
|
||||||
|
path = "fuzz_targets/linux_loader_cmdline.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "mem"
|
||||||
|
path = "fuzz_targets/mem.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "net"
|
||||||
|
path = "fuzz_targets/net.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
|
||||||
|
|||||||
153
fuzz/fuzz_targets/balloon.rs
Normal file
153
fuzz/fuzz_targets/balloon.rs
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
// 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 = 512 * 1024;
|
||||||
|
const BALLOON_SIZE: u64 = 512 * 1024;
|
||||||
|
// Number of queues
|
||||||
|
const QUEUE_NUM: usize = 3;
|
||||||
|
// Max entries in the queue.
|
||||||
|
const QUEUE_SIZE: u16 = 64;
|
||||||
|
// Descriptor table alignment
|
||||||
|
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||||
|
// Avalable ring alignment
|
||||||
|
const AVAIL_RING_ALIGN_SIZE: u64 = 2;
|
||||||
|
// Used ring alignment
|
||||||
|
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||||
|
// Descriptor table size
|
||||||
|
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
|
||||||
|
// Available ring size
|
||||||
|
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
||||||
|
// Used ring size
|
||||||
|
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
|
||||||
|
|
||||||
|
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(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.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,
|
||||||
@@ -103,27 +60,38 @@ fuzz_target!(|bytes| {
|
|||||||
SeccompAction::Allow,
|
SeccompAction::Allow,
|
||||||
None,
|
None,
|
||||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.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 +105,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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
194
fuzz/fuzz_targets/console.rs
Normal file
194
fuzz/fuzz_targets/console.rs
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
// Copyright © 2022 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use seccompiler::SeccompAction;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||||
|
use virtio_queue::{Queue, QueueT};
|
||||||
|
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||||
|
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||||
|
|
||||||
|
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||||
|
|
||||||
|
macro_rules! align {
|
||||||
|
($n:expr, $align:expr) => {{
|
||||||
|
(($n + $align - 1) / $align) * $align
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
const CONSOLE_INPUT_SIZE: usize = 128;
|
||||||
|
const QUEUE_DATA_SIZE: usize = 4;
|
||||||
|
const MEM_SIZE: usize = 32 * 1024 * 1024;
|
||||||
|
// Guest memory gap
|
||||||
|
const GUEST_MEM_GAP: u64 = 1 * 1024 * 1024;
|
||||||
|
// Guest physical address for the first virt queue
|
||||||
|
const BASE_VIRT_QUEUE_ADDR: u64 = MEM_SIZE as u64 + GUEST_MEM_GAP;
|
||||||
|
// Number of queues
|
||||||
|
const QUEUE_NUM: usize = 2;
|
||||||
|
// Max entries in the queue.
|
||||||
|
const QUEUE_SIZE: u16 = 256;
|
||||||
|
// Descriptor table alignment
|
||||||
|
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||||
|
// Used ring alignment
|
||||||
|
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||||
|
// Descriptor table size
|
||||||
|
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
|
||||||
|
// Available ring size
|
||||||
|
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
||||||
|
// Padding size before used ring
|
||||||
|
const PADDING_SIZE: u64 = align!(AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE) - AVAIL_RING_SIZE;
|
||||||
|
// Used ring size
|
||||||
|
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
|
||||||
|
// Virtio-queue size in bytes
|
||||||
|
const QUEUE_BYTES_SIZE: usize = align!(
|
||||||
|
DESC_TABLE_SIZE + AVAIL_RING_SIZE + PADDING_SIZE + USED_RING_SIZE,
|
||||||
|
DESC_TABLE_ALIGN_SIZE
|
||||||
|
) as usize;
|
||||||
|
|
||||||
|
fuzz_target!(|bytes| {
|
||||||
|
if bytes.len() < (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + CONSOLE_INPUT_SIZE
|
||||||
|
|| bytes.len()
|
||||||
|
> (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + CONSOLE_INPUT_SIZE + MEM_SIZE
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (pipe_rx, mut pipe_tx) = create_pipe().unwrap();
|
||||||
|
let output = unsafe {
|
||||||
|
File::from_raw_fd(
|
||||||
|
memfd_create(&std::ffi::CString::new("fuzz_console_output").unwrap()).unwrap(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let endpoint = virtio_devices::Endpoint::FilePair(output, pipe_rx);
|
||||||
|
|
||||||
|
let (mut console, _) = virtio_devices::Console::new(
|
||||||
|
"fuzzer_console".to_owned(),
|
||||||
|
endpoint,
|
||||||
|
None, // resize_pipe
|
||||||
|
false, // iommu
|
||||||
|
SeccompAction::Allow,
|
||||||
|
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let console_input_bytes = &bytes[..CONSOLE_INPUT_SIZE];
|
||||||
|
let queue_data = &bytes[CONSOLE_INPUT_SIZE..CONSOLE_INPUT_SIZE + QUEUE_DATA_SIZE * QUEUE_NUM];
|
||||||
|
let queue_bytes = &bytes[CONSOLE_INPUT_SIZE + QUEUE_DATA_SIZE * QUEUE_NUM
|
||||||
|
..CONSOLE_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM];
|
||||||
|
let mem_bytes = &bytes[CONSOLE_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM..];
|
||||||
|
|
||||||
|
// Setup the virt queues with the input bytes
|
||||||
|
let mut queues = setup_virt_queues(
|
||||||
|
&[
|
||||||
|
&queue_data[..QUEUE_DATA_SIZE].try_into().unwrap(),
|
||||||
|
&queue_data[QUEUE_DATA_SIZE..QUEUE_DATA_SIZE * 2]
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
],
|
||||||
|
BASE_VIRT_QUEUE_ADDR,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Setup the guest memory with the input bytes
|
||||||
|
let mem = GuestMemoryMmap::from_ranges(&[
|
||||||
|
(GuestAddress(0), MEM_SIZE),
|
||||||
|
(GuestAddress(BASE_VIRT_QUEUE_ADDR), queue_bytes.len()),
|
||||||
|
])
|
||||||
|
.unwrap();
|
||||||
|
if mem
|
||||||
|
.write_slice(queue_bytes, GuestAddress(BASE_VIRT_QUEUE_ADDR))
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||||
|
|
||||||
|
let input_queue = queues.remove(0);
|
||||||
|
let input_evt = EventFd::new(0).unwrap();
|
||||||
|
let input_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(input_evt.as_raw_fd())) };
|
||||||
|
let output_queue = queues.remove(0);
|
||||||
|
let output_evt = EventFd::new(0).unwrap();
|
||||||
|
let output_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(output_evt.as_raw_fd())) };
|
||||||
|
|
||||||
|
// Kick the 'queue' events and endpoint event before activate the console device
|
||||||
|
input_queue_evt.write(1).unwrap();
|
||||||
|
output_queue_evt.write(1).unwrap();
|
||||||
|
pipe_tx.write_all(console_input_bytes).unwrap(); // To use fuzzed data;
|
||||||
|
|
||||||
|
console
|
||||||
|
.activate(
|
||||||
|
guest_memory,
|
||||||
|
Arc::new(NoopVirtioInterrupt {}),
|
||||||
|
vec![(0, input_queue, input_evt), (1, output_queue, output_evt)],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Wait for the events to finish and console device worker thread to return
|
||||||
|
console.wait_for_epoll_threads();
|
||||||
|
});
|
||||||
|
|
||||||
|
pub struct NoopVirtioInterrupt {}
|
||||||
|
|
||||||
|
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||||
|
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_virt_queues(bytes: &[&[u8; QUEUE_DATA_SIZE]], base_addr: u64) -> Vec<Queue> {
|
||||||
|
let mut queues = Vec::new();
|
||||||
|
for (i, b) in bytes.iter().enumerate() {
|
||||||
|
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||||
|
|
||||||
|
let desc_table_addr = base_addr + (QUEUE_BYTES_SIZE * i) as u64;
|
||||||
|
let avail_ring_addr = desc_table_addr + DESC_TABLE_SIZE;
|
||||||
|
let used_ring_addr = avail_ring_addr + PADDING_SIZE + AVAIL_RING_SIZE;
|
||||||
|
q.try_set_desc_table_address(GuestAddress(desc_table_addr))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_avail_ring_address(GuestAddress(avail_ring_addr))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_used_ring_address(GuestAddress(used_ring_addr))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
q.set_next_avail(b[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
|
||||||
|
q.set_next_used(b[1] as u16);
|
||||||
|
q.set_event_idx(b[2] % 2 != 0);
|
||||||
|
q.set_size(b[3] as u16 % QUEUE_SIZE);
|
||||||
|
|
||||||
|
q.set_ready(true);
|
||||||
|
queues.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
queues
|
||||||
|
}
|
||||||
|
|
||||||
|
fn memfd_create(name: &std::ffi::CStr) -> Result<RawFd, std::io::Error> {
|
||||||
|
let res = unsafe { libc::syscall(libc::SYS_memfd_create, name.as_ptr(), 0) };
|
||||||
|
|
||||||
|
if res < 0 {
|
||||||
|
Err(std::io::Error::last_os_error())
|
||||||
|
} else {
|
||||||
|
Ok(res as RawFd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_pipe() -> Result<(File, File), std::io::Error> {
|
||||||
|
let mut pipe = [-1; 2];
|
||||||
|
if unsafe { libc::pipe2(pipe.as_mut_ptr(), libc::O_CLOEXEC) } == -1 {
|
||||||
|
return Err(std::io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
let rx = unsafe { File::from_raw_fd(pipe[0]) };
|
||||||
|
let tx = unsafe { File::from_raw_fd(pipe[1]) };
|
||||||
|
|
||||||
|
Ok((rx, tx))
|
||||||
|
}
|
||||||
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
145
fuzz/fuzz_targets/iommu.rs
Normal file
145
fuzz/fuzz_targets/iommu.rs
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
// Copyright © 2022 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use seccompiler::SeccompAction;
|
||||||
|
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||||
|
use virtio_queue::{Queue, QueueT};
|
||||||
|
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||||
|
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||||
|
|
||||||
|
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||||
|
|
||||||
|
macro_rules! align {
|
||||||
|
($n:expr, $align:expr) => {{
|
||||||
|
(($n + $align - 1) / $align) * $align
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
const QUEUE_DATA_SIZE: usize = 4;
|
||||||
|
const MEM_SIZE: usize = 32 * 1024 * 1024;
|
||||||
|
// Reuse what's being done from DeviceManager::get_msi_iova_space()
|
||||||
|
const IOVA_SPACE_SIZE: usize = (0xfeef_ffff - 0xfee0_0000) + 1;
|
||||||
|
|
||||||
|
// Max entries in the queue.
|
||||||
|
const QUEUE_SIZE: u16 = 256;
|
||||||
|
// Descriptor table alignment
|
||||||
|
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||||
|
// Avalable ring alignment
|
||||||
|
const AVAIL_RING_ALIGN_SIZE: u64 = 2;
|
||||||
|
// Used ring alignment
|
||||||
|
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||||
|
// Descriptor table size
|
||||||
|
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
|
||||||
|
// Available ring size
|
||||||
|
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
||||||
|
// Used ring size
|
||||||
|
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
|
||||||
|
|
||||||
|
// Guest memory gap
|
||||||
|
const GUEST_MEM_GAP: u64 = 1 * 1024 * 1024;
|
||||||
|
// Guest physical address for descriptor table.
|
||||||
|
const DESC_TABLE_ADDR: u64 = align!(MEM_SIZE as u64 + GUEST_MEM_GAP, DESC_TABLE_ALIGN_SIZE);
|
||||||
|
// Guest physical address for available ring
|
||||||
|
const AVAIL_RING_ADDR: u64 = align!(DESC_TABLE_ADDR + DESC_TABLE_SIZE, AVAIL_RING_ALIGN_SIZE);
|
||||||
|
// Guest physical address for used ring
|
||||||
|
const USED_RING_ADDR: u64 = align!(AVAIL_RING_ADDR + AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE);
|
||||||
|
// Virtio-queue size in bytes
|
||||||
|
const QUEUE_BYTES_SIZE: usize = (USED_RING_ADDR + USED_RING_SIZE - DESC_TABLE_ADDR) as usize;
|
||||||
|
|
||||||
|
fuzz_target!(|bytes| {
|
||||||
|
if bytes.len() < (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE)
|
||||||
|
|| bytes.len() > (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE + MEM_SIZE)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (mut iommu, _) = virtio_devices::Iommu::new(
|
||||||
|
"fuzzer_iommu".to_owned(),
|
||||||
|
SeccompAction::Allow,
|
||||||
|
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||||
|
((MEM_SIZE - IOVA_SPACE_SIZE) as u64, (MEM_SIZE - 1) as u64),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let queue_data = &bytes[..QUEUE_DATA_SIZE];
|
||||||
|
let queue_bytes = &bytes[QUEUE_DATA_SIZE..QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE];
|
||||||
|
let mem_bytes = &bytes[QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE..];
|
||||||
|
|
||||||
|
// Setup the request queue with the input bytes
|
||||||
|
let request_queue = setup_virt_queue(queue_data.try_into().unwrap());
|
||||||
|
// Given the "event queue" events are not handled from the current
|
||||||
|
// implementation of virtio-iommu, we simply setup the 'event_queue'
|
||||||
|
// with exactly the same content as the 'request_queue'.
|
||||||
|
let _event_queue = setup_virt_queue(queue_data.try_into().unwrap());
|
||||||
|
|
||||||
|
// Setup the guest memory with the input bytes
|
||||||
|
let mem = GuestMemoryMmap::from_ranges(&[
|
||||||
|
(GuestAddress(0), MEM_SIZE),
|
||||||
|
(GuestAddress(DESC_TABLE_ADDR), QUEUE_BYTES_SIZE),
|
||||||
|
])
|
||||||
|
.unwrap();
|
||||||
|
if mem
|
||||||
|
.write_slice(queue_bytes, GuestAddress(DESC_TABLE_ADDR))
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||||
|
|
||||||
|
let request_evt = EventFd::new(0).unwrap();
|
||||||
|
let request_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(request_evt.as_raw_fd())) };
|
||||||
|
let _event_evt = EventFd::new(0).unwrap();
|
||||||
|
|
||||||
|
// Kick the 'queue' event before activate the vIOMMU device
|
||||||
|
request_queue_evt.write(1).unwrap();
|
||||||
|
|
||||||
|
iommu
|
||||||
|
.activate(
|
||||||
|
guest_memory,
|
||||||
|
Arc::new(NoopVirtioInterrupt {}),
|
||||||
|
vec![
|
||||||
|
(0, request_queue, request_evt),
|
||||||
|
(0, _event_queue, _event_evt),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
// Wait for the events to finish and vIOMMU device worker thread to return
|
||||||
|
iommu.wait_for_epoll_threads();
|
||||||
|
});
|
||||||
|
|
||||||
|
pub struct NoopVirtioInterrupt {}
|
||||||
|
|
||||||
|
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||||
|
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue {
|
||||||
|
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||||
|
q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
|
||||||
|
q.set_next_used(bytes[1] as u16);
|
||||||
|
q.set_event_idx(bytes[2] % 2 != 0);
|
||||||
|
q.set_size(bytes[3] as u16 % QUEUE_SIZE);
|
||||||
|
|
||||||
|
q.try_set_desc_table_address(GuestAddress(DESC_TABLE_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_avail_ring_address(GuestAddress(AVAIL_RING_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_used_ring_address(GuestAddress(USED_RING_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.set_ready(true);
|
||||||
|
|
||||||
|
q
|
||||||
|
}
|
||||||
50
fuzz/fuzz_targets/linux_loader.rs
Normal file
50
fuzz/fuzz_targets/linux_loader.rs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright 2018 The Chromium OS Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
//
|
||||||
|
// Copyright © 2022 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use linux_loader::loader::KernelLoader;
|
||||||
|
use std::ffi;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io;
|
||||||
|
use std::io::{Seek, SeekFrom, Write};
|
||||||
|
use std::os::unix::io::{FromRawFd, RawFd};
|
||||||
|
use vm_memory::{bitmap::AtomicBitmap, GuestAddress};
|
||||||
|
|
||||||
|
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||||
|
|
||||||
|
const MEM_SIZE: usize = 256 * 1024 * 1024;
|
||||||
|
// From 'arch::x86_64::layout::HIGH_RAM_START'
|
||||||
|
const HIGH_RAM_START: GuestAddress = GuestAddress(0x100000);
|
||||||
|
|
||||||
|
fuzz_target!(|bytes| {
|
||||||
|
let shm = memfd_create(&ffi::CString::new("fuzz_load_kernel").unwrap(), 0).unwrap();
|
||||||
|
let mut kernel_file: File = unsafe { File::from_raw_fd(shm) };
|
||||||
|
kernel_file.write_all(&bytes).unwrap();
|
||||||
|
kernel_file.seek(SeekFrom::Start(0)).unwrap();
|
||||||
|
|
||||||
|
let guest_memory = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
|
||||||
|
linux_loader::loader::elf::Elf::load(
|
||||||
|
&guest_memory,
|
||||||
|
None,
|
||||||
|
&mut kernel_file,
|
||||||
|
Some(HIGH_RAM_START),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
});
|
||||||
|
|
||||||
|
fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> {
|
||||||
|
let res = unsafe { libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags) };
|
||||||
|
|
||||||
|
if res < 0 {
|
||||||
|
Err(io::Error::last_os_error())
|
||||||
|
} else {
|
||||||
|
Ok(res as RawFd)
|
||||||
|
}
|
||||||
|
}
|
||||||
34
fuzz/fuzz_targets/linux_loader_cmdline.rs
Normal file
34
fuzz/fuzz_targets/linux_loader_cmdline.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2018 The Chromium OS Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
//
|
||||||
|
// Copyright © 2022 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use vm_memory::{bitmap::AtomicBitmap, GuestAddress};
|
||||||
|
|
||||||
|
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||||
|
|
||||||
|
const MEM_SIZE: usize = 256 * 1024 * 1024;
|
||||||
|
// From 'arch::x86_64::layout::CMDLINE_START'
|
||||||
|
const CMDLINE_START: GuestAddress = GuestAddress(0x20000);
|
||||||
|
|
||||||
|
fuzz_target!(|bytes| {
|
||||||
|
let payload_config = vmm::config::PayloadConfig {
|
||||||
|
firmware: None,
|
||||||
|
kernel: None,
|
||||||
|
cmdline: Some(String::from_utf8_lossy(&bytes).to_string()),
|
||||||
|
initramfs: None,
|
||||||
|
};
|
||||||
|
let kernel_cmdline = match vmm::vm::Vm::generate_cmdline(&payload_config) {
|
||||||
|
Ok(cmdline) => cmdline,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
let guest_memory = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
|
||||||
|
|
||||||
|
linux_loader::loader::load_cmdline(&guest_memory, CMDLINE_START, &kernel_cmdline).ok();
|
||||||
|
});
|
||||||
179
fuzz/fuzz_targets/mem.rs
Normal file
179
fuzz/fuzz_targets/mem.rs
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
// Copyright © 2022 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use seccompiler::SeccompAction;
|
||||||
|
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use virtio_devices::{BlocksState, Mem, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||||
|
use virtio_queue::{Queue, QueueT};
|
||||||
|
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||||
|
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||||
|
|
||||||
|
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||||
|
type GuestRegionMmap = vm_memory::GuestRegionMmap<AtomicBitmap>;
|
||||||
|
|
||||||
|
macro_rules! align {
|
||||||
|
($n:expr, $align:expr) => {{
|
||||||
|
(($n + $align - 1) / $align) * $align
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
const VIRTIO_MEM_DATA_SIZE: usize = 1;
|
||||||
|
const QUEUE_DATA_SIZE: usize = 4;
|
||||||
|
// The size of the guest memory for the virtio-mem region
|
||||||
|
const MEM_SIZE: usize = 128 * 1024 * 1024;
|
||||||
|
// The start address of the virtio-mem region in the guest memory
|
||||||
|
const VIRTIO_MEM_REGION_ADDRESS: u64 = 0;
|
||||||
|
|
||||||
|
// Max entries in the queue.
|
||||||
|
const QUEUE_SIZE: u16 = 64;
|
||||||
|
// Descriptor table alignment
|
||||||
|
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||||
|
// Avalable ring alignment
|
||||||
|
const AVAIL_RING_ALIGN_SIZE: u64 = 2;
|
||||||
|
// Used ring alignment
|
||||||
|
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||||
|
// Descriptor table size
|
||||||
|
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
|
||||||
|
// Available ring size
|
||||||
|
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
||||||
|
// Used ring size
|
||||||
|
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
|
||||||
|
|
||||||
|
// Guest memory gap
|
||||||
|
const GUEST_MEM_GAP: u64 = 1 * 1024 * 1024;
|
||||||
|
// Guest physical address for descriptor table.
|
||||||
|
const DESC_TABLE_ADDR: u64 = align!(MEM_SIZE as u64 + GUEST_MEM_GAP, DESC_TABLE_ALIGN_SIZE);
|
||||||
|
// Guest physical address for available ring
|
||||||
|
const AVAIL_RING_ADDR: u64 = align!(DESC_TABLE_ADDR + DESC_TABLE_SIZE, AVAIL_RING_ALIGN_SIZE);
|
||||||
|
// Guest physical address for used ring
|
||||||
|
const USED_RING_ADDR: u64 = align!(AVAIL_RING_ADDR + AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE);
|
||||||
|
// Virtio-queue size in bytes
|
||||||
|
const QUEUE_BYTES_SIZE: usize = (USED_RING_ADDR + USED_RING_SIZE - DESC_TABLE_ADDR) as usize;
|
||||||
|
|
||||||
|
fuzz_target!(|bytes| {
|
||||||
|
if bytes.len() < VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE
|
||||||
|
|| bytes.len() > (VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE + MEM_SIZE)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let virtio_mem_data = &bytes[..VIRTIO_MEM_DATA_SIZE];
|
||||||
|
let queue_data = &bytes[VIRTIO_MEM_DATA_SIZE..VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE];
|
||||||
|
let queue_bytes = &bytes[VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE
|
||||||
|
..VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE];
|
||||||
|
let mem_bytes = &bytes[VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE..];
|
||||||
|
|
||||||
|
// Create a virtio-mem device based on the input bytes;
|
||||||
|
let (mut virtio_mem, virtio_mem_region) =
|
||||||
|
create_dummy_virtio_mem(virtio_mem_data.try_into().unwrap());
|
||||||
|
|
||||||
|
// Setup the virt queue with the input bytes
|
||||||
|
let q = setup_virt_queue(queue_data.try_into().unwrap());
|
||||||
|
|
||||||
|
// Setup the guest memory with the input bytes
|
||||||
|
let mem = GuestMemoryMmap::from_ranges(&[
|
||||||
|
(GuestAddress(DESC_TABLE_ADDR), QUEUE_BYTES_SIZE), // guest region for the virt queue
|
||||||
|
])
|
||||||
|
.unwrap();
|
||||||
|
if mem
|
||||||
|
.write_slice(queue_bytes, GuestAddress(DESC_TABLE_ADDR))
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Add the memory region for the virtio-mem device
|
||||||
|
let mem = mem.insert_region(virtio_mem_region).unwrap();
|
||||||
|
if mem
|
||||||
|
.write_slice(mem_bytes, GuestAddress(VIRTIO_MEM_REGION_ADDRESS))
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||||
|
|
||||||
|
let evt = EventFd::new(0).unwrap();
|
||||||
|
let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) };
|
||||||
|
|
||||||
|
// Kick the 'queue' event before activate the virtio-mem device
|
||||||
|
queue_evt.write(1).unwrap();
|
||||||
|
|
||||||
|
virtio_mem
|
||||||
|
.activate(
|
||||||
|
guest_memory,
|
||||||
|
Arc::new(NoopVirtioInterrupt {}),
|
||||||
|
vec![(0, q, evt)],
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
// Wait for the events to finish and virtio-mem device worker thread to return
|
||||||
|
virtio_mem.wait_for_epoll_threads();
|
||||||
|
});
|
||||||
|
|
||||||
|
pub struct NoopVirtioInterrupt {}
|
||||||
|
|
||||||
|
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||||
|
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a dummy virtio-mem device for fuzzing purpose only
|
||||||
|
fn create_dummy_virtio_mem(bytes: &[u8; VIRTIO_MEM_DATA_SIZE]) -> (Mem, Arc<GuestRegionMmap>) {
|
||||||
|
let numa_id = if bytes[0] % 2 != 0 { Some(0) } else { None };
|
||||||
|
|
||||||
|
let region = vmm::memory_manager::MemoryManager::create_ram_region(
|
||||||
|
&None,
|
||||||
|
0,
|
||||||
|
GuestAddress(VIRTIO_MEM_REGION_ADDRESS),
|
||||||
|
MEM_SIZE,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
numa_id,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let blocks_state = Arc::new(Mutex::new(BlocksState::new(region.size() as u64)));
|
||||||
|
|
||||||
|
(
|
||||||
|
Mem::new(
|
||||||
|
"fuzzer_mem".to_owned(),
|
||||||
|
®ion,
|
||||||
|
SeccompAction::Allow,
|
||||||
|
numa_id.map(|i| i as u16),
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||||
|
blocks_state.clone(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
region,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue {
|
||||||
|
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||||
|
q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
|
||||||
|
q.set_next_used(bytes[1] as u16);
|
||||||
|
q.set_event_idx(bytes[2] % 2 != 0);
|
||||||
|
q.set_size(bytes[3] as u16 % QUEUE_SIZE);
|
||||||
|
|
||||||
|
q.try_set_desc_table_address(GuestAddress(DESC_TABLE_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_avail_ring_address(GuestAddress(AVAIL_RING_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_used_ring_address(GuestAddress(USED_RING_ADDR))
|
||||||
|
.unwrap();
|
||||||
|
q.set_ready(true);
|
||||||
|
|
||||||
|
q
|
||||||
|
}
|
||||||
285
fuzz/fuzz_targets/net.rs
Normal file
285
fuzz/fuzz_targets/net.rs
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
// Copyright © 2022 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use seccompiler::SeccompAction;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
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::EpollContext;
|
||||||
|
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 TAP_INPUT_SIZE: usize = 128;
|
||||||
|
const QUEUE_DATA_SIZE: usize = 4;
|
||||||
|
const MEM_SIZE: usize = 32 * 1024 * 1024;
|
||||||
|
// Guest memory gap
|
||||||
|
const GUEST_MEM_GAP: u64 = 1 * 1024 * 1024;
|
||||||
|
// Guest physical address for the first virt queue
|
||||||
|
const BASE_VIRT_QUEUE_ADDR: u64 = MEM_SIZE as u64 + GUEST_MEM_GAP;
|
||||||
|
// Number of queues
|
||||||
|
const QUEUE_NUM: usize = 2;
|
||||||
|
// Max entries in the queue.
|
||||||
|
const QUEUE_SIZE: u16 = 256;
|
||||||
|
// Descriptor table alignment
|
||||||
|
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||||
|
// Used ring alignment
|
||||||
|
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||||
|
// Descriptor table size
|
||||||
|
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
|
||||||
|
// Available ring size
|
||||||
|
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
||||||
|
// Padding size before used ring
|
||||||
|
const PADDING_SIZE: u64 = align!(AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE) - AVAIL_RING_SIZE;
|
||||||
|
// Used ring size
|
||||||
|
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
|
||||||
|
// Virtio-queue size in bytes
|
||||||
|
const QUEUE_BYTES_SIZE: usize = align!(
|
||||||
|
DESC_TABLE_SIZE + AVAIL_RING_SIZE + PADDING_SIZE + USED_RING_SIZE,
|
||||||
|
DESC_TABLE_ALIGN_SIZE
|
||||||
|
) as usize;
|
||||||
|
|
||||||
|
fuzz_target!(|bytes| {
|
||||||
|
if bytes.len() < TAP_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM
|
||||||
|
|| bytes.len()
|
||||||
|
> TAP_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + MEM_SIZE
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (dummy_tap_frontend, dummy_tap_backend) = create_socketpair().unwrap();
|
||||||
|
let if_name = "fuzzer_tap_name".as_bytes().to_vec();
|
||||||
|
let tap = net_util::Tap::new_for_fuzzing(dummy_tap_frontend, if_name);
|
||||||
|
|
||||||
|
let mut net = virtio_devices::Net::new_with_tap(
|
||||||
|
"fuzzer_net".to_owned(),
|
||||||
|
vec![tap],
|
||||||
|
None, // guest_mac
|
||||||
|
false, // iommu
|
||||||
|
QUEUE_NUM,
|
||||||
|
QUEUE_SIZE,
|
||||||
|
SeccompAction::Allow,
|
||||||
|
None,
|
||||||
|
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||||
|
None,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let tap_input_bytes = &bytes[..TAP_INPUT_SIZE];
|
||||||
|
let queue_data = &bytes[TAP_INPUT_SIZE..TAP_INPUT_SIZE + QUEUE_DATA_SIZE * QUEUE_NUM];
|
||||||
|
let queue_bytes = &bytes[TAP_INPUT_SIZE + QUEUE_DATA_SIZE * QUEUE_NUM
|
||||||
|
..TAP_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM];
|
||||||
|
let mem_bytes = &bytes[TAP_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM..];
|
||||||
|
|
||||||
|
// Setup the virt queues with the input bytes
|
||||||
|
let mut queues = setup_virt_queues(
|
||||||
|
&[
|
||||||
|
&queue_data[..QUEUE_DATA_SIZE].try_into().unwrap(),
|
||||||
|
&queue_data[QUEUE_DATA_SIZE..QUEUE_DATA_SIZE * 2]
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
],
|
||||||
|
BASE_VIRT_QUEUE_ADDR,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Setup the guest memory with the input bytes
|
||||||
|
let mem = GuestMemoryMmap::from_ranges(&[
|
||||||
|
(GuestAddress(0), MEM_SIZE),
|
||||||
|
(GuestAddress(BASE_VIRT_QUEUE_ADDR), queue_bytes.len()),
|
||||||
|
])
|
||||||
|
.unwrap();
|
||||||
|
if mem
|
||||||
|
.write_slice(queue_bytes, GuestAddress(BASE_VIRT_QUEUE_ADDR))
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||||
|
|
||||||
|
let input_queue = queues.remove(0);
|
||||||
|
let input_evt = EventFd::new(0).unwrap();
|
||||||
|
let input_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(input_evt.as_raw_fd())) };
|
||||||
|
let output_queue = queues.remove(0);
|
||||||
|
let output_evt = EventFd::new(0).unwrap();
|
||||||
|
let output_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(output_evt.as_raw_fd())) };
|
||||||
|
|
||||||
|
// Start the thread of dummy tap backend to handle the rx and tx from the virtio-net
|
||||||
|
let exit_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
|
||||||
|
let tap_backend_thread = {
|
||||||
|
let dummy_tap_backend = dummy_tap_backend.try_clone().unwrap();
|
||||||
|
let tap_input_bytes: [u8; TAP_INPUT_SIZE] = tap_input_bytes[..].try_into().unwrap();
|
||||||
|
let exit_evt = exit_evt.try_clone().unwrap();
|
||||||
|
std::thread::Builder::new()
|
||||||
|
.name("dummy_tap_backend".to_string())
|
||||||
|
.spawn(move || {
|
||||||
|
tap_backend_stub(dummy_tap_backend, &tap_input_bytes, exit_evt);
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Kick the 'queue' events and endpoint event before activate the net device
|
||||||
|
input_queue_evt.write(1).unwrap();
|
||||||
|
output_queue_evt.write(1).unwrap();
|
||||||
|
|
||||||
|
net.activate(
|
||||||
|
guest_memory,
|
||||||
|
Arc::new(NoopVirtioInterrupt {}),
|
||||||
|
vec![(0, input_queue, input_evt), (1, output_queue, output_evt)],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Wait for the events to finish and net device worker thread to return
|
||||||
|
net.wait_for_epoll_threads();
|
||||||
|
// Terminate the thread for the dummy tap backend
|
||||||
|
exit_evt.write(1).ok();
|
||||||
|
tap_backend_thread.join().unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
pub struct NoopVirtioInterrupt {}
|
||||||
|
|
||||||
|
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||||
|
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_virt_queues(bytes: &[&[u8; QUEUE_DATA_SIZE]], base_addr: u64) -> Vec<Queue> {
|
||||||
|
let mut queues = Vec::new();
|
||||||
|
for (i, b) in bytes.iter().enumerate() {
|
||||||
|
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||||
|
|
||||||
|
let desc_table_addr = base_addr + (QUEUE_BYTES_SIZE * i) as u64;
|
||||||
|
let avail_ring_addr = desc_table_addr + DESC_TABLE_SIZE;
|
||||||
|
let used_ring_addr = avail_ring_addr + PADDING_SIZE + AVAIL_RING_SIZE;
|
||||||
|
q.try_set_desc_table_address(GuestAddress(desc_table_addr))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_avail_ring_address(GuestAddress(avail_ring_addr))
|
||||||
|
.unwrap();
|
||||||
|
q.try_set_used_ring_address(GuestAddress(used_ring_addr))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
q.set_next_avail(b[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
|
||||||
|
q.set_next_used(b[1] as u16);
|
||||||
|
q.set_event_idx(b[2] % 2 != 0);
|
||||||
|
q.set_size(b[3] as u16 % QUEUE_SIZE);
|
||||||
|
|
||||||
|
q.set_ready(true);
|
||||||
|
queues.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
queues
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_socketpair() -> Result<(File, File), std::io::Error> {
|
||||||
|
let mut fds = [-1, -1];
|
||||||
|
unsafe {
|
||||||
|
let ret = libc::socketpair(
|
||||||
|
libc::AF_UNIX,
|
||||||
|
libc::SOCK_STREAM | libc::SOCK_NONBLOCK,
|
||||||
|
0,
|
||||||
|
fds.as_mut_ptr(),
|
||||||
|
);
|
||||||
|
if ret == -1 {
|
||||||
|
return Err(std::io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let socket1 = unsafe { File::from_raw_fd(fds[0]) };
|
||||||
|
let socket2 = unsafe { File::from_raw_fd(fds[1]) };
|
||||||
|
Ok((socket1, socket2))
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EpollEvent {
|
||||||
|
Exit = 0,
|
||||||
|
Rx = 1,
|
||||||
|
Tx = 2,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u64> for EpollEvent {
|
||||||
|
fn from(v: u64) -> Self {
|
||||||
|
use EpollEvent::*;
|
||||||
|
match v {
|
||||||
|
0 => Exit,
|
||||||
|
1 => Rx,
|
||||||
|
2 => Tx,
|
||||||
|
_ => Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the rx and tx requests from the virtio-net device
|
||||||
|
fn tap_backend_stub(
|
||||||
|
mut dummy_tap: File,
|
||||||
|
tap_input_bytes: &[u8; TAP_INPUT_SIZE],
|
||||||
|
exit_evt: EventFd,
|
||||||
|
) {
|
||||||
|
let mut epoll = EpollContext::new().unwrap();
|
||||||
|
epoll
|
||||||
|
.add_event_custom(&exit_evt, EpollEvent::Exit as u64, epoll::Events::EPOLLIN)
|
||||||
|
.unwrap();
|
||||||
|
let dummy_tap_write = dummy_tap.try_clone().unwrap();
|
||||||
|
epoll
|
||||||
|
.add_event_custom(
|
||||||
|
&dummy_tap_write,
|
||||||
|
EpollEvent::Rx as u64,
|
||||||
|
epoll::Events::EPOLLOUT,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
epoll
|
||||||
|
.add_event_custom(&dummy_tap, EpollEvent::Tx as u64, epoll::Events::EPOLLIN)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let epoll_fd = epoll.as_raw_fd();
|
||||||
|
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); 3];
|
||||||
|
loop {
|
||||||
|
let 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!"),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for event in events.iter().take(num_events) {
|
||||||
|
let dispatch_event: EpollEvent = event.data.into();
|
||||||
|
match dispatch_event {
|
||||||
|
EpollEvent::Exit => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EpollEvent::Rx => {
|
||||||
|
dummy_tap.write_all(tap_input_bytes).unwrap();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
EpollEvent::Tx => {
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
dummy_tap.read_to_end(&mut buffer).ok();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("Unexpected Epoll event");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
151
fuzz/fuzz_targets/pmem.rs
Normal file
151
fuzz/fuzz_targets/pmem.rs
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
// 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(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.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
|
||||||
|
}
|
||||||
135
fuzz/fuzz_targets/rng.rs
Normal file
135
fuzz/fuzz_targets/rng.rs
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
// 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(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let queue_data = &bytes[..QUEUE_DATA_SIZE];
|
||||||
|
let queue_bytes = &bytes[QUEUE_DATA_SIZE..QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE];
|
||||||
|
let mem_bytes = &bytes[QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE..];
|
||||||
|
|
||||||
|
// Setup the 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
|
||||||
|
}
|
||||||
74
fuzz/fuzz_targets/serial.rs
Normal file
74
fuzz/fuzz_targets/serial.rs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// 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())),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
100
fuzz/fuzz_targets/watchdog.rs
Normal file
100
fuzz/fuzz_targets/watchdog.rs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// 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(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.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,24 +11,25 @@ mshv = ["mshv-ioctls", "mshv-bindings"]
|
|||||||
tdx = []
|
tdx = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.58"
|
anyhow = "1.0.68"
|
||||||
epoll = "4.3.1"
|
byteorder = "1.4.3"
|
||||||
thiserror = "1.0.31"
|
thiserror = "1.0.38"
|
||||||
libc = "0.2.126"
|
libc = "0.2.139"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
kvm-ioctls = { version = "0.11.0", optional = true }
|
kvm-ioctls = { version = "0.12.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.6.0-tdx", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||||
mshv-bindings = { git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true }
|
mshv-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.138", features = ["rc", "derive"] }
|
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||||
serde_json = "1.0.82"
|
serde_with = { version = "2.1.0", default-features = false, features = ["macros"] }
|
||||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic"] }
|
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||||
vmm-sys-util = { version = "0.9.0", features = ["with-serde"] }
|
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic"] }
|
||||||
|
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
||||||
version = "1.17.0"
|
version = "1.18.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]
|
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
env_logger = "0.9.0"
|
env_logger = "0.10.0"
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||||
|
|
||||||
use crate::{CpuState, Device, GicState, HypervisorDeviceError, HypervisorVmError};
|
use crate::{CpuState, GicState, HypervisorDeviceError, HypervisorVmError};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
/// Errors thrown while setting up the VGIC.
|
/// Errors thrown while setting up the VGIC.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -17,6 +16,18 @@ pub enum Error {
|
|||||||
}
|
}
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
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
|
/// Hypervisor agnostic interface for a virtualized GIC
|
||||||
pub trait Vgic: Send + Sync {
|
pub trait Vgic: Send + Sync {
|
||||||
/// Returns the fdt compatibility property of the device
|
/// Returns the fdt compatibility property of the device
|
||||||
@@ -40,8 +51,6 @@ pub trait Vgic: Send + Sync {
|
|||||||
/// Returns the MSI reg property of the device
|
/// Returns the MSI reg property of the device
|
||||||
fn msi_properties(&self) -> [u64; 2];
|
fn msi_properties(&self) -> [u64; 2];
|
||||||
|
|
||||||
fn set_its_device(&mut self, its_device: Option<Arc<dyn Device>>);
|
|
||||||
|
|
||||||
/// Get the values of GICR_TYPER for each vCPU.
|
/// Get the values of GICR_TYPER for each vCPU.
|
||||||
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]);
|
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]);
|
||||||
|
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ impl<T: Debug> Display for Exception<T> {
|
|||||||
self.vector,
|
self.vector,
|
||||||
self.ip,
|
self.ip,
|
||||||
self.error
|
self.error
|
||||||
.map(|e| format!(": error {:x}", e))
|
.map(|e| format!(": error {e:x}"))
|
||||||
.unwrap_or_else(|| "".to_owned()),
|
.unwrap_or_else(|| "".to_owned()),
|
||||||
self.payload
|
self.payload
|
||||||
.map(|payload| format!(": payload {:x}", payload))
|
.map(|payload| format!(": payload {payload:x}"))
|
||||||
.unwrap_or_else(|| "".to_owned())
|
.unwrap_or_else(|| "".to_owned())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ fn get_op<T: CpuStateManager>(
|
|||||||
OpKind::Immediate16 => insn.immediate16() as u64,
|
OpKind::Immediate16 => insn.immediate16() as u64,
|
||||||
OpKind::Immediate32 => insn.immediate32() as u64,
|
OpKind::Immediate32 => insn.immediate32() as u64,
|
||||||
OpKind::Immediate32to64 => insn.immediate32to64() as u64,
|
OpKind::Immediate32to64 => insn.immediate32to64() as u64,
|
||||||
OpKind::Immediate64 => insn.immediate64() as u64,
|
OpKind::Immediate64 => insn.immediate64(),
|
||||||
k => return Err(PlatformError::InvalidOperand(anyhow!("{:?}", k))),
|
k => return Err(PlatformError::InvalidOperand(anyhow!("{:?}", k))),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,10 @@
|
|||||||
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::{CR0_PE, EFER_LMA};
|
use crate::arch::x86::regs::{CR0_PE, EFER_LMA};
|
||||||
use crate::arch::x86::{segment_type_expand_down, segment_type_ro, Exception, SegmentRegisterOps};
|
use crate::arch::x86::{
|
||||||
use crate::x86_64::{SegmentRegister, SpecialRegisters, StandardRegisters};
|
segment_type_expand_down, segment_type_ro, Exception, SegmentRegister, SpecialRegisters,
|
||||||
|
StandardRegisters,
|
||||||
|
};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use iced_x86::*;
|
use iced_x86::*;
|
||||||
|
|
||||||
|
|||||||
@@ -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,145 @@ 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;
|
||||||
|
|
||||||
|
// SAFETY: plain old data type
|
||||||
|
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;
|
||||||
|
|
||||||
|
// SAFETY: plain old data type
|
||||||
|
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")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::aarch64::{RegList, Register, StandardRegisters};
|
use crate::arch::x86::{
|
||||||
|
CpuIdEntry, FpuState, LapicState, MsrEntry, SpecialRegisters, StandardRegisters,
|
||||||
|
};
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
use crate::kvm::{TdxExitDetails, TdxExitStatus};
|
use crate::kvm::{TdxExitDetails, TdxExitStatus};
|
||||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
|
||||||
use crate::x86_64::SuspendRegisters;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
use crate::x86_64::Xsave;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
use crate::x86_64::{CpuId, LapicState};
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
use crate::x86_64::{
|
|
||||||
ExtendedControlRegisters, FpuState, MsrEntries, SpecialRegisters, StandardRegisters, VcpuEvents,
|
|
||||||
};
|
|
||||||
use crate::CpuState;
|
use crate::CpuState;
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
use crate::DeviceAttr;
|
|
||||||
#[cfg(feature = "kvm")]
|
|
||||||
use crate::MpState;
|
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}")]
|
||||||
@@ -65,12 +48,12 @@ pub enum HypervisorCpuError {
|
|||||||
///
|
///
|
||||||
/// Setting floating point registers error
|
/// Setting floating point registers error
|
||||||
///
|
///
|
||||||
#[error("Failed to set special register: {0}")]
|
#[error("Failed to set floating point registers: {0}")]
|
||||||
SetFloatingPointRegs(#[source] anyhow::Error),
|
SetFloatingPointRegs(#[source] anyhow::Error),
|
||||||
///
|
///
|
||||||
/// Getting floating point register error
|
/// Getting floating point register error
|
||||||
///
|
///
|
||||||
#[error("Failed to get special register: {0}")]
|
#[error("Failed to get floating points registers: {0}")]
|
||||||
GetFloatingPointRegs(#[source] anyhow::Error),
|
GetFloatingPointRegs(#[source] anyhow::Error),
|
||||||
///
|
///
|
||||||
/// Setting Cpuid error
|
/// Setting Cpuid error
|
||||||
@@ -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,117 +324,66 @@ 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.
|
|
||||||
///
|
|
||||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
|
||||||
fn set_core_registers(&self, state: &StandardRegisters) -> Result<()>;
|
|
||||||
///
|
|
||||||
/// Save the state of the system registers.
|
|
||||||
///
|
|
||||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
|
||||||
fn system_registers(&self, state: &mut Vec<Register>) -> Result<()>;
|
|
||||||
///
|
|
||||||
/// Restore the state of the system registers.
|
|
||||||
///
|
|
||||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
|
||||||
fn set_system_registers(&self, state: &[Register]) -> Result<()>;
|
|
||||||
///
|
|
||||||
/// Read the MPIDR - Multiprocessor Affinity Register.
|
|
||||||
///
|
|
||||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
|
||||||
fn read_mpidr(&self) -> Result<u64>;
|
|
||||||
///
|
///
|
||||||
/// Configure core registers for a given CPU.
|
/// Configure core registers for a given CPU.
|
||||||
///
|
///
|
||||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
#[cfg(target_arch = "aarch64")]
|
||||||
fn setup_regs(&self, cpu_id: u8, boot_ip: u64, fdt_start: u64) -> Result<()>;
|
fn setup_regs(&self, cpu_id: u8, boot_ip: u64, fdt_start: u64) -> Result<()>;
|
||||||
///
|
///
|
||||||
|
/// Check if the CPU supports PMU
|
||||||
|
///
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
fn has_pmu_support(&self) -> bool;
|
||||||
|
///
|
||||||
|
/// Initialize PMU
|
||||||
|
///
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
fn init_pmu(&self, irq: u32) -> Result<()>;
|
||||||
|
///
|
||||||
/// Retrieve the vCPU state.
|
/// Retrieve the vCPU state.
|
||||||
/// This function is necessary to snapshot the VM
|
/// This function is necessary to snapshot the VM
|
||||||
///
|
///
|
||||||
@@ -480,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.
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
use crate::arch::x86::CpuIdEntry;
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
use crate::kvm::TdxCapabilities;
|
use crate::kvm::TdxCapabilities;
|
||||||
use crate::vm::Vm;
|
use crate::vm::Vm;
|
||||||
#[cfg(target_arch = "x86_64")]
|
use crate::HypervisorType;
|
||||||
use crate::x86_64::CpuId;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
use crate::x86_64::MsrList;
|
|
||||||
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
|
||||||
///
|
///
|
||||||
@@ -84,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
|
||||||
@@ -100,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!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::arch::aarch64::gic::{Error, Result};
|
use crate::arch::aarch64::gic::{Error, Result};
|
||||||
|
use crate::device::HypervisorDeviceError;
|
||||||
use crate::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 crate::Device;
|
use kvm_ioctls::DeviceFd;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Distributor registers as detailed at page 456 from
|
Distributor registers as detailed at page 456 from
|
||||||
@@ -77,7 +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(gic: &Arc<dyn Device>, offset: u32, val: &u32, set: bool) -> Result<()> {
|
fn dist_attr_access(gic: &DeviceFd, 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,
|
||||||
@@ -85,28 +85,30 @@ fn dist_attr_access(gic: &Arc<dyn Device>, offset: u32, val: &u32, set: bool) ->
|
|||||||
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 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 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 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 {
|
||||||
@@ -115,12 +117,13 @@ fn get_interrupts_num(gic: &Arc<dyn 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 Device>, reg: &DistReg, base: u32) -> Result<u32> {
|
fn compute_reg_len(gic: &DeviceFd, reg: &DistReg, base: u32) -> Result<u32> {
|
||||||
// FIXME:
|
// FIXME:
|
||||||
// Redefine some GIC constants to avoid the dependency on `layout` crate.
|
// Redefine some GIC constants to avoid the dependency on `layout` crate.
|
||||||
// This is temporary solution, will be fixed in future refactoring.
|
// This is temporary solution, will be fixed in future refactoring.
|
||||||
@@ -147,7 +150,7 @@ fn compute_reg_len(gic: &Arc<dyn Device>, reg: &DistReg, base: u32) -> Result<u3
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set distributor registers of the GIC.
|
/// Set distributor registers of the GIC.
|
||||||
pub fn set_dist_regs(gic: &Arc<dyn 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 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 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 {
|
||||||
|
|||||||
@@ -3,14 +3,14 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::arch::aarch64::gic::{Error, Result};
|
use crate::arch::aarch64::gic::{Error, Result};
|
||||||
|
use crate::device::HypervisorDeviceError;
|
||||||
use crate::kvm::kvm_bindings::{
|
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 crate::Device;
|
use kvm_ioctls::DeviceFd;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
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;
|
||||||
@@ -79,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 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
|
||||||
@@ -93,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 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;
|
||||||
@@ -156,7 +152,7 @@ pub fn get_icc_regs(gic: &Arc<dyn Device>, gicr_typer: &[u64]) -> Result<Vec<u32
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set ICC registers.
|
/// Set ICC registers.
|
||||||
pub fn set_icc_regs(gic: &Arc<dyn Device>, gicr_typer: &[u64], state: &[u32]) -> Result<()> {
|
pub fn set_icc_regs(gic: &DeviceFd, 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 {
|
||||||
|
|||||||
@@ -4,16 +4,17 @@ mod dist_regs;
|
|||||||
mod icc_regs;
|
mod icc_regs;
|
||||||
mod redist_regs;
|
mod redist_regs;
|
||||||
|
|
||||||
use crate::arch::aarch64::gic::{Error, Result, Vgic};
|
use crate::arch::aarch64::gic::{Error, Result, Vgic, VgicConfig};
|
||||||
use crate::kvm::kvm_bindings;
|
use crate::device::HypervisorDeviceError;
|
||||||
use crate::{CpuState, Device, Vm};
|
use crate::kvm::{kvm_bindings, KvmVm};
|
||||||
|
use crate::{CpuState, Vm};
|
||||||
use dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
use dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
||||||
use icc_regs::{get_icc_regs, set_icc_regs};
|
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 redist_regs::{construct_gicr_typers, get_redist_regs, set_redist_regs};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
const GITS_CTLR: u32 = 0x0000;
|
const GITS_CTLR: u32 = 0x0000;
|
||||||
const GITS_IIDR: u32 = 0x0004;
|
const GITS_IIDR: u32 = 0x0004;
|
||||||
@@ -27,7 +28,7 @@ const GITS_BASER: u32 = 0x0100;
|
|||||||
/// This is a helper function to get/set the ITS device attribute depending
|
/// This is a helper function to get/set the ITS device attribute depending
|
||||||
/// the bool parameter `set` provided.
|
/// the bool parameter `set` provided.
|
||||||
pub fn gicv3_its_attr_access(
|
pub fn gicv3_its_attr_access(
|
||||||
its_device: &Arc<dyn Device>,
|
its_device: &DeviceFd,
|
||||||
group: u32,
|
group: u32,
|
||||||
attr: u32,
|
attr: u32,
|
||||||
val: &u64,
|
val: &u64,
|
||||||
@@ -40,20 +41,22 @@ pub fn gicv3_its_attr_access(
|
|||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
if set {
|
if set {
|
||||||
its_device
|
its_device.set_device_attr(&gicv3_its_attr).map_err(|e| {
|
||||||
.set_device_attr(&gicv3_its_attr)
|
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||||
.map_err(Error::SetDeviceAttribute)
|
})
|
||||||
} else {
|
} else {
|
||||||
its_device
|
its_device
|
||||||
.get_device_attr(&mut gicv3_its_attr)
|
.get_device_attr(&mut gicv3_its_attr)
|
||||||
.map_err(Error::GetDeviceAttribute)
|
.map_err(|e| {
|
||||||
|
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Function that saves/restores ITS tables into guest RAM.
|
/// Function that saves/restores ITS tables into guest RAM.
|
||||||
///
|
///
|
||||||
/// The tables get flushed to guest RAM whenever the VM gets stopped.
|
/// The tables get flushed to guest RAM whenever the VM gets stopped.
|
||||||
pub fn gicv3_its_tables_access(its_device: &Arc<dyn Device>, save: bool) -> Result<()> {
|
pub fn gicv3_its_tables_access(its_device: &DeviceFd, save: bool) -> Result<()> {
|
||||||
let attr = if save {
|
let attr = if save {
|
||||||
u64::from(kvm_bindings::KVM_DEV_ARM_ITS_SAVE_TABLES)
|
u64::from(kvm_bindings::KVM_DEV_ARM_ITS_SAVE_TABLES)
|
||||||
} else {
|
} else {
|
||||||
@@ -68,15 +71,15 @@ pub fn gicv3_its_tables_access(its_device: &Arc<dyn Device>, save: bool) -> Resu
|
|||||||
};
|
};
|
||||||
its_device
|
its_device
|
||||||
.set_device_attr(&init_gic_attr)
|
.set_device_attr(&init_gic_attr)
|
||||||
.map_err(Error::SetDeviceAttribute)
|
.map_err(|e| Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into())))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct KvmGicV3Its {
|
pub struct KvmGicV3Its {
|
||||||
/// The hypervisor agnostic device for the GicV3
|
/// The KVM device for the GicV3
|
||||||
device: Arc<dyn Device>,
|
device: DeviceFd,
|
||||||
|
|
||||||
/// The hypervisor agnostic device for the Its device
|
/// The KVM device for the Its device
|
||||||
its_device: Option<Arc<dyn Device>>,
|
its_device: Option<DeviceFd>,
|
||||||
|
|
||||||
/// Vector holding values of GICR_TYPER for each vCPU
|
/// Vector holding values of GICR_TYPER for each vCPU
|
||||||
gicr_typers: Vec<u64>,
|
gicr_typers: Vec<u64>,
|
||||||
@@ -127,33 +130,21 @@ impl KvmGicV3Its {
|
|||||||
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3
|
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device(&self) -> &Arc<dyn Device> {
|
|
||||||
&self.device
|
|
||||||
}
|
|
||||||
|
|
||||||
fn its_device(&self) -> Option<&Arc<dyn Device>> {
|
|
||||||
self.its_device.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Setup the device-specific attributes
|
/// Setup the device-specific attributes
|
||||||
fn init_device_attributes(&mut self, vm: &dyn Vm, nr_irqs: u32) -> Result<()> {
|
fn init_device_attributes(&mut self, vm: &KvmVm, nr_irqs: u32) -> Result<()> {
|
||||||
// GicV3 part attributes
|
// GicV3 part attributes
|
||||||
/* Setting up the distributor attribute.
|
/* Setting up the distributor attribute. */
|
||||||
We are placing the GIC below 1GB so we need to substract the size of the distributor.
|
|
||||||
*/
|
|
||||||
Self::set_device_attribute(
|
Self::set_device_attribute(
|
||||||
self.device(),
|
&self.device,
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||||
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST),
|
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST),
|
||||||
&self.dist_addr as *const u64 as u64,
|
&self.dist_addr as *const u64 as u64,
|
||||||
0,
|
0,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
/* Setting up the redistributors' attribute.
|
/* Setting up the redistributors' attribute. */
|
||||||
We are calculating here the start of the redistributors address. We have one per CPU.
|
|
||||||
*/
|
|
||||||
Self::set_device_attribute(
|
Self::set_device_attribute(
|
||||||
self.device(),
|
&self.device,
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||||
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST),
|
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST),
|
||||||
&self.redists_addr as *const u64 as u64,
|
&self.redists_addr as *const u64 as u64,
|
||||||
@@ -171,6 +162,9 @@ impl KvmGicV3Its {
|
|||||||
.create_device(&mut its_device)
|
.create_device(&mut its_device)
|
||||||
.map_err(Error::CreateGic)?;
|
.map_err(Error::CreateGic)?;
|
||||||
|
|
||||||
|
// We know vm is KvmVm
|
||||||
|
let its_fd = its_fd.to_kvm().unwrap();
|
||||||
|
|
||||||
Self::set_device_attribute(
|
Self::set_device_attribute(
|
||||||
&its_fd,
|
&its_fd,
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||||
@@ -187,14 +181,14 @@ impl KvmGicV3Its {
|
|||||||
0,
|
0,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.set_its_device(Some(its_fd));
|
self.its_device = Some(its_fd);
|
||||||
|
|
||||||
/* We need to tell the kernel how many irqs to support with this vgic.
|
/* We need to tell the kernel how many irqs to support with this vgic.
|
||||||
* See the `layout` module for details.
|
* See the `layout` module for details.
|
||||||
*/
|
*/
|
||||||
let nr_irqs_ptr = &nr_irqs as *const u32;
|
let nr_irqs_ptr = &nr_irqs as *const u32;
|
||||||
Self::set_device_attribute(
|
Self::set_device_attribute(
|
||||||
self.device(),
|
&self.device,
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
|
||||||
0,
|
0,
|
||||||
nr_irqs_ptr as u64,
|
nr_irqs_ptr as u64,
|
||||||
@@ -205,7 +199,7 @@ impl KvmGicV3Its {
|
|||||||
* See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
|
* See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
|
||||||
*/
|
*/
|
||||||
Self::set_device_attribute(
|
Self::set_device_attribute(
|
||||||
self.device(),
|
&self.device,
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||||
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
|
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
|
||||||
0,
|
0,
|
||||||
@@ -214,19 +208,24 @@ impl KvmGicV3Its {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a KVM Vgic device
|
/// Create a KVM Vgic device
|
||||||
fn create_device(vm: &dyn Vm) -> Result<Arc<dyn Device>> {
|
fn create_device(vm: &KvmVm) -> Result<DeviceFd> {
|
||||||
let mut gic_device = kvm_bindings::kvm_create_device {
|
let mut gic_device = kvm_bindings::kvm_create_device {
|
||||||
type_: Self::version(),
|
type_: Self::version(),
|
||||||
fd: 0,
|
fd: 0,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.create_device(&mut gic_device).map_err(Error::CreateGic)
|
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
|
/// Set a GIC device attribute
|
||||||
fn set_device_attribute(
|
fn set_device_attribute(
|
||||||
device: &Arc<dyn Device>,
|
device: &DeviceFd,
|
||||||
group: u32,
|
group: u32,
|
||||||
attr: u64,
|
attr: u64,
|
||||||
addr: u64,
|
addr: u64,
|
||||||
@@ -238,41 +237,33 @@ impl KvmGicV3Its {
|
|||||||
attr,
|
attr,
|
||||||
addr,
|
addr,
|
||||||
};
|
};
|
||||||
device
|
device.set_device_attr(&attr).map_err(|e| {
|
||||||
.set_device_attr(&attr)
|
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||||
.map_err(Error::SetDeviceAttribute)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Method to initialize the GIC device
|
/// Method to initialize the GIC device
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
pub fn new(
|
pub fn new(vm: &dyn Vm, config: VgicConfig) -> Result<KvmGicV3Its> {
|
||||||
vm: &dyn Vm,
|
// This is inside KVM module
|
||||||
vcpu_count: u64,
|
let vm = vm.as_any().downcast_ref::<KvmVm>().expect("Wrong VM type?");
|
||||||
dist_addr: u64,
|
|
||||||
dist_size: u64,
|
|
||||||
redist_size: u64,
|
|
||||||
msi_size: u64,
|
|
||||||
nr_irqs: u32,
|
|
||||||
) -> Result<KvmGicV3Its> {
|
|
||||||
let vgic = Self::create_device(vm)?;
|
let vgic = Self::create_device(vm)?;
|
||||||
let redists_size: u64 = redist_size * vcpu_count;
|
|
||||||
let redists_addr: u64 = dist_addr - redists_size;
|
|
||||||
let msi_addr: u64 = redists_addr - msi_size;
|
|
||||||
|
|
||||||
let mut gic_device = KvmGicV3Its {
|
let mut gic_device = KvmGicV3Its {
|
||||||
device: vgic,
|
device: vgic,
|
||||||
its_device: None,
|
its_device: None,
|
||||||
gicr_typers: vec![0; vcpu_count.try_into().unwrap()],
|
gicr_typers: vec![0; config.vcpu_count.try_into().unwrap()],
|
||||||
dist_addr,
|
dist_addr: config.dist_addr,
|
||||||
dist_size,
|
dist_size: config.dist_size,
|
||||||
redists_addr,
|
redists_addr: config.redists_addr,
|
||||||
redists_size,
|
redists_size: config.redists_size,
|
||||||
msi_addr,
|
msi_addr: config.msi_addr,
|
||||||
msi_size,
|
msi_size: config.msi_size,
|
||||||
vcpu_count,
|
vcpu_count: config.vcpu_count,
|
||||||
};
|
};
|
||||||
|
|
||||||
gic_device.init_device_attributes(vm, nr_irqs)?;
|
gic_device.init_device_attributes(vm, config.nr_irqs)?;
|
||||||
|
|
||||||
Ok(gic_device)
|
Ok(gic_device)
|
||||||
}
|
}
|
||||||
@@ -312,10 +303,6 @@ impl Vgic for KvmGicV3Its {
|
|||||||
[self.msi_addr, self.msi_size]
|
[self.msi_addr, self.msi_size]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_its_device(&mut self, its_device: Option<Arc<dyn Device>>) {
|
|
||||||
self.its_device = its_device;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
|
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
|
||||||
let gicr_typers = construct_gicr_typers(vcpu_states);
|
let gicr_typers = construct_gicr_typers(vcpu_states);
|
||||||
self.gicr_typers = gicr_typers;
|
self.gicr_typers = gicr_typers;
|
||||||
@@ -329,18 +316,18 @@ impl Vgic for KvmGicV3Its {
|
|||||||
fn state(&self) -> Result<Gicv3ItsState> {
|
fn state(&self) -> Result<Gicv3ItsState> {
|
||||||
let gicr_typers = self.gicr_typers.clone();
|
let gicr_typers = self.gicr_typers.clone();
|
||||||
|
|
||||||
let gicd_ctlr = read_ctlr(self.device())?;
|
let gicd_ctlr = read_ctlr(&self.device)?;
|
||||||
|
|
||||||
let dist_state = get_dist_regs(self.device())?;
|
let dist_state = get_dist_regs(&self.device)?;
|
||||||
|
|
||||||
let rdist_state = get_redist_regs(self.device(), &gicr_typers)?;
|
let rdist_state = get_redist_regs(&self.device, &gicr_typers)?;
|
||||||
|
|
||||||
let icc_state = get_icc_regs(self.device(), &gicr_typers)?;
|
let icc_state = get_icc_regs(&self.device, &gicr_typers)?;
|
||||||
|
|
||||||
let its_baser_state: [u64; 8] = [0; 8];
|
let its_baser_state: [u64; 8] = [0; 8];
|
||||||
for i in 0..8 {
|
for i in 0..8 {
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_access(
|
||||||
self.its_device().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_BASER + i * 8,
|
GITS_BASER + i * 8,
|
||||||
&its_baser_state[i as usize],
|
&its_baser_state[i as usize],
|
||||||
@@ -350,7 +337,7 @@ impl Vgic for KvmGicV3Its {
|
|||||||
|
|
||||||
let its_ctlr_state: u64 = 0;
|
let its_ctlr_state: u64 = 0;
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_access(
|
||||||
self.its_device().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_CTLR,
|
GITS_CTLR,
|
||||||
&its_ctlr_state,
|
&its_ctlr_state,
|
||||||
@@ -359,7 +346,7 @@ impl Vgic for KvmGicV3Its {
|
|||||||
|
|
||||||
let its_cbaser_state: u64 = 0;
|
let its_cbaser_state: u64 = 0;
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_access(
|
||||||
self.its_device().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_CBASER,
|
GITS_CBASER,
|
||||||
&its_cbaser_state,
|
&its_cbaser_state,
|
||||||
@@ -368,7 +355,7 @@ impl Vgic for KvmGicV3Its {
|
|||||||
|
|
||||||
let its_creadr_state: u64 = 0;
|
let its_creadr_state: u64 = 0;
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_access(
|
||||||
self.its_device().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_CREADR,
|
GITS_CREADR,
|
||||||
&its_creadr_state,
|
&its_creadr_state,
|
||||||
@@ -377,7 +364,7 @@ impl Vgic for KvmGicV3Its {
|
|||||||
|
|
||||||
let its_cwriter_state: u64 = 0;
|
let its_cwriter_state: u64 = 0;
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_access(
|
||||||
self.its_device().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_CWRITER,
|
GITS_CWRITER,
|
||||||
&its_cwriter_state,
|
&its_cwriter_state,
|
||||||
@@ -386,7 +373,7 @@ impl Vgic for KvmGicV3Its {
|
|||||||
|
|
||||||
let its_iidr_state: u64 = 0;
|
let its_iidr_state: u64 = 0;
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_access(
|
||||||
self.its_device().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_IIDR,
|
GITS_IIDR,
|
||||||
&its_iidr_state,
|
&its_iidr_state,
|
||||||
@@ -411,17 +398,17 @@ impl Vgic for KvmGicV3Its {
|
|||||||
fn set_state(&mut self, state: &Gicv3ItsState) -> Result<()> {
|
fn set_state(&mut self, state: &Gicv3ItsState) -> Result<()> {
|
||||||
let gicr_typers = self.gicr_typers.clone();
|
let gicr_typers = self.gicr_typers.clone();
|
||||||
|
|
||||||
write_ctlr(self.device(), state.gicd_ctlr)?;
|
write_ctlr(&self.device, state.gicd_ctlr)?;
|
||||||
|
|
||||||
set_dist_regs(self.device(), &state.dist)?;
|
set_dist_regs(&self.device, &state.dist)?;
|
||||||
|
|
||||||
set_redist_regs(self.device(), &gicr_typers, &state.rdist)?;
|
set_redist_regs(&self.device, &gicr_typers, &state.rdist)?;
|
||||||
|
|
||||||
set_icc_regs(self.device(), &gicr_typers, &state.icc)?;
|
set_icc_regs(&self.device, &gicr_typers, &state.icc)?;
|
||||||
|
|
||||||
//Restore GICv3ITS registers
|
//Restore GICv3ITS registers
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_access(
|
||||||
self.its_device().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_IIDR,
|
GITS_IIDR,
|
||||||
&state.its_iidr,
|
&state.its_iidr,
|
||||||
@@ -429,7 +416,7 @@ impl Vgic for KvmGicV3Its {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_access(
|
||||||
self.its_device().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_CBASER,
|
GITS_CBASER,
|
||||||
&state.its_cbaser,
|
&state.its_cbaser,
|
||||||
@@ -437,7 +424,7 @@ impl Vgic for KvmGicV3Its {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_access(
|
||||||
self.its_device().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_CREADR,
|
GITS_CREADR,
|
||||||
&state.its_creadr,
|
&state.its_creadr,
|
||||||
@@ -445,7 +432,7 @@ impl Vgic for KvmGicV3Its {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_access(
|
||||||
self.its_device().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_CWRITER,
|
GITS_CWRITER,
|
||||||
&state.its_cwriter,
|
&state.its_cwriter,
|
||||||
@@ -454,7 +441,7 @@ impl Vgic for KvmGicV3Its {
|
|||||||
|
|
||||||
for i in 0..8 {
|
for i in 0..8 {
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_access(
|
||||||
self.its_device().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_BASER + i * 8,
|
GITS_BASER + i * 8,
|
||||||
&state.its_baser[i as usize],
|
&state.its_baser[i as usize],
|
||||||
@@ -463,10 +450,10 @@ impl Vgic for KvmGicV3Its {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Restore ITS tables
|
// Restore ITS tables
|
||||||
gicv3_its_tables_access(self.its_device().unwrap(), false)?;
|
gicv3_its_tables_access(self.its_device.as_ref().unwrap(), false)?;
|
||||||
|
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_access(
|
||||||
self.its_device().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_CTLR,
|
GITS_CTLR,
|
||||||
&state.its_ctlr,
|
&state.its_ctlr,
|
||||||
@@ -485,11 +472,11 @@ impl Vgic for KvmGicV3Its {
|
|||||||
addr: 0,
|
addr: 0,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
self.device()
|
self.device.set_device_attr(&init_gic_attr).map_err(|e| {
|
||||||
.set_device_attr(&init_gic_attr)
|
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||||
.map_err(Error::SetDeviceAttribute)?;
|
})?;
|
||||||
// Flush ITS tables to guest RAM.
|
// Flush ITS tables to guest RAM.
|
||||||
gicv3_its_tables_access(self.its_device().unwrap(), true)
|
gicv3_its_tables_access(self.its_device.as_ref().unwrap(), true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -498,23 +485,30 @@ mod tests {
|
|||||||
use crate::aarch64::gic::{
|
use crate::aarch64::gic::{
|
||||||
get_dist_regs, get_icc_regs, get_redist_regs, set_dist_regs, set_icc_regs, set_redist_regs,
|
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;
|
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]
|
#[test]
|
||||||
fn test_create_gic() {
|
fn test_create_gic() {
|
||||||
let hv = crate::new().unwrap();
|
let hv = crate::new().unwrap();
|
||||||
let vm = hv.create_vm().unwrap();
|
let vm = hv.create_vm().unwrap();
|
||||||
|
|
||||||
assert!(KvmGicV3Its::new(
|
assert!(KvmGicV3Its::new(&*vm, create_test_vgic_config()).is_ok());
|
||||||
&*vm,
|
|
||||||
1,
|
|
||||||
0x0900_0000 - 0x01_0000,
|
|
||||||
0x01_0000,
|
|
||||||
0x02_0000,
|
|
||||||
0x02_0000,
|
|
||||||
256
|
|
||||||
)
|
|
||||||
.is_ok());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -522,23 +516,14 @@ mod tests {
|
|||||||
let hv = crate::new().unwrap();
|
let hv = crate::new().unwrap();
|
||||||
let vm = hv.create_vm().unwrap();
|
let vm = hv.create_vm().unwrap();
|
||||||
let _ = vm.create_vcpu(0, None).unwrap();
|
let _ = vm.create_vcpu(0, None).unwrap();
|
||||||
let gic = KvmGicV3Its::new(
|
let gic = KvmGicV3Its::new(&*vm, create_test_vgic_config()).expect("Cannot create gic");
|
||||||
&*vm,
|
|
||||||
1,
|
|
||||||
0x0900_0000 - 0x01_0000,
|
|
||||||
0x01_0000,
|
|
||||||
0x02_0000,
|
|
||||||
0x02_0000,
|
|
||||||
256,
|
|
||||||
)
|
|
||||||
.expect("Cannot create gic");
|
|
||||||
|
|
||||||
let res = get_dist_regs(gic.device());
|
let res = get_dist_regs(&gic.device);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let state = res.unwrap();
|
let state = res.unwrap();
|
||||||
assert_eq!(state.len(), 568);
|
assert_eq!(state.len(), 568);
|
||||||
|
|
||||||
let res = set_dist_regs(gic.device(), &state);
|
let res = set_dist_regs(&gic.device, &state);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,25 +532,16 @@ mod tests {
|
|||||||
let hv = crate::new().unwrap();
|
let hv = crate::new().unwrap();
|
||||||
let vm = hv.create_vm().unwrap();
|
let vm = hv.create_vm().unwrap();
|
||||||
let _ = vm.create_vcpu(0, None).unwrap();
|
let _ = vm.create_vcpu(0, None).unwrap();
|
||||||
let gic = KvmGicV3Its::new(
|
let gic = KvmGicV3Its::new(&*vm, create_test_vgic_config()).expect("Cannot create gic");
|
||||||
&*vm,
|
|
||||||
1,
|
|
||||||
0x0900_0000 - 0x01_0000,
|
|
||||||
0x01_0000,
|
|
||||||
0x02_0000,
|
|
||||||
0x02_0000,
|
|
||||||
256,
|
|
||||||
)
|
|
||||||
.expect("Cannot create gic");
|
|
||||||
|
|
||||||
let gicr_typer = vec![123];
|
let gicr_typer = vec![123];
|
||||||
let res = get_redist_regs(gic.device(), &gicr_typer);
|
let res = get_redist_regs(&gic.device, &gicr_typer);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let state = res.unwrap();
|
let state = res.unwrap();
|
||||||
println!("{}", state.len());
|
println!("{}", state.len());
|
||||||
assert!(state.len() == 24);
|
assert!(state.len() == 24);
|
||||||
|
|
||||||
assert!(set_redist_regs(gic.device(), &gicr_typer, &state).is_ok());
|
assert!(set_redist_regs(&gic.device, &gicr_typer, &state).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -573,25 +549,16 @@ mod tests {
|
|||||||
let hv = crate::new().unwrap();
|
let hv = crate::new().unwrap();
|
||||||
let vm = hv.create_vm().unwrap();
|
let vm = hv.create_vm().unwrap();
|
||||||
let _ = vm.create_vcpu(0, None).unwrap();
|
let _ = vm.create_vcpu(0, None).unwrap();
|
||||||
let gic = KvmGicV3Its::new(
|
let gic = KvmGicV3Its::new(&*vm, create_test_vgic_config()).expect("Cannot create gic");
|
||||||
&*vm,
|
|
||||||
1,
|
|
||||||
0x0900_0000 - 0x01_0000,
|
|
||||||
0x01_0000,
|
|
||||||
0x02_0000,
|
|
||||||
0x02_0000,
|
|
||||||
256,
|
|
||||||
)
|
|
||||||
.expect("Cannot create gic");
|
|
||||||
|
|
||||||
let gicr_typer = vec![123];
|
let gicr_typer = vec![123];
|
||||||
let res = get_icc_regs(gic.device(), &gicr_typer);
|
let res = get_icc_regs(&gic.device, &gicr_typer);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let state = res.unwrap();
|
let state = res.unwrap();
|
||||||
println!("{}", state.len());
|
println!("{}", state.len());
|
||||||
assert!(state.len() == 9);
|
assert!(state.len() == 9);
|
||||||
|
|
||||||
assert!(set_icc_regs(gic.device(), &gicr_typer, &state).is_ok());
|
assert!(set_icc_regs(&gic.device, &gicr_typer, &state).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -600,14 +567,7 @@ mod tests {
|
|||||||
let vm = hv.create_vm().unwrap();
|
let vm = hv.create_vm().unwrap();
|
||||||
let _ = vm.create_vcpu(0, None).unwrap();
|
let _ = vm.create_vcpu(0, None).unwrap();
|
||||||
let gic = vm
|
let gic = vm
|
||||||
.create_vgic(
|
.create_vgic(create_test_vgic_config())
|
||||||
1,
|
|
||||||
0x0900_0000 - 0x01_0000,
|
|
||||||
0x01_0000,
|
|
||||||
0x02_0000,
|
|
||||||
0x02_0000,
|
|
||||||
256,
|
|
||||||
)
|
|
||||||
.expect("Cannot create gic");
|
.expect("Cannot create gic");
|
||||||
|
|
||||||
assert!(gic.lock().unwrap().save_data_tables().is_ok());
|
assert!(gic.lock().unwrap().save_data_tables().is_ok());
|
||||||
|
|||||||
@@ -3,9 +3,16 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::arch::aarch64::gic::{Error, Result};
|
use crate::arch::aarch64::gic::{Error, Result};
|
||||||
use crate::kvm::kvm_bindings::{kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS};
|
use crate::device::HypervisorDeviceError;
|
||||||
use crate::{CpuState, Device};
|
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;
|
||||||
@@ -32,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
|
||||||
|
| KVM_REG_SIZE_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.
|
||||||
@@ -83,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 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 Device>,
|
gic: &DeviceFd,
|
||||||
gicr_typer: &[u64],
|
gicr_typer: &[u64],
|
||||||
state: &mut Vec<u32>,
|
state: &mut Vec<u32>,
|
||||||
reg_list: &[RdistReg],
|
reg_list: &[RdistReg],
|
||||||
@@ -137,7 +146,7 @@ fn access_redists_aux(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get redistributor registers.
|
/// Get redistributor registers.
|
||||||
pub fn get_redist_regs(gic: &Arc<dyn 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(
|
||||||
@@ -154,7 +163,7 @@ pub fn get_redist_regs(gic: &Arc<dyn Device>, gicr_typer: &[u64]) -> Result<Vec<
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set redistributor registers.
|
/// Set redistributor registers.
|
||||||
pub fn set_redist_regs(gic: &Arc<dyn Device>, gicr_typer: &[u64], state: &[u32]) -> Result<()> {
|
pub fn set_redist_regs(gic: &DeviceFd, 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(
|
||||||
@@ -190,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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,8 @@ pub mod gic;
|
|||||||
|
|
||||||
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,
|
||||||
@@ -28,8 +24,8 @@ 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
|
||||||
// an instance of that structure.
|
// an instance of that structure.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! offset__of {
|
macro_rules! offset_of {
|
||||||
($str:ty, $($field:ident)+) => ({
|
($str:ty, $field:ident) => {{
|
||||||
let tmp: std::mem::MaybeUninit<$str> = std::mem::MaybeUninit::uninit();
|
let tmp: std::mem::MaybeUninit<$str> = std::mem::MaybeUninit::uninit();
|
||||||
let base = tmp.as_ptr();
|
let base = tmp.as_ptr();
|
||||||
|
|
||||||
@@ -38,14 +34,16 @@ macro_rules! offset__of {
|
|||||||
// SAFETY: The pointer is valid and aligned, just not initialised. Using `addr_of` ensures
|
// SAFETY: The pointer is valid and aligned, just not initialised. Using `addr_of` ensures
|
||||||
// that we don't actually read from `base` (which would be UB) nor create an intermediate
|
// that we don't actually read from `base` (which would be UB) nor create an intermediate
|
||||||
// reference.
|
// reference.
|
||||||
let member = unsafe { core::ptr::addr_of!((*base).$($field)*) } as *const u8;
|
let member = unsafe { core::ptr::addr_of!((*base).$field) } as *const u8;
|
||||||
|
|
||||||
// Avoid warnings when nesting `unsafe` blocks.
|
// Avoid warnings when nesting `unsafe` blocks.
|
||||||
#[allow(unused_unsafe)]
|
#[allow(unused_unsafe)]
|
||||||
// SAFETY: The two pointers are within the same allocated object `tmp`. All requirements
|
// SAFETY: The two pointers are within the same allocated object `tmp`. All requirements
|
||||||
// from offset_from are upheld.
|
// from offset_from are upheld.
|
||||||
unsafe { member.offset_from(base as *const u8) as usize }
|
unsafe {
|
||||||
});
|
member.offset_from(base as *const u8) as usize
|
||||||
|
}
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Following are macros that help with getting the ID of a aarch64 core register.
|
// Following are macros that help with getting the ID of a aarch64 core register.
|
||||||
@@ -85,32 +83,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.
|
||||||
@@ -127,8 +99,7 @@ pub fn is_system_register(regid: u64) -> bool {
|
|||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
!(size != KVM_REG_SIZE_U32 && size != KVM_REG_SIZE_U64),
|
!(size != KVM_REG_SIZE_U32 && size != KVM_REG_SIZE_U64),
|
||||||
"Unexpected register size for system register {}",
|
"Unexpected register size for system register {size}"
|
||||||
size
|
|
||||||
);
|
);
|
||||||
|
|
||||||
true
|
true
|
||||||
@@ -149,8 +120,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,
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user