mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
328 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e702dcd5e | ||
|
|
602d704558 | ||
|
|
38a2808d85 | ||
|
|
5e2f218832 | ||
|
|
2fb8854ff6 | ||
|
|
883bb22b65 | ||
|
|
442fe6afcb | ||
|
|
e64b66054e | ||
|
|
ceb1be9f50 | ||
|
|
7d5ea5ca37 | ||
|
|
d5839fe03c | ||
|
|
de1b7f5e83 | ||
|
|
cf8a348238 | ||
|
|
2197989782 | ||
|
|
5d4fe8efb9 | ||
|
|
638e29bdcc | ||
|
|
d1560b4223 | ||
|
|
6d7224dfee | ||
|
|
f38adfa166 | ||
|
|
7030b15e63 | ||
|
|
ec79820b3f | ||
|
|
b9117c9c50 | ||
|
|
13ef424bf1 | ||
|
|
c0faa75922 | ||
|
|
5d411d257a | ||
|
|
77f4e35bc8 | ||
|
|
b2249827f5 | ||
|
|
f0c5592ba1 | ||
|
|
b92856f41b | ||
|
|
6a90902a4b | ||
|
|
0e9513f2b7 | ||
|
|
283ae7b33e | ||
|
|
102a1c9abc | ||
|
|
32124bce13 | ||
|
|
5f89461a7e | ||
|
|
b00cc483f9 | ||
|
|
705fbefebe | ||
|
|
d448dbd751 | ||
|
|
9d29769536 | ||
|
|
d9f7fd1db8 | ||
|
|
a6d994bf75 | ||
|
|
86aa60a226 | ||
|
|
800f971381 | ||
|
|
341a4558fe | ||
|
|
81b30bf390 | ||
|
|
526eba90e7 | ||
|
|
451a084881 | ||
|
|
a5f60338a2 | ||
|
|
a18896bd63 | ||
|
|
2b457584e0 | ||
|
|
57ee9b4f9c | ||
|
|
45b01d592a | ||
|
|
ef16ee37d9 | ||
|
|
851ab0ad43 | ||
|
|
70380d289f | ||
|
|
4b87964093 | ||
|
|
16809026cd | ||
|
|
c427f3a862 | ||
|
|
07475d2bc1 | ||
|
|
4d80be3a04 | ||
|
|
af06adfd65 | ||
|
|
5ff53db239 | ||
|
|
84b643f80b | ||
|
|
d7d6054b8c | ||
|
|
96bc282759 | ||
|
|
a69d8c63b3 | ||
|
|
0eade51306 | ||
|
|
ea7999e064 | ||
|
|
5f3ff3c44a | ||
|
|
0299bec152 | ||
|
|
61529a6158 | ||
|
|
e5370cddf0 | ||
|
|
c30339debc | ||
|
|
819e7f7e4d | ||
|
|
de2fcc2d87 | ||
|
|
d4892f41b3 | ||
|
|
4d7a4c598a | ||
|
|
d4a163dd39 | ||
|
|
c1e613a9ff | ||
|
|
fcebc4491a | ||
|
|
f75515eedb | ||
|
|
4cea713adf | ||
|
|
148955f725 | ||
|
|
49ef29c9b5 | ||
|
|
62db13ba0e | ||
|
|
d9ed281719 | ||
|
|
56f0cfefa8 | ||
|
|
815c7f45c4 | ||
|
|
4817578ce9 | ||
|
|
13fae28635 | ||
|
|
14907d0752 | ||
|
|
aad5cd7858 | ||
|
|
99a2551046 | ||
|
|
8d31dfb154 | ||
|
|
5e1806aed2 | ||
|
|
72e213ebda | ||
|
|
df242e9468 | ||
|
|
3ce463f482 | ||
|
|
d38e951234 | ||
|
|
1be40e2339 | ||
|
|
bc04e75b4b | ||
|
|
5f7a847822 | ||
|
|
5976a37cf4 | ||
|
|
1cfb793528 | ||
|
|
30bc8ffa8d | ||
|
|
8b7c859d14 | ||
|
|
d1ba50f10e | ||
|
|
bb38e4e599 | ||
|
|
f5899d15f6 | ||
|
|
a4d83ce9c5 | ||
|
|
e1a893793a | ||
|
|
4c411e1836 | ||
|
|
04ec80c012 | ||
|
|
bd0ee6236a | ||
|
|
4afd8d96f9 | ||
|
|
2434e76ee0 | ||
|
|
1670a5d8af | ||
|
|
b312293604 | ||
|
|
0974e06900 | ||
|
|
2b607da2b3 | ||
|
|
85ec160fa1 | ||
|
|
d2f71cebca | ||
|
|
c1f496d912 | ||
|
|
c17a11c1b8 | ||
|
|
4980a5f778 | ||
|
|
0287e6a603 | ||
|
|
ac43825f79 | ||
|
|
7975207e0f | ||
|
|
e2288a8d2c | ||
|
|
cb5ea05945 | ||
|
|
d6db3a9d32 | ||
|
|
d68fec594e | ||
|
|
6f4d82bd61 | ||
|
|
437e6088e6 | ||
|
|
7cba80484b | ||
|
|
4cbfccc166 | ||
|
|
9dca409210 | ||
|
|
ffb9a051c3 | ||
|
|
53eaa8b89a | ||
|
|
bae13c5c56 | ||
|
|
6105d3ca83 | ||
|
|
668b210055 | ||
|
|
5bd113e625 | ||
|
|
dc3903012d | ||
|
|
3a67537227 | ||
|
|
afe798fc19 | ||
|
|
53b8e1d01e | ||
|
|
3f67a002a2 | ||
|
|
43a6eda400 | ||
|
|
ce902c19ec | ||
|
|
f160ba41b5 | ||
|
|
627a1456a7 | ||
|
|
d1f337aef1 | ||
|
|
fa22cb0be5 | ||
|
|
7bc3452139 | ||
|
|
6113483363 | ||
|
|
8ba5682e3b | ||
|
|
c7e51e51e5 | ||
|
|
9d42f48f49 | ||
|
|
ec0e0b1b96 | ||
|
|
2123a65288 | ||
|
|
fc6ff07fd7 | ||
|
|
83b1a30f29 | ||
|
|
274f1aa2e7 | ||
|
|
a1ce37c2af | ||
|
|
6e35fe741e | ||
|
|
b713ded414 | ||
|
|
88aceca983 | ||
|
|
3029fbeafd | ||
|
|
0b4c153d4d | ||
|
|
7dd260f82f | ||
|
|
30975ea102 | ||
|
|
1afac185ff | ||
|
|
aa6e83126c | ||
|
|
1b59ab3d7b | ||
|
|
311fc05417 | ||
|
|
b52966a12c | ||
|
|
31686b91d3 | ||
|
|
1a23a98847 | ||
|
|
2b8b04c129 | ||
|
|
d8e71da071 | ||
|
|
7f47a030e7 | ||
|
|
88f7124c5f | ||
|
|
85894476d8 | ||
|
|
a5763bcb6c | ||
|
|
3981642dd1 | ||
|
|
4889c4847c | ||
|
|
0e798c0e92 | ||
|
|
032098b10c | ||
|
|
81bee58ab3 | ||
|
|
328a1710f6 | ||
|
|
12120c2e3e | ||
|
|
cf1ec56d3a | ||
|
|
044f3f758e | ||
|
|
6d1077fc3c | ||
|
|
399c596af8 | ||
|
|
1e01b5eabc | ||
|
|
ff651e0e28 | ||
|
|
11838fca11 | ||
|
|
ddf332b127 | ||
|
|
eaee6eec2b | ||
|
|
0692cb99fb | ||
|
|
b1cc3e3cc3 | ||
|
|
c31b34c9f8 | ||
|
|
821e606663 | ||
|
|
66520de49b | ||
|
|
e80a3847ca | ||
|
|
11c0aa3f24 | ||
|
|
9abb12fd71 | ||
|
|
db785e6969 | ||
|
|
0604721c72 | ||
|
|
3bcf22fbb0 | ||
|
|
830da38e5a | ||
|
|
8d0e12b1d1 | ||
|
|
3ca684b26e | ||
|
|
50bdd1bf84 | ||
|
|
a24cafd051 | ||
|
|
beeb2f5136 | ||
|
|
3fe4c3eafb | ||
|
|
339912a576 | ||
|
|
7ea0bf41be | ||
|
|
44f200d67d | ||
|
|
18d8c5669f | ||
|
|
57b7f35739 | ||
|
|
ed4537f4ae | ||
|
|
885412a99e | ||
|
|
128da81d83 | ||
|
|
0235bc4cee | ||
|
|
09a9cce95e | ||
|
|
ff818869dd | ||
|
|
c70f133b72 | ||
|
|
328cf39be1 | ||
|
|
64ad7657d1 | ||
|
|
cb6a53698c | ||
|
|
a91637067a | ||
|
|
88f3537b47 | ||
|
|
f557aadb67 | ||
|
|
ff6514572e | ||
|
|
11ec1e647c | ||
|
|
226816bb04 | ||
|
|
6e32c4531e | ||
|
|
9c994f882a | ||
|
|
b76d0e8b50 | ||
|
|
e151483f70 | ||
|
|
b904d666b0 | ||
|
|
0b99f96fc4 | ||
|
|
6d395c51db | ||
|
|
c4e8e653ac | ||
|
|
49e342314d | ||
|
|
0d9749282a | ||
|
|
83f3b34405 | ||
|
|
7bf0cc1ed5 | ||
|
|
fa09045f55 | ||
|
|
367201ed1e | ||
|
|
068470696f | ||
|
|
e2db476f6e | ||
|
|
07d1208dd5 | ||
|
|
4548de194d | ||
|
|
1b9ce69afa | ||
|
|
51548471fd | ||
|
|
70074ddf9f | ||
|
|
200cba0e20 | ||
|
|
5fd79571b7 | ||
|
|
3e1b0bc124 | ||
|
|
1632dbbf29 | ||
|
|
8d2e590886 | ||
|
|
e880aeed8d | ||
|
|
8bdf59bbee | ||
|
|
14edd6d9de | ||
|
|
01097aa130 | ||
|
|
9ca2c336f2 | ||
|
|
b1a1b2a622 | ||
|
|
556bda74a0 | ||
|
|
8db85eca1f | ||
|
|
b5766028a8 | ||
|
|
35757ec45a | ||
|
|
a51b48a095 | ||
|
|
d1ee623f0e | ||
|
|
a2752fe04f | ||
|
|
cc5dd8c297 | ||
|
|
2ed96cd3ed | ||
|
|
e02efe9ba0 | ||
|
|
654ac49bd6 | ||
|
|
8013e47856 | ||
|
|
7216d934de | ||
|
|
f45bbbfcac | ||
|
|
d87024a752 | ||
|
|
bb94dd18ee | ||
|
|
00e4f28e8f | ||
|
|
89661f6579 | ||
|
|
cd7eecc0b0 | ||
|
|
78ec651cbf | ||
|
|
3e54a586ce | ||
|
|
9d5c5a6410 | ||
|
|
8d072fef15 | ||
|
|
1dd1850747 | ||
|
|
0bead9ebe1 | ||
|
|
05a86d892e | ||
|
|
4d8dacec5e | ||
|
|
cc4422d58b | ||
|
|
239f422203 | ||
|
|
5636d91524 | ||
|
|
11c58870c4 | ||
|
|
b74907fa1b | ||
|
|
dbe67fca7f | ||
|
|
d3fc12b160 | ||
|
|
d73d3203dc | ||
|
|
16e1449f1e | ||
|
|
9dd1698556 | ||
|
|
d46dd4b31f | ||
|
|
0e75e7d079 | ||
|
|
be81fea46a | ||
|
|
cd010052f8 | ||
|
|
725bb8d038 | ||
|
|
ddfac7df0b | ||
|
|
6118359240 | ||
|
|
0a294bf2d7 | ||
|
|
baf10a45a9 | ||
|
|
c655e22e42 | ||
|
|
dd6ee3b39a | ||
|
|
8b651ec7ee | ||
|
|
5857d4851b | ||
|
|
0f63f8d001 | ||
|
|
2571e59438 | ||
|
|
d32e7fceb5 | ||
|
|
86655f4967 | ||
|
|
d9d1cdb0c6 | ||
|
|
ca155a790c |
4
.github/workflows/audit.yaml
vendored
4
.github/workflows/audit.yaml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
name: Audit
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions-rs/audit-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
10
.github/workflows/build.yaml
vendored
10
.github/workflows/build.yaml
vendored
@@ -13,13 +13,13 @@ jobs:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
- "1.64"
|
||||
- "1.70"
|
||||
target:
|
||||
- x86_64-unknown-linux-gnu
|
||||
- x86_64-unknown-linux-musl
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -51,6 +51,12 @@ jobs:
|
||||
- name: Build (mshv)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
|
||||
- name: Build (sev_snp)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "sev_snp" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
|
||||
- name: Build (igvm)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "igvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
|
||||
- name: Build (mshv + kvm)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
|
||||
|
||||
2
.github/workflows/dco.yaml
vendored
2
.github/workflows/dco.yaml
vendored
@@ -5,7 +5,7 @@ jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python 3.x
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
|
||||
2
.github/workflows/docker-image.yaml
vendored
2
.github/workflows/docker-image.yaml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
2
.github/workflows/fuzz-build.yaml
vendored
2
.github/workflows/fuzz-build.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
- x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Rust toolchain (${{ matrix.rust }})
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
|
||||
26
.github/workflows/gitlint.yaml
vendored
Normal file
26
.github/workflows/gitlint.yaml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Commit messages check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
gitlint:
|
||||
name: Check commit messages
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install --upgrade gitlint
|
||||
- name: Lint git commit messages
|
||||
run: |
|
||||
gitlint --commits origin/$GITHUB_BASE_REF..
|
||||
2
.github/workflows/hadolint.yaml
vendored
2
.github/workflows/hadolint.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Lint Dockerfile
|
||||
uses: hadolint/hadolint-action@master
|
||||
|
||||
16
.github/workflows/openapi.yaml
vendored
Normal file
16
.github/workflows/openapi.yaml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: Cloud Hypervisor OpenAPI Validation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
Validate:
|
||||
runs-on: ubuntu-latest
|
||||
container: openapitools/openapi-generator-cli
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Validate OpenAPI
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
/usr/local/bin/docker-entrypoint.sh validate -i vmm/src/api/openapi/cloud-hypervisor.yaml
|
||||
27
.github/workflows/quality.yaml
vendored
27
.github/workflows/quality.yaml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
experimental: true
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -101,6 +101,22 @@ jobs:
|
||||
command: clippy
|
||||
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 (sev_snp)
|
||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "sev_snp" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
|
||||
- name: Clippy (igvm)
|
||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "igvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
|
||||
- name: Clippy (kvm + tdx)
|
||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||
uses: actions-rs/cargo@v1
|
||||
@@ -111,3 +127,12 @@ jobs:
|
||||
|
||||
- name: Check build did not modify any files
|
||||
run: test -z "$(git status --porcelain)"
|
||||
|
||||
typos:
|
||||
if: github.event_name == 'pull_request'
|
||||
name: Typos / Spellcheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# Executes "typos ."
|
||||
- uses: crate-ci/typos@v1.16.11
|
||||
|
||||
12
.github/workflows/release.yaml
vendored
12
.github/workflows/release.yaml
vendored
@@ -8,7 +8,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
- name: Install musl-gcc
|
||||
run: sudo apt install -y musl-tools
|
||||
- name: Create release directory
|
||||
@@ -16,29 +16,29 @@ jobs:
|
||||
- name: Install Rust toolchain (x86_64-unknown-linux-gnu)
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "1.67.1"
|
||||
toolchain: "1.70"
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- name: Install Rust toolchain (x86_64-unknown-linux-musl)
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "1.67.1"
|
||||
toolchain: "1.70"
|
||||
target: x86_64-unknown-linux-musl
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
toolchain: "1.67.1"
|
||||
toolchain: "1.70"
|
||||
command: build
|
||||
args: --all --release --features mshv --target=x86_64-unknown-linux-gnu
|
||||
- name: Static Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
toolchain: "1.67.1"
|
||||
toolchain: "1.70"
|
||||
command: build
|
||||
args: --all --release --features mshv --target=x86_64-unknown-linux-musl
|
||||
- name: Install Rust toolchain (aarch64-unknown-linux-musl)
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "1.67.1"
|
||||
toolchain: "1.70"
|
||||
target: aarch64-unknown-linux-musl
|
||||
override: true
|
||||
- name: Create Release
|
||||
|
||||
15
.gitlint
Normal file
15
.gitlint
Normal file
@@ -0,0 +1,15 @@
|
||||
[general]
|
||||
extra-path=scripts/gitlint/rules.py
|
||||
regex-style-search=true
|
||||
|
||||
[ignore-by-author-name]
|
||||
regex=dependabot
|
||||
ignore=all
|
||||
|
||||
# default 72
|
||||
[title-max-length]
|
||||
line-length=72
|
||||
|
||||
# default 80
|
||||
[body-max-line-length]
|
||||
line-length=72
|
||||
21
.typos.toml
Normal file
21
.typos.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
# Configuration for https://github.com/crate-ci/typos
|
||||
|
||||
[files]
|
||||
extend-exclude = [
|
||||
"hypervisor/src/kvm/x86_64/mod.rs",
|
||||
"resources/linux-config-*",
|
||||
]
|
||||
|
||||
[default.extend-words]
|
||||
ba = "ba"
|
||||
CLASSE = "CLASSE"
|
||||
conectix = "conectix"
|
||||
Dake = "Dake"
|
||||
EXTINT = "EXTINT"
|
||||
INOUT = "INOUT"
|
||||
liness = "liness"
|
||||
outout = "outout"
|
||||
|
||||
[default.extend-identifiers]
|
||||
fo = "fo"
|
||||
fpr = "fpr"
|
||||
@@ -5,7 +5,7 @@ License](https://opensource.org/licenses/Apache-2.0) and the [BSD 3
|
||||
Clause](https://opensource.org/licenses/BSD-3-Clause) license. Individual files
|
||||
contain details of their licensing and changes to that file are under the same
|
||||
license unless the contribution changes the license of the file. When importing
|
||||
code from a third party project (e.g. Firecracker or CrosVM) please respect the
|
||||
code from a third party project (e.g. Firecracker or crosvm) please respect the
|
||||
license of those projects.
|
||||
|
||||
New code should be under the [Apache v2
|
||||
|
||||
800
Cargo.lock
generated
800
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
25
Cargo.toml
25
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cloud-hypervisor"
|
||||
version = "34.0.0"
|
||||
version = "37.0.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
default-run = "cloud-hypervisor"
|
||||
@@ -15,7 +15,7 @@ homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
||||
# 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.64"
|
||||
rust-version = "1.70"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
@@ -29,25 +29,25 @@ strip = false
|
||||
debug = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.71"
|
||||
anyhow = "1.0.75"
|
||||
api_client = { path = "api_client" }
|
||||
argh = "0.1.9"
|
||||
clap = { version = "4.4.7", features = ["string"] }
|
||||
dhat = { version = "0.3.2", optional = true }
|
||||
epoll = "4.3.3"
|
||||
event_monitor = { path = "event_monitor" }
|
||||
hypervisor = { path = "hypervisor" }
|
||||
libc = "0.2.139"
|
||||
log = { version = "0.4.17", features = ["std"] }
|
||||
libc = "0.2.147"
|
||||
log = { version = "0.4.20", features = ["std"] }
|
||||
option_parser = { path = "option_parser" }
|
||||
seccompiler = "0.3.0"
|
||||
serde_json = "1.0.96"
|
||||
seccompiler = "0.4.0"
|
||||
serde_json = "1.0.107"
|
||||
signal-hook = "0.3.17"
|
||||
thiserror = "1.0.40"
|
||||
tpm = { path = "tpm"}
|
||||
tracer = { path = "tracer" }
|
||||
vmm = { path = "vmm" }
|
||||
vmm-sys-util = "0.11.0"
|
||||
vm-memory = "0.11.0"
|
||||
vm-memory = "0.13.1"
|
||||
zbus = { version = "3.11.1", optional = true }
|
||||
|
||||
# List of patched crates
|
||||
@@ -55,24 +55,27 @@ zbus = { version = "3.11.1", optional = true }
|
||||
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" }
|
||||
vhost = { git = "https://github.com/rust-vmm/vhost", branch = "main" }
|
||||
|
||||
[dev-dependencies]
|
||||
dirs = "5.0.0"
|
||||
net_util = { path = "net_util" }
|
||||
once_cell = "1.18.0"
|
||||
serde_json = "1.0.96"
|
||||
serde_json = "1.0.107"
|
||||
test_infra = { path = "test_infra" }
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
# Please adjust `vmm::feature_list()` accordingly when changing the
|
||||
# feature list below
|
||||
[features]
|
||||
default = ["kvm", "io_uring"]
|
||||
dbus_api = ["zbus", "vmm/dbus_api"]
|
||||
dhat-heap = ["dhat"] # For heap profiling
|
||||
guest_debug = ["vmm/guest_debug"]
|
||||
igvm = ["vmm/igvm", "mshv"]
|
||||
io_uring = ["vmm/io_uring"]
|
||||
kvm = ["vmm/kvm"]
|
||||
mshv = ["vmm/mshv"]
|
||||
sev_snp = ["igvm", "vmm/sev_snp", "mshv"]
|
||||
tdx = ["vmm/tdx"]
|
||||
tracing = ["vmm/tracing", "tracer/tracing"]
|
||||
|
||||
|
||||
323
Jenkinsfile
vendored
323
Jenkinsfile
vendored
@@ -22,7 +22,7 @@ pipeline {
|
||||
steps {
|
||||
script {
|
||||
runWorkers = false
|
||||
echo 'No changes requring a build'
|
||||
echo 'No changes requiring a build'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,9 @@ pipeline {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
environment {
|
||||
AUTH_DOWNLOAD_TOKEN = credentials('8a26fd74-d40e-414c-9132-ff3f867806ef')
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
@@ -64,11 +67,6 @@ pipeline {
|
||||
sh 'scripts/prepare_vdpa.sh'
|
||||
}
|
||||
}
|
||||
stage('Run OpenAPI tests') {
|
||||
steps {
|
||||
sh 'scripts/run_openapi_tests.sh'
|
||||
}
|
||||
}
|
||||
stage('Run unit tests') {
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --unit'
|
||||
@@ -125,6 +123,9 @@ pipeline {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
environment {
|
||||
AUTH_DOWNLOAD_TOKEN = credentials('8a26fd74-d40e-414c-9132-ff3f867806ef')
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
@@ -142,7 +143,7 @@ pipeline {
|
||||
}
|
||||
steps {
|
||||
sh 'sudo modprobe openvswitch'
|
||||
sh 'scripts/dev_cli.sh tests --integration'
|
||||
sh 'scripts/dev_cli.sh tests --integration -- -- --skip common_parallel::test_vfio'
|
||||
}
|
||||
}
|
||||
stage('Run live-migration integration tests') {
|
||||
@@ -151,7 +152,7 @@ pipeline {
|
||||
}
|
||||
steps {
|
||||
sh 'sudo modprobe openvswitch'
|
||||
sh 'scripts/dev_cli.sh tests --integration-live-migration --integration -- -- --skip live_migration::live_migration_parallel::test_live_upgrade_watchdog --skip live_migration::live_migration_parallel::test_live_upgrade_watchdog_local'
|
||||
sh 'scripts/dev_cli.sh tests --integration-live-migration'
|
||||
}
|
||||
}
|
||||
stage('Run integration tests for musl') {
|
||||
@@ -160,7 +161,7 @@ pipeline {
|
||||
}
|
||||
steps {
|
||||
sh 'sudo modprobe openvswitch'
|
||||
sh 'scripts/dev_cli.sh tests --integration --libc musl'
|
||||
sh 'scripts/dev_cli.sh tests --integration --libc musl -- -- --skip common_parallel::test_vfio'
|
||||
}
|
||||
}
|
||||
stage('Run live-migration integration tests for musl') {
|
||||
@@ -169,7 +170,7 @@ pipeline {
|
||||
}
|
||||
steps {
|
||||
sh 'sudo modprobe openvswitch'
|
||||
sh 'scripts/dev_cli.sh tests --integration-live-migration --libc musl -- -- --skip live_migration::live_migration_parallel::test_live_upgrade_watchdog --skip live_migration::live_migration_parallel::test_live_upgrade_watchdog_local'
|
||||
sh 'scripts/dev_cli.sh tests --integration-live-migration --libc musl'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,7 +208,7 @@ pipeline {
|
||||
}
|
||||
stage('Install azure-cli') {
|
||||
steps {
|
||||
installAzureCli('bionic', 'arm64')
|
||||
installAzureCli('focal', 'arm64')
|
||||
}
|
||||
}
|
||||
stage('Download Windows image') {
|
||||
@@ -295,146 +296,152 @@ pipeline {
|
||||
}
|
||||
}
|
||||
}
|
||||
// stage('Worker build - Metrics') {
|
||||
// agent { node { label 'jammy-metrics' } }
|
||||
// when {
|
||||
// branch 'main'
|
||||
// beforeAgent true
|
||||
// expression {
|
||||
// return runWorkers
|
||||
// }
|
||||
// }
|
||||
// environment {
|
||||
// METRICS_PUBLISH_KEY = credentials('52e0945f-ce7a-43d1-87af-67d1d87cc40f')
|
||||
// }
|
||||
// stages {
|
||||
// stage('Checkout') {
|
||||
// steps {
|
||||
// checkout scm
|
||||
// }
|
||||
// }
|
||||
// stage('Run metrics tests') {
|
||||
// options {
|
||||
// timeout(time: 1, unit: 'HOURS')
|
||||
// }
|
||||
// steps {
|
||||
// sh 'scripts/dev_cli.sh tests --metrics -- -- --report-file /root/workloads/metrics.json'
|
||||
// }
|
||||
// }
|
||||
// stage('Upload metrics report') {
|
||||
// steps {
|
||||
// sh 'curl -X PUT https://cloud-hypervisor-metrics.azurewebsites.net/api/publishmetrics -H "x-functions-key: $METRICS_PUBLISH_KEY" -T ~/workloads/metrics.json'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// stage('Worker build - Rate Limiter') {
|
||||
// agent { node { label 'focal-metrics' } }
|
||||
// when {
|
||||
// branch 'main'
|
||||
// beforeAgent true
|
||||
// expression {
|
||||
// return runWorkers
|
||||
// }
|
||||
// }
|
||||
// stages {
|
||||
// stage('Checkout') {
|
||||
// steps {
|
||||
// checkout scm
|
||||
// }
|
||||
// }
|
||||
// stage('Run rate-limiter integration tests') {
|
||||
// options {
|
||||
// timeout(time: 10, unit: 'MINUTES')
|
||||
// }
|
||||
// steps {
|
||||
// sh 'scripts/dev_cli.sh tests --integration-rate-limiter'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// stage('Worker build - SGX') {
|
||||
// agent { node { label 'jammy-sgx' } }
|
||||
// when {
|
||||
// beforeAgent true
|
||||
// allOf {
|
||||
// branch 'main'
|
||||
// expression {
|
||||
// return runWorkers
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// stages {
|
||||
// stage('Checkout') {
|
||||
// steps {
|
||||
// checkout scm
|
||||
// }
|
||||
// }
|
||||
// stage('Run SGX integration tests') {
|
||||
// options {
|
||||
// timeout(time: 1, unit: 'HOURS')
|
||||
// }
|
||||
// steps {
|
||||
// sh 'scripts/dev_cli.sh tests --integration-sgx'
|
||||
// }
|
||||
// }
|
||||
// stage('Run SGX integration tests for musl') {
|
||||
// options {
|
||||
// timeout(time: 1, unit: 'HOURS')
|
||||
// }
|
||||
// steps {
|
||||
// sh 'scripts/dev_cli.sh tests --integration-sgx --libc musl'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// post {
|
||||
// always {
|
||||
// sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||
// deleteDir()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// stage('Worker build - VFIO') {
|
||||
// agent { node { label 'jammy-vfio' } }
|
||||
// when {
|
||||
// beforeAgent true
|
||||
// allOf {
|
||||
// branch 'main'
|
||||
// expression {
|
||||
// return runWorkers
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// stages {
|
||||
// stage('Checkout') {
|
||||
// steps {
|
||||
// checkout scm
|
||||
// }
|
||||
// }
|
||||
// stage('Run VFIO integration tests') {
|
||||
// options {
|
||||
// timeout(time: 1, unit: 'HOURS')
|
||||
// }
|
||||
// steps {
|
||||
// sh 'scripts/dev_cli.sh tests --integration-vfio'
|
||||
// }
|
||||
// }
|
||||
// stage('Run VFIO integration tests for musl') {
|
||||
// options {
|
||||
// timeout(time: 1, unit: 'HOURS')
|
||||
// }
|
||||
// steps {
|
||||
// sh 'scripts/dev_cli.sh tests --integration-vfio --libc musl'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// post {
|
||||
// always {
|
||||
// sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||
// deleteDir()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
stage('Worker build - Metrics') {
|
||||
agent { node { label 'jammy-metrics' } }
|
||||
when {
|
||||
branch 'main'
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
environment {
|
||||
METRICS_PUBLISH_KEY = credentials('52e0945f-ce7a-43d1-87af-67d1d87cc40f')
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage('Run metrics tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --metrics -- -- --report-file /root/workloads/metrics.json'
|
||||
}
|
||||
}
|
||||
stage('Upload metrics report') {
|
||||
steps {
|
||||
sh 'curl -X PUT https://cloud-hypervisor-metrics.azurewebsites.net/api/publishmetrics -H "x-functions-key: $METRICS_PUBLISH_KEY" -T ~/workloads/metrics.json'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Worker build - Rate Limiter') {
|
||||
agent { node { label 'focal-metrics' } }
|
||||
when {
|
||||
branch 'main'
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage('Run rate-limiter integration tests') {
|
||||
options {
|
||||
timeout(time: 10, unit: 'MINUTES')
|
||||
}
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --integration-rate-limiter'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Worker build - SGX') {
|
||||
agent { node { label 'jammy-sgx' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
allOf {
|
||||
branch 'main'
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
}
|
||||
environment {
|
||||
AUTH_DOWNLOAD_TOKEN = credentials('8a26fd74-d40e-414c-9132-ff3f867806ef')
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage('Run SGX integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --integration-sgx'
|
||||
}
|
||||
}
|
||||
stage('Run SGX integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --integration-sgx --libc musl'
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||
deleteDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Worker build - VFIO') {
|
||||
agent { node { label 'jammy-vfio' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
allOf {
|
||||
branch 'main'
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
}
|
||||
environment {
|
||||
AUTH_DOWNLOAD_TOKEN = credentials('8a26fd74-d40e-414c-9132-ff3f867806ef')
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage('Run VFIO integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --integration-vfio'
|
||||
}
|
||||
}
|
||||
stage('Run VFIO integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh 'scripts/dev_cli.sh tests --integration-vfio --libc musl'
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||
deleteDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -505,5 +512,19 @@ def boolean skipWorkerBuild() {
|
||||
return true
|
||||
}
|
||||
|
||||
if (sh(
|
||||
returnStatus: true,
|
||||
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v '^\\.'"
|
||||
) != 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (sh(
|
||||
returnStatus: true,
|
||||
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v 'gitlint'"
|
||||
) != 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -150,7 +150,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 --disk path=/tmp/ubuntu-cloudinit.img \
|
||||
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
--net "tap=,mac=,ip=,mask="
|
||||
@@ -163,7 +163,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 --disk path=/tmp/ubuntu-cloudinit.img \
|
||||
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
--net "tap=,mac=,ip=,mask=" \
|
||||
@@ -225,7 +225,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 --disk path=/tmp/ubuntu-cloudinit.img \
|
||||
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||
--cmdline "console=hvc0 root=/dev/vda1 rw" \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
@@ -239,7 +239,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 --disk path=/tmp/ubuntu-cloudinit.img \
|
||||
--disk path=focal-server-cloudimg-arm64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||
--cmdline "console=hvc0 root=/dev/vda1 rw" \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
|
||||
@@ -6,21 +6,22 @@ edition = "2021"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
sev_snp = []
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.71"
|
||||
anyhow = "1.0.75"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.139"
|
||||
linux-loader = { version = "0.9.0", features = ["elf", "bzimage", "pe"] }
|
||||
log = "0.4.17"
|
||||
serde = { version = "1.0.164", features = ["rc", "derive"] }
|
||||
libc = "0.2.147"
|
||||
linux-loader = { version = "0.10.0", features = ["elf", "bzimage", "pe"] }
|
||||
log = "0.4.20"
|
||||
serde = { version = "1.0.168", features = ["rc", "derive"] }
|
||||
thiserror = "1.0.40"
|
||||
uuid = "1.3.4"
|
||||
versionize = "0.1.10"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-bitmap"] }
|
||||
vm-memory = { version = "0.13.1", features = ["backend-mmap", "backend-bitmap"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ pub enum CacheLevel {
|
||||
L3 = 3,
|
||||
}
|
||||
|
||||
/// NOTE: CACHE SIZE file directory example,
|
||||
/// NOTE: cache size file directory example,
|
||||
/// "/sys/devices/system/cpu/cpu0/cache/index0/size".
|
||||
pub fn get_cache_size(cache_level: CacheLevel) -> u32 {
|
||||
let mut file_directory: String = "/sys/devices/system/cpu/cpu0/cache".to_string();
|
||||
@@ -111,7 +111,7 @@ pub fn get_cache_size(cache_level: CacheLevel) -> u32 {
|
||||
|
||||
let file_path = Path::new(&file_directory);
|
||||
if !file_path.exists() {
|
||||
error!("File: {} not exist.", file_directory);
|
||||
warn!("File: {} does not exist.", file_directory);
|
||||
0
|
||||
} else {
|
||||
info!("File: {} exist.", file_directory);
|
||||
@@ -132,7 +132,7 @@ pub fn get_cache_size(cache_level: CacheLevel) -> u32 {
|
||||
}
|
||||
}
|
||||
|
||||
/// NOTE: CACHE COHERENCY LINE SIZE file directory example,
|
||||
/// NOTE: coherency_line_size file directory example,
|
||||
/// "/sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size".
|
||||
pub fn get_cache_coherency_line_size(cache_level: CacheLevel) -> u32 {
|
||||
let mut file_directory: String = "/sys/devices/system/cpu/cpu0/cache".to_string();
|
||||
@@ -145,7 +145,7 @@ pub fn get_cache_coherency_line_size(cache_level: CacheLevel) -> u32 {
|
||||
|
||||
let file_path = Path::new(&file_directory);
|
||||
if !file_path.exists() {
|
||||
error!("File: {} not exist.", file_directory);
|
||||
warn!("File: {} does not exist.", file_directory);
|
||||
0
|
||||
} else {
|
||||
info!("File: {} exist.", file_directory);
|
||||
@@ -155,7 +155,7 @@ pub fn get_cache_coherency_line_size(cache_level: CacheLevel) -> u32 {
|
||||
}
|
||||
}
|
||||
|
||||
/// NOTE: CACHE NUMBER OF SETS file directory example,
|
||||
/// NOTE: number_of_sets file directory example,
|
||||
/// "/sys/devices/system/cpu/cpu0/cache/index0/number_of_sets".
|
||||
pub fn get_cache_number_of_sets(cache_level: CacheLevel) -> u32 {
|
||||
let mut file_directory: String = "/sys/devices/system/cpu/cpu0/cache".to_string();
|
||||
@@ -168,7 +168,7 @@ pub fn get_cache_number_of_sets(cache_level: CacheLevel) -> u32 {
|
||||
|
||||
let file_path = Path::new(&file_directory);
|
||||
if !file_path.exists() {
|
||||
error!("File: {} not exist.", file_directory);
|
||||
warn!("File: {} does not exist.", file_directory);
|
||||
0
|
||||
} else {
|
||||
info!("File: {} exist.", file_directory);
|
||||
@@ -178,6 +178,41 @@ pub fn get_cache_number_of_sets(cache_level: CacheLevel) -> u32 {
|
||||
}
|
||||
}
|
||||
|
||||
/// NOTE: shared_cpu_list file directory example,
|
||||
/// "/sys/devices/system/cpu/cpu0/cache/index0/shared_cpu_list".
|
||||
pub fn get_cache_shared(cache_level: CacheLevel) -> bool {
|
||||
let mut file_directory: String = "/sys/devices/system/cpu/cpu0/cache".to_string();
|
||||
let mut result = true;
|
||||
|
||||
match cache_level {
|
||||
CacheLevel::L1D | CacheLevel::L1I => result = false,
|
||||
CacheLevel::L2 => file_directory += "/index2/shared_cpu_list",
|
||||
CacheLevel::L3 => file_directory += "/index3/shared_cpu_list",
|
||||
}
|
||||
|
||||
if !result {
|
||||
return false;
|
||||
}
|
||||
|
||||
let file_path = Path::new(&file_directory);
|
||||
if !file_path.exists() {
|
||||
warn!("File: {} does not exist.", file_directory);
|
||||
result = false;
|
||||
} else {
|
||||
info!("File: {} exist.", file_directory);
|
||||
|
||||
let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted.");
|
||||
let src = src.trim();
|
||||
if src.is_empty() {
|
||||
result = false;
|
||||
} else {
|
||||
result = src.contains('-') || src.contains(',');
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Creates the flattened device tree for this aarch64 VM.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
|
||||
@@ -255,6 +290,8 @@ fn create_cpu_nodes(
|
||||
fdt.property_u32("#size-cells", 0x0)?;
|
||||
|
||||
let num_cpus = vcpu_mpidr.len();
|
||||
let (threads_per_core, cores_per_package, packages) = vcpu_topology.unwrap_or((1, 1, 1));
|
||||
let max_cpus: u32 = (threads_per_core * cores_per_package * packages).into();
|
||||
|
||||
// Add cache info.
|
||||
// L1 Data Cache Info.
|
||||
@@ -277,10 +314,14 @@ fn create_cpu_nodes(
|
||||
let mut l3_cache_line_size: u32 = 0;
|
||||
let mut l3_cache_sets: u32 = 0;
|
||||
|
||||
// Cache Shared Info.
|
||||
let mut l2_cache_shared: bool = false;
|
||||
let mut l3_cache_shared: bool = false;
|
||||
|
||||
let cache_path = Path::new("/sys/devices/system/cpu/cpu0/cache");
|
||||
let cache_exist: bool = cache_path.exists();
|
||||
if !cache_exist {
|
||||
error!("cache sysfs system does not exist.");
|
||||
warn!("cache sysfs system does not exist.");
|
||||
} else {
|
||||
info!("cache sysfs system exists.");
|
||||
// L1 Data Cache Info.
|
||||
@@ -302,6 +343,14 @@ fn create_cpu_nodes(
|
||||
l3_cache_size = get_cache_size(CacheLevel::L3);
|
||||
l3_cache_line_size = get_cache_coherency_line_size(CacheLevel::L3);
|
||||
l3_cache_sets = get_cache_number_of_sets(CacheLevel::L3);
|
||||
|
||||
// Cache Shared Info.
|
||||
if l2_cache_size != 0 {
|
||||
l2_cache_shared = get_cache_shared(CacheLevel::L2);
|
||||
}
|
||||
if l3_cache_size != 0 {
|
||||
l3_cache_shared = get_cache_shared(CacheLevel::L3);
|
||||
}
|
||||
}
|
||||
|
||||
for (cpu_id, mpidr) in vcpu_mpidr.iter().enumerate().take(num_cpus) {
|
||||
@@ -318,21 +367,6 @@ fn create_cpu_nodes(
|
||||
fdt.property_u32("reg", (mpidr & 0x7FFFFF) as u32)?;
|
||||
fdt.property_u32("phandle", cpu_id as u32 + FIRST_VCPU_PHANDLE)?;
|
||||
|
||||
if cache_exist && l1_d_cache_size != 0 && l1_i_cache_size != 0 {
|
||||
// Add cache info.
|
||||
fdt.property_u32("d-cache-size", l1_d_cache_size)?;
|
||||
fdt.property_u32("d-cache-line-size", l1_d_cache_line_size)?;
|
||||
fdt.property_u32("d-cache-sets", l1_d_cache_sets)?;
|
||||
|
||||
fdt.property_u32("i-cache-size", l1_i_cache_size)?;
|
||||
fdt.property_u32("i-cache-line-size", l1_i_cache_line_size)?;
|
||||
fdt.property_u32("i-cache-sets", l1_i_cache_sets)?;
|
||||
|
||||
if l2_cache_size != 0 {
|
||||
fdt.property_u32("next-level-cache", L2_CACHE_PHANDLE)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Add `numa-node-id` property if there is any numa config.
|
||||
if numa_nodes.len() > 1 {
|
||||
for numa_node_idx in 0..numa_nodes.len() {
|
||||
@@ -343,37 +377,89 @@ fn create_cpu_nodes(
|
||||
}
|
||||
}
|
||||
|
||||
if cache_exist && l1_d_cache_size != 0 && l1_i_cache_size != 0 {
|
||||
// Add cache info.
|
||||
fdt.property_u32("d-cache-size", l1_d_cache_size)?;
|
||||
fdt.property_u32("d-cache-line-size", l1_d_cache_line_size)?;
|
||||
fdt.property_u32("d-cache-sets", l1_d_cache_sets)?;
|
||||
|
||||
fdt.property_u32("i-cache-size", l1_i_cache_size)?;
|
||||
fdt.property_u32("i-cache-line-size", l1_i_cache_line_size)?;
|
||||
fdt.property_u32("i-cache-sets", l1_i_cache_sets)?;
|
||||
|
||||
if l2_cache_size != 0 && !l2_cache_shared {
|
||||
fdt.property_u32(
|
||||
"next-level-cache",
|
||||
cpu_id as u32 + max_cpus + FIRST_VCPU_PHANDLE + L2_CACHE_PHANDLE,
|
||||
)?;
|
||||
|
||||
let l2_cache_name = "l2-cache0";
|
||||
let l2_cache_node = fdt.begin_node(l2_cache_name)?;
|
||||
// PHANDLE is used to mark device node, and PHANDLE is unique. To avoid phandle
|
||||
// conflicts with other device nodes, consider the previous CPU PHANDLE, so the
|
||||
// CPU L2 cache PHANDLE must start from the largest CPU PHANDLE plus 1.
|
||||
fdt.property_u32(
|
||||
"phandle",
|
||||
cpu_id as u32 + max_cpus + FIRST_VCPU_PHANDLE + L2_CACHE_PHANDLE,
|
||||
)?;
|
||||
|
||||
fdt.property_string("compatible", "cache")?;
|
||||
fdt.property_u32("cache-size", l2_cache_size)?;
|
||||
fdt.property_u32("cache-line-size", l2_cache_line_size)?;
|
||||
fdt.property_u32("cache-sets", l2_cache_sets)?;
|
||||
fdt.property_u32("cache-level", 2)?;
|
||||
|
||||
if l3_cache_size != 0 && l3_cache_shared {
|
||||
let package_id: u32 = cpu_id as u32 / cores_per_package as u32;
|
||||
fdt.property_u32(
|
||||
"next-level-cache",
|
||||
package_id
|
||||
+ num_cpus as u32
|
||||
+ max_cpus
|
||||
+ FIRST_VCPU_PHANDLE
|
||||
+ L2_CACHE_PHANDLE
|
||||
+ L3_CACHE_PHANDLE,
|
||||
)?;
|
||||
}
|
||||
|
||||
fdt.end_node(l2_cache_node)?;
|
||||
}
|
||||
if l2_cache_size != 0 && l2_cache_shared {
|
||||
warn!("L2 cache shared with other cpus");
|
||||
}
|
||||
}
|
||||
|
||||
fdt.end_node(cpu_node)?;
|
||||
}
|
||||
|
||||
if cache_exist && l2_cache_size != 0 {
|
||||
let l2_cache_name = "l2-cache0";
|
||||
let l2_cache_node = fdt.begin_node(l2_cache_name)?;
|
||||
fdt.property_u32("phandle", L2_CACHE_PHANDLE)?;
|
||||
fdt.property_string("compatible", "cache")?;
|
||||
fdt.property_u32("cache-size", l2_cache_size)?;
|
||||
fdt.property_u32("cache-line-size", l2_cache_line_size)?;
|
||||
fdt.property_u32("cache-sets", l2_cache_sets)?;
|
||||
fdt.property_u32("cache-level", 2)?;
|
||||
if cache_exist && l3_cache_size != 0 && !l2_cache_shared && l3_cache_shared {
|
||||
let mut i: u32 = 0;
|
||||
while i < packages.into() {
|
||||
let l3_cache_name = "l3-cache0";
|
||||
let l3_cache_node = fdt.begin_node(l3_cache_name)?;
|
||||
// ARM L3 cache is generally shared within the package (socket), so the
|
||||
// L3 cache node pointed to by the CPU in the package has the same L3
|
||||
// cache PHANDLE. The L3 cache phandle must start from the largest L2
|
||||
// cache PHANDLE plus 1 to avoid duplication.
|
||||
fdt.property_u32(
|
||||
"phandle",
|
||||
i + num_cpus as u32
|
||||
+ max_cpus
|
||||
+ FIRST_VCPU_PHANDLE
|
||||
+ L2_CACHE_PHANDLE
|
||||
+ L3_CACHE_PHANDLE,
|
||||
)?;
|
||||
|
||||
if l3_cache_size != 0 {
|
||||
fdt.property_u32("next-level-cache", L3_CACHE_PHANDLE)?;
|
||||
fdt.property_string("compatible", "cache")?;
|
||||
fdt.property_null("cache-unified")?;
|
||||
fdt.property_u32("cache-size", l3_cache_size)?;
|
||||
fdt.property_u32("cache-line-size", l3_cache_line_size)?;
|
||||
fdt.property_u32("cache-sets", l3_cache_sets)?;
|
||||
fdt.property_u32("cache-level", 3)?;
|
||||
fdt.end_node(l3_cache_node)?;
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
fdt.end_node(l2_cache_node)?;
|
||||
}
|
||||
|
||||
if cache_exist && l3_cache_size != 0 {
|
||||
let l3_cache_name = "l3-cache0";
|
||||
let l3_cache_node = fdt.begin_node(l3_cache_name)?;
|
||||
fdt.property_u32("phandle", L3_CACHE_PHANDLE)?;
|
||||
fdt.property_string("compatible", "cache")?;
|
||||
fdt.property_null("cache-unified")?;
|
||||
fdt.property_u32("cache-size", l3_cache_size)?;
|
||||
fdt.property_u32("cache-line-size", l3_cache_line_size)?;
|
||||
fdt.property_u32("cache-sets", l3_cache_sets)?;
|
||||
fdt.property_u32("cache-level", 3)?;
|
||||
fdt.end_node(l3_cache_node)?;
|
||||
}
|
||||
|
||||
if let Some(topology) = vcpu_topology {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Copyright 2020 Arm Limited (or its affiliates). All rights reserved.
|
||||
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
use std::os::fd::AsFd;
|
||||
use std::result;
|
||||
use vm_memory::{Bytes, GuestAddress, GuestMemory};
|
||||
use vm_memory::{GuestAddress, GuestMemory};
|
||||
|
||||
/// Errors thrown while loading UEFI binary
|
||||
#[derive(Debug)]
|
||||
@@ -24,7 +25,7 @@ pub fn load_uefi<F, M: GuestMemory>(
|
||||
uefi_image: &mut F,
|
||||
) -> Result<()>
|
||||
where
|
||||
F: Read + Seek,
|
||||
F: Read + Seek + AsFd,
|
||||
{
|
||||
let uefi_size = uefi_image
|
||||
.seek(SeekFrom::End(0))
|
||||
@@ -36,6 +37,6 @@ where
|
||||
}
|
||||
uefi_image.rewind().map_err(|_| Error::SeekUefiStart)?;
|
||||
guest_mem
|
||||
.read_exact_from(guest_addr, uefi_image, uefi_size)
|
||||
.read_exact_volatile_from(guest_addr, &mut uefi_image.as_fd(), uefi_size)
|
||||
.map_err(|_| Error::ReadUefiImage)
|
||||
}
|
||||
|
||||
@@ -91,8 +91,9 @@ pub mod x86_64;
|
||||
#[cfg(target_arch = "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, _NSIG,
|
||||
generate_ram_ranges, get_host_cpu_phys_bits, initramfs_load_addr, layout,
|
||||
layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START, regs, CpuidConfig, CpuidFeatureEntry,
|
||||
EntryPoint, _NSIG,
|
||||
};
|
||||
|
||||
/// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
|
||||
@@ -108,6 +109,7 @@ pub struct NumaNode {
|
||||
pub memory_regions: Vec<Arc<GuestRegionMmap>>,
|
||||
pub hotplug_regions: Vec<Arc<GuestRegionMmap>>,
|
||||
pub cpus: Vec<u8>,
|
||||
pub pci_segments: Vec<u16>,
|
||||
pub distances: BTreeMap<u32, u8>,
|
||||
pub memory_zones: Vec<String>,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
|
||||
@@ -37,6 +37,9 @@ 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
|
||||
const AMX_BF16: u8 = 22; // AMX tile computation on bfloat16 numbers
|
||||
const AMX_TILE: u8 = 24; // AMX tile load/store instructions
|
||||
const AMX_INT8: u8 = 25; // AMX tile computation on 8-bit integers
|
||||
|
||||
// KVM feature bits
|
||||
#[cfg(feature = "tdx")]
|
||||
@@ -60,7 +63,7 @@ pub const _NSIG: i32 = 65;
|
||||
/// is to be used to configure the guest initial state.
|
||||
pub struct EntryPoint {
|
||||
/// Address in guest memory where the guest must start execution
|
||||
pub entry_addr: Option<GuestAddress>,
|
||||
pub entry_addr: GuestAddress,
|
||||
}
|
||||
|
||||
const E820_RAM: u32 = 1;
|
||||
@@ -145,6 +148,15 @@ struct BootParamsWrapper(boot_params);
|
||||
// SAFETY: BootParamsWrap is a wrapper over `boot_params` (a series of ints).
|
||||
unsafe impl ByteValued for BootParamsWrapper {}
|
||||
|
||||
pub struct CpuidConfig {
|
||||
pub sgx_epc_sections: Option<Vec<SgxEpcSection>>,
|
||||
pub phys_bits: u8,
|
||||
pub kvm_hyperv: bool,
|
||||
#[cfg(feature = "tdx")]
|
||||
pub tdx: bool,
|
||||
pub amx: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Error writing MP table to memory.
|
||||
@@ -552,11 +564,7 @@ impl CpuidFeatureEntry {
|
||||
|
||||
pub fn generate_common_cpuid(
|
||||
hypervisor: &Arc<dyn hypervisor::Hypervisor>,
|
||||
topology: Option<(u8, u8, u8)>,
|
||||
sgx_epc_sections: Option<Vec<SgxEpcSection>>,
|
||||
phys_bits: u8,
|
||||
kvm_hyperv: bool,
|
||||
#[cfg(feature = "tdx")] tdx_enabled: bool,
|
||||
config: &CpuidConfig,
|
||||
) -> super::Result<Vec<CpuIdEntry>> {
|
||||
// SAFETY: cpuid called with valid leaves
|
||||
if unsafe { x86_64::__cpuid(1) }.ecx & 1 << HYPERVISOR_ECX_BIT == 1 << HYPERVISOR_ECX_BIT {
|
||||
@@ -574,7 +582,10 @@ pub fn generate_common_cpuid(
|
||||
);
|
||||
}
|
||||
|
||||
info!("Generating guest CPUID for with physical address size: {phys_bits}");
|
||||
info!(
|
||||
"Generating guest CPUID for with physical address size: {}",
|
||||
config.phys_bits
|
||||
);
|
||||
let cpuid_patches = vec![
|
||||
// Patch tsc deadline timer bit
|
||||
CpuidPatch {
|
||||
@@ -615,16 +626,12 @@ pub fn generate_common_cpuid(
|
||||
|
||||
CpuidPatch::patch_cpuid(&mut cpuid, cpuid_patches);
|
||||
|
||||
if let Some(t) = topology {
|
||||
update_cpuid_topology(&mut cpuid, t.0, t.1, t.2);
|
||||
}
|
||||
|
||||
if let Some(sgx_epc_sections) = sgx_epc_sections {
|
||||
if let Some(sgx_epc_sections) = &config.sgx_epc_sections {
|
||||
update_cpuid_sgx(&mut cpuid, sgx_epc_sections)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
let tdx_capabilities = if tdx_enabled {
|
||||
let tdx_capabilities = if config.tdx {
|
||||
let caps = hypervisor
|
||||
.tdx_capabilities()
|
||||
.map_err(Error::TdxCapabilities)?;
|
||||
@@ -637,6 +644,12 @@ pub fn generate_common_cpuid(
|
||||
// Update some existing CPUID
|
||||
for entry in cpuid.as_mut_slice().iter_mut() {
|
||||
match entry.function {
|
||||
// Clear AMX related bits if the AMX feature is not enabled
|
||||
0x7 => {
|
||||
if !config.amx && entry.index == 0 {
|
||||
entry.edx &= !(1 << AMX_BF16 | 1 << AMX_TILE | 1 << AMX_INT8)
|
||||
}
|
||||
}
|
||||
0xd =>
|
||||
{
|
||||
#[cfg(feature = "tdx")]
|
||||
@@ -672,12 +685,12 @@ pub fn generate_common_cpuid(
|
||||
}
|
||||
// Set CPU physical bits
|
||||
0x8000_0008 => {
|
||||
entry.eax = (entry.eax & 0xffff_ff00) | (phys_bits as u32 & 0xff);
|
||||
entry.eax = (entry.eax & 0xffff_ff00) | (config.phys_bits as u32 & 0xff);
|
||||
}
|
||||
0x4000_0001 => {
|
||||
// These features are not supported by TDX
|
||||
#[cfg(feature = "tdx")]
|
||||
if tdx_enabled {
|
||||
if config.tdx {
|
||||
entry.eax &= !(1 << KVM_FEATURE_CLOCKSOURCE_BIT
|
||||
| 1 << KVM_FEATURE_CLOCKSOURCE2_BIT
|
||||
| 1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT
|
||||
@@ -705,7 +718,7 @@ pub fn generate_common_cpuid(
|
||||
});
|
||||
}
|
||||
|
||||
if kvm_hyperv {
|
||||
if config.kvm_hyperv {
|
||||
// Remove conflicting entries
|
||||
cpuid.retain(|c| c.function != 0x4000_0000);
|
||||
cpuid.retain(|c| c.function != 0x4000_0001);
|
||||
@@ -761,11 +774,26 @@ pub fn configure_vcpu(
|
||||
boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>,
|
||||
cpuid: Vec<CpuIdEntry>,
|
||||
kvm_hyperv: bool,
|
||||
cpu_vendor: CpuVendor,
|
||||
topology: Option<(u8, u8, u8)>,
|
||||
) -> super::Result<()> {
|
||||
// Per vCPU CPUID changes; common are handled via generate_common_cpuid()
|
||||
let mut cpuid = cpuid;
|
||||
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));
|
||||
if matches!(cpu_vendor, CpuVendor::AMD) {
|
||||
CpuidPatch::set_cpuid_reg(
|
||||
&mut cpuid,
|
||||
0x8000_001e,
|
||||
Some(0),
|
||||
CpuidReg::EAX,
|
||||
u32::from(id),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(t) = topology {
|
||||
update_cpuid_topology(&mut cpuid, t.0, t.1, t.2, cpu_vendor, id);
|
||||
}
|
||||
|
||||
// Set ApicId in cpuid for each vcpu
|
||||
// SAFETY: get host cpuid when eax=1
|
||||
@@ -811,12 +839,10 @@ pub fn configure_vcpu(
|
||||
|
||||
regs::setup_msrs(vcpu).map_err(Error::MsrsConfiguration)?;
|
||||
if let Some((kernel_entry_point, guest_memory)) = boot_setup {
|
||||
if let Some(entry_addr) = kernel_entry_point.entry_addr {
|
||||
// Safe to unwrap because this method is called after the VM is configured
|
||||
regs::setup_regs(vcpu, entry_addr.raw_value()).map_err(Error::RegsConfiguration)?;
|
||||
regs::setup_fpu(vcpu).map_err(Error::FpuConfiguration)?;
|
||||
regs::setup_sregs(&guest_memory.memory(), vcpu).map_err(Error::SregsConfiguration)?;
|
||||
}
|
||||
regs::setup_regs(vcpu, kernel_entry_point.entry_addr.raw_value())
|
||||
.map_err(Error::RegsConfiguration)?;
|
||||
regs::setup_fpu(vcpu).map_err(Error::FpuConfiguration)?;
|
||||
regs::setup_sregs(&guest_memory.memory(), vcpu).map_err(Error::SregsConfiguration)?;
|
||||
}
|
||||
interrupts::set_lint(vcpu).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
|
||||
Ok(())
|
||||
@@ -900,6 +926,112 @@ pub fn configure_system(
|
||||
)
|
||||
}
|
||||
|
||||
type RamRange = (u64, u64);
|
||||
|
||||
/// Returns usable physical memory ranges for the guest
|
||||
/// These should be used to create e820_RAM memory maps
|
||||
///
|
||||
/// There are up to two usable physical memory ranges,
|
||||
/// divided by the gap at the end of 32bit address space.
|
||||
pub fn generate_ram_ranges(
|
||||
guest_mem: &GuestMemoryMmap,
|
||||
) -> super::Result<(RamRange, Option<RamRange>)> {
|
||||
// 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;
|
||||
|
||||
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 usable physical memory ranges, devidided by the
|
||||
gap at the end of 32bit address space (e.g. between 3G and 4G)."
|
||||
);
|
||||
return Err(super::Error::MemmapTableSetup);
|
||||
}
|
||||
|
||||
// Generate the first usable physical memory range before the gap
|
||||
let first_ram_range = {
|
||||
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!(
|
||||
"first usable physical memory range, start: 0x{:08x}, end: 0x{:08x}",
|
||||
high_ram_start, first_region_end
|
||||
);
|
||||
|
||||
(high_ram_start, *first_region_end)
|
||||
};
|
||||
|
||||
// Generate the second usable physical memory range after the gap if any
|
||||
let second_ram_range = 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!(
|
||||
"Second usable physical memory range, start: 0x{:08x}, end: 0x{:08x}",
|
||||
ram_64bit_start, second_region_end
|
||||
);
|
||||
|
||||
Some((ram_64bit_start, *second_region_end))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok((first_ram_range, second_ram_range))
|
||||
}
|
||||
|
||||
fn configure_pvh(
|
||||
guest_mem: &GuestMemoryMmap,
|
||||
cmdline_addr: GuestAddress,
|
||||
@@ -946,100 +1078,31 @@ fn configure_pvh(
|
||||
// Create the memory map entries.
|
||||
add_memmap_entry(&mut memmap, 0, layout::EBDA_START.raw_value(), E820_RAM);
|
||||
|
||||
// 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;
|
||||
// Get usable physical memory ranges
|
||||
let (first_ram_range, second_ram_range) = generate_ram_ranges(guest_mem)?;
|
||||
|
||||
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);
|
||||
}
|
||||
// Create e820 memory map entry before the gap
|
||||
info!(
|
||||
"create_memmap_entry, start: 0x{:08x}, end: 0x{:08x}",
|
||||
first_ram_range.0, first_ram_range.1
|
||||
);
|
||||
add_memmap_entry(
|
||||
&mut memmap,
|
||||
first_ram_range.0,
|
||||
first_ram_range.1 - first_ram_range.0,
|
||||
E820_RAM,
|
||||
);
|
||||
|
||||
// Create e820 memory map after the gap if any
|
||||
if let Some(second_ram_range) = second_ram_range {
|
||||
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
|
||||
second_ram_range.0, second_ram_range.1
|
||||
);
|
||||
add_memmap_entry(
|
||||
&mut memmap,
|
||||
ram_64bit_start,
|
||||
second_region_end - ram_64bit_start,
|
||||
second_ram_range.0,
|
||||
second_ram_range.1 - second_ram_range.0,
|
||||
E820_RAM,
|
||||
);
|
||||
}
|
||||
@@ -1162,6 +1225,8 @@ fn update_cpuid_topology(
|
||||
threads_per_core: u8,
|
||||
cores_per_die: u8,
|
||||
dies_per_package: u8,
|
||||
cpu_vendor: CpuVendor,
|
||||
id: u8,
|
||||
) {
|
||||
let thread_width = 8 - (threads_per_core - 1).leading_zeros();
|
||||
let core_width = (8 - (cores_per_die - 1).leading_zeros()) + thread_width;
|
||||
@@ -1218,13 +1283,72 @@ fn update_cpuid_topology(
|
||||
u32::from(dies_per_package * cores_per_die * threads_per_core),
|
||||
);
|
||||
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(2), CpuidReg::ECX, 5 << 8);
|
||||
|
||||
if matches!(cpu_vendor, CpuVendor::AMD) {
|
||||
CpuidPatch::set_cpuid_reg(
|
||||
cpuid,
|
||||
0x8000_001e,
|
||||
Some(0),
|
||||
CpuidReg::EBX,
|
||||
((threads_per_core as u32 - 1) << 8) | (id as u32 & 0xff),
|
||||
);
|
||||
CpuidPatch::set_cpuid_reg(
|
||||
cpuid,
|
||||
0x8000_001e,
|
||||
Some(0),
|
||||
CpuidReg::ECX,
|
||||
((dies_per_package as u32 - 1) << 8) | (thread_width + die_width) & 0xff,
|
||||
);
|
||||
CpuidPatch::set_cpuid_reg(cpuid, 0x8000_001e, Some(0), CpuidReg::EDX, 0);
|
||||
if cores_per_die * threads_per_core > 1 {
|
||||
CpuidPatch::set_cpuid_reg(
|
||||
cpuid,
|
||||
0x8000_0001,
|
||||
Some(0),
|
||||
CpuidReg::ECX,
|
||||
(1u32 << 1) | (1u32 << 22),
|
||||
);
|
||||
CpuidPatch::set_cpuid_reg(
|
||||
cpuid,
|
||||
0x0000_0001,
|
||||
Some(0),
|
||||
CpuidReg::EBX,
|
||||
((id as u32) << 24)
|
||||
| (8 << 8)
|
||||
| (((cores_per_die * threads_per_core) as u32) << 16),
|
||||
);
|
||||
let cpuid_patches = vec![
|
||||
// Patch tsc deadline timer bit
|
||||
CpuidPatch {
|
||||
function: 1,
|
||||
index: 0,
|
||||
flags_bit: None,
|
||||
eax_bit: None,
|
||||
ebx_bit: None,
|
||||
ecx_bit: None,
|
||||
edx_bit: Some(28),
|
||||
},
|
||||
];
|
||||
CpuidPatch::patch_cpuid(cpuid, cpuid_patches);
|
||||
CpuidPatch::set_cpuid_reg(
|
||||
cpuid,
|
||||
0x8000_0008,
|
||||
Some(0),
|
||||
CpuidReg::ECX,
|
||||
((thread_width + core_width + die_width) << 12)
|
||||
| ((cores_per_die * threads_per_core) - 1) as u32,
|
||||
);
|
||||
} else {
|
||||
CpuidPatch::set_cpuid_reg(cpuid, 0x8000_0008, Some(0), CpuidReg::ECX, 0u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The goal is to update the CPUID sub-leaves to reflect the number of EPC
|
||||
// sections exposed to the guest.
|
||||
fn update_cpuid_sgx(
|
||||
cpuid: &mut Vec<CpuIdEntry>,
|
||||
epc_sections: Vec<SgxEpcSection>,
|
||||
epc_sections: &Vec<SgxEpcSection>,
|
||||
) -> Result<(), Error> {
|
||||
// Something's wrong if there's no EPC section.
|
||||
if epc_sections.is_empty() {
|
||||
|
||||
@@ -9,7 +9,6 @@ use crate::layout::{APIC_START, HIGH_RAM_START, IOAPIC_START};
|
||||
use crate::x86_64::mpspec;
|
||||
use crate::GuestMemoryMmap;
|
||||
use libc::c_char;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::result;
|
||||
use std::slice;
|
||||
@@ -154,7 +153,7 @@ pub fn setup_mptable(offset: GuestAddress, mem: &GuestMemoryMmap, num_cpus: u8)
|
||||
return Err(Error::AddressOverflow);
|
||||
}
|
||||
|
||||
mem.read_exact_from(base_mp, &mut io::repeat(0), mp_size)
|
||||
mem.read_exact_volatile_from(base_mp, &mut vec![0; mp_size].as_slice(), mp_size)
|
||||
.map_err(Error::Clear)?;
|
||||
|
||||
{
|
||||
@@ -291,7 +290,10 @@ pub fn setup_mptable(offset: GuestAddress, mem: &GuestMemoryMmap, num_cpus: u8)
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::layout::MPTABLE_START;
|
||||
use vm_memory::{GuestAddress, GuestUsize};
|
||||
use vm_memory::{
|
||||
bitmap::BitmapSlice, GuestAddress, GuestUsize, VolatileMemoryError, VolatileSlice,
|
||||
WriteVolatile,
|
||||
};
|
||||
|
||||
fn table_entry_size(type_: u8) -> usize {
|
||||
match type_ as u32 {
|
||||
@@ -351,20 +353,24 @@ mod tests {
|
||||
let mpc_table: MpcTableWrapper = mem.read_obj(mpc_offset).unwrap();
|
||||
|
||||
struct Sum(u8);
|
||||
impl io::Write for Sum {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
for v in buf.iter() {
|
||||
impl WriteVolatile for Sum {
|
||||
fn write_volatile<B: BitmapSlice>(
|
||||
&mut self,
|
||||
buf: &VolatileSlice<B>,
|
||||
) -> result::Result<usize, VolatileMemoryError> {
|
||||
let mut tmp = vec![0u8; buf.len()];
|
||||
tmp.write_all_volatile(buf)?;
|
||||
|
||||
for v in tmp.iter() {
|
||||
self.0 = self.0.wrapping_add(*v);
|
||||
}
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let mut sum = Sum(0);
|
||||
mem.write_to(mpc_offset, &mut sum, mpc_table.0.length as usize)
|
||||
mem.write_volatile_to(mpc_offset, &mut sum, mpc_table.0.length as usize)
|
||||
.unwrap();
|
||||
assert_eq!(sum.0, 0);
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ fn tdvf_descriptor_offset(file: &mut File) -> Result<(SeekFrom, bool), TdvfError
|
||||
// We start after the footer GUID and the table length.
|
||||
let mut offset = table_size - 18;
|
||||
|
||||
debug!("Parsing GUIDed structure");
|
||||
debug!("Parsing GUID structure");
|
||||
while offset >= 18 {
|
||||
let entry_uuid = Uuid::from_slice_le(&table[offset - 16..offset])
|
||||
.map_err(TdvfError::UuidCreation)?;
|
||||
|
||||
@@ -10,18 +10,18 @@ io_uring = ["dep:io-uring"]
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.4.3"
|
||||
crc32c = "0.6.3"
|
||||
io-uring = { version = "0.6.0", optional = true }
|
||||
libc = "0.2.139"
|
||||
log = "0.4.17"
|
||||
crc32c = "0.6.4"
|
||||
io-uring = { version = "0.6.2", optional = true }
|
||||
libc = "0.2.147"
|
||||
log = "0.4.20"
|
||||
remain = "0.2.11"
|
||||
smallvec = "1.10.0"
|
||||
smallvec = "1.11.0"
|
||||
thiserror = "1.0.40"
|
||||
uuid = { version = "1.3.4", features = ["v4"] }
|
||||
versionize = "0.1.10"
|
||||
versionize_derive = "0.1.4"
|
||||
virtio-bindings = { version = "0.2.0", features = ["virtio-v5_0_0"] }
|
||||
virtio-queue = "0.8.0"
|
||||
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
virtio-queue = "0.10.0"
|
||||
vm-memory = { version = "0.13.1", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
@@ -2,12 +2,9 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
use libc::{ioctl, S_IFBLK, S_IFMT};
|
||||
use std::fs::File;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use crate::DiskTopology;
|
||||
use thiserror::Error;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum DiskFileError {
|
||||
@@ -19,88 +16,6 @@ pub enum DiskFileError {
|
||||
NewAsyncIo(#[source] std::io::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DiskTopology {
|
||||
pub logical_block_size: u64,
|
||||
pub physical_block_size: u64,
|
||||
pub minimum_io_size: u64,
|
||||
pub optimal_io_size: u64,
|
||||
}
|
||||
|
||||
impl Default for DiskTopology {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
logical_block_size: 512,
|
||||
physical_block_size: 512,
|
||||
minimum_io_size: 512,
|
||||
optimal_io_size: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ioctl_io_nr!(BLKSSZGET, 0x12, 104);
|
||||
ioctl_io_nr!(BLKPBSZGET, 0x12, 123);
|
||||
ioctl_io_nr!(BLKIOMIN, 0x12, 120);
|
||||
ioctl_io_nr!(BLKIOOPT, 0x12, 121);
|
||||
|
||||
enum BlockSize {
|
||||
LogicalBlock,
|
||||
PhysicalBlock,
|
||||
MinimumIo,
|
||||
OptimalIo,
|
||||
}
|
||||
|
||||
impl DiskTopology {
|
||||
fn is_block_device(f: &mut File) -> std::io::Result<bool> {
|
||||
let mut stat = std::mem::MaybeUninit::<libc::stat>::uninit();
|
||||
// SAFETY: FFI call with a valid fd and buffer
|
||||
let ret = unsafe { libc::fstat(f.as_raw_fd(), stat.as_mut_ptr()) };
|
||||
if ret != 0 {
|
||||
return Err(std::io::Error::last_os_error());
|
||||
}
|
||||
|
||||
// SAFETY: stat is valid at this point
|
||||
let is_block = unsafe { (*stat.as_ptr()).st_mode & S_IFMT == S_IFBLK };
|
||||
Ok(is_block)
|
||||
}
|
||||
|
||||
// libc::ioctl() takes different types on different architectures
|
||||
fn query_block_size(f: &mut File, block_size_type: BlockSize) -> std::io::Result<u64> {
|
||||
let mut block_size = 0;
|
||||
// SAFETY: FFI call with correct arguments
|
||||
let ret = unsafe {
|
||||
ioctl(
|
||||
f.as_raw_fd(),
|
||||
match block_size_type {
|
||||
BlockSize::LogicalBlock => BLKSSZGET(),
|
||||
BlockSize::PhysicalBlock => BLKPBSZGET(),
|
||||
BlockSize::MinimumIo => BLKIOMIN(),
|
||||
BlockSize::OptimalIo => BLKIOOPT(),
|
||||
} as _,
|
||||
&mut block_size,
|
||||
)
|
||||
};
|
||||
if ret != 0 {
|
||||
return Err(std::io::Error::last_os_error());
|
||||
};
|
||||
|
||||
Ok(block_size)
|
||||
}
|
||||
|
||||
pub fn probe(f: &mut File) -> std::io::Result<Self> {
|
||||
if !Self::is_block_device(f)? {
|
||||
return Ok(DiskTopology::default());
|
||||
}
|
||||
|
||||
Ok(DiskTopology {
|
||||
logical_block_size: Self::query_block_size(f, BlockSize::LogicalBlock)?,
|
||||
physical_block_size: Self::query_block_size(f, BlockSize::PhysicalBlock)?,
|
||||
minimum_io_size: Self::query_block_size(f, BlockSize::MinimumIo)?,
|
||||
optimal_io_size: Self::query_block_size(f, BlockSize::OptimalIo)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub type DiskFileResult<T> = std::result::Result<T, DiskFileError>;
|
||||
|
||||
pub trait DiskFile: Send {
|
||||
|
||||
177
block/src/lib.rs
177
block/src/lib.rs
@@ -24,6 +24,7 @@ pub mod qcow_sync;
|
||||
///
|
||||
/// Enabled with the `"io_uring"` feature
|
||||
pub mod raw_async;
|
||||
pub mod raw_async_aio;
|
||||
pub mod raw_sync;
|
||||
pub mod vhd;
|
||||
pub mod vhdx;
|
||||
@@ -35,6 +36,7 @@ use crate::qcow::{QcowFile, RawFile};
|
||||
use crate::vhdx::{Vhdx, VhdxError};
|
||||
#[cfg(feature = "io_uring")]
|
||||
use io_uring::{opcode, IoUring, Probe};
|
||||
use libc::{ioctl, S_IFBLK, S_IFMT};
|
||||
use smallvec::SmallVec;
|
||||
use std::alloc::{alloc_zeroed, dealloc, Layout};
|
||||
use std::cmp;
|
||||
@@ -44,6 +46,7 @@ use std::fmt::Debug;
|
||||
use std::fs::File;
|
||||
use std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
|
||||
use std::os::linux::fs::MetadataExt;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::path::Path;
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
@@ -59,7 +62,9 @@ use vm_memory::{
|
||||
GuestMemoryError, GuestMemoryLoadGuard,
|
||||
};
|
||||
use vm_virtio::{AccessPlatform, Translatable};
|
||||
use vmm_sys_util::aio;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr};
|
||||
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
|
||||
@@ -113,8 +118,8 @@ fn build_device_id(disk_path: &Path) -> result::Result<String, Error> {
|
||||
Ok(device_id)
|
||||
}
|
||||
|
||||
pub fn build_disk_image_id(disk_path: &Path) -> Vec<u8> {
|
||||
let mut default_disk_image_id = vec![0; VIRTIO_BLK_ID_BYTES as usize];
|
||||
pub fn build_serial(disk_path: &Path) -> Vec<u8> {
|
||||
let mut default_serial = vec![0; VIRTIO_BLK_ID_BYTES as usize];
|
||||
match build_device_id(disk_path) {
|
||||
Err(_) => {
|
||||
warn!("Could not generate device id. We'll use a default.");
|
||||
@@ -124,24 +129,28 @@ pub fn build_disk_image_id(disk_path: &Path) -> Vec<u8> {
|
||||
// This will also zero out any leftover bytes.
|
||||
let disk_id = m.as_bytes();
|
||||
let bytes_to_copy = cmp::min(disk_id.len(), VIRTIO_BLK_ID_BYTES as usize);
|
||||
default_disk_image_id[..bytes_to_copy].clone_from_slice(&disk_id[..bytes_to_copy])
|
||||
default_serial[..bytes_to_copy].clone_from_slice(&disk_id[..bytes_to_copy])
|
||||
}
|
||||
}
|
||||
default_disk_image_id
|
||||
default_serial
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ExecuteError {
|
||||
#[error("Bad request: {0}")]
|
||||
BadRequest(Error),
|
||||
#[error("Falied to flush: {0}")]
|
||||
#[error("Failed to flush: {0}")]
|
||||
Flush(io::Error),
|
||||
#[error("Failed to read: {0}")]
|
||||
Read(GuestMemoryError),
|
||||
#[error("Failed to read_exact: {0}")]
|
||||
ReadExact(io::Error),
|
||||
#[error("Failed to seek: {0}")]
|
||||
Seek(io::Error),
|
||||
#[error("Failed to write: {0}")]
|
||||
Write(GuestMemoryError),
|
||||
#[error("Failed to write_all: {0}")]
|
||||
WriteAll(io::Error),
|
||||
#[error("Unsupported request: {0}")]
|
||||
Unsupported(u32),
|
||||
#[error("Failed to submit io uring: {0}")]
|
||||
@@ -164,8 +173,10 @@ impl ExecuteError {
|
||||
ExecuteError::BadRequest(_) => VIRTIO_BLK_S_IOERR,
|
||||
ExecuteError::Flush(_) => VIRTIO_BLK_S_IOERR,
|
||||
ExecuteError::Read(_) => VIRTIO_BLK_S_IOERR,
|
||||
ExecuteError::ReadExact(_) => VIRTIO_BLK_S_IOERR,
|
||||
ExecuteError::Seek(_) => VIRTIO_BLK_S_IOERR,
|
||||
ExecuteError::Write(_) => VIRTIO_BLK_S_IOERR,
|
||||
ExecuteError::WriteAll(_) => VIRTIO_BLK_S_IOERR,
|
||||
ExecuteError::Unsupported(_) => VIRTIO_BLK_S_UNSUPP,
|
||||
ExecuteError::SubmitIoUring(_) => VIRTIO_BLK_S_IOERR,
|
||||
ExecuteError::GetHostAddress(_) => VIRTIO_BLK_S_IOERR,
|
||||
@@ -327,7 +338,7 @@ impl Request {
|
||||
disk: &mut T,
|
||||
disk_nsectors: u64,
|
||||
mem: &GuestMemoryMmap,
|
||||
disk_id: &[u8],
|
||||
serial: &[u8],
|
||||
) -> result::Result<u32, ExecuteError> {
|
||||
disk.seek(SeekFrom::Start(self.sector << SECTOR_SHIFT))
|
||||
.map_err(ExecuteError::Seek)?;
|
||||
@@ -346,23 +357,31 @@ impl Request {
|
||||
|
||||
match self.request_type {
|
||||
RequestType::In => {
|
||||
mem.read_exact_from(*data_addr, disk, *data_len as usize)
|
||||
.map_err(ExecuteError::Read)?;
|
||||
let mut buf = vec![0u8; *data_len as usize];
|
||||
disk.read_exact(&mut buf).map_err(ExecuteError::ReadExact)?;
|
||||
mem.read_exact_volatile_from(
|
||||
*data_addr,
|
||||
&mut buf.as_slice(),
|
||||
*data_len as usize,
|
||||
)
|
||||
.map_err(ExecuteError::Read)?;
|
||||
len += data_len;
|
||||
}
|
||||
RequestType::Out => {
|
||||
mem.write_all_to(*data_addr, disk, *data_len as usize)
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
mem.write_all_volatile_to(*data_addr, &mut buf, *data_len as usize)
|
||||
.map_err(ExecuteError::Write)?;
|
||||
disk.write_all(&buf).map_err(ExecuteError::WriteAll)?;
|
||||
if !self.writeback {
|
||||
disk.flush().map_err(ExecuteError::Flush)?;
|
||||
}
|
||||
}
|
||||
RequestType::Flush => disk.flush().map_err(ExecuteError::Flush)?,
|
||||
RequestType::GetDeviceId => {
|
||||
if (*data_len as usize) < disk_id.len() {
|
||||
if (*data_len as usize) < serial.len() {
|
||||
return Err(ExecuteError::BadRequest(Error::InvalidOffset));
|
||||
}
|
||||
mem.write_slice(disk_id, *data_addr)
|
||||
mem.write_slice(serial, *data_addr)
|
||||
.map_err(ExecuteError::Write)?;
|
||||
}
|
||||
RequestType::Unsupported(t) => return Err(ExecuteError::Unsupported(t)),
|
||||
@@ -376,7 +395,7 @@ impl Request {
|
||||
mem: &GuestMemoryMmap,
|
||||
disk_nsectors: u64,
|
||||
disk_image: &mut dyn AsyncIo,
|
||||
disk_id: &[u8],
|
||||
serial: &[u8],
|
||||
user_data: u64,
|
||||
) -> result::Result<bool, ExecuteError> {
|
||||
let sector = self.sector;
|
||||
@@ -403,13 +422,13 @@ impl Request {
|
||||
let origin_ptr = mem
|
||||
.get_slice(*data_addr, *data_len as usize)
|
||||
.map_err(ExecuteError::GetHostAddress)?
|
||||
.as_ptr();
|
||||
.ptr_guard();
|
||||
|
||||
// Verify the buffer alignment.
|
||||
// In case it's not properly aligned, an intermediate buffer is
|
||||
// created with the correct alignment, and a copy from/to the
|
||||
// origin buffer is performed, depending on the type of operation.
|
||||
let iov_base = if (origin_ptr as u64) % SECTOR_SIZE != 0 {
|
||||
let iov_base = if (origin_ptr.as_ptr() as u64) % SECTOR_SIZE != 0 {
|
||||
let layout =
|
||||
Layout::from_size_align(*data_len as usize, SECTOR_SIZE as usize).unwrap();
|
||||
// SAFETY: layout has non-zero size
|
||||
@@ -425,15 +444,13 @@ impl Request {
|
||||
if request_type == RequestType::Out {
|
||||
// SAFETY: destination buffer has been allocated with
|
||||
// the proper size.
|
||||
unsafe {
|
||||
std::ptr::copy(origin_ptr as *const u8, aligned_ptr, *data_len as usize)
|
||||
};
|
||||
unsafe { std::ptr::copy(origin_ptr.as_ptr(), aligned_ptr, *data_len as usize) };
|
||||
}
|
||||
|
||||
// Store both origin and aligned pointers for complete_async()
|
||||
// to process them.
|
||||
self.aligned_operations.push(AlignedOperation {
|
||||
origin_ptr: origin_ptr as u64,
|
||||
origin_ptr: origin_ptr.as_ptr() as u64,
|
||||
aligned_ptr: aligned_ptr as u64,
|
||||
size: *data_len as usize,
|
||||
layout,
|
||||
@@ -441,7 +458,7 @@ impl Request {
|
||||
|
||||
aligned_ptr as *mut libc::c_void
|
||||
} else {
|
||||
origin_ptr as *mut libc::c_void
|
||||
origin_ptr.as_ptr() as *mut libc::c_void
|
||||
};
|
||||
|
||||
let iovec = libc::iovec {
|
||||
@@ -480,10 +497,10 @@ impl Request {
|
||||
} else {
|
||||
return Err(ExecuteError::BadRequest(Error::TooManyDescriptors));
|
||||
};
|
||||
if (data_len as usize) < disk_id.len() {
|
||||
if (data_len as usize) < serial.len() {
|
||||
return Err(ExecuteError::BadRequest(Error::InvalidOffset));
|
||||
}
|
||||
mem.write_slice(disk_id, data_addr)
|
||||
mem.write_slice(serial, data_addr)
|
||||
.map_err(ExecuteError::Write)?;
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -564,6 +581,11 @@ unsafe impl ByteValued for VirtioBlockConfig {}
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
unsafe impl ByteValued for VirtioBlockGeometry {}
|
||||
|
||||
/// Check if aio can be used on the current system.
|
||||
pub fn block_aio_is_supported() -> bool {
|
||||
aio::IoContext::new(1).is_ok()
|
||||
}
|
||||
|
||||
/// Check if io_uring for block device can be used on the current system, as
|
||||
/// it correctly supports the expected io_uring features.
|
||||
pub fn block_io_uring_is_supported() -> bool {
|
||||
@@ -728,24 +750,33 @@ pub enum ImageType {
|
||||
const QCOW_MAGIC: u32 = 0x5146_49fb;
|
||||
const VHDX_SIGN: u64 = 0x656C_6966_7864_6876;
|
||||
|
||||
/// Read a block into memory aligned by the source block size (needed for O_DIRECT)
|
||||
pub fn read_aligned_block_size(f: &mut File) -> std::io::Result<Vec<u8>> {
|
||||
let blocksize = DiskTopology::probe(f)?.logical_block_size as usize;
|
||||
// SAFETY: We are allocating memory that is naturally aligned (size = alignment) and we meet
|
||||
// requirements for safety from Vec::from_raw_parts() as we are using the global allocator
|
||||
// and transferring ownership of the memory.
|
||||
let mut data = unsafe {
|
||||
Vec::from_raw_parts(
|
||||
alloc_zeroed(Layout::from_size_align_unchecked(blocksize, blocksize)),
|
||||
blocksize,
|
||||
blocksize,
|
||||
)
|
||||
};
|
||||
f.read_exact(&mut data)?;
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
/// Determine image type through file parsing.
|
||||
pub fn detect_image_type(f: &mut File) -> std::io::Result<ImageType> {
|
||||
// We must create a buffer aligned on 512 bytes with a size being a
|
||||
// multiple of 512 bytes as the file might be opened with O_DIRECT flag.
|
||||
#[repr(align(512))]
|
||||
struct Sector {
|
||||
data: [u8; 512],
|
||||
}
|
||||
let mut s = Sector { data: [0; 512] };
|
||||
|
||||
f.read_exact(&mut s.data)?;
|
||||
let block = read_aligned_block_size(f)?;
|
||||
|
||||
// Check 4 first bytes to get the header value and determine the image type
|
||||
let image_type = if u32::from_be_bytes(s.data[0..4].try_into().unwrap()) == QCOW_MAGIC {
|
||||
let image_type = if u32::from_be_bytes(block[0..4].try_into().unwrap()) == QCOW_MAGIC {
|
||||
ImageType::Qcow2
|
||||
} else if vhd::is_fixed_vhd(f)? {
|
||||
ImageType::FixedVhd
|
||||
} else if u64::from_le_bytes(s.data[0..8].try_into().unwrap()) == VHDX_SIGN {
|
||||
} else if u64::from_le_bytes(block[0..8].try_into().unwrap()) == VHDX_SIGN {
|
||||
ImageType::Vhdx
|
||||
} else {
|
||||
ImageType::Raw
|
||||
@@ -776,3 +807,85 @@ pub fn create_disk_file(mut file: File, direct_io: bool) -> Result<Box<dyn Block
|
||||
ImageType::Raw => Box::new(RawFile::new(file, direct_io)) as Box<dyn BlockBackend>,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DiskTopology {
|
||||
pub logical_block_size: u64,
|
||||
pub physical_block_size: u64,
|
||||
pub minimum_io_size: u64,
|
||||
pub optimal_io_size: u64,
|
||||
}
|
||||
|
||||
impl Default for DiskTopology {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
logical_block_size: 512,
|
||||
physical_block_size: 512,
|
||||
minimum_io_size: 512,
|
||||
optimal_io_size: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ioctl_io_nr!(BLKSSZGET, 0x12, 104);
|
||||
ioctl_io_nr!(BLKPBSZGET, 0x12, 123);
|
||||
ioctl_io_nr!(BLKIOMIN, 0x12, 120);
|
||||
ioctl_io_nr!(BLKIOOPT, 0x12, 121);
|
||||
|
||||
enum BlockSize {
|
||||
LogicalBlock,
|
||||
PhysicalBlock,
|
||||
MinimumIo,
|
||||
OptimalIo,
|
||||
}
|
||||
|
||||
impl DiskTopology {
|
||||
fn is_block_device(f: &File) -> std::io::Result<bool> {
|
||||
let mut stat = std::mem::MaybeUninit::<libc::stat>::uninit();
|
||||
// SAFETY: FFI call with a valid fd and buffer
|
||||
let ret = unsafe { libc::fstat(f.as_raw_fd(), stat.as_mut_ptr()) };
|
||||
if ret != 0 {
|
||||
return Err(std::io::Error::last_os_error());
|
||||
}
|
||||
|
||||
// SAFETY: stat is valid at this point
|
||||
let is_block = unsafe { (*stat.as_ptr()).st_mode & S_IFMT == S_IFBLK };
|
||||
Ok(is_block)
|
||||
}
|
||||
|
||||
// libc::ioctl() takes different types on different architectures
|
||||
fn query_block_size(f: &File, block_size_type: BlockSize) -> std::io::Result<u64> {
|
||||
let mut block_size = 0;
|
||||
// SAFETY: FFI call with correct arguments
|
||||
let ret = unsafe {
|
||||
ioctl(
|
||||
f.as_raw_fd(),
|
||||
match block_size_type {
|
||||
BlockSize::LogicalBlock => BLKSSZGET(),
|
||||
BlockSize::PhysicalBlock => BLKPBSZGET(),
|
||||
BlockSize::MinimumIo => BLKIOMIN(),
|
||||
BlockSize::OptimalIo => BLKIOOPT(),
|
||||
} as _,
|
||||
&mut block_size,
|
||||
)
|
||||
};
|
||||
if ret != 0 {
|
||||
return Err(std::io::Error::last_os_error());
|
||||
};
|
||||
|
||||
Ok(block_size)
|
||||
}
|
||||
|
||||
pub fn probe(f: &File) -> std::io::Result<Self> {
|
||||
if !Self::is_block_device(f)? {
|
||||
return Ok(DiskTopology::default());
|
||||
}
|
||||
|
||||
Ok(DiskTopology {
|
||||
logical_block_size: Self::query_block_size(f, BlockSize::LogicalBlock)?,
|
||||
physical_block_size: Self::query_block_size(f, BlockSize::PhysicalBlock)?,
|
||||
minimum_io_size: Self::query_block_size(f, BlockSize::MinimumIo)?,
|
||||
optimal_io_size: Self::query_block_size(f, BlockSize::OptimalIo)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult, DiskTopology,
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
||||
};
|
||||
use crate::DiskTopology;
|
||||
use io_uring::{opcode, squeue, types, IoUring};
|
||||
use std::fs::File;
|
||||
use std::io::{Seek, SeekFrom};
|
||||
@@ -36,7 +37,7 @@ impl DiskFile for RawFileDisk {
|
||||
}
|
||||
|
||||
fn topology(&mut self) -> DiskTopology {
|
||||
if let Ok(topology) = DiskTopology::probe(&mut self.file) {
|
||||
if let Ok(topology) = DiskTopology::probe(&self.file) {
|
||||
topology
|
||||
} else {
|
||||
warn!("Unable to get device topology. Using default topology");
|
||||
|
||||
150
block/src/raw_async_aio.rs
Normal file
150
block/src/raw_async_aio.rs
Normal file
@@ -0,0 +1,150 @@
|
||||
// Copyright © 2023 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
//
|
||||
// Copyright © 2023 Crusoe Energy Systems LLC
|
||||
//
|
||||
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
||||
};
|
||||
use crate::DiskTopology;
|
||||
use std::fs::File;
|
||||
use std::io::{Seek, SeekFrom};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use vmm_sys_util::aio;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
pub struct RawFileDiskAio {
|
||||
file: File,
|
||||
}
|
||||
|
||||
impl RawFileDiskAio {
|
||||
pub fn new(file: File) -> Self {
|
||||
RawFileDiskAio { file }
|
||||
}
|
||||
}
|
||||
|
||||
impl DiskFile for RawFileDiskAio {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
self.file
|
||||
.seek(SeekFrom::End(0))
|
||||
.map_err(DiskFileError::Size)
|
||||
}
|
||||
|
||||
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
Ok(Box::new(
|
||||
RawFileAsyncAio::new(self.file.as_raw_fd(), ring_depth)
|
||||
.map_err(DiskFileError::NewAsyncIo)?,
|
||||
) as Box<dyn AsyncIo>)
|
||||
}
|
||||
|
||||
fn topology(&mut self) -> DiskTopology {
|
||||
if let Ok(topology) = DiskTopology::probe(&self.file) {
|
||||
topology
|
||||
} else {
|
||||
warn!("Unable to get device topology. Using default topology");
|
||||
DiskTopology::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RawFileAsyncAio {
|
||||
fd: RawFd,
|
||||
ctx: aio::IoContext,
|
||||
eventfd: EventFd,
|
||||
}
|
||||
|
||||
impl RawFileAsyncAio {
|
||||
pub fn new(fd: RawFd, queue_depth: u32) -> std::io::Result<Self> {
|
||||
let eventfd = EventFd::new(libc::EFD_NONBLOCK)?;
|
||||
let ctx = aio::IoContext::new(queue_depth)?;
|
||||
|
||||
Ok(RawFileAsyncAio { fd, ctx, eventfd })
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncIo for RawFileAsyncAio {
|
||||
fn notifier(&self) -> &EventFd {
|
||||
&self.eventfd
|
||||
}
|
||||
|
||||
fn read_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
let iocbs = [&mut aio::IoControlBlock {
|
||||
aio_fildes: self.fd.as_raw_fd() as u32,
|
||||
aio_lio_opcode: aio::IOCB_CMD_PREADV as u16,
|
||||
aio_buf: iovecs.as_ptr() as u64,
|
||||
aio_nbytes: iovecs.len() as u64,
|
||||
aio_offset: offset,
|
||||
aio_data: user_data,
|
||||
aio_flags: aio::IOCB_FLAG_RESFD,
|
||||
aio_resfd: self.eventfd.as_raw_fd() as u32,
|
||||
..Default::default()
|
||||
}];
|
||||
let _ = self
|
||||
.ctx
|
||||
.submit(&iocbs[..])
|
||||
.map_err(AsyncIoError::ReadVectored)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
let iocbs = [&mut aio::IoControlBlock {
|
||||
aio_fildes: self.fd.as_raw_fd() as u32,
|
||||
aio_lio_opcode: aio::IOCB_CMD_PWRITEV as u16,
|
||||
aio_buf: iovecs.as_ptr() as u64,
|
||||
aio_nbytes: iovecs.len() as u64,
|
||||
aio_offset: offset,
|
||||
aio_data: user_data,
|
||||
aio_flags: aio::IOCB_FLAG_RESFD,
|
||||
aio_resfd: self.eventfd.as_raw_fd() as u32,
|
||||
..Default::default()
|
||||
}];
|
||||
let _ = self
|
||||
.ctx
|
||||
.submit(&iocbs[..])
|
||||
.map_err(AsyncIoError::WriteVectored)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
|
||||
if let Some(user_data) = user_data {
|
||||
let iocbs = [&mut aio::IoControlBlock {
|
||||
aio_fildes: self.fd.as_raw_fd() as u32,
|
||||
aio_lio_opcode: aio::IOCB_CMD_FSYNC as u16,
|
||||
aio_data: user_data,
|
||||
aio_flags: aio::IOCB_FLAG_RESFD,
|
||||
aio_resfd: self.eventfd.as_raw_fd() as u32,
|
||||
..Default::default()
|
||||
}];
|
||||
let _ = self.ctx.submit(&iocbs[..]).map_err(AsyncIoError::Fsync)?;
|
||||
} else {
|
||||
// SAFETY: FFI call with a valid fd
|
||||
unsafe { libc::fsync(self.fd) };
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn next_completed_request(&mut self) -> Option<(u64, i32)> {
|
||||
let mut events: [aio::IoEvent; 1] = [aio::IoEvent::default()];
|
||||
let rc = self.ctx.get_events(0, &mut events, None).unwrap();
|
||||
if rc == 0 {
|
||||
None
|
||||
} else {
|
||||
Some((events[0].data, events[0].res as i32))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult, DiskTopology,
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
||||
};
|
||||
use crate::DiskTopology;
|
||||
use std::collections::VecDeque;
|
||||
use std::fs::File;
|
||||
use std::io::{Seek, SeekFrom};
|
||||
@@ -33,7 +34,7 @@ impl DiskFile for RawFileDiskSync {
|
||||
}
|
||||
|
||||
fn topology(&mut self) -> DiskTopology {
|
||||
if let Ok(topology) = DiskTopology::probe(&mut self.file) {
|
||||
if let Ok(topology) = DiskTopology::probe(&self.file) {
|
||||
topology
|
||||
} else {
|
||||
warn!("Unable to get device topology. Using default topology");
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{read_aligned_block_size, DiskTopology};
|
||||
use std::convert::TryInto;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
use std::io::{Seek, SeekFrom};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct VhdFooter {
|
||||
@@ -27,37 +28,33 @@ pub struct VhdFooter {
|
||||
|
||||
impl VhdFooter {
|
||||
pub fn new(file: &mut File) -> std::io::Result<VhdFooter> {
|
||||
// We must create a buffer aligned on 512 bytes with a size being a
|
||||
// multiple of 512 bytes as the file might be opened with O_DIRECT flag.
|
||||
#[repr(align(512))]
|
||||
struct Sector {
|
||||
data: [u8; 512],
|
||||
}
|
||||
let mut s = Sector { data: [0; 512] };
|
||||
let blocksize = DiskTopology::probe(file)?.logical_block_size as usize;
|
||||
|
||||
// Place the cursor 512 bytes before the end of the file, as this is
|
||||
// where the footer starts.
|
||||
file.seek(SeekFrom::End(-512))?;
|
||||
// Place the cursor in the last block of the file
|
||||
file.seek(SeekFrom::End(0 - (blocksize as i64)))?;
|
||||
// Read in the last block
|
||||
let data = read_aligned_block_size(file)?;
|
||||
|
||||
// Fill in the VhdFooter structure
|
||||
file.read_exact(&mut s.data)?;
|
||||
// We only care about the last sector
|
||||
let offset = blocksize - 512;
|
||||
let sector = &data[offset..];
|
||||
|
||||
Ok(VhdFooter {
|
||||
cookie: u64::from_be_bytes(s.data[0..8].try_into().unwrap()),
|
||||
features: u32::from_be_bytes(s.data[8..12].try_into().unwrap()),
|
||||
file_format_version: u32::from_be_bytes(s.data[12..16].try_into().unwrap()),
|
||||
data_offset: u64::from_be_bytes(s.data[16..24].try_into().unwrap()),
|
||||
time_stamp: u32::from_be_bytes(s.data[24..28].try_into().unwrap()),
|
||||
creator_application: u32::from_be_bytes(s.data[28..32].try_into().unwrap()),
|
||||
creator_version: u32::from_be_bytes(s.data[32..36].try_into().unwrap()),
|
||||
creator_host_os: u32::from_be_bytes(s.data[36..40].try_into().unwrap()),
|
||||
original_size: u64::from_be_bytes(s.data[40..48].try_into().unwrap()),
|
||||
current_size: u64::from_be_bytes(s.data[48..56].try_into().unwrap()),
|
||||
disk_geometry: u32::from_be_bytes(s.data[56..60].try_into().unwrap()),
|
||||
disk_type: u32::from_be_bytes(s.data[60..64].try_into().unwrap()),
|
||||
checksum: u32::from_be_bytes(s.data[64..68].try_into().unwrap()),
|
||||
unique_id: u128::from_be_bytes(s.data[68..84].try_into().unwrap()),
|
||||
saved_state: u8::from_be_bytes(s.data[84..85].try_into().unwrap()),
|
||||
cookie: u64::from_be_bytes(sector[0..8].try_into().unwrap()),
|
||||
features: u32::from_be_bytes(sector[8..12].try_into().unwrap()),
|
||||
file_format_version: u32::from_be_bytes(sector[12..16].try_into().unwrap()),
|
||||
data_offset: u64::from_be_bytes(sector[16..24].try_into().unwrap()),
|
||||
time_stamp: u32::from_be_bytes(sector[24..28].try_into().unwrap()),
|
||||
creator_application: u32::from_be_bytes(sector[28..32].try_into().unwrap()),
|
||||
creator_version: u32::from_be_bytes(sector[32..36].try_into().unwrap()),
|
||||
creator_host_os: u32::from_be_bytes(sector[36..40].try_into().unwrap()),
|
||||
original_size: u64::from_be_bytes(sector[40..48].try_into().unwrap()),
|
||||
current_size: u64::from_be_bytes(sector[48..56].try_into().unwrap()),
|
||||
disk_geometry: u32::from_be_bytes(sector[56..60].try_into().unwrap()),
|
||||
disk_type: u32::from_be_bytes(sector[60..64].try_into().unwrap()),
|
||||
checksum: u32::from_be_bytes(sector[64..68].try_into().unwrap()),
|
||||
unique_id: u128::from_be_bytes(sector[68..84].try_into().unwrap()),
|
||||
saved_state: u8::from_be_bytes(sector[84..85].try_into().unwrap()),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ pub enum VhdxHeaderError {
|
||||
SeekRegionTableEntries(#[source] io::Error),
|
||||
#[error("Failed to seek in region table header {0}")]
|
||||
SeekRegionTableHeader(#[source] io::Error),
|
||||
#[error("We do not recongize this entry")]
|
||||
#[error("We do not recognize this entry")]
|
||||
UnrecognizedRegionEntry,
|
||||
#[error("Failed to write header {0}")]
|
||||
WriteHeader(#[source] io::Error),
|
||||
@@ -305,7 +305,7 @@ impl RegionInfo {
|
||||
|
||||
if (entry.required & REGION_ENTRY_REQUIRED) == 1 {
|
||||
// This implementation doesn't recognize this field.
|
||||
// Therefore, accoding to the spec, we are throwing an error.
|
||||
// Therefore, according to the spec, we are throwing an error.
|
||||
return Err(VhdxHeaderError::UnrecognizedRegionEntry);
|
||||
}
|
||||
}
|
||||
@@ -444,7 +444,7 @@ impl VhdxHeader {
|
||||
}
|
||||
}
|
||||
|
||||
// Update the provided headers accoding to the spec
|
||||
// Update the provided headers according to the spec
|
||||
fn update_headers(
|
||||
f: &mut File,
|
||||
header_1: Result<Header>,
|
||||
@@ -468,7 +468,7 @@ impl VhdxHeader {
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the checksum of a buffer that itself containts its checksum
|
||||
/// Calculates the checksum of a buffer that itself contains its checksum
|
||||
/// Therefore, before calculating, the existing checksum is retrieved and the
|
||||
/// corresponding field is made zero. After the calculation, the existing checksum
|
||||
/// is put back to the buffer.
|
||||
|
||||
@@ -96,7 +96,7 @@ pub struct DiskSpec {
|
||||
}
|
||||
|
||||
impl DiskSpec {
|
||||
/// Parse all meatadata from the provided file and store info in DiskSpec
|
||||
/// Parse all metadata from the provided file and store info in DiskSpec
|
||||
/// structure.
|
||||
pub fn new(f: &mut File, metadata_region: &RegionTableEntry) -> Result<DiskSpec> {
|
||||
let mut disk_spec = DiskSpec::default();
|
||||
@@ -133,7 +133,7 @@ impl DiskSpec {
|
||||
.map_err(VhdxMetadataError::ReadMetadata)?;
|
||||
|
||||
// MUST be at least 1 MiB and not greater than 256 MiB
|
||||
if disk_spec.block_size < BLOCK_SIZE_MIN && disk_spec.block_size > BLOCK_SIZE_MAX {
|
||||
if disk_spec.block_size < BLOCK_SIZE_MIN || disk_spec.block_size > BLOCK_SIZE_MAX {
|
||||
return Err(VhdxMetadataError::InvalidBlockSize);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
|
||||
anyhow = "1.0.71"
|
||||
anyhow = "1.0.75"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "2.3.3"
|
||||
bitflags = "2.4.1"
|
||||
byteorder = "1.4.3"
|
||||
event_monitor = { path = "../event_monitor" }
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.139"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.147"
|
||||
log = "0.4.20"
|
||||
pci = { path = "../pci" }
|
||||
thiserror = "1.0.40"
|
||||
tpm = { path = "../tpm" }
|
||||
@@ -21,7 +21,7 @@ versionize = "0.1.10"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = "0.11.0"
|
||||
vm-memory = "0.13.1"
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ impl Aml for AcpiGedDevice {
|
||||
&aml::Name::new(
|
||||
"_CRS".into(),
|
||||
&aml::ResourceTemplate::new(vec![&aml::AddressSpace::new_memory(
|
||||
aml::AddressSpaceCachable::NotCacheable,
|
||||
aml::AddressSpaceCacheable::NotCacheable,
|
||||
true,
|
||||
self.address.0,
|
||||
self.address.0 + GED_DEVICE_ACPI_SIZE as u64 - 1,
|
||||
|
||||
@@ -27,7 +27,7 @@ pub const IRQ_LEGACY_BASE: usize = layout::IRQ_BASE as usize;
|
||||
pub const IRQ_LEGACY_COUNT: usize = 32;
|
||||
pub const GIC_SNAPSHOT_ID: &str = "gic-v3-its";
|
||||
|
||||
// Gic (Generic Interupt Controller) struct provides all the functionality of a
|
||||
// Gic (Generic Interrupt Controller) struct provides all the functionality of a
|
||||
// GIC device. It wraps a hypervisor-emulated GIC device (Vgic) provided by the
|
||||
// `hypervisor` crate.
|
||||
// Gic struct also implements InterruptController to provide interrupt delivery
|
||||
|
||||
@@ -31,7 +31,7 @@ const GPIOIC: u64 = 0x41c; // Interrupt Clear Register
|
||||
const GPIOAFSEL: u64 = 0x420; // Mode Control Select Register
|
||||
// From 0x424 to 0xFDC => reserved space.
|
||||
// From 0xFE0 to 0xFFC => Peripheral and PrimeCell Identification Registers which are Read Only registers.
|
||||
// Thses registers can conceptually be treated as a 32-bit register, and PartNumber[11:0] is used to identify the peripheral.
|
||||
// These registers can conceptually be treated as a 32-bit register, and PartNumber[11:0] is used to identify the peripheral.
|
||||
// We are putting the expected values (look at 'Reset value' column from above mentioned document) in an array.
|
||||
const GPIO_ID: [u8; 8] = [0x61, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1];
|
||||
// ID Margins
|
||||
@@ -57,7 +57,7 @@ impl fmt::Display for Error {
|
||||
write!(f, "Could not trigger GPIO interrupt: {e}.")
|
||||
}
|
||||
Error::GpioTriggerKeyFailure(key) => {
|
||||
write!(f, "Invalid GPIO Input key triggerd: {key}.")
|
||||
write!(f, "Invalid GPIO Input key triggered: {key}.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,8 +166,8 @@ impl Serial {
|
||||
Self::new(id, interrupt, None, state)
|
||||
}
|
||||
|
||||
pub fn set_out(&mut self, out: Box<dyn io::Write + Send>) {
|
||||
self.out = Some(out);
|
||||
pub fn set_out(&mut self, out: Option<Box<dyn io::Write + Send>>) {
|
||||
self.out = out;
|
||||
}
|
||||
|
||||
/// Queues raw bytes for the guest to read and signals the interrupt if the line status would
|
||||
|
||||
@@ -201,8 +201,8 @@ impl Pl011 {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_out(&mut self, out: Box<dyn io::Write + Send>) {
|
||||
self.out = Some(out);
|
||||
pub fn set_out(&mut self, out: Option<Box<dyn io::Write + Send>>) {
|
||||
self.out = out;
|
||||
}
|
||||
|
||||
fn state(&self) -> Pl011State {
|
||||
|
||||
@@ -26,6 +26,7 @@ const PVPANIC_VENDOR_ID: u16 = 0x1b36;
|
||||
const PVPANIC_DEVICE_ID: u16 = 0x0011;
|
||||
|
||||
pub const PVPANIC_DEVICE_MMIO_SIZE: u64 = 0x2;
|
||||
pub const PVPANIC_DEVICE_MMIO_ALIGNMENT: u64 = 0x10;
|
||||
|
||||
const PVPANIC_PANICKED: u8 = 1 << 0;
|
||||
const PVPANIC_CRASH_LOADED: u8 = 1 << 1;
|
||||
@@ -38,9 +39,8 @@ pub enum PvPanicError {
|
||||
RetrievePciConfigurationState(#[source] anyhow::Error),
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum PvPanicSubclass {
|
||||
enum PvPanicSubclass {
|
||||
Other = 0x80,
|
||||
}
|
||||
|
||||
@@ -181,8 +181,9 @@ impl PciDevice for PvPanicDevice {
|
||||
|
||||
fn allocate_bars(
|
||||
&mut self,
|
||||
allocator: &Arc<Mutex<SystemAllocator>>,
|
||||
_mmio_allocator: &mut AddressAllocator,
|
||||
_allocator: &Arc<Mutex<SystemAllocator>>,
|
||||
mmio32_allocator: &mut AddressAllocator,
|
||||
_mmio64_allocator: &mut AddressAllocator,
|
||||
resources: Option<Vec<Resource>>,
|
||||
) -> std::result::Result<Vec<PciBarConfiguration>, PciDeviceError> {
|
||||
let mut bars = Vec::new();
|
||||
@@ -190,10 +191,8 @@ impl PciDevice for PvPanicDevice {
|
||||
let bar_id = 0;
|
||||
let region_size = PVPANIC_DEVICE_MMIO_SIZE;
|
||||
let restoring = resources.is_some();
|
||||
let bar_addr = allocator
|
||||
.lock()
|
||||
.unwrap()
|
||||
.allocate_mmio_hole_addresses(None, region_size, None)
|
||||
let bar_addr = mmio32_allocator
|
||||
.allocate(None, region_size, Some(PVPANIC_DEVICE_MMIO_ALIGNMENT))
|
||||
.ok_or(PciDeviceError::IoAllocationFailed(region_size))?;
|
||||
|
||||
let bar = PciBarConfiguration::default()
|
||||
@@ -218,11 +217,12 @@ impl PciDevice for PvPanicDevice {
|
||||
|
||||
fn free_bars(
|
||||
&mut self,
|
||||
allocator: &mut SystemAllocator,
|
||||
_mmio_allocator: &mut AddressAllocator,
|
||||
_allocator: &mut SystemAllocator,
|
||||
mmio32_allocator: &mut AddressAllocator,
|
||||
_mmio64_allocator: &mut AddressAllocator,
|
||||
) -> std::result::Result<(), PciDeviceError> {
|
||||
for bar in self.bar_regions.drain(..) {
|
||||
allocator.free_mmio_hole_addresses(GuestAddress(bar.addr()), bar.size());
|
||||
mmio32_allocator.free(GuestAddress(bar.addr()), bar.size());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
40
docs/amd_sev_snp.md
Normal file
40
docs/amd_sev_snp.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# AMD SEV-SNP
|
||||
|
||||
### WARNING
|
||||
This feature is only currently supported on MSHV.
|
||||
|
||||
AMD Secure Encrypted Virtualization & Secure Nested Paging (SEV-SNP) is an AMD
|
||||
technology designed to add strong memory integrity protection to help prevent
|
||||
malicious hypervisor-based attacks like data replay, memory-remapping and more
|
||||
in order to create an isolated execution environment. Here are some useful
|
||||
links:
|
||||
|
||||
* [SNP Homepage] (https://www.amd.com/en/processors/amd-secure-encrypted-virtualization)
|
||||
more information about SEV-SNP technical aspects, design and specification.
|
||||
|
||||
## Cloud Hypervisor support
|
||||
|
||||
It is required to use a machine which has enabled support for AMD SEV-SNP in
|
||||
the BIOS.
|
||||
|
||||
On the Cloud Hypervisor side, all you need is to build the project with the
|
||||
`sev_snp` feature enabled:
|
||||
|
||||
```bash
|
||||
cargo build --no-default-features --features "sev_snp"
|
||||
```
|
||||
|
||||
**Note**
|
||||
Please note that `sev_snp` cannot be enabled in conjunction with `tdx` feature flag.
|
||||
|
||||
You can run a SEV-SNP VM using the following command:
|
||||
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
--platform sev_snp=on \
|
||||
--cpus boot=1 \
|
||||
--memory size=1G \
|
||||
--disk path=ubuntu.img
|
||||
```
|
||||
|
||||
For more information related to Microsoft Hypervisor please see [mshv.md](mshv.md)
|
||||
36
docs/api.md
36
docs/api.md
@@ -206,11 +206,14 @@ curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1
|
||||
|
||||
### D-Bus API
|
||||
|
||||
Cloud Hypervisor offers a D-Bus API as an alternative to its REST API. As of
|
||||
writing this document, the D-Bus API mirrors the functionality of the REST
|
||||
API and shares the same set of endpoints, meaning that it supports every call
|
||||
that is supported by the REST API and can be a drop-in replacement since it
|
||||
also consumes/produces JSON.
|
||||
Cloud Hypervisor offers a D-Bus API as an alternative to its REST API. This
|
||||
D-Bus API fully reflects the functionality of the REST API, exposing the
|
||||
same group of endpoints. It can be a drop-in replacement since it also
|
||||
consumes/produces JSON.
|
||||
|
||||
In addition, the D-Bus API also exposes events from `event-monitor` in the
|
||||
form of a D-Bus signal to which users can subscribe. For more information,
|
||||
see [D-Bus API Interface](#d-bus-api-interface).
|
||||
|
||||
#### D-Bus API Location and availability
|
||||
|
||||
@@ -245,8 +248,23 @@ which in turn can be used to control and manage Cloud Hypervisor.
|
||||
|
||||
#### D-Bus API Interface
|
||||
|
||||
Please refer to the [REST API](#rest-api) documentation. As previously
|
||||
mentioned, the D-Bus API currently mirrors the behaviour of the REST API.
|
||||
Please refer to the [REST API](#rest-api) documentation for everything that
|
||||
is in common with the REST API. As previously mentioned, the D-Bus API can
|
||||
be used as a drop-in replacement for the [REST API](#rest-api).
|
||||
|
||||
The D-Bus interface also exposes a signal, named `Event`, which is emitted
|
||||
whenever a new event is published from the `event-monitor` crate. Here is its
|
||||
definition in XML format:
|
||||
|
||||
```xml
|
||||
<node>
|
||||
<interface name="org.cloudhypervisor.DBusApi1">
|
||||
<signal name="Event">
|
||||
<arg name="event" type="s"/>
|
||||
</signal>
|
||||
</interface>
|
||||
</node>
|
||||
```
|
||||
|
||||
### Command Line Interface
|
||||
|
||||
@@ -277,7 +295,7 @@ From the CLI, one can:
|
||||
The REST API, D-Bus API and the CLI all rely on a common, [internal API](#internal-api).
|
||||
|
||||
The CLI options are parsed by the
|
||||
[argh crate](https://docs.rs/argh/latest/argh/) and then translated into
|
||||
[clap crate](https://docs.rs/clap/4.3.11/clap/) and then translated into
|
||||
[internal API](#internal-api) commands.
|
||||
|
||||
The REST API is processed by an HTTP thread using the
|
||||
@@ -309,7 +327,7 @@ As a summary, the REST API, the D-Bus API and the CLI are essentially frontends
|
||||
| | +------------------------+
|
||||
| +----------+ | VMM
|
||||
| CLI | | |
|
||||
+----------->+ argh +--------------+
|
||||
+----------->+ clap +--------------+
|
||||
| |
|
||||
+----------+
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ Value is a boolean set to `off` by default.
|
||||
_Example_
|
||||
|
||||
```
|
||||
--ballloon size=2G,deflate_on_oom=on
|
||||
--balloon size=2G,deflate_on_oom=on
|
||||
```
|
||||
|
||||
### `free_page_reporting`
|
||||
@@ -72,5 +72,5 @@ Value is a boolean set to `off` by default.
|
||||
_Example_
|
||||
|
||||
```
|
||||
--ballloon size=0,free_page_reporting=on
|
||||
--balloon size=0,free_page_reporting=on
|
||||
```
|
||||
|
||||
81
docs/coverage.md
Normal file
81
docs/coverage.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# Code coverage
|
||||
|
||||
LLVM provides a set of tools to collect code coverage data and present the data
|
||||
in human-consumable forms.
|
||||
|
||||
## Building a suitable binary
|
||||
|
||||
The compiler flag to generate code coverage data has been stabilized since Rust
|
||||
1.60.
|
||||
|
||||
An instrumented binary can be built with the following command:
|
||||
|
||||
```shell
|
||||
cargo clean && RUSTFLAGS='-C instrument-coverage' cargo build
|
||||
```
|
||||
|
||||
Using either `debug` or `release` profile is fine. You will need to adjust
|
||||
the path for some commands.
|
||||
|
||||
## Running the binary
|
||||
|
||||
Run the binary as you normally would. When the process exits, you will see
|
||||
files with the prefix `profraw`.
|
||||
|
||||
Multiple runs of the same binary will produce multiple `profraw` files.
|
||||
|
||||
The more diverse the runs are, the better. Try to exercise different features
|
||||
as much as possible.
|
||||
|
||||
## Combining raw data
|
||||
|
||||
Raw data files can be combined with `llvm-profdata`.
|
||||
|
||||
```shell
|
||||
rustup component add llvm-tools-preview
|
||||
# Assuming profraw files reside in the current directory and its children directories
|
||||
find . -name '*.profraw' -exec llvm-profdata merge -sparse {} -o coverage.profdata \;
|
||||
```
|
||||
|
||||
A file named `coverage.profdata` will be generated.
|
||||
|
||||
## Generating HTML files for human consumption
|
||||
|
||||
This can be done either with LLVM or `grcov`.
|
||||
|
||||
Here is an example using grcov.
|
||||
|
||||
```shell
|
||||
cargo install grcov
|
||||
# Assuming the profdata file is in the top level directory of the Cloud Hypervisor repository
|
||||
grcov . --binary-path ./target/x86_64-unknown-linux-gnu/release -s . -t html --branch --ignore-not-existing -o coverage-html-output/
|
||||
```
|
||||
|
||||
You can then open the `index.html` file under coverage-html-output to see the
|
||||
results.
|
||||
|
||||
## Notes on running the in-tree integration tests and unit tests
|
||||
|
||||
Please set RUSTFLAGS the same way while invoking `dev_cli.sh`. The script will
|
||||
pass RUSTFLAGS to the container.
|
||||
|
||||
Since the `profraw` files are generated from within the container, the file
|
||||
paths embedded in the data files are going to be different. It is easier to do
|
||||
the data processing from within the container if you don't want to fight the
|
||||
tool chain.
|
||||
|
||||
```shell
|
||||
# Get a shell
|
||||
./scripts/dev_cli.sh shell
|
||||
|
||||
# Install llvm-tools-preview for llvm-profdata
|
||||
rustup component add llvm-tools-preview
|
||||
# Merge data files by using the following command
|
||||
find . -name '*.profraw' -exec `rustc --print sysroot`/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-profdata merge -sparse {} -o coverage.profdata \;
|
||||
|
||||
# As of writing, the container has Rust 1.67.1. It is too old for grcov.
|
||||
rustup install stable
|
||||
cargo +stable install grcov
|
||||
# Run grcov as usual
|
||||
grcov . --binary-path ./target/x86_64-unknown-linux-gnu/release -s . -t html --branch --ignore-not-existing -o coverage-html-output/
|
||||
```
|
||||
@@ -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
|
||||
(`-v -v -v`). It is also recommended to have it log into a dedicated file in order
|
||||
(`-vvv`). 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 \
|
||||
-v -v -v
|
||||
-vvv
|
||||
```
|
||||
|
||||
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 \
|
||||
|
||||
@@ -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:
|
||||
@@ -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.
|
||||
|
||||
@@ -47,12 +47,15 @@ The custom Linux kernel for the guest can be built with the [TDX Tools](https://
|
||||
The firmware can be built as follows:
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install uuid-dev nasm iasl build-essential python3-distutils git
|
||||
|
||||
git clone https://github.com/tianocore/edk2.git
|
||||
cd edk2
|
||||
git checkout 13b97736c876919b9786055829caaa4fa46984b7
|
||||
git submodule update --init --recursive
|
||||
make -C BaseTools
|
||||
source ./edksetup.sh
|
||||
git submodule update --init --recursive
|
||||
make -C BaseTools -j `nproc`
|
||||
build -p OvmfPkg/IntelTdx/IntelTdxX64.dsc -a X64 -t GCC5 -b RELEASE
|
||||
```
|
||||
|
||||
|
||||
@@ -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 --disk path=tmp.img\
|
||||
--disk path=focal-1.raw path=focal-nested.raw 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 --disk path=tmp.img\
|
||||
--disk path=focal-2.raw path=focal-nested.raw 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 --disk path=/dev/vdc \
|
||||
--api-socket /tmp/api1 \
|
||||
--disk path=/dev/vdb 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
|
||||
|
||||
@@ -7,13 +7,13 @@ The target audience of this document is both:
|
||||
|
||||
## Control
|
||||
|
||||
The number of `-v` parameters passed to the `cloud-hypervisor` binary will determine the log level. Currenly the default is log messages up to `WARN:` (`warn!`) are included by default. The `--log-file` allows the log to be sent to a location other than `stderr`.
|
||||
The number of `-v` parameters passed to the `cloud-hypervisor` binary will determine the log level. Currently the default is log messages up to `WARN:` (`warn!`) are included by default. The `--log-file` allows the log to be sent to a location other than `stderr`.
|
||||
|
||||
## Levels
|
||||
|
||||
### `error!()`
|
||||
|
||||
For immediate, unrecoverable errors where it does not make sense for the execution to continue as the behaviour of the VM is considerablely impacted.
|
||||
For immediate, unrecoverable errors where it does not make sense for the execution to continue as the behaviour of the VM is considerably impacted.
|
||||
|
||||
Cloud Hypervisor should exit shortly after reporting this error (with a non-zero exit code). Generally this should be used during initial construction of the VM state before the virtual CPUs have begun running code.
|
||||
|
||||
@@ -23,7 +23,7 @@ Users should react to this error by checking their initial VM configuration.
|
||||
|
||||
### `warn!()`
|
||||
|
||||
A serious problem has occured but the execution of the VM can continue although some functionality might be impacted.
|
||||
A serious problem has occurred but the execution of the VM can continue although some functionality might be impacted.
|
||||
|
||||
A typical example of where this level of message should be generated is during an API call request that cannot be fulfilled.
|
||||
|
||||
@@ -38,6 +38,6 @@ This level is for the benefit of developers. It should be used for sporadic and
|
||||
|
||||
### `debug!()`
|
||||
|
||||
Use `-v -v` to enable.
|
||||
Use `-vv` 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`.
|
||||
@@ -222,7 +222,7 @@ struct MemoryZoneConfig {
|
||||
--memory-zone <memory-zone> 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"
|
||||
```
|
||||
|
||||
This parameter expects one or more occurences, allowing for a list of memory
|
||||
This parameter expects one or more occurrences, allowing for a list of memory
|
||||
zones to be defined. It must be used with `--memory size=0`, clearly indicating
|
||||
that the memory will be described through advanced parameters.
|
||||
|
||||
@@ -516,7 +516,7 @@ different distances, it can be described with the following example.
|
||||
_Example_
|
||||
|
||||
```
|
||||
--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]
|
||||
--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]
|
||||
```
|
||||
|
||||
### `memory_zones`
|
||||
@@ -540,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 --numa guest_numa_id=1,memory_zones=mem0`
|
||||
`--numa guest_numa_id=0,memory_zones=mem0 guest_numa_id=1,memory_zones=mem0`
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--memory size=0
|
||||
--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
|
||||
--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
|
||||
```
|
||||
|
||||
### `sgx_epc_sections`
|
||||
@@ -567,12 +567,21 @@ _Example_
|
||||
|
||||
```
|
||||
--sgx-epc id=epc0,size=32M id=epc1,size=64M id=epc2,size=32M
|
||||
--numa guest_numa_id=0,sgx_epc_sections=epc1 --numa guest_numa_id=1,sgx_epc_sections=[epc0,epc2]
|
||||
--numa guest_numa_id=0,sgx_epc_sections=epc1 guest_numa_id=1,sgx_epc_sections=[epc0,epc2]
|
||||
```
|
||||
|
||||
### PCI bus
|
||||
|
||||
Cloud Hypervisor supports only one PCI bus, which is why it has been tied to
|
||||
the NUMA node 0 by default. It is the user responsibility to organize the NUMA
|
||||
nodes correctly so that vCPUs and guest RAM which should be located on the same
|
||||
NUMA node as the PCI bus end up on the NUMA node 0.
|
||||
Cloud Hypervisor supports guests with one or more PCI segments. The default PCI segment always
|
||||
has affinity to NUMA node 0. Be default, all other PCI segments have afffinity to NUMA node 0.
|
||||
The user may configure the NUMA affinity for any additional PCI segments.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--platform num_pci_segments=2
|
||||
--memory-zone size=16G,host_numa_node=0,id=mem0
|
||||
--memory-zone size=16G,host_numa_node=1,id=mem1
|
||||
--numa guest_numa_id=0,memory_zones=mem0,pci_segments=[0]
|
||||
--numa guest_numa_id=1,memory_zones=mem1,pci_segments=[1]
|
||||
```
|
||||
|
||||
48
docs/mshv.md
Normal file
48
docs/mshv.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Microsoft Hypervisor
|
||||
|
||||
The Microsoft Hypervisor is a Type 1 hypervisor which runs on x64 and ARM64 architectures. As the foundation of the Hyper-V virtualization stack, it runs millions of Linux and Windows guests in Azure and on-premises deployments. It supports nested virtualization, and security features like AMD's SEV-SNP. It also supports various features in Windows such as [Device guard and confidential guard](https://techcommunity.microsoft.com/t5/iis-support-blog/windows-10-device-guard-and-credential-guard-demystified/ba-p/376419), and [WSL2](https://docs.microsoft.com/en-us/windows/wsl/wsl2-faq)
|
||||
|
||||
Since 2020, Microsoft has been releasing open-source components to support Linux running as root partition on the Microsoft Hypervisor.
|
||||
|
||||
1. Kernel patches to support Linux booting as root partition
|
||||
2. A Linux kernel driver exposing an IOCTL interface for managing guest partitions, via a device node - /dev/mshv
|
||||
3. Rust bindings and IOCTL wrappers
|
||||
4. IGVM related crates
|
||||
|
||||
## Components
|
||||
|
||||
The following components are related to MSHV support with Cloud-Hypervisor:
|
||||
|
||||
* [igvm-crates](https://github.com/microsoft/igvm) : Parsing IGVM file
|
||||
|
||||
* [mshv-crates](https://github.com/rust-vmm/mshv) : Rust crates to interact with kernel module (/dev/mshv)
|
||||
|
||||
* [igvm-tooling](https://github.com/microsoft/igvm-tooling) : Tool to generate IGVM file
|
||||
|
||||
## IGVM
|
||||
|
||||
Independent Guest Virtual Machine (IGVM) file format.The format specification can be found in the igvm_defs crate, with a Rust implementation of the binary format in the igvm crate.
|
||||
|
||||
The IGVM file format is designed to encapsulate all information required to launch a virtual machine on any given virtualization stack, with support for different isolation technologies such as AMD SEV-SNP and Intel TDX.
|
||||
|
||||
At a conceptual level, this file format is a set of commands created by the tool that generated the file, used by the loader to construct the initial guest state. The file format also contains measurement information that the underlying platform will use to confirm that the file was loaded correctly and signed by the appropriate authorities.
|
||||
|
||||
Cloud Hypervisor can be built using igvm feature flag along with mshv and/or sev-snp. IGVM only works with MSHV.
|
||||
|
||||
## SEV-SNP
|
||||
|
||||
AMD's [Secure Encrypted Virtualization (SEV)](https://www.amd.com/en/developer/sev.html) and extensions such as Secure Nested Paging (SEV-SNP) encrypt memory and restrict access to a guest VM's memory and registers, securing it against a compromised hypervisor or VMM. They utilize the Platform Security Processor (PSP) to store keys and encrypt/decrypt the data. Microsoft has been continuously adding/improving support for SEV-SNP on Microsoft Hyper-V. Cloud-Hypervisor can be built with the sev_snp feature including mshv and igvm feature.
|
||||
|
||||
## Use Cases
|
||||
|
||||
Cloud Hypervisor can be built to run on an MSHV root partition by enabling the mshv feature, e.g.:
|
||||
|
||||
```cargo build --locked --all --all-targets --no-default-features --tests --examples --features mshv```
|
||||
|
||||
Cloud Hypervisor on MSHV can boot Linux guests using an IGVM file. IGVM feature depends on mshv for running legacy VMs.e.g.:
|
||||
|
||||
```cargo build --locked --all --all-targets --no-default-features --tests --examples --features igvm```
|
||||
|
||||
For running confidential VMs on mshv, you will only need to enable sev_snp, it requires and enables mshv and igvm automatically, eg.:
|
||||
|
||||
```cargo build --locked --all --all-targets --no-default-features --tests --examples --features sev_snp```
|
||||
@@ -25,7 +25,7 @@ $ perf record -g target/profiling/cloud-hypervisor \
|
||||
--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:
|
||||
@@ -52,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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Tracing
|
||||
|
||||
Cloud Hypervisor has a basic tracing infrastucture, particularly focussed on
|
||||
Cloud Hypervisor has a basic tracing infrastructure, particularly focussed on
|
||||
the tracing of the initial VM setup.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -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 --disk vhost_user=true,socket=/var/tmp/vhost.1,num_queues=4,queue_size=128 \
|
||||
--disk path=images/focal-server-cloudimg-amd64.raw vhost_user=true,socket=/var/tmp/vhost.1,num_queues=4,queue_size=128 \
|
||||
--console off \
|
||||
--serial tty \
|
||||
--rng
|
||||
|
||||
@@ -50,14 +50,14 @@ The examples use __socat__ `>=1.7.4` to illustrate the VSOCK functionality. Howe
|
||||
|
||||
### Connecting from Host to Guest
|
||||
|
||||
The host starts to listen on the defined port:
|
||||
The guest starts to listen on the defined port:
|
||||
|
||||
`$ socat - VSOCK-LISTEN:1234`
|
||||
|
||||
Once the host is listening, the guest can send data:
|
||||
Once the guest is listening, the host can send data:
|
||||
|
||||
`echo -e "CONNECT 1234\\nHello from host!" | socat - UNIX-CONNECT:/tmp/ch.vsock
|
||||
|
||||
|
||||
Note the string `CONNECT <port>` prepended to the actual data. It is possible for the guest to start listening on different ports, thus the specific command is needed to instruct VSOCK to which listener the host wants to connect. It needs to be sent once per connection. Once the connection established, data transfers can take place directly.
|
||||
|
||||
### Connecting from Guest to Host
|
||||
|
||||
@@ -49,7 +49,7 @@ qemu-system-x86_64 \
|
||||
-m 4G \
|
||||
-bios ./$OVMF_DIR/OVMF_CODE.fd \
|
||||
-cdrom ./$WIN_ISO_FILE \
|
||||
-drive file=./$VIRTIO_ISO_FILE,index=0,media=cdrom
|
||||
-drive file=./$VIRTIO_ISO_FILE,index=0,media=cdrom \
|
||||
-drive if=none,id=root,file=./$IMG_FILE \
|
||||
-device virtio-blk-pci,drive=root,disable-legacy=on \
|
||||
-device virtio-net-pci,netdev=mynet0,disable-legacy=on \
|
||||
@@ -135,7 +135,7 @@ This section illustrates the Windows specific aspects of the VM network configur
|
||||
|
||||
### Basic Networking
|
||||
|
||||
As the simplest option, using `--net tap=` in the Cloud Hypervisor command line will create a `vmtapX` device on the host with the default IPv4 adress `192.168.249.1`. After SAC becomes available, the guest configuration can be set with
|
||||
As the simplest option, using `--net tap=` in the Cloud Hypervisor command line will create a `vmtapX` device on the host with the default IPv4 address `192.168.249.1`. After SAC becomes available, the guest configuration can be set with
|
||||
|
||||
<pre>
|
||||
SAC>i 10 192.168.249.2 255.255.255.0 192.168.249.1
|
||||
@@ -203,13 +203,13 @@ The Windows guest debugging process relies heavily on QEMU and [socat](http://ww
|
||||
|
||||
The connection between both guests happens over TCP, whereby on the guest side it is automatically translated to a COM port. Because the VMs are connected through TCP, the debugging infrastructure can be distributed over the network. The serial port, while slowly transferring data, is common enough to support a wide range of cases and tools.
|
||||
|
||||
In this excercise, [WinDbg](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/) is used. Any other debugger of choice with the ability to use serial connection can be used instead.
|
||||
In this exercise, [WinDbg](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/) is used. Any other debugger of choice with the ability to use serial connection can be used instead.
|
||||
|
||||
### Debugger and Debuggee
|
||||
|
||||
#### WinDbg VM
|
||||
|
||||
For simplicity, the debugger VM is supposed to be only running under QEMU. It will require VGA and doesn't neccessarily depend on UEFI. As an OS, it can carry any supported Windows OS where the debugger of choice can be installed. The simplest way is to follow the image preparation instructions from the previous chapter, but avoid using the OVMF firmware. It is also not required to use VirtIO drivers, whereby it might be useful in some case. Though, while creating the image file for the debugger VM, be sure to choose a sufficient disk size that counts in the need to save the corresponding debug symbols and sources.
|
||||
For simplicity, the debugger VM is supposed to be only running under QEMU. It will require VGA and doesn't necessarily depend on UEFI. As an OS, it can carry any supported Windows OS where the debugger of choice can be installed. The simplest way is to follow the image preparation instructions from the previous chapter, but avoid using the OVMF firmware. It is also not required to use VirtIO drivers, whereby it might be useful in some case. Though, while creating the image file for the debugger VM, be sure to choose a sufficient disk size that counts in the need to save the corresponding debug symbols and sources.
|
||||
|
||||
To create the debugger Windows VM, the following command can be used:
|
||||
|
||||
@@ -244,7 +244,7 @@ This will configure the debugging to be enabled and instruct to use the serial p
|
||||
bcdedit /dbgsettings serial debugport:1 baudrate:115200
|
||||
```
|
||||
|
||||
##### Turn On Kernel Debuging
|
||||
##### Turn On Kernel Debugging
|
||||
|
||||
```cmd
|
||||
bcdedit /debug on
|
||||
|
||||
@@ -6,6 +6,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
flume = "0.10.14"
|
||||
libc = "0.2.139"
|
||||
serde = { version = "1.0.164", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.96"
|
||||
libc = "0.2.147"
|
||||
once_cell = "1.18.0"
|
||||
serde = { version = "1.0.168", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.107"
|
||||
|
||||
@@ -3,15 +3,17 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
static mut MONITOR: Option<MonitorHandle> = None;
|
||||
static MONITOR: OnceCell<MonitorHandle> = OnceCell::new();
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Event<'a> {
|
||||
@@ -23,7 +25,24 @@ struct Event<'a> {
|
||||
|
||||
pub struct Monitor {
|
||||
pub rx: flume::Receiver<String>,
|
||||
pub file: File,
|
||||
pub file: Option<File>,
|
||||
pub broadcast: Vec<flume::Sender<Arc<String>>>,
|
||||
}
|
||||
|
||||
impl Monitor {
|
||||
pub fn new(rx: flume::Receiver<String>, file: Option<File>) -> Self {
|
||||
Self {
|
||||
rx,
|
||||
file,
|
||||
broadcast: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subscribe(&mut self) -> flume::Receiver<Arc<String>> {
|
||||
let (tx, rx) = flume::unbounded();
|
||||
self.broadcast.push(tx);
|
||||
rx
|
||||
}
|
||||
}
|
||||
|
||||
struct MonitorHandle {
|
||||
@@ -50,32 +69,32 @@ fn set_file_nonblocking(file: &File) -> io::Result<()> {
|
||||
|
||||
/// This function must only be called once from the main thread before any threads
|
||||
/// are created to avoid race conditions.
|
||||
pub fn set_monitor(file: File) -> io::Result<Monitor> {
|
||||
// SAFETY: there is only one caller of this function, so MONITOR is written to only once
|
||||
assert!(unsafe { MONITOR.is_none() });
|
||||
pub fn set_monitor(file: Option<File>) -> io::Result<Monitor> {
|
||||
// There is only one caller of this function, so MONITOR is written to only once
|
||||
assert!(MONITOR.get().is_none());
|
||||
|
||||
if let Some(ref file) = file {
|
||||
set_file_nonblocking(file)?;
|
||||
}
|
||||
|
||||
set_file_nonblocking(&file)?;
|
||||
let (tx, rx) = flume::unbounded();
|
||||
let monitor = Monitor { rx, file };
|
||||
let monitor = Monitor::new(rx, file);
|
||||
|
||||
// SAFETY: MONITOR is None. Nobody else can hold a reference to it.
|
||||
unsafe {
|
||||
MONITOR = Some(MonitorHandle {
|
||||
tx,
|
||||
start: Instant::now(),
|
||||
});
|
||||
};
|
||||
MONITOR.get_or_init(|| MonitorHandle {
|
||||
tx,
|
||||
start: Instant::now(),
|
||||
});
|
||||
|
||||
Ok(monitor)
|
||||
}
|
||||
|
||||
pub fn event_log(source: &str, event: &str, properties: Option<&HashMap<Cow<str>, Cow<str>>>) {
|
||||
// SAFETY: `MONITOR` is always in a valid state (None or Some), because it
|
||||
// is set only once before any threads are spawned, and it's not mutated
|
||||
// `MONITOR` is always in a valid state (None or Some), because it is set
|
||||
// only once before any threads are spawned, and it's not mutated
|
||||
// afterwards. This function only creates immutable references to `MONITOR`.
|
||||
// Because `MONITOR.tx` is `Sync`, it's safe to share `MONITOR` across
|
||||
// threads, making this function thread-safe.
|
||||
if let Some(monitor_handle) = unsafe { MONITOR.as_ref() } {
|
||||
if let Some(monitor_handle) = MONITOR.get().as_ref() {
|
||||
let event = Event {
|
||||
timestamp: monitor_handle.start.elapsed(),
|
||||
source,
|
||||
|
||||
416
fuzz/Cargo.lock
generated
416
fuzz/Cargo.lock
generated
@@ -5,16 +5,64 @@ version = 3
|
||||
[[package]]
|
||||
name = "acpi_tables"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#05a609136387cc1cc9b499cee4320020325c263f"
|
||||
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#1a733bf690ccc10bdfeacad33e3c9f6cce0008fd"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.72"
|
||||
name = "anstream"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854"
|
||||
checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -25,9 +73,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.3.0"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e"
|
||||
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
@@ -57,34 +105,6 @@ 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 = "autocfg"
|
||||
version = "1.1.0"
|
||||
@@ -108,9 +128,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.3.3"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
|
||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||
|
||||
[[package]]
|
||||
name = "block"
|
||||
@@ -136,21 +156,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.13.0"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
|
||||
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.82"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@@ -162,13 +182,40 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "33.0.0"
|
||||
version = "36.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
"argh",
|
||||
"clap",
|
||||
"epoll",
|
||||
"event_monitor",
|
||||
"hypervisor",
|
||||
@@ -210,6 +257,12 @@ dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "crc32c"
|
||||
version = "0.6.4"
|
||||
@@ -246,7 +299,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.23",
|
||||
"syn 2.0.32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -257,7 +310,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.23",
|
||||
"syn 2.0.32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -267,7 +320,7 @@ dependencies = [
|
||||
"acpi_tables",
|
||||
"anyhow",
|
||||
"arch",
|
||||
"bitflags 2.3.3",
|
||||
"bitflags 2.4.1",
|
||||
"byteorder",
|
||||
"event_monitor",
|
||||
"hypervisor",
|
||||
@@ -291,7 +344,7 @@ version = "4.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79"
|
||||
dependencies = [
|
||||
"bitflags 2.3.3",
|
||||
"bitflags 2.4.1",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -301,6 +354,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"flume",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
@@ -332,21 +386,21 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.28"
|
||||
version = "0.3.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
||||
checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.28"
|
||||
version = "0.3.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
|
||||
checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.10"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
||||
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
@@ -381,9 +435,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.6.0"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b7b36074613a723279637061b40db993208908a94f10ccb14436ce735bc0f57"
|
||||
checksum = "460648e47a07a43110fbfa2e0b14afb2be920093c31e5dccc50e49568e099762"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
@@ -397,18 +451,18 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.26"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2"
|
||||
checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.64"
|
||||
version = "0.3.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
|
||||
checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
@@ -436,15 +490,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.147"
|
||||
version = "0.2.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.4.6"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "beb09950ae85a0a94b27676cccf37da5ff13f27076aa1adbc6545dd0d0e1bd4e"
|
||||
checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"cc",
|
||||
@@ -453,18 +507,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-loader"
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d3adb7b28e189741eca3b1a4a27de0bf15e0907c9d4b0c74bd2d7d84ef72e08"
|
||||
checksum = "132a531b85b3a164012ab682c72f8f2cce7757f187be5f60782fd2b4cda9cb34"
|
||||
dependencies = [
|
||||
"vm-memory",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.10"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
|
||||
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
@@ -472,14 +526,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.19"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#b538bf89e50be83b6fa9ab1896727ff61e02fa13"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#a4d632f2c5ea45712c0d2002dc909a63879e85c3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"vmm-sys-util",
|
||||
@@ -524,9 +578,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.18.0"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "option_parser"
|
||||
@@ -572,23 +626,23 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.23",
|
||||
"syn 2.0.32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.66"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
|
||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.32"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -610,7 +664,7 @@ checksum = "bce3a7139d2ee67d07538ee5dba997364fbc243e7e7143e96eb830c74bfaa082"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.23",
|
||||
"syn 2.0.32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -624,9 +678,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.15"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
@@ -636,44 +690,44 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "seccompiler"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f6575e3c2b3a0fe2ef3e53855b6a8dead7c29f783da5e123d378c8c6a89017e"
|
||||
checksum = "345a3e4dddf721a478089d4697b83c6c0a8f5bf16086f6c13397e4534eb6e2e5"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.18"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
|
||||
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.168"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d614f89548720367ded108b3c843be93f3a341e22d5674ca0dd5cd57f34926af"
|
||||
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.168"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fe589678c688e44177da4f27152ee2d190757271dc7f1d5b6b9f68d869d641"
|
||||
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.23",
|
||||
"syn 2.0.32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.104"
|
||||
version = "1.0.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
|
||||
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -682,9 +736,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "3.2.0"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1402f54f9a3b9e2efe71c1cea24e648acce55887983553eeb858cf3115acfd49"
|
||||
checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_with_macros",
|
||||
@@ -692,14 +746,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "3.2.0"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9197f1ad0e3c173a0222d3c4404fb04c3afe87e962bcb327af73e8301fa203c7"
|
||||
checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.23",
|
||||
"syn 2.0.32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -727,9 +781,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.11.0"
|
||||
version = "1.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
|
||||
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
@@ -759,9 +813,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.23"
|
||||
version = "2.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737"
|
||||
checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -770,22 +824,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.44"
|
||||
version = "1.0.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90"
|
||||
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.44"
|
||||
version = "1.0.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
|
||||
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.23",
|
||||
"syn 2.0.32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -814,15 +868,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.11"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.4.1"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
|
||||
checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
@@ -857,7 +917,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio-bindings"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#89f8e77dd1a2829197ecde65b686bafcc8a1def4"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#59c604fa6e42080f0a47c124ba29454fe4cb7475"
|
||||
dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
@@ -865,7 +925,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio-ioctls"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#89f8e77dd1a2829197ecde65b686bafcc8a1def4"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#59c604fa6e42080f0a47c124ba29454fe4cb7475"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
@@ -881,7 +941,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio_user"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio-user?branch=main#eef6bec4d421f08ed1688fe67c5ea33aabbf5069"
|
||||
source = "git+https://github.com/rust-vmm/vfio-user?branch=main#6c72e997e61d9e84b8ee691ad63ece6c717cf5aa"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
@@ -897,11 +957,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vhost"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84f81f436bca4541f4d33172e1202882c9d437db34ed17fc6d84c8ff2bde21f5"
|
||||
checksum = "289adfce099c71f8310f895932ccd978f352ca494ea47496dbe20d4241888b82"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"bitflags 2.4.1",
|
||||
"libc",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
@@ -909,9 +969,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-bindings"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c18d7b74098a946470ea265b5bacbbf877abc3373021388454de0d47735a5b98"
|
||||
checksum = "878bcb1b2812a10c30d53b0ed054999de3d98f25ece91fc173973f9c57aaae86"
|
||||
|
||||
[[package]]
|
||||
name = "virtio-devices"
|
||||
@@ -949,9 +1009,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.8.0"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91aebb1df33db33cbf04d4c2445e4f78d0b0c8e65acfd16a4ee95ef63ca252f8"
|
||||
checksum = "73a01db2cfb6c4b9bc20608b1336263d16714ea8db05de9fec2a254e076f9385"
|
||||
dependencies = [
|
||||
"log",
|
||||
"virtio-bindings",
|
||||
@@ -988,12 +1048,13 @@ source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#c5a99ab71b130435927
|
||||
|
||||
[[package]]
|
||||
name = "vm-memory"
|
||||
version = "0.11.0"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d6ea57fe00f9086c59eeeb68e102dd611686bc3c28520fa465996d4d4bdce07"
|
||||
checksum = "5376c9ee5ebe2103a310d8241936cfb93c946734b0479a4fa5bdf7a64abbacd8"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"libc",
|
||||
"thiserror",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@@ -1027,11 +1088,14 @@ dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"arch",
|
||||
"bitflags 2.3.3",
|
||||
"bitflags 2.4.1",
|
||||
"block",
|
||||
"cfg-if",
|
||||
"clap",
|
||||
"devices",
|
||||
"epoll",
|
||||
"event_monitor",
|
||||
"flume",
|
||||
"hypervisor",
|
||||
"libc",
|
||||
"linux-loader",
|
||||
@@ -1066,9 +1130,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vmm-sys-util"
|
||||
version = "0.11.1"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd64fe09d8e880e600c324e7d664760a17f56e9672b7495a86381b49e4f72f46"
|
||||
checksum = "48b7b084231214f7427041e4220d77dfe726897a6d41fddee450696e66ff2a29"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
@@ -1084,9 +1148,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.87"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
|
||||
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -1094,24 +1158,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.87"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
|
||||
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.23",
|
||||
"syn 2.0.32",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.87"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
|
||||
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -1119,22 +1183,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.87"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
|
||||
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.23",
|
||||
"syn 2.0.32",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.87"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
|
||||
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
@@ -1159,10 +1223,76 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.6.3"
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3b9c234616391070b0b173963ebc65a9195068e7ed3731c6edac2ec45ebe106"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d075cf85bbb114e933343e087b92f2146bac0d55b534cbb8188becf0039948e"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
@@ -1170,11 +1300,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.6.3"
|
||||
version = "0.7.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f7f3a471f98d0a61c34322fbbfd10c384b07687f680d4119813713f72308d91"
|
||||
checksum = "86cd5ca076997b97ef09d3ad65efe811fa68c9e874cb636ccb211223a813b0c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.23",
|
||||
"syn 2.0.32",
|
||||
]
|
||||
|
||||
@@ -8,22 +8,25 @@ edition = "2021"
|
||||
[package.metadata]
|
||||
cargo-fuzz = true
|
||||
|
||||
[features]
|
||||
igvm = []
|
||||
|
||||
[dependencies]
|
||||
block = { path = "../block" }
|
||||
devices = { path = "../devices" }
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.147"
|
||||
libfuzzer-sys = "0.4.6"
|
||||
linux-loader = { version = "0.9.0", features = ["elf", "bzimage", "pe"] }
|
||||
libc = "0.2.150"
|
||||
libfuzzer-sys = "0.4.7"
|
||||
linux-loader = { version = "0.10.0", features = ["elf", "bzimage", "pe"] }
|
||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||
net_util = { path = "../net_util" }
|
||||
once_cell = "1.18.0"
|
||||
seccompiler = "0.3.0"
|
||||
once_cell = "1.19.0"
|
||||
seccompiler = "0.4.0"
|
||||
virtio-devices = { path = "../virtio-devices" }
|
||||
virtio-queue = "0.8.0"
|
||||
virtio-queue = "0.10.0"
|
||||
vmm = { path = "../vmm" }
|
||||
vmm-sys-util = "0.11.1"
|
||||
vm-memory = "0.11.0"
|
||||
vmm-sys-util = "0.11.2"
|
||||
vm-memory = "0.13.1"
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ const QUEUE_NUM: usize = 3;
|
||||
const QUEUE_SIZE: u16 = 64;
|
||||
// Descriptor table alignment
|
||||
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||
// Avalable ring alignment
|
||||
// Available ring alignment
|
||||
const AVAIL_RING_ALIGN_SIZE: u64 = 2;
|
||||
// Used ring alignment
|
||||
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||
|
||||
@@ -57,6 +57,7 @@ fuzz_target!(|bytes| {
|
||||
false,
|
||||
2,
|
||||
256,
|
||||
None,
|
||||
SeccompAction::Allow,
|
||||
None,
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
|
||||
@@ -30,7 +30,7 @@ const IOVA_SPACE_SIZE: usize = (0xfeef_ffff - 0xfee0_0000) + 1;
|
||||
const QUEUE_SIZE: u16 = 256;
|
||||
// Descriptor table alignment
|
||||
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||
// Avalable ring alignment
|
||||
// Available ring alignment
|
||||
const AVAIL_RING_ALIGN_SIZE: u64 = 2;
|
||||
// Used ring alignment
|
||||
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||
|
||||
@@ -23,6 +23,8 @@ fuzz_target!(|bytes| {
|
||||
kernel: None,
|
||||
cmdline: Some(String::from_utf8_lossy(&bytes).to_string()),
|
||||
initramfs: None,
|
||||
#[cfg(feature = "igvm")]
|
||||
igvm: None,
|
||||
};
|
||||
let kernel_cmdline = match vmm::vm::Vm::generate_cmdline(&payload_config) {
|
||||
Ok(cmdline) => cmdline,
|
||||
|
||||
@@ -33,7 +33,7 @@ const VIRTIO_MEM_REGION_ADDRESS: u64 = 0;
|
||||
const QUEUE_SIZE: u16 = 64;
|
||||
// Descriptor table alignment
|
||||
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||
// Avalable ring alignment
|
||||
// Available ring alignment
|
||||
const AVAIL_RING_ALIGN_SIZE: u64 = 2;
|
||||
// Used ring alignment
|
||||
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||
|
||||
@@ -28,7 +28,7 @@ const MEM_SIZE: usize = 1 * 1024 * 1024;
|
||||
const QUEUE_SIZE: u16 = 256;
|
||||
// Descriptor table alignment
|
||||
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||
// Avalable ring alignment
|
||||
// Available ring alignment
|
||||
const AVAIL_RING_ALIGN_SIZE: u64 = 2;
|
||||
// Used ring alignment
|
||||
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||
|
||||
@@ -8,27 +8,30 @@ license = "Apache-2.0 OR BSD-3-Clause"
|
||||
[features]
|
||||
kvm = ["kvm-ioctls", "kvm-bindings", "vfio-ioctls/kvm"]
|
||||
mshv = ["mshv-ioctls", "mshv-bindings", "vfio-ioctls/mshv", "iced-x86"]
|
||||
sev_snp = ["igvm_parser", "igvm_defs"]
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.71"
|
||||
anyhow = "1.0.75"
|
||||
byteorder = "1.4.3"
|
||||
thiserror = "1.0.40"
|
||||
libc = "0.2.139"
|
||||
log = "0.4.17"
|
||||
igvm_defs = { git = "https://github.com/microsoft/igvm", branch = "main", package = "igvm_defs", optional = true }
|
||||
igvm_parser = { git = "https://github.com/microsoft/igvm", branch = "main", package = "igvm", optional = true }
|
||||
libc = "0.2.147"
|
||||
log = "0.4.20"
|
||||
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.164", features = ["rc", "derive"] }
|
||||
serde_with = { version = "3.0.0", default-features = false, features = ["macros"] }
|
||||
serde = { version = "1.0.168", features = ["rc", "derive"] }
|
||||
serde_with = { version = "3.4.0", default-features = false, features = ["macros"] }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vm-memory = { version = "0.13.1", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }
|
||||
thiserror = "1.0.40"
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
||||
optional = true
|
||||
version = "1.19.0"
|
||||
version = "1.20.0"
|
||||
default-features = false
|
||||
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::arch::x86::Exception;
|
||||
// CMP affects OF, SF, ZF, AF, PF and CF
|
||||
const FLAGS_MASK: u64 = CF | PF | AF | ZF | SF | OF;
|
||||
|
||||
// TODO: Switch to inline asm when that's stable. Executing CMP (or any arthimetic instructions)
|
||||
// TODO: Switch to inline asm when that's stable. Executing CMP (or any arithmetic instructions)
|
||||
// natively and extracting RFLAGS will be much faster and make the code simpler.
|
||||
fn calc_rflags_cpazso(op0: u64, op1: u64, op_size: usize) -> u64 {
|
||||
let op_bits = op_size * 8;
|
||||
|
||||
@@ -18,8 +18,8 @@ fn get_op<T: CpuStateManager>(
|
||||
insn: &Instruction,
|
||||
op_index: u32,
|
||||
op_size: usize,
|
||||
state: &mut T,
|
||||
platform: &mut dyn PlatformEmulator<CpuState = T>,
|
||||
state: &T,
|
||||
platform: &dyn PlatformEmulator<CpuState = T>,
|
||||
) -> Result<u64, PlatformError> {
|
||||
if insn.op_count() < op_index + 1 {
|
||||
return Err(PlatformError::InvalidOperand(anyhow!(
|
||||
@@ -124,7 +124,7 @@ fn memory_operand_address<T: CpuStateManager>(
|
||||
address += index;
|
||||
}
|
||||
|
||||
address += insn.memory_displacement64();
|
||||
address = address.wrapping_add(insn.memory_displacement64());
|
||||
|
||||
// Translate to a linear address.
|
||||
state.linearize(insn.memory_segment(), address, write)
|
||||
|
||||
@@ -95,12 +95,12 @@ pub enum HypervisorCpuError {
|
||||
#[error("Failed to get Msr entries: {0}")]
|
||||
GetMsrEntries(#[source] anyhow::Error),
|
||||
///
|
||||
/// Setting MSR entries error
|
||||
/// Setting multi-processing state error
|
||||
///
|
||||
#[error("Failed to set MP state: {0}")]
|
||||
SetMpState(#[source] anyhow::Error),
|
||||
///
|
||||
/// Getting Msr entries error
|
||||
/// Getting multi-processing state error
|
||||
///
|
||||
#[error("Failed to get MP state: {0}")]
|
||||
GetMpState(#[source] anyhow::Error),
|
||||
@@ -242,7 +242,7 @@ pub enum HypervisorCpuError {
|
||||
UnknownTdxVmCall,
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
///
|
||||
/// Failed to intialize PMU
|
||||
/// Failed to initialize PMU
|
||||
///
|
||||
#[error("Failed to initialize PMU")]
|
||||
InitializePmu,
|
||||
@@ -252,6 +252,26 @@ pub enum HypervisorCpuError {
|
||||
///
|
||||
#[error("Failed to get TSC frequency: {0}")]
|
||||
GetTscKhz(#[source] anyhow::Error),
|
||||
///
|
||||
/// Error setting TSC frequency
|
||||
///
|
||||
#[error("Failed to set TSC frequency: {0}")]
|
||||
SetTscKhz(#[source] anyhow::Error),
|
||||
///
|
||||
/// Error reading value at given GPA
|
||||
///
|
||||
#[error("Failed to read from GPA: {0}")]
|
||||
GpaRead(#[source] anyhow::Error),
|
||||
///
|
||||
/// Error writing value at given GPA
|
||||
///
|
||||
#[error("Failed to write to GPA: {0}")]
|
||||
GpaWrite(#[source] anyhow::Error),
|
||||
///
|
||||
/// Error getting CPUID leaf
|
||||
///
|
||||
#[error("Failed to get CPUID entries: {0}")]
|
||||
GetCpuidVales(#[source] anyhow::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -447,6 +467,7 @@ 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
|
||||
@@ -454,4 +475,24 @@ pub trait Vcpu: Send + Sync {
|
||||
fn tsc_khz(&self) -> Result<Option<u32>> {
|
||||
Ok(None)
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Set the frequency of the TSC if available
|
||||
///
|
||||
fn set_tsc_khz(&self, _freq: u32) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call to retrieve cpuid leaf
|
||||
///
|
||||
fn get_cpuid_values(
|
||||
&self,
|
||||
_function: u32,
|
||||
_index: u32,
|
||||
_xfem: u64,
|
||||
_xss: u64,
|
||||
) -> Result<[u32; 4]> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,11 @@ pub enum HypervisorError {
|
||||
///
|
||||
#[error("Unsupported CPU:{0}")]
|
||||
UnsupportedCpu(#[source] anyhow::Error),
|
||||
///
|
||||
/// Launching a VM with unsupported VM Type
|
||||
///
|
||||
#[error("Unsupported VmType")]
|
||||
UnsupportedVmType(),
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
@@ -356,6 +356,7 @@ impl vm::Vm for KvmVm {
|
||||
.set_identity_map_address(address)
|
||||
.map_err(|e| vm::HypervisorVmError::SetIdentityMapAddress(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the address of the three-page region in the VM's address space.
|
||||
@@ -365,6 +366,7 @@ impl vm::Vm for KvmVm {
|
||||
.set_tss_address(offset)
|
||||
.map_err(|e| vm::HypervisorVmError::SetTssAddress(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Creates an in-kernel interrupt controller.
|
||||
///
|
||||
@@ -373,6 +375,7 @@ impl vm::Vm for KvmVm {
|
||||
.create_irq_chip()
|
||||
.map_err(|e| vm::HypervisorVmError::CreateIrq(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Registers an event that will, when signaled, trigger the `gsi` IRQ.
|
||||
///
|
||||
@@ -381,6 +384,7 @@ impl vm::Vm for KvmVm {
|
||||
.register_irqfd(fd, gsi)
|
||||
.map_err(|e| vm::HypervisorVmError::RegisterIrqFd(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Unregisters an event that will, when signaled, trigger the `gsi` IRQ.
|
||||
///
|
||||
@@ -389,6 +393,7 @@ impl vm::Vm for KvmVm {
|
||||
.unregister_irqfd(fd, gsi)
|
||||
.map_err(|e| vm::HypervisorVmError::UnregisterIrqFd(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Creates a VcpuFd object from a vcpu RawFd.
|
||||
///
|
||||
@@ -411,6 +416,7 @@ impl vm::Vm for KvmVm {
|
||||
};
|
||||
Ok(Arc::new(vcpu))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
///
|
||||
/// Creates a virtual GIC device.
|
||||
@@ -420,6 +426,7 @@ impl vm::Vm for KvmVm {
|
||||
.map_err(|e| vm::HypervisorVmError::CreateVgic(anyhow!("Vgic error {:?}", e)))?;
|
||||
Ok(Arc::new(Mutex::new(gic_device)))
|
||||
}
|
||||
|
||||
///
|
||||
/// Registers an event to be signaled whenever a certain address is written to.
|
||||
///
|
||||
@@ -447,6 +454,7 @@ impl vm::Vm for KvmVm {
|
||||
.map_err(|e| vm::HypervisorVmError::RegisterIoEvent(e.into()))
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Unregisters an event from a certain address it has been previously registered to.
|
||||
///
|
||||
@@ -541,6 +549,7 @@ impl vm::Vm for KvmVm {
|
||||
.set_gsi_routing(&irq_routing[0])
|
||||
.map_err(|e| vm::HypervisorVmError::SetGsiRouting(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Creates a memory region structure that can be used with {create/remove}_user_memory_region
|
||||
///
|
||||
@@ -567,6 +576,7 @@ impl vm::Vm for KvmVm {
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
///
|
||||
/// Creates a guest physical memory region.
|
||||
///
|
||||
@@ -603,6 +613,7 @@ impl vm::Vm for KvmVm {
|
||||
.map_err(|e| vm::HypervisorVmError::CreateUserMemory(e.into()))
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Removes a guest physical memory region.
|
||||
///
|
||||
@@ -621,6 +632,7 @@ impl vm::Vm for KvmVm {
|
||||
.map_err(|e| vm::HypervisorVmError::RemoveUserMemory(e.into()))
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the preferred CPU target type which can be emulated by KVM on underlying host.
|
||||
///
|
||||
@@ -630,6 +642,7 @@ impl vm::Vm for KvmVm {
|
||||
.get_preferred_target(kvi)
|
||||
.map_err(|e| vm::HypervisorVmError::GetPreferredTarget(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn enable_split_irq(&self) -> vm::Result<()> {
|
||||
// Create split irqchip
|
||||
@@ -645,6 +658,7 @@ impl vm::Vm for KvmVm {
|
||||
.map_err(|e| vm::HypervisorVmError::EnableSplitIrq(e.into()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn enable_sgx_attribute(&self, file: File) -> vm::Result<()> {
|
||||
let mut cap = kvm_enable_cap {
|
||||
@@ -657,6 +671,7 @@ impl vm::Vm for KvmVm {
|
||||
.map_err(|e| vm::HypervisorVmError::EnableSgxAttribute(e.into()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Retrieve guest clock.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn get_clock(&self) -> vm::Result<ClockData> {
|
||||
@@ -666,6 +681,7 @@ impl vm::Vm for KvmVm {
|
||||
.map_err(|e| vm::HypervisorVmError::GetClock(e.into()))?
|
||||
.into())
|
||||
}
|
||||
|
||||
/// Set guest clock.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn set_clock(&self, data: &ClockData) -> vm::Result<()> {
|
||||
@@ -674,6 +690,7 @@ impl vm::Vm for KvmVm {
|
||||
.set_clock(&data)
|
||||
.map_err(|e| vm::HypervisorVmError::SetClock(e.into()))
|
||||
}
|
||||
|
||||
/// Create a device that is used for passthrough
|
||||
fn create_passthrough_device(&self) -> vm::Result<VfioDeviceFd> {
|
||||
let mut vfio_dev = kvm_create_device {
|
||||
@@ -685,6 +702,7 @@ impl vm::Vm for KvmVm {
|
||||
self.create_device(&mut vfio_dev)
|
||||
.map_err(|e| vm::HypervisorVmError::CreatePassthroughDevice(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Start logging dirty pages
|
||||
///
|
||||
@@ -826,6 +844,7 @@ impl vm::Vm for KvmVm {
|
||||
)
|
||||
.map_err(vm::HypervisorVmError::InitMemRegionTdx)
|
||||
}
|
||||
|
||||
/// Downcast to the underlying KvmVm type
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
@@ -892,7 +911,9 @@ pub enum KvmError {
|
||||
#[error("Capability missing: {0:?}")]
|
||||
CapabilityMissing(Cap),
|
||||
}
|
||||
|
||||
pub type KvmResult<T> = result::Result<T, KvmError>;
|
||||
|
||||
impl KvmHypervisor {
|
||||
/// Create a hypervisor based on Kvm
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
@@ -906,6 +927,7 @@ impl KvmHypervisor {
|
||||
|
||||
Ok(Arc::new(KvmHypervisor { kvm: kvm_obj }))
|
||||
}
|
||||
|
||||
/// Check if the hypervisor is available
|
||||
pub fn is_available() -> hypervisor::Result<bool> {
|
||||
match std::fs::metadata("/dev/kvm") {
|
||||
@@ -917,6 +939,7 @@ impl KvmHypervisor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of Hypervisor trait for KVM
|
||||
///
|
||||
/// # Examples
|
||||
@@ -935,6 +958,7 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
||||
fn hypervisor_type(&self) -> HypervisorType {
|
||||
HypervisorType::Kvm
|
||||
}
|
||||
|
||||
/// Create a KVM vm object of a specific VM type and return the object as Vm trait object
|
||||
///
|
||||
/// # Examples
|
||||
@@ -1090,6 +1114,7 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
||||
self.kvm.get_max_vcpus().min(u32::MAX as usize) as u32
|
||||
}
|
||||
}
|
||||
|
||||
/// Vcpu struct for KVM
|
||||
pub struct KvmVcpu {
|
||||
fd: VcpuFd,
|
||||
@@ -1099,6 +1124,7 @@ pub struct KvmVcpu {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
hyperv_synic: AtomicBool,
|
||||
}
|
||||
|
||||
/// Implementation of Vcpu trait for KVM
|
||||
///
|
||||
/// # Examples
|
||||
@@ -1123,6 +1149,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetStandardRegs(e.into()))?
|
||||
.into())
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the vCPU general purpose registers.
|
||||
/// The `KVM_GET_REGS` ioctl is not available on AArch64, `KVM_GET_ONE_REG`
|
||||
@@ -1205,7 +1232,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
off += std::mem::size_of::<u64>();
|
||||
}
|
||||
|
||||
// Now moving on to floting point registers which are stored in the user_fpsimd_state in the kernel:
|
||||
// Now moving on to floating point registers which are stored in the user_fpsimd_state in the kernel:
|
||||
// https://elixir.free-electrons.com/linux/v4.9.62/source/arch/arm64/include/uapi/asm/kvm.h#L53
|
||||
let mut off = offset_of!(kvm_regs, fp_regs) + offset_of!(user_fpsimd_state, vregs);
|
||||
for i in 0..32 {
|
||||
@@ -1235,6 +1262,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.unwrap();
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the vCPU general purpose registers using the `KVM_SET_REGS` ioctl.
|
||||
@@ -1357,6 +1385,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetSpecialRegs(e.into()))?
|
||||
.into())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the vCPU special registers using the `KVM_SET_SREGS` ioctl.
|
||||
@@ -1367,6 +1396,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.set_sregs(&sregs)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetSpecialRegs(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Returns the floating point state (FPU) from the vCPU.
|
||||
@@ -1378,6 +1408,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetFloatingPointRegs(e.into()))?
|
||||
.into())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Set the floating point state (FPU) of a vCPU using the `KVM_SET_FPU` ioct.
|
||||
@@ -1388,6 +1419,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.set_fpu(&fpu)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetFloatingPointRegs(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call to setup the CPUID registers.
|
||||
@@ -1402,6 +1434,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.set_cpuid2(&kvm_cpuid)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCpuid(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call to enable HyperV SynIC
|
||||
@@ -1419,6 +1452,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.enable_cap(&cap)
|
||||
.map_err(|e| cpu::HypervisorCpuError::EnableHyperVSyncIc(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// X86 specific call to retrieve the CPUID registers.
|
||||
///
|
||||
@@ -1433,6 +1467,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Returns the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
|
||||
@@ -1444,6 +1479,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetlapicState(e.into()))?
|
||||
.into())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
|
||||
@@ -1454,6 +1490,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.set_lapic(&klapic)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetLapicState(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Returns the model-specific registers (MSR) for this vCPU.
|
||||
@@ -1475,6 +1512,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
|
||||
Ok(succ)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Setup the model-specific registers (MSR) for this vCPU.
|
||||
@@ -1487,6 +1525,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.set_msrs(&kvm_msrs)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetMsrEntries(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the vcpu's current "multiprocessing state".
|
||||
///
|
||||
@@ -1497,6 +1536,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetMpState(e.into()))?
|
||||
.into())
|
||||
}
|
||||
|
||||
///
|
||||
/// Sets the vcpu's current "multiprocessing state".
|
||||
///
|
||||
@@ -1505,6 +1545,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.set_mp_state(mp_state.into())
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetMpState(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Translates guest virtual address to guest physical address using the `KVM_TRANSLATE` ioctl.
|
||||
@@ -1523,6 +1564,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
_ => Ok((tr.physical_address, 0)),
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Triggers the running of the current virtual CPU returning an exit reason.
|
||||
///
|
||||
@@ -1614,6 +1656,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Let the guest know that it has been paused, which prevents from
|
||||
@@ -1631,6 +1674,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Sets debug registers to set hardware breakpoints and/or enable single step.
|
||||
///
|
||||
@@ -1684,12 +1728,14 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.set_guest_debug(&dbg)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetDebugRegs(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn vcpu_init(&self, kvi: &VcpuInit) -> cpu::Result<()> {
|
||||
self.fd
|
||||
.vcpu_init(kvi)
|
||||
.map_err(|e| cpu::HypervisorCpuError::VcpuInit(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Gets a list of the guest registers that are supported for the
|
||||
/// KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
|
||||
@@ -1700,6 +1746,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.get_reg_list(reg_list)
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetRegList(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Gets the value of a system register
|
||||
///
|
||||
@@ -1733,6 +1780,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.try_into()
|
||||
.unwrap())
|
||||
}
|
||||
|
||||
///
|
||||
/// Configure core registers for a given CPU.
|
||||
///
|
||||
@@ -1890,6 +1938,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
};
|
||||
|
||||
let vcpu_events = self.get_vcpu_events()?;
|
||||
let tsc_khz = self.tsc_khz()?;
|
||||
|
||||
Ok(VcpuKvmState {
|
||||
cpuid,
|
||||
@@ -1902,9 +1951,11 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
xsave,
|
||||
xcrs,
|
||||
mp_state,
|
||||
tsc_khz,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the current AArch64 CPU state
|
||||
///
|
||||
@@ -1954,6 +2005,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
|
||||
Ok(state.into())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Restore the previously saved CPU state
|
||||
@@ -2004,6 +2056,10 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
self.set_lapic(&state.lapic_state)?;
|
||||
self.set_fpu(&state.fpu)?;
|
||||
|
||||
if let Some(freq) = state.tsc_khz {
|
||||
self.set_tsc_khz(freq)?;
|
||||
}
|
||||
|
||||
// Try to set all MSRs previously stored.
|
||||
// If the number of MSRs set from SET_MSRS is different from the
|
||||
// expected amount, we fallback onto a slower method by setting MSRs
|
||||
@@ -2039,6 +2095,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Restore the previously saved AArch64 CPU state
|
||||
///
|
||||
@@ -2113,6 +2170,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
TdxExitStatus::InvalidOperand => TDG_VP_VMCALL_INVALID_OPERAND,
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Return the list of initial MSR entries for a VCPU
|
||||
@@ -2138,6 +2196,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
]
|
||||
.to_vec()
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn has_pmu_support(&self) -> bool {
|
||||
let cpu_attr = kvm_bindings::kvm_device_attr {
|
||||
@@ -2148,6 +2207,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
};
|
||||
self.fd.has_device_attr(&cpu_attr).is_ok()
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn init_pmu(&self, irq: u32) -> cpu::Result<()> {
|
||||
let cpu_attr = kvm_bindings::kvm_device_attr {
|
||||
@@ -2186,6 +2246,23 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
Ok(v) => Ok(Some(v)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Set the frequency of the TSC if available
|
||||
///
|
||||
fn set_tsc_khz(&self, freq: u32) -> cpu::Result<()> {
|
||||
match self.fd.set_tsc_khz(freq) {
|
||||
Err(e) => {
|
||||
if e.errno() == libc::EIO {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(cpu::HypervisorCpuError::SetTscKhz(e.into()))
|
||||
}
|
||||
}
|
||||
Ok(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KvmVcpu {
|
||||
@@ -2198,6 +2275,7 @@ impl KvmVcpu {
|
||||
.get_xsave()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetXsaveState(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call that sets the vcpu's current "xsave struct".
|
||||
@@ -2207,6 +2285,7 @@ impl KvmVcpu {
|
||||
.set_xsave(xsave)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetXsaveState(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call that returns the vcpu's current "xcrs".
|
||||
@@ -2216,6 +2295,7 @@ impl KvmVcpu {
|
||||
.get_xcrs()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetXcsr(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call that sets the vcpu's current "xcrs".
|
||||
@@ -2225,6 +2305,7 @@ impl KvmVcpu {
|
||||
.set_xcrs(xcrs)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetXcsr(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Returns currently pending exceptions, interrupts, and NMIs as well as related
|
||||
@@ -2235,6 +2316,7 @@ impl KvmVcpu {
|
||||
.get_vcpu_events()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetVcpuEvents(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets pending exceptions, interrupts, and NMIs as well as related states
|
||||
|
||||
@@ -67,6 +67,7 @@ pub struct VcpuKvmState {
|
||||
pub xsave: Xsave,
|
||||
pub xcrs: ExtendedControlRegisters,
|
||||
pub mp_state: MpState,
|
||||
pub tsc_khz: Option<u32>,
|
||||
}
|
||||
|
||||
impl From<StandardRegisters> for kvm_regs {
|
||||
|
||||
@@ -140,7 +140,7 @@ pub enum MpState {
|
||||
#[cfg(feature = "kvm")]
|
||||
Kvm(kvm_bindings::kvm_mp_state),
|
||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||
Mshv, /* MSHV does not supprt MpState yet */
|
||||
Mshv, /* MSHV does not support MpState yet */
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@@ -164,7 +164,7 @@ pub enum ClockData {
|
||||
#[cfg(feature = "kvm")]
|
||||
Kvm(kvm_bindings::kvm_clock_data),
|
||||
#[cfg(feature = "mshv")]
|
||||
Mshv, /* MSHV does not supprt ClockData yet */
|
||||
Mshv, /* MSHV does not support ClockData yet */
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
|
||||
@@ -14,19 +14,27 @@ use crate::vec_with_array_field;
|
||||
use crate::vm::{self, InterruptSourceConfig, VmOps};
|
||||
use crate::HypervisorType;
|
||||
pub use mshv_bindings::*;
|
||||
use mshv_ioctls::{set_registers_64, Mshv, NoDatamatch, VcpuFd, VmFd};
|
||||
use mshv_ioctls::{set_registers_64, Mshv, NoDatamatch, VcpuFd, VmFd, VmType};
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use vfio_ioctls::VfioDeviceFd;
|
||||
use vm::DataMatch;
|
||||
|
||||
#[cfg(feature = "sev_snp")]
|
||||
mod snp_constants;
|
||||
// x86_64 dependencies
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub mod x86_64;
|
||||
#[cfg(feature = "sev_snp")]
|
||||
use snp_constants::*;
|
||||
|
||||
use crate::{
|
||||
ClockData, CpuState, IoEventAddress, IrqRoutingEntry, MpState, UserMemoryRegion,
|
||||
USER_MEMORY_REGION_EXECUTE, USER_MEMORY_REGION_READ, USER_MEMORY_REGION_WRITE,
|
||||
};
|
||||
#[cfg(feature = "sev_snp")]
|
||||
use igvm_defs::IGVM_VHS_SNP_ID_BLOCK;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use x86_64::VcpuMshvState;
|
||||
@@ -192,6 +200,7 @@ impl MshvHypervisor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of Hypervisor trait for Mshv
|
||||
///
|
||||
/// # Examples
|
||||
@@ -210,21 +219,15 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
fn hypervisor_type(&self) -> HypervisorType {
|
||||
HypervisorType::Mshv
|
||||
}
|
||||
/// Create a mshv vm object and return the object as Vm trait object
|
||||
///
|
||||
/// # 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>> {
|
||||
|
||||
fn create_vm_with_type(&self, vm_type: u64) -> hypervisor::Result<Arc<dyn crate::Vm>> {
|
||||
let mshv_vm_type: VmType = match VmType::try_from(vm_type) {
|
||||
Ok(vm_type) => vm_type,
|
||||
Err(_) => return Err(hypervisor::HypervisorError::UnsupportedVmType()),
|
||||
};
|
||||
let fd: VmFd;
|
||||
loop {
|
||||
match self.mshv.create_vm() {
|
||||
match self.mshv.create_vm_with_type(mshv_vm_type) {
|
||||
Ok(res) => fd = res,
|
||||
Err(e) => {
|
||||
if e.errno() == libc::EINTR {
|
||||
@@ -240,6 +243,33 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
break;
|
||||
}
|
||||
|
||||
// Set additional partition property for SEV-SNP partition.
|
||||
if mshv_vm_type == VmType::Snp {
|
||||
let snp_policy = snp::get_default_snp_guest_policy();
|
||||
let vmgexit_offloads = snp::get_default_vmgexit_offload_features();
|
||||
// SAFETY: access union fields
|
||||
unsafe {
|
||||
debug!(
|
||||
"Setting the partition isolation policy as: 0x{:x}",
|
||||
snp_policy.as_uint64
|
||||
);
|
||||
fd.set_partition_property(
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_ISOLATION_POLICY,
|
||||
snp_policy.as_uint64,
|
||||
)
|
||||
.map_err(|e| hypervisor::HypervisorError::SetPartitionProperty(e.into()))?;
|
||||
debug!(
|
||||
"Setting the partition property to enable VMGEXIT offloads as : 0x{:x}",
|
||||
vmgexit_offloads.as_uint64
|
||||
);
|
||||
fd.set_partition_property(
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_SEV_VMGEXIT_OFFLOADS,
|
||||
vmgexit_offloads.as_uint64,
|
||||
)
|
||||
.map_err(|e| hypervisor::HypervisorError::SetPartitionProperty(e.into()))?;
|
||||
}
|
||||
}
|
||||
|
||||
// Default Microsoft Hypervisor behavior for unimplemented MSR is to
|
||||
// send a fault to the guest if it tries to access it. It is possible
|
||||
// to override this behavior with a more suitable option i.e., ignore
|
||||
@@ -271,6 +301,22 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
dirty_log_slots: Arc::new(RwLock::new(HashMap::new())),
|
||||
}))
|
||||
}
|
||||
|
||||
/// Create a mshv vm object and return the object as Vm trait object
|
||||
///
|
||||
/// # 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 vm_type = 0;
|
||||
self.create_vm_with_type(vm_type)
|
||||
}
|
||||
///
|
||||
/// Get the supported CpuID
|
||||
///
|
||||
@@ -293,6 +339,8 @@ pub struct MshvVcpu {
|
||||
cpuid: Vec<CpuIdEntry>,
|
||||
msrs: Vec<MsrEntry>,
|
||||
vm_ops: Option<Arc<dyn vm::VmOps>>,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
vm_fd: Arc<VmFd>,
|
||||
}
|
||||
|
||||
/// Implementation of Vcpu trait for Microsoft Hypervisor
|
||||
@@ -319,6 +367,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetStandardRegs(e.into()))?
|
||||
.into())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the vCPU general purpose registers.
|
||||
@@ -329,6 +378,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
.set_regs(®s)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetStandardRegs(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Returns the vCPU special registers.
|
||||
@@ -340,6 +390,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetSpecialRegs(e.into()))?
|
||||
.into())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the vCPU special registers.
|
||||
@@ -350,6 +401,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
.set_sregs(&sregs)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetSpecialRegs(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Returns the floating point state (FPU) from the vCPU.
|
||||
@@ -361,6 +413,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetFloatingPointRegs(e.into()))?
|
||||
.into())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Set the floating point state (FPU) of a vCPU.
|
||||
@@ -393,6 +446,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
|
||||
Ok(succ)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Setup the model-specific registers (MSR) for this vCPU.
|
||||
@@ -414,6 +468,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
/* We always have SynIC enabled on MSHV */
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
fn run(&self) -> std::result::Result<cpu::VmExit, cpu::HypervisorCpuError> {
|
||||
let hv_message: hv_message = hv_message::default();
|
||||
@@ -562,6 +617,439 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
debug!("Exception Info {:?}", { info.exception_vector });
|
||||
Ok(cpu::VmExit::Ignore)
|
||||
}
|
||||
hv_message_type_HVMSG_X64_APIC_EOI => {
|
||||
let info = x.to_apic_eoi_info().unwrap();
|
||||
// The kernel should dispatch the EOI to the correct thread.
|
||||
// Check the VP index is the same as the one we have.
|
||||
assert!(info.vp_index == self.vp_index as u32);
|
||||
// The interrupt vector in info is u32, but x86 only supports 256 vectors.
|
||||
// There is no good way to recover from this if the hypervisor messes around.
|
||||
// Just unwrap.
|
||||
Ok(cpu::VmExit::IoapicEoi(
|
||||
info.interrupt_vector.try_into().unwrap(),
|
||||
))
|
||||
}
|
||||
#[cfg(feature = "sev_snp")]
|
||||
hv_message_type_HVMSG_X64_SEV_VMGEXIT_INTERCEPT => {
|
||||
let info = x.to_vmg_intercept_info().unwrap();
|
||||
let ghcb_data = info.ghcb_msr >> GHCB_INFO_BIT_WIDTH;
|
||||
let ghcb_msr = svm_ghcb_msr {
|
||||
as_uint64: info.ghcb_msr,
|
||||
};
|
||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||
let ghcb_op = unsafe { ghcb_msr.__bindgen_anon_2.ghcb_info() as u32 };
|
||||
// Sanity check on the header fields before handling other operations.
|
||||
assert!(info.header.intercept_access_type == HV_INTERCEPT_ACCESS_EXECUTE as u8);
|
||||
|
||||
match ghcb_op {
|
||||
GHCB_INFO_HYP_FEATURE_REQUEST => {
|
||||
// Pre-condition: GHCB data must be zero
|
||||
assert!(ghcb_data == 0);
|
||||
let mut ghcb_response = GHCB_INFO_HYP_FEATURE_RESPONSE as u64;
|
||||
// Indicate support for basic SEV-SNP features
|
||||
ghcb_response |=
|
||||
(GHCB_HYP_FEATURE_SEV_SNP << GHCB_INFO_BIT_WIDTH) as u64;
|
||||
// Indicate support for SEV-SNP AP creation
|
||||
ghcb_response |= (GHCB_HYP_FEATURE_SEV_SNP_AP_CREATION
|
||||
<< GHCB_INFO_BIT_WIDTH)
|
||||
as u64;
|
||||
debug!(
|
||||
"GHCB_INFO_HYP_FEATURE_REQUEST: Supported features: {:0x}",
|
||||
ghcb_response
|
||||
);
|
||||
let arr_reg_name_value =
|
||||
[(hv_register_name_HV_X64_REGISTER_GHCB, ghcb_response)];
|
||||
set_registers_64!(self.fd, arr_reg_name_value)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
|
||||
}
|
||||
GHCB_INFO_REGISTER_REQUEST => {
|
||||
let mut ghcb_gpa = hv_x64_register_sev_ghcb::default();
|
||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||
unsafe {
|
||||
ghcb_gpa.__bindgen_anon_1.set_enabled(1);
|
||||
ghcb_gpa
|
||||
.__bindgen_anon_1
|
||||
.set_page_number(ghcb_msr.__bindgen_anon_2.gpa_page_number());
|
||||
}
|
||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||
let reg_name_value = unsafe {
|
||||
[(
|
||||
hv_register_name_HV_X64_REGISTER_SEV_GHCB_GPA,
|
||||
ghcb_gpa.as_uint64,
|
||||
)]
|
||||
};
|
||||
|
||||
set_registers_64!(self.fd, reg_name_value)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
|
||||
|
||||
let mut resp_ghcb_msr = svm_ghcb_msr::default();
|
||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||
unsafe {
|
||||
resp_ghcb_msr
|
||||
.__bindgen_anon_2
|
||||
.set_ghcb_info(GHCB_INFO_REGISTER_RESPONSE as u64);
|
||||
resp_ghcb_msr.__bindgen_anon_2.set_gpa_page_number(
|
||||
ghcb_msr.__bindgen_anon_2.gpa_page_number(),
|
||||
);
|
||||
}
|
||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||
let reg_name_value = unsafe {
|
||||
[(
|
||||
hv_register_name_HV_X64_REGISTER_GHCB,
|
||||
resp_ghcb_msr.as_uint64,
|
||||
)]
|
||||
};
|
||||
|
||||
set_registers_64!(self.fd, reg_name_value)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
|
||||
}
|
||||
GHCB_INFO_SEV_INFO_REQUEST => {
|
||||
let sev_cpuid_function = 0x8000_001F;
|
||||
let cpu_leaf = self
|
||||
.fd
|
||||
.get_cpuid_values(sev_cpuid_function, 0, 0, 0)
|
||||
.unwrap();
|
||||
let ebx = cpu_leaf[1];
|
||||
// First 6-byte of EBX represents page table encryption bit number
|
||||
let pbit_encryption = (ebx & 0x3f) as u8;
|
||||
let mut ghcb_response = GHCB_INFO_SEV_INFO_RESPONSE as u64;
|
||||
|
||||
// GHCBData[63:48] specifies the maximum GHCB protocol version supported
|
||||
ghcb_response |= (GHCB_PROTOCOL_VERSION_MAX as u64) << 48;
|
||||
// GHCBData[47:32] specifies the minimum GHCB protocol version supported
|
||||
ghcb_response |= (GHCB_PROTOCOL_VERSION_MIN as u64) << 32;
|
||||
// GHCBData[31:24] specifies the SEV page table encryption bit number.
|
||||
ghcb_response |= (pbit_encryption as u64) << 24;
|
||||
|
||||
let arr_reg_name_value =
|
||||
[(hv_register_name_HV_X64_REGISTER_GHCB, ghcb_response)];
|
||||
set_registers_64!(self.fd, arr_reg_name_value)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
|
||||
}
|
||||
GHCB_INFO_NORMAL => {
|
||||
let exit_code =
|
||||
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_code as u32;
|
||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||
let pfn = unsafe { ghcb_msr.__bindgen_anon_2.gpa_page_number() };
|
||||
let ghcb_gpa = pfn << GHCB_INFO_BIT_WIDTH;
|
||||
match exit_code {
|
||||
SVM_EXITCODE_HV_DOORBELL_PAGE => {
|
||||
let exit_info1 =
|
||||
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info1 as u32;
|
||||
match exit_info1 {
|
||||
SVM_NAE_HV_DOORBELL_PAGE_GET_PREFERRED => {
|
||||
// Hypervisor does not have any preference for doorbell GPA.
|
||||
let preferred_doorbell_gpa: u64 = 0xFFFFFFFFFFFFFFFF;
|
||||
let mut swei2_rw_gpa_arg =
|
||||
mshv_bindings::mshv_read_write_gpa {
|
||||
base_gpa: ghcb_gpa + GHCB_SW_EXITINFO2_OFFSET,
|
||||
byte_count: std::mem::size_of::<u64>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
swei2_rw_gpa_arg.data.copy_from_slice(
|
||||
&preferred_doorbell_gpa.to_le_bytes(),
|
||||
);
|
||||
self.fd.gpa_write(&mut swei2_rw_gpa_arg).map_err(
|
||||
|e| cpu::HypervisorCpuError::GpaWrite(e.into()),
|
||||
)?;
|
||||
}
|
||||
SVM_NAE_HV_DOORBELL_PAGE_SET => {
|
||||
let exit_info2 = info
|
||||
.__bindgen_anon_2
|
||||
.__bindgen_anon_1
|
||||
.sw_exit_info2;
|
||||
let mut ghcb_doorbell_gpa =
|
||||
hv_x64_register_sev_hv_doorbell::default();
|
||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||
unsafe {
|
||||
ghcb_doorbell_gpa.__bindgen_anon_1.set_enabled(1);
|
||||
ghcb_doorbell_gpa
|
||||
.__bindgen_anon_1
|
||||
.set_page_number(exit_info2 >> PAGE_SHIFT);
|
||||
}
|
||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||
let reg_names = unsafe {
|
||||
[(
|
||||
hv_register_name_HV_X64_REGISTER_SEV_DOORBELL_GPA,
|
||||
ghcb_doorbell_gpa.as_uint64,
|
||||
)]
|
||||
};
|
||||
set_registers_64!(self.fd, reg_names).map_err(|e| {
|
||||
cpu::HypervisorCpuError::SetRegister(e.into())
|
||||
})?;
|
||||
|
||||
let mut swei2_rw_gpa_arg =
|
||||
mshv_bindings::mshv_read_write_gpa {
|
||||
base_gpa: ghcb_gpa + GHCB_SW_EXITINFO2_OFFSET,
|
||||
byte_count: std::mem::size_of::<u64>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
swei2_rw_gpa_arg.data[0..8]
|
||||
.copy_from_slice(&exit_info2.to_le_bytes());
|
||||
self.fd.gpa_write(&mut swei2_rw_gpa_arg).map_err(
|
||||
|e| cpu::HypervisorCpuError::GpaWrite(e.into()),
|
||||
)?;
|
||||
|
||||
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||
let mut swei1_rw_gpa_arg =
|
||||
mshv_bindings::mshv_read_write_gpa {
|
||||
base_gpa: ghcb_gpa + GHCB_SW_EXITINFO1_OFFSET,
|
||||
byte_count: std::mem::size_of::<u64>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
self.fd.gpa_write(&mut swei1_rw_gpa_arg).map_err(
|
||||
|e| cpu::HypervisorCpuError::GpaWrite(e.into()),
|
||||
)?;
|
||||
}
|
||||
SVM_NAE_HV_DOORBELL_PAGE_QUERY => {
|
||||
let mut reg_assocs = [ hv_register_assoc {
|
||||
name: hv_register_name_HV_X64_REGISTER_SEV_DOORBELL_GPA,
|
||||
..Default::default()
|
||||
} ];
|
||||
self.fd.get_reg(&mut reg_assocs).unwrap();
|
||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||
let doorbell_gpa = unsafe { reg_assocs[0].value.reg64 };
|
||||
let mut swei2_rw_gpa_arg =
|
||||
mshv_bindings::mshv_read_write_gpa {
|
||||
base_gpa: ghcb_gpa + GHCB_SW_EXITINFO2_OFFSET,
|
||||
byte_count: std::mem::size_of::<u64>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
swei2_rw_gpa_arg
|
||||
.data
|
||||
.copy_from_slice(&doorbell_gpa.to_le_bytes());
|
||||
self.fd.gpa_write(&mut swei2_rw_gpa_arg).map_err(
|
||||
|e| cpu::HypervisorCpuError::GpaWrite(e.into()),
|
||||
)?;
|
||||
}
|
||||
SVM_NAE_HV_DOORBELL_PAGE_CLEAR => {
|
||||
let mut swei2_rw_gpa_arg =
|
||||
mshv_bindings::mshv_read_write_gpa {
|
||||
base_gpa: ghcb_gpa + GHCB_SW_EXITINFO2_OFFSET,
|
||||
byte_count: std::mem::size_of::<u64>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
self.fd.gpa_write(&mut swei2_rw_gpa_arg).map_err(
|
||||
|e| cpu::HypervisorCpuError::GpaWrite(e.into()),
|
||||
)?;
|
||||
}
|
||||
_ => {
|
||||
panic!(
|
||||
"SVM_EXITCODE_HV_DOORBELL_PAGE: Unhandled exit code: {:0x}",
|
||||
exit_info1
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
SVM_EXITCODE_SNP_EXTENDED_GUEST_REQUEST => {
|
||||
warn!("Fetching extended guest request is not supported");
|
||||
// Extended guest request is not supported by the Hypervisor
|
||||
// Returning the error to the guest
|
||||
// 0x6 means `The NAE event was not valid`
|
||||
// Reference: GHCB Spec, page 42
|
||||
let value: u64 = 0x6;
|
||||
let mut swei2_rw_gpa_arg = mshv_bindings::mshv_read_write_gpa {
|
||||
base_gpa: ghcb_gpa + GHCB_SW_EXITINFO2_OFFSET,
|
||||
byte_count: std::mem::size_of::<u64>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
swei2_rw_gpa_arg.data.copy_from_slice(&value.to_le_bytes());
|
||||
self.fd
|
||||
.gpa_write(&mut swei2_rw_gpa_arg)
|
||||
.map_err(|e| cpu::HypervisorCpuError::GpaWrite(e.into()))?;
|
||||
}
|
||||
SVM_EXITCODE_IOIO_PROT => {
|
||||
let exit_info1 =
|
||||
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info1 as u32;
|
||||
let port_info = hv_sev_vmgexit_port_info {
|
||||
as_uint32: exit_info1,
|
||||
};
|
||||
|
||||
let port =
|
||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||
unsafe { port_info.__bindgen_anon_1.intercepted_port() };
|
||||
let mut len = 4;
|
||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||
unsafe {
|
||||
if port_info.__bindgen_anon_1.operand_size_16bit() == 1 {
|
||||
len = 2;
|
||||
} else if port_info.__bindgen_anon_1.operand_size_8bit()
|
||||
== 1
|
||||
{
|
||||
len = 1;
|
||||
}
|
||||
}
|
||||
let is_write =
|
||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||
unsafe { port_info.__bindgen_anon_1.access_type() == 0 };
|
||||
let mut rax_rw_gpa_arg: mshv_read_write_gpa =
|
||||
mshv_bindings::mshv_read_write_gpa {
|
||||
base_gpa: ghcb_gpa + GHCB_RAX_OFFSET,
|
||||
byte_count: std::mem::size_of::<u64>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
self.fd
|
||||
.gpa_read(&mut rax_rw_gpa_arg)
|
||||
.map_err(|e| cpu::HypervisorCpuError::GpaRead(e.into()))?;
|
||||
|
||||
if is_write {
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
vm_ops
|
||||
.pio_write(
|
||||
port.into(),
|
||||
&rax_rw_gpa_arg.data[0..len],
|
||||
)
|
||||
.map_err(|e| {
|
||||
cpu::HypervisorCpuError::RunVcpu(e.into())
|
||||
})?;
|
||||
}
|
||||
} else {
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
vm_ops
|
||||
.pio_read(
|
||||
port.into(),
|
||||
&mut rax_rw_gpa_arg.data[0..len],
|
||||
)
|
||||
.map_err(|e| {
|
||||
cpu::HypervisorCpuError::RunVcpu(e.into())
|
||||
})?;
|
||||
}
|
||||
|
||||
self.fd.gpa_write(&mut rax_rw_gpa_arg).map_err(|e| {
|
||||
cpu::HypervisorCpuError::GpaWrite(e.into())
|
||||
})?;
|
||||
}
|
||||
|
||||
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||
let mut swei1_rw_gpa_arg = mshv_bindings::mshv_read_write_gpa {
|
||||
base_gpa: ghcb_gpa + GHCB_SW_EXITINFO1_OFFSET,
|
||||
byte_count: std::mem::size_of::<u64>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
self.fd
|
||||
.gpa_write(&mut swei1_rw_gpa_arg)
|
||||
.map_err(|e| cpu::HypervisorCpuError::GpaWrite(e.into()))?;
|
||||
}
|
||||
SVM_EXITCODE_MMIO_READ => {
|
||||
let src_gpa =
|
||||
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info1;
|
||||
let dst_gpa = info.__bindgen_anon_2.__bindgen_anon_1.sw_scratch;
|
||||
let data_len =
|
||||
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info2
|
||||
as usize;
|
||||
// Sanity check to make sure data len is within supported range.
|
||||
assert!(data_len <= 0x8);
|
||||
|
||||
let mut data: Vec<u8> = vec![0; data_len];
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
vm_ops.mmio_read(src_gpa, &mut data[0..data_len]).map_err(
|
||||
|e| cpu::HypervisorCpuError::RunVcpu(e.into()),
|
||||
)?;
|
||||
}
|
||||
let mut arg: mshv_read_write_gpa =
|
||||
mshv_bindings::mshv_read_write_gpa {
|
||||
base_gpa: dst_gpa,
|
||||
byte_count: data_len as u32,
|
||||
..Default::default()
|
||||
};
|
||||
arg.data[0..data_len].copy_from_slice(&data);
|
||||
|
||||
self.fd
|
||||
.gpa_write(&mut arg)
|
||||
.map_err(|e| cpu::HypervisorCpuError::GpaWrite(e.into()))?;
|
||||
}
|
||||
SVM_EXITCODE_MMIO_WRITE => {
|
||||
let dst_gpa =
|
||||
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info1;
|
||||
let src_gpa = info.__bindgen_anon_2.__bindgen_anon_1.sw_scratch;
|
||||
let data_len =
|
||||
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info2
|
||||
as usize;
|
||||
// Sanity check to make sure data len is within supported range.
|
||||
assert!(data_len <= 0x8);
|
||||
let mut arg: mshv_read_write_gpa =
|
||||
mshv_bindings::mshv_read_write_gpa {
|
||||
base_gpa: src_gpa,
|
||||
byte_count: data_len as u32,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
self.fd
|
||||
.gpa_read(&mut arg)
|
||||
.map_err(|e| cpu::HypervisorCpuError::GpaRead(e.into()))?;
|
||||
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
vm_ops
|
||||
.mmio_write(dst_gpa, &arg.data[0..data_len])
|
||||
.map_err(|e| {
|
||||
cpu::HypervisorCpuError::RunVcpu(e.into())
|
||||
})?;
|
||||
}
|
||||
}
|
||||
SVM_EXITCODE_SNP_GUEST_REQUEST => {
|
||||
let req_gpa =
|
||||
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info1;
|
||||
let rsp_gpa =
|
||||
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info2;
|
||||
|
||||
let mshv_psp_req =
|
||||
mshv_issue_psp_guest_request { req_gpa, rsp_gpa };
|
||||
self.vm_fd
|
||||
.psp_issue_guest_request(&mshv_psp_req)
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
|
||||
|
||||
debug!(
|
||||
"SNP guest request: req_gpa {:0x} rsp_gpa {:0x}",
|
||||
req_gpa, rsp_gpa
|
||||
);
|
||||
|
||||
let mut swei2_rw_gpa_arg = mshv_bindings::mshv_read_write_gpa {
|
||||
base_gpa: ghcb_gpa + GHCB_SW_EXITINFO2_OFFSET,
|
||||
byte_count: std::mem::size_of::<u64>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
self.fd
|
||||
.gpa_write(&mut swei2_rw_gpa_arg)
|
||||
.map_err(|e| cpu::HypervisorCpuError::GpaWrite(e.into()))?;
|
||||
}
|
||||
SVM_EXITCODE_SNP_AP_CREATION => {
|
||||
let vmsa_gpa =
|
||||
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info2;
|
||||
let apic_id =
|
||||
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info1 >> 32;
|
||||
debug!(
|
||||
"SNP AP CREATE REQUEST with VMSA GPA {:0x}, and APIC ID {:?}",
|
||||
vmsa_gpa, apic_id
|
||||
);
|
||||
|
||||
let mshv_ap_create_req = mshv_sev_snp_ap_create {
|
||||
vp_id: apic_id,
|
||||
vmsa_gpa,
|
||||
};
|
||||
self.vm_fd
|
||||
.sev_snp_ap_create(&mshv_ap_create_req)
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
|
||||
|
||||
let mut swei2_rw_gpa_arg = mshv_bindings::mshv_read_write_gpa {
|
||||
base_gpa: ghcb_gpa + GHCB_SW_EXITINFO2_OFFSET,
|
||||
byte_count: std::mem::size_of::<u64>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
self.fd
|
||||
.gpa_write(&mut swei2_rw_gpa_arg)
|
||||
.map_err(|e| cpu::HypervisorCpuError::GpaWrite(e.into()))?;
|
||||
}
|
||||
_ => panic!(
|
||||
"GHCB_INFO_NORMAL: Unhandled exit code: {:0x}",
|
||||
exit_code
|
||||
),
|
||||
}
|
||||
}
|
||||
_ => panic!("Unsupported VMGEXIT operation: {:0x}", ghcb_op),
|
||||
}
|
||||
|
||||
Ok(cpu::VmExit::Ignore)
|
||||
}
|
||||
exit => Err(cpu::HypervisorCpuError::RunVcpu(anyhow!(
|
||||
"Unhandled VCPU exit {:?}",
|
||||
exit
|
||||
@@ -577,6 +1065,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call to setup the CPUID registers.
|
||||
@@ -590,6 +1079,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
.register_intercept_result_cpuid(&mshv_cpuid)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCpuid(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call to retrieve the CPUID registers.
|
||||
@@ -597,6 +1087,23 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
fn get_cpuid2(&self, _num_entries: usize) -> cpu::Result<Vec<CpuIdEntry>> {
|
||||
Ok(self.cpuid.clone())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call to retrieve cpuid leaf
|
||||
///
|
||||
fn get_cpuid_values(
|
||||
&self,
|
||||
function: u32,
|
||||
index: u32,
|
||||
xfem: u64,
|
||||
xss: u64,
|
||||
) -> cpu::Result<[u32; 4]> {
|
||||
self.fd
|
||||
.get_cpuid_values(function, index, xfem, xss)
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetCpuidVales(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Returns the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
|
||||
@@ -608,6 +1115,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetlapicState(e.into()))?
|
||||
.into())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
|
||||
@@ -618,18 +1126,21 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
.set_lapic(&lapic)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetLapicState(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the vcpu's current "multiprocessing state".
|
||||
///
|
||||
fn get_mp_state(&self) -> cpu::Result<MpState> {
|
||||
Ok(MpState::Mshv)
|
||||
}
|
||||
|
||||
///
|
||||
/// Sets the vcpu's current "multiprocessing state".
|
||||
///
|
||||
fn set_mp_state(&self, _mp_state: MpState) -> cpu::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Set CPU state
|
||||
///
|
||||
@@ -655,6 +1166,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetDebugRegs(e.into()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Get CPU State
|
||||
///
|
||||
@@ -691,6 +1203,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Translate guest virtual address to guest physical address
|
||||
@@ -707,6 +1220,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
|
||||
Ok((gpa, result_code))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Return the list of initial MSR entries for a VCPU
|
||||
@@ -739,6 +1253,7 @@ impl MshvVcpu {
|
||||
.get_xsave()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetXsaveState(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call that sets the vcpu's current "xsave struct".
|
||||
@@ -748,6 +1263,7 @@ impl MshvVcpu {
|
||||
.set_xsave(xsave)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetXsaveState(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call that returns the vcpu's current "xcrs".
|
||||
@@ -757,6 +1273,7 @@ impl MshvVcpu {
|
||||
.get_xcrs()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetXcsr(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// X86 specific call that sets the vcpu's current "xcrs".
|
||||
@@ -766,6 +1283,7 @@ impl MshvVcpu {
|
||||
.set_xcrs(xcrs)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetXcsr(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Returns currently pending exceptions, interrupts, and NMIs as well as related
|
||||
@@ -776,6 +1294,7 @@ impl MshvVcpu {
|
||||
.get_vcpu_events()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetVcpuEvents(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets pending exceptions, interrupts, and NMIs as well as related states
|
||||
@@ -955,6 +1474,7 @@ impl vm::Vm for MshvVm {
|
||||
fn set_identity_map_address(&self, _address: u64) -> vm::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the address of the three-page region in the VM's address space.
|
||||
@@ -962,12 +1482,14 @@ impl vm::Vm for MshvVm {
|
||||
fn set_tss_address(&self, _offset: usize) -> vm::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Creates an in-kernel interrupt controller.
|
||||
///
|
||||
fn create_irq_chip(&self) -> vm::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Registers an event that will, when signaled, trigger the `gsi` IRQ.
|
||||
///
|
||||
@@ -980,6 +1502,7 @@ impl vm::Vm for MshvVm {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Unregisters an event that will, when signaled, trigger the `gsi` IRQ.
|
||||
///
|
||||
@@ -992,6 +1515,7 @@ impl vm::Vm for MshvVm {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Creates a VcpuFd object from a vcpu RawFd.
|
||||
///
|
||||
@@ -1010,17 +1534,22 @@ impl vm::Vm for MshvVm {
|
||||
cpuid: Vec::new(),
|
||||
msrs: self.msrs.clone(),
|
||||
vm_ops,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
vm_fd: self.fd.clone(),
|
||||
};
|
||||
Ok(Arc::new(vcpu))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn enable_split_irq(&self) -> vm::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn enable_sgx_attribute(&self, _file: File) -> vm::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_ioevent(
|
||||
&self,
|
||||
fd: &EventFd,
|
||||
@@ -1051,6 +1580,7 @@ impl vm::Vm for MshvVm {
|
||||
.map_err(|e| vm::HypervisorVmError::RegisterIoEvent(e.into()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Unregister an event from a certain address it has been previously registered to.
|
||||
fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> vm::Result<()> {
|
||||
let addr = &mshv_ioctls::IoEventAddress::from(*addr);
|
||||
@@ -1175,6 +1705,7 @@ impl vm::Vm for MshvVm {
|
||||
.set_msi_routing(&msi_routing[0])
|
||||
.map_err(|e| vm::HypervisorVmError::SetGsiRouting(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Start logging dirty pages
|
||||
///
|
||||
@@ -1183,6 +1714,7 @@ impl vm::Vm for MshvVm {
|
||||
.enable_dirty_page_tracking()
|
||||
.map_err(|e| vm::HypervisorVmError::StartDirtyLog(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Stop logging dirty pages
|
||||
///
|
||||
@@ -1201,6 +1733,7 @@ impl vm::Vm for MshvVm {
|
||||
.map_err(|e| vm::HypervisorVmError::StartDirtyLog(e.into()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Get dirty pages bitmap (one bit per page)
|
||||
///
|
||||
@@ -1213,18 +1746,115 @@ impl vm::Vm for MshvVm {
|
||||
)
|
||||
.map_err(|e| vm::HypervisorVmError::GetDirtyLog(e.into()))
|
||||
}
|
||||
|
||||
/// Retrieve guest clock.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn get_clock(&self) -> vm::Result<ClockData> {
|
||||
Ok(ClockData::Mshv)
|
||||
}
|
||||
|
||||
/// Set guest clock.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn set_clock(&self, _data: &ClockData) -> vm::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Downcast to the underlying MshvVm type
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
/// Initialize the SEV-SNP VM
|
||||
#[cfg(feature = "sev_snp")]
|
||||
fn sev_snp_init(&self) -> vm::Result<()> {
|
||||
self.fd
|
||||
.set_partition_property(
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_ISOLATION_STATE,
|
||||
hv_partition_isolation_state_HV_PARTITION_ISOLATION_SECURE as u64,
|
||||
)
|
||||
.map_err(|e| vm::HypervisorVmError::InitializeSevSnp(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Importing isolated pages, these pages will be used
|
||||
/// for the PSP(Platform Security Processor) measurement.
|
||||
#[cfg(feature = "sev_snp")]
|
||||
fn import_isolated_pages(
|
||||
&self,
|
||||
page_type: u32,
|
||||
page_size: u32,
|
||||
pages: &[u64],
|
||||
) -> vm::Result<()> {
|
||||
if pages.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut isolated_pages =
|
||||
vec_with_array_field::<mshv_import_isolated_pages, u64>(pages.len());
|
||||
isolated_pages[0].num_pages = pages.len() as u64;
|
||||
isolated_pages[0].page_type = page_type;
|
||||
isolated_pages[0].page_size = page_size;
|
||||
// SAFETY: isolated_pages initialized with pages.len() and now it is being turned into
|
||||
// pages_slice with pages.len() again. It is guaranteed to be large enough to hold
|
||||
// everything from pages.
|
||||
unsafe {
|
||||
let pages_slice: &mut [u64] = isolated_pages[0].page_number.as_mut_slice(pages.len());
|
||||
pages_slice.copy_from_slice(pages);
|
||||
}
|
||||
self.fd
|
||||
.import_isolated_pages(&isolated_pages[0])
|
||||
.map_err(|e| vm::HypervisorVmError::ImportIsolatedPages(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Complete isolated import, telling the hypervisor that
|
||||
/// importing the pages to guest memory is complete.
|
||||
///
|
||||
#[cfg(feature = "sev_snp")]
|
||||
fn complete_isolated_import(
|
||||
&self,
|
||||
snp_id_block: IGVM_VHS_SNP_ID_BLOCK,
|
||||
host_data: &[u8],
|
||||
id_block_enabled: u8,
|
||||
) -> vm::Result<()> {
|
||||
let mut auth_info = hv_snp_id_auth_info {
|
||||
id_key_algorithm: snp_id_block.id_key_algorithm,
|
||||
auth_key_algorithm: snp_id_block.author_key_algorithm,
|
||||
..Default::default()
|
||||
};
|
||||
// Each of r/s component is 576 bits long
|
||||
auth_info.id_block_signature[..SIG_R_COMPONENT_SIZE_IN_BYTES]
|
||||
.copy_from_slice(snp_id_block.id_key_signature.r_comp.as_ref());
|
||||
auth_info.id_block_signature
|
||||
[SIG_R_COMPONENT_SIZE_IN_BYTES..SIG_R_AND_S_COMPONENT_SIZE_IN_BYTES]
|
||||
.copy_from_slice(snp_id_block.id_key_signature.s_comp.as_ref());
|
||||
auth_info.id_key[..ECDSA_CURVE_ID_SIZE_IN_BYTES]
|
||||
.copy_from_slice(snp_id_block.id_public_key.curve.to_le_bytes().as_ref());
|
||||
auth_info.id_key[ECDSA_SIG_X_COMPONENT_START..ECDSA_SIG_X_COMPONENT_END]
|
||||
.copy_from_slice(snp_id_block.id_public_key.qx.as_ref());
|
||||
auth_info.id_key[ECDSA_SIG_Y_COMPONENT_START..ECDSA_SIG_Y_COMPONENT_END]
|
||||
.copy_from_slice(snp_id_block.id_public_key.qy.as_ref());
|
||||
|
||||
let data = mshv_complete_isolated_import {
|
||||
import_data: hv_partition_complete_isolated_import_data {
|
||||
psp_parameters: hv_psp_launch_finish_data {
|
||||
id_block: hv_snp_id_block {
|
||||
launch_digest: snp_id_block.ld,
|
||||
family_id: snp_id_block.family_id,
|
||||
image_id: snp_id_block.image_id,
|
||||
version: snp_id_block.version,
|
||||
guest_svn: snp_id_block.guest_svn,
|
||||
policy: get_default_snp_guest_policy(),
|
||||
},
|
||||
id_auth_info: auth_info,
|
||||
host_data: host_data[0..32].try_into().unwrap(),
|
||||
id_block_enabled,
|
||||
author_key_enabled: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
self.fd
|
||||
.complete_isolated_import(&data)
|
||||
.map_err(|e| vm::HypervisorVmError::CompleteIsolatedImport(e.into()))
|
||||
}
|
||||
}
|
||||
|
||||
24
hypervisor/src/mshv/snp_constants.rs
Normal file
24
hypervisor/src/mshv/snp_constants.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
|
||||
//
|
||||
// Copyright © 2020, Microsoft Corporation
|
||||
//
|
||||
|
||||
// Reference: https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/56860.pdf
|
||||
// Chapter 10
|
||||
pub const SIG_R_COMPONENT_SIZE_IN_BYTES: usize = 72;
|
||||
pub const SIG_R_AND_S_COMPONENT_SIZE_IN_BYTES: usize = 144;
|
||||
pub const ECDSA_CURVE_ID_SIZE_IN_BYTES: usize = 4;
|
||||
pub const ECDSA_SIG_X_COMPONENT_SIZE_IN_BYTES: usize = 72;
|
||||
pub const ECDSA_SIG_Y_COMPONENT_SIZE_IN_BYTES: usize = 72;
|
||||
pub const ECDSA_SIG_X_COMPONENT_START: usize = ECDSA_CURVE_ID_SIZE_IN_BYTES;
|
||||
pub const ECDSA_SIG_X_COMPONENT_END: usize =
|
||||
ECDSA_SIG_X_COMPONENT_START + ECDSA_SIG_X_COMPONENT_SIZE_IN_BYTES;
|
||||
pub const ECDSA_SIG_Y_COMPONENT_START: usize = ECDSA_SIG_X_COMPONENT_END;
|
||||
pub const ECDSA_SIG_Y_COMPONENT_END: usize =
|
||||
ECDSA_SIG_X_COMPONENT_END + ECDSA_SIG_Y_COMPONENT_SIZE_IN_BYTES;
|
||||
|
||||
// These constants are derived from GHCB spec Sect. 2.6 Table 3 GHCB Layout
|
||||
// Link: https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/56421.pdf
|
||||
pub const GHCB_RAX_OFFSET: u64 = 0x01F8;
|
||||
pub const GHCB_SW_EXITINFO1_OFFSET: u64 = 0x398;
|
||||
pub const GHCB_SW_EXITINFO2_OFFSET: u64 = 0x3A0;
|
||||
@@ -19,6 +19,8 @@ use crate::cpu::Vcpu;
|
||||
use crate::ClockData;
|
||||
use crate::UserMemoryRegion;
|
||||
use crate::{IoEventAddress, IrqRoutingEntry};
|
||||
#[cfg(feature = "sev_snp")]
|
||||
use igvm_defs::IGVM_VHS_SNP_ID_BLOCK;
|
||||
use std::any::Any;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use std::fs::File;
|
||||
@@ -190,6 +192,13 @@ pub enum HypervisorVmError {
|
||||
#[error("Failed to assert virtual Interrupt: {0}")]
|
||||
AsserttVirtualInterrupt(#[source] anyhow::Error),
|
||||
|
||||
#[cfg(feature = "sev_snp")]
|
||||
///
|
||||
/// Error initializing SEV-SNP on the VM
|
||||
///
|
||||
#[error("Failed to initialize SEV-SNP: {0}")]
|
||||
InitializeSevSnp(#[source] std::io::Error),
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
///
|
||||
/// Error initializing TDX on the VM
|
||||
@@ -213,6 +222,15 @@ pub enum HypervisorVmError {
|
||||
///
|
||||
#[error("Failed to create Vgic: {0}")]
|
||||
CreateVgic(#[source] anyhow::Error),
|
||||
///
|
||||
/// Import isolated pages error
|
||||
///
|
||||
#[error("Failed to import isolated pages: {0}")]
|
||||
ImportIsolatedPages(#[source] anyhow::Error),
|
||||
/// Failed to complete isolated import
|
||||
///
|
||||
#[error("Failed to complete isolated import: {0}")]
|
||||
CompleteIsolatedImport(#[source] anyhow::Error),
|
||||
}
|
||||
///
|
||||
/// Result type for returning from a function
|
||||
@@ -324,8 +342,13 @@ pub trait Vm: Send + Sync + Any {
|
||||
fn stop_dirty_log(&self) -> Result<()>;
|
||||
/// Get dirty pages bitmap
|
||||
fn get_dirty_log(&self, slot: u32, base_gpa: u64, memory_size: u64) -> Result<Vec<u64>>;
|
||||
#[cfg(feature = "sev_snp")]
|
||||
/// Initialize SEV-SNP on this VM
|
||||
fn sev_snp_init(&self) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
#[cfg(feature = "tdx")]
|
||||
/// Initalize TDX on this VM
|
||||
/// Initialize TDX on this VM
|
||||
fn tdx_init(&self, _cpuid: &[CpuIdEntry], _max_vcpus: u32) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
@@ -335,7 +358,7 @@ pub trait Vm: Send + Sync + Any {
|
||||
unimplemented!()
|
||||
}
|
||||
#[cfg(feature = "tdx")]
|
||||
/// Initalize a TDX memory region for this VM
|
||||
/// Initialize a TDX memory region for this VM
|
||||
fn tdx_init_memory_region(
|
||||
&self,
|
||||
_host_address: u64,
|
||||
@@ -347,6 +370,26 @@ pub trait Vm: Send + Sync + Any {
|
||||
}
|
||||
/// Downcast to the underlying hypervisor VM type
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
/// Import the isolated pages
|
||||
#[cfg(feature = "sev_snp")]
|
||||
fn import_isolated_pages(
|
||||
&self,
|
||||
_page_type: u32,
|
||||
_page_size: u32,
|
||||
_pages: &[u64],
|
||||
) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
/// Complete the isolated import
|
||||
#[cfg(feature = "sev_snp")]
|
||||
fn complete_isolated_import(
|
||||
&self,
|
||||
_snp_id_block: IGVM_VHS_SNP_ID_BLOCK,
|
||||
_host_data: &[u8],
|
||||
_id_block_enabled: u8,
|
||||
) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VmOps: Send + Sync {
|
||||
|
||||
@@ -7,22 +7,22 @@ edition = "2021"
|
||||
[dependencies]
|
||||
epoll = "4.3.3"
|
||||
getrandom = "0.2.10"
|
||||
libc = "0.2.139"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.147"
|
||||
log = "0.4.20"
|
||||
net_gen = { path = "../net_gen" }
|
||||
rate_limiter = { path = "../rate_limiter" }
|
||||
serde = "1.0.164"
|
||||
serde = "1.0.168"
|
||||
thiserror = "1.0.40"
|
||||
versionize = "0.1.10"
|
||||
versionize_derive = "0.1.4"
|
||||
virtio-bindings = "0.2.0"
|
||||
virtio-queue = "0.8.0"
|
||||
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
virtio-queue = "0.10.0"
|
||||
vm-memory = { version = "0.13.1", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
[dev-dependencies]
|
||||
once_cell = "1.18.0"
|
||||
pnet = "0.33.0"
|
||||
pnet_datalink = "0.33.0"
|
||||
serde_json = "1.0.96"
|
||||
pnet = "0.34.0"
|
||||
pnet_datalink = "0.34.0"
|
||||
serde_json = "1.0.107"
|
||||
|
||||
@@ -72,6 +72,10 @@ pub fn open_tap(
|
||||
let mut taps: Vec<Tap> = Vec::new();
|
||||
let mut ifname: String = String::new();
|
||||
let vnet_hdr_size = vnet_hdr_len() as i32;
|
||||
// Check if the given interface exists before we create it.
|
||||
let tap_existed = if_name.map_or(false, |n| {
|
||||
Path::new(&format!("/sys/class/net/{n}")).exists()
|
||||
});
|
||||
|
||||
// In case the tap interface already exists, check if the number of
|
||||
// queues is appropriate. The tap might not support multiqueue while
|
||||
@@ -87,11 +91,19 @@ pub fn open_tap(
|
||||
Some(name) => Tap::open_named(name, num_rx_q, flags).map_err(Error::TapOpen)?,
|
||||
None => Tap::new(num_rx_q).map_err(Error::TapOpen)?,
|
||||
};
|
||||
if let Some(ip) = ip_addr {
|
||||
tap.set_ip_addr(ip).map_err(Error::TapSetIp)?;
|
||||
}
|
||||
if let Some(mask) = netmask {
|
||||
tap.set_netmask(mask).map_err(Error::TapSetNetmask)?;
|
||||
// Don't overwrite ip configuration of existing interfaces:
|
||||
if !tap_existed {
|
||||
if let Some(ip) = ip_addr {
|
||||
tap.set_ip_addr(ip).map_err(Error::TapSetIp)?;
|
||||
}
|
||||
if let Some(mask) = netmask {
|
||||
tap.set_netmask(mask).map_err(Error::TapSetNetmask)?;
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
"Tap {} already exists. IP configuration will not be overwritten.",
|
||||
if_name.unwrap_or_default()
|
||||
);
|
||||
}
|
||||
if let Some(mac) = host_mac {
|
||||
tap.set_mac_addr(*mac).map_err(Error::TapSetMac)?
|
||||
|
||||
@@ -38,7 +38,7 @@ impl TxVirtio {
|
||||
pub fn process_desc_chain(
|
||||
&mut self,
|
||||
mem: &GuestMemoryMmap,
|
||||
tap: &mut Tap,
|
||||
tap: &Tap,
|
||||
queue: &mut Queue,
|
||||
rate_limiter: &mut Option<RateLimiter>,
|
||||
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
||||
@@ -64,9 +64,9 @@ impl TxVirtio {
|
||||
.memory()
|
||||
.get_slice(desc_addr, desc.len() as usize)
|
||||
.map_err(NetQueuePairError::GuestMemory)?
|
||||
.as_ptr();
|
||||
.ptr_guard_mut();
|
||||
let iovec = libc::iovec {
|
||||
iov_base: buf as *mut libc::c_void,
|
||||
iov_base: buf.as_ptr() as *mut libc::c_void,
|
||||
iov_len: desc.len() as libc::size_t,
|
||||
};
|
||||
iovecs.push(iovec);
|
||||
@@ -164,7 +164,7 @@ impl RxVirtio {
|
||||
pub fn process_desc_chain(
|
||||
&mut self,
|
||||
mem: &GuestMemoryMmap,
|
||||
tap: &mut Tap,
|
||||
tap: &Tap,
|
||||
queue: &mut Queue,
|
||||
rate_limiter: &mut Option<RateLimiter>,
|
||||
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
||||
@@ -203,9 +203,9 @@ impl RxVirtio {
|
||||
.memory()
|
||||
.get_slice(desc_addr, desc.len() as usize)
|
||||
.map_err(NetQueuePairError::GuestMemory)?
|
||||
.as_ptr();
|
||||
.ptr_guard_mut();
|
||||
let iovec = libc::iovec {
|
||||
iov_base: buf as *mut libc::c_void,
|
||||
iov_base: buf.as_ptr() as *mut libc::c_void,
|
||||
iov_len: desc.len() as libc::size_t,
|
||||
};
|
||||
iovecs.push(iovec);
|
||||
@@ -357,7 +357,7 @@ impl NetQueuePair {
|
||||
) -> Result<bool, NetQueuePairError> {
|
||||
let tx_tap_retry = self.tx.process_desc_chain(
|
||||
mem,
|
||||
&mut self.tap,
|
||||
&self.tap,
|
||||
queue,
|
||||
&mut self.tx_rate_limiter,
|
||||
self.access_platform.as_ref(),
|
||||
@@ -407,7 +407,7 @@ impl NetQueuePair {
|
||||
) -> Result<bool, NetQueuePairError> {
|
||||
self.rx_desc_avail = !self.rx.process_desc_chain(
|
||||
mem,
|
||||
&mut self.tap,
|
||||
&self.tap,
|
||||
queue,
|
||||
&mut self.rx_rate_limiter,
|
||||
self.access_platform.as_ref(),
|
||||
|
||||
@@ -203,7 +203,7 @@ impl Tap {
|
||||
let mut ifreq: net_gen::ifreq = Default::default();
|
||||
|
||||
// Get current config including name
|
||||
// SAFETY: IOCTL with correct arugments
|
||||
// SAFETY: IOCTL with correct arguments
|
||||
unsafe { Self::ioctl_with_mut_ref(&tap_file, net_gen::TUNGETIFF(), &mut ifreq)? };
|
||||
|
||||
// SAFETY: We only access one field of the ifru union
|
||||
|
||||
@@ -10,7 +10,7 @@ kvm = ["vfio-ioctls/kvm"]
|
||||
mshv = ["vfio-ioctls/mshv"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.71"
|
||||
anyhow = "1.0.75"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main", features = ["fam-wrappers"] }
|
||||
@@ -18,13 +18,13 @@ vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", defau
|
||||
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.164", features = ["derive"] }
|
||||
libc = "0.2.147"
|
||||
log = "0.4.20"
|
||||
serde = { version = "1.0.168", features = ["derive"] }
|
||||
thiserror = "1.0.40"
|
||||
versionize = "0.1.10"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-memory = { version = "0.13.1", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
|
||||
@@ -265,10 +265,10 @@ pub enum PciExpressCapabilityId {
|
||||
VendorSpecificExtendedCapability = 0x000b,
|
||||
ConfigurationAccessCorrelation = 0x000c,
|
||||
AccessControlServices = 0x000d,
|
||||
AlternativeRoutingIdentificationIntepretation = 0x000e,
|
||||
AlternativeRoutingIdentificationInterpretation = 0x000e,
|
||||
AddressTranslationServices = 0x000f,
|
||||
SingleRootIoVirtualization = 0x0010,
|
||||
DeprecatedMultiRootIoVirtualzation = 0x0011,
|
||||
DeprecatedMultiRootIoVirtualization = 0x0011,
|
||||
Multicast = 0x0012,
|
||||
PageRequestInterface = 0x0013,
|
||||
ReservedForAmd = 0x0014,
|
||||
@@ -279,7 +279,7 @@ pub enum PciExpressCapabilityId {
|
||||
SecondaryPciExpress = 0x0019,
|
||||
ProtocolMultiplexing = 0x001a,
|
||||
ProcessAddressSpaceId = 0x001b,
|
||||
LnRequestor = 0x001c,
|
||||
LnRequester = 0x001c,
|
||||
DownstreamPortContainment = 0x001d,
|
||||
L1PmSubstates = 0x001e,
|
||||
PrecisionTimeMeasurement = 0x001f,
|
||||
@@ -319,10 +319,10 @@ impl From<u16> for PciExpressCapabilityId {
|
||||
0x000b => PciExpressCapabilityId::VendorSpecificExtendedCapability,
|
||||
0x000c => PciExpressCapabilityId::ConfigurationAccessCorrelation,
|
||||
0x000d => PciExpressCapabilityId::AccessControlServices,
|
||||
0x000e => PciExpressCapabilityId::AlternativeRoutingIdentificationIntepretation,
|
||||
0x000e => PciExpressCapabilityId::AlternativeRoutingIdentificationInterpretation,
|
||||
0x000f => PciExpressCapabilityId::AddressTranslationServices,
|
||||
0x0010 => PciExpressCapabilityId::SingleRootIoVirtualization,
|
||||
0x0011 => PciExpressCapabilityId::DeprecatedMultiRootIoVirtualzation,
|
||||
0x0011 => PciExpressCapabilityId::DeprecatedMultiRootIoVirtualization,
|
||||
0x0012 => PciExpressCapabilityId::Multicast,
|
||||
0x0013 => PciExpressCapabilityId::PageRequestInterface,
|
||||
0x0014 => PciExpressCapabilityId::ReservedForAmd,
|
||||
@@ -333,7 +333,7 @@ impl From<u16> for PciExpressCapabilityId {
|
||||
0x0019 => PciExpressCapabilityId::SecondaryPciExpress,
|
||||
0x001a => PciExpressCapabilityId::ProtocolMultiplexing,
|
||||
0x001b => PciExpressCapabilityId::ProcessAddressSpaceId,
|
||||
0x001c => PciExpressCapabilityId::LnRequestor,
|
||||
0x001c => PciExpressCapabilityId::LnRequester,
|
||||
0x001d => PciExpressCapabilityId::DownstreamPortContainment,
|
||||
0x001e => PciExpressCapabilityId::L1PmSubstates,
|
||||
0x001f => PciExpressCapabilityId::PrecisionTimeMeasurement,
|
||||
|
||||
@@ -58,7 +58,8 @@ pub trait PciDevice: BusDevice {
|
||||
fn allocate_bars(
|
||||
&mut self,
|
||||
_allocator: &Arc<Mutex<SystemAllocator>>,
|
||||
_mmio_allocator: &mut AddressAllocator,
|
||||
_mmio32_allocator: &mut AddressAllocator,
|
||||
_mmio64_allocator: &mut AddressAllocator,
|
||||
_resources: Option<Vec<Resource>>,
|
||||
) -> Result<Vec<PciBarConfiguration>> {
|
||||
Ok(Vec::new())
|
||||
@@ -68,7 +69,8 @@ pub trait PciDevice: BusDevice {
|
||||
fn free_bars(
|
||||
&mut self,
|
||||
_allocator: &mut SystemAllocator,
|
||||
_mmio_allocator: &mut AddressAllocator,
|
||||
_mmio32_allocator: &mut AddressAllocator,
|
||||
_mmio64_allocator: &mut AddressAllocator,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -502,8 +502,8 @@ 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.
|
||||
/// In case msix table offset is not page size aligned, we need do some fixup to achieve it.
|
||||
/// Because 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 {
|
||||
if let Some(msix) = self.interrupt.msix.as_mut() {
|
||||
let msix_cap = &mut msix.cap;
|
||||
@@ -529,10 +529,13 @@ impl VfioCommon {
|
||||
}
|
||||
}
|
||||
|
||||
// The `allocator` argument is unused on `aarch64`
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) fn allocate_bars(
|
||||
&mut self,
|
||||
allocator: &Arc<Mutex<SystemAllocator>>,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
mmio32_allocator: &mut AddressAllocator,
|
||||
mmio64_allocator: &mut AddressAllocator,
|
||||
resources: Option<Vec<Resource>>,
|
||||
) -> Result<Vec<PciBarConfiguration>, PciDeviceError> {
|
||||
let mut bars = Vec::new();
|
||||
@@ -681,21 +684,15 @@ impl VfioCommon {
|
||||
}
|
||||
PciBarRegionType::Memory32BitRegion => {
|
||||
// BAR allocation must be naturally aligned
|
||||
allocator
|
||||
.lock()
|
||||
.unwrap()
|
||||
.allocate_mmio_hole_addresses(
|
||||
restored_bar_addr,
|
||||
region_size,
|
||||
Some(region_size),
|
||||
)
|
||||
mmio32_allocator
|
||||
.allocate(restored_bar_addr, region_size, Some(region_size))
|
||||
.ok_or(PciDeviceError::IoAllocationFailed(region_size))?
|
||||
}
|
||||
PciBarRegionType::Memory64BitRegion => {
|
||||
// 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
|
||||
mmio64_allocator
|
||||
.allocate(
|
||||
restored_bar_addr,
|
||||
region_size,
|
||||
@@ -742,10 +739,13 @@ impl VfioCommon {
|
||||
Ok(bars)
|
||||
}
|
||||
|
||||
// The `allocator` argument is unused on `aarch64`
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) fn free_bars(
|
||||
&mut self,
|
||||
allocator: &mut SystemAllocator,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
mmio32_allocator: &mut AddressAllocator,
|
||||
mmio64_allocator: &mut AddressAllocator,
|
||||
) -> Result<(), PciDeviceError> {
|
||||
for region in self.mmio_regions.iter() {
|
||||
match region.type_ {
|
||||
@@ -756,10 +756,10 @@ impl VfioCommon {
|
||||
error!("I/O region is not supported");
|
||||
}
|
||||
PciBarRegionType::Memory32BitRegion => {
|
||||
allocator.free_mmio_hole_addresses(region.start, region.length);
|
||||
mmio32_allocator.free(region.start, region.length);
|
||||
}
|
||||
PciBarRegionType::Memory64BitRegion => {
|
||||
mmio_allocator.free(region.start, region.length);
|
||||
mmio64_allocator.free(region.start, region.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -911,7 +911,7 @@ impl VfioCommon {
|
||||
let cap_next: u16 = ((ext_cap_hdr >> 20) & 0xfff) as u16;
|
||||
|
||||
match PciExpressCapabilityId::from(cap_id) {
|
||||
PciExpressCapabilityId::AlternativeRoutingIdentificationIntepretation
|
||||
PciExpressCapabilityId::AlternativeRoutingIdentificationInterpretation
|
||||
| PciExpressCapabilityId::ResizeableBar
|
||||
| PciExpressCapabilityId::SingleRootIoVirtualization => {
|
||||
let reg_idx = (current_offset / 4) as usize;
|
||||
@@ -1411,7 +1411,7 @@ impl VfioPciDevice {
|
||||
// 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. If these region don't align
|
||||
// with pagesize, we can achive it by enlarging its range.
|
||||
// with pagesize, we can achieve 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.
|
||||
@@ -1694,19 +1694,22 @@ impl PciDevice for VfioPciDevice {
|
||||
fn allocate_bars(
|
||||
&mut self,
|
||||
allocator: &Arc<Mutex<SystemAllocator>>,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
mmio32_allocator: &mut AddressAllocator,
|
||||
mmio64_allocator: &mut AddressAllocator,
|
||||
resources: Option<Vec<Resource>>,
|
||||
) -> Result<Vec<PciBarConfiguration>, PciDeviceError> {
|
||||
self.common
|
||||
.allocate_bars(allocator, mmio_allocator, resources)
|
||||
.allocate_bars(allocator, mmio32_allocator, mmio64_allocator, resources)
|
||||
}
|
||||
|
||||
fn free_bars(
|
||||
&mut self,
|
||||
allocator: &mut SystemAllocator,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
mmio32_allocator: &mut AddressAllocator,
|
||||
mmio64_allocator: &mut AddressAllocator,
|
||||
) -> Result<(), PciDeviceError> {
|
||||
self.common.free_bars(allocator, mmio_allocator)
|
||||
self.common
|
||||
.free_bars(allocator, mmio32_allocator, mmio64_allocator)
|
||||
}
|
||||
|
||||
fn write_config_register(
|
||||
|
||||
@@ -397,19 +397,22 @@ impl PciDevice for VfioUserPciDevice {
|
||||
fn allocate_bars(
|
||||
&mut self,
|
||||
allocator: &Arc<Mutex<SystemAllocator>>,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
mmio32_allocator: &mut AddressAllocator,
|
||||
mmio64_allocator: &mut AddressAllocator,
|
||||
resources: Option<Vec<Resource>>,
|
||||
) -> Result<Vec<PciBarConfiguration>, PciDeviceError> {
|
||||
self.common
|
||||
.allocate_bars(allocator, mmio_allocator, resources)
|
||||
.allocate_bars(allocator, mmio32_allocator, mmio64_allocator, resources)
|
||||
}
|
||||
|
||||
fn free_bars(
|
||||
&mut self,
|
||||
allocator: &mut SystemAllocator,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
mmio32_allocator: &mut AddressAllocator,
|
||||
mmio64_allocator: &mut AddressAllocator,
|
||||
) -> Result<(), PciDeviceError> {
|
||||
self.common.free_bars(allocator, mmio_allocator)
|
||||
self.common
|
||||
.free_bars(allocator, mmio32_allocator, mmio64_allocator)
|
||||
}
|
||||
|
||||
fn as_any(&mut self) -> &mut dyn Any {
|
||||
|
||||
@@ -6,10 +6,10 @@ edition = "2021"
|
||||
build = "../build.rs"
|
||||
|
||||
[dependencies]
|
||||
argh = "0.1.9"
|
||||
clap = { version = "4.4.7", features = ["wrap_help"] }
|
||||
dirs = "5.0.0"
|
||||
serde = { version = "1.0.164", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.96"
|
||||
serde = { version = "1.0.168", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.107"
|
||||
test_infra = { path = "../test_infra" }
|
||||
thiserror = "1.0.40"
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
@@ -8,11 +8,11 @@ extern crate test_infra;
|
||||
|
||||
mod performance_tests;
|
||||
|
||||
use argh::FromArgs;
|
||||
use clap::{Arg, ArgAction, Command as ClapCommand};
|
||||
use performance_tests::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fmt,
|
||||
env, fmt,
|
||||
process::Command,
|
||||
sync::{mpsc::channel, Arc},
|
||||
thread,
|
||||
@@ -639,41 +639,45 @@ 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 opts: Options = argh::from_env();
|
||||
|
||||
if opts.version {
|
||||
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILD_VERSION"));
|
||||
return;
|
||||
}
|
||||
let cmd_arguments = ClapCommand::new("performance-metrics")
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
.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 available 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),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("timeout")
|
||||
.long("timeout")
|
||||
.help("Override test timeout, Ex. --timeout 5")
|
||||
.num_args(1),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
// 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
|
||||
@@ -683,7 +687,7 @@ fn main() {
|
||||
.filter(|t| !(cfg!(target_arch = "aarch64") && t.name == "virtio_net_latency_us"))
|
||||
.collect();
|
||||
|
||||
if opts.list_tests {
|
||||
if cmd_arguments.get_flag("list-tests") {
|
||||
for test in test_list.iter() {
|
||||
println!("\"{}\" ({})", test.name, test.control);
|
||||
}
|
||||
@@ -691,7 +695,10 @@ fn main() {
|
||||
return;
|
||||
}
|
||||
|
||||
let test_filter = opts.keywords.iter().collect::<Vec<&String>>();
|
||||
let test_filter = match cmd_arguments.get_many::<String>("test-filter") {
|
||||
Some(s) => s.collect(),
|
||||
None => Vec::new(),
|
||||
};
|
||||
|
||||
// Run performance tests sequentially and report results (in both readable/json format)
|
||||
let mut metrics_report: MetricsReport = Default::default();
|
||||
@@ -699,8 +706,16 @@ fn main() {
|
||||
init_tests();
|
||||
|
||||
let overrides = Arc::new(PerformanceTestOverrides {
|
||||
test_iterations: opts.iterations,
|
||||
test_timeout: opts.timeout,
|
||||
test_iterations: cmd_arguments
|
||||
.get_one::<String>("iterations")
|
||||
.map(|s| s.parse())
|
||||
.transpose()
|
||||
.unwrap_or_default(),
|
||||
test_timeout: cmd_arguments
|
||||
.get_one::<String>("timeout")
|
||||
.map(|s| s.parse())
|
||||
.transpose()
|
||||
.unwrap_or_default(),
|
||||
});
|
||||
|
||||
for test in test_list.iter() {
|
||||
@@ -719,18 +734,19 @@ fn main() {
|
||||
|
||||
cleanup_tests();
|
||||
|
||||
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())
|
||||
};
|
||||
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())
|
||||
};
|
||||
|
||||
report_file
|
||||
.write_all(
|
||||
|
||||
@@ -358,13 +358,11 @@ 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()
|
||||
|
||||
@@ -4,6 +4,6 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.139"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.147"
|
||||
log = "0.4.20"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
@@ -46,9 +46,11 @@
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use std::io;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Mutex;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{fmt, io};
|
||||
use vmm_sys_util::timerfd::TimerFd;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -271,27 +273,27 @@ pub enum BucketUpdate {
|
||||
/// implementation. These events are meant to be consumed by the user of this struct.
|
||||
/// On each such event, the user must call the `event_handler()` method.
|
||||
pub struct RateLimiter {
|
||||
inner: Mutex<RateLimiterInner>,
|
||||
|
||||
// Internal flag that quickly determines timer state.
|
||||
timer_active: AtomicBool,
|
||||
}
|
||||
|
||||
struct RateLimiterInner {
|
||||
bandwidth: Option<TokenBucket>,
|
||||
ops: Option<TokenBucket>,
|
||||
|
||||
timer_fd: TimerFd,
|
||||
// Internal flag that quickly determines timer state.
|
||||
timer_active: bool,
|
||||
}
|
||||
|
||||
impl PartialEq for RateLimiter {
|
||||
fn eq(&self, other: &RateLimiter) -> bool {
|
||||
self.bandwidth == other.bandwidth && self.ops == other.ops
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for RateLimiter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"RateLimiter {{ bandwidth: {:?}, ops: {:?} }}",
|
||||
self.bandwidth, self.ops
|
||||
)
|
||||
impl RateLimiterInner {
|
||||
// Arm the timer of the rate limiter with the provided `Duration` (which will fire only once).
|
||||
fn activate_timer(&mut self, dur: Duration, flag: &AtomicBool) {
|
||||
// Panic when failing to arm the timer (same handling in crate TimerFd::set_state())
|
||||
self.timer_fd
|
||||
.reset(dur, None)
|
||||
.expect("Can't arm the timer (unexpected 'timerfd_settime' failure).");
|
||||
flag.store(true, Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,35 +357,28 @@ impl RateLimiter {
|
||||
}
|
||||
|
||||
Ok(RateLimiter {
|
||||
bandwidth: bytes_token_bucket,
|
||||
ops: ops_token_bucket,
|
||||
timer_fd,
|
||||
timer_active: false,
|
||||
inner: Mutex::new(RateLimiterInner {
|
||||
bandwidth: bytes_token_bucket,
|
||||
ops: ops_token_bucket,
|
||||
timer_fd,
|
||||
}),
|
||||
timer_active: AtomicBool::new(false),
|
||||
})
|
||||
}
|
||||
|
||||
// Arm the timer of the rate limiter with the provided `Duration` (which will fire only once).
|
||||
fn activate_timer(&mut self, dur: Duration) {
|
||||
// Panic when failing to arm the timer (same handling in crate TimerFd::set_state())
|
||||
self.timer_fd
|
||||
.reset(dur, None)
|
||||
.expect("Can't arm the timer (unexpected 'timerfd_settime' failure).");
|
||||
self.timer_active = true;
|
||||
}
|
||||
|
||||
/// Attempts to consume tokens and returns whether that is possible.
|
||||
///
|
||||
/// If rate limiting is disabled on provided `token_type`, this function will always succeed.
|
||||
pub fn consume(&mut self, tokens: u64, token_type: TokenType) -> bool {
|
||||
pub fn consume(&self, tokens: u64, token_type: TokenType) -> bool {
|
||||
// If the timer is active, we can't consume tokens from any bucket and the function fails.
|
||||
if self.timer_active {
|
||||
if self.is_blocked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut guard = self.inner.lock().unwrap();
|
||||
// Identify the required token bucket.
|
||||
let token_bucket = match token_type {
|
||||
TokenType::Bytes => self.bandwidth.as_mut(),
|
||||
TokenType::Ops => self.ops.as_mut(),
|
||||
TokenType::Bytes => guard.bandwidth.as_mut(),
|
||||
TokenType::Ops => guard.ops.as_mut(),
|
||||
};
|
||||
// Try to consume from the token bucket.
|
||||
if let Some(bucket) = token_bucket {
|
||||
@@ -393,8 +388,8 @@ impl RateLimiter {
|
||||
// register a timer to replenish the bucket and resume processing;
|
||||
// make sure there is only one running timer for this limiter.
|
||||
BucketReduction::Failure => {
|
||||
if !self.timer_active {
|
||||
self.activate_timer(TIMER_REFILL_DUR);
|
||||
if !self.is_blocked() {
|
||||
guard.activate_timer(TIMER_REFILL_DUR, &self.timer_active);
|
||||
}
|
||||
false
|
||||
}
|
||||
@@ -409,7 +404,10 @@ impl RateLimiter {
|
||||
// order to enforce the bandwidth limit we need to prevent
|
||||
// further calls to the rate limiter for
|
||||
// `ratio * refill_time` milliseconds.
|
||||
self.activate_timer(Duration::from_millis((ratio * refill_time as f64) as u64));
|
||||
guard.activate_timer(
|
||||
Duration::from_millis((ratio * refill_time as f64) as u64),
|
||||
&self.timer_active,
|
||||
);
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -424,11 +422,12 @@ impl RateLimiter {
|
||||
///
|
||||
/// Can be used to *manually* add tokens to a bucket. Useful for reverting a
|
||||
/// `consume()` if needed.
|
||||
pub fn manual_replenish(&mut self, tokens: u64, token_type: TokenType) {
|
||||
pub fn manual_replenish(&self, tokens: u64, token_type: TokenType) {
|
||||
let mut guard = self.inner.lock().unwrap();
|
||||
// Identify the required token bucket.
|
||||
let token_bucket = match token_type {
|
||||
TokenType::Bytes => self.bandwidth.as_mut(),
|
||||
TokenType::Ops => self.ops.as_mut(),
|
||||
TokenType::Bytes => guard.bandwidth.as_mut(),
|
||||
TokenType::Ops => guard.ops.as_mut(),
|
||||
};
|
||||
// Add tokens to the token bucket.
|
||||
if let Some(bucket) = token_bucket {
|
||||
@@ -442,7 +441,7 @@ impl RateLimiter {
|
||||
/// budget for it.
|
||||
/// An event will be generated on the exported FD when the limiter 'unblocks'.
|
||||
pub fn is_blocked(&self) -> bool {
|
||||
self.timer_active
|
||||
self.timer_active.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// This function needs to be called every time there is an event on the
|
||||
@@ -451,11 +450,12 @@ impl RateLimiter {
|
||||
/// # Errors
|
||||
///
|
||||
/// If the rate limiter is disabled or is not blocked, an error is returned.
|
||||
pub fn event_handler(&mut self) -> Result<(), Error> {
|
||||
pub fn event_handler(&self) -> Result<(), Error> {
|
||||
let mut guard = self.inner.lock().unwrap();
|
||||
loop {
|
||||
// Note: As we manually added the `O_NONBLOCK` flag to the FD, the following
|
||||
// `timer_fd::wait()` won't block (which is different from its default behavior.)
|
||||
match self.timer_fd.wait() {
|
||||
match guard.timer_fd.wait() {
|
||||
Err(e) => {
|
||||
let err: std::io::Error = e.into();
|
||||
match err.kind() {
|
||||
@@ -469,7 +469,7 @@ impl RateLimiter {
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.timer_active = false;
|
||||
self.timer_active.store(false, Ordering::Relaxed);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@@ -479,27 +479,18 @@ impl RateLimiter {
|
||||
/// Updates the parameters of the token buckets associated with this RateLimiter.
|
||||
// TODO: Please note that, right now, the buckets become full after being updated.
|
||||
pub fn update_buckets(&mut self, bytes: BucketUpdate, ops: BucketUpdate) {
|
||||
let mut guard = self.inner.lock().unwrap();
|
||||
match bytes {
|
||||
BucketUpdate::Disabled => self.bandwidth = None,
|
||||
BucketUpdate::Update(tb) => self.bandwidth = Some(tb),
|
||||
BucketUpdate::Disabled => guard.bandwidth = None,
|
||||
BucketUpdate::Update(tb) => guard.bandwidth = Some(tb),
|
||||
BucketUpdate::None => (),
|
||||
};
|
||||
match ops {
|
||||
BucketUpdate::Disabled => self.ops = None,
|
||||
BucketUpdate::Update(tb) => self.ops = Some(tb),
|
||||
BucketUpdate::Disabled => guard.ops = None,
|
||||
BucketUpdate::Update(tb) => guard.ops = Some(tb),
|
||||
BucketUpdate::None => (),
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns an immutable view of the inner bandwidth token bucket.
|
||||
pub fn bandwidth(&self) -> Option<&TokenBucket> {
|
||||
self.bandwidth.as_ref()
|
||||
}
|
||||
|
||||
/// Returns an immutable view of the inner ops token bucket.
|
||||
pub fn ops(&self) -> Option<&TokenBucket> {
|
||||
self.ops.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for RateLimiter {
|
||||
@@ -510,7 +501,8 @@ impl AsRawFd for RateLimiter {
|
||||
/// Will return a negative value if rate limiting is disabled on both
|
||||
/// token types.
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.timer_fd.as_raw_fd()
|
||||
let guard = self.inner.lock().unwrap();
|
||||
guard.timer_fd.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,6 +517,7 @@ impl Default for RateLimiter {
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use std::fmt;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -557,11 +550,33 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
impl RateLimiter {
|
||||
fn get_token_bucket(&self, token_type: TokenType) -> Option<&TokenBucket> {
|
||||
match token_type {
|
||||
TokenType::Bytes => self.bandwidth.as_ref(),
|
||||
TokenType::Ops => self.ops.as_ref(),
|
||||
}
|
||||
pub fn bandwidth(&self) -> Option<TokenBucket> {
|
||||
let guard = self.inner.lock().unwrap();
|
||||
guard.bandwidth.clone()
|
||||
}
|
||||
|
||||
pub fn ops(&self) -> Option<TokenBucket> {
|
||||
let guard = self.inner.lock().unwrap();
|
||||
guard.ops.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for RateLimiter {
|
||||
fn eq(&self, other: &RateLimiter) -> bool {
|
||||
let self_guard = self.inner.lock().unwrap();
|
||||
let other_guard = other.inner.lock().unwrap();
|
||||
self_guard.bandwidth == other_guard.bandwidth && self_guard.ops == other_guard.ops
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for RateLimiter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let guard = self.inner.lock().unwrap();
|
||||
write!(
|
||||
f,
|
||||
"RateLimiter {{ bandwidth: {:?}, ops: {:?} }}",
|
||||
guard.bandwidth, guard.ops
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -640,7 +655,7 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_rate_limiter_default() {
|
||||
let mut l = RateLimiter::default();
|
||||
let l = RateLimiter::default();
|
||||
|
||||
// limiter should not be blocked
|
||||
assert!(!l.is_blocked());
|
||||
@@ -659,14 +674,13 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn test_rate_limiter_new() {
|
||||
let l = RateLimiter::new(1000, 1001, 1002, 1003, 1004, 1005).unwrap();
|
||||
|
||||
let bw = l.bandwidth.unwrap();
|
||||
let bw = l.bandwidth().unwrap();
|
||||
assert_eq!(bw.capacity(), 1000);
|
||||
assert_eq!(bw.one_time_burst(), 1001);
|
||||
assert_eq!(bw.refill_time_ms(), 1002);
|
||||
assert_eq!(bw.budget(), 1000);
|
||||
|
||||
let ops = l.ops.unwrap();
|
||||
let ops = l.ops().unwrap();
|
||||
assert_eq!(ops.capacity(), 1003);
|
||||
assert_eq!(ops.one_time_burst(), 1004);
|
||||
assert_eq!(ops.refill_time_ms(), 1005);
|
||||
@@ -676,20 +690,20 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn test_rate_limiter_manual_replenish() {
|
||||
// rate limiter with limit of 1000 bytes/s and 1000 ops/s
|
||||
let mut l = RateLimiter::new(1000, 0, 1000, 1000, 0, 1000).unwrap();
|
||||
let l = RateLimiter::new(1000, 0, 1000, 1000, 0, 1000).unwrap();
|
||||
|
||||
// consume 123 bytes
|
||||
assert!(l.consume(123, TokenType::Bytes));
|
||||
l.manual_replenish(23, TokenType::Bytes);
|
||||
{
|
||||
let bytes_tb = l.get_token_bucket(TokenType::Bytes).unwrap();
|
||||
let bytes_tb = l.bandwidth().unwrap();
|
||||
assert_eq!(bytes_tb.budget(), 900);
|
||||
}
|
||||
// consume 123 ops
|
||||
assert!(l.consume(123, TokenType::Ops));
|
||||
l.manual_replenish(23, TokenType::Ops);
|
||||
{
|
||||
let bytes_tb = l.get_token_bucket(TokenType::Ops).unwrap();
|
||||
let bytes_tb = l.ops().unwrap();
|
||||
assert_eq!(bytes_tb.budget(), 900);
|
||||
}
|
||||
}
|
||||
@@ -697,7 +711,7 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn test_rate_limiter_bandwidth() {
|
||||
// rate limiter with limit of 1000 bytes/s
|
||||
let mut l = RateLimiter::new(1000, 0, 1000, 0, 0, 0).unwrap();
|
||||
let l = RateLimiter::new(1000, 0, 1000, 0, 0, 0).unwrap();
|
||||
|
||||
// limiter should not be blocked
|
||||
assert!(!l.is_blocked());
|
||||
@@ -730,7 +744,7 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn test_rate_limiter_ops() {
|
||||
// rate limiter with limit of 1000 ops/s
|
||||
let mut l = RateLimiter::new(0, 0, 0, 1000, 0, 1000).unwrap();
|
||||
let l = RateLimiter::new(0, 0, 0, 1000, 0, 1000).unwrap();
|
||||
|
||||
// limiter should not be blocked
|
||||
assert!(!l.is_blocked());
|
||||
@@ -763,7 +777,7 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn test_rate_limiter_full() {
|
||||
// rate limiter with limit of 1000 bytes/s and 1000 ops/s
|
||||
let mut l = RateLimiter::new(1000, 0, 1000, 1000, 0, 1000).unwrap();
|
||||
let l = RateLimiter::new(1000, 0, 1000, 1000, 0, 1000).unwrap();
|
||||
|
||||
// limiter should not be blocked
|
||||
assert!(!l.is_blocked());
|
||||
@@ -799,7 +813,7 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn test_rate_limiter_overconsumption() {
|
||||
// initialize the rate limiter
|
||||
let mut l = RateLimiter::new(1000, 0, 1000, 1000, 0, 1000).unwrap();
|
||||
let l = RateLimiter::new(1000, 0, 1000, 1000, 0, 1000).unwrap();
|
||||
// try to consume 2.5x the bucket size
|
||||
// we are "borrowing" 1.5x the bucket size in tokens since
|
||||
// the bucket is full
|
||||
@@ -818,7 +832,7 @@ pub(crate) mod tests {
|
||||
assert!(!l.is_blocked());
|
||||
|
||||
// reset the rate limiter
|
||||
let mut l = RateLimiter::new(1000, 0, 1000, 1000, 0, 1000).unwrap();
|
||||
let l = RateLimiter::new(1000, 0, 1000, 1000, 0, 1000).unwrap();
|
||||
// try to consume 1.5x the bucket size
|
||||
// we are "borrowing" 1.5x the bucket size in tokens since
|
||||
// the bucket is full, should arm the timer to 0.5x replenish
|
||||
@@ -846,7 +860,7 @@ pub(crate) mod tests {
|
||||
assert!(!l.consume(100, TokenType::Bytes));
|
||||
|
||||
// after waiting out the full duration, rate limiter should be
|
||||
// availale again
|
||||
// available again
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
assert!(l.event_handler().is_ok());
|
||||
assert!(!l.is_blocked());
|
||||
@@ -857,12 +871,12 @@ pub(crate) mod tests {
|
||||
fn test_update_buckets() {
|
||||
let mut x = RateLimiter::new(1000, 2000, 1000, 10, 20, 1000).unwrap();
|
||||
|
||||
let initial_bw = x.bandwidth.clone();
|
||||
let initial_ops = x.ops.clone();
|
||||
let initial_bw = x.bandwidth();
|
||||
let initial_ops = x.ops();
|
||||
|
||||
x.update_buckets(BucketUpdate::None, BucketUpdate::None);
|
||||
assert_eq!(x.bandwidth, initial_bw);
|
||||
assert_eq!(x.ops, initial_ops);
|
||||
assert_eq!(x.bandwidth(), initial_bw);
|
||||
assert_eq!(x.ops(), initial_ops);
|
||||
|
||||
let new_bw = TokenBucket::new(123, 0, 57).unwrap();
|
||||
let new_ops = TokenBucket::new(321, 12346, 89).unwrap();
|
||||
@@ -871,18 +885,21 @@ pub(crate) mod tests {
|
||||
BucketUpdate::Update(new_ops.clone()),
|
||||
);
|
||||
|
||||
// We have manually adjust the last_update field, because it changes when update_buckets()
|
||||
// constructs new buckets (and thus gets a different value for last_update). We do this so
|
||||
// it makes sense to test the following assertions.
|
||||
x.bandwidth.as_mut().unwrap().last_update = new_bw.last_update;
|
||||
x.ops.as_mut().unwrap().last_update = new_ops.last_update;
|
||||
{
|
||||
let mut guard = x.inner.lock().unwrap();
|
||||
// We have manually adjust the last_update field, because it changes when update_buckets()
|
||||
// constructs new buckets (and thus gets a different value for last_update). We do this so
|
||||
// it makes sense to test the following assertions.
|
||||
guard.bandwidth.as_mut().unwrap().last_update = new_bw.last_update;
|
||||
guard.ops.as_mut().unwrap().last_update = new_ops.last_update;
|
||||
}
|
||||
|
||||
assert_eq!(x.bandwidth, Some(new_bw));
|
||||
assert_eq!(x.ops, Some(new_ops));
|
||||
assert_eq!(x.bandwidth(), Some(new_bw));
|
||||
assert_eq!(x.ops(), Some(new_ops));
|
||||
|
||||
x.update_buckets(BucketUpdate::Disabled, BucketUpdate::Disabled);
|
||||
assert_eq!(x.bandwidth, None);
|
||||
assert_eq!(x.ops, None);
|
||||
assert_eq!(x.bandwidth(), None);
|
||||
assert_eq!(x.ops(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
334
release-notes.md
334
release-notes.md
@@ -1,36 +1,60 @@
|
||||
- [v37.0](#v370)
|
||||
- [Long Term Support (LTS) Release](#long-term-support-lts-release)
|
||||
- [Improved VFIO Device Passthrough with Multiple PCI Segments](#improved-vfio-device-passthrough-with-multiple-pci-segments)
|
||||
- [Configurable Named TAP Devices](#configurable-named-tap-devices)
|
||||
- [TTY Output from Both Serial Device and Virtio Console](#tty-output-from-both-serial-device-and-virtio-console)
|
||||
- [Faster VM Restoration from Snapshots](#faster-vm-restoration-from-snapshots)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||
- [Contributors](#contributors)
|
||||
- [v36.0](#v360)
|
||||
- [Command Line Changes](#command-line-changes)
|
||||
- [Enabled Features Reported via API Endpoint and CLI](#enabled-features-reported-via-api-endpoint-and-cli)
|
||||
- [NUMA Support for PCI segments](#numa-support-for-pci-segments)
|
||||
- [CPU Topology Support on AMD Platforms](#cpu-topology-support-on-amd-platforms)
|
||||
- [Unix Socket Backend for Serial Port](#unix-socket-backend-for-serial-port)
|
||||
- [AIO Backend for Block Devices](#aio-backend-for-block-devices)
|
||||
- [Documentation Improvements](#documentation-improvements)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
||||
- [Contributors](#contributors-1)
|
||||
- [v35.0](#v350)
|
||||
- [`virtio-vsock` Support for Linux Guest Kernel v6.3+](#virtio-vsock-support-for-linux-guest-kernel-v63)
|
||||
- [User Specified Serial Number for `virtio-block`](#user-specified-serial-number-for-virtio-block)
|
||||
- [vCPU TSC Frequency Included in Migration State](#vcpu-tsc-frequency-included-in-migration-state)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
||||
- [Contributors](#contributors-2)
|
||||
- [v34.0](#v340)
|
||||
- [Paravirtualised Panic Device Support](#paravirtualised-panic-device-support)
|
||||
- [Improvements to VM Core Dump](#improvements-to-vm-core-dump)
|
||||
- [QCOW2 Support for Backing Files](#qcow2-support-for-backing-files)
|
||||
- [Minimum Host Kernel Bump](#minimum-host-kernel-bump)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||
- [Contributors](#contributors)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-3)
|
||||
- [Contributors](#contributors-3)
|
||||
- [v33.0](#v330)
|
||||
- [D-Bus based API](#d-bus-based-api)
|
||||
- [Expose Host CPU Cache Details for AArch64](#expose-host-cpu-cache-details-for-aarch64)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
||||
- [Contributors](#contributors-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-4)
|
||||
- [Contributors](#contributors-4)
|
||||
- [v32.0](#v320)
|
||||
- [Increased PCI Segment Limit](#increased-pci-segment-limit)
|
||||
- [API Changes](#api-changes)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
||||
- [Contributors](#contributors-2)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-5)
|
||||
- [Contributors](#contributors-5)
|
||||
- [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-3)
|
||||
- [Contributors](#contributors-3)
|
||||
- [Documentation Improvements](#documentation-improvements-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-6)
|
||||
- [Contributors](#contributors-6)
|
||||
- [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-4)
|
||||
- [Contributors](#contributors-4)
|
||||
- [Documentation Improvements](#documentation-improvements-2)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-7)
|
||||
- [Contributors](#contributors-7)
|
||||
- [v28.2](#v282)
|
||||
- [v29.0](#v290)
|
||||
- [Release Binary Supports Both MSHV and KVM](#release-binary-supports-both-mshv-and-kvm)
|
||||
@@ -40,20 +64,20 @@
|
||||
- [`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-5)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-8)
|
||||
- [Removals](#removals)
|
||||
- [Deprecations](#deprecations)
|
||||
- [Contributors](#contributors-5)
|
||||
- [Contributors](#contributors-8)
|
||||
- [v28.1](#v281)
|
||||
- [v28.0](#v280)
|
||||
- [Community Engagement (Reminder)](#community-engagement-reminder)
|
||||
- [Long Term Support (LTS) Release](#long-term-support-lts-release)
|
||||
- [Long Term Support (LTS) Release](#long-term-support-lts-release-1)
|
||||
- [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-6)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-9)
|
||||
- [Removals](#removals-1)
|
||||
- [Contributors](#contributors-6)
|
||||
- [Contributors](#contributors-9)
|
||||
- [v27.0](#v270)
|
||||
- [Community Engagement](#community-engagement)
|
||||
- [Prebuilt Packages](#prebuilt-packages)
|
||||
@@ -62,41 +86,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-7)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-10)
|
||||
- [Deprecations](#deprecations-1)
|
||||
- [Contributors](#contributors-7)
|
||||
- [Contributors](#contributors-10)
|
||||
- [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-8)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-11)
|
||||
- [Deprecations](#deprecations-2)
|
||||
- [Removals](#removals-2)
|
||||
- [Contributors](#contributors-8)
|
||||
- [Contributors](#contributors-11)
|
||||
- [v25.0](#v250)
|
||||
- [`ch-remote` Improvements](#ch-remote-improvements-1)
|
||||
- [VM "Coredump" Support](#vm-coredump-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-9)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-12)
|
||||
- [Removals](#removals-3)
|
||||
- [Contributors](#contributors-9)
|
||||
- [Contributors](#contributors-12)
|
||||
- [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-10)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-13)
|
||||
- [Notable Improvements](#notable-improvements)
|
||||
- [Deprecations](#deprecations-3)
|
||||
- [New on the Website](#new-on-the-website)
|
||||
- [Contributors](#contributors-10)
|
||||
- [Contributors](#contributors-13)
|
||||
- [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-11)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-14)
|
||||
- [Deprecations](#deprecations-4)
|
||||
- [Contributors](#contributors-11)
|
||||
- [Contributors](#contributors-14)
|
||||
- [v22.1](#v221)
|
||||
- [v22.0](#v220)
|
||||
- [GDB Debug Stub Support](#gdb-debug-stub-support)
|
||||
@@ -107,13 +131,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-12)
|
||||
- [Contributors](#contributors-12)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-15)
|
||||
- [Contributors](#contributors-15)
|
||||
- [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-13)
|
||||
- [Contributors](#contributors-13)
|
||||
- [Notable Bug fixes](#notable-bug-fixes-16)
|
||||
- [Contributors](#contributors-16)
|
||||
- [v20.2](#v202)
|
||||
- [v20.1](#v201)
|
||||
- [v20.0](#v200)
|
||||
@@ -122,8 +146,8 @@
|
||||
- [Improved VFIO support](#improved-vfio-support)
|
||||
- [Safer code](#safer-code)
|
||||
- [Extended documentation](#extended-documentation)
|
||||
- [Notable bug fixes](#notable-bug-fixes-14)
|
||||
- [Contributors](#contributors-14)
|
||||
- [Notable bug fixes](#notable-bug-fixes-17)
|
||||
- [Contributors](#contributors-17)
|
||||
- [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)
|
||||
@@ -131,8 +155,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-15)
|
||||
- [Contributors](#contributors-15)
|
||||
- [Notable bug fixes](#notable-bug-fixes-18)
|
||||
- [Contributors](#contributors-18)
|
||||
- [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)
|
||||
@@ -142,23 +166,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-16)
|
||||
- [Contributors](#contributors-16)
|
||||
- [Notable bug fixes](#notable-bug-fixes-19)
|
||||
- [Contributors](#contributors-19)
|
||||
- [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-17)
|
||||
- [Contributors](#contributors-17)
|
||||
- [Notable bug fixes](#notable-bug-fixes-20)
|
||||
- [Contributors](#contributors-20)
|
||||
- [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-18)
|
||||
- [Notable bug fixes](#notable-bug-fixes-21)
|
||||
- [Removed functionality](#removed-functionality)
|
||||
- [Contributors](#contributors-18)
|
||||
- [Contributors](#contributors-21)
|
||||
- [v15.0](#v150)
|
||||
- [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees)
|
||||
- [Network device rate limiting](#network-device-rate-limiting)
|
||||
@@ -166,7 +190,7 @@
|
||||
- [`--api-socket` supports file descriptor parameter](#--api-socket-supports-file-descriptor-parameter)
|
||||
- [Bug fixes](#bug-fixes)
|
||||
- [Deprecations](#deprecations-5)
|
||||
- [Contributors](#contributors-19)
|
||||
- [Contributors](#contributors-22)
|
||||
- [v0.14.1](#v0141)
|
||||
- [v0.14.0](#v0140)
|
||||
- [Structured event monitoring](#structured-event-monitoring)
|
||||
@@ -176,7 +200,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-20)
|
||||
- [Contributors](#contributors-23)
|
||||
- [v0.13.0](#v0130)
|
||||
- [Wider VFIO device support](#wider-vfio-device-support)
|
||||
- [Improved huge page support](#improved-huge-page-support)
|
||||
@@ -184,13 +208,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-21)
|
||||
- [Contributors](#contributors-24)
|
||||
- [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-22)
|
||||
- [Contributors](#contributors-25)
|
||||
- [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)
|
||||
@@ -202,15 +226,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-19)
|
||||
- [Contributors](#contributors-23)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-22)
|
||||
- [Contributors](#contributors-26)
|
||||
- [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-20)
|
||||
- [Contributors](#contributors-24)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-23)
|
||||
- [Contributors](#contributors-27)
|
||||
- [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)
|
||||
@@ -223,17 +247,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-21)
|
||||
- [Contributors](#contributors-25)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-24)
|
||||
- [Contributors](#contributors-28)
|
||||
- [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-22)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-25)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes)
|
||||
- [Contributors](#contributors-26)
|
||||
- [Contributors](#contributors-29)
|
||||
- [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)
|
||||
@@ -243,14 +267,14 @@
|
||||
- [`Seccomp` Sandboxing](#seccomp-sandboxing)
|
||||
- [Updated Distribution Support](#updated-distribution-support)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes-1)
|
||||
- [Contributors](#contributors-27)
|
||||
- [Contributors](#contributors-30)
|
||||
- [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-28)
|
||||
- [Contributors](#contributors-31)
|
||||
- [v0.5.1](#v051)
|
||||
- [v0.5.0](#v050)
|
||||
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
|
||||
@@ -258,7 +282,7 @@
|
||||
- [New Interrupt Management Framework](#new-interrupt-management-framework)
|
||||
- [Development Tools](#development-tools)
|
||||
- [Kata Containers Integration](#kata-containers-integration)
|
||||
- [Contributors](#contributors-29)
|
||||
- [Contributors](#contributors-32)
|
||||
- [v0.4.0](#v040)
|
||||
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
|
||||
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
|
||||
@@ -267,7 +291,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-30)
|
||||
- [Contributors](#contributors-33)
|
||||
- [v0.3.0](#v030)
|
||||
- [Block device offloading](#block-device-offloading)
|
||||
- [Network device backend](#network-device-backend)
|
||||
@@ -294,6 +318,196 @@
|
||||
- [Unit testing](#unit-testing)
|
||||
- [Integration tests parallelization](#integration-tests-parallelization)
|
||||
|
||||
# v37.0
|
||||
|
||||
This release has been tracked in our [roadmap
|
||||
project](https://github.com/orgs/cloud-hypervisor/projects/6) as iteration
|
||||
v37.0. The following user visible changes have been made:
|
||||
|
||||
### Long Term Support (LTS) Release
|
||||
|
||||
This release is a LTS release. Point releases for bug fixes will be made
|
||||
for the next 18 months; live migration and live upgrade will be
|
||||
supported between the point releases of the LTS.
|
||||
|
||||
### Multiple PCI segments Support for 32-bit VFIO devices
|
||||
|
||||
Now VFIO devices with 32-bit memory BARs can be attached to non-zero PCI
|
||||
segments on the guest, allowing users to have more 32-bit devices and
|
||||
assign such devices to appropriate NUMA nodes for better performance.
|
||||
|
||||
### Configurable Named TAP Devices
|
||||
|
||||
Named TAP devices now accepts IP configuration from users, such as IP
|
||||
and MAC address, as long as the named TAP device is created by Cloud
|
||||
Hypervisor (e.g. not existing TAP devices).
|
||||
|
||||
### TTY Output from Both Serial Device and Virtio Console
|
||||
|
||||
Now legacy serial device and virtio console can be set as TTY mode as
|
||||
the same time. This allows users to capture early boot logs with the
|
||||
legacy serial device without losing performance benefits of using
|
||||
virtio-console, when appropriate kernel configuration is used (such as
|
||||
using kernel command-line `console=hvc0 earlyprintk=ttyS0` on x86).
|
||||
|
||||
### Faster VM Restoration from Snapshots
|
||||
|
||||
The speed of VM restoration from snapshots is improved with a better
|
||||
implementation of deserializing JSON files.
|
||||
|
||||
### Notable Bug Fixes
|
||||
|
||||
* Fix aio backend behavior for block devices when writeback cache
|
||||
disabled (#5930)
|
||||
* Fix PvPanic device PCI BAR alignment (#5956)
|
||||
* Bug fix to OpenAPI specification file (#5967)
|
||||
* Error out early for live migration when TDX is enabled (#6025)
|
||||
|
||||
### Contributors
|
||||
|
||||
Many thanks to everyone who has contributed to our release:
|
||||
|
||||
* Bo Chen <chen.bo@intel.com>
|
||||
* Jinank Jain <jinankjain@microsoft.com>
|
||||
* Markus Sütter <markus.suetter@secunet.com>
|
||||
* Michael Zhao <michael.zhao@arm.com>
|
||||
* Muminul Islam <muislam@microsoft.com>
|
||||
* Rob Bradford <rbradford@rivosinc.com>
|
||||
* Rui Chang <rui.chang@arm.com>
|
||||
* Ruslan Mstoi <ruslan.mstoi@intel.com>
|
||||
* Thomas Barrett <tbarrett@crusoeenergy.com>
|
||||
* Wei Liu <liuwe@microsoft.com>
|
||||
* Yi Wang <foxywang@tencent.com>
|
||||
* Yong He <alexyonghe@tencent.com>
|
||||
|
||||
# v36.0
|
||||
|
||||
This release has been tracked in our [roadmap
|
||||
project](https://github.com/orgs/cloud-hypervisor/projects/6) as iteration
|
||||
v36.0. The following user visible changes have been made:
|
||||
|
||||
### Command Line Changes
|
||||
|
||||
We switched back to use the `clap` crate to create our command line,
|
||||
since the `argh` crate is barely maintained. There were several syntax
|
||||
changes:
|
||||
|
||||
* All `--option value` commands now are `--option=value`.
|
||||
* The `--disk DISK1 --disk DISK2` command now is `--disk DISK1 DISK2`.
|
||||
* The `-v -v -v`command now is `-vvv`.
|
||||
|
||||
Note: the released binary size increased around 0.3M due to this change.
|
||||
|
||||
### Enabled Features Reported via API Endpoint and CLI
|
||||
|
||||
Now the enabled (Cargo) features of the running Cloud Hypervisor
|
||||
instance can be queried via API endpoint (`/vmm.ping`) and CLI
|
||||
(`--version -v`).
|
||||
|
||||
### NUMA Support for PCI segments
|
||||
|
||||
The `--numa` command is augmented with a new option `pci_segment=`, so
|
||||
that users can define the relationship between PCI segments and NUMA
|
||||
nodes. Examples can be found from the [memory documentation](docs/memory.md)
|
||||
|
||||
### CPU Topology Support on AMD Platforms
|
||||
|
||||
Now the CPU topology on x86_64 platforms supports multiple vendors.
|
||||
|
||||
### Unix Socket Backend for Serial Port
|
||||
|
||||
The `--serial` command is augmented with a new option `socket=`, allowing
|
||||
users to access the serial port using a Unix socket.
|
||||
|
||||
### AIO Backend for Block Devices
|
||||
|
||||
An AIO backend is added for `virtio-block` devices to improve block
|
||||
device performance when the `io_uring` feature is not supported by the
|
||||
host Operating System.
|
||||
|
||||
### Documentation Improvements
|
||||
|
||||
* New [documentation](docs/coverage.md) for collecting coverage data
|
||||
* Various typo fixes
|
||||
|
||||
### Notable Bug Fixes
|
||||
|
||||
* Fix a deadlock when TDX is enabled (#5845)
|
||||
* Only advertise AMX feature bits to guest when the AMX cpu feature is
|
||||
enabled (#5834)
|
||||
* Correct default value for vCPU topology on AArch64 (#5893)
|
||||
|
||||
### Contributors
|
||||
|
||||
Many thanks to everyone who has contributed to our release:
|
||||
|
||||
* Anatol Belski <anbelski@linux.microsoft.com>
|
||||
* Bo Chen <chen.bo@intel.com>
|
||||
* Dario Nieuwenhuis <dirbaio@dirbaio.net>
|
||||
* Jianyong Wu <jianyong.wu@arm.com>
|
||||
* Jinank Jain <jinankjain@microsoft.com>
|
||||
* Muminul Islam <muislam@microsoft.com>
|
||||
* Praveen K Paladugu <prapal@linux.microsoft.com>
|
||||
* Ravi kumar Veeramally <ravikumar.veeramally@intel.com>
|
||||
* Rob Bradford <rbradford@rivosinc.com>
|
||||
* Thomas Barrett <tbarrett@crusoeenergy.com>
|
||||
* Wei Liu <liuwe@microsoft.com>
|
||||
* Yi Wang <foxywang@tencent.com>
|
||||
* dom.song <dom.song@amperecomputing.com>
|
||||
|
||||
# v35.0
|
||||
|
||||
This release has been tracked in our [roadmap
|
||||
project](https://github.com/orgs/cloud-hypervisor/projects/6) as iteration
|
||||
v35.0. The following user visible changes have been made:
|
||||
|
||||
### `virtio-vsock` Support for Linux Guest Kernel v6.3+
|
||||
|
||||
Since kernel v6.3, a vsock packet can be included in a single descriptor,
|
||||
instead of being split over two descriptors. Our `virtio-vsock` implementation
|
||||
now support both situations.
|
||||
|
||||
### User Specified Serial Number for `virtio-block`
|
||||
|
||||
A new option `serial` is added to the `--block` command that allows users to
|
||||
specify a serial number for block devices which will be guest visible.
|
||||
|
||||
### vCPU TSC Frequency Included in Migration State
|
||||
|
||||
This ensures migration works correctly between hosts that have different TSC
|
||||
frequencies if the guest is running with TSC as the source of timekeeping.
|
||||
|
||||
### Notable Bug Fixes
|
||||
|
||||
* Disallow concurrent CPU resizing (#5668)
|
||||
* Handle APIC EOI message for MSHV (#5681)
|
||||
* Use wrapping add for memory offset from instruction emulator (#5719)
|
||||
* Add global spell check via the 'typos' GitHub action (#5720)
|
||||
* Ensure probing reads are block size aligned (#5727)
|
||||
* Multiple bug fixes around the latency counter for block devices
|
||||
(#5712, #5750, #5762, #5763)
|
||||
* Replace unsound `static mut` with `once_cell` (#5772)
|
||||
|
||||
### Contributors
|
||||
|
||||
Many thanks to everyone who has contributed to our release:
|
||||
|
||||
* Alyssa Ross <hi@alyssa.is>
|
||||
* Anatol Belski <anbelski@linux.microsoft.com>
|
||||
* Bo Chen <chen.bo@intel.com>
|
||||
* Christian Blichmann <cblichmann@google.com>
|
||||
* Jianyong Wu <jianyong.wu@arm.com>
|
||||
* Jinank Jain <jinankjain@microsoft.com>
|
||||
* Julian Stecklina <julian.stecklina@cyberus-technology.de>
|
||||
* Omer Faruk Bayram <omer.faruk@sartura.hr>
|
||||
* Philipp Schuster <philipp.schuster@cyberus-technology.de>
|
||||
* Rob Bradford <rbradford@rivosinc.com>
|
||||
* Ruslan Mstoi <ruslan.mstoi@intel.com>
|
||||
* Thomas Barrett <tbarrett@crusoeenergy.com>
|
||||
* Wei Liu <liuwe@microsoft.com>
|
||||
* Yi Wang <foxywang@tencent.com>
|
||||
* zhongbingnan <zhongbingnan@bytedance.com>
|
||||
|
||||
# v34.0
|
||||
|
||||
This release has been tracked in our [roadmap
|
||||
@@ -873,7 +1087,7 @@ binary with the detection of the hypervisor to use made at runtime.
|
||||
### Notable Bug Fixes
|
||||
|
||||
* The prefetchable flag is preserved on BARs for VFIO devices (#4353, #4454)
|
||||
* PCI Express capabilties for functionality we do not support are now filtered
|
||||
* PCI Express capabilities for functionality we do not support are now filtered
|
||||
out (#4456)
|
||||
* GDB breakpoint support is more reliable (#4354, #4363)
|
||||
* `SIGINT` and `SIGTERM` signals are now handled before the VM has booted
|
||||
@@ -1228,10 +1442,10 @@ is now 5.15. (#3530)
|
||||
|
||||
### Notable Bug fixes
|
||||
|
||||
* Multiple fixes were made to the OpenAPI YAML file to match the implementaion (#3555,#3562)
|
||||
* Multiple fixes were made to the OpenAPI YAML file to match the implementation (#3555,#3562)
|
||||
* Avoid live migration deadlock when triggered during the kernel boot (#3585)
|
||||
* Support live migration within firmware (#3586)
|
||||
* Validate the `virtio-net` desciptor chain (#3548)
|
||||
* Validate the `virtio-net` descriptor chain (#3548)
|
||||
* `direct=on` (`O_DIRECT`) can now be used with a guest that makes unaligned accesses (e.g. firmware) (#3587)
|
||||
|
||||
### Contributors
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
FROM ubuntu:20.04 as dev
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG RUST_TOOLCHAIN="1.67.1"
|
||||
ARG RUST_TOOLCHAIN="1.70.0"
|
||||
ARG CLH_SRC_DIR="/cloud-hypervisor"
|
||||
ARG CLH_BUILD_DIR="$CLH_SRC_DIR/build"
|
||||
ARG CARGO_REGISTRY_DIR="$CLH_BUILD_DIR/cargo_registry"
|
||||
@@ -130,17 +130,18 @@ RUN echo 'source $CARGO_HOME/env' >> "$HOME"/.bashrc \
|
||||
RUN if [ "$TARGETARCH" = "amd64" ]; then \
|
||||
git clone https://github.com/spdk/spdk \
|
||||
&& cd spdk \
|
||||
&& git checkout 6301f8915de32baed10dba1eebed556a6749211a \
|
||||
&& git checkout ef8bcce58f3f02b79c0619a297e4f17e81e62b24 \
|
||||
&& git submodule update --init \
|
||||
&& apt-get update \
|
||||
&& ./scripts/pkgdep.sh \
|
||||
&& apt-get clean \
|
||||
&& ./configure --with-vfio-user \
|
||||
&& make -j $(nproc) TARGET_ARCHITECTURE=skylake \
|
||||
&& rm -rf /usr/local/bin/spdk-nvme \
|
||||
&& mkdir /usr/local/bin/spdk-nvme \
|
||||
&& cp ./build/bin/nvmf_tgt /usr/local/bin/spdk-nvme \
|
||||
&& cp ./scripts/rpc.py /usr/local/bin/spdk-nvme \
|
||||
&& cp -r ./scripts/rpc /usr/local/bin/spdk-nvme \
|
||||
&& cp -f ./build/bin/nvmf_tgt /usr/local/bin/spdk-nvme \
|
||||
&& cp -f ./scripts/rpc.py /usr/local/bin/spdk-nvme \
|
||||
&& cp -rf ./python /usr/local/bin \
|
||||
&& cd .. && rm -rf spdk; fi
|
||||
|
||||
# install swtpm only for x86_64 arch
|
||||
|
||||
@@ -639,7 +639,7 @@ CONFIG_VIRTUALIZATION=y
|
||||
CONFIG_KVM=y
|
||||
# CONFIG_KVM_WERROR is not set
|
||||
CONFIG_KVM_INTEL=y
|
||||
# CONFIG_KVM_AMD is not set
|
||||
CONFIG_KVM_AMD=y
|
||||
CONFIG_KVM_SMM=y
|
||||
# CONFIG_KVM_XEN is not set
|
||||
CONFIG_AS_AVX512=y
|
||||
|
||||
0
scripts/check-image-compatability.sh → scripts/check-image-compatibility.sh
Executable file → Normal file
0
scripts/check-image-compatability.sh → scripts/check-image-compatibility.sh
Executable file → Normal file
@@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
export BUILD_TARGET=${BUILD_TARGET-aarch64-unknown-linux-gnu}
|
||||
|
||||
WORKLOADS_DIR="$HOME/workloads"
|
||||
|
||||
mkdir -p "$WORKLOADS_DIR"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
CLI_NAME="Cloud Hypervisor"
|
||||
|
||||
CTR_IMAGE_TAG="ghcr.io/cloud-hypervisor/cloud-hypervisor"
|
||||
CTR_IMAGE_VERSION="20230804-0"
|
||||
CTR_IMAGE_VERSION="20231108-0"
|
||||
: "${CTR_IMAGE:=${CTR_IMAGE_TAG}:${CTR_IMAGE_VERSION}}"
|
||||
|
||||
DOCKER_RUNTIME="docker"
|
||||
@@ -282,10 +282,10 @@ cmd_build() {
|
||||
[ $build = "release" ] && cargo_args+=("--release")
|
||||
cargo_args+=(--target "$target")
|
||||
|
||||
rustflags=""
|
||||
rustflags="$RUSTFLAGS"
|
||||
target_cc=""
|
||||
if [ "$(uname -m)" = "aarch64" ] && [ "$libc" = "musl" ]; then
|
||||
rustflags="-C link-arg=-lgcc -C link_arg=-specs -C link_arg=/usr/lib/aarch64-linux-musl/musl-gcc.specs"
|
||||
rustflags="$rustflags -C link-arg=-lgcc -C link_arg=-specs -C link_arg=/usr/lib/aarch64-linux-musl/musl-gcc.specs"
|
||||
target_cc="musl-gcc"
|
||||
fi
|
||||
|
||||
@@ -396,6 +396,13 @@ cmd_tests() {
|
||||
process_volumes_args
|
||||
target="$(uname -m)-unknown-linux-${libc}"
|
||||
|
||||
rustflags="$RUSTFLAGS"
|
||||
target_cc=""
|
||||
if [ "$(uname -m)" = "aarch64" ] && [ "$libc" = "musl" ]; then
|
||||
rustflags="$rustflags -C link-arg=-lgcc -C link_arg=-specs -C link_arg=/usr/lib/aarch64-linux-musl/musl-gcc.specs"
|
||||
target_cc="musl-gcc"
|
||||
fi
|
||||
|
||||
if [[ "$unit" = true ]]; then
|
||||
say "Running unit tests for $target..."
|
||||
$DOCKER_RUNTIME run \
|
||||
@@ -406,6 +413,8 @@ cmd_tests() {
|
||||
--cap-add net_admin \
|
||||
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" $exported_volumes \
|
||||
--env BUILD_TARGET="$target" \
|
||||
--env RUSTFLAGS="$rustflags" \
|
||||
--env TARGET_CC="$target_cc" \
|
||||
"$CTR_IMAGE" \
|
||||
./scripts/run_unit_tests.sh "$@" || fix_dir_perms $? || exit $?
|
||||
fi
|
||||
@@ -424,7 +433,10 @@ cmd_tests() {
|
||||
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" $exported_volumes \
|
||||
--volume "$CLH_INTEGRATION_WORKLOADS:$CTR_CLH_INTEGRATION_WORKLOADS" \
|
||||
--env USER="root" \
|
||||
--env CH_LIBC="${libc}" \
|
||||
--env BUILD_TARGET="$target" \
|
||||
--env RUSTFLAGS="$rustflags" \
|
||||
--env TARGET_CC="$target_cc" \
|
||||
--env AUTH_DOWNLOAD_TOKEN="$AUTH_DOWNLOAD_TOKEN" \
|
||||
"$CTR_IMAGE" \
|
||||
dbus-run-session ./scripts/run_integration_tests_"$(uname -m)".sh "$@" || fix_dir_perms $? || exit $?
|
||||
fi
|
||||
@@ -443,7 +455,10 @@ cmd_tests() {
|
||||
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" $exported_volumes \
|
||||
--volume "$CLH_INTEGRATION_WORKLOADS:$CTR_CLH_INTEGRATION_WORKLOADS" \
|
||||
--env USER="root" \
|
||||
--env CH_LIBC="${libc}" \
|
||||
--env BUILD_TARGET="$target" \
|
||||
--env RUSTFLAGS="$rustflags" \
|
||||
--env TARGET_CC="$target_cc" \
|
||||
--env AUTH_DOWNLOAD_TOKEN="$AUTH_DOWNLOAD_TOKEN" \
|
||||
"$CTR_IMAGE" \
|
||||
./scripts/run_integration_tests_sgx.sh "$@" || fix_dir_perms $? || exit $?
|
||||
fi
|
||||
@@ -462,7 +477,10 @@ cmd_tests() {
|
||||
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" $exported_volumes \
|
||||
--volume "$CLH_INTEGRATION_WORKLOADS:$CTR_CLH_INTEGRATION_WORKLOADS" \
|
||||
--env USER="root" \
|
||||
--env CH_LIBC="${libc}" \
|
||||
--env BUILD_TARGET="$target" \
|
||||
--env RUSTFLAGS="$rustflags" \
|
||||
--env TARGET_CC="$target_cc" \
|
||||
--env AUTH_DOWNLOAD_TOKEN="$AUTH_DOWNLOAD_TOKEN" \
|
||||
"$CTR_IMAGE" \
|
||||
./scripts/run_integration_tests_vfio.sh "$@" || fix_dir_perms $? || exit $?
|
||||
fi
|
||||
@@ -481,7 +499,10 @@ cmd_tests() {
|
||||
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" $exported_volumes \
|
||||
--volume "$CLH_INTEGRATION_WORKLOADS:$CTR_CLH_INTEGRATION_WORKLOADS" \
|
||||
--env USER="root" \
|
||||
--env CH_LIBC="${libc}" \
|
||||
--env BUILD_TARGET="$target" \
|
||||
--env RUSTFLAGS="$rustflags" \
|
||||
--env TARGET_CC="$target_cc" \
|
||||
--env AUTH_DOWNLOAD_TOKEN="$AUTH_DOWNLOAD_TOKEN" \
|
||||
"$CTR_IMAGE" \
|
||||
./scripts/run_integration_tests_windows_"$(uname -m)".sh "$@" || fix_dir_perms $? || exit $?
|
||||
fi
|
||||
@@ -500,7 +521,10 @@ cmd_tests() {
|
||||
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" $exported_volumes \
|
||||
--volume "$CLH_INTEGRATION_WORKLOADS:$CTR_CLH_INTEGRATION_WORKLOADS" \
|
||||
--env USER="root" \
|
||||
--env CH_LIBC="${libc}" \
|
||||
--env BUILD_TARGET="$target" \
|
||||
--env RUSTFLAGS="$rustflags" \
|
||||
--env TARGET_CC="$target_cc" \
|
||||
--env AUTH_DOWNLOAD_TOKEN="$AUTH_DOWNLOAD_TOKEN" \
|
||||
"$CTR_IMAGE" \
|
||||
./scripts/run_integration_tests_live_migration.sh "$@" || fix_dir_perms $? || exit $?
|
||||
fi
|
||||
@@ -519,7 +543,10 @@ cmd_tests() {
|
||||
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" $exported_volumes \
|
||||
--volume "$CLH_INTEGRATION_WORKLOADS:$CTR_CLH_INTEGRATION_WORKLOADS" \
|
||||
--env USER="root" \
|
||||
--env CH_LIBC="${libc}" \
|
||||
--env BUILD_TARGET="$target" \
|
||||
--env RUSTFLAGS="$rustflags" \
|
||||
--env TARGET_CC="$target_cc" \
|
||||
--env AUTH_DOWNLOAD_TOKEN="$AUTH_DOWNLOAD_TOKEN" \
|
||||
"$CTR_IMAGE" \
|
||||
./scripts/run_integration_tests_rate_limiter.sh "$@" || fix_dir_perms $? || exit $?
|
||||
fi
|
||||
@@ -538,8 +565,11 @@ cmd_tests() {
|
||||
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" $exported_volumes \
|
||||
--volume "$CLH_INTEGRATION_WORKLOADS:$CTR_CLH_INTEGRATION_WORKLOADS" \
|
||||
--env USER="root" \
|
||||
--env CH_LIBC="${libc}" \
|
||||
--env BUILD_TARGET="$target" \
|
||||
--env RUSTFLAGS="$rustflags" \
|
||||
--env TARGET_CC="$target_cc" \
|
||||
--env RUST_BACKTRACE="${RUST_BACKTRACE}" \
|
||||
--env AUTH_DOWNLOAD_TOKEN="$AUTH_DOWNLOAD_TOKEN" \
|
||||
"$CTR_IMAGE" \
|
||||
./scripts/run_metrics.sh "$@" || fix_dir_perms $? || exit $?
|
||||
fi
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user