mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
278 Commits
v29.0
...
stable/v31
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a63fa2613 | ||
|
|
3a63143f33 | ||
|
|
064c1e2c8b | ||
|
|
15b9d14876 | ||
|
|
9da363e79b | ||
|
|
bbfd810c3b | ||
|
|
8fb86d2284 | ||
|
|
797110cca1 | ||
|
|
19ca5c0b84 | ||
|
|
9fe9b8504d | ||
|
|
4fc3dd5004 | ||
|
|
6bc6365c35 | ||
|
|
e139cdfd69 | ||
|
|
541de8b757 | ||
|
|
184dac70a0 | ||
|
|
022b489e7b | ||
|
|
399e2f9f7d | ||
|
|
22cc96494f | ||
|
|
f98402ec15 | ||
|
|
acc54ade7b | ||
|
|
0ebbb3f8a2 | ||
|
|
95511287ec | ||
|
|
5a3af30e6a | ||
|
|
034b48faf7 | ||
|
|
ba3e02ce86 | ||
|
|
5492259af9 | ||
|
|
cc1254d5e1 | ||
|
|
34bb3319d4 | ||
|
|
d530569ac2 | ||
|
|
75956e64ec | ||
|
|
ae646c2a00 | ||
|
|
04d3e5bbf5 | ||
|
|
cbe972659c | ||
|
|
1e4e03d110 | ||
|
|
4876f7550d | ||
|
|
c0146e3ef1 | ||
|
|
77a205881b | ||
|
|
321421c53e | ||
|
|
48a87e699d | ||
|
|
147a800d5d | ||
|
|
eaf8cbd47d | ||
|
|
9d24e862eb | ||
|
|
11324ac21c | ||
|
|
ce75865e2c | ||
|
|
f3522e85fc | ||
|
|
d3ac6a85e0 | ||
|
|
38a1b45783 | ||
|
|
e9841db486 | ||
|
|
c1f555cde3 | ||
|
|
505f4dfa53 | ||
|
|
67ad3ff1ba | ||
|
|
755cabea4c | ||
|
|
1ed4898d28 | ||
|
|
a807b91f86 | ||
|
|
f6236087d8 | ||
|
|
95f83320b1 | ||
|
|
dcc858a403 | ||
|
|
57ea412c64 | ||
|
|
e32c9525c0 | ||
|
|
2b4f60e57b | ||
|
|
5736205cf0 | ||
|
|
a8d1849485 | ||
|
|
55937f63bc | ||
|
|
426d5d6dd0 | ||
|
|
3f3d07b3dc | ||
|
|
296cb909f4 | ||
|
|
e7d52fc736 | ||
|
|
a598b935f2 | ||
|
|
1c0b6f1090 | ||
|
|
1b12790c13 | ||
|
|
bd307dcab5 | ||
|
|
83d757d89a | ||
|
|
1b8e7ac06c | ||
|
|
389e4fe8be | ||
|
|
06a9c3b839 | ||
|
|
36cf46e381 | ||
|
|
1efdea7466 | ||
|
|
e662a2e12c | ||
|
|
5496bd5dfb | ||
|
|
e655de1e39 | ||
|
|
a55a642b70 | ||
|
|
ded8959b9a | ||
|
|
53e49636ca | ||
|
|
0b0f3c69f0 | ||
|
|
6650f553bd | ||
|
|
c5bca514b9 | ||
|
|
c4106c0592 | ||
|
|
61f89e7dcc | ||
|
|
7a4be1534f | ||
|
|
cf0c4e07b6 | ||
|
|
36ec53c4a0 | ||
|
|
c7997ea4e5 | ||
|
|
b2d3aba19c | ||
|
|
9cd97bb64e | ||
|
|
868fdf9dfe | ||
|
|
12729eb034 | ||
|
|
a062401be6 | ||
|
|
09cc139b53 | ||
|
|
4635ebd4b2 | ||
|
|
ca96ff601b | ||
|
|
feb523ef78 | ||
|
|
7e47464a48 | ||
|
|
4c4f79c831 | ||
|
|
0fd701ac75 | ||
|
|
64924d5c2b | ||
|
|
9c11927305 | ||
|
|
b13d0e53c5 | ||
|
|
6001240ca1 | ||
|
|
9534604ef3 | ||
|
|
c152f9f6bc | ||
|
|
325e62372d | ||
|
|
9421884334 | ||
|
|
fcd09946b7 | ||
|
|
a20e481abe | ||
|
|
6e4374c392 | ||
|
|
44452cbe22 | ||
|
|
7b2ebd2fa5 | ||
|
|
8a0f6014ce | ||
|
|
d78da5d252 | ||
|
|
b35a94d83c | ||
|
|
961319d8ad | ||
|
|
b97f929706 | ||
|
|
b71564f284 | ||
|
|
f2be30f699 | ||
|
|
b14216e854 | ||
|
|
3498e67bda | ||
|
|
24d163b244 | ||
|
|
90697d4ae0 | ||
|
|
73c4156775 | ||
|
|
02f774824b | ||
|
|
74cd3d0dc2 | ||
|
|
85962099c7 | ||
|
|
04ee1efa70 | ||
|
|
5258a870e0 | ||
|
|
76d6d28f3e | ||
|
|
5132e3b907 | ||
|
|
599c5280ed | ||
|
|
2f649bb6ad | ||
|
|
ece0e6fa92 | ||
|
|
2593b67864 | ||
|
|
191e865261 | ||
|
|
ca3a441c49 | ||
|
|
de3ca97095 | ||
|
|
74dcb37ec3 | ||
|
|
1d55de9c74 | ||
|
|
2e94a86b31 | ||
|
|
996bdc6e08 | ||
|
|
9d18012c52 | ||
|
|
6a8bd8e8c5 | ||
|
|
d9184e5256 | ||
|
|
5e475e70e6 | ||
|
|
bd1e1677bd | ||
|
|
595c9d13a9 | ||
|
|
c2616f3f4f | ||
|
|
7ceb126184 | ||
|
|
879411e70f | ||
|
|
1181fd21fd | ||
|
|
c8967fcc37 | ||
|
|
19e893fa53 | ||
|
|
aa502b3e41 | ||
|
|
10531f052b | ||
|
|
b072eb454e | ||
|
|
b7c2a0f23f | ||
|
|
874d524a13 | ||
|
|
91e2601523 | ||
|
|
01900e3c2b | ||
|
|
e35ef40029 | ||
|
|
aa0af5f8d7 | ||
|
|
46066d6ae1 | ||
|
|
51c1738d55 | ||
|
|
0567def931 | ||
|
|
0110fb4edc | ||
|
|
4f945743cb | ||
|
|
8f6afec520 | ||
|
|
b14427540b | ||
|
|
ea4a95c4f6 | ||
|
|
f5084f7c65 | ||
|
|
45ab9f77b7 | ||
|
|
f0ea8eb70b | ||
|
|
3377e0e983 | ||
|
|
e9435b1327 | ||
|
|
09fca4a5dd | ||
|
|
1143d54ee0 | ||
|
|
934b20a77a | ||
|
|
b54ce6c3db | ||
|
|
3dd01443d5 | ||
|
|
43227cd5c4 | ||
|
|
176e438d17 | ||
|
|
c22c4675b3 | ||
|
|
7ccf58a019 | ||
|
|
48e1af7d3c | ||
|
|
341a69c39c | ||
|
|
5d34d85b92 | ||
|
|
bcd46906ec | ||
|
|
bc59ca72f3 | ||
|
|
995945dd25 | ||
|
|
8edd81ec82 | ||
|
|
726cfa41f4 | ||
|
|
d19a5d5356 | ||
|
|
69e8f60b91 | ||
|
|
c6eb69caba | ||
|
|
e4ed9bc557 | ||
|
|
f4509c3611 | ||
|
|
ecbb8ab282 | ||
|
|
29ebecccb0 | ||
|
|
5646a917ff | ||
|
|
6e22f23831 | ||
|
|
8e996ff2fe | ||
|
|
2d2f356d94 | ||
|
|
938c16f2b2 | ||
|
|
105a7dd7aa | ||
|
|
11ef495e6b | ||
|
|
34b3170680 | ||
|
|
427a2bacf5 | ||
|
|
1325c76525 | ||
|
|
4a51a6615f | ||
|
|
d5ce855649 | ||
|
|
496f932276 | ||
|
|
c33efe294a | ||
|
|
2d022d0c85 | ||
|
|
5b31b19530 | ||
|
|
ad202f9b7a | ||
|
|
3a225aaa23 | ||
|
|
1bfa07f48e | ||
|
|
8e682bcb00 | ||
|
|
1ee2922dbc | ||
|
|
2ce503f457 | ||
|
|
574576c8e9 | ||
|
|
aaf67c9ae4 | ||
|
|
8b9da4e286 | ||
|
|
271f11d9ec | ||
|
|
e436b382cc | ||
|
|
1ce1fe7334 | ||
|
|
73af65f417 | ||
|
|
cffde0ff65 | ||
|
|
e41b7d90d5 | ||
|
|
60425471dd | ||
|
|
2b76e1d7ba | ||
|
|
15ace525be | ||
|
|
8db630763a | ||
|
|
cd1470e289 | ||
|
|
99d8c34861 | ||
|
|
6725771dc3 | ||
|
|
0e8fd0bc17 | ||
|
|
0ef1e668a5 | ||
|
|
477332a94a | ||
|
|
7b102c4fa3 | ||
|
|
1e159dbefb | ||
|
|
a4ef6e57a7 | ||
|
|
ec45daac19 | ||
|
|
c773330a65 | ||
|
|
e4ae668bcd | ||
|
|
f6c058da56 | ||
|
|
089dffcb49 | ||
|
|
9d7034b9c6 | ||
|
|
a86edd3032 | ||
|
|
7805fe58e2 | ||
|
|
e66ffffd43 | ||
|
|
843c8ad51c | ||
|
|
a334831d54 | ||
|
|
dab9f1cf30 | ||
|
|
3a81f9328f | ||
|
|
e2886a4b1f | ||
|
|
c6d3f61a42 | ||
|
|
4689abd9f4 | ||
|
|
f479f0605d | ||
|
|
5034bea8c4 | ||
|
|
b8172408f8 | ||
|
|
b0a2ce0598 | ||
|
|
a0922930b1 | ||
|
|
d1ec65873e | ||
|
|
54e9ed9932 | ||
|
|
111225a2a5 | ||
|
|
fe49056129 | ||
|
|
d5558aea2a | ||
|
|
1ba995d952 | ||
|
|
c6c081099f | ||
|
|
3a1254a9b2 |
2
.github/workflows/build.yaml
vendored
2
.github/workflows/build.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
- "1.60"
|
||||
- "1.62"
|
||||
target:
|
||||
- x86_64-unknown-linux-gnu
|
||||
- x86_64-unknown-linux-musl
|
||||
|
||||
20
.github/workflows/docker-image.yaml
vendored
20
.github/workflows/docker-image.yaml
vendored
@@ -7,6 +7,10 @@ on:
|
||||
pull_request:
|
||||
paths: resources/Dockerfile
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -20,19 +24,19 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Login to DockerHub
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: docker/login-action@v1
|
||||
- name: Login to ghcr
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
# list of Docker images to use as base name for tags
|
||||
images: cloudhypervisor/dev
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=raw,value={{date 'YYYYMMDD'}}-0
|
||||
|
||||
16
.github/workflows/quality.yaml
vendored
16
.github/workflows/quality.yaml
vendored
@@ -51,7 +51,7 @@ jobs:
|
||||
run: |
|
||||
set -e
|
||||
commits=$(git rev-list origin/${{ github.base_ref }}..${{ github.sha }})
|
||||
for commit in $commits; do git checkout $commit; cargo check --tests --all --target=${{ matrix.target }}; done
|
||||
for commit in $commits; do git checkout $commit; cargo check --tests --examples --all --target=${{ matrix.target }}; done
|
||||
git checkout ${{ github.sha }}
|
||||
|
||||
- name: Formatting (rustfmt)
|
||||
@@ -62,28 +62,28 @@ jobs:
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
|
||||
- name: Clippy (default features)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --tests -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
|
||||
- name: Clippy (default features + guest_debug)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
|
||||
- name: Clippy (default features + tracing)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --features "tracing" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "tracing" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
|
||||
- name: Clippy (mshv)
|
||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||
@@ -91,7 +91,7 @@ jobs:
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
|
||||
- name: Clippy (mshv + kvm)
|
||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||
@@ -99,7 +99,7 @@ jobs:
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --features "mshv,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "mshv,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
|
||||
- name: Clippy (kvm + tdx)
|
||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --features "tdx,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "tdx,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
|
||||
- name: Check build did not modify any files
|
||||
run: test -z "$(git status --porcelain)"
|
||||
|
||||
11
.github/workflows/release.yaml
vendored
11
.github/workflows/release.yaml
vendored
@@ -3,6 +3,7 @@ on: [pull_request, create]
|
||||
|
||||
jobs:
|
||||
release:
|
||||
if: (github.event_name == 'create' && github.event.ref_type == 'tag') || github.event_name == 'pull_request'
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -15,29 +16,29 @@ jobs:
|
||||
- name: Install Rust toolchain (x86_64-unknown-linux-gnu)
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "1.62"
|
||||
toolchain: "1.67.1"
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- name: Install Rust toolchain (x86_64-unknown-linux-musl)
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "1.62"
|
||||
toolchain: "1.67.1"
|
||||
target: x86_64-unknown-linux-musl
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
toolchain: "1.62"
|
||||
toolchain: "1.67.1"
|
||||
command: build
|
||||
args: --all --release --no-default-features --features "kvm,mshv" --target=x86_64-unknown-linux-gnu
|
||||
- name: Static Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
toolchain: "1.62"
|
||||
toolchain: "1.67.1"
|
||||
command: build
|
||||
args: --all --release --no-default-features --features "kvm,mshv" --target=x86_64-unknown-linux-musl
|
||||
- name: Install Rust toolchain (aarch64-unknown-linux-musl)
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "1.62"
|
||||
toolchain: "1.67.1"
|
||||
target: aarch64-unknown-linux-musl
|
||||
override: true
|
||||
- name: Create Release
|
||||
|
||||
592
Cargo.lock
generated
592
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
30
Cargo.toml
30
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cloud-hypervisor"
|
||||
version = "29.0.0"
|
||||
version = "31.2.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
default-run = "cloud-hypervisor"
|
||||
@@ -12,10 +12,10 @@ homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
||||
# Keep in sync with version in .github/workflows/build.yaml
|
||||
# Policy on MSRV (see #4318):
|
||||
# Can only be bumped by:
|
||||
# a.) A dependency requires it,
|
||||
# a.) A dependency requires it,
|
||||
# b.) If we want to use a new feature and that MSRV is at least 6 months old,
|
||||
# c.) There is a security issue that is addressed by the toolchain update.
|
||||
rust-version = "1.60"
|
||||
rust-version = "1.62"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
@@ -23,10 +23,16 @@ codegen-units = 1
|
||||
opt-level = "s"
|
||||
strip = true
|
||||
|
||||
[profile.profiling]
|
||||
inherits = "release"
|
||||
strip = false
|
||||
debug = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.68"
|
||||
anyhow = "1.0.69"
|
||||
api_client = { path = "api_client" }
|
||||
clap = { version = "4.0.32", features = ["wrap_help","cargo","string"] }
|
||||
argh = "0.1.9"
|
||||
dhat = { version = "0.3.2", optional = true }
|
||||
epoll = "4.3.1"
|
||||
event_monitor = { path = "event_monitor" }
|
||||
hypervisor = { path = "hypervisor" }
|
||||
@@ -34,9 +40,9 @@ libc = "0.2.139"
|
||||
log = { version = "0.4.17", features = ["std"] }
|
||||
option_parser = { path = "option_parser" }
|
||||
seccompiler = "0.3.0"
|
||||
serde_json = "1.0.89"
|
||||
signal-hook = "0.3.14"
|
||||
thiserror = "1.0.38"
|
||||
serde_json = "1.0.95"
|
||||
signal-hook = "0.3.15"
|
||||
thiserror = "1.0.39"
|
||||
tpm = { path = "tpm"}
|
||||
tracer = { path = "tracer" }
|
||||
vmm = { path = "vmm" }
|
||||
@@ -46,18 +52,20 @@ vm-memory = "0.10.0"
|
||||
# List of patched crates
|
||||
[patch.crates-io]
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx" }
|
||||
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "main" }
|
||||
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
|
||||
|
||||
[dev-dependencies]
|
||||
dirs = "4.0.0"
|
||||
net_util = { path = "net_util" }
|
||||
once_cell = "1.16.0"
|
||||
serde_json = "1.0.89"
|
||||
once_cell = "1.17.1"
|
||||
serde_json = "1.0.95"
|
||||
test_infra = { path = "test_infra" }
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
[features]
|
||||
default = ["kvm"]
|
||||
dhat-heap = ["dhat"] # For heap profiling
|
||||
guest_debug = ["vmm/guest_debug"]
|
||||
kvm = ["vmm/kvm"]
|
||||
mshv = ["vmm/mshv"]
|
||||
@@ -66,7 +74,6 @@ tracing = ["vmm/tracing", "tracer/tracing"]
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"acpi_tables",
|
||||
"api_client",
|
||||
"arch",
|
||||
"block_util",
|
||||
@@ -83,7 +90,6 @@ members = [
|
||||
"serial_buffer",
|
||||
"test_infra",
|
||||
"tracer",
|
||||
"vfio_user",
|
||||
"vhdx",
|
||||
"vhost_user_block",
|
||||
"vhost_user_net",
|
||||
|
||||
5
Jenkinsfile
vendored
5
Jenkinsfile
vendored
@@ -1,6 +1,9 @@
|
||||
def runWorkers = true
|
||||
pipeline {
|
||||
agent none
|
||||
options {
|
||||
timeout(time: 4, unit: 'HOURS')
|
||||
}
|
||||
stages {
|
||||
stage('Early checks') {
|
||||
agent { node { label 'built-in' } }
|
||||
@@ -214,7 +217,7 @@ pipeline {
|
||||
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"'
|
||||
sh 'az storage blob download --container-name private-images --file "$HOME/workloads/windows-server-2022-amd64-2.raw" --name windows-server-2022-amd64-2.raw --connection-string "$AZURE_CONNECTION_STRING"'
|
||||
}
|
||||
}
|
||||
stage('Run Windows guest integration tests') {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
- Sebastien Boeuf - @sboeuf
|
||||
- Robert Bradford - @rbradford
|
||||
- Bo Chen - @likebreath
|
||||
- Samuel Ortiz - @sameo
|
||||
- Wei Liu - @liuw
|
||||
- Michael Zhao - @michael2012z
|
||||
|
||||
18
README.md
18
README.md
@@ -113,7 +113,7 @@ Firmware](https://github.com/cloud-hypervisor/rust-hypervisor-firmware) or an
|
||||
edk2 UEFI firmware called `CLOUDHV` / `CLOUDHV_EFI`.)
|
||||
|
||||
Binary builds of the firmware files are available for the latest release of
|
||||
[Rust Hyperivor
|
||||
[Rust Hypervisor
|
||||
Firmware](https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/latest)
|
||||
and [our edk2
|
||||
repository](https://github.com/cloud-hypervisor/edk2/releases/latest)
|
||||
@@ -124,7 +124,7 @@ may be required.
|
||||
### Firmware Booting
|
||||
|
||||
Cloud Hypervisor supports booting disk images containing all needed components
|
||||
to run cloud workloads, a.k.a. cloud images.
|
||||
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
|
||||
@@ -147,7 +147,7 @@ $ sudo setcap cap_net_admin+ep ./cloud-hypervisor
|
||||
$ ./create-cloud-init.sh
|
||||
$ ./cloud-hypervisor \
|
||||
--kernel ./hypervisor-fw \
|
||||
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||
--disk path=focal-server-cloudimg-amd64.raw --disk path=/tmp/ubuntu-cloudinit.img \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
--net "tap=,mac=,ip=,mask="
|
||||
@@ -160,7 +160,7 @@ GRUB) is required then it necessary to switch to the serial console instead of
|
||||
```shell
|
||||
$ ./cloud-hypervisor \
|
||||
--kernel ./hypervisor-fw \
|
||||
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||
--disk path=focal-server-cloudimg-amd64.raw --disk path=/tmp/ubuntu-cloudinit.img \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
--net "tap=,mac=,ip=,mask=" \
|
||||
@@ -178,7 +178,7 @@ To build the kernel:
|
||||
|
||||
```shell
|
||||
# Clone the Cloud Hypervisor Linux branch
|
||||
$ git clone --depth 1 https://github.com/cloud-hypervisor/linux.git -b ch-5.15.12 linux-cloud-hypervisor
|
||||
$ git clone --depth 1 https://github.com/cloud-hypervisor/linux.git -b ch-6.2 linux-cloud-hypervisor
|
||||
$ pushd linux-cloud-hypervisor
|
||||
# Use the x86-64 cloud-hypervisor kernel config to build your kernel for x86-64
|
||||
$ wget https://raw.githubusercontent.com/cloud-hypervisor/cloud-hypervisor/main/resources/linux-config-x86_64
|
||||
@@ -222,7 +222,7 @@ $ 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 path=/tmp/ubuntu-cloudinit.img \
|
||||
--disk path=focal-server-cloudimg-amd64.raw --disk path=/tmp/ubuntu-cloudinit.img \
|
||||
--cmdline "console=hvc0 root=/dev/vda1 rw" \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
@@ -236,7 +236,7 @@ $ sudo setcap cap_net_admin+ep ./cloud-hypervisor
|
||||
$ ./create-cloud-init.sh
|
||||
$ ./cloud-hypervisor \
|
||||
--kernel ./linux-cloud-hypervisor/arch/arm64/boot/Image \
|
||||
--disk path=focal-server-cloudimg-arm64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||
--disk path=focal-server-cloudimg-arm64.raw --disk path=/tmp/ubuntu-cloudinit.img \
|
||||
--cmdline "console=hvc0 root=/dev/vda1 rw" \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
@@ -372,8 +372,8 @@ are all equal and welcome means of contribution. See the
|
||||
|
||||
## 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/).
|
||||
Get an [invite to our Slack channel](https://join.slack.com/t/cloud-hypervisor/shared_invite/enQtNjY3MTE3MDkwNDQ4LWQ1MTA1ZDVmODkwMWQ1MTRhYzk4ZGNlN2UwNTI3ZmFlODU0OTcwOWZjMTkwZDExYWE3YjFmNzgzY2FmNDAyMjI),
|
||||
[join us on Slack](https://cloud-hypervisor.slack.com/), and [participate in our community activities](https://cloud-hypervisor.slack.com/archives/C04R5DUQVBN).
|
||||
|
||||
## Mailing list
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
[package]
|
||||
name = "acpi_tables"
|
||||
version = "0.1.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
vm-memory = "0.10.0"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +0,0 @@
|
||||
// Copyright © 2019 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
pub mod aml;
|
||||
pub mod rsdp;
|
||||
pub mod sdt;
|
||||
|
||||
fn generate_checksum(data: &[u8]) -> u8 {
|
||||
(255 - data.iter().fold(0u8, |acc, x| acc.wrapping_add(*x))).wrapping_add(1)
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright © 2019 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use vm_memory::ByteValued;
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct Rsdp {
|
||||
pub signature: [u8; 8],
|
||||
pub checksum: u8,
|
||||
pub oem_id: [u8; 6],
|
||||
pub revision: u8,
|
||||
_rsdt_addr: u32,
|
||||
pub length: u32,
|
||||
pub xsdt_addr: u64,
|
||||
pub extended_checksum: u8,
|
||||
_reserved: [u8; 3],
|
||||
}
|
||||
|
||||
// SAFETY: Rsdp only contains a series of integers
|
||||
unsafe impl ByteValued for Rsdp {}
|
||||
|
||||
impl Rsdp {
|
||||
pub fn new(oem_id: [u8; 6], xsdt_addr: u64) -> Self {
|
||||
let mut rsdp = Rsdp {
|
||||
signature: *b"RSD PTR ",
|
||||
checksum: 0,
|
||||
oem_id,
|
||||
revision: 2,
|
||||
_rsdt_addr: 0,
|
||||
length: std::mem::size_of::<Rsdp>() as u32,
|
||||
xsdt_addr,
|
||||
extended_checksum: 0,
|
||||
_reserved: [0; 3],
|
||||
};
|
||||
|
||||
rsdp.checksum = super::generate_checksum(&rsdp.as_slice()[0..19]);
|
||||
rsdp.extended_checksum = super::generate_checksum(rsdp.as_slice());
|
||||
rsdp
|
||||
}
|
||||
|
||||
pub fn len() -> usize {
|
||||
std::mem::size_of::<Rsdp>()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Rsdp;
|
||||
use vm_memory::bytes::ByteValued;
|
||||
|
||||
#[test]
|
||||
fn test_rsdp() {
|
||||
let rsdp = Rsdp::new(*b"CHYPER", 0xdead_beef);
|
||||
let sum = rsdp
|
||||
.as_slice()
|
||||
.iter()
|
||||
.fold(0u8, |acc, x| acc.wrapping_add(*x));
|
||||
assert_eq!(sum, 0);
|
||||
let sum: u8 = rsdp
|
||||
.as_slice()
|
||||
.iter()
|
||||
.fold(0u8, |acc, x| acc.wrapping_add(*x));
|
||||
assert_eq!(sum, 0);
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
// Copyright © 2019 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GenericAddress {
|
||||
pub address_space_id: u8,
|
||||
pub register_bit_width: u8,
|
||||
pub register_bit_offset: u8,
|
||||
pub access_size: u8,
|
||||
pub address: u64,
|
||||
}
|
||||
|
||||
impl GenericAddress {
|
||||
pub fn io_port_address<T>(address: u16) -> Self {
|
||||
GenericAddress {
|
||||
address_space_id: 1,
|
||||
register_bit_width: 8 * std::mem::size_of::<T>() as u8,
|
||||
register_bit_offset: 0,
|
||||
access_size: std::mem::size_of::<T>() as u8,
|
||||
address: u64::from(address),
|
||||
}
|
||||
}
|
||||
pub fn mmio_address<T>(address: u64) -> Self {
|
||||
GenericAddress {
|
||||
address_space_id: 0,
|
||||
register_bit_width: 8 * std::mem::size_of::<T>() as u8,
|
||||
register_bit_offset: 0,
|
||||
access_size: std::mem::size_of::<T>() as u8,
|
||||
address,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Sdt {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Sdt {
|
||||
pub fn new(
|
||||
signature: [u8; 4],
|
||||
length: u32,
|
||||
revision: u8,
|
||||
oem_id: [u8; 6],
|
||||
oem_table: [u8; 8],
|
||||
oem_revision: u32,
|
||||
) -> Self {
|
||||
assert!(length >= 36);
|
||||
|
||||
let mut data = Vec::with_capacity(length as usize);
|
||||
data.extend_from_slice(&signature);
|
||||
data.extend_from_slice(&length.to_le_bytes());
|
||||
data.push(revision);
|
||||
data.push(0); // checksum
|
||||
data.extend_from_slice(&oem_id);
|
||||
data.extend_from_slice(&oem_table);
|
||||
data.extend_from_slice(&oem_revision.to_le_bytes());
|
||||
data.extend_from_slice(b"CLDH");
|
||||
data.extend_from_slice(&0u32.to_le_bytes());
|
||||
assert_eq!(data.len(), 36);
|
||||
|
||||
data.resize(length as usize, 0);
|
||||
let mut sdt = Sdt { data };
|
||||
|
||||
sdt.update_checksum();
|
||||
sdt
|
||||
}
|
||||
|
||||
pub fn update_checksum(&mut self) {
|
||||
self.data[9] = 0;
|
||||
let checksum = super::generate_checksum(self.data.as_slice());
|
||||
self.data[9] = checksum
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
self.data.as_slice()
|
||||
}
|
||||
|
||||
pub fn append<T>(&mut self, value: T) {
|
||||
let orig_length = self.data.len();
|
||||
let new_length = orig_length + std::mem::size_of::<T>();
|
||||
self.data.resize(new_length, 0);
|
||||
self.write_u32(4, new_length as u32);
|
||||
self.write(orig_length, value);
|
||||
}
|
||||
|
||||
pub fn append_slice(&mut self, data: &[u8]) {
|
||||
let orig_length = self.data.len();
|
||||
let new_length = orig_length + data.len();
|
||||
self.write_u32(4, new_length as u32);
|
||||
self.data.extend_from_slice(data);
|
||||
self.update_checksum();
|
||||
}
|
||||
|
||||
/// Write a value at the given offset
|
||||
pub fn write<T>(&mut self, offset: usize, value: T) {
|
||||
assert!((offset + (std::mem::size_of::<T>() - 1)) < self.data.len());
|
||||
// SAFETY: The assertion above makes sure we don't do out of bounds write.
|
||||
unsafe {
|
||||
*(((self.data.as_mut_ptr() as usize) + offset) as *mut T) = value;
|
||||
}
|
||||
self.update_checksum();
|
||||
}
|
||||
|
||||
pub fn write_u8(&mut self, offset: usize, val: u8) {
|
||||
self.write(offset, val);
|
||||
}
|
||||
|
||||
pub fn write_u16(&mut self, offset: usize, val: u16) {
|
||||
self.write(offset, val);
|
||||
}
|
||||
|
||||
pub fn write_u32(&mut self, offset: usize, val: u32) {
|
||||
self.write(offset, val);
|
||||
}
|
||||
|
||||
pub fn write_u64(&mut self, offset: usize, val: u64) {
|
||||
self.write(offset, val);
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.data.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.data.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Sdt;
|
||||
|
||||
#[test]
|
||||
fn test_sdt() {
|
||||
let mut sdt = Sdt::new(*b"TEST", 40, 1, *b"CLOUDH", *b"TESTTEST", 1);
|
||||
let sum: u8 = sdt
|
||||
.as_slice()
|
||||
.iter()
|
||||
.fold(0u8, |acc, x| acc.wrapping_add(*x));
|
||||
assert_eq!(sum, 0);
|
||||
sdt.write_u32(36, 0x12345678);
|
||||
let sum: u8 = sdt
|
||||
.as_slice()
|
||||
.iter()
|
||||
.fold(0u8, |acc, x| acc.wrapping_add(*x));
|
||||
assert_eq!(sum, 0);
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,16 @@ default = []
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.68"
|
||||
anyhow = "1.0.69"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.139"
|
||||
linux-loader = { version = "0.8.1", features = ["elf", "bzimage", "pe"] }
|
||||
log = "0.4.17"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
thiserror = "1.0.38"
|
||||
uuid = "1.2.2"
|
||||
versionize = "0.1.9"
|
||||
thiserror = "1.0.39"
|
||||
uuid = "1.3.0"
|
||||
versionize = "0.1.10"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-bitmap"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
|
||||
@@ -191,9 +191,13 @@ fn create_cpu_nodes(
|
||||
let cpu_map_node = fdt.begin_node("cpu-map")?;
|
||||
|
||||
// Create device tree nodes with regard of above mapping.
|
||||
for cluster_idx in 0..packages {
|
||||
let cluster_name = format!("cluster{cluster_idx:x}");
|
||||
let cluster_node = fdt.begin_node(&cluster_name)?;
|
||||
for package_idx in 0..packages {
|
||||
let package_name = format!("socket{package_idx:x}");
|
||||
let package_node = fdt.begin_node(&package_name)?;
|
||||
|
||||
// Cluster is the container of cores, and it is mandatory in the CPU topology.
|
||||
// Add a default "cluster0" in each socket/package.
|
||||
let cluster_node = fdt.begin_node("cluster0")?;
|
||||
|
||||
for core_idx in 0..cores_per_package {
|
||||
let core_name = format!("core{core_idx:x}");
|
||||
@@ -202,7 +206,7 @@ fn create_cpu_nodes(
|
||||
for thread_idx in 0..threads_per_core {
|
||||
let thread_name = format!("thread{thread_idx:x}");
|
||||
let thread_node = fdt.begin_node(&thread_name)?;
|
||||
let cpu_idx = threads_per_core * cores_per_package * cluster_idx
|
||||
let cpu_idx = threads_per_core * cores_per_package * package_idx
|
||||
+ threads_per_core * core_idx
|
||||
+ thread_idx;
|
||||
fdt.property_u32("cpu", cpu_idx as u32 + FIRST_VCPU_PHANDLE)?;
|
||||
@@ -212,6 +216,7 @@ fn create_cpu_nodes(
|
||||
fdt.end_node(core_node)?;
|
||||
}
|
||||
fdt.end_node(cluster_node)?;
|
||||
fdt.end_node(package_node)?;
|
||||
}
|
||||
fdt.end_node(cpu_map_node)?;
|
||||
} else {
|
||||
@@ -255,32 +260,86 @@ fn create_memory_node(
|
||||
fdt.end_node(memory_node)?;
|
||||
}
|
||||
} else {
|
||||
let last_addr = guest_mem.last_addr().raw_value();
|
||||
if last_addr < super::layout::MEM_32BIT_RESERVED_START.raw_value() {
|
||||
// Case 1: all RAM is under the hole
|
||||
let mem_size = last_addr - super::layout::RAM_START.raw_value() + 1;
|
||||
let mem_reg_prop = [super::layout::RAM_START.raw_value(), mem_size];
|
||||
let memory_node = fdt.begin_node("memory")?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||
fdt.end_node(memory_node)?;
|
||||
} else {
|
||||
// Case 2: RAM is split by the hole
|
||||
// Region 1: RAM before the hole
|
||||
let mem_size = super::layout::MEM_32BIT_RESERVED_START.raw_value()
|
||||
- super::layout::RAM_START.raw_value();
|
||||
let mem_reg_prop = [super::layout::RAM_START.raw_value(), mem_size];
|
||||
let memory_node_name = format!("memory@{:x}", super::layout::RAM_START.raw_value());
|
||||
// Note: memory regions from "GuestMemory" are sorted and non-zero sized.
|
||||
let ram_regions = {
|
||||
let mut ram_regions = Vec::new();
|
||||
let mut current_start = guest_mem
|
||||
.iter()
|
||||
.next()
|
||||
.map(GuestMemoryRegion::start_addr)
|
||||
.expect("GuestMemory must have one memory region at least")
|
||||
.raw_value();
|
||||
let mut current_end = current_start;
|
||||
|
||||
for (start, size) in guest_mem
|
||||
.iter()
|
||||
.map(|m| (m.start_addr().raw_value(), m.len()))
|
||||
{
|
||||
if current_end == start {
|
||||
// This zone is continuous with the previous one.
|
||||
current_end += size;
|
||||
} else {
|
||||
ram_regions.push((current_start, current_end));
|
||||
|
||||
current_start = start;
|
||||
current_end = start + size;
|
||||
}
|
||||
}
|
||||
|
||||
ram_regions.push((current_start, current_end));
|
||||
|
||||
ram_regions
|
||||
};
|
||||
|
||||
if ram_regions.len() > 2 {
|
||||
panic!(
|
||||
"There should be up to two non-continuous regions, devidided by the
|
||||
gap at the end of 32bit address space."
|
||||
);
|
||||
}
|
||||
|
||||
// Create the memory node for memory region before the gap
|
||||
{
|
||||
let (first_region_start, first_region_end) = ram_regions
|
||||
.first()
|
||||
.expect("There should be at last one memory region");
|
||||
let ram_start = super::layout::RAM_START.raw_value();
|
||||
let mem_32bit_reserved_start = super::layout::MEM_32BIT_RESERVED_START.raw_value();
|
||||
|
||||
if !((first_region_start <= &ram_start)
|
||||
&& (first_region_end > &ram_start)
|
||||
&& (first_region_end <= &mem_32bit_reserved_start))
|
||||
{
|
||||
panic!(
|
||||
"Unexpected first memory region layout: (start: 0x{:08x}, end: 0x{:08x}).
|
||||
ram_start: 0x{:08x}, mem_32bit_reserved_start: 0x{:08x}",
|
||||
first_region_start, first_region_end, ram_start, mem_32bit_reserved_start
|
||||
);
|
||||
}
|
||||
|
||||
let mem_size = first_region_end - ram_start;
|
||||
let mem_reg_prop = [ram_start, mem_size];
|
||||
let memory_node_name = format!("memory@{:x}", ram_start);
|
||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||
fdt.end_node(memory_node)?;
|
||||
}
|
||||
|
||||
// Region 2: RAM after the hole
|
||||
let mem_size = last_addr - super::layout::RAM_64BIT_START.raw_value() + 1;
|
||||
let mem_reg_prop = [super::layout::RAM_64BIT_START.raw_value(), mem_size];
|
||||
let memory_node_name =
|
||||
format!("memory@{:x}", super::layout::RAM_64BIT_START.raw_value());
|
||||
// Create the memory map entry for memory region after the gap if any
|
||||
if let Some((second_region_start, second_region_end)) = ram_regions.get(1) {
|
||||
let ram_64bit_start = super::layout::RAM_64BIT_START.raw_value();
|
||||
|
||||
if second_region_start != &ram_64bit_start {
|
||||
panic!(
|
||||
"Unexpected second memory region layout: start: 0x{:08x}, ram_64bit_start: 0x{:08x}",
|
||||
second_region_start, ram_64bit_start
|
||||
);
|
||||
}
|
||||
|
||||
let mem_size = second_region_end - ram_64bit_start;
|
||||
let mem_reg_prop = [ram_64bit_start, mem_size];
|
||||
let memory_node_name = format!("memory@{:x}", ram_64bit_start);
|
||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||
|
||||
@@ -19,7 +19,9 @@ use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic, GuestUsize};
|
||||
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic};
|
||||
|
||||
pub const _NSIG: i32 = 65;
|
||||
|
||||
/// Errors thrown while configuring aarch64 system.
|
||||
#[derive(Debug)]
|
||||
@@ -81,8 +83,8 @@ pub fn configure_vcpu(
|
||||
Ok(mpidr)
|
||||
}
|
||||
|
||||
pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> {
|
||||
let mut regions = vec![
|
||||
pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> {
|
||||
vec![
|
||||
// 0 MiB ~ 256 MiB: UEFI, GIC and legacy devices
|
||||
(
|
||||
GuestAddress(0),
|
||||
@@ -101,39 +103,21 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
|
||||
layout::PCI_MMCONFIG_SIZE as usize,
|
||||
RegionType::Reserved,
|
||||
),
|
||||
];
|
||||
|
||||
let ram_32bit_space_size =
|
||||
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START);
|
||||
|
||||
// RAM space
|
||||
// Case1: guest memory fits before the gap
|
||||
if size <= ram_32bit_space_size {
|
||||
regions.push((layout::RAM_START, size as usize, RegionType::Ram));
|
||||
// Case2: guest memory extends beyond the gap
|
||||
} else {
|
||||
// Push memory before the gap
|
||||
regions.push((
|
||||
// 1GiB ~ 4032 MiB: RAM before the gap
|
||||
(
|
||||
layout::RAM_START,
|
||||
ram_32bit_space_size as usize,
|
||||
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START) as usize,
|
||||
RegionType::Ram,
|
||||
));
|
||||
// Other memory is placed after 4GiB
|
||||
regions.push((
|
||||
layout::RAM_64BIT_START,
|
||||
(size - ram_32bit_space_size) as usize,
|
||||
RegionType::Ram,
|
||||
));
|
||||
}
|
||||
|
||||
// Add the 32-bit reserved memory hole as a reserved region
|
||||
regions.push((
|
||||
layout::MEM_32BIT_RESERVED_START,
|
||||
layout::MEM_32BIT_RESERVED_SIZE as usize,
|
||||
RegionType::Reserved,
|
||||
));
|
||||
|
||||
regions
|
||||
),
|
||||
// 4GiB ~ inf: RAM after the gap
|
||||
(layout::RAM_64BIT_START, usize::MAX, RegionType::Ram),
|
||||
// Add the 32-bit reserved memory hole as a reserved region
|
||||
(
|
||||
layout::MEM_32BIT_RESERVED_START,
|
||||
layout::MEM_32BIT_RESERVED_SIZE as usize,
|
||||
RegionType::Reserved,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
/// Configures the system and should be called once per vm before starting vcpu threads.
|
||||
@@ -215,26 +199,12 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_arch_memory_regions_dram_2gb() {
|
||||
let regions = arch_memory_regions((1usize << 31) as u64); //2GB
|
||||
assert_eq!(5, regions.len());
|
||||
assert_eq!(layout::RAM_START, regions[3].0);
|
||||
assert_eq!((1usize << 31), regions[3].1);
|
||||
assert_eq!(RegionType::Ram, regions[3].2);
|
||||
assert_eq!(RegionType::Reserved, regions[4].2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arch_memory_regions_dram_4gb() {
|
||||
let regions = arch_memory_regions((1usize << 32) as u64); //4GB
|
||||
let ram_32bit_space_size =
|
||||
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START) as usize;
|
||||
fn test_arch_memory_regions_dram() {
|
||||
let regions = arch_memory_regions();
|
||||
assert_eq!(6, regions.len());
|
||||
assert_eq!(layout::RAM_START, regions[3].0);
|
||||
assert_eq!(ram_32bit_space_size, regions[3].1);
|
||||
assert_eq!(RegionType::Ram, regions[3].2);
|
||||
assert_eq!(RegionType::Reserved, regions[5].2);
|
||||
assert_eq!(RegionType::Ram, regions[4].2);
|
||||
assert_eq!(((1usize << 32) - ram_32bit_space_size), regions[4].1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
///
|
||||
/// AArch64 system register encoding:
|
||||
/// See https://developer.arm.com/documentation/ddi0487 (chapter D12)
|
||||
///
|
||||
/// 31 22 21 20 19 18 16 15 12 11 8 7 5 4 0
|
||||
/// +----------+---+-----+-----+-----+-----+-----+----+
|
||||
/// |1101010100| L | op0 | op1 | CRn | CRm | op2 | Rt |
|
||||
/// +----------+---+-----+-----+-----+-----+-----+----+
|
||||
///
|
||||
/// Notes:
|
||||
/// - L and Rt are reserved as implementation defined fields, ignored.
|
||||
///
|
||||
// AArch64 system register encoding:
|
||||
// See https://developer.arm.com/documentation/ddi0487 (chapter D12)
|
||||
//
|
||||
// 31 22 21 20 19 18 16 15 12 11 8 7 5 4 0
|
||||
// +----------+---+-----+-----+-----+-----+-----+----+
|
||||
// |1101010100| L | op0 | op1 | CRn | CRm | op2 | Rt |
|
||||
// +----------+---+-----+-----+-----+-----+-----+----+
|
||||
//
|
||||
// Notes:
|
||||
// - L and Rt are reserved as implementation defined fields, ignored.
|
||||
|
||||
const SYSREG_HEAD: u32 = 0b1101010100u32 << 22;
|
||||
const SYSREG_OP0_SHIFT: u32 = 19;
|
||||
const SYSREG_OP0_MASK: u32 = 0b11u32 << 19;
|
||||
|
||||
@@ -82,7 +82,7 @@ pub mod aarch64;
|
||||
pub use aarch64::{
|
||||
arch_memory_regions, configure_system, configure_vcpu, fdt::DeviceInfoForFdt,
|
||||
get_host_cpu_phys_bits, initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE,
|
||||
layout::IRQ_BASE, uefi, EntryPoint,
|
||||
layout::IRQ_BASE, uefi, EntryPoint, _NSIG,
|
||||
};
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -92,7 +92,7 @@ pub mod x86_64;
|
||||
pub use x86_64::{
|
||||
arch_memory_regions, configure_system, configure_vcpu, generate_common_cpuid,
|
||||
get_host_cpu_phys_bits, initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE,
|
||||
layout::CMDLINE_START, regs, CpuidFeatureEntry, EntryPoint,
|
||||
layout::CMDLINE_START, regs, CpuidFeatureEntry, EntryPoint, _NSIG,
|
||||
};
|
||||
|
||||
/// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::GuestMemoryMmap;
|
||||
use crate::InitramfsConfig;
|
||||
use crate::RegionType;
|
||||
use hypervisor::arch::x86::{CpuIdEntry, CPUID_FLAG_VALID_INDEX};
|
||||
use hypervisor::HypervisorError;
|
||||
use hypervisor::{HypervisorCpuError, HypervisorError};
|
||||
use linux_loader::loader::bootparam::boot_params;
|
||||
use linux_loader::loader::elf::start_info::{
|
||||
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
|
||||
@@ -36,6 +36,7 @@ pub mod tdx;
|
||||
const TSC_DEADLINE_TIMER_ECX_BIT: u8 = 24; // tsc deadline timer ecx bit.
|
||||
const HYPERVISOR_ECX_BIT: u8 = 31; // Hypervisor ecx bit.
|
||||
const MTRR_EDX_BIT: u8 = 12; // Hypervisor ecx bit.
|
||||
const INVARIANT_TSC_EDX_BIT: u8 = 8; // Invariant TSC bit on 0x8000_0007 EDX
|
||||
|
||||
// KVM feature bits
|
||||
const KVM_FEATURE_ASYNC_PF_INT_BIT: u8 = 14;
|
||||
@@ -52,6 +53,8 @@ const KVM_FEATURE_ASYNC_PF_VMEXIT_BIT: u8 = 10;
|
||||
#[cfg(feature = "tdx")]
|
||||
const KVM_FEATURE_STEAL_TIME_BIT: u8 = 5;
|
||||
|
||||
pub const _NSIG: i32 = 65;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
/// Specifies the entry point address where the guest must start
|
||||
/// executing code, as well as which of the supported boot protocols
|
||||
@@ -193,6 +196,9 @@ pub enum Error {
|
||||
// Error writing EBDA address
|
||||
EbdaSetup(vm_memory::GuestMemoryError),
|
||||
|
||||
// Error getting CPU TSC frequency
|
||||
GetTscFrequency(HypervisorCpuError),
|
||||
|
||||
/// Error retrieving TDX capabilities through the hypervisor (kvm/mshv) API
|
||||
#[cfg(feature = "tdx")]
|
||||
TdxCapabilities(HypervisorError),
|
||||
@@ -605,7 +611,9 @@ pub fn generate_common_cpuid(
|
||||
];
|
||||
|
||||
// Supported CPUID
|
||||
let mut cpuid = hypervisor.get_cpuid().map_err(Error::CpuidGetSupported)?;
|
||||
let mut cpuid = hypervisor
|
||||
.get_supported_cpuid()
|
||||
.map_err(Error::CpuidGetSupported)?;
|
||||
|
||||
CpuidPatch::patch_cpuid(&mut cpuid, cpuid_patches);
|
||||
|
||||
@@ -768,6 +776,41 @@ pub fn configure_vcpu(
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id));
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1f, None, CpuidReg::EDX, u32::from(id));
|
||||
|
||||
// Set ApicId in cpuid for each vcpu
|
||||
// SAFETY: get host cpuid when eax=1
|
||||
let mut cpu_ebx = unsafe { core::arch::x86_64::__cpuid(1) }.ebx;
|
||||
cpu_ebx &= 0xffffff;
|
||||
cpu_ebx |= (id as u32) << 24;
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1, None, CpuidReg::EBX, cpu_ebx);
|
||||
|
||||
// The TSC frequency CPUID leaf should not be included when running with HyperV emulation
|
||||
if !kvm_hyperv {
|
||||
if let Some(tsc_khz) = vcpu.tsc_khz().map_err(Error::GetTscFrequency)? {
|
||||
// Need to check that the TSC doesn't vary with dynamic frequency
|
||||
// SAFETY: cpuid called with valid leaves
|
||||
if unsafe { std::arch::x86_64::__cpuid(0x8000_0007) }.edx
|
||||
& (1u32 << INVARIANT_TSC_EDX_BIT)
|
||||
> 0
|
||||
{
|
||||
CpuidPatch::set_cpuid_reg(
|
||||
&mut cpuid,
|
||||
0x4000_0000,
|
||||
None,
|
||||
CpuidReg::EAX,
|
||||
0x4000_0010,
|
||||
);
|
||||
cpuid.retain(|c| c.function != 0x4000_0010);
|
||||
cpuid.push(CpuIdEntry {
|
||||
function: 0x4000_0010,
|
||||
eax: tsc_khz,
|
||||
ebx: 1000000, /* LAPIC resolution of 1ns (freq: 1GHz) is hardcoded in KVM's
|
||||
* APIC_BUS_CYCLE_NS */
|
||||
..Default::default()
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
vcpu.set_cpuid2(&cpuid)
|
||||
.map_err(|e| Error::SetSupportedCpusFailed(e.into()))?;
|
||||
|
||||
@@ -792,47 +835,29 @@ pub fn configure_vcpu(
|
||||
/// These should be used to configure the GuestMemory structure for the platform.
|
||||
/// For x86_64 all addresses are valid from the start of the kernel except a
|
||||
/// carve out at the end of 32bit address space.
|
||||
pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> {
|
||||
let reserved_memory_gap_start = layout::MEM_32BIT_RESERVED_START
|
||||
.checked_add(layout::MEM_32BIT_DEVICES_SIZE)
|
||||
.expect("32-bit reserved region is too large");
|
||||
|
||||
let requested_memory_size = GuestAddress(size);
|
||||
let mut regions = Vec::new();
|
||||
|
||||
// case1: guest memory fits before the gap
|
||||
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 {
|
||||
// push memory before the gap
|
||||
regions.push((
|
||||
pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> {
|
||||
vec![
|
||||
// 0 GiB ~ 3GiB: memory before the gap
|
||||
(
|
||||
GuestAddress(0),
|
||||
layout::MEM_32BIT_RESERVED_START.raw_value() as usize,
|
||||
RegionType::Ram,
|
||||
));
|
||||
regions.push((
|
||||
layout::RAM_64BIT_START,
|
||||
requested_memory_size.unchecked_offset_from(layout::MEM_32BIT_RESERVED_START) as usize,
|
||||
RegionType::Ram,
|
||||
));
|
||||
}
|
||||
|
||||
// Add the 32-bit device memory hole as a sub region.
|
||||
regions.push((
|
||||
layout::MEM_32BIT_RESERVED_START,
|
||||
layout::MEM_32BIT_DEVICES_SIZE as usize,
|
||||
RegionType::SubRegion,
|
||||
));
|
||||
|
||||
// Add the 32-bit reserved memory hole as a sub region.
|
||||
regions.push((
|
||||
reserved_memory_gap_start,
|
||||
(layout::MEM_32BIT_RESERVED_SIZE - layout::MEM_32BIT_DEVICES_SIZE) as usize,
|
||||
RegionType::Reserved,
|
||||
));
|
||||
|
||||
regions
|
||||
),
|
||||
// 4 GiB ~ inf: memory after the gap
|
||||
(layout::RAM_64BIT_START, usize::MAX, RegionType::Ram),
|
||||
// 3 GiB ~ 3712 MiB: 32-bit device memory hole
|
||||
(
|
||||
layout::MEM_32BIT_RESERVED_START,
|
||||
layout::MEM_32BIT_DEVICES_SIZE as usize,
|
||||
RegionType::SubRegion,
|
||||
),
|
||||
// 3712 MiB ~ 3968 MiB: 32-bit reserved memory hole
|
||||
(
|
||||
layout::MEM_32BIT_RESERVED_START.unchecked_add(layout::MEM_32BIT_DEVICES_SIZE),
|
||||
(layout::MEM_32BIT_RESERVED_SIZE - layout::MEM_32BIT_DEVICES_SIZE) as usize,
|
||||
RegionType::Reserved,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
/// Configures the system and should be called once per vm before starting vcpu threads.
|
||||
@@ -930,30 +955,102 @@ fn configure_pvh(
|
||||
// Create the memory map entries.
|
||||
add_memmap_entry(&mut memmap, 0, layout::EBDA_START.raw_value(), E820_RAM);
|
||||
|
||||
let mem_end = guest_mem.last_addr();
|
||||
// Merge continuous memory regions into one region.
|
||||
// Note: memory regions from "GuestMemory" are sorted and non-zero sized.
|
||||
let ram_regions = {
|
||||
let mut ram_regions = Vec::new();
|
||||
let mut current_start = guest_mem
|
||||
.iter()
|
||||
.next()
|
||||
.map(GuestMemoryRegion::start_addr)
|
||||
.expect("GuestMemory must have one memory region at least")
|
||||
.raw_value();
|
||||
let mut current_end = current_start;
|
||||
|
||||
if mem_end < layout::MEM_32BIT_RESERVED_START {
|
||||
add_memmap_entry(
|
||||
&mut memmap,
|
||||
layout::HIGH_RAM_START.raw_value(),
|
||||
mem_end.unchecked_offset_from(layout::HIGH_RAM_START) + 1,
|
||||
E820_RAM,
|
||||
);
|
||||
} else {
|
||||
add_memmap_entry(
|
||||
&mut memmap,
|
||||
layout::HIGH_RAM_START.raw_value(),
|
||||
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::HIGH_RAM_START),
|
||||
E820_RAM,
|
||||
);
|
||||
if mem_end > layout::RAM_64BIT_START {
|
||||
add_memmap_entry(
|
||||
&mut memmap,
|
||||
layout::RAM_64BIT_START.raw_value(),
|
||||
mem_end.unchecked_offset_from(layout::RAM_64BIT_START) + 1,
|
||||
E820_RAM,
|
||||
);
|
||||
for (start, size) in guest_mem
|
||||
.iter()
|
||||
.map(|m| (m.start_addr().raw_value(), m.len()))
|
||||
{
|
||||
if current_end == start {
|
||||
// This zone is continuous with the previous one.
|
||||
current_end += size;
|
||||
} else {
|
||||
ram_regions.push((current_start, current_end));
|
||||
|
||||
current_start = start;
|
||||
current_end = start + size;
|
||||
}
|
||||
}
|
||||
|
||||
ram_regions.push((current_start, current_end));
|
||||
|
||||
ram_regions
|
||||
};
|
||||
|
||||
if ram_regions.len() > 2 {
|
||||
error!(
|
||||
"There should be up to two non-continuous regions, devidided by the
|
||||
gap at the end of 32bit address space (e.g. between 3G and 4G)."
|
||||
);
|
||||
return Err(super::Error::MemmapTableSetup);
|
||||
}
|
||||
|
||||
// Create the memory map entry for memory region before the gap
|
||||
{
|
||||
let (first_region_start, first_region_end) =
|
||||
ram_regions.first().ok_or(super::Error::MemmapTableSetup)?;
|
||||
let high_ram_start = layout::HIGH_RAM_START.raw_value();
|
||||
let mem_32bit_reserved_start = layout::MEM_32BIT_RESERVED_START.raw_value();
|
||||
|
||||
if !((first_region_start <= &high_ram_start)
|
||||
&& (first_region_end > &high_ram_start)
|
||||
&& (first_region_end <= &mem_32bit_reserved_start))
|
||||
{
|
||||
error!(
|
||||
"Unexpected first memory region layout: (start: 0x{:08x}, end: 0x{:08x}).
|
||||
high_ram_start: 0x{:08x}, mem_32bit_reserved_start: 0x{:08x}",
|
||||
first_region_start, first_region_end, high_ram_start, mem_32bit_reserved_start
|
||||
);
|
||||
|
||||
return Err(super::Error::MemmapTableSetup);
|
||||
}
|
||||
|
||||
info!(
|
||||
"create_memmap_entry, start: 0x{:08x}, end: 0x{:08x})",
|
||||
high_ram_start, first_region_end
|
||||
);
|
||||
|
||||
add_memmap_entry(
|
||||
&mut memmap,
|
||||
high_ram_start,
|
||||
first_region_end - high_ram_start,
|
||||
E820_RAM,
|
||||
);
|
||||
}
|
||||
|
||||
// Create the memory map entry for memory region after the gap if any
|
||||
if let Some((second_region_start, second_region_end)) = ram_regions.get(1) {
|
||||
let ram_64bit_start = layout::RAM_64BIT_START.raw_value();
|
||||
|
||||
if second_region_start != &ram_64bit_start {
|
||||
error!(
|
||||
"Unexpected second memory region layout: start: 0x{:08x}, ram_64bit_start: 0x{:08x}",
|
||||
second_region_start, ram_64bit_start
|
||||
);
|
||||
|
||||
return Err(super::Error::MemmapTableSetup);
|
||||
}
|
||||
|
||||
info!(
|
||||
"create_memmap_entry, start: 0x{:08x}, end: 0x{:08x})",
|
||||
ram_64bit_start, second_region_end
|
||||
);
|
||||
add_memmap_entry(
|
||||
&mut memmap,
|
||||
ram_64bit_start,
|
||||
second_region_end - ram_64bit_start,
|
||||
E820_RAM,
|
||||
);
|
||||
}
|
||||
|
||||
add_memmap_entry(
|
||||
@@ -1189,16 +1286,8 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn regions_lt_4gb() {
|
||||
let regions = arch_memory_regions(1 << 29);
|
||||
assert_eq!(3, regions.len());
|
||||
assert_eq!(GuestAddress(0), regions[0].0);
|
||||
assert_eq!(1usize << 29, regions[0].1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regions_gt_4gb() {
|
||||
let regions = arch_memory_regions((1 << 32) + 0x8000);
|
||||
fn regions_base_addr() {
|
||||
let regions = arch_memory_regions();
|
||||
assert_eq!(4, regions.len());
|
||||
assert_eq!(GuestAddress(0), regions[0].0);
|
||||
assert_eq!(GuestAddress(1 << 32), regions[1].0);
|
||||
@@ -1222,11 +1311,10 @@ mod tests {
|
||||
assert!(config_err.is_err());
|
||||
|
||||
// Now assigning some memory that falls before the 32bit memory hole.
|
||||
let mem_size = 128 << 20;
|
||||
let arch_mem_regions = arch_memory_regions(mem_size);
|
||||
let arch_mem_regions = arch_memory_regions();
|
||||
let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions
|
||||
.iter()
|
||||
.filter(|r| r.2 == RegionType::Ram)
|
||||
.filter(|r| r.2 == RegionType::Ram && r.1 != usize::MAX)
|
||||
.map(|r| (r.0, r.1))
|
||||
.collect();
|
||||
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
||||
@@ -1244,48 +1332,18 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Now assigning some memory that is equal to the start of the 32bit memory hole.
|
||||
let mem_size = 3328 << 20;
|
||||
let arch_mem_regions = arch_memory_regions(mem_size);
|
||||
let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions
|
||||
.iter()
|
||||
.filter(|r| r.2 == RegionType::Ram)
|
||||
.map(|r| (r.0, r.1))
|
||||
.collect();
|
||||
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
||||
configure_system(
|
||||
&gm,
|
||||
GuestAddress(0),
|
||||
&None,
|
||||
no_vcpus,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
configure_system(
|
||||
&gm,
|
||||
GuestAddress(0),
|
||||
&None,
|
||||
no_vcpus,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Now assigning some memory that falls after the 32bit memory hole.
|
||||
let mem_size = 3330 << 20;
|
||||
let arch_mem_regions = arch_memory_regions(mem_size);
|
||||
let arch_mem_regions = arch_memory_regions();
|
||||
let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions
|
||||
.iter()
|
||||
.filter(|r| r.2 == RegionType::Ram)
|
||||
.map(|r| (r.0, r.1))
|
||||
.map(|r| {
|
||||
if r.1 == usize::MAX {
|
||||
(r.0, 128 << 20)
|
||||
} else {
|
||||
(r.0, r.1)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
||||
configure_system(
|
||||
|
||||
@@ -55,7 +55,7 @@ pub struct TdvfSection {
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub enum TdvfSectionType {
|
||||
Bfv,
|
||||
Cfv,
|
||||
@@ -64,15 +64,10 @@ pub enum TdvfSectionType {
|
||||
PermMem,
|
||||
Payload,
|
||||
PayloadParam,
|
||||
#[default]
|
||||
Reserved = 0xffffffff,
|
||||
}
|
||||
|
||||
impl Default for TdvfSectionType {
|
||||
fn default() -> Self {
|
||||
TdvfSectionType::Reserved
|
||||
}
|
||||
}
|
||||
|
||||
fn tdvf_descriptor_offset(file: &mut File) -> Result<(SeekFrom, bool), TdvfError> {
|
||||
// Let's first try to identify the presence of the table footer GUID
|
||||
file.seek(SeekFrom::End(-0x30))
|
||||
@@ -202,21 +197,16 @@ pub fn parse_tdvf_sections(file: &mut File) -> Result<(Vec<TdvfSection>, bool),
|
||||
}
|
||||
|
||||
#[repr(u16)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
enum HobType {
|
||||
Handoff = 0x1,
|
||||
ResourceDescriptor = 0x3,
|
||||
GuidExtension = 0x4,
|
||||
#[default]
|
||||
Unused = 0xfffe,
|
||||
EndOfHobList = 0xffff,
|
||||
}
|
||||
|
||||
impl Default for HobType {
|
||||
fn default() -> Self {
|
||||
HobType::Unused
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
struct HobHeader {
|
||||
@@ -266,19 +256,14 @@ struct HobGuidType {
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub enum PayloadImageType {
|
||||
#[default]
|
||||
ExecutablePayload,
|
||||
BzImage,
|
||||
RawVmLinux,
|
||||
}
|
||||
|
||||
impl Default for PayloadImageType {
|
||||
fn default() -> Self {
|
||||
PayloadImageType::ExecutablePayload
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
pub struct PayloadInfo {
|
||||
|
||||
@@ -8,17 +8,17 @@ edition = "2021"
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
io-uring = "0.5.11"
|
||||
io-uring = "0.5.13"
|
||||
libc = "0.2.139"
|
||||
log = "0.4.17"
|
||||
qcow = { path = "../qcow" }
|
||||
smallvec = "1.10.0"
|
||||
thiserror = "1.0.38"
|
||||
versionize = "0.1.9"
|
||||
thiserror = "1.0.39"
|
||||
versionize = "0.1.10"
|
||||
versionize_derive = "0.1.4"
|
||||
vhdx = { path = "../vhdx" }
|
||||
virtio-bindings = { version = "0.1.0", features = ["virtio-v5_0_0"] }
|
||||
virtio-queue = "0.7.0"
|
||||
virtio-bindings = { version = "0.2.0", features = ["virtio-v5_0_0"] }
|
||||
virtio-queue = "0.7.1"
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
@@ -38,7 +38,7 @@ use std::time::Instant;
|
||||
use thiserror::Error;
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use virtio_bindings::bindings::virtio_blk::*;
|
||||
use virtio_bindings::virtio_blk::*;
|
||||
use virtio_queue::DescriptorChain;
|
||||
use vm_memory::{
|
||||
bitmap::AtomicBitmap, bitmap::Bitmap, ByteValued, Bytes, GuestAddress, GuestMemory,
|
||||
@@ -201,7 +201,7 @@ pub struct Request {
|
||||
pub data_descriptors: SmallVec<[(GuestAddress, u32); 1]>,
|
||||
pub status_addr: GuestAddress,
|
||||
pub writeback: bool,
|
||||
pub aligned_operations: Vec<AlignedOperation>,
|
||||
pub aligned_operations: SmallVec<[AlignedOperation; 1]>,
|
||||
pub start: Instant,
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ impl Request {
|
||||
data_descriptors: SmallVec::with_capacity(1),
|
||||
status_addr: GuestAddress(0),
|
||||
writeback: true,
|
||||
aligned_operations: Vec::new(),
|
||||
aligned_operations: SmallVec::with_capacity(1),
|
||||
start: Instant::now(),
|
||||
};
|
||||
|
||||
@@ -602,7 +602,7 @@ where
|
||||
completion_list: &mut VecDeque<(u64, i32)>,
|
||||
) -> AsyncIoResult<()> {
|
||||
// Convert libc::iovec into IoSliceMut
|
||||
let mut slices = Vec::new();
|
||||
let mut slices: SmallVec<[IoSliceMut; 1]> = SmallVec::with_capacity(iovecs.len());
|
||||
for iovec in iovecs.iter() {
|
||||
// SAFETY: on Linux IoSliceMut wraps around libc::iovec
|
||||
slices.push(IoSliceMut::new(unsafe { std::mem::transmute(*iovec) }));
|
||||
@@ -635,9 +635,9 @@ where
|
||||
completion_list: &mut VecDeque<(u64, i32)>,
|
||||
) -> AsyncIoResult<()> {
|
||||
// Convert libc::iovec into IoSlice
|
||||
let mut slices = Vec::new();
|
||||
let mut slices: SmallVec<[IoSlice; 1]> = SmallVec::with_capacity(iovecs.len());
|
||||
for iovec in iovecs.iter() {
|
||||
// SAFETY: on Linux IoSliceMut wraps around libc::iovec
|
||||
// SAFETY: on Linux IoSlice wraps around libc::iovec
|
||||
slices.push(IoSlice::new(unsafe { std::mem::transmute(*iovec) }));
|
||||
}
|
||||
|
||||
|
||||
@@ -5,18 +5,17 @@ authors = ["The Chromium OS Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { path = "../acpi_tables" }
|
||||
anyhow = "1.0.68"
|
||||
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
|
||||
anyhow = "1.0.69"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "1.3.2"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.139"
|
||||
log = "0.4.17"
|
||||
phf = { version = "0.11.1", features = ["macros"] }
|
||||
thiserror = "1.0.38"
|
||||
thiserror = "1.0.39"
|
||||
tpm = { path = "../tpm" }
|
||||
versionize = "0.1.9"
|
||||
versionize = "0.1.10"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = "0.10.0"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//
|
||||
|
||||
use super::AcpiNotificationFlags;
|
||||
use acpi_tables::{aml, aml::Aml};
|
||||
use acpi_tables::{aml, Aml, AmlSink};
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::time::Instant;
|
||||
use vm_device::interrupt::InterruptSourceGroup;
|
||||
@@ -103,11 +103,11 @@ impl BusDevice for AcpiGedDevice {
|
||||
}
|
||||
|
||||
impl Aml for AcpiGedDevice {
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
fn to_aml_bytes(&self, sink: &mut dyn AmlSink) {
|
||||
aml::Device::new(
|
||||
"_SB_.GEC_".into(),
|
||||
vec![
|
||||
&aml::Name::new("_HID".into(), &aml::EisaName::new("PNP0A06")),
|
||||
&aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0A06")),
|
||||
&aml::Name::new("_UID".into(), &"Generic Event Controller"),
|
||||
&aml::Name::new(
|
||||
"_CRS".into(),
|
||||
@@ -121,12 +121,13 @@ impl Aml for AcpiGedDevice {
|
||||
&aml::OpRegion::new(
|
||||
"GDST".into(),
|
||||
aml::OpRegionSpace::SystemMemory,
|
||||
self.address.0 as usize,
|
||||
GED_DEVICE_ACPI_SIZE,
|
||||
&(self.address.0 as usize),
|
||||
&GED_DEVICE_ACPI_SIZE,
|
||||
),
|
||||
&aml::Field::new(
|
||||
"GDST".into(),
|
||||
aml::FieldAccessType::Byte,
|
||||
aml::FieldLockRule::NoLock,
|
||||
aml::FieldUpdateRule::WriteAsZeroes,
|
||||
vec![aml::FieldEntry::Named(*b"GDAT", 8)],
|
||||
),
|
||||
@@ -163,7 +164,7 @@ impl Aml for AcpiGedDevice {
|
||||
),
|
||||
],
|
||||
)
|
||||
.append_aml_bytes(bytes);
|
||||
.to_aml_bytes(sink);
|
||||
aml::Device::new(
|
||||
"_SB_.GED_".into(),
|
||||
vec![
|
||||
@@ -187,7 +188,7 @@ impl Aml for AcpiGedDevice {
|
||||
),
|
||||
],
|
||||
)
|
||||
.append_aml_bytes(bytes)
|
||||
.to_aml_bytes(sink)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,13 +8,11 @@ use anyhow::anyhow;
|
||||
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)]
|
||||
@@ -23,62 +21,135 @@ pub enum Error {
|
||||
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>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum LocStateFields {
|
||||
TpmEstablished,
|
||||
LocAssigned,
|
||||
ActiveLocality,
|
||||
Reserved,
|
||||
TpmRegValidSts,
|
||||
}
|
||||
|
||||
enum LocStsFields {
|
||||
Granted,
|
||||
BeenSeized,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum IntfIdFields {
|
||||
InterfaceType,
|
||||
InterfaceVersion,
|
||||
CapLocality,
|
||||
CapCRBIdleBypass,
|
||||
Reserved1,
|
||||
CapDataXferSizeSupport,
|
||||
CapFIFO,
|
||||
CapCRB,
|
||||
CapIFRes,
|
||||
InterfaceSelector,
|
||||
IntfSelLock,
|
||||
Reserved2,
|
||||
Rid,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum IntfId2Fields {
|
||||
Vid,
|
||||
Did,
|
||||
}
|
||||
|
||||
enum CtrlStsFields {
|
||||
TpmSts,
|
||||
TpmIdle,
|
||||
}
|
||||
|
||||
enum CrbRegister {
|
||||
LocState(LocStateFields),
|
||||
LocSts(LocStsFields),
|
||||
IntfId(IntfIdFields),
|
||||
IntfId2(IntfId2Fields),
|
||||
CtrlSts(CtrlStsFields),
|
||||
}
|
||||
|
||||
/* 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
|
||||
// Field => (base, offset, length)
|
||||
// base: starting position of the register
|
||||
// offset: 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 fn get_crb_loc_state_field(f: LocStateFields) -> (u32, u32, u32) {
|
||||
let (offset, len) = match f {
|
||||
LocStateFields::TpmEstablished => (0, 1),
|
||||
LocStateFields::LocAssigned => (1, 1),
|
||||
LocStateFields::ActiveLocality => (2, 3),
|
||||
LocStateFields::Reserved => (5, 2),
|
||||
LocStateFields::TpmRegValidSts => (7, 1),
|
||||
};
|
||||
|
||||
(CRB_LOC_STATE, offset, len)
|
||||
}
|
||||
|
||||
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 fn get_crb_loc_sts_field(f: LocStsFields) -> (u32, u32, u32) {
|
||||
let (offset, len) = match f {
|
||||
LocStsFields::Granted => (0, 1),
|
||||
LocStsFields::BeenSeized => (1, 1),
|
||||
};
|
||||
|
||||
(CRB_LOC_STS, offset, len)
|
||||
}
|
||||
|
||||
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 fn get_crb_intf_id_field(f: IntfIdFields) -> (u32, u32, u32) {
|
||||
let (offset, len) = match f {
|
||||
IntfIdFields::InterfaceType => (0, 4),
|
||||
IntfIdFields::InterfaceVersion => (4, 4),
|
||||
IntfIdFields::CapLocality => (8, 1),
|
||||
IntfIdFields::CapCRBIdleBypass => (9, 1),
|
||||
IntfIdFields::Reserved1 => (10, 1),
|
||||
IntfIdFields::CapDataXferSizeSupport => (11, 2),
|
||||
IntfIdFields::CapFIFO => (13, 1),
|
||||
IntfIdFields::CapCRB => (14, 1),
|
||||
IntfIdFields::CapIFRes => (15, 2),
|
||||
IntfIdFields::InterfaceSelector => (17, 2),
|
||||
IntfIdFields::IntfSelLock => (19, 1),
|
||||
IntfIdFields::Reserved2 => (20, 4),
|
||||
IntfIdFields::Rid => (24, 8),
|
||||
};
|
||||
|
||||
(CRB_INTF_ID, offset, len)
|
||||
}
|
||||
|
||||
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 fn get_crb_intf_id2_field(f: IntfId2Fields) -> (u32, u32, u32) {
|
||||
let (offset, len) = match f {
|
||||
IntfId2Fields::Vid => (0, 16),
|
||||
IntfId2Fields::Did => (16, 16),
|
||||
};
|
||||
|
||||
(CRB_INTF_ID2, offset, len)
|
||||
}
|
||||
|
||||
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 fn get_crb_ctrl_sts_field(f: CtrlStsFields) -> (u32, u32, u32) {
|
||||
let (offset, len) = match f {
|
||||
CtrlStsFields::TpmSts => (0, 1),
|
||||
CtrlStsFields::TpmIdle => (1, 1),
|
||||
};
|
||||
|
||||
(CRB_CTRL_STS, offset, len)
|
||||
}
|
||||
const CRB_CTRL_CANCEL: u32 = 0x48;
|
||||
const CRB_CANCEL_INVOKE: u32 = 1 << 0;
|
||||
const CRB_CTRL_START: u32 = 0x4C;
|
||||
@@ -94,7 +165,7 @@ 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;
|
||||
const TPM_CRB_R_MAX: usize = CRB_DATA_BUFFER as usize;
|
||||
|
||||
// CRB Protocol details
|
||||
const CRB_INTF_TYPE_CRB_ACTIVE: u32 = 0b1;
|
||||
@@ -109,47 +180,29 @@ 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]> {
|
||||
// Returns (register base, offset, len)
|
||||
const fn get_field(reg: CrbRegister) -> (u32, u32, u32) {
|
||||
match reg {
|
||||
CRB_LOC_STATE => CRB_LOC_STATE_FIELDS,
|
||||
CRB_LOC_STS => CRB_LOC_STS_FIELDS,
|
||||
CRB_INTF_ID => CRB_INTF_ID_FIELDS,
|
||||
CRB_INTF_ID2 => CRB_INTF_ID2_FIELDS,
|
||||
CRB_CTRL_STS => CRB_CTRL_STS_FIELDS,
|
||||
_ => {
|
||||
panic!("Fields in '{reg:?}' register were accessed which are Invalid");
|
||||
}
|
||||
CrbRegister::LocState(f) => get_crb_loc_state_field(f),
|
||||
CrbRegister::LocSts(f) => get_crb_loc_sts_field(f),
|
||||
CrbRegister::IntfId(f) => get_crb_intf_id_field(f),
|
||||
CrbRegister::IntfId2(f) => get_crb_intf_id2_field(f),
|
||||
CrbRegister::CtrlSts(f) => get_crb_ctrl_sts_field(f),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
)
|
||||
}
|
||||
// Set a particular field in a Register
|
||||
fn set_reg_field(regs: &mut [u32; TPM_CRB_R_MAX], reg: CrbRegister, value: u32) {
|
||||
let (base, offset, len) = get_field(reg);
|
||||
let mask = (!(0_u32) >> (32 - len)) << offset;
|
||||
regs[base as usize] = (regs[base as usize] & !mask) | ((value << offset) & mask);
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
// Get the value of a particular field in a Register
|
||||
const fn get_reg_field(regs: &[u32; TPM_CRB_R_MAX], reg: CrbRegister) -> u32 {
|
||||
let (base, offset, len) = get_field(reg);
|
||||
let mask = (!(0_u32) >> (32 - len)) << offset;
|
||||
(regs[base as usize] & mask) >> offset
|
||||
}
|
||||
|
||||
fn locality_from_addr(addr: u32) -> u8 {
|
||||
@@ -158,8 +211,7 @@ fn locality_from_addr(addr: u32) -> u8 {
|
||||
|
||||
pub struct Tpm {
|
||||
emulator: Emulator,
|
||||
cmd: Option<BackendCmd>,
|
||||
regs: [u32; TPM_CRB_R_MAX as usize],
|
||||
regs: [u32; TPM_CRB_R_MAX],
|
||||
backend_buff_size: usize,
|
||||
data_buff: [u8; TPM_CRB_BUFFER_MAX],
|
||||
data_buff_len: usize,
|
||||
@@ -171,8 +223,7 @@ impl Tpm {
|
||||
.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],
|
||||
regs: [0; TPM_CRB_R_MAX],
|
||||
backend_buff_size: TPM_CRB_BUFFER_MAX,
|
||||
data_buff: [0; TPM_CRB_BUFFER_MAX],
|
||||
data_buff_len: 0,
|
||||
@@ -182,74 +233,93 @@ impl Tpm {
|
||||
}
|
||||
|
||||
fn get_active_locality(&mut self) -> u32 {
|
||||
if get_reg_field(&self.regs, CRB_LOC_STATE, "locAssigned") == 0 {
|
||||
if get_reg_field(
|
||||
&self.regs,
|
||||
CrbRegister::LocState(LocStateFields::LocAssigned),
|
||||
) == 0
|
||||
{
|
||||
return TPM_CRB_NO_LOCALITY;
|
||||
}
|
||||
get_reg_field(&self.regs, CRB_LOC_STATE, "activeLocality")
|
||||
get_reg_field(
|
||||
&self.regs,
|
||||
CrbRegister::LocState(LocStateFields::ActiveLocality),
|
||||
)
|
||||
}
|
||||
|
||||
fn request_completed(&mut self, result: isize) {
|
||||
fn request_completed(&mut self, success: bool) {
|
||||
self.regs[CRB_CTRL_START as usize] = !CRB_START_INVOKE;
|
||||
if result != 0 {
|
||||
set_reg_field(&mut self.regs, CRB_CTRL_STS, "tpmSts", 1);
|
||||
if !success {
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CrbRegister::CtrlSts(CtrlStsFields::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);
|
||||
let cur_buff_size = self.emulator.get_buffer_size();
|
||||
self.regs = [0; TPM_CRB_R_MAX];
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CRB_INTF_ID,
|
||||
"InterfaceType",
|
||||
CrbRegister::LocState(LocStateFields::TpmRegValidSts),
|
||||
1,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CrbRegister::CtrlSts(CtrlStsFields::TpmIdle),
|
||||
1,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CrbRegister::IntfId(IntfIdFields::InterfaceType),
|
||||
CRB_INTF_TYPE_CRB_ACTIVE,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CRB_INTF_ID,
|
||||
"InterfaceVersion",
|
||||
CrbRegister::IntfId(IntfIdFields::InterfaceVersion),
|
||||
CRB_INTF_VERSION_CRB,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CRB_INTF_ID,
|
||||
"CapLocality",
|
||||
CrbRegister::IntfId(IntfIdFields::CapLocality),
|
||||
CRB_INTF_CAP_LOCALITY_0_ONLY,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CRB_INTF_ID,
|
||||
"CapCRBIdleBypass",
|
||||
CrbRegister::IntfId(IntfIdFields::CapCRBIdleBypass),
|
||||
CRB_INTF_CAP_IDLE_FAST,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CRB_INTF_ID,
|
||||
"CapDataXferSizeSupport",
|
||||
CrbRegister::IntfId(IntfIdFields::CapDataXferSizeSupport),
|
||||
CRB_INTF_CAP_XFER_SIZE_64,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CRB_INTF_ID,
|
||||
"CapFIFO",
|
||||
CrbRegister::IntfId(IntfIdFields::CapFIFO),
|
||||
CRB_INTF_CAP_FIFO_NOT_SUPPORTED,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CRB_INTF_ID,
|
||||
"CapCRB",
|
||||
CrbRegister::IntfId(IntfIdFields::CapCRB),
|
||||
CRB_INTF_CAP_CRB_SUPPORTED,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CRB_INTF_ID,
|
||||
"InterfaceSelector",
|
||||
CrbRegister::IntfId(IntfIdFields::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);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CrbRegister::IntfId(IntfIdFields::Rid),
|
||||
0b0000,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CrbRegister::IntfId2(IntfId2Fields::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;
|
||||
@@ -268,7 +338,6 @@ impl Tpm {
|
||||
}
|
||||
}
|
||||
|
||||
//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;
|
||||
@@ -367,10 +436,18 @@ impl BusDevice for Tpm {
|
||||
}
|
||||
CRB_CTRL_REQ => match v {
|
||||
CRB_CTRL_REQ_CMD_READY => {
|
||||
set_reg_field(&mut self.regs, CRB_CTRL_STS, "tpmIdle", 0);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CrbRegister::CtrlSts(CtrlStsFields::TpmIdle),
|
||||
0,
|
||||
);
|
||||
}
|
||||
CRB_CTRL_REQ_GO_IDLE => {
|
||||
set_reg_field(&mut self.regs, CRB_CTRL_STS, "tpmIdle", 1);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CrbRegister::CtrlSts(CtrlStsFields::TpmIdle),
|
||||
1,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
error!("Invalid value passed to CRTL_REQ register");
|
||||
@@ -393,27 +470,14 @@ impl BusDevice for Tpm {
|
||||
{
|
||||
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(),
|
||||
let mut cmd = BackendCmd {
|
||||
buffer: &mut self.data_buff,
|
||||
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());
|
||||
let status = self.emulator.deliver_request(&mut cmd).is_ok();
|
||||
|
||||
self.request_completed(TPM_SUCCESS as isize);
|
||||
self.request_completed(status);
|
||||
}
|
||||
}
|
||||
CRB_LOC_CTRL => {
|
||||
@@ -424,13 +488,33 @@ impl BusDevice for Tpm {
|
||||
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);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CrbRegister::LocState(LocStateFields::LocAssigned),
|
||||
0,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CrbRegister::LocSts(LocStsFields::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);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CrbRegister::LocSts(LocStsFields::Granted),
|
||||
1,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CrbRegister::LocSts(LocStsFields::BeenSeized),
|
||||
0,
|
||||
);
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
CrbRegister::LocState(LocStateFields::LocAssigned),
|
||||
1,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
error!("Invalid value to write in CRB_LOC_CTRL {:#X} ", v);
|
||||
@@ -456,10 +540,10 @@ mod tests {
|
||||
|
||||
#[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);
|
||||
let mut regs: [u32; TPM_CRB_R_MAX] = [0; TPM_CRB_R_MAX];
|
||||
set_reg_field(&mut regs, CrbRegister::IntfId(IntfIdFields::Rid), 0xAC);
|
||||
assert_eq!(
|
||||
get_reg_field(®s, CRB_INTF_ID, "RID"),
|
||||
get_reg_field(®s, CrbRegister::IntfId(IntfIdFields::Rid)),
|
||||
0xAC,
|
||||
concat!("Test: ", stringify!(set_get_reg_field))
|
||||
);
|
||||
|
||||
69
docs/api.md
69
docs/api.md
@@ -78,33 +78,40 @@ The Cloud Hypervisor API exposes the following actions through its endpoints:
|
||||
|
||||
#### Virtual Machine (VM) Actions
|
||||
|
||||
| Action | Endpoint | Request Body | Response Body | Prerequisites |
|
||||
| ---------------------------------- | --------------------- | --------------------------- | ------------------------ | -------------------------------- |
|
||||
| Create the VM | `/vm.create` | `/schemas/VmConfig` | N/A | The VM is not created yet |
|
||||
| Delete the VM | `/vm.delete` | N/A | N/A | N/A |
|
||||
| Boot the VM | `/vm.boot` | N/A | N/A | The VM is created but not booted |
|
||||
| Shut the VM down | `/vm.shutdown` | N/A | N/A | The VM is booted |
|
||||
| Reboot the VM | `/vm.reboot` | N/A | N/A | The VM is booted |
|
||||
| Trigger power button of the VM | `/vm.power-button` | N/A | N/A | The VM is booted |
|
||||
| Pause the VM | `/vm.pause` | N/A | N/A | The VM is booted |
|
||||
| Resume the VM | `/vm.resume` | N/A | N/A | The VM is paused |
|
||||
| Task a snapshot of the VM | `/vm.snapshot` | `/schemas/VmSnapshotConfig` | N/A | The VM is paused |
|
||||
| Perform a coredump of the VM | `/vm.coredump` | `/schemas/VmCoredumpData` | N/A | The VM is paused |
|
||||
| Restore the VM from a snapshot | `/vm.restore` | `/schemas/RestoreConfig` | N/A | The VM is created but not booted |
|
||||
| Add/remove CPUs to/from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted |
|
||||
| Add/remove memory from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted |
|
||||
| Add/remove memory from a zone | `/vm.resize-zone` | `/schemas/VmResizeZone` | N/A | The VM is booted |
|
||||
| Dump the VM information | `/vm.info` | N/A | `/schemas/VmInfo` | The VM is created |
|
||||
| Add VFIO PCI device to the VM | `/vm.add-device` | `/schemas/VmAddDevice` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add disk device to the VM | `/vm.add-disk` | `/schemas/DiskConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add fs device to the VM | `/vm.add-fs` | `/schemas/FsConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add pmem device to the VM | `/vm.add-pmem` | `/schemas/PmemConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add network device to the VM | `/vm.add-net` | `/schemas/NetConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add userspace PCI device to the VM | `/vm.add-user-device` | `/schemas/VmAddUserDevice` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add vdpa device to the VM | `/vm.add-vdpa` | `/schemas/VdpaConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add vsock device to the VM | `/vm.add-vsock` | `/schemas/VsockConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Remove device from the VM | `/vm.remove-device` | `/schemas/VmRemoveDevice` | N/A | The VM is booted |
|
||||
| Dump the VM counters | `/vm.counters` | N/A | `/schemas/VmCounters` | The VM is booted |
|
||||
| Action | Endpoint | Request Body | Response Body | Prerequisites |
|
||||
| ---------------------------------- | ----------------------- | ------------------------------- | ------------------------ | ------------------------------------------------------ |
|
||||
| Create the VM | `/vm.create` | `/schemas/VmConfig` | N/A | The VM is not created yet |
|
||||
| Delete the VM | `/vm.delete` | N/A | N/A | N/A |
|
||||
| Boot the VM | `/vm.boot` | N/A | N/A | The VM is created but not booted |
|
||||
| Shut the VM down | `/vm.shutdown` | N/A | N/A | The VM is booted |
|
||||
| Reboot the VM | `/vm.reboot` | N/A | N/A | The VM is booted |
|
||||
| Trigger power button of the VM | `/vm.power-button` | N/A | N/A | The VM is booted |
|
||||
| Pause the VM | `/vm.pause` | N/A | N/A | The VM is booted |
|
||||
| Resume the VM | `/vm.resume` | N/A | N/A | The VM is paused |
|
||||
| Task a snapshot of the VM | `/vm.snapshot` | `/schemas/VmSnapshotConfig` | N/A | The VM is paused |
|
||||
| Perform a coredump of the VM* | `/vm.coredump` | `/schemas/VmCoredumpData` | N/A | The VM is paused |
|
||||
| Restore the VM from a snapshot | `/vm.restore` | `/schemas/RestoreConfig` | N/A | The VM is created but not booted |
|
||||
| Add/remove CPUs to/from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted |
|
||||
| Add/remove memory from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted |
|
||||
| Add/remove memory from a zone | `/vm.resize-zone` | `/schemas/VmResizeZone` | N/A | The VM is booted |
|
||||
| Dump the VM information | `/vm.info` | N/A | `/schemas/VmInfo` | The VM is created |
|
||||
| Add VFIO PCI device to the VM | `/vm.add-device` | `/schemas/VmAddDevice` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add disk device to the VM | `/vm.add-disk` | `/schemas/DiskConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add fs device to the VM | `/vm.add-fs` | `/schemas/FsConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add pmem device to the VM | `/vm.add-pmem` | `/schemas/PmemConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add network device to the VM | `/vm.add-net` | `/schemas/NetConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add userspace PCI device to the VM | `/vm.add-user-device` | `/schemas/VmAddUserDevice` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add vdpa device to the VM | `/vm.add-vdpa` | `/schemas/VdpaConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Add vsock device to the VM | `/vm.add-vsock` | `/schemas/VsockConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
| Remove device from the VM | `/vm.remove-device` | `/schemas/VmRemoveDevice` | N/A | The VM is booted |
|
||||
| Dump the VM counters | `/vm.counters` | N/A | `/schemas/VmCounters` | The VM is booted |
|
||||
| Prepare to receive a migration | `/vm.receive-migration` | `/schemas/ReceiveMigrationData` | N/A | N/A |
|
||||
| Start to send migration to target | `/vm.send-migration` | `/schemas/SendMigrationData` | N/A | The VM is booted and (shared mem or hugepages enabled) |
|
||||
|
||||
* The `vmcoredump` action is available exclusively for the `x86_64`
|
||||
architecture and can be executed only when the `guest_debug` feature is
|
||||
enabled. Without this feature, the corresponding REST API endpoint is not
|
||||
available.
|
||||
|
||||
### REST API Examples
|
||||
|
||||
@@ -217,11 +224,11 @@ From the CLI, one can either:
|
||||
The REST API and the CLI both rely on a common, [internal API](#internal-api).
|
||||
|
||||
The CLI options are parsed by the
|
||||
[clap crate](https://docs.rs/clap/2.33.0/clap/) and then translated into
|
||||
[argh crate](https://docs.rs/argh/latest/argh/) and then translated into
|
||||
[internal API](#internal-api) commands.
|
||||
|
||||
The REST API is processed by an HTTP thread using the
|
||||
[Firecracker's `micro_http`](https://github.com/firecracker-microvm/firecracker/tree/master/src/micro_http)
|
||||
[Firecracker's `micro_http`](https://github.com/firecracker-microvm/micro-http)
|
||||
crate. As with the CLI, the HTTP requests eventually get translated into
|
||||
[internal API](#internal-api) commands.
|
||||
|
||||
@@ -245,7 +252,7 @@ As a summary, the REST API and the CLI are essentially frontends for the
|
||||
| | +------------------------+
|
||||
| +----------+ | VMM
|
||||
| CLI | | |
|
||||
+----------->+ clap +--------------+
|
||||
+----------->+ argh +--------------+
|
||||
| |
|
||||
+----------+
|
||||
|
||||
@@ -379,5 +386,5 @@ APIs work together, let's look at a complete VM creation flow, from the
|
||||
```
|
||||
1. The Cloud Hypervisor HTTP thread sends the formed HTTP response back to the
|
||||
user. This is abstracted by the
|
||||
[micro_http](https://github.com/firecracker-microvm/firecracker/tree/master/src/micro_http)
|
||||
[micro_http](https://github.com/firecracker-microvm/micro-http)
|
||||
crate.
|
||||
|
||||
@@ -27,7 +27,7 @@ distributions please replace the package manager and package name.
|
||||
# Install basic packages needed. For a package list targeting for more
|
||||
# functionalities for example the test, please see resources/Dockerfile.
|
||||
$ sudo apt-get update
|
||||
$ sudo apt install git build-essential m4 bison flex uuid-dev qemu-utils
|
||||
$ sudo apt install git build-essential m4 bison flex uuid-dev qemu-utils musl-tools
|
||||
# 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
|
||||
|
||||
@@ -88,7 +88,7 @@ Ubuntu distributions.
|
||||
|
||||
```bash
|
||||
apt update
|
||||
apt install fio iperf iperf3 socat stress cpuid
|
||||
apt install fio iperf iperf3 socat stress cpuid tpm2-tools
|
||||
```
|
||||
|
||||
### Remove counterproductive packages
|
||||
@@ -170,8 +170,8 @@ We usually start from one of the custom cloud image we have previously created
|
||||
but we can use a stock cloud image as well.
|
||||
|
||||
```bash
|
||||
wget https://cloud-hypervisor.azureedge.net/jammy-server-cloudimg-amd64-custom-20221118-1.raw
|
||||
mv jammy-server-cloudimg-amd64-custom-20221118-1.raw jammy-server-cloudimg-amd64-nvidia.raw
|
||||
wget https://cloud-hypervisor.azureedge.net/jammy-server-cloudimg-amd64-custom-20230119-0.raw
|
||||
mv jammy-server-cloudimg-amd64-custom-20230119-0.raw jammy-server-cloudimg-amd64-nvidia.raw
|
||||
```
|
||||
|
||||
### Extend the image size
|
||||
|
||||
@@ -36,7 +36,7 @@ Assuming parts of the guest software stack have been instrumented to use the
|
||||
`cloud-hypervisor` debug I/O port, we may want to gather the related logs.
|
||||
|
||||
To do so we need to start `cloud-hypervisor` with the right debug level
|
||||
(`-vvv`). It is also recommended to have it log into a dedicated file in order
|
||||
(`-v -v -v`). It is also recommended to have it log into a dedicated file in order
|
||||
to easily grep for the tracing logs (e.g.
|
||||
`--log-file /tmp/cloud-hypervisor.log`):
|
||||
|
||||
@@ -48,7 +48,7 @@ to easily grep for the tracing logs (e.g.
|
||||
--memory size=1024M \
|
||||
--rng \
|
||||
--log-file /tmp/ch-fw.log \
|
||||
-vvv
|
||||
-v -v -v
|
||||
```
|
||||
|
||||
After booting the guest, we then have to grep for the debug I/O port traces in
|
||||
|
||||
@@ -8,7 +8,7 @@ To enable debugging with GDB, build with the `guest_debug` feature enabled:
|
||||
cargo build --features guest_debug
|
||||
```
|
||||
|
||||
To use the `--gdb` option, specify the Unix Domain Socket with `--path` that Cloud Hypervisor will use to communicate with the host's GDB:
|
||||
To use the `--gdb` option, specify the Unix Domain Socket with `path` that Cloud Hypervisor will use to communicate with the host's GDB:
|
||||
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
|
||||
41
docs/heap-profiling.md
Normal file
41
docs/heap-profiling.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Heap profiling
|
||||
|
||||
Cloud Hypervisor supports generating a profile using
|
||||
[dhat](https://docs.rs/dhat/latest/dhat/) of the heap allocations made during
|
||||
the runtime of the process.
|
||||
|
||||
## Building a suitable binary
|
||||
|
||||
This adds the symbol information to the release binary but does not otherwise
|
||||
affect the performance.
|
||||
|
||||
```
|
||||
$ cargo build --profile profiling --features "dhat-heap"
|
||||
```
|
||||
|
||||
## Generating output
|
||||
|
||||
Cloud Hypervisor can then be run as usual. However it is necessary to run with `--seccomp false` as the profiling requires extra syscalls.
|
||||
|
||||
```
|
||||
$ target/profiling/cloud-hypervisor \
|
||||
--kernel ~/src/linux/vmlinux \
|
||||
--pmem file=~/workloads/focal.raw \
|
||||
--cpus boot=1 --memory size=1G \
|
||||
--cmdline "root=/dev/pmem0p1 console=ttyS0" \
|
||||
--serial tty --console off \
|
||||
--api-socket /tmp/api1 \
|
||||
--seccomp false
|
||||
```
|
||||
|
||||
When the VMM exits a message like the following will be shown:
|
||||
|
||||
```
|
||||
dhat: Total: 384,582 bytes in 3,512 blocks
|
||||
dhat: At t-gmax: 133,885 bytes in 379 blocks
|
||||
dhat: At t-end: 12,160 bytes in 20 blocks
|
||||
dhat: The data has been saved to dhat-heap.json, and is viewable with dhat/dh_view.html
|
||||
```
|
||||
|
||||
The JSON output can then be uploaded to [the dh_view tool](https://nnethercote.github.io/dh_view/dh_view.html) for analysis.
|
||||
|
||||
@@ -4,7 +4,7 @@ Currently Cloud Hypervisor supports hot plugging of CPUs devices (x86 only), PCI
|
||||
|
||||
## Kernel support
|
||||
|
||||
For hotplug on Cloud Hypervisor ACPI GED support is needed. This can either be achieved by turning on `CONFIG_ACPI_REDUCED_HARDWARE_ONLY`
|
||||
For hotplug on Cloud Hypervisor ACPI GED support is needed. This can either be achieved by turning on `CONFIG_ACPI_REDUCED_HARDWARE_ONLY`
|
||||
or by using this kernel patch (available in 5.5-rc1 and later): https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/patch/drivers/acpi/Makefile?id=ac36d37e943635fc072e9d4f47e40a48fbcdb3f0
|
||||
|
||||
## CPU Hot Plug
|
||||
@@ -27,16 +27,16 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||
--memory size=1024M \
|
||||
--net "tap=,mac=,ip=,mask=" \
|
||||
--rng \
|
||||
--api-socket=/tmp/ch-socket
|
||||
--api-socket /tmp/ch-socket
|
||||
$ popd
|
||||
```
|
||||
|
||||
Notice the addition of `--api-socket=/tmp/ch-socket` and a `max` parameter on `--cpus boot=4,max=8`.
|
||||
Notice the addition of `--api-socket /tmp/ch-socket` and a `max` parameter on `--cpus boot=4,max=8`.
|
||||
|
||||
To ask the VMM to add additional vCPUs then use the resize API:
|
||||
|
||||
```shell
|
||||
./ch-remote --api-socket=/tmp/ch-socket resize --cpus 8
|
||||
./ch-remote --api-socket /tmp/ch-socket resize --cpus 8
|
||||
```
|
||||
|
||||
The extra vCPU threads will be created and advertised to the running kernel. The kernel does not bring up the CPUs immediately and instead the user must "online" them from inside the VM:
|
||||
@@ -56,7 +56,7 @@ After a reboot the added CPUs will remain.
|
||||
Removing CPUs works similarly by reducing the number in the "desired_vcpus" field of the reisze API. The CPUs will be automatically offlined inside the guest so there is no need to run any commands inside the guest:
|
||||
|
||||
```shell
|
||||
./ch-remote --api-socket=/tmp/ch-socket resize --cpus 2
|
||||
./ch-remote --api-socket /tmp/ch-socket resize --cpus 2
|
||||
```
|
||||
|
||||
As per adding CPUs to the guest, after a reboot the VM will be running with the reduced number of vCPUs.
|
||||
@@ -85,7 +85,7 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||
--memory size=1024M,hotplug_size=8192M \
|
||||
--net "tap=,mac=,ip=,mask=" \
|
||||
--rng \
|
||||
--api-socket=/tmp/ch-socket
|
||||
--api-socket /tmp/ch-socket
|
||||
$ popd
|
||||
```
|
||||
|
||||
@@ -98,7 +98,7 @@ root@ch-guest ~ # echo online | sudo tee /sys/devices/system/memory/auto_online_
|
||||
To ask the VMM to expand the RAM for the VM:
|
||||
|
||||
```shell
|
||||
./ch-remote --api-socket=/tmp/ch-socket resize --memory 3G
|
||||
./ch-remote --api-socket /tmp/ch-socket resize --memory 3G
|
||||
```
|
||||
|
||||
The new memory is now available to use inside the VM:
|
||||
@@ -134,14 +134,14 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||
--disk path=focal-server-cloudimg-amd64.raw \
|
||||
--memory size=1024M,hotplug_size=8192M,hotplug_method=virtio-mem \
|
||||
--net "tap=,mac=,ip=,mask=" \
|
||||
--api-socket=/tmp/ch-socket
|
||||
--api-socket /tmp/ch-socket
|
||||
$ popd
|
||||
```
|
||||
|
||||
To ask the VMM to expand the RAM for the VM (request is in bytes):
|
||||
|
||||
```shell
|
||||
./ch-remote --api-socket=/tmp/ch-socket resize --memory 3G
|
||||
./ch-remote --api-socket /tmp/ch-socket resize --memory 3G
|
||||
```
|
||||
|
||||
The new memory is now available to use inside the VM:
|
||||
@@ -159,7 +159,7 @@ The same API can also be used to reduce the desired RAM for a VM. It is importan
|
||||
|
||||
Extra PCI devices can be added and removed from a running `cloud-hypervisor` instance. This is controlled by making a HTTP API request to the VMM to ask for the additional device to be added, or for the existing device to be removed.
|
||||
|
||||
Note: On AArch64 platform, PCI device hotplug can only be achieved using ACPI. Please refer to the [documentation](arm64.md#uefi-booting) for more information.
|
||||
Note: On AArch64 platform, PCI device hotplug can only be achieved using ACPI. Please refer to the [documentation](uefi.md#building-uefi-firmware-for-aarch64) for more information.
|
||||
|
||||
To use PCI device hotplug start the VM with the HTTP server.
|
||||
|
||||
@@ -172,17 +172,17 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
--net "tap=,mac=,ip=,mask=" \
|
||||
--api-socket=/tmp/ch-socket
|
||||
--api-socket /tmp/ch-socket
|
||||
```
|
||||
|
||||
Notice the addition of `--api-socket=/tmp/ch-socket`.
|
||||
Notice the addition of `--api-socket /tmp/ch-socket`.
|
||||
|
||||
### Add VFIO Device
|
||||
|
||||
To ask the VMM to add additional VFIO device then use the `add-device` API.
|
||||
|
||||
```shell
|
||||
./ch-remote --api-socket=/tmp/ch-socket add-device path=/sys/bus/pci/devices/0000:01:00.0/
|
||||
./ch-remote --api-socket /tmp/ch-socket add-device path=/sys/bus/pci/devices/0000:01:00.0/
|
||||
```
|
||||
|
||||
### Add Disk Device
|
||||
@@ -190,7 +190,7 @@ To ask the VMM to add additional VFIO device then use the `add-device` API.
|
||||
To ask the VMM to add additional disk device then use the `add-disk` API.
|
||||
|
||||
```shell
|
||||
./ch-remote --api-socket=/tmp/ch-socket add-disk path=/foo/bar/cloud.img
|
||||
./ch-remote --api-socket /tmp/ch-socket add-disk path=/foo/bar/cloud.img
|
||||
```
|
||||
|
||||
### Add Fs Device
|
||||
@@ -198,7 +198,7 @@ To ask the VMM to add additional disk device then use the `add-disk` API.
|
||||
To ask the VMM to add additional fs device then use the `add-fs` API.
|
||||
|
||||
```shell
|
||||
./ch-remote --api-socket=/tmp/ch-socket add-fs tag=myfs,socket=/foo/bar/virtiofs.sock
|
||||
./ch-remote --api-socket /tmp/ch-socket add-fs tag=myfs,socket=/foo/bar/virtiofs.sock
|
||||
```
|
||||
|
||||
### Add Net Device
|
||||
@@ -206,7 +206,7 @@ To ask the VMM to add additional fs device then use the `add-fs` API.
|
||||
To ask the VMM to add additional network device then use the `add-net` API.
|
||||
|
||||
```shell
|
||||
./ch-remote --api-socket=/tmp/ch-socket add-net tap=chtap0
|
||||
./ch-remote --api-socket /tmp/ch-socket add-net tap=chtap0
|
||||
```
|
||||
|
||||
### Add Pmem Device
|
||||
@@ -214,7 +214,7 @@ To ask the VMM to add additional network device then use the `add-net` API.
|
||||
To ask the VMM to add additional PMEM device then use the `add-pmem` API.
|
||||
|
||||
```shell
|
||||
./ch-remote --api-socket=/tmp/ch-socket add-pmem file=/foo/bar.cloud.img
|
||||
./ch-remote --api-socket /tmp/ch-socket add-pmem file=/foo/bar.cloud.img
|
||||
```
|
||||
|
||||
### Add Vsock Device
|
||||
@@ -222,7 +222,7 @@ To ask the VMM to add additional PMEM device then use the `add-pmem` API.
|
||||
To ask the VMM to add additional vsock device then use the `add-vsock` API.
|
||||
|
||||
```shell
|
||||
./ch-remote --api-socket=/tmp/ch-socket add-vsock cid=3,socket=/foo/bar/vsock.sock
|
||||
./ch-remote --api-socket /tmp/ch-socket add-vsock cid=3,socket=/foo/bar/vsock.sock
|
||||
```
|
||||
|
||||
### Common Across All PCI Devices
|
||||
@@ -244,7 +244,7 @@ After a reboot the added PCI device will remain.
|
||||
Removing a PCI device works the same way for all kind of PCI devices. The unique identifier related to the device must be provided. This identifier can be provided by the user when adding the new device, or by default Cloud Hypervisor will assign one.
|
||||
|
||||
```shell
|
||||
./ch-remote --api-socket=/tmp/ch-socket remove-device _disk0
|
||||
./ch-remote --api-socket /tmp/ch-socket remove-device _disk0
|
||||
```
|
||||
|
||||
As per adding a PCI device to the guest, after a reboot the VM will be running without the removed PCI device.
|
||||
|
||||
@@ -2,49 +2,64 @@
|
||||
|
||||
Intel® Trust Domain Extensions (Intel® TDX) is an Intel technology designed to
|
||||
isolate virtual machines from the VMM, hypervisor and any other software on the
|
||||
host platform.
|
||||
host platform. Here are some useful links:
|
||||
|
||||
For more information about TDX technical aspects, design and specification
|
||||
please refer to the
|
||||
[TDX Homepage](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-trust-domain-extensions.html).
|
||||
* [TDX Homepage](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-trust-domain-extensions.html):
|
||||
more information about TDX technical aspects, design and specification
|
||||
|
||||
The required Linux changes for the host side can be found in the
|
||||
[KVM TDX tree](https://github.com/intel/tdx/tree/kvm) while the changes for
|
||||
the guest side can be found in the [Guest TDX tree](https://github.com/intel/tdx/tree/guest).
|
||||
* [KVM TDX tree](https://github.com/intel/tdx/tree/kvm): the required
|
||||
Linux kernel changes for the host side
|
||||
|
||||
The TDVF firmware can be found in the
|
||||
[EDK2 staging project](https://github.com/tianocore/edk2-staging/tree/TDVF).
|
||||
* [Guest TDX tree](https://github.com/intel/tdx/tree/guest): the Linux
|
||||
kernel changes for the guest side
|
||||
|
||||
The TDShim firmware can be found in the
|
||||
[Confidential Containers project](https://github.com/confidential-containers/td-shim).
|
||||
* [EDK2 project](https://github.com/tianocore/edk2): the TDVF firmware
|
||||
|
||||
* [Confidential Containers project](https://github.com/confidential-containers/td-shim):
|
||||
the TDShim firmware
|
||||
|
||||
* [TDX Tools](https://github.com/intel/tdx-tools): a collection of tools
|
||||
and scripts to setup TDX environment for testing purpose (such as
|
||||
installing required packages on the host, creating guest images, and
|
||||
building the custom Linux kernel for TDX host and guest)
|
||||
|
||||
## Cloud Hypervisor support
|
||||
|
||||
First, you must be running on a machine with TDX enabled in hardware, and
|
||||
It is required to use a machine with TDX enabled in hardware and
|
||||
with the host OS compiled from the [KVM TDX tree](https://github.com/intel/tdx/tree/kvm).
|
||||
The host environment can also be setup with the [TDX Tools](https://github.com/intel/tdx-tools).
|
||||
|
||||
Cloud Hypervisor can run TDX VM (Trust Domain) by loading a TD firmware,
|
||||
Cloud Hypervisor can run TDX VM (Trust Domain) by loading a TD firmware ([TDVF](https://github.com/tianocore/edk2)),
|
||||
which will then load the guest kernel from the image. The image must be custom
|
||||
as it must include a kernel built from the [Guest TDX tree](https://github.com/intel/tdx/tree/guest).
|
||||
Cloud Hypervisor can also boot a TDX VM with direct kernel boot using [TDshim](https://github.com/confidential-containers/td-shim).
|
||||
The custom Linux kernel for the guest can be built with the [TDX Tools](https://github.com/intel/tdx-tools).
|
||||
|
||||
> **Note**
|
||||
> The latest version of custom host and guest kernel being tested is
|
||||
> from [TDX Tools - 2023ww01](https://github.com/intel/tdx-tools/commits/2023ww01).
|
||||
|
||||
### TDVF
|
||||
|
||||
> **Note**
|
||||
> The latest version of TDVF being tested is [_13b9773_](https://github.com/tianocore/edk2/commit/13b97736c876919b9786055829caaa4fa46984b7).
|
||||
|
||||
The firmware can be built as follows:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/tianocore/edk2-staging.git
|
||||
cd edk2-staging
|
||||
git checkout origin/TDVF
|
||||
git clone https://github.com/tianocore/edk2.git
|
||||
cd edk2
|
||||
git checkout 13b97736c876919b9786055829caaa4fa46984b7
|
||||
git submodule update --init --recursive
|
||||
make -C BaseTools
|
||||
source ./edksetup.sh
|
||||
build -p OvmfPkg/OvmfCh.dsc -a X64 -t GCC5 -b RELEASE
|
||||
build -p OvmfPkg/IntelTdx/IntelTdxX64.dsc -a X64 -t GCC5 -b RELEASE
|
||||
```
|
||||
|
||||
If debug logs are needed, here is the alternative command:
|
||||
|
||||
```bash
|
||||
build -p OvmfPkg/OvmfCh.dsc -a X64 -t GCC5 -D DEBUG_ON_SERIAL_PORT=TRUE
|
||||
build -p OvmfPkg/IntelTdx/IntelTdxX64.dsc -a X64 -t GCC5 -D DEBUG_ON_SERIAL_PORT=TRUE
|
||||
```
|
||||
|
||||
On the Cloud Hypervisor side, all you need is to build the project with the
|
||||
@@ -62,7 +77,7 @@ meaning it will be printing guest kernel logs to the `virtio-console` device.
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
--platform tdx=on
|
||||
--firmware edk2-staging/Build/OvmfCh/RELEASE_GCC5/FV/OVMF.fd \
|
||||
--firmware edk2/Build/IntelTdx/RELEASE_GCC5/FV/OVMF.fd \
|
||||
--cpus boot=1 \
|
||||
--memory size=1G \
|
||||
--disk path=tdx_guest_img
|
||||
@@ -74,7 +89,7 @@ firmware:
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
--platform tdx=on
|
||||
--firmware edk2-staging/Build/OvmfCh/DEBUG_GCC5/FV/OVMF.fd \
|
||||
--firmware edk2/Build/IntelTdx/DEBUG_GCC5/FV/OVMF.fd \
|
||||
--cpus boot=1 \
|
||||
--memory size=1G \
|
||||
--disk path=tdx_guest_img \
|
||||
@@ -84,21 +99,60 @@ firmware:
|
||||
|
||||
### TDShim
|
||||
|
||||
> **Note**
|
||||
> The latest version of TDShim being tested is [_66bb334_](https://github.com/confidential-containers/td-shim/tree/66bb33451befbf1291abe3cfea7ee9e99d922b0d).
|
||||
|
||||
This is a lightweight version of the TDVF, written in Rust and designed for
|
||||
direct kernel boot, which is useful for containers use cases.
|
||||
|
||||
You can find the instructions for building the firmware directly from the
|
||||
project [documentation](https://github.com/confidential-containers/td-shim/tree/staging#how-to-build).
|
||||
To build TDShim from source, it is required to install `Rust`, `NASM`,
|
||||
and `LLVM` first. The TDshim can be build as follows:
|
||||
```bash
|
||||
git clone https://github.com/confidential-containers/td-shim
|
||||
cd td-shim
|
||||
git checkout 66bb33451befbf1291abe3cfea7ee9e99d922b0d
|
||||
cargo install cargo-xbuild
|
||||
export CC=clang
|
||||
export AR=llvm-ar
|
||||
export CC_x86_64_unknown_none=clang
|
||||
export AR_x86_64_unknown_none=llvm-ar
|
||||
git submodule update --init --recursive
|
||||
./sh_script/preparation.sh
|
||||
cargo xbuild -p td-shim --target x86_64-unknown-none --release --features=main,tdx
|
||||
cargo run -p td-shim-tools --bin td-shim-ld --features=linker -- target/x86_64-unknown-none/release/ResetVector.bin target/x86_64-unknown-none/release/td-shim -o target/release/final.bin
|
||||
```
|
||||
|
||||
If debug logs from the TDShim is needed, here are the alternative
|
||||
commands:
|
||||
```bash
|
||||
cargo xbuild -p td-shim --target x86_64-unknown-none --features=main,tdx
|
||||
cargo run -p td-shim-tools --bin td-shim-ld --features=linker -- target/x86_64-unknown-none/debug/ResetVector.bin target/x86_64-unknown-none/debug/td-shim -o target/debug/final.bin
|
||||
```
|
||||
|
||||
And run a TDX VM by providing the firmware previously built, along with a guest
|
||||
kernel built from the [Guest TDX tree](https://github.com/intel/tdx/tree/guest).
|
||||
kernel built from the [Guest TDX tree](https://github.com/intel/tdx/tree/guest)
|
||||
or the [TDX Tools](https://github.com/intel/tdx-tools).
|
||||
The appropriate kernel boot options must be provided through the `--cmdline`
|
||||
option as well.
|
||||
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
--platform tdx=on
|
||||
--firmware tdshim \
|
||||
--firmware td-shim/target/release/final.bin \
|
||||
--kernel bzImage \
|
||||
--cmdline "root=/dev/vda3 console=hvc0 rw"
|
||||
--cpus boot=1 \
|
||||
--memory size=1G \
|
||||
--disk path=tdx_guest_img
|
||||
```
|
||||
|
||||
And here is the alternative command when looking for debug logs from the
|
||||
TDShim:
|
||||
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
--platform tdx=on
|
||||
--firmware td-shim/target/debug/final.bin \
|
||||
--kernel bzImage \
|
||||
--cmdline "root=/dev/vda3 console=hvc0 rw"
|
||||
--cpus boot=1 \
|
||||
|
||||
@@ -245,7 +245,7 @@ e.g.
|
||||
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
--api-socket=/tmp/api \
|
||||
--api-socket /tmp/api \
|
||||
--cpus boot=1 \
|
||||
--memory size=4G,hugepages=on \
|
||||
--disk path=focal-server-cloudimg-amd64.raw \
|
||||
@@ -260,7 +260,7 @@ requiring the IOMMU then may be hotplugged:
|
||||
e.g.
|
||||
|
||||
```bash
|
||||
./ch-remote --api-socket=/tmp/api add-device path=/sys/bus/pci/devices/0000:00:04.0,iommu=on,pci_segment=1
|
||||
./ch-remote --api-socket /tmp/api add-device path=/sys/bus/pci/devices/0000:00:04.0,iommu=on,pci_segment=1
|
||||
```
|
||||
|
||||
Devices that cannot be placed behind an IOMMU (e.g. lacking an `iommu=` option)
|
||||
|
||||
@@ -16,22 +16,22 @@ $ target/release/cloud-hypervisor
|
||||
--disk path=~/workloads/focal.raw \
|
||||
--cpus boot=1 --memory size=1G,shared=on \
|
||||
--cmdline "root=/dev/vda1 console=ttyS0" \
|
||||
--serial tty --console off --api-socket=/tmp/api1
|
||||
--serial tty --console off --api-socket /tmp/api1
|
||||
```
|
||||
|
||||
Launch the destination VM from the same directory (on the host machine):
|
||||
```bash
|
||||
$ target/release/cloud-hypervisor --api-socket=/tmp/api2
|
||||
$ target/release/cloud-hypervisor --api-socket /tmp/api2
|
||||
```
|
||||
|
||||
Get ready for receiving migration for the destination VM (on the host machine):
|
||||
```bash
|
||||
$ target/release/ch-remote --api-socket=/tmp/api2 receive-migration unix:/tmp/sock
|
||||
$ target/release/ch-remote --api-socket /tmp/api2 receive-migration unix:/tmp/sock
|
||||
```
|
||||
|
||||
Start to send migration for the source VM (on the host machine):
|
||||
```bash
|
||||
$ target/release/ch-remote --api-socket=/tmp/api1 send-migration --local unix:/tmp/sock
|
||||
$ target/release/ch-remote --api-socket /tmp/api1 send-migration --local unix:/tmp/sock
|
||||
```
|
||||
|
||||
When the above commands completed, the source VM should be successfully
|
||||
@@ -51,7 +51,7 @@ $ sudo /target/release/cloud-hypervisor \
|
||||
--cpus boot=1 --memory size=512M \
|
||||
--kernel vmlinux \
|
||||
--cmdline "root=/dev/vda1 console=ttyS0" \
|
||||
--disk path=focal-1.raw path=focal-nested.raw path=tmp.img\
|
||||
--disk path=focal-1.raw path=focal-nested.raw --disk path=tmp.img\
|
||||
--net ip=192.168.101.1
|
||||
```
|
||||
|
||||
@@ -63,7 +63,7 @@ $ sudo /target/release/cloud-hypervisor \
|
||||
--cpus boot=1 --memory size=512M \
|
||||
--kernel vmlinux \
|
||||
--cmdline "root=/dev/vda1 console=ttyS0" \
|
||||
--disk path=focal-2.raw path=focal-nested.raw path=tmp.img\
|
||||
--disk path=focal-2.raw path=focal-nested.raw --disk path=tmp.img\
|
||||
--net ip=192.168.102.1
|
||||
```
|
||||
|
||||
@@ -74,8 +74,8 @@ vm-1:~$ sudo ./cloud-hypervisor \
|
||||
--memory size=128M \
|
||||
--kernel vmlinux \
|
||||
--cmdline "console=ttyS0 root=/dev/vda1" \
|
||||
--disk path=/dev/vdb path=/dev/vdc \
|
||||
--api-socket=/tmp/api1 \
|
||||
--disk path=/dev/vdb --disk path=/dev/vdc \
|
||||
--api-socket /tmp/api1 \
|
||||
--net ip=192.168.100.1
|
||||
vm-1:~$ # setup the guest network if needed
|
||||
vm-1:~$ sudo ip addr add 192.168.101.2/24 dev ens4
|
||||
@@ -108,7 +108,7 @@ echo "tmp = $tmp"
|
||||
|
||||
Launch the nested destination VM (inside the guest OS of the VM 2):
|
||||
```bash
|
||||
vm-2:~$ sudo ./cloud-hypervisor --api-socket=/tmp/api2
|
||||
vm-2:~$ sudo ./cloud-hypervisor --api-socket /tmp/api2
|
||||
vm-2:~$ # setup the guest network with the following commands if needed
|
||||
vm-2:~$ sudo ip addr add 192.168.102.2/24 dev ens4
|
||||
vm-2:~$ sudo ip link set up dev ens4
|
||||
@@ -122,7 +122,7 @@ vm-2:~$ ping 192.168.101.2 # This should succeed
|
||||
Get ready for receiving migration for the nested destination VM (inside
|
||||
the guest OS of the VM 2):
|
||||
```bash
|
||||
vm-2:~$ sudo ./ch-remote --api-socket=/tmp/api2 receive-migration unix:/tmp/sock2
|
||||
vm-2:~$ sudo ./ch-remote --api-socket /tmp/api2 receive-migration unix:/tmp/sock2
|
||||
vm-2:~$ sudo socat TCP-LISTEN:6000,reuseaddr UNIX-CLIENT:/tmp/sock2
|
||||
```
|
||||
|
||||
@@ -130,7 +130,7 @@ Start to send migration for the nested source VM (inside the guest OS of
|
||||
the VM 1):
|
||||
```bash
|
||||
vm-1:~$ sudo socat UNIX-LISTEN:/tmp/sock1,reuseaddr TCP:192.168.102.2:6000
|
||||
vm-1:~$ sudo ./ch-remote --api-socket=/tmp/api1 send-migration unix:/tmp/sock1
|
||||
vm-1:~$ sudo ./ch-remote --api-socket /tmp/api1 send-migration unix:/tmp/sock1
|
||||
```
|
||||
|
||||
When the above commands completed, the source VM should be successfully
|
||||
|
||||
@@ -38,6 +38,6 @@ This level is for the benefit of developers. It should be used for sporadic and
|
||||
|
||||
### `debug!()`
|
||||
|
||||
Use `-vv` to enable.
|
||||
Use `-v -v` to enable.
|
||||
|
||||
For the most verbose of logging messages. It is acceptable to "spam" the log with repeated invocations of the same message. This level of logging would be combined with `--log-file`.
|
||||
For the most verbose of logging messages. It is acceptable to "spam" the log with repeated invocations of the same message. This level of logging would be combined with `--log-file`.
|
||||
|
||||
@@ -266,11 +266,8 @@ _Example_
|
||||
|
||||
### `file`
|
||||
|
||||
Path to the file backing the memory zone. This can be either a file or a
|
||||
directory. In case of a file, it will be opened and used as the backing file
|
||||
for the `mmap(2)` operation. In case of a directory, a temporary file with no
|
||||
hard link on the filesystem will be created. This file will be used as the
|
||||
backing file for the `mmap(2)` operation.
|
||||
Path to the file backing the memory zone. The file will be opened and used as
|
||||
the backing file for the `mmap(2)` operation.
|
||||
|
||||
This option can be particularly useful when trying to back a part of the guest
|
||||
RAM with a well known file. In the context of the snapshot/restore feature, and
|
||||
@@ -519,7 +516,7 @@ different distances, it can be described with the following example.
|
||||
_Example_
|
||||
|
||||
```
|
||||
--numa guest_numa_id=0,distances=[1@15,2@25] guest_numa_id=1,distances=[0@15,2@20] guest_numa_id=2,distances=[0@25,1@20]
|
||||
--numa guest_numa_id=0,distances=[1@15,2@25] --numa guest_numa_id=1,distances=[0@15,2@20] guest_numa_id=2,distances=[0@25,1@20]
|
||||
```
|
||||
|
||||
### `memory_zones`
|
||||
@@ -543,14 +540,14 @@ demarcate the list.
|
||||
|
||||
Note that a memory zone must belong to a single NUMA node. The following
|
||||
configuration is incorrect, therefore not allowed:
|
||||
`--numa guest_numa_id=0,memory_zones=mem0 guest_numa_id=1,memory_zones=mem0`
|
||||
`--numa guest_numa_id=0,memory_zones=mem0 --numa guest_numa_id=1,memory_zones=mem0`
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--memory size=0
|
||||
--memory-zone id=mem0,size=1G id=mem1,size=1G id=mem2,size=1G
|
||||
--numa guest_numa_id=0,memory_zones=[mem0,mem2] guest_numa_id=1,memory_zones=mem1
|
||||
--memory-zone id=mem0,size=1G id=mem1,size=1G --memory-zone id=mem2,size=1G
|
||||
--numa guest_numa_id=0,memory_zones=[mem0,mem2] --numa guest_numa_id=1,memory_zones=mem1
|
||||
```
|
||||
|
||||
### `sgx_epc_sections`
|
||||
@@ -570,7 +567,7 @@ _Example_
|
||||
|
||||
```
|
||||
--sgx-epc id=epc0,size=32M id=epc1,size=64M id=epc2,size=32M
|
||||
--numa guest_numa_id=0,sgx_epc_sections=epc1 guest_numa_id=1,sgx_epc_sections=[epc0,epc2]
|
||||
--numa guest_numa_id=0,sgx_epc_sections=epc1 --numa guest_numa_id=1,sgx_epc_sections=[epc0,epc2]
|
||||
```
|
||||
|
||||
### PCI bus
|
||||
|
||||
@@ -4,20 +4,12 @@
|
||||
|
||||
## Building a suitable binary
|
||||
|
||||
Modify the `Cargo.toml` file to add `debug = 1` to the `[profile.release]` block. It should look like this:
|
||||
|
||||
```
|
||||
[profile.release]
|
||||
lto = true
|
||||
debug = 1
|
||||
```
|
||||
|
||||
This adds the symbol information to the release binary but does not otherwise affect the performance.
|
||||
|
||||
The binary must also be built with frame pointers included so that the call graph can be captured by the profiler.
|
||||
|
||||
```
|
||||
$ cargo clean && RUSTFLAGS='-C force-frame-pointers=y' cargo build --release
|
||||
$ cargo clean && RUSTFLAGS='-C force-frame-pointers=y' cargo build --profile profiling
|
||||
```
|
||||
|
||||
## Profiling
|
||||
@@ -27,13 +19,13 @@ $ cargo clean && RUSTFLAGS='-C force-frame-pointers=y' cargo build --release
|
||||
e.g.
|
||||
|
||||
```
|
||||
$ perf record -g target/release/cloud-hypervisor \
|
||||
$ perf record -g target/profiling/cloud-hypervisor \
|
||||
--kernel ~/src/linux/vmlinux \
|
||||
--pmem file=~/workloads/focal.raw \
|
||||
--cpus boot=1 --memory size=1G \
|
||||
--cmdline "root=/dev/pmem0p1 console=ttyS0" \
|
||||
--serial tty --console off \
|
||||
--api-socket=/tmp/api1
|
||||
--api-socket /tmp/api1
|
||||
```
|
||||
|
||||
For analysing the samples:
|
||||
@@ -60,5 +52,5 @@ $ perf record --call-graph lbr --all-user --user-callchains -g target/release/cl
|
||||
--cpus boot=1 --memory size=1G \
|
||||
--cmdline "root=/dev/pmem0p1 console=ttyS0" \
|
||||
--serial tty --console off \
|
||||
--api-socket=/tmp/api1
|
||||
--api-socket /tmp/api1
|
||||
```
|
||||
|
||||
@@ -25,14 +25,14 @@ First thing, we must run a Cloud Hypervisor VM:
|
||||
At any point in time when the VM is running, one might choose to pause it:
|
||||
|
||||
```bash
|
||||
./ch-remote --api-socket=/tmp/cloud-hypervisor.sock pause
|
||||
./ch-remote --api-socket /tmp/cloud-hypervisor.sock pause
|
||||
```
|
||||
|
||||
Once paused, the VM can be safely snapshot into the specified directory and
|
||||
using the following command:
|
||||
|
||||
```bash
|
||||
./ch-remote --api-socket=/tmp/cloud-hypervisor.sock snapshot file:///home/foo/snapshot
|
||||
./ch-remote --api-socket /tmp/cloud-hypervisor.sock snapshot file:///home/foo/snapshot
|
||||
```
|
||||
|
||||
Given the directory was present on the system, the snapshot will succeed and
|
||||
@@ -79,7 +79,7 @@ Or using two different commands from two terminals:
|
||||
./cloud-hypervisor --api-socket /tmp/cloud-hypervisor.sock
|
||||
|
||||
# Second terminal
|
||||
./ch-remote --api-socket=/tmp/cloud-hypervisor.sock restore source_url=file:///home/foo/snapshot
|
||||
./ch-remote --api-socket /tmp/cloud-hypervisor.sock restore source_url=file:///home/foo/snapshot
|
||||
```
|
||||
|
||||
Remember the VM is restored in a `paused` state, which was the VM's state when
|
||||
@@ -87,7 +87,7 @@ it was snapshot. For this reason, one must explicitly `resume` the VM before to
|
||||
start using it.
|
||||
|
||||
```bash
|
||||
./ch-remote --api-socket=/tmp/cloud-hypervisor.sock resume
|
||||
./ch-remote --api-socket /tmp/cloud-hypervisor.sock resume
|
||||
```
|
||||
|
||||
At this point, the VM is fully restored and is identical to the VM which was
|
||||
|
||||
@@ -94,7 +94,7 @@ VMs run in client mode. They connect to the socket created by the `dpdkvhostuser
|
||||
--memory size=1024M,hugepages=on,shared=true \
|
||||
--kernel linux/arch/x86/boot/compressed/vmlinux.bin \
|
||||
--cmdline "console=ttyS0 root=/dev/vda1 rw iommu=off" \
|
||||
--disk path=images/focal-server-cloudimg-amd64.raw vhost_user=true,socket=/var/tmp/vhost.1,num_queues=4,queue_size=128 \
|
||||
--disk path=images/focal-server-cloudimg-amd64.raw --disk vhost_user=true,socket=/var/tmp/vhost.1,num_queues=4,queue_size=128 \
|
||||
--console off \
|
||||
--serial tty \
|
||||
--rng
|
||||
|
||||
@@ -7,4 +7,4 @@ edition = "2021"
|
||||
[dependencies]
|
||||
libc = "0.2.139"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.89"
|
||||
serde_json = "1.0.95"
|
||||
|
||||
475
fuzz/Cargo.lock
generated
475
fuzz/Cargo.lock
generated
@@ -5,15 +5,16 @@ version = 3
|
||||
[[package]]
|
||||
name = "acpi_tables"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#0b892e2c2053c4ecfac8d9e5a52babae75114702"
|
||||
dependencies = [
|
||||
"vm-memory",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.68"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
|
||||
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -24,15 +25,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29d47fbf90d5149a107494b15a7dc8d69b351be2db3bb9691740e88ec17fd880"
|
||||
checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.5.1"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164"
|
||||
checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
|
||||
|
||||
[[package]]
|
||||
name = "arch"
|
||||
@@ -56,6 +57,34 @@ dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argh"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab257697eb9496bf75526f0217b5ed64636a9cfafa78b8365c71bd283fcef93e"
|
||||
dependencies = [
|
||||
"argh_derive",
|
||||
"argh_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argh_derive"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b382dbd3288e053331f03399e1db106c9fb0d8562ad62cb04859ae926f324fa6"
|
||||
dependencies = [
|
||||
"argh_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argh_shared"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64cb94155d965e3d37ffbbe7cc5b82c3dd79dd33bd48e536f73d2cfb8d85506f"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
@@ -84,7 +113,7 @@ dependencies = [
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"vhdx",
|
||||
"virtio-bindings",
|
||||
"virtio-bindings 0.2.0",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
"vm-virtio",
|
||||
@@ -99,9 +128,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.78"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
@@ -112,37 +141,13 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"is-terminal",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"terminal_size",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "28.0.0"
|
||||
version = "30.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
"clap",
|
||||
"argh",
|
||||
"epoll",
|
||||
"event_monitor",
|
||||
"hypervisor",
|
||||
@@ -203,9 +208,9 @@ checksum = "55626594feae15d266d52440b26ff77de0e22230cf0c113abe619084c1ddc910"
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.14.2"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa"
|
||||
checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
@@ -213,27 +218,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.14.2"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f"
|
||||
checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.14.2"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e"
|
||||
checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -248,7 +253,6 @@ dependencies = [
|
||||
"hypervisor",
|
||||
"libc",
|
||||
"log",
|
||||
"phf",
|
||||
"thiserror",
|
||||
"tpm",
|
||||
"versionize",
|
||||
@@ -269,27 +273,6 @@ 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"
|
||||
@@ -301,9 +284,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fdt"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "964f5becd44d069dca0beea2b4bc05639ae7bf3b3f5369c295aff360bb57cca2"
|
||||
checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
@@ -322,15 +305,6 @@ dependencies = [
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hypervisor"
|
||||
version = "0.1.0"
|
||||
@@ -365,49 +339,27 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.5.11"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9ddf18110cedc09617f5a965bdb51adb0c0fb02c791e4ab22f7a21bb25269b0"
|
||||
checksum = "dd1e1a01cfb924fd8c5c43b6827965db394f5a3a16c599ce03452266e1cf984c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.25"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
|
||||
checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -424,9 +376,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kvm-ioctls"
|
||||
version = "0.12.0"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3a321cabd827642499c77e27314f388dd83a717a5ca716b86476fb947f73ae4"
|
||||
checksum = "b8f8dc9c1896e5f144ec5d07169bc29f39a047686d29585a91f30489abfaeb6b"
|
||||
dependencies = [
|
||||
"kvm-bindings",
|
||||
"libc",
|
||||
@@ -441,15 +393,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.139"
|
||||
version = "0.2.141"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8fff891139ee62800da71b7fd5b508d570b9ad95e614a53c6f453ca08366038"
|
||||
checksum = "beb09950ae85a0a94b27676cccf37da5ff13f27076aa1adbc6545dd0d0e1bd4e"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"cc",
|
||||
@@ -465,12 +417,6 @@ dependencies = [
|
||||
"vm-memory",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
@@ -483,7 +429,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#fbef706e28c7ca7cce8dfec7ca073f3b359879d3"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#b538bf89e50be83b6fa9ab1896727ff61e02fa13"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"vmm-sys-util",
|
||||
@@ -510,7 +456,7 @@ dependencies = [
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"virtio-bindings",
|
||||
"virtio-bindings 0.2.0",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
"vm-virtio",
|
||||
@@ -519,20 +465,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.0"
|
||||
version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "option_parser"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||
|
||||
[[package]]
|
||||
name = "pci"
|
||||
version = "0.1.0"
|
||||
@@ -556,53 +496,11 @@ dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.49"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
|
||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -620,28 +518,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||
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"
|
||||
@@ -653,13 +536,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "remain"
|
||||
version = "0.2.6"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5704e2cda92fd54202f05430725317ba0ea7d0c96b246ca0a92e45177127ba3b"
|
||||
checksum = "e13cca257d068dd3a390d04b2c3009a3fad2ee5048dfa8f239d048372810470c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -671,25 +554,11 @@ 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.12"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||
|
||||
[[package]]
|
||||
name = "seccompiler"
|
||||
@@ -702,35 +571,35 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.16"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
|
||||
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.152"
|
||||
version = "1.0.159"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
|
||||
checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.152"
|
||||
version = "1.0.159"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
|
||||
checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.91"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
|
||||
checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -739,9 +608,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "2.1.0"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25bf4a5a814902cd1014dbccfa4d4560fb8432c779471e96e035602519f82eef"
|
||||
checksum = "331bb8c3bf9b92457ab7abecf07078c13f7d270ba490103e84e8b014490cd0b0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_with_macros",
|
||||
@@ -749,14 +618,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "2.2.0"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1966009f3c05f095697c537312f5415d1e3ed31ce0a56942bac4c771c5c335e"
|
||||
checksum = "859011bddcc11f289f07f467cc1fe01c7a941daa4d8f6c40d4d1c92eb6d9319c"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -765,9 +634,9 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.14"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
|
||||
checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
@@ -775,19 +644,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
|
||||
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.10.0"
|
||||
@@ -802,9 +665,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.107"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -812,42 +675,34 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
name = "syn"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
checksum = "2c622ae390c9302e214c31013517c2061ecb2699935882c60a9b37f82f8625ae"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb20089a8ba2b69debd491f8d2d023761cbf196e999218c591fa1e7e15a21907"
|
||||
dependencies = [
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.37"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.37"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -858,6 +713,7 @@ dependencies = [
|
||||
"byteorder",
|
||||
"libc",
|
||||
"log",
|
||||
"net_gen",
|
||||
"thiserror",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
@@ -875,24 +731,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.6"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.2.2"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
|
||||
checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "versionize"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6e2495726cf917e7ba7ec8bf0f0fceab543dd38d0a4195ed6bef331e38a290f"
|
||||
checksum = "dca4b7062e7e6d685901e815c35f9671e059de97c1c0905eeff8592f3fff442f"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"crc64",
|
||||
@@ -900,7 +756,7 @@ dependencies = [
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
"versionize_derive",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
@@ -908,17 +764,17 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "versionize_derive"
|
||||
version = "0.1.4"
|
||||
source = "git+https://github.com/cloud-hypervisor/versionize_derive?branch=ch#ae35ef7a3ddabd3371ab8ac0193a383aff6e4b1b"
|
||||
source = "git+https://github.com/cloud-hypervisor/versionize_derive?branch=ch#e502b1d4aabab342386f0c53780d49f21a6a1df6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vfio-bindings"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#7ef33f6759ae6d37a54cd66f749aa47c1c81509c"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#43439e056ddfa84a4f7906ee7f2f58be70505c08"
|
||||
dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
@@ -926,7 +782,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio-ioctls"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#7ef33f6759ae6d37a54cd66f749aa47c1c81509c"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#43439e056ddfa84a4f7906ee7f2f58be70505c08"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
@@ -942,7 +798,9 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio_user"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio-user?branch=main#afbbd5722885e961ce12baea12efe01d52ce14b0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
@@ -985,6 +843,12 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ff512178285488516ed85f15b5d0113a7cdb89e9e8a760b269ae4f02b84bd6b"
|
||||
|
||||
[[package]]
|
||||
name = "virtio-bindings"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b9084faf91b9aa9676ae2cac8f1432df2839d9566e6f19f29dbc13a8b831dff"
|
||||
|
||||
[[package]]
|
||||
name = "virtio-devices"
|
||||
version = "0.1.0"
|
||||
@@ -1010,7 +874,7 @@ dependencies = [
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"vhost",
|
||||
"virtio-bindings",
|
||||
"virtio-bindings 0.2.0",
|
||||
"virtio-queue",
|
||||
"vm-allocator",
|
||||
"vm-device",
|
||||
@@ -1022,12 +886,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19e927d93d54c365034fd7f31a5f458a1f540de4a37c52e892670dad9692173c"
|
||||
checksum = "3ba81e2bcc21c0d2fc5e6683e79367e26ad219197423a498df801d79d5ba77bd"
|
||||
dependencies = [
|
||||
"log",
|
||||
"virtio-bindings",
|
||||
"virtio-bindings 0.1.0",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
@@ -1102,7 +966,6 @@ dependencies = [
|
||||
"arch",
|
||||
"bitflags",
|
||||
"block_util",
|
||||
"clap",
|
||||
"devices",
|
||||
"epoll",
|
||||
"event_monitor",
|
||||
@@ -1137,13 +1000,14 @@ dependencies = [
|
||||
"vm-migration",
|
||||
"vm-virtio",
|
||||
"vmm-sys-util",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vmm-sys-util"
|
||||
version = "0.11.0"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc06a16ee8ebf0d9269aed304030b0d20a866b8b3dd3d4ce532596ac567a0d24"
|
||||
checksum = "dd64fe09d8e880e600c324e7d664760a17f56e9672b7495a86381b49e4f72f46"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -1173,15 +1037,6 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
@@ -1189,58 +1044,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.42.0"
|
||||
name = "zerocopy"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||
checksum = "332f188cc1bcf1fe1064b8c58d150f497e697f49774aa846f2dc949d9a25f236"
|
||||
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",
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.0"
|
||||
name = "zerocopy-derive"
|
||||
version = "0.3.2"
|
||||
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"
|
||||
checksum = "6505e6815af7de1746a08f69c69606bb45695a17149517680f3b2149713b19a3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
@@ -12,19 +12,19 @@ cargo-fuzz = true
|
||||
block_util = { path = "../block_util" }
|
||||
devices = { path = "../devices" }
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.138"
|
||||
libfuzzer-sys = "0.4.5"
|
||||
libc = "0.2.141"
|
||||
libfuzzer-sys = "0.4.6"
|
||||
linux-loader = { version = "0.8.1", features = ["elf", "bzimage", "pe"] }
|
||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||
net_util = { path = "../net_util" }
|
||||
once_cell = "1.17.0"
|
||||
once_cell = "1.17.1"
|
||||
qcow = { path = "../qcow" }
|
||||
seccompiler = "0.3.0"
|
||||
vhdx = { path = "../vhdx" }
|
||||
virtio-devices = { path = "../virtio-devices" }
|
||||
virtio-queue = "0.7.0"
|
||||
virtio-queue = "0.7.1"
|
||||
vmm = { path = "../vmm" }
|
||||
vmm-sys-util = "0.11.0"
|
||||
vmm-sys-util = "0.11.1"
|
||||
vm-memory = "0.10.0"
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
|
||||
@@ -6,22 +6,22 @@ edition = "2021"
|
||||
license = "Apache-2.0 OR BSD-3-Clause"
|
||||
|
||||
[features]
|
||||
kvm = ["kvm-ioctls", "kvm-bindings"]
|
||||
mshv = ["mshv-ioctls", "mshv-bindings"]
|
||||
kvm = ["kvm-ioctls", "kvm-bindings", "vfio-ioctls/kvm"]
|
||||
mshv = ["mshv-ioctls", "mshv-bindings", "vfio-ioctls/mshv"]
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.68"
|
||||
anyhow = "1.0.69"
|
||||
byteorder = "1.4.3"
|
||||
thiserror = "1.0.38"
|
||||
thiserror = "1.0.39"
|
||||
libc = "0.2.139"
|
||||
log = "0.4.17"
|
||||
kvm-ioctls = { version = "0.12.0", optional = true }
|
||||
kvm-ioctls = { version = "0.13.0", optional = true }
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
mshv-bindings = { git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", branch = "main", optional = true}
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
serde_with = { version = "2.1.0", default-features = false, features = ["macros"] }
|
||||
serde_with = { version = "2.3.1", default-features = false, features = ["macros"] }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }
|
||||
|
||||
@@ -15,73 +15,89 @@ use crate::arch::x86::emulator::instructions::*;
|
||||
use crate::arch::x86::regs::DF;
|
||||
use crate::arch::x86::Exception;
|
||||
|
||||
macro_rules! movs {
|
||||
($bound:ty) => {
|
||||
fn emulate(
|
||||
&self,
|
||||
insn: &Instruction,
|
||||
state: &mut T,
|
||||
platform: &mut dyn PlatformEmulator<CpuState = T>,
|
||||
) -> Result<(), EmulationError<Exception>> {
|
||||
let mut count: u64 = if insn.has_rep_prefix() {
|
||||
state
|
||||
.read_reg(Register::ECX)
|
||||
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
let mut rsi = state
|
||||
.read_reg(Register::RSI)
|
||||
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||
let mut rdi = state
|
||||
.read_reg(Register::RDI)
|
||||
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||
|
||||
let df = (state.flags() & DF) != 0;
|
||||
let len = std::mem::size_of::<$bound>();
|
||||
|
||||
while count > 0 {
|
||||
let mut memory: [u8; 4] = [0; 4];
|
||||
|
||||
let src = state
|
||||
.linearize(Register::DS, rsi, false)
|
||||
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||
let dst = state
|
||||
.linearize(Register::ES, rdi, true)
|
||||
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||
|
||||
platform
|
||||
.read_memory(src, &mut memory[0..len])
|
||||
.map_err(EmulationError::PlatformEmulationError)?;
|
||||
platform
|
||||
.write_memory(dst, &memory[0..len])
|
||||
.map_err(EmulationError::PlatformEmulationError)?;
|
||||
|
||||
if df {
|
||||
rsi = rsi.wrapping_sub(len as u64);
|
||||
rdi = rdi.wrapping_sub(len as u64);
|
||||
} else {
|
||||
rsi = rsi.wrapping_add(len as u64);
|
||||
rdi = rdi.wrapping_add(len as u64);
|
||||
}
|
||||
count -= 1;
|
||||
}
|
||||
|
||||
state
|
||||
.write_reg(Register::RSI, rsi)
|
||||
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||
state
|
||||
.write_reg(Register::RDI, rdi)
|
||||
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||
if insn.has_rep_prefix() {
|
||||
state
|
||||
.write_reg(Register::ECX, 0)
|
||||
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct Movsd_m32_m32;
|
||||
impl<T: CpuStateManager> InstructionHandler<T> for Movsd_m32_m32 {
|
||||
fn emulate(
|
||||
&self,
|
||||
insn: &Instruction,
|
||||
state: &mut T,
|
||||
platform: &mut dyn PlatformEmulator<CpuState = T>,
|
||||
) -> Result<(), EmulationError<Exception>> {
|
||||
let mut count: u64 = if insn.has_rep_prefix() {
|
||||
state
|
||||
.read_reg(Register::ECX)
|
||||
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?
|
||||
} else {
|
||||
1
|
||||
};
|
||||
movs!(u32);
|
||||
}
|
||||
|
||||
let mut rsi = state
|
||||
.read_reg(Register::RSI)
|
||||
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||
let mut rdi = state
|
||||
.read_reg(Register::RDI)
|
||||
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||
pub struct Movsw_m16_m16;
|
||||
impl<T: CpuStateManager> InstructionHandler<T> for Movsw_m16_m16 {
|
||||
movs!(u16);
|
||||
}
|
||||
|
||||
let df = (state.flags() & DF) != 0;
|
||||
let len = std::mem::size_of::<u32>();
|
||||
|
||||
while count > 0 {
|
||||
let mut memory: [u8; 4] = [0; 4];
|
||||
|
||||
let src = state
|
||||
.linearize(Register::DS, rsi, false)
|
||||
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||
let dst = state
|
||||
.linearize(Register::ES, rdi, true)
|
||||
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||
|
||||
platform
|
||||
.read_memory(src, &mut memory[0..len])
|
||||
.map_err(EmulationError::PlatformEmulationError)?;
|
||||
platform
|
||||
.write_memory(dst, &memory[0..len])
|
||||
.map_err(EmulationError::PlatformEmulationError)?;
|
||||
|
||||
if df {
|
||||
rsi = rsi.wrapping_sub(len as u64);
|
||||
rdi = rdi.wrapping_sub(len as u64);
|
||||
} else {
|
||||
rsi = rsi.wrapping_add(len as u64);
|
||||
rdi = rdi.wrapping_add(len as u64);
|
||||
}
|
||||
count -= 1;
|
||||
}
|
||||
|
||||
state
|
||||
.write_reg(Register::RSI, rsi)
|
||||
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||
state
|
||||
.write_reg(Register::RDI, rdi)
|
||||
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||
if insn.has_rep_prefix() {
|
||||
state
|
||||
.write_reg(Register::ECX, 0)
|
||||
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub struct Movsb_m8_m8;
|
||||
impl<T: CpuStateManager> InstructionHandler<T> for Movsb_m8_m8 {
|
||||
movs!(u8);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -142,4 +158,127 @@ mod tests {
|
||||
vmm.read_memory(0x8 + 8, &mut data).unwrap();
|
||||
assert_eq!(0x0, <u32>::from_le_bytes(data));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rep_movsw_m16_m16() {
|
||||
let ip: u64 = 0x1000;
|
||||
let memory: [u8; 24] = [
|
||||
0x78, 0x56, 0x34, 0x12, // 0x12345678
|
||||
0xdd, 0xcc, 0xbb, 0xaa, // 0xaabbccdd
|
||||
0xa5, 0x5a, 0xa5, 0x5a, // 0x5aa55aa5
|
||||
0x00, 0x00, 0x00, 0x00, // 0x00000000
|
||||
0x00, 0x00, 0x00, 0x00, // 0x00000000
|
||||
0x00, 0x00, 0x00, 0x00, // 0x00000000
|
||||
];
|
||||
let insn = [0x66, 0xf3, 0xa5]; // rep movsw
|
||||
let regs = vec![(Register::ECX, 6), (Register::ESI, 0), (Register::EDI, 0xc)];
|
||||
let mut data = [0u8; 2];
|
||||
|
||||
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
|
||||
|
||||
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
|
||||
|
||||
vmm.read_memory(0xc, &mut data).unwrap();
|
||||
assert_eq!(0x5678, <u16>::from_le_bytes(data));
|
||||
vmm.read_memory(0xc + 2, &mut data).unwrap();
|
||||
assert_eq!(0x1234, <u16>::from_le_bytes(data));
|
||||
vmm.read_memory(0xc + 4, &mut data).unwrap();
|
||||
assert_eq!(0xccdd, <u16>::from_le_bytes(data));
|
||||
vmm.read_memory(0xc + 6, &mut data).unwrap();
|
||||
assert_eq!(0xaabb, <u16>::from_le_bytes(data));
|
||||
vmm.read_memory(0xc + 8, &mut data).unwrap();
|
||||
assert_eq!(0x5aa5, <u16>::from_le_bytes(data));
|
||||
vmm.read_memory(0xc + 10, &mut data).unwrap();
|
||||
assert_eq!(0x5aa5, <u16>::from_le_bytes(data));
|
||||
// The rest should be default value 0 from MockVmm
|
||||
vmm.read_memory(0xc + 12, &mut data).unwrap();
|
||||
assert_eq!(0x0, <u16>::from_le_bytes(data));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_movsw_m16_m16() {
|
||||
let ip: u64 = 0x1000;
|
||||
let memory: [u8; 4] = [
|
||||
0x78, 0x56, 0x34, 0x12, // 0x12345678
|
||||
];
|
||||
let insn = [0x66, 0xa5]; // movsw
|
||||
let regs = vec![(Register::ESI, 0), (Register::EDI, 0x8)];
|
||||
let mut data = [0u8; 2];
|
||||
|
||||
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
|
||||
|
||||
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
|
||||
|
||||
vmm.read_memory(0x8, &mut data).unwrap();
|
||||
assert_eq!(0x5678, <u16>::from_le_bytes(data));
|
||||
// Only two bytes were copied, so the value at 0xa should be zero
|
||||
vmm.read_memory(0xa, &mut data).unwrap();
|
||||
assert_eq!(0x0, <u16>::from_le_bytes(data));
|
||||
// The rest should be default value 0 from MockVmm
|
||||
vmm.read_memory(0x4, &mut data).unwrap();
|
||||
assert_eq!(0x0, <u16>::from_le_bytes(data));
|
||||
vmm.read_memory(0x8 + 8, &mut data).unwrap();
|
||||
assert_eq!(0x0, <u16>::from_le_bytes(data));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_movsb_m8_m8() {
|
||||
let ip: u64 = 0x1000;
|
||||
let memory: [u8; 4] = [
|
||||
0x78, 0x56, 0x34, 0x12, // 0x12345678
|
||||
];
|
||||
let insn = [0x66, 0xa4]; // movsb
|
||||
let regs = vec![(Register::ESI, 0), (Register::EDI, 0x8)];
|
||||
let mut data = [0u8; 1];
|
||||
|
||||
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
|
||||
|
||||
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
|
||||
|
||||
vmm.read_memory(0x8, &mut data).unwrap();
|
||||
assert_eq!(0x78, data[0]);
|
||||
// Only one byte was copied, so the value at 0x9 should be zero
|
||||
vmm.read_memory(0x9, &mut data).unwrap();
|
||||
assert_eq!(0x0, data[0]);
|
||||
// The rest should be default value 0 from MockVmm
|
||||
vmm.read_memory(0x4, &mut data).unwrap();
|
||||
assert_eq!(0x0, data[0]);
|
||||
// the src value is left as is after movb
|
||||
vmm.read_memory(0x0, &mut data).unwrap();
|
||||
assert_eq!(0x78, data[0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rep_movsb_m8_m8() {
|
||||
let ip: u64 = 0x1000;
|
||||
let memory: [u8; 16] = [
|
||||
0x78, 0x56, 0x34, 0x12, // 0x12345678
|
||||
0xbb, 0xaa, 0x00, 0x00, // 0x0000aabb
|
||||
0x00, 0x00, 0x00, 0x00, // 0x00000000
|
||||
0x00, 0x00, 0x00, 0x00, // 0x00000000
|
||||
];
|
||||
let insn = [0x66, 0xf3, 0xa4]; // rep movsw
|
||||
let regs = vec![(Register::ECX, 6), (Register::ESI, 0), (Register::EDI, 0x8)];
|
||||
let mut data = [0u8; 1];
|
||||
|
||||
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
|
||||
|
||||
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
|
||||
|
||||
vmm.read_memory(0x8, &mut data).unwrap();
|
||||
assert_eq!(0x78, data[0]);
|
||||
vmm.read_memory(0x8 + 1, &mut data).unwrap();
|
||||
assert_eq!(0x56, data[0]);
|
||||
vmm.read_memory(0x8 + 2, &mut data).unwrap();
|
||||
assert_eq!(0x34, data[0]);
|
||||
vmm.read_memory(0x8 + 3, &mut data).unwrap();
|
||||
assert_eq!(0x12, data[0]);
|
||||
vmm.read_memory(0x8 + 4, &mut data).unwrap();
|
||||
assert_eq!(0xbb, data[0]);
|
||||
vmm.read_memory(0x8 + 5, &mut data).unwrap();
|
||||
assert_eq!(0xaa, data[0]);
|
||||
// The rest should be default value 0 from MockVmm
|
||||
vmm.read_memory(0x8 + 6, &mut data).unwrap();
|
||||
assert_eq!(0x0, data[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,6 +526,8 @@ impl<'a, T: CpuStateManager> Emulator<'a, T> {
|
||||
(mov, Movzx_r64_rm16),
|
||||
// MOVS
|
||||
(movs, Movsd_m32_m32),
|
||||
(movs, Movsw_m16_m16),
|
||||
(movs, Movsb_m8_m8),
|
||||
// OR
|
||||
(or, Or_rm8_r8)
|
||||
);
|
||||
|
||||
@@ -237,6 +237,12 @@ pub enum HypervisorCpuError {
|
||||
///
|
||||
#[error("Failed to initialize PMU")]
|
||||
InitializePmu,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Error getting TSC frequency
|
||||
///
|
||||
#[error("Failed to get TSC frequency: {0}")]
|
||||
GetTscKhz(#[source] anyhow::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -432,4 +438,11 @@ pub trait Vcpu: Send + Sync {
|
||||
/// Return the list of initial MSR entries for a VCPU
|
||||
///
|
||||
fn boot_msr_entries(&self) -> Vec<MsrEntry>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Get the frequency of the TSC if available
|
||||
///
|
||||
fn tsc_khz(&self) -> Result<Option<u32>> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ pub trait Hypervisor: Send + Sync {
|
||||
///
|
||||
/// Get the supported CpuID
|
||||
///
|
||||
fn get_cpuid(&self) -> Result<Vec<CpuIdEntry>>;
|
||||
fn get_supported_cpuid(&self) -> Result<Vec<CpuIdEntry>>;
|
||||
///
|
||||
/// Check particular extensions if any
|
||||
///
|
||||
@@ -133,4 +133,7 @@ pub trait Hypervisor: Send + Sync {
|
||||
fn get_guest_debug_hw_bps(&self) -> usize {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Get maximum number of vCPUs
|
||||
fn get_max_vcpus(&self) -> u32;
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ pub use {
|
||||
const KVM_CAP_SGX_ATTRIBUTE: u32 = 196;
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
const KVM_EXIT_TDX: u32 = 35;
|
||||
const KVM_EXIT_TDX: u32 = 50;
|
||||
#[cfg(feature = "tdx")]
|
||||
const TDG_VP_VMCALL_GET_QUOTE: u64 = 0x10002;
|
||||
#[cfg(feature = "tdx")]
|
||||
@@ -330,21 +330,22 @@ impl KvmVm {
|
||||
Ok(VfioDeviceFd::new_from_kvm(device_fd))
|
||||
}
|
||||
/// Checks if a particular `Cap` is available.
|
||||
fn check_extension(&self, c: Cap) -> bool {
|
||||
pub fn check_extension(&self, c: Cap) -> bool {
|
||||
self.fd.check_extension(c)
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Implementation of Vm trait for KVM
|
||||
/// Example:
|
||||
/// #[cfg(feature = "kvm")]
|
||||
/// extern crate hypervisor
|
||||
/// let kvm = hypervisor::kvm::KvmHypervisor::new().unwrap();
|
||||
/// let hypervisor: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
|
||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||
/// vm.set/get().unwrap()
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use hypervisor::kvm::KvmHypervisor;
|
||||
/// # use std::sync::Arc;
|
||||
/// let kvm = KvmHypervisor::new().unwrap();
|
||||
/// let hypervisor = Arc::new(kvm);
|
||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||
/// ```
|
||||
impl vm::Vm for KvmVm {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
@@ -746,36 +747,34 @@ impl vm::Vm for KvmVm {
|
||||
///
|
||||
#[cfg(feature = "tdx")]
|
||||
fn tdx_init(&self, cpuid: &[CpuIdEntry], max_vcpus: u32) -> vm::Result<()> {
|
||||
use std::io::{Error, ErrorKind};
|
||||
let cpuid: Vec<kvm_bindings::kvm_cpuid_entry2> =
|
||||
const TDX_ATTR_SEPT_VE_DISABLE: usize = 28;
|
||||
|
||||
let mut cpuid: Vec<kvm_bindings::kvm_cpuid_entry2> =
|
||||
cpuid.iter().map(|e| (*e).into()).collect();
|
||||
let kvm_cpuid = kvm_bindings::CpuId::from_entries(&cpuid).map_err(|_| {
|
||||
vm::HypervisorVmError::InitializeTdx(Error::new(
|
||||
ErrorKind::Other,
|
||||
"failed to allocate CpuId",
|
||||
))
|
||||
})?;
|
||||
cpuid.resize(256, kvm_bindings::kvm_cpuid_entry2::default());
|
||||
|
||||
#[repr(C)]
|
||||
struct TdxInitVm {
|
||||
max_vcpus: u32,
|
||||
tsc_khz: u32,
|
||||
attributes: u64,
|
||||
cpuid: u64,
|
||||
max_vcpus: u32,
|
||||
padding: u32,
|
||||
mrconfigid: [u64; 6],
|
||||
mrowner: [u64; 6],
|
||||
mrownerconfig: [u64; 6],
|
||||
reserved: [u64; 43],
|
||||
cpuid_nent: u32,
|
||||
cpuid_padding: u32,
|
||||
cpuid_entries: [kvm_bindings::kvm_cpuid_entry2; 256],
|
||||
}
|
||||
let data = TdxInitVm {
|
||||
attributes: 1 << TDX_ATTR_SEPT_VE_DISABLE,
|
||||
max_vcpus,
|
||||
tsc_khz: 0,
|
||||
attributes: 0,
|
||||
cpuid: kvm_cpuid.as_fam_struct_ptr() as u64,
|
||||
padding: 0,
|
||||
mrconfigid: [0; 6],
|
||||
mrowner: [0; 6],
|
||||
mrownerconfig: [0; 6],
|
||||
reserved: [0; 43],
|
||||
cpuid_nent: cpuid.len() as u32,
|
||||
cpuid_padding: 0,
|
||||
cpuid_entries: cpuid.as_slice().try_into().unwrap(),
|
||||
};
|
||||
|
||||
tdx_command(
|
||||
@@ -837,19 +836,23 @@ impl vm::Vm for KvmVm {
|
||||
fn tdx_command(
|
||||
fd: &RawFd,
|
||||
command: TdxCommand,
|
||||
metadata: u32,
|
||||
flags: u32,
|
||||
data: u64,
|
||||
) -> std::result::Result<(), std::io::Error> {
|
||||
#[repr(C)]
|
||||
struct TdxIoctlCmd {
|
||||
command: TdxCommand,
|
||||
metadata: u32,
|
||||
flags: u32,
|
||||
data: u64,
|
||||
error: u64,
|
||||
unused: u64,
|
||||
}
|
||||
let cmd = TdxIoctlCmd {
|
||||
command,
|
||||
metadata,
|
||||
flags,
|
||||
data,
|
||||
error: 0,
|
||||
unused: 0,
|
||||
};
|
||||
// SAFETY: FFI call. All input parameters are valid.
|
||||
let ret = unsafe {
|
||||
@@ -915,13 +918,16 @@ impl KvmHypervisor {
|
||||
}
|
||||
}
|
||||
/// Implementation of Hypervisor trait for KVM
|
||||
/// Example:
|
||||
/// #[cfg(feature = "kvm")]
|
||||
/// extern crate hypervisor
|
||||
/// let kvm = hypervisor::kvm::KvmHypervisor::new().unwrap();
|
||||
/// let hypervisor: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
|
||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use hypervisor::kvm::KvmHypervisor;
|
||||
/// # use std::sync::Arc;
|
||||
/// let kvm = KvmHypervisor::new().unwrap();
|
||||
/// let hypervisor = Arc::new(kvm);
|
||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||
/// ```
|
||||
impl hypervisor::Hypervisor for KvmHypervisor {
|
||||
///
|
||||
/// Returns the type of the hypervisor
|
||||
@@ -930,13 +936,15 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
||||
HypervisorType::Kvm
|
||||
}
|
||||
/// Create a KVM vm object of a specific VM type and return the object as Vm trait object
|
||||
/// Example
|
||||
/// # extern crate hypervisor;
|
||||
/// # use hypervisor::KvmHypervisor;
|
||||
/// use hypervisor::KvmVm;
|
||||
/// let hypervisor = KvmHypervisor::new().unwrap();
|
||||
/// let vm = hypervisor.create_vm_with_type(KvmVmType::LegacyVm).unwrap()
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use hypervisor::kvm::KvmHypervisor;
|
||||
/// use hypervisor::kvm::KvmVm;
|
||||
/// let hypervisor = KvmHypervisor::new().unwrap();
|
||||
/// let vm = hypervisor.create_vm_with_type(0).unwrap();
|
||||
/// ```
|
||||
fn create_vm_with_type(&self, vm_type: u64) -> hypervisor::Result<Arc<dyn vm::Vm>> {
|
||||
let fd: VmFd;
|
||||
loop {
|
||||
@@ -990,13 +998,15 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
||||
}
|
||||
|
||||
/// Create a KVM vm object and return the object as Vm trait object
|
||||
/// Example
|
||||
/// # extern crate hypervisor;
|
||||
/// # use hypervisor::KvmHypervisor;
|
||||
/// use hypervisor::KvmVm;
|
||||
/// let hypervisor = KvmHypervisor::new().unwrap();
|
||||
/// let vm = hypervisor.create_vm().unwrap()
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use hypervisor::kvm::KvmHypervisor;
|
||||
/// use hypervisor::kvm::KvmVm;
|
||||
/// let hypervisor = KvmHypervisor::new().unwrap();
|
||||
/// let vm = hypervisor.create_vm().unwrap();
|
||||
/// ```
|
||||
fn create_vm(&self) -> hypervisor::Result<Arc<dyn vm::Vm>> {
|
||||
#[allow(unused_mut)]
|
||||
let mut vm_type: u64 = 0; // Create with default platform type
|
||||
@@ -1021,7 +1031,7 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
||||
///
|
||||
/// X86 specific call to get the system supported CPUID values.
|
||||
///
|
||||
fn get_cpuid(&self) -> hypervisor::Result<Vec<CpuIdEntry>> {
|
||||
fn get_supported_cpuid(&self) -> hypervisor::Result<Vec<CpuIdEntry>> {
|
||||
let kvm_cpuid = self
|
||||
.kvm
|
||||
.get_supported_cpuid(kvm_bindings::KVM_MAX_CPUID_ENTRIES)
|
||||
@@ -1074,6 +1084,11 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
||||
self.kvm.get_guest_debug_hw_bps() as usize
|
||||
}
|
||||
}
|
||||
|
||||
/// Get maximum number of vCPUs
|
||||
fn get_max_vcpus(&self) -> u32 {
|
||||
self.kvm.get_max_vcpus().min(u32::MAX as usize) as u32
|
||||
}
|
||||
}
|
||||
/// Vcpu struct for KVM
|
||||
pub struct KvmVcpu {
|
||||
@@ -1085,15 +1100,17 @@ pub struct KvmVcpu {
|
||||
hyperv_synic: AtomicBool,
|
||||
}
|
||||
/// Implementation of Vcpu trait for KVM
|
||||
/// Example:
|
||||
/// #[cfg(feature = "kvm")]
|
||||
/// extern crate hypervisor
|
||||
/// let kvm = hypervisor::kvm::KvmHypervisor::new().unwrap();
|
||||
/// let hypervisor: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use hypervisor::kvm::KvmHypervisor;
|
||||
/// # use std::sync::Arc;
|
||||
/// let kvm = KvmHypervisor::new().unwrap();
|
||||
/// let hypervisor = Arc::new(kvm);
|
||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||
/// let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||
/// vcpu.get/set().unwrap()
|
||||
///
|
||||
/// ```
|
||||
impl cpu::Vcpu for KvmVcpu {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
@@ -1795,11 +1812,10 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate hypervisor;
|
||||
/// # use hypervisor::KvmHypervisor;
|
||||
/// # use hypervisor::kvm::KvmHypervisor;
|
||||
/// # use std::sync::Arc;
|
||||
/// let kvm = hypervisor::kvm::KvmHypervisor::new().unwrap();
|
||||
/// let hv: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
|
||||
/// let kvm = KvmHypervisor::new().unwrap();
|
||||
/// let hv = Arc::new(kvm);
|
||||
/// let vm = hv.create_vm().expect("new VM fd creation failed");
|
||||
/// vm.enable_split_irq().unwrap();
|
||||
/// let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||
@@ -1967,11 +1983,10 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate hypervisor;
|
||||
/// # use hypervisor::KvmHypervisor;
|
||||
/// # use hypervisor::kvm::KvmHypervisor;
|
||||
/// # use std::sync::Arc;
|
||||
/// let kvm = hypervisor::kvm::KvmHypervisor::new().unwrap();
|
||||
/// let hv: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
|
||||
/// let kvm = KvmHypervisor::new().unwrap();
|
||||
/// let hv = Arc::new(kvm);
|
||||
/// let vm = hv.create_vm().expect("new VM fd creation failed");
|
||||
/// vm.enable_split_irq().unwrap();
|
||||
/// let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||
@@ -2154,6 +2169,23 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.set_device_attr(&cpu_attr)
|
||||
.map_err(|_| cpu::HypervisorCpuError::InitializePmu)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Get the frequency of the TSC if available
|
||||
///
|
||||
fn tsc_khz(&self) -> cpu::Result<Option<u32>> {
|
||||
match self.fd.get_tsc_khz() {
|
||||
Err(e) => {
|
||||
if e.errno() == libc::EIO {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(cpu::HypervisorCpuError::GetTscKhz(e.into()))
|
||||
}
|
||||
}
|
||||
Ok(v) => Ok(Some(v)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KvmVcpu {
|
||||
|
||||
@@ -50,6 +50,9 @@ pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
|
||||
if !kvm.check_extension(Cap::ImmediateExit) {
|
||||
return Err(KvmError::CapabilityMissing(Cap::ImmediateExit));
|
||||
}
|
||||
if !kvm.check_extension(Cap::GetTscKhz) {
|
||||
return Err(KvmError::CapabilityMissing(Cap::GetTscKhz));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
|
||||
@@ -48,9 +48,9 @@ mod cpu;
|
||||
/// Device related module
|
||||
mod device;
|
||||
|
||||
pub use crate::hypervisor::{Hypervisor, HypervisorError};
|
||||
pub use cpu::{HypervisorCpuError, Vcpu, VmExit};
|
||||
pub use device::HypervisorDeviceError;
|
||||
pub use hypervisor::{Hypervisor, HypervisorError};
|
||||
#[cfg(all(feature = "kvm", target_arch = "aarch64"))]
|
||||
pub use kvm::{aarch64, GicState};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -195,13 +195,16 @@ impl MshvHypervisor {
|
||||
}
|
||||
}
|
||||
/// Implementation of Hypervisor trait for Mshv
|
||||
/// Example:
|
||||
/// #[cfg(feature = "mshv")]
|
||||
/// extern crate hypervisor
|
||||
/// let mshv = hypervisor::mshv::MshvHypervisor::new().unwrap();
|
||||
/// let hypervisor: Arc<dyn hypervisor::Hypervisor> = Arc::new(mshv);
|
||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use hypervisor::mshv::MshvHypervisor;
|
||||
/// # use std::sync::Arc;
|
||||
/// let mshv = MshvHypervisor::new().unwrap();
|
||||
/// let hypervisor = Arc::new(mshv);
|
||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||
/// ```
|
||||
impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
///
|
||||
/// Returns the type of the hypervisor
|
||||
@@ -210,13 +213,16 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
HypervisorType::Mshv
|
||||
}
|
||||
/// Create a mshv vm object and return the object as Vm trait object
|
||||
/// Example
|
||||
/// # extern crate hypervisor;
|
||||
/// # use hypervisor::MshvHypervisor;
|
||||
/// use hypervisor::MshvVm;
|
||||
/// let hypervisor = MshvHypervisor::new().unwrap();
|
||||
/// let vm = hypervisor.create_vm().unwrap()
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate hypervisor;
|
||||
/// # use hypervisor::mshv::MshvHypervisor;
|
||||
/// use hypervisor::mshv::MshvVm;
|
||||
/// let hypervisor = MshvHypervisor::new().unwrap();
|
||||
/// let vm = hypervisor.create_vm().unwrap();
|
||||
/// ```
|
||||
fn create_vm(&self) -> hypervisor::Result<Arc<dyn vm::Vm>> {
|
||||
let fd: VmFd;
|
||||
loop {
|
||||
@@ -270,9 +276,16 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
///
|
||||
/// Get the supported CpuID
|
||||
///
|
||||
fn get_cpuid(&self) -> hypervisor::Result<Vec<CpuIdEntry>> {
|
||||
fn get_supported_cpuid(&self) -> hypervisor::Result<Vec<CpuIdEntry>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
/// Get maximum number of vCPUs
|
||||
fn get_max_vcpus(&self) -> u32 {
|
||||
// TODO: Using HV_MAXIMUM_PROCESSORS would be better
|
||||
// but the ioctl API is limited to u8
|
||||
256
|
||||
}
|
||||
}
|
||||
|
||||
/// Vcpu struct for Microsoft Hypervisor
|
||||
@@ -285,15 +298,17 @@ pub struct MshvVcpu {
|
||||
}
|
||||
|
||||
/// Implementation of Vcpu trait for Microsoft Hypervisor
|
||||
/// Example:
|
||||
/// #[cfg(feature = "mshv")]
|
||||
/// extern crate hypervisor
|
||||
/// let mshv = hypervisor::mshv::MshvHypervisor::new().unwrap();
|
||||
/// let hypervisor: Arc<dyn hypervisor::Hypervisor> = Arc::new(mshv);
|
||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||
/// let vcpu = vm.create_vcpu(0).unwrap();
|
||||
/// vcpu.get/set().unwrap()
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use hypervisor::mshv::MshvHypervisor;
|
||||
/// # use std::sync::Arc;
|
||||
/// let mshv = MshvHypervisor::new().unwrap();
|
||||
/// let hypervisor = Arc::new(mshv);
|
||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||
/// let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||
/// ```
|
||||
impl cpu::Vcpu for MshvVcpu {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
@@ -568,8 +583,14 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
///
|
||||
/// X86 specific call to setup the CPUID registers.
|
||||
///
|
||||
fn set_cpuid2(&self, _cpuid: &[CpuIdEntry]) -> cpu::Result<()> {
|
||||
Ok(())
|
||||
fn set_cpuid2(&self, cpuid: &[CpuIdEntry]) -> cpu::Result<()> {
|
||||
let cpuid: Vec<mshv_bindings::hv_cpuid_entry> = cpuid.iter().map(|e| (*e).into()).collect();
|
||||
let mshv_cpuid = <CpuId>::from_entries(&cpuid)
|
||||
.map_err(|_| cpu::HypervisorCpuError::SetCpuid(anyhow!("failed to create CpuId")))?;
|
||||
|
||||
self.fd
|
||||
.register_intercept_result_cpuid(&mshv_cpuid)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCpuid(e.into()))
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
@@ -917,15 +938,17 @@ impl MshvVm {
|
||||
|
||||
///
|
||||
/// Implementation of Vm trait for Mshv
|
||||
/// Example:
|
||||
/// #[cfg(feature = "mshv")]
|
||||
/// # extern crate hypervisor;
|
||||
/// # use hypervisor::MshvHypervisor;
|
||||
/// let mshv = MshvHypervisor::new().unwrap();
|
||||
/// let hypervisor: Arc<dyn hypervisor::Hypervisor> = Arc::new(mshv);
|
||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||
/// vm.set/get().unwrap()
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate hypervisor;
|
||||
/// # use hypervisor::mshv::MshvHypervisor;
|
||||
/// # use std::sync::Arc;
|
||||
/// let mshv = MshvHypervisor::new().unwrap();
|
||||
/// let hypervisor = Arc::new(mshv);
|
||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||
/// ```
|
||||
impl vm::Vm for MshvVm {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
|
||||
@@ -28,10 +28,12 @@ pub mod inn;
|
||||
// generated with bindgen /usr/include/linux/sockios.h --no-unstable-rust
|
||||
// --constified-enum '*' --with-derive-default
|
||||
pub mod sockios;
|
||||
pub use if_tun::*;
|
||||
pub use iff::*;
|
||||
pub use inn::*;
|
||||
pub use sockios::*;
|
||||
pub use if_tun::{
|
||||
sock_fprog, IFF_MULTI_QUEUE, IFF_NO_PI, IFF_TAP, IFF_VNET_HDR, TUN_F_CSUM, TUN_F_TSO4,
|
||||
TUN_F_TSO6, TUN_F_TSO_ECN, TUN_F_UFO,
|
||||
};
|
||||
pub use iff::{ifreq, net_device_flags_IFF_UP, setsockopt, sockaddr, AF_INET};
|
||||
pub use inn::sockaddr_in;
|
||||
|
||||
pub const TUNTAP: ::std::os::raw::c_uint = 84;
|
||||
|
||||
|
||||
@@ -12,17 +12,17 @@ log = "0.4.17"
|
||||
net_gen = { path = "../net_gen" }
|
||||
rate_limiter = { path = "../rate_limiter" }
|
||||
serde = "1.0.151"
|
||||
thiserror = "1.0.38"
|
||||
versionize = "0.1.9"
|
||||
thiserror = "1.0.39"
|
||||
versionize = "0.1.10"
|
||||
versionize_derive = "0.1.4"
|
||||
virtio-bindings = "0.1.0"
|
||||
virtio-queue = "0.7.0"
|
||||
virtio-bindings = "0.2.0"
|
||||
virtio-queue = "0.7.1"
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
[dev-dependencies]
|
||||
once_cell = "1.16.0"
|
||||
pnet = "0.31.0"
|
||||
pnet_datalink = "0.31.0"
|
||||
serde_json = "1.0.89"
|
||||
once_cell = "1.17.1"
|
||||
pnet = "0.33.0"
|
||||
pnet_datalink = "0.33.0"
|
||||
serde_json = "1.0.95"
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::GuestMemoryMmap;
|
||||
use crate::Tap;
|
||||
use libc::c_uint;
|
||||
use std::sync::Arc;
|
||||
use virtio_bindings::bindings::virtio_net::{
|
||||
use virtio_bindings::virtio_net::{
|
||||
VIRTIO_NET_CTRL_GUEST_OFFLOADS, VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, VIRTIO_NET_CTRL_MQ,
|
||||
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN,
|
||||
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_ERR, VIRTIO_NET_F_GUEST_CSUM,
|
||||
|
||||
@@ -21,7 +21,7 @@ use std::{io, mem, net};
|
||||
use thiserror::Error;
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use virtio_bindings::bindings::virtio_net::{
|
||||
use virtio_bindings::virtio_net::{
|
||||
virtio_net_hdr_v1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN,
|
||||
VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4,
|
||||
VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ,
|
||||
|
||||
@@ -10,20 +10,21 @@ kvm = ["vfio-ioctls/kvm"]
|
||||
mshv = ["vfio-ioctls/mshv"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.68"
|
||||
anyhow = "1.0.69"
|
||||
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" }
|
||||
vfio_user = { git = "https://github.com/rust-vmm/vfio-user", branch = "main" }
|
||||
|
||||
vmm-sys-util = "0.11.0"
|
||||
libc = "0.2.139"
|
||||
log = "0.4.17"
|
||||
serde = { version = "1.0.151", features = ["derive"] }
|
||||
thiserror = "1.0.38"
|
||||
versionize = "0.1.9"
|
||||
thiserror = "1.0.39"
|
||||
versionize = "0.1.10"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = "0.10.0"
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
|
||||
@@ -511,6 +511,16 @@ impl MsixCap {
|
||||
self.pba & 0xffff_fff8
|
||||
}
|
||||
|
||||
pub fn table_set_offset(&mut self, addr: u32) {
|
||||
self.table &= 0x7;
|
||||
self.table += addr;
|
||||
}
|
||||
|
||||
pub fn pba_set_offset(&mut self, addr: u32) {
|
||||
self.pba &= 0x7;
|
||||
self.pba += addr;
|
||||
}
|
||||
|
||||
pub fn table_bir(&self) -> u32 {
|
||||
self.table & 0x7
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use crate::{
|
||||
use anyhow::anyhow;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use hypervisor::HypervisorVmError;
|
||||
use libc::{sysconf, _SC_PAGESIZE};
|
||||
use std::any::Any;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::io;
|
||||
@@ -27,6 +28,9 @@ use vfio_bindings::bindings::vfio::*;
|
||||
use vfio_ioctls::{
|
||||
VfioContainer, VfioDevice, VfioIrq, VfioRegionInfoCap, VfioRegionSparseMmapArea,
|
||||
};
|
||||
use vm_allocator::page_size::{
|
||||
align_page_size_down, align_page_size_up, is_4k_aligned, is_4k_multiple, is_page_size_aligned,
|
||||
};
|
||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||
use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig,
|
||||
@@ -498,6 +502,29 @@ impl VfioCommon {
|
||||
Ok(vfio_common)
|
||||
}
|
||||
|
||||
/// In case msix table offset is not page size aligned, we need do some fixup to achive it.
|
||||
/// Becuse we don't want the MMIO RW region and trap region overlap each other.
|
||||
fn fixup_msix_region(&mut self, bar_id: u32, region_size: u64) -> u64 {
|
||||
let msix = self.interrupt.msix.as_mut().unwrap();
|
||||
let msix_cap = &mut msix.cap;
|
||||
|
||||
// Suppose table_bir equals to pba_bir here. Am I right?
|
||||
let (table_offset, table_size) = msix_cap.table_range();
|
||||
if is_page_size_aligned(table_offset) || msix_cap.table_bir() != bar_id {
|
||||
return region_size;
|
||||
}
|
||||
|
||||
let (pba_offset, pba_size) = msix_cap.pba_range();
|
||||
let msix_sz = align_page_size_up(table_size + pba_size);
|
||||
// Expand region to hold RW and trap region which both page size aligned
|
||||
let size = std::cmp::max(region_size * 2, msix_sz * 2);
|
||||
// let table starts from the middle of the region
|
||||
msix_cap.table_set_offset((size / 2) as u32);
|
||||
msix_cap.pba_set_offset((size / 2 + pba_offset - table_offset) as u32);
|
||||
|
||||
size
|
||||
}
|
||||
|
||||
pub(crate) fn allocate_bars(
|
||||
&mut self,
|
||||
allocator: &Arc<Mutex<SystemAllocator>>,
|
||||
@@ -661,9 +688,16 @@ impl VfioCommon {
|
||||
.ok_or(PciDeviceError::IoAllocationFailed(region_size))?
|
||||
}
|
||||
PciBarRegionType::Memory64BitRegion => {
|
||||
// BAR allocation must be naturally aligned
|
||||
// We need do some fixup to keep MMIO RW region and msix cap region page size
|
||||
// aligned.
|
||||
region_size = self.fixup_msix_region(bar_id, region_size);
|
||||
mmio_allocator
|
||||
.allocate(restored_bar_addr, region_size, Some(region_size))
|
||||
.allocate(
|
||||
restored_bar_addr,
|
||||
region_size,
|
||||
// SAFETY: FFI call. Trivially safe.
|
||||
Some(unsafe { sysconf(_SC_PAGESIZE) as GuestUsize }),
|
||||
)
|
||||
.ok_or(PciDeviceError::IoAllocationFailed(region_size))?
|
||||
}
|
||||
};
|
||||
@@ -800,6 +834,23 @@ impl VfioCommon {
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn get_msix_cap_idx(&self) -> Option<usize> {
|
||||
let mut cap_next = self
|
||||
.vfio_wrapper
|
||||
.read_config_byte(PCI_CONFIG_CAPABILITY_OFFSET);
|
||||
|
||||
while cap_next != 0 {
|
||||
let cap_id = self.vfio_wrapper.read_config_byte(cap_next.into());
|
||||
if PciCapabilityId::from(cap_id) == PciCapabilityId::MsiX {
|
||||
return Some(cap_next as usize);
|
||||
} else {
|
||||
cap_next = self.vfio_wrapper.read_config_byte((cap_next + 1).into());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn parse_capabilities(&mut self, bdf: PciBdf) {
|
||||
let mut cap_next = self
|
||||
.vfio_wrapper
|
||||
@@ -1154,6 +1205,15 @@ impl VfioCommon {
|
||||
return self.configuration.read_reg(reg_idx);
|
||||
}
|
||||
|
||||
if let Some(id) = self.get_msix_cap_idx() {
|
||||
let msix = self.interrupt.msix.as_mut().unwrap();
|
||||
if reg_idx * 4 == id + 4 {
|
||||
return msix.cap.table;
|
||||
} else if reg_idx * 4 == id + 8 {
|
||||
return msix.cap.pba;
|
||||
}
|
||||
}
|
||||
|
||||
// Since we don't support passing multi-functions devices, we should
|
||||
// mask the multi-function bit, bit 7 of the Header Type byte on the
|
||||
// register 3.
|
||||
@@ -1316,18 +1376,6 @@ impl VfioPciDevice {
|
||||
self.iommu_attached
|
||||
}
|
||||
|
||||
fn align_4k(address: u64) -> u64 {
|
||||
(address + 0xfff) & 0xffff_ffff_ffff_f000
|
||||
}
|
||||
|
||||
fn is_4k_aligned(address: u64) -> bool {
|
||||
(address & 0xfff) == 0
|
||||
}
|
||||
|
||||
fn is_4k_multiple(size: u64) -> bool {
|
||||
(size & 0xfff) == 0
|
||||
}
|
||||
|
||||
fn generate_sparse_areas(
|
||||
caps: &[VfioRegionInfoCap],
|
||||
region_index: u32,
|
||||
@@ -1339,14 +1387,14 @@ impl VfioPciDevice {
|
||||
match cap {
|
||||
VfioRegionInfoCap::SparseMmap(sparse_mmap) => return Ok(sparse_mmap.areas.clone()),
|
||||
VfioRegionInfoCap::MsixMappable => {
|
||||
if !Self::is_4k_aligned(region_start) {
|
||||
if !is_4k_aligned(region_start) {
|
||||
error!(
|
||||
"Region start address 0x{:x} must be at least aligned on 4KiB",
|
||||
region_start
|
||||
);
|
||||
return Err(VfioPciError::RegionAlignment);
|
||||
}
|
||||
if !Self::is_4k_multiple(region_size) {
|
||||
if !is_4k_multiple(region_size) {
|
||||
error!(
|
||||
"Region size 0x{:x} must be at least a multiple of 4KiB",
|
||||
region_size
|
||||
@@ -1358,7 +1406,8 @@ impl VfioPciDevice {
|
||||
// the MSI-X PBA table, we must calculate the subregions
|
||||
// around them, leading to a list of sparse areas.
|
||||
// We want to make sure we will still trap MMIO accesses
|
||||
// to these MSI-X specific ranges.
|
||||
// to these MSI-X specific ranges. If these region don't align
|
||||
// with pagesize, we can achive it by enlarging its range.
|
||||
//
|
||||
// Using a BtreeMap as the list provided through the iterator is sorted
|
||||
// by key. This ensures proper split of the whole region.
|
||||
@@ -1366,10 +1415,14 @@ impl VfioPciDevice {
|
||||
if let Some(msix) = vfio_msix {
|
||||
if region_index == msix.cap.table_bir() {
|
||||
let (offset, size) = msix.cap.table_range();
|
||||
let offset = align_page_size_down(offset);
|
||||
let size = align_page_size_up(size);
|
||||
inter_ranges.insert(offset, size);
|
||||
}
|
||||
if region_index == msix.cap.pba_bir() {
|
||||
let (offset, size) = msix.cap.pba_range();
|
||||
let offset = align_page_size_down(offset);
|
||||
let size = align_page_size_up(size);
|
||||
inter_ranges.insert(offset, size);
|
||||
}
|
||||
}
|
||||
@@ -1383,8 +1436,7 @@ impl VfioPciDevice {
|
||||
size: range_offset - current_offset,
|
||||
});
|
||||
}
|
||||
|
||||
current_offset = Self::align_4k(range_offset + range_size);
|
||||
current_offset = align_page_size_down(range_offset + range_size);
|
||||
}
|
||||
|
||||
if region_size > current_offset {
|
||||
@@ -1482,6 +1534,15 @@ impl VfioPciDevice {
|
||||
return Err(VfioPciError::MmapArea);
|
||||
}
|
||||
|
||||
if !is_page_size_aligned(area.size) || !is_page_size_aligned(area.offset) {
|
||||
warn!(
|
||||
"Could not mmap sparse area that is not page size aligned (offset = 0x{:x}, size = 0x{:x})",
|
||||
area.offset,
|
||||
area.size,
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let user_memory_region = UserMemoryRegion {
|
||||
slot: (self.memory_slot)(),
|
||||
start: region.start.0 + area.offset,
|
||||
|
||||
@@ -3,13 +3,13 @@ name = "performance-metrics"
|
||||
version = "0.1.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
build = "build.rs"
|
||||
build = "../build.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.0.32", features = ["wrap_help"] }
|
||||
argh = "0.1.9"
|
||||
dirs = "4.0.0"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.89"
|
||||
serde_json = "1.0.95"
|
||||
test_infra = { path = "../test_infra" }
|
||||
thiserror = "1.0.38"
|
||||
thiserror = "1.0.39"
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright © 2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let mut git_human_readable = "v".to_owned() + env!("CARGO_PKG_VERSION");
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This println!() has a special behavior, as it will set the environment
|
||||
// variable GIT_HUMAN_READABLE, so that it can be reused from the binary.
|
||||
// Particularly, this is used from the main.rs to display the exact
|
||||
// version information.
|
||||
println!("cargo:rustc-env=GIT_HUMAN_READABLE={git_human_readable}");
|
||||
}
|
||||
@@ -8,11 +8,11 @@ extern crate test_infra;
|
||||
|
||||
mod performance_tests;
|
||||
|
||||
use clap::{Arg, ArgAction, Command as ClapCommand};
|
||||
use argh::FromArgs;
|
||||
use performance_tests::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
env, fmt,
|
||||
fmt,
|
||||
process::Command,
|
||||
sync::{mpsc::channel, Arc},
|
||||
thread,
|
||||
@@ -110,18 +110,23 @@ impl Default for MetricsReport {
|
||||
#[derive(Default)]
|
||||
pub struct PerformanceTestOverrides {
|
||||
test_iterations: Option<u32>,
|
||||
test_timeout: Option<u32>,
|
||||
}
|
||||
|
||||
impl fmt::Display for PerformanceTestOverrides {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if let Some(test_iterations) = self.test_iterations {
|
||||
write!(f, "test_iterations = {test_iterations}")?;
|
||||
write!(f, "test_iterations = {test_iterations}, ")?;
|
||||
}
|
||||
if let Some(test_timeout) = self.test_timeout {
|
||||
write!(f, "test_timeout = {test_timeout}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PerformanceTestControl {
|
||||
test_timeout: u32,
|
||||
test_iterations: u32,
|
||||
@@ -188,7 +193,14 @@ impl PerformanceTest {
|
||||
.test_iterations
|
||||
.unwrap_or(self.control.test_iterations)
|
||||
{
|
||||
metrics.push((self.func_ptr)(&self.control));
|
||||
// update the timeout in control if passed explicitly and run testcase with it
|
||||
if let Some(test_timeout) = overrides.test_timeout {
|
||||
let mut control: PerformanceTestControl = self.control.clone();
|
||||
control.test_timeout = test_timeout;
|
||||
metrics.push((self.func_ptr)(&control));
|
||||
} else {
|
||||
metrics.push((self.func_ptr)(&self.control));
|
||||
}
|
||||
}
|
||||
|
||||
let mean = (self.unit_adjuster)(mean(&metrics).unwrap());
|
||||
@@ -207,9 +219,9 @@ impl PerformanceTest {
|
||||
|
||||
// Calculate the timeout for each test
|
||||
// Note: To cover the setup/cleanup time, 20s is added for each iteration of the test
|
||||
pub fn calc_timeout(&self, test_iterations: &Option<u32>) -> u64 {
|
||||
((self.control.test_timeout + 20) * test_iterations.unwrap_or(self.control.test_iterations))
|
||||
as u64
|
||||
pub fn calc_timeout(&self, test_iterations: &Option<u32>, test_timeout: &Option<u32>) -> u64 {
|
||||
((test_timeout.unwrap_or(self.control.test_timeout) + 20)
|
||||
* test_iterations.unwrap_or(self.control.test_iterations)) as u64
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,6 +599,7 @@ fn run_test_with_timeout(
|
||||
) -> Result<PerformanceTestResult, Error> {
|
||||
let (sender, receiver) = channel::<Result<PerformanceTestResult, Error>>();
|
||||
let test_iterations = overrides.test_iterations;
|
||||
let test_timeout = overrides.test_timeout;
|
||||
let overrides = overrides.clone();
|
||||
thread::spawn(move || {
|
||||
println!(
|
||||
@@ -609,7 +622,7 @@ fn run_test_with_timeout(
|
||||
});
|
||||
|
||||
// Todo: Need to cleanup/kill all hanging child processes
|
||||
let test_timeout = test.calc_timeout(&test_iterations);
|
||||
let test_timeout = test.calc_timeout(&test_iterations, &test_timeout);
|
||||
receiver
|
||||
.recv_timeout(Duration::from_secs(test_timeout))
|
||||
.map_err(|_| {
|
||||
@@ -626,39 +639,41 @@ fn date() -> String {
|
||||
String::from_utf8_lossy(&output.stdout).trim().to_string()
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
/// Generate the performance metrics data for Cloud Hypervisor
|
||||
struct Options {
|
||||
#[argh(switch, long = "list-tests")]
|
||||
/// print the list of available metrics tests
|
||||
list_tests: bool,
|
||||
|
||||
#[argh(option, long = "test-filter")]
|
||||
/// filter metrics tests to run based on provided keywords
|
||||
keywords: Vec<String>,
|
||||
|
||||
#[argh(option, long = "report-file")]
|
||||
/// report file. Stderr is used if not specified
|
||||
report_file: Option<String>,
|
||||
|
||||
#[argh(option, long = "iterations")]
|
||||
/// override number of test iterations
|
||||
iterations: Option<u32>,
|
||||
|
||||
#[argh(option, long = "timeout")]
|
||||
/// override test timeout, Ex. --timeout 5
|
||||
timeout: Option<u32>,
|
||||
|
||||
#[argh(switch, short = 'V', long = "version")]
|
||||
/// print version information
|
||||
version: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let cmd_arguments = ClapCommand::new("performance-metrics")
|
||||
.version(env!("GIT_HUMAN_READABLE"))
|
||||
.author(env!("CARGO_PKG_AUTHORS"))
|
||||
.about("Generate the performance metrics data for Cloud Hypervisor")
|
||||
.arg(
|
||||
Arg::new("test-filter")
|
||||
.long("test-filter")
|
||||
.help("Filter metrics tests to run based on provided keywords")
|
||||
.num_args(1)
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("list-tests")
|
||||
.long("list-tests")
|
||||
.help("Print the list of availale metrics tests")
|
||||
.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")
|
||||
.num_args(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("iterations")
|
||||
.long("iterations")
|
||||
.help("Override number of test iterations")
|
||||
.num_args(1),
|
||||
)
|
||||
.get_matches();
|
||||
let opts: Options = argh::from_env();
|
||||
|
||||
if opts.version {
|
||||
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILT_VERSION"));
|
||||
return;
|
||||
}
|
||||
|
||||
// It seems that the tool (ethr) used for testing the virtio-net latency
|
||||
// is not stable on AArch64, and therefore the latency test is currently
|
||||
@@ -668,7 +683,7 @@ fn main() {
|
||||
.filter(|t| !(cfg!(target_arch = "aarch64") && t.name == "virtio_net_latency_us"))
|
||||
.collect();
|
||||
|
||||
if cmd_arguments.get_flag("list-tests") {
|
||||
if opts.list_tests {
|
||||
for test in test_list.iter() {
|
||||
println!("\"{}\" ({})", test.name, test.control);
|
||||
}
|
||||
@@ -676,10 +691,7 @@ fn main() {
|
||||
return;
|
||||
}
|
||||
|
||||
let test_filter = match cmd_arguments.get_many::<String>("test-filter") {
|
||||
Some(s) => s.collect(),
|
||||
None => Vec::new(),
|
||||
};
|
||||
let test_filter = opts.keywords.iter().collect::<Vec<&String>>();
|
||||
|
||||
// Run performance tests sequentially and report results (in both readable/json format)
|
||||
let mut metrics_report: MetricsReport = Default::default();
|
||||
@@ -687,11 +699,8 @@ fn main() {
|
||||
init_tests();
|
||||
|
||||
let overrides = Arc::new(PerformanceTestOverrides {
|
||||
test_iterations: cmd_arguments
|
||||
.get_one::<String>("iterations")
|
||||
.map(|s| s.parse())
|
||||
.transpose()
|
||||
.unwrap_or_default(),
|
||||
test_iterations: opts.iterations,
|
||||
test_timeout: opts.timeout,
|
||||
});
|
||||
|
||||
for test in test_list.iter() {
|
||||
@@ -710,19 +719,18 @@ fn main() {
|
||||
|
||||
cleanup_tests();
|
||||
|
||||
let mut report_file: Box<dyn std::io::Write + Send> =
|
||||
if let Some(file) = cmd_arguments.get_one::<String>("report-file") {
|
||||
Box::new(
|
||||
std::fs::File::create(std::path::Path::new(file))
|
||||
.map_err(|e| {
|
||||
eprintln!("Error opening report file: {file}: {e}");
|
||||
std::process::exit(1);
|
||||
})
|
||||
.unwrap(),
|
||||
)
|
||||
} else {
|
||||
Box::new(std::io::stdout())
|
||||
};
|
||||
let mut report_file: Box<dyn std::io::Write + Send> = if let Some(ref file) = opts.report_file {
|
||||
Box::new(
|
||||
std::fs::File::create(std::path::Path::new(file))
|
||||
.map_err(|e| {
|
||||
eprintln!("Error opening report file: {file}: {e}");
|
||||
std::process::exit(1);
|
||||
})
|
||||
.unwrap(),
|
||||
)
|
||||
} else {
|
||||
Box::new(std::io::stdout())
|
||||
};
|
||||
|
||||
report_file
|
||||
.write_all(
|
||||
|
||||
@@ -168,7 +168,6 @@ fn parse_boot_time_output(output: &[u8]) -> Result<f64, Error> {
|
||||
std::panic::catch_unwind(|| {
|
||||
let l: Vec<String> = String::from_utf8_lossy(output)
|
||||
.lines()
|
||||
.into_iter()
|
||||
.filter(|l| l.contains("Debug I/O port: Kernel code"))
|
||||
.map(|l| l.to_string())
|
||||
.collect();
|
||||
@@ -359,11 +358,13 @@ pub fn performance_block_io(control: &PerformanceTestControl) -> f64 {
|
||||
guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!(
|
||||
"path={}",
|
||||
guest.disk_config.disk(DiskType::CloudInit).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!("path={BLK_IO_TEST_IMG}").as_str(),
|
||||
])
|
||||
.default_net()
|
||||
|
||||
@@ -66,7 +66,7 @@ impl QcowRawFile {
|
||||
non_zero_flags: u64,
|
||||
) -> io::Result<()> {
|
||||
self.file.seek(SeekFrom::Start(offset))?;
|
||||
let mut buffer = BufWriter::with_capacity(table.len() * size_of::<u64>(), &mut self.file);
|
||||
let mut buffer = BufWriter::with_capacity(std::mem::size_of_val(table), &mut self.file);
|
||||
for addr in table {
|
||||
let val = if *addr == 0 {
|
||||
0
|
||||
@@ -91,7 +91,7 @@ impl QcowRawFile {
|
||||
/// Writes a refcount block to the file.
|
||||
pub fn write_refcount_block(&mut self, offset: u64, table: &[u16]) -> io::Result<()> {
|
||||
self.file.seek(SeekFrom::Start(offset))?;
|
||||
let mut buffer = BufWriter::with_capacity(table.len() * size_of::<u16>(), &mut self.file);
|
||||
let mut buffer = BufWriter::with_capacity(std::mem::size_of_val(table), &mut self.file);
|
||||
for count in table {
|
||||
buffer.write_u16::<BigEndian>(*count)?;
|
||||
}
|
||||
|
||||
286
release-notes.md
286
release-notes.md
@@ -1,3 +1,21 @@
|
||||
- [v31.2](#v312)
|
||||
- [v31.1](#v311)
|
||||
- [v31.0](#v310)
|
||||
- [Update to Latest `acpi_tables`](#update-to-latest-acpi_tables)
|
||||
- [Update Reference Kernel to 6.2](#update-reference-kernel-to-62)
|
||||
- [Improvements on Console `SIGWINCH` Handler](#improvements-on-console-sigwinch-handler)
|
||||
- [Remove Directory Support from `MemoryZoneConfig::file`](#remove-directory-support-from-memoryzoneconfigfile)
|
||||
- [Documentation Improvements](#documentation-improvements)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||
- [Contributors](#contributors)
|
||||
- [v30.0](#v300)
|
||||
- [Command Line Changes for Reduced Binary Size](#command-line-changes-for-reduced-binary-size)
|
||||
- [Basic vfio-user Server Support](#basic-vfio-user-server-support)
|
||||
- [Heap Profiling Support](#heap-profiling-support)
|
||||
- [Documentation Improvements](#documentation-improvements-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
||||
- [Contributors](#contributors-1)
|
||||
- [v28.2](#v282)
|
||||
- [v29.0](#v290)
|
||||
- [Release Binary Supports Both MSHV and KVM](#release-binary-supports-both-mshv-and-kvm)
|
||||
- [Snapshot/Restore and Live Migration Improvements](#snapshotrestore-and-live-migration-improvements)
|
||||
@@ -6,10 +24,10 @@
|
||||
- [`AArch64` Documentation Integration](#aarch64-documentation-integration)
|
||||
- [`virtio-block` Counters Enhancement](#virtio-block-counters-enhancement)
|
||||
- [TCP Offload Control](#tcp-offload-control)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
||||
- [Removals](#removals)
|
||||
- [Deprecations](#deprecations)
|
||||
- [Contributors](#contributors)
|
||||
- [Contributors](#contributors-2)
|
||||
- [v28.1](#v281)
|
||||
- [v28.0](#v280)
|
||||
- [Community Engagement (Reminder)](#community-engagement-reminder)
|
||||
@@ -17,9 +35,9 @@
|
||||
- [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-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-3)
|
||||
- [Removals](#removals-1)
|
||||
- [Contributors](#contributors-1)
|
||||
- [Contributors](#contributors-3)
|
||||
- [v27.0](#v270)
|
||||
- [Community Engagement](#community-engagement)
|
||||
- [Prebuilt Packages](#prebuilt-packages)
|
||||
@@ -28,41 +46,41 @@
|
||||
- [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-2)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-4)
|
||||
- [Deprecations](#deprecations-1)
|
||||
- [Contributors](#contributors-2)
|
||||
- [Contributors](#contributors-4)
|
||||
- [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-3)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-5)
|
||||
- [Deprecations](#deprecations-2)
|
||||
- [Removals](#removals-2)
|
||||
- [Contributors](#contributors-3)
|
||||
- [Contributors](#contributors-5)
|
||||
- [v25.0](#v250)
|
||||
- [`ch-remote` Improvements](#ch-remote-improvements-1)
|
||||
- [VM "Coredump" Support](#vm-coredump-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-4)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-6)
|
||||
- [Removals](#removals-3)
|
||||
- [Contributors](#contributors-4)
|
||||
- [Contributors](#contributors-6)
|
||||
- [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-5)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-7)
|
||||
- [Notable Improvements](#notable-improvements)
|
||||
- [Deprecations](#deprecations-3)
|
||||
- [New on the Website](#new-on-the-website)
|
||||
- [Contributors](#contributors-5)
|
||||
- [Contributors](#contributors-7)
|
||||
- [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-6)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-8)
|
||||
- [Deprecations](#deprecations-4)
|
||||
- [Contributors](#contributors-6)
|
||||
- [Contributors](#contributors-8)
|
||||
- [v22.1](#v221)
|
||||
- [v22.0](#v220)
|
||||
- [GDB Debug Stub Support](#gdb-debug-stub-support)
|
||||
@@ -73,13 +91,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-7)
|
||||
- [Contributors](#contributors-7)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-9)
|
||||
- [Contributors](#contributors-9)
|
||||
- [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-8)
|
||||
- [Contributors](#contributors-8)
|
||||
- [Notable Bug fixes](#notable-bug-fixes-10)
|
||||
- [Contributors](#contributors-10)
|
||||
- [v20.2](#v202)
|
||||
- [v20.1](#v201)
|
||||
- [v20.0](#v200)
|
||||
@@ -88,8 +106,8 @@
|
||||
- [Improved VFIO support](#improved-vfio-support)
|
||||
- [Safer code](#safer-code)
|
||||
- [Extended documentation](#extended-documentation)
|
||||
- [Notable bug fixes](#notable-bug-fixes-9)
|
||||
- [Contributors](#contributors-9)
|
||||
- [Notable bug fixes](#notable-bug-fixes-11)
|
||||
- [Contributors](#contributors-11)
|
||||
- [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)
|
||||
@@ -97,8 +115,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-10)
|
||||
- [Contributors](#contributors-10)
|
||||
- [Notable bug fixes](#notable-bug-fixes-12)
|
||||
- [Contributors](#contributors-12)
|
||||
- [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)
|
||||
@@ -108,23 +126,23 @@
|
||||
- [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-11)
|
||||
- [Contributors](#contributors-11)
|
||||
- [Notable bug fixes](#notable-bug-fixes-13)
|
||||
- [Contributors](#contributors-13)
|
||||
- [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-12)
|
||||
- [Contributors](#contributors-12)
|
||||
- [Notable bug fixes](#notable-bug-fixes-14)
|
||||
- [Contributors](#contributors-14)
|
||||
- [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-13)
|
||||
- [Notable bug fixes](#notable-bug-fixes-15)
|
||||
- [Removed functionality](#removed-functionality)
|
||||
- [Contributors](#contributors-13)
|
||||
- [Contributors](#contributors-15)
|
||||
- [v15.0](#v150)
|
||||
- [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees)
|
||||
- [Network device rate limiting](#network-device-rate-limiting)
|
||||
@@ -132,7 +150,7 @@
|
||||
- [`--api-socket` supports file descriptor parameter](#--api-socket-supports-file-descriptor-parameter)
|
||||
- [Bug fixes](#bug-fixes)
|
||||
- [Deprecations](#deprecations-5)
|
||||
- [Contributors](#contributors-14)
|
||||
- [Contributors](#contributors-16)
|
||||
- [v0.14.1](#v0141)
|
||||
- [v0.14.0](#v0140)
|
||||
- [Structured event monitoring](#structured-event-monitoring)
|
||||
@@ -142,7 +160,7 @@
|
||||
- [PTY control for serial and `virtio-console`](#pty-control-for-serial-and-virtio-console)
|
||||
- [Block device rate limiting](#block-device-rate-limiting)
|
||||
- [Deprecations](#deprecations-6)
|
||||
- [Contributors](#contributors-15)
|
||||
- [Contributors](#contributors-17)
|
||||
- [v0.13.0](#v0130)
|
||||
- [Wider VFIO device support](#wider-vfio-device-support)
|
||||
- [Improved huge page support](#improved-huge-page-support)
|
||||
@@ -150,13 +168,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-16)
|
||||
- [Contributors](#contributors-18)
|
||||
- [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-17)
|
||||
- [Contributors](#contributors-19)
|
||||
- [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)
|
||||
@@ -168,15 +186,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-14)
|
||||
- [Contributors](#contributors-18)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-16)
|
||||
- [Contributors](#contributors-20)
|
||||
- [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-15)
|
||||
- [Contributors](#contributors-19)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-17)
|
||||
- [Contributors](#contributors-21)
|
||||
- [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)
|
||||
@@ -189,17 +207,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-16)
|
||||
- [Contributors](#contributors-20)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-18)
|
||||
- [Contributors](#contributors-22)
|
||||
- [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-17)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-19)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes)
|
||||
- [Contributors](#contributors-21)
|
||||
- [Contributors](#contributors-23)
|
||||
- [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)
|
||||
@@ -209,14 +227,14 @@
|
||||
- [`Seccomp` Sandboxing](#seccomp-sandboxing)
|
||||
- [Updated Distribution Support](#updated-distribution-support)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes-1)
|
||||
- [Contributors](#contributors-22)
|
||||
- [Contributors](#contributors-24)
|
||||
- [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-23)
|
||||
- [Contributors](#contributors-25)
|
||||
- [v0.5.1](#v051)
|
||||
- [v0.5.0](#v050)
|
||||
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
|
||||
@@ -224,7 +242,7 @@
|
||||
- [New Interrupt Management Framework](#new-interrupt-management-framework)
|
||||
- [Development Tools](#development-tools)
|
||||
- [Kata Containers Integration](#kata-containers-integration)
|
||||
- [Contributors](#contributors-24)
|
||||
- [Contributors](#contributors-26)
|
||||
- [v0.4.0](#v040)
|
||||
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
|
||||
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
|
||||
@@ -233,7 +251,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-25)
|
||||
- [Contributors](#contributors-27)
|
||||
- [v0.3.0](#v030)
|
||||
- [Block device offloading](#block-device-offloading)
|
||||
- [Network device backend](#network-device-backend)
|
||||
@@ -260,6 +278,172 @@
|
||||
- [Unit testing](#unit-testing)
|
||||
- [Integration tests parallelization](#integration-tests-parallelization)
|
||||
|
||||
# v31.2
|
||||
|
||||
This is a bug fix release. The following issues have been addressed:
|
||||
|
||||
* The number of vCPUs is capped at the hypervisor maximum (#5357)
|
||||
* Fixes for TTY reset (#5414)
|
||||
* CPU topology fixes on MSHV (#5325)
|
||||
* Seccomp fixes for older distributions (#5397)
|
||||
* Report errors explicitly to users when VM failed to boot (#5453)
|
||||
* Fix VFIO on platforms with non-4k page size (#5450, #5469)
|
||||
* Fix TDX initialization (#5454)
|
||||
* Ensure all guest memory regions are page-size aligned (#5496)
|
||||
* Fix seccomp filter lists related to virtio-console, serial and pty
|
||||
(#5506, #5524)
|
||||
* Populate APIC ID properly (#5512)
|
||||
* Ignore and warn TAP FDs in more situations (#5522)
|
||||
|
||||
# v31.1
|
||||
|
||||
This is a bug fix release. The following issues have been addressed:
|
||||
|
||||
* Ignore and warn TAP FDs sent via the HTTP request body (#5350)
|
||||
* Properly preserve and close valid FDs for TAP devices (#5373)
|
||||
* Only use `KVM_ARM_VCPU_PMU_V3` if available (#5360)
|
||||
* Only touch the tty flags if it's being used (#5343)
|
||||
* Fix seccomp filter lists for vhost-user devices (#5361)
|
||||
|
||||
# v31.0
|
||||
|
||||
This release has been tracked in our [roadmap
|
||||
project](https://github.com/orgs/cloud-hypervisor/projects/6) as iteration
|
||||
v31.0. The following user visible changes have been made:
|
||||
|
||||
### Update to Latest `acpi_tables`
|
||||
|
||||
Adapted to the latest [acpi_tables](https://github.com/rust-vmm/acpi_tables).
|
||||
There has been significant API changes in the crate.
|
||||
|
||||
### Update Reference Kernel to 6.2
|
||||
|
||||
Updated the recommended guest kernel version from 6.1.6 to 6.2.
|
||||
|
||||
### Improvements on Console `SIGWINCH` Handler
|
||||
|
||||
A separate thread had been created to capture the `SIGWINCH` signal and resize
|
||||
the guest console. Now the thread is skipped if the console is not resizable.
|
||||
|
||||
Two completely different code paths existed for handling console resizing, one
|
||||
for `tty` and the other for `pty`. That makes the understanding of the console
|
||||
handling code unnecessarily complicated. Now the code paths are unified. Both
|
||||
`tty` and `pty` are supported in single `SIGWINCH` handler. And the new handler
|
||||
can works with kernel versions earlier than v5.5.
|
||||
|
||||
### Remove Directory Support from `MemoryZoneConfig::file`
|
||||
|
||||
Setting a directory to `MemoryZoneConfig::file` is no longer supported.
|
||||
|
||||
Before this change, user can set a directory to `file` of the `--memory-zone`
|
||||
option. In that case, a temporary file will be created as the backing file for
|
||||
the `mmap(2)` operation. This functionality has been unnecessary since we had
|
||||
the native support for hugepages and allocating anonymous shared memory.
|
||||
|
||||
### Documentation Improvements
|
||||
|
||||
* Various improvements in API document
|
||||
* Improvements in Doc comments
|
||||
* Updated Slack channel information in README
|
||||
|
||||
### Notable Bug Fixes
|
||||
|
||||
* Fixed the offset setting while removing the entire mapping of `vhost-user` FS
|
||||
client.
|
||||
* Fixed the `ShutdownVmm` and `Shutdown` commands to call the correct API
|
||||
endpoint.
|
||||
|
||||
### Contributors
|
||||
|
||||
Many thanks to everyone who has contributed to our release:
|
||||
|
||||
* Alyssa Ross <hi@alyssa.is>
|
||||
* Bo Chen <chen.bo@intel.com>
|
||||
* Daniel Farina <daniel@fdr.io>
|
||||
* Dom <peng6662001@163.com>
|
||||
* Hao Xu <howeyxu@tencent.com>
|
||||
* Muminul Islam <muislam@microsoft.com>
|
||||
* Omer Faruk Bayram <omer.faruk@sartura.hr>
|
||||
* Ravi kumar Veeramally <ravikumar.veeramally@intel.com>
|
||||
* Rob Bradford <rbradford@rivosinc.com>
|
||||
* Ruslan Mstoi <ruslan.mstoi@intel.com>
|
||||
* Smit Gardhariya <gardhariya.smit@gmail.com>
|
||||
* Yang <ailin.yang@intel.com>
|
||||
* Yong He <alexyonghe@tencent.com>
|
||||
|
||||
# v30.0
|
||||
|
||||
This release has been tracked in our [roadmap
|
||||
project](https://github.com/orgs/cloud-hypervisor/projects/6) as iteration
|
||||
v30.0. The following user visible changes have been made:
|
||||
|
||||
### Command Line Changes for Reduced Binary Size
|
||||
|
||||
The `clap` crate was replaced by the `argh` crate to create our command
|
||||
line, which reduced our release binary size from 3.6MB to 3.3MB. There
|
||||
were several syntax changes:
|
||||
|
||||
* All `--option=value` commands now are `--option value`.
|
||||
* The `--disk DISK1 DISK2` command now is `--disk DISK1 --disk DISK2`.
|
||||
* The `-vvv` command now is `-v -v -v`
|
||||
|
||||
### Basic vfio-user Server Support
|
||||
|
||||
Our `vfio-user` crate is extended to provide basic server side support
|
||||
with an example of gpio vfio-user device. This crate now is moved to [its
|
||||
own repository](https://github.com/rust-vmm/vfio-user) under the
|
||||
`rust-vmm` organization.
|
||||
|
||||
### Heap Profiling Support
|
||||
|
||||
A new building target is added for profiling purposes with examples of
|
||||
heap profiling using `dhat` gated by the `dhat-heap` feature.
|
||||
|
||||
### Documentation Improvements
|
||||
|
||||
The documentation on Intel TDX is expanded with details of the building
|
||||
and using [TD-Shim](https://github.com/confidential-containers/td-shim),
|
||||
references to [TDX Tools](https://github.com/intel/tdx-tools), and
|
||||
version information of guest/host kernel/TDVF/TDShim being tested. Also,
|
||||
a new 'heap profiling' documentation is added with improvements on the
|
||||
existing 'profiling' documentation.
|
||||
|
||||
### Notable Bug Fixes
|
||||
|
||||
* Close FDs for TAP devices that are provided to VM (#5199, #5206)
|
||||
* Set vcpu thread status properly and signal `exit_evt` upon thread exit (#5211)
|
||||
* Populate CPUID leaf 0x4000_0010 (TSC frequency) (#5178, #5179)
|
||||
* Inform the TPM guest driver upon failed TPM requests on the host (#5151)
|
||||
* Bug fix to OpenAPI specification file (#5186)
|
||||
|
||||
### Contributors
|
||||
|
||||
Many thanks to everyone who has contributed to our release:
|
||||
|
||||
* Anatol Belski <anbelski@linux.microsoft.com>
|
||||
* Anirudh Rayabharam <anrayabh@linux.microsoft.com>
|
||||
* Bo Chen <chen.bo@intel.com>
|
||||
* Jinank Jain <jinankjain@microsoft.com>
|
||||
* Kaihang Zhang <kaihang.zhang@smartx.com>
|
||||
* Michael Zhao <michael.zhao@arm.com>
|
||||
* Muminul Islam <muislam@microsoft.com>
|
||||
* Philipp Schuster <philipp.schuster@cyberus-technology.de>
|
||||
* Praveen K Paladugu <prapal@linux.microsoft.com>
|
||||
* Ravi kumar Veeramally <ravikumar.veeramally@intel.com>
|
||||
* Rob Bradford <robert.bradford@intel.com>
|
||||
* Ruslan Mstoi <ruslan.mstoi@intel.com>
|
||||
* Sebastien Boeuf <sebastien.boeuf@intel.com>
|
||||
* Wei Liu <liuwe@microsoft.com>
|
||||
* Yong He <alexyonghe@tencent.com>
|
||||
* Yu Li <liyu.yukiteru@bytedance.com>
|
||||
|
||||
# v28.2
|
||||
This is a bug fix release. The following issues have been addressed:
|
||||
|
||||
* Fix QCOW2 refcount table size (#5034)
|
||||
* Fix unpause support on MSHV in dual binary (#5037)
|
||||
* Threads inside `virtio` devices are now shutdown on reboot (#5095)
|
||||
|
||||
# v29.0
|
||||
|
||||
This release has been tracked in our [roadmap
|
||||
@@ -406,7 +590,7 @@ different firmware options and to recommend the use of pre-built binaries.
|
||||
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:
|
||||
@@ -414,7 +598,7 @@ 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:
|
||||
@@ -921,7 +1105,7 @@ Many thanks to everyone who has contributed to our release:
|
||||
* Sebastien Boeuf <sebastien.boeuf@intel.com>
|
||||
* Wei Liu <liuwe@microsoft.com>
|
||||
* Ziye Yang <ziye.yang@intel.com>
|
||||
|
||||
|
||||
# v20.2
|
||||
|
||||
This is a bug fix release. The following issues have been addressed:
|
||||
@@ -1020,7 +1204,7 @@ significant improvements in the boot time of the guest.
|
||||
### Improved TDX support
|
||||
|
||||
When using the latest TDVF firmware the ACPI tables created by the VMM are now
|
||||
exposed via the firmware to the guest.
|
||||
exposed via the firmware to the guest.
|
||||
|
||||
### Live migration enhancements
|
||||
|
||||
@@ -1107,7 +1291,7 @@ tree.
|
||||
|
||||
### Power button support on AArch64
|
||||
|
||||
Use of the ACPI power button (e.g `ch-remote --api-socket=<API socket> power-button`)
|
||||
Use of the ACPI power button (e.g `ch-remote --api-socket=<API socket> power-button`)
|
||||
is now supported when running on AArch64.
|
||||
|
||||
### Notable bug fixes
|
||||
@@ -1519,7 +1703,7 @@ Highlights for `cloud-hypervisor` version 0.11.0 include:
|
||||
### `io_uring` support by default for `virtio-block`
|
||||
|
||||
Provided that the host OS supports it (Linux kernel 5.8+) then `io_uring` will
|
||||
be used for a significantly higher performance block device.
|
||||
be used for a significantly higher performance block device.
|
||||
|
||||
### Windows Guest Support
|
||||
|
||||
@@ -1693,7 +1877,7 @@ A new abstraction has been introduced, in the form of a `hypervisor` crate so as
|
||||
Multiple improvements have been made to the VM snapshot/restore support that was added in the last release. This includes persisting more vCPU state and in particular preserving the guest paravirtualized clock in order to avoid vCPU hangs inside the guest when running with multiple vCPUs.
|
||||
|
||||
### Virtio Memory Ballooning Support
|
||||
|
||||
|
||||
A `virtio-balloon` device has been added, controlled through the `resize` control, which allows the reclamation of host memory by resizing a memory balloon inside the guest.
|
||||
|
||||
### Enhancements to ARM64 Support
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# When changing this file don't forget to update the tag name in the
|
||||
# .github/workflows/docker-image.yaml file if doing multiple per day
|
||||
|
||||
FROM ubuntu:20.04 as dev
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG RUST_TOOLCHAIN="1.62.0"
|
||||
ARG RUST_TOOLCHAIN="1.67.1"
|
||||
ARG CLH_SRC_DIR="/cloud-hypervisor"
|
||||
ARG CLH_BUILD_DIR="$CLH_SRC_DIR/build"
|
||||
ARG CARGO_REGISTRY_DIR="$CLH_BUILD_DIR/cargo_registry"
|
||||
@@ -58,7 +60,10 @@ RUN update-alternatives --set ovs-vswitchd /usr/lib/openvswitch-switch-dpdk/ovs-
|
||||
RUN if [ "$TARGETARCH" = "amd64" ]; then \
|
||||
apt-get update \
|
||||
&& apt-get -yq upgrade \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -yq gcc-multilib \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -yq gcc-multilib gawk \
|
||||
libtool expect gnutls-dev gnutls-bin libfuse-dev \
|
||||
libjson-glib-dev libgmp-dev libtasn1-dev python3-twisted \
|
||||
net-tools softhsm2 \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*; fi
|
||||
|
||||
@@ -125,6 +130,25 @@ RUN if [ "$TARGETARCH" = "amd64" ]; then \
|
||||
&& cp -r ./scripts/rpc /usr/local/bin/spdk-nvme \
|
||||
&& cd .. && rm -rf spdk; fi
|
||||
|
||||
# install swtpm only for x86_64 arch
|
||||
RUN if [ "$TARGETARCH" = "amd64" ]; then \
|
||||
git clone https://github.com/stefanberger/libtpms libtpms_build \
|
||||
&& cd libtpms_build \
|
||||
&& git checkout v0.8.8 \
|
||||
&& ./autogen.sh \
|
||||
&& make \
|
||||
&& make install \
|
||||
&& cd .. \
|
||||
&& git clone https://github.com/stefanberger/swtpm swtpm_build \
|
||||
&& cd swtpm_build \
|
||||
&& git checkout v0.8.0 \
|
||||
&& ./autogen.sh \
|
||||
&& make \
|
||||
&& make install \
|
||||
&& cd .. \
|
||||
&& ldconfig \
|
||||
&& rm -rf swtpm_build libtpms_build; fi
|
||||
|
||||
# install ethr tool for performance tests
|
||||
RUN if [ "$TARGETARCH" = "amd64" ]; then \
|
||||
wget https://github.com/microsoft/ethr/releases/latest/download/ethr_linux.zip \
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,6 @@
|
||||
|
||||
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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -x
|
||||
|
||||
rm -f /tmp/ubuntu-cloudinit.img
|
||||
mkdosfs -n cidata -C /tmp/ubuntu-cloudinit.img 8192
|
||||
mkdosfs -n CIDATA -C /tmp/ubuntu-cloudinit.img 8192
|
||||
mcopy -oi /tmp/ubuntu-cloudinit.img -s test_data/cloud-init/ubuntu/user-data ::
|
||||
mcopy -oi /tmp/ubuntu-cloudinit.img -s test_data/cloud-init/ubuntu/meta-data ::
|
||||
mcopy -oi /tmp/ubuntu-cloudinit.img -s test_data/cloud-init/ubuntu/network-config ::
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
CLI_NAME="Cloud Hypervisor"
|
||||
|
||||
CTR_IMAGE_TAG="cloudhypervisor/dev"
|
||||
CTR_IMAGE_VERSION="20220705-0"
|
||||
CTR_IMAGE_TAG="ghcr.io/cloud-hypervisor/cloud-hypervisor"
|
||||
CTR_IMAGE_VERSION="20230316-0"
|
||||
CTR_IMAGE="${CTR_IMAGE_TAG}:${CTR_IMAGE_VERSION}"
|
||||
|
||||
DOCKER_RUNTIME="docker"
|
||||
@@ -168,10 +168,15 @@ process_volumes_args() {
|
||||
exported_volumes="$exported_volumes --volume $var"
|
||||
done
|
||||
}
|
||||
|
||||
cmd_help() {
|
||||
echo ""
|
||||
echo "Cloud Hypervisor $(basename "$0")"
|
||||
echo "Usage: $(basename "$0") <command> [<command args>]"
|
||||
echo "Usage: $(basename "$0") [flags] <command> [<command args>]"
|
||||
echo ""
|
||||
echo "Available flags":
|
||||
echo ""
|
||||
echo " --local Set the container image version being used to \"local\"."
|
||||
echo ""
|
||||
echo "Available commands:"
|
||||
echo ""
|
||||
@@ -379,6 +384,10 @@ cmd_tests() {
|
||||
exported_device="/dev/mshv"
|
||||
fi
|
||||
|
||||
if [ ! -e "${exported_device}" ] ; then
|
||||
die "${exported_device} does not exist on the system"
|
||||
fi
|
||||
|
||||
set -- '--hypervisor' "$hypervisor" "$@"
|
||||
|
||||
ensure_build_dir
|
||||
@@ -621,6 +630,12 @@ cmd_shell() {
|
||||
fix_dir_perms $?
|
||||
}
|
||||
|
||||
if [ $# = 0 ]; then
|
||||
cmd_help
|
||||
say_err "Please specify command to run!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse main command line args.
|
||||
#
|
||||
while [ $# -gt 0 ]; do
|
||||
|
||||
@@ -148,7 +148,7 @@ update_workloads() {
|
||||
popd
|
||||
|
||||
# Download Cloud Hypervisor binary from its last stable release
|
||||
LAST_RELEASE_VERSION="v26.0"
|
||||
LAST_RELEASE_VERSION="v30.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
|
||||
@@ -240,7 +240,9 @@ sudo bash -c "echo 10 > /sys/kernel/mm/ksm/sleep_millisecs"
|
||||
sudo bash -c "echo 1 > /sys/kernel/mm/ksm/run"
|
||||
|
||||
# Both test_vfio and ovs-dpdk rely on hugepages
|
||||
echo 6144 | sudo tee /proc/sys/vm/nr_hugepages
|
||||
HUGEPAGESIZE=`grep Hugepagesize /proc/meminfo | awk '{print $2}'`
|
||||
PAGE_NUM=`echo $((12288 * 1024 / $HUGEPAGESIZE))`
|
||||
echo $PAGE_NUM | sudo tee /proc/sys/vm/nr_hugepages
|
||||
sudo chmod a+rwX /dev/hugepages
|
||||
|
||||
# Run all direct kernel boot (Device Tree) test cases in mod `parallel`
|
||||
|
||||
@@ -46,7 +46,7 @@ fi
|
||||
popd
|
||||
|
||||
# Download Cloud Hypervisor binary from its last stable release
|
||||
LAST_RELEASE_VERSION="v26.0"
|
||||
LAST_RELEASE_VERSION="v30.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
|
||||
@@ -56,26 +56,8 @@ 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
|
||||
build_custom_linux
|
||||
fi
|
||||
|
||||
BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
|
||||
@@ -89,7 +71,9 @@ fi
|
||||
cargo build --no-default-features --features "kvm,mshv" --all --release --target $BUILD_TARGET
|
||||
|
||||
# Test ovs-dpdk relies on hugepages
|
||||
echo 6144 | sudo tee /proc/sys/vm/nr_hugepages
|
||||
HUGEPAGESIZE=`grep Hugepagesize /proc/meminfo | awk '{print $2}'`
|
||||
PAGE_NUM=`echo $((12288 * 1024 / $HUGEPAGESIZE))`
|
||||
echo $PAGE_NUM | sudo tee /proc/sys/vm/nr_hugepages
|
||||
sudo chmod a+rwX /dev/hugepages
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
@@ -45,29 +45,7 @@ if [ $? -ne 0 ]; then
|
||||
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_custom_linux
|
||||
|
||||
BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
|
||||
CFLAGS=""
|
||||
|
||||
@@ -21,7 +21,7 @@ rm -f $FW
|
||||
time wget --quiet $FW_URL || exit 1
|
||||
popd
|
||||
|
||||
JAMMY_OS_IMAGE_NAME="jammy-server-cloudimg-amd64-custom-20221118-1.qcow2"
|
||||
JAMMY_OS_IMAGE_NAME="jammy-server-cloudimg-amd64-custom-20230119-0.qcow2"
|
||||
JAMMY_OS_IMAGE_URL="https://cloud-hypervisor.azureedge.net/$JAMMY_OS_IMAGE_NAME"
|
||||
JAMMY_OS_IMAGE="$WORKLOADS_DIR/$JAMMY_OS_IMAGE_NAME"
|
||||
if [ ! -f "$JAMMY_OS_IMAGE" ]; then
|
||||
@@ -30,7 +30,7 @@ if [ ! -f "$JAMMY_OS_IMAGE" ]; then
|
||||
popd
|
||||
fi
|
||||
|
||||
JAMMY_OS_RAW_IMAGE_NAME="jammy-server-cloudimg-amd64-custom-20221118-1.raw"
|
||||
JAMMY_OS_RAW_IMAGE_NAME="jammy-server-cloudimg-amd64-custom-20230119-0.raw"
|
||||
JAMMY_OS_RAW_IMAGE="$WORKLOADS_DIR/$JAMMY_OS_RAW_IMAGE_NAME"
|
||||
if [ ! -f "$JAMMY_OS_RAW_IMAGE" ]; then
|
||||
pushd $WORKLOADS_DIR
|
||||
|
||||
@@ -12,9 +12,7 @@ FOCAL_OS_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_IMAGE_NAME"
|
||||
FW="$WORKLOADS_DIR/hypervisor-fw"
|
||||
|
||||
VMLINUX_IMAGE="$WORKLOADS_DIR/vmlinux"
|
||||
if [ ! -f "$VMLINUX_IMAGE" ]; then
|
||||
build_custom_linux
|
||||
fi
|
||||
build_custom_linux
|
||||
|
||||
BLK_IMAGE="$WORKLOADS_DIR/blk.img"
|
||||
MNT_DIR="mount_image"
|
||||
@@ -52,16 +50,13 @@ cp target/$BUILD_TARGET/release/cloud-hypervisor $VFIO_DIR
|
||||
cp target/$BUILD_TARGET/release/ch-remote $VFIO_DIR
|
||||
|
||||
# test_vfio rely on hugepages
|
||||
echo 6144 | sudo tee /proc/sys/vm/nr_hugepages
|
||||
HUGEPAGESIZE=`grep Hugepagesize /proc/meminfo | awk '{print $2}'`
|
||||
PAGE_NUM=`echo $((12288 * 1024 / $HUGEPAGESIZE))`
|
||||
echo $PAGE_NUM | sudo tee /proc/sys/vm/nr_hugepages
|
||||
sudo chmod a+rwX /dev/hugepages
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
time cargo test "vfio::test_vfio" -- ${test_binary_args[*]}
|
||||
time cargo test "vfio::test_nvidia" -- --test-threads=1 ${test_binary_args[*]}
|
||||
RES=$?
|
||||
|
||||
if [ $RES -eq 0 ]; then
|
||||
time cargo test "vfio::test_nvidia" -- --test-threads=1 ${test_binary_args[*]}
|
||||
RES=$?
|
||||
fi
|
||||
|
||||
exit $RES
|
||||
|
||||
@@ -11,7 +11,7 @@ test_features=""
|
||||
if [ "$hypervisor" = "mshv" ] ; then
|
||||
test_features="--no-default-features --features mshv"
|
||||
fi
|
||||
WIN_IMAGE_FILE="/root/workloads/windows-server-2019.raw"
|
||||
WIN_IMAGE_FILE="/root/workloads/windows-server-2022-amd64-2.raw"
|
||||
|
||||
WORKLOADS_DIR="/root/workloads"
|
||||
OVMF_FW_URL=$(curl --silent https://api.github.com/repos/cloud-hypervisor/edk2/releases/latest | grep "browser_download_url" | grep -o 'https://.*[^ "]')
|
||||
|
||||
@@ -53,7 +53,7 @@ if [ ! -f "$FOCAL_OS_RAW_IMAGE" ]; then
|
||||
popd
|
||||
fi
|
||||
|
||||
JAMMY_OS_IMAGE_NAME="jammy-server-cloudimg-amd64-custom-20221118-1.qcow2"
|
||||
JAMMY_OS_IMAGE_NAME="jammy-server-cloudimg-amd64-custom-20230119-0.qcow2"
|
||||
JAMMY_OS_IMAGE_URL="https://cloud-hypervisor.azureedge.net/$JAMMY_OS_IMAGE_NAME"
|
||||
JAMMY_OS_IMAGE="$WORKLOADS_DIR/$JAMMY_OS_IMAGE_NAME"
|
||||
if [ ! -f "$JAMMY_OS_IMAGE" ]; then
|
||||
@@ -62,7 +62,7 @@ if [ ! -f "$JAMMY_OS_IMAGE" ]; then
|
||||
popd
|
||||
fi
|
||||
|
||||
JAMMY_OS_RAW_IMAGE_NAME="jammy-server-cloudimg-amd64-custom-20221118-1.raw"
|
||||
JAMMY_OS_RAW_IMAGE_NAME="jammy-server-cloudimg-amd64-custom-20230119-0.raw"
|
||||
JAMMY_OS_RAW_IMAGE="$WORKLOADS_DIR/$JAMMY_OS_RAW_IMAGE_NAME"
|
||||
if [ ! -f "$JAMMY_OS_RAW_IMAGE" ]; then
|
||||
pushd $WORKLOADS_DIR
|
||||
@@ -169,7 +169,9 @@ sudo bash -c "echo 10 > /sys/kernel/mm/ksm/sleep_millisecs"
|
||||
sudo bash -c "echo 1 > /sys/kernel/mm/ksm/run"
|
||||
|
||||
# Both test_vfio, ovs-dpdk and vDPA tests rely on hugepages
|
||||
echo 6144 | sudo tee /proc/sys/vm/nr_hugepages
|
||||
HUGEPAGESIZE=`grep Hugepagesize /proc/meminfo | awk '{print $2}'`
|
||||
PAGE_NUM=`echo $((12288 * 1024 / $HUGEPAGESIZE))`
|
||||
echo $PAGE_NUM | sudo tee /proc/sys/vm/nr_hugepages
|
||||
sudo chmod a+rwX /dev/hugepages
|
||||
|
||||
# Update max locked memory to 'unlimited' to avoid issues with vDPA
|
||||
|
||||
@@ -95,7 +95,9 @@ fi
|
||||
cargo build --no-default-features --features "kvm,mshv" --all --release --target $BUILD_TARGET
|
||||
|
||||
# setup hugepages
|
||||
echo 6144 | sudo tee /proc/sys/vm/nr_hugepages
|
||||
HUGEPAGESIZE=`grep Hugepagesize /proc/meminfo | awk '{print $2}'`
|
||||
PAGE_NUM=`echo $((12288 * 1024 / $HUGEPAGESIZE))`
|
||||
echo $PAGE_NUM | sudo tee /proc/sys/vm/nr_hugepages
|
||||
sudo chmod a+rwX /dev/hugepages
|
||||
|
||||
if [ -n "$test_filter" ]; then
|
||||
|
||||
@@ -22,3 +22,4 @@ fi
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
cargo test --lib --bins --target $BUILD_TARGET --workspace ${cargo_args[@]} || exit 1
|
||||
cargo test --doc --target $BUILD_TARGET --workspace ${cargo_args[@]} || exit 1
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
d4a44acc6014d5f83dea1c625c43d677a95fa75f alpine-minirootfs-x86_64.tar.gz
|
||||
f1eccdc5e1b515dbad294426ab081b47ebfb97c0 focal-server-cloudimg-amd64-custom-20210609-0.qcow2
|
||||
7f5a8358243a96adf61f5c20139b29f308f2c0e3 focal-server-cloudimg-amd64-custom-20210609-0.raw
|
||||
b12e52203bc78400b2f099420b411b49ef4be7b8 jammy-server-cloudimg-amd64-custom-20221118-1.qcow2
|
||||
d843d5b249e8db3c0158eaf56ade895ed10342b5 jammy-server-cloudimg-amd64-custom-20221118-1.raw
|
||||
864c074e2f1bd753667a35188b510d83a1d62793 jammy-server-cloudimg-amd64-custom-20230119-0.qcow2
|
||||
24358ee053f94e7f710ea4ce9e7a63eff2a1eb25 jammy-server-cloudimg-amd64-custom-20230119-0.raw
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ build_custom_linux() {
|
||||
ARCH=$(uname -m)
|
||||
SRCDIR=$PWD
|
||||
LINUX_CUSTOM_DIR="$WORKLOADS_DIR/linux-custom"
|
||||
LINUX_CUSTOM_BRANCH="ch-5.15.12"
|
||||
LINUX_CUSTOM_BRANCH="ch-6.2"
|
||||
LINUX_CUSTOM_URL="https://github.com/cloud-hypervisor/linux.git"
|
||||
|
||||
checkout_repo "$LINUX_CUSTOM_DIR" "$LINUX_CUSTOM_URL" "$LINUX_CUSTOM_BRANCH"
|
||||
|
||||
@@ -7,7 +7,7 @@ use api_client::simple_api_command;
|
||||
use api_client::simple_api_command_with_fds;
|
||||
use api_client::simple_api_full_command;
|
||||
use api_client::Error as ApiClientError;
|
||||
use clap::{Arg, ArgAction, ArgMatches, Command};
|
||||
use argh::FromArgs;
|
||||
use option_parser::{ByteSized, ByteSizedParseError};
|
||||
use std::fmt;
|
||||
use std::io::Read;
|
||||
@@ -18,7 +18,6 @@ use std::process;
|
||||
enum Error {
|
||||
Connect(std::io::Error),
|
||||
ApiClient(ApiClientError),
|
||||
InvalidCpuCount(std::num::ParseIntError),
|
||||
InvalidMemorySize(ByteSizedParseError),
|
||||
InvalidBalloonSize(ByteSizedParseError),
|
||||
AddDeviceConfig(vmm::config::Error),
|
||||
@@ -40,7 +39,6 @@ impl fmt::Display for Error {
|
||||
match self {
|
||||
ApiClient(e) => e.fmt(f),
|
||||
Connect(e) => write!(f, "Error opening HTTP socket: {e}"),
|
||||
InvalidCpuCount(e) => write!(f, "Error parsing CPU count: {e}"),
|
||||
InvalidMemorySize(e) => write!(f, "Error parsing memory size: {e:?}"),
|
||||
InvalidBalloonSize(e) => write!(f, "Error parsing balloon size: {e:?}"),
|
||||
AddDeviceConfig(e) => write!(f, "Error parsing device syntax: {e}"),
|
||||
@@ -60,16 +58,10 @@ impl fmt::Display for Error {
|
||||
|
||||
fn resize_api_command(
|
||||
socket: &mut UnixStream,
|
||||
cpus: Option<&str>,
|
||||
memory: Option<&str>,
|
||||
balloon: Option<&str>,
|
||||
desired_vcpus: Option<u8>,
|
||||
memory: &Option<String>,
|
||||
balloon: &Option<String>,
|
||||
) -> Result<(), Error> {
|
||||
let desired_vcpus: Option<u8> = if let Some(cpus) = cpus {
|
||||
Some(cpus.parse().map_err(Error::InvalidCpuCount)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let desired_ram: Option<u64> = if let Some(memory) = memory {
|
||||
Some(
|
||||
memory
|
||||
@@ -325,366 +317,387 @@ fn create_api_command(socket: &mut UnixStream, path: &str) -> Result<(), Error>
|
||||
simple_api_command(socket, "PUT", "create", Some(&data)).map_err(Error::ApiClient)
|
||||
}
|
||||
|
||||
fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
let mut socket = UnixStream::connect(matches.get_one::<String>("api-socket").unwrap())
|
||||
.map_err(Error::Connect)?;
|
||||
fn do_command(toplevel: &TopLevel) -> Result<(), Error> {
|
||||
let mut socket =
|
||||
UnixStream::connect(toplevel.api_socket.as_deref().unwrap()).map_err(Error::Connect)?;
|
||||
|
||||
match matches.subcommand_name() {
|
||||
Some("info") => {
|
||||
match toplevel.command {
|
||||
SubCommandEnum::Boot(_) => {
|
||||
simple_api_command(&mut socket, "PUT", "boot", None).map_err(Error::ApiClient)
|
||||
}
|
||||
SubCommandEnum::Delete(_) => {
|
||||
simple_api_command(&mut socket, "PUT", "delete", None).map_err(Error::ApiClient)
|
||||
}
|
||||
SubCommandEnum::ShutdownVmm(_) => {
|
||||
simple_api_full_command(&mut socket, "PUT", "vmm.shutdown", None)
|
||||
.map_err(Error::ApiClient)
|
||||
}
|
||||
SubCommandEnum::Resume(_) => {
|
||||
simple_api_command(&mut socket, "PUT", "resume", None).map_err(Error::ApiClient)
|
||||
}
|
||||
SubCommandEnum::PowerButton(_) => {
|
||||
simple_api_command(&mut socket, "PUT", "power-button", None).map_err(Error::ApiClient)
|
||||
}
|
||||
SubCommandEnum::Reboot(_) => {
|
||||
simple_api_command(&mut socket, "PUT", "reboot", None).map_err(Error::ApiClient)
|
||||
}
|
||||
SubCommandEnum::Pause(_) => {
|
||||
simple_api_command(&mut socket, "PUT", "pause", None).map_err(Error::ApiClient)
|
||||
}
|
||||
SubCommandEnum::Info(_) => {
|
||||
simple_api_command(&mut socket, "GET", "info", None).map_err(Error::ApiClient)
|
||||
}
|
||||
Some("counters") => {
|
||||
SubCommandEnum::Counters(_) => {
|
||||
simple_api_command(&mut socket, "GET", "counters", None).map_err(Error::ApiClient)
|
||||
}
|
||||
Some("ping") => {
|
||||
SubCommandEnum::Ping(_) => {
|
||||
simple_api_full_command(&mut socket, "GET", "vmm.ping", None).map_err(Error::ApiClient)
|
||||
}
|
||||
Some("shutdown-vmm") => simple_api_full_command(&mut socket, "PUT", "vmm.shutdown", None)
|
||||
.map_err(Error::ApiClient),
|
||||
Some("resize") => resize_api_command(
|
||||
SubCommandEnum::Shutdown(_) => {
|
||||
simple_api_command(&mut socket, "PUT", "shutdown", None).map_err(Error::ApiClient)
|
||||
}
|
||||
SubCommandEnum::Resize(ref config) => {
|
||||
resize_api_command(&mut socket, config.cpus, &config.memory, &config.balloon)
|
||||
}
|
||||
SubCommandEnum::ResizeZone(ref config) => {
|
||||
resize_zone_api_command(&mut socket, &config.id, &config.size)
|
||||
}
|
||||
SubCommandEnum::AddDevice(ref config) => {
|
||||
add_device_api_command(&mut socket, &config.device_config)
|
||||
}
|
||||
SubCommandEnum::RemoveDevice(ref config) => {
|
||||
remove_device_api_command(&mut socket, &config.device_config)
|
||||
}
|
||||
SubCommandEnum::AddDisk(ref config) => {
|
||||
add_disk_api_command(&mut socket, &config.disk_config)
|
||||
}
|
||||
SubCommandEnum::AddFs(ref config) => add_fs_api_command(&mut socket, &config.fs_config),
|
||||
SubCommandEnum::AddPmem(ref config) => {
|
||||
add_pmem_api_command(&mut socket, &config.pmem_config)
|
||||
}
|
||||
SubCommandEnum::AddNet(ref config) => add_net_api_command(&mut socket, &config.net_config),
|
||||
SubCommandEnum::AddUserDevice(ref config) => {
|
||||
add_user_device_api_command(&mut socket, &config.device_config)
|
||||
}
|
||||
SubCommandEnum::AddVdpa(ref config) => {
|
||||
add_vdpa_api_command(&mut socket, &config.vdpa_config)
|
||||
}
|
||||
SubCommandEnum::AddVsock(ref config) => {
|
||||
add_vsock_api_command(&mut socket, &config.vsock_config)
|
||||
}
|
||||
SubCommandEnum::Snapshot(ref config) => {
|
||||
snapshot_api_command(&mut socket, &config.snapshot_config)
|
||||
}
|
||||
SubCommandEnum::Restore(ref config) => {
|
||||
restore_api_command(&mut socket, &config.restore_config)
|
||||
}
|
||||
SubCommandEnum::Coredump(ref config) => {
|
||||
coredump_api_command(&mut socket, &config.coredump_config)
|
||||
}
|
||||
SubCommandEnum::SendMigration(ref config) => send_migration_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("resize")
|
||||
.unwrap()
|
||||
.get_one::<String>("cpus")
|
||||
.map(|x| x as &str),
|
||||
matches
|
||||
.subcommand_matches("resize")
|
||||
.unwrap()
|
||||
.get_one::<String>("memory")
|
||||
.map(|x| x as &str),
|
||||
matches
|
||||
.subcommand_matches("resize")
|
||||
.unwrap()
|
||||
.get_one::<String>("balloon")
|
||||
.map(|x| x as &str),
|
||||
&config.send_migration_config,
|
||||
config.send_migration_local,
|
||||
),
|
||||
Some("resize-zone") => resize_zone_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("resize-zone")
|
||||
.unwrap()
|
||||
.get_one::<String>("id")
|
||||
.unwrap(),
|
||||
matches
|
||||
.subcommand_matches("resize-zone")
|
||||
.unwrap()
|
||||
.get_one::<String>("size")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-device") => add_device_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("add-device")
|
||||
.unwrap()
|
||||
.get_one::<String>("device_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("remove-device") => remove_device_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("remove-device")
|
||||
.unwrap()
|
||||
.get_one::<String>("id")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-disk") => add_disk_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("add-disk")
|
||||
.unwrap()
|
||||
.get_one::<String>("disk_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-fs") => add_fs_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("add-fs")
|
||||
.unwrap()
|
||||
.get_one::<String>("fs_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-pmem") => add_pmem_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("add-pmem")
|
||||
.unwrap()
|
||||
.get_one::<String>("pmem_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-net") => add_net_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("add-net")
|
||||
.unwrap()
|
||||
.get_one::<String>("net_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-user-device") => add_user_device_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("add-user-device")
|
||||
.unwrap()
|
||||
.get_one::<String>("device_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-vdpa") => add_vdpa_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("add-vdpa")
|
||||
.unwrap()
|
||||
.get_one::<String>("vdpa_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("add-vsock") => add_vsock_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("add-vsock")
|
||||
.unwrap()
|
||||
.get_one::<String>("vsock_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("snapshot") => snapshot_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("snapshot")
|
||||
.unwrap()
|
||||
.get_one::<String>("snapshot_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("restore") => restore_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("restore")
|
||||
.unwrap()
|
||||
.get_one::<String>("restore_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("coredump") => coredump_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("coredump")
|
||||
.unwrap()
|
||||
.get_one::<String>("coredump_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("send-migration") => send_migration_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("send-migration")
|
||||
.unwrap()
|
||||
.get_one::<String>("send_migration_config")
|
||||
.unwrap(),
|
||||
matches
|
||||
.subcommand_matches("send-migration")
|
||||
.unwrap()
|
||||
.get_flag("send_migration_local"),
|
||||
),
|
||||
Some("receive-migration") => receive_migration_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("receive-migration")
|
||||
.unwrap()
|
||||
.get_one::<String>("receive_migration_config")
|
||||
.unwrap(),
|
||||
),
|
||||
Some("create") => create_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
.subcommand_matches("create")
|
||||
.unwrap()
|
||||
.get_one::<String>("path")
|
||||
.unwrap(),
|
||||
),
|
||||
Some(c) => simple_api_command(&mut socket, "PUT", c, None).map_err(Error::ApiClient),
|
||||
None => unreachable!(),
|
||||
SubCommandEnum::ReceiveMigration(ref config) => {
|
||||
receive_migration_api_command(&mut socket, &config.receive_migration_config)
|
||||
}
|
||||
SubCommandEnum::Create(ref config) => create_api_command(&mut socket, &config.vm_config),
|
||||
SubCommandEnum::Version(_) => {
|
||||
// Already handled outside of this function
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[doc = "Remotely control a cloud-hypervisor VMM.\n\nPlease refer to cloud-hypervisor for configuration syntaxes."]
|
||||
struct TopLevel {
|
||||
#[argh(subcommand)]
|
||||
command: SubCommandEnum,
|
||||
|
||||
#[argh(option, long = "api-socket")]
|
||||
/// HTTP API socket path (UNIX domain socket)
|
||||
api_socket: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand)]
|
||||
enum SubCommandEnum {
|
||||
AddDevice(AddDeviceSubcommand),
|
||||
AddDisk(AddDiskSubcommand),
|
||||
AddFs(AddFsSubcommand),
|
||||
AddPmem(AddPmemSubcommand),
|
||||
AddNet(AddNetSubcommand),
|
||||
AddUserDevice(AddUserDeviceSubcommand),
|
||||
AddVdpa(AddVdpaSubcommand),
|
||||
AddVsock(AddVsockSubcommand),
|
||||
RemoveDevice(RemoveDeviceSubcommand),
|
||||
Info(InfoSubcommand),
|
||||
Counters(CountersSubcommand),
|
||||
Pause(PauseSubcommand),
|
||||
Reboot(RebootSubcommand),
|
||||
PowerButton(PowerButtonSubcommand),
|
||||
Resume(ResumeSubcommand),
|
||||
Boot(BootSubcommand),
|
||||
Delete(DeleteSubcommand),
|
||||
Shutdown(ShutdownSubcommand),
|
||||
Ping(PingSubcommand),
|
||||
ShutdownVmm(ShutdownVmmSubcommand),
|
||||
Resize(ResizeSubcommand),
|
||||
ResizeZone(ResizeZoneSubcommand),
|
||||
Snapshot(SnapshotSubcommand),
|
||||
Restore(RestoreSubcommand),
|
||||
Coredump(CoredumpSubcommand),
|
||||
SendMigration(SendMigrationSubcommand),
|
||||
ReceiveMigration(ReceiveMigrationSubcommand),
|
||||
Create(CreateSubcommand),
|
||||
Version(VersionSubcommand),
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "add-device")]
|
||||
/// Add VFIO device
|
||||
struct AddDeviceSubcommand {
|
||||
#[argh(positional)]
|
||||
/// device config
|
||||
device_config: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "add-disk")]
|
||||
/// Add block device
|
||||
struct AddDiskSubcommand {
|
||||
#[argh(positional)]
|
||||
/// disk config
|
||||
disk_config: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "add-fs")]
|
||||
/// Add virtio-fs backed fs device
|
||||
struct AddFsSubcommand {
|
||||
#[argh(positional)]
|
||||
/// virtio-fs config
|
||||
fs_config: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "add-pmem")]
|
||||
/// Add virtio-fs backed fs device
|
||||
struct AddPmemSubcommand {
|
||||
#[argh(positional)]
|
||||
/// pmem config
|
||||
pmem_config: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "add-net")]
|
||||
/// Add virtio-fs backed fs device
|
||||
struct AddNetSubcommand {
|
||||
#[argh(positional)]
|
||||
/// net config
|
||||
net_config: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "add-user-device")]
|
||||
/// Add userspace device
|
||||
struct AddUserDeviceSubcommand {
|
||||
#[argh(positional)]
|
||||
/// device config
|
||||
device_config: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "add-vdpa")]
|
||||
/// Add vdpa device
|
||||
struct AddVdpaSubcommand {
|
||||
#[argh(positional)]
|
||||
/// vdpa config
|
||||
vdpa_config: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "add-vsock")]
|
||||
/// Add vsock device
|
||||
struct AddVsockSubcommand {
|
||||
#[argh(positional)]
|
||||
/// vsock config
|
||||
vsock_config: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "remove-device")]
|
||||
/// Remove VFIO device
|
||||
struct RemoveDeviceSubcommand {
|
||||
#[argh(positional)]
|
||||
/// device config
|
||||
device_config: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "info")]
|
||||
/// Information on the VM
|
||||
struct InfoSubcommand {}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "counters")]
|
||||
/// Counters from the VM
|
||||
struct CountersSubcommand {}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "pause")]
|
||||
/// Pause the VM
|
||||
struct PauseSubcommand {}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "reboot")]
|
||||
/// Reboot the VM
|
||||
struct RebootSubcommand {}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "power-button")]
|
||||
/// Trigger a power button in the VM
|
||||
struct PowerButtonSubcommand {}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "resume")]
|
||||
/// Resume the VM
|
||||
struct ResumeSubcommand {}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "boot")]
|
||||
/// Boot a created VM
|
||||
struct BootSubcommand {}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "delete")]
|
||||
/// Delete a VM
|
||||
struct DeleteSubcommand {}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "shutdown")]
|
||||
/// Shutdown a VM
|
||||
struct ShutdownSubcommand {}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "ping")]
|
||||
/// Ping the VMM to check for API server availability
|
||||
struct PingSubcommand {}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "shutdown-vmm")]
|
||||
/// Shutdown the VMM
|
||||
struct ShutdownVmmSubcommand {}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "resize")]
|
||||
/// Resize the VM
|
||||
struct ResizeSubcommand {
|
||||
#[argh(option, long = "cpus")]
|
||||
/// new VCPUs count
|
||||
cpus: Option<u8>,
|
||||
|
||||
#[argh(option, long = "memory")]
|
||||
/// new memory size in bytes (supports K/M/G suffix)"
|
||||
memory: Option<String>,
|
||||
|
||||
#[argh(option, long = "balloon")]
|
||||
/// new balloon size in bytes (supports K/M/G suffix)"
|
||||
balloon: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "resize-zone")]
|
||||
/// Resize a memory zone
|
||||
struct ResizeZoneSubcommand {
|
||||
#[argh(option, long = "id")]
|
||||
/// memory zone identifier
|
||||
id: String,
|
||||
|
||||
#[argh(option, long = "size")]
|
||||
/// new memory size in bytes (supports K/M/G suffix)"
|
||||
size: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "snapshot")]
|
||||
/// Create a snapshot from VM
|
||||
struct SnapshotSubcommand {
|
||||
#[argh(positional)]
|
||||
/// destination_url
|
||||
snapshot_config: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "restore")]
|
||||
/// Restore VM from a snapshot
|
||||
struct RestoreSubcommand {
|
||||
#[argh(positional)]
|
||||
/// restore config
|
||||
restore_config: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "coredump")]
|
||||
/// Create a coredump from VM
|
||||
struct CoredumpSubcommand {
|
||||
#[argh(positional)]
|
||||
/// coredump config
|
||||
coredump_config: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "send-migration")]
|
||||
/// Initiate a VM migration
|
||||
struct SendMigrationSubcommand {
|
||||
#[argh(switch, long = "local")]
|
||||
/// local migration
|
||||
send_migration_local: bool,
|
||||
|
||||
#[argh(positional)]
|
||||
/// destination_url
|
||||
send_migration_config: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "receive-migration")]
|
||||
/// Receive a VM migration
|
||||
struct ReceiveMigrationSubcommand {
|
||||
#[argh(positional)]
|
||||
/// receiver url
|
||||
receive_migration_config: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "create")]
|
||||
/// Create a VM from a JSON configuration
|
||||
struct CreateSubcommand {
|
||||
#[argh(positional, default = "String::from(\"-\")")]
|
||||
/// vm config
|
||||
vm_config: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand, name = "version")]
|
||||
/// Print version information
|
||||
struct VersionSubcommand {}
|
||||
|
||||
fn main() {
|
||||
let app = Command::new("ch-remote")
|
||||
.author(env!("CARGO_PKG_AUTHORS"))
|
||||
.subcommand_required(true)
|
||||
.about("Remotely control a cloud-hypervisor VMM.")
|
||||
.arg(
|
||||
Arg::new("api-socket")
|
||||
.long("api-socket")
|
||||
.help("HTTP API socket path (UNIX domain socket).")
|
||||
.num_args(1)
|
||||
.required(true),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("add-device").about("Add VFIO device").arg(
|
||||
Arg::new("device_config")
|
||||
.index(1)
|
||||
.help(vmm::config::DeviceConfig::SYNTAX),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("add-disk").about("Add block device").arg(
|
||||
Arg::new("disk_config")
|
||||
.index(1)
|
||||
.help(vmm::config::DiskConfig::SYNTAX),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("add-fs")
|
||||
.about("Add virtio-fs backed fs device")
|
||||
.arg(
|
||||
Arg::new("fs_config")
|
||||
.index(1)
|
||||
.help(vmm::config::FsConfig::SYNTAX),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("add-pmem")
|
||||
.about("Add persistent memory device")
|
||||
.arg(
|
||||
Arg::new("pmem_config")
|
||||
.index(1)
|
||||
.help(vmm::config::PmemConfig::SYNTAX),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("add-net").about("Add network device").arg(
|
||||
Arg::new("net_config")
|
||||
.index(1)
|
||||
.help(vmm::config::NetConfig::SYNTAX),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("add-user-device")
|
||||
.about("Add userspace device")
|
||||
.arg(
|
||||
Arg::new("device_config")
|
||||
.index(1)
|
||||
.help(vmm::config::UserDeviceConfig::SYNTAX),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("add-vdpa").about("Add vDPA device").arg(
|
||||
Arg::new("vdpa_config")
|
||||
.index(1)
|
||||
.help(vmm::config::VdpaConfig::SYNTAX),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("add-vsock").about("Add vsock device").arg(
|
||||
Arg::new("vsock_config")
|
||||
.index(1)
|
||||
.help(vmm::config::VsockConfig::SYNTAX),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("remove-device")
|
||||
.about("Remove VFIO device")
|
||||
.arg(Arg::new("id").index(1).help("<device_id>")),
|
||||
)
|
||||
.subcommand(Command::new("info").about("Info on the VM"))
|
||||
.subcommand(Command::new("counters").about("Counters from the VM"))
|
||||
.subcommand(Command::new("pause").about("Pause the VM"))
|
||||
.subcommand(Command::new("reboot").about("Reboot the VM"))
|
||||
.subcommand(Command::new("power-button").about("Trigger a power button in the VM"))
|
||||
.subcommand(
|
||||
Command::new("resize")
|
||||
.about("Resize the VM")
|
||||
.arg(
|
||||
Arg::new("cpus")
|
||||
.long("cpus")
|
||||
.help("New vCPUs count")
|
||||
.num_args(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("memory")
|
||||
.long("memory")
|
||||
.help("New memory size in bytes (supports K/M/G suffix)")
|
||||
.num_args(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("balloon")
|
||||
.long("balloon")
|
||||
.help("New balloon size in bytes (supports K/M/G suffix)")
|
||||
.num_args(1),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("resize-zone")
|
||||
.about("Resize a memory zone")
|
||||
.arg(
|
||||
Arg::new("id")
|
||||
.long("id")
|
||||
.help("Memory zone identifier")
|
||||
.num_args(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("size")
|
||||
.long("size")
|
||||
.help("New memory zone size in bytes (supports K/M/G suffix)")
|
||||
.num_args(1),
|
||||
),
|
||||
)
|
||||
.subcommand(Command::new("resume").about("Resume the VM"))
|
||||
.subcommand(Command::new("boot").about("Boot a created VM"))
|
||||
.subcommand(Command::new("delete").about("Delete a VM"))
|
||||
.subcommand(Command::new("shutdown").about("Shutdown the VM"))
|
||||
.subcommand(
|
||||
Command::new("snapshot")
|
||||
.about("Create a snapshot from VM")
|
||||
.arg(
|
||||
Arg::new("snapshot_config")
|
||||
.index(1)
|
||||
.help("<destination_url>"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("restore")
|
||||
.about("Restore VM from a snapshot")
|
||||
.arg(
|
||||
Arg::new("restore_config")
|
||||
.index(1)
|
||||
.help(vmm::config::RestoreConfig::SYNTAX),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("coredump")
|
||||
.about("Create a coredump from VM")
|
||||
.arg(Arg::new("coredump_config").index(1).help("<file_path>")),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("send-migration")
|
||||
.about("Initiate a VM migration")
|
||||
.arg(
|
||||
Arg::new("send_migration_config")
|
||||
.index(1)
|
||||
.help("<destination_url>"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("send_migration_local")
|
||||
.long("local")
|
||||
.num_args(0)
|
||||
.action(ArgAction::SetTrue),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("receive-migration")
|
||||
.about("Receive a VM migration")
|
||||
.arg(
|
||||
Arg::new("receive_migration_config")
|
||||
.index(1)
|
||||
.help("<receiver_url>"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("create")
|
||||
.about("Create VM from a JSON configuration")
|
||||
.arg(Arg::new("path").index(1).default_value("-")),
|
||||
)
|
||||
.subcommand(Command::new("ping").about("Ping the VMM to check for API server availability"))
|
||||
.subcommand(Command::new("shutdown-vmm").about("Shutdown the VMM"));
|
||||
let toplevel: TopLevel = argh::from_env();
|
||||
|
||||
let matches = app.get_matches();
|
||||
if matches!(toplevel.command, SubCommandEnum::Version(_)) {
|
||||
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILT_VERSION"));
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(e) = do_command(&matches) {
|
||||
if toplevel.api_socket.is_none() {
|
||||
println!("Please specify --api-socket");
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
if let Err(e) = do_command(&toplevel) {
|
||||
eprintln!("Error running command: {e}");
|
||||
process::exit(1)
|
||||
};
|
||||
|
||||
735
src/main.rs
735
src/main.rs
@@ -6,9 +6,9 @@
|
||||
#[macro_use]
|
||||
extern crate event_monitor;
|
||||
|
||||
use clap::{Arg, ArgAction, ArgGroup, ArgMatches, Command};
|
||||
use argh::FromArgs;
|
||||
use libc::EFD_NONBLOCK;
|
||||
use log::LevelFilter;
|
||||
use log::{warn, LevelFilter};
|
||||
use option_parser::OptionParser;
|
||||
use seccompiler::SeccompAction;
|
||||
use signal_hook::consts::SIGSYS;
|
||||
@@ -21,7 +21,10 @@ use thiserror::Error;
|
||||
use vmm::config;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
use vmm_sys_util::signal::block_signal;
|
||||
use vmm_sys_util::terminal::Terminal;
|
||||
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
#[global_allocator]
|
||||
static ALLOC: dhat::Alloc = dhat::Alloc;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum Error {
|
||||
@@ -30,6 +33,8 @@ enum Error {
|
||||
#[cfg(feature = "guest_debug")]
|
||||
#[error("Failed to create Debug EventFd: {0}")]
|
||||
CreateDebugEventFd(#[source] std::io::Error),
|
||||
#[error("Failed to create exit EventFd: {0}")]
|
||||
CreateExitEventFd(#[source] std::io::Error),
|
||||
#[error("Failed to open hypervisor interface (is hypervisor interface available?): {0}")]
|
||||
CreateHypervisor(#[source] hypervisor::HypervisorError),
|
||||
#[error("Failed to start the VMM thread: {0}")]
|
||||
@@ -87,9 +92,9 @@ impl log::Log for Logger {
|
||||
let duration = now.duration_since(self.start);
|
||||
|
||||
if record.file().is_some() && record.line().is_some() {
|
||||
writeln!(
|
||||
write!(
|
||||
*(*(self.output.lock().unwrap())),
|
||||
"cloud-hypervisor: {:.6?}: <{}> {}:{}:{} -- {}",
|
||||
"cloud-hypervisor: {:.6?}: <{}> {}:{}:{} -- {}\r\n",
|
||||
duration,
|
||||
std::thread::current().name().unwrap_or("anonymous"),
|
||||
record.level(),
|
||||
@@ -98,9 +103,9 @@ impl log::Log for Logger {
|
||||
record.args()
|
||||
)
|
||||
} else {
|
||||
writeln!(
|
||||
write!(
|
||||
*(*(self.output.lock().unwrap())),
|
||||
"cloud-hypervisor: {:.6?}: <{}> {}:{} -- {}",
|
||||
"cloud-hypervisor: {:.6?}: <{}> {}:{} -- {}\r\n",
|
||||
duration,
|
||||
std::thread::current().name().unwrap_or("anonymous"),
|
||||
record.level(),
|
||||
@@ -113,296 +118,273 @@ impl log::Log for Logger {
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
fn prepare_default_values() -> (String, String, String) {
|
||||
let default_vcpus =
|
||||
format! {"boot={},max_phys_bits={}", config::DEFAULT_VCPUS,config::DEFAULT_MAX_PHYS_BITS};
|
||||
let default_memory = format! {"size={}M", config::DEFAULT_MEMORY_MB};
|
||||
let default_rng = format! {"src={}", config::DEFAULT_RNG_SOURCE};
|
||||
|
||||
(default_vcpus, default_memory, default_rng)
|
||||
fn default_vcpus() -> String {
|
||||
format!(
|
||||
"boot={},max_phys_bits={}",
|
||||
config::DEFAULT_VCPUS,
|
||||
config::DEFAULT_MAX_PHYS_BITS
|
||||
)
|
||||
}
|
||||
|
||||
fn create_app(default_vcpus: String, default_memory: String, default_rng: String) -> Command {
|
||||
#[allow(clippy::let_and_return)]
|
||||
let app = Command::new("cloud-hypervisor")
|
||||
// 'BUILT_VERSION' is set by the build script 'build.rs' at
|
||||
// compile time
|
||||
.version(env!("BUILT_VERSION"))
|
||||
.author(env!("CARGO_PKG_AUTHORS"))
|
||||
.about("Launch a cloud-hypervisor VMM.")
|
||||
.group(ArgGroup::new("vm-config").multiple(true))
|
||||
.group(ArgGroup::new("vmm-config").multiple(true))
|
||||
.group(ArgGroup::new("logging").multiple(true))
|
||||
.arg(
|
||||
Arg::new("cpus")
|
||||
.long("cpus")
|
||||
.help(
|
||||
"boot=<boot_vcpus>,max=<max_vcpus>,\
|
||||
topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>,\
|
||||
kvm_hyperv=on|off,max_phys_bits=<maximum_number_of_physical_bits>,\
|
||||
affinity=<list_of_vcpus_with_their_associated_cpuset>,\
|
||||
features=<list_of_features_to_enable>",
|
||||
)
|
||||
.default_value(default_vcpus)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("platform")
|
||||
.long("platform")
|
||||
.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>",
|
||||
)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("memory")
|
||||
.long("memory")
|
||||
.help(
|
||||
"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_value(default_memory)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("memory-zone")
|
||||
.long("memory-zone")
|
||||
.help(
|
||||
"User defined memory zone parameters \
|
||||
\"size=<guest_memory_region_size>,file=<backing_file>,\
|
||||
shared=on|off,\
|
||||
hugepages=on|off,hugepage_size=<hugepage_size>,\
|
||||
host_numa_node=<node_id>,\
|
||||
id=<zone_identifier>,hotplug_size=<hotpluggable_memory_size>,\
|
||||
hotplugged_size=<hotplugged_memory_size>,\
|
||||
prefault=on|off\"",
|
||||
)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("firmware")
|
||||
.long("firmware")
|
||||
.help("Path to firmware that is loaded in an architectural specific way")
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("kernel")
|
||||
.long("kernel")
|
||||
.help(
|
||||
"Path to kernel to load. This may be a kernel or firmware that supports a PVH \
|
||||
entry point (e.g. vmlinux) or architecture equivalent",
|
||||
)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("initramfs")
|
||||
.long("initramfs")
|
||||
.help("Path to initramfs image")
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("cmdline")
|
||||
.long("cmdline")
|
||||
.help("Kernel command line")
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("disk")
|
||||
.long("disk")
|
||||
.help(config::DiskConfig::SYNTAX)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("net")
|
||||
.long("net")
|
||||
.help(config::NetConfig::SYNTAX)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("rng")
|
||||
.long("rng")
|
||||
.help(
|
||||
"Random number generator parameters \"src=<entropy_source_path>,iommu=on|off\"",
|
||||
)
|
||||
.default_value(default_rng)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("balloon")
|
||||
.long("balloon")
|
||||
.help(config::BalloonConfig::SYNTAX)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("fs")
|
||||
.long("fs")
|
||||
.help(config::FsConfig::SYNTAX)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("pmem")
|
||||
.long("pmem")
|
||||
.help(config::PmemConfig::SYNTAX)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("serial")
|
||||
.long("serial")
|
||||
.help("Control serial port: off|null|pty|tty|file=/path/to/a/file")
|
||||
.default_value("null")
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("console")
|
||||
.long("console")
|
||||
.help(
|
||||
"Control (virtio) console: \"off|null|pty|tty|file=/path/to/a/file,iommu=on|off\"",
|
||||
)
|
||||
.default_value("tty")
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("device")
|
||||
.long("device")
|
||||
.help(config::DeviceConfig::SYNTAX)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("user-device")
|
||||
.long("user-device")
|
||||
.help(config::UserDeviceConfig::SYNTAX)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("vdpa")
|
||||
.long("vdpa")
|
||||
.help(config::VdpaConfig::SYNTAX)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("vsock")
|
||||
.long("vsock")
|
||||
.help(config::VsockConfig::SYNTAX)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("numa")
|
||||
.long("numa")
|
||||
.help(config::NumaConfig::SYNTAX)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("watchdog")
|
||||
.long("watchdog")
|
||||
.help("Enable virtio-watchdog")
|
||||
.num_args(0)
|
||||
.action(ArgAction::SetTrue)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("v")
|
||||
.short('v')
|
||||
.action(ArgAction::Count)
|
||||
.help("Sets the level of debugging output")
|
||||
.group("logging"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("log-file")
|
||||
.long("log-file")
|
||||
.help("Log file. Standard error is used if not specified")
|
||||
.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>.")
|
||||
.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>")
|
||||
.num_args(1)
|
||||
.group("vmm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("restore")
|
||||
.long("restore")
|
||||
.help(config::RestoreConfig::SYNTAX)
|
||||
.num_args(1)
|
||||
.group("vmm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("seccomp")
|
||||
.long("seccomp")
|
||||
.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"),
|
||||
fn default_memory() -> String {
|
||||
format!("size={}M", config::DEFAULT_MEMORY_MB)
|
||||
}
|
||||
|
||||
);
|
||||
fn default_rng() -> String {
|
||||
format!("src={}", config::DEFAULT_RNG_SOURCE)
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
/// Launch a cloud-hypervisor VMM.
|
||||
pub struct TopLevel {
|
||||
#[argh(option, long = "cpus", default = "default_vcpus()")]
|
||||
/// boot=<boot_vcpus>,max=<max_vcpus>,topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>,kvm_hyperv=on|off,max_phys_bits=<maximum_number_of_physical_bits>,affinity=<list_of_vcpus_with_their_associated_cpuset>,features=<list_of_features_to_enable>
|
||||
cpus: String,
|
||||
|
||||
#[argh(option, long = "platform")]
|
||||
/// 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>
|
||||
platform: Option<String>,
|
||||
|
||||
#[argh(option, long = "memory", default = "default_memory()")]
|
||||
/// 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
|
||||
memory: String,
|
||||
|
||||
#[argh(option, long = "memory-zone")]
|
||||
/// size=<guest_memory_region_size>,file=<backing_file>,shared=on|off,hugepages=on|off,hugepage_size=<hugepage_size>,host_numa_node=<node_id>,id=<zone_identifier>,hotplug_size=<hotpluggable_memory_size>,hotplugged_size=<hotplugged_memory_size>,prefault=on|off
|
||||
memory_zone: Vec<String>,
|
||||
|
||||
#[argh(option, long = "firmware")]
|
||||
/// path to firmware that is loaded in an architectural specific way
|
||||
firmware: Option<String>,
|
||||
|
||||
#[argh(option, long = "kernel")]
|
||||
/// path to kernel or firmware that supports a PVH entry point or architecture equivalent
|
||||
kernel: Option<String>,
|
||||
|
||||
#[argh(option, long = "initramfs")]
|
||||
/// path to initramfs image
|
||||
initramfs: Option<String>,
|
||||
|
||||
#[argh(option, long = "cmdline")]
|
||||
/// kernel command line
|
||||
cmdline: Option<String>,
|
||||
|
||||
#[argh(option, long = "disk")]
|
||||
/// path=<disk_image_path>,readonly=on|off,direct=on|off,iommu=on|off,num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,vhost_user=on|off,socket=<vhost_user_socket_path>,bw_size=<bytes>,bw_one_time_burst=<bytes>,bw_refill_time=<ms>,ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>,id=<device_id>,pci_segment=<segment_id>
|
||||
disk: Vec<String>,
|
||||
|
||||
#[argh(option, long = "net")]
|
||||
/// tap=<if_name>,ip=<ip_addr>,mask=<net_mask>,mac=<mac_addr>,fd=<fd1,fd2...>,iommu=on|off,num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,id=<device_id>,vhost_user=<vhost_user_enable>,socket=<vhost_user_socket_path>,vhost_mode=client|server,bw_size=<bytes>,bw_one_time_burst=<bytes>,bw_refill_time=<ms>,ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>,pci_segment=<segment_id>offload_tso=on|off,offload_ufo=on|off,offload_csum=on|off
|
||||
net: Vec<String>,
|
||||
|
||||
#[argh(option, long = "rng", default = "default_rng()")]
|
||||
/// src=<entropy_source_path>,iommu=on|off
|
||||
rng: String,
|
||||
|
||||
#[argh(option, long = "balloon")]
|
||||
/// size=<balloon_size>,deflate_on_oom=on|off,free_page_reporting=on|off
|
||||
balloon: Option<String>,
|
||||
|
||||
#[argh(option, long = "fs")]
|
||||
/// tag=<tag_name>,socket=<socket_path>,num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,id=<device_id>,pci_segment=<segment_id>
|
||||
fs: Vec<String>,
|
||||
|
||||
#[argh(option, long = "pmem")]
|
||||
/// file=<backing_file_path>,size=<persistent_memory_size>,iommu=on|off,discard_writes=on|off,id=<device_id>,pci_segment=<segment_id>
|
||||
pmem: Vec<String>,
|
||||
|
||||
#[argh(option, long = "serial", default = "String::from(\"null\")")]
|
||||
/// off|null|pty|tty|file=/path/to/a/file
|
||||
serial: String,
|
||||
|
||||
#[argh(option, long = "console", default = "String::from(\"tty\")")]
|
||||
/// off|null|pty|tty|file=/path/to/a/file,iommu=on|off
|
||||
console: String,
|
||||
|
||||
#[argh(option, long = "device")]
|
||||
/// path=<device_path>,iommu=on|off,id=<device_id>,pci_segment=<segment_id>
|
||||
device: Vec<String>,
|
||||
|
||||
#[argh(option, long = "user-device")]
|
||||
/// socket=<socket_path>,id=<device_id>,pci_segment=<segment_id>
|
||||
user_device: Vec<String>,
|
||||
|
||||
#[argh(option, long = "vdpa")]
|
||||
/// path=<device_path>,num_queues=<number_of_queues>,iommu=on|off,id=<device_id>,pci_segment=<segment_id>
|
||||
vdpa: Vec<String>,
|
||||
|
||||
#[argh(option, long = "vsock")]
|
||||
/// cid=<context_id>,socket=<socket_path>,iommu=on|off,id=<device_id>,pci_segment=<segment_id>
|
||||
vsock: Option<String>,
|
||||
|
||||
#[argh(option, long = "numa")]
|
||||
/// guest_numa_id=<node_id>,cpus=<cpus_id>,distances=<list_of_distances_to_destination_nodes>,memory_zones=<list_of_memory_zones>,sgx_epc_sections=<list_of_sgx_epc_sections>
|
||||
numa: Vec<String>,
|
||||
|
||||
#[argh(switch, long = "watchdog")]
|
||||
/// enable virtio-watchdog
|
||||
watchdog: bool,
|
||||
|
||||
#[argh(switch, short = 'v')]
|
||||
/// set the level of debugging output
|
||||
verbosity: u8,
|
||||
|
||||
#[argh(option, long = "log-file")]
|
||||
/// path to log file
|
||||
log_file: Option<String>,
|
||||
|
||||
#[argh(option, long = "api-socket")]
|
||||
/// path=<path/to/a/file>|fd=<fd>
|
||||
api_socket: Option<String>,
|
||||
|
||||
#[argh(option, long = "event-monitor")]
|
||||
/// path=<path/to/a/file>|fd=<fd>
|
||||
event_monitor: Option<String>,
|
||||
|
||||
#[argh(option, long = "restore")]
|
||||
/// source_url=<source_url>,prefault=on|off
|
||||
restore: Option<String>,
|
||||
|
||||
#[argh(option, long = "seccomp", default = "String::from(\"true\")")]
|
||||
/// seccomp configuration (true, false or log)
|
||||
seccomp: String,
|
||||
|
||||
#[argh(option, long = "tpm")]
|
||||
/// socket=<path/to/a/socket>
|
||||
tpm: Option<String>,
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let app = app.arg(
|
||||
Arg::new("sgx-epc")
|
||||
.long("sgx-epc")
|
||||
.help(config::SgxEpcConfig::SYNTAX)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
);
|
||||
#[argh(option, long = "sgx-epc")]
|
||||
/// id=<epc_section_identifier>,size=<epc_section_size>,prefault=on|off
|
||||
sgx_epc: Vec<String>,
|
||||
|
||||
#[cfg(feature = "guest_debug")]
|
||||
let app = app.arg(
|
||||
Arg::new("gdb")
|
||||
.long("gdb")
|
||||
.help("GDB socket (UNIX domain socket): path=</path/to/a/file>")
|
||||
.num_args(1)
|
||||
.group("vmm-config"),
|
||||
);
|
||||
#[argh(option, long = "gdb")]
|
||||
/// path=<path/to/a/file>
|
||||
gdb: Option<String>,
|
||||
|
||||
app
|
||||
#[argh(switch, short = 'V', long = "version")]
|
||||
/// print version information
|
||||
version: bool,
|
||||
}
|
||||
|
||||
fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
let log_level = match cmd_arguments.get_count("v") {
|
||||
impl TopLevel {
|
||||
fn to_vm_params(&self) -> config::VmParams<'_> {
|
||||
let cpus = &self.cpus;
|
||||
let memory = &self.memory;
|
||||
let memory_zones = if !self.memory_zone.is_empty() {
|
||||
Some(self.memory_zone.iter().map(|x| x.as_str()).collect())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let rng = &self.rng;
|
||||
let serial = &self.serial;
|
||||
let firmware = self.firmware.as_deref();
|
||||
let kernel = self.kernel.as_deref();
|
||||
let initramfs = self.initramfs.as_deref();
|
||||
let cmdline = self.cmdline.as_deref();
|
||||
|
||||
let disks = if !self.disk.is_empty() {
|
||||
Some(self.disk.iter().map(|x| x.as_str()).collect())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let net = if !self.net.is_empty() {
|
||||
Some(self.net.iter().map(|x| x.as_str()).collect())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let console = &self.console;
|
||||
let balloon = self.balloon.as_deref();
|
||||
let fs = if !self.fs.is_empty() {
|
||||
Some(self.fs.iter().map(|x| x.as_str()).collect())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let pmem = if !self.pmem.is_empty() {
|
||||
Some(self.pmem.iter().map(|x| x.as_str()).collect())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let devices = if !self.device.is_empty() {
|
||||
Some(self.device.iter().map(|x| x.as_str()).collect())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let user_devices = if !self.user_device.is_empty() {
|
||||
Some(self.user_device.iter().map(|x| x.as_str()).collect())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let vdpa = if !self.vdpa.is_empty() {
|
||||
Some(self.vdpa.iter().map(|x| x.as_str()).collect())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let vsock = self.vsock.as_deref();
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let sgx_epc = if !self.sgx_epc.is_empty() {
|
||||
Some(self.sgx_epc.iter().map(|x| x.as_str()).collect())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let numa = if !self.numa.is_empty() {
|
||||
Some(self.numa.iter().map(|x| x.as_str()).collect())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let watchdog = self.watchdog;
|
||||
let platform = self.platform.as_deref();
|
||||
#[cfg(feature = "guest_debug")]
|
||||
let gdb = self.gdb.is_some();
|
||||
let tpm = self.tpm.as_deref();
|
||||
|
||||
config::VmParams {
|
||||
cpus,
|
||||
memory,
|
||||
memory_zones,
|
||||
firmware,
|
||||
kernel,
|
||||
initramfs,
|
||||
cmdline,
|
||||
disks,
|
||||
net,
|
||||
rng,
|
||||
balloon,
|
||||
fs,
|
||||
pmem,
|
||||
serial,
|
||||
console,
|
||||
devices,
|
||||
user_devices,
|
||||
vdpa,
|
||||
vsock,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
sgx_epc,
|
||||
numa,
|
||||
watchdog,
|
||||
#[cfg(feature = "guest_debug")]
|
||||
gdb,
|
||||
platform,
|
||||
tpm,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start_vmm(toplevel: TopLevel) -> Result<Option<String>, Error> {
|
||||
let log_level = match toplevel.verbosity {
|
||||
0 => LevelFilter::Warn,
|
||||
1 => LevelFilter::Info,
|
||||
2 => LevelFilter::Debug,
|
||||
_ => LevelFilter::Trace,
|
||||
};
|
||||
|
||||
let log_file: Box<dyn std::io::Write + Send> = if let Some(file) =
|
||||
cmd_arguments.get_one::<String>("log-file")
|
||||
{
|
||||
let log_file: Box<dyn std::io::Write + Send> = if let Some(ref file) = toplevel.log_file {
|
||||
Box::new(std::fs::File::create(std::path::Path::new(file)).map_err(Error::LogFileCreation)?)
|
||||
} else {
|
||||
Box::new(std::io::stderr())
|
||||
@@ -415,32 +397,26 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
.map(|()| log::set_max_level(log_level))
|
||||
.map_err(Error::LoggerSetup)?;
|
||||
|
||||
let (api_socket_path, api_socket_fd) =
|
||||
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();
|
||||
let (api_socket_path, api_socket_fd) = if let Some(ref socket_config) = toplevel.api_socket {
|
||||
let mut parser = OptionParser::new();
|
||||
parser.add("path").add("fd");
|
||||
parser.parse(socket_config).unwrap_or_default();
|
||||
|
||||
if let Some(fd) = parser.get("fd") {
|
||||
(
|
||||
None,
|
||||
Some(fd.parse::<RawFd>().map_err(Error::ParsingApiSocket)?),
|
||||
)
|
||||
} else if let Some(path) = parser.get("path") {
|
||||
(Some(path), None)
|
||||
} else {
|
||||
(
|
||||
cmd_arguments
|
||||
.get_one::<String>("api-socket")
|
||||
.map(|s| s.to_string()),
|
||||
None,
|
||||
)
|
||||
}
|
||||
if let Some(fd) = parser.get("fd") {
|
||||
(
|
||||
None,
|
||||
Some(fd.parse::<RawFd>().map_err(Error::ParsingApiSocket)?),
|
||||
)
|
||||
} else if let Some(path) = parser.get("path") {
|
||||
(Some(path), None)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
(toplevel.api_socket.as_ref().map(|s| s.to_string()), None)
|
||||
}
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
if let Some(monitor_config) = cmd_arguments.get_one::<String>("event-monitor") {
|
||||
if let Some(ref monitor_config) = toplevel.event_monitor {
|
||||
let mut parser = OptionParser::new();
|
||||
parser.add("path").add("fd");
|
||||
parser
|
||||
@@ -470,18 +446,14 @@ 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.get_one::<String>("seccomp") {
|
||||
match seccomp_value as &str {
|
||||
"true" => SeccompAction::Trap,
|
||||
"false" => SeccompAction::Allow,
|
||||
"log" => SeccompAction::Log,
|
||||
_ => {
|
||||
// The user providing an invalid value will be rejected by clap
|
||||
panic!("Invalid parameter {seccomp_value} for \"--seccomp\" flag");
|
||||
}
|
||||
let seccomp_action = match &toplevel.seccomp as &str {
|
||||
"true" => SeccompAction::Trap,
|
||||
"false" => SeccompAction::Allow,
|
||||
"log" => SeccompAction::Log,
|
||||
val => {
|
||||
// The user providing an invalid value will be rejected
|
||||
panic!("Invalid parameter {val} for \"--seccomp\" flag");
|
||||
}
|
||||
} else {
|
||||
SeccompAction::Trap
|
||||
};
|
||||
|
||||
if seccomp_action == SeccompAction::Trap {
|
||||
@@ -521,7 +493,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
let hypervisor = hypervisor::new().map_err(Error::CreateHypervisor)?;
|
||||
|
||||
#[cfg(feature = "guest_debug")]
|
||||
let gdb_socket_path = if let Some(gdb_config) = cmd_arguments.get_one::<String>("gdb") {
|
||||
let gdb_socket_path = if let Some(ref gdb_config) = toplevel.gdb {
|
||||
let mut parser = OptionParser::new();
|
||||
parser.add("path");
|
||||
parser.parse(gdb_config).map_err(Error::ParsingGdb)?;
|
||||
@@ -539,6 +511,8 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
#[cfg(feature = "guest_debug")]
|
||||
let vm_debug_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateDebugEventFd)?;
|
||||
|
||||
let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateExitEventFd)?;
|
||||
|
||||
let vmm_thread = vmm::start_vmm_thread(
|
||||
env!("CARGO_PKG_VERSION").to_string(),
|
||||
&api_socket_path,
|
||||
@@ -552,34 +526,46 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
debug_evt.try_clone().unwrap(),
|
||||
#[cfg(feature = "guest_debug")]
|
||||
vm_debug_evt.try_clone().unwrap(),
|
||||
exit_evt.try_clone().unwrap(),
|
||||
&seccomp_action,
|
||||
hypervisor,
|
||||
)
|
||||
.map_err(Error::StartVmmThread)?;
|
||||
|
||||
let payload_present =
|
||||
cmd_arguments.contains_id("kernel") || cmd_arguments.contains_id("firmware");
|
||||
let r: Result<(), Error> = (|| {
|
||||
let payload_present = toplevel.kernel.is_some() || toplevel.firmware.is_some();
|
||||
|
||||
if payload_present {
|
||||
let vm_params = config::VmParams::from_arg_matches(&cmd_arguments);
|
||||
let vm_config = config::VmConfig::parse(vm_params).map_err(Error::ParsingConfig)?;
|
||||
if payload_present {
|
||||
let vm_params = toplevel.to_vm_params();
|
||||
let vm_config = config::VmConfig::parse(vm_params).map_err(Error::ParsingConfig)?;
|
||||
|
||||
// Create and boot the VM based off the VM config we just built.
|
||||
let sender = api_request_sender.clone();
|
||||
vmm::api::vm_create(
|
||||
api_evt.try_clone().unwrap(),
|
||||
api_request_sender,
|
||||
Arc::new(Mutex::new(vm_config)),
|
||||
)
|
||||
.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.get_one::<String>("restore") {
|
||||
vmm::api::vm_restore(
|
||||
api_evt.try_clone().unwrap(),
|
||||
api_request_sender,
|
||||
Arc::new(config::RestoreConfig::parse(restore_params).map_err(Error::ParsingRestore)?),
|
||||
)
|
||||
.map_err(Error::VmRestore)?;
|
||||
// Create and boot the VM based off the VM config we just built.
|
||||
let sender = api_request_sender.clone();
|
||||
vmm::api::vm_create(
|
||||
api_evt.try_clone().unwrap(),
|
||||
api_request_sender,
|
||||
Arc::new(Mutex::new(vm_config)),
|
||||
)
|
||||
.map_err(Error::VmCreate)?;
|
||||
vmm::api::vm_boot(api_evt.try_clone().unwrap(), sender).map_err(Error::VmBoot)?;
|
||||
} else if let Some(restore_params) = toplevel.restore {
|
||||
vmm::api::vm_restore(
|
||||
api_evt.try_clone().unwrap(),
|
||||
api_request_sender,
|
||||
Arc::new(
|
||||
config::RestoreConfig::parse(&restore_params).map_err(Error::ParsingRestore)?,
|
||||
),
|
||||
)
|
||||
.map_err(Error::VmRestore)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})();
|
||||
|
||||
if r.is_err() {
|
||||
if let Err(e) = exit_evt.write(1) {
|
||||
warn!("writing to exit EventFd: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
vmm_thread
|
||||
@@ -587,17 +573,25 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
.map_err(Error::ThreadJoin)?
|
||||
.map_err(Error::VmmThread)?;
|
||||
|
||||
Ok(api_socket_path)
|
||||
r.map(|_| api_socket_path)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
let _profiler = dhat::Profiler::new_heap();
|
||||
|
||||
// Ensure all created files (.e.g sockets) are only accessible by this user
|
||||
// SAFETY: trivially safe
|
||||
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 exit_code = match start_vmm(cmd_arguments) {
|
||||
let toplevel: TopLevel = argh::from_env();
|
||||
|
||||
if toplevel.version {
|
||||
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILT_VERSION"));
|
||||
return;
|
||||
}
|
||||
|
||||
let exit_code = match start_vmm(toplevel) {
|
||||
Ok(path) => {
|
||||
path.map(|s| std::fs::remove_file(s).ok());
|
||||
0
|
||||
@@ -608,13 +602,8 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
// SAFETY: trivially safe
|
||||
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.
|
||||
std::io::stdin().lock().set_canon_mode().unwrap();
|
||||
}
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
drop(_profiler);
|
||||
|
||||
std::process::exit(exit_code);
|
||||
}
|
||||
@@ -622,19 +611,45 @@ fn main() {
|
||||
#[cfg(test)]
|
||||
mod unit_tests {
|
||||
use crate::config::HotplugMethod;
|
||||
use crate::{create_app, prepare_default_values};
|
||||
use crate::TopLevel;
|
||||
use std::path::PathBuf;
|
||||
use vmm::config::{
|
||||
ConsoleConfig, ConsoleOutputMode, CpuFeatures, CpusConfig, MemoryConfig, PayloadConfig,
|
||||
RngConfig, VmConfig, VmParams,
|
||||
RngConfig, VmConfig,
|
||||
};
|
||||
|
||||
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);
|
||||
// Taken from argh
|
||||
fn cmd<'a>(default: &'a str, path: &'a str) -> &'a str {
|
||||
std::path::Path::new(path)
|
||||
.file_name()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or(default)
|
||||
}
|
||||
|
||||
let vm_params = VmParams::from_arg_matches(&cmd_arguments);
|
||||
// Some code taken from argh since it does not provide a helper to parse arbitrary strings
|
||||
fn get_vm_config_from_vec(args: &[&str]) -> VmConfig {
|
||||
let strings: Vec<String> = args.iter().map(|x| x.to_string()).collect();
|
||||
let cmd = cmd(&strings[0], &strings[0]);
|
||||
let strs: Vec<&str> = strings.iter().map(|s| s.as_str()).collect();
|
||||
let toplevel = <TopLevel as argh::FromArgs>::from_args(&[cmd], &strs[1..]).unwrap_or_else(
|
||||
|early_exit| {
|
||||
std::process::exit(match early_exit.status {
|
||||
Ok(()) => {
|
||||
println!("{}", early_exit.output);
|
||||
0
|
||||
}
|
||||
Err(()) => {
|
||||
eprintln!(
|
||||
"{}\nRun {} --help for more information.",
|
||||
early_exit.output, cmd
|
||||
);
|
||||
1
|
||||
}
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let vm_params = toplevel.to_vm_params();
|
||||
|
||||
VmConfig::parse(vm_params).unwrap()
|
||||
}
|
||||
@@ -724,6 +739,7 @@ mod unit_tests {
|
||||
gdb: false,
|
||||
platform: None,
|
||||
tpm: None,
|
||||
preserved_fds: None,
|
||||
};
|
||||
|
||||
assert_eq!(expected_vm_config, result_vm_config);
|
||||
@@ -893,6 +909,7 @@ mod unit_tests {
|
||||
"/path/to/kernel",
|
||||
"--disk",
|
||||
"path=/path/to/disk/1",
|
||||
"--disk",
|
||||
"path=/path/to/disk/2",
|
||||
],
|
||||
r#"{
|
||||
@@ -911,6 +928,7 @@ mod unit_tests {
|
||||
"/path/to/kernel",
|
||||
"--disk",
|
||||
"path=/path/to/disk/1",
|
||||
"--disk",
|
||||
"path=/path/to/disk/2",
|
||||
],
|
||||
r#"{
|
||||
@@ -1186,6 +1204,7 @@ mod unit_tests {
|
||||
"--memory", "shared=true",
|
||||
"--fs",
|
||||
"tag=virtiofs1,socket=/path/to/sock1",
|
||||
"--fs",
|
||||
"tag=virtiofs2,socket=/path/to/sock2",
|
||||
],
|
||||
r#"{
|
||||
@@ -1204,6 +1223,7 @@ mod unit_tests {
|
||||
"--memory", "shared=true",
|
||||
"--fs",
|
||||
"tag=virtiofs1,socket=/path/to/sock1",
|
||||
"--fs",
|
||||
"tag=virtiofs2,socket=/path/to/sock2",
|
||||
],
|
||||
r#"{
|
||||
@@ -1266,6 +1286,7 @@ mod unit_tests {
|
||||
"/path/to/kernel",
|
||||
"--pmem",
|
||||
"file=/path/to/img/1,size=1G",
|
||||
"--pmem",
|
||||
"file=/path/to/img/2,size=2G",
|
||||
],
|
||||
r#"{
|
||||
@@ -1432,6 +1453,7 @@ mod unit_tests {
|
||||
"/path/to/kernel",
|
||||
"--device",
|
||||
"path=/path/to/device/1",
|
||||
"--device",
|
||||
"path=/path/to/device/2",
|
||||
],
|
||||
r#"{
|
||||
@@ -1450,6 +1472,7 @@ mod unit_tests {
|
||||
"/path/to/kernel",
|
||||
"--device",
|
||||
"path=/path/to/device/1",
|
||||
"--device",
|
||||
"path=/path/to/device/2",
|
||||
],
|
||||
r#"{
|
||||
@@ -1526,6 +1549,7 @@ mod unit_tests {
|
||||
"/path/to/kernel",
|
||||
"--vdpa",
|
||||
"path=/path/to/device/1",
|
||||
"--vdpa",
|
||||
"path=/path/to/device/2,num_queues=2",
|
||||
],
|
||||
r#"{
|
||||
@@ -1544,6 +1568,7 @@ mod unit_tests {
|
||||
"/path/to/kernel",
|
||||
"--vdpa",
|
||||
"path=/path/to/device/1",
|
||||
"--vdpa",
|
||||
"path=/path/to/device/2",
|
||||
],
|
||||
r#"{
|
||||
|
||||
@@ -8,9 +8,9 @@ edition = "2021"
|
||||
dirs = "4.0.0"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.139"
|
||||
once_cell = "1.16.0"
|
||||
once_cell = "1.17.1"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.89"
|
||||
ssh2 = { version = "0.9.3", features = ["vendored-openssl"] }
|
||||
serde_json = "1.0.95"
|
||||
ssh2 = { version = "0.9.4", features = ["vendored-openssl"] }
|
||||
vmm-sys-util = "0.11.0"
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
@@ -301,7 +301,7 @@ impl DiskConfig for UbuntuDiskConfig {
|
||||
.expect("Expected writing out network-config to succeed");
|
||||
|
||||
std::process::Command::new("mkdosfs")
|
||||
.args(["-n", "cidata"])
|
||||
.args(["-n", "CIDATA"])
|
||||
.args(["-C", cloudinit_file_path.as_str()])
|
||||
.arg("8192")
|
||||
.output()
|
||||
@@ -1159,7 +1159,7 @@ impl ToString for VerbosityLevel {
|
||||
match self {
|
||||
Warn => "".to_string(),
|
||||
Info => "-v".to_string(),
|
||||
Debug => "-vv".to_string(),
|
||||
Debug => "-v -v".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1210,7 +1210,7 @@ impl<'a> GuestCommand<'a> {
|
||||
self.command.arg("-v");
|
||||
}
|
||||
Debug => {
|
||||
self.command.arg("-vv");
|
||||
self.command.args(["-v", "-v"]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1278,6 +1278,7 @@ impl<'a> GuestCommand<'a> {
|
||||
.unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!(
|
||||
"path={}",
|
||||
self.guest.disk_config.disk(DiskType::CloudInit).unwrap()
|
||||
@@ -1351,6 +1352,7 @@ pub fn parse_iperf3_output(output: &[u8], sender: bool, bandwidth: bool) -> Resu
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum FioOps {
|
||||
Read,
|
||||
RandomRead,
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
extern crate test_infra;
|
||||
|
||||
use api_client::simple_api_command;
|
||||
use api_client::simple_api_full_command;
|
||||
use net_util::MacAddr;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
@@ -37,8 +39,8 @@ mod x86_64 {
|
||||
pub const FOCAL_IMAGE_NAME_QCOW2: &str = "focal-server-cloudimg-amd64-custom-20210609-0.qcow2";
|
||||
pub const FOCAL_IMAGE_NAME_VHD: &str = "focal-server-cloudimg-amd64-custom-20210609-0.vhd";
|
||||
pub const FOCAL_IMAGE_NAME_VHDX: &str = "focal-server-cloudimg-amd64-custom-20210609-0.vhdx";
|
||||
pub const JAMMY_IMAGE_NAME: &str = "jammy-server-cloudimg-amd64-custom-20221118-1.raw";
|
||||
pub const WINDOWS_IMAGE_NAME: &str = "windows-server-2019.raw";
|
||||
pub const JAMMY_IMAGE_NAME: &str = "jammy-server-cloudimg-amd64-custom-20230119-0.raw";
|
||||
pub const WINDOWS_IMAGE_NAME: &str = "windows-server-2022-amd64-2.raw";
|
||||
pub const OVMF_NAME: &str = "CLOUDHV.fd";
|
||||
pub const GREP_SERIAL_IRQ_CMD: &str = "grep -c 'IO-APIC.*ttyS0' /proc/interrupts || true";
|
||||
}
|
||||
@@ -224,29 +226,37 @@ fn prepare_vhost_user_net_daemon(
|
||||
(command, vunet_socket_path)
|
||||
}
|
||||
|
||||
fn curl_command(api_socket: &str, method: &str, url: &str, http_body: Option<&str>) {
|
||||
let mut curl_args: Vec<&str> = ["--unix-socket", api_socket, "-i", "-X", method, url].to_vec();
|
||||
fn prepare_swtpm_daemon(tmp_dir: &TempDir) -> (std::process::Command, String) {
|
||||
let swtpm_tpm_dir = String::from(tmp_dir.as_path().join("swtpm").to_str().unwrap());
|
||||
let swtpm_socket_path = String::from(
|
||||
tmp_dir
|
||||
.as_path()
|
||||
.join("swtpm")
|
||||
.join("swtpm.sock")
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
);
|
||||
std::fs::create_dir(&swtpm_tpm_dir).unwrap();
|
||||
|
||||
if let Some(body) = http_body {
|
||||
curl_args.push("-H");
|
||||
curl_args.push("Accept: application/json");
|
||||
curl_args.push("-H");
|
||||
curl_args.push("Content-Type: application/json");
|
||||
curl_args.push("-d");
|
||||
curl_args.push(body);
|
||||
}
|
||||
let mut swtpm_command = Command::new("swtpm");
|
||||
let swtpm_args = [
|
||||
"socket",
|
||||
"--tpmstate",
|
||||
&format!("dir={swtpm_tpm_dir}"),
|
||||
"--ctrl",
|
||||
&format!("type=unixio,path={swtpm_socket_path}"),
|
||||
"--flags",
|
||||
"startup-clear",
|
||||
"--tpm2",
|
||||
];
|
||||
swtpm_command.args(swtpm_args);
|
||||
|
||||
let status = Command::new("curl")
|
||||
.args(curl_args)
|
||||
.status()
|
||||
.expect("Failed to launch curl command");
|
||||
|
||||
assert!(status.success());
|
||||
(swtpm_command, swtpm_socket_path)
|
||||
}
|
||||
|
||||
fn remote_command(api_socket: &str, command: &str, arg: Option<&str>) -> bool {
|
||||
let mut cmd = Command::new(clh_command("ch-remote"));
|
||||
cmd.args([&format!("--api-socket={api_socket}"), command]);
|
||||
cmd.args(["--api-socket", api_socket, command]);
|
||||
|
||||
if let Some(arg) = arg {
|
||||
cmd.arg(arg);
|
||||
@@ -264,7 +274,7 @@ fn remote_command(api_socket: &str, command: &str, arg: Option<&str>) -> bool {
|
||||
|
||||
fn remote_command_w_output(api_socket: &str, command: &str, arg: Option<&str>) -> (bool, Vec<u8>) {
|
||||
let mut cmd = Command::new(clh_command("ch-remote"));
|
||||
cmd.args([&format!("--api-socket={api_socket}"), command]);
|
||||
cmd.args(["--api-socket", api_socket, command]);
|
||||
|
||||
if let Some(arg) = arg {
|
||||
cmd.arg(arg);
|
||||
@@ -283,18 +293,18 @@ fn resize_command(
|
||||
event_file: Option<&str>,
|
||||
) -> bool {
|
||||
let mut cmd = Command::new(clh_command("ch-remote"));
|
||||
cmd.args([&format!("--api-socket={api_socket}"), "resize"]);
|
||||
cmd.args(["--api-socket", api_socket, "resize"]);
|
||||
|
||||
if let Some(desired_vcpus) = desired_vcpus {
|
||||
cmd.arg(format!("--cpus={desired_vcpus}"));
|
||||
cmd.args(["--cpus", &format!("{desired_vcpus}")]);
|
||||
}
|
||||
|
||||
if let Some(desired_ram) = desired_ram {
|
||||
cmd.arg(format!("--memory={desired_ram}"));
|
||||
cmd.args(["--memory", &format!("{desired_ram}")]);
|
||||
}
|
||||
|
||||
if let Some(desired_balloon) = desired_balloon {
|
||||
cmd.arg(format!("--balloon={desired_balloon}"));
|
||||
cmd.args(["--balloon", &format!("{desired_balloon}")]);
|
||||
}
|
||||
|
||||
let ret = cmd.status().expect("Failed to launch ch-remote").success();
|
||||
@@ -319,10 +329,13 @@ fn resize_command(
|
||||
fn resize_zone_command(api_socket: &str, id: &str, desired_size: &str) -> bool {
|
||||
let mut cmd = Command::new(clh_command("ch-remote"));
|
||||
cmd.args([
|
||||
&format!("--api-socket={api_socket}"),
|
||||
"--api-socket",
|
||||
api_socket,
|
||||
"resize-zone",
|
||||
&format!("--id={id}"),
|
||||
&format!("--size={desired_size}"),
|
||||
"--id",
|
||||
id,
|
||||
"--size",
|
||||
desired_size,
|
||||
]);
|
||||
|
||||
cmd.status().expect("Failed to launch ch-remote").success()
|
||||
@@ -375,7 +388,7 @@ fn setup_ovs_dpdk_guests(
|
||||
.args(["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.default_disks()
|
||||
.args(["--net", guest1.default_net_string().as_str(), "vhost_user=true,socket=/tmp/dpdkvhostclient1,num_queues=2,queue_size=256,vhost_mode=server"])
|
||||
.args(["--net", guest1.default_net_string().as_str(), "--net", "vhost_user=true,socket=/tmp/dpdkvhostclient1,num_queues=2,queue_size=256,vhost_mode=server"])
|
||||
.capture_output()
|
||||
.spawn()
|
||||
.unwrap();
|
||||
@@ -425,7 +438,7 @@ fn setup_ovs_dpdk_guests(
|
||||
.args(["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.default_disks()
|
||||
.args(["--net", guest2.default_net_string().as_str(), "vhost_user=true,socket=/tmp/dpdkvhostclient2,num_queues=2,queue_size=256,vhost_mode=server"])
|
||||
.args(["--net", guest2.default_net_string().as_str(), "--net", "vhost_user=true,socket=/tmp/dpdkvhostclient2,num_queues=2,queue_size=256,vhost_mode=server"])
|
||||
.capture_output()
|
||||
.spawn()
|
||||
.unwrap();
|
||||
@@ -658,13 +671,17 @@ fn _test_guest_numa_nodes(acpi: bool) {
|
||||
.args([
|
||||
"--memory-zone",
|
||||
"id=mem0,size=1G,hotplug_size=3G",
|
||||
"--memory-zone",
|
||||
"id=mem1,size=2G,hotplug_size=3G",
|
||||
"--memory-zone",
|
||||
"id=mem2,size=3G,hotplug_size=3G",
|
||||
])
|
||||
.args([
|
||||
"--numa",
|
||||
"guest_numa_id=0,cpus=[0-2,9],distances=[1@15,2@20],memory_zones=mem0",
|
||||
"--numa",
|
||||
"guest_numa_id=1,cpus=[3-4,6-8],distances=[0@20,2@25],memory_zones=mem1",
|
||||
"--numa",
|
||||
"guest_numa_id=2,cpus=[5,10-11],distances=[0@25,1@30],memory_zones=mem2",
|
||||
])
|
||||
.args(["--kernel", kernel_path.to_str().unwrap()])
|
||||
@@ -967,11 +984,13 @@ fn test_vhost_user_blk(
|
||||
guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!(
|
||||
"path={}",
|
||||
guest.disk_config.disk(DiskType::CloudInit).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
blk_params.as_str(),
|
||||
])
|
||||
.default_net()
|
||||
@@ -1111,6 +1130,7 @@ fn test_boot_from_vhost_user_blk(
|
||||
.args([
|
||||
"--disk",
|
||||
blk_boot_params.as_str(),
|
||||
"--disk",
|
||||
format!(
|
||||
"path={}",
|
||||
guest.disk_config.disk(DiskType::CloudInit).unwrap()
|
||||
@@ -1793,6 +1813,7 @@ fn _test_virtio_iommu(acpi: bool) {
|
||||
guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!(
|
||||
"path={},iommu=on",
|
||||
guest.disk_config.disk(DiskType::CloudInit).unwrap()
|
||||
@@ -1883,7 +1904,7 @@ fn enable_guest_watchdog(guest: &Guest, watchdog_sec: u32) {
|
||||
}
|
||||
|
||||
mod common_parallel {
|
||||
use std::{fs::OpenOptions, io::SeekFrom};
|
||||
use std::{fs::OpenOptions, io::SeekFrom, os::unix::net::UnixStream};
|
||||
|
||||
use crate::*;
|
||||
|
||||
@@ -2025,19 +2046,16 @@ mod common_parallel {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_cpu_topology_421() {
|
||||
test_cpu_topology(4, 2, 1, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_cpu_topology_142() {
|
||||
test_cpu_topology(1, 4, 2, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_cpu_topology_262() {
|
||||
test_cpu_topology(2, 6, 2, false);
|
||||
}
|
||||
@@ -2208,7 +2226,9 @@ mod common_parallel {
|
||||
.args([
|
||||
"--memory-zone",
|
||||
"id=mem0,size=1G,hotplug_size=2G",
|
||||
"id=mem1,size=1G,file=/dev/shm",
|
||||
"--memory-zone",
|
||||
"id=mem1,size=1G,shared=on",
|
||||
"--memory-zone",
|
||||
"id=mem2,size=1G,host_numa_node=0,hotplug_size=2G",
|
||||
])
|
||||
.args(["--kernel", kernel_path.to_str().unwrap()])
|
||||
@@ -2460,11 +2480,13 @@ mod common_parallel {
|
||||
guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!(
|
||||
"path={}",
|
||||
guest.disk_config.disk(DiskType::CloudInit).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!("path={test_disk_path},pci_segment=15").as_str(),
|
||||
])
|
||||
.capture_output()
|
||||
@@ -2599,11 +2621,13 @@ mod common_parallel {
|
||||
guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!(
|
||||
"path={}",
|
||||
guest.disk_config.disk(DiskType::CloudInit).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!(
|
||||
"path={},readonly=on,direct=on,num_queues=4,_disable_io_uring={}",
|
||||
blk_file_path.to_str().unwrap(),
|
||||
@@ -2765,11 +2789,13 @@ mod common_parallel {
|
||||
guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!(
|
||||
"path={}",
|
||||
guest.disk_config.disk(DiskType::CloudInit).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!("path={vhdx_path}").as_str(),
|
||||
])
|
||||
.default_net()
|
||||
@@ -2839,6 +2865,7 @@ mod common_parallel {
|
||||
.args([
|
||||
"--disk",
|
||||
format!("path={},direct=on", os_path.as_path().to_str().unwrap()).as_str(),
|
||||
"--disk",
|
||||
format!(
|
||||
"path={}",
|
||||
guest.disk_config.disk(DiskType::CloudInit).unwrap()
|
||||
@@ -3216,7 +3243,9 @@ mod common_parallel {
|
||||
.args([
|
||||
"--net",
|
||||
guest.default_net_string().as_str(),
|
||||
"--net",
|
||||
"tap=,mac=8a:6b:6f:5a:de:ac,ip=192.168.3.1,mask=255.255.255.0",
|
||||
"--net",
|
||||
"tap=mytap1,mac=fe:1f:9e:e1:60:f2,ip=192.168.4.1,mask=255.255.255.0",
|
||||
])
|
||||
.capture_output()
|
||||
@@ -3694,7 +3723,7 @@ mod common_parallel {
|
||||
// line (We tag the command line from cloud-hypervisor for that purpose).
|
||||
// The third device is added to validate that hotplug works correctly since
|
||||
// it is being added to the L2 VM through hotplugging mechanism.
|
||||
// Also, we pass-through a vitio-blk device to the L2 VM to test the 32-bit
|
||||
// Also, we pass-through a virtio-blk device to the L2 VM to test the 32-bit
|
||||
// vfio device support
|
||||
fn test_vfio() {
|
||||
setup_vfio_network_interfaces();
|
||||
@@ -3756,12 +3785,15 @@ mod common_parallel {
|
||||
guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!(
|
||||
"path={}",
|
||||
guest.disk_config.disk(DiskType::CloudInit).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!("path={}", vfio_disk_path.to_str().unwrap()).as_str(),
|
||||
"--disk",
|
||||
format!("path={},iommu=on", blk_file_path.to_str().unwrap()).as_str(),
|
||||
])
|
||||
.args([
|
||||
@@ -3774,16 +3806,19 @@ mod common_parallel {
|
||||
.args([
|
||||
"--net",
|
||||
format!("tap={},mac={}", vfio_tap0, guest.network.guest_mac).as_str(),
|
||||
"--net",
|
||||
format!(
|
||||
"tap={},mac={},iommu=on",
|
||||
vfio_tap1, guest.network.l2_guest_mac1
|
||||
)
|
||||
.as_str(),
|
||||
"--net",
|
||||
format!(
|
||||
"tap={},mac={},iommu=on",
|
||||
vfio_tap2, guest.network.l2_guest_mac2
|
||||
)
|
||||
.as_str(),
|
||||
"--net",
|
||||
format!(
|
||||
"tap={},mac={},iommu=on",
|
||||
vfio_tap3, guest.network.l2_guest_mac3
|
||||
@@ -3861,7 +3896,7 @@ mod common_parallel {
|
||||
let vfio_hotplug_output = guest
|
||||
.ssh_command_l1(
|
||||
"sudo /mnt/ch-remote \
|
||||
--api-socket=/tmp/ch_api.sock \
|
||||
--api-socket /tmp/ch_api.sock \
|
||||
add-device path=/sys/bus/pci/devices/0000:00:09.0,id=vfio123",
|
||||
)
|
||||
.unwrap();
|
||||
@@ -3901,7 +3936,7 @@ mod common_parallel {
|
||||
guest
|
||||
.ssh_command_l1(
|
||||
"sudo /mnt/ch-remote \
|
||||
--api-socket=/tmp/ch_api.sock \
|
||||
--api-socket /tmp/ch_api.sock \
|
||||
remove-device vfio123",
|
||||
)
|
||||
.unwrap();
|
||||
@@ -3932,7 +3967,7 @@ mod common_parallel {
|
||||
guest
|
||||
.ssh_command_l1(
|
||||
"sudo /mnt/ch-remote \
|
||||
--api-socket=/tmp/ch_api.sock \
|
||||
--api-socket /tmp/ch_api.sock \
|
||||
resize --memory=1073741824",
|
||||
)
|
||||
.unwrap();
|
||||
@@ -4008,8 +4043,9 @@ mod common_parallel {
|
||||
|
||||
thread::sleep(std::time::Duration::new(1, 0));
|
||||
|
||||
let mut socket = UnixStream::connect(&api_socket).unwrap();
|
||||
// Verify API server is running
|
||||
curl_command(&api_socket, "GET", "http://localhost/api/v1/vmm.ping", None);
|
||||
simple_api_full_command(&mut socket, "GET", "vmm.ping", None).unwrap();
|
||||
|
||||
// Create the VM first
|
||||
let cpu_count: u8 = 4;
|
||||
@@ -4062,8 +4098,9 @@ mod common_parallel {
|
||||
|
||||
thread::sleep(std::time::Duration::new(1, 0));
|
||||
|
||||
let mut socket = UnixStream::connect(&api_socket).unwrap();
|
||||
// Verify API server is running
|
||||
curl_command(&api_socket, "GET", "http://localhost/api/v1/vmm.ping", None);
|
||||
simple_api_full_command(&mut socket, "GET", "vmm.ping", None).unwrap();
|
||||
|
||||
// Create the VM first
|
||||
let cpu_count: u8 = 4;
|
||||
@@ -4074,15 +4111,12 @@ mod common_parallel {
|
||||
);
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
curl_command(
|
||||
&api_socket,
|
||||
"PUT",
|
||||
"http://localhost/api/v1/vm.create",
|
||||
Some(&http_body),
|
||||
);
|
||||
// socket has to be created again inside catch_unwind block to avoid errors
|
||||
let mut socket = UnixStream::connect(&api_socket).unwrap();
|
||||
simple_api_command(&mut socket, "PUT", "create", Some(&http_body)).unwrap();
|
||||
|
||||
// Then boot it
|
||||
curl_command(&api_socket, "PUT", "http://localhost/api/v1/vm.boot", None);
|
||||
simple_api_command(&mut socket, "PUT", "boot", None).unwrap();
|
||||
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
|
||||
@@ -4099,15 +4133,10 @@ mod common_parallel {
|
||||
thread::sleep(std::time::Duration::new(20, 0));
|
||||
|
||||
// Then shut it down
|
||||
curl_command(
|
||||
&api_socket,
|
||||
"PUT",
|
||||
"http://localhost/api/v1/vm.shutdown",
|
||||
None,
|
||||
);
|
||||
simple_api_command(&mut socket, "PUT", "shutdown", None).unwrap();
|
||||
|
||||
// Then boot it again
|
||||
curl_command(&api_socket, "PUT", "http://localhost/api/v1/vm.boot", None);
|
||||
simple_api_command(&mut socket, "PUT", "boot", None).unwrap();
|
||||
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
|
||||
@@ -4140,8 +4169,9 @@ mod common_parallel {
|
||||
|
||||
thread::sleep(std::time::Duration::new(1, 0));
|
||||
|
||||
let mut socket = UnixStream::connect(&api_socket).unwrap();
|
||||
// Verify API server is running
|
||||
curl_command(&api_socket, "GET", "http://localhost/api/v1/vmm.ping", None);
|
||||
simple_api_full_command(&mut socket, "GET", "vmm.ping", None).unwrap();
|
||||
|
||||
// Create the VM first
|
||||
let cpu_count: u8 = 4;
|
||||
@@ -4152,15 +4182,12 @@ mod common_parallel {
|
||||
);
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
curl_command(
|
||||
&api_socket,
|
||||
"PUT",
|
||||
"http://localhost/api/v1/vm.create",
|
||||
Some(&http_body),
|
||||
);
|
||||
// socket has to be created again inside catch_unwind block to avoid errors
|
||||
let mut socket = UnixStream::connect(&api_socket).unwrap();
|
||||
simple_api_command(&mut socket, "PUT", "create", Some(&http_body)).unwrap();
|
||||
|
||||
// Then boot it
|
||||
curl_command(&api_socket, "PUT", "http://localhost/api/v1/vm.boot", None);
|
||||
simple_api_command(&mut socket, "PUT", "boot", None).unwrap();
|
||||
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
|
||||
@@ -4177,22 +4204,12 @@ mod common_parallel {
|
||||
thread::sleep(std::time::Duration::new(20, 0));
|
||||
|
||||
// Then delete it
|
||||
curl_command(
|
||||
&api_socket,
|
||||
"PUT",
|
||||
"http://localhost/api/v1/vm.delete",
|
||||
None,
|
||||
);
|
||||
simple_api_command(&mut socket, "PUT", "delete", None).unwrap();
|
||||
|
||||
curl_command(
|
||||
&api_socket,
|
||||
"PUT",
|
||||
"http://localhost/api/v1/vm.create",
|
||||
Some(&http_body),
|
||||
);
|
||||
simple_api_command(&mut socket, "PUT", "create", Some(&http_body)).unwrap();
|
||||
|
||||
// Then boot it again
|
||||
curl_command(&api_socket, "PUT", "http://localhost/api/v1/vm.boot", None);
|
||||
simple_api_command(&mut socket, "PUT", "boot", None).unwrap();
|
||||
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
|
||||
@@ -4226,8 +4243,9 @@ mod common_parallel {
|
||||
|
||||
thread::sleep(std::time::Duration::new(1, 0));
|
||||
|
||||
let mut socket = UnixStream::connect(&api_socket).unwrap();
|
||||
// Verify API server is running
|
||||
curl_command(&api_socket, "GET", "http://localhost/api/v1/vmm.ping", None);
|
||||
simple_api_full_command(&mut socket, "GET", "vmm.ping", None).unwrap();
|
||||
|
||||
// Create the VM first
|
||||
let cpu_count: u8 = 4;
|
||||
@@ -4236,15 +4254,12 @@ mod common_parallel {
|
||||
direct_kernel_boot_path().to_str().unwrap(),
|
||||
DIRECT_KERNEL_BOOT_CMDLINE,
|
||||
);
|
||||
curl_command(
|
||||
&api_socket,
|
||||
"PUT",
|
||||
"http://localhost/api/v1/vm.create",
|
||||
Some(&http_body),
|
||||
);
|
||||
|
||||
simple_api_command(&mut socket, "PUT", "create", Some(&http_body)).unwrap();
|
||||
|
||||
// Then boot it
|
||||
curl_command(&api_socket, "PUT", "http://localhost/api/v1/vm.boot", None);
|
||||
simple_api_command(&mut socket, "PUT", "boot", None).unwrap();
|
||||
|
||||
thread::sleep(std::time::Duration::new(20, 0));
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
@@ -4320,6 +4335,7 @@ mod common_parallel {
|
||||
.args([
|
||||
"--net",
|
||||
guest.default_net_string().as_str(),
|
||||
"--net",
|
||||
"tap=,mac=8a:6b:6f:5a:de:ac,ip=192.168.3.1,mask=255.255.255.0",
|
||||
])
|
||||
.capture_output()
|
||||
@@ -5061,11 +5077,13 @@ mod common_parallel {
|
||||
guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!(
|
||||
"path={}",
|
||||
guest.disk_config.disk(DiskType::CloudInit).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!("path={}", &loop_dev).as_str(),
|
||||
])
|
||||
.default_net()
|
||||
@@ -5178,6 +5196,7 @@ mod common_parallel {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_virtio_balloon_free_page_reporting() {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(focal));
|
||||
@@ -5630,6 +5649,7 @@ mod common_parallel {
|
||||
guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
cloudinit_params.as_str(),
|
||||
])
|
||||
.args(["--net", net_params.as_str()])
|
||||
@@ -6215,6 +6235,18 @@ mod common_parallel {
|
||||
.unwrap_or_default(),
|
||||
2
|
||||
);
|
||||
|
||||
guest.reboot_linux(0, None);
|
||||
|
||||
assert_eq!(
|
||||
guest
|
||||
.ssh_command("ip -o link | wc -l")
|
||||
.unwrap()
|
||||
.trim()
|
||||
.parse::<u32>()
|
||||
.unwrap_or_default(),
|
||||
2
|
||||
);
|
||||
});
|
||||
|
||||
let _ = child.kill();
|
||||
@@ -6696,12 +6728,58 @@ mod common_parallel {
|
||||
|
||||
handle_child_output(r, &output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn test_tpm() {
|
||||
let focal = UbuntuDiskConfig::new(JAMMY_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(focal));
|
||||
|
||||
let (mut swtpm_command, swtpm_socket_path) = prepare_swtpm_daemon(&guest.tmp_dir);
|
||||
|
||||
let mut guest_cmd = GuestCommand::new(&guest);
|
||||
guest_cmd
|
||||
.args(["--cpus", "boot=1"])
|
||||
.args(["--memory", "size=512M"])
|
||||
.args(["--kernel", fw_path(FwType::RustHypervisorFirmware).as_str()])
|
||||
.args(["--tpm", &format!("socket={swtpm_socket_path}")])
|
||||
.capture_output()
|
||||
.default_disks()
|
||||
.default_net();
|
||||
|
||||
// Start swtpm daemon
|
||||
let mut swtpm_child = swtpm_command.spawn().unwrap();
|
||||
thread::sleep(std::time::Duration::new(10, 0));
|
||||
let mut child = guest_cmd.spawn().unwrap();
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
assert_eq!(
|
||||
guest.ssh_command("ls /dev/tpm0").unwrap().trim(),
|
||||
"/dev/tpm0"
|
||||
);
|
||||
guest.ssh_command("sudo tpm2_selftest -f").unwrap();
|
||||
guest
|
||||
.ssh_command("echo 'hello' > /tmp/checksum_test; ")
|
||||
.unwrap();
|
||||
guest.ssh_command("cmp <(sudo tpm2_pcrevent /tmp/checksum_test | grep sha256 | awk '{print $2}') <(sha256sum /tmp/checksum_test| awk '{print $1}')").unwrap();
|
||||
});
|
||||
|
||||
let _ = swtpm_child.kill();
|
||||
let _d_out = swtpm_child.wait_with_output().unwrap();
|
||||
|
||||
let _ = child.kill();
|
||||
let output = child.wait_with_output().unwrap();
|
||||
|
||||
handle_child_output(r, &output);
|
||||
}
|
||||
}
|
||||
|
||||
mod common_sequential {
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
use crate::*;
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_memory_mergeable_on() {
|
||||
test_memory_mergeable(true)
|
||||
}
|
||||
@@ -7669,7 +7747,9 @@ mod windows {
|
||||
.args([
|
||||
"--net",
|
||||
windows_guest.guest().default_net_string().as_str(),
|
||||
"--net",
|
||||
"tap=,mac=8a:6b:6f:5a:de:ac,ip=192.168.3.1,mask=255.255.255.0",
|
||||
"--net",
|
||||
"tap=mytap42,mac=fe:1f:9e:e1:60:f2,ip=192.168.4.1,mask=255.255.255.0",
|
||||
])
|
||||
.capture_output()
|
||||
@@ -7822,12 +7902,15 @@ mod vfio {
|
||||
guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!(
|
||||
"path={}",
|
||||
guest.disk_config.disk(DiskType::CloudInit).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!("path={}", vfio_disk_path.to_str().unwrap()).as_str(),
|
||||
"--disk",
|
||||
format!("path={},iommu=on", blk_file_path.to_str().unwrap()).as_str(),
|
||||
])
|
||||
.args([
|
||||
@@ -7840,16 +7923,19 @@ mod vfio {
|
||||
.args([
|
||||
"--net",
|
||||
format!("tap={},mac={}", vfio_tap0, guest.network.guest_mac).as_str(),
|
||||
"--net",
|
||||
format!(
|
||||
"tap={},mac={},iommu=on",
|
||||
vfio_tap1, guest.network.l2_guest_mac1
|
||||
)
|
||||
.as_str(),
|
||||
"--net",
|
||||
format!(
|
||||
"tap={},mac={},iommu=on",
|
||||
vfio_tap2, guest.network.l2_guest_mac2
|
||||
)
|
||||
.as_str(),
|
||||
"--net",
|
||||
format!(
|
||||
"tap={},mac={},iommu=on",
|
||||
vfio_tap3, guest.network.l2_guest_mac3
|
||||
@@ -7927,7 +8013,7 @@ mod vfio {
|
||||
let vfio_hotplug_output = guest
|
||||
.ssh_command_l1(
|
||||
"sudo /mnt/ch-remote \
|
||||
--api-socket=/tmp/ch_api.sock \
|
||||
--api-socket /tmp/ch_api.sock \
|
||||
add-device path=/sys/bus/pci/devices/0000:00:09.0,id=vfio123",
|
||||
)
|
||||
.unwrap();
|
||||
@@ -7967,7 +8053,7 @@ mod vfio {
|
||||
guest
|
||||
.ssh_command_l1(
|
||||
"sudo /mnt/ch-remote \
|
||||
--api-socket=/tmp/ch_api.sock \
|
||||
--api-socket /tmp/ch_api.sock \
|
||||
remove-device vfio123",
|
||||
)
|
||||
.unwrap();
|
||||
@@ -7998,7 +8084,7 @@ mod vfio {
|
||||
guest
|
||||
.ssh_command_l1(
|
||||
"sudo /mnt/ch-remote \
|
||||
--api-socket=/tmp/ch_api.sock \
|
||||
--api-socket /tmp/ch_api.sock \
|
||||
resize --memory=1073741824",
|
||||
)
|
||||
.unwrap();
|
||||
@@ -8157,7 +8243,8 @@ mod live_migration {
|
||||
// Start to receive migration from the destintion VM
|
||||
let mut receive_migration = Command::new(clh_command("ch-remote"))
|
||||
.args([
|
||||
&format!("--api-socket={dest_api_socket}"),
|
||||
"--api-socket",
|
||||
dest_api_socket,
|
||||
"receive-migration",
|
||||
&format! {"unix:{migration_socket}"},
|
||||
])
|
||||
@@ -8170,14 +8257,15 @@ mod live_migration {
|
||||
// Start to send migration from the source VM
|
||||
|
||||
let mut args = [
|
||||
format!("--api-socket={}", &src_api_socket),
|
||||
"--api-socket".to_string(),
|
||||
src_api_socket.to_string(),
|
||||
"send-migration".to_string(),
|
||||
format! {"unix:{migration_socket}"},
|
||||
]
|
||||
.to_vec();
|
||||
|
||||
if local {
|
||||
args.insert(2, "--local".to_string());
|
||||
args.insert(3, "--local".to_string());
|
||||
}
|
||||
|
||||
let mut send_migration = Command::new(clh_command("ch-remote"))
|
||||
@@ -8636,11 +8724,15 @@ mod live_migration {
|
||||
"size=0,hotplug_method=virtio-mem,shared=on",
|
||||
"--memory-zone",
|
||||
"id=mem0,size=1G,hotplug_size=4G,shared=on",
|
||||
"--memory-zone",
|
||||
"id=mem1,size=1G,hotplug_size=4G,shared=on",
|
||||
"--memory-zone",
|
||||
"id=mem2,size=2G,hotplug_size=4G,shared=on",
|
||||
"--numa",
|
||||
"guest_numa_id=0,cpus=[0-2,9],distances=[1@15,2@20],memory_zones=mem0",
|
||||
"--numa",
|
||||
"guest_numa_id=1,cpus=[3-4,6-8],distances=[0@20,2@25],memory_zones=mem1",
|
||||
"--numa",
|
||||
"guest_numa_id=2,cpus=[5,10-11],distances=[0@25,1@30],memory_zones=mem2",
|
||||
]
|
||||
} else {
|
||||
@@ -8649,11 +8741,15 @@ mod live_migration {
|
||||
"size=0,hotplug_method=virtio-mem",
|
||||
"--memory-zone",
|
||||
"id=mem0,size=1G,hotplug_size=4G",
|
||||
"--memory-zone",
|
||||
"id=mem1,size=1G,hotplug_size=4G",
|
||||
"--memory-zone",
|
||||
"id=mem2,size=2G,hotplug_size=4G",
|
||||
"--numa",
|
||||
"guest_numa_id=0,cpus=[0-2,9],distances=[1@15,2@20],memory_zones=mem0",
|
||||
"--numa",
|
||||
"guest_numa_id=1,cpus=[3-4,6-8],distances=[0@20,2@25],memory_zones=mem1",
|
||||
"--numa",
|
||||
"guest_numa_id=2,cpus=[5,10-11],distances=[0@25,1@30],memory_zones=mem2",
|
||||
]
|
||||
};
|
||||
@@ -9211,51 +9307,43 @@ mod live_migration {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_live_upgrade_basic() {
|
||||
_test_live_migration(true, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_live_upgrade_local() {
|
||||
_test_live_migration(true, true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_live_upgrade_numa() {
|
||||
_test_live_migration_numa(true, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_live_upgrade_numa_local() {
|
||||
_test_live_migration_numa(true, true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_live_upgrade_watchdog() {
|
||||
_test_live_migration_watchdog(true, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_live_upgrade_watchdog_local() {
|
||||
_test_live_migration_watchdog(true, true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_live_upgrade_balloon() {
|
||||
_test_live_migration_balloon(true, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_live_upgrade_balloon_local() {
|
||||
_test_live_migration_balloon(true, true)
|
||||
}
|
||||
@@ -9282,7 +9370,6 @@ mod live_migration {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_live_upgrade_ovs_dpdk() {
|
||||
@@ -9290,7 +9377,6 @@ mod live_migration {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_live_upgrade_ovs_dpdk_local() {
|
||||
@@ -9491,11 +9577,13 @@ mod rate_limiter {
|
||||
guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
format!(
|
||||
"path={}",
|
||||
guest.disk_config.disk(DiskType::CloudInit).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
"--disk",
|
||||
test_blk_params.as_str(),
|
||||
])
|
||||
.default_net()
|
||||
|
||||
@@ -10,5 +10,6 @@ anyhow = "1.0.66"
|
||||
byteorder = "1.4.3"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
net_gen = { path = "../net_gen" }
|
||||
thiserror = "1.0.37"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
@@ -30,8 +30,8 @@ const PTM_CAP_SET_BUFFERSIZE: u64 = 1 << 13;
|
||||
|
||||
///Check if the input command is selftest
|
||||
///
|
||||
pub fn is_selftest(input: Vec<u8>, in_len: usize) -> bool {
|
||||
if in_len >= TPM_REQ_HDR_SIZE {
|
||||
pub fn is_selftest(input: &[u8]) -> bool {
|
||||
if input.len() >= TPM_REQ_HDR_SIZE {
|
||||
let ordinal: &[u8; 4] = input[6..6 + 4]
|
||||
.try_into()
|
||||
.expect("slice with incorrect length");
|
||||
@@ -61,18 +61,14 @@ pub enum Error {
|
||||
|
||||
type Result<T> = anyhow::Result<T, Error>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BackendCmd {
|
||||
pub locality: u8,
|
||||
pub input: Vec<u8>,
|
||||
pub struct BackendCmd<'a> {
|
||||
// This buffer is used for both input and output.
|
||||
// When used for input, the length of the data is input_len.
|
||||
pub buffer: &'a mut [u8],
|
||||
pub input_len: usize,
|
||||
pub output: Vec<u8>,
|
||||
pub output_len: usize,
|
||||
pub selftest_done: bool,
|
||||
}
|
||||
|
||||
pub struct Emulator {
|
||||
cmd: Option<BackendCmd>,
|
||||
caps: PtmCap, /* capabilities of the TPM */
|
||||
control_socket: SocketDev,
|
||||
data_fd: RawFd,
|
||||
@@ -100,7 +96,6 @@ impl Emulator {
|
||||
})?;
|
||||
|
||||
let mut emulator = Self {
|
||||
cmd: None,
|
||||
caps: 0,
|
||||
control_socket: socket,
|
||||
data_fd: -1,
|
||||
@@ -148,6 +143,27 @@ impl Emulator {
|
||||
self.run_control_cmd(Commands::CmdSetDatafd, &mut res, 0, mem::size_of::<u32>())?;
|
||||
debug!("data fd in cloud-hypervisor = {:?}", fds[0]);
|
||||
self.data_fd = fds[0];
|
||||
|
||||
// SAFETY: FFI calls and return value of the unsafe call is checked
|
||||
unsafe {
|
||||
let tv = net_gen::iff::timeval {
|
||||
tv_sec: 0,
|
||||
tv_usec: 100000, // Set recv timeout to 100ms
|
||||
};
|
||||
let ret = net_gen::setsockopt(
|
||||
fds[0],
|
||||
net_gen::iff::SOL_SOCKET as i32,
|
||||
net_gen::iff::SO_RCVTIMEO as i32,
|
||||
&tv as *const _ as *const libc::c_void,
|
||||
std::mem::size_of::<net_gen::iff::timeval>() as u32,
|
||||
);
|
||||
if ret == -1 {
|
||||
return Err(Error::PrepareDataFd(anyhow!(
|
||||
"Failed to set receive timeout on data fd socket. Error Code {:?}",
|
||||
std::io::Error::last_os_error()
|
||||
)));
|
||||
}
|
||||
}
|
||||
self.control_socket.set_datafd(fds[0]);
|
||||
Ok(())
|
||||
}
|
||||
@@ -202,18 +218,18 @@ impl Emulator {
|
||||
debug!("Control Cmd to send : {:02X?}", cmd);
|
||||
|
||||
let cmd_no = (cmd as u32).to_be_bytes();
|
||||
let n: isize = (mem::size_of::<u32>() + msg_len_in) as isize;
|
||||
let n = mem::size_of::<u32>() + msg_len_in;
|
||||
|
||||
let converted_req = msg.ptm_to_request();
|
||||
debug!("converted request: {:02X?}", converted_req);
|
||||
|
||||
let mut buf = Vec::<u8>::with_capacity(n as usize);
|
||||
let mut buf = Vec::<u8>::with_capacity(n);
|
||||
|
||||
buf.extend(cmd_no);
|
||||
buf.extend(converted_req);
|
||||
debug!("full Control request {:02X?}", buf);
|
||||
|
||||
let _res = self.control_socket.write(&buf).map_err(|e| {
|
||||
let written = self.control_socket.write(&buf).map_err(|e| {
|
||||
Error::RunControlCmd(anyhow!(
|
||||
"Failed while running {:02X?} Control Cmd. Error: {:?}",
|
||||
cmd,
|
||||
@@ -221,7 +237,23 @@ impl Emulator {
|
||||
))
|
||||
})?;
|
||||
|
||||
let mut output = [0_u8; TPM_CRB_BUFFER_MAX];
|
||||
if written < buf.len() {
|
||||
return Err(Error::RunControlCmd(anyhow!(
|
||||
"Truncated write while running {:02X?} Control Cmd",
|
||||
cmd,
|
||||
)));
|
||||
}
|
||||
|
||||
// The largest response is 16 bytes so far.
|
||||
if msg_len_out > 16 {
|
||||
return Err(Error::RunControlCmd(anyhow!(
|
||||
"Response size is too large for Cmd {:02X?}, max 16 wanted {}",
|
||||
cmd,
|
||||
msg_len_out
|
||||
)));
|
||||
}
|
||||
|
||||
let mut output = [0u8; 16];
|
||||
|
||||
// Every Control Cmd gets atleast a result code in response. Read it
|
||||
let read_size = self.control_socket.read(&mut output).map_err(|e| {
|
||||
@@ -277,119 +309,82 @@ impl Emulator {
|
||||
}
|
||||
|
||||
self.established_flag_cached = true;
|
||||
if est.resp.bit != 0 {
|
||||
self.established_flag = false;
|
||||
} else {
|
||||
self.established_flag = true;
|
||||
}
|
||||
self.established_flag = est.resp.bit == 0;
|
||||
|
||||
self.established_flag
|
||||
}
|
||||
|
||||
/// Function to write to data socket and read the response from it
|
||||
fn unix_tx_bufs(&mut self) -> Result<()> {
|
||||
let isselftest: bool;
|
||||
pub fn deliver_request(&mut self, cmd: &mut BackendCmd) -> Result<()> {
|
||||
// SAFETY: type "sockaddr_storage" is valid with an all-zero byte-pattern value
|
||||
let mut addr: sockaddr_storage = unsafe { mem::zeroed() };
|
||||
let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;
|
||||
let isselftest = is_selftest(&cmd.buffer[0..cmd.input_len]);
|
||||
|
||||
if let Some(ref mut cmd) = self.cmd {
|
||||
cmd.selftest_done = false;
|
||||
isselftest = is_selftest(cmd.input.to_vec(), cmd.input_len);
|
||||
debug!(
|
||||
"Send cmd: {:02X?} of len {:?} on data_ioc ",
|
||||
cmd.buffer, cmd.input_len
|
||||
);
|
||||
|
||||
debug!(
|
||||
"Send cmd: {:02X?} of len {:?} on data_ioc ",
|
||||
cmd.input, cmd.input_len
|
||||
let data_vecs = [libc::iovec {
|
||||
iov_base: cmd.buffer.as_ptr() as *mut libc::c_void,
|
||||
iov_len: cmd.input_len,
|
||||
}; 1];
|
||||
|
||||
// SAFETY: all zero values from the unsafe method are updated before usage
|
||||
let mut msghdr: libc::msghdr = unsafe { mem::zeroed() };
|
||||
msghdr.msg_name = ptr::null_mut();
|
||||
msghdr.msg_namelen = 0;
|
||||
msghdr.msg_iov = data_vecs.as_ptr() as *mut libc::iovec;
|
||||
msghdr.msg_iovlen = data_vecs.len() as _;
|
||||
msghdr.msg_control = ptr::null_mut();
|
||||
msghdr.msg_controllen = 0;
|
||||
msghdr.msg_flags = 0;
|
||||
// SAFETY: FFI call and the return value of the unsafe method is checked
|
||||
unsafe {
|
||||
let ret = libc::sendmsg(self.data_fd, &msghdr, 0);
|
||||
if ret == -1 {
|
||||
return Err(Error::SendReceive(anyhow!(
|
||||
"Failed to send tpm command over Data FD. Error Code {:?}",
|
||||
std::io::Error::last_os_error()
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
let output_len;
|
||||
// SAFETY: FFI calls and return value from unsafe method is checked
|
||||
unsafe {
|
||||
let ret = libc::recvfrom(
|
||||
self.data_fd,
|
||||
cmd.buffer.as_mut_ptr() as *mut c_void,
|
||||
cmd.buffer.len(),
|
||||
0,
|
||||
&mut addr as *mut libc::sockaddr_storage as *mut libc::sockaddr,
|
||||
&mut len as *mut socklen_t,
|
||||
);
|
||||
|
||||
let data_vecs = [libc::iovec {
|
||||
iov_base: cmd.input.as_mut_ptr() as *mut libc::c_void,
|
||||
iov_len: cmd.input.len(),
|
||||
}; 1];
|
||||
|
||||
// SAFETY: all zero values from the unsafe method are updated before usage
|
||||
let mut msghdr: libc::msghdr = unsafe { mem::zeroed() };
|
||||
msghdr.msg_name = ptr::null_mut();
|
||||
msghdr.msg_namelen = 0;
|
||||
msghdr.msg_iov = data_vecs.as_ptr() as *mut libc::iovec;
|
||||
msghdr.msg_iovlen = data_vecs.len() as _;
|
||||
msghdr.msg_control = ptr::null_mut();
|
||||
msghdr.msg_controllen = 0;
|
||||
msghdr.msg_flags = 0;
|
||||
// SAFETY: FFI call and the return value of the unsafe method is checked
|
||||
unsafe {
|
||||
let ret = libc::sendmsg(self.data_fd, &msghdr, 0);
|
||||
if ret == -1 {
|
||||
return Err(Error::SendReceive(anyhow!(
|
||||
"Failed to send tpm command over Data FD. Error Code {:?}",
|
||||
std::io::Error::last_os_error()
|
||||
)));
|
||||
}
|
||||
if ret == -1 {
|
||||
return Err(Error::SendReceive(anyhow!(
|
||||
"Failed to receive response for tpm command over Data FD. Error Code {:?}",
|
||||
std::io::Error::last_os_error()
|
||||
)));
|
||||
}
|
||||
output_len = ret as usize;
|
||||
}
|
||||
debug!(
|
||||
"response = {:02X?} len = {:?} selftest = {:?}",
|
||||
cmd.buffer, output_len, isselftest
|
||||
);
|
||||
|
||||
cmd.output.fill(0);
|
||||
// SAFETY: FFI calls and return value from unsafe method is checked
|
||||
unsafe {
|
||||
let ret = libc::recvfrom(
|
||||
self.data_fd,
|
||||
cmd.output.as_ptr() as *mut c_void,
|
||||
cmd.output.len(),
|
||||
0,
|
||||
&mut addr as *mut libc::sockaddr_storage as *mut libc::sockaddr,
|
||||
&mut len as *mut socklen_t,
|
||||
);
|
||||
if ret == -1 {
|
||||
return Err(Error::SendReceive(anyhow!(
|
||||
"Failed to receive response for tpm command over Data FD. Error Code {:?}",
|
||||
std::io::Error::last_os_error()
|
||||
)));
|
||||
}
|
||||
cmd.output_len = ret as usize;
|
||||
}
|
||||
debug!(
|
||||
"response = {:02X?} len = {:?} selftest = {:?}",
|
||||
cmd.output, cmd.output_len, isselftest
|
||||
);
|
||||
|
||||
if isselftest {
|
||||
if cmd.output_len < 10 {
|
||||
return Err(Error::SelfTest(anyhow!(
|
||||
"Self test response should have 10 bytes. Only {:?} returned",
|
||||
cmd.output_len
|
||||
)));
|
||||
}
|
||||
let mut errcode: [u8; 4] = [0; 4];
|
||||
errcode.copy_from_slice(&cmd.output[6..6 + 4]);
|
||||
cmd.selftest_done = u32::from_ne_bytes(errcode).to_be() == 0;
|
||||
}
|
||||
if isselftest && output_len < 10 {
|
||||
return Err(Error::SelfTest(anyhow!(
|
||||
"Self test response should have 10 bytes. Only {:?} returned",
|
||||
output_len
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn deliver_request(&mut self, in_cmd: &mut BackendCmd) -> Result<Vec<u8>> {
|
||||
if self.cmd.is_some() {
|
||||
//previous request did not finish cleanly
|
||||
return Err(Error::DeliverRequest(anyhow!(
|
||||
"Cannot deliver tpm Request as previous cmd did not complete."
|
||||
)));
|
||||
}
|
||||
self.cmd = Some(in_cmd.clone());
|
||||
|
||||
self.unix_tx_bufs()?;
|
||||
|
||||
let output = self.cmd.as_ref().unwrap().output.clone();
|
||||
in_cmd.output.fill(0);
|
||||
in_cmd.output.clone_from(&output);
|
||||
|
||||
self.backend_request_completed();
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
pub fn backend_request_completed(&mut self) {
|
||||
self.cmd = None;
|
||||
}
|
||||
|
||||
pub fn cancel_cmd(&mut self) -> Result<()> {
|
||||
let mut res: PtmResult = 0;
|
||||
|
||||
@@ -409,7 +404,7 @@ impl Emulator {
|
||||
}
|
||||
|
||||
/// Configure buffersize to use while communicating with swtpm
|
||||
fn set_buffer_size(&mut self, wantedsize: usize, actualsize: &mut usize) -> Result<()> {
|
||||
fn set_buffer_size(&mut self, wantedsize: usize) -> Result<usize> {
|
||||
let mut psbs: PtmSetBufferSize = PtmSetBufferSize::new(wantedsize as u32);
|
||||
|
||||
self.stop_tpm()?;
|
||||
@@ -421,19 +416,15 @@ impl Emulator {
|
||||
4 * mem::size_of::<u32>(),
|
||||
)?;
|
||||
|
||||
*actualsize = psbs.get_bufsize() as usize;
|
||||
|
||||
Ok(())
|
||||
Ok(psbs.get_bufsize() as usize)
|
||||
}
|
||||
|
||||
pub fn startup_tpm(&mut self, buffersize: usize) -> Result<()> {
|
||||
let mut init: PtmInit = PtmInit::new();
|
||||
|
||||
let mut actual_size: usize = 0;
|
||||
|
||||
if buffersize != 0 {
|
||||
self.set_buffer_size(buffersize, &mut actual_size)?;
|
||||
debug!("set tpm buffersize to {:?} during Startup", buffersize);
|
||||
let actual_size = self.set_buffer_size(buffersize)?;
|
||||
debug!("set tpm buffersize to {:?} during Startup", actual_size);
|
||||
}
|
||||
|
||||
self.run_control_cmd(
|
||||
@@ -454,12 +445,7 @@ impl Emulator {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_buffer_size(&mut self) -> Result<usize> {
|
||||
let mut curr_buf_size: usize = 0;
|
||||
|
||||
match self.set_buffer_size(0, &mut curr_buf_size) {
|
||||
Err(_) => Ok(TPM_CRB_BUFFER_MAX),
|
||||
_ => Ok(curr_buf_size),
|
||||
}
|
||||
pub fn get_buffer_size(&mut self) -> usize {
|
||||
self.set_buffer_size(0).unwrap_or(TPM_CRB_BUFFER_MAX)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ pub mod emulator;
|
||||
pub mod socket;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use std::convert::TryInto;
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -71,7 +70,7 @@ pub trait Ptm {
|
||||
// Convert PTM Request to bytes to be sent to tpm
|
||||
fn ptm_to_request(&self) -> Vec<u8>;
|
||||
|
||||
// Update PTM from tpm's reponse
|
||||
// Update PTM from tpm's response
|
||||
fn update_ptm_with_response(&mut self, buf: &[u8]) -> Result<()>;
|
||||
|
||||
// Update tpm result
|
||||
@@ -90,8 +89,7 @@ pub type PtmResult = u32;
|
||||
|
||||
impl Ptm for PtmResult {
|
||||
fn ptm_to_request(&self) -> Vec<u8> {
|
||||
let buf: Vec<u8> = Vec::<u8>::new();
|
||||
buf
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn get_member_type(&self) -> MemberType {
|
||||
@@ -99,13 +97,15 @@ impl Ptm for PtmResult {
|
||||
}
|
||||
|
||||
fn update_ptm_with_response(&mut self, buf: &[u8]) -> Result<()> {
|
||||
if buf.len() < 4 {
|
||||
let expected_len = 4;
|
||||
let len = buf.len();
|
||||
if len != expected_len {
|
||||
return Err(Error::ConvertToPtm(anyhow!(
|
||||
"PtmRes buffer is of insufficient length. Buffer length should be atleast 4"
|
||||
"PtmRes buffer is of incorrect length. Got {len} expected {expected_len}."
|
||||
)));
|
||||
}
|
||||
|
||||
*self = u32::from_be_bytes(buf[0..4].try_into().unwrap());
|
||||
*self = u32::from_be_bytes(buf[..].try_into().unwrap());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -126,22 +126,22 @@ impl Ptm for PtmCap {
|
||||
fn ptm_to_request(&self) -> Vec<u8> {
|
||||
// tpm's GetCapability call doesn't need any supporting message
|
||||
// return an empty Buffer
|
||||
let buf: Vec<u8> = Vec::<u8>::new();
|
||||
buf
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn get_member_type(&self) -> MemberType {
|
||||
MemberType::Cap
|
||||
}
|
||||
|
||||
fn update_ptm_with_response(&mut self, mut buf: &[u8]) -> Result<()> {
|
||||
let buf_len = buf.len();
|
||||
if buf_len != 8 {
|
||||
fn update_ptm_with_response(&mut self, buf: &[u8]) -> Result<()> {
|
||||
let expected_len = 8;
|
||||
let len = buf.len();
|
||||
if len != expected_len {
|
||||
return Err(Error::ConvertToPtm(anyhow!(
|
||||
"Response for GetCapability cmd is of incorrect length: {:?}. Response buffer should be 8 bytes long",
|
||||
buf_len)));
|
||||
"Response for GetCapability cmd is of incorrect length. Got {len} expected {expected_len}."
|
||||
)));
|
||||
}
|
||||
*self = buf.read_u64::<BigEndian>().unwrap();
|
||||
*self = u64::from_be_bytes(buf[..].try_into().unwrap());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -187,8 +187,7 @@ impl Ptm for PtmEst {
|
||||
fn ptm_to_request(&self) -> Vec<u8> {
|
||||
// tpm's GetTpmEstablished call doesn't need any supporting message
|
||||
// return an empty Buffer
|
||||
let buf: Vec<u8> = Vec::<u8>::new();
|
||||
buf
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn get_member_type(&self) -> MemberType {
|
||||
@@ -196,15 +195,15 @@ impl Ptm for PtmEst {
|
||||
}
|
||||
|
||||
fn update_ptm_with_response(&mut self, buf: &[u8]) -> Result<()> {
|
||||
if buf.len() < 5 {
|
||||
let expected_len = 8;
|
||||
let len = buf.len();
|
||||
if len != expected_len {
|
||||
return Err(Error::ConvertToPtm(anyhow!(
|
||||
"Response for GetTpmEstablished cmd is of incorrect length. Response buffer should be 5 bytes long"
|
||||
"Response for GetTpmEstablished cmd is of incorrect length. Got {len} expected {expected_len}."
|
||||
)));
|
||||
}
|
||||
let mut res = &buf[0..4];
|
||||
self.set_result_code(res.read_u32::<BigEndian>().unwrap());
|
||||
let bit = &buf[4];
|
||||
self.resp.bit = *bit;
|
||||
self.set_result_code(u32::from_be_bytes(buf[..4].try_into().unwrap()));
|
||||
self.resp.bit = buf[4];
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -258,14 +257,15 @@ impl Ptm for PtmInit {
|
||||
}
|
||||
|
||||
fn update_ptm_with_response(&mut self, buf: &[u8]) -> Result<()> {
|
||||
if buf.len() != 4 {
|
||||
let expected_len = 4;
|
||||
let len = buf.len();
|
||||
if len != expected_len {
|
||||
return Err(Error::ConvertToPtm(anyhow!(
|
||||
"Response for Init cmd is of incorrect length. Response buffer should be 4 bytes long"
|
||||
"Response for Init cmd is of incorrect length. Got {len} expected {expected_len}."
|
||||
)));
|
||||
}
|
||||
self.set_member_type(MemberType::Response);
|
||||
let mut res = &buf[0..4];
|
||||
self.set_result_code(res.read_u32::<BigEndian>().unwrap());
|
||||
self.set_result_code(u32::from_be_bytes(buf[..].try_into().unwrap()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -346,23 +346,24 @@ impl Ptm for PtmSetBufferSize {
|
||||
}
|
||||
|
||||
fn update_ptm_with_response(&mut self, buf: &[u8]) -> Result<()> {
|
||||
if buf.len() != 16 {
|
||||
let expected_len = 16;
|
||||
let len = buf.len();
|
||||
if len != expected_len {
|
||||
return Err(Error::ConvertToPtm(anyhow!(
|
||||
"Response for CmdSetBufferSize cmd is of incorrect length. Response buffer should be 16 bytes long"
|
||||
"Response for CmdSetBufferSize cmd is of incorrect length. Got {len} expected {expected_len}."
|
||||
)));
|
||||
}
|
||||
self.set_member_type(MemberType::Response);
|
||||
let mut res = &buf[0..4];
|
||||
self.set_result_code(res.read_u32::<BigEndian>().unwrap());
|
||||
self.set_result_code(u32::from_be_bytes(buf[0..4].try_into().unwrap()));
|
||||
|
||||
let mut bufsize = &buf[4..8];
|
||||
self.resp.bufsize = bufsize.read_u32::<BigEndian>().unwrap();
|
||||
let bufsize = &buf[4..8];
|
||||
self.resp.bufsize = u32::from_be_bytes(bufsize.try_into().unwrap());
|
||||
|
||||
let mut minsize = &buf[8..12];
|
||||
self.resp.minsize = minsize.read_u32::<BigEndian>().unwrap();
|
||||
let minsize = &buf[8..12];
|
||||
self.resp.minsize = u32::from_be_bytes(minsize.try_into().unwrap());
|
||||
|
||||
let mut maxsize = &buf[12..16];
|
||||
self.resp.maxsize = maxsize.read_u32::<BigEndian>().unwrap();
|
||||
let maxsize = &buf[12..16];
|
||||
self.resp.maxsize = u32::from_be_bytes(maxsize.try_into().unwrap());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use crate::TPM_CRB_BUFFER_MAX;
|
||||
use anyhow::anyhow;
|
||||
use std::io::Read;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
@@ -122,23 +121,15 @@ impl SocketDev {
|
||||
}
|
||||
|
||||
pub fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
let newbuf: &mut [u8] = &mut [0; TPM_CRB_BUFFER_MAX];
|
||||
|
||||
if self.stream.is_none() {
|
||||
return Err(Error::ReadFromSocket(anyhow!(
|
||||
"Stream for tpm socket was not initialized"
|
||||
)));
|
||||
}
|
||||
let mut socket = self.stream.as_ref().unwrap();
|
||||
let size: usize = socket.read(newbuf).map_err(|e| {
|
||||
let size: usize = socket.read(buf).map_err(|e| {
|
||||
Error::ReadFromSocket(anyhow!("Failed to read from socket. Error Code {:?}", e))
|
||||
})?;
|
||||
if buf.len() < size {
|
||||
return Err(Error::ReadFromSocket(anyhow!(
|
||||
"Input buffer is of insufficient size"
|
||||
)));
|
||||
}
|
||||
buf[0..size].clone_from_slice(&newbuf[0..size]);
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ edition = "2021"
|
||||
[dependencies]
|
||||
libc = "0.2.139"
|
||||
log = "0.4.17"
|
||||
once_cell = "1.16.0"
|
||||
once_cell = "1.17.1"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.89"
|
||||
serde_json = "1.0.95"
|
||||
|
||||
[features]
|
||||
tracing = []
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
[package]
|
||||
name = "vfio_user"
|
||||
version = "0.1.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.139"
|
||||
log = "0.4.17"
|
||||
serde = { version = "1.0.151", features = ["rc"] }
|
||||
serde_derive = "1.0.149"
|
||||
serde_json = "1.0.89"
|
||||
thiserror = "1.0.38"
|
||||
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main", features = ["fam-wrappers"] }
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = "0.11.0"
|
||||
@@ -1,796 +0,0 @@
|
||||
// Copyright © 2021 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use std::ffi::CString;
|
||||
use std::fs::File;
|
||||
use std::io::{IoSlice, Read, Write};
|
||||
use std::mem::size_of;
|
||||
use std::num::Wrapping;
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::os::unix::prelude::RawFd;
|
||||
use std::path::Path;
|
||||
use thiserror::Error;
|
||||
use vfio_bindings::bindings::vfio::*;
|
||||
use vm_memory::{ByteValued, FileOffset};
|
||||
use vmm_sys_util::sock_ctrl_msg::ScmSocket;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(u16)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Command {
|
||||
Unknown = 0,
|
||||
Version = 1,
|
||||
DmaMap = 2,
|
||||
DmaUnmap = 3,
|
||||
DeviceGetInfo = 4,
|
||||
DeviceGetRegionInfo = 5,
|
||||
GetRegionIoFds = 6,
|
||||
GetIrqInfo = 7,
|
||||
SetIrqs = 8,
|
||||
RegionRead = 9,
|
||||
RegionWrite = 10,
|
||||
DmaRead = 11,
|
||||
DmaWrite = 12,
|
||||
DeviceReset = 13,
|
||||
UserDirtyPages = 14,
|
||||
}
|
||||
|
||||
impl Default for Command {
|
||||
fn default() -> Self {
|
||||
Command::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(u32)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum HeaderFlags {
|
||||
Command = 0,
|
||||
Reply = 1,
|
||||
NoReply = 1 << 4,
|
||||
Error = 1 << 5,
|
||||
}
|
||||
|
||||
impl Default for HeaderFlags {
|
||||
fn default() -> Self {
|
||||
HeaderFlags::Command
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct Header {
|
||||
message_id: u16,
|
||||
command: Command,
|
||||
message_size: u32,
|
||||
flags: u32,
|
||||
error: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct Version {
|
||||
header: Header,
|
||||
major: u16,
|
||||
minor: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct MigrationCapabilities {
|
||||
pgsize: u32,
|
||||
}
|
||||
|
||||
const fn default_max_msg_fds() -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
const fn default_max_data_xfer_size() -> u32 {
|
||||
1048576
|
||||
}
|
||||
|
||||
const fn default_migration_capabilities() -> MigrationCapabilities {
|
||||
MigrationCapabilities { pgsize: 4096 }
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[allow(dead_code)]
|
||||
enum DmaMapFlags {
|
||||
Unknown = 0,
|
||||
ReadOnly = 1,
|
||||
WriteOnly = 2,
|
||||
ReadWrite = 3,
|
||||
}
|
||||
|
||||
impl Default for DmaMapFlags {
|
||||
fn default() -> Self {
|
||||
Self::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct DmaMap {
|
||||
header: Header,
|
||||
argsz: u32,
|
||||
flags: DmaMapFlags,
|
||||
offset: u64,
|
||||
address: u64,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct DmaUnmap {
|
||||
header: Header,
|
||||
argsz: u32,
|
||||
flags: u32,
|
||||
address: u64,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct DeviceGetInfo {
|
||||
header: Header,
|
||||
argsz: u32,
|
||||
flags: u32,
|
||||
num_regions: u32,
|
||||
num_irqs: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct DeviceGetRegionInfo {
|
||||
header: Header,
|
||||
region_info: vfio_region_info,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct RegionAccess {
|
||||
header: Header,
|
||||
offset: u64,
|
||||
region: u32,
|
||||
count: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct GetIrqInfo {
|
||||
header: Header,
|
||||
argsz: u32,
|
||||
flags: u32,
|
||||
index: u32,
|
||||
count: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct SetIrqs {
|
||||
header: Header,
|
||||
argsz: u32,
|
||||
flags: u32,
|
||||
index: u32,
|
||||
start: u32,
|
||||
count: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct DeviceReset {
|
||||
header: Header,
|
||||
}
|
||||
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for Header {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for Version {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for DmaMap {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for DmaUnmap {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for DeviceGetInfo {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for DeviceGetRegionInfo {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for RegionAccess {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for GetIrqInfo {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for SetIrqs {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for DeviceReset {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct Capabilities {
|
||||
#[serde(default = "default_max_msg_fds")]
|
||||
max_msg_fds: u32,
|
||||
#[serde(default = "default_max_data_xfer_size")]
|
||||
max_data_xfer_size: u32,
|
||||
#[serde(default = "default_migration_capabilities")]
|
||||
migration: MigrationCapabilities,
|
||||
}
|
||||
|
||||
impl Default for Capabilities {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_msg_fds: default_max_msg_fds(),
|
||||
max_data_xfer_size: default_max_data_xfer_size(),
|
||||
migration: default_migration_capabilities(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Client {
|
||||
stream: UnixStream,
|
||||
next_message_id: Wrapping<u16>,
|
||||
num_irqs: u32,
|
||||
resettable: bool,
|
||||
regions: Vec<Region>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Region {
|
||||
pub flags: u32,
|
||||
pub index: u32,
|
||||
pub size: u64,
|
||||
pub file_offset: Option<FileOffset>,
|
||||
pub sparse_areas: Vec<vfio_region_sparse_mmap_area>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IrqInfo {
|
||||
pub index: u32,
|
||||
pub flags: u32,
|
||||
pub count: u32,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Error connecting: {0}")]
|
||||
Connect(#[source] std::io::Error),
|
||||
#[error("Error serializing capabilities: {0}")]
|
||||
SerializeCapabilites(#[source] serde_json::Error),
|
||||
#[error("Error deserializing capabilities: {0}")]
|
||||
DeserializeCapabilites(#[source] serde_json::Error),
|
||||
#[error("Error writing to stream: {0}")]
|
||||
StreamWrite(#[source] std::io::Error),
|
||||
#[error("Error reading from stream: {0}")]
|
||||
StreamRead(#[source] std::io::Error),
|
||||
#[error("Error shutting down stream: {0}")]
|
||||
StreamShutdown(#[source] std::io::Error),
|
||||
#[error("Error writing with file descriptors: {0}")]
|
||||
SendWithFd(#[source] vmm_sys_util::errno::Error),
|
||||
#[error("Error reading with file descriptors: {0}")]
|
||||
ReceiveWithFd(#[source] vmm_sys_util::errno::Error),
|
||||
#[error("Not a PCI device")]
|
||||
NotPciDevice,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new(path: &Path) -> Result<Client, Error> {
|
||||
let stream = UnixStream::connect(path).map_err(Error::Connect)?;
|
||||
|
||||
let mut client = Client {
|
||||
next_message_id: Wrapping(0),
|
||||
stream,
|
||||
num_irqs: 0,
|
||||
resettable: false,
|
||||
regions: Vec::new(),
|
||||
};
|
||||
|
||||
client.negotiate_version()?;
|
||||
|
||||
client.regions = client.get_regions()?;
|
||||
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
fn negotiate_version(&mut self) -> Result<(), Error> {
|
||||
let caps = Capabilities::default();
|
||||
|
||||
let version_data = serde_json::to_string(&caps).map_err(Error::SerializeCapabilites)?;
|
||||
|
||||
let version = Version {
|
||||
header: Header {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::Version,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: (size_of::<Version>() + version_data.len() + 1) as u32,
|
||||
..Default::default()
|
||||
},
|
||||
major: 0,
|
||||
minor: 1,
|
||||
};
|
||||
debug!("Command: {:?}", version);
|
||||
|
||||
let version_data = CString::new(version_data.as_bytes()).unwrap();
|
||||
let bufs = vec![
|
||||
IoSlice::new(version.as_slice()),
|
||||
IoSlice::new(version_data.as_bytes_with_nul()),
|
||||
];
|
||||
|
||||
// TODO: Use write_all_vectored() when ready
|
||||
let _ = self
|
||||
.stream
|
||||
.write_vectored(&bufs)
|
||||
.map_err(Error::StreamWrite)?;
|
||||
|
||||
debug!(
|
||||
"Sent client version information: major = {} minor = {} capabilities = {:?}",
|
||||
version.major, version.minor, &caps
|
||||
);
|
||||
|
||||
self.next_message_id += Wrapping(1);
|
||||
|
||||
let mut server_version: Version = Version::default();
|
||||
self.stream
|
||||
.read_exact(server_version.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
|
||||
debug!("Reply: {:?}", server_version);
|
||||
|
||||
let mut server_version_data = Vec::new();
|
||||
server_version_data.resize(
|
||||
server_version.header.message_size as usize - size_of::<Version>(),
|
||||
0,
|
||||
);
|
||||
self.stream
|
||||
.read_exact(server_version_data.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
|
||||
let server_caps: Capabilities =
|
||||
serde_json::from_slice(&server_version_data[0..server_version_data.len() - 1])
|
||||
.map_err(Error::DeserializeCapabilites)?;
|
||||
|
||||
debug!(
|
||||
"Received server version information: major = {} minor = {} capabilities = {:?}",
|
||||
server_version.major, server_version.minor, &server_caps
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn dma_map(
|
||||
&mut self,
|
||||
offset: u64,
|
||||
address: u64,
|
||||
size: u64,
|
||||
fd: RawFd,
|
||||
) -> Result<(), Error> {
|
||||
let dma_map = DmaMap {
|
||||
header: Header {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::DmaMap,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: size_of::<DmaMap>() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
argsz: (size_of::<DmaMap>() - size_of::<Header>()) as u32,
|
||||
flags: DmaMapFlags::ReadWrite,
|
||||
offset,
|
||||
address,
|
||||
size,
|
||||
};
|
||||
debug!("Command: {:?}", dma_map);
|
||||
self.next_message_id += Wrapping(1);
|
||||
self.stream
|
||||
.send_with_fd(dma_map.as_slice(), fd)
|
||||
.map_err(Error::SendWithFd)?;
|
||||
|
||||
let mut reply = Header::default();
|
||||
self.stream
|
||||
.read_exact(reply.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
debug!("Reply: {:?}", reply);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn dma_unmap(&mut self, address: u64, size: u64) -> Result<(), Error> {
|
||||
let dma_unmap = DmaUnmap {
|
||||
header: Header {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::DmaUnmap,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: size_of::<DmaUnmap>() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
argsz: (size_of::<DmaUnmap>() - size_of::<Header>()) as u32,
|
||||
flags: 0,
|
||||
address,
|
||||
size,
|
||||
};
|
||||
debug!("Command: {:?}", dma_unmap);
|
||||
self.next_message_id += Wrapping(1);
|
||||
self.stream
|
||||
.write_all(dma_unmap.as_slice())
|
||||
.map_err(Error::StreamWrite)?;
|
||||
|
||||
let mut reply = DmaUnmap::default();
|
||||
self.stream
|
||||
.read_exact(reply.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
debug!("Reply: {:?}", reply);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) -> Result<(), Error> {
|
||||
let reset = DeviceReset {
|
||||
header: Header {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::DeviceReset,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: size_of::<DeviceReset>() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
debug!("Command: {:?}", reset);
|
||||
self.next_message_id += Wrapping(1);
|
||||
self.stream
|
||||
.write_all(reset.as_slice())
|
||||
.map_err(Error::StreamWrite)?;
|
||||
|
||||
let mut reply = Header::default();
|
||||
self.stream
|
||||
.read_exact(reply.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
debug!("Reply: {:?}", reply);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_regions(&mut self) -> Result<Vec<Region>, Error> {
|
||||
let get_info = DeviceGetInfo {
|
||||
header: Header {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::DeviceGetInfo,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: size_of::<DeviceGetInfo>() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
argsz: size_of::<DeviceGetInfo>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
debug!("Command: {:?}", get_info);
|
||||
self.next_message_id += Wrapping(1);
|
||||
|
||||
self.stream
|
||||
.write_all(get_info.as_slice())
|
||||
.map_err(Error::StreamWrite)?;
|
||||
|
||||
let mut reply = DeviceGetInfo::default();
|
||||
self.stream
|
||||
.read_exact(reply.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
debug!("Reply: {:?}", reply);
|
||||
self.num_irqs = reply.num_irqs;
|
||||
|
||||
if reply.flags & VFIO_DEVICE_FLAGS_PCI != VFIO_DEVICE_FLAGS_PCI {
|
||||
return Err(Error::NotPciDevice);
|
||||
}
|
||||
|
||||
self.resettable = reply.flags & VFIO_DEVICE_FLAGS_RESET != VFIO_DEVICE_FLAGS_RESET;
|
||||
|
||||
let num_regions = reply.num_regions;
|
||||
let mut regions = Vec::new();
|
||||
for index in 0..num_regions {
|
||||
let (region_info, fd, sparse_areas) = self.get_region_info(index)?;
|
||||
regions.push(Region {
|
||||
flags: region_info.flags,
|
||||
index: region_info.index,
|
||||
size: region_info.size,
|
||||
file_offset: fd.map(|fd| FileOffset::new(fd, region_info.offset)),
|
||||
sparse_areas,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(regions)
|
||||
}
|
||||
|
||||
fn get_region_info(
|
||||
&mut self,
|
||||
index: u32,
|
||||
) -> Result<
|
||||
(
|
||||
vfio_region_info,
|
||||
Option<File>,
|
||||
Vec<vfio_region_sparse_mmap_area>,
|
||||
),
|
||||
Error,
|
||||
> {
|
||||
// Retrieve the region info without capability
|
||||
let mut get_region_info = DeviceGetRegionInfo {
|
||||
header: Header {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::DeviceGetRegionInfo,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: std::mem::size_of::<DeviceGetRegionInfo>() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
region_info: vfio_region_info {
|
||||
argsz: size_of::<vfio_region_info>() as u32,
|
||||
index,
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
debug!("Command: {:?}", get_region_info);
|
||||
self.next_message_id += Wrapping(1);
|
||||
|
||||
self.stream
|
||||
.write_all(get_region_info.as_slice())
|
||||
.map_err(Error::StreamWrite)?;
|
||||
|
||||
let mut reply = DeviceGetRegionInfo::default();
|
||||
let (_, fd) = self
|
||||
.stream
|
||||
.recv_with_fd(reply.as_mut_slice())
|
||||
.map_err(Error::ReceiveWithFd)?;
|
||||
debug!("Reply: {:?}", reply);
|
||||
|
||||
// Retrieve the region info again with capabilities if needed
|
||||
if reply.region_info.argsz > std::mem::size_of::<vfio_region_info>() as u32 {
|
||||
get_region_info.region_info.argsz = reply.region_info.argsz;
|
||||
debug!("Command: {:?}", get_region_info);
|
||||
self.next_message_id += Wrapping(1);
|
||||
|
||||
self.stream
|
||||
.write_all(get_region_info.as_slice())
|
||||
.map_err(Error::StreamWrite)?;
|
||||
|
||||
let mut reply = DeviceGetRegionInfo::default();
|
||||
let (_, fd) = self
|
||||
.stream
|
||||
.recv_with_fd(reply.as_mut_slice())
|
||||
.map_err(Error::ReceiveWithFd)?;
|
||||
debug!("Reply: {:?}", reply);
|
||||
|
||||
let cap_size = reply.region_info.argsz - std::mem::size_of::<vfio_region_info>() as u32;
|
||||
assert_eq!(
|
||||
cap_size,
|
||||
reply.header.message_size - size_of::<DeviceGetRegionInfo>() as u32
|
||||
);
|
||||
let mut cap_data = vec![0; cap_size as usize];
|
||||
self.stream
|
||||
.read_exact(cap_data.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
|
||||
let sparse_areas = Self::parse_region_caps(&cap_data, &reply.region_info)?;
|
||||
|
||||
Ok((reply.region_info, fd, sparse_areas))
|
||||
} else {
|
||||
Ok((reply.region_info, fd, Vec::new()))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_region_caps(
|
||||
cap_data: &[u8],
|
||||
region_info: &vfio_region_info,
|
||||
) -> Result<Vec<vfio_region_sparse_mmap_area>, Error> {
|
||||
let mut sparse_areas: Vec<vfio_region_sparse_mmap_area> = Vec::new();
|
||||
|
||||
let cap_size = cap_data.len() as u32;
|
||||
let cap_header_size = size_of::<vfio_info_cap_header>() as u32;
|
||||
let mmap_cap_size = size_of::<vfio_region_info_cap_sparse_mmap>() as u32;
|
||||
let mmap_area_size = size_of::<vfio_region_sparse_mmap_area>() as u32;
|
||||
|
||||
let cap_data_ptr = cap_data.as_ptr() as *const u8;
|
||||
let mut region_info_offset = region_info.cap_offset;
|
||||
while region_info_offset != 0 {
|
||||
// calculate the offset from the begining of the cap_data based on the offset
|
||||
// that is relative to the begining of the VFIO region info structure
|
||||
let cap_offset = region_info_offset - size_of::<vfio_region_info>() as u32;
|
||||
if cap_offset + cap_header_size > cap_size {
|
||||
warn!(
|
||||
"Unexpected end of cap data: 'cap_offset + cap_header_size > cap_size' \
|
||||
cap_offset = {}, cap_header_size = {}, cap_size = {}",
|
||||
cap_offset, cap_header_size, cap_size
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// SAFETY: `cap_data_ptr` is valid and the `cap_offset` is checked above
|
||||
let cap_ptr = unsafe { cap_data_ptr.offset(cap_offset as isize) };
|
||||
// SAFETY: `cap_ptr` is valid
|
||||
let cap_header = unsafe { &*(cap_ptr as *const vfio_info_cap_header) };
|
||||
match cap_header.id as u32 {
|
||||
VFIO_REGION_INFO_CAP_SPARSE_MMAP => {
|
||||
if cap_offset + mmap_cap_size > cap_size {
|
||||
warn!(
|
||||
"Unexpected end of cap data: 'cap_offset + mmap_cap_size > cap_size' \
|
||||
cap_offset = {}, mmap_cap_size = {}, cap_size = {}",
|
||||
cap_offset, mmap_cap_size, cap_size
|
||||
);
|
||||
break;
|
||||
}
|
||||
// SAFETY: `cap_ptr` is valid and its size is also checked above
|
||||
let sparse_mmap = unsafe {
|
||||
&*(cap_ptr as *mut u8 as *const vfio_region_info_cap_sparse_mmap)
|
||||
};
|
||||
|
||||
let area_num = sparse_mmap.nr_areas;
|
||||
if cap_offset + mmap_cap_size + area_num * mmap_area_size > cap_size {
|
||||
warn!("Unexpected end of cap data: 'cap_offset + mmap_cap_size + area_num * mmap_area_size > cap_size' \
|
||||
cap_offset = {}, mmap_cap_size = {}, area_num = {}, mmap_area_size = {}, cap_size = {}",
|
||||
cap_offset, mmap_cap_size, area_num, mmap_area_size, cap_size);
|
||||
break;
|
||||
}
|
||||
let areas =
|
||||
// SAFETY: `sparse_mmap` is valid and its size is also checked above
|
||||
unsafe { sparse_mmap.areas.as_slice(sparse_mmap.nr_areas as usize) };
|
||||
for area in areas.iter() {
|
||||
sparse_areas.push(*area);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
warn!(
|
||||
"Ignoring unsupported vfio region capability (id = '{}')",
|
||||
cap_header.id
|
||||
);
|
||||
}
|
||||
}
|
||||
region_info_offset = cap_header.next;
|
||||
}
|
||||
|
||||
Ok(sparse_areas)
|
||||
}
|
||||
|
||||
pub fn region_read(&mut self, region: u32, offset: u64, data: &mut [u8]) -> Result<(), Error> {
|
||||
let region_read = RegionAccess {
|
||||
header: Header {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::RegionRead,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: size_of::<RegionAccess>() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
offset,
|
||||
count: data.len() as u32,
|
||||
region,
|
||||
};
|
||||
debug!("Command: {:?}", region_read);
|
||||
self.next_message_id += Wrapping(1);
|
||||
self.stream
|
||||
.write_all(region_read.as_slice())
|
||||
.map_err(Error::StreamWrite)?;
|
||||
|
||||
let mut reply = RegionAccess::default();
|
||||
self.stream
|
||||
.read_exact(reply.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
debug!("Reply: {:?}", reply);
|
||||
self.stream.read_exact(data).map_err(Error::StreamRead)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn region_write(&mut self, region: u32, offset: u64, data: &[u8]) -> Result<(), Error> {
|
||||
let region_write = RegionAccess {
|
||||
header: Header {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::RegionWrite,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: (size_of::<RegionAccess>() + data.len()) as u32,
|
||||
..Default::default()
|
||||
},
|
||||
offset,
|
||||
count: data.len() as u32,
|
||||
region,
|
||||
};
|
||||
debug!("Command: {:?}", region_write);
|
||||
self.next_message_id += Wrapping(1);
|
||||
|
||||
let bufs = vec![IoSlice::new(region_write.as_slice()), IoSlice::new(data)];
|
||||
|
||||
// TODO: Use write_all_vectored() when ready
|
||||
let _ = self
|
||||
.stream
|
||||
.write_vectored(&bufs)
|
||||
.map_err(Error::StreamWrite)?;
|
||||
|
||||
let mut reply = RegionAccess::default();
|
||||
self.stream
|
||||
.read_exact(reply.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
debug!("Reply: {:?}", reply);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_irq_info(&mut self, index: u32) -> Result<IrqInfo, Error> {
|
||||
let get_irq_info = GetIrqInfo {
|
||||
header: Header {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::GetIrqInfo,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: size_of::<GetIrqInfo>() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
argsz: (size_of::<GetIrqInfo>() - size_of::<Header>()) as u32,
|
||||
flags: 0,
|
||||
index,
|
||||
count: 0,
|
||||
};
|
||||
debug!("Command: {:?}", get_irq_info);
|
||||
self.next_message_id += Wrapping(1);
|
||||
|
||||
self.stream
|
||||
.write_all(get_irq_info.as_slice())
|
||||
.map_err(Error::StreamWrite)?;
|
||||
|
||||
let mut reply = GetIrqInfo::default();
|
||||
self.stream
|
||||
.read_exact(reply.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
debug!("Reply: {:?}", reply);
|
||||
|
||||
Ok(IrqInfo {
|
||||
index: reply.index,
|
||||
flags: reply.flags,
|
||||
count: reply.count,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_irqs(
|
||||
&mut self,
|
||||
index: u32,
|
||||
flags: u32,
|
||||
start: u32,
|
||||
count: u32,
|
||||
fds: &[RawFd],
|
||||
) -> Result<(), Error> {
|
||||
let set_irqs = SetIrqs {
|
||||
header: Header {
|
||||
message_id: self.next_message_id.0,
|
||||
command: Command::SetIrqs,
|
||||
flags: HeaderFlags::Command as u32,
|
||||
message_size: size_of::<SetIrqs>() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
argsz: (size_of::<SetIrqs>() - size_of::<Header>()) as u32,
|
||||
flags,
|
||||
start,
|
||||
index,
|
||||
count,
|
||||
};
|
||||
debug!("Command: {:?}", set_irqs);
|
||||
self.next_message_id += Wrapping(1);
|
||||
|
||||
self.stream
|
||||
.send_with_fds(&[set_irqs.as_slice()], fds)
|
||||
.map_err(Error::SendWithFd)?;
|
||||
|
||||
let mut reply = Header::default();
|
||||
self.stream
|
||||
.read_exact(reply.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
debug!("Reply: {:?}", reply);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn region(&self, region_index: u32) -> Option<&Region> {
|
||||
self.regions
|
||||
.iter()
|
||||
.find(|®ion| region.index == region_index)
|
||||
}
|
||||
|
||||
pub fn resettable(&self) -> bool {
|
||||
self.resettable
|
||||
}
|
||||
|
||||
pub fn shutdown(&self) -> Result<(), Error> {
|
||||
self.stream
|
||||
.shutdown(std::net::Shutdown::Both)
|
||||
.map_err(Error::StreamShutdown)
|
||||
}
|
||||
}
|
||||
@@ -11,5 +11,5 @@ crc32c = "0.6.3"
|
||||
libc = "0.2.139"
|
||||
log = "0.4.17"
|
||||
remain = "0.2.6"
|
||||
thiserror = "1.0.38"
|
||||
uuid = { version = "1.2.2", features = ["v4"] }
|
||||
thiserror = "1.0.39"
|
||||
uuid = { version = "1.3.0", features = ["v4"] }
|
||||
|
||||
@@ -145,11 +145,7 @@ impl DiskSpec {
|
||||
let bits = f
|
||||
.read_u32::<LittleEndian>()
|
||||
.map_err(VhdxMetadataError::ReadMetadata)?;
|
||||
if bits & BLOCK_HAS_PARENT != 0 {
|
||||
disk_spec.has_parent = true;
|
||||
} else {
|
||||
disk_spec.has_parent = false;
|
||||
}
|
||||
disk_spec.has_parent = bits & BLOCK_HAS_PARENT != 0;
|
||||
|
||||
metadata_presence |= METADATA_FILE_PARAMETER_PRESENT;
|
||||
} else if metadata_entry.item_id
|
||||
|
||||
@@ -3,10 +3,11 @@ name = "vhost_user_block"
|
||||
version = "0.1.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
build = "../build.rs"
|
||||
|
||||
[dependencies]
|
||||
argh = "0.1.9"
|
||||
block_util = { path = "../block_util" }
|
||||
clap = { version = "4.0.32", features = ["wrap_help"] }
|
||||
env_logger = "0.10.0"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.139"
|
||||
@@ -15,8 +16,8 @@ option_parser = { path = "../option_parser" }
|
||||
qcow = { path = "../qcow" }
|
||||
vhost = { version = "0.6.0", features = ["vhost-user-slave"] }
|
||||
vhost-user-backend = "0.8.0"
|
||||
virtio-bindings = "0.1.0"
|
||||
virtio-queue = "0.7.0"
|
||||
virtio-bindings = "0.2.0"
|
||||
virtio-queue = "0.7.1"
|
||||
vm-memory = "0.10.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
|
||||
@@ -31,8 +31,9 @@ use std::{convert, error, fmt, io};
|
||||
use vhost::vhost_user::message::*;
|
||||
use vhost::vhost_user::Listener;
|
||||
use vhost_user_backend::{VhostUserBackendMut, VhostUserDaemon, VringRwLock, VringState, VringT};
|
||||
use virtio_bindings::bindings::virtio_blk::*;
|
||||
use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
||||
use virtio_bindings::virtio_blk::*;
|
||||
use virtio_bindings::virtio_config::VIRTIO_F_VERSION_1;
|
||||
use virtio_bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
||||
use virtio_queue::QueueT;
|
||||
use vm_memory::GuestAddressSpace;
|
||||
use vm_memory::{bitmap::AtomicBitmap, ByteValued, Bytes, GuestMemoryAtomic};
|
||||
@@ -70,11 +71,6 @@ enum Error {
|
||||
SocketParameterMissing,
|
||||
}
|
||||
|
||||
pub const SYNTAX: &str = "vhost-user-block backend parameters \
|
||||
\"path=<image_path>,socket=<socket_path>,num_queues=<number_of_queues>,\
|
||||
queue_size=<size_of_each_queue>,readonly=true|false,direct=true|false,\
|
||||
poll_queue=true|false\"";
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "vhost_user_block_error: {self:?}")
|
||||
|
||||
@@ -10,25 +10,36 @@
|
||||
|
||||
extern crate vhost_user_block;
|
||||
|
||||
use clap::{Arg, Command};
|
||||
use argh::FromArgs;
|
||||
use vhost_user_block::start_block_backend;
|
||||
|
||||
#[derive(FromArgs)]
|
||||
/// Launch a vhost-user-blk backend.
|
||||
struct TopLevel {
|
||||
#[argh(option, long = "block-backend")]
|
||||
/// vhost-user-block backend parameters
|
||||
/// path=<image_path>,socket=<socket_path>,num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,readonly=true|false,direct=true|false,poll_queue=true|false
|
||||
backend_command: Option<String>,
|
||||
|
||||
#[argh(switch, short = 'V', long = "version")]
|
||||
/// print version information
|
||||
version: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
||||
let cmd_arguments = Command::new("vhost-user-blk backend")
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
.author(env!("CARGO_PKG_AUTHORS"))
|
||||
.about("Launch a vhost-user-blk backend.")
|
||||
.arg(
|
||||
Arg::new("block-backend")
|
||||
.long("block-backend")
|
||||
.help(vhost_user_block::SYNTAX)
|
||||
.num_args(1)
|
||||
.required(true),
|
||||
)
|
||||
.get_matches();
|
||||
let toplevel: TopLevel = argh::from_env();
|
||||
|
||||
let backend_command = cmd_arguments.get_one::<String>("block-backend").unwrap();
|
||||
start_block_backend(backend_command);
|
||||
if toplevel.version {
|
||||
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILT_VERSION"));
|
||||
return;
|
||||
}
|
||||
|
||||
if toplevel.backend_command.is_none() {
|
||||
println!("Please specify --block-backend");
|
||||
std::process::exit(1)
|
||||
}
|
||||
|
||||
start_block_backend(&toplevel.backend_command.unwrap());
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user