mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
383 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
18
.github/workflows/build.yaml
vendored
18
.github/workflows/build.yaml
vendored
@@ -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)"
|
||||
|
||||
526
Cargo.lock
generated
526
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
42
Cargo.toml
42
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cloud-hypervisor"
|
||||
version = "26.0.0"
|
||||
version = "28.1.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
default-run = "cloud-hypervisor"
|
||||
@@ -14,54 +14,52 @@ rust-version = "1.60"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
opt-level = "s"
|
||||
|
||||
[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-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" }
|
||||
|
||||
[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 +77,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"
|
||||
|
||||
@@ -5,4 +5,4 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
vmm-sys-util = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
@@ -141,18 +141,20 @@ fn parse_http_response(socket: &mut dyn Read) -> Result<Option<String>, Error> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn simple_api_command_with_fds<T: Read + Write + ScmSocket>(
|
||||
/// Make an API request using the fully qualified command name.
|
||||
/// For example, full_command could be "vm.create" or "vmm.ping".
|
||||
pub fn simple_api_full_command_with_fds_and_response<T: Read + Write + ScmSocket>(
|
||||
socket: &mut T,
|
||||
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
|
||||
"{} /api/v1/{} HTTP/1.1\r\nHost: localhost\r\nAccept: */*\r\n",
|
||||
method, full_command
|
||||
)
|
||||
.as_bytes()],
|
||||
&request_fds,
|
||||
@@ -175,12 +177,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)?;
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -231,7 +231,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 +631,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);
|
||||
@@ -764,11 +778,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 +879,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 +948,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 +1140,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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>> {
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
2
build.rs
2
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;
|
||||
|
||||
@@ -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"
|
||||
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())
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
470
devices/src/tpm.rs
Normal file
470
devices/src/tpm.rs
Normal file
@@ -0,0 +1,470 @@
|
||||
// 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 '{:?}' register were accessed which are Invalid",
|
||||
reg
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,6 @@ 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"
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.89"
|
||||
|
||||
448
fuzz/Cargo.lock
generated
448
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"
|
||||
@@ -115,9 +98,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 +113,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",
|
||||
"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.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -168,6 +152,8 @@ dependencies = [
|
||||
"serde_json",
|
||||
"signal-hook",
|
||||
"thiserror",
|
||||
"tpm",
|
||||
"tracer",
|
||||
"vm-memory",
|
||||
"vmm",
|
||||
"vmm-sys-util",
|
||||
@@ -214,9 +200,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 +210,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 +224,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",
|
||||
@@ -259,6 +245,9 @@ dependencies = [
|
||||
"hypervisor",
|
||||
"libc",
|
||||
"log",
|
||||
"phf",
|
||||
"thiserror",
|
||||
"tpm",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"vm-device",
|
||||
@@ -277,6 +266,27 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno-dragonfly"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event_monitor"
|
||||
version = "0.1.0"
|
||||
@@ -300,26 +310,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 +364,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",
|
||||
"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 +422,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kvm-ioctls"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/rust-vmm/kvm-ioctls?branch=main#1e03e29cdfbb0cb108a98de7a78045a5a517f18e"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3a321cabd827642499c77e27314f388dd83a717a5ca716b86476fb947f73ae4"
|
||||
dependencies = [
|
||||
"kvm-bindings",
|
||||
"libc",
|
||||
@@ -413,15 +439,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 +456,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 +481,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 +517,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 +527,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 +555,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.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -551,6 +625,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 +651,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 +669,20 @@ dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.11"
|
||||
@@ -588,33 +691,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 +726,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 +737,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 +747,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 +757,10 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial_buffer"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.14"
|
||||
@@ -673,6 +780,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 +800,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 +820,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 +915,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#7ef33f6759ae6d37a54cd66f749aa47c1c81509c"
|
||||
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#7ef33f6759ae6d37a54cd66f749aa47c1c81509c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
@@ -841,9 +967,9 @@ 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",
|
||||
"libc",
|
||||
@@ -877,6 +1003,7 @@ dependencies = [
|
||||
"seccompiler",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serial_buffer",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
@@ -893,9 +1020,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 +1054,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",
|
||||
@@ -990,8 +1117,10 @@ dependencies = [
|
||||
"seccompiler",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serial_buffer",
|
||||
"signal-hook",
|
||||
"thiserror",
|
||||
"tracer",
|
||||
"uuid",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
@@ -1010,9 +1139,9 @@ 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",
|
||||
"libc",
|
||||
@@ -1056,3 +1185,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.12.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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -347,7 +346,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 +406,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,12 +417,16 @@ 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
@@ -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()))?;
|
||||
}
|
||||
|
||||
|
||||
@@ -703,7 +703,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 +1092,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"
|
||||
|
||||
@@ -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"
|
||||
once_cell = "1.16.0"
|
||||
pnet = "0.31.0"
|
||||
pnet_datalink = "0.31.0"
|
||||
serde_json = "1.0.83"
|
||||
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 {
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
@@ -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.
|
||||
|
||||
@@ -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" }
|
||||
@@ -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>;
|
||||
|
||||
@@ -37,6 +39,7 @@ impl Display for Error {
|
||||
write!(f, "failed to register an IO BAR, addr={} err={}", addr, 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -413,7 +413,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 +526,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;
|
||||
|
||||
@@ -599,7 +599,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(),
|
||||
});
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
@@ -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(),
|
||||
@@ -579,7 +579,7 @@ 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| {
|
||||
|
||||
@@ -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 {
|
||||
@@ -94,128 +77,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 +92,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 +121,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 +133,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)
|
||||
@@ -506,14 +278,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 +305,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 +333,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 +349,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={}",
|
||||
@@ -668,7 +368,7 @@ pub fn performance_block_io(control: &PerformanceTestControl) -> f64 {
|
||||
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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -255,10 +255,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 +473,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));
|
||||
@@ -566,7 +566,7 @@ impl QcowFile {
|
||||
|
||||
// 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 +705,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)?;
|
||||
@@ -872,7 +873,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;
|
||||
@@ -962,7 +963,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 +1006,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 +1123,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 +1195,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));
|
||||
}
|
||||
|
||||
@@ -1416,7 +1417,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 +1479,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 +1583,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<()>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -4,6 +4,6 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.131"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
vmm-sys-util = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
288
release-notes.md
288
release-notes.md
@@ -1,35 +1,56 @@
|
||||
- [v28.1](#v281)
|
||||
- [v28.0](#v280)
|
||||
- [Community Engagement (Reminder)](#community-engagement-reminder)
|
||||
- [Long Term Support (LTS) Release](#long-term-support-lts-release)
|
||||
- [Virtualised TPM Support](#virtualised-tpm-support)
|
||||
- [Transparent Huge Page Support](#transparent-huge-page-support)
|
||||
- [README Quick Start Improved](#readme-quick-start-improved)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||
- [Removals](#removals)
|
||||
- [Contributors](#contributors)
|
||||
- [v27.0](#v270)
|
||||
- [Community Engagement](#community-engagement)
|
||||
- [Prebuilt Packages](#prebuilt-packages)
|
||||
- [Network Device MTU Exposed to Guest](#network-device-mtu-exposed-to-guest)
|
||||
- [Boot Tracing](#boot-tracing)
|
||||
- [Simplified Build Feature Flags](#simplified-build-feature-flags)
|
||||
- [Asynchronous Kernel Loading](#asynchronous-kernel-loading)
|
||||
- [GDB Support for AArch64](#gdb-support-for-aarch64)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
||||
- [Deprecations](#deprecations)
|
||||
- [Contributors](#contributors-1)
|
||||
- [v26.0](#v260)
|
||||
- [SMBIOS Improvements via `--platform`](#smbios-improvements-via---platform)
|
||||
- [Unified Binary MSHV and KVM Support](#unified-binary-mshv-and-kvm-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||
- [Deprecations](#deprecations)
|
||||
- [Removals](#removals)
|
||||
- [Contributors](#contributors)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
||||
- [Deprecations](#deprecations-1)
|
||||
- [Removals](#removals-1)
|
||||
- [Contributors](#contributors-2)
|
||||
- [v25.0](#v250)
|
||||
- [`ch-remote` Improvements](#ch-remote-improvements)
|
||||
- [VM "Coredump" Support](#vm-coredump-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
||||
- [Removals](#removals-1)
|
||||
- [Contributors](#contributors-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-3)
|
||||
- [Removals](#removals-2)
|
||||
- [Contributors](#contributors-3)
|
||||
- [v24.0](#v240)
|
||||
- [Bypass Mode for `virtio-iommu`](#bypass-mode-for-virtio-iommu)
|
||||
- [Ensure Identifiers Uniqueness](#ensure-identifiers-uniqueness)
|
||||
- [Sparse Mmap support](#sparse-mmap-support)
|
||||
- [Expose Platform Serial Number](#expose-platform-serial-number)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-4)
|
||||
- [Notable Improvements](#notable-improvements)
|
||||
- [Deprecations](#deprecations-1)
|
||||
- [Deprecations](#deprecations-2)
|
||||
- [New on the Website](#new-on-the-website)
|
||||
- [Contributors](#contributors-2)
|
||||
- [Contributors](#contributors-4)
|
||||
- [v23.1](#v231)
|
||||
- [v23.0](#v230)
|
||||
- [vDPA Support](#vdpa-support)
|
||||
- [Updated OS Support list](#updated-os-support-list)
|
||||
- [`AArch64` Memory Map Improvements](#aarch64-memory-map-improvements)
|
||||
- [`AMX` Support](#amx-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-3)
|
||||
- [Deprecations](#deprecations-2)
|
||||
- [Contributors](#contributors-3)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-5)
|
||||
- [Deprecations](#deprecations-3)
|
||||
- [Contributors](#contributors-5)
|
||||
- [v22.1](#v221)
|
||||
- [v22.0](#v220)
|
||||
- [GDB Debug Stub Support](#gdb-debug-stub-support)
|
||||
@@ -40,13 +61,13 @@
|
||||
- [PMU Support for AArch64](#pmu-support-for-aarch64)
|
||||
- [Documentation Under CC-BY-4.0 License](#documentation-under-cc-by-40-license)
|
||||
- [Deprecation of "Classic" `virtiofsd`](#deprecation-of-classic-virtiofsd)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-4)
|
||||
- [Contributors](#contributors-4)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-6)
|
||||
- [Contributors](#contributors-6)
|
||||
- [v21.0](#v210)
|
||||
- [Efficient Local Live Migration (for Live Upgrade)](#efficient-local-live-migration-for-live-upgrade)
|
||||
- [Recommended Kernel is Now 5.15](#recommended-kernel-is-now-515)
|
||||
- [Notable Bug fixes](#notable-bug-fixes-5)
|
||||
- [Contributors](#contributors-5)
|
||||
- [Notable Bug fixes](#notable-bug-fixes-7)
|
||||
- [Contributors](#contributors-7)
|
||||
- [v20.2](#v202)
|
||||
- [v20.1](#v201)
|
||||
- [v20.0](#v200)
|
||||
@@ -55,8 +76,8 @@
|
||||
- [Improved VFIO support](#improved-vfio-support)
|
||||
- [Safer code](#safer-code)
|
||||
- [Extended documentation](#extended-documentation)
|
||||
- [Notable bug fixes](#notable-bug-fixes-6)
|
||||
- [Contributors](#contributors-6)
|
||||
- [Notable bug fixes](#notable-bug-fixes-8)
|
||||
- [Contributors](#contributors-8)
|
||||
- [v19.0](#v190)
|
||||
- [Improved PTY handling for serial and `virtio-console`](#improved-pty-handling-for-serial-and-virtio-console)
|
||||
- [PCI boot time optimisations](#pci-boot-time-optimisations)
|
||||
@@ -64,8 +85,8 @@
|
||||
- [Live migration enhancements](#live-migration-enhancements)
|
||||
- [`virtio-mem` support with `vfio-user`](#virtio-mem-support-with-vfio-user)
|
||||
- [AArch64 for `virtio-iommu`](#aarch64-for-virtio-iommu)
|
||||
- [Notable bug fixes](#notable-bug-fixes-7)
|
||||
- [Contributors](#contributors-7)
|
||||
- [Notable bug fixes](#notable-bug-fixes-9)
|
||||
- [Contributors](#contributors-9)
|
||||
- [v18.0](#v180)
|
||||
- [Experimental User Device (`vfio-user`) support](#experimental-user-device-vfio-user-support)
|
||||
- [Migration support for `vhost-user` devices](#migration-support-for-vhost-user-devices)
|
||||
@@ -75,31 +96,31 @@
|
||||
- [Live migration on MSHV hypervisor](#live-migration-on-mshv-hypervisor)
|
||||
- [AArch64 CPU topology support](#aarch64-cpu-topology-support)
|
||||
- [Power button support on AArch64](#power-button-support-on-aarch64)
|
||||
- [Notable bug fixes](#notable-bug-fixes-8)
|
||||
- [Contributors](#contributors-8)
|
||||
- [Notable bug fixes](#notable-bug-fixes-10)
|
||||
- [Contributors](#contributors-10)
|
||||
- [v17.0](#v170)
|
||||
- [ARM64 NUMA support using ACPI](#arm64-numa-support-using-acpi)
|
||||
- [`Seccomp` support for MSHV backend](#seccomp-support-for-mshv-backend)
|
||||
- [Hotplug of `macvtap` devices](#hotplug-of-macvtap-devices)
|
||||
- [Improved SGX support](#improved-sgx-support)
|
||||
- [Inflight tracking for `vhost-user` devices](#inflight-tracking-for-vhost-user-devices)
|
||||
- [Notable bug fixes](#notable-bug-fixes-9)
|
||||
- [Contributors](#contributors-9)
|
||||
- [Notable bug fixes](#notable-bug-fixes-11)
|
||||
- [Contributors](#contributors-11)
|
||||
- [v16.0](#v160)
|
||||
- [Improved live migration support](#improved-live-migration-support)
|
||||
- [Improved `vhost-user` support](#improved-vhost-user-support)
|
||||
- [ARM64 ACPI and UEFI support](#arm64-acpi-and-uefi-support)
|
||||
- [Notable bug fixes](#notable-bug-fixes-10)
|
||||
- [Notable bug fixes](#notable-bug-fixes-12)
|
||||
- [Removed functionality](#removed-functionality)
|
||||
- [Contributors](#contributors-10)
|
||||
- [Contributors](#contributors-12)
|
||||
- [v15.0](#v150)
|
||||
- [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees)
|
||||
- [Network device rate limiting](#network-device-rate-limiting)
|
||||
- [Support for runtime control of `virtio-net` guest offload](#support-for-runtime-control-of-virtio-net-guest-offload)
|
||||
- [`--api-socket` supports file descriptor parameter](#--api-socket-supports-file-descriptor-parameter)
|
||||
- [Bug fixes](#bug-fixes)
|
||||
- [Deprecations](#deprecations-3)
|
||||
- [Contributors](#contributors-11)
|
||||
- [Deprecations](#deprecations-4)
|
||||
- [Contributors](#contributors-13)
|
||||
- [v0.14.1](#v0141)
|
||||
- [v0.14.0](#v0140)
|
||||
- [Structured event monitoring](#structured-event-monitoring)
|
||||
@@ -108,8 +129,8 @@
|
||||
- [Updated hotplug documentation](#updated-hotplug-documentation)
|
||||
- [PTY control for serial and `virtio-console`](#pty-control-for-serial-and-virtio-console)
|
||||
- [Block device rate limiting](#block-device-rate-limiting)
|
||||
- [Deprecations](#deprecations-4)
|
||||
- [Contributors](#contributors-12)
|
||||
- [Deprecations](#deprecations-5)
|
||||
- [Contributors](#contributors-14)
|
||||
- [v0.13.0](#v0130)
|
||||
- [Wider VFIO device support](#wider-vfio-device-support)
|
||||
- [Improved huge page support](#improved-huge-page-support)
|
||||
@@ -117,13 +138,13 @@
|
||||
- [VHD disk image support](#vhd-disk-image-support)
|
||||
- [Improved Virtio device threading](#improved-virtio-device-threading)
|
||||
- [Clean shutdown support via synthetic power button](#clean-shutdown-support-via-synthetic-power-button)
|
||||
- [Contributors](#contributors-13)
|
||||
- [Contributors](#contributors-15)
|
||||
- [v0.12.0](#v0120)
|
||||
- [ARM64 enhancements](#arm64-enhancements)
|
||||
- [Removal of `vhost-user-net` and `vhost-user-block` self spawning](#removal-of-vhost-user-net-and-vhost-user-block-self-spawning)
|
||||
- [Migration of `vhost-user-fs` backend](#migration-of-vhost-user-fs-backend)
|
||||
- [Enhanced "info" API](#enhanced-info-api)
|
||||
- [Contributors](#contributors-14)
|
||||
- [Contributors](#contributors-16)
|
||||
- [v0.11.0](#v0110)
|
||||
- [`io_uring` support by default for `virtio-block`](#io_uring-support-by-default-for-virtio-block)
|
||||
- [Windows Guest Support](#windows-guest-support)
|
||||
@@ -135,15 +156,15 @@
|
||||
- [Default Log Level Changed](#default-log-level-changed)
|
||||
- [New `--balloon` Parameter Added](#new---balloon-parameter-added)
|
||||
- [Experimental `virtio-watchdog` Support](#experimental-virtio-watchdog-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-11)
|
||||
- [Contributors](#contributors-15)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-13)
|
||||
- [Contributors](#contributors-17)
|
||||
- [v0.10.0](#v0100)
|
||||
- [`virtio-block` Support for Multiple Descriptors](#virtio-block-support-for-multiple-descriptors)
|
||||
- [Memory Zones](#memory-zones)
|
||||
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements)
|
||||
- [Preliminary KVM HyperV Emulation Control](#preliminary-kvm-hyperv-emulation-control)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-12)
|
||||
- [Contributors](#contributors-16)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-14)
|
||||
- [Contributors](#contributors-18)
|
||||
- [v0.9.0](#v090)
|
||||
- [`io_uring` Based Block Device Support](#io_uring-based-block-device-support)
|
||||
- [Block and Network Device Statistics](#block-and-network-device-statistics)
|
||||
@@ -156,17 +177,17 @@
|
||||
- [Enhancements to ARM64 Support](#enhancements-to-arm64-support)
|
||||
- [Intel SGX Support](#intel-sgx-support)
|
||||
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-13)
|
||||
- [Contributors](#contributors-17)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-15)
|
||||
- [Contributors](#contributors-19)
|
||||
- [v0.8.0](#v080)
|
||||
- [Experimental Snapshot and Restore Support](#experimental-snapshot-and-restore-support)
|
||||
- [Experimental ARM64 Support](#experimental-arm64-support)
|
||||
- [Support for Using 5-level Paging in Guests](#support-for-using-5-level-paging-in-guests)
|
||||
- [Virtio Device Interrupt Suppression for Network Devices](#virtio-device-interrupt-suppression-for-network-devices)
|
||||
- [`vhost_user_fs` Improvements](#vhost_user_fs-improvements)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-14)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-16)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes)
|
||||
- [Contributors](#contributors-18)
|
||||
- [Contributors](#contributors-20)
|
||||
- [v0.7.0](#v070)
|
||||
- [Block, Network, Persistent Memory (PMEM), VirtioFS and Vsock hotplug](#block-network-persistent-memory-pmem-virtiofs-and-vsock-hotplug)
|
||||
- [Alternative `libc` Support](#alternative-libc-support)
|
||||
@@ -176,14 +197,14 @@
|
||||
- [`Seccomp` Sandboxing](#seccomp-sandboxing)
|
||||
- [Updated Distribution Support](#updated-distribution-support)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes-1)
|
||||
- [Contributors](#contributors-19)
|
||||
- [Contributors](#contributors-21)
|
||||
- [v0.6.0](#v060)
|
||||
- [Directly Assigned Devices Hotplug](#directly-assigned-devices-hotplug)
|
||||
- [Shared Filesystem Improvements](#shared-filesystem-improvements)
|
||||
- [Block and Networking IO Self Offloading](#block-and-networking-io-self-offloading)
|
||||
- [Command Line Interface](#command-line-interface)
|
||||
- [PVH Boot](#pvh-boot)
|
||||
- [Contributors](#contributors-20)
|
||||
- [Contributors](#contributors-22)
|
||||
- [v0.5.1](#v051)
|
||||
- [v0.5.0](#v050)
|
||||
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
|
||||
@@ -191,7 +212,7 @@
|
||||
- [New Interrupt Management Framework](#new-interrupt-management-framework)
|
||||
- [Development Tools](#development-tools)
|
||||
- [Kata Containers Integration](#kata-containers-integration)
|
||||
- [Contributors](#contributors-21)
|
||||
- [Contributors](#contributors-23)
|
||||
- [v0.4.0](#v040)
|
||||
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
|
||||
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
|
||||
@@ -200,7 +221,7 @@
|
||||
- [Userspace IOAPIC by default](#userspace-ioapic-by-default)
|
||||
- [PCI BAR reprogramming](#pci-bar-reprogramming)
|
||||
- [New `cloud-hypervisor` organization](#new-cloud-hypervisor-organization)
|
||||
- [Contributors](#contributors-22)
|
||||
- [Contributors](#contributors-24)
|
||||
- [v0.3.0](#v030)
|
||||
- [Block device offloading](#block-device-offloading)
|
||||
- [Network device backend](#network-device-backend)
|
||||
@@ -227,6 +248,183 @@
|
||||
- [Unit testing](#unit-testing)
|
||||
- [Integration tests parallelization](#integration-tests-parallelization)
|
||||
|
||||
# v28.1
|
||||
This is a bug fix release. The following issues have been addressed:
|
||||
|
||||
* Update dependencies including a version of `linux-loader` that
|
||||
addresses an infinite loop issue ([details](https://github.com/rust-vmm/linux-loader/security/advisories/GHSA-52h2-m2cf-9jh6))
|
||||
* Fix bugs related to `virtio-net` including an integer overflow issue
|
||||
(#4924, #4949)
|
||||
* Use host `cpuid` information for L2 cache for older KVM on x86 (#4920)
|
||||
* Improve README and documentation
|
||||
|
||||
# v28.0
|
||||
|
||||
This release has been tracked in our new [roadmap
|
||||
project](https://github.com/orgs/cloud-hypervisor/projects/6) as iteration
|
||||
v28.0.
|
||||
|
||||
### Community Engagement (Reminder)
|
||||
|
||||
Just a reminder that we have a new mailing list to support broader community
|
||||
discussions. Please consider
|
||||
[subscribing](https://lists.cloudhypervisor.org/g/dev/). We plan to use
|
||||
this to announce a regular meeting for those interested in talking about Cloud
|
||||
Hypervisor development.
|
||||
|
||||
### Long Term Support (LTS) Release
|
||||
|
||||
This is the first version of Cloud Hypervisor to be released under the LTS
|
||||
release process. Point releases for bug fixes will be made for the next 18
|
||||
months; live migration and live upgrade will be supported between the point
|
||||
releases of the LTS.
|
||||
|
||||
### Virtualised TPM Support
|
||||
|
||||
Support for adding an emulated CRB TPM has been added. This has it's own [TPM
|
||||
documentation](docs/tpm.md).
|
||||
|
||||
### Transparent Huge Page Support
|
||||
|
||||
By default, but controllable through `--memory thp=off` if it possible to back
|
||||
the guest memory with Transparent Huge Pages (no file backing/`shared=off`)
|
||||
then this will be used resulting in improved boot performance.
|
||||
|
||||
### README Quick Start Improved
|
||||
|
||||
The README has been refreshed with the quick start guide updated to reflect the
|
||||
different firmware options and to recommend the use of pre-built binaries.
|
||||
|
||||
### Notable Bug Fixes
|
||||
|
||||
* Inappropriate Copy-on-Write of pinned pages (e.g. VFIO) leading to higher
|
||||
memory consumption (#4835)
|
||||
* Multiple `virtio` device bug fixes found through fuzzing (#4859, #4799)
|
||||
* Large QCOW files (> 4TiB) are now supported (#4767)
|
||||
* Support for > 31 vCPUS on aarch64 (#4863)
|
||||
* Multiple fixes to OpenAPI specification file (#4720, #4811)
|
||||
* Programming of the MSI-X table has been optimised leading to faster boot on
|
||||
newer Linux kernels (#4744)
|
||||
* Error on reboot from race to close TAP devices (#4871)
|
||||
* Non-spec compliant virtio-block read-only support (#4888)
|
||||
|
||||
### Removals
|
||||
|
||||
The following functionality has been removed:
|
||||
|
||||
* Support for non-PVH firmware booting has been removed (#4511)
|
||||
* I/O ports used for older versions of firmware have been removed (#3926)
|
||||
* Deprecated API options for kernel/cmdline/initramfs have been removed (#4737)
|
||||
|
||||
### Contributors
|
||||
|
||||
Many thanks to everyone who has contributed to our release:
|
||||
|
||||
* Anatol Belski <anbelski@linux.microsoft.com>
|
||||
* Bo Chen <chen.bo@intel.com>
|
||||
* Fabiano Fidêncio <fabiano.fidencio@intel.com>
|
||||
* Jianyong Wu <jianyong.wu@arm.com>
|
||||
* Jinank Jain <jinankjain@microsoft.com>
|
||||
* Jinrong Liang <cloudliang@tencent.com>
|
||||
* lv.mengzhao <lv.mengzhao@zte.com.cn>
|
||||
* Michael Zhao <michael.zhao@arm.com>
|
||||
* Muminul Islam <muislam@microsoft.com>
|
||||
* Praveen K Paladugu <prapal@linux.microsoft.com>
|
||||
* Rob Bradford <robert.bradford@intel.com>
|
||||
* Sebastien Boeuf <sebastien.boeuf@intel.com>
|
||||
* Wei Liu <liuwe@microsoft.com>
|
||||
|
||||
# v27.0
|
||||
|
||||
This release has been tracked in our new [roadmap
|
||||
project](https://github.com/orgs/cloud-hypervisor/projects/6) as iteration
|
||||
v27.0.
|
||||
|
||||
### Community Engagement
|
||||
|
||||
A new mailing list has been created to support broader community discussions.
|
||||
Please consider [subscribing](https://lists.cloudhypervisor.org/g/dev/); an
|
||||
announcement of a regular meeting will be announced via this list shortly.
|
||||
|
||||
### Prebuilt Packages
|
||||
|
||||
Prebuilt packages are now available. Please see [this
|
||||
document](https://github.com/cloud-hypervisor/obs-packaging/blob/main/README.md)
|
||||
on how to install. These packages also include packages for the different
|
||||
firmware options available.
|
||||
|
||||
### Network Device MTU Exposed to Guest
|
||||
|
||||
The MTU for the TAP device associated with a `virtio-net` device is now exposed
|
||||
to the guest. If the user provides a MTU with `--net mtu=..` then that MTU is
|
||||
applied to created TAP interfaces. This functionality is also exposed for
|
||||
`vhost-user-net` devices including those created with the reference backend
|
||||
(#4658, #4676.)
|
||||
|
||||
### Boot Tracing
|
||||
|
||||
Support for generating a trace report for the boot time has been added
|
||||
including a script for generating an SVG from that trace (#4659.)
|
||||
|
||||
### Simplified Build Feature Flags
|
||||
|
||||
The set of feature flags, for e.g. experimental features, have been simplified:
|
||||
|
||||
* `msvh` and `kvm` features provide support for those specific hypervisors
|
||||
(with `kvm` enabled by default),
|
||||
* `tdx` provides support for Intel TDX; and although there is no MSHV support
|
||||
now it is now possible to compile with the `mshv` feature (#4696,)
|
||||
* `tracing` adds support for boot tracing,
|
||||
* `guest_debug` now covers both support for gdbing a guest (formerly `gdb`
|
||||
feature) and dumping guest memory.
|
||||
|
||||
The following feature flags were removed as the functionality was enabled by
|
||||
default: `amx`, `fwdebug`, `cmos` and `common` (#4679, #4632.)
|
||||
|
||||
### Asynchronous Kernel Loading
|
||||
|
||||
AArch64 has gained support for loading the guest kernel asynchronously like
|
||||
x86-64. (#4538)
|
||||
|
||||
### GDB Support for AArch64
|
||||
|
||||
GDB stub support (accessed through `--gdb` under `guest_debug` feature) is now
|
||||
available on AArch64 as well as as x86-64.
|
||||
|
||||
### Notable Bug Fixes
|
||||
|
||||
* This version incorporates a version of `virtio-queue` that addresses an issue
|
||||
where a rogue guest can potentially DoS the VMM (rust-vmm/vm-virtio#196.)
|
||||
* Improvements around PTY handling for `virtio-console` and serial devices
|
||||
(#4520, #4533, #4535.)
|
||||
* Improved error handling in virtio devices (#4626, #4605, #4509, #4631, #4697)
|
||||
|
||||
### Deprecations
|
||||
|
||||
Deprecated features will be removed in a subsequent release and users should
|
||||
plan to use alternatives.
|
||||
|
||||
* Booting legacy firmware (compiled without a PVH header) has been deprecated.
|
||||
All the firmware options (Cloud Hypervisor OVMF and Rust Hypervisor Firmware)
|
||||
support booting with PVH so support for loading firmware in a legacy mode is no
|
||||
longer needed. This functionality will be removed in the next release.
|
||||
|
||||
### Contributors
|
||||
|
||||
Many thanks to everyone who has contributed to our release:
|
||||
|
||||
* Anatol Belski <anbelski@linux.microsoft.com>
|
||||
* Bo Chen <chen.bo@intel.com>
|
||||
* James O. D. Hunt <james.o.hunt@intel.com>
|
||||
* Jianyong Wu <jianyong.wu@arm.com>
|
||||
* Markus Napierkowski <markus.napierkowski@cyberus-technology.de>
|
||||
* Michael Zhao <michael.zhao@arm.com>
|
||||
* Nuno Das Neves <nudasnev@microsoft.com>
|
||||
* Rob Bradford <robert.bradford@intel.com>
|
||||
* Sebastien Boeuf <sebastien.boeuf@intel.com>
|
||||
* Smit Gardhariya <sgardhariya@microsoft.com>
|
||||
* Wei Liu <liuwe@microsoft.com>
|
||||
|
||||
# v26.0
|
||||
|
||||
This release has been tracked through the [v26.0
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
# If this flag is set to 1, rustup installation needs to exist on the system and
|
||||
# <arch>-unknown-linux-gnu target is required.
|
||||
# If this flag is set to 0, distro specific Rust packages will be pulled into the build environment.
|
||||
%define using_rustup 1
|
||||
# If this flag is set to 1, <arch>-unknown-linux-musl target is required.
|
||||
%define using_musl_libc 1
|
||||
# If this flag is set to 1, the vendored crates archive and cargo.toml need to be prepared and
|
||||
# offline build is implied. Attached script update_src can be used for the vendorization.
|
||||
# If this flag is set to 0, access to the internet is required during the build.
|
||||
%define using_vendored_crates 0
|
||||
|
||||
Name: cloud-hypervisor
|
||||
Summary: Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on top of KVM.
|
||||
Version: 26.0
|
||||
Release: 0%{?dist}
|
||||
License: ASL 2.0 or BSD-3-clause
|
||||
Group: Applications/System
|
||||
Source0: https://github.com/cloud-hypervisor/cloud-hypervisor/archive/v%{version}.tar.gz
|
||||
%if 0%{?using_vendored_crates}
|
||||
Source1: vendor.tar.gz
|
||||
Source2: config.toml
|
||||
%endif
|
||||
ExclusiveArch: x86_64 aarch64
|
||||
|
||||
BuildRequires: gcc
|
||||
BuildRequires: glibc-devel
|
||||
BuildRequires: binutils
|
||||
BuildRequires: git
|
||||
BuildRequires: openssl-devel
|
||||
|
||||
%if ! 0%{?using_rustup}
|
||||
BuildRequires: rust
|
||||
BuildRequires: cargo
|
||||
%endif
|
||||
|
||||
Requires: bash
|
||||
Requires: glibc
|
||||
Requires: libgcc
|
||||
Requires: libcap
|
||||
|
||||
%ifarch x86_64
|
||||
%define rust_def_target x86_64-unknown-linux-gnu
|
||||
%if 0%{?using_musl_libc}
|
||||
%define rust_musl_target x86_64-unknown-linux-musl
|
||||
%endif
|
||||
%endif
|
||||
%ifarch aarch64
|
||||
%define rust_def_target aarch64-unknown-linux-gnu
|
||||
%if 0%{?using_musl_libc}
|
||||
%define rust_musl_target aarch64-unknown-linux-musl
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?using_vendored_crates}
|
||||
%define cargo_offline --offline
|
||||
%endif
|
||||
|
||||
%description
|
||||
Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on top of KVM. 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 Linux* distributions with most I/O handled by paravirtualised devices (i.e. virtio), no requirement for legacy devices and recent CPUs and KVM.
|
||||
|
||||
%prep
|
||||
|
||||
%setup -q
|
||||
%if 0%{?using_vendored_crates}
|
||||
tar xf %{SOURCE1}
|
||||
mkdir -p .cargo
|
||||
cp %{SOURCE2} .cargo/
|
||||
%endif
|
||||
|
||||
%install
|
||||
rm -rf %{buildroot}
|
||||
install -d %{buildroot}%{_bindir}
|
||||
install -D -m755 ./target/%{rust_def_target}/release/cloud-hypervisor %{buildroot}%{_bindir}
|
||||
install -D -m755 ./target/%{rust_def_target}/release/ch-remote %{buildroot}%{_bindir}
|
||||
install -d %{buildroot}%{_libdir}
|
||||
install -d %{buildroot}%{_libdir}/cloud-hypervisor
|
||||
install -D -m755 target/%{rust_def_target}/release/vhost_user_block %{buildroot}%{_libdir}/cloud-hypervisor
|
||||
install -D -m755 target/%{rust_def_target}/release/vhost_user_net %{buildroot}%{_libdir}/cloud-hypervisor
|
||||
|
||||
%if 0%{?using_musl_libc}
|
||||
install -d %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
install -D -m755 target/%{rust_musl_target}/release/cloud-hypervisor %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
install -D -m755 target/%{rust_musl_target}/release/vhost_user_block %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
install -D -m755 target/%{rust_musl_target}/release/vhost_user_net %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
install -D -m755 target/%{rust_musl_target}/release/ch-remote %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
%endif
|
||||
|
||||
|
||||
%build
|
||||
cargo_version=$(cargo --version)
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Cargo not found, please install cargo. exiting"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
%if 0%{?using_rustup}
|
||||
which rustup
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Rustup not found please install rustup #curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
|
||||
fi
|
||||
%endif
|
||||
|
||||
echo ${cargo_version}
|
||||
|
||||
%if 0%{?using_rustup}
|
||||
rustup target list --installed | grep -e "%{rust_def_target}"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Target %{rust_def_target} not found, please install(#rustup target add %{rust_def_target}). exiting"
|
||||
fi
|
||||
%if 0%{?using_musl_libc}
|
||||
rustup target list --installed | grep -e "%{rust_musl_target}"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Target %{rust_musl_target} not found, please install(#rustup target add %{rust_musl_target}). exiting"
|
||||
fi
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?using_vendored_crates}
|
||||
# For vendored build, prepend this so openssl-sys doesn't trigger full OpenSSL build
|
||||
export OPENSSL_NO_VENDOR=1
|
||||
%endif
|
||||
cargo build --release --target=%{rust_def_target} --all %{cargo_offline}
|
||||
%if 0%{?using_musl_libc}
|
||||
cargo build --release --target=%{rust_musl_target} --all %{cargo_offline}
|
||||
%endif
|
||||
|
||||
|
||||
%clean
|
||||
rm -rf %{buildroot}
|
||||
|
||||
%files
|
||||
%defattr(-,root,root,-)
|
||||
%{_bindir}/ch-remote
|
||||
%caps(cap_net_admin=ep) %{_bindir}/cloud-hypervisor
|
||||
%{_libdir}/cloud-hypervisor/vhost_user_block
|
||||
%caps(cap_net_admin=ep) %{_libdir}/cloud-hypervisor/vhost_user_net
|
||||
%if 0%{?using_musl_libc}
|
||||
%{_libdir}/cloud-hypervisor/static/ch-remote
|
||||
%caps(cap_net_admim=ep) %{_libdir}/cloud-hypervisor/static/cloud-hypervisor
|
||||
%{_libdir}/cloud-hypervisor/static/vhost_user_block
|
||||
%caps(cap_net_admin=ep) %{_libdir}/cloud-hypervisor/static/vhost_user_net
|
||||
%endif
|
||||
%license LICENSE-APACHE
|
||||
%license LICENSE-BSD-3-Clause
|
||||
|
||||
|
||||
%changelog
|
||||
* Wed Aug 17 2022 Rob Bradford <robert.bradford@intel.com> 26.0-0
|
||||
- Update to 26.0
|
||||
|
||||
* Thu Jul 07 2022 Rob Bradford <robert.bradford@intel.com> 25.0-0
|
||||
- Update to 25.0
|
||||
|
||||
* Wed May 25 2022 Sebastien Boeuf <sebastien.boeuf@intel.com> 24.0-0
|
||||
- Update to 24.0
|
||||
|
||||
* Tue May 18 2022 Anatol Belski <anbelski@linux.microsoft.com> - 23.1-0
|
||||
- Update to 23.1
|
||||
- Add support for aarch64 build
|
||||
- Add offline build configuration using vendored crates
|
||||
- Fix dependency for openssl-sys
|
||||
|
||||
* Thu Apr 13 2022 Rob Bradford <robert.bradford@intel.com> 23.0-0
|
||||
- Update to 23.0
|
||||
|
||||
* Thu Mar 03 2022 Rob Bradford <robert.bradford@intel.com> 22.0-0
|
||||
- Update to 22.0
|
||||
|
||||
* Thu Jan 20 2022 Rob Bradford <robert.bradford@intel.com> 21.0-0
|
||||
- Update to 21.0
|
||||
|
||||
* Thu Dec 02 2021 Sebastien Boeuf <sebastien.boeuf@intel.com> 20.0-0
|
||||
- Update to 20.0
|
||||
|
||||
* Mon Nov 08 2021 Fabiano Fidêncio <fabiano.fidencio@intel.com> 19.0-0
|
||||
- Update to 19.0
|
||||
|
||||
* Fri May 28 2021 Muminul Islam <muislam@microsoft.com> 15.0-0
|
||||
- Update version to 15.0
|
||||
|
||||
* Wed Jul 22 2020 Muminul Islam <muislam@microsoft.com> 0.8.0-0
|
||||
- Initial version
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
CH_VER=$1
|
||||
CH_SPEC_DIR=$(pwd)
|
||||
CH_TMP_DIR=$CH_SPEC_DIR/ch_tmp
|
||||
CH_SRC_DIR=$CH_SPEC_DIR/SOURCES
|
||||
|
||||
if [ -z $CH_VER ]; then
|
||||
echo "Replace sources with the given <version>"
|
||||
echo "Usage: $0 <version>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -rf $CH_TMP_DIR
|
||||
if [ ! -d $CH_SRC_DIR ]; then
|
||||
mkdir $CH_SRC_DIR
|
||||
fi
|
||||
rm -f $CH_SRC_DIR/v*.tar.gz $CH_SRC_DIR/config.toml $CH_SRC_DIR/vendor.tar.gz
|
||||
|
||||
|
||||
wget https://github.com/cloud-hypervisor/cloud-hypervisor/archive/v${CH_VER}.tar.gz -O $CH_SRC_DIR/v${CH_VER}.tar.gz
|
||||
mkdir $CH_TMP_DIR
|
||||
tar xf $CH_SRC_DIR/v${CH_VER}.tar.gz -C $CH_TMP_DIR --strip-components=1
|
||||
|
||||
|
||||
pushd $CH_TMP_DIR
|
||||
|
||||
cargo vendor > $CH_SRC_DIR/config.toml
|
||||
tar czvf $CH_SRC_DIR/vendor.tar.gz vendor
|
||||
|
||||
popd
|
||||
|
||||
rm -rf $CH_TMP_DIR
|
||||
|
||||
98
scripts/ch-trace-visualiser.py
Executable file
98
scripts/ch-trace-visualiser.py
Executable file
@@ -0,0 +1,98 @@
|
||||
#!/bin/env python3
|
||||
#
|
||||
# Copyright © 2022 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
from colorsys import hsv_to_rgb
|
||||
from random import random
|
||||
import xml
|
||||
import json
|
||||
from sys import argv, stderr
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
width = 1000
|
||||
height = 200
|
||||
padding = 10
|
||||
|
||||
if len(argv) < 3:
|
||||
stderr.write("./ch-trace-visualiser <trace file> <output file>\n")
|
||||
exit(1)
|
||||
|
||||
|
||||
def nano_time(duration):
|
||||
return (duration["secs"] * 10 ** 9) + duration["nanos"]
|
||||
|
||||
|
||||
def duration_to_px_x(start):
|
||||
return (nano_time(start) * (width - 2 * padding)) / total_time
|
||||
|
||||
|
||||
def duration_to_px_width(start, end):
|
||||
return ((nano_time(end) - nano_time(start)) * (width - 2 * padding)) / total_time
|
||||
|
||||
|
||||
def duration_ms(start, end):
|
||||
return (nano_time(end) - nano_time(start)) // 1000000
|
||||
|
||||
|
||||
f = open(argv[1])
|
||||
report = json.load(f)
|
||||
total_time = nano_time(report["duration"])
|
||||
|
||||
svg = ET.Element("svg", attrib={"width": str(width), "height": str(height),
|
||||
"xmlns": "http://www.w3.org/2000/svg",
|
||||
"xmlns:svg": "http://www.w3.org/2000/svg"
|
||||
})
|
||||
|
||||
|
||||
def add_traced_block(thread_group, depth, traced_block):
|
||||
g = ET.SubElement(thread_group, "g",
|
||||
attrib={"transform": "translate(%d,%d)" % (
|
||||
duration_to_px_x(traced_block["timestamp"]),
|
||||
(depth * 18))})
|
||||
width = str(duration_to_px_width(
|
||||
traced_block["timestamp"], traced_block["end_timestamp"]))
|
||||
|
||||
clip = ET.SubElement(g, "clipPath", attrib={
|
||||
"id": "clip_%s" % (traced_block["event"]),
|
||||
})
|
||||
ET.SubElement(clip, "rect", attrib={
|
||||
"width": width,
|
||||
"height": "1.5em",
|
||||
"x": "0",
|
||||
"y": "0"
|
||||
})
|
||||
|
||||
(red, green, blue) = hsv_to_rgb(random(), 0.3, 0.75)
|
||||
ET.SubElement(g, "rect", attrib={
|
||||
"width": width,
|
||||
"height": "1.5em",
|
||||
"fill": "#%x%x%x" % (int(red * 255), int(green * 255), int(blue * 255))
|
||||
})
|
||||
text = ET.SubElement(g, "text", attrib={
|
||||
"x": "0.2em", "y": "1em", "clip-path": "url(#clip_%s)" % (traced_block["event"])})
|
||||
text.text = "%s (%dms)" % (traced_block["event"], duration_ms(
|
||||
traced_block["timestamp"], traced_block["end_timestamp"]))
|
||||
|
||||
|
||||
thread_size = (height - (2 * padding)) / len(report["events"])
|
||||
thread_offset = padding
|
||||
|
||||
for thread in report["events"]:
|
||||
thread_events = report["events"][thread]
|
||||
thread_events = sorted(
|
||||
thread_events, key=lambda traced_block: nano_time(traced_block["timestamp"]))
|
||||
thread_group = ET.SubElement(
|
||||
svg, "g", attrib={"transform": "translate(%d,%d)" % (padding, thread_offset)})
|
||||
thread_text = ET.SubElement(thread_group, "text", attrib={
|
||||
"y": "1em"}).text = "Thread: %s" % (thread)
|
||||
thread_children = ET.SubElement(thread_group, "g", attrib={
|
||||
"transform": "translate(0, 18)"})
|
||||
for traced_block in thread_events:
|
||||
add_traced_block(thread_children, traced_block["depth"], traced_block)
|
||||
thread_offset += thread_size + padding
|
||||
|
||||
xml = ET.ElementTree(element=svg)
|
||||
xml.write(argv[2], xml_declaration=True)
|
||||
@@ -191,6 +191,7 @@ cmd_help() {
|
||||
echo " --integration-vfio Run the VFIO integration tests."
|
||||
echo " --integration-windows Run the Windows guest integration tests."
|
||||
echo " --integration-live-migration Run the live-migration integration tests."
|
||||
echo " --integration-rate-limiter Run the rate-limiter integration tests."
|
||||
echo " --libc Select the C library Cloud Hypervisor will be built against. Default is gnu"
|
||||
echo " --metrics Generate performance metrics"
|
||||
echo " --volumes Hash separated volumes to be exported. Example --volumes /mnt:/mnt#/myvol:/myvol"
|
||||
@@ -226,7 +227,7 @@ cmd_build() {
|
||||
} ;;
|
||||
"--debug") { build="debug"; } ;;
|
||||
"--release") { build="release"; } ;;
|
||||
"--runtime")
|
||||
"--runtime")
|
||||
shift
|
||||
DOCKER_RUNTIME="$1"
|
||||
export DOCKER_RUNTIME
|
||||
@@ -321,6 +322,7 @@ cmd_tests() {
|
||||
integration_vfio=false
|
||||
integration_windows=false
|
||||
integration_live_migration=false
|
||||
integration_rate_limiter=false
|
||||
metrics=false
|
||||
libc="gnu"
|
||||
arg_vols=""
|
||||
@@ -338,6 +340,7 @@ cmd_tests() {
|
||||
"--integration-vfio") { integration_vfio=true; } ;;
|
||||
"--integration-windows") { integration_windows=true; } ;;
|
||||
"--integration-live-migration") { integration_live_migration=true; } ;;
|
||||
"--integration-rate-limiter") { integration_rate_limiter=true; } ;;
|
||||
"--metrics") { metrics=true; } ;;
|
||||
"--libc")
|
||||
shift
|
||||
@@ -493,6 +496,25 @@ cmd_tests() {
|
||||
./scripts/run_integration_tests_live_migration.sh "$@" || fix_dir_perms $? || exit $?
|
||||
fi
|
||||
|
||||
if [ "$integration_rate_limiter" = true ]; then
|
||||
say "Running 'rate limiter' integration tests for $target..."
|
||||
$DOCKER_RUNTIME run \
|
||||
--workdir "$CTR_CLH_ROOT_DIR" \
|
||||
--rm \
|
||||
--privileged \
|
||||
--security-opt seccomp=unconfined \
|
||||
--ipc=host \
|
||||
--net="$CTR_CLH_NET" \
|
||||
--mount type=tmpfs,destination=/tmp \
|
||||
--volume /dev:/dev \
|
||||
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" $exported_volumes \
|
||||
--volume "$CLH_INTEGRATION_WORKLOADS:$CTR_CLH_INTEGRATION_WORKLOADS" \
|
||||
--env USER="root" \
|
||||
--env CH_LIBC="${libc}" \
|
||||
"$CTR_IMAGE" \
|
||||
./scripts/run_integration_tests_rate_limiter.sh "$@" || fix_dir_perms $? || exit $?
|
||||
fi
|
||||
|
||||
if [ "$metrics" = true ]; then
|
||||
say "Generating performance metrics for $target..."
|
||||
$DOCKER_RUNTIME run \
|
||||
|
||||
@@ -148,7 +148,7 @@ update_workloads() {
|
||||
popd
|
||||
|
||||
# Download Cloud Hypervisor binary from its last stable release
|
||||
LAST_RELEASE_VERSION="v23.0"
|
||||
LAST_RELEASE_VERSION="v26.0"
|
||||
CH_RELEASE_URL="https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/$LAST_RELEASE_VERSION/cloud-hypervisor-static-aarch64"
|
||||
CH_RELEASE_NAME="cloud-hypervisor-static-aarch64"
|
||||
pushd $WORKLOADS_DIR
|
||||
|
||||
@@ -15,7 +15,7 @@ process_common_args "$@"
|
||||
features=""
|
||||
|
||||
if [ "$hypervisor" = "mshv" ] ; then
|
||||
features="--no-default-features --features mshv,common"
|
||||
features="--no-default-features --features mshv"
|
||||
fi
|
||||
|
||||
cp scripts/sha1sums-x86_64 $WORKLOADS_DIR
|
||||
@@ -46,7 +46,7 @@ fi
|
||||
popd
|
||||
|
||||
# Download Cloud Hypervisor binary from its last stable release
|
||||
LAST_RELEASE_VERSION="v23.0"
|
||||
LAST_RELEASE_VERSION="v26.0"
|
||||
CH_RELEASE_URL="https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/$LAST_RELEASE_VERSION/cloud-hypervisor-static"
|
||||
CH_RELEASE_NAME="cloud-hypervisor-static"
|
||||
pushd $WORKLOADS_DIR
|
||||
|
||||
89
scripts/run_integration_tests_rate_limiter.sh
Executable file
89
scripts/run_integration_tests_rate_limiter.sh
Executable file
@@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
set -x
|
||||
|
||||
source $HOME/.cargo/env
|
||||
source $(dirname "$0")/test-util.sh
|
||||
|
||||
export BUILD_TARGET=${BUILD_TARGET-x86_64-unknown-linux-gnu}
|
||||
|
||||
WORKLOADS_DIR="$HOME/workloads"
|
||||
mkdir -p "$WORKLOADS_DIR"
|
||||
|
||||
process_common_args "$@"
|
||||
|
||||
# For now these values are default for kvm
|
||||
features=""
|
||||
|
||||
if [ "$hypervisor" = "mshv" ] ; then
|
||||
features="--no-default-features --features mshv"
|
||||
fi
|
||||
|
||||
cp scripts/sha1sums-x86_64 $WORKLOADS_DIR
|
||||
|
||||
FOCAL_OS_IMAGE_NAME="focal-server-cloudimg-amd64-custom-20210609-0.qcow2"
|
||||
FOCAL_OS_IMAGE_URL="https://cloud-hypervisor.azureedge.net/$FOCAL_OS_IMAGE_NAME"
|
||||
FOCAL_OS_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_IMAGE_NAME"
|
||||
if [ ! -f "$FOCAL_OS_IMAGE" ]; then
|
||||
pushd $WORKLOADS_DIR
|
||||
time wget --quiet $FOCAL_OS_IMAGE_URL || exit 1
|
||||
popd
|
||||
fi
|
||||
|
||||
FOCAL_OS_RAW_IMAGE_NAME="focal-server-cloudimg-amd64-custom-20210609-0.raw"
|
||||
FOCAL_OS_RAW_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_RAW_IMAGE_NAME"
|
||||
if [ ! -f "$FOCAL_OS_RAW_IMAGE" ]; then
|
||||
pushd $WORKLOADS_DIR
|
||||
time qemu-img convert -p -f qcow2 -O raw $FOCAL_OS_IMAGE_NAME $FOCAL_OS_RAW_IMAGE_NAME || exit 1
|
||||
popd
|
||||
fi
|
||||
|
||||
pushd $WORKLOADS_DIR
|
||||
grep focal sha1sums-x86_64 | sha1sum --check
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "sha1sum validation of images failed, remove invalid images to fix the issue."
|
||||
exit 1
|
||||
fi
|
||||
popd
|
||||
|
||||
# Build custom kernel based on virtio-pmem and virtio-fs upstream patches
|
||||
VMLINUX_IMAGE="$WORKLOADS_DIR/vmlinux"
|
||||
|
||||
LINUX_CUSTOM_DIR="$WORKLOADS_DIR/linux-custom"
|
||||
|
||||
if [ ! -f "$VMLINUX_IMAGE" ]; then
|
||||
SRCDIR=$PWD
|
||||
pushd $WORKLOADS_DIR
|
||||
time git clone --depth 1 "https://github.com/cloud-hypervisor/linux.git" -b "ch-5.15.12" $LINUX_CUSTOM_DIR
|
||||
cp $SRCDIR/resources/linux-config-x86_64 $LINUX_CUSTOM_DIR/.config
|
||||
popd
|
||||
fi
|
||||
|
||||
if [ ! -f "$VMLINUX_IMAGE" ]; then
|
||||
pushd $LINUX_CUSTOM_DIR
|
||||
time make bzImage -j `nproc`
|
||||
cp vmlinux $VMLINUX_IMAGE || exit 1
|
||||
popd
|
||||
fi
|
||||
|
||||
if [ -d "$LINUX_CUSTOM_DIR" ]; then
|
||||
rm -rf $LINUX_CUSTOM_DIR
|
||||
fi
|
||||
|
||||
BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
|
||||
CFLAGS=""
|
||||
TARGET_CC=""
|
||||
if [[ "${BUILD_TARGET}" == "x86_64-unknown-linux-musl" ]]; then
|
||||
TARGET_CC="musl-gcc"
|
||||
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
|
||||
fi
|
||||
|
||||
cargo build --all --release $features --target $BUILD_TARGET
|
||||
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||
strip target/$BUILD_TARGET/release/vhost_user_net
|
||||
strip target/$BUILD_TARGET/release/ch-remote
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
time cargo test $features "rate_limiter::$test_filter" -- --test-threads=1 ${test_binary_args[*]}
|
||||
RES=$?
|
||||
|
||||
exit $RES
|
||||
@@ -9,7 +9,7 @@ process_common_args "$@"
|
||||
features=""
|
||||
|
||||
if [ "$hypervisor" = "mshv" ] ; then
|
||||
features="--no-default-features --features mshv,common"
|
||||
features="--no-default-features --features mshv"
|
||||
fi
|
||||
WIN_IMAGE_FILE="/root/workloads/windows-server-2019.raw"
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ process_common_args "$@"
|
||||
features=""
|
||||
|
||||
if [ "$hypervisor" = "mshv" ] ; then
|
||||
features="--no-default-features --features mshv,common"
|
||||
features="--no-default-features --features mshv"
|
||||
fi
|
||||
|
||||
cp scripts/sha1sums-x86_64 $WORKLOADS_DIR
|
||||
@@ -124,7 +124,9 @@ popd
|
||||
|
||||
# Build custom kernel based on virtio-pmem and virtio-fs upstream patches
|
||||
VMLINUX_IMAGE="$WORKLOADS_DIR/vmlinux"
|
||||
build_custom_linux
|
||||
if [ ! -f "$VMLINUX_IMAGE" ]; then
|
||||
build_custom_linux
|
||||
fi
|
||||
|
||||
VIRTIOFSD="$WORKLOADS_DIR/virtiofsd"
|
||||
VIRTIOFSD_DIR="virtiofsd_build"
|
||||
|
||||
@@ -32,7 +32,7 @@ process_common_args "$@"
|
||||
features=""
|
||||
|
||||
if [ "$hypervisor" = "mshv" ]; then
|
||||
features="--no-default-features --features mshv,common"
|
||||
features="--no-default-features --features mshv"
|
||||
fi
|
||||
|
||||
cp scripts/sha1sums-${TEST_ARCH} $WORKLOADS_DIR
|
||||
|
||||
@@ -10,7 +10,7 @@ cargo_args=("")
|
||||
|
||||
if [[ $hypervisor = "mshv" ]]; then
|
||||
cargo_args+=("--no-default-features")
|
||||
cargo_args+=("--features common,$hypervisor")
|
||||
cargo_args+=("--features $hypervisor")
|
||||
elif [[ $(uname -m) = "x86_64" ]]; then
|
||||
cargo_args+=("--features tdx")
|
||||
fi
|
||||
|
||||
5
serial_buffer/Cargo.toml
Normal file
5
serial_buffer/Cargo.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
[package]
|
||||
name = "serial_buffer"
|
||||
version = "0.1.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
115
serial_buffer/src/lib.rs
Normal file
115
serial_buffer/src/lib.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright © 2021 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
io::Write,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
const MAX_BUFFER_SIZE: usize = 1 << 20;
|
||||
|
||||
// Circular buffer implementation for serial output.
|
||||
// Read from head; push to tail
|
||||
pub struct SerialBuffer {
|
||||
buffer: VecDeque<u8>,
|
||||
out: Box<dyn Write + Send>,
|
||||
write_out: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl SerialBuffer {
|
||||
pub fn new(out: Box<dyn Write + Send>, write_out: Arc<AtomicBool>) -> Self {
|
||||
Self {
|
||||
buffer: VecDeque::new(),
|
||||
out,
|
||||
write_out,
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_buffer(&mut self, buf: &[u8]) {
|
||||
if buf.len() >= MAX_BUFFER_SIZE {
|
||||
let offset = buf.len() - MAX_BUFFER_SIZE;
|
||||
self.buffer = VecDeque::from(buf[offset..].to_vec());
|
||||
return;
|
||||
}
|
||||
|
||||
let num_allowed_bytes = MAX_BUFFER_SIZE - buf.len();
|
||||
if self.buffer.len() > num_allowed_bytes {
|
||||
let num_bytes_to_remove = self.buffer.len() - num_allowed_bytes;
|
||||
self.buffer.drain(..num_bytes_to_remove);
|
||||
}
|
||||
|
||||
self.buffer.extend(buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for SerialBuffer {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
|
||||
// Simply fill the buffer if we're not allowed to write to the out
|
||||
// device.
|
||||
if !self.write_out.load(Ordering::Acquire) {
|
||||
self.fill_buffer(buf);
|
||||
return Ok(buf.len());
|
||||
}
|
||||
|
||||
// In case we're allowed to write to the out device, we flush the
|
||||
// content of the buffer.
|
||||
self.flush()?;
|
||||
|
||||
// If after flushing the buffer, it's still not empty, that means
|
||||
// only a subset of the bytes was written and we should fill the buffer
|
||||
// with what's coming from the serial.
|
||||
if !self.buffer.is_empty() {
|
||||
self.fill_buffer(buf);
|
||||
return Ok(buf.len());
|
||||
}
|
||||
|
||||
// We reach this point if we're allowed to write to the out device
|
||||
// and we know there's nothing left in the buffer.
|
||||
let mut offset = 0;
|
||||
loop {
|
||||
match self.out.write(&buf[offset..]) {
|
||||
Ok(written_bytes) => {
|
||||
if written_bytes < buf.len() - offset {
|
||||
offset += written_bytes;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if !matches!(e.kind(), std::io::ErrorKind::WouldBlock) {
|
||||
return Err(e);
|
||||
}
|
||||
self.fill_buffer(&buf[offset..]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Make sure we flush anything that might have been written to the
|
||||
// out device.
|
||||
self.out.flush()?;
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
// This function flushes the content of the buffer to the out device if
|
||||
// it is allowed to, otherwise this is a no-op.
|
||||
fn flush(&mut self) -> Result<(), std::io::Error> {
|
||||
if !self.write_out.load(Ordering::Acquire) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
while let Some(byte) = self.buffer.pop_front() {
|
||||
if self.out.write_all(&[byte]).is_err() {
|
||||
self.buffer.push_front(byte);
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.out.flush()
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ extern crate clap;
|
||||
use api_client::simple_api_command;
|
||||
use api_client::simple_api_command_with_fds;
|
||||
use api_client::Error as ApiClientError;
|
||||
use clap::{Arg, ArgMatches, Command};
|
||||
use clap::{Arg, ArgAction, ArgMatches, Command};
|
||||
use option_parser::{ByteSized, ByteSizedParseError};
|
||||
use std::fmt;
|
||||
use std::io::Read;
|
||||
@@ -328,8 +328,8 @@ fn create_api_command(socket: &mut UnixStream, path: &str) -> Result<(), Error>
|
||||
}
|
||||
|
||||
fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
let mut socket =
|
||||
UnixStream::connect(matches.value_of("api-socket").unwrap()).map_err(Error::Connect)?;
|
||||
let mut socket = UnixStream::connect(matches.get_one::<String>("api-socket").unwrap())
|
||||
.map_err(Error::Connect)?;
|
||||
|
||||
match matches.subcommand_name() {
|
||||
Some("info") => {
|
||||
@@ -343,27 +343,30 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("resize")
|
||||
.unwrap()
|
||||
.value_of("cpus"),
|
||||
.get_one::<String>("cpus")
|
||||
.map(|x| x as &str),
|
||||
matches
|
||||
.subcommand_matches("resize")
|
||||
.unwrap()
|
||||
.value_of("memory"),
|
||||
.get_one::<String>("memory")
|
||||
.map(|x| x as &str),
|
||||
matches
|
||||
.subcommand_matches("resize")
|
||||
.unwrap()
|
||||
.value_of("balloon"),
|
||||
.get_one::<String>("balloon")
|
||||
.map(|x| x as &str),
|
||||
),
|
||||
Some("resize-zone") => resize_zone_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("resize-zone")
|
||||
.unwrap()
|
||||
.value_of("id")
|
||||
.get_one::<String>("id")
|
||||
.unwrap(),
|
||||
matches
|
||||
.subcommand_matches("resize-zone")
|
||||
.unwrap()
|
||||
.value_of("size")
|
||||
.get_one::<String>("size")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-device") => add_device_api_command(
|
||||
@@ -371,7 +374,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("add-device")
|
||||
.unwrap()
|
||||
.value_of("device_config")
|
||||
.get_one::<String>("device_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("remove-device") => remove_device_api_command(
|
||||
@@ -379,7 +382,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("remove-device")
|
||||
.unwrap()
|
||||
.value_of("id")
|
||||
.get_one::<String>("id")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-disk") => add_disk_api_command(
|
||||
@@ -387,7 +390,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("add-disk")
|
||||
.unwrap()
|
||||
.value_of("disk_config")
|
||||
.get_one::<String>("disk_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-fs") => add_fs_api_command(
|
||||
@@ -395,7 +398,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("add-fs")
|
||||
.unwrap()
|
||||
.value_of("fs_config")
|
||||
.get_one::<String>("fs_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-pmem") => add_pmem_api_command(
|
||||
@@ -403,7 +406,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("add-pmem")
|
||||
.unwrap()
|
||||
.value_of("pmem_config")
|
||||
.get_one::<String>("pmem_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-net") => add_net_api_command(
|
||||
@@ -411,7 +414,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("add-net")
|
||||
.unwrap()
|
||||
.value_of("net_config")
|
||||
.get_one::<String>("net_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-user-device") => add_user_device_api_command(
|
||||
@@ -419,7 +422,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("add-user-device")
|
||||
.unwrap()
|
||||
.value_of("device_config")
|
||||
.get_one::<String>("device_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-vdpa") => add_vdpa_api_command(
|
||||
@@ -427,7 +430,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("add-vdpa")
|
||||
.unwrap()
|
||||
.value_of("vdpa_config")
|
||||
.get_one::<String>("vdpa_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-vsock") => add_vsock_api_command(
|
||||
@@ -435,7 +438,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("add-vsock")
|
||||
.unwrap()
|
||||
.value_of("vsock_config")
|
||||
.get_one::<String>("vsock_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("snapshot") => snapshot_api_command(
|
||||
@@ -443,7 +446,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("snapshot")
|
||||
.unwrap()
|
||||
.value_of("snapshot_config")
|
||||
.get_one::<String>("snapshot_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("restore") => restore_api_command(
|
||||
@@ -451,7 +454,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("restore")
|
||||
.unwrap()
|
||||
.value_of("restore_config")
|
||||
.get_one::<String>("restore_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("coredump") => coredump_api_command(
|
||||
@@ -459,7 +462,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("coredump")
|
||||
.unwrap()
|
||||
.value_of("coredump_config")
|
||||
.get_one::<String>("coredump_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("send-migration") => send_migration_api_command(
|
||||
@@ -467,19 +470,19 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("send-migration")
|
||||
.unwrap()
|
||||
.value_of("send_migration_config")
|
||||
.get_one::<String>("send_migration_config")
|
||||
.unwrap(),
|
||||
matches
|
||||
.subcommand_matches("send-migration")
|
||||
.unwrap()
|
||||
.is_present("send_migration_local"),
|
||||
.get_flag("send_migration_local"),
|
||||
),
|
||||
Some("receive-migration") => receive_migration_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("receive-migration")
|
||||
.unwrap()
|
||||
.value_of("receive_migration_config")
|
||||
.get_one::<String>("receive_migration_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("create") => create_api_command(
|
||||
@@ -487,7 +490,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
matches
|
||||
.subcommand_matches("create")
|
||||
.unwrap()
|
||||
.value_of("path")
|
||||
.get_one::<String>("path")
|
||||
.unwrap(),
|
||||
),
|
||||
Some(c) => simple_api_command(&mut socket, "PUT", c, None).map_err(Error::ApiClient),
|
||||
@@ -504,8 +507,7 @@ fn main() {
|
||||
Arg::new("api-socket")
|
||||
.long("api-socket")
|
||||
.help("HTTP API socket path (UNIX domain socket).")
|
||||
.takes_value(true)
|
||||
.number_of_values(1)
|
||||
.num_args(1)
|
||||
.required(true),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -587,22 +589,19 @@ fn main() {
|
||||
Arg::new("cpus")
|
||||
.long("cpus")
|
||||
.help("New vCPUs count")
|
||||
.takes_value(true)
|
||||
.number_of_values(1),
|
||||
.num_args(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("memory")
|
||||
.long("memory")
|
||||
.help("New memory size in bytes (supports K/M/G suffix)")
|
||||
.takes_value(true)
|
||||
.number_of_values(1),
|
||||
.num_args(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("balloon")
|
||||
.long("balloon")
|
||||
.help("New balloon size in bytes (supports K/M/G suffix)")
|
||||
.takes_value(true)
|
||||
.number_of_values(1),
|
||||
.num_args(1),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -612,15 +611,13 @@ fn main() {
|
||||
Arg::new("id")
|
||||
.long("id")
|
||||
.help("Memory zone identifier")
|
||||
.takes_value(true)
|
||||
.number_of_values(1),
|
||||
.num_args(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("size")
|
||||
.long("size")
|
||||
.help("New memory zone size in bytes (supports K/M/G suffix)")
|
||||
.takes_value(true)
|
||||
.number_of_values(1),
|
||||
.num_args(1),
|
||||
),
|
||||
)
|
||||
.subcommand(Command::new("resume").about("Resume the VM"))
|
||||
@@ -661,7 +658,8 @@ fn main() {
|
||||
.arg(
|
||||
Arg::new("send_migration_local")
|
||||
.long("local")
|
||||
.takes_value(false),
|
||||
.num_args(0)
|
||||
.action(ArgAction::SetTrue),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
|
||||
185
src/main.rs
185
src/main.rs
@@ -8,7 +8,7 @@ extern crate clap;
|
||||
#[macro_use]
|
||||
extern crate event_monitor;
|
||||
|
||||
use clap::{Arg, ArgGroup, ArgMatches, Command};
|
||||
use clap::{Arg, ArgAction, ArgGroup, ArgMatches, Command};
|
||||
use libc::EFD_NONBLOCK;
|
||||
use log::LevelFilter;
|
||||
use option_parser::OptionParser;
|
||||
@@ -29,7 +29,7 @@ use vmm_sys_util::terminal::Terminal;
|
||||
enum Error {
|
||||
#[error("Failed to create API EventFd: {0}")]
|
||||
CreateApiEventFd(#[source] std::io::Error),
|
||||
#[cfg(feature = "gdb")]
|
||||
#[cfg(feature = "guest_debug")]
|
||||
#[error("Failed to create Debug EventFd: {0}")]
|
||||
CreateDebugEventFd(#[source] std::io::Error),
|
||||
#[error("Failed to open hypervisor interface (is hypervisor interface available?): {0}")]
|
||||
@@ -58,10 +58,10 @@ enum Error {
|
||||
BareEventMonitor,
|
||||
#[error("Error doing event monitor I/O: {0}")]
|
||||
EventMonitorIo(std::io::Error),
|
||||
#[cfg(feature = "gdb")]
|
||||
#[cfg(feature = "guest_debug")]
|
||||
#[error("Error parsing --gdb: {0}")]
|
||||
ParsingGdb(option_parser::OptionParserError),
|
||||
#[cfg(feature = "gdb")]
|
||||
#[cfg(feature = "guest_debug")]
|
||||
#[error("Error parsing --gdb: path required")]
|
||||
BareGdb,
|
||||
#[error("Error creating log file: {0}")]
|
||||
@@ -124,11 +124,7 @@ fn prepare_default_values() -> (String, String, String) {
|
||||
(default_vcpus, default_memory, default_rng)
|
||||
}
|
||||
|
||||
fn create_app<'a>(
|
||||
default_vcpus: &'a str,
|
||||
default_memory: &'a str,
|
||||
default_rng: &'a str,
|
||||
) -> Command<'a> {
|
||||
fn create_app(default_vcpus: String, default_memory: String, default_rng: String) -> Command {
|
||||
let app = Command::new("cloud-hypervisor")
|
||||
// 'BUILT_VERSION' is set by the build script 'build.rs' at
|
||||
// compile time
|
||||
@@ -157,7 +153,7 @@ fn create_app<'a>(
|
||||
.help(
|
||||
"num_pci_segments=<num_pci_segments>,iommu_segments=<list_of_segments>,serial_number=<dmi_device_serial_number>,uuid=<dmi_device_uuid>,oem_strings=<list_of_strings>",
|
||||
)
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
@@ -170,7 +166,7 @@ fn create_app<'a>(
|
||||
hotplug_method=acpi|virtio-mem,\
|
||||
hotplug_size=<hotpluggable_memory_size>,\
|
||||
hotplugged_size=<hotplugged_memory_size>,\
|
||||
prefault=on|off\"",
|
||||
prefault=on|off,thp=on|off\"",
|
||||
)
|
||||
.default_value(default_memory)
|
||||
.group("vm-config"),
|
||||
@@ -188,15 +184,14 @@ fn create_app<'a>(
|
||||
hotplugged_size=<hotplugged_memory_size>,\
|
||||
prefault=on|off\"",
|
||||
)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("firmware")
|
||||
.long("firmware")
|
||||
.help("Path to firmware that is loaded in an architectural specific way")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
@@ -206,37 +201,35 @@ fn create_app<'a>(
|
||||
"Path to kernel to load. This may be a kernel or firmware that supports a PVH \
|
||||
entry point (e.g. vmlinux) or architecture equivalent",
|
||||
)
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("initramfs")
|
||||
.long("initramfs")
|
||||
.help("Path to initramfs image")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("cmdline")
|
||||
.long("cmdline")
|
||||
.help("Kernel command line")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("disk")
|
||||
.long("disk")
|
||||
.help(config::DiskConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("net")
|
||||
.long("net")
|
||||
.help(config::NetConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
@@ -252,23 +245,21 @@ fn create_app<'a>(
|
||||
Arg::new("balloon")
|
||||
.long("balloon")
|
||||
.help(config::BalloonConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("fs")
|
||||
.long("fs")
|
||||
.help(config::FsConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("pmem")
|
||||
.long("pmem")
|
||||
.help(config::PmemConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
@@ -291,53 +282,49 @@ fn create_app<'a>(
|
||||
Arg::new("device")
|
||||
.long("device")
|
||||
.help(config::DeviceConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("user-device")
|
||||
.long("user-device")
|
||||
.help(config::UserDeviceConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("vdpa")
|
||||
.long("vdpa")
|
||||
.help(config::VdpaConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("vsock")
|
||||
.long("vsock")
|
||||
.help(config::VsockConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.number_of_values(1)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("numa")
|
||||
.long("numa")
|
||||
.help(config::NumaConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("watchdog")
|
||||
.long("watchdog")
|
||||
.help("Enable virtio-watchdog")
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.action(ArgAction::SetTrue)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("v")
|
||||
.short('v')
|
||||
.multiple_occurrences(true)
|
||||
.action(ArgAction::Count)
|
||||
.help("Sets the level of debugging output")
|
||||
.group("logging"),
|
||||
)
|
||||
@@ -345,40 +332,44 @@ fn create_app<'a>(
|
||||
Arg::new("log-file")
|
||||
.long("log-file")
|
||||
.help("Log file. Standard error is used if not specified")
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1)
|
||||
.group("logging"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("api-socket")
|
||||
.long("api-socket")
|
||||
.help("HTTP API socket (UNIX domain socket): path=</path/to/a/file> or fd=<fd>.")
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1)
|
||||
.group("vmm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("event-monitor")
|
||||
.long("event-monitor")
|
||||
.help("File to report events on: path=</path/to/a/file> or fd=<fd>")
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1)
|
||||
.group("vmm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("restore")
|
||||
.long("restore")
|
||||
.help(config::RestoreConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1)
|
||||
.group("vmm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("seccomp")
|
||||
.long("seccomp")
|
||||
.takes_value(true)
|
||||
.possible_values(&["true", "false", "log"])
|
||||
.num_args(1)
|
||||
.value_parser(["true", "false", "log"])
|
||||
.default_value("true"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("tpm")
|
||||
.long("tpm")
|
||||
.num_args(1)
|
||||
.help(config::TpmConfig::SYNTAX)
|
||||
.group("vmm-config"),
|
||||
|
||||
);
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -386,34 +377,24 @@ fn create_app<'a>(
|
||||
Arg::new("sgx-epc")
|
||||
.long("sgx-epc")
|
||||
.help(config::SgxEpcConfig::SYNTAX)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
);
|
||||
|
||||
#[cfg(feature = "gdb")]
|
||||
#[cfg(feature = "guest_debug")]
|
||||
let app = app.arg(
|
||||
Arg::new("gdb")
|
||||
.long("gdb")
|
||||
.help("GDB socket (UNIX domain socket): path=</path/to/a/file>")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.group("vmm-config"),
|
||||
);
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
let app = app.arg(
|
||||
Arg::new("tdx")
|
||||
.long("tdx")
|
||||
.help("TDX Support: firmware=<tdvf path>")
|
||||
.takes_value(true)
|
||||
.group("vm-config"),
|
||||
);
|
||||
|
||||
app
|
||||
}
|
||||
|
||||
fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
let log_level = match cmd_arguments.occurrences_of("v") {
|
||||
let log_level = match cmd_arguments.get_count("v") {
|
||||
0 => LevelFilter::Warn,
|
||||
1 => LevelFilter::Info,
|
||||
2 => LevelFilter::Debug,
|
||||
@@ -421,7 +402,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
};
|
||||
|
||||
let log_file: Box<dyn std::io::Write + Send> = if let Some(file) =
|
||||
cmd_arguments.value_of("log-file")
|
||||
cmd_arguments.get_one::<String>("log-file")
|
||||
{
|
||||
Box::new(std::fs::File::create(std::path::Path::new(file)).map_err(Error::LogFileCreation)?)
|
||||
} else {
|
||||
@@ -436,7 +417,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
.map_err(Error::LoggerSetup)?;
|
||||
|
||||
let (api_socket_path, api_socket_fd) =
|
||||
if let Some(socket_config) = cmd_arguments.value_of("api-socket") {
|
||||
if let Some(socket_config) = cmd_arguments.get_one::<String>("api-socket") {
|
||||
let mut parser = OptionParser::new();
|
||||
parser.add("path").add("fd");
|
||||
parser.parse(socket_config).unwrap_or_default();
|
||||
@@ -450,7 +431,9 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
(Some(path), None)
|
||||
} else {
|
||||
(
|
||||
cmd_arguments.value_of("api-socket").map(|s| s.to_string()),
|
||||
cmd_arguments
|
||||
.get_one::<String>("api-socket")
|
||||
.map(|s| s.to_string()),
|
||||
None,
|
||||
)
|
||||
}
|
||||
@@ -458,7 +441,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
if let Some(monitor_config) = cmd_arguments.value_of("event-monitor") {
|
||||
if let Some(monitor_config) = cmd_arguments.get_one::<String>("event-monitor") {
|
||||
let mut parser = OptionParser::new();
|
||||
parser.add("path").add("fd");
|
||||
parser
|
||||
@@ -487,8 +470,8 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
let api_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateApiEventFd)?;
|
||||
|
||||
let http_sender = api_request_sender.clone();
|
||||
let seccomp_action = if let Some(seccomp_value) = cmd_arguments.value_of("seccomp") {
|
||||
match seccomp_value {
|
||||
let seccomp_action = if let Some(seccomp_value) = cmd_arguments.get_one::<String>("seccomp") {
|
||||
match seccomp_value as &str {
|
||||
"true" => SeccompAction::Trap,
|
||||
"false" => SeccompAction::Allow,
|
||||
"log" => SeccompAction::Log,
|
||||
@@ -537,8 +520,8 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
|
||||
let hypervisor = hypervisor::new().map_err(Error::CreateHypervisor)?;
|
||||
|
||||
#[cfg(feature = "gdb")]
|
||||
let gdb_socket_path = if let Some(gdb_config) = cmd_arguments.value_of("gdb") {
|
||||
#[cfg(feature = "guest_debug")]
|
||||
let gdb_socket_path = if let Some(gdb_config) = cmd_arguments.get_one::<String>("gdb") {
|
||||
let mut parser = OptionParser::new();
|
||||
parser.add("path");
|
||||
parser.parse(gdb_config).map_err(Error::ParsingGdb)?;
|
||||
@@ -551,9 +534,9 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
#[cfg(feature = "gdb")]
|
||||
#[cfg(feature = "guest_debug")]
|
||||
let debug_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateDebugEventFd)?;
|
||||
#[cfg(feature = "gdb")]
|
||||
#[cfg(feature = "guest_debug")]
|
||||
let vm_debug_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateDebugEventFd)?;
|
||||
|
||||
let vmm_thread = vmm::start_vmm_thread(
|
||||
@@ -563,11 +546,11 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
api_evt.try_clone().unwrap(),
|
||||
http_sender,
|
||||
api_request_receiver,
|
||||
#[cfg(feature = "gdb")]
|
||||
#[cfg(feature = "guest_debug")]
|
||||
gdb_socket_path,
|
||||
#[cfg(feature = "gdb")]
|
||||
#[cfg(feature = "guest_debug")]
|
||||
debug_evt.try_clone().unwrap(),
|
||||
#[cfg(feature = "gdb")]
|
||||
#[cfg(feature = "guest_debug")]
|
||||
vm_debug_evt.try_clone().unwrap(),
|
||||
&seccomp_action,
|
||||
hypervisor,
|
||||
@@ -575,12 +558,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
.map_err(Error::StartVmmThread)?;
|
||||
|
||||
let payload_present =
|
||||
cmd_arguments.is_present("kernel") || cmd_arguments.is_present("firmware");
|
||||
|
||||
// Can't test for "vm-config" group as some have default values. The kernel (or tdx if enabled)
|
||||
// is the only required option for booting the VM.
|
||||
#[cfg(feature = "tdx")]
|
||||
let payload_present = payload_present || cmd_arguments.is_present("tdx");
|
||||
cmd_arguments.contains_id("kernel") || cmd_arguments.contains_id("firmware");
|
||||
|
||||
if payload_present {
|
||||
let vm_params = config::VmParams::from_arg_matches(&cmd_arguments);
|
||||
@@ -595,7 +573,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
)
|
||||
.map_err(Error::VmCreate)?;
|
||||
vmm::api::vm_boot(api_evt.try_clone().unwrap(), sender).map_err(Error::VmBoot)?;
|
||||
} else if let Some(restore_params) = cmd_arguments.value_of("restore") {
|
||||
} else if let Some(restore_params) = cmd_arguments.get_one::<String>("restore") {
|
||||
vmm::api::vm_restore(
|
||||
api_evt.try_clone().unwrap(),
|
||||
api_request_sender,
|
||||
@@ -617,7 +595,7 @@ fn main() {
|
||||
let _ = unsafe { libc::umask(0o077) };
|
||||
|
||||
let (default_vcpus, default_memory, default_rng) = prepare_default_values();
|
||||
let cmd_arguments = create_app(&default_vcpus, &default_memory, &default_rng).get_matches();
|
||||
let cmd_arguments = create_app(default_vcpus, default_memory, default_rng).get_matches();
|
||||
let exit_code = match start_vmm(cmd_arguments) {
|
||||
Ok(path) => {
|
||||
path.map(|s| std::fs::remove_file(s).ok());
|
||||
@@ -629,7 +607,7 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
let on_tty = unsafe { libc::isatty(libc::STDIN_FILENO as i32) } != 0;
|
||||
let on_tty = unsafe { libc::isatty(libc::STDIN_FILENO) } != 0;
|
||||
if on_tty {
|
||||
// Don't forget to set the terminal in canonical mode
|
||||
// before to exit.
|
||||
@@ -645,14 +623,14 @@ mod unit_tests {
|
||||
use crate::{create_app, prepare_default_values};
|
||||
use std::path::PathBuf;
|
||||
use vmm::config::{
|
||||
CmdlineConfig, ConsoleConfig, ConsoleOutputMode, CpuFeatures, CpusConfig, MemoryConfig,
|
||||
PayloadConfig, RngConfig, VmConfig, VmParams,
|
||||
ConsoleConfig, ConsoleOutputMode, CpuFeatures, CpusConfig, MemoryConfig, PayloadConfig,
|
||||
RngConfig, VmConfig, VmParams,
|
||||
};
|
||||
|
||||
fn get_vm_config_from_vec(args: &[&str]) -> VmConfig {
|
||||
let (default_vcpus, default_memory, default_rng) = prepare_default_values();
|
||||
let cmd_arguments =
|
||||
create_app(&default_vcpus, &default_memory, &default_rng).get_matches_from(args);
|
||||
create_app(default_vcpus, default_memory, default_rng).get_matches_from(args);
|
||||
|
||||
let vm_params = VmParams::from_arg_matches(&cmd_arguments);
|
||||
|
||||
@@ -706,12 +684,8 @@ mod unit_tests {
|
||||
hugepage_size: None,
|
||||
prefault: false,
|
||||
zones: None,
|
||||
thp: true,
|
||||
},
|
||||
kernel: None,
|
||||
cmdline: CmdlineConfig {
|
||||
args: String::default(),
|
||||
},
|
||||
initramfs: None,
|
||||
payload: Some(PayloadConfig {
|
||||
kernel: Some(PathBuf::from("/path/to/kernel")),
|
||||
..Default::default()
|
||||
@@ -744,11 +718,10 @@ mod unit_tests {
|
||||
sgx_epc: None,
|
||||
numa: None,
|
||||
watchdog: false,
|
||||
#[cfg(feature = "tdx")]
|
||||
tdx: None,
|
||||
#[cfg(feature = "gdb")]
|
||||
#[cfg(feature = "guest_debug")]
|
||||
gdb: false,
|
||||
platform: None,
|
||||
tpm: None,
|
||||
};
|
||||
|
||||
assert_eq!(expected_vm_config, result_vm_config);
|
||||
@@ -1668,4 +1641,26 @@ mod unit_tests {
|
||||
compare_vm_config_cli_vs_json(cli, openapi, *equal);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_valid_vm_config_tpm_socket() {
|
||||
vec![(
|
||||
vec![
|
||||
"cloud-hypervisor",
|
||||
"--kernel",
|
||||
"/path/to/kernel",
|
||||
"--tpm",
|
||||
"socket=/path/to/tpm/sock",
|
||||
],
|
||||
r#"{
|
||||
"payload": {"kernel": "/path/to/kernel"},
|
||||
"tpm": {"socket": "/path/to/tpm/sock"}
|
||||
}"#,
|
||||
true,
|
||||
)]
|
||||
.iter()
|
||||
.for_each(|(cli, openapi, equal)| {
|
||||
compare_vm_config_cli_vs_json(cli, openapi, *equal);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,4 +73,4 @@ write_files:
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo -n "@DEFAULT_TCP_LISTENER_MESSAGE" | nc -w0 @HOST_IP @TCP_LISTENER_PORT
|
||||
echo -n "@DEFAULT_TCP_LISTENER_MESSAGE" > /dev/tcp/@HOST_IP/@TCP_LISTENER_PORT
|
||||
|
||||
@@ -7,8 +7,10 @@ edition = "2021"
|
||||
[dependencies]
|
||||
dirs = "4.0.0"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.131"
|
||||
once_cell = "1.13.1"
|
||||
libc = "0.2.138"
|
||||
once_cell = "1.16.0"
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.89"
|
||||
ssh2 = { version = "0.9.3", features = ["vendored-openssl"] }
|
||||
vmm-sys-util = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
@@ -4,11 +4,10 @@
|
||||
//
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use serde_json::Value;
|
||||
use ssh2::Session;
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::Debug;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::TcpListener;
|
||||
@@ -20,13 +19,29 @@ use std::process::{Child, Command, ExitStatus, Output, Stdio};
|
||||
use std::str::FromStr;
|
||||
use std::sync::Mutex;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use std::{fmt, fs};
|
||||
use vmm_sys_util::tempdir::TempDir;
|
||||
use wait_timeout::ChildExt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WaitTimeoutError {
|
||||
Timedout,
|
||||
ExitStatus,
|
||||
General(std::io::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Parsing(std::num::ParseIntError),
|
||||
SshCommand(SshCommandError),
|
||||
WaitForBoot(WaitForBootError),
|
||||
EthrLogFile(std::io::Error),
|
||||
EthrLogParse,
|
||||
FioOutputParse,
|
||||
Iperf3Parse,
|
||||
Spawn(std::io::Error),
|
||||
WaitTimeout(WaitTimeoutError),
|
||||
}
|
||||
|
||||
impl From<SshCommandError> for Error {
|
||||
@@ -75,7 +90,7 @@ impl GuestNetworkConfig {
|
||||
|
||||
match (|| -> Result<(), WaitForBootError> {
|
||||
let listener =
|
||||
TcpListener::bind(&listen_addr.as_str()).map_err(WaitForBootError::Listen)?;
|
||||
TcpListener::bind(listen_addr.as_str()).map_err(WaitForBootError::Listen)?;
|
||||
listener
|
||||
.set_nonblocking(true)
|
||||
.expect("Cannot set non-blocking for tcp listener");
|
||||
@@ -213,7 +228,7 @@ impl Drop for WindowsDiskConfig {
|
||||
|
||||
// losetup -d <loopback_device>
|
||||
std::process::Command::new("losetup")
|
||||
.args(&["-d", self.loopback_device.as_str()])
|
||||
.args(["-d", self.loopback_device.as_str()])
|
||||
.output()
|
||||
.expect("Expect removing loopback device to succeed");
|
||||
}
|
||||
@@ -285,8 +300,8 @@ impl DiskConfig for UbuntuDiskConfig {
|
||||
.expect("Expected writing out network-config to succeed");
|
||||
|
||||
std::process::Command::new("mkdosfs")
|
||||
.args(&["-n", "cidata"])
|
||||
.args(&["-C", cloudinit_file_path.as_str()])
|
||||
.args(["-n", "cidata"])
|
||||
.args(["-C", cloudinit_file_path.as_str()])
|
||||
.arg("8192")
|
||||
.output()
|
||||
.expect("Expect creating disk image to succeed");
|
||||
@@ -296,8 +311,8 @@ impl DiskConfig for UbuntuDiskConfig {
|
||||
.for_each(|x| {
|
||||
std::process::Command::new("mcopy")
|
||||
.arg("-o")
|
||||
.args(&["-i", cloudinit_file_path.as_str()])
|
||||
.args(&["-s", cloud_init_directory.join(x).to_str().unwrap(), "::"])
|
||||
.args(["-i", cloudinit_file_path.as_str()])
|
||||
.args(["-s", cloud_init_directory.join(x).to_str().unwrap(), "::"])
|
||||
.output()
|
||||
.expect("Expect copying files to disk image to succeed");
|
||||
});
|
||||
@@ -381,7 +396,7 @@ impl DiskConfig for WindowsDiskConfig {
|
||||
std::process::Command::new("dmsetup")
|
||||
.arg("create")
|
||||
.arg(windows_snapshot_cow.as_str())
|
||||
.args(&[
|
||||
.args([
|
||||
"--table",
|
||||
format!("0 {} linear {} 0", cow_file_blk_size, self.loopback_device).as_str(),
|
||||
])
|
||||
@@ -400,7 +415,7 @@ impl DiskConfig for WindowsDiskConfig {
|
||||
std::process::Command::new("dmsetup")
|
||||
.arg("create")
|
||||
.arg(windows_snapshot.as_str())
|
||||
.args(&[
|
||||
.args([
|
||||
"--table",
|
||||
format!(
|
||||
"0 {} snapshot /dev/mapper/windows-base /dev/mapper/{} P 8",
|
||||
@@ -708,14 +723,14 @@ pub fn ssh_command_ip(
|
||||
|
||||
pub fn exec_host_command_status(command: &str) -> ExitStatus {
|
||||
std::process::Command::new("bash")
|
||||
.args(&["-c", command])
|
||||
.args(["-c", command])
|
||||
.status()
|
||||
.unwrap_or_else(|_| panic!("Expected '{}' to run", command))
|
||||
}
|
||||
|
||||
pub fn exec_host_command_output(command: &str) -> Output {
|
||||
std::process::Command::new("bash")
|
||||
.args(&["-c", command])
|
||||
.args(["-c", command])
|
||||
.output()
|
||||
.unwrap_or_else(|_| panic!("Expected '{}' to run", command))
|
||||
}
|
||||
@@ -781,6 +796,13 @@ impl Guest {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn default_net_string_w_mtu(&self, mtu: u16) -> String {
|
||||
format!(
|
||||
"tap=,mac={},ip={},mask=255.255.255.0,mtu={}",
|
||||
self.network.guest_mac, self.network.host_ip, mtu
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ssh_command(&self, command: &str) -> Result<String, SshCommandError> {
|
||||
ssh_command_ip(
|
||||
command,
|
||||
@@ -1063,7 +1085,7 @@ impl Guest {
|
||||
}
|
||||
|
||||
pub fn reboot_linux(&self, current_reboot_count: u32, custom_timeout: Option<i32>) {
|
||||
let list_boots_cmd = "sudo journalctl --list-boots | wc -l";
|
||||
let list_boots_cmd = "sudo last | grep -c reboot";
|
||||
let boot_count = self
|
||||
.ssh_command(list_boots_cmd)
|
||||
.unwrap()
|
||||
@@ -1278,7 +1300,7 @@ impl<'a> GuestCommand<'a> {
|
||||
|
||||
pub fn default_disks(&mut self) -> &mut Self {
|
||||
if self.guest.disk_config.disk(DiskType::CloudInit).is_some() {
|
||||
self.args(&[
|
||||
self.args([
|
||||
"--disk",
|
||||
format!(
|
||||
"path={}",
|
||||
@@ -1295,7 +1317,7 @@ impl<'a> GuestCommand<'a> {
|
||||
.as_str(),
|
||||
])
|
||||
} else {
|
||||
self.args(&[
|
||||
self.args([
|
||||
"--disk",
|
||||
format!(
|
||||
"path={}",
|
||||
@@ -1310,7 +1332,7 @@ impl<'a> GuestCommand<'a> {
|
||||
}
|
||||
|
||||
pub fn default_net(&mut self) -> &mut Self {
|
||||
self.args(&["--net", self.guest.default_net_string().as_str()])
|
||||
self.args(["--net", self.guest.default_net_string().as_str()])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1320,3 +1342,343 @@ pub fn clh_command(cmd: &str) -> String {
|
||||
|target| format!("target/{}/release/{}", target, cmd),
|
||||
)
|
||||
}
|
||||
|
||||
pub 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
|
||||
})
|
||||
}
|
||||
|
||||
pub enum FioOps {
|
||||
Read,
|
||||
RandomRead,
|
||||
Write,
|
||||
RandomWrite,
|
||||
ReadWrite,
|
||||
RandRW,
|
||||
}
|
||||
|
||||
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"),
|
||||
FioOps::ReadWrite => write!(f, "rw"),
|
||||
FioOps::RandRW => write!(f, "randrw"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub 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, write) = match fio_ops {
|
||||
FioOps::Read | FioOps::RandomRead => (true, false),
|
||||
FioOps::Write | FioOps::RandomWrite => (false, true),
|
||||
FioOps::ReadWrite | FioOps::RandRW => (true, true),
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
if write {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
total_bps
|
||||
})
|
||||
.map_err(|_| {
|
||||
eprintln!(
|
||||
"=============== Fio output ===============\n\n{}\n\n===========end============\n\n",
|
||||
output
|
||||
);
|
||||
Error::FioOutputParse
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_fio_output_iops(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, write) = match fio_ops {
|
||||
FioOps::Read | FioOps::RandomRead => (true, false),
|
||||
FioOps::Write | FioOps::RandomWrite => (false, true),
|
||||
FioOps::ReadWrite | FioOps::RandRW => (true, true),
|
||||
};
|
||||
|
||||
let mut total_iops = 0_f64;
|
||||
for j in jobs {
|
||||
if read {
|
||||
let ios = j["read"]["total_ios"]
|
||||
.as_u64()
|
||||
.expect("'fio' parse error: missing entry 'read.total_ios'");
|
||||
let runtime = j["read"]["runtime"]
|
||||
.as_u64()
|
||||
.expect("'fio' parse error: missing entry 'read.runtime'")
|
||||
as f64
|
||||
/ 1000_f64;
|
||||
total_iops += ios as f64 / runtime;
|
||||
}
|
||||
if write {
|
||||
let ios = j["write"]["total_ios"]
|
||||
.as_u64()
|
||||
.expect("'fio' parse error: missing entry 'write.total_ios'");
|
||||
let runtime = j["write"]["runtime"]
|
||||
.as_u64()
|
||||
.expect("'fio' parse error: missing entry 'write.runtime'")
|
||||
as f64
|
||||
/ 1000_f64;
|
||||
total_iops += ios as f64 / runtime;
|
||||
}
|
||||
}
|
||||
|
||||
total_iops
|
||||
})
|
||||
.map_err(|_| {
|
||||
eprintln!(
|
||||
"=============== Fio output ===============\n\n{}\n\n===========end============\n\n",
|
||||
output
|
||||
);
|
||||
Error::FioOutputParse
|
||||
})
|
||||
}
|
||||
|
||||
// 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(())
|
||||
}
|
||||
|
||||
pub 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))?;
|
||||
}
|
||||
|
||||
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 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
|
||||
})
|
||||
}
|
||||
|
||||
pub 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,
|
||||
)?;
|
||||
|
||||
// Start the ethr server on the guest
|
||||
guest.ssh_command(&format!("{} -s &> /dev/null &", ethr_remote_path))?;
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user