mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
448 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d412ac2182 | ||
|
|
628bb74cce | ||
|
|
5dc1668927 | ||
|
|
8d8791c95d | ||
|
|
ade49a9fd2 | ||
|
|
87d765a168 | ||
|
|
98b28d5866 | ||
|
|
c2664e6a8d | ||
|
|
88087c18b0 | ||
|
|
22867232e1 | ||
|
|
3f489286c5 | ||
|
|
13772c5a65 | ||
|
|
fde74c57ad | ||
|
|
b665aa19f4 | ||
|
|
3d3693699b | ||
|
|
1ed31abb5d | ||
|
|
1446873b28 | ||
|
|
5f04c9b6fe | ||
|
|
46d59f0bc4 | ||
|
|
9add5263f7 | ||
|
|
dfaa151dfc | ||
|
|
2cd5b0b5e0 | ||
|
|
d08365a131 | ||
|
|
496d4c204b | ||
|
|
e0b863ecfe | ||
|
|
beb353eab2 | ||
|
|
1cfe096fb1 | ||
|
|
c8c68f3f77 | ||
|
|
77634a3c05 | ||
|
|
ab18a923f1 | ||
|
|
9f316b2e50 | ||
|
|
a03f17a275 | ||
|
|
c14f9ee2f4 | ||
|
|
ec56f07316 | ||
|
|
574d86dd4d | ||
|
|
a633b009ed | ||
|
|
b475965632 | ||
|
|
11b1ab40a5 | ||
|
|
10e77ebd1e | ||
|
|
167ae5a78a | ||
|
|
8736256564 | ||
|
|
55e08edd7f | ||
|
|
54f66aaadc | ||
|
|
d656058c21 | ||
|
|
3227372fe0 | ||
|
|
ac3f51002d | ||
|
|
0d2e2cd4c3 | ||
|
|
1fc969d37a | ||
|
|
27f5016ad3 | ||
|
|
a0ddcc68d7 | ||
|
|
df5aecd144 | ||
|
|
862d267302 | ||
|
|
44d9c7fd42 | ||
|
|
12abe2dd2b | ||
|
|
39a81c596f | ||
|
|
3b0d2e796b | ||
|
|
42357c01f3 | ||
|
|
b7b5b9d7e6 | ||
|
|
a63e064004 | ||
|
|
62c1f39ea2 | ||
|
|
460ba718d4 | ||
|
|
c91a8e1324 | ||
|
|
1adfb7e9f8 | ||
|
|
8dd4d42053 | ||
|
|
3834b43878 | ||
|
|
52605cc0e4 | ||
|
|
92beda1e32 | ||
|
|
167fef382a | ||
|
|
5126e9b26e | ||
|
|
59bd682b1f | ||
|
|
8e3b351038 | ||
|
|
3f8d06b47e | ||
|
|
66460765a3 | ||
|
|
b748b8987d | ||
|
|
c9483fb080 | ||
|
|
f52cd3bb85 | ||
|
|
57cd12fabc | ||
|
|
869c649e81 | ||
|
|
a9e30c88df | ||
|
|
1666375a31 | ||
|
|
9a0be7db3a | ||
|
|
324c5deb37 | ||
|
|
09e79a5e9b | ||
|
|
af261f231c | ||
|
|
7122e2989c | ||
|
|
d0b253d15f | ||
|
|
59baa29deb | ||
|
|
19fdf8bc79 | ||
|
|
58b902d036 | ||
|
|
e3213c8a79 | ||
|
|
2e2ce47271 | ||
|
|
d3a8332282 | ||
|
|
c64004b9a9 | ||
|
|
149e424b6e | ||
|
|
b07d471d4f | ||
|
|
345e65c9c2 | ||
|
|
f93aa42319 | ||
|
|
fa4bf92feb | ||
|
|
f30d460fa3 | ||
|
|
2c94773bdc | ||
|
|
9f1d2d34e8 | ||
|
|
57508a4b1c | ||
|
|
012f2572d5 | ||
|
|
b9dbe3a2f7 | ||
|
|
6230929d51 | ||
|
|
04d034a0bc | ||
|
|
f603afc46e | ||
|
|
b68add2d0d | ||
|
|
0f44db5da4 | ||
|
|
a7dccf94cf | ||
|
|
0e6e539d9b | ||
|
|
1cb1cff882 | ||
|
|
fefbc356a2 | ||
|
|
8b37448d28 | ||
|
|
92d083f593 | ||
|
|
58d8795b53 | ||
|
|
b37e2ed378 | ||
|
|
ef8fb9bd25 | ||
|
|
cfafc85b9c | ||
|
|
683491a955 | ||
|
|
2bbb08b2a4 | ||
|
|
b77eb63688 | ||
|
|
6722c303b0 | ||
|
|
6e0bd73c90 | ||
|
|
103fe1f48b | ||
|
|
f4e1b72477 | ||
|
|
65628e8d94 | ||
|
|
851de1e0b3 | ||
|
|
a9ec0f33c0 | ||
|
|
9266ea4995 | ||
|
|
6b8070de95 | ||
|
|
a5712641d2 | ||
|
|
aad4dc3b6b | ||
|
|
f4495de143 | ||
|
|
99d9a3d299 | ||
|
|
df7c728399 | ||
|
|
1e5a4e8d77 | ||
|
|
ff3fb91ba6 | ||
|
|
86c19816c6 | ||
|
|
ad1cb130a5 | ||
|
|
0c75e7f7b2 | ||
|
|
cb171d4a23 | ||
|
|
bc310bb173 | ||
|
|
76f4641004 | ||
|
|
dcb12c05a7 | ||
|
|
078c0408b3 | ||
|
|
b99b2bc990 | ||
|
|
0cd6d96874 | ||
|
|
a5d0ff7039 | ||
|
|
da1ab77848 | ||
|
|
989d3d3960 | ||
|
|
1559b7b684 | ||
|
|
e96474a5bd | ||
|
|
2a36a18be3 | ||
|
|
0e386581e0 | ||
|
|
1f0e5eb66a | ||
|
|
157db33d65 | ||
|
|
40df6c3787 | ||
|
|
b425c5f57c | ||
|
|
b4e3942708 | ||
|
|
802f489e4d | ||
|
|
fdecd94b20 | ||
|
|
2af2cc539f | ||
|
|
7d71319210 | ||
|
|
e710e21744 | ||
|
|
9c658e21a5 | ||
|
|
38620eaea8 | ||
|
|
a388d76228 | ||
|
|
710a860e9b | ||
|
|
83f22ac779 | ||
|
|
71b4aad3f1 | ||
|
|
d7afa3c47e | ||
|
|
073374e2b0 | ||
|
|
d4f40487e3 | ||
|
|
f63cf2ebc0 | ||
|
|
46031cb211 | ||
|
|
c52ccf3992 | ||
|
|
7b31871a36 | ||
|
|
1b58c63734 | ||
|
|
d1435a44af | ||
|
|
a75d71f2c8 | ||
|
|
96209e7a16 | ||
|
|
7a68689794 | ||
|
|
6e6b519a74 | ||
|
|
958ef69295 | ||
|
|
ffc222b26e | ||
|
|
5b706422e8 | ||
|
|
84105992b7 | ||
|
|
0235ed3388 | ||
|
|
58066e2da4 | ||
|
|
62d6553714 | ||
|
|
2a45e44bda | ||
|
|
099cdd2af8 | ||
|
|
340fd6571a | ||
|
|
02f951a9c3 | ||
|
|
22be5f9d0f | ||
|
|
a9ccb20e5a | ||
|
|
2b150ac2ea | ||
|
|
204bf2e3f5 | ||
|
|
2a40fe5541 | ||
|
|
7ae4e2b8fe | ||
|
|
f17f4079b4 | ||
|
|
342851c88c | ||
|
|
37c3b0429a | ||
|
|
af14044fb8 | ||
|
|
fa39d1e9b9 | ||
|
|
00714dc54a | ||
|
|
87fb9cedc9 | ||
|
|
ce2928d842 | ||
|
|
a18b08c682 | ||
|
|
43935c3b95 | ||
|
|
c3c550c454 | ||
|
|
7da632def6 | ||
|
|
f38ef20e3b | ||
|
|
a033b1ef67 | ||
|
|
ffa7d27b5e | ||
|
|
da725daca0 | ||
|
|
0fe0eb4c97 | ||
|
|
8877211d9e | ||
|
|
f907ff5d82 | ||
|
|
89a2562c5a | ||
|
|
29cf637f3f | ||
|
|
fd9fa2a681 | ||
|
|
756aebafda | ||
|
|
d4d63502be | ||
|
|
961793ba8c | ||
|
|
cbeed02a7c | ||
|
|
10eb687687 | ||
|
|
83cc554f90 | ||
|
|
7d8d27c1b4 | ||
|
|
d692dfb8e3 | ||
|
|
7ad58457b0 | ||
|
|
62d2189ade | ||
|
|
7e7855bc8f | ||
|
|
11cc35e6b5 | ||
|
|
89677c3181 | ||
|
|
307cfeba27 | ||
|
|
43bb09df50 | ||
|
|
81d570e271 | ||
|
|
31ca22d4b6 | ||
|
|
cf995451a2 | ||
|
|
4fea40f008 | ||
|
|
2daab89987 | ||
|
|
1bc63e7848 | ||
|
|
2115a41568 | ||
|
|
4b4354a52c | ||
|
|
67751d07d6 | ||
|
|
509fd8b03e | ||
|
|
1294f052b2 | ||
|
|
01b38b9c59 | ||
|
|
db51e9e209 | ||
|
|
57dfcc06d3 | ||
|
|
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 |
20
.github/workflows/build.yaml
vendored
20
.github/workflows/build.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
- "1.60"
|
||||
- "1.65"
|
||||
target:
|
||||
- x86_64-unknown-linux-gnu
|
||||
- x86_64-unknown-linux-musl
|
||||
@@ -36,26 +36,20 @@ jobs:
|
||||
- name: Build (default features)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor -- -D warnings
|
||||
|
||||
- name: Build (common + kvm)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "common,kvm" -- -D warnings
|
||||
- name: Build (kvm)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "kvm" -- -D warnings
|
||||
|
||||
- name: Build (default features + tdx)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "tdx" -- -D warnings
|
||||
|
||||
- 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)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "guest_debug" -- -D warnings
|
||||
|
||||
- name: Build (common + mshv)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "common,mshv" -- -D warnings
|
||||
- name: Build (mshv)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv" -- -D warnings
|
||||
|
||||
- name: Build (common + mshv + kvm)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "common,mshv,kvm" -- -D warnings
|
||||
- name: Build (mshv + kvm)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv,kvm" -- -D warnings
|
||||
|
||||
- name: Release Build (default features)
|
||||
run: cargo build --locked --all --release --target=${{ matrix.target }}
|
||||
|
||||
53
.github/workflows/quality-aarch64.yaml
vendored
53
.github/workflows/quality-aarch64.yaml
vendored
@@ -1,53 +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: 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: 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
|
||||
75
.github/workflows/quality.yaml
vendored
75
.github/workflows/quality.yaml
vendored
@@ -14,14 +14,21 @@ jobs:
|
||||
- stable
|
||||
target:
|
||||
- x86_64-unknown-linux-gnu
|
||||
- aarch64-unknown-linux-gnu
|
||||
experimental: [false]
|
||||
include:
|
||||
- rust: beta
|
||||
target: x86_64-unknown-linux-gnu
|
||||
experimental: true
|
||||
- rust: beta
|
||||
target: aarch64-unknown-linux-gnu
|
||||
experimental: true
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Rust toolchain (${{ matrix.rust }})
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -31,33 +38,67 @@ jobs:
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Debug Check (default features)
|
||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||
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
|
||||
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)
|
||||
run: cargo fmt -- --check
|
||||
|
||||
- name: Clippy (common + kvm)
|
||||
run: cargo clippy --locked --all --all-targets --no-default-features --tests --features "common,kvm" -- -D warnings
|
||||
- name: Clippy (kvm)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --locked --all --all-targets --no-default-features --tests --features "kvm" -- -D warnings
|
||||
|
||||
- name: Clippy (default features)
|
||||
run: cargo clippy --locked --all --all-targets --tests -- -D warnings
|
||||
|
||||
- name: Clippy (default features + amx)
|
||||
run: cargo clippy --locked --all --all-targets --tests --features "amx" -- -D warnings
|
||||
|
||||
- name: Clippy (default features + gdb)
|
||||
run: cargo clippy --locked --all --all-targets --tests --features "gdb" -- -D warnings
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --locked --all --all-targets --tests -- -D warnings
|
||||
|
||||
- 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: --locked --all --all-targets --tests --features "guest_debug" -- -D warnings
|
||||
|
||||
- name: Clippy (common + mshv)
|
||||
run: cargo clippy --locked --all --all-targets --no-default-features --tests --features "common,mshv" -- -D warnings
|
||||
- name: Clippy (default features + tracing)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --locked --all --all-targets --tests --features "tracing" -- -D warnings
|
||||
|
||||
- name: Clippy (common + mshv + kvm)
|
||||
run: cargo clippy --locked --all --all-targets --no-default-features --tests --features "common,mshv,kvm" -- -D warnings
|
||||
- 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: --locked --all --all-targets --no-default-features --tests --features "mshv" -- -D warnings
|
||||
|
||||
- name: Clippy (mshv + kvm)
|
||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --locked --all --all-targets --no-default-features --tests --features "mshv,kvm" -- -D warnings
|
||||
|
||||
- name: Clippy (kvm + tdx)
|
||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --locked --all --all-targets --no-default-features --tests --features "tdx,kvm" -- -D warnings
|
||||
|
||||
- name: Check build did not modify any files
|
||||
run: test -z "$(git status --porcelain)"
|
||||
|
||||
90
.github/workflows/release.yaml
vendored
90
.github/workflows/release.yaml
vendored
@@ -1,62 +1,48 @@
|
||||
name: Cloud Hypervisor Release
|
||||
on: [create]
|
||||
on: [pull_request, create]
|
||||
|
||||
jobs:
|
||||
release:
|
||||
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||
if: (github.event_name == 'create' && github.event.ref_type == 'tag') || github.event_name == 'pull_request'
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Install musl-gcc
|
||||
run: sudo apt install -y musl-tools
|
||||
run: sudo apt install -y musl-tools
|
||||
- name: Create release directory
|
||||
run: rsync -rv --exclude=.git . ../cloud-hypervisor-${{ github.event.ref }}
|
||||
- name: Install Rust toolchain (x86_64-unknown-linux-gnu)
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "1.62"
|
||||
target: x86_64-unknown-linux-gnu
|
||||
toolchain: "1.65"
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- name: Install Rust toolchain (x86_64-unknown-linux-musl)
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "1.62"
|
||||
target: x86_64-unknown-linux-musl
|
||||
toolchain: "1.65"
|
||||
target: x86_64-unknown-linux-musl
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
toolchain: "1.62"
|
||||
toolchain: "1.65"
|
||||
command: build
|
||||
args: --all --release --target=x86_64-unknown-linux-gnu
|
||||
- name: Static Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
toolchain: "1.62"
|
||||
toolchain: "1.65"
|
||||
command: build
|
||||
args: --all --release --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)
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "1.62"
|
||||
target: aarch64-unknown-linux-musl
|
||||
override: true
|
||||
- name: Static Build (AArch64)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: true
|
||||
command: build
|
||||
args: --all --release --target=aarch64-unknown-linux-musl
|
||||
- name: Vendor
|
||||
working-directory: ../cloud-hypervisor-${{ github.event.ref }}
|
||||
run: |
|
||||
mkdir ../vendor-cargo-home
|
||||
export CARGO_HOME=$(realpath ../vendor-cargo-home)
|
||||
mkdir .cargo
|
||||
cargo vendor > .cargo/config.toml
|
||||
toolchain: "1.65"
|
||||
target: aarch64-unknown-linux-musl
|
||||
override: true
|
||||
- name: Create Release
|
||||
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
@@ -66,20 +52,8 @@ jobs:
|
||||
release_name: ${{ github.ref }}
|
||||
draft: 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
|
||||
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||
id: upload-release-cloud-hypervisor
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
@@ -90,6 +64,7 @@ jobs:
|
||||
asset_name: cloud-hypervisor
|
||||
asset_content_type: application/octet-stream
|
||||
- name: Upload static cloud-hypervisor
|
||||
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||
id: upload-release-static-cloud-hypervisor
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
@@ -100,6 +75,7 @@ jobs:
|
||||
asset_name: cloud-hypervisor-static
|
||||
asset_content_type: application/octet-stream
|
||||
- name: Upload ch-remote
|
||||
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||
id: upload-release-ch-remote
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
@@ -110,6 +86,7 @@ jobs:
|
||||
asset_name: ch-remote
|
||||
asset_content_type: application/octet-stream
|
||||
- name: Upload static-ch-remote
|
||||
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||
id: upload-release-static-ch-remote
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
@@ -119,7 +96,18 @@ jobs:
|
||||
asset_path: target/x86_64-unknown-linux-musl/release/ch-remote
|
||||
asset_name: ch-remote-static
|
||||
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
|
||||
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||
id: upload-release-static-aarch64-cloud-hypervisor
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
@@ -130,6 +118,7 @@ jobs:
|
||||
asset_name: cloud-hypervisor-static-aarch64
|
||||
asset_content_type: application/octet-stream
|
||||
- name: Upload static AArch64 ch-remote
|
||||
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||
id: upload-release-static-aarch64-ch-remote
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
@@ -139,3 +128,24 @@ jobs:
|
||||
asset_path: target/aarch64-unknown-linux-musl/release/ch-remote
|
||||
asset_name: ch-remote-static-aarch64
|
||||
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
|
||||
|
||||
633
Cargo.lock
generated
633
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
49
Cargo.toml
49
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cloud-hypervisor"
|
||||
version = "26.0.0"
|
||||
version = "28.4.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
default-run = "cloud-hypervisor"
|
||||
@@ -10,58 +10,63 @@ description = "Open source Virtual Machine Monitor (VMM) that runs on top of KVM
|
||||
homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
||||
# Minimum buildable version:
|
||||
# Keep in sync with version in .github/workflows/build.yaml
|
||||
rust-version = "1.60"
|
||||
# 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.65"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
opt-level = "s"
|
||||
strip = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.62"
|
||||
anyhow = "1.0.66"
|
||||
api_client = { path = "api_client" }
|
||||
clap = { version = "3.2.17", features = ["wrap_help","cargo"] }
|
||||
clap = { version = "4.0.29", features = ["wrap_help","cargo","string"] }
|
||||
epoll = "4.3.1"
|
||||
event_monitor = { path = "event_monitor" }
|
||||
hypervisor = { path = "hypervisor" }
|
||||
libc = "0.2.131"
|
||||
libc = "0.2.138"
|
||||
log = { version = "0.4.17", features = ["std"] }
|
||||
option_parser = { path = "option_parser" }
|
||||
seccompiler = "0.2.0"
|
||||
serde_json = "1.0.83"
|
||||
seccompiler = "0.3.0"
|
||||
serde_json = "1.0.89"
|
||||
signal-hook = "0.3.14"
|
||||
thiserror = "1.0.32"
|
||||
thiserror = "1.0.37"
|
||||
tpm = { path = "tpm"}
|
||||
tracer = { path = "tracer" }
|
||||
vmm = { path = "vmm" }
|
||||
vmm-sys-util = "0.10.0"
|
||||
vm-memory = "0.8.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
vm-memory = "0.10.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.2.17", features = ["cargo"] }
|
||||
clap = { version = "4.0.29", features = ["cargo"] }
|
||||
|
||||
# List of patched crates
|
||||
[patch.crates-io]
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0-tdx" }
|
||||
kvm-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" }
|
||||
|
||||
[dev-dependencies]
|
||||
dirs = "4.0.0"
|
||||
net_util = { path = "net_util" }
|
||||
once_cell = "1.13.1"
|
||||
serde_json = "1.0.83"
|
||||
once_cell = "1.16.0"
|
||||
serde_json = "1.0.89"
|
||||
test_infra = { path = "test_infra" }
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
[features]
|
||||
default = ["common", "kvm"]
|
||||
# Common features for all hypervisors
|
||||
common = ["fwdebug"]
|
||||
amx = ["vmm/amx"]
|
||||
cmos = ["vmm/cmos"]
|
||||
fwdebug = ["vmm/fwdebug"]
|
||||
gdb = ["vmm/gdb"]
|
||||
default = ["kvm"]
|
||||
guest_debug = ["vmm/guest_debug"]
|
||||
kvm = ["vmm/kvm"]
|
||||
mshv = ["vmm/mshv"]
|
||||
tdx = ["vmm/tdx"]
|
||||
tracing = ["vmm/tracing", "tracer/tracing"]
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
@@ -79,7 +84,9 @@ members = [
|
||||
"performance-metrics",
|
||||
"qcow",
|
||||
"rate_limiter",
|
||||
"serial_buffer",
|
||||
"test_infra",
|
||||
"tracer",
|
||||
"vfio_user",
|
||||
"vhdx",
|
||||
"vhost_user_block",
|
||||
|
||||
757
Jenkinsfile
vendored
757
Jenkinsfile
vendored
@@ -1,452 +1,333 @@
|
||||
def runWorkers = true
|
||||
pipeline{
|
||||
agent none
|
||||
stages {
|
||||
stage ('Early checks') {
|
||||
agent { node { label 'built-in' } }
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Check for documentation only changes') {
|
||||
when {
|
||||
expression {
|
||||
return docsFileOnly()
|
||||
}
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
runWorkers = false
|
||||
echo "Documentation only changes, no need to run the CI"
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Check for fuzzer files only changes') {
|
||||
when {
|
||||
expression {
|
||||
return fuzzFileOnly()
|
||||
}
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
runWorkers = false
|
||||
echo "Fuzzer cargo files only changes, no need to run the CI"
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Check for RFC/WIP builds') {
|
||||
when {
|
||||
changeRequest comparator: 'REGEXP', title: '.*(rfc|RFC|wip|WIP).*'
|
||||
beforeAgent true
|
||||
}
|
||||
steps {
|
||||
error("Failing as this is marked as a WIP or RFC PR.")
|
||||
}
|
||||
}
|
||||
stage ('Cancel older builds') {
|
||||
when { not { branch 'main' } }
|
||||
steps {
|
||||
cancelPreviousBuilds()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Build') {
|
||||
parallel {
|
||||
stage ('Worker build') {
|
||||
agent { node { label 'jammy' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Prepare environment') {
|
||||
steps {
|
||||
sh "scripts/prepare_vdpa.sh"
|
||||
}
|
||||
}
|
||||
stage ('Run OpenAPI tests') {
|
||||
steps {
|
||||
sh "scripts/run_openapi_tests.sh"
|
||||
}
|
||||
}
|
||||
stage ('Run unit tests') {
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --unit"
|
||||
}
|
||||
}
|
||||
stage ('Run integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "sudo modprobe openvswitch"
|
||||
sh "scripts/dev_cli.sh tests --integration"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('AArch64 worker build') {
|
||||
agent { node { label 'bionic-arm64' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
environment {
|
||||
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Run unit tests') {
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --unit --libc musl"
|
||||
}
|
||||
}
|
||||
stage ('Run integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "sudo modprobe openvswitch"
|
||||
sh "scripts/dev_cli.sh tests --integration --libc musl"
|
||||
}
|
||||
}
|
||||
stage ('Install azure-cli') {
|
||||
steps {
|
||||
installAzureCli("bionic", "arm64")
|
||||
}
|
||||
}
|
||||
stage ('Download Windows image') {
|
||||
steps {
|
||||
sh '''#!/bin/bash -x
|
||||
IMG_BASENAME=windows-11-iot-enterprise-aarch64.raw
|
||||
IMG_PATH=$HOME/workloads/$IMG_BASENAME
|
||||
IMG_GZ_PATH=$HOME/workloads/$IMG_BASENAME.gz
|
||||
IMG_GZ_BLOB_NAME=windows-11-iot-enterprise-aarch64-9-min.raw.gz
|
||||
cp "scripts/$IMG_BASENAME.sha1" "$HOME/workloads/"
|
||||
pushd "$HOME/workloads"
|
||||
if sha1sum "$IMG_BASENAME.sha1" --check; then
|
||||
exit
|
||||
fi
|
||||
popd
|
||||
mkdir -p "$HOME/workloads"
|
||||
az storage blob download \
|
||||
--container-name private-images \
|
||||
--file "$IMG_GZ_PATH" \
|
||||
--name "$IMG_GZ_BLOB_NAME" \
|
||||
--connection-string "$AZURE_CONNECTION_STRING"
|
||||
gzip -d $IMG_GZ_PATH
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage ('Run Windows guest integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-windows --libc musl"
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||
deleteDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Worker build (musl)') {
|
||||
agent { node { label 'jammy' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Prepare environment') {
|
||||
steps {
|
||||
sh "scripts/prepare_vdpa.sh"
|
||||
}
|
||||
}
|
||||
stage ('Run unit tests for musl') {
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --unit --libc musl"
|
||||
}
|
||||
}
|
||||
stage ('Run integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "sudo modprobe openvswitch"
|
||||
sh "scripts/dev_cli.sh tests --integration --libc musl"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Worker build SGX') {
|
||||
agent { node { label 'bionic-sgx' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
allOf {
|
||||
branch 'main'
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Run SGX integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-sgx"
|
||||
}
|
||||
}
|
||||
stage ('Run SGX integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-sgx --libc musl"
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||
deleteDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Worker build VFIO') {
|
||||
agent { node { label 'bionic-vfio' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
allOf {
|
||||
branch 'main'
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Run VFIO integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-vfio"
|
||||
}
|
||||
}
|
||||
stage ('Run VFIO integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-vfio --libc musl"
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||
deleteDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Worker build - Windows guest') {
|
||||
agent { node { label 'jammy' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
environment {
|
||||
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Install azure-cli') {
|
||||
steps {
|
||||
installAzureCli("jammy", "amd64")
|
||||
}
|
||||
}
|
||||
stage ('Download assets') {
|
||||
steps {
|
||||
sh "mkdir ${env.HOME}/workloads"
|
||||
sh 'az storage blob download --container-name private-images --file "$HOME/workloads/windows-server-2019.raw" --name windows-server-2019.raw --connection-string "$AZURE_CONNECTION_STRING"'
|
||||
}
|
||||
}
|
||||
stage ('Run Windows guest integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-windows"
|
||||
}
|
||||
}
|
||||
stage ('Run Windows guest integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-windows --libc musl"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Worker build - Live Migration') {
|
||||
agent { node { label 'jammy' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Run live-migration integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "sudo modprobe openvswitch"
|
||||
sh "scripts/dev_cli.sh tests --integration-live-migration"
|
||||
}
|
||||
}
|
||||
stage ('Run live-migration integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "sudo modprobe openvswitch"
|
||||
sh "scripts/dev_cli.sh tests --integration-live-migration --libc musl"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Worker build - Metrics') {
|
||||
agent { node { label 'focal-metrics' } }
|
||||
when {
|
||||
branch 'main'
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
environment {
|
||||
METRICS_PUBLISH_KEY = credentials('52e0945f-ce7a-43d1-87af-67d1d87cc40f')
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Run metrics tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --metrics -- -- --report-file /root/workloads/metrics.json'
|
||||
}
|
||||
}
|
||||
stage ('Upload metrics report') {
|
||||
steps {
|
||||
sh 'curl -X PUT https://cloud-hypervisor-metrics.azurewebsites.net/api/publishmetrics -H "x-functions-key: $METRICS_PUBLISH_KEY" -T ~/workloads/metrics.json'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
regression {
|
||||
script {
|
||||
if (env.BRANCH_NAME == 'main') {
|
||||
slackSend (color: '#ff0000', message: '"main" branch build is now failing')
|
||||
}
|
||||
}
|
||||
}
|
||||
fixed {
|
||||
script {
|
||||
if (env.BRANCH_NAME == 'main') {
|
||||
slackSend (color: '#00ff00', message: '"main" branch build is now fixed')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pipeline {
|
||||
agent none
|
||||
stages {
|
||||
stage('Early checks') {
|
||||
agent { node { label 'built-in' } }
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage('Check for documentation only changes') {
|
||||
when {
|
||||
expression {
|
||||
return docsFileOnly()
|
||||
}
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
runWorkers = false
|
||||
echo 'Documentation only changes, no need to run the CI'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Check for fuzzer files only changes') {
|
||||
when {
|
||||
expression {
|
||||
return fuzzFileOnly()
|
||||
}
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
runWorkers = false
|
||||
echo 'Fuzzer cargo files only changes, no need to run the CI'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Check for RFC/WIP builds') {
|
||||
when {
|
||||
changeRequest comparator: 'REGEXP', title: '.*(rfc|RFC|wip|WIP).*'
|
||||
beforeAgent true
|
||||
}
|
||||
steps {
|
||||
error('Failing as this is marked as a WIP or RFC PR.')
|
||||
}
|
||||
}
|
||||
stage('Cancel older builds') {
|
||||
when { not { branch 'main' } }
|
||||
steps {
|
||||
cancelPreviousBuilds()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Build') {
|
||||
parallel {
|
||||
stage('Worker build') {
|
||||
agent { node { label 'jammy' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage('Prepare environment') {
|
||||
steps {
|
||||
sh 'scripts/prepare_vdpa.sh'
|
||||
}
|
||||
}
|
||||
stage('Run OpenAPI tests') {
|
||||
steps {
|
||||
sh 'scripts/run_openapi_tests.sh'
|
||||
}
|
||||
}
|
||||
stage('Run unit tests') {
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --unit'
|
||||
}
|
||||
}
|
||||
stage('Run integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'sudo modprobe openvswitch'
|
||||
sh 'scripts/dev_cli.sh tests --integration'
|
||||
}
|
||||
}
|
||||
stage('Run live-migration integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'sudo modprobe openvswitch'
|
||||
sh 'scripts/dev_cli.sh tests --integration-live-migration'
|
||||
}
|
||||
}
|
||||
stage('Run unit tests for musl') {
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --unit --libc musl'
|
||||
}
|
||||
}
|
||||
stage('Run integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'sudo modprobe openvswitch'
|
||||
sh 'scripts/dev_cli.sh tests --integration --libc musl'
|
||||
}
|
||||
}
|
||||
stage('Run live-migration integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'sudo modprobe openvswitch'
|
||||
sh 'scripts/dev_cli.sh tests --integration-live-migration --libc musl'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('AArch64 worker build') {
|
||||
agent { node { label 'bionic-arm64' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
environment {
|
||||
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage('Run unit tests') {
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --unit --libc musl'
|
||||
}
|
||||
}
|
||||
stage('Run integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'sudo modprobe openvswitch'
|
||||
sh 'scripts/dev_cli.sh tests --integration --libc musl'
|
||||
}
|
||||
}
|
||||
stage('Install azure-cli') {
|
||||
steps {
|
||||
installAzureCli('bionic', 'arm64')
|
||||
}
|
||||
}
|
||||
stage('Download Windows image') {
|
||||
steps {
|
||||
sh '''#!/bin/bash -x
|
||||
IMG_BASENAME=windows-11-iot-enterprise-aarch64.raw
|
||||
IMG_PATH=$HOME/workloads/$IMG_BASENAME
|
||||
IMG_GZ_PATH=$HOME/workloads/$IMG_BASENAME.gz
|
||||
IMG_GZ_BLOB_NAME=windows-11-iot-enterprise-aarch64-9-min.raw.gz
|
||||
cp "scripts/$IMG_BASENAME.sha1" "$HOME/workloads/"
|
||||
pushd "$HOME/workloads"
|
||||
if sha1sum "$IMG_BASENAME.sha1" --check; then
|
||||
exit
|
||||
fi
|
||||
popd
|
||||
mkdir -p "$HOME/workloads"
|
||||
az storage blob download \
|
||||
--container-name private-images \
|
||||
--file "$IMG_GZ_PATH" \
|
||||
--name "$IMG_GZ_BLOB_NAME" \
|
||||
--connection-string "$AZURE_CONNECTION_STRING"
|
||||
gzip -d $IMG_GZ_PATH
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Run Windows guest integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --integration-windows --libc musl'
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||
deleteDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Worker build - Windows guest') {
|
||||
agent { node { label 'jammy' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
environment {
|
||||
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage('Install azure-cli') {
|
||||
steps {
|
||||
installAzureCli('jammy', 'amd64')
|
||||
}
|
||||
}
|
||||
stage('Download assets') {
|
||||
steps {
|
||||
sh "mkdir ${env.HOME}/workloads"
|
||||
sh 'az storage blob download --container-name private-images --file "$HOME/workloads/windows-server-2019.raw" --name windows-server-2019.raw --connection-string "$AZURE_CONNECTION_STRING"'
|
||||
}
|
||||
}
|
||||
stage('Run Windows guest integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --integration-windows'
|
||||
}
|
||||
}
|
||||
stage('Run Windows guest integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --integration-windows --libc musl'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Worker build - Metrics') {
|
||||
agent { node { label 'jammy-metrics' } }
|
||||
when {
|
||||
branch 'main'
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
environment {
|
||||
METRICS_PUBLISH_KEY = credentials('52e0945f-ce7a-43d1-87af-67d1d87cc40f')
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage('Run metrics tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --metrics -- -- --report-file /root/workloads/metrics.json'
|
||||
}
|
||||
}
|
||||
stage('Upload metrics report') {
|
||||
steps {
|
||||
sh 'curl -X PUT https://cloud-hypervisor-metrics.azurewebsites.net/api/publishmetrics -H "x-functions-key: $METRICS_PUBLISH_KEY" -T ~/workloads/metrics.json'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
regression {
|
||||
script {
|
||||
if (env.BRANCH_NAME == 'main') {
|
||||
slackSend(color: '#ff0000', message: '"main" branch build is now failing', channel: '#jenkins-ci')
|
||||
}
|
||||
}
|
||||
}
|
||||
fixed {
|
||||
script {
|
||||
if (env.BRANCH_NAME == 'main') {
|
||||
slackSend(color: '#00ff00', message: '"main" branch build is now fixed', channel: '#jenkins-ci')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def cancelPreviousBuilds() {
|
||||
// Check for other instances of this particular build, cancel any that are older than the current one
|
||||
def jobName = env.JOB_NAME
|
||||
def currentBuildNumber = env.BUILD_NUMBER.toInteger()
|
||||
def currentJob = Jenkins.instance.getItemByFullName(jobName)
|
||||
// Check for other instances of this particular build, cancel any that are older than the current one
|
||||
def jobName = env.JOB_NAME
|
||||
def currentBuildNumber = env.BUILD_NUMBER.toInteger()
|
||||
def currentJob = Jenkins.instance.getItemByFullName(jobName)
|
||||
|
||||
// Loop through all instances of this particular job/branch
|
||||
for (def build : currentJob.builds) {
|
||||
if (build.isBuilding() && (build.number.toInteger() < currentBuildNumber)) {
|
||||
echo "Older build still queued. Sending kill signal to build number: ${build.number}"
|
||||
build.doStop()
|
||||
}
|
||||
}
|
||||
// Loop through all instances of this particular job/branch
|
||||
for (def build : currentJob.builds) {
|
||||
if (build.isBuilding() && (build.number.toInteger() < currentBuildNumber)) {
|
||||
echo "Older build still queued. Sending kill signal to build number: ${build.number}"
|
||||
build.doStop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def installAzureCli(distro, arch) {
|
||||
sh "sudo apt install -y ca-certificates curl apt-transport-https lsb-release gnupg"
|
||||
sh "curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null"
|
||||
sh "echo \"deb [arch=${arch}] https://packages.microsoft.com/repos/azure-cli/ ${distro} main\" | sudo tee /etc/apt/sources.list.d/azure-cli.list"
|
||||
sh "sudo apt update"
|
||||
sh "sudo apt install -y azure-cli"
|
||||
sh 'sudo apt install -y ca-certificates curl apt-transport-https lsb-release gnupg'
|
||||
sh 'curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null'
|
||||
sh "echo \"deb [arch=${arch}] https://packages.microsoft.com/repos/azure-cli/ ${distro} main\" | sudo tee /etc/apt/sources.list.d/azure-cli.list"
|
||||
sh 'sudo apt update'
|
||||
sh 'sudo apt install -y azure-cli'
|
||||
}
|
||||
|
||||
def boolean docsFileOnly() {
|
||||
if (env.CHANGE_TARGET == null) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
return sh(
|
||||
@@ -457,7 +338,7 @@ def boolean docsFileOnly() {
|
||||
|
||||
def boolean fuzzFileOnly() {
|
||||
if (env.CHANGE_TARGET == null) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
return sh(
|
||||
|
||||
352
README.md
352
README.md
@@ -4,42 +4,43 @@
|
||||
- [Architectures](#architectures)
|
||||
- [Guest OS](#guest-os)
|
||||
- [2. Getting Started](#2-getting-started)
|
||||
- [Preparation](#preparation)
|
||||
- [Install prerequisites](#install-prerequisites)
|
||||
- [Clone and build](#clone-and-build)
|
||||
- [Containerized builds and tests](#containerized-builds-and-tests)
|
||||
- [Run](#run)
|
||||
- [Cloud image](#cloud-image)
|
||||
- [Custom kernel and disk image](#custom-kernel-and-disk-image)
|
||||
- [Building your kernel](#building-your-kernel)
|
||||
- [Host OS](#host-os)
|
||||
- [Use Pre-built Binaries](#use-pre-built-binaries)
|
||||
- [Packages](#packages)
|
||||
- [Building from Source](#building-from-source)
|
||||
- [Booting Linux](#booting-linux)
|
||||
- [Firmware Booting](#firmware-booting)
|
||||
- [Custom Kernel and Disk Image](#custom-kernel-and-disk-image)
|
||||
- [Building your Kernel](#building-your-kernel)
|
||||
- [Disk image](#disk-image)
|
||||
- [Booting the guest VM](#booting-the-guest-vm)
|
||||
- [3. Status](#3-status)
|
||||
- [Hot Plug](#hot-plug)
|
||||
- [Device Model](#device-model)
|
||||
- [TODO](#todo)
|
||||
- [4. `rust-vmm` project dependency](#4-rust-vmm-project-dependency)
|
||||
- [Firecracker and crosvm](#firecracker-and-crosvm)
|
||||
- [Roadmap](#roadmap)
|
||||
- [4. Relationship with _Rust VMM_ Project](#4-relationship-with-rust-vmm-project)
|
||||
- [Differences with Firecracker and crosvm](#differences-with-firecracker-and-crosvm)
|
||||
- [5. Community](#5-community)
|
||||
- [Contribute](#contribute)
|
||||
- [Join us](#join-us)
|
||||
- [Slack](#slack)
|
||||
- [Mailing list](#mailing-list)
|
||||
- [Security issues](#security-issues)
|
||||
|
||||
# 1. What is Cloud Hypervisor?
|
||||
|
||||
Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on
|
||||
top of [KVM](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt)
|
||||
hypervisor and Microsoft Hypervisor (MSHV).
|
||||
top of the [KVM](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt)
|
||||
hypervisor and the Microsoft Hypervisor (MSHV).
|
||||
|
||||
The project focuses on exclusively running modern, cloud workloads, on top of
|
||||
a limited set of hardware architectures and platforms. Cloud workloads refers
|
||||
to those that are usually run by customers inside a cloud provider. For our
|
||||
purposes this means modern operating systems with most I/O handled by
|
||||
paravirtualised devices (i.e. virtio), no requirement for legacy devices, and
|
||||
The project focuses on running modern, _Cloud Workloads_, on specific, common,
|
||||
hardware architectures. In this case _Cloud Workloads_ refers to those that are
|
||||
run by customers inside a Cloud Service Provider. This means modern operating
|
||||
systems with most I/O handled by
|
||||
paravirtualised devices (e.g. _virtio_), no requirement for legacy devices, and
|
||||
64-bit CPUs.
|
||||
|
||||
Cloud Hypervisor is implemented in [Rust](https://www.rust-lang.org/) and is
|
||||
based on the [rust-vmm](https://github.com/rust-vmm) crates.
|
||||
based on the [Rust VMM](https://github.com/rust-vmm) crates.
|
||||
|
||||
## Objectives
|
||||
|
||||
@@ -59,7 +60,7 @@ based on the [rust-vmm](https://github.com/rust-vmm) crates.
|
||||
### Architectures
|
||||
|
||||
Cloud Hypervisor supports the `x86-64` and `AArch64` architectures. There are
|
||||
some small differences in functionality between the two architectures
|
||||
minor differences in functionality between the two architectures
|
||||
(see [#1125](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/1125)).
|
||||
|
||||
### Guest OS
|
||||
@@ -68,142 +69,121 @@ Cloud Hypervisor supports `64-bit Linux` and Windows 10/Windows Server 2019.
|
||||
|
||||
# 2. Getting Started
|
||||
|
||||
Below sections describe how to build and run Cloud Hypervisor on the `x86_64`
|
||||
platform. For getting started on the `AArch64` platform, please refer to the
|
||||
[Arm64 documentation](docs/arm64.md).
|
||||
The following sections describe how to build and run Cloud Hypervisor on the
|
||||
`x86-64` platform. For getting started on the `AArch64` platform, please refer
|
||||
to the
|
||||
[AArch64 documentation](docs/arm64.md).
|
||||
|
||||
## Preparation
|
||||
## Host OS
|
||||
|
||||
We create a folder to build and run `cloud-hypervisor` at `$HOME/cloud-hypervisor`
|
||||
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
|
||||
|
||||
The instructions below are for the `x86-64` platform. For `AArch64` please see
|
||||
the [AArch64 specific documentation](docs/arm64.md).
|
||||
|
||||
Cloud Hypervisor supports direct kernel boot (if the kernel is built with PVH
|
||||
support) or booting via a firmware (either [Rust Hypervisor
|
||||
Firmware](https://github.com/cloud-hypervisor/rust-hypervisor-firmware) or an
|
||||
edk2 UEFI firmware called `CLOUDHV`.)
|
||||
|
||||
Binary builds of the firmware files are available for the latest release of
|
||||
[Rust Hyperivor
|
||||
Firmware](https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/latest)
|
||||
and [our edk2
|
||||
repository](https://github.com/cloud-hypervisor/edk2/releases/latest)
|
||||
|
||||
The choice of firmware depends on your guest OS choice; some experimentation
|
||||
may be required.
|
||||
|
||||
### Firmware Booting
|
||||
|
||||
Cloud Hypervisor supports booting disk images containing all needed components
|
||||
to run cloud workloads, a.k.a. cloud images.
|
||||
|
||||
The following sample commands will download an Ubuntu Cloud image, converting
|
||||
it into a format that Cloud Hypervisor can use and a firmware to boot the image
|
||||
with.
|
||||
|
||||
```shell
|
||||
$ export CLOUDH=$HOME/cloud-hypervisor
|
||||
$ mkdir $CLOUDH
|
||||
```
|
||||
|
||||
## Install prerequisites
|
||||
|
||||
You need to install some prerequisite packages in order to build and test Cloud
|
||||
Hypervisor. Here, all the steps are based on Ubuntu, for other Linux
|
||||
distributions please replace the package manager and package name.
|
||||
|
||||
```shell
|
||||
# Install build-essential, git, and qemu-utils
|
||||
$ sudo apt install git build-essential qemu-utils
|
||||
# Install rust tool chain
|
||||
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
# If you want to build statically linked binary please add musl target
|
||||
$ rustup target add x86_64-unknown-linux-musl
|
||||
```
|
||||
|
||||
## Clone and build
|
||||
|
||||
First you need to clone and build the cloud-hypervisor 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
|
||||
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-amd64.img focal-server-cloudimg-amd64.raw
|
||||
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.4.1/hypervisor-fw
|
||||
$ popd
|
||||
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.4.2/hypervisor-fw
|
||||
```
|
||||
|
||||
The Ubuntu cloud images do not ship with a default password so it necessary to
|
||||
use a `cloud-init` disk image to customise the image on the first boot. A basic
|
||||
`cloud-init` image is generated by this [script](scripts/create-cloud-init.sh).
|
||||
This seeds the image with a default username/password of `cloud/cloud123`. It
|
||||
is only necessary to add this disk image on the first boot.
|
||||
|
||||
```shell
|
||||
$ pushd $CLOUDH
|
||||
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor/target/release/cloud-hypervisor
|
||||
$ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor
|
||||
$ ./create-cloud-init.sh
|
||||
$ ./cloud-hypervisor \
|
||||
--kernel ./hypervisor-fw \
|
||||
--disk path=focal-server-cloudimg-amd64.raw \
|
||||
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
--net "tap=,mac=,ip=,mask="
|
||||
$ popd
|
||||
```
|
||||
|
||||
Multiple arguments can be given to the `--disk` parameter.
|
||||
If access to the firmware messages or interaction with the boot loader (e.g.
|
||||
GRUB) is required then it necessary to switch to the serial console instead of
|
||||
`virtio-console`.
|
||||
|
||||
### Custom kernel and disk image
|
||||
```shell
|
||||
$ ./cloud-hypervisor \
|
||||
--kernel ./hypervisor-fw \
|
||||
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
--net "tap=,mac=,ip=,mask=" \
|
||||
--serial tty \
|
||||
--console off
|
||||
```
|
||||
|
||||
#### Building your kernel
|
||||
### Custom Kernel and Disk Image
|
||||
|
||||
Cloud Hypervisor also supports direct kernel boot into a `vmlinux` ELF kernel.
|
||||
In order to support virtio-watchdog we have our own development branch. You are
|
||||
of course able to use your own kernel but these instructions will continue with
|
||||
the version that we develop and test against.
|
||||
#### Building your Kernel
|
||||
|
||||
Cloud Hypervisor also supports direct kernel boot into a `vmlinux` ELF kernel (compiled with PVH support). In order to support development there is a custom branch; however provided the required options are enabled any recent kernel will suffice.
|
||||
|
||||
To build the kernel:
|
||||
|
||||
```shell
|
||||
|
||||
# Clone the Cloud Hypervisor Linux branch
|
||||
$ pushd $CLOUDH
|
||||
$ git clone --depth 1 https://github.com/cloud-hypervisor/linux.git -b ch-5.15.12 linux-cloud-hypervisor
|
||||
$ pushd linux-cloud-hypervisor
|
||||
|
||||
# Use the cloud-hypervisor kernel config to build your kernel
|
||||
$ cp $CLOUDH/cloud-hypervisor/resources/linux-config-x86_64 .config
|
||||
$ wget https://raw.githubusercontent.com/cloud-hypervisor/cloud-hypervisor/main/resources/linux-config-x86_64
|
||||
$ cp linux-config-x86_64 .config
|
||||
$ KCFLAGS="-Wa,-mx86-used-note=no" make bzImage -j `nproc`
|
||||
$ popd
|
||||
```
|
||||
@@ -213,42 +193,34 @@ The `vmlinux` kernel image will then be located at
|
||||
|
||||
#### Disk image
|
||||
|
||||
For the disk image, we will use a Ubuntu cloud image that contains a root
|
||||
partition:
|
||||
For the disk image the same Ubuntu image as before can be used. This contains
|
||||
an `ext4` root filesystem.
|
||||
|
||||
```shell
|
||||
$ pushd $CLOUDH
|
||||
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
|
||||
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-amd64.img focal-server-cloudimg-amd64.raw
|
||||
$ popd
|
||||
```
|
||||
|
||||
#### Booting the guest VM
|
||||
|
||||
Now we can directly boot into our custom kernel and make it use the Ubuntu root
|
||||
partition. If we want to have 4 vCPUs and 1024 MBytes of memory:
|
||||
These sample commands boot the disk image using the custom kernel whilst also
|
||||
supplying the desired kernel command line.
|
||||
|
||||
```shell
|
||||
$ pushd $CLOUDH
|
||||
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor/target/release/cloud-hypervisor
|
||||
$ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor
|
||||
$ ./create-cloud-init.sh
|
||||
$ ./cloud-hypervisor \
|
||||
--kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
|
||||
--disk path=focal-server-cloudimg-amd64.raw \
|
||||
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||
--cmdline "console=hvc0 root=/dev/vda1 rw" \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
--net "tap=,mac=,ip=,mask="
|
||||
```
|
||||
|
||||
The above example use the `virtio-console` device as the guest console, and this
|
||||
device may not be enabled soon enough by the guest kernel to get early kernel
|
||||
debug messages.
|
||||
If earlier kernel messages are required the serial console should be used instead of `virtio-console`.
|
||||
|
||||
When in need for earlier debug messages, using the legacy serial device based
|
||||
console is preferred:
|
||||
|
||||
```
|
||||
$ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||
```./cloud-hypervisor \
|
||||
--kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
|
||||
--console off \
|
||||
--serial tty \
|
||||
@@ -261,8 +233,8 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||
|
||||
# 3. Status
|
||||
|
||||
Cloud Hypervisor is under active development. The following stability guarantees
|
||||
are currently made:
|
||||
Cloud Hypervisor is under active development. The following stability
|
||||
guarantees are currently made:
|
||||
|
||||
* The API (including command line options) will not be removed or changed in a
|
||||
breaking way without a minimum of 2 major releases notice. Where possible
|
||||
@@ -280,16 +252,18 @@ Currently the following items are **not** guaranteed across updates:
|
||||
* The following features are considered experimental and may change
|
||||
substantially between releases: TDX, vfio-user, vDPA.
|
||||
|
||||
As of 2022-04-05, the following cloud images are supported:
|
||||
Further details can be found in the [release documentation](docs/releases.md).
|
||||
|
||||
- [Ubuntu Bionic](https://cloud-images.ubuntu.com/bionic/current/) (cloudimg)
|
||||
- [Ubuntu Focal](https://cloud-images.ubuntu.com/focal/current/) (cloudimg)
|
||||
- [Ubuntu Jammy](https://cloud-images.ubuntu.com/jammy/current/) (cloudimg)
|
||||
As of 2022-10-13, the following cloud images are supported:
|
||||
|
||||
- [Ubuntu Bionic](https://cloud-images.ubuntu.com/bionic/current/) (bionic-server-cloudimg-amd64.img)
|
||||
- [Ubuntu Focal](https://cloud-images.ubuntu.com/focal/current/) (focal-server-cloudimg-amd64.img)
|
||||
- [Ubuntu Jammy](https://cloud-images.ubuntu.com/jammy/current/) (jammy-server-cloudimg-amd64.img )
|
||||
- [Fedora 36](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/x86_64/images/) (Fedora-Cloud-Base-36-1.5.x86_64.raw.xz)
|
||||
|
||||
Direct kernel boot to userspace should work with a rootfs from most
|
||||
distributions.
|
||||
|
||||
Further details can be found in the [release documentation](docs/releases.md).
|
||||
distributions although you may need to enable exotic filesystem types in the
|
||||
reference kernel configuration (e.g. XFS or btrfs.)
|
||||
|
||||
## Hot Plug
|
||||
|
||||
@@ -302,14 +276,12 @@ Cloud Hypervisor supports hotplug of CPUs, passthrough devices (VFIO),
|
||||
Details of the device model can be found in this
|
||||
[documentation](docs/device_model.md).
|
||||
|
||||
## TODO
|
||||
## Roadmap
|
||||
|
||||
We are not tracking the Cloud Hypervisor TODO list from a specific git tracked
|
||||
file but through
|
||||
[github issues](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/new)
|
||||
instead.
|
||||
The project roadmap is tracked through a [GitHub
|
||||
project](https://github.com/orgs/cloud-hypervisor/projects/6).
|
||||
|
||||
# 4. `rust-vmm` project dependency
|
||||
# 4. Relationship with _Rust VMM_ Project
|
||||
|
||||
In order to satisfy the design goal of having a high-performance,
|
||||
security-focused hypervisor the decision was made to use the
|
||||
@@ -318,39 +290,26 @@ focus on memory and thread safety makes it an ideal candidate for implementing
|
||||
VMMs.
|
||||
|
||||
Instead of implementing the VMM components from scratch, Cloud Hypervisor is
|
||||
importing the [rust-vmm](https://github.com/rust-vmm) crates, and sharing code
|
||||
importing the [Rust VMM](https://github.com/rust-vmm) crates, and sharing code
|
||||
and architecture together with other VMMs like e.g. Amazon's
|
||||
[Firecracker](https://firecracker-microvm.github.io/) and Google's
|
||||
[crosvm](https://chromium.googlesource.com/chromiumos/platform/crosvm/).
|
||||
|
||||
Cloud Hypervisor embraces the rust-vmm project goals, which is to be able to
|
||||
share and re-use as many virtualization crates as possible. As such, the Cloud
|
||||
Hypervisor relationship with the rust-vmm project is twofold:
|
||||
Cloud Hypervisor embraces the _Rust VMM_ project's goals, which is to be able
|
||||
to share and re-use as many virtualization crates as possible.
|
||||
|
||||
1. It will use as much of the rust-vmm code as possible. Any new rust-vmm crate
|
||||
that's relevant to the project goals will be integrated as soon as possible.
|
||||
2. As it is likely that the rust-vmm project will lack some of the features that
|
||||
Cloud Hypervisor needs (e.g. ACPI, VFIO, vhost-user, etc), we will be using
|
||||
the Cloud Hypervisor VMM to implement and test them, and contribute them back
|
||||
to the rust-vmm project.
|
||||
|
||||
## Firecracker and crosvm
|
||||
## Differences with Firecracker and crosvm
|
||||
|
||||
A large part of the Cloud Hypervisor code is based on either the Firecracker or
|
||||
the crosvm projects implementations. Both of these are VMMs written in Rust with
|
||||
a focus on safety and security, like Cloud Hypervisor.
|
||||
the crosvm project's implementations. Both of these are VMMs written in Rust
|
||||
with a focus on safety and security, like Cloud Hypervisor.
|
||||
|
||||
However we want to emphasize that the Cloud Hypervisor project is neither a fork
|
||||
nor a reimplementation of any of those projects. The goals and use cases we're
|
||||
trying to meet are different. We're aiming at supporting cloud workloads, i.e.
|
||||
those modern, full Linux distribution images currently being run by Cloud
|
||||
Service Provider (CSP) tenants.
|
||||
The goal of the Cloud Hypervisor project differs from the aforementioned
|
||||
projects in that it aims to be a general purpose VMM for _Cloud Workloads_ and
|
||||
not limited to container/serverless or client workloads.
|
||||
|
||||
Our primary target is not to support client or serverless use cases, and as such
|
||||
our code base already diverges from the crosvm and Firecracker ones. As we add
|
||||
more features to support our use cases, we believe that the divergence will
|
||||
increase while at the same time sharing as much of the fundamental
|
||||
virtualization code through the rust-vmm project crates as possible.
|
||||
The Cloud Hypervisor community thanks the communities of both the Firecracker
|
||||
and crosvm projects for their excellent work.
|
||||
|
||||
# 5. Community
|
||||
|
||||
@@ -360,21 +319,28 @@ repository.
|
||||
|
||||
## Contribute
|
||||
|
||||
We are working on building a global, diverse and collaborative community around
|
||||
the Cloud Hypervisor project. Anyone who is interested in
|
||||
The project strongly believes in building a global, diverse and collaborative
|
||||
community around the Cloud Hypervisor project. Anyone who is interested in
|
||||
[contributing](CONTRIBUTING.md) to the project is welcome to participate.
|
||||
|
||||
We believe that contributing to a open source project like Cloud Hypervisor
|
||||
covers a lot more than just sending code. Testing, documentation, pull request
|
||||
Contributing to a open source project like Cloud Hypervisor covers a lot more
|
||||
than just sending code. Testing, documentation, pull request
|
||||
reviews, bug reports, feature requests, project improvement suggestions, etc,
|
||||
are all equal and welcome means of contribution. See the
|
||||
[CONTRIBUTING](CONTRIBUTING.md) document for more details.
|
||||
|
||||
## Join us
|
||||
## Slack
|
||||
|
||||
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/).
|
||||
|
||||
## 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
|
||||
|
||||
Please contact the maintainers listed in the MAINTAINERS.md file with security issues.
|
||||
|
||||
@@ -5,4 +5,4 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
vm-memory = "0.8.0"
|
||||
vm-memory = "0.10.0"
|
||||
|
||||
@@ -1083,7 +1083,7 @@ impl<'a, T> CreateField<'a, T> {
|
||||
buffer,
|
||||
offset,
|
||||
field,
|
||||
phantom: PhantomData::default(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,4 +5,4 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
vmm-sys-util = "0.10.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 {
|
||||
use Error::*;
|
||||
match self {
|
||||
Socket(e) => write!(f, "Error writing to or reading from HTTP socket: {}", e),
|
||||
SocketSendFds(e) => write!(f, "Error writing to or reading from HTTP socket: {}", e),
|
||||
StatusCodeParsing(e) => write!(f, "Error parsing HTTP status code: {}", e),
|
||||
Socket(e) => write!(f, "Error writing to or reading from HTTP socket: {e}"),
|
||||
SocketSendFds(e) => write!(f, "Error writing to or reading from HTTP socket: {e}"),
|
||||
StatusCodeParsing(e) => write!(f, "Error parsing HTTP status code: {e}"),
|
||||
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) => {
|
||||
if let Some(o) = o {
|
||||
write!(f, "Server responded with an error: {:?}: {}", s, o)
|
||||
write!(f, "Server responded with an error: {s:?}: {o}")
|
||||
} 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> {
|
||||
let header_str = format!("{}: ", header);
|
||||
let header_str = format!("{header}: ");
|
||||
res.find(&header_str)
|
||||
.map(|o| &res[o + header_str.len()..o + res[o..].find('\r').unwrap()])
|
||||
}
|
||||
@@ -141,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,
|
||||
method: &str,
|
||||
c: &str,
|
||||
full_command: &str,
|
||||
request_body: Option<&str>,
|
||||
request_fds: Vec<RawFd>,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<Option<String>, Error> {
|
||||
socket
|
||||
.send_with_fds(
|
||||
&[format!(
|
||||
"{} /api/v1/vm.{} HTTP/1.1\r\nHost: localhost\r\nAccept: */*\r\n",
|
||||
method, c
|
||||
"{method} /api/v1/{full_command} HTTP/1.1\r\nHost: localhost\r\nAccept: */*\r\n"
|
||||
)
|
||||
.as_bytes()],
|
||||
&request_fds,
|
||||
@@ -175,12 +176,69 @@ pub fn simple_api_command_with_fds<T: Read + Write + ScmSocket>(
|
||||
|
||||
socket.flush().map_err(Error::Socket)?;
|
||||
|
||||
if let Some(body) = parse_http_response(socket)? {
|
||||
println!("{}", body);
|
||||
parse_http_response(socket)
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
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>(
|
||||
socket: &mut T,
|
||||
method: &str,
|
||||
|
||||
@@ -9,20 +9,20 @@ default = []
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.62"
|
||||
anyhow = "1.0.66"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.131"
|
||||
linux-loader = { version = "0.4.0", features = ["elf", "bzimage", "pe"] }
|
||||
libc = "0.2.138"
|
||||
linux-loader = { version = "0.8.1", features = ["elf", "bzimage", "pe"] }
|
||||
log = "0.4.17"
|
||||
serde = { version = "1.0.143", features = ["rc", "derive"] }
|
||||
thiserror = "1.0.32"
|
||||
uuid = "1.1.2"
|
||||
versionize = "0.1.6"
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
thiserror = "1.0.37"
|
||||
uuid = "1.2.2"
|
||||
versionize = "0.1.9"
|
||||
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" }
|
||||
vmm-sys-util = { version = "0.10.0", features = ["with-serde"] }
|
||||
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }
|
||||
|
||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||
fdt_parser = { version = "0.1.4", package = "fdt" }
|
||||
|
||||
@@ -51,8 +51,6 @@ const SIZE_CELLS: u32 = 0x2;
|
||||
// Look for "The 1st cell..."
|
||||
const GIC_FDT_IRQ_TYPE_SPI: u32 = 0;
|
||||
const GIC_FDT_IRQ_TYPE_PPI: u32 = 1;
|
||||
const GIC_FDT_IRQ_PPI_CPU_SHIFT: u32 = 8;
|
||||
const GIC_FDT_IRQ_PPI_CPU_MASK: u32 = 0xff << GIC_FDT_IRQ_PPI_CPU_SHIFT;
|
||||
|
||||
// From https://elixir.bootlin.com/linux/v4.9.62/source/include/dt-bindings/interrupt-controller/irq.h#L17
|
||||
const IRQ_TYPE_EDGE_RISING: u32 = 1;
|
||||
@@ -121,7 +119,7 @@ pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHash
|
||||
create_gic_node(&mut fdt, gic_device)?;
|
||||
create_timer_node(&mut fdt)?;
|
||||
if pmu_supported {
|
||||
create_pmu_node(&mut fdt, vcpu_mpidr.len())?;
|
||||
create_pmu_node(&mut fdt)?;
|
||||
}
|
||||
create_clock_node(&mut fdt)?;
|
||||
create_psci_node(&mut fdt)?;
|
||||
@@ -162,7 +160,7 @@ fn create_cpu_nodes(
|
||||
let num_cpus = vcpu_mpidr.len();
|
||||
|
||||
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)?;
|
||||
fdt.property_string("device_type", "cpu")?;
|
||||
fdt.property_string("compatible", "arm,arm-v8")?;
|
||||
@@ -194,15 +192,15 @@ fn create_cpu_nodes(
|
||||
|
||||
// Create device tree nodes with regard of above mapping.
|
||||
for cluster_idx in 0..packages {
|
||||
let cluster_name = format!("cluster{:x}", cluster_idx);
|
||||
let cluster_name = format!("cluster{cluster_idx:x}");
|
||||
let cluster_node = fdt.begin_node(&cluster_name)?;
|
||||
|
||||
for core_idx in 0..cores_per_package {
|
||||
let core_name = format!("core{:x}", core_idx);
|
||||
let core_name = format!("core{core_idx:x}");
|
||||
let core_node = fdt.begin_node(&core_name)?;
|
||||
|
||||
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 cpu_idx = threads_per_core * cores_per_package * cluster_idx
|
||||
+ threads_per_core * core_idx
|
||||
@@ -249,7 +247,7 @@ fn create_memory_node(
|
||||
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)?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||
@@ -541,16 +539,9 @@ fn create_devices_node<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::Buil
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_pmu_node(fdt: &mut FdtWriter, cpu_nums: usize) -> FdtWriterResult<()> {
|
||||
let num_cpus = cpu_nums as u64 as u32;
|
||||
fn create_pmu_node(fdt: &mut FdtWriter) -> FdtWriterResult<()> {
|
||||
let compatible = "arm,armv8-pmuv3";
|
||||
let cpu_mask: u32 =
|
||||
(((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK;
|
||||
let irq = [
|
||||
GIC_FDT_IRQ_TYPE_PPI,
|
||||
AARCH64_PMU_IRQ,
|
||||
cpu_mask | IRQ_TYPE_LEVEL_HI,
|
||||
];
|
||||
let irq = [GIC_FDT_IRQ_TYPE_PPI, AARCH64_PMU_IRQ, IRQ_TYPE_LEVEL_HI];
|
||||
|
||||
let pmu_node = fdt.begin_node("pmu")?;
|
||||
fdt.property_string("compatible", compatible)?;
|
||||
@@ -679,7 +670,7 @@ fn create_pci_nodes(
|
||||
|
||||
// See kernel document Documentation/devicetree/bindings/virtio/iommu.txt
|
||||
// for virtio-iommu node settings.
|
||||
let virtio_iommu_node_name = format!("virtio_iommu@{:x}", virtio_iommu_bdf);
|
||||
let virtio_iommu_node_name = format!("virtio_iommu@{virtio_iommu_bdf:x}");
|
||||
let virtio_iommu_node = fdt.begin_node(&virtio_iommu_node_name)?;
|
||||
fdt.property_u32("#iommu-cells", 1)?;
|
||||
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;
|
||||
|
||||
/// 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.
|
||||
/// 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;
|
||||
|
||||
/// 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_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_SIZE: u64 = 0x0400_0000;
|
||||
|
||||
/// TPM Address Range
|
||||
/// This Address range is specific to CRB Interface
|
||||
pub const TPM_START: GuestAddress = GuestAddress(0xfed4_0000);
|
||||
pub const TPM_SIZE: u64 = 0x1000;
|
||||
|
||||
/// Start of 64-bit RAM.
|
||||
pub const RAM_64BIT_START: GuestAddress = GuestAddress(0x1_0000_0000);
|
||||
|
||||
|
||||
@@ -39,3 +39,6 @@ macro_rules! arm64_sys_reg {
|
||||
}
|
||||
|
||||
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 {
|
||||
return Err(Error::UefiTooBig);
|
||||
}
|
||||
uefi_image
|
||||
.seek(SeekFrom::Start(0))
|
||||
.map_err(|_| Error::SeekUefiStart)?;
|
||||
uefi_image.rewind().map_err(|_| Error::SeekUefiStart)?;
|
||||
guest_mem
|
||||
.read_exact_from(guest_addr, uefi_image, uefi_size)
|
||||
.map_err(|_| Error::ReadUefiImage)
|
||||
|
||||
@@ -145,7 +145,7 @@ pub const PAGE_SIZE: usize = 4096;
|
||||
|
||||
impl fmt::Display for DeviceType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,11 @@ pub const KVM_TSS_SIZE: u64 = (3 * 4) << 10;
|
||||
pub const KVM_IDENTITY_MAP_START: GuestAddress = GuestAddress(KVM_TSS_START.0 + KVM_TSS_SIZE);
|
||||
pub const KVM_IDENTITY_MAP_SIZE: u64 = 4 << 10;
|
||||
|
||||
/// TPM Address Range
|
||||
/// This Address range is specific to CRB Interface
|
||||
pub const TPM_START: GuestAddress = GuestAddress(0xfed4_0000);
|
||||
pub const TPM_SIZE: u64 = 0x1000;
|
||||
|
||||
// IOAPIC
|
||||
pub const IOAPIC_START: GuestAddress = GuestAddress(0xfec0_0000);
|
||||
pub const IOAPIC_SIZE: u64 = 0x20;
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::GuestMemoryMmap;
|
||||
use crate::InitramfsConfig;
|
||||
use crate::RegionType;
|
||||
use hypervisor::arch::x86::{CpuIdEntry, CPUID_FLAG_VALID_INDEX};
|
||||
use hypervisor::HypervisorError;
|
||||
use hypervisor::{HypervisorCpuError, HypervisorError};
|
||||
use linux_loader::loader::bootparam::boot_params;
|
||||
use linux_loader::loader::elf::start_info::{
|
||||
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
|
||||
@@ -36,6 +36,7 @@ pub mod tdx;
|
||||
const TSC_DEADLINE_TIMER_ECX_BIT: u8 = 24; // tsc deadline timer ecx bit.
|
||||
const HYPERVISOR_ECX_BIT: u8 = 31; // Hypervisor ecx bit.
|
||||
const MTRR_EDX_BIT: u8 = 12; // Hypervisor ecx bit.
|
||||
const INVARIANT_TSC_EDX_BIT: u8 = 8; // Invariant TSC bit on 0x8000_0007 EDX
|
||||
|
||||
// KVM feature bits
|
||||
const KVM_FEATURE_ASYNC_PF_INT_BIT: u8 = 14;
|
||||
@@ -191,6 +192,9 @@ pub enum Error {
|
||||
// Error writing EBDA address
|
||||
EbdaSetup(vm_memory::GuestMemoryError),
|
||||
|
||||
// Error getting CPU TSC frequency
|
||||
GetTscFrequency(HypervisorCpuError),
|
||||
|
||||
/// Error retrieving TDX capabilities through the hypervisor (kvm/mshv) API
|
||||
#[cfg(feature = "tdx")]
|
||||
TdxCapabilities(HypervisorError),
|
||||
@@ -231,7 +235,7 @@ impl CpuidPatch {
|
||||
) {
|
||||
let mut entry_found = false;
|
||||
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;
|
||||
match reg {
|
||||
CpuidReg::EAX => {
|
||||
@@ -631,6 +635,20 @@ pub fn generate_common_cpuid(
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copy host L2 cache details if not populated by KVM
|
||||
0x8000_0006 => {
|
||||
if entry.eax == 0 && entry.ebx == 0 && entry.ecx == 0 && entry.edx == 0 {
|
||||
// SAFETY: cpuid called with valid leaves
|
||||
if unsafe { std::arch::x86_64::__cpuid(0x8000_0000).eax } >= 0x8000_0006 {
|
||||
// SAFETY: cpuid called with valid leaves
|
||||
let leaf = unsafe { std::arch::x86_64::__cpuid(0x8000_0006) };
|
||||
entry.eax = leaf.eax;
|
||||
entry.ebx = leaf.ebx;
|
||||
entry.ecx = leaf.ecx;
|
||||
entry.edx = leaf.edx;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set CPU physical bits
|
||||
0x8000_0008 => {
|
||||
entry.eax = (entry.eax & 0xffff_ff00) | (phys_bits as u32 & 0xff);
|
||||
@@ -735,6 +753,41 @@ pub fn configure_vcpu(
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id));
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1f, None, CpuidReg::EDX, u32::from(id));
|
||||
|
||||
// Set ApicId in cpuid for each vcpu
|
||||
// SAFETY: get host cpuid when eax=1
|
||||
let mut cpu_ebx = unsafe { core::arch::x86_64::__cpuid(1) }.ebx;
|
||||
cpu_ebx &= 0xffffff;
|
||||
cpu_ebx |= (id as u32) << 24;
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1, None, CpuidReg::EBX, cpu_ebx);
|
||||
|
||||
// The TSC frequency CPUID leaf should not be included when running with HyperV emulation
|
||||
if !kvm_hyperv {
|
||||
if let Some(tsc_khz) = vcpu.tsc_khz().map_err(Error::GetTscFrequency)? {
|
||||
// Need to check that the TSC doesn't vary with dynamic frequency
|
||||
// SAFETY: cpuid called with valid leaves
|
||||
if unsafe { std::arch::x86_64::__cpuid(0x8000_0007) }.edx
|
||||
& (1u32 << INVARIANT_TSC_EDX_BIT)
|
||||
> 0
|
||||
{
|
||||
CpuidPatch::set_cpuid_reg(
|
||||
&mut cpuid,
|
||||
0x4000_0000,
|
||||
None,
|
||||
CpuidReg::EAX,
|
||||
0x4000_0010,
|
||||
);
|
||||
cpuid.retain(|c| c.function != 0x4000_0010);
|
||||
cpuid.push(CpuIdEntry {
|
||||
function: 0x4000_0010,
|
||||
eax: tsc_khz,
|
||||
ebx: 1000000, /* LAPIC resolution of 1ns (freq: 1GHz) is hardcoded in KVM's
|
||||
* APIC_BUS_CYCLE_NS */
|
||||
..Default::default()
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
vcpu.set_cpuid2(&cpuid)
|
||||
.map_err(|e| Error::SetSupportedCpusFailed(e.into()))?;
|
||||
|
||||
@@ -764,11 +817,11 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
|
||||
.checked_add(layout::MEM_32BIT_DEVICES_SIZE)
|
||||
.expect("32-bit reserved region is too large");
|
||||
|
||||
let requested_memory_size = GuestAddress(size as u64);
|
||||
let requested_memory_size = GuestAddress(size);
|
||||
let mut regions = Vec::new();
|
||||
|
||||
// case1: guest memory fits before the gap
|
||||
if size as u64 <= layout::MEM_32BIT_RESERVED_START.raw_value() {
|
||||
if size <= layout::MEM_32BIT_RESERVED_START.raw_value() {
|
||||
regions.push((GuestAddress(0), size as usize, RegionType::Ram));
|
||||
// case2: guest memory extends beyond the gap
|
||||
} else {
|
||||
@@ -865,7 +918,7 @@ fn configure_pvh(
|
||||
start_info.0.magic = XEN_HVM_START_MAGIC_VALUE;
|
||||
start_info.0.version = 1; // pvh has version 1
|
||||
start_info.0.nr_modules = 0;
|
||||
start_info.0.cmdline_paddr = cmdline_addr.raw_value() as u64;
|
||||
start_info.0.cmdline_paddr = cmdline_addr.raw_value();
|
||||
start_info.0.memmap_paddr = layout::MEMMAP_START.raw_value();
|
||||
|
||||
if let Some(rsdp_addr) = rsdp_addr {
|
||||
@@ -934,7 +987,7 @@ fn configure_pvh(
|
||||
add_memmap_entry(
|
||||
&mut memmap,
|
||||
sgx_epc_region.start().raw_value(),
|
||||
sgx_epc_region.size() as u64,
|
||||
sgx_epc_region.size(),
|
||||
E820_RESERVED,
|
||||
);
|
||||
}
|
||||
@@ -1126,7 +1179,7 @@ fn update_cpuid_sgx(
|
||||
for (i, epc_section) in epc_sections.iter().enumerate() {
|
||||
let subleaf_idx = i + 2;
|
||||
let start = epc_section.start().raw_value();
|
||||
let size = epc_section.size() as u64;
|
||||
let size = epc_section.size();
|
||||
let eax = (start & 0xffff_f000) as u32 | 0x1;
|
||||
let ebx = (start >> 32) as u32;
|
||||
let ecx = (size & 0xffff_f000) as u32 | (leaf.ecx & 0xf);
|
||||
@@ -1302,6 +1355,6 @@ mod tests {
|
||||
add_memmap_entry(&mut memmap, 0, 0x1000, E820_RAM);
|
||||
add_memmap_entry(&mut memmap, 0x10000, 0xa000, E820_RESERVED);
|
||||
|
||||
assert_eq!(format!("{:?}", memmap), format!("{:?}", expected_memmap));
|
||||
assert_eq!(format!("{memmap:?}"), format!("{expected_memmap:?}"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ mod tests {
|
||||
mpspec::MP_IOAPIC => mem::size_of::<MpcIoapicWrapper>(),
|
||||
mpspec::MP_INTSRC => mem::size_of::<MpcIntsrcWrapper>(),
|
||||
mpspec::MP_LINTSRC => mem::size_of::<MpcLintsrcWrapper>(),
|
||||
_ => panic!("unrecognized mpc table entry type: {}", type_),
|
||||
_ => panic!("unrecognized mpc table entry type: {type_}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ pub fn configure_segments_and_sregs(
|
||||
mem: &GuestMemoryMmap,
|
||||
sregs: &mut SpecialRegisters,
|
||||
) -> Result<()> {
|
||||
let gdt_table: [u64; BOOT_GDT_MAX as usize] = {
|
||||
let gdt_table: [u64; BOOT_GDT_MAX] = {
|
||||
// Configure GDT entries as specified by PVH boot protocol
|
||||
[
|
||||
gdt_entry(0, 0, 0), // NULL
|
||||
|
||||
@@ -48,10 +48,10 @@ impl Display for Error {
|
||||
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),
|
||||
ParseUuid(e) => format!("Failure to parse uuid: {e}"),
|
||||
};
|
||||
|
||||
write!(f, "SMBIOS error: {}", description)
|
||||
write!(f, "SMBIOS error: {description}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ pub struct TdvfSection {
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub enum TdvfSectionType {
|
||||
Bfv,
|
||||
Cfv,
|
||||
@@ -55,15 +55,10 @@ pub enum TdvfSectionType {
|
||||
PermMem,
|
||||
Payload,
|
||||
PayloadParam,
|
||||
#[default]
|
||||
Reserved = 0xffffffff,
|
||||
}
|
||||
|
||||
impl Default for TdvfSectionType {
|
||||
fn default() -> Self {
|
||||
TdvfSectionType::Reserved
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_tdvf_sections(file: &mut File) -> Result<Vec<TdvfSection>, TdvfError> {
|
||||
// The 32-bit offset to the TDVF metadata is located 32 bytes from
|
||||
// the end of the file.
|
||||
@@ -120,21 +115,16 @@ pub fn parse_tdvf_sections(file: &mut File) -> Result<Vec<TdvfSection>, TdvfErro
|
||||
}
|
||||
|
||||
#[repr(u16)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
enum HobType {
|
||||
Handoff = 0x1,
|
||||
ResourceDescriptor = 0x3,
|
||||
GuidExtension = 0x4,
|
||||
#[default]
|
||||
Unused = 0xfffe,
|
||||
EndOfHobList = 0xffff,
|
||||
}
|
||||
|
||||
impl Default for HobType {
|
||||
fn default() -> Self {
|
||||
HobType::Unused
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
struct HobHeader {
|
||||
@@ -184,19 +174,14 @@ struct HobGuidType {
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub enum PayloadImageType {
|
||||
#[default]
|
||||
ExecutablePayload,
|
||||
BzImage,
|
||||
RawVmLinux,
|
||||
}
|
||||
|
||||
impl Default for PayloadImageType {
|
||||
fn default() -> Self {
|
||||
PayloadImageType::ExecutablePayload
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
pub struct PayloadInfo {
|
||||
@@ -447,7 +432,7 @@ mod tests {
|
||||
let mut f = std::fs::File::open("tdvf.fd").unwrap();
|
||||
let sections = parse_tdvf_sections(&mut f).unwrap();
|
||||
for section in sections {
|
||||
eprintln!("{:x?}", section)
|
||||
eprintln!("{section:x?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,17 @@ edition = "2021"
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
io-uring = "0.5.3"
|
||||
libc = "0.2.131"
|
||||
io-uring = "0.5.9"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
qcow = { path = "../qcow" }
|
||||
thiserror = "1.0.32"
|
||||
versionize = "0.1.6"
|
||||
thiserror = "1.0.37"
|
||||
versionize = "0.1.9"
|
||||
versionize_derive = "0.1.4"
|
||||
vhdx = { path = "../vhdx" }
|
||||
virtio-bindings = { version = "0.1.0", features = ["virtio-v5_0_0"] }
|
||||
virtio-queue = "0.5.0"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
virtio-queue = "0.7.0"
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
|
||||
@@ -51,23 +51,23 @@ pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Guest gave us bad memory addresses.")]
|
||||
#[error("Guest gave us bad memory addresses")]
|
||||
GuestMemory(GuestMemoryError),
|
||||
#[error("Guest gave us offsets that would have overflowed a usize.")]
|
||||
#[error("Guest gave us offsets that would have overflowed a usize")]
|
||||
CheckedOffset(GuestAddress, usize),
|
||||
#[error("Guest gave us a write only descriptor that protocol says to read from.")]
|
||||
#[error("Guest gave us a write only descriptor that protocol says to read from")]
|
||||
UnexpectedWriteOnlyDescriptor,
|
||||
#[error("Guest gave us a read only descriptor that protocol says to write to.")]
|
||||
#[error("Guest gave us a read only descriptor that protocol says to write to")]
|
||||
UnexpectedReadOnlyDescriptor,
|
||||
#[error("Guest gave us too few descriptors in a descriptor chain.")]
|
||||
#[error("Guest gave us too few descriptors in a descriptor chain")]
|
||||
DescriptorChainTooShort,
|
||||
#[error("Guest gave us a descriptor that was too short to use.")]
|
||||
#[error("Guest gave us a descriptor that was too short to use")]
|
||||
DescriptorLengthTooSmall,
|
||||
#[error("Getting a block's metadata fails for any reason.")]
|
||||
#[error("Getting a block's metadata fails for any reason")]
|
||||
GetFileMetadata,
|
||||
#[error("The requested operation would cause a seek beyond disk end.")]
|
||||
#[error("The requested operation would cause a seek beyond disk end")]
|
||||
InvalidOffset,
|
||||
#[error("The requested operation does not support multiple descriptors.")]
|
||||
#[error("The requested operation does not support multiple descriptors")]
|
||||
TooManyDescriptors,
|
||||
}
|
||||
|
||||
@@ -566,15 +566,15 @@ pub fn block_io_uring_is_supported() -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check IORING_OP_READ is supported
|
||||
if !probe.is_supported(opcode::Read::CODE) {
|
||||
info!("{} IORING_OP_READ operation not supported", error_msg);
|
||||
// Check IORING_OP_READV is supported
|
||||
if !probe.is_supported(opcode::Readv::CODE) {
|
||||
info!("{} IORING_OP_READV operation not supported", error_msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check IORING_OP_WRITE is supported
|
||||
if !probe.is_supported(opcode::Write::CODE) {
|
||||
info!("{} IORING_OP_WRITE operation not supported", error_msg);
|
||||
// Check IORING_OP_WRITEV is supported
|
||||
if !probe.is_supported(opcode::Writev::CODE) {
|
||||
info!("{} IORING_OP_WRITEV operation not supported", error_msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ impl DiskFile for QcowDiskSync {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
let mut file = self.qcow_file.lock().unwrap();
|
||||
|
||||
Ok(file.seek(SeekFrom::End(0)).map_err(DiskFileError::Size)? as u64)
|
||||
file.seek(SeekFrom::End(0)).map_err(DiskFileError::Size)
|
||||
}
|
||||
|
||||
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
|
||||
@@ -23,10 +23,9 @@ impl RawFileDisk {
|
||||
|
||||
impl DiskFile for RawFileDisk {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
Ok(self
|
||||
.file
|
||||
self.file
|
||||
.seek(SeekFrom::End(0))
|
||||
.map_err(DiskFileError::Size)? as u64)
|
||||
.map_err(DiskFileError::Size)
|
||||
}
|
||||
|
||||
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
|
||||
@@ -22,10 +22,9 @@ impl RawFileDiskSync {
|
||||
|
||||
impl DiskFile for RawFileDiskSync {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
Ok(self
|
||||
.file
|
||||
self.file
|
||||
.seek(SeekFrom::End(0))
|
||||
.map_err(DiskFileError::Size)? as u64)
|
||||
.map_err(DiskFileError::Size)
|
||||
}
|
||||
|
||||
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
@@ -72,7 +71,7 @@ impl AsyncIo for RawFileSync {
|
||||
let result = unsafe {
|
||||
libc::preadv(
|
||||
self.fd as libc::c_int,
|
||||
iovecs.as_ptr() as *const libc::iovec,
|
||||
iovecs.as_ptr(),
|
||||
iovecs.len() as libc::c_int,
|
||||
offset,
|
||||
)
|
||||
@@ -96,7 +95,7 @@ impl AsyncIo for RawFileSync {
|
||||
let result = unsafe {
|
||||
libc::pwritev(
|
||||
self.fd as libc::c_int,
|
||||
iovecs.as_ptr() as *const libc::iovec,
|
||||
iovecs.as_ptr(),
|
||||
iovecs.len() as libc::c_int,
|
||||
offset,
|
||||
)
|
||||
@@ -118,7 +117,7 @@ impl AsyncIo for RawFileSync {
|
||||
}
|
||||
|
||||
if let Some(user_data) = user_data {
|
||||
self.completion_list.push((user_data, result as i32));
|
||||
self.completion_list.push((user_data, result));
|
||||
self.eventfd.write(1).unwrap();
|
||||
}
|
||||
|
||||
|
||||
4
build.rs
4
build.rs
@@ -11,7 +11,7 @@ use std::process::Command;
|
||||
fn main() {
|
||||
let mut version = "v".to_owned() + crate_version!();
|
||||
|
||||
if let Ok(git_out) = Command::new("git").args(&["describe", "--dirty"]).output() {
|
||||
if let Ok(git_out) = Command::new("git").args(["describe", "--dirty"]).output() {
|
||||
if git_out.status.success() {
|
||||
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {
|
||||
version = git_out_str;
|
||||
@@ -23,5 +23,5 @@ fn main() {
|
||||
// variable BUILT_VERSION, so that it can be reused from the binary.
|
||||
// Particularly, this is used from src/main.rs to display the exact
|
||||
// version.
|
||||
println!("cargo:rustc-env=BUILT_VERSION={}", version);
|
||||
println!("cargo:rustc-env=BUILT_VERSION={version}");
|
||||
}
|
||||
|
||||
@@ -6,24 +6,25 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { path = "../acpi_tables" }
|
||||
anyhow = "1.0.62"
|
||||
anyhow = "1.0.66"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "1.3.2"
|
||||
bitflags = "2.4.1"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.131"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
versionize = "0.1.6"
|
||||
phf = { version = "0.11.1", features = ["macros"] }
|
||||
thiserror = "1.0.37"
|
||||
tpm = { path = "../tpm" }
|
||||
versionize = "0.1.9"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = "0.8.0"
|
||||
vm-memory = "0.10.0"
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vmm-sys-util = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||
arch = { path = "../arch" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
cmos = []
|
||||
fwdebug = []
|
||||
|
||||
@@ -105,18 +105,17 @@ impl BusDevice for AcpiGedDevice {
|
||||
impl Aml for AcpiGedDevice {
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
aml::Device::new(
|
||||
"_SB_.GED_".into(),
|
||||
"_SB_.GEC_".into(),
|
||||
vec![
|
||||
&aml::Name::new("_HID".into(), &"ACPI0013"),
|
||||
&aml::Name::new("_UID".into(), &aml::ZERO),
|
||||
&aml::Name::new("_HID".into(), &aml::EisaName::new("PNP0A06")),
|
||||
&aml::Name::new("_UID".into(), &"Generic Event Controller"),
|
||||
&aml::Name::new(
|
||||
"_CRS".into(),
|
||||
&aml::ResourceTemplate::new(vec![&aml::Interrupt::new(
|
||||
&aml::ResourceTemplate::new(vec![&aml::AddressSpace::new_memory(
|
||||
aml::AddressSpaceCachable::NotCacheable,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
self.ged_irq,
|
||||
self.address.0,
|
||||
self.address.0 + GED_DEVICE_ACPI_SIZE as u64 - 1,
|
||||
)]),
|
||||
),
|
||||
&aml::OpRegion::new(
|
||||
@@ -132,8 +131,8 @@ impl Aml for AcpiGedDevice {
|
||||
vec![aml::FieldEntry::Named(*b"GDAT", 8)],
|
||||
),
|
||||
&aml::Method::new(
|
||||
"_EVT".into(),
|
||||
1,
|
||||
"ESCN".into(),
|
||||
0,
|
||||
true,
|
||||
vec![
|
||||
&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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,17 @@ use super::interrupt_controller::{Error, InterruptController};
|
||||
extern crate arch;
|
||||
use anyhow::anyhow;
|
||||
use arch::layout;
|
||||
use hypervisor::{arch::aarch64::gic::Vgic, CpuState};
|
||||
use hypervisor::{
|
||||
arch::aarch64::gic::{Vgic, VgicConfig},
|
||||
CpuState,
|
||||
};
|
||||
use std::result;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
|
||||
LegacyIrqSourceConfig, MsiIrqGroupConfig,
|
||||
};
|
||||
use vm_memory::Address;
|
||||
use vm_memory::address::Address;
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
@@ -52,21 +55,28 @@ impl Gic {
|
||||
})
|
||||
}
|
||||
|
||||
/// 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 create_vgic(
|
||||
&mut self,
|
||||
vm: &Arc<dyn hypervisor::Vm>,
|
||||
vcpu_count: u64,
|
||||
config: VgicConfig,
|
||||
) -> Result<Arc<Mutex<dyn Vgic>>> {
|
||||
let vgic = vm
|
||||
.create_vgic(
|
||||
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)?;
|
||||
let vgic = vm.create_vgic(config).map_err(Error::CreateGic)?;
|
||||
self.vgic = Some(vgic.clone());
|
||||
Ok(vgic.clone())
|
||||
}
|
||||
|
||||
@@ -40,11 +40,11 @@ impl DebugIoPortRange {
|
||||
impl fmt::Display for DebugIoPortRange {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
DebugIoPortRange::Firmware => write!(f, "{}: Firmware", DEBUG_IOPORT_PREFIX),
|
||||
DebugIoPortRange::Bootloader => write!(f, "{}: Bootloader", DEBUG_IOPORT_PREFIX),
|
||||
DebugIoPortRange::Kernel => write!(f, "{}: Kernel", DEBUG_IOPORT_PREFIX),
|
||||
DebugIoPortRange::Userspace => write!(f, "{}: Userspace", DEBUG_IOPORT_PREFIX),
|
||||
DebugIoPortRange::Custom => write!(f, "{}: Custom", DEBUG_IOPORT_PREFIX),
|
||||
DebugIoPortRange::Firmware => write!(f, "{DEBUG_IOPORT_PREFIX}: Firmware"),
|
||||
DebugIoPortRange::Bootloader => write!(f, "{DEBUG_IOPORT_PREFIX}: Bootloader"),
|
||||
DebugIoPortRange::Kernel => write!(f, "{DEBUG_IOPORT_PREFIX}: Kernel"),
|
||||
DebugIoPortRange::Userspace => write!(f, "{DEBUG_IOPORT_PREFIX}: Userspace"),
|
||||
DebugIoPortRange::Custom => write!(f, "{DEBUG_IOPORT_PREFIX}: Custom"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,13 +51,13 @@ pub enum Error {
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
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::GpioInterruptFailure(ref e) => {
|
||||
write!(f, "Could not trigger GPIO interrupt: {}.", e)
|
||||
write!(f, "Could not trigger GPIO interrupt: {e}.")
|
||||
}
|
||||
Error::GpioTriggerKeyFailure(key) => {
|
||||
write!(f, "Invalid GPIO Input key triggerd: {}.", key)
|
||||
write!(f, "Invalid GPIO Input key triggerd: {key}.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
mod cmos;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
mod debug_port;
|
||||
#[cfg(feature = "fwdebug")]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
mod fwdebug;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
mod gpio_pl061;
|
||||
@@ -22,7 +22,7 @@ mod uart_pl011;
|
||||
pub use self::cmos::Cmos;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use self::debug_port::DebugPort;
|
||||
#[cfg(feature = "fwdebug")]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use self::fwdebug::FwDebugDevice;
|
||||
pub use self::i8042::I8042Device;
|
||||
pub use self::serial::Serial;
|
||||
|
||||
@@ -48,8 +48,8 @@ pub enum Error {
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Error::BadWriteOffset(offset) => write!(f, "Bad Write Offset: {}", offset),
|
||||
Error::InterruptFailure(e) => write!(f, "Failed to trigger interrupt: {}", e),
|
||||
Error::BadWriteOffset(offset) => write!(f, "Bad Write Offset: {offset}"),
|
||||
Error::InterruptFailure(e) => write!(f, "Failed to trigger interrupt: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ impl Serial {
|
||||
}
|
||||
|
||||
fn handle_write(&mut self, offset: u8, v: u8) -> Result<()> {
|
||||
match offset as u8 {
|
||||
match offset {
|
||||
DLAB_LOW if self.is_dlab_set() => {
|
||||
self.baud_divisor = (self.baud_divisor & 0xff00) | u16::from(v)
|
||||
}
|
||||
|
||||
@@ -60,11 +60,11 @@ pub enum Error {
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
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::InterruptFailure(e) => write!(f, "Failed to trigger interrupt: {}", e),
|
||||
Error::WriteAllFailure(e) => write!(f, "Failed to write: {}", e),
|
||||
Error::FlushFailure(e) => write!(f, "Failed to flush: {}", e),
|
||||
Error::InterruptFailure(e) => write!(f, "Failed to trigger interrupt: {e}"),
|
||||
Error::WriteAllFailure(e) => write!(f, "Failed to write: {e}"),
|
||||
Error::FlushFailure(e) => write!(f, "Failed to flush: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,25 +306,25 @@ impl Pl011 {
|
||||
let elapsed = self.timestamp.elapsed();
|
||||
|
||||
match self.debug {
|
||||
0x00..=0x1f => info!(
|
||||
0x00..=0x1f => warn!(
|
||||
"[Debug I/O port: Firmware code: 0x{:x}] {}.{:>06} seconds",
|
||||
self.debug,
|
||||
elapsed.as_secs(),
|
||||
elapsed.as_micros()
|
||||
),
|
||||
0x20..=0x3f => info!(
|
||||
0x20..=0x3f => warn!(
|
||||
"[Debug I/O port: Bootloader code: 0x{:x}] {}.{:>06} seconds",
|
||||
self.debug,
|
||||
elapsed.as_secs(),
|
||||
elapsed.as_micros()
|
||||
),
|
||||
0x40..=0x5f => info!(
|
||||
0x40..=0x5f => warn!(
|
||||
"[Debug I/O port: Kernel code: 0x{:x}] {}.{:>06} seconds",
|
||||
self.debug,
|
||||
elapsed.as_secs(),
|
||||
elapsed.as_micros()
|
||||
),
|
||||
0x60..=0x7f => info!(
|
||||
0x60..=0x7f => warn!(
|
||||
"[Debug I/O port: Userspace code: 0x{:x}] {}.{:>06} seconds",
|
||||
self.debug,
|
||||
elapsed.as_secs(),
|
||||
|
||||
@@ -19,6 +19,7 @@ pub mod interrupt_controller;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub mod ioapic;
|
||||
pub mod legacy;
|
||||
pub mod tpm;
|
||||
|
||||
pub use self::acpi::{AcpiGedDevice, AcpiPmTimerDevice, AcpiShutdownDevice};
|
||||
|
||||
|
||||
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))
|
||||
);
|
||||
}
|
||||
}
|
||||
82
docs/building.md
Normal file
82
docs/building.md
Normal file
@@ -0,0 +1,82 @@
|
||||
- [Building Cloud Hypervisor](#building-cloud-hypervisor)
|
||||
- [Preparation](#preparation)
|
||||
- [Install prerequisites](#install-prerequisites)
|
||||
- [Clone and build](#clone-and-build)
|
||||
- [Containerized builds and tests](#containerized-builds-and-tests)
|
||||
|
||||
# Building Cloud Hypervisor
|
||||
|
||||
We recommend users use the pre-built binaries that are mentioned in the README.md file in the root of the repository. Building from source is only necessary if you wish to make modifications.
|
||||
|
||||
## Preparation
|
||||
|
||||
We create a folder to build and run `cloud-hypervisor` at `$HOME/cloud-hypervisor`
|
||||
|
||||
```shell
|
||||
$ export CLOUDH=$HOME/cloud-hypervisor
|
||||
$ mkdir $CLOUDH
|
||||
```
|
||||
|
||||
## Install prerequisites
|
||||
|
||||
You need to install some prerequisite packages in order to build and test Cloud
|
||||
Hypervisor. Here, all the steps are based on Ubuntu, for other Linux
|
||||
distributions please replace the package manager and package name.
|
||||
|
||||
```shell
|
||||
# Install build-essential, git, and qemu-utils
|
||||
$ sudo apt install git build-essential qemu-utils
|
||||
# Install rust tool chain
|
||||
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
# If you want to build statically linked binary please add musl target
|
||||
$ rustup target add x86_64-unknown-linux-musl
|
||||
```
|
||||
|
||||
## Clone and build
|
||||
|
||||
First you need to clone and build the Cloud Hypervisor repository:
|
||||
|
||||
```shell
|
||||
$ pushd $CLOUDH
|
||||
$ git clone https://github.com/cloud-hypervisor/cloud-hypervisor.git
|
||||
$ cd cloud-hypervisor
|
||||
$ cargo build --release
|
||||
|
||||
# We need to give the cloud-hypervisor binary the NET_ADMIN capabilities for it to set TAP interfaces up on the host.
|
||||
$ sudo setcap cap_net_admin+ep ./target/release/cloud-hypervisor
|
||||
|
||||
# If you want to build statically linked binary
|
||||
$ cargo build --release --target=x86_64-unknown-linux-musl --all
|
||||
$ popd
|
||||
```
|
||||
|
||||
This will build a `cloud-hypervisor` binary under
|
||||
`$CLOUDH/cloud-hypervisor/target/release/cloud-hypervisor`.
|
||||
|
||||
### Containerized builds and tests
|
||||
|
||||
If you want to build and test Cloud Hypervisor without having to install all the
|
||||
required dependencies (The rust toolchain, cargo tools, etc), you can also use
|
||||
Cloud Hypervisor's development script: `dev_cli.sh`. Please note that upon its
|
||||
first invocation, this script will pull a fairly large container image.
|
||||
|
||||
For example, to build the Cloud Hypervisor release binary:
|
||||
|
||||
```shell
|
||||
$ pushd $CLOUDH
|
||||
$ cd cloud-hypervisor
|
||||
$ ./scripts/dev_cli.sh build --release
|
||||
```
|
||||
|
||||
With `dev_cli.sh`, one can also run the Cloud Hypervisor CI locally. This can be
|
||||
very convenient for debugging CI errors without having to fully rely on the
|
||||
Cloud Hypervisor CI infrastructure.
|
||||
|
||||
For example, to run the Cloud Hypervisor unit tests:
|
||||
|
||||
```shell
|
||||
$ ./scripts/dev_cli.sh tests --unit
|
||||
```
|
||||
|
||||
Run the `./scripts/dev_cli.sh --help` command to view all the supported
|
||||
development script commands and their related options.
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
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
|
||||
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:
|
||||
@@ -44,4 +44,4 @@ Continuing.
|
||||
|
||||
Breakpoint 1, 0x00000000001121b7 in ?? ()
|
||||
(gdb)
|
||||
```
|
||||
```
|
||||
|
||||
@@ -61,7 +61,8 @@ meaning it will be printing guest kernel logs to the `virtio-console` device.
|
||||
|
||||
```bash
|
||||
./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 \
|
||||
--memory size=1G \
|
||||
--disk path=tdx_guest_img
|
||||
@@ -72,7 +73,8 @@ firmware:
|
||||
|
||||
```bash
|
||||
./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 \
|
||||
--memory size=1G \
|
||||
--disk path=tdx_guest_img \
|
||||
@@ -95,7 +97,8 @@ option as well.
|
||||
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
--tdx firmware=tdshim \
|
||||
--platform tdx=on
|
||||
--firmware tdshim \
|
||||
--kernel bzImage \
|
||||
--cmdline "root=/dev/vda3 console=hvc0 rw"
|
||||
--cpus boot=1 \
|
||||
|
||||
@@ -20,12 +20,13 @@ struct MemoryConfig {
|
||||
hugepages: bool,
|
||||
hugepage_size: Option<u64>,
|
||||
prefault: bool,
|
||||
thp: bool
|
||||
zones: Option<Vec<MemoryZoneConfig>>,
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
--memory <memory> Memory parameters "size=<guest_memory_size>,mergeable=on|off,shared=on|off,hugepages=on|off,hugepage_size=<hugepage_size>,hotplug_method=acpi|virtio-mem,hotplug_size=<hotpluggable_memory_size>,hotplugged_size=<hotplugged_memory_size>,prefault=on|off" [default: size=512M]
|
||||
--memory <memory> Memory parameters "size=<guest_memory_size>,mergeable=on|off,shared=on|off,hugepages=on|off,hugepage_size=<hugepage_size>,hotplug_method=acpi|virtio-mem,hotplug_size=<hotpluggable_memory_size>,hotplugged_size=<hotplugged_memory_size>,prefault=on|off,thp=on|off" [default: size=512M,thp=on]
|
||||
```
|
||||
|
||||
### `size`
|
||||
@@ -117,6 +118,9 @@ needing access to the guest RAM content.
|
||||
By default this option is turned off, which results in performing `mmap(2)`
|
||||
with `MAP_PRIVATE` flag.
|
||||
|
||||
If `hugepages=on` then the value of this field is ignored as huge pages always
|
||||
requires `MAP_SHARED`.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
@@ -140,6 +144,9 @@ behaviour, e.g. error with `ReadKernelImage` is common. If there is a strange
|
||||
error with `hugepages` enabled, just disable it or check whether there are enough
|
||||
huge pages.
|
||||
|
||||
If `hugepages=on` then the value of `shared` is ignored as huge pages always
|
||||
requires `MAP_SHARED`.
|
||||
|
||||
By default this option is turned off.
|
||||
|
||||
_Example_
|
||||
@@ -171,6 +178,25 @@ _Example_
|
||||
--memory size=1G,prefault=on
|
||||
```
|
||||
|
||||
### `thp`
|
||||
|
||||
Specifies if private anonymous memory for the guest (i.e. `shared=off` and no
|
||||
backing file) should be labelled `MADV_HUGEPAGE` with `madvise(2)` indicating
|
||||
to the kernel that this memory may be backed with huge pages transparently.
|
||||
|
||||
The use of transparent huge pages can improve the performance of the guest as
|
||||
there will fewer virtualisation related page faults. Unlike using
|
||||
`hugepages=on` a specific number of huge pages do not need to be allocated by
|
||||
the kernel.
|
||||
|
||||
By default this option is turned on.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--memory size=1G,thp=on
|
||||
```
|
||||
|
||||
## Advanced Parameters
|
||||
|
||||
`MemoryZoneConfig` or what is known as `--memory-zone` from the CLI perspective
|
||||
@@ -271,6 +297,9 @@ other processes running on the host. One can use this option when running
|
||||
vhost-user devices as part of the VM device model, as they will be driven
|
||||
by standalone daemons needing access to the guest RAM content.
|
||||
|
||||
If `hugepages=on` then the value of this field is ignored as huge pages always
|
||||
requires `MAP_SHARED`.
|
||||
|
||||
By default this option is turned off, which result in performing `mmap(2)`
|
||||
with `MAP_PRIVATE` flag.
|
||||
|
||||
@@ -298,6 +327,9 @@ behaviour, e.g. error with `ReadKernelImage` is common. If there is a strange
|
||||
error with `hugepages` enabled, just disable it or check whether there are enough
|
||||
huge pages.
|
||||
|
||||
If `hugepages=on` then the value of `shared` is ignored as huge pages always
|
||||
requires `MAP_SHARED`.
|
||||
|
||||
By default this option is turned off.
|
||||
|
||||
_Example_
|
||||
|
||||
@@ -64,5 +64,5 @@ it will log every system call issued by the process. It is important to use
|
||||
`-f` option in order to trace each and every thread belonging to the process.
|
||||
|
||||
```
|
||||
strace -f ./cloud-hypervisor ...
|
||||
strace --decode-pids=comm -f ./cloud-hypervisor ...
|
||||
```
|
||||
|
||||
122
docs/tpm.md
Normal file
122
docs/tpm.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# TPM
|
||||
Tpm in Cloud-Hypervisor is emulated using `swtpm` as the backend. [swtpm](https://github.com/stefanberger/swtpm) is the link to swtpm project.
|
||||
|
||||
Current implementation only supports TPM `2.0` version. At the moment only
|
||||
`CRB Interface` is implemented. This interface is described in
|
||||
[TCG PC Client Platform TPM Profile Specification for TPM 2.0, Revision 01.05 v4](https://trustedcomputinggroup.org/wp-content/uploads/PC-Client-Specific-Platform-TPM-Profile-for-TPM-2p0-v1p05p_r14_pub.pdf).
|
||||
|
||||
|
||||
## Usage
|
||||
`--tpm`, an optional argument, can be passed to enable tpm device.
|
||||
This argument takes an UNIX domain Socket as a `socket` value.
|
||||
|
||||
_Example_
|
||||
|
||||
An Example invocation with `--tpm` argument:
|
||||
|
||||
```
|
||||
./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||
--kernel ./hypervisor-fw \
|
||||
--disk path=focal-server-cloudimg-amd64.raw \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
--net "tap=,mac=,ip=,mask=" \
|
||||
--tpm socket="/var/run/swtpm.socket"
|
||||
```
|
||||
|
||||
## swtpm
|
||||
Before invoking cloud-hypervisor with `--tpm` argument, a `swtpm`
|
||||
process should be started to listen at the input socket. Below is an
|
||||
example invocation of swtpm process.
|
||||
|
||||
```
|
||||
swtpm socket --tpmstate dir=/var/run/swtpm \
|
||||
--ctrl type=unixio,path="/var/run/swtpm.socket" \
|
||||
--flags startup-clear \
|
||||
--tpm2
|
||||
```
|
||||
|
||||
## Guest
|
||||
After starting a guest with the above commands, ensure below listed modules are
|
||||
loaded in the guest:
|
||||
|
||||
```
|
||||
# lsmod | grep tpm
|
||||
tpm_crb 20480 0
|
||||
tpm 81920 1 tpm_crb
|
||||
```
|
||||
|
||||
Below is the IO Memory map configured in the guest:
|
||||
|
||||
```
|
||||
# cat /proc/iomem | grep MSFT
|
||||
fed40000-fed40fff : MSFT0101:00
|
||||
fed40000-fed40fff : MSFT0101:00
|
||||
```
|
||||
Below are the devices created in the guest:
|
||||
|
||||
```
|
||||
# ls /dev/tpm*
|
||||
/dev/tpm0 /dev/tpmrm0
|
||||
```
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
Inside the guest install `tpm2-tools` package. This package provides some
|
||||
commands to run against TPM that supports 2.0 version.
|
||||
|
||||
_Examples_
|
||||
```
|
||||
// Run Self Test
|
||||
# tpm2_selftest -f
|
||||
# echo $?
|
||||
0
|
||||
|
||||
|
||||
# echo "hello" > input.txt
|
||||
// this command generates hash of the input file using all the algos supported by TPM
|
||||
|
||||
# tpm2_pcrevent input.txt
|
||||
sha1: f572d396fae9206628714fb2ce00f72e94f2258f
|
||||
sha256: 5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03
|
||||
sha384: 1d0f284efe3edea4b9ca3bd514fa134b17eae361ccc7a1eefeff801b9bd6604e01f21f6bf249ef030599f0c
|
||||
218f2ba8c
|
||||
sha512: e7c22b994c59d9cf2b48e549b1e24666636045930d3da7c1acb299d1c3b7f931f94aae41edda2c2b207a36e
|
||||
10f8bcb8d45223e54878f5b316e7ce3b6bc019629
|
||||
|
||||
// verify one of the hashes
|
||||
# sha256sum input.txt
|
||||
5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03 input.txt
|
||||
```
|
||||
|
||||
### Bundled Functional Test
|
||||
|
||||
Build time dependencies for `tpm2-tss` are captured in [INSTALL](https://github.com/tpm2-software/tpm2-tss/blob/master/INSTALL.md).
|
||||
|
||||
```
|
||||
# git clone https://github.com/tpm2-software/tpm2-tss.git
|
||||
# cd tpm2-tss
|
||||
# ./configure --enable-integration --with-devicetests="mandatory,optional" --with-device=/dev/tpm0
|
||||
# sudo make check-device
|
||||
.
|
||||
.
|
||||
.
|
||||
.
|
||||
============================================================================
|
||||
Testsuite summary for tpm2-tss 3.2.0-74-ge03617d9
|
||||
============================================================================
|
||||
# TOTAL: 154
|
||||
# PASS: 88
|
||||
# SKIP: 7
|
||||
# XFAIL: 0
|
||||
# FAIL: 59
|
||||
# XPASS: 0
|
||||
# ERROR: 0
|
||||
============================================================================
|
||||
See ./test-suite.log
|
||||
Please report to https://github.com/tpm2-software/tpm2-tss/issues
|
||||
============================================================================
|
||||
```
|
||||
The same set of failures are noticed while running these tests on `Qemu` with
|
||||
its TPM implementation.
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
This section illustrates the Windows specific corner points for the VM network configuration. For the extended networking guide, including bridging for multiple VMs, follow [networking.md](networking.md).
|
||||
This section illustrates the Windows specific aspects of the VM network configuration.
|
||||
|
||||
### Basic Networking
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.131"
|
||||
serde = { version = "1.0.143", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.83"
|
||||
libc = "0.2.138"
|
||||
once_cell = "1.16.0"
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.89"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
@@ -11,12 +12,13 @@ use std::io::Write;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
static mut MONITOR: Option<(File, Instant)> = None;
|
||||
static MONITOR: OnceCell<(File, Instant)> = OnceCell::new();
|
||||
|
||||
/// This function must only be called once from the main process before any threads
|
||||
/// are created to avoid race conditions
|
||||
pub fn set_monitor(file: File) -> Result<(), std::io::Error> {
|
||||
assert!(unsafe { MONITOR.is_none() });
|
||||
// There is only one caller of this function, so MONITOR is written to only once
|
||||
assert!(MONITOR.get().is_none());
|
||||
let fd = file.as_raw_fd();
|
||||
let ret = unsafe {
|
||||
let mut flags = libc::fcntl(fd, libc::F_GETFL);
|
||||
@@ -26,9 +28,9 @@ pub fn set_monitor(file: File) -> Result<(), std::io::Error> {
|
||||
if ret < 0 {
|
||||
return Err(std::io::Error::last_os_error());
|
||||
}
|
||||
unsafe {
|
||||
MONITOR = Some((file, Instant::now()));
|
||||
};
|
||||
|
||||
MONITOR.get_or_init(|| (file, Instant::now()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -41,7 +43,7 @@ struct Event<'a> {
|
||||
}
|
||||
|
||||
pub fn event_log(source: &str, event: &str, properties: Option<&HashMap<Cow<str>, Cow<str>>>) {
|
||||
if let Some((file, start)) = unsafe { MONITOR.as_ref() } {
|
||||
if let Some((file, start)) = MONITOR.get().as_ref() {
|
||||
let e = Event {
|
||||
timestamp: start.elapsed(),
|
||||
source,
|
||||
|
||||
469
fuzz/Cargo.lock
generated
469
fuzz/Cargo.lock
generated
@@ -11,9 +11,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.62"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305"
|
||||
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -24,9 +24,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.1.3"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a7924531f38b1970ff630f03eb20a2fde69db5c590c93b0f3482e95dcc5fd60"
|
||||
checksum = "29d47fbf90d5149a107494b15a7dc8d69b351be2db3bb9691740e88ec17fd880"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
@@ -56,23 +56,6 @@ dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
@@ -88,6 +71,12 @@ version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||
|
||||
[[package]]
|
||||
name = "block_util"
|
||||
version = "0.1.0"
|
||||
@@ -115,9 +104,12 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
version = "1.0.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -127,33 +119,31 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.2.17"
|
||||
version = "4.0.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29e724a68d9319343bb3328c9cc2dfde263f4b3142ee1059a9980580171c954b"
|
||||
checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"is-terminal",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"terminal_size",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.4"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||
checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "25.0.0"
|
||||
version = "28.3.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -168,6 +158,8 @@ dependencies = [
|
||||
"serde_json",
|
||||
"signal-hook",
|
||||
"thiserror",
|
||||
"tpm",
|
||||
"tracer",
|
||||
"vm-memory",
|
||||
"vmm",
|
||||
"vmm-sys-util",
|
||||
@@ -214,9 +206,9 @@ checksum = "55626594feae15d266d52440b26ff77de0e22230cf0c113abe619084c1ddc910"
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.14.1"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02"
|
||||
checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
@@ -224,9 +216,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.14.1"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f"
|
||||
checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
@@ -238,9 +230,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.14.1"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5"
|
||||
checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
@@ -254,11 +246,14 @@ dependencies = [
|
||||
"acpi_tables",
|
||||
"anyhow",
|
||||
"arch",
|
||||
"bitflags",
|
||||
"bitflags 2.4.1",
|
||||
"byteorder",
|
||||
"hypervisor",
|
||||
"libc",
|
||||
"log",
|
||||
"phf",
|
||||
"thiserror",
|
||||
"tpm",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"vm-device",
|
||||
@@ -273,7 +268,28 @@ version = "4.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20df693c700404f7e19d4d6fae6b15215d2913c27955d2b9d6f2c0f537511cd0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"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",
|
||||
]
|
||||
|
||||
@@ -282,6 +298,7 @@ name = "event_monitor"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
@@ -300,26 +317,20 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.7"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.20"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7a30908dbce072eca83216eab939d2290080e00ca71611b96a09e5cdce5f3fa"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -360,35 +371,56 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.1"
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||
checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.5.3"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "427496d698429d86ff947537dff68facb83fe753b7abea4fab4ac1c4b7a4b94b"
|
||||
checksum = "7ba34abb5175052fc1a2227a10d2275b7386c9990167de9786c0b88d8b062330"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.3"
|
||||
name = "is-terminal"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
|
||||
checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kvm-bindings"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.5.0-tdx#52e56d0e8ef0f6ea32fc0492e6a175b73617a49f"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.6.0-tdx#7d9ffb47e5b9b1989577258800a0f57c93f1445f"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -397,8 +429,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kvm-ioctls"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/rust-vmm/kvm-ioctls?branch=main#1e03e29cdfbb0cb108a98de7a78045a5a517f18e"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8f8dc9c1896e5f144ec5d07169bc29f39a047686d29585a91f30489abfaeb6b"
|
||||
dependencies = [
|
||||
"kvm-bindings",
|
||||
"libc",
|
||||
@@ -413,15 +446,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.131"
|
||||
version = "0.2.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04c3b4822ccebfa39c02fc03d1534441b22ead323fa0f48bb7ddd8e6ba076a40"
|
||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.4.3"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "336244aaeab6a12df46480dc585802aa743a72d66b11937844c61bbca84c991d"
|
||||
checksum = "c8fff891139ee62800da71b7fd5b508d570b9ad95e614a53c6f453ca08366038"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"cc",
|
||||
@@ -430,13 +463,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-loader"
|
||||
version = "0.4.0"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a5e77493808403a6bd56a301a64ea6b9342e36ea845044bf0dfdf56fe52fa08"
|
||||
checksum = "b9259ddbfbb52cc918f6bbc60390004ddd0228cf1d85f402009ff2b3d95de83f"
|
||||
dependencies = [
|
||||
"vm-memory",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
@@ -449,7 +488,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#863b0370ba7e57f7df5b908ada9e5b44809ccae9"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#4b18a043e997da5b5f679e3defc279fec908753e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"vmm-sys-util",
|
||||
@@ -485,9 +524,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.13.1"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
|
||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||
|
||||
[[package]]
|
||||
name = "option_parser"
|
||||
@@ -495,9 +534,9 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.3.0"
|
||||
version = "6.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
|
||||
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||
|
||||
[[package]]
|
||||
name = "pci"
|
||||
@@ -523,10 +562,52 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.43"
|
||||
name = "phf"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||
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.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -551,6 +632,21 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "rate_limiter"
|
||||
version = "0.1.0"
|
||||
@@ -562,9 +658,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "remain"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06df529c0d271b27ac4a2c260f5ce2914b60bd44702cecec7b9f271adbdde23b"
|
||||
checksum = "1a81bc6a3aaeb180f767bd2bda8c03bac5b93b3b69a783c66f35d16a4df276cf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -580,6 +676,20 @@ dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.11"
|
||||
@@ -588,33 +698,33 @@ checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||
|
||||
[[package]]
|
||||
name = "seccompiler"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e01d1292a1131b22ccea49f30bd106f1238b5ddeec1a98d39268dcc31d540e68"
|
||||
checksum = "6f6575e3c2b3a0fe2ef3e53855b6a8dead7c29f783da5e123d378c8c6a89017e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.13"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711"
|
||||
checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.143"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553"
|
||||
checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.143"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391"
|
||||
checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -623,9 +733,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.83"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
|
||||
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -634,9 +744,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "2.0.0"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89df7a26519371a3cce44fbb914c2819c84d9b897890987fa3ab096491cc0ea8"
|
||||
checksum = "25bf4a5a814902cd1014dbccfa4d4560fb8432c779471e96e035602519f82eef"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_with_macros",
|
||||
@@ -644,9 +754,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "2.0.0"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de337f322382fcdfbb21a014f7c224ee041a23785651db67b9827403178f698f"
|
||||
checksum = "e3452b4c0f6c1e357f73fdb87cd1efabaa12acf328c7a528e252893baeb3f4aa"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
@@ -654,6 +764,10 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial_buffer"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.14"
|
||||
@@ -673,6 +787,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
@@ -687,9 +807,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.99"
|
||||
version = "1.0.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
|
||||
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -707,63 +827,77 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.1.17"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
|
||||
checksum = "cb20089a8ba2b69debd491f8d2d023761cbf196e999218c591fa1e7e15a21907"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||
dependencies = [
|
||||
"terminal_size",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.32"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
|
||||
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.32"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
|
||||
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"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]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.3"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.1.2"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
|
||||
checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "versionize"
|
||||
version = "0.1.6"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7429cf68de8f091b667d27323ed323afd39584a56d533995b12ddd748e5e6ca9"
|
||||
checksum = "d6e2495726cf917e7ba7ec8bf0f0fceab543dd38d0a4195ed6bef331e38a290f"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"crc64",
|
||||
@@ -788,17 +922,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vfio-bindings"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43449b404c488f70507dca193debd4bea361fe8089869b947adc19720e464bce"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#43439e056ddfa84a4f7906ee7f2f58be70505c08"
|
||||
dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vfio-ioctls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#1ff2324e5cb0fccab825f9c96bb9387a1a01863f"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#43439e056ddfa84a4f7906ee7f2f58be70505c08"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
@@ -841,11 +974,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vhost"
|
||||
version = "0.4.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53567fd9ab820e4f3cc156f24146882fee3c365194c3e1dea74723265f27fc88"
|
||||
checksum = "c9b791c5b0717a0558888a4cf7240cea836f39a99cb342e12ce633dcaa078072"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
@@ -877,6 +1010,7 @@ dependencies = [
|
||||
"seccompiler",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serial_buffer",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
@@ -893,9 +1027,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.5.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4f59652909f276e6edd8bf36e9f106480b2202f5f046717b3de14f1b4072a28"
|
||||
checksum = "19e927d93d54c365034fd7f31a5f458a1f540de4a37c52e892670dad9692173c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"virtio-bindings",
|
||||
@@ -927,14 +1061,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vm-fdt"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#720e48e435b791ec6cbe8d2b229448b71dcb1ab9"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#c5a99ab71b130435927d19b50c85fcd5ce904a8c"
|
||||
|
||||
[[package]]
|
||||
name = "vm-memory"
|
||||
version = "0.8.0"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "767ed8aaebbff902e02e6d3749dc2baef55e46565f8a6414a065e5baee4b4a81"
|
||||
checksum = "688a70366615b45575a424d9c665561c1b5ab2224d494f706b6a6812911a827c"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"libc",
|
||||
@@ -971,7 +1105,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"arch",
|
||||
"bitflags",
|
||||
"bitflags 2.4.1",
|
||||
"block_util",
|
||||
"clap",
|
||||
"devices",
|
||||
@@ -990,8 +1124,10 @@ dependencies = [
|
||||
"seccompiler",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serial_buffer",
|
||||
"signal-hook",
|
||||
"thiserror",
|
||||
"tracer",
|
||||
"uuid",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
@@ -1010,11 +1146,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vmm-sys-util"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08604d7be03eb26e33b3cee3ed4aef2bf550b305d1cca60e84da5d28d3790b62"
|
||||
checksum = "cc06a16ee8ebf0d9269aed304030b0d20a866b8b3dd3d4ce532596ac567a0d24"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -1056,3 +1192,60 @@ name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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"
|
||||
|
||||
@@ -12,18 +12,18 @@ cargo-fuzz = true
|
||||
block_util = { path = "../block_util" }
|
||||
devices = { path = "../devices" }
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.131"
|
||||
libfuzzer-sys = "0.4.3"
|
||||
libc = "0.2.135"
|
||||
libfuzzer-sys = "0.4.5"
|
||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||
once_cell = "1.13.1"
|
||||
once_cell = "1.16.0"
|
||||
qcow = { path = "../qcow" }
|
||||
seccompiler = "0.2.0"
|
||||
seccompiler = "0.3.0"
|
||||
vhdx = { path = "../vhdx" }
|
||||
virtio-devices = { path = "../virtio-devices" }
|
||||
virtio-queue = "0.5.0"
|
||||
virtio-queue = "0.7.0"
|
||||
vmm = { path = "../vmm" }
|
||||
vmm-sys-util = "0.10.0"
|
||||
vm-memory = "0.8.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
vm-memory = "0.10.0"
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
|
||||
@@ -31,14 +31,19 @@ vm-virtio = { path = "../vm-virtio" }
|
||||
path = ".."
|
||||
|
||||
[patch.crates-io]
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0-tdx" }
|
||||
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "main" }
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx" }
|
||||
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
[workspace]
|
||||
members = ["."]
|
||||
|
||||
[[bin]]
|
||||
name = "balloon"
|
||||
path = "fuzz_targets/balloon.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "block"
|
||||
path = "fuzz_targets/block.rs"
|
||||
@@ -51,18 +56,48 @@ path = "fuzz_targets/cmos.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "console"
|
||||
path = "fuzz_targets/console.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "http_api"
|
||||
path = "fuzz_targets/http_api.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "iommu"
|
||||
path = "fuzz_targets/iommu.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "mem"
|
||||
path = "fuzz_targets/mem.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "pmem"
|
||||
path = "fuzz_targets/pmem.rs"
|
||||
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"
|
||||
@@ -74,3 +109,9 @@ name = "vhdx"
|
||||
path = "fuzz_targets/vhdx.rs"
|
||||
test = 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.
|
||||
// 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]
|
||||
|
||||
@@ -16,37 +20,35 @@ use std::sync::Arc;
|
||||
use virtio_devices::{Block, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||
use virtio_queue::{Queue, QueueT};
|
||||
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||
use vmm::{EpollContext, EpollDispatch};
|
||||
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
|
||||
const MEM_SIZE: u64 = 256 * 1024 * 1024;
|
||||
const QUEUE_SIZE: u16 = 16; // Max entries in the queue.
|
||||
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() as u64 > MEM_SIZE {
|
||||
if bytes.len() < QUEUE_DATA_SIZE || bytes.len() > (QUEUE_DATA_SIZE + MEM_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE as usize)]).unwrap();
|
||||
if mem.write_slice(bytes, GuestAddress(0 as u64)).is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||
|
||||
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||
q.set_ready(true);
|
||||
q.set_size(QUEUE_SIZE / 2);
|
||||
|
||||
let evt = EventFd::new(0).unwrap();
|
||||
let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) };
|
||||
let queue_data = &bytes[..QUEUE_DATA_SIZE];
|
||||
let mem_bytes = &bytes[QUEUE_DATA_SIZE..];
|
||||
|
||||
// Create a virtio-block device backed by a synchronous raw file
|
||||
let shm = memfd_create(&ffi::CString::new("fuzz").unwrap(), 0).unwrap();
|
||||
let disk_file: File = unsafe { File::from_raw_fd(shm) };
|
||||
let qcow_disk = Box::new(RawFileDiskSync::new(disk_file)) as Box<dyn DiskFile>;
|
||||
|
||||
let mut block = Block::new(
|
||||
"tmp".to_owned(),
|
||||
qcow_disk,
|
||||
@@ -58,9 +60,26 @@ fuzz_target!(|bytes| {
|
||||
SeccompAction::Allow,
|
||||
None,
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
None,
|
||||
)
|
||||
.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
|
||||
.activate(
|
||||
guest_memory,
|
||||
@@ -69,11 +88,8 @@ fuzz_target!(|bytes| {
|
||||
)
|
||||
.ok();
|
||||
|
||||
queue_evt.write(77).unwrap(); // Rings the doorbell, any byte will do.
|
||||
|
||||
wait_queue_event_processed(queue_evt);
|
||||
|
||||
block.reset(); // Ensure the virtio-block device thread is killed and joined
|
||||
// Wait for the events to finish and block device worker thread to return
|
||||
block.wait_for_epoll_threads();
|
||||
});
|
||||
|
||||
fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> {
|
||||
@@ -94,28 +110,20 @@ impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||
}
|
||||
}
|
||||
|
||||
fn wait_queue_event_processed(queue_evt: EventFd) {
|
||||
let mut epoll = EpollContext::new().unwrap();
|
||||
epoll.add_event(&queue_evt, EpollDispatch::Api).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);
|
||||
|
||||
let epoll_fd = epoll.as_raw_fd();
|
||||
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); 1];
|
||||
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);
|
||||
|
||||
loop {
|
||||
let num_events = match epoll::wait(epoll_fd, 0, &mut events[..]) {
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::Interrupted {
|
||||
continue;
|
||||
}
|
||||
|
||||
println!("Warning: Unexpected epoll::wait() error!");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if num_events == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
q
|
||||
}
|
||||
|
||||
194
fuzz/fuzz_targets/console.rs
Normal file
194
fuzz/fuzz_targets/console.rs
Normal file
@@ -0,0 +1,194 @@
|
||||
// Copyright © 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use seccompiler::SeccompAction;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::sync::Arc;
|
||||
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||
use virtio_queue::{Queue, QueueT};
|
||||
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
|
||||
macro_rules! align {
|
||||
($n:expr, $align:expr) => {{
|
||||
(($n + $align - 1) / $align) * $align
|
||||
}};
|
||||
}
|
||||
|
||||
const CONSOLE_INPUT_SIZE: usize = 128;
|
||||
const QUEUE_DATA_SIZE: usize = 4;
|
||||
const MEM_SIZE: usize = 32 * 1024 * 1024;
|
||||
// Guest memory gap
|
||||
const GUEST_MEM_GAP: u64 = 1 * 1024 * 1024;
|
||||
// Guest physical address for the first virt queue
|
||||
const BASE_VIRT_QUEUE_ADDR: u64 = MEM_SIZE as u64 + GUEST_MEM_GAP;
|
||||
// Number of queues
|
||||
const QUEUE_NUM: usize = 2;
|
||||
// Max entries in the queue.
|
||||
const QUEUE_SIZE: u16 = 256;
|
||||
// Descriptor table alignment
|
||||
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||
// Used ring alignment
|
||||
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||
// Descriptor table size
|
||||
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
|
||||
// Available ring size
|
||||
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
||||
// Padding size before used ring
|
||||
const PADDING_SIZE: u64 = align!(AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE) - AVAIL_RING_SIZE;
|
||||
// Used ring size
|
||||
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
|
||||
// Virtio-queue size in bytes
|
||||
const QUEUE_BYTES_SIZE: usize = align!(
|
||||
DESC_TABLE_SIZE + AVAIL_RING_SIZE + PADDING_SIZE + USED_RING_SIZE,
|
||||
DESC_TABLE_ALIGN_SIZE
|
||||
) as usize;
|
||||
|
||||
fuzz_target!(|bytes| {
|
||||
if bytes.len() < (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + CONSOLE_INPUT_SIZE
|
||||
|| bytes.len()
|
||||
> (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + CONSOLE_INPUT_SIZE + MEM_SIZE
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let (pipe_rx, mut pipe_tx) = create_pipe().unwrap();
|
||||
let output = unsafe {
|
||||
File::from_raw_fd(
|
||||
memfd_create(&std::ffi::CString::new("fuzz_console_output").unwrap()).unwrap(),
|
||||
)
|
||||
};
|
||||
let endpoint = virtio_devices::Endpoint::FilePair(output, pipe_rx);
|
||||
|
||||
let (mut console, _) = virtio_devices::Console::new(
|
||||
"fuzzer_console".to_owned(),
|
||||
endpoint,
|
||||
None, // resize_pipe
|
||||
false, // iommu
|
||||
SeccompAction::Allow,
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let console_input_bytes = &bytes[..CONSOLE_INPUT_SIZE];
|
||||
let queue_data = &bytes[CONSOLE_INPUT_SIZE..CONSOLE_INPUT_SIZE + QUEUE_DATA_SIZE * QUEUE_NUM];
|
||||
let queue_bytes = &bytes[CONSOLE_INPUT_SIZE + QUEUE_DATA_SIZE * QUEUE_NUM
|
||||
..CONSOLE_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM];
|
||||
let mem_bytes = &bytes[CONSOLE_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM..];
|
||||
|
||||
// Setup the virt queues with the input bytes
|
||||
let mut queues = setup_virt_queues(
|
||||
&[
|
||||
&queue_data[..QUEUE_DATA_SIZE].try_into().unwrap(),
|
||||
&queue_data[QUEUE_DATA_SIZE..QUEUE_DATA_SIZE * 2]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
],
|
||||
BASE_VIRT_QUEUE_ADDR,
|
||||
);
|
||||
|
||||
// Setup the guest memory with the input bytes
|
||||
let mem = GuestMemoryMmap::from_ranges(&[
|
||||
(GuestAddress(0), MEM_SIZE),
|
||||
(GuestAddress(BASE_VIRT_QUEUE_ADDR), queue_bytes.len()),
|
||||
])
|
||||
.unwrap();
|
||||
if mem
|
||||
.write_slice(queue_bytes, GuestAddress(BASE_VIRT_QUEUE_ADDR))
|
||||
.is_err()
|
||||
{
|
||||
return;
|
||||
}
|
||||
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
||||
return;
|
||||
}
|
||||
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||
|
||||
let input_queue = queues.remove(0);
|
||||
let input_evt = EventFd::new(0).unwrap();
|
||||
let input_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(input_evt.as_raw_fd())) };
|
||||
let output_queue = queues.remove(0);
|
||||
let output_evt = EventFd::new(0).unwrap();
|
||||
let output_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(output_evt.as_raw_fd())) };
|
||||
|
||||
// Kick the 'queue' events and endpoint event before activate the console device
|
||||
input_queue_evt.write(1).unwrap();
|
||||
output_queue_evt.write(1).unwrap();
|
||||
pipe_tx.write_all(console_input_bytes).unwrap(); // To use fuzzed data;
|
||||
|
||||
console
|
||||
.activate(
|
||||
guest_memory,
|
||||
Arc::new(NoopVirtioInterrupt {}),
|
||||
vec![(0, input_queue, input_evt), (1, output_queue, output_evt)],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Wait for the events to finish and console device worker thread to return
|
||||
console.wait_for_epoll_threads();
|
||||
});
|
||||
|
||||
pub struct NoopVirtioInterrupt {}
|
||||
|
||||
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_virt_queues(bytes: &[&[u8; QUEUE_DATA_SIZE]], base_addr: u64) -> Vec<Queue> {
|
||||
let mut queues = Vec::new();
|
||||
for (i, b) in bytes.iter().enumerate() {
|
||||
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||
|
||||
let desc_table_addr = base_addr + (QUEUE_BYTES_SIZE * i) as u64;
|
||||
let avail_ring_addr = desc_table_addr + DESC_TABLE_SIZE;
|
||||
let used_ring_addr = avail_ring_addr + PADDING_SIZE + AVAIL_RING_SIZE;
|
||||
q.try_set_desc_table_address(GuestAddress(desc_table_addr))
|
||||
.unwrap();
|
||||
q.try_set_avail_ring_address(GuestAddress(avail_ring_addr))
|
||||
.unwrap();
|
||||
q.try_set_used_ring_address(GuestAddress(used_ring_addr))
|
||||
.unwrap();
|
||||
|
||||
q.set_next_avail(b[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
|
||||
q.set_next_used(b[1] as u16);
|
||||
q.set_event_idx(b[2] % 2 != 0);
|
||||
q.set_size(b[3] as u16 % QUEUE_SIZE);
|
||||
|
||||
q.set_ready(true);
|
||||
queues.push(q);
|
||||
}
|
||||
|
||||
queues
|
||||
}
|
||||
|
||||
fn memfd_create(name: &std::ffi::CStr) -> Result<RawFd, std::io::Error> {
|
||||
let res = unsafe { libc::syscall(libc::SYS_memfd_create, name.as_ptr(), 0) };
|
||||
|
||||
if res < 0 {
|
||||
Err(std::io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(res as RawFd)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_pipe() -> Result<(File, File), std::io::Error> {
|
||||
let mut pipe = [-1; 2];
|
||||
if unsafe { libc::pipe2(pipe.as_mut_ptr(), libc::O_CLOEXEC) } == -1 {
|
||||
return Err(std::io::Error::last_os_error());
|
||||
}
|
||||
let rx = unsafe { File::from_raw_fd(pipe[0]) };
|
||||
let tx = unsafe { File::from_raw_fd(pipe[1]) };
|
||||
|
||||
Ok((rx, tx))
|
||||
}
|
||||
145
fuzz/fuzz_targets/iommu.rs
Normal file
145
fuzz/fuzz_targets/iommu.rs
Normal file
@@ -0,0 +1,145 @@
|
||||
// Copyright © 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use seccompiler::SeccompAction;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||
use std::sync::Arc;
|
||||
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||
use virtio_queue::{Queue, QueueT};
|
||||
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
|
||||
macro_rules! align {
|
||||
($n:expr, $align:expr) => {{
|
||||
(($n + $align - 1) / $align) * $align
|
||||
}};
|
||||
}
|
||||
|
||||
const QUEUE_DATA_SIZE: usize = 4;
|
||||
const MEM_SIZE: usize = 32 * 1024 * 1024;
|
||||
// Reuse what's being done from DeviceManager::get_msi_iova_space()
|
||||
const IOVA_SPACE_SIZE: usize = (0xfeef_ffff - 0xfee0_0000) + 1;
|
||||
|
||||
// Max entries in the queue.
|
||||
const QUEUE_SIZE: u16 = 256;
|
||||
// Descriptor table alignment
|
||||
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||
// Avalable ring alignment
|
||||
const AVAIL_RING_ALIGN_SIZE: u64 = 2;
|
||||
// Used ring alignment
|
||||
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||
// Descriptor table size
|
||||
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
|
||||
// Available ring size
|
||||
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
||||
// Used ring size
|
||||
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
|
||||
|
||||
// Guest memory gap
|
||||
const GUEST_MEM_GAP: u64 = 1 * 1024 * 1024;
|
||||
// Guest physical address for descriptor table.
|
||||
const DESC_TABLE_ADDR: u64 = align!(MEM_SIZE as u64 + GUEST_MEM_GAP, DESC_TABLE_ALIGN_SIZE);
|
||||
// Guest physical address for available ring
|
||||
const AVAIL_RING_ADDR: u64 = align!(DESC_TABLE_ADDR + DESC_TABLE_SIZE, AVAIL_RING_ALIGN_SIZE);
|
||||
// Guest physical address for used ring
|
||||
const USED_RING_ADDR: u64 = align!(AVAIL_RING_ADDR + AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE);
|
||||
// Virtio-queue size in bytes
|
||||
const QUEUE_BYTES_SIZE: usize = (USED_RING_ADDR + USED_RING_SIZE - DESC_TABLE_ADDR) as usize;
|
||||
|
||||
fuzz_target!(|bytes| {
|
||||
if bytes.len() < (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE)
|
||||
|| bytes.len() > (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE + MEM_SIZE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let (mut iommu, _) = virtio_devices::Iommu::new(
|
||||
"fuzzer_iommu".to_owned(),
|
||||
SeccompAction::Allow,
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
((MEM_SIZE - IOVA_SPACE_SIZE) as u64, (MEM_SIZE - 1) as u64),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let queue_data = &bytes[..QUEUE_DATA_SIZE];
|
||||
let queue_bytes = &bytes[QUEUE_DATA_SIZE..QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE];
|
||||
let mem_bytes = &bytes[QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE..];
|
||||
|
||||
// Setup the request queue with the input bytes
|
||||
let request_queue = setup_virt_queue(queue_data.try_into().unwrap());
|
||||
// Given the "event queue" events are not handled from the current
|
||||
// implementation of virtio-iommu, we simply setup the 'event_queue'
|
||||
// with exactly the same content as the 'request_queue'.
|
||||
let _event_queue = setup_virt_queue(queue_data.try_into().unwrap());
|
||||
|
||||
// Setup the guest memory with the input bytes
|
||||
let mem = GuestMemoryMmap::from_ranges(&[
|
||||
(GuestAddress(0), MEM_SIZE),
|
||||
(GuestAddress(DESC_TABLE_ADDR), QUEUE_BYTES_SIZE),
|
||||
])
|
||||
.unwrap();
|
||||
if mem
|
||||
.write_slice(queue_bytes, GuestAddress(DESC_TABLE_ADDR))
|
||||
.is_err()
|
||||
{
|
||||
return;
|
||||
}
|
||||
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
||||
return;
|
||||
}
|
||||
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||
|
||||
let request_evt = EventFd::new(0).unwrap();
|
||||
let request_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(request_evt.as_raw_fd())) };
|
||||
let _event_evt = EventFd::new(0).unwrap();
|
||||
|
||||
// Kick the 'queue' event before activate the vIOMMU device
|
||||
request_queue_evt.write(1).unwrap();
|
||||
|
||||
iommu
|
||||
.activate(
|
||||
guest_memory,
|
||||
Arc::new(NoopVirtioInterrupt {}),
|
||||
vec![
|
||||
(0, request_queue, request_evt),
|
||||
(0, _event_queue, _event_evt),
|
||||
],
|
||||
)
|
||||
.ok();
|
||||
|
||||
// Wait for the events to finish and vIOMMU device worker thread to return
|
||||
iommu.wait_for_epoll_threads();
|
||||
});
|
||||
|
||||
pub struct NoopVirtioInterrupt {}
|
||||
|
||||
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue {
|
||||
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||
q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
|
||||
q.set_next_used(bytes[1] as u16);
|
||||
q.set_event_idx(bytes[2] % 2 != 0);
|
||||
q.set_size(bytes[3] as u16 % QUEUE_SIZE);
|
||||
|
||||
q.try_set_desc_table_address(GuestAddress(DESC_TABLE_ADDR))
|
||||
.unwrap();
|
||||
q.try_set_avail_ring_address(GuestAddress(AVAIL_RING_ADDR))
|
||||
.unwrap();
|
||||
q.try_set_used_ring_address(GuestAddress(USED_RING_ADDR))
|
||||
.unwrap();
|
||||
q.set_ready(true);
|
||||
|
||||
q
|
||||
}
|
||||
179
fuzz/fuzz_targets/mem.rs
Normal file
179
fuzz/fuzz_targets/mem.rs
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright © 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use seccompiler::SeccompAction;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use virtio_devices::{BlocksState, Mem, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||
use virtio_queue::{Queue, QueueT};
|
||||
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
type GuestRegionMmap = vm_memory::GuestRegionMmap<AtomicBitmap>;
|
||||
|
||||
macro_rules! align {
|
||||
($n:expr, $align:expr) => {{
|
||||
(($n + $align - 1) / $align) * $align
|
||||
}};
|
||||
}
|
||||
|
||||
const VIRTIO_MEM_DATA_SIZE: usize = 1;
|
||||
const QUEUE_DATA_SIZE: usize = 4;
|
||||
// The size of the guest memory for the virtio-mem region
|
||||
const MEM_SIZE: usize = 128 * 1024 * 1024;
|
||||
// The start address of the virtio-mem region in the guest memory
|
||||
const VIRTIO_MEM_REGION_ADDRESS: u64 = 0;
|
||||
|
||||
// Max entries in the queue.
|
||||
const QUEUE_SIZE: u16 = 64;
|
||||
// Descriptor table alignment
|
||||
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||
// Avalable ring alignment
|
||||
const AVAIL_RING_ALIGN_SIZE: u64 = 2;
|
||||
// Used ring alignment
|
||||
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||
// Descriptor table size
|
||||
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
|
||||
// Available ring size
|
||||
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
||||
// Used ring size
|
||||
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
|
||||
|
||||
// Guest memory gap
|
||||
const GUEST_MEM_GAP: u64 = 1 * 1024 * 1024;
|
||||
// Guest physical address for descriptor table.
|
||||
const DESC_TABLE_ADDR: u64 = align!(MEM_SIZE as u64 + GUEST_MEM_GAP, DESC_TABLE_ALIGN_SIZE);
|
||||
// Guest physical address for available ring
|
||||
const AVAIL_RING_ADDR: u64 = align!(DESC_TABLE_ADDR + DESC_TABLE_SIZE, AVAIL_RING_ALIGN_SIZE);
|
||||
// Guest physical address for used ring
|
||||
const USED_RING_ADDR: u64 = align!(AVAIL_RING_ADDR + AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE);
|
||||
// Virtio-queue size in bytes
|
||||
const QUEUE_BYTES_SIZE: usize = (USED_RING_ADDR + USED_RING_SIZE - DESC_TABLE_ADDR) as usize;
|
||||
|
||||
fuzz_target!(|bytes| {
|
||||
if bytes.len() < VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE
|
||||
|| bytes.len() > (VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE + MEM_SIZE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let virtio_mem_data = &bytes[..VIRTIO_MEM_DATA_SIZE];
|
||||
let queue_data = &bytes[VIRTIO_MEM_DATA_SIZE..VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE];
|
||||
let queue_bytes = &bytes[VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE
|
||||
..VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE];
|
||||
let mem_bytes = &bytes[VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE..];
|
||||
|
||||
// Create a virtio-mem device based on the input bytes;
|
||||
let (mut virtio_mem, virtio_mem_region) =
|
||||
create_dummy_virtio_mem(virtio_mem_data.try_into().unwrap());
|
||||
|
||||
// Setup the virt queue with the input bytes
|
||||
let q = setup_virt_queue(queue_data.try_into().unwrap());
|
||||
|
||||
// Setup the guest memory with the input bytes
|
||||
let mem = GuestMemoryMmap::from_ranges(&[
|
||||
(GuestAddress(DESC_TABLE_ADDR), QUEUE_BYTES_SIZE), // guest region for the virt queue
|
||||
])
|
||||
.unwrap();
|
||||
if mem
|
||||
.write_slice(queue_bytes, GuestAddress(DESC_TABLE_ADDR))
|
||||
.is_err()
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Add the memory region for the virtio-mem device
|
||||
let mem = mem.insert_region(virtio_mem_region).unwrap();
|
||||
if mem
|
||||
.write_slice(mem_bytes, GuestAddress(VIRTIO_MEM_REGION_ADDRESS))
|
||||
.is_err()
|
||||
{
|
||||
return;
|
||||
}
|
||||
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||
|
||||
let evt = EventFd::new(0).unwrap();
|
||||
let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) };
|
||||
|
||||
// Kick the 'queue' event before activate the virtio-mem device
|
||||
queue_evt.write(1).unwrap();
|
||||
|
||||
virtio_mem
|
||||
.activate(
|
||||
guest_memory,
|
||||
Arc::new(NoopVirtioInterrupt {}),
|
||||
vec![(0, q, evt)],
|
||||
)
|
||||
.ok();
|
||||
|
||||
// Wait for the events to finish and virtio-mem device worker thread to return
|
||||
virtio_mem.wait_for_epoll_threads();
|
||||
});
|
||||
|
||||
pub struct NoopVirtioInterrupt {}
|
||||
|
||||
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Create a dummy virtio-mem device for fuzzing purpose only
|
||||
fn create_dummy_virtio_mem(bytes: &[u8; VIRTIO_MEM_DATA_SIZE]) -> (Mem, Arc<GuestRegionMmap>) {
|
||||
let numa_id = if bytes[0] % 2 != 0 { Some(0) } else { None };
|
||||
|
||||
let region = vmm::memory_manager::MemoryManager::create_ram_region(
|
||||
&None,
|
||||
0,
|
||||
GuestAddress(VIRTIO_MEM_REGION_ADDRESS),
|
||||
MEM_SIZE,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
numa_id,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let blocks_state = Arc::new(Mutex::new(BlocksState::new(region.size() as u64)));
|
||||
|
||||
(
|
||||
Mem::new(
|
||||
"fuzzer_mem".to_owned(),
|
||||
®ion,
|
||||
SeccompAction::Allow,
|
||||
numa_id.map(|i| i as u16),
|
||||
0,
|
||||
false,
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
blocks_state.clone(),
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
region,
|
||||
)
|
||||
}
|
||||
|
||||
fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue {
|
||||
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||
q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
|
||||
q.set_next_used(bytes[1] as u16);
|
||||
q.set_event_idx(bytes[2] % 2 != 0);
|
||||
q.set_size(bytes[3] as u16 % QUEUE_SIZE);
|
||||
|
||||
q.try_set_desc_table_address(GuestAddress(DESC_TABLE_ADDR))
|
||||
.unwrap();
|
||||
q.try_set_avail_ring_address(GuestAddress(AVAIL_RING_ADDR))
|
||||
.unwrap();
|
||||
q.try_set_used_ring_address(GuestAddress(USED_RING_ADDR))
|
||||
.unwrap();
|
||||
q.set_ready(true);
|
||||
|
||||
q
|
||||
}
|
||||
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
|
||||
}
|
||||
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,20 +11,20 @@ mshv = ["mshv-ioctls", "mshv-bindings"]
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.62"
|
||||
anyhow = "1.0.66"
|
||||
byteorder = "1.4.3"
|
||||
thiserror = "1.0.32"
|
||||
libc = "0.2.131"
|
||||
thiserror = "1.0.37"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
kvm-ioctls = { version = "0.11.0", optional = true }
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0-tdx", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
kvm-ioctls = { version = "0.13.0", optional = true }
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
mshv-bindings = { git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", branch = "main", optional = true}
|
||||
serde = { version = "1.0.143", features = ["rc", "derive"] }
|
||||
serde_with = { version = "2.0.0", default-features = false, features = ["macros"] }
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
serde_with = { version = "2.1.0", default-features = false, features = ["macros"] }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = { version = "0.10.0", features = ["with-serde"] }
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
||||
version = "1.17.0"
|
||||
@@ -32,4 +32,4 @@ default-features = false
|
||||
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.9.0"
|
||||
env_logger = "0.10.0"
|
||||
|
||||
@@ -16,6 +16,18 @@ pub enum 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
|
||||
pub trait Vgic: Send + Sync {
|
||||
/// Returns the fdt compatibility property of the device
|
||||
|
||||
@@ -24,10 +24,10 @@ impl<T: Debug> Display for Exception<T> {
|
||||
self.vector,
|
||||
self.ip,
|
||||
self.error
|
||||
.map(|e| format!(": error {:x}", e))
|
||||
.map(|e| format!(": error {e:x}"))
|
||||
.unwrap_or_else(|| "".to_owned()),
|
||||
self.payload
|
||||
.map(|payload| format!(": payload {:x}", payload))
|
||||
.map(|payload| format!(": payload {payload:x}"))
|
||||
.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.
|
||||
let af = ((cout >> 3) & 0x1) << AF_SHIFT;
|
||||
|
||||
let zf = if result & (!0u64 >> (63 - msb_shift)) == 0 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
} << ZF_SHIFT;
|
||||
let zf = u64::from(result & (!0u64 >> (63 - msb_shift)) == 0) << ZF_SHIFT;
|
||||
|
||||
let sf = ((result >> msb_shift) & 0x1) << SF_SHIFT;
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ fn get_op<T: CpuStateManager>(
|
||||
insn: &Instruction,
|
||||
op_index: u32,
|
||||
op_size: usize,
|
||||
state: &mut T,
|
||||
platform: &mut dyn PlatformEmulator<CpuState = T>,
|
||||
state: &T,
|
||||
platform: &dyn PlatformEmulator<CpuState = T>,
|
||||
) -> Result<u64, PlatformError> {
|
||||
if insn.op_count() < op_index + 1 {
|
||||
return Err(PlatformError::InvalidOperand(anyhow!(
|
||||
@@ -56,7 +56,7 @@ fn get_op<T: CpuStateManager>(
|
||||
OpKind::Immediate16 => insn.immediate16() as u64,
|
||||
OpKind::Immediate32 => insn.immediate32() as u64,
|
||||
OpKind::Immediate32to64 => insn.immediate32to64() as u64,
|
||||
OpKind::Immediate64 => insn.immediate64() as u64,
|
||||
OpKind::Immediate64 => insn.immediate64(),
|
||||
k => return Err(PlatformError::InvalidOperand(anyhow!("{:?}", k))),
|
||||
};
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ use crate::kvm::{TdxExitDetails, TdxExitStatus};
|
||||
use crate::CpuState;
|
||||
use crate::MpState;
|
||||
use thiserror::Error;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
@@ -49,12 +48,12 @@ pub enum HypervisorCpuError {
|
||||
///
|
||||
/// Setting floating point registers error
|
||||
///
|
||||
#[error("Failed to set special register: {0}")]
|
||||
#[error("Failed to set floating point registers: {0}")]
|
||||
SetFloatingPointRegs(#[source] anyhow::Error),
|
||||
///
|
||||
/// Getting floating point register error
|
||||
///
|
||||
#[error("Failed to get special register: {0}")]
|
||||
#[error("Failed to get floating points registers: {0}")]
|
||||
GetFloatingPointRegs(#[source] anyhow::Error),
|
||||
///
|
||||
/// Setting Cpuid error
|
||||
@@ -238,6 +237,12 @@ pub enum HypervisorCpuError {
|
||||
///
|
||||
#[error("Failed to initialize PMU")]
|
||||
InitializePmu,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Error getting TSC frequency
|
||||
///
|
||||
#[error("Failed to get TSC frequency: {0}")]
|
||||
GetTscKhz(#[source] anyhow::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -347,7 +352,6 @@ pub trait Vcpu: Send + Sync {
|
||||
fn notify_guest_clock_paused(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets debug registers to set hardware breakpoints and/or enable single step.
|
||||
///
|
||||
@@ -408,7 +412,9 @@ pub trait Vcpu: Send + Sync {
|
||||
/// Initialize TDX support on the vCPU
|
||||
///
|
||||
#[cfg(feature = "tdx")]
|
||||
fn tdx_init(&self, hob_address: u64) -> Result<()>;
|
||||
fn tdx_init(&self, _hob_address: u64) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
///
|
||||
/// Set the "immediate_exit" state
|
||||
///
|
||||
@@ -417,15 +423,26 @@ pub trait Vcpu: Send + Sync {
|
||||
///
|
||||
/// 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")]
|
||||
///
|
||||
/// 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>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Get the frequency of the TSC if available
|
||||
///
|
||||
fn tsc_khz(&self) -> Result<Option<u32>> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,12 +117,20 @@ pub trait Hypervisor: Send + Sync {
|
||||
}
|
||||
#[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;
|
||||
///
|
||||
/// Retrieve TDX capabilities
|
||||
///
|
||||
#[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!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ mod dist_regs;
|
||||
mod icc_regs;
|
||||
mod redist_regs;
|
||||
|
||||
use crate::arch::aarch64::gic::{Error, Result, Vgic};
|
||||
use crate::arch::aarch64::gic::{Error, Result, Vgic, VgicConfig};
|
||||
use crate::device::HypervisorDeviceError;
|
||||
use crate::kvm::{kvm_bindings, KvmVm};
|
||||
use crate::{CpuState, Vm};
|
||||
@@ -133,9 +133,7 @@ impl KvmGicV3Its {
|
||||
/// Setup the device-specific attributes
|
||||
fn init_device_attributes(&mut self, vm: &KvmVm, nr_irqs: u32) -> Result<()> {
|
||||
// GicV3 part attributes
|
||||
/* Setting up the distributor attribute.
|
||||
We are placing the GIC below 1GB so we need to substract the size of the distributor.
|
||||
*/
|
||||
/* Setting up the distributor attribute. */
|
||||
Self::set_device_attribute(
|
||||
&self.device,
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
@@ -144,9 +142,7 @@ impl KvmGicV3Its {
|
||||
0,
|
||||
)?;
|
||||
|
||||
/* Setting up the redistributors' attribute.
|
||||
We are calculating here the start of the redistributors address. We have one per CPU.
|
||||
*/
|
||||
/* Setting up the redistributors' attribute. */
|
||||
Self::set_device_attribute(
|
||||
&self.device,
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
@@ -248,37 +244,26 @@ impl KvmGicV3Its {
|
||||
|
||||
/// Method to initialize the GIC device
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new(
|
||||
vm: &dyn Vm,
|
||||
vcpu_count: u64,
|
||||
dist_addr: u64,
|
||||
dist_size: u64,
|
||||
redist_size: u64,
|
||||
msi_size: u64,
|
||||
nr_irqs: u32,
|
||||
) -> Result<KvmGicV3Its> {
|
||||
pub fn new(vm: &dyn Vm, config: VgicConfig) -> Result<KvmGicV3Its> {
|
||||
// This is inside KVM module
|
||||
let vm = vm.as_any().downcast_ref::<KvmVm>().expect("Wrong VM type?");
|
||||
|
||||
let vgic = Self::create_device(vm)?;
|
||||
let 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 {
|
||||
device: vgic,
|
||||
its_device: None,
|
||||
gicr_typers: vec![0; vcpu_count.try_into().unwrap()],
|
||||
dist_addr,
|
||||
dist_size,
|
||||
redists_addr,
|
||||
redists_size,
|
||||
msi_addr,
|
||||
msi_size,
|
||||
vcpu_count,
|
||||
gicr_typers: vec![0; config.vcpu_count.try_into().unwrap()],
|
||||
dist_addr: config.dist_addr,
|
||||
dist_size: config.dist_size,
|
||||
redists_addr: config.redists_addr,
|
||||
redists_size: config.redists_size,
|
||||
msi_addr: config.msi_addr,
|
||||
msi_size: config.msi_size,
|
||||
vcpu_count: config.vcpu_count,
|
||||
};
|
||||
|
||||
gic_device.init_device_attributes(vm, nr_irqs)?;
|
||||
gic_device.init_device_attributes(vm, config.nr_irqs)?;
|
||||
|
||||
Ok(gic_device)
|
||||
}
|
||||
@@ -500,23 +485,30 @@ mod tests {
|
||||
use crate::aarch64::gic::{
|
||||
get_dist_regs, get_icc_regs, get_redist_regs, set_dist_regs, set_icc_regs, set_redist_regs,
|
||||
};
|
||||
use crate::arch::aarch64::gic::VgicConfig;
|
||||
use crate::kvm::KvmGicV3Its;
|
||||
|
||||
fn create_test_vgic_config() -> VgicConfig {
|
||||
VgicConfig {
|
||||
vcpu_count: 1,
|
||||
dist_addr: 0x0900_0000 - 0x01_0000,
|
||||
dist_size: 0x01_0000,
|
||||
// dist_addr - redists_size
|
||||
redists_addr: 0x0900_0000 - 0x01_0000 - 0x02_0000,
|
||||
redists_size: 0x02_0000,
|
||||
// redists_addr - msi_size
|
||||
msi_addr: 0x0900_0000 - 0x01_0000 - 0x02_0000 - 0x02_0000,
|
||||
msi_size: 0x02_0000,
|
||||
nr_irqs: 256,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_gic() {
|
||||
let hv = crate::new().unwrap();
|
||||
let vm = hv.create_vm().unwrap();
|
||||
|
||||
assert!(KvmGicV3Its::new(
|
||||
&*vm,
|
||||
1,
|
||||
0x0900_0000 - 0x01_0000,
|
||||
0x01_0000,
|
||||
0x02_0000,
|
||||
0x02_0000,
|
||||
256
|
||||
)
|
||||
.is_ok());
|
||||
assert!(KvmGicV3Its::new(&*vm, create_test_vgic_config()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -524,16 +516,7 @@ mod tests {
|
||||
let hv = crate::new().unwrap();
|
||||
let vm = hv.create_vm().unwrap();
|
||||
let _ = vm.create_vcpu(0, None).unwrap();
|
||||
let gic = KvmGicV3Its::new(
|
||||
&*vm,
|
||||
1,
|
||||
0x0900_0000 - 0x01_0000,
|
||||
0x01_0000,
|
||||
0x02_0000,
|
||||
0x02_0000,
|
||||
256,
|
||||
)
|
||||
.expect("Cannot create gic");
|
||||
let gic = KvmGicV3Its::new(&*vm, create_test_vgic_config()).expect("Cannot create gic");
|
||||
|
||||
let res = get_dist_regs(&gic.device);
|
||||
assert!(res.is_ok());
|
||||
@@ -549,16 +532,7 @@ mod tests {
|
||||
let hv = crate::new().unwrap();
|
||||
let vm = hv.create_vm().unwrap();
|
||||
let _ = vm.create_vcpu(0, None).unwrap();
|
||||
let gic = KvmGicV3Its::new(
|
||||
&*vm,
|
||||
1,
|
||||
0x0900_0000 - 0x01_0000,
|
||||
0x01_0000,
|
||||
0x02_0000,
|
||||
0x02_0000,
|
||||
256,
|
||||
)
|
||||
.expect("Cannot create gic");
|
||||
let gic = KvmGicV3Its::new(&*vm, create_test_vgic_config()).expect("Cannot create gic");
|
||||
|
||||
let gicr_typer = vec![123];
|
||||
let res = get_redist_regs(&gic.device, &gicr_typer);
|
||||
@@ -575,16 +549,7 @@ mod tests {
|
||||
let hv = crate::new().unwrap();
|
||||
let vm = hv.create_vm().unwrap();
|
||||
let _ = vm.create_vcpu(0, None).unwrap();
|
||||
let gic = KvmGicV3Its::new(
|
||||
&*vm,
|
||||
1,
|
||||
0x0900_0000 - 0x01_0000,
|
||||
0x01_0000,
|
||||
0x02_0000,
|
||||
0x02_0000,
|
||||
256,
|
||||
)
|
||||
.expect("Cannot create gic");
|
||||
let gic = KvmGicV3Its::new(&*vm, create_test_vgic_config()).expect("Cannot create gic");
|
||||
|
||||
let gicr_typer = vec![123];
|
||||
let res = get_icc_regs(&gic.device, &gicr_typer);
|
||||
@@ -602,14 +567,7 @@ mod tests {
|
||||
let vm = hv.create_vm().unwrap();
|
||||
let _ = vm.create_vcpu(0, None).unwrap();
|
||||
let gic = vm
|
||||
.create_vgic(
|
||||
1,
|
||||
0x0900_0000 - 0x01_0000,
|
||||
0x01_0000,
|
||||
0x02_0000,
|
||||
0x02_0000,
|
||||
256,
|
||||
)
|
||||
.create_vgic(create_test_vgic_config())
|
||||
.expect("Cannot create gic");
|
||||
|
||||
assert!(gic.lock().unwrap().save_data_tables().is_ok());
|
||||
|
||||
@@ -200,13 +200,7 @@ pub fn construct_gicr_typers(vcpu_states: &[CpuState]) -> Vec<u64> {
|
||||
let mut gicr_typers: Vec<u64> = Vec::new();
|
||||
for (index, state) in vcpu_states.iter().enumerate() {
|
||||
let state: VcpuKvmState = state.clone().into();
|
||||
let last = {
|
||||
if index == vcpu_states.len() - 1 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
};
|
||||
let last = (index == vcpu_states.len() - 1) as u64;
|
||||
// state.sys_regs is a big collection of system registers, including MIPDR_EL1
|
||||
let mpidr: Vec<Register> = state
|
||||
.sys_regs
|
||||
|
||||
@@ -97,8 +97,7 @@ pub fn is_system_register(regid: u64) -> bool {
|
||||
|
||||
assert!(
|
||||
!(size != KVM_REG_SIZE_U32 && size != KVM_REG_SIZE_U64),
|
||||
"Unexpected register size for system register {}",
|
||||
size
|
||||
"Unexpected register size for system register {size}"
|
||||
);
|
||||
|
||||
true
|
||||
|
||||
@@ -16,7 +16,7 @@ pub use crate::aarch64::{
|
||||
VcpuKvmState,
|
||||
};
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::arch::aarch64::gic::Vgic;
|
||||
use crate::arch::aarch64::gic::{Vgic, VgicConfig};
|
||||
use crate::cpu;
|
||||
use crate::hypervisor;
|
||||
use crate::vec_with_array_field;
|
||||
@@ -60,8 +60,8 @@ use crate::{
|
||||
use aarch64::{RegList, Register, StandardRegisters};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use kvm_bindings::{
|
||||
kvm_enable_cap, kvm_guest_debug, kvm_msr_entry, MsrList, KVM_CAP_HYPERV_SYNIC,
|
||||
KVM_CAP_SPLIT_IRQCHIP, KVM_GUESTDBG_ENABLE, KVM_GUESTDBG_SINGLESTEP, KVM_GUESTDBG_USE_HW_BP,
|
||||
kvm_enable_cap, kvm_msr_entry, MsrList, KVM_CAP_HYPERV_SYNIC, KVM_CAP_SPLIT_IRQCHIP,
|
||||
KVM_GUESTDBG_USE_HW_BP,
|
||||
};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use x86_64::check_required_kvm_extensions;
|
||||
@@ -74,16 +74,17 @@ pub use kvm_bindings;
|
||||
#[cfg(feature = "tdx")]
|
||||
use kvm_bindings::KVMIO;
|
||||
pub use kvm_bindings::{
|
||||
kvm_clock_data, kvm_create_device, kvm_device_type_KVM_DEV_TYPE_VFIO, kvm_irq_routing,
|
||||
kvm_irq_routing_entry, kvm_mp_state, kvm_userspace_memory_region, KVM_IRQ_ROUTING_IRQCHIP,
|
||||
KVM_IRQ_ROUTING_MSI, KVM_MEM_LOG_DIRTY_PAGES, KVM_MEM_READONLY, KVM_MSI_VALID_DEVID,
|
||||
kvm_clock_data, kvm_create_device, kvm_device_type_KVM_DEV_TYPE_VFIO, kvm_guest_debug,
|
||||
kvm_irq_routing, kvm_irq_routing_entry, kvm_mp_state, kvm_userspace_memory_region,
|
||||
KVM_GUESTDBG_ENABLE, KVM_GUESTDBG_SINGLESTEP, KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI,
|
||||
KVM_MEM_LOG_DIRTY_PAGES, KVM_MEM_READONLY, KVM_MSI_VALID_DEVID,
|
||||
};
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use kvm_bindings::{
|
||||
kvm_regs, user_fpsimd_state, user_pt_regs, KVM_NR_SPSR, KVM_REG_ARM64, KVM_REG_ARM64_SYSREG,
|
||||
KVM_REG_ARM64_SYSREG_CRM_MASK, KVM_REG_ARM64_SYSREG_CRN_MASK, KVM_REG_ARM64_SYSREG_OP0_MASK,
|
||||
KVM_REG_ARM64_SYSREG_OP1_MASK, KVM_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM_CORE,
|
||||
KVM_REG_SIZE_U128, KVM_REG_SIZE_U32, KVM_REG_SIZE_U64,
|
||||
kvm_regs, user_fpsimd_state, user_pt_regs, KVM_GUESTDBG_USE_HW, KVM_NR_SPSR, KVM_REG_ARM64,
|
||||
KVM_REG_ARM64_SYSREG, KVM_REG_ARM64_SYSREG_CRM_MASK, KVM_REG_ARM64_SYSREG_CRN_MASK,
|
||||
KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP1_MASK, KVM_REG_ARM64_SYSREG_OP2_MASK,
|
||||
KVM_REG_ARM_CORE, KVM_REG_SIZE_U128, KVM_REG_SIZE_U32, KVM_REG_SIZE_U64,
|
||||
};
|
||||
pub use kvm_ioctls;
|
||||
pub use kvm_ioctls::{Cap, Kvm};
|
||||
@@ -329,7 +330,7 @@ impl KvmVm {
|
||||
Ok(VfioDeviceFd::new_from_kvm(device_fd))
|
||||
}
|
||||
/// Checks if a particular `Cap` is available.
|
||||
fn check_extension(&self, c: Cap) -> bool {
|
||||
pub fn check_extension(&self, c: Cap) -> bool {
|
||||
self.fd.check_extension(c)
|
||||
}
|
||||
}
|
||||
@@ -413,25 +414,9 @@ impl vm::Vm for KvmVm {
|
||||
///
|
||||
/// Creates a virtual GIC device.
|
||||
///
|
||||
fn create_vgic(
|
||||
&self,
|
||||
vcpu_count: u64,
|
||||
dist_addr: u64,
|
||||
dist_size: u64,
|
||||
redist_size: u64,
|
||||
msi_size: u64,
|
||||
nr_irqs: u32,
|
||||
) -> vm::Result<Arc<Mutex<dyn Vgic>>> {
|
||||
let gic_device = KvmGicV3Its::new(
|
||||
self,
|
||||
vcpu_count,
|
||||
dist_addr,
|
||||
dist_size,
|
||||
redist_size,
|
||||
msi_size,
|
||||
nr_irqs,
|
||||
)
|
||||
.map_err(|e| vm::HypervisorVmError::CreateVgic(anyhow!("Vgic error {:?}", e)))?;
|
||||
fn create_vgic(&self, config: VgicConfig) -> vm::Result<Arc<Mutex<dyn Vgic>>> {
|
||||
let gic_device = KvmGicV3Its::new(self, config)
|
||||
.map_err(|e| vm::HypervisorVmError::CreateVgic(anyhow!("Vgic error {:?}", e)))?;
|
||||
Ok(Arc::new(Mutex::new(gic_device)))
|
||||
}
|
||||
///
|
||||
@@ -837,7 +822,7 @@ impl vm::Vm for KvmVm {
|
||||
tdx_command(
|
||||
&self.fd.as_raw_fd(),
|
||||
TdxCommand::InitMemRegion,
|
||||
if measure { 1 } else { 0 },
|
||||
u32::from(measure),
|
||||
&data as *const _ as u64,
|
||||
)
|
||||
.map_err(vm::HypervisorVmError::InitMemRegionTdx)
|
||||
@@ -1075,6 +1060,20 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the number of supported hardware breakpoints
|
||||
///
|
||||
fn get_guest_debug_hw_bps(&self) -> usize {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
4
|
||||
}
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
{
|
||||
self.kvm.get_guest_debug_hw_bps() as usize
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Vcpu struct for KVM
|
||||
pub struct KvmVcpu {
|
||||
@@ -1124,7 +1123,9 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
state.regs.regs[i] = self
|
||||
.fd
|
||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off))
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?;
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?
|
||||
.try_into()
|
||||
.unwrap();
|
||||
off += std::mem::size_of::<u64>();
|
||||
}
|
||||
|
||||
@@ -1134,28 +1135,36 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
state.regs.sp = self
|
||||
.fd
|
||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off))
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?;
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
// Second one, the program counter.
|
||||
let off = offset__of!(user_pt_regs, pc);
|
||||
state.regs.pc = self
|
||||
.fd
|
||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off))
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?;
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
// Next is the processor state.
|
||||
let off = offset__of!(user_pt_regs, pstate);
|
||||
state.regs.pstate = self
|
||||
.fd
|
||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off))
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?;
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
// The stack pointer associated with EL1
|
||||
let off = offset__of!(kvm_regs, sp_el1);
|
||||
state.sp_el1 = self
|
||||
.fd
|
||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off))
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?;
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
// Exception Link Register for EL1, when taking an exception to EL1, this register
|
||||
// holds the address to which to return afterwards.
|
||||
@@ -1163,7 +1172,9 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
state.elr_el1 = self
|
||||
.fd
|
||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off))
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?;
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
// Saved Program Status Registers, there are 5 of them used in the kernel.
|
||||
let mut off = offset__of!(kvm_regs, spsr);
|
||||
@@ -1171,7 +1182,9 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
state.spsr[i] = self
|
||||
.fd
|
||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off))
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?;
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?
|
||||
.try_into()
|
||||
.unwrap();
|
||||
off += std::mem::size_of::<u64>();
|
||||
}
|
||||
|
||||
@@ -1182,8 +1195,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
state.fp_regs.vregs[i] = self
|
||||
.fd
|
||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U128, off))
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?
|
||||
.into();
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?;
|
||||
off += mem::size_of::<u128>();
|
||||
}
|
||||
|
||||
@@ -1193,7 +1205,8 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.fd
|
||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U32, off))
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?
|
||||
as u32;
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
// Floating-point Control Register
|
||||
let off = offset__of!(kvm_regs, fp_regs) + offset__of!(user_fpsimd_state, fpcr);
|
||||
@@ -1201,7 +1214,8 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.fd
|
||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U32, off))
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCoreRegister(e.into()))?
|
||||
as u32;
|
||||
.try_into()
|
||||
.unwrap();
|
||||
Ok(state)
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -1229,7 +1243,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
self.fd
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, off),
|
||||
state.regs.regs[i],
|
||||
state.regs.regs[i].into(),
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
off += std::mem::size_of::<u64>();
|
||||
@@ -1237,33 +1251,51 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
|
||||
let off = offset__of!(user_pt_regs, sp);
|
||||
self.fd
|
||||
.set_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off), state.regs.sp)
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, off),
|
||||
state.regs.sp.into(),
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
|
||||
let off = offset__of!(user_pt_regs, pc);
|
||||
self.fd
|
||||
.set_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off), state.regs.pc)
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, off),
|
||||
state.regs.pc.into(),
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
|
||||
let off = offset__of!(user_pt_regs, pstate);
|
||||
self.fd
|
||||
.set_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off), state.regs.pstate)
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, off),
|
||||
state.regs.pstate.into(),
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
|
||||
let off = offset__of!(kvm_regs, sp_el1);
|
||||
self.fd
|
||||
.set_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off), state.sp_el1)
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, off),
|
||||
state.sp_el1.into(),
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
|
||||
let off = offset__of!(kvm_regs, elr_el1);
|
||||
self.fd
|
||||
.set_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off), state.elr_el1)
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, off),
|
||||
state.elr_el1.into(),
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
|
||||
let mut off = offset__of!(kvm_regs, spsr);
|
||||
for i in 0..KVM_NR_SPSR as usize {
|
||||
self.fd
|
||||
.set_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off), state.spsr[i])
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, off),
|
||||
state.spsr[i].into(),
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
off += std::mem::size_of::<u64>();
|
||||
}
|
||||
@@ -1273,7 +1305,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
self.fd
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U128, off),
|
||||
state.fp_regs.vregs[i] as u64,
|
||||
state.fp_regs.vregs[i],
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
off += mem::size_of::<u128>();
|
||||
@@ -1283,7 +1315,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
self.fd
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U32, off),
|
||||
state.fp_regs.fpsr as u64,
|
||||
state.fp_regs.fpsr.into(),
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
|
||||
@@ -1291,7 +1323,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
self.fd
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U32, off),
|
||||
state.fp_regs.fpcr as u64,
|
||||
state.fp_regs.fpcr.into(),
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
Ok(())
|
||||
@@ -1582,7 +1614,6 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets debug registers to set hardware breakpoints and/or enable single step.
|
||||
///
|
||||
@@ -1591,32 +1622,47 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
addrs: &[vm_memory::GuestAddress],
|
||||
singlestep: bool,
|
||||
) -> cpu::Result<()> {
|
||||
if addrs.len() > 4 {
|
||||
return Err(cpu::HypervisorCpuError::SetDebugRegs(anyhow!(
|
||||
"Support 4 breakpoints at most but {} addresses are passed",
|
||||
addrs.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let mut dbg = kvm_guest_debug {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
control: KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP,
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
control: KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW,
|
||||
..Default::default()
|
||||
};
|
||||
if singlestep {
|
||||
dbg.control |= KVM_GUESTDBG_SINGLESTEP;
|
||||
}
|
||||
|
||||
// Set bits 9 and 10.
|
||||
// bit 9: GE (global exact breakpoint enable) flag.
|
||||
// bit 10: always 1.
|
||||
dbg.arch.debugreg[7] = 0x0600;
|
||||
// Set the debug registers.
|
||||
// Here we assume that the number of addresses do not exceed what
|
||||
// `Hypervisor::get_guest_debug_hw_bps()` specifies.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
// Set bits 9 and 10.
|
||||
// bit 9: GE (global exact breakpoint enable) flag.
|
||||
// bit 10: always 1.
|
||||
dbg.arch.debugreg[7] = 0x0600;
|
||||
|
||||
for (i, addr) in addrs.iter().enumerate() {
|
||||
dbg.arch.debugreg[i] = addr.0;
|
||||
// Set global breakpoint enable flag
|
||||
dbg.arch.debugreg[7] |= 2 << (i * 2);
|
||||
for (i, addr) in addrs.iter().enumerate() {
|
||||
dbg.arch.debugreg[i] = addr.0;
|
||||
// Set global breakpoint enable flag
|
||||
dbg.arch.debugreg[7] |= 2 << (i * 2);
|
||||
}
|
||||
}
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
{
|
||||
for (i, addr) in addrs.iter().enumerate() {
|
||||
// DBGBCR_EL1 (Debug Breakpoint Control Registers, D13.3.2):
|
||||
// bit 0: 1 (Enabled)
|
||||
// bit 1~2: 0b11 (PMC = EL1/EL0)
|
||||
// bit 5~8: 0b1111 (BAS = AArch64)
|
||||
// others: 0
|
||||
dbg.arch.dbg_bcr[i] = 0b1u64 | 0b110u64 | 0b1_1110_0000u64;
|
||||
// DBGBVR_EL1 (Debug Breakpoint Value Registers, D13.3.3):
|
||||
// bit 2~52: VA[2:52]
|
||||
dbg.arch.dbg_bvr[i] = (!0u64 >> 11) & addr.0;
|
||||
}
|
||||
}
|
||||
|
||||
self.fd
|
||||
.set_guest_debug(&dbg)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetDebugRegs(e.into()))
|
||||
@@ -1663,9 +1709,12 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
| KVM_REG_ARM64_SYSREG_CRN_MASK
|
||||
| KVM_REG_ARM64_SYSREG_CRM_MASK
|
||||
| KVM_REG_ARM64_SYSREG_OP2_MASK)) as u64);
|
||||
self.fd
|
||||
Ok(self
|
||||
.fd
|
||||
.get_one_reg(id)
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetSysRegister(e.into()))
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetSysRegister(e.into()))?
|
||||
.try_into()
|
||||
.unwrap())
|
||||
}
|
||||
///
|
||||
/// Configure core registers for a given CPU.
|
||||
@@ -1691,7 +1740,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
self.fd
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, pstate),
|
||||
PSTATE_FAULT_BITS_64,
|
||||
PSTATE_FAULT_BITS_64.into(),
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
|
||||
@@ -1700,7 +1749,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
// Setting the PC (Processor Counter) to the current program address (kernel address).
|
||||
let pc = offset__of!(user_pt_regs, pc) + kreg_off;
|
||||
self.fd
|
||||
.set_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, pc), boot_ip as u64)
|
||||
.set_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, pc), boot_ip.into())
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
|
||||
// Last mandatory thing to set -> the address pointing to the FDT (also called DTB).
|
||||
@@ -1709,7 +1758,10 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
// We are choosing to place it the end of DRAM. See `get_fdt_addr`.
|
||||
let regs0 = offset__of!(user_pt_regs, regs) + kreg_off;
|
||||
self.fd
|
||||
.set_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, regs0), fdt_start)
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, regs0),
|
||||
fdt_start.into(),
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
}
|
||||
Ok(())
|
||||
@@ -1876,7 +1928,9 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
addr: self
|
||||
.fd
|
||||
.get_one_reg(*index)
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetSysRegister(e.into()))?,
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetSysRegister(e.into()))?
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1981,7 +2035,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
// Set system registers
|
||||
for reg in &state.sys_regs {
|
||||
self.fd
|
||||
.set_one_reg(reg.id, reg.addr)
|
||||
.set_one_reg(reg.id, reg.addr.into())
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetSysRegister(e.into()))?;
|
||||
}
|
||||
|
||||
@@ -2098,6 +2152,23 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.set_device_attr(&cpu_attr)
|
||||
.map_err(|_| cpu::HypervisorCpuError::InitializePmu)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Get the frequency of the TSC if available
|
||||
///
|
||||
fn tsc_khz(&self) -> cpu::Result<Option<u32>> {
|
||||
match self.fd.get_tsc_khz() {
|
||||
Err(e) => {
|
||||
if e.errno() == libc::EIO {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(cpu::HypervisorCpuError::GetTscKhz(e.into()))
|
||||
}
|
||||
}
|
||||
Ok(v) => Ok(Some(v)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KvmVcpu {
|
||||
|
||||
@@ -50,6 +50,9 @@ pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
|
||||
if !kvm.check_extension(Cap::ImmediateExit) {
|
||||
return Err(KvmError::CapabilityMissing(Cap::ImmediateExit));
|
||||
}
|
||||
if !kvm.check_extension(Cap::GetTscKhz) {
|
||||
return Err(KvmError::CapabilityMissing(Cap::GetTscKhz));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
|
||||
@@ -61,7 +61,7 @@ pub use vm::{
|
||||
Vm, VmOps,
|
||||
};
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum HypervisorType {
|
||||
Kvm,
|
||||
Mshv,
|
||||
|
||||
@@ -38,9 +38,7 @@ use std::fs::File;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::arch::x86::{
|
||||
CpuIdEntry, FpuState, LapicState, MsrEntry, SpecialRegisters, StandardRegisters,
|
||||
};
|
||||
use crate::arch::x86::{CpuIdEntry, FpuState, MsrEntry};
|
||||
|
||||
const DIRTY_BITMAP_CLEAR_DIRTY: u64 = 0x4;
|
||||
const DIRTY_BITMAP_SET_DIRTY: u64 = 0x8;
|
||||
@@ -299,7 +297,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
///
|
||||
/// Returns the vCPU general purpose registers.
|
||||
///
|
||||
fn get_regs(&self) -> cpu::Result<StandardRegisters> {
|
||||
fn get_regs(&self) -> cpu::Result<crate::arch::x86::StandardRegisters> {
|
||||
Ok(self
|
||||
.fd
|
||||
.get_regs()
|
||||
@@ -310,7 +308,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
///
|
||||
/// Sets the vCPU general purpose registers.
|
||||
///
|
||||
fn set_regs(&self, regs: &StandardRegisters) -> cpu::Result<()> {
|
||||
fn set_regs(&self, regs: &crate::arch::x86::StandardRegisters) -> cpu::Result<()> {
|
||||
let regs = (*regs).into();
|
||||
self.fd
|
||||
.set_regs(®s)
|
||||
@@ -320,7 +318,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
///
|
||||
/// Returns the vCPU special registers.
|
||||
///
|
||||
fn get_sregs(&self) -> cpu::Result<SpecialRegisters> {
|
||||
fn get_sregs(&self) -> cpu::Result<crate::arch::x86::SpecialRegisters> {
|
||||
Ok(self
|
||||
.fd
|
||||
.get_sregs()
|
||||
@@ -331,7 +329,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
///
|
||||
/// Sets the vCPU special registers.
|
||||
///
|
||||
fn set_sregs(&self, sregs: &SpecialRegisters) -> cpu::Result<()> {
|
||||
fn set_sregs(&self, sregs: &crate::arch::x86::SpecialRegisters) -> cpu::Result<()> {
|
||||
let sregs = (*sregs).into();
|
||||
self.fd
|
||||
.set_sregs(&sregs)
|
||||
@@ -581,7 +579,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
///
|
||||
/// Returns the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
|
||||
///
|
||||
fn get_lapic(&self) -> cpu::Result<LapicState> {
|
||||
fn get_lapic(&self) -> cpu::Result<crate::arch::x86::LapicState> {
|
||||
Ok(self
|
||||
.fd
|
||||
.get_lapic()
|
||||
@@ -592,7 +590,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
///
|
||||
/// Sets the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
|
||||
///
|
||||
fn set_lapic(&self, lapic: &LapicState) -> cpu::Result<()> {
|
||||
fn set_lapic(&self, lapic: &crate::arch::x86::LapicState) -> cpu::Result<()> {
|
||||
let lapic: mshv_bindings::LapicState = (*lapic).clone().into();
|
||||
self.fd
|
||||
.set_lapic(&lapic)
|
||||
@@ -703,7 +701,6 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
msr!(msr_index::MSR_LSTAR),
|
||||
msr!(msr_index::MSR_KERNEL_GS_BASE),
|
||||
msr!(msr_index::MSR_SYSCALL_MASK),
|
||||
msr!(msr_index::MSR_IA32_TSC),
|
||||
msr_data!(msr_index::MSR_MTRRdefType, MTRR_ENABLE | MTRR_MEM_TYPE_WB),
|
||||
]
|
||||
.to_vec()
|
||||
@@ -1093,7 +1090,7 @@ impl vm::Vm for MshvVm {
|
||||
flags,
|
||||
guest_pfn: guest_phys_addr >> PAGE_SHIFT,
|
||||
size: memory_size,
|
||||
userspace_addr: userspace_addr as u64,
|
||||
userspace_addr,
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::aarch64::VcpuInit;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::arch::aarch64::gic::Vgic;
|
||||
use crate::arch::aarch64::gic::{Vgic, VgicConfig};
|
||||
#[cfg(feature = "tdx")]
|
||||
use crate::arch::x86::CpuIdEntry;
|
||||
use crate::cpu::Vcpu;
|
||||
@@ -273,15 +273,7 @@ pub trait Vm: Send + Sync + Any {
|
||||
/// Creates a new KVM vCPU file descriptor and maps the memory corresponding
|
||||
fn create_vcpu(&self, id: u8, vm_ops: Option<Arc<dyn VmOps>>) -> Result<Arc<dyn Vcpu>>;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn create_vgic(
|
||||
&self,
|
||||
vcpu_count: u64,
|
||||
dist_addr: u64,
|
||||
dist_size: u64,
|
||||
redist_size: u64,
|
||||
msi_size: u64,
|
||||
nr_irqs: u32,
|
||||
) -> Result<Arc<Mutex<dyn Vgic>>>;
|
||||
fn create_vgic(&self, config: VgicConfig) -> Result<Arc<Mutex<dyn Vgic>>>;
|
||||
|
||||
/// Registers an event to be signaled whenever a certain address is written to.
|
||||
fn register_ioevent(
|
||||
@@ -334,19 +326,25 @@ pub trait Vm: Send + Sync + Any {
|
||||
fn get_dirty_log(&self, slot: u32, base_gpa: u64, memory_size: u64) -> Result<Vec<u64>>;
|
||||
#[cfg(feature = "tdx")]
|
||||
/// Initalize TDX on this VM
|
||||
fn tdx_init(&self, cpuid: &[CpuIdEntry], max_vcpus: u32) -> Result<()>;
|
||||
fn tdx_init(&self, _cpuid: &[CpuIdEntry], _max_vcpus: u32) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
#[cfg(feature = "tdx")]
|
||||
/// Finalize the configuration of TDX on this VM
|
||||
fn tdx_finalize(&self) -> Result<()>;
|
||||
fn tdx_finalize(&self) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
#[cfg(feature = "tdx")]
|
||||
/// Initalize a TDX memory region for this VM
|
||||
fn tdx_init_memory_region(
|
||||
&self,
|
||||
host_address: u64,
|
||||
guest_address: u64,
|
||||
size: u64,
|
||||
measure: bool,
|
||||
) -> Result<()>;
|
||||
_host_address: u64,
|
||||
_guest_address: u64,
|
||||
_size: u64,
|
||||
_measure: bool,
|
||||
) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
/// Downcast to the underlying hypervisor VM type
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
}
|
||||
|
||||
@@ -5,4 +5,4 @@ authors = ["The Chromium OS Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
vmm-sys-util = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
@@ -28,10 +28,12 @@ pub mod inn;
|
||||
// generated with bindgen /usr/include/linux/sockios.h --no-unstable-rust
|
||||
// --constified-enum '*' --with-derive-default
|
||||
pub mod sockios;
|
||||
pub use if_tun::*;
|
||||
pub use iff::*;
|
||||
pub use inn::*;
|
||||
pub use sockios::*;
|
||||
pub use if_tun::{
|
||||
sock_fprog, IFF_MULTI_QUEUE, IFF_NO_PI, IFF_TAP, IFF_VNET_HDR, TUN_F_CSUM, TUN_F_TSO4,
|
||||
TUN_F_TSO6, TUN_F_TSO_ECN, TUN_F_UFO,
|
||||
};
|
||||
pub use iff::{ifreq, net_device_flags_IFF_UP, setsockopt, sockaddr, AF_INET};
|
||||
pub use inn::sockaddr_in;
|
||||
|
||||
pub const TUNTAP: ::std::os::raw::c_uint = 84;
|
||||
|
||||
|
||||
@@ -6,23 +6,23 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
epoll = "4.3.1"
|
||||
getrandom = "0.2.7"
|
||||
libc = "0.2.131"
|
||||
getrandom = "0.2.8"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
net_gen = { path = "../net_gen" }
|
||||
rate_limiter = { path = "../rate_limiter" }
|
||||
serde = "1.0.143"
|
||||
thiserror = "1.0.32"
|
||||
versionize = "0.1.6"
|
||||
serde = "1.0.150"
|
||||
thiserror = "1.0.37"
|
||||
versionize = "0.1.9"
|
||||
versionize_derive = "0.1.4"
|
||||
virtio-bindings = "0.1.0"
|
||||
virtio-queue = "0.5.0"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
virtio-queue = "0.7.0"
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
[dev-dependencies]
|
||||
once_cell = "1.13.1"
|
||||
pnet = "0.31.0"
|
||||
pnet_datalink = "0.31.0"
|
||||
serde_json = "1.0.83"
|
||||
once_cell = "1.16.0"
|
||||
pnet = "0.34.0"
|
||||
pnet_datalink = "0.34.0"
|
||||
serde_json = "1.0.89"
|
||||
|
||||
@@ -131,17 +131,19 @@ pub fn build_net_config_space(
|
||||
config: &mut VirtioNetConfig,
|
||||
mac: MacAddr,
|
||||
num_queues: usize,
|
||||
mtu: Option<u16>,
|
||||
avail_features: &mut u64,
|
||||
) {
|
||||
config.mac.copy_from_slice(mac.get_bytes());
|
||||
*avail_features |= 1 << VIRTIO_NET_F_MAC;
|
||||
|
||||
build_net_config_space_with_mq(config, num_queues, avail_features);
|
||||
build_net_config_space_with_mq(config, num_queues, mtu, avail_features);
|
||||
}
|
||||
|
||||
pub fn build_net_config_space_with_mq(
|
||||
config: &mut VirtioNetConfig,
|
||||
num_queues: usize,
|
||||
mtu: Option<u16>,
|
||||
avail_features: &mut u64,
|
||||
) {
|
||||
let num_queue_pairs = (num_queues / 2) as u16;
|
||||
@@ -151,6 +153,9 @@ pub fn build_net_config_space_with_mq(
|
||||
config.max_virtqueue_pairs = num_queue_pairs;
|
||||
*avail_features |= 1 << VIRTIO_NET_F_MQ;
|
||||
}
|
||||
if let Some(mtu) = mtu {
|
||||
config.mtu = mtu;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn virtio_features_to_tap_offload(features: u64) -> c_uint {
|
||||
|
||||
@@ -125,7 +125,7 @@ impl<'de> Deserialize<'de> for MacAddr {
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
MacAddr::parse_str(&s)
|
||||
.map_err(|e| D::Error::custom(format!("The provided MAC address is invalid: {}", e)))
|
||||
.map_err(|e| D::Error::custom(format!("The provided MAC address is invalid: {e}")))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ mod tests {
|
||||
|
||||
let mac = MacAddr::parse_str("12:34:56:78:9a:BC").unwrap();
|
||||
|
||||
println!("parsed MAC address: {}", mac);
|
||||
println!("parsed MAC address: {mac}");
|
||||
|
||||
let bytes = mac.get_bytes();
|
||||
assert_eq!(bytes, [0x12u8, 0x34, 0x56, 0x78, 0x9a, 0xbc]);
|
||||
|
||||
@@ -12,9 +12,9 @@ use thiserror::Error;
|
||||
pub enum Error {
|
||||
#[error("Failed to convert an hexadecimal string into an integer: {0}")]
|
||||
ConvertHexStringToInt(std::num::ParseIntError),
|
||||
#[error("Error related to the multiqueue support (no support TAP side).")]
|
||||
#[error("Error related to the multiqueue support (no support TAP side)")]
|
||||
MultiQueueNoTapSupport,
|
||||
#[error("Error related to the multiqueue support (no support device side).")]
|
||||
#[error("Error related to the multiqueue support (no support device side)")]
|
||||
MultiQueueNoDeviceSupport,
|
||||
#[error("Failed to read the TAP flags from sysfs: {0}")]
|
||||
ReadSysfsTunFlags(io::Error),
|
||||
@@ -30,6 +30,8 @@ pub enum Error {
|
||||
TapGetMac(TapError),
|
||||
#[error("Setting vnet header size failed: {0}")]
|
||||
TapSetVnetHdrSize(TapError),
|
||||
#[error("Setting MTU failed: {0}")]
|
||||
TapSetMtu(TapError),
|
||||
#[error("Enabling tap interface failed: {0}")]
|
||||
TapEnable(TapError),
|
||||
}
|
||||
@@ -39,7 +41,7 @@ type Result<T> = std::result::Result<T, Error>;
|
||||
fn check_mq_support(if_name: &Option<&str>, queue_pairs: usize) -> Result<()> {
|
||||
if let Some(tap_name) = if_name {
|
||||
let mq = queue_pairs > 1;
|
||||
let path = format!("/sys/class/net/{}/tun_flags", tap_name);
|
||||
let path = format!("/sys/class/net/{tap_name}/tun_flags");
|
||||
// interface does not exist, check is not required
|
||||
if !Path::new(&path).exists() {
|
||||
return Ok(());
|
||||
@@ -63,6 +65,7 @@ pub fn open_tap(
|
||||
ip_addr: Option<Ipv4Addr>,
|
||||
netmask: Option<Ipv4Addr>,
|
||||
host_mac: &mut Option<MacAddr>,
|
||||
mtu: Option<u16>,
|
||||
num_rx_q: usize,
|
||||
flags: Option<i32>,
|
||||
) -> Result<Vec<Tap>> {
|
||||
@@ -95,6 +98,9 @@ pub fn open_tap(
|
||||
} else {
|
||||
*host_mac = Some(tap.get_mac_addr().map_err(Error::TapGetMac)?)
|
||||
}
|
||||
if let Some(mtu) = mtu {
|
||||
tap.set_mtu(mtu as i32).map_err(Error::TapSetMtu)?;
|
||||
}
|
||||
tap.enable().map_err(Error::TapEnable)?;
|
||||
|
||||
tap.set_vnet_hdr_size(vnet_hdr_size)
|
||||
|
||||
@@ -86,7 +86,7 @@ impl TxVirtio {
|
||||
let result = unsafe {
|
||||
libc::writev(
|
||||
tap.as_raw_fd() as libc::c_int,
|
||||
iovecs.as_ptr() as *const libc::iovec,
|
||||
iovecs.as_ptr(),
|
||||
iovecs.len() as libc::c_int,
|
||||
)
|
||||
};
|
||||
@@ -104,6 +104,10 @@ impl TxVirtio {
|
||||
return Err(NetQueuePairError::WriteTap(e));
|
||||
}
|
||||
|
||||
if (result as usize) < vnet_hdr_len() {
|
||||
return Err(NetQueuePairError::InvalidVirtioNetHeader);
|
||||
}
|
||||
|
||||
self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
|
||||
self.counter_frames += Wrapping(1);
|
||||
|
||||
@@ -185,7 +189,7 @@ impl RxVirtio {
|
||||
.translate_gva(access_platform, desc.len() as usize),
|
||||
10,
|
||||
)
|
||||
.unwrap();
|
||||
.ok_or(NetQueuePairError::DescriptorInvalidHeader)?;
|
||||
let mut next_desc = Some(desc);
|
||||
|
||||
let mut iovecs = Vec::new();
|
||||
@@ -220,7 +224,7 @@ impl RxVirtio {
|
||||
let result = unsafe {
|
||||
libc::readv(
|
||||
tap.as_raw_fd() as libc::c_int,
|
||||
iovecs.as_ptr() as *const libc::iovec,
|
||||
iovecs.as_ptr(),
|
||||
iovecs.len() as libc::c_int,
|
||||
)
|
||||
};
|
||||
@@ -238,6 +242,10 @@ impl RxVirtio {
|
||||
return Err(NetQueuePairError::ReadTap(e));
|
||||
}
|
||||
|
||||
if (result as usize) < vnet_hdr_len() {
|
||||
return Err(NetQueuePairError::InvalidVirtioNetHeader);
|
||||
}
|
||||
|
||||
// Write num_buffers to guest memory. We simply write 1 as we
|
||||
// never spread the frame over more than one descriptor chain.
|
||||
desc_chain
|
||||
@@ -288,7 +296,7 @@ pub struct NetCounters {
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum NetQueuePairError {
|
||||
#[error("No memory configured.")]
|
||||
#[error("No memory configured")]
|
||||
NoMemoryConfigured,
|
||||
#[error("Error registering listener: {0}")]
|
||||
RegisterListener(io::Error),
|
||||
@@ -302,9 +310,9 @@ pub enum NetQueuePairError {
|
||||
GuestMemory(vm_memory::GuestMemoryError),
|
||||
#[error("Returned an error while iterating through the queue: {0}")]
|
||||
QueueIteratorFailed(virtio_queue::Error),
|
||||
#[error("Descriptor chain is too short.")]
|
||||
#[error("Descriptor chain is too short")]
|
||||
DescriptorChainTooShort,
|
||||
#[error("Descriptor chain does not contain valid descriptors.")]
|
||||
#[error("Descriptor chain does not contain valid descriptors")]
|
||||
DescriptorChainInvalid,
|
||||
#[error("Failed to determine if queue needed notification: {0}")]
|
||||
QueueNeedsNotification(virtio_queue::Error),
|
||||
@@ -312,6 +320,10 @@ pub enum NetQueuePairError {
|
||||
QueueEnableNotification(virtio_queue::Error),
|
||||
#[error("Failed to add used index to the queue: {0}")]
|
||||
QueueAddUsed(virtio_queue::Error),
|
||||
#[error("Descriptor with invalid virtio-net header")]
|
||||
DescriptorInvalidHeader,
|
||||
#[error("Invalid virtio-net header")]
|
||||
InvalidVirtioNetHeader,
|
||||
}
|
||||
|
||||
pub struct NetQueuePair {
|
||||
|
||||
@@ -26,13 +26,13 @@ pub enum Error {
|
||||
ConfigureTap(IoError),
|
||||
#[error("Unable to retrieve features: {0}")]
|
||||
GetFeatures(IoError),
|
||||
#[error("Missing multiqueue support in the kernel.")]
|
||||
#[error("Missing multiqueue support in the kernel")]
|
||||
MultiQueueKernelSupport,
|
||||
#[error("ioctl failed: {0}")]
|
||||
IoctlError(IoError),
|
||||
#[error("Failed to create a socket: {0}")]
|
||||
NetUtil(NetUtilError),
|
||||
#[error("Invalid interface name.")]
|
||||
#[error("Invalid interface name")]
|
||||
InvalidIfname,
|
||||
#[error("Error parsing MAC data: {0}")]
|
||||
MacParsing(IoError),
|
||||
@@ -297,6 +297,37 @@ impl Tap {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mtu(&self) -> Result<i32> {
|
||||
let sock = create_unix_socket().map_err(Error::NetUtil)?;
|
||||
|
||||
let ifreq = self.get_ifreq();
|
||||
|
||||
// ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
let ret = unsafe { ioctl_with_ref(&sock, net_gen::sockios::SIOCGIFMTU as c_ulong, &ifreq) };
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
let mtu = unsafe { ifreq.ifr_ifru.ifru_mtu };
|
||||
|
||||
Ok(mtu)
|
||||
}
|
||||
|
||||
pub fn set_mtu(&self, mtu: i32) -> Result<()> {
|
||||
let sock = create_unix_socket().map_err(Error::NetUtil)?;
|
||||
|
||||
let mut ifreq = self.get_ifreq();
|
||||
ifreq.ifr_ifru.ifru_mtu = mtu;
|
||||
|
||||
// ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
let ret = unsafe { ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFMTU as c_ulong, &ifreq) };
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the offload flags for the tap interface.
|
||||
pub fn set_offload(&self, flags: c_uint) -> Result<()> {
|
||||
// ioctl is safe. Called with a valid tap fd, and we check the return.
|
||||
@@ -555,7 +586,7 @@ mod tests {
|
||||
let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
|
||||
|
||||
let t = Tap::new(1).unwrap();
|
||||
println!("created tap: {:?}", t);
|
||||
println!("created tap: {t:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -28,10 +28,10 @@ pub enum OptionParserError {
|
||||
impl fmt::Display for OptionParserError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
OptionParserError::UnknownOption(s) => write!(f, "unknown option: {}", s),
|
||||
OptionParserError::InvalidSyntax(s) => write!(f, "invalid syntax:{}", s),
|
||||
OptionParserError::UnknownOption(s) => write!(f, "unknown option: {s}"),
|
||||
OptionParserError::InvalidSyntax(s) => write!(f, "invalid syntax:{s}"),
|
||||
OptionParserError::Conversion(field, value) => {
|
||||
write!(f, "unable to convert {} for {}", value, field)
|
||||
write!(f, "unable to convert {value} for {field}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,23 +10,20 @@ kvm = ["vfio-ioctls/kvm"]
|
||||
mshv = ["vfio-ioctls/mshv"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.62"
|
||||
anyhow = "1.0.66"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main", features = ["fam-wrappers"] }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vfio_user = { path = "../vfio_user" }
|
||||
vmm-sys-util = "0.10.0"
|
||||
libc = "0.2.131"
|
||||
vmm-sys-util = "0.11.0"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
serde = { version = "1.0.143", features = ["derive"] }
|
||||
thiserror = "1.0.32"
|
||||
versionize = "0.1.6"
|
||||
serde = { version = "1.0.150", features = ["derive"] }
|
||||
thiserror = "1.0.37"
|
||||
versionize = "0.1.9"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = "0.8.0"
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
|
||||
[dependencies.vfio-bindings]
|
||||
version = "0.3.1"
|
||||
features = ["fam-wrappers"]
|
||||
vm-memory = "0.10.0"
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
@@ -514,9 +514,9 @@ impl Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::Error::*;
|
||||
match self {
|
||||
BarAddressInvalid(a, s) => write!(f, "address {} size {} too big", a, s),
|
||||
BarInUse(b) => write!(f, "bar {} already used", b),
|
||||
BarInUse64(b) => write!(f, "64bit bar {} already used(requires two regs)", b),
|
||||
BarAddressInvalid(a, s) => write!(f, "address {a} size {s} too big"),
|
||||
BarInUse(b) => write!(f, "bar {b} already used"),
|
||||
BarInUse64(b) => write!(f, "64bit bar {b} already used(requires two regs)"),
|
||||
BarInvalid(b) => write!(f, "bar {} invalid, max {}", b, NUM_BAR_REGS - 1),
|
||||
BarInvalid64(b) => write!(
|
||||
f,
|
||||
@@ -524,18 +524,18 @@ impl Display for Error {
|
||||
b,
|
||||
NUM_BAR_REGS - 1
|
||||
),
|
||||
BarSizeInvalid(s) => write!(f, "bar address {} not a power of two", s),
|
||||
BarSizeInvalid(s) => write!(f, "bar address {s} not a power of two"),
|
||||
CapabilityEmpty => write!(f, "empty capabilities are invalid"),
|
||||
CapabilityLengthInvalid(l) => write!(f, "Invalid capability length {}", l),
|
||||
CapabilitySpaceFull(s) => write!(f, "capability of size {} doesn't fit", s),
|
||||
CapabilityLengthInvalid(l) => write!(f, "Invalid capability length {l}"),
|
||||
CapabilitySpaceFull(s) => write!(f, "capability of size {s} doesn't fit"),
|
||||
Decode32BarSize => write!(f, "failed to decode 32 bits BAR size"),
|
||||
Decode64BarSize => write!(f, "failed to decode 64 bits BAR size"),
|
||||
Encode32BarSize => write!(f, "failed to encode 32 bits BAR size"),
|
||||
Encode64BarSize => write!(f, "failed to encode 64 bits BAR size"),
|
||||
RomBarAddressInvalid(a, s) => write!(f, "address {} size {} too big", a, s),
|
||||
RomBarInUse(b) => write!(f, "rom bar {} already used", b),
|
||||
RomBarAddressInvalid(a, s) => write!(f, "address {a} size {s} too big"),
|
||||
RomBarInUse(b) => write!(f, "rom bar {b} already used"),
|
||||
RomBarInvalid(b) => write!(f, "rom bar {} invalid, max {}", b, NUM_BAR_REGS - 1),
|
||||
RomBarSizeInvalid(s) => write!(f, "rom bar address {} not a power of two", s),
|
||||
RomBarSizeInvalid(s) => write!(f, "rom bar address {s} not a power of two"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ pub enum Error {
|
||||
IoRegistrationFailed(u64, configuration::Error),
|
||||
/// Expected resource not found.
|
||||
MissingResource,
|
||||
/// Invalid resource.
|
||||
InvalidResource(Resource),
|
||||
}
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
@@ -29,14 +31,15 @@ impl Display for Error {
|
||||
use self::Error::*;
|
||||
|
||||
match self {
|
||||
CapabilitiesSetup(e) => write!(f, "failed to add capability {}", e),
|
||||
CapabilitiesSetup(e) => write!(f, "failed to add capability {e}"),
|
||||
IoAllocationFailed(size) => {
|
||||
write!(f, "failed to allocate space for an IO BAR, size={}", size)
|
||||
write!(f, "failed to allocate space for an IO BAR, size={size}")
|
||||
}
|
||||
IoRegistrationFailed(addr, e) => {
|
||||
write!(f, "failed to register an IO BAR, addr={} err={}", addr, e)
|
||||
write!(f, "failed to register an IO BAR, addr={addr} err={e}")
|
||||
}
|
||||
MissingResource => write!(f, "failed to find expected resource"),
|
||||
InvalidResource(r) => write!(f, "invalid resource {r:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ enum Error {
|
||||
UpdateInterruptRoute(io::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Versionize)]
|
||||
#[derive(Debug, Clone, Versionize, Eq, PartialEq)]
|
||||
pub struct MsixTableEntry {
|
||||
pub msg_addr_lo: u32,
|
||||
pub msg_addr_hi: u32,
|
||||
@@ -258,7 +258,7 @@ impl MsixConfig {
|
||||
let modulo_offset = offset % MSIX_TABLE_ENTRIES_MODULO;
|
||||
|
||||
// Store the value of the entry before modification
|
||||
let mut old_entry: Option<MsixTableEntry> = None;
|
||||
let old_entry = self.table_entries[index].clone();
|
||||
|
||||
match data.len() {
|
||||
4 => {
|
||||
@@ -268,7 +268,6 @@ impl MsixConfig {
|
||||
0x4 => self.table_entries[index].msg_addr_hi = value,
|
||||
0x8 => self.table_entries[index].msg_data = value,
|
||||
0xc => {
|
||||
old_entry = Some(self.table_entries[index].clone());
|
||||
self.table_entries[index].vector_ctl = value;
|
||||
}
|
||||
_ => error!("invalid offset"),
|
||||
@@ -284,7 +283,6 @@ impl MsixConfig {
|
||||
self.table_entries[index].msg_addr_hi = (value >> 32) as u32;
|
||||
}
|
||||
0x8 => {
|
||||
old_entry = Some(self.table_entries[index].clone());
|
||||
self.table_entries[index].msg_data = (value & 0xffff_ffffu64) as u32;
|
||||
self.table_entries[index].vector_ctl = (value >> 32) as u32;
|
||||
}
|
||||
@@ -296,10 +294,18 @@ impl MsixConfig {
|
||||
_ => error!("invalid data length"),
|
||||
};
|
||||
|
||||
// Update interrupt routes
|
||||
if self.enabled && !self.masked {
|
||||
let table_entry = &self.table_entries[index];
|
||||
let table_entry = &self.table_entries[index];
|
||||
|
||||
// Optimisation to avoid excessive updates
|
||||
if &old_entry == table_entry {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update interrupt routes
|
||||
// Optimisation: only update routes if the entry is not masked;
|
||||
// this is safe because if the entry is masked (starts masked as per spec)
|
||||
// in the table then it won't be triggered. (See: #4273)
|
||||
if self.enabled && !self.masked && !table_entry.masked() {
|
||||
let config = MsiIrqSourceConfig {
|
||||
high_addr: table_entry.msg_addr_hi,
|
||||
low_addr: table_entry.msg_addr_lo,
|
||||
@@ -323,15 +329,15 @@ impl MsixConfig {
|
||||
// has been injected, the pending bit in the PBA needs to be cleared.
|
||||
// All of this is valid only if MSI-X has not been masked for the whole
|
||||
// device.
|
||||
if let Some(old_entry) = old_entry {
|
||||
// Check if bit has been flipped
|
||||
if !self.masked()
|
||||
&& old_entry.masked()
|
||||
&& !self.table_entries[index].masked()
|
||||
&& self.get_pba_bit(index as u16) == 1
|
||||
{
|
||||
self.inject_msix_and_clear_pba(index);
|
||||
}
|
||||
|
||||
// Check if bit has been flipped
|
||||
if !self.masked()
|
||||
&& self.enabled()
|
||||
&& old_entry.masked()
|
||||
&& !table_entry.masked()
|
||||
&& self.get_pba_bit(index as u16) == 1
|
||||
{
|
||||
self.inject_msix_and_clear_pba(index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ use crate::{
|
||||
use anyhow::anyhow;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use hypervisor::HypervisorVmError;
|
||||
use libc::{sysconf, _SC_PAGESIZE};
|
||||
use std::any::Any;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::io;
|
||||
@@ -413,7 +414,7 @@ impl VfioCommon {
|
||||
resources: Option<Vec<Resource>>,
|
||||
) -> Result<Vec<PciBarConfiguration>, PciDeviceError> {
|
||||
let mut bars = Vec::new();
|
||||
let mut bar_id = VFIO_PCI_BAR0_REGION_INDEX as u32;
|
||||
let mut bar_id = VFIO_PCI_BAR0_REGION_INDEX;
|
||||
|
||||
// Going through all regular regions to compute the BAR size.
|
||||
// We're not saving the BAR address to restore it, because we
|
||||
@@ -526,7 +527,7 @@ impl VfioCommon {
|
||||
}
|
||||
|
||||
// Invert and add 1 to to find size
|
||||
region_size = (!combined_size + 1) as u64;
|
||||
region_size = !combined_size + 1;
|
||||
} else {
|
||||
region_type = PciBarRegionType::Memory32BitRegion;
|
||||
|
||||
@@ -571,7 +572,12 @@ impl VfioCommon {
|
||||
PciBarRegionType::Memory64BitRegion => {
|
||||
// BAR allocation must be naturally aligned
|
||||
mmio_allocator
|
||||
.allocate(restored_bar_addr, region_size, Some(region_size))
|
||||
.allocate(
|
||||
restored_bar_addr,
|
||||
region_size,
|
||||
// SAFETY: FFI call. Trivially safe.
|
||||
Some(unsafe { sysconf(_SC_PAGESIZE) as GuestUsize }),
|
||||
)
|
||||
.ok_or(PciDeviceError::IoAllocationFailed(region_size))?
|
||||
}
|
||||
};
|
||||
@@ -599,7 +605,7 @@ impl VfioCommon {
|
||||
start: bar_addr,
|
||||
length: region_size,
|
||||
type_: region_type,
|
||||
index: bar_id as u32,
|
||||
index: bar_id,
|
||||
user_memory_regions: Vec::new(),
|
||||
});
|
||||
|
||||
@@ -1278,8 +1284,10 @@ impl VfioPciDevice {
|
||||
self.iommu_attached
|
||||
}
|
||||
|
||||
fn align_4k(address: u64) -> u64 {
|
||||
(address + 0xfff) & 0xffff_ffff_ffff_f000
|
||||
fn align_page_size(address: u64) -> u64 {
|
||||
// SAFETY: FFI call. Trivially safe.
|
||||
let page_size = unsafe { sysconf(_SC_PAGESIZE) as u64 };
|
||||
(address + page_size - 1) & !(page_size - 1)
|
||||
}
|
||||
|
||||
fn is_4k_aligned(address: u64) -> bool {
|
||||
@@ -1339,6 +1347,7 @@ impl VfioPciDevice {
|
||||
let mut sparse_areas = Vec::new();
|
||||
let mut current_offset = 0;
|
||||
for (range_offset, range_size) in inter_ranges {
|
||||
let range_offset = Self::align_page_size(range_offset);
|
||||
if range_offset > current_offset {
|
||||
sparse_areas.push(VfioRegionSparseMmapArea {
|
||||
offset: current_offset,
|
||||
@@ -1346,13 +1355,13 @@ impl VfioPciDevice {
|
||||
});
|
||||
}
|
||||
|
||||
current_offset = Self::align_4k(range_offset + range_size);
|
||||
current_offset = Self::align_page_size(range_offset + range_size);
|
||||
}
|
||||
|
||||
if region_size > current_offset {
|
||||
sparse_areas.push(VfioRegionSparseMmapArea {
|
||||
offset: current_offset,
|
||||
size: region_size - current_offset,
|
||||
offset: Self::align_page_size(current_offset),
|
||||
size: Self::align_page_size(region_size - current_offset),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -276,12 +276,7 @@ impl VfioUserPciDevice {
|
||||
self.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.dma_map(
|
||||
offset,
|
||||
region.start_addr().raw_value(),
|
||||
region.len() as u64,
|
||||
fd,
|
||||
)
|
||||
.dma_map(offset, region.start_addr().raw_value(), region.len(), fd)
|
||||
.map_err(VfioUserPciDeviceError::DmaMap)
|
||||
}
|
||||
|
||||
@@ -292,7 +287,7 @@ impl VfioUserPciDevice {
|
||||
self.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.dma_unmap(region.start_addr().raw_value(), region.len() as u64)
|
||||
.dma_unmap(region.start_addr().raw_value(), region.len())
|
||||
.map_err(VfioUserPciDeviceError::DmaUnmap)
|
||||
}
|
||||
}
|
||||
@@ -622,13 +617,13 @@ impl<M: GuestAddressSpace + Sync + Send> ExternalDmaMapping for VfioUserDmaMappi
|
||||
.map_err(|e| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("Error mapping region: {}", e),
|
||||
format!("Error mapping region: {e}"),
|
||||
)
|
||||
})
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("Region not found for 0x{:x}", gpa),
|
||||
format!("Region not found for 0x{gpa:x}"),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -641,7 +636,7 @@ impl<M: GuestAddressSpace + Sync + Send> ExternalDmaMapping for VfioUserDmaMappi
|
||||
.map_err(|e| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("Error unmapping region: {}", e),
|
||||
format!("Error unmapping region: {e}"),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ edition = "2021"
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.2.17", features = ["wrap_help","cargo"] }
|
||||
clap = { version = "4.0.29", features = ["wrap_help","cargo"] }
|
||||
dirs = "4.0.0"
|
||||
serde = { version = "1.0.143", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.83"
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.89"
|
||||
test_infra = { path = "../test_infra" }
|
||||
thiserror = "1.0.32"
|
||||
thiserror = "1.0.37"
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.2.17", features = ["cargo"] }
|
||||
clap = { version = "4.0.29", features = ["cargo"] }
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let mut git_human_readable = "v".to_owned() + crate_version!();
|
||||
if let Ok(git_out) = Command::new("git").args(&["describe", "--dirty"]).output() {
|
||||
if let Ok(git_out) = Command::new("git").args(["describe", "--dirty"]).output() {
|
||||
if git_out.status.success() {
|
||||
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {
|
||||
git_human_readable = git_out_str;
|
||||
@@ -22,5 +22,5 @@ fn main() {
|
||||
// variable GIT_HUMAN_READABLE, so that it can be reused from the binary.
|
||||
// Particularly, this is used from the main.rs to display the exact
|
||||
// version information.
|
||||
println!("cargo:rustc-env=GIT_HUMAN_READABLE={}", git_human_readable);
|
||||
println!("cargo:rustc-env=GIT_HUMAN_READABLE={git_human_readable}");
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ extern crate clap;
|
||||
|
||||
mod performance_tests;
|
||||
|
||||
use clap::{Arg, Command as ClapCommand};
|
||||
use clap::{Arg, ArgAction, Command as ClapCommand};
|
||||
use performance_tests::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
@@ -20,6 +20,7 @@ use std::{
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
use test_infra::FioOps;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
@@ -51,7 +52,7 @@ pub struct MetricsReport {
|
||||
impl Default for MetricsReport {
|
||||
fn default() -> Self {
|
||||
let mut git_human_readable = "".to_string();
|
||||
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() {
|
||||
git_human_readable = String::from_utf8(git_out.stdout)
|
||||
.unwrap()
|
||||
@@ -66,7 +67,7 @@ impl Default for MetricsReport {
|
||||
}
|
||||
|
||||
let mut git_revision = "".to_string();
|
||||
if let Ok(git_out) = Command::new("git").args(&["rev-parse", "HEAD"]).output() {
|
||||
if let Ok(git_out) = Command::new("git").args(["rev-parse", "HEAD"]).output() {
|
||||
if git_out.status.success() {
|
||||
git_revision = String::from_utf8(git_out.stdout)
|
||||
.unwrap()
|
||||
@@ -82,7 +83,7 @@ impl Default for MetricsReport {
|
||||
|
||||
let mut git_commit_date = "".to_string();
|
||||
if let Ok(git_out) = Command::new("git")
|
||||
.args(&["show", "-s", "--format=%cd"])
|
||||
.args(["show", "-s", "--format=%cd"])
|
||||
.output()
|
||||
{
|
||||
if git_out.status.success() {
|
||||
@@ -116,7 +117,7 @@ pub struct PerformanceTestOverrides {
|
||||
impl fmt::Display for PerformanceTestOverrides {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if let Some(test_iterations) = self.test_iterations {
|
||||
write!(f, "test_iterations = {}", test_iterations)?;
|
||||
write!(f, "test_iterations = {test_iterations}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -140,19 +141,19 @@ impl fmt::Display for PerformanceTestControl {
|
||||
self.test_timeout, self.test_iterations
|
||||
);
|
||||
if let Some(o) = self.num_queues {
|
||||
output = format!("{}, num_queues = {}", output, o);
|
||||
output = format!("{output}, num_queues = {o}");
|
||||
}
|
||||
if let Some(o) = self.queue_size {
|
||||
output = format!("{}, queue_size = {}", output, o);
|
||||
output = format!("{output}, queue_size = {o}");
|
||||
}
|
||||
if let Some(o) = self.net_rx {
|
||||
output = format!("{}, net_rx = {}", output, o);
|
||||
output = format!("{output}, net_rx = {o}");
|
||||
}
|
||||
if let Some(o) = &self.fio_ops {
|
||||
output = format!("{}, fio_ops = {}", output, o);
|
||||
output = format!("{output}, fio_ops = {o}");
|
||||
}
|
||||
|
||||
write!(f, "{}", output)
|
||||
write!(f, "{output}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,29 +503,28 @@ fn main() {
|
||||
Arg::new("test-filter")
|
||||
.long("test-filter")
|
||||
.help("Filter metrics tests to run based on provided keywords")
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("list-tests")
|
||||
.long("list-tests")
|
||||
.help("Print the list of availale metrics tests")
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.action(ArgAction::SetTrue)
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("report-file")
|
||||
.long("report-file")
|
||||
.help("Report file. Standard error is used if not specified")
|
||||
.takes_value(true),
|
||||
.num_args(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("iterations")
|
||||
.long("iterations")
|
||||
.help("Override number of test iterations")
|
||||
.takes_value(true),
|
||||
.num_args(1),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
@@ -536,7 +536,7 @@ fn main() {
|
||||
.filter(|t| !(cfg!(target_arch = "aarch64") && t.name == "virtio_net_latency_us"))
|
||||
.collect();
|
||||
|
||||
if cmd_arguments.is_present("list-tests") {
|
||||
if cmd_arguments.get_flag("list-tests") {
|
||||
for test in test_list.iter() {
|
||||
println!("\"{}\" ({})", test.name, test.control);
|
||||
}
|
||||
@@ -544,7 +544,7 @@ fn main() {
|
||||
return;
|
||||
}
|
||||
|
||||
let test_filter = match cmd_arguments.values_of("test-filter") {
|
||||
let test_filter = match cmd_arguments.get_many::<String>("test-filter") {
|
||||
Some(s) => s.collect(),
|
||||
None => Vec::new(),
|
||||
};
|
||||
@@ -556,7 +556,7 @@ fn main() {
|
||||
|
||||
let overrides = Arc::new(PerformanceTestOverrides {
|
||||
test_iterations: cmd_arguments
|
||||
.value_of("iterations")
|
||||
.get_one::<String>("iterations")
|
||||
.map(|s| s.parse())
|
||||
.transpose()
|
||||
.unwrap_or_default(),
|
||||
@@ -569,7 +569,7 @@ fn main() {
|
||||
metrics_report.results.push(r);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Aborting test due to error: '{:?}'", e);
|
||||
eprintln!("Aborting test due to error: '{e:?}'");
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
@@ -579,11 +579,11 @@ fn main() {
|
||||
cleanup_tests();
|
||||
|
||||
let mut report_file: Box<dyn std::io::Write + Send> =
|
||||
if let Some(file) = cmd_arguments.value_of("report-file") {
|
||||
if let Some(file) = cmd_arguments.get_one::<String>("report-file") {
|
||||
Box::new(
|
||||
std::fs::File::create(std::path::Path::new(file))
|
||||
.map_err(|e| {
|
||||
eprintln!("Error opening report file: {}: {}", file, e);
|
||||
eprintln!("Error opening report file: {file}: {e}");
|
||||
std::process::exit(1);
|
||||
})
|
||||
.unwrap(),
|
||||
@@ -599,7 +599,7 @@ fn main() {
|
||||
.as_bytes(),
|
||||
)
|
||||
.map_err(|e| {
|
||||
eprintln!("Error writing report file: {}", e);
|
||||
eprintln!("Error writing report file: {e}");
|
||||
std::process::exit(1);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
@@ -6,40 +6,23 @@
|
||||
// Performance tests
|
||||
|
||||
use crate::{mean, PerformanceTestControl};
|
||||
use serde_json::Value;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::string::String;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use std::{fmt, fs};
|
||||
use test_infra::Error as InfraError;
|
||||
use test_infra::*;
|
||||
use wait_timeout::ChildExt;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub const FOCAL_IMAGE_NAME: &str = "focal-server-cloudimg-amd64-custom-20210609-0.raw";
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub const FOCAL_IMAGE_NAME: &str = "focal-server-cloudimg-arm64-custom-20210929-0-update-tool.raw";
|
||||
|
||||
#[derive(Debug)]
|
||||
enum WaitTimeoutError {
|
||||
Timedout,
|
||||
ExitStatus,
|
||||
General(std::io::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
BootTimeParse,
|
||||
EthrLogFile(std::io::Error),
|
||||
EthrLogParse,
|
||||
FioOutputParse,
|
||||
Iperf3Parse,
|
||||
Infra(InfraError),
|
||||
Spawn(std::io::Error),
|
||||
Scp(SshCommandError),
|
||||
WaitTimeout(WaitTimeoutError),
|
||||
}
|
||||
|
||||
impl From<InfraError> for Error {
|
||||
@@ -54,8 +37,7 @@ pub fn init_tests() {
|
||||
// The test image can not be created on tmpfs (e.g. /tmp) filesystem,
|
||||
// as tmpfs does not support O_DIRECT
|
||||
assert!(exec_host_command_output(&format!(
|
||||
"dd if=/dev/zero of={} bs=1M count=4096",
|
||||
BLK_IO_TEST_IMG
|
||||
"dd if=/dev/zero of={BLK_IO_TEST_IMG} bs=1M count=4096"
|
||||
))
|
||||
.status
|
||||
.success());
|
||||
@@ -63,7 +45,7 @@ pub fn init_tests() {
|
||||
|
||||
pub fn cleanup_tests() {
|
||||
fs::remove_file(BLK_IO_TEST_IMG)
|
||||
.unwrap_or_else(|_| panic!("Failed to remove file '{}'.", BLK_IO_TEST_IMG));
|
||||
.unwrap_or_else(|_| panic!("Failed to remove file '{BLK_IO_TEST_IMG}'."));
|
||||
}
|
||||
|
||||
// Performance tests are expected to be executed sequentially, so we can
|
||||
@@ -94,128 +76,6 @@ fn direct_kernel_boot_path() -> PathBuf {
|
||||
kernel_path
|
||||
}
|
||||
|
||||
// Wait the child process for a given timeout
|
||||
fn child_wait_timeout(child: &mut Child, timeout: u64) -> Result<(), WaitTimeoutError> {
|
||||
match child.wait_timeout(Duration::from_secs(timeout)) {
|
||||
Err(e) => {
|
||||
return Err(WaitTimeoutError::General(e));
|
||||
}
|
||||
Ok(s) => match s {
|
||||
None => {
|
||||
return Err(WaitTimeoutError::Timedout);
|
||||
}
|
||||
Some(s) => {
|
||||
if !s.success() {
|
||||
return Err(WaitTimeoutError::ExitStatus);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_iperf3_output(output: &[u8], sender: bool) -> Result<f64, Error> {
|
||||
std::panic::catch_unwind(|| {
|
||||
let s = String::from_utf8_lossy(output);
|
||||
let v: Value = serde_json::from_str(&s).expect("'iperf3' parse error: invalid json output");
|
||||
|
||||
let bps: f64 = if sender {
|
||||
v["end"]["sum_sent"]["bits_per_second"]
|
||||
.as_f64()
|
||||
.expect("'iperf3' parse error: missing entry 'end.sum_sent.bits_per_second'")
|
||||
} else {
|
||||
v["end"]["sum_received"]["bits_per_second"]
|
||||
.as_f64()
|
||||
.expect("'iperf3' parse error: missing entry 'end.sum_received.bits_per_second'")
|
||||
};
|
||||
|
||||
bps
|
||||
})
|
||||
.map_err(|_| {
|
||||
eprintln!(
|
||||
"=============== iperf3 output ===============\n\n{}\n\n===========end============\n\n",
|
||||
String::from_utf8_lossy(output)
|
||||
);
|
||||
Error::Iperf3Parse
|
||||
})
|
||||
}
|
||||
|
||||
fn measure_virtio_net_throughput(
|
||||
test_timeout: u32,
|
||||
queue_pairs: u32,
|
||||
guest: &Guest,
|
||||
receive: bool,
|
||||
) -> Result<f64, Error> {
|
||||
let default_port = 5201;
|
||||
|
||||
// 1. start the iperf3 server on the guest
|
||||
for n in 0..queue_pairs {
|
||||
guest
|
||||
.ssh_command(&format!("iperf3 -s -p {} -D", default_port + n))
|
||||
.map_err(InfraError::SshCommand)?;
|
||||
}
|
||||
|
||||
thread::sleep(Duration::new(1, 0));
|
||||
|
||||
// 2. start the iperf3 client on host to measure RX through-put
|
||||
let mut clients = Vec::new();
|
||||
for n in 0..queue_pairs {
|
||||
let mut cmd = Command::new("iperf3");
|
||||
cmd.args(&[
|
||||
"-J", // Output in JSON format
|
||||
"-c",
|
||||
&guest.network.guest_ip,
|
||||
"-p",
|
||||
&format!("{}", default_port + n),
|
||||
"-t",
|
||||
&format!("{}", test_timeout),
|
||||
]);
|
||||
// For measuring the guest transmit throughput (as a sender),
|
||||
// use reverse mode of the iperf3 client on the host
|
||||
if !receive {
|
||||
cmd.args(&["-R"]);
|
||||
}
|
||||
let client = cmd
|
||||
.stderr(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.map_err(Error::Spawn)?;
|
||||
|
||||
clients.push(client);
|
||||
}
|
||||
|
||||
let mut err: Option<Error> = None;
|
||||
let mut bps = Vec::new();
|
||||
let mut failed = false;
|
||||
for c in clients {
|
||||
let mut c = c;
|
||||
if let Err(e) = child_wait_timeout(&mut c, test_timeout as u64 + 5) {
|
||||
err = Some(Error::WaitTimeout(e));
|
||||
failed = true;
|
||||
}
|
||||
|
||||
if !failed {
|
||||
// Safe to unwrap as we know the child has terminated succesffully
|
||||
let output = c.wait_with_output().unwrap();
|
||||
bps.push(parse_iperf3_output(&output.stdout, receive)?);
|
||||
} else {
|
||||
let _ = c.kill();
|
||||
let output = c.wait_with_output().unwrap();
|
||||
println!(
|
||||
"=============== Client output [Error] ===============\n\n{}\n\n===========end============\n\n",
|
||||
String::from_utf8_lossy(&output.stdout)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(e) = err {
|
||||
Err(e)
|
||||
} else {
|
||||
Ok(bps.iter().sum())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn performance_net_throughput(control: &PerformanceTestControl) -> f64 {
|
||||
let test_timeout = control.test_timeout;
|
||||
let rx = control.net_rx.unwrap();
|
||||
@@ -231,12 +91,12 @@ pub fn performance_net_throughput(control: &PerformanceTestControl) -> f64 {
|
||||
);
|
||||
|
||||
let mut child = GuestCommand::new(&guest)
|
||||
.args(&["--cpus", &format!("boot={}", num_queues)])
|
||||
.args(&["--memory", "size=4G"])
|
||||
.args(&["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(&["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.args(["--cpus", &format!("boot={num_queues}")])
|
||||
.args(["--memory", "size=4G"])
|
||||
.args(["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.default_disks()
|
||||
.args(&["--net", net_params.as_str()])
|
||||
.args(["--net", net_params.as_str()])
|
||||
.capture_output()
|
||||
.verbosity(VerbosityLevel::Warn)
|
||||
.set_print_cmd(false)
|
||||
@@ -260,95 +120,6 @@ pub fn performance_net_throughput(control: &PerformanceTestControl) -> f64 {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_ethr_latency_output(output: &[u8]) -> Result<Vec<f64>, Error> {
|
||||
std::panic::catch_unwind(|| {
|
||||
let s = String::from_utf8_lossy(output);
|
||||
let mut latency = Vec::new();
|
||||
for l in s.lines() {
|
||||
let v: Value = serde_json::from_str(l).expect("'ethr' parse error: invalid json line");
|
||||
// Skip header/summary lines
|
||||
if let Some(avg) = v["Avg"].as_str() {
|
||||
// Assume the latency unit is always "us"
|
||||
latency.push(
|
||||
avg.split("us").collect::<Vec<&str>>()[0]
|
||||
.parse::<f64>()
|
||||
.expect("'ethr' parse error: invalid 'Avg' entry"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
assert!(
|
||||
!latency.is_empty(),
|
||||
"'ethr' parse error: no valid latency data found"
|
||||
);
|
||||
|
||||
latency
|
||||
})
|
||||
.map_err(|_| {
|
||||
eprintln!(
|
||||
"=============== ethr output ===============\n\n{}\n\n===========end============\n\n",
|
||||
String::from_utf8_lossy(output)
|
||||
);
|
||||
Error::EthrLogParse
|
||||
})
|
||||
}
|
||||
|
||||
fn measure_virtio_net_latency(guest: &Guest, test_timeout: u32) -> Result<Vec<f64>, Error> {
|
||||
// copy the 'ethr' tool to the guest image
|
||||
let ethr_path = "/usr/local/bin/ethr";
|
||||
let ethr_remote_path = "/tmp/ethr";
|
||||
scp_to_guest(
|
||||
Path::new(ethr_path),
|
||||
Path::new(ethr_remote_path),
|
||||
&guest.network.guest_ip,
|
||||
//DEFAULT_SSH_RETRIES,
|
||||
1,
|
||||
DEFAULT_SSH_TIMEOUT,
|
||||
)
|
||||
.map_err(Error::Scp)?;
|
||||
|
||||
// Start the ethr server on the guest
|
||||
guest
|
||||
.ssh_command(&format!("{} -s &> /dev/null &", ethr_remote_path))
|
||||
.map_err(InfraError::SshCommand)?;
|
||||
|
||||
thread::sleep(Duration::new(10, 0));
|
||||
|
||||
// Start the ethr client on the host
|
||||
let log_file = guest
|
||||
.tmp_dir
|
||||
.as_path()
|
||||
.join("ethr.client.log")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
let mut c = Command::new(ethr_path)
|
||||
.args(&[
|
||||
"-c",
|
||||
&guest.network.guest_ip,
|
||||
"-t",
|
||||
"l",
|
||||
"-o",
|
||||
&log_file, // file output is JSON format
|
||||
"-d",
|
||||
&format!("{}s", test_timeout),
|
||||
])
|
||||
.stderr(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.map_err(Error::Spawn)?;
|
||||
|
||||
if let Err(e) = child_wait_timeout(&mut c, test_timeout as u64 + 5).map_err(Error::WaitTimeout)
|
||||
{
|
||||
let _ = c.kill();
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
// Parse the ethr latency test output
|
||||
let content = fs::read(log_file).map_err(Error::EthrLogFile)?;
|
||||
parse_ethr_latency_output(&content)
|
||||
}
|
||||
|
||||
pub fn performance_net_latency(control: &PerformanceTestControl) -> f64 {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = performance_test_new_guest(Box::new(focal));
|
||||
@@ -361,12 +132,12 @@ pub fn performance_net_latency(control: &PerformanceTestControl) -> f64 {
|
||||
);
|
||||
|
||||
let mut child = GuestCommand::new(&guest)
|
||||
.args(&["--cpus", &format!("boot={}", num_queues)])
|
||||
.args(&["--memory", "size=4G"])
|
||||
.args(&["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(&["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.args(["--cpus", &format!("boot={num_queues}")])
|
||||
.args(["--memory", "size=4G"])
|
||||
.args(["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.default_disks()
|
||||
.args(&["--net", net_params.as_str()])
|
||||
.args(["--net", net_params.as_str()])
|
||||
.capture_output()
|
||||
.verbosity(VerbosityLevel::Warn)
|
||||
.set_print_cmd(false)
|
||||
@@ -397,7 +168,6 @@ fn parse_boot_time_output(output: &[u8]) -> Result<f64, Error> {
|
||||
std::panic::catch_unwind(|| {
|
||||
let l: Vec<String> = String::from_utf8_lossy(output)
|
||||
.lines()
|
||||
.into_iter()
|
||||
.filter(|l| l.contains("Debug I/O port: Kernel code"))
|
||||
.map(|l| l.to_string())
|
||||
.collect();
|
||||
@@ -506,14 +276,14 @@ pub fn performance_boot_time(control: &PerformanceTestControl) -> f64 {
|
||||
let mut cmd = GuestCommand::new(&guest);
|
||||
|
||||
let c = cmd
|
||||
.args(&[
|
||||
.args([
|
||||
"--cpus",
|
||||
&format!("boot={}", control.num_boot_vcpus.unwrap_or(1)),
|
||||
])
|
||||
.args(&["--memory", "size=1G"])
|
||||
.args(&["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(&["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.args(&["--console", "off"])
|
||||
.args(["--memory", "size=1G"])
|
||||
.args(["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.args(["--console", "off"])
|
||||
.default_disks();
|
||||
|
||||
measure_boot_time(c, control.test_timeout).unwrap()
|
||||
@@ -533,15 +303,15 @@ pub fn performance_boot_time_pmem(control: &PerformanceTestControl) -> f64 {
|
||||
let guest = performance_test_new_guest(Box::new(focal));
|
||||
let mut cmd = GuestCommand::new(&guest);
|
||||
let c = cmd
|
||||
.args(&[
|
||||
.args([
|
||||
"--cpus",
|
||||
&format!("boot={}", control.num_boot_vcpus.unwrap_or(1)),
|
||||
])
|
||||
.args(&["--memory", "size=1G,hugepages=on"])
|
||||
.args(&["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(&["--cmdline", "root=/dev/pmem0p1 console=ttyS0 quiet rw"])
|
||||
.args(&["--console", "off"])
|
||||
.args(&[
|
||||
.args(["--memory", "size=1G,hugepages=on"])
|
||||
.args(["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(["--cmdline", "root=/dev/pmem0p1 console=ttyS0 quiet rw"])
|
||||
.args(["--console", "off"])
|
||||
.args([
|
||||
"--pmem",
|
||||
format!(
|
||||
"file={}",
|
||||
@@ -561,78 +331,6 @@ pub fn performance_boot_time_pmem(control: &PerformanceTestControl) -> f64 {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum FioOps {
|
||||
Read,
|
||||
RandomRead,
|
||||
Write,
|
||||
RandomWrite,
|
||||
}
|
||||
|
||||
impl fmt::Display for FioOps {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
FioOps::Read => write!(f, "read"),
|
||||
FioOps::RandomRead => write!(f, "randread"),
|
||||
FioOps::Write => write!(f, "write"),
|
||||
FioOps::RandomWrite => write!(f, "randwrite"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_fio_output(output: &str, fio_ops: &FioOps, num_jobs: u32) -> Result<f64, Error> {
|
||||
std::panic::catch_unwind(|| {
|
||||
let v: Value =
|
||||
serde_json::from_str(output).expect("'fio' parse error: invalid json output");
|
||||
let jobs = v["jobs"]
|
||||
.as_array()
|
||||
.expect("'fio' parse error: missing entry 'jobs'");
|
||||
assert_eq!(
|
||||
jobs.len(),
|
||||
num_jobs as usize,
|
||||
"'fio' parse error: Unexpected number of 'fio' jobs."
|
||||
);
|
||||
|
||||
let read = match fio_ops {
|
||||
FioOps::Read | FioOps::RandomRead => true,
|
||||
FioOps::Write | FioOps::RandomWrite => false,
|
||||
};
|
||||
|
||||
let mut total_bps = 0_f64;
|
||||
for j in jobs {
|
||||
if read {
|
||||
let bytes = j["read"]["io_bytes"]
|
||||
.as_u64()
|
||||
.expect("'fio' parse error: missing entry 'read.io_bytes'");
|
||||
let runtime = j["read"]["runtime"]
|
||||
.as_u64()
|
||||
.expect("'fio' parse error: missing entry 'read.runtime'")
|
||||
as f64
|
||||
/ 1000_f64;
|
||||
total_bps += bytes as f64 / runtime as f64;
|
||||
} else {
|
||||
let bytes = j["write"]["io_bytes"]
|
||||
.as_u64()
|
||||
.expect("'fio' parse error: missing entry 'write.io_bytes'");
|
||||
let runtime = j["write"]["runtime"]
|
||||
.as_u64()
|
||||
.expect("'fio' parse error: missing entry 'write.runtime'")
|
||||
as f64
|
||||
/ 1000_f64;
|
||||
total_bps += bytes as f64 / runtime as f64;
|
||||
}
|
||||
}
|
||||
|
||||
total_bps
|
||||
})
|
||||
.map_err(|_| {
|
||||
eprintln!(
|
||||
"=============== Fio output ===============\n\n{}\n\n===========end============\n\n",
|
||||
output
|
||||
);
|
||||
Error::FioOutputParse
|
||||
})
|
||||
}
|
||||
|
||||
pub fn performance_block_io(control: &PerformanceTestControl) -> f64 {
|
||||
let test_timeout = control.test_timeout;
|
||||
let num_queues = control.num_queues.unwrap();
|
||||
@@ -649,11 +347,11 @@ pub fn performance_block_io(control: &PerformanceTestControl) -> f64 {
|
||||
.to_string();
|
||||
|
||||
let mut child = GuestCommand::new(&guest)
|
||||
.args(&["--cpus", &format!("boot={}", num_queues)])
|
||||
.args(&["--memory", "size=4G"])
|
||||
.args(&["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(&["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.args(&[
|
||||
.args(["--cpus", &format!("boot={num_queues}")])
|
||||
.args(["--memory", "size=4G"])
|
||||
.args(["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.args([
|
||||
"--disk",
|
||||
format!(
|
||||
"path={}",
|
||||
@@ -665,10 +363,10 @@ pub fn performance_block_io(control: &PerformanceTestControl) -> f64 {
|
||||
guest.disk_config.disk(DiskType::CloudInit).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
format!("path={}", BLK_IO_TEST_IMG).as_str(),
|
||||
format!("path={BLK_IO_TEST_IMG}").as_str(),
|
||||
])
|
||||
.default_net()
|
||||
.args(&["--api-socket", &api_socket])
|
||||
.args(["--api-socket", &api_socket])
|
||||
.capture_output()
|
||||
.verbosity(VerbosityLevel::Warn)
|
||||
.set_print_cmd(false)
|
||||
@@ -681,8 +379,7 @@ pub fn performance_block_io(control: &PerformanceTestControl) -> f64 {
|
||||
let fio_command = format!(
|
||||
"sudo fio --filename=/dev/vdc --name=test --output-format=json \
|
||||
--direct=1 --bs=4k --ioengine=io_uring --iodepth=64 \
|
||||
--rw={} --runtime={} --numjobs={}",
|
||||
fio_ops, test_timeout, num_queues
|
||||
--rw={fio_ops} --runtime={test_timeout} --numjobs={num_queues}"
|
||||
);
|
||||
let output = guest
|
||||
.ssh_command(&fio_command)
|
||||
|
||||
@@ -10,7 +10,7 @@ path = "src/qcow.rs"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.4.3"
|
||||
libc = "0.2.131"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
remain = "0.2.4"
|
||||
vmm-sys-util = "0.10.0"
|
||||
remain = "0.2.5"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
160
qcow/src/qcow.rs
160
qcow/src/qcow.rs
@@ -80,45 +80,41 @@ impl Display for Error {
|
||||
match self {
|
||||
BackingFilesNotSupported => write!(f, "backing files not supported"),
|
||||
CompressedBlocksNotSupported => write!(f, "compressed blocks not supported"),
|
||||
EvictingCache(e) => write!(f, "failed to evict cache: {}", e),
|
||||
FileTooBig(size) => write!(
|
||||
f,
|
||||
"file larger than max of {}: {}",
|
||||
MAX_QCOW_FILE_SIZE, size
|
||||
),
|
||||
GettingFileSize(e) => write!(f, "failed to get file size: {}", e),
|
||||
GettingRefcount(e) => write!(f, "failed to get refcount: {}", e),
|
||||
EvictingCache(e) => write!(f, "failed to evict cache: {e}"),
|
||||
FileTooBig(size) => write!(f, "file larger than max of {MAX_QCOW_FILE_SIZE}: {size}"),
|
||||
GettingFileSize(e) => write!(f, "failed to get file size: {e}"),
|
||||
GettingRefcount(e) => write!(f, "failed to get refcount: {e}"),
|
||||
InvalidClusterIndex => write!(f, "invalid cluster index"),
|
||||
InvalidClusterSize => write!(f, "invalid cluster size"),
|
||||
InvalidIndex => write!(f, "invalid index"),
|
||||
InvalidL1TableOffset => write!(f, "invalid L1 table offset"),
|
||||
InvalidL1TableSize(size) => write!(f, "invalid L1 table size {}", size),
|
||||
InvalidL1TableSize(size) => write!(f, "invalid L1 table size {size}"),
|
||||
InvalidMagic => write!(f, "invalid magic"),
|
||||
InvalidOffset(_) => write!(f, "invalid offset"),
|
||||
InvalidRefcountTableOffset => write!(f, "invalid refcount table offset"),
|
||||
InvalidRefcountTableSize(size) => write!(f, "invalid refcount table size: {}", size),
|
||||
InvalidRefcountTableSize(size) => write!(f, "invalid refcount table size: {size}"),
|
||||
NoFreeClusters => write!(f, "no free clusters"),
|
||||
NoRefcountClusters => write!(f, "no refcount clusters"),
|
||||
NotEnoughSpaceForRefcounts => write!(f, "not enough space for refcounts"),
|
||||
OpeningFile(e) => write!(f, "failed to open file: {}", e),
|
||||
ReadingData(e) => write!(f, "failed to read data: {}", e),
|
||||
ReadingHeader(e) => write!(f, "failed to read header: {}", e),
|
||||
ReadingPointers(e) => write!(f, "failed to read pointers: {}", e),
|
||||
ReadingRefCountBlock(e) => write!(f, "failed to read ref count block: {}", e),
|
||||
ReadingRefCounts(e) => write!(f, "failed to read ref counts: {}", e),
|
||||
RebuildingRefCounts(e) => write!(f, "failed to rebuild ref counts: {}", e),
|
||||
OpeningFile(e) => write!(f, "failed to open file: {e}"),
|
||||
ReadingData(e) => write!(f, "failed to read data: {e}"),
|
||||
ReadingHeader(e) => write!(f, "failed to read header: {e}"),
|
||||
ReadingPointers(e) => write!(f, "failed to read pointers: {e}"),
|
||||
ReadingRefCountBlock(e) => write!(f, "failed to read ref count block: {e}"),
|
||||
ReadingRefCounts(e) => write!(f, "failed to read ref counts: {e}"),
|
||||
RebuildingRefCounts(e) => write!(f, "failed to rebuild ref counts: {e}"),
|
||||
RefcountTableOffEnd => write!(f, "refcount table offset past file end"),
|
||||
RefcountTableTooLarge => write!(f, "too many clusters specified for refcount table"),
|
||||
SeekingFile(e) => write!(f, "failed to seek file: {}", e),
|
||||
SettingFileSize(e) => write!(f, "failed to set file size: {}", e),
|
||||
SettingRefcountRefcount(e) => write!(f, "failed to set refcount refcount: {}", e),
|
||||
SeekingFile(e) => write!(f, "failed to seek file: {e}"),
|
||||
SettingFileSize(e) => write!(f, "failed to set file size: {e}"),
|
||||
SettingRefcountRefcount(e) => write!(f, "failed to set refcount refcount: {e}"),
|
||||
SizeTooSmallForNumberOfClusters => write!(f, "size too small for number of clusters"),
|
||||
TooManyL1Entries(count) => write!(f, "l1 entry table too large: {}", count),
|
||||
TooManyRefcounts(count) => write!(f, "ref count table too large: {}", count),
|
||||
TooManyL1Entries(count) => write!(f, "l1 entry table too large: {count}"),
|
||||
TooManyRefcounts(count) => write!(f, "ref count table too large: {count}"),
|
||||
UnsupportedRefcountOrder => write!(f, "unsupported refcount order"),
|
||||
UnsupportedVersion(v) => write!(f, "unsupported version: {}", v),
|
||||
WritingData(e) => write!(f, "failed to write data: {}", e),
|
||||
WritingHeader(e) => write!(f, "failed to write header: {}", e),
|
||||
UnsupportedVersion(v) => write!(f, "unsupported version: {v}"),
|
||||
WritingData(e) => write!(f, "failed to write data: {e}"),
|
||||
WritingHeader(e) => write!(f, "failed to write header: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,7 +186,7 @@ pub struct QcowHeader {
|
||||
impl QcowHeader {
|
||||
/// Creates a QcowHeader from a reference to a file.
|
||||
pub fn new(f: &mut RawFile) -> Result<QcowHeader> {
|
||||
f.seek(SeekFrom::Start(0)).map_err(Error::ReadingHeader)?;
|
||||
f.rewind().map_err(Error::ReadingHeader)?;
|
||||
let magic = f.read_u32::<BigEndian>().map_err(Error::ReadingHeader)?;
|
||||
if magic != QCOW_MAGIC {
|
||||
return Err(Error::InvalidMagic);
|
||||
@@ -255,10 +251,10 @@ impl QcowHeader {
|
||||
let cluster_bits: u32 = DEFAULT_CLUSTER_BITS;
|
||||
let cluster_size: u32 = 0x01 << cluster_bits;
|
||||
// L2 blocks are always one cluster long. They contain cluster_size/sizeof(u64) addresses.
|
||||
let l2_size: u32 = cluster_size / size_of::<u64>() as u32;
|
||||
let entries_per_cluster: u32 = cluster_size / size_of::<u64>() as u32;
|
||||
let num_clusters: u32 = div_round_up_u64(size, u64::from(cluster_size)) as u32;
|
||||
let num_l2_clusters: u32 = div_round_up_u32(num_clusters, l2_size);
|
||||
let l1_clusters: u32 = div_round_up_u32(num_l2_clusters, cluster_size);
|
||||
let num_l2_clusters: u32 = div_round_up_u32(num_clusters, entries_per_cluster);
|
||||
let l1_clusters: u32 = div_round_up_u32(num_l2_clusters, entries_per_cluster);
|
||||
let header_clusters = div_round_up_u32(size_of::<QcowHeader>() as u32, cluster_size);
|
||||
QcowHeader {
|
||||
magic: QCOW_MAGIC,
|
||||
@@ -473,10 +469,10 @@ impl QcowFile {
|
||||
QcowFile::rebuild_refcounts(&mut raw_file, header)?;
|
||||
}
|
||||
|
||||
let l2_size = cluster_size / size_of::<u64>() as u64;
|
||||
let entries_per_cluster = cluster_size / size_of::<u64>() as u64;
|
||||
let num_clusters = div_round_up_u64(header.size, cluster_size);
|
||||
let num_l2_clusters = div_round_up_u64(num_clusters, l2_size);
|
||||
let l1_clusters = div_round_up_u64(num_l2_clusters, cluster_size);
|
||||
let num_l2_clusters = div_round_up_u64(num_clusters, entries_per_cluster);
|
||||
let l1_clusters = div_round_up_u64(num_l2_clusters, entries_per_cluster);
|
||||
let header_clusters = div_round_up_u64(size_of::<QcowHeader>() as u64, cluster_size);
|
||||
if num_l2_clusters > MAX_RAM_POINTER_TABLE_SIZE {
|
||||
return Err(Error::TooManyL1Entries(num_l2_clusters));
|
||||
@@ -559,14 +555,14 @@ impl QcowFile {
|
||||
/// Creates a new QcowFile at the given path.
|
||||
pub fn new(mut file: RawFile, version: u32, virtual_size: u64) -> Result<QcowFile> {
|
||||
let header = QcowHeader::create_for_size(version, virtual_size);
|
||||
file.seek(SeekFrom::Start(0)).map_err(Error::SeekingFile)?;
|
||||
file.rewind().map_err(Error::SeekingFile)?;
|
||||
header.write_to(&mut file)?;
|
||||
|
||||
let mut qcow = Self::from(file)?;
|
||||
|
||||
// Set the refcount for each refcount table cluster.
|
||||
let cluster_size = 0x01u64 << qcow.header.cluster_bits;
|
||||
let refcount_table_base = qcow.header.refcount_table_offset as u64;
|
||||
let refcount_table_base = qcow.header.refcount_table_offset;
|
||||
let end_cluster_addr =
|
||||
refcount_table_base + u64::from(qcow.header.refcount_table_clusters) * cluster_size;
|
||||
|
||||
@@ -705,7 +701,8 @@ impl QcowFile {
|
||||
header: QcowHeader,
|
||||
cluster_size: u64,
|
||||
) -> Result<()> {
|
||||
let l1_clusters = div_round_up_u64(u64::from(header.l1_size), cluster_size);
|
||||
let entries_per_cluster = cluster_size / size_of::<u64>() as u64;
|
||||
let l1_clusters = div_round_up_u64(u64::from(header.l1_size), entries_per_cluster);
|
||||
let l1_table_offset = header.l1_table_offset;
|
||||
for i in 0..l1_clusters {
|
||||
add_ref(refcounts, cluster_size, l1_table_offset + i * cluster_size)?;
|
||||
@@ -776,10 +773,8 @@ impl QcowFile {
|
||||
refcounts: &mut [u16],
|
||||
cluster_size: u64,
|
||||
refblock_clusters: u64,
|
||||
pointers_per_cluster: u64,
|
||||
) -> Result<Vec<u64>> {
|
||||
let refcount_table_entries = div_round_up_u64(refblock_clusters, pointers_per_cluster);
|
||||
let mut ref_table = vec![0; refcount_table_entries as usize];
|
||||
let mut ref_table = vec![0; refblock_clusters as usize];
|
||||
let mut first_free_cluster: u64 = 0;
|
||||
for refblock_addr in &mut ref_table {
|
||||
loop {
|
||||
@@ -811,10 +806,7 @@ impl QcowFile {
|
||||
) -> Result<()> {
|
||||
// Rewrite the header with lazy refcounts enabled while we are rebuilding the tables.
|
||||
header.compatible_features |= COMPATIBLE_FEATURES_LAZY_REFCOUNTS;
|
||||
raw_file
|
||||
.file_mut()
|
||||
.seek(SeekFrom::Start(0))
|
||||
.map_err(Error::SeekingFile)?;
|
||||
raw_file.file_mut().rewind().map_err(Error::SeekingFile)?;
|
||||
header.write_to(raw_file.file_mut())?;
|
||||
|
||||
for (i, refblock_addr) in ref_table.iter().enumerate() {
|
||||
@@ -849,10 +841,7 @@ impl QcowFile {
|
||||
|
||||
// Rewrite the header again, now with lazy refcounts disabled.
|
||||
header.compatible_features &= !COMPATIBLE_FEATURES_LAZY_REFCOUNTS;
|
||||
raw_file
|
||||
.file_mut()
|
||||
.seek(SeekFrom::Start(0))
|
||||
.map_err(Error::SeekingFile)?;
|
||||
raw_file.file_mut().rewind().map_err(Error::SeekingFile)?;
|
||||
header.write_to(raw_file.file_mut())?;
|
||||
|
||||
Ok(())
|
||||
@@ -872,7 +861,7 @@ impl QcowFile {
|
||||
let pointers_per_cluster = cluster_size / size_of::<u64>() as u64;
|
||||
let data_clusters = div_round_up_u64(header.size, cluster_size);
|
||||
let l2_clusters = div_round_up_u64(data_clusters, pointers_per_cluster);
|
||||
let l1_clusters = div_round_up_u64(l2_clusters, cluster_size);
|
||||
let l1_clusters = div_round_up_u64(l2_clusters, pointers_per_cluster);
|
||||
let header_clusters = div_round_up_u64(size_of::<QcowHeader>() as u64, cluster_size);
|
||||
let max_clusters = data_clusters + l2_clusters + l1_clusters + header_clusters;
|
||||
let mut max_valid_cluster_index = max_clusters;
|
||||
@@ -906,12 +895,7 @@ impl QcowFile {
|
||||
set_refcount_table_refcounts(&mut refcounts, header, cluster_size)?;
|
||||
|
||||
// Allocate clusters to store the new reference count blocks.
|
||||
let ref_table = alloc_refblocks(
|
||||
&mut refcounts,
|
||||
cluster_size,
|
||||
refblock_clusters,
|
||||
pointers_per_cluster,
|
||||
)?;
|
||||
let ref_table = alloc_refblocks(&mut refcounts, cluster_size, refblock_clusters)?;
|
||||
|
||||
// Write updated reference counts and point the reftable at them.
|
||||
write_refblocks(
|
||||
@@ -962,7 +946,7 @@ impl QcowFile {
|
||||
// Gets the offset of the given guest address in the host file. If L1, L2, or data clusters have
|
||||
// yet to be allocated, return None.
|
||||
fn file_offset_read(&mut self, address: u64) -> std::io::Result<Option<u64>> {
|
||||
if address >= self.virtual_size() as u64 {
|
||||
if address >= self.virtual_size() {
|
||||
return Err(std::io::Error::from_raw_os_error(EINVAL));
|
||||
}
|
||||
|
||||
@@ -1005,7 +989,7 @@ impl QcowFile {
|
||||
// Gets the offset of the given guest address in the host file. If L1, L2, or data clusters need
|
||||
// to be allocated, they will be.
|
||||
fn file_offset_write(&mut self, address: u64) -> std::io::Result<u64> {
|
||||
if address >= self.virtual_size() as u64 {
|
||||
if address >= self.virtual_size() {
|
||||
return Err(std::io::Error::from_raw_os_error(EINVAL));
|
||||
}
|
||||
|
||||
@@ -1122,7 +1106,7 @@ impl QcowFile {
|
||||
|
||||
// Returns true if the cluster containing `address` is already allocated.
|
||||
fn cluster_allocated(&mut self, address: u64) -> std::io::Result<bool> {
|
||||
if address >= self.virtual_size() as u64 {
|
||||
if address >= self.virtual_size() {
|
||||
return Err(std::io::Error::from_raw_os_error(EINVAL));
|
||||
}
|
||||
|
||||
@@ -1194,7 +1178,7 @@ impl QcowFile {
|
||||
// Deallocate the storage for the cluster starting at `address`.
|
||||
// Any future reads of this cluster will return all zeroes.
|
||||
fn deallocate_cluster(&mut self, address: u64) -> std::io::Result<()> {
|
||||
if address >= self.virtual_size() as u64 {
|
||||
if address >= self.virtual_size() {
|
||||
return Err(std::io::Error::from_raw_os_error(EINVAL));
|
||||
}
|
||||
|
||||
@@ -1239,7 +1223,7 @@ impl QcowFile {
|
||||
.map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("failed to get cluster refcount: {}", e),
|
||||
format!("failed to get cluster refcount: {e}"),
|
||||
)
|
||||
})?;
|
||||
if refcount == 0 {
|
||||
@@ -1416,7 +1400,7 @@ impl Drop for QcowFile {
|
||||
|
||||
impl Read for QcowFile {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
let address: u64 = self.current_offset as u64;
|
||||
let address: u64 = self.current_offset;
|
||||
let read_count: usize = self.limit_range_file(address, buf.len());
|
||||
|
||||
let mut nread: usize = 0;
|
||||
@@ -1478,7 +1462,7 @@ impl Seek for QcowFile {
|
||||
|
||||
impl Write for QcowFile {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
let address: u64 = self.current_offset as u64;
|
||||
let address: u64 = self.current_offset;
|
||||
let write_count: usize = self.limit_range_file(address, buf.len());
|
||||
|
||||
let mut nwritten: usize = 0;
|
||||
@@ -1582,12 +1566,12 @@ fn offset_is_cluster_boundary(offset: u64, cluster_bits: u32) -> Result<()> {
|
||||
|
||||
// Ceiling of the division of `dividend`/`divisor`.
|
||||
fn div_round_up_u64(dividend: u64, divisor: u64) -> u64 {
|
||||
dividend / divisor + if dividend % divisor != 0 { 1 } else { 0 }
|
||||
dividend / divisor + u64::from(dividend % divisor != 0)
|
||||
}
|
||||
|
||||
// Ceiling of the division of `dividend`/`divisor`.
|
||||
fn div_round_up_u32(dividend: u32, divisor: u32) -> u32 {
|
||||
dividend / divisor + if dividend % divisor != 0 { 1 } else { 0 }
|
||||
dividend / divisor + u32::from(dividend % divisor != 0)
|
||||
}
|
||||
|
||||
fn convert_copy<R, W>(reader: &mut R, writer: &mut W, offset: u64, size: u64) -> Result<()>
|
||||
@@ -1655,9 +1639,7 @@ where
|
||||
R: Read + Seek + SeekHole,
|
||||
{
|
||||
let src_size = reader.seek(SeekFrom::End(0)).map_err(Error::SeekingFile)?;
|
||||
reader
|
||||
.seek(SeekFrom::Start(0))
|
||||
.map_err(Error::SeekingFile)?;
|
||||
reader.rewind().map_err(Error::SeekingFile)?;
|
||||
|
||||
// Ensure the destination file is empty before writing to it.
|
||||
dst_file.set_len(0).map_err(Error::SettingFileSize)?;
|
||||
@@ -1699,10 +1681,8 @@ pub fn convert(mut src_file: RawFile, dst_file: RawFile, dst_type: ImageType) ->
|
||||
|
||||
/// Detect the type of an image file by checking for a valid qcow2 header.
|
||||
pub fn detect_image_type(file: &mut RawFile) -> Result<ImageType> {
|
||||
let orig_seek = file
|
||||
.seek(SeekFrom::Current(0))
|
||||
.map_err(Error::SeekingFile)?;
|
||||
file.seek(SeekFrom::Start(0)).map_err(Error::SeekingFile)?;
|
||||
let orig_seek = file.stream_position().map_err(Error::SeekingFile)?;
|
||||
file.rewind().map_err(Error::SeekingFile)?;
|
||||
let magic = file.read_u32::<BigEndian>().map_err(Error::ReadingHeader)?;
|
||||
let image_type = if magic == QCOW_MAGIC {
|
||||
ImageType::Qcow2
|
||||
@@ -1793,7 +1773,7 @@ mod tests {
|
||||
let mut disk_file: RawFile = RawFile::new(TempFile::new().unwrap().into_file(), false);
|
||||
disk_file.write_all(header).unwrap();
|
||||
disk_file.set_len(0x1_0000_0000).unwrap();
|
||||
disk_file.seek(SeekFrom::Start(0)).unwrap();
|
||||
disk_file.rewind().unwrap();
|
||||
|
||||
testfn(disk_file); // File closed when the function exits.
|
||||
}
|
||||
@@ -1815,7 +1795,7 @@ mod tests {
|
||||
header
|
||||
.write_to(&mut disk_file)
|
||||
.expect("Failed to write header to temporary file.");
|
||||
disk_file.seek(SeekFrom::Start(0)).unwrap();
|
||||
disk_file.rewind().unwrap();
|
||||
QcowFile::from(disk_file).expect("Failed to create Qcow from default Header");
|
||||
}
|
||||
|
||||
@@ -1826,7 +1806,7 @@ mod tests {
|
||||
header
|
||||
.write_to(&mut disk_file)
|
||||
.expect("Failed to write header to temporary file.");
|
||||
disk_file.seek(SeekFrom::Start(0)).unwrap();
|
||||
disk_file.rewind().unwrap();
|
||||
QcowFile::from(disk_file).expect("Failed to create Qcow from default Header");
|
||||
}
|
||||
|
||||
@@ -1954,7 +1934,7 @@ mod tests {
|
||||
q.write_all(b"test first bytes")
|
||||
.expect("Failed to write test string.");
|
||||
let mut buf = [0u8; 4];
|
||||
q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
|
||||
q.rewind().expect("Failed to seek.");
|
||||
q.read_exact(&mut buf).expect("Failed to read.");
|
||||
assert_eq!(&buf, b"test");
|
||||
});
|
||||
@@ -2006,15 +1986,15 @@ mod tests {
|
||||
let mut q = QcowFile::from(disk_file).unwrap();
|
||||
// Write some test data.
|
||||
let b = [0x55u8; CHUNK_SIZE];
|
||||
q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
|
||||
q.rewind().expect("Failed to seek.");
|
||||
q.write_all(&b).expect("Failed to write test string.");
|
||||
// Overwrite the full cluster with zeroes.
|
||||
q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
|
||||
q.rewind().expect("Failed to seek.");
|
||||
let nwritten = q.write_zeroes(CHUNK_SIZE).expect("Failed to write zeroes.");
|
||||
assert_eq!(nwritten, CHUNK_SIZE);
|
||||
// Verify that the data was zeroed out.
|
||||
let mut buf = [0u8; CHUNK_SIZE];
|
||||
q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
|
||||
q.rewind().expect("Failed to seek.");
|
||||
q.read_exact(&mut buf).expect("Failed to read.");
|
||||
assert_eq!(buf[0], 0);
|
||||
assert_eq!(buf[CHUNK_SIZE - 1], 0);
|
||||
@@ -2456,7 +2436,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
// Check that address 0 is still zeros.
|
||||
qcow_file.seek(SeekFrom::Start(0)).expect("Failed to seek.");
|
||||
qcow_file.rewind().expect("Failed to seek.");
|
||||
let nread = qcow_file.read(&mut readback).expect("Failed to read.");
|
||||
assert_eq!(nread, BLOCK_SIZE);
|
||||
for read in readback.iter() {
|
||||
@@ -2480,7 +2460,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn seek_cur(file: &mut QcowFile) -> u64 {
|
||||
file.seek(SeekFrom::Current(0)).unwrap()
|
||||
file.stream_position().unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2544,7 +2524,7 @@ mod tests {
|
||||
assert_eq!(seek_cur(&mut file), 0xFFFF);
|
||||
|
||||
// seek_hole at or after the end of the file should return None
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
file.rewind().unwrap();
|
||||
assert_eq!(file.seek_hole(0x30000).unwrap(), None);
|
||||
assert_eq!(seek_cur(&mut file), 0);
|
||||
assert_eq!(file.seek_hole(0x30001).unwrap(), None);
|
||||
@@ -2562,33 +2542,33 @@ mod tests {
|
||||
assert_eq!(seek_cur(&mut file), 0xFFFF);
|
||||
|
||||
// seek_hole within data should return the next hole
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
file.rewind().unwrap();
|
||||
assert_eq!(file.seek_hole(0x10000).unwrap(), Some(0x20000));
|
||||
assert_eq!(seek_cur(&mut file), 0x20000);
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
file.rewind().unwrap();
|
||||
assert_eq!(file.seek_hole(0x10001).unwrap(), Some(0x20000));
|
||||
assert_eq!(seek_cur(&mut file), 0x20000);
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
file.rewind().unwrap();
|
||||
assert_eq!(file.seek_hole(0x1FFFF).unwrap(), Some(0x20000));
|
||||
assert_eq!(seek_cur(&mut file), 0x20000);
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
file.rewind().unwrap();
|
||||
assert_eq!(file.seek_hole(0xFFFF).unwrap(), Some(0xFFFF));
|
||||
assert_eq!(seek_cur(&mut file), 0xFFFF);
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
file.rewind().unwrap();
|
||||
assert_eq!(file.seek_hole(0x10000).unwrap(), Some(0x20000));
|
||||
assert_eq!(seek_cur(&mut file), 0x20000);
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
file.rewind().unwrap();
|
||||
assert_eq!(file.seek_hole(0x1FFFF).unwrap(), Some(0x20000));
|
||||
assert_eq!(seek_cur(&mut file), 0x20000);
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
file.rewind().unwrap();
|
||||
assert_eq!(file.seek_hole(0x20000).unwrap(), Some(0x20000));
|
||||
assert_eq!(seek_cur(&mut file), 0x20000);
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
file.rewind().unwrap();
|
||||
assert_eq!(file.seek_hole(0x20001).unwrap(), Some(0x20001));
|
||||
assert_eq!(seek_cur(&mut file), 0x20001);
|
||||
|
||||
// seek_hole at EOF should return None
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
file.rewind().unwrap();
|
||||
assert_eq!(file.seek_hole(0x30000).unwrap(), None);
|
||||
assert_eq!(seek_cur(&mut file), 0);
|
||||
|
||||
@@ -2599,10 +2579,10 @@ mod tests {
|
||||
// seek_hole within [0x20000, 0x30000) should now find the hole at EOF
|
||||
assert_eq!(file.seek_hole(0x20000).unwrap(), Some(0x30000));
|
||||
assert_eq!(seek_cur(&mut file), 0x30000);
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
file.rewind().unwrap();
|
||||
assert_eq!(file.seek_hole(0x20001).unwrap(), Some(0x30000));
|
||||
assert_eq!(seek_cur(&mut file), 0x30000);
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
file.rewind().unwrap();
|
||||
assert_eq!(file.seek_hole(0x30000).unwrap(), None);
|
||||
assert_eq!(seek_cur(&mut file), 0);
|
||||
});
|
||||
|
||||
@@ -66,7 +66,7 @@ impl QcowRawFile {
|
||||
non_zero_flags: u64,
|
||||
) -> io::Result<()> {
|
||||
self.file.seek(SeekFrom::Start(offset))?;
|
||||
let mut buffer = BufWriter::with_capacity(table.len() * size_of::<u64>(), &mut self.file);
|
||||
let mut buffer = BufWriter::with_capacity(std::mem::size_of_val(table), &mut self.file);
|
||||
for addr in table {
|
||||
let val = if *addr == 0 {
|
||||
0
|
||||
@@ -91,7 +91,7 @@ impl QcowRawFile {
|
||||
/// Writes a refcount block to the file.
|
||||
pub fn write_refcount_block(&mut self, offset: u64, table: &[u16]) -> io::Result<()> {
|
||||
self.file.seek(SeekFrom::Start(offset))?;
|
||||
let mut buffer = BufWriter::with_capacity(table.len() * size_of::<u16>(), &mut self.file);
|
||||
let mut buffer = BufWriter::with_capacity(std::mem::size_of_val(table), &mut self.file);
|
||||
for count in table {
|
||||
buffer.write_u16::<BigEndian>(*count)?;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ impl RawFile {
|
||||
|
||||
fn round_up(&self, offset: u64) -> u64 {
|
||||
let align: u64 = self.alignment.try_into().unwrap();
|
||||
((offset / (align + 1)) + 1) * align
|
||||
((offset + align - 1) / align) * align
|
||||
}
|
||||
|
||||
fn round_down(&self, offset: u64) -> u64 {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user