mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b0d2e796b | ||
|
|
42357c01f3 | ||
|
|
b7b5b9d7e6 | ||
|
|
a63e064004 | ||
|
|
62c1f39ea2 | ||
|
|
460ba718d4 | ||
|
|
c91a8e1324 | ||
|
|
1adfb7e9f8 | ||
|
|
8dd4d42053 | ||
|
|
3834b43878 | ||
|
|
52605cc0e4 | ||
|
|
92beda1e32 | ||
|
|
167fef382a | ||
|
|
5126e9b26e | ||
|
|
59bd682b1f | ||
|
|
8e3b351038 | ||
|
|
3f8d06b47e |
12
.github/workflows/build.yaml
vendored
12
.github/workflows/build.yaml
vendored
@@ -34,22 +34,22 @@ jobs:
|
||||
override: true
|
||||
|
||||
- name: Build (default features)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
run: cargo rustc --locked --bin cloud-hypervisor -- -D warnings
|
||||
|
||||
- name: Build (kvm)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "kvm" -- -D warnings
|
||||
|
||||
- name: Build (default features + tdx)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "tdx" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "tdx" -- -D warnings
|
||||
|
||||
- name: Build (default features + guest_debug)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "guest_debug" -- -D warnings
|
||||
|
||||
- name: Build (mshv)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv" -- -D warnings
|
||||
|
||||
- name: Build (mshv + kvm)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv,kvm" -- -D warnings
|
||||
|
||||
- name: Release Build (default features)
|
||||
run: cargo build --locked --all --release --target=${{ matrix.target }}
|
||||
|
||||
27
.github/workflows/quality.yaml
vendored
27
.github/workflows/quality.yaml
vendored
@@ -13,24 +13,15 @@ jobs:
|
||||
rust:
|
||||
- stable
|
||||
target:
|
||||
- aarch64-unknown-linux-gnu
|
||||
- aarch64-unknown-linux-musl
|
||||
- x86_64-unknown-linux-gnu
|
||||
- x86_64-unknown-linux-musl
|
||||
|
||||
- aarch64-unknown-linux-gnu
|
||||
experimental: [false]
|
||||
include:
|
||||
- rust: beta
|
||||
target: aarch64-unknown-linux-gnu
|
||||
experimental: true
|
||||
- rust: beta
|
||||
target: aarch64-unknown-linux-musl
|
||||
experimental: true
|
||||
- rust: beta
|
||||
target: x86_64-unknown-linux-gnu
|
||||
experimental: true
|
||||
- rust: beta
|
||||
target: x86_64-unknown-linux-musl
|
||||
target: aarch64-unknown-linux-gnu
|
||||
experimental: true
|
||||
steps:
|
||||
- name: Code checkout
|
||||
@@ -62,28 +53,28 @@ jobs:
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
args: --locked --all --all-targets --no-default-features --tests --features "kvm" -- -D warnings
|
||||
|
||||
- name: Clippy (default features)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --tests -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
args: --locked --all --all-targets --tests -- -D warnings
|
||||
|
||||
- name: Clippy (default features + guest_debug)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
args: --locked --all --all-targets --tests --features "guest_debug" -- -D warnings
|
||||
|
||||
- name: Clippy (default features + tracing)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --features "tracing" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
args: --locked --all --all-targets --tests --features "tracing" -- -D warnings
|
||||
|
||||
- name: Clippy (mshv)
|
||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||
@@ -91,7 +82,7 @@ jobs:
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
args: --locked --all --all-targets --no-default-features --tests --features "mshv" -- -D warnings
|
||||
|
||||
- name: Clippy (mshv + kvm)
|
||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||
@@ -99,7 +90,7 @@ jobs:
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --features "mshv,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
args: --locked --all --all-targets --no-default-features --tests --features "mshv,kvm" -- -D warnings
|
||||
|
||||
- name: Clippy (kvm + tdx)
|
||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||
@@ -107,7 +98,7 @@ jobs:
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --features "tdx,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||
args: --locked --all --all-targets --no-default-features --tests --features "tdx,kvm" -- -D warnings
|
||||
|
||||
- name: Check build did not modify any files
|
||||
run: test -z "$(git status --porcelain)"
|
||||
|
||||
13
.github/workflows/release.yaml
vendored
13
.github/workflows/release.yaml
vendored
@@ -3,6 +3,7 @@ on: [pull_request, create]
|
||||
|
||||
jobs:
|
||||
release:
|
||||
if: (github.event_name == 'create' && github.event.ref_type == 'tag') || github.event_name == 'pull_request'
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -25,15 +26,15 @@ jobs:
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
toolchain: "1.62"
|
||||
command: build
|
||||
args: --all --release --no-default-features --features "kvm,mshv" --target=x86_64-unknown-linux-gnu
|
||||
toolchain: "1.62"
|
||||
command: build
|
||||
args: --all --release --target=x86_64-unknown-linux-gnu
|
||||
- name: Static Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
toolchain: "1.62"
|
||||
command: build
|
||||
args: --all --release --no-default-features --features "kvm,mshv" --target=x86_64-unknown-linux-musl
|
||||
toolchain: "1.62"
|
||||
command: build
|
||||
args: --all --release --target=x86_64-unknown-linux-musl
|
||||
- name: Install Rust toolchain (aarch64-unknown-linux-musl)
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,4 +5,3 @@
|
||||
**/Cargo.lock
|
||||
**/rusty-tags.vi
|
||||
/rpm/SOURCES
|
||||
/.vscode
|
||||
|
||||
94
Cargo.lock
generated
94
Cargo.lock
generated
@@ -20,9 +20,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.68"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
|
||||
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -88,7 +88,6 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"qcow",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
@@ -108,9 +107,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.78"
|
||||
version = "1.0.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -120,9 +119,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.0.32"
|
||||
version = "4.0.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39"
|
||||
checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
@@ -144,7 +143,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "29.0.0"
|
||||
version = "28.2.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -409,11 +408,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iced-x86"
|
||||
version = "1.18.0"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dd04b950d75b3498320253b17fb92745b2cc79ead8814aede2f7c1bab858bec"
|
||||
checksum = "158f5204401d08f91d19176112146d75e99b3cf745092e268fa7be33e09adcec"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -443,9 +443,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.5.11"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9ddf18110cedc09617f5a965bdb51adb0c0fb02c791e4ab22f7a21bb25269b0"
|
||||
checksum = "7ba34abb5175052fc1a2227a10d2275b7386c9990167de9786c0b88d8b062330"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -462,9 +462,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.2"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
|
||||
checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"io-lifetimes",
|
||||
@@ -474,9 +474,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.5"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||
|
||||
[[package]]
|
||||
name = "kvm-bindings"
|
||||
@@ -507,9 +507,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.139"
|
||||
version = "0.2.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||
|
||||
[[package]]
|
||||
name = "libssh2-sys"
|
||||
@@ -548,9 +548,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.1.4"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||
checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
@@ -595,7 +595,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mshv-bindings"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#fb19bb30110c3a3268ad815897bba407fce1843c"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#10d0c5208cc41a3958e0fd31fabf2c88b18a97b5"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"serde",
|
||||
@@ -607,7 +607,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mshv-ioctls"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#fb19bb30110c3a3268ad815897bba407fce1843c"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#10d0c5208cc41a3958e0fd31fabf2c88b18a97b5"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mshv-bindings",
|
||||
@@ -678,9 +678,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.80"
|
||||
version = "0.9.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7"
|
||||
checksum = "5454462c0eced1e97f2ec09036abc8da362e66802f66fd20f86854d9d8cbcbc4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
@@ -713,9 +713,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.5"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
|
||||
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"instant",
|
||||
@@ -727,9 +727,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.11"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
|
||||
checksum = "cf1c2c742266c2f1041c914ba65355a83ae8747b05f208319784083583494b4b"
|
||||
|
||||
[[package]]
|
||||
name = "pci"
|
||||
@@ -926,9 +926,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -996,9 +996,9 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||
|
||||
[[package]]
|
||||
name = "remain"
|
||||
version = "0.2.6"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5704e2cda92fd54202f05430725317ba0ea7d0c96b246ca0a92e45177127ba3b"
|
||||
checksum = "1a81bc6a3aaeb180f767bd2bda8c03bac5b93b3b69a783c66f35d16a4df276cf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1051,24 +1051,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.16"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
|
||||
checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.151"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0"
|
||||
checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.151"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8"
|
||||
checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1155,6 +1155,12 @@ dependencies = [
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
@@ -1208,18 +1214,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.38"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
|
||||
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.38"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1251,9 +1257,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.6"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
|
||||
18
Cargo.toml
18
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cloud-hypervisor"
|
||||
version = "29.0.0"
|
||||
version = "28.2.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
default-run = "cloud-hypervisor"
|
||||
@@ -10,11 +10,6 @@ description = "Open source Virtual Machine Monitor (VMM) that runs on top of KVM
|
||||
homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
||||
# Minimum buildable version:
|
||||
# Keep in sync with version in .github/workflows/build.yaml
|
||||
# Policy on MSRV (see #4318):
|
||||
# Can only be bumped by:
|
||||
# a.) A dependency requires it,
|
||||
# b.) If we want to use a new feature and that MSRV is at least 6 months old,
|
||||
# c.) There is a security issue that is addressed by the toolchain update.
|
||||
rust-version = "1.60"
|
||||
|
||||
[profile.release]
|
||||
@@ -24,25 +19,28 @@ opt-level = "s"
|
||||
strip = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.68"
|
||||
anyhow = "1.0.66"
|
||||
api_client = { path = "api_client" }
|
||||
clap = { version = "4.0.32", features = ["wrap_help","cargo","string"] }
|
||||
clap = { version = "4.0.29", features = ["wrap_help","cargo","string"] }
|
||||
epoll = "4.3.1"
|
||||
event_monitor = { path = "event_monitor" }
|
||||
hypervisor = { path = "hypervisor" }
|
||||
libc = "0.2.139"
|
||||
libc = "0.2.138"
|
||||
log = { version = "0.4.17", features = ["std"] }
|
||||
option_parser = { path = "option_parser" }
|
||||
seccompiler = "0.3.0"
|
||||
serde_json = "1.0.89"
|
||||
signal-hook = "0.3.14"
|
||||
thiserror = "1.0.38"
|
||||
thiserror = "1.0.37"
|
||||
tpm = { path = "tpm"}
|
||||
tracer = { path = "tracer" }
|
||||
vmm = { path = "vmm" }
|
||||
vmm-sys-util = "0.11.0"
|
||||
vm-memory = "0.10.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "4.0.29", features = ["cargo"] }
|
||||
|
||||
# List of patched crates
|
||||
[patch.crates-io]
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx" }
|
||||
|
||||
153
Jenkinsfile
vendored
153
Jenkinsfile
vendored
@@ -10,16 +10,29 @@ pipeline {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage('Check if worker build can be skipped') {
|
||||
stage('Check for documentation only changes') {
|
||||
when {
|
||||
expression {
|
||||
return skipWorkerBuild()
|
||||
return docsFileOnly()
|
||||
}
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
runWorkers = false
|
||||
echo 'No changes requring a build'
|
||||
echo 'Documentation only changes, no need to run the CI'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Check for fuzzer files only changes') {
|
||||
when {
|
||||
expression {
|
||||
return fuzzFileOnly()
|
||||
}
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
runWorkers = false
|
||||
echo 'Fuzzer cargo files only changes, no need to run the CI'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -268,113 +281,6 @@ pipeline {
|
||||
}
|
||||
}
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -419,31 +325,24 @@ def installAzureCli(distro, arch) {
|
||||
sh 'sudo apt install -y azure-cli'
|
||||
}
|
||||
|
||||
def boolean skipWorkerBuild() {
|
||||
def boolean docsFileOnly() {
|
||||
if (env.CHANGE_TARGET == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (sh(
|
||||
return sh(
|
||||
returnStatus: true,
|
||||
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v '\\.md'"
|
||||
) != 0) {
|
||||
return true
|
||||
) != 0
|
||||
}
|
||||
|
||||
def boolean fuzzFileOnly() {
|
||||
if (env.CHANGE_TARGET == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (sh(
|
||||
return sh(
|
||||
returnStatus: true,
|
||||
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v -E 'fuzz/'"
|
||||
) != 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (sh(
|
||||
returnStatus: true,
|
||||
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v -E '.github/'"
|
||||
) != 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
) != 0
|
||||
}
|
||||
|
||||
85
README.md
85
README.md
@@ -69,12 +69,10 @@ Cloud Hypervisor supports `64-bit Linux` and Windows 10/Windows Server 2019.
|
||||
|
||||
# 2. Getting Started
|
||||
|
||||
The following sections describe how to build and run Cloud Hypervisor.
|
||||
|
||||
## Prerequisites for AArch64
|
||||
|
||||
- AArch64 servers (recommended) or development boards equipped with the GICv3
|
||||
interrupt controller.
|
||||
The following sections describe how to build and run Cloud Hypervisor on the
|
||||
`x86-64` platform. For getting started on the `AArch64` platform, please refer
|
||||
to the
|
||||
[AArch64 documentation](docs/arm64.md).
|
||||
|
||||
## Host OS
|
||||
|
||||
@@ -107,10 +105,13 @@ do not wish to use the pre-built binaries.
|
||||
|
||||
## Booting Linux
|
||||
|
||||
Cloud Hypervisor supports direct kernel boot (the x86-64 kernel requires the kernel
|
||||
built with PVH support) or booting via a firmware (either [Rust Hypervisor
|
||||
The instructions below are for the `x86-64` platform. For `AArch64` please see
|
||||
the [AArch64 specific documentation](docs/arm64.md).
|
||||
|
||||
Cloud Hypervisor supports direct kernel boot (if the kernel is built with PVH
|
||||
support) or booting via a firmware (either [Rust Hypervisor
|
||||
Firmware](https://github.com/cloud-hypervisor/rust-hypervisor-firmware) or an
|
||||
edk2 UEFI firmware called `CLOUDHV` / `CLOUDHV_EFI`.)
|
||||
edk2 UEFI firmware called `CLOUDHV`.)
|
||||
|
||||
Binary builds of the firmware files are available for the latest release of
|
||||
[Rust Hyperivor
|
||||
@@ -172,7 +173,7 @@ $ ./cloud-hypervisor \
|
||||
|
||||
#### Building your Kernel
|
||||
|
||||
Cloud Hypervisor also supports direct kernel boot. For x86-64, a `vmlinux` ELF kernel (compiled with PVH support) is needed. In order to support development there is a custom branch; however provided the required options are enabled any recent kernel will suffice.
|
||||
Cloud Hypervisor also supports direct kernel boot into a `vmlinux` ELF kernel (compiled with PVH support). In order to support development there is a custom branch; however provided the required options are enabled any recent kernel will suffice.
|
||||
|
||||
To build the kernel:
|
||||
|
||||
@@ -180,23 +181,15 @@ To build the kernel:
|
||||
# Clone the Cloud Hypervisor Linux branch
|
||||
$ git clone --depth 1 https://github.com/cloud-hypervisor/linux.git -b ch-5.15.12 linux-cloud-hypervisor
|
||||
$ pushd linux-cloud-hypervisor
|
||||
# Use the x86-64 cloud-hypervisor kernel config to build your kernel for x86-64
|
||||
# Use the cloud-hypervisor kernel config to build your kernel
|
||||
$ wget https://raw.githubusercontent.com/cloud-hypervisor/cloud-hypervisor/main/resources/linux-config-x86_64
|
||||
# Use the AArch64 cloud-hypervisor kernel config to build your kernel for AArch64
|
||||
$ wget https://raw.githubusercontent.com/cloud-hypervisor/cloud-hypervisor/main/resources/linux-config-aarch64
|
||||
$ cp linux-config-x86_64 .config # x86-64
|
||||
$ cp linux-config-aarch64 .config # AArch64
|
||||
# Do native build of the x86-64 kernel
|
||||
$ cp linux-config-x86_64 .config
|
||||
$ KCFLAGS="-Wa,-mx86-used-note=no" make bzImage -j `nproc`
|
||||
# Do native build of the AArch64 kernel
|
||||
$ make -j `nproc`
|
||||
$ popd
|
||||
```
|
||||
|
||||
For x86-64, the `vmlinux` kernel image will then be located at
|
||||
The `vmlinux` kernel image will then be located at
|
||||
`linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin`.
|
||||
For AArch64, the `Image` kernel image will then be located at
|
||||
`linux-cloud-hypervisor/arch/arm64/boot/Image`.
|
||||
|
||||
#### Disk image
|
||||
|
||||
@@ -204,10 +197,8 @@ For the disk image the same Ubuntu image as before can be used. This contains
|
||||
an `ext4` root filesystem.
|
||||
|
||||
```shell
|
||||
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img # x86-64
|
||||
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-arm64.img # AArch64
|
||||
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-amd64.img focal-server-cloudimg-amd64.raw # x86-64
|
||||
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-arm64.img focal-server-cloudimg-arm64.raw # AArch64
|
||||
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
|
||||
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-amd64.img focal-server-cloudimg-amd64.raw
|
||||
```
|
||||
|
||||
#### Booting the guest VM
|
||||
@@ -215,8 +206,6 @@ $ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-arm64.img focal-serv
|
||||
These sample commands boot the disk image using the custom kernel whilst also
|
||||
supplying the desired kernel command line.
|
||||
|
||||
- x86-64
|
||||
|
||||
```shell
|
||||
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor
|
||||
$ ./create-cloud-init.sh
|
||||
@@ -229,26 +218,9 @@ $ ./cloud-hypervisor \
|
||||
--net "tap=,mac=,ip=,mask="
|
||||
```
|
||||
|
||||
- AArch64
|
||||
|
||||
```shell
|
||||
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor
|
||||
$ ./create-cloud-init.sh
|
||||
$ ./cloud-hypervisor \
|
||||
--kernel ./linux-cloud-hypervisor/arch/arm64/boot/Image \
|
||||
--disk path=focal-server-cloudimg-arm64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||
--cmdline "console=hvc0 root=/dev/vda1 rw" \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
--net "tap=,mac=,ip=,mask="
|
||||
```
|
||||
|
||||
If earlier kernel messages are required the serial console should be used instead of `virtio-console`.
|
||||
|
||||
- x86-64
|
||||
|
||||
```shell
|
||||
$ ./cloud-hypervisor \
|
||||
```./cloud-hypervisor \
|
||||
--kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
|
||||
--console off \
|
||||
--serial tty \
|
||||
@@ -259,20 +231,6 @@ $ ./cloud-hypervisor \
|
||||
--net "tap=,mac=,ip=,mask="
|
||||
```
|
||||
|
||||
- AArch64
|
||||
|
||||
```shell
|
||||
$ ./cloud-hypervisor \
|
||||
--kernel ./linux-cloud-hypervisor/arch/arm64/boot/Image \
|
||||
--console off \
|
||||
--serial tty \
|
||||
--disk path=focal-server-cloudimg-arm64.raw \
|
||||
--cmdline "console=ttyAMA0 root=/dev/vda1 rw" \
|
||||
--cpus boot=4 \
|
||||
--memory size=1024M \
|
||||
--net "tap=,mac=,ip=,mask="
|
||||
```
|
||||
|
||||
# 3. Status
|
||||
|
||||
Cloud Hypervisor is under active development. The following stability
|
||||
@@ -296,11 +254,12 @@ Currently the following items are **not** guaranteed across updates:
|
||||
|
||||
Further details can be found in the [release documentation](docs/releases.md).
|
||||
|
||||
As of 2023-01-03, the following cloud images are supported:
|
||||
As of 2022-10-13, the following cloud images are supported:
|
||||
|
||||
- [Ubuntu Focal](https://cloud-images.ubuntu.com/focal/current/) (focal-server-cloudimg-{amd64,arm64}.img)
|
||||
- [Ubuntu Jammy](https://cloud-images.ubuntu.com/jammy/current/) (jammy-server-cloudimg-{amd64,arm64}.img )
|
||||
- [Fedora 36](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/) ([Fedora-Cloud-Base-36-1.5.x86_64.raw.xz](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/x86_64/images/) / [Fedora-Cloud-Base-36-1.5.aarch64.raw.xz](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/aarch64/images/))
|
||||
- [Ubuntu Bionic](https://cloud-images.ubuntu.com/bionic/current/) (bionic-server-cloudimg-amd64.img)
|
||||
- [Ubuntu Focal](https://cloud-images.ubuntu.com/focal/current/) (focal-server-cloudimg-amd64.img)
|
||||
- [Ubuntu Jammy](https://cloud-images.ubuntu.com/jammy/current/) (jammy-server-cloudimg-amd64.img )
|
||||
- [Fedora 36](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/x86_64/images/) (Fedora-Cloud-Base-36-1.5.x86_64.raw.xz)
|
||||
|
||||
Direct kernel boot to userspace should work with a rootfs from most
|
||||
distributions although you may need to enable exotic filesystem types in the
|
||||
|
||||
@@ -38,6 +38,7 @@ pub struct Sdt {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[allow(clippy::len_without_is_empty)]
|
||||
impl Sdt {
|
||||
pub fn new(
|
||||
signature: [u8; 4],
|
||||
@@ -97,7 +98,6 @@ impl Sdt {
|
||||
/// Write a value at the given offset
|
||||
pub fn write<T>(&mut self, offset: usize, value: T) {
|
||||
assert!((offset + (std::mem::size_of::<T>() - 1)) < self.data.len());
|
||||
// SAFETY: The assertion above makes sure we don't do out of bounds write.
|
||||
unsafe {
|
||||
*(((self.data.as_mut_ptr() as usize) + offset) as *mut T) = value;
|
||||
}
|
||||
@@ -123,10 +123,6 @@ impl Sdt {
|
||||
pub fn len(&self) -> usize {
|
||||
self.data.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.data.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -9,14 +9,14 @@ default = []
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.68"
|
||||
anyhow = "1.0.66"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.139"
|
||||
libc = "0.2.138"
|
||||
linux-loader = { version = "0.8.1", features = ["elf", "bzimage", "pe"] }
|
||||
log = "0.4.17"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
thiserror = "1.0.38"
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
thiserror = "1.0.37"
|
||||
uuid = "1.2.2"
|
||||
versionize = "0.1.9"
|
||||
versionize_derive = "0.1.4"
|
||||
|
||||
@@ -259,7 +259,7 @@ fn create_memory_node(
|
||||
if last_addr < super::layout::MEM_32BIT_RESERVED_START.raw_value() {
|
||||
// Case 1: all RAM is under the hole
|
||||
let mem_size = last_addr - super::layout::RAM_START.raw_value() + 1;
|
||||
let mem_reg_prop = [super::layout::RAM_START.raw_value(), mem_size];
|
||||
let mem_reg_prop = [super::layout::RAM_START.raw_value() as u64, mem_size as u64];
|
||||
let memory_node = fdt.begin_node("memory")?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||
@@ -269,7 +269,7 @@ fn create_memory_node(
|
||||
// Region 1: RAM before the hole
|
||||
let mem_size = super::layout::MEM_32BIT_RESERVED_START.raw_value()
|
||||
- super::layout::RAM_START.raw_value();
|
||||
let mem_reg_prop = [super::layout::RAM_START.raw_value(), mem_size];
|
||||
let mem_reg_prop = [super::layout::RAM_START.raw_value() as u64, mem_size as u64];
|
||||
let memory_node_name = format!("memory@{:x}", super::layout::RAM_START.raw_value());
|
||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
@@ -278,7 +278,10 @@ fn create_memory_node(
|
||||
|
||||
// Region 2: RAM after the hole
|
||||
let mem_size = last_addr - super::layout::RAM_64BIT_START.raw_value() + 1;
|
||||
let mem_reg_prop = [super::layout::RAM_64BIT_START.raw_value(), mem_size];
|
||||
let mem_reg_prop = [
|
||||
super::layout::RAM_64BIT_START.raw_value() as u64,
|
||||
mem_size as u64,
|
||||
];
|
||||
let memory_node_name =
|
||||
format!("memory@{:x}", super::layout::RAM_64BIT_START.raw_value());
|
||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||
@@ -300,7 +303,7 @@ fn create_chosen_node(
|
||||
fdt.property_string("bootargs", cmdline)?;
|
||||
|
||||
if let Some(initrd_config) = initrd {
|
||||
let initrd_start = initrd_config.address.raw_value();
|
||||
let initrd_start = initrd_config.address.raw_value() as u64;
|
||||
let initrd_end = initrd_config.address.raw_value() + initrd_config.size as u64;
|
||||
fdt.property_u64("linux,initrd-start", initrd_start)?;
|
||||
fdt.property_u64("linux,initrd-end", initrd_end)?;
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic, GuestUsize};
|
||||
use vm_memory::{Address, GuestAddress, GuestMemory, GuestUsize};
|
||||
|
||||
/// Errors thrown while configuring aarch64 system.
|
||||
#[derive(Debug)]
|
||||
@@ -64,9 +64,9 @@ pub struct EntryPoint {
|
||||
pub fn configure_vcpu(
|
||||
vcpu: &Arc<dyn hypervisor::Vcpu>,
|
||||
id: u8,
|
||||
boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>,
|
||||
kernel_entry_point: Option<EntryPoint>,
|
||||
) -> super::Result<u64> {
|
||||
if let Some((kernel_entry_point, _guest_memory)) = boot_setup {
|
||||
if let Some(kernel_entry_point) = kernel_entry_point {
|
||||
vcpu.setup_regs(
|
||||
id,
|
||||
kernel_entry_point.entry_addr.raw_value(),
|
||||
@@ -108,7 +108,7 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
|
||||
|
||||
// RAM space
|
||||
// Case1: guest memory fits before the gap
|
||||
if size <= ram_32bit_space_size {
|
||||
if size as u64 <= ram_32bit_space_size {
|
||||
regions.push((layout::RAM_START, size as usize, RegionType::Ram));
|
||||
// Case2: guest memory extends beyond the gap
|
||||
} else {
|
||||
@@ -231,7 +231,7 @@ mod tests {
|
||||
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START) as usize;
|
||||
assert_eq!(6, regions.len());
|
||||
assert_eq!(layout::RAM_START, regions[3].0);
|
||||
assert_eq!(ram_32bit_space_size, regions[3].1);
|
||||
assert_eq!(ram_32bit_space_size as usize, regions[3].1);
|
||||
assert_eq!(RegionType::Ram, regions[3].2);
|
||||
assert_eq!(RegionType::Reserved, regions[5].2);
|
||||
assert_eq!(RegionType::Ram, regions[4].2);
|
||||
|
||||
@@ -99,7 +99,7 @@ pub use x86_64::{
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[inline(always)]
|
||||
fn pagesize() -> usize {
|
||||
// SAFETY: Trivially safe
|
||||
// Trivially safe
|
||||
unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
|
||||
}
|
||||
|
||||
|
||||
@@ -125,11 +125,9 @@ struct MemmapTableEntryWrapper(hvm_memmap_table_entry);
|
||||
#[derive(Copy, Clone, Default)]
|
||||
struct ModlistEntryWrapper(hvm_modlist_entry);
|
||||
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
// SAFETY: These data structures only contain a series of integers
|
||||
unsafe impl ByteValued for StartInfoWrapper {}
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
unsafe impl ByteValued for MemmapTableEntryWrapper {}
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
unsafe impl ByteValued for ModlistEntryWrapper {}
|
||||
|
||||
// This is a workaround to the Rust enforcement specifying that any implementation of a foreign
|
||||
@@ -547,30 +545,13 @@ impl CpuidFeatureEntry {
|
||||
}
|
||||
|
||||
pub fn generate_common_cpuid(
|
||||
hypervisor: &Arc<dyn hypervisor::Hypervisor>,
|
||||
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,
|
||||
) -> super::Result<Vec<CpuIdEntry>> {
|
||||
// SAFETY: cpuid called with valid leaves
|
||||
if unsafe { x86_64::__cpuid(1) }.ecx & 1 << HYPERVISOR_ECX_BIT == 1 << HYPERVISOR_ECX_BIT {
|
||||
// SAFETY: cpuid called with valid leaves
|
||||
let hypervisor_cpuid = unsafe { x86_64::__cpuid(0x4000_0000) };
|
||||
|
||||
let mut identifier: [u8; 12] = [0; 12];
|
||||
identifier[0..4].copy_from_slice(&hypervisor_cpuid.ebx.to_le_bytes()[..]);
|
||||
identifier[4..8].copy_from_slice(&hypervisor_cpuid.ecx.to_le_bytes()[..]);
|
||||
identifier[8..12].copy_from_slice(&hypervisor_cpuid.edx.to_le_bytes()[..]);
|
||||
|
||||
info!(
|
||||
"Running under nested virtualisation. Hypervisor string: {}",
|
||||
String::from_utf8_lossy(&identifier)
|
||||
);
|
||||
}
|
||||
|
||||
info!("Generating guest CPUID for with physical address size: {phys_bits}");
|
||||
let cpuid_patches = vec![
|
||||
// Patch tsc deadline timer bit
|
||||
CpuidPatch {
|
||||
@@ -694,7 +675,6 @@ pub fn generate_common_cpuid(
|
||||
// Copy CPU identification string
|
||||
for i in 0x8000_0002..=0x8000_0004 {
|
||||
cpuid.retain(|c| c.function != i);
|
||||
// SAFETY: call cpuid with valid leaves
|
||||
let leaf = unsafe { std::arch::x86_64::__cpuid(i) };
|
||||
cpuid.push(CpuIdEntry {
|
||||
function: i,
|
||||
@@ -759,7 +739,8 @@ pub fn generate_common_cpuid(
|
||||
pub fn configure_vcpu(
|
||||
vcpu: &Arc<dyn hypervisor::Vcpu>,
|
||||
id: u8,
|
||||
boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>,
|
||||
kernel_entry_point: Option<EntryPoint>,
|
||||
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
cpuid: Vec<CpuIdEntry>,
|
||||
kvm_hyperv: bool,
|
||||
) -> super::Result<()> {
|
||||
@@ -776,12 +757,12 @@ 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(kernel_entry_point) = kernel_entry_point {
|
||||
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_sregs(&vm_memory.memory(), vcpu).map_err(Error::SregsConfiguration)?;
|
||||
}
|
||||
}
|
||||
interrupts::set_lint(vcpu).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
|
||||
@@ -1044,7 +1025,6 @@ pub fn initramfs_load_addr(
|
||||
}
|
||||
|
||||
pub fn get_host_cpu_phys_bits() -> u8 {
|
||||
// SAFETY: call cpuid with valid leaves
|
||||
unsafe {
|
||||
let leaf = x86_64::__cpuid(0x8000_0000);
|
||||
|
||||
@@ -1155,7 +1135,6 @@ fn update_cpuid_sgx(
|
||||
|
||||
// Get host CPUID for leaf 0x12, subleaf 0x2. This is to retrieve EPC
|
||||
// properties such as confidentiality and integrity.
|
||||
// SAFETY: call cpuid with valid leaves
|
||||
let leaf = unsafe { std::arch::x86_64::__cpuid_count(0x12, 0x2) };
|
||||
|
||||
for (i, epc_section) in epc_sections.iter().enumerate() {
|
||||
|
||||
@@ -37,17 +37,11 @@ struct MpfIntelWrapper(mpspec::mpf_intel);
|
||||
|
||||
// SAFETY: These `mpspec` wrapper types are only data, reading them from data is a safe initialization.
|
||||
unsafe impl ByteValued for MpcBusWrapper {}
|
||||
// SAFETY: see above
|
||||
unsafe impl ByteValued for MpcCpuWrapper {}
|
||||
// SAFETY: see above
|
||||
unsafe impl ByteValued for MpcIntsrcWrapper {}
|
||||
// SAFETY: see above
|
||||
unsafe impl ByteValued for MpcIoapicWrapper {}
|
||||
// SAFETY: see above
|
||||
unsafe impl ByteValued for MpcTableWrapper {}
|
||||
// SAFETY: see above
|
||||
unsafe impl ByteValued for MpcLintsrcWrapper {}
|
||||
// SAFETY: see above
|
||||
unsafe impl ByteValued for MpfIntelWrapper {}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -101,7 +95,7 @@ const CPU_FEATURE_APIC: u32 = 0x200;
|
||||
const CPU_FEATURE_FPU: u32 = 0x001;
|
||||
|
||||
fn compute_checksum<T: Copy>(v: &T) -> u8 {
|
||||
// SAFETY: we are only reading the bytes within the size of the `T` reference `v`.
|
||||
// Safe because we are only reading the bytes within the size of the `T` reference `v`.
|
||||
let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
|
||||
let mut checksum: u8 = 0;
|
||||
for i in v_slice.iter() {
|
||||
|
||||
@@ -67,7 +67,7 @@ const PCI_SUPPORTED: u64 = 1 << 7;
|
||||
const IS_VIRTUAL_MACHINE: u8 = 1 << 4;
|
||||
|
||||
fn compute_checksum<T: Copy>(v: &T) -> u8 {
|
||||
// SAFETY: we are only reading the bytes within the size of the `T` reference `v`.
|
||||
// Safe because we are only reading the bytes within the size of the `T` reference `v`.
|
||||
let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
|
||||
let mut checksum: u8 = 0;
|
||||
for i in v_slice.iter() {
|
||||
@@ -145,15 +145,11 @@ struct SmbiosEndOfTable {
|
||||
handle: u16,
|
||||
}
|
||||
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
// SAFETY: These data structures only contain a series of integers
|
||||
unsafe impl ByteValued for Smbios30Entrypoint {}
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
unsafe impl ByteValued for SmbiosBiosInfo {}
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
unsafe impl ByteValued for SmbiosSysInfo {}
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
unsafe impl ByteValued for SmbiosOemStrings {}
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
unsafe impl ByteValued for SmbiosEndOfTable {}
|
||||
|
||||
fn write_and_incr<T: ByteValued>(
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
use crate::GuestMemoryMmap;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemoryError};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
@@ -15,8 +13,6 @@ pub enum TdvfError {
|
||||
ReadDescriptor(#[source] std::io::Error),
|
||||
#[error("Failed read TDVF descriptor offset: {0}")]
|
||||
ReadDescriptorOffset(#[source] std::io::Error),
|
||||
#[error("Failed read GUID table: {0}")]
|
||||
ReadGuidTable(#[source] std::io::Error),
|
||||
#[error("Invalid descriptor signature")]
|
||||
InvalidDescriptorSignature,
|
||||
#[error("Invalid descriptor size")]
|
||||
@@ -25,13 +21,8 @@ pub enum TdvfError {
|
||||
InvalidDescriptorVersion,
|
||||
#[error("Failed to write HOB details to guest memory: {0}")]
|
||||
GuestMemoryWriteHob(#[source] GuestMemoryError),
|
||||
#[error("Failed to create Uuid: {0}")]
|
||||
UuidCreation(#[source] uuid::Error),
|
||||
}
|
||||
|
||||
const TABLE_FOOTER_GUID: &str = "96b582de-1fb2-45f7-baea-a366c55a082d";
|
||||
const TDVF_METADATA_OFFSET_GUID: &str = "e47a6535-984a-4798-865e-4685a7bf8ec2";
|
||||
|
||||
// TDVF_DESCRIPTOR
|
||||
#[repr(packed)]
|
||||
#[derive(Default)]
|
||||
@@ -73,72 +64,7 @@ impl Default for TdvfSectionType {
|
||||
}
|
||||
}
|
||||
|
||||
fn tdvf_descriptor_offset(file: &mut File) -> Result<(SeekFrom, bool), TdvfError> {
|
||||
// Let's first try to identify the presence of the table footer GUID
|
||||
file.seek(SeekFrom::End(-0x30))
|
||||
.map_err(TdvfError::ReadGuidTable)?;
|
||||
let mut table_footer_guid: [u8; 16] = [0; 16];
|
||||
file.read_exact(&mut table_footer_guid)
|
||||
.map_err(TdvfError::ReadGuidTable)?;
|
||||
let uuid =
|
||||
Uuid::from_slice_le(table_footer_guid.as_slice()).map_err(TdvfError::UuidCreation)?;
|
||||
let expected_uuid = Uuid::from_str(TABLE_FOOTER_GUID).map_err(TdvfError::UuidCreation)?;
|
||||
if uuid == expected_uuid {
|
||||
// Retrieve the table size
|
||||
file.seek(SeekFrom::End(-0x32))
|
||||
.map_err(TdvfError::ReadGuidTable)?;
|
||||
let mut table_size: [u8; 2] = [0; 2];
|
||||
file.read_exact(&mut table_size)
|
||||
.map_err(TdvfError::ReadGuidTable)?;
|
||||
let table_size = u16::from_le_bytes(table_size) as usize;
|
||||
let mut table: Vec<u8> = vec![0; table_size];
|
||||
|
||||
// Read the entire table
|
||||
file.seek(SeekFrom::End(-(table_size as i64 + 0x20)))
|
||||
.map_err(TdvfError::ReadGuidTable)?;
|
||||
file.read_exact(table.as_mut_slice())
|
||||
.map_err(TdvfError::ReadGuidTable)?;
|
||||
|
||||
// Let's start from the top and go backward down the table.
|
||||
// We start after the footer GUID and the table length.
|
||||
let mut offset = table_size - 18;
|
||||
|
||||
debug!("Parsing GUIDed structure");
|
||||
while offset >= 18 {
|
||||
let entry_uuid = Uuid::from_slice_le(&table[offset - 16..offset])
|
||||
.map_err(TdvfError::UuidCreation)?;
|
||||
let entry_size =
|
||||
u16::from_le_bytes(table[offset - 18..offset - 16].try_into().unwrap()) as usize;
|
||||
debug!(
|
||||
"Entry GUID = {}, size = {}",
|
||||
entry_uuid.hyphenated().to_string(),
|
||||
entry_size
|
||||
);
|
||||
|
||||
// Avoid going through an infinite loop if the entry size is 0
|
||||
if entry_size == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
offset -= entry_size;
|
||||
|
||||
let expected_uuid =
|
||||
Uuid::from_str(TDVF_METADATA_OFFSET_GUID).map_err(TdvfError::UuidCreation)?;
|
||||
if entry_uuid == expected_uuid && entry_size == 22 {
|
||||
return Ok((
|
||||
SeekFrom::End(
|
||||
-(u32::from_le_bytes(table[offset..offset + 4].try_into().unwrap()) as i64),
|
||||
),
|
||||
true,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we end up here, this means the firmware doesn't support the new way
|
||||
// of exposing the TDVF descriptor offset through the table of GUIDs.
|
||||
// That's why we fallback onto the deprecated method.
|
||||
|
||||
pub fn parse_tdvf_sections(file: &mut File) -> Result<Vec<TdvfSection>, TdvfError> {
|
||||
// The 32-bit offset to the TDVF metadata is located 32 bytes from
|
||||
// the end of the file.
|
||||
// See "TDVF Metadata Pointer" in "TDX Virtual Firmware Design Guide
|
||||
@@ -148,21 +74,13 @@ fn tdvf_descriptor_offset(file: &mut File) -> Result<(SeekFrom, bool), TdvfError
|
||||
let mut descriptor_offset: [u8; 4] = [0; 4];
|
||||
file.read_exact(&mut descriptor_offset)
|
||||
.map_err(TdvfError::ReadDescriptorOffset)?;
|
||||
let descriptor_offset = u32::from_le_bytes(descriptor_offset) as u64;
|
||||
|
||||
Ok((
|
||||
SeekFrom::Start(u32::from_le_bytes(descriptor_offset) as u64),
|
||||
false,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn parse_tdvf_sections(file: &mut File) -> Result<(Vec<TdvfSection>, bool), TdvfError> {
|
||||
let (descriptor_offset, guid_found) = tdvf_descriptor_offset(file)?;
|
||||
|
||||
file.seek(descriptor_offset)
|
||||
file.seek(SeekFrom::Start(descriptor_offset))
|
||||
.map_err(TdvfError::ReadDescriptor)?;
|
||||
|
||||
let mut descriptor: TdvfDescriptor = Default::default();
|
||||
// SAFETY: we read exactly the size of the descriptor header
|
||||
// Safe as we read exactly the size of the descriptor header
|
||||
file.read_exact(unsafe {
|
||||
std::slice::from_raw_parts_mut(
|
||||
&mut descriptor as *mut _ as *mut u8,
|
||||
@@ -189,7 +107,7 @@ pub fn parse_tdvf_sections(file: &mut File) -> Result<(Vec<TdvfSection>, bool),
|
||||
let mut sections = Vec::new();
|
||||
sections.resize_with(descriptor.num_sections as usize, TdvfSection::default);
|
||||
|
||||
// SAFETY: we read exactly the advertised sections
|
||||
// Safe as we read exactly the advertised sections
|
||||
file.read_exact(unsafe {
|
||||
std::slice::from_raw_parts_mut(
|
||||
sections.as_mut_ptr() as *mut u8,
|
||||
@@ -198,7 +116,7 @@ pub fn parse_tdvf_sections(file: &mut File) -> Result<(Vec<TdvfSection>, bool),
|
||||
})
|
||||
.map_err(TdvfError::ReadDescriptor)?;
|
||||
|
||||
Ok((sections, guid_found))
|
||||
Ok(sections)
|
||||
}
|
||||
|
||||
#[repr(u16)]
|
||||
@@ -293,17 +211,12 @@ struct TdPayload {
|
||||
payload_info: PayloadInfo,
|
||||
}
|
||||
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
// SAFETY: These data structures only contain a series of integers
|
||||
unsafe impl ByteValued for HobHeader {}
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
unsafe impl ByteValued for HobHandoffInfoTable {}
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
unsafe impl ByteValued for HobResourceDescriptor {}
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
unsafe impl ByteValued for HobGuidType {}
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
unsafe impl ByteValued for PayloadInfo {}
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
unsafe impl ByteValued for TdPayload {}
|
||||
|
||||
pub struct TdHob {
|
||||
@@ -399,19 +312,12 @@ impl TdHob {
|
||||
physical_start: u64,
|
||||
resource_length: u64,
|
||||
ram: bool,
|
||||
guid_found: bool,
|
||||
) -> Result<(), TdvfError> {
|
||||
self.add_resource(
|
||||
mem,
|
||||
physical_start,
|
||||
resource_length,
|
||||
if ram {
|
||||
if guid_found {
|
||||
0x7 /* EFI_RESOURCE_MEMORY_UNACCEPTED */
|
||||
} else {
|
||||
0 /* EFI_RESOURCE_SYSTEM_MEMORY */
|
||||
}
|
||||
} else if guid_found {
|
||||
0 /* EFI_RESOURCE_SYSTEM_MEMORY */
|
||||
} else {
|
||||
0x5 /*EFI_RESOURCE_MEMORY_RESERVED */
|
||||
@@ -539,7 +445,7 @@ mod tests {
|
||||
#[ignore]
|
||||
fn test_parse_tdvf_sections() {
|
||||
let mut f = std::fs::File::open("tdvf.fd").unwrap();
|
||||
let (sections, _) = parse_tdvf_sections(&mut f).unwrap();
|
||||
let sections = parse_tdvf_sections(&mut f).unwrap();
|
||||
for section in sections {
|
||||
eprintln!("{section:x?}")
|
||||
}
|
||||
|
||||
@@ -8,12 +8,11 @@ edition = "2021"
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
io-uring = "0.5.11"
|
||||
libc = "0.2.139"
|
||||
io-uring = "0.5.9"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
qcow = { path = "../qcow" }
|
||||
smallvec = "1.10.0"
|
||||
thiserror = "1.0.38"
|
||||
thiserror = "1.0.37"
|
||||
versionize = "0.1.9"
|
||||
versionize_derive = "0.1.4"
|
||||
vhdx = { path = "../vhdx" }
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
use libc::{ioctl, S_IFBLK, S_IFMT};
|
||||
use std::convert::TryInto;
|
||||
use std::fs::File;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use thiserror::Error;
|
||||
@@ -53,21 +54,19 @@ enum BlockSize {
|
||||
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
|
||||
#[allow(clippy::useless_conversion)]
|
||||
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(),
|
||||
@@ -76,7 +75,9 @@ impl DiskTopology {
|
||||
BlockSize::PhysicalBlock => BLKPBSZGET(),
|
||||
BlockSize::MinimumIo => BLKIOMIN(),
|
||||
BlockSize::OptimalIo => BLKIOOPT(),
|
||||
} as _,
|
||||
}
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
&mut block_size,
|
||||
)
|
||||
};
|
||||
@@ -131,15 +132,15 @@ pub trait AsyncIo: Send {
|
||||
fn read_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()>;
|
||||
fn write_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()>;
|
||||
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()>;
|
||||
fn next_completed_request(&mut self) -> Option<(u64, i32)>;
|
||||
fn complete(&mut self) -> Vec<(u64, i32)>;
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ impl AsyncIo for FixedVhdAsync {
|
||||
fn read_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
if offset as u64 >= self.size {
|
||||
@@ -83,7 +83,7 @@ impl AsyncIo for FixedVhdAsync {
|
||||
fn write_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
if offset as u64 >= self.size {
|
||||
@@ -104,7 +104,7 @@ impl AsyncIo for FixedVhdAsync {
|
||||
self.raw_file_async.fsync(user_data)
|
||||
}
|
||||
|
||||
fn next_completed_request(&mut self) -> Option<(u64, i32)> {
|
||||
self.raw_file_async.next_completed_request()
|
||||
fn complete(&mut self) -> Vec<(u64, i32)> {
|
||||
self.raw_file_async.complete()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ impl AsyncIo for FixedVhdSync {
|
||||
fn read_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
if offset as u64 >= self.size {
|
||||
@@ -81,7 +81,7 @@ impl AsyncIo for FixedVhdSync {
|
||||
fn write_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
if offset as u64 >= self.size {
|
||||
@@ -101,7 +101,7 @@ impl AsyncIo for FixedVhdSync {
|
||||
self.raw_file_sync.fsync(user_data)
|
||||
}
|
||||
|
||||
fn next_completed_request(&mut self) -> Option<(u64, i32)> {
|
||||
self.raw_file_sync.next_completed_request()
|
||||
fn complete(&mut self) -> Vec<(u64, i32)> {
|
||||
self.raw_file_sync.complete()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,8 @@ pub mod vhdx_sync;
|
||||
|
||||
use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult};
|
||||
use io_uring::{opcode, IoUring, Probe};
|
||||
use smallvec::SmallVec;
|
||||
use std::alloc::{alloc_zeroed, dealloc, Layout};
|
||||
use std::cmp;
|
||||
use std::collections::VecDeque;
|
||||
use std::convert::TryInto;
|
||||
use std::fs::File;
|
||||
use std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
|
||||
@@ -34,7 +32,6 @@ use std::path::Path;
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
use std::sync::MutexGuard;
|
||||
use std::time::Instant;
|
||||
use thiserror::Error;
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
@@ -198,11 +195,10 @@ pub struct AlignedOperation {
|
||||
pub struct Request {
|
||||
pub request_type: RequestType,
|
||||
pub sector: u64,
|
||||
pub data_descriptors: SmallVec<[(GuestAddress, u32); 1]>,
|
||||
pub data_descriptors: Vec<(GuestAddress, u32)>,
|
||||
pub status_addr: GuestAddress,
|
||||
pub writeback: bool,
|
||||
pub aligned_operations: Vec<AlignedOperation>,
|
||||
pub start: Instant,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
@@ -230,11 +226,10 @@ impl Request {
|
||||
let mut req = Request {
|
||||
request_type: request_type(desc_chain.memory(), hdr_desc_addr)?,
|
||||
sector: sector(desc_chain.memory(), hdr_desc_addr)?,
|
||||
data_descriptors: SmallVec::with_capacity(1),
|
||||
data_descriptors: Vec::new(),
|
||||
status_addr: GuestAddress(0),
|
||||
writeback: true,
|
||||
aligned_operations: Vec::new(),
|
||||
start: Instant::now(),
|
||||
};
|
||||
|
||||
let status_desc;
|
||||
@@ -254,7 +249,6 @@ impl Request {
|
||||
return Err(Error::DescriptorChainTooShort);
|
||||
}
|
||||
} else {
|
||||
req.data_descriptors.reserve_exact(1);
|
||||
while desc.has_next() {
|
||||
if desc.is_write_only() && req.request_type == RequestType::Out {
|
||||
return Err(Error::UnexpectedWriteOnlyDescriptor);
|
||||
@@ -359,8 +353,7 @@ impl Request {
|
||||
let request_type = self.request_type;
|
||||
let offset = (sector << SECTOR_SHIFT) as libc::off_t;
|
||||
|
||||
let mut iovecs: SmallVec<[libc::iovec; 1]> =
|
||||
SmallVec::with_capacity(self.data_descriptors.len());
|
||||
let mut iovecs = Vec::new();
|
||||
for (data_addr, data_len) in &self.data_descriptors {
|
||||
if *data_len == 0 {
|
||||
continue;
|
||||
@@ -388,7 +381,7 @@ impl Request {
|
||||
let iov_base = if (origin_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
|
||||
// Safe because layout has non-zero size
|
||||
let aligned_ptr = unsafe { alloc_zeroed(layout) };
|
||||
if aligned_ptr.is_null() {
|
||||
return Err(ExecuteError::TemporaryBufferAllocation(
|
||||
@@ -399,7 +392,7 @@ impl Request {
|
||||
// We need to perform the copy beforehand in case we're writing
|
||||
// data out.
|
||||
if request_type == RequestType::Out {
|
||||
// SAFETY: destination buffer has been allocated with
|
||||
// Safe because destination buffer has been allocated with
|
||||
// the proper size.
|
||||
unsafe {
|
||||
std::ptr::copy(origin_ptr as *const u8, aligned_ptr, *data_len as usize)
|
||||
@@ -437,12 +430,12 @@ impl Request {
|
||||
.mark_dirty(0, *data_len as usize);
|
||||
}
|
||||
disk_image
|
||||
.read_vectored(offset, &iovecs, user_data)
|
||||
.read_vectored(offset, iovecs, user_data)
|
||||
.map_err(ExecuteError::AsyncRead)?;
|
||||
}
|
||||
RequestType::Out => {
|
||||
disk_image
|
||||
.write_vectored(offset, &iovecs, user_data)
|
||||
.write_vectored(offset, iovecs, user_data)
|
||||
.map_err(ExecuteError::AsyncWrite)?;
|
||||
}
|
||||
RequestType::Flush => {
|
||||
@@ -474,7 +467,7 @@ impl Request {
|
||||
// We need to perform the copy after the data has been read inside
|
||||
// the aligned buffer in case we're reading data in.
|
||||
if self.request_type == RequestType::In {
|
||||
// SAFETY: origin buffer has been allocated with the
|
||||
// Safe because origin buffer has been allocated with the
|
||||
// proper size.
|
||||
unsafe {
|
||||
std::ptr::copy(
|
||||
@@ -486,7 +479,7 @@ impl Request {
|
||||
}
|
||||
|
||||
// Free the temporary aligned buffer.
|
||||
// SAFETY: aligned_ptr was allocated by alloc_zeroed with the same
|
||||
// Safe because aligned_ptr was allocated by alloc_zeroed with the same
|
||||
// layout
|
||||
unsafe {
|
||||
dealloc(
|
||||
@@ -535,9 +528,8 @@ pub struct VirtioBlockGeometry {
|
||||
pub sectors: u8,
|
||||
}
|
||||
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
// SAFETY: these data structures only contain a series of integers
|
||||
unsafe impl ByteValued for VirtioBlockConfig {}
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
unsafe impl ByteValued for VirtioBlockGeometry {}
|
||||
|
||||
/// Check if io_uring for block device can be used on the current system, as
|
||||
@@ -596,15 +588,14 @@ where
|
||||
fn read_vectored_sync(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
eventfd: &EventFd,
|
||||
completion_list: &mut VecDeque<(u64, i32)>,
|
||||
completion_list: &mut Vec<(u64, i32)>,
|
||||
) -> AsyncIoResult<()> {
|
||||
// Convert libc::iovec into IoSliceMut
|
||||
let mut slices = Vec::new();
|
||||
for iovec in iovecs.iter() {
|
||||
// SAFETY: on Linux IoSliceMut wraps around libc::iovec
|
||||
slices.push(IoSliceMut::new(unsafe { std::mem::transmute(*iovec) }));
|
||||
}
|
||||
|
||||
@@ -620,7 +611,7 @@ where
|
||||
.map_err(AsyncIoError::ReadVectored)?
|
||||
};
|
||||
|
||||
completion_list.push_back((user_data, result as i32));
|
||||
completion_list.push((user_data, result as i32));
|
||||
eventfd.write(1).unwrap();
|
||||
|
||||
Ok(())
|
||||
@@ -629,15 +620,14 @@ where
|
||||
fn write_vectored_sync(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
eventfd: &EventFd,
|
||||
completion_list: &mut VecDeque<(u64, i32)>,
|
||||
completion_list: &mut Vec<(u64, i32)>,
|
||||
) -> AsyncIoResult<()> {
|
||||
// Convert libc::iovec into IoSlice
|
||||
let mut slices = Vec::new();
|
||||
for iovec in iovecs.iter() {
|
||||
// SAFETY: on Linux IoSliceMut wraps around libc::iovec
|
||||
slices.push(IoSlice::new(unsafe { std::mem::transmute(*iovec) }));
|
||||
}
|
||||
|
||||
@@ -653,7 +643,7 @@ where
|
||||
.map_err(AsyncIoError::WriteVectored)?
|
||||
};
|
||||
|
||||
completion_list.push_back((user_data, result as i32));
|
||||
completion_list.push((user_data, result as i32));
|
||||
eventfd.write(1).unwrap();
|
||||
|
||||
Ok(())
|
||||
@@ -663,7 +653,7 @@ where
|
||||
&mut self,
|
||||
user_data: Option<u64>,
|
||||
eventfd: &EventFd,
|
||||
completion_list: &mut VecDeque<(u64, i32)>,
|
||||
completion_list: &mut Vec<(u64, i32)>,
|
||||
) -> AsyncIoResult<()> {
|
||||
let result: i32 = {
|
||||
let mut file = self.file();
|
||||
@@ -675,7 +665,7 @@ where
|
||||
};
|
||||
|
||||
if let Some(user_data) = user_data {
|
||||
completion_list.push_back((user_data, result));
|
||||
completion_list.push((user_data, result));
|
||||
eventfd.write(1).unwrap();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult};
|
||||
use crate::AsyncAdaptor;
|
||||
use qcow::{QcowFile, RawFile, Result as QcowResult};
|
||||
use std::collections::VecDeque;
|
||||
use std::fs::File;
|
||||
use std::io::{Seek, SeekFrom};
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
@@ -38,7 +37,7 @@ impl DiskFile for QcowDiskSync {
|
||||
pub struct QcowSync {
|
||||
qcow_file: Arc<Mutex<QcowFile>>,
|
||||
eventfd: EventFd,
|
||||
completion_list: VecDeque<(u64, i32)>,
|
||||
completion_list: Vec<(u64, i32)>,
|
||||
}
|
||||
|
||||
impl QcowSync {
|
||||
@@ -47,7 +46,7 @@ impl QcowSync {
|
||||
qcow_file,
|
||||
eventfd: EventFd::new(libc::EFD_NONBLOCK)
|
||||
.expect("Failed creating EventFd for QcowSync"),
|
||||
completion_list: VecDeque::new(),
|
||||
completion_list: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,7 +65,7 @@ impl AsyncIo for QcowSync {
|
||||
fn read_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
self.qcow_file.read_vectored_sync(
|
||||
@@ -81,7 +80,7 @@ impl AsyncIo for QcowSync {
|
||||
fn write_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
self.qcow_file.write_vectored_sync(
|
||||
@@ -98,7 +97,7 @@ impl AsyncIo for QcowSync {
|
||||
.fsync_sync(user_data, &self.eventfd, &mut self.completion_list)
|
||||
}
|
||||
|
||||
fn next_completed_request(&mut self) -> Option<(u64, i32)> {
|
||||
self.completion_list.pop_front()
|
||||
fn complete(&mut self) -> Vec<(u64, i32)> {
|
||||
self.completion_list.drain(..).collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,12 +76,12 @@ impl AsyncIo for RawFileAsync {
|
||||
fn read_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
let (submitter, mut sq, _) = self.io_uring.split();
|
||||
|
||||
// SAFETY: we know the file descriptor is valid and we
|
||||
// Safe because we know the file descriptor is valid and we
|
||||
// relied on vm-memory to provide the buffer address.
|
||||
let _ = unsafe {
|
||||
sq.push(
|
||||
@@ -104,12 +104,12 @@ impl AsyncIo for RawFileAsync {
|
||||
fn write_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
let (submitter, mut sq, _) = self.io_uring.split();
|
||||
|
||||
// SAFETY: we know the file descriptor is valid and we
|
||||
// Safe because we know the file descriptor is valid and we
|
||||
// relied on vm-memory to provide the buffer address.
|
||||
let _ = unsafe {
|
||||
sq.push(
|
||||
@@ -133,7 +133,7 @@ impl AsyncIo for RawFileAsync {
|
||||
if let Some(user_data) = user_data {
|
||||
let (submitter, mut sq, _) = self.io_uring.split();
|
||||
|
||||
// SAFETY: we know the file descriptor is valid.
|
||||
// Safe because we know the file descriptor is valid.
|
||||
let _ = unsafe {
|
||||
sq.push(
|
||||
&opcode::Fsync::new(types::Fd(self.fd))
|
||||
@@ -148,17 +148,20 @@ impl AsyncIo for RawFileAsync {
|
||||
sq.sync();
|
||||
submitter.submit().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)> {
|
||||
self.io_uring
|
||||
.completion()
|
||||
.next()
|
||||
.map(|entry| (entry.user_data(), entry.result()))
|
||||
fn complete(&mut self) -> Vec<(u64, i32)> {
|
||||
let mut completion_list = Vec::new();
|
||||
|
||||
let cq = self.io_uring.completion();
|
||||
for cq_entry in cq {
|
||||
completion_list.push((cq_entry.user_data(), cq_entry.result()));
|
||||
}
|
||||
|
||||
completion_list
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult, DiskTopology,
|
||||
};
|
||||
use std::collections::VecDeque;
|
||||
use std::fs::File;
|
||||
use std::io::{Seek, SeekFrom};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
@@ -45,7 +44,7 @@ impl DiskFile for RawFileDiskSync {
|
||||
pub struct RawFileSync {
|
||||
fd: RawFd,
|
||||
eventfd: EventFd,
|
||||
completion_list: VecDeque<(u64, i32)>,
|
||||
completion_list: Vec<(u64, i32)>,
|
||||
}
|
||||
|
||||
impl RawFileSync {
|
||||
@@ -53,7 +52,7 @@ impl RawFileSync {
|
||||
RawFileSync {
|
||||
fd,
|
||||
eventfd: EventFd::new(libc::EFD_NONBLOCK).expect("Failed creating EventFd for RawFile"),
|
||||
completion_list: VecDeque::new(),
|
||||
completion_list: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,10 +65,9 @@ impl AsyncIo for RawFileSync {
|
||||
fn read_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
// SAFETY: FFI call with valid arguments
|
||||
let result = unsafe {
|
||||
libc::preadv(
|
||||
self.fd as libc::c_int,
|
||||
@@ -82,7 +80,7 @@ impl AsyncIo for RawFileSync {
|
||||
return Err(AsyncIoError::ReadVectored(std::io::Error::last_os_error()));
|
||||
}
|
||||
|
||||
self.completion_list.push_back((user_data, result as i32));
|
||||
self.completion_list.push((user_data, result as i32));
|
||||
self.eventfd.write(1).unwrap();
|
||||
|
||||
Ok(())
|
||||
@@ -91,10 +89,9 @@ impl AsyncIo for RawFileSync {
|
||||
fn write_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
// SAFETY: FFI call with valid arguments
|
||||
let result = unsafe {
|
||||
libc::pwritev(
|
||||
self.fd as libc::c_int,
|
||||
@@ -107,28 +104,27 @@ impl AsyncIo for RawFileSync {
|
||||
return Err(AsyncIoError::WriteVectored(std::io::Error::last_os_error()));
|
||||
}
|
||||
|
||||
self.completion_list.push_back((user_data, result as i32));
|
||||
self.completion_list.push((user_data, result as i32));
|
||||
self.eventfd.write(1).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
|
||||
// SAFETY: FFI call
|
||||
let result = unsafe { libc::fsync(self.fd as libc::c_int) };
|
||||
if result < 0 {
|
||||
return Err(AsyncIoError::Fsync(std::io::Error::last_os_error()));
|
||||
}
|
||||
|
||||
if let Some(user_data) = user_data {
|
||||
self.completion_list.push_back((user_data, result));
|
||||
self.completion_list.push((user_data, result));
|
||||
self.eventfd.write(1).unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn next_completed_request(&mut self) -> Option<(u64, i32)> {
|
||||
self.completion_list.pop_front()
|
||||
fn complete(&mut self) -> Vec<(u64, i32)> {
|
||||
self.completion_list.drain(..).collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult};
|
||||
use crate::AsyncAdaptor;
|
||||
use std::collections::VecDeque;
|
||||
use std::fs::File;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use vhdx::vhdx::{Result as VhdxResult, Vhdx};
|
||||
@@ -38,7 +37,7 @@ impl DiskFile for VhdxDiskSync {
|
||||
pub struct VhdxSync {
|
||||
vhdx_file: Arc<Mutex<Vhdx>>,
|
||||
eventfd: EventFd,
|
||||
completion_list: VecDeque<(u64, i32)>,
|
||||
completion_list: Vec<(u64, i32)>,
|
||||
}
|
||||
|
||||
impl VhdxSync {
|
||||
@@ -46,7 +45,7 @@ impl VhdxSync {
|
||||
Ok(VhdxSync {
|
||||
vhdx_file,
|
||||
eventfd: EventFd::new(libc::EFD_NONBLOCK)?,
|
||||
completion_list: VecDeque::new(),
|
||||
completion_list: Vec::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -65,7 +64,7 @@ impl AsyncIo for VhdxSync {
|
||||
fn read_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
self.vhdx_file.read_vectored_sync(
|
||||
@@ -80,7 +79,7 @@ impl AsyncIo for VhdxSync {
|
||||
fn write_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
iovecs: Vec<libc::iovec>,
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
self.vhdx_file.write_vectored_sync(
|
||||
@@ -97,7 +96,7 @@ impl AsyncIo for VhdxSync {
|
||||
.fsync_sync(user_data, &self.eventfd, &mut self.completion_list)
|
||||
}
|
||||
|
||||
fn next_completed_request(&mut self) -> Option<(u64, i32)> {
|
||||
self.completion_list.pop_front()
|
||||
fn complete(&mut self) -> Vec<(u64, i32)> {
|
||||
self.completion_list.drain(..).collect()
|
||||
}
|
||||
}
|
||||
|
||||
5
build.rs
5
build.rs
@@ -3,10 +3,13 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#[macro_use(crate_version)]
|
||||
extern crate clap;
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let mut version = "v".to_owned() + env!("CARGO_PKG_VERSION");
|
||||
let mut version = "v".to_owned() + crate_version!();
|
||||
|
||||
if let Ok(git_out) = Command::new("git").args(["describe", "--dirty"]).output() {
|
||||
if git_out.status.success() {
|
||||
|
||||
@@ -6,15 +6,15 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { path = "../acpi_tables" }
|
||||
anyhow = "1.0.68"
|
||||
anyhow = "1.0.66"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "1.3.2"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.139"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
phf = { version = "0.11.1", features = ["macros"] }
|
||||
thiserror = "1.0.38"
|
||||
thiserror = "1.0.37"
|
||||
tpm = { path = "../tpm" }
|
||||
versionize = "0.1.9"
|
||||
versionize_derive = "0.1.4"
|
||||
|
||||
@@ -8,7 +8,7 @@ use anyhow::anyhow;
|
||||
use arch::layout;
|
||||
use hypervisor::{
|
||||
arch::aarch64::gic::{Vgic, VgicConfig},
|
||||
CpuState, GicState,
|
||||
CpuState,
|
||||
};
|
||||
use std::result;
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -25,7 +25,6 @@ type Result<T> = result::Result<T, Error>;
|
||||
// Reserve 32 IRQs for legacy devices.
|
||||
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 device. It wraps a hypervisor-emulated GIC device (Vgic) provided by the
|
||||
@@ -40,9 +39,8 @@ pub struct Gic {
|
||||
|
||||
impl Gic {
|
||||
pub fn new(
|
||||
vcpu_count: u8,
|
||||
_vcpu_count: u8,
|
||||
interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
vm: Arc<dyn hypervisor::Vm>,
|
||||
) -> Result<Gic> {
|
||||
let interrupt_source_group = interrupt_manager
|
||||
.create_group(MsiIrqGroupConfig {
|
||||
@@ -51,34 +49,45 @@ impl Gic {
|
||||
})
|
||||
.map_err(Error::CreateInterruptSourceGroup)?;
|
||||
|
||||
let vgic = vm
|
||||
.create_vgic(Gic::create_default_config(vcpu_count as u64))
|
||||
.map_err(Error::CreateGic)?;
|
||||
|
||||
let gic = Gic {
|
||||
Ok(Gic {
|
||||
interrupt_source_group,
|
||||
vgic: Some(vgic),
|
||||
};
|
||||
gic.enable()?;
|
||||
|
||||
Ok(gic)
|
||||
vgic: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn restore_vgic(
|
||||
/// Default config implied by arch::layout
|
||||
pub fn create_default_config(vcpu_count: u64) -> VgicConfig {
|
||||
let redists_size = layout::GIC_V3_REDIST_SIZE * vcpu_count;
|
||||
let redists_addr = layout::GIC_V3_DIST_START.raw_value() - redists_size;
|
||||
VgicConfig {
|
||||
vcpu_count,
|
||||
dist_addr: layout::GIC_V3_DIST_START.raw_value(),
|
||||
dist_size: layout::GIC_V3_DIST_SIZE,
|
||||
redists_addr,
|
||||
redists_size,
|
||||
msi_addr: redists_addr - layout::GIC_V3_ITS_SIZE,
|
||||
msi_size: layout::GIC_V3_ITS_SIZE,
|
||||
nr_irqs: layout::IRQ_NUM,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_vgic(
|
||||
&mut self,
|
||||
state: Option<GicState>,
|
||||
saved_vcpu_states: &[CpuState],
|
||||
) -> Result<()> {
|
||||
self.set_gicr_typers(saved_vcpu_states);
|
||||
self.vgic
|
||||
.clone()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_state(&state.unwrap())
|
||||
.map_err(Error::RestoreGic)
|
||||
vm: &Arc<dyn hypervisor::Vm>,
|
||||
config: VgicConfig,
|
||||
) -> Result<Arc<Mutex<dyn Vgic>>> {
|
||||
let vgic = vm.create_vgic(config).map_err(Error::CreateGic)?;
|
||||
self.vgic = Some(vgic.clone());
|
||||
Ok(vgic.clone())
|
||||
}
|
||||
|
||||
pub fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
|
||||
let vgic = self.vgic.as_ref().unwrap().clone();
|
||||
vgic.lock().unwrap().set_gicr_typers(vcpu_states);
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptController for Gic {
|
||||
fn enable(&self) -> Result<()> {
|
||||
// Set irqfd for legacy interrupts
|
||||
self.interrupt_source_group
|
||||
@@ -104,33 +113,6 @@ impl Gic {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Default config implied by arch::layout
|
||||
pub fn create_default_config(vcpu_count: u64) -> VgicConfig {
|
||||
let redists_size = layout::GIC_V3_REDIST_SIZE * vcpu_count;
|
||||
let redists_addr = layout::GIC_V3_DIST_START.raw_value() - redists_size;
|
||||
VgicConfig {
|
||||
vcpu_count,
|
||||
dist_addr: layout::GIC_V3_DIST_START.raw_value(),
|
||||
dist_size: layout::GIC_V3_DIST_SIZE,
|
||||
redists_addr,
|
||||
redists_size,
|
||||
msi_addr: redists_addr - layout::GIC_V3_ITS_SIZE,
|
||||
msi_size: layout::GIC_V3_ITS_SIZE,
|
||||
nr_irqs: layout::IRQ_NUM,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_vgic(&mut self) -> Result<Arc<Mutex<dyn Vgic>>> {
|
||||
Ok(self.vgic.clone().unwrap())
|
||||
}
|
||||
|
||||
pub fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
|
||||
let vgic = self.vgic.as_ref().unwrap().clone();
|
||||
vgic.lock().unwrap().set_gicr_typers(vcpu_states);
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptController for Gic {
|
||||
// This should be called anytime an interrupt needs to be injected into the
|
||||
// running guest.
|
||||
fn service_irq(&mut self, irq: usize) -> Result<()> {
|
||||
@@ -146,15 +128,27 @@ impl InterruptController for Gic {
|
||||
}
|
||||
}
|
||||
|
||||
pub const GIC_V3_ITS_SNAPSHOT_ID: &str = "gic-v3-its";
|
||||
impl Snapshottable for Gic {
|
||||
fn id(&self) -> String {
|
||||
GIC_SNAPSHOT_ID.to_string()
|
||||
GIC_V3_ITS_SNAPSHOT_ID.to_string()
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
let vgic = self.vgic.as_ref().unwrap().clone();
|
||||
let state = vgic.lock().unwrap().state().unwrap();
|
||||
Snapshot::new_from_state(&state)
|
||||
Snapshot::new_from_state(&self.id(), &state)
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
let vgic = self.vgic.as_ref().unwrap().clone();
|
||||
vgic.lock()
|
||||
.unwrap()
|
||||
.set_state(&snapshot.to_state(&self.id())?)
|
||||
.map_err(|e| {
|
||||
MigratableError::Restore(anyhow!("Could not restore GICv3ITS state {:?}", e))
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,12 +24,8 @@ pub enum Error {
|
||||
UpdateInterrupt(io::Error),
|
||||
/// Failed enabling the interrupt.
|
||||
EnableInterrupt(io::Error),
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
/// Failed creating GIC device.
|
||||
CreateGic(hypervisor::HypervisorVmError),
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
/// Failed restoring GIC device.
|
||||
RestoreGic(hypervisor::arch::aarch64::gic::Error),
|
||||
}
|
||||
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
@@ -59,6 +55,8 @@ pub struct MsiMessage {
|
||||
// IOAPIC (X86) or GIC (Arm).
|
||||
pub trait InterruptController: Send {
|
||||
fn service_irq(&mut self, irq: usize) -> Result<()>;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn enable(&self) -> Result<()>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn end_of_interrupt(&mut self, vec: u8);
|
||||
fn notifier(&self, irq: usize) -> Option<EventFd>;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
// See https://pdos.csail.mit.edu/6.828/2016/readings/ia32/ioapic.pdf for a specification.
|
||||
|
||||
use super::interrupt_controller::{Error, InterruptController};
|
||||
use anyhow::anyhow;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use std::result;
|
||||
use std::sync::{Arc, Barrier};
|
||||
@@ -193,7 +194,6 @@ impl Ioapic {
|
||||
id: String,
|
||||
apic_address: GuestAddress,
|
||||
interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
state: Option<IoapicState>,
|
||||
) -> Result<Ioapic> {
|
||||
let interrupt_source_group = interrupt_manager
|
||||
.create_group(MsiIrqGroupConfig {
|
||||
@@ -202,47 +202,17 @@ impl Ioapic {
|
||||
})
|
||||
.map_err(Error::CreateInterruptSourceGroup)?;
|
||||
|
||||
let (id_reg, reg_sel, reg_entries, used_entries, apic_address) = if let Some(state) = &state
|
||||
{
|
||||
(
|
||||
state.id_reg,
|
||||
state.reg_sel,
|
||||
state.reg_entries,
|
||||
state.used_entries,
|
||||
GuestAddress(state.apic_address),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
0,
|
||||
0,
|
||||
[0x10000; NUM_IOAPIC_PINS],
|
||||
[false; NUM_IOAPIC_PINS],
|
||||
apic_address,
|
||||
)
|
||||
};
|
||||
|
||||
// The IOAPIC is created with entries already masked. The guest will be
|
||||
// in charge of unmasking them if/when necessary.
|
||||
let ioapic = Ioapic {
|
||||
Ok(Ioapic {
|
||||
id,
|
||||
id_reg,
|
||||
reg_sel,
|
||||
reg_entries,
|
||||
used_entries,
|
||||
id_reg: 0,
|
||||
reg_sel: 0,
|
||||
reg_entries: [0x10000; NUM_IOAPIC_PINS],
|
||||
used_entries: [false; NUM_IOAPIC_PINS],
|
||||
apic_address,
|
||||
interrupt_source_group,
|
||||
};
|
||||
|
||||
// When restoring the Ioapic, we must enable used entries.
|
||||
if state.is_some() {
|
||||
for (irq, entry) in ioapic.used_entries.iter().enumerate() {
|
||||
if *entry {
|
||||
ioapic.update_entry(irq)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ioapic)
|
||||
})
|
||||
}
|
||||
|
||||
fn ioapic_write(&mut self, val: u32) {
|
||||
@@ -329,6 +299,21 @@ impl Ioapic {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &IoapicState) -> Result<()> {
|
||||
self.id_reg = state.id_reg;
|
||||
self.reg_sel = state.reg_sel;
|
||||
self.reg_entries = state.reg_entries;
|
||||
self.used_entries = state.used_entries;
|
||||
self.apic_address = GuestAddress(state.apic_address);
|
||||
for (irq, entry) in self.used_entries.iter().enumerate() {
|
||||
if *entry {
|
||||
self.update_entry(irq)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_entry(&self, irq: usize) -> Result<()> {
|
||||
let entry = self.reg_entries[irq];
|
||||
|
||||
@@ -438,7 +423,18 @@ impl Snapshottable for Ioapic {
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
Snapshot::new_from_versioned_state(&self.state())
|
||||
Snapshot::new_from_versioned_state(&self.id, &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?)
|
||||
.map_err(|e| {
|
||||
MigratableError::Restore(anyhow!(
|
||||
"Could not restore state for {}: {:?}",
|
||||
self.id,
|
||||
e
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ impl BusDevice for Cmos {
|
||||
let day;
|
||||
let month;
|
||||
let year;
|
||||
// SAFETY: The clock_gettime and gmtime_r calls are safe as long as the structs they are
|
||||
// The clock_gettime and gmtime_r calls are safe as long as the structs they are
|
||||
// given are large enough, and neither of them fail. It is safe to zero initialize
|
||||
// the tm and timespec struct because it contains only plain data.
|
||||
let update_in_progress = unsafe {
|
||||
|
||||
@@ -106,39 +106,18 @@ impl VersionMapped for GpioState {}
|
||||
|
||||
impl Gpio {
|
||||
/// Constructs an PL061 GPIO device.
|
||||
pub fn new(
|
||||
id: String,
|
||||
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||
state: Option<GpioState>,
|
||||
) -> Self {
|
||||
let (data, old_in_data, dir, isense, ibe, iev, im, istate, afsel) =
|
||||
if let Some(state) = state {
|
||||
(
|
||||
state.data,
|
||||
state.old_in_data,
|
||||
state.dir,
|
||||
state.isense,
|
||||
state.ibe,
|
||||
state.iev,
|
||||
state.im,
|
||||
state.istate,
|
||||
state.afsel,
|
||||
)
|
||||
} else {
|
||||
(0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
};
|
||||
|
||||
pub fn new(id: String, interrupt: Arc<dyn InterruptSourceGroup>) -> Self {
|
||||
Self {
|
||||
id,
|
||||
data,
|
||||
old_in_data,
|
||||
dir,
|
||||
isense,
|
||||
ibe,
|
||||
iev,
|
||||
im,
|
||||
istate,
|
||||
afsel,
|
||||
data: 0,
|
||||
old_in_data: 0,
|
||||
dir: 0,
|
||||
isense: 0,
|
||||
ibe: 0,
|
||||
iev: 0,
|
||||
im: 0,
|
||||
istate: 0,
|
||||
afsel: 0,
|
||||
interrupt,
|
||||
}
|
||||
}
|
||||
@@ -157,12 +136,24 @@ impl Gpio {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &GpioState) {
|
||||
self.data = state.data;
|
||||
self.old_in_data = state.old_in_data;
|
||||
self.dir = state.dir;
|
||||
self.isense = state.isense;
|
||||
self.ibe = state.ibe;
|
||||
self.iev = state.iev;
|
||||
self.im = state.im;
|
||||
self.istate = state.istate;
|
||||
self.afsel = state.afsel;
|
||||
}
|
||||
|
||||
fn pl061_internal_update(&mut self) {
|
||||
// FIXME:
|
||||
// Missing Output Interrupt Emulation.
|
||||
|
||||
// Input Edging Interrupt Emulation.
|
||||
let changed = (self.old_in_data ^ self.data) & !self.dir;
|
||||
let changed = ((self.old_in_data ^ self.data) & !self.dir) as u32;
|
||||
if changed > 0 {
|
||||
self.old_in_data = self.data;
|
||||
for i in 0..N_GPIOS {
|
||||
@@ -328,7 +319,12 @@ impl Snapshottable for Gpio {
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
Snapshot::new_from_versioned_state(&self.state())
|
||||
Snapshot::new_from_versioned_state(&self.id, &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,7 +378,6 @@ mod tests {
|
||||
let mut gpio = Gpio::new(
|
||||
String::from(GPIO_NAME),
|
||||
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||
None,
|
||||
);
|
||||
let mut data = [0; 4];
|
||||
|
||||
|
||||
@@ -61,10 +61,12 @@ pub enum ClockType {
|
||||
/// Equivalent to `libc::CLOCK_MONOTONIC`.
|
||||
Monotonic,
|
||||
/// Equivalent to `libc::CLOCK_REALTIME`.
|
||||
#[allow(dead_code)]
|
||||
Real,
|
||||
/// Equivalent to `libc::CLOCK_PROCESS_CPUTIME_ID`.
|
||||
ProcessCpu,
|
||||
/// Equivalent to `libc::CLOCK_THREAD_CPUTIME_ID`.
|
||||
#[allow(dead_code)]
|
||||
ThreadCpu,
|
||||
}
|
||||
|
||||
@@ -99,7 +101,7 @@ pub struct LocalTime {
|
||||
|
||||
impl LocalTime {
|
||||
/// Returns the [LocalTime](struct.LocalTime.html) structure for the calling moment.
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code)]
|
||||
pub fn now() -> LocalTime {
|
||||
let mut timespec = libc::timespec {
|
||||
tv_sec: 0,
|
||||
@@ -119,7 +121,7 @@ impl LocalTime {
|
||||
tm_zone: std::ptr::null(),
|
||||
};
|
||||
|
||||
// SAFETY: the parameters are valid.
|
||||
// Safe because the parameters are valid.
|
||||
unsafe {
|
||||
libc::clock_gettime(libc::CLOCK_REALTIME, &mut timespec);
|
||||
libc::localtime_r(×pec.tv_sec, &mut tm);
|
||||
@@ -171,6 +173,22 @@ impl Default for TimestampUs {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a timestamp in nanoseconds from a monotonic clock.
|
||||
///
|
||||
/// Uses `_rdstc` on `x86_64` and [`get_time`](fn.get_time.html) on other architectures.
|
||||
#[allow(dead_code)]
|
||||
pub fn timestamp_cycles() -> u64 {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
// Safe because there's nothing that can go wrong with this call.
|
||||
unsafe {
|
||||
std::arch::x86_64::_rdtsc() as u64
|
||||
}
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
{
|
||||
get_time(ClockType::Monotonic)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a timestamp in nanoseconds based on the provided clock type.
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -181,7 +199,7 @@ pub fn get_time(clock_type: ClockType) -> u64 {
|
||||
tv_sec: 0,
|
||||
tv_nsec: 0,
|
||||
};
|
||||
// SAFETY: the parameters are valid.
|
||||
// Safe because the parameters are valid.
|
||||
unsafe { libc::clock_gettime(clock_type.into(), &mut time_struct) };
|
||||
seconds_to_nanoseconds(time_struct.tv_sec).unwrap() as u64 + (time_struct.tv_nsec as u64)
|
||||
}
|
||||
@@ -507,6 +525,7 @@ mod tests {
|
||||
($test_name: ident, $write_fn_name: ident, $read_fn_name: ident, $is_be: expr, $data_type: ty) => {
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
#[allow(overflowing_literals)]
|
||||
let test_cases = [
|
||||
(
|
||||
0x0123_4567_89AB_CDEF as u64,
|
||||
|
||||
@@ -63,6 +63,7 @@ pub struct Serial {
|
||||
id: String,
|
||||
interrupt_enable: u8,
|
||||
interrupt_identification: u8,
|
||||
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||
line_control: u8,
|
||||
line_status: u8,
|
||||
modem_control: u8,
|
||||
@@ -70,7 +71,6 @@ pub struct Serial {
|
||||
scratch: u8,
|
||||
baud_divisor: u16,
|
||||
in_buffer: VecDeque<u8>,
|
||||
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||
out: Option<Box<dyn io::Write + Send>>,
|
||||
}
|
||||
|
||||
@@ -93,56 +93,19 @@ impl Serial {
|
||||
id: String,
|
||||
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||
out: Option<Box<dyn io::Write + Send>>,
|
||||
state: Option<SerialState>,
|
||||
) -> Serial {
|
||||
let (
|
||||
interrupt_enable,
|
||||
interrupt_identification,
|
||||
line_control,
|
||||
line_status,
|
||||
modem_control,
|
||||
modem_status,
|
||||
scratch,
|
||||
baud_divisor,
|
||||
in_buffer,
|
||||
) = if let Some(state) = state {
|
||||
(
|
||||
state.interrupt_enable,
|
||||
state.interrupt_identification,
|
||||
state.line_control,
|
||||
state.line_status,
|
||||
state.modem_control,
|
||||
state.modem_status,
|
||||
state.scratch,
|
||||
state.baud_divisor,
|
||||
state.in_buffer.into(),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
0,
|
||||
DEFAULT_INTERRUPT_IDENTIFICATION,
|
||||
DEFAULT_LINE_CONTROL,
|
||||
DEFAULT_LINE_STATUS,
|
||||
DEFAULT_MODEM_CONTROL,
|
||||
DEFAULT_MODEM_STATUS,
|
||||
0,
|
||||
DEFAULT_BAUD_DIVISOR,
|
||||
VecDeque::new(),
|
||||
)
|
||||
};
|
||||
|
||||
Serial {
|
||||
id,
|
||||
interrupt_enable,
|
||||
interrupt_identification,
|
||||
line_control,
|
||||
line_status,
|
||||
modem_control,
|
||||
modem_status,
|
||||
scratch,
|
||||
baud_divisor,
|
||||
in_buffer,
|
||||
interrupt_enable: 0,
|
||||
interrupt_identification: DEFAULT_INTERRUPT_IDENTIFICATION,
|
||||
interrupt,
|
||||
line_control: DEFAULT_LINE_CONTROL,
|
||||
line_status: DEFAULT_LINE_STATUS,
|
||||
modem_control: DEFAULT_MODEM_CONTROL,
|
||||
modem_status: DEFAULT_MODEM_STATUS,
|
||||
scratch: 0,
|
||||
baud_divisor: DEFAULT_BAUD_DIVISOR,
|
||||
in_buffer: VecDeque::new(),
|
||||
out,
|
||||
}
|
||||
}
|
||||
@@ -152,18 +115,13 @@ impl Serial {
|
||||
id: String,
|
||||
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||
out: Box<dyn io::Write + Send>,
|
||||
state: Option<SerialState>,
|
||||
) -> Serial {
|
||||
Self::new(id, interrupt, Some(out), state)
|
||||
Self::new(id, interrupt, Some(out))
|
||||
}
|
||||
|
||||
/// Constructs a Serial port with no connected output.
|
||||
pub fn new_sink(
|
||||
id: String,
|
||||
interrupt: Arc<dyn InterruptSourceGroup>,
|
||||
state: Option<SerialState>,
|
||||
) -> Serial {
|
||||
Self::new(id, interrupt, None, state)
|
||||
pub fn new_sink(id: String, interrupt: Arc<dyn InterruptSourceGroup>) -> Serial {
|
||||
Self::new(id, interrupt, None)
|
||||
}
|
||||
|
||||
pub fn set_out(&mut self, out: Box<dyn io::Write + Send>) {
|
||||
@@ -284,6 +242,18 @@ impl Serial {
|
||||
in_buffer: self.in_buffer.clone().into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &SerialState) {
|
||||
self.interrupt_enable = state.interrupt_enable;
|
||||
self.interrupt_identification = state.interrupt_identification;
|
||||
self.line_control = state.line_control;
|
||||
self.line_status = state.line_status;
|
||||
self.modem_control = state.modem_control;
|
||||
self.modem_status = state.modem_status;
|
||||
self.scratch = state.scratch;
|
||||
self.baud_divisor = state.baud_divisor;
|
||||
self.in_buffer = state.in_buffer.clone().into();
|
||||
}
|
||||
}
|
||||
|
||||
impl BusDevice for Serial {
|
||||
@@ -334,7 +304,12 @@ impl Snapshottable for Serial {
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
Snapshot::new_from_versioned_state(&self.state())
|
||||
Snapshot::new_from_versioned_state(&self.id, &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,7 +384,6 @@ mod tests {
|
||||
String::from(SERIAL_NAME),
|
||||
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||
Box::new(serial_out.clone()),
|
||||
None,
|
||||
);
|
||||
|
||||
serial.write(0, DATA as u64, &[b'x', b'y']);
|
||||
@@ -430,7 +404,6 @@ mod tests {
|
||||
String::from(SERIAL_NAME),
|
||||
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||
Box::new(serial_out),
|
||||
None,
|
||||
);
|
||||
|
||||
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd
|
||||
@@ -467,7 +440,6 @@ mod tests {
|
||||
let mut serial = Serial::new_sink(
|
||||
String::from(SERIAL_NAME),
|
||||
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||
None,
|
||||
);
|
||||
|
||||
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd
|
||||
@@ -490,7 +462,6 @@ mod tests {
|
||||
let mut serial = Serial::new_sink(
|
||||
String::from(SERIAL_NAME),
|
||||
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||
None,
|
||||
);
|
||||
|
||||
serial.write(0, LCR as u64, &[LCR_DLAB_BIT]);
|
||||
@@ -512,7 +483,6 @@ mod tests {
|
||||
let mut serial = Serial::new_sink(
|
||||
String::from(SERIAL_NAME),
|
||||
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||
None,
|
||||
);
|
||||
|
||||
serial.write(0, MCR as u64, &[MCR_LOOP_BIT]);
|
||||
@@ -539,7 +509,6 @@ mod tests {
|
||||
let mut serial = Serial::new_sink(
|
||||
String::from(SERIAL_NAME),
|
||||
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||
None,
|
||||
);
|
||||
|
||||
serial.write(0, SCR as u64, &[0x12]);
|
||||
|
||||
@@ -122,79 +122,24 @@ impl Pl011 {
|
||||
irq: Arc<dyn InterruptSourceGroup>,
|
||||
out: Option<Box<dyn io::Write + Send>>,
|
||||
timestamp: Instant,
|
||||
state: Option<Pl011State>,
|
||||
) -> Self {
|
||||
let (
|
||||
flags,
|
||||
lcr,
|
||||
rsr,
|
||||
cr,
|
||||
dmacr,
|
||||
debug,
|
||||
int_enabled,
|
||||
int_level,
|
||||
read_fifo,
|
||||
ilpr,
|
||||
ibrd,
|
||||
fbrd,
|
||||
ifl,
|
||||
read_count,
|
||||
read_trigger,
|
||||
) = if let Some(state) = state {
|
||||
(
|
||||
state.flags,
|
||||
state.lcr,
|
||||
state.rsr,
|
||||
state.cr,
|
||||
state.dmacr,
|
||||
state.debug,
|
||||
state.int_enabled,
|
||||
state.int_level,
|
||||
state.read_fifo.into(),
|
||||
state.ilpr,
|
||||
state.ibrd,
|
||||
state.fbrd,
|
||||
state.ifl,
|
||||
state.read_count,
|
||||
state.read_trigger,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
0x90,
|
||||
0,
|
||||
0,
|
||||
0x300,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
VecDeque::new(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0x12,
|
||||
0,
|
||||
1,
|
||||
)
|
||||
};
|
||||
|
||||
Self {
|
||||
id,
|
||||
flags,
|
||||
lcr,
|
||||
rsr,
|
||||
cr,
|
||||
dmacr,
|
||||
debug,
|
||||
int_enabled,
|
||||
int_level,
|
||||
read_fifo,
|
||||
ilpr,
|
||||
ibrd,
|
||||
fbrd,
|
||||
ifl,
|
||||
read_count,
|
||||
read_trigger,
|
||||
flags: 0x90u32,
|
||||
lcr: 0u32,
|
||||
rsr: 0u32,
|
||||
cr: 0x300u32,
|
||||
dmacr: 0u32,
|
||||
debug: 0u32,
|
||||
int_enabled: 0u32,
|
||||
int_level: 0u32,
|
||||
read_fifo: VecDeque::new(),
|
||||
ilpr: 0u32,
|
||||
ibrd: 0u32,
|
||||
fbrd: 0u32,
|
||||
ifl: 0x12u32,
|
||||
read_count: 0u32,
|
||||
read_trigger: 1u32,
|
||||
irq,
|
||||
out,
|
||||
timestamp,
|
||||
@@ -225,6 +170,24 @@ impl Pl011 {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &Pl011State) {
|
||||
self.flags = state.flags;
|
||||
self.lcr = state.lcr;
|
||||
self.rsr = state.rsr;
|
||||
self.cr = state.cr;
|
||||
self.dmacr = state.dmacr;
|
||||
self.debug = state.debug;
|
||||
self.int_enabled = state.int_enabled;
|
||||
self.int_level = state.int_level;
|
||||
self.read_fifo = state.read_fifo.clone().into();
|
||||
self.ilpr = state.ilpr;
|
||||
self.ibrd = state.ibrd;
|
||||
self.fbrd = state.fbrd;
|
||||
self.ifl = state.ifl;
|
||||
self.read_count = state.read_count;
|
||||
self.read_trigger = state.read_trigger;
|
||||
}
|
||||
|
||||
/// Queues raw bytes for the guest to read and signals the interrupt
|
||||
pub fn queue_input_bytes(&mut self, c: &[u8]) -> vmm_sys_util::errno::Result<()> {
|
||||
self.read_fifo.extend(c);
|
||||
@@ -454,7 +417,12 @@ impl Snapshottable for Pl011 {
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
Snapshot::new_from_versioned_state(&self.state())
|
||||
Snapshot::new_from_versioned_state(&self.id, &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,13 +498,12 @@ mod tests {
|
||||
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||
Some(Box::new(pl011_out.clone())),
|
||||
Instant::now(),
|
||||
None,
|
||||
);
|
||||
|
||||
pl011.write(0, UARTDR, &[b'x', b'y']);
|
||||
pl011.write(0, UARTDR, &[b'a']);
|
||||
pl011.write(0, UARTDR, &[b'b']);
|
||||
pl011.write(0, UARTDR, &[b'c']);
|
||||
pl011.write(0, UARTDR as u64, &[b'x', b'y']);
|
||||
pl011.write(0, UARTDR as u64, &[b'a']);
|
||||
pl011.write(0, UARTDR as u64, &[b'b']);
|
||||
pl011.write(0, UARTDR as u64, &[b'c']);
|
||||
assert_eq!(
|
||||
pl011_out.buf.lock().unwrap().as_slice(),
|
||||
&[b'x', b'a', b'b', b'c']
|
||||
@@ -552,7 +519,6 @@ mod tests {
|
||||
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||
Some(Box::new(pl011_out)),
|
||||
Instant::now(),
|
||||
None,
|
||||
);
|
||||
|
||||
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd
|
||||
@@ -563,11 +529,11 @@ mod tests {
|
||||
assert_eq!(intr_evt.read().unwrap(), 2);
|
||||
|
||||
let mut data = [0u8];
|
||||
pl011.read(0, UARTDR, &mut data);
|
||||
pl011.read(0, UARTDR as u64, &mut data);
|
||||
assert_eq!(data[0], b'a');
|
||||
pl011.read(0, UARTDR, &mut data);
|
||||
pl011.read(0, UARTDR as u64, &mut data);
|
||||
assert_eq!(data[0], b'b');
|
||||
pl011.read(0, UARTDR, &mut data);
|
||||
pl011.read(0, UARTDR as u64, &mut data);
|
||||
assert_eq!(data[0], b'c');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,9 +33,11 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
macro_rules! generate_read_fn {
|
||||
($fn_name: ident, $data_type: ty, $byte_type: ty, $type_size: expr, $endian_type: ident) => {
|
||||
#[allow(dead_code)]
|
||||
pub fn $fn_name(input: &[$byte_type]) -> $data_type {
|
||||
assert!($type_size == std::mem::size_of::<$data_type>());
|
||||
let mut array = [0u8; $type_size];
|
||||
@@ -47,9 +49,11 @@ macro_rules! generate_read_fn {
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
macro_rules! generate_write_fn {
|
||||
($fn_name: ident, $data_type: ty, $byte_type: ty, $endian_type: ident) => {
|
||||
#[allow(dead_code)]
|
||||
pub fn $fn_name(buf: &mut [$byte_type], n: $data_type) {
|
||||
for (byte, read) in buf
|
||||
.iter_mut()
|
||||
|
||||
152
docs/arm64.md
Normal file
152
docs/arm64.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# How to build and test Cloud Hypervisor on AArch64
|
||||
|
||||
This document introduces how to build and test Cloud Hypervisor on AArch64.
|
||||
Currently, Cloud Hypervisor supports 2 methods of booting on AArch64: UEFI
|
||||
booting and direct-kernel booting. The document covers both methods.
|
||||
|
||||
All the steps are based on Ubuntu. We use the Ubuntu cloud image for guest VM
|
||||
disk.
|
||||
|
||||
## Hardware requirements
|
||||
|
||||
- AArch64 servers (recommended) or development boards equipped with the GICv3
|
||||
interrupt controller.
|
||||
|
||||
- On development boards that have constrained RAM resources, if the creation of
|
||||
a VM consumes a large portion of the free memory on the host, it may be required
|
||||
to enable swap. For example, this was required on a board with 3 GB of RAM
|
||||
booting a 2 GB VM at a point in time when 2.8 GB were free. Without enabling
|
||||
swap the `cloud-hypervisor` process was terminated by the OOM killer. In this
|
||||
situation memory was allocated for the virtual machine using memfd while the
|
||||
page cache was filled, leading to a situation where the kernel could not even
|
||||
drop caches. Making a small section of swap available (observably, 1 to 15 MB),
|
||||
this situation can be resolved and the resulting memory footprint of
|
||||
`cloud-hypervisor` is as expected.
|
||||
|
||||
## Getting started
|
||||
|
||||
We create a folder to build and run Cloud Hypervisor at `$HOME/cloud-hypervisor`
|
||||
|
||||
```shell
|
||||
$ export CLOUDH=$HOME/cloud-hypervisor
|
||||
$ mkdir $CLOUDH
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You need to install some prerequisite packages to build and test Cloud Hypervisor.
|
||||
|
||||
### Tools
|
||||
|
||||
```bash
|
||||
# Install rust tool chain
|
||||
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
# Install the tools used for building guest kernel, EDK2 and converting guest disk
|
||||
$ sudo apt-get update
|
||||
$ sudo apt-get install git build-essential m4 bison flex uuid-dev qemu-utils
|
||||
```
|
||||
|
||||
### Building Cloud Hypervisor
|
||||
|
||||
```bash
|
||||
$ pushd $CLOUDH
|
||||
$ git clone https://github.com/cloud-hypervisor/cloud-hypervisor.git
|
||||
$ cd cloud-hypervisor
|
||||
$ cargo build
|
||||
$ popd
|
||||
```
|
||||
|
||||
### Disk image
|
||||
|
||||
Download the Ubuntu cloud image and convert the image type.
|
||||
|
||||
```bash
|
||||
$ pushd $CLOUDH
|
||||
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-arm64.img
|
||||
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-arm64.img focal-server-cloudimg-arm64.raw
|
||||
$ popd
|
||||
```
|
||||
|
||||
## UEFI booting
|
||||
|
||||
This part introduces how to build EDK2 firmware and boot Cloud Hypervisor with it.
|
||||
|
||||
### Building EDK2
|
||||
|
||||
```bash
|
||||
$ pushd $CLOUDH
|
||||
|
||||
# Clone source code repos
|
||||
$ git clone --depth 1 https://github.com/tianocore/edk2.git -b master
|
||||
$ cd edk2
|
||||
$ git submodule update --init
|
||||
$ cd ..
|
||||
$ git clone --depth 1 https://github.com/tianocore/edk2-platforms.git -b master
|
||||
$ git clone --depth 1 https://github.com/acpica/acpica.git -b master
|
||||
|
||||
# Build tools
|
||||
$ export PACKAGES_PATH="$PWD/edk2:$PWD/edk2-platforms"
|
||||
$ export IASL_PREFIX="$PWD/acpica/generate/unix/bin/"
|
||||
$ make -C acpica
|
||||
$ cd edk2/
|
||||
$ . edksetup.sh
|
||||
$ cd ..
|
||||
$ make -C edk2/BaseTools
|
||||
|
||||
# Build EDK2
|
||||
$ build -a AARCH64 -t GCC5 -p ArmVirtPkg/ArmVirtCloudHv.dsc -b RELEASE
|
||||
|
||||
$ popd
|
||||
```
|
||||
|
||||
If the build goes well, the EDK2 binary is available at
|
||||
`edk2/Build/ArmVirtCloudHv-AARCH64/RELEASE_GCC5/FV/CLOUDHV_EFI.fd`.
|
||||
|
||||
### Booting the guest VM
|
||||
|
||||
```bash
|
||||
$ pushd $CLOUDH
|
||||
$ sudo RUST_BACKTRACE=1 $CLOUDH/cloud-hypervisor/target/debug/cloud-hypervisor \
|
||||
--api-socket /tmp/cloud-hypervisor.sock \
|
||||
--kernel $CLOUDH/edk2/Build/ArmVirtCloudHv-AARCH64/RELEASE_GCC5/FV/CLOUDHV_EFI.fd \
|
||||
--disk path=$CLOUDH/focal-server-cloudimg-arm64.raw \
|
||||
--cpus boot=4 \
|
||||
--memory size=4096M \
|
||||
--net tap=,mac=12:34:56:78:90:01,ip=192.168.1.1,mask=255.255.255.0 \
|
||||
--serial tty \
|
||||
--console off
|
||||
$ popd
|
||||
```
|
||||
|
||||
## Direct-kernel booting
|
||||
|
||||
Alternativelly, you can build your own kernel for guest VM. This way, UEFI is
|
||||
not involved and ACPI cannot be enabled.
|
||||
|
||||
### Building kernel
|
||||
|
||||
```bash
|
||||
$ pushd $CLOUDH
|
||||
$ git clone --depth 1 "https://github.com/cloud-hypervisor/linux.git" -b ch-5.12
|
||||
$ cd linux
|
||||
$ cp $CLOUDH/cloud-hypervisor/resources/linux-config-aarch64 .config
|
||||
$ make -j `nproc`
|
||||
$ popd
|
||||
```
|
||||
|
||||
### Booting the guest VM
|
||||
|
||||
```bash
|
||||
$ pushd $CLOUDH
|
||||
$ sudo $CLOUDH/cloud-hypervisor/target/debug/cloud-hypervisor \
|
||||
--api-socket /tmp/cloud-hypervisor.sock \
|
||||
--kernel $CLOUDH/linux/arch/arm64/boot/Image \
|
||||
--disk path=focal-server-cloudimg-arm64.raw \
|
||||
--cmdline "keep_bootcon console=ttyAMA0 reboot=k panic=1 root=/dev/vda1 rw" \
|
||||
--cpus boot=4 \
|
||||
--memory size=4096M \
|
||||
--net tap=,mac=12:34:56:78:90:01,ip=192.168.1.1,mask=255.255.255.0 \
|
||||
--serial tty \
|
||||
--console off
|
||||
$ popd
|
||||
```
|
||||
@@ -24,15 +24,12 @@ Hypervisor. Here, all the steps are based on Ubuntu, for other Linux
|
||||
distributions please replace the package manager and package name.
|
||||
|
||||
```shell
|
||||
# Install basic packages needed. For a package list targeting for more
|
||||
# functionalities for example the test, please see resources/Dockerfile.
|
||||
$ sudo apt-get update
|
||||
$ sudo apt install git build-essential m4 bison flex uuid-dev qemu-utils
|
||||
# Install build-essential, git, and qemu-utils
|
||||
$ sudo apt install git build-essential qemu-utils
|
||||
# Install rust tool chain
|
||||
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
# If you want to build statically linked binary please add musl target
|
||||
$ rustup target add x86_64-unknown-linux-musl # x86-64
|
||||
$ rustup target add aarch64-unknown-linux-musl # AArch64
|
||||
$ rustup target add x86_64-unknown-linux-musl
|
||||
```
|
||||
|
||||
## Clone and build
|
||||
@@ -49,8 +46,7 @@ $ cargo build --release
|
||||
$ sudo setcap cap_net_admin+ep ./target/release/cloud-hypervisor
|
||||
|
||||
# If you want to build statically linked binary
|
||||
$ cargo build --release --target=x86_64-unknown-linux-musl --all # x86-64
|
||||
$ cargo build --release --target=aarch64-unknown-linux-musl --all # AArch64
|
||||
$ cargo build --release --target=x86_64-unknown-linux-musl --all
|
||||
$ popd
|
||||
```
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ Ubuntu distributions.
|
||||
|
||||
```bash
|
||||
apt update
|
||||
apt install fio iperf iperf3 socat stress cpuid
|
||||
apt install fio iperf iperf3 socat stress
|
||||
```
|
||||
|
||||
### Remove counterproductive packages
|
||||
@@ -158,172 +158,3 @@ as we might need to update the direct kernel boot command line, replacing
|
||||
`/dev/vda1` with the appropriate partition number.
|
||||
|
||||
Update all references to the previous image name to the new one.
|
||||
|
||||
## NVIDIA image for VFIO baremetal CI
|
||||
|
||||
Here we are going to describe how to create a cloud image that contains the
|
||||
necessary NVIDIA drivers for our VFIO baremetal CI.
|
||||
|
||||
### Download base image
|
||||
|
||||
We usually start from one of the custom cloud image we have previously created
|
||||
but we can use a stock cloud image as well.
|
||||
|
||||
```bash
|
||||
wget https://cloud-hypervisor.azureedge.net/jammy-server-cloudimg-amd64-custom-20221118-1.raw
|
||||
mv jammy-server-cloudimg-amd64-custom-20221118-1.raw jammy-server-cloudimg-amd64-nvidia.raw
|
||||
```
|
||||
|
||||
### Extend the image size
|
||||
|
||||
The NVIDIA drivers consume lots of space, which is why we must resize the image
|
||||
before we proceed any further.
|
||||
|
||||
```bash
|
||||
qemu-img resize jammy-server-cloudimg-amd64-nvidia.raw 5G
|
||||
```
|
||||
|
||||
### Resize the partition
|
||||
|
||||
We use `parted` for fixing the GPT after the image was resized, as well as for
|
||||
resizing the `Linux` partition.
|
||||
|
||||
```bash
|
||||
sudo parted jammy-server-cloudimg-amd64-nvidia.raw
|
||||
|
||||
(parted) print
|
||||
Warning: Not all of the space available to jammy-server-cloudimg-amd64-nvidia.raw
|
||||
appears to be used, you can fix the GPT to use all of the space (an extra 5873664
|
||||
blocks) or continue with the current setting?
|
||||
Fix/Ignore? Fix
|
||||
Model: (file)
|
||||
Disk jammy-server-cloudimg-amd64-nvidia.raw: 5369MB
|
||||
Sector size (logical/physical): 512B/512B
|
||||
Partition Table: gpt
|
||||
Disk Flags:
|
||||
|
||||
Number Start End Size File system Name Flags
|
||||
14 1049kB 5243kB 4194kB bios_grub
|
||||
15 5243kB 116MB 111MB fat32 boot, esp
|
||||
1 116MB 2361MB 2245MB ext4
|
||||
|
||||
(parted) resizepart 1 5369MB
|
||||
(parted) print
|
||||
Model: (file)
|
||||
Disk jammy-server-cloudimg-amd64-nvidia.raw: 5369MB
|
||||
Sector size (logical/physical): 512B/512B
|
||||
Partition Table: gpt
|
||||
Disk Flags:
|
||||
|
||||
Number Start End Size File system Name Flags
|
||||
14 1049kB 5243kB 4194kB bios_grub
|
||||
15 5243kB 116MB 111MB fat32 boot, esp
|
||||
1 116MB 5369MB 5252MB ext4
|
||||
|
||||
(parted) quit
|
||||
```
|
||||
|
||||
### Create a macvtap interface
|
||||
|
||||
Rely on the following [documentation](docs/macvtap-bridge.md) to set up a
|
||||
macvtap interface to provide your VM with proper connectivity.
|
||||
|
||||
### Boot the image
|
||||
|
||||
It is particularly important to boot with a `cloud-init` disk attached to the
|
||||
VM as it will automatically resize the Linux `ext4` filesystem based on the
|
||||
partition that we have previously resized.
|
||||
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
--kernel hypervisor-fw \
|
||||
--disk path=focal-server-cloudimg-amd64-nvidia.raw path=/tmp/ubuntu-cloudinit.img \
|
||||
--cpus boot=4 \
|
||||
--memory size=4G \
|
||||
--net fd=3,mac=$mac 3<>$"$tapdevice"
|
||||
```
|
||||
|
||||
### Bring up connectivity
|
||||
|
||||
If your network has a DHCP server, run the following from your VM
|
||||
|
||||
```bash
|
||||
sudo dhclient
|
||||
```
|
||||
|
||||
But if that's not the case, let's give it an IP manually (the IP addresses
|
||||
depend on your actual network) and set the DNS server IP address as well.
|
||||
|
||||
```bash
|
||||
sudo ip addr add 192.168.2.10/24 dev ens4
|
||||
sudo ip link set up dev ens4
|
||||
sudo ip route add default via 192.168.2.1
|
||||
sudo resolvectl dns ens4 8.8.8.8
|
||||
```
|
||||
|
||||
#### Check connectivity and update the image
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt upgrade
|
||||
```
|
||||
|
||||
### Install NVIDIA drivers
|
||||
|
||||
The following steps and commands are referenced from the
|
||||
[NVIDIA official documentation](https://docs.nvidia.com/datacenter/tesla/tesla-installation-notes/index.html#ubuntu-lts)
|
||||
about Tesla compute cards.
|
||||
|
||||
```bash
|
||||
distribution=$(. /etc/os-release;echo $ID$VERSION_ID | sed -e 's/\.//g')
|
||||
wget https://developer.download.nvidia.com/compute/cuda/repos/$distribution/x86_64/cuda-keyring_1.0-1_all.deb
|
||||
sudo dpkg -i cuda-keyring_1.0-1_all.deb
|
||||
sudo apt-key del 7fa2af80
|
||||
sudo apt update
|
||||
sudo apt -y install cuda-drivers
|
||||
```
|
||||
|
||||
### Check the `nvidia-smi` tool
|
||||
|
||||
Quickly validate that you can find and run the `nvidia-smi` command from your
|
||||
VM. At this point it should fail given no NVIDIA card has been passed through
|
||||
the VM, therefore no NVIDIA driver is loaded.
|
||||
|
||||
### Workaround LA57 reboot issue
|
||||
|
||||
Add `reboot=a` to `GRUB_CMDLINE_LINUX` in `etc/default/grub` so that the VM
|
||||
will be booted with the ACPI reboot type. This resolves a reboot issue when
|
||||
running on 5-level paging systems.
|
||||
|
||||
```bash
|
||||
sudo vim /etc/default/grub
|
||||
sudo update-grub
|
||||
sudo reboot
|
||||
```
|
||||
|
||||
### Remove previous logins
|
||||
|
||||
Since our integration tests rely on past logins to count the number of reboots,
|
||||
we must ensure to clear the list.
|
||||
|
||||
```bash
|
||||
>/var/log/lastlog
|
||||
>/var/log/wtmp
|
||||
>/var/log/btmp
|
||||
```
|
||||
|
||||
### Clear history
|
||||
|
||||
```
|
||||
history -c
|
||||
rm /home/cloud/.bash_history
|
||||
```
|
||||
|
||||
### Reset cloud-init
|
||||
|
||||
This is mandatory as we want `cloud-init` provisioning to work again when a new
|
||||
VM will be booted with this image.
|
||||
|
||||
```
|
||||
sudo cloud-init clean
|
||||
```
|
||||
37
docs/uefi.md
37
docs/uefi.md
@@ -2,19 +2,17 @@
|
||||
|
||||
Cloud Hypervisor supports UEFI boot through the utilization of the EDK II based UEFI firmware.
|
||||
|
||||
## Building UEFI Firmware for x86-64
|
||||
## Building UEFI Firmware
|
||||
|
||||
To avoid any unnecessary issues, it is recommended to use Ubuntu 18.04 and its default toolset. Any other compatible Linux distribution is otherwise suitable, however it is suggested to use a temporary Docker container with Ubuntu 18.04 for a quick build on an existing Linux machine.
|
||||
|
||||
Please note that nasm-2.15 is required for the build to succeed.
|
||||
|
||||
The commands below will compile an OVMF firmware suitable for Cloud Hypervisor.
|
||||
|
||||
```shell
|
||||
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 clone https://github.com/cloud-hypervisor/edk2 -b ch
|
||||
cd edk2
|
||||
. edksetup.sh
|
||||
git submodule update --init
|
||||
@@ -29,40 +27,11 @@ build
|
||||
|
||||
After the successful build, the resulting firmware binaries are available under `Build/CloudHvX64/DEBUG_GCC5/FV` underneath the edk2 checkout.
|
||||
|
||||
## Building UEFI Firmware for AArch64
|
||||
|
||||
```shell
|
||||
# On an AArch64 machine:
|
||||
$ sudo apt-get update
|
||||
$ sudo apt-get install uuid-dev nasm iasl build-essential python3-distutils git
|
||||
$ git clone --depth 1 https://github.com/tianocore/edk2.git -b master
|
||||
$ cd edk2
|
||||
$ git submodule update --init
|
||||
$ cd ..
|
||||
$ git clone --depth 1 https://github.com/tianocore/edk2-platforms.git -b master
|
||||
$ git clone --depth 1 https://github.com/acpica/acpica.git -b master
|
||||
|
||||
# Build tools
|
||||
$ export PACKAGES_PATH="$PWD/edk2:$PWD/edk2-platforms"
|
||||
$ export IASL_PREFIX="$PWD/acpica/generate/unix/bin/"
|
||||
$ make -C acpica
|
||||
$ cd edk2/
|
||||
$ . edksetup.sh
|
||||
$ cd ..
|
||||
$ make -C edk2/BaseTools
|
||||
|
||||
# Build EDK2
|
||||
$ build -a AARCH64 -t GCC5 -p ArmVirtPkg/ArmVirtCloudHv.dsc -b RELEASE
|
||||
```
|
||||
|
||||
If the build goes well, the EDK2 binary is available at
|
||||
`edk2/Build/ArmVirtCloudHv-AARCH64/RELEASE_GCC5/FV/CLOUDHV_EFI.fd`.
|
||||
|
||||
## Using OVMF Binaries
|
||||
|
||||
Any UEFI capable image can be booted using the Cloud Hypervisor specific firmware. Windows guests under Cloud Hypervisor only support UEFI boot, therefore OVMF is mandatory there.
|
||||
|
||||
To make Cloud Hypervisor use UEFI boot, pass the `CLOUDHV.fd` (for x86-64) / `CLOUDHV_EFI.fd` (for AArch64) file path as an argument to the `--kernel` option. The firmware file will be opened in read only mode.
|
||||
To make Cloud Hypervisor use UEFI boot, pass the `CLOUDHV.fd` file path as an argument to the `--kernel` option. The firmware file will be opened in read only mode.
|
||||
|
||||
# Links
|
||||
|
||||
|
||||
@@ -5,6 +5,6 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.139"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
libc = "0.2.138"
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.89"
|
||||
|
||||
@@ -16,10 +16,8 @@ static mut MONITOR: Option<(File, Instant)> = None;
|
||||
/// This function must only be called once from the main process before any threads
|
||||
/// are created to avoid race conditions
|
||||
pub fn set_monitor(file: File) -> Result<(), std::io::Error> {
|
||||
// SAFETY: there is only one caller of this function, so MONITOR is written to only once
|
||||
assert!(unsafe { MONITOR.is_none() });
|
||||
let fd = file.as_raw_fd();
|
||||
// SAFETY: FFI call to configure the fd
|
||||
let ret = unsafe {
|
||||
let mut flags = libc::fcntl(fd, libc::F_GETFL);
|
||||
flags |= libc::O_NONBLOCK;
|
||||
@@ -28,7 +26,6 @@ pub fn set_monitor(file: File) -> Result<(), std::io::Error> {
|
||||
if ret < 0 {
|
||||
return Err(std::io::Error::last_os_error());
|
||||
}
|
||||
// SAFETY: MONITOR is None. Nobody else can hold a reference to it.
|
||||
unsafe {
|
||||
MONITOR = Some((file, Instant::now()));
|
||||
};
|
||||
@@ -44,7 +41,6 @@ struct Event<'a> {
|
||||
}
|
||||
|
||||
pub fn event_log(source: &str, event: &str, properties: Option<&HashMap<Cow<str>, Cow<str>>>) {
|
||||
// SAFETY: MONITOR is always in a valid state (None or Some).
|
||||
if let Some((file, start)) = unsafe { MONITOR.as_ref() } {
|
||||
let e = Event {
|
||||
timestamp: start.elapsed(),
|
||||
|
||||
92
fuzz/Cargo.lock
generated
92
fuzz/Cargo.lock
generated
@@ -11,9 +11,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.68"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
|
||||
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -79,7 +79,6 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"qcow",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
@@ -99,9 +98,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.78"
|
||||
version = "1.0.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
@@ -114,9 +113,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.0.32"
|
||||
version = "4.0.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39"
|
||||
checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
@@ -170,9 +169,7 @@ dependencies = [
|
||||
"epoll",
|
||||
"libc",
|
||||
"libfuzzer-sys",
|
||||
"linux-loader",
|
||||
"micro_http",
|
||||
"net_util",
|
||||
"once_cell",
|
||||
"qcow",
|
||||
"seccompiler",
|
||||
@@ -352,11 +349,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iced-x86"
|
||||
version = "1.18.0"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dd04b950d75b3498320253b17fb92745b2cc79ead8814aede2f7c1bab858bec"
|
||||
checksum = "158f5204401d08f91d19176112146d75e99b3cf745092e268fa7be33e09adcec"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -377,9 +375,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.5.11"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9ddf18110cedc09617f5a965bdb51adb0c0fb02c791e4ab22f7a21bb25269b0"
|
||||
checksum = "7ba34abb5175052fc1a2227a10d2275b7386c9990167de9786c0b88d8b062330"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -387,9 +385,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.2"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
|
||||
checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"io-lifetimes",
|
||||
@@ -399,9 +397,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.5"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
@@ -441,9 +439,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.139"
|
||||
version = "0.2.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
@@ -467,9 +465,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.1.4"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||
checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@@ -483,7 +481,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#fbef706e28c7ca7cce8dfec7ca073f3b359879d3"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#4b18a043e997da5b5f679e3defc279fec908753e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"vmm-sys-util",
|
||||
@@ -519,9 +517,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.0"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
|
||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||
|
||||
[[package]]
|
||||
name = "option_parser"
|
||||
@@ -600,9 +598,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.49"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
|
||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -653,9 +651,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "remain"
|
||||
version = "0.2.6"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5704e2cda92fd54202f05430725317ba0ea7d0c96b246ca0a92e45177127ba3b"
|
||||
checksum = "1a81bc6a3aaeb180f767bd2bda8c03bac5b93b3b69a783c66f35d16a4df276cf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -687,9 +685,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.12"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||
|
||||
[[package]]
|
||||
name = "seccompiler"
|
||||
@@ -702,24 +700,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.16"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
|
||||
checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.152"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
|
||||
checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.152"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
|
||||
checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -728,9 +726,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.91"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
|
||||
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -749,9 +747,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "2.2.0"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1966009f3c05f095697c537312f5415d1e3ed31ce0a56942bac4c771c5c335e"
|
||||
checksum = "e3452b4c0f6c1e357f73fdb87cd1efabaa12acf328c7a528e252893baeb3f4aa"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
@@ -789,10 +787,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.10.0"
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
@@ -802,9 +800,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.107"
|
||||
version = "1.0.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -875,9 +873,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.6"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
|
||||
@@ -12,12 +12,10 @@ cargo-fuzz = true
|
||||
block_util = { path = "../block_util" }
|
||||
devices = { path = "../devices" }
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.138"
|
||||
libc = "0.2.135"
|
||||
libfuzzer-sys = "0.4.5"
|
||||
linux-loader = { version = "0.8.1", features = ["elf", "bzimage", "pe"] }
|
||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||
net_util = { path = "../net_util" }
|
||||
once_cell = "1.17.0"
|
||||
once_cell = "1.16.0"
|
||||
qcow = { path = "../qcow" }
|
||||
seccompiler = "0.3.0"
|
||||
vhdx = { path = "../vhdx" }
|
||||
@@ -76,30 +74,12 @@ path = "fuzz_targets/iommu.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "linux_loader"
|
||||
path = "fuzz_targets/linux_loader.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "linux_loader_cmdline"
|
||||
path = "fuzz_targets/linux_loader_cmdline.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "mem"
|
||||
path = "fuzz_targets/mem.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "net"
|
||||
path = "fuzz_targets/net.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "pmem"
|
||||
path = "fuzz_targets/pmem.rs"
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright 2018 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// Copyright © 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use linux_loader::loader::KernelLoader;
|
||||
use std::ffi;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{Seek, SeekFrom, Write};
|
||||
use std::os::unix::io::{FromRawFd, RawFd};
|
||||
use vm_memory::{bitmap::AtomicBitmap, GuestAddress};
|
||||
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
|
||||
const MEM_SIZE: usize = 256 * 1024 * 1024;
|
||||
// From 'arch::x86_64::layout::HIGH_RAM_START'
|
||||
const HIGH_RAM_START: GuestAddress = GuestAddress(0x100000);
|
||||
|
||||
fuzz_target!(|bytes| {
|
||||
let shm = memfd_create(&ffi::CString::new("fuzz_load_kernel").unwrap(), 0).unwrap();
|
||||
let mut kernel_file: File = unsafe { File::from_raw_fd(shm) };
|
||||
kernel_file.write_all(&bytes).unwrap();
|
||||
kernel_file.seek(SeekFrom::Start(0)).unwrap();
|
||||
|
||||
let guest_memory = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
|
||||
linux_loader::loader::elf::Elf::load(
|
||||
&guest_memory,
|
||||
None,
|
||||
&mut kernel_file,
|
||||
Some(HIGH_RAM_START),
|
||||
)
|
||||
.ok();
|
||||
});
|
||||
|
||||
fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> {
|
||||
let res = unsafe { libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags) };
|
||||
|
||||
if res < 0 {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(res as RawFd)
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright 2018 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// Copyright © 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use vm_memory::{bitmap::AtomicBitmap, GuestAddress};
|
||||
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
|
||||
const MEM_SIZE: usize = 256 * 1024 * 1024;
|
||||
// From 'arch::x86_64::layout::CMDLINE_START'
|
||||
const CMDLINE_START: GuestAddress = GuestAddress(0x20000);
|
||||
|
||||
fuzz_target!(|bytes| {
|
||||
let payload_config = vmm::config::PayloadConfig {
|
||||
firmware: None,
|
||||
kernel: None,
|
||||
cmdline: Some(String::from_utf8_lossy(&bytes).to_string()),
|
||||
initramfs: None,
|
||||
};
|
||||
let kernel_cmdline = match vmm::vm::Vm::generate_cmdline(&payload_config) {
|
||||
Ok(cmdline) => cmdline,
|
||||
_ => return,
|
||||
};
|
||||
let guest_memory = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
|
||||
|
||||
linux_loader::loader::load_cmdline(&guest_memory, CMDLINE_START, &kernel_cmdline).ok();
|
||||
});
|
||||
@@ -1,285 +0,0 @@
|
||||
// Copyright © 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use seccompiler::SeccompAction;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||
use std::sync::Arc;
|
||||
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||
use virtio_queue::{Queue, QueueT};
|
||||
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||
use vmm::EpollContext;
|
||||
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
|
||||
macro_rules! align {
|
||||
($n:expr, $align:expr) => {{
|
||||
(($n + $align - 1) / $align) * $align
|
||||
}};
|
||||
}
|
||||
|
||||
const TAP_INPUT_SIZE: usize = 128;
|
||||
const QUEUE_DATA_SIZE: usize = 4;
|
||||
const MEM_SIZE: usize = 32 * 1024 * 1024;
|
||||
// Guest memory gap
|
||||
const GUEST_MEM_GAP: u64 = 1 * 1024 * 1024;
|
||||
// Guest physical address for the first virt queue
|
||||
const BASE_VIRT_QUEUE_ADDR: u64 = MEM_SIZE as u64 + GUEST_MEM_GAP;
|
||||
// Number of queues
|
||||
const QUEUE_NUM: usize = 2;
|
||||
// Max entries in the queue.
|
||||
const QUEUE_SIZE: u16 = 256;
|
||||
// Descriptor table alignment
|
||||
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
|
||||
// Used ring alignment
|
||||
const USED_RING_ALIGN_SIZE: u64 = 4;
|
||||
// Descriptor table size
|
||||
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
|
||||
// Available ring size
|
||||
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
|
||||
// Padding size before used ring
|
||||
const PADDING_SIZE: u64 = align!(AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE) - AVAIL_RING_SIZE;
|
||||
// Used ring size
|
||||
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
|
||||
// Virtio-queue size in bytes
|
||||
const QUEUE_BYTES_SIZE: usize = align!(
|
||||
DESC_TABLE_SIZE + AVAIL_RING_SIZE + PADDING_SIZE + USED_RING_SIZE,
|
||||
DESC_TABLE_ALIGN_SIZE
|
||||
) as usize;
|
||||
|
||||
fuzz_target!(|bytes| {
|
||||
if bytes.len() < TAP_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM
|
||||
|| bytes.len()
|
||||
> TAP_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + MEM_SIZE
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let (dummy_tap_frontend, dummy_tap_backend) = create_socketpair().unwrap();
|
||||
let if_name = "fuzzer_tap_name".as_bytes().to_vec();
|
||||
let tap = net_util::Tap::new_for_fuzzing(dummy_tap_frontend, if_name);
|
||||
|
||||
let mut net = virtio_devices::Net::new_with_tap(
|
||||
"fuzzer_net".to_owned(),
|
||||
vec![tap],
|
||||
None, // guest_mac
|
||||
false, // iommu
|
||||
QUEUE_NUM,
|
||||
QUEUE_SIZE,
|
||||
SeccompAction::Allow,
|
||||
None,
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
None,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let tap_input_bytes = &bytes[..TAP_INPUT_SIZE];
|
||||
let queue_data = &bytes[TAP_INPUT_SIZE..TAP_INPUT_SIZE + QUEUE_DATA_SIZE * QUEUE_NUM];
|
||||
let queue_bytes = &bytes[TAP_INPUT_SIZE + QUEUE_DATA_SIZE * QUEUE_NUM
|
||||
..TAP_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM];
|
||||
let mem_bytes = &bytes[TAP_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM..];
|
||||
|
||||
// Setup the virt queues with the input bytes
|
||||
let mut queues = setup_virt_queues(
|
||||
&[
|
||||
&queue_data[..QUEUE_DATA_SIZE].try_into().unwrap(),
|
||||
&queue_data[QUEUE_DATA_SIZE..QUEUE_DATA_SIZE * 2]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
],
|
||||
BASE_VIRT_QUEUE_ADDR,
|
||||
);
|
||||
|
||||
// Setup the guest memory with the input bytes
|
||||
let mem = GuestMemoryMmap::from_ranges(&[
|
||||
(GuestAddress(0), MEM_SIZE),
|
||||
(GuestAddress(BASE_VIRT_QUEUE_ADDR), queue_bytes.len()),
|
||||
])
|
||||
.unwrap();
|
||||
if mem
|
||||
.write_slice(queue_bytes, GuestAddress(BASE_VIRT_QUEUE_ADDR))
|
||||
.is_err()
|
||||
{
|
||||
return;
|
||||
}
|
||||
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
|
||||
return;
|
||||
}
|
||||
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||
|
||||
let input_queue = queues.remove(0);
|
||||
let input_evt = EventFd::new(0).unwrap();
|
||||
let input_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(input_evt.as_raw_fd())) };
|
||||
let output_queue = queues.remove(0);
|
||||
let output_evt = EventFd::new(0).unwrap();
|
||||
let output_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(output_evt.as_raw_fd())) };
|
||||
|
||||
// Start the thread of dummy tap backend to handle the rx and tx from the virtio-net
|
||||
let exit_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
|
||||
let tap_backend_thread = {
|
||||
let dummy_tap_backend = dummy_tap_backend.try_clone().unwrap();
|
||||
let tap_input_bytes: [u8; TAP_INPUT_SIZE] = tap_input_bytes[..].try_into().unwrap();
|
||||
let exit_evt = exit_evt.try_clone().unwrap();
|
||||
std::thread::Builder::new()
|
||||
.name("dummy_tap_backend".to_string())
|
||||
.spawn(move || {
|
||||
tap_backend_stub(dummy_tap_backend, &tap_input_bytes, exit_evt);
|
||||
})
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
// Kick the 'queue' events and endpoint event before activate the net device
|
||||
input_queue_evt.write(1).unwrap();
|
||||
output_queue_evt.write(1).unwrap();
|
||||
|
||||
net.activate(
|
||||
guest_memory,
|
||||
Arc::new(NoopVirtioInterrupt {}),
|
||||
vec![(0, input_queue, input_evt), (1, output_queue, output_evt)],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Wait for the events to finish and net device worker thread to return
|
||||
net.wait_for_epoll_threads();
|
||||
// Terminate the thread for the dummy tap backend
|
||||
exit_evt.write(1).ok();
|
||||
tap_backend_thread.join().unwrap();
|
||||
});
|
||||
|
||||
pub struct NoopVirtioInterrupt {}
|
||||
|
||||
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_virt_queues(bytes: &[&[u8; QUEUE_DATA_SIZE]], base_addr: u64) -> Vec<Queue> {
|
||||
let mut queues = Vec::new();
|
||||
for (i, b) in bytes.iter().enumerate() {
|
||||
let mut q = Queue::new(QUEUE_SIZE).unwrap();
|
||||
|
||||
let desc_table_addr = base_addr + (QUEUE_BYTES_SIZE * i) as u64;
|
||||
let avail_ring_addr = desc_table_addr + DESC_TABLE_SIZE;
|
||||
let used_ring_addr = avail_ring_addr + PADDING_SIZE + AVAIL_RING_SIZE;
|
||||
q.try_set_desc_table_address(GuestAddress(desc_table_addr))
|
||||
.unwrap();
|
||||
q.try_set_avail_ring_address(GuestAddress(avail_ring_addr))
|
||||
.unwrap();
|
||||
q.try_set_used_ring_address(GuestAddress(used_ring_addr))
|
||||
.unwrap();
|
||||
|
||||
q.set_next_avail(b[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
|
||||
q.set_next_used(b[1] as u16);
|
||||
q.set_event_idx(b[2] % 2 != 0);
|
||||
q.set_size(b[3] as u16 % QUEUE_SIZE);
|
||||
|
||||
q.set_ready(true);
|
||||
queues.push(q);
|
||||
}
|
||||
|
||||
queues
|
||||
}
|
||||
|
||||
fn create_socketpair() -> Result<(File, File), std::io::Error> {
|
||||
let mut fds = [-1, -1];
|
||||
unsafe {
|
||||
let ret = libc::socketpair(
|
||||
libc::AF_UNIX,
|
||||
libc::SOCK_STREAM | libc::SOCK_NONBLOCK,
|
||||
0,
|
||||
fds.as_mut_ptr(),
|
||||
);
|
||||
if ret == -1 {
|
||||
return Err(std::io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
|
||||
let socket1 = unsafe { File::from_raw_fd(fds[0]) };
|
||||
let socket2 = unsafe { File::from_raw_fd(fds[1]) };
|
||||
Ok((socket1, socket2))
|
||||
}
|
||||
|
||||
enum EpollEvent {
|
||||
Exit = 0,
|
||||
Rx = 1,
|
||||
Tx = 2,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl From<u64> for EpollEvent {
|
||||
fn from(v: u64) -> Self {
|
||||
use EpollEvent::*;
|
||||
match v {
|
||||
0 => Exit,
|
||||
1 => Rx,
|
||||
2 => Tx,
|
||||
_ => Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the rx and tx requests from the virtio-net device
|
||||
fn tap_backend_stub(
|
||||
mut dummy_tap: File,
|
||||
tap_input_bytes: &[u8; TAP_INPUT_SIZE],
|
||||
exit_evt: EventFd,
|
||||
) {
|
||||
let mut epoll = EpollContext::new().unwrap();
|
||||
epoll
|
||||
.add_event_custom(&exit_evt, EpollEvent::Exit as u64, epoll::Events::EPOLLIN)
|
||||
.unwrap();
|
||||
let dummy_tap_write = dummy_tap.try_clone().unwrap();
|
||||
epoll
|
||||
.add_event_custom(
|
||||
&dummy_tap_write,
|
||||
EpollEvent::Rx as u64,
|
||||
epoll::Events::EPOLLOUT,
|
||||
)
|
||||
.unwrap();
|
||||
epoll
|
||||
.add_event_custom(&dummy_tap, EpollEvent::Tx as u64, epoll::Events::EPOLLIN)
|
||||
.unwrap();
|
||||
|
||||
let epoll_fd = epoll.as_raw_fd();
|
||||
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); 3];
|
||||
loop {
|
||||
let num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) {
|
||||
Ok(num_events) => num_events,
|
||||
Err(e) => match e.raw_os_error() {
|
||||
Some(libc::EAGAIN) | Some(libc::EINTR) => continue,
|
||||
_ => panic!("Unexpected epoll::wait error!"),
|
||||
},
|
||||
};
|
||||
|
||||
for event in events.iter().take(num_events) {
|
||||
let dispatch_event: EpollEvent = event.data.into();
|
||||
match dispatch_event {
|
||||
EpollEvent::Exit => {
|
||||
return;
|
||||
}
|
||||
EpollEvent::Rx => {
|
||||
dummy_tap.write_all(tap_input_bytes).unwrap();
|
||||
break;
|
||||
}
|
||||
EpollEvent::Tx => {
|
||||
let mut buffer = Vec::new();
|
||||
dummy_tap.read_to_end(&mut buffer).ok();
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
panic!("Unexpected Epoll event");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ fuzz_target!(|bytes| {
|
||||
let mut serial = Serial::new_sink(
|
||||
"serial".into(),
|
||||
Arc::new(TestInterrupt::new(EventFd::new(EFD_NONBLOCK).unwrap())),
|
||||
None,
|
||||
);
|
||||
|
||||
let mut i = 0;
|
||||
|
||||
@@ -11,23 +11,23 @@ mshv = ["mshv-ioctls", "mshv-bindings"]
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.68"
|
||||
anyhow = "1.0.66"
|
||||
byteorder = "1.4.3"
|
||||
thiserror = "1.0.38"
|
||||
libc = "0.2.139"
|
||||
thiserror = "1.0.37"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
kvm-ioctls = { version = "0.12.0", optional = true }
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
mshv-bindings = { git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", branch = "main", optional = true}
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
serde_with = { version = "2.1.0", default-features = false, features = ["macros"] }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
||||
version = "1.18.0"
|
||||
version = "1.17.0"
|
||||
default-features = false
|
||||
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]
|
||||
|
||||
|
||||
@@ -272,7 +272,6 @@ impl LapicState {
|
||||
use std::io::Cursor;
|
||||
use std::mem;
|
||||
|
||||
// SAFETY: plain old data type
|
||||
let sliceu8 = unsafe {
|
||||
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
|
||||
// Cursors are only readable on arrays of u8, not i8(c_char).
|
||||
@@ -291,7 +290,6 @@ impl LapicState {
|
||||
use std::io::Cursor;
|
||||
use std::mem;
|
||||
|
||||
// SAFETY: plain old data type
|
||||
let sliceu8 = unsafe {
|
||||
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
|
||||
// Cursors are only readable on arrays of u8, not i8(c_char).
|
||||
|
||||
@@ -39,8 +39,8 @@ const GICR_ICFGR0: u32 = GICR_SGI_OFFSET + 0x0C00;
|
||||
const KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT: u32 = 32;
|
||||
const KVM_DEV_ARM_VGIC_V3_MPIDR_MASK: u64 = 0xffffffff << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT as u64;
|
||||
|
||||
const KVM_ARM64_SYSREG_MPIDR_EL1: u64 = KVM_REG_ARM64
|
||||
| KVM_REG_SIZE_U64
|
||||
const KVM_ARM64_SYSREG_MPIDR_EL1: u64 = KVM_REG_ARM64 as u64
|
||||
| KVM_REG_SIZE_U64 as u64
|
||||
| KVM_REG_ARM64_SYSREG as u64
|
||||
| (((3_u64) << KVM_REG_ARM64_SYSREG_OP0_SHIFT) & KVM_REG_ARM64_SYSREG_OP0_MASK as u64)
|
||||
| (((5_u64) << KVM_REG_ARM64_SYSREG_OP2_SHIFT) & KVM_REG_ARM64_SYSREG_OP2_MASK as u64);
|
||||
|
||||
@@ -24,8 +24,8 @@ pub use {kvm_ioctls::Cap, kvm_ioctls::Kvm};
|
||||
// This macro gets the offset of a structure (i.e `str`) member (i.e `field`) without having
|
||||
// an instance of that structure.
|
||||
#[macro_export]
|
||||
macro_rules! offset_of {
|
||||
($str:ty, $field:ident) => {{
|
||||
macro_rules! offset__of {
|
||||
($str:ty, $($field:ident)+) => ({
|
||||
let tmp: std::mem::MaybeUninit<$str> = std::mem::MaybeUninit::uninit();
|
||||
let base = tmp.as_ptr();
|
||||
|
||||
@@ -34,16 +34,14 @@ macro_rules! offset_of {
|
||||
// SAFETY: The pointer is valid and aligned, just not initialised. Using `addr_of` ensures
|
||||
// that we don't actually read from `base` (which would be UB) nor create an intermediate
|
||||
// reference.
|
||||
let member = unsafe { core::ptr::addr_of!((*base).$field) } as *const u8;
|
||||
let member = unsafe { core::ptr::addr_of!((*base).$($field)*) } as *const u8;
|
||||
|
||||
// Avoid warnings when nesting `unsafe` blocks.
|
||||
#[allow(unused_unsafe)]
|
||||
// SAFETY: The two pointers are within the same allocated object `tmp`. All requirements
|
||||
// from offset_from are upheld.
|
||||
unsafe {
|
||||
member.offset_from(base as *const u8) as usize
|
||||
}
|
||||
}};
|
||||
unsafe { member.offset_from(base as *const u8) as usize }
|
||||
});
|
||||
}
|
||||
|
||||
// Following are macros that help with getting the ID of a aarch64 core register.
|
||||
|
||||
@@ -23,7 +23,7 @@ use crate::vec_with_array_field;
|
||||
use crate::vm::{self, InterruptSourceConfig, VmOps};
|
||||
use crate::HypervisorType;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::{arm64_core_reg_id, offset_of};
|
||||
use crate::{arm64_core_reg_id, offset__of};
|
||||
use kvm_ioctls::{NoDatamatch, VcpuFd, VmFd};
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
@@ -1114,7 +1114,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn get_regs(&self) -> cpu::Result<StandardRegisters> {
|
||||
let mut state: StandardRegisters = kvm_regs::default();
|
||||
let mut off = offset_of!(user_pt_regs, regs);
|
||||
let mut off = offset__of!(user_pt_regs, regs);
|
||||
// There are 31 user_pt_regs:
|
||||
// https://elixir.free-electrons.com/linux/v4.14.174/source/arch/arm64/include/uapi/asm/ptrace.h#L72
|
||||
// These actually are the general-purpose registers of the Armv8-a
|
||||
@@ -1131,7 +1131,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
|
||||
// We are now entering the "Other register" section of the ARMv8-a architecture.
|
||||
// First one, stack pointer.
|
||||
let off = offset_of!(user_pt_regs, sp);
|
||||
let off = offset__of!(user_pt_regs, sp);
|
||||
state.regs.sp = self
|
||||
.fd
|
||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off))
|
||||
@@ -1140,7 +1140,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.unwrap();
|
||||
|
||||
// Second one, the program counter.
|
||||
let off = offset_of!(user_pt_regs, pc);
|
||||
let off = offset__of!(user_pt_regs, pc);
|
||||
state.regs.pc = self
|
||||
.fd
|
||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off))
|
||||
@@ -1149,7 +1149,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.unwrap();
|
||||
|
||||
// Next is the processor state.
|
||||
let off = offset_of!(user_pt_regs, pstate);
|
||||
let off = offset__of!(user_pt_regs, pstate);
|
||||
state.regs.pstate = self
|
||||
.fd
|
||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off))
|
||||
@@ -1158,7 +1158,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.unwrap();
|
||||
|
||||
// The stack pointer associated with EL1
|
||||
let off = offset_of!(kvm_regs, sp_el1);
|
||||
let off = offset__of!(kvm_regs, sp_el1);
|
||||
state.sp_el1 = self
|
||||
.fd
|
||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off))
|
||||
@@ -1168,7 +1168,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
|
||||
// Exception Link Register for EL1, when taking an exception to EL1, this register
|
||||
// holds the address to which to return afterwards.
|
||||
let off = offset_of!(kvm_regs, elr_el1);
|
||||
let off = offset__of!(kvm_regs, elr_el1);
|
||||
state.elr_el1 = self
|
||||
.fd
|
||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off))
|
||||
@@ -1177,7 +1177,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.unwrap();
|
||||
|
||||
// Saved Program Status Registers, there are 5 of them used in the kernel.
|
||||
let mut off = offset_of!(kvm_regs, spsr);
|
||||
let mut off = offset__of!(kvm_regs, spsr);
|
||||
for i in 0..KVM_NR_SPSR as usize {
|
||||
state.spsr[i] = self
|
||||
.fd
|
||||
@@ -1190,7 +1190,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
|
||||
// Now moving on to floting 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);
|
||||
let mut off = offset__of!(kvm_regs, fp_regs) + offset__of!(user_fpsimd_state, vregs);
|
||||
for i in 0..32 {
|
||||
state.fp_regs.vregs[i] = self
|
||||
.fd
|
||||
@@ -1200,7 +1200,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
}
|
||||
|
||||
// Floating-point Status Register
|
||||
let off = offset_of!(kvm_regs, fp_regs) + offset_of!(user_fpsimd_state, fpsr);
|
||||
let off = offset__of!(kvm_regs, fp_regs) + offset__of!(user_fpsimd_state, fpsr);
|
||||
state.fp_regs.fpsr = self
|
||||
.fd
|
||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U32, off))
|
||||
@@ -1209,7 +1209,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.unwrap();
|
||||
|
||||
// Floating-point Control Register
|
||||
let off = offset_of!(kvm_regs, fp_regs) + offset_of!(user_fpsimd_state, fpcr);
|
||||
let off = offset__of!(kvm_regs, fp_regs) + offset__of!(user_fpsimd_state, fpcr);
|
||||
state.fp_regs.fpcr = self
|
||||
.fd
|
||||
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U32, off))
|
||||
@@ -1238,7 +1238,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
fn set_regs(&self, state: &StandardRegisters) -> cpu::Result<()> {
|
||||
// The function follows the exact identical order from `state`. Look there
|
||||
// for some additional info on registers.
|
||||
let mut off = offset_of!(user_pt_regs, regs);
|
||||
let mut off = offset__of!(user_pt_regs, regs);
|
||||
for i in 0..31 {
|
||||
self.fd
|
||||
.set_one_reg(
|
||||
@@ -1249,7 +1249,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
off += std::mem::size_of::<u64>();
|
||||
}
|
||||
|
||||
let off = offset_of!(user_pt_regs, sp);
|
||||
let off = offset__of!(user_pt_regs, sp);
|
||||
self.fd
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, off),
|
||||
@@ -1257,7 +1257,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
|
||||
let off = offset_of!(user_pt_regs, pc);
|
||||
let off = offset__of!(user_pt_regs, pc);
|
||||
self.fd
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, off),
|
||||
@@ -1265,7 +1265,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
|
||||
let off = offset_of!(user_pt_regs, pstate);
|
||||
let off = offset__of!(user_pt_regs, pstate);
|
||||
self.fd
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, off),
|
||||
@@ -1273,7 +1273,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
|
||||
let off = offset_of!(kvm_regs, sp_el1);
|
||||
let off = offset__of!(kvm_regs, sp_el1);
|
||||
self.fd
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, off),
|
||||
@@ -1281,7 +1281,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
|
||||
let off = offset_of!(kvm_regs, elr_el1);
|
||||
let off = offset__of!(kvm_regs, elr_el1);
|
||||
self.fd
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, off),
|
||||
@@ -1289,7 +1289,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
|
||||
let mut off = offset_of!(kvm_regs, spsr);
|
||||
let mut off = offset__of!(kvm_regs, spsr);
|
||||
for i in 0..KVM_NR_SPSR as usize {
|
||||
self.fd
|
||||
.set_one_reg(
|
||||
@@ -1300,7 +1300,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
off += std::mem::size_of::<u64>();
|
||||
}
|
||||
|
||||
let mut off = offset_of!(kvm_regs, fp_regs) + offset_of!(user_fpsimd_state, vregs);
|
||||
let mut off = offset__of!(kvm_regs, fp_regs) + offset__of!(user_fpsimd_state, vregs);
|
||||
for i in 0..32 {
|
||||
self.fd
|
||||
.set_one_reg(
|
||||
@@ -1311,7 +1311,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
off += mem::size_of::<u128>();
|
||||
}
|
||||
|
||||
let off = offset_of!(kvm_regs, fp_regs) + offset_of!(user_fpsimd_state, fpsr);
|
||||
let off = offset__of!(kvm_regs, fp_regs) + offset__of!(user_fpsimd_state, fpsr);
|
||||
self.fd
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U32, off),
|
||||
@@ -1319,7 +1319,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
|
||||
let off = offset_of!(kvm_regs, fp_regs) + offset_of!(user_fpsimd_state, fpcr);
|
||||
let off = offset__of!(kvm_regs, fp_regs) + offset__of!(user_fpsimd_state, fpcr);
|
||||
self.fd
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U32, off),
|
||||
@@ -1700,8 +1700,8 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
// it to the corresponding KVM ID, and call `KVM_GET_ONE_REG` API to
|
||||
// get the value of the system parameter.
|
||||
//
|
||||
let id: u64 = KVM_REG_ARM64
|
||||
| KVM_REG_SIZE_U64
|
||||
let id: u64 = KVM_REG_ARM64 as u64
|
||||
| KVM_REG_SIZE_U64 as u64
|
||||
| KVM_REG_ARM64_SYSREG as u64
|
||||
| ((((sys_reg) >> 5)
|
||||
& (KVM_REG_ARM64_SYSREG_OP0_MASK
|
||||
@@ -1733,10 +1733,10 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
const PSTATE_FAULT_BITS_64: u64 =
|
||||
PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT;
|
||||
|
||||
let kreg_off = offset_of!(kvm_regs, regs);
|
||||
let kreg_off = offset__of!(kvm_regs, regs);
|
||||
|
||||
// Get the register index of the PSTATE (Processor State) register.
|
||||
let pstate = offset_of!(user_pt_regs, pstate) + kreg_off;
|
||||
let pstate = offset__of!(user_pt_regs, pstate) + kreg_off;
|
||||
self.fd
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, pstate),
|
||||
@@ -1747,7 +1747,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
// Other vCPUs are powered off initially awaiting PSCI wakeup.
|
||||
if cpu_id == 0 {
|
||||
// Setting the PC (Processor Counter) to the current program address (kernel address).
|
||||
let pc = offset_of!(user_pt_regs, pc) + kreg_off;
|
||||
let pc = offset__of!(user_pt_regs, pc) + kreg_off;
|
||||
self.fd
|
||||
.set_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, pc), boot_ip.into())
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.into()))?;
|
||||
@@ -1756,7 +1756,7 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
// "The device tree blob (dtb) must be placed on an 8-byte boundary and must
|
||||
// not exceed 2 megabytes in size." -> https://www.kernel.org/doc/Documentation/arm64/booting.txt.
|
||||
// We are choosing to place it the end of DRAM. See `get_fdt_addr`.
|
||||
let regs0 = offset_of!(user_pt_regs, regs) + kreg_off;
|
||||
let regs0 = offset__of!(user_pt_regs, regs) + kreg_off;
|
||||
self.fd
|
||||
.set_one_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, regs0),
|
||||
@@ -2066,7 +2066,6 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
#[cfg(feature = "tdx")]
|
||||
fn get_tdx_exit_details(&mut self) -> cpu::Result<TdxExitDetails> {
|
||||
let kvm_run = self.fd.get_kvm_run();
|
||||
// SAFETY: accessing a union field in a valid structure
|
||||
let tdx_vmcall = unsafe { &mut kvm_run.__bindgen_anon_1.tdx.u.vmcall };
|
||||
|
||||
tdx_vmcall.status_code = TDG_VP_VMCALL_INVALID_OPERAND;
|
||||
@@ -2090,7 +2089,6 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
#[cfg(feature = "tdx")]
|
||||
fn set_tdx_status(&mut self, status: TdxExitStatus) {
|
||||
let kvm_run = self.fd.get_kvm_run();
|
||||
// SAFETY: accessing a union field in a valid structure
|
||||
let tdx_vmcall = unsafe { &mut kvm_run.__bindgen_anon_1.tdx.u.vmcall };
|
||||
|
||||
tdx_vmcall.status_code = match status {
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
//! - arm64
|
||||
//!
|
||||
|
||||
#![allow(clippy::significant_drop_in_scrutinee)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate anyhow;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -59,11 +61,9 @@ pub use vm::{
|
||||
Vm, VmOps,
|
||||
};
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum HypervisorType {
|
||||
#[cfg(feature = "kvm")]
|
||||
Kvm,
|
||||
#[cfg(feature = "mshv")]
|
||||
Mshv,
|
||||
}
|
||||
|
||||
|
||||
@@ -446,10 +446,10 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
/* Advance RIP and update RAX */
|
||||
let arr_reg_name_value = [
|
||||
(
|
||||
hv_x64_register_name_HV_X64_REGISTER_RIP,
|
||||
hv_register_name::HV_X64_REGISTER_RIP,
|
||||
info.header.rip + insn_len,
|
||||
),
|
||||
(hv_x64_register_name_HV_X64_REGISTER_RAX, ret_rax),
|
||||
(hv_register_name::HV_X64_REGISTER_RAX, ret_rax),
|
||||
];
|
||||
set_registers_64!(self.fd, arr_reg_name_value)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
|
||||
@@ -458,13 +458,12 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// SAFETY: access_info is valid, otherwise we won't be here
|
||||
assert!(
|
||||
// SAFETY: access_info is valid, otherwise we won't be here
|
||||
(unsafe { access_info.__bindgen_anon_1.string_op() } != 1),
|
||||
"String IN/OUT not supported"
|
||||
);
|
||||
assert!(
|
||||
// SAFETY: access_info is valid, otherwise we won't be here
|
||||
(unsafe { access_info.__bindgen_anon_1.rep_prefix() } != 1),
|
||||
"Rep IN/OUT not supported"
|
||||
);
|
||||
@@ -495,10 +494,10 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
/* Advance RIP and update RAX */
|
||||
let arr_reg_name_value = [
|
||||
(
|
||||
hv_x64_register_name_HV_X64_REGISTER_RIP,
|
||||
hv_register_name::HV_X64_REGISTER_RIP,
|
||||
info.header.rip + insn_len,
|
||||
),
|
||||
(hv_x64_register_name_HV_X64_REGISTER_RAX, ret_rax),
|
||||
(hv_register_name::HV_X64_REGISTER_RAX, ret_rax),
|
||||
];
|
||||
set_registers_64!(self.fd, arr_reg_name_value)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
|
||||
|
||||
@@ -1187,7 +1187,6 @@ pub struct ifreq {
|
||||
|
||||
impl Default for ifreq {
|
||||
fn default() -> Self {
|
||||
// SAFETY: all zeros is a valid pattern for this data type
|
||||
unsafe { std::mem::zeroed() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ edition = "2021"
|
||||
[dependencies]
|
||||
epoll = "4.3.1"
|
||||
getrandom = "0.2.8"
|
||||
libc = "0.2.139"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
net_gen = { path = "../net_gen" }
|
||||
rate_limiter = { path = "../rate_limiter" }
|
||||
serde = "1.0.151"
|
||||
thiserror = "1.0.38"
|
||||
serde = "1.0.150"
|
||||
thiserror = "1.0.37"
|
||||
versionize = "0.1.9"
|
||||
versionize_derive = "0.1.4"
|
||||
virtio-bindings = "0.1.0"
|
||||
|
||||
@@ -66,34 +66,32 @@ fn create_sockaddr(ip_addr: net::Ipv4Addr) -> net_gen::sockaddr {
|
||||
let addr_in = net_gen::sockaddr_in {
|
||||
sin_family: net_gen::AF_INET as u16,
|
||||
sin_port: 0,
|
||||
// SAFETY: ip_addr can be safely transmute to in_addr
|
||||
sin_addr: unsafe { mem::transmute(ip_addr.octets()) },
|
||||
__pad: [0; 8usize],
|
||||
};
|
||||
|
||||
// SAFETY: addr_in can be safely transmute to sockaddr
|
||||
unsafe { mem::transmute(addr_in) }
|
||||
}
|
||||
|
||||
fn create_inet_socket() -> Result<net::UdpSocket> {
|
||||
// SAFETY: we check the return value.
|
||||
// This is safe since we check the return value.
|
||||
let sock = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
|
||||
if sock < 0 {
|
||||
return Err(Error::CreateSocket(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
// SAFETY: nothing else will use or hold onto the raw sock fd.
|
||||
// This is safe; nothing else will use or hold onto the raw sock fd.
|
||||
Ok(unsafe { net::UdpSocket::from_raw_fd(sock) })
|
||||
}
|
||||
|
||||
fn create_unix_socket() -> Result<net::UdpSocket> {
|
||||
// SAFETY: we check the return value.
|
||||
// This is safe since we check the return value.
|
||||
let sock = unsafe { libc::socket(libc::AF_UNIX, libc::SOCK_DGRAM, 0) };
|
||||
if sock < 0 {
|
||||
return Err(Error::CreateSocket(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
// SAFETY: nothing else will use or hold onto the raw sock fd.
|
||||
// This is safe; nothing else will use or hold onto the raw sock fd.
|
||||
Ok(unsafe { net::UdpSocket::from_raw_fd(sock) })
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,6 @@ impl TxVirtio {
|
||||
}
|
||||
|
||||
let len = if !iovecs.is_empty() {
|
||||
// SAFETY: FFI call with correct arguments
|
||||
let result = unsafe {
|
||||
libc::writev(
|
||||
tap.as_raw_fd() as libc::c_int,
|
||||
@@ -222,7 +221,6 @@ impl RxVirtio {
|
||||
}
|
||||
|
||||
let len = if !iovecs.is_empty() {
|
||||
// SAFETY: FFI call with correct arguments
|
||||
let result = unsafe {
|
||||
libc::readv(
|
||||
tap.as_raw_fd() as libc::c_int,
|
||||
|
||||
@@ -28,8 +28,8 @@ pub enum Error {
|
||||
GetFeatures(IoError),
|
||||
#[error("Missing multiqueue support in the kernel")]
|
||||
MultiQueueKernelSupport,
|
||||
#[error("ioctl ({0}) failed: {1}")]
|
||||
IoctlError(c_ulong, IoError),
|
||||
#[error("ioctl failed: {0}")]
|
||||
IoctlError(IoError),
|
||||
#[error("Failed to create a socket: {0}")]
|
||||
NetUtil(NetUtilError),
|
||||
#[error("Invalid interface name")]
|
||||
@@ -87,37 +87,9 @@ fn build_terminated_if_name(if_name: &str) -> Result<Vec<u8>> {
|
||||
}
|
||||
|
||||
impl Tap {
|
||||
unsafe fn ioctl_with_mut_ref<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: &mut T) -> Result<()> {
|
||||
let ret = ioctl_with_mut_ref(fd, req, arg);
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(req, IoError::last_os_error()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn ioctl_with_ref<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: &T) -> Result<()> {
|
||||
let ret = ioctl_with_ref(fd, req, arg);
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(req, IoError::last_os_error()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn ioctl_with_val<F: AsRawFd>(fd: &F, req: c_ulong, arg: c_ulong) -> Result<()> {
|
||||
let ret = ioctl_with_val(fd, req, arg);
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(req, IoError::last_os_error()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn open_named(if_name: &str, num_queue_pairs: usize, flags: Option<i32>) -> Result<Tap> {
|
||||
let terminated_if_name = build_terminated_if_name(if_name)?;
|
||||
|
||||
// SAFETY: FFI call
|
||||
let fd = unsafe {
|
||||
// Open calls are safe because we give a constant null-terminated
|
||||
// string and verify the result.
|
||||
@@ -130,14 +102,13 @@ impl Tap {
|
||||
return Err(Error::OpenTun(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
// SAFETY: We just checked that the fd is valid.
|
||||
// We just checked that the fd is valid.
|
||||
let tuntap = unsafe { File::from_raw_fd(fd) };
|
||||
|
||||
// Let's validate some features before going any further.
|
||||
// ioctl is safe since we call it with a valid tap fd and check the return
|
||||
// value.
|
||||
let mut features = 0;
|
||||
// SAFETY: IOCTL with correct arguments
|
||||
let ret = unsafe { ioctl_with_mut_ref(&tuntap, net_gen::TUNGETFEATURES(), &mut features) };
|
||||
if ret < 0 {
|
||||
return Err(Error::GetFeatures(IoError::last_os_error()));
|
||||
@@ -152,7 +123,6 @@ impl Tap {
|
||||
// don't call as_mut on the same union field more than once, this block
|
||||
// is safe.
|
||||
let mut ifreq: net_gen::ifreq = Default::default();
|
||||
// SAFETY: see the comment above.
|
||||
unsafe {
|
||||
let ifrn_name = ifreq.ifr_ifrn.ifrn_name.as_mut();
|
||||
let name_slice = &mut ifrn_name[..terminated_if_name.len()];
|
||||
@@ -164,16 +134,16 @@ impl Tap {
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: ioctl is safe since we call it with a valid tap fd and check the return
|
||||
// ioctl is safe since we call it with a valid tap fd and check the return
|
||||
// value.
|
||||
let ret = unsafe { ioctl_with_mut_ref(&tuntap, net_gen::TUNSETIFF(), &mut ifreq) };
|
||||
if ret < 0 {
|
||||
return Err(Error::ConfigureTap(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
// SAFETY: only the name is accessed, and it's cloned out.
|
||||
let mut if_name = unsafe { ifreq.ifr_ifrn.ifrn_name }.to_vec();
|
||||
if_name.truncate(terminated_if_name.len() - 1);
|
||||
// Safe since only the name is accessed, and it's cloned out.
|
||||
Ok(Tap {
|
||||
tap_file: tuntap,
|
||||
if_name,
|
||||
@@ -188,7 +158,6 @@ impl Tap {
|
||||
pub fn from_tap_fd(fd: RawFd, num_queue_pairs: usize) -> Result<Tap> {
|
||||
// Ensure that the file is opened non-blocking, this is particularly
|
||||
// needed when opened via the shell for macvtap.
|
||||
// SAFETY: FFI call
|
||||
let ret = unsafe {
|
||||
let mut flags = libc::fcntl(fd, libc::F_GETFL);
|
||||
flags |= libc::O_NONBLOCK;
|
||||
@@ -198,20 +167,19 @@ impl Tap {
|
||||
return Err(Error::ConfigureTap(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
// SAFETY: fd is a tap fd
|
||||
let tap_file = unsafe { File::from_raw_fd(fd) };
|
||||
let mut ifreq: net_gen::ifreq = Default::default();
|
||||
|
||||
// Get current config including name
|
||||
// SAFETY: IOCTL with correct arugments
|
||||
unsafe { Self::ioctl_with_mut_ref(&tap_file, net_gen::TUNGETIFF(), &mut ifreq)? };
|
||||
|
||||
// SAFETY: We only access one field of the ifru union
|
||||
let ret = unsafe { ioctl_with_mut_ref(&tap_file, net_gen::TUNGETIFF(), &mut ifreq) };
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(IoError::last_os_error()));
|
||||
}
|
||||
// We only access one field of the ifru union, hence this is safe.
|
||||
let if_name = unsafe { ifreq.ifr_ifrn.ifrn_name }.to_vec();
|
||||
|
||||
// Try and update flags. Depending on how the tap was created (macvtap
|
||||
// or via open_named()) this might return -EEXIST so we just ignore that.
|
||||
// SAFETY: access union fields
|
||||
unsafe {
|
||||
ifreq.ifr_ifru.ifru_flags =
|
||||
(net_gen::IFF_TAP | net_gen::IFF_NO_PI | net_gen::IFF_VNET_HDR) as c_short;
|
||||
@@ -219,7 +187,6 @@ impl Tap {
|
||||
ifreq.ifr_ifru.ifru_flags |= net_gen::IFF_MULTI_QUEUE as c_short;
|
||||
}
|
||||
}
|
||||
// SAFETY: IOCTL with correct arguments
|
||||
let ret = unsafe { ioctl_with_mut_ref(&tap_file, net_gen::TUNSETIFF(), &mut ifreq) };
|
||||
if ret < 0 && IoError::last_os_error().raw_os_error().unwrap() != libc::EEXIST {
|
||||
return Err(Error::ConfigureTap(IoError::last_os_error()));
|
||||
@@ -241,8 +208,14 @@ impl Tap {
|
||||
|
||||
ifreq.ifr_ifru.ifru_addr = addr;
|
||||
|
||||
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFADDR as c_ulong, &ifreq) }
|
||||
// ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
let ret =
|
||||
unsafe { ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFADDR as c_ulong, &ifreq) };
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set mac addr for tap interface.
|
||||
@@ -260,10 +233,13 @@ impl Tap {
|
||||
|
||||
let mut ifreq = self.get_ifreq();
|
||||
|
||||
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCGIFHWADDR as c_ulong, &ifreq)? };
|
||||
|
||||
// SAFETY: We only access one field of the ifru union
|
||||
// ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
let ret =
|
||||
unsafe { ioctl_with_ref(&sock, net_gen::sockios::SIOCGIFHWADDR as c_ulong, &ifreq) };
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(IoError::last_os_error()));
|
||||
}
|
||||
// We only access one field of the ifru union, hence this is safe.
|
||||
unsafe {
|
||||
let ifru_hwaddr = &mut ifreq.ifr_ifru.ifru_hwaddr;
|
||||
for (i, v) in addr.get_bytes().iter().enumerate() {
|
||||
@@ -271,8 +247,14 @@ impl Tap {
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFHWADDR as c_ulong, &ifreq) }
|
||||
// ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
let ret =
|
||||
unsafe { ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFHWADDR as c_ulong, &ifreq) };
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get mac addr for tap interface.
|
||||
@@ -281,10 +263,14 @@ impl Tap {
|
||||
|
||||
let ifreq = self.get_ifreq();
|
||||
|
||||
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCGIFHWADDR as c_ulong, &ifreq)? };
|
||||
// ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
let ret =
|
||||
unsafe { ioctl_with_ref(&sock, net_gen::sockios::SIOCGIFHWADDR as c_ulong, &ifreq) };
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
// SAFETY: We only access one field of the ifru union
|
||||
// We only access one field of the ifru union, hence this is safe.
|
||||
let addr = unsafe {
|
||||
MacAddr::from_bytes(&ifreq.ifr_ifru.ifru_hwaddr.sa_data[0..MAC_ADDR_LEN])
|
||||
.map_err(Error::MacParsing)?
|
||||
@@ -301,45 +287,57 @@ impl Tap {
|
||||
|
||||
ifreq.ifr_ifru.ifru_addr = addr;
|
||||
|
||||
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFNETMASK as c_ulong, &ifreq) }
|
||||
// ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
let ret =
|
||||
unsafe { ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFNETMASK as c_ulong, &ifreq) };
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(fuzzing))]
|
||||
pub fn mtu(&self) -> Result<i32> {
|
||||
let sock = create_unix_socket().map_err(Error::NetUtil)?;
|
||||
|
||||
let ifreq = self.get_ifreq();
|
||||
|
||||
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCGIFMTU as c_ulong, &ifreq)? };
|
||||
// ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
let ret = unsafe { ioctl_with_ref(&sock, net_gen::sockios::SIOCGIFMTU as c_ulong, &ifreq) };
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
// SAFETY: access a union field
|
||||
let mtu = unsafe { ifreq.ifr_ifru.ifru_mtu };
|
||||
|
||||
Ok(mtu)
|
||||
}
|
||||
|
||||
#[cfg(fuzzing)]
|
||||
pub fn mtu(&self) -> Result<i32> {
|
||||
// Consistent with the `virtio_devices::net::MIN_MTU`
|
||||
Ok(1280)
|
||||
}
|
||||
|
||||
pub fn set_mtu(&self, mtu: i32) -> Result<()> {
|
||||
let sock = create_unix_socket().map_err(Error::NetUtil)?;
|
||||
|
||||
let mut ifreq = self.get_ifreq();
|
||||
ifreq.ifr_ifru.ifru_mtu = mtu;
|
||||
|
||||
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFMTU as c_ulong, &ifreq) }
|
||||
// ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
let ret = unsafe { ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFMTU as c_ulong, &ifreq) };
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the offload flags for the tap interface.
|
||||
pub fn set_offload(&self, flags: c_uint) -> Result<()> {
|
||||
// SAFETY: ioctl is safe. Called with a valid tap fd, and we check the return.
|
||||
unsafe { Self::ioctl_with_val(&self.tap_file, net_gen::TUNSETOFFLOAD(), flags as c_ulong) }
|
||||
// ioctl is safe. Called with a valid tap fd, and we check the return.
|
||||
let ret =
|
||||
unsafe { ioctl_with_val(&self.tap_file, net_gen::TUNSETOFFLOAD(), flags as c_ulong) };
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Enable the tap interface.
|
||||
@@ -348,11 +346,13 @@ impl Tap {
|
||||
|
||||
let mut ifreq = self.get_ifreq();
|
||||
|
||||
// SAFETY: IOCTL with correct arguments
|
||||
unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCGIFFLAGS as c_ulong, &ifreq)? };
|
||||
let ret =
|
||||
unsafe { ioctl_with_ref(&sock, net_gen::sockios::SIOCGIFFLAGS as c_ulong, &ifreq) };
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
// If TAP device is already up don't try and enable it
|
||||
// SAFETY: access a union field
|
||||
let ifru_flags = unsafe { ifreq.ifr_ifru.ifru_flags };
|
||||
if ifru_flags & net_gen::net_device_flags_IFF_UP as i16
|
||||
== net_gen::net_device_flags_IFF_UP as i16
|
||||
@@ -362,14 +362,25 @@ impl Tap {
|
||||
|
||||
ifreq.ifr_ifru.ifru_flags = net_gen::net_device_flags_IFF_UP as i16;
|
||||
|
||||
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFFLAGS as c_ulong, &ifreq) }
|
||||
// ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
let ret =
|
||||
unsafe { ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFFLAGS as c_ulong, &ifreq) };
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the size of the vnet hdr.
|
||||
pub fn set_vnet_hdr_size(&self, size: c_int) -> Result<()> {
|
||||
// SAFETY: ioctl is safe. Called with a valid tap fd, and we check the return.
|
||||
unsafe { Self::ioctl_with_ref(&self.tap_file, net_gen::TUNSETVNETHDRSZ(), &size) }
|
||||
// ioctl is safe. Called with a valid tap fd, and we check the return.
|
||||
let ret = unsafe { ioctl_with_ref(&self.tap_file, net_gen::TUNSETVNETHDRSZ(), &size) };
|
||||
if ret < 0 {
|
||||
return Err(Error::IoctlError(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_ifreq(&self) -> net_gen::ifreq {
|
||||
@@ -377,7 +388,6 @@ impl Tap {
|
||||
|
||||
// This sets the name of the interface, which is the only entry
|
||||
// in a single-field union.
|
||||
// SAFETY: access union fields and we're sure the copy is okay.
|
||||
unsafe {
|
||||
let ifrn_name = ifreq.ifr_ifrn.ifrn_name.as_mut();
|
||||
let name_slice = &mut ifrn_name[..self.if_name.len()];
|
||||
@@ -390,11 +400,6 @@ impl Tap {
|
||||
pub fn get_if_name(&self) -> Vec<u8> {
|
||||
self.if_name.clone()
|
||||
}
|
||||
|
||||
#[cfg(fuzzing)]
|
||||
pub fn new_for_fuzzing(tap_file: File, if_name: Vec<u8>) -> Self {
|
||||
Tap { tap_file, if_name }
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Tap {
|
||||
|
||||
@@ -10,20 +10,20 @@ kvm = ["vfio-ioctls/kvm"]
|
||||
mshv = ["vfio-ioctls/mshv"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.68"
|
||||
anyhow = "1.0.66"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main", features = ["fam-wrappers"] }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vfio_user = { path = "../vfio_user" }
|
||||
vmm-sys-util = "0.11.0"
|
||||
libc = "0.2.139"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
serde = { version = "1.0.151", features = ["derive"] }
|
||||
thiserror = "1.0.38"
|
||||
serde = { version = "1.0.150", features = ["derive"] }
|
||||
thiserror = "1.0.37"
|
||||
versionize = "0.1.9"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = "0.10.0"
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
@@ -62,7 +62,6 @@ impl PciRoot {
|
||||
0,
|
||||
0,
|
||||
None,
|
||||
None,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,6 @@ const CAPABILITY_MAX_OFFSET: usize = 192;
|
||||
|
||||
const INTERRUPT_LINE_PIN_REG: usize = 15;
|
||||
|
||||
pub const PCI_CONFIGURATION_ID: &str = "pci_configuration";
|
||||
|
||||
/// Represents the types of PCI headers allowed in the configuration registers.
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum PciHeaderType {
|
||||
@@ -396,7 +394,7 @@ fn decode_64_bits_bar_size(bar_size_hi: u32, bar_size_lo: u32) -> Option<u64> {
|
||||
None
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Versionize)]
|
||||
#[derive(Default, Clone, Copy, Versionize)]
|
||||
struct PciBar {
|
||||
addr: u32,
|
||||
size: u32,
|
||||
@@ -405,7 +403,7 @@ struct PciBar {
|
||||
}
|
||||
|
||||
#[derive(Versionize)]
|
||||
pub struct PciConfigurationState {
|
||||
struct PciConfigurationState {
|
||||
registers: Vec<u32>,
|
||||
writable_bits: Vec<u32>,
|
||||
bars: Vec<PciBar>,
|
||||
@@ -555,78 +553,46 @@ impl PciConfiguration {
|
||||
subsystem_vendor_id: u16,
|
||||
subsystem_id: u16,
|
||||
msix_config: Option<Arc<Mutex<MsixConfig>>>,
|
||||
state: Option<PciConfigurationState>,
|
||||
) -> Self {
|
||||
let (
|
||||
registers,
|
||||
writable_bits,
|
||||
bars,
|
||||
rom_bar_addr,
|
||||
rom_bar_size,
|
||||
rom_bar_used,
|
||||
last_capability,
|
||||
msix_cap_reg_idx,
|
||||
) = if let Some(state) = state {
|
||||
(
|
||||
state.registers.try_into().unwrap(),
|
||||
state.writable_bits.try_into().unwrap(),
|
||||
state.bars.try_into().unwrap(),
|
||||
state.rom_bar_addr,
|
||||
state.rom_bar_size,
|
||||
state.rom_bar_used,
|
||||
state.last_capability,
|
||||
state.msix_cap_reg_idx,
|
||||
)
|
||||
let mut registers = [0u32; NUM_CONFIGURATION_REGISTERS];
|
||||
let mut writable_bits = [0u32; NUM_CONFIGURATION_REGISTERS];
|
||||
registers[0] = u32::from(device_id) << 16 | u32::from(vendor_id);
|
||||
// TODO(dverkamp): Status should be write-1-to-clear
|
||||
writable_bits[1] = 0x0000_ffff; // Status (r/o), command (r/w)
|
||||
let pi = if let Some(pi) = programming_interface {
|
||||
pi.get_register_value()
|
||||
} else {
|
||||
let mut registers = [0u32; NUM_CONFIGURATION_REGISTERS];
|
||||
let mut writable_bits = [0u32; NUM_CONFIGURATION_REGISTERS];
|
||||
registers[0] = u32::from(device_id) << 16 | u32::from(vendor_id);
|
||||
// TODO(dverkamp): Status should be write-1-to-clear
|
||||
writable_bits[1] = 0x0000_ffff; // Status (r/o), command (r/w)
|
||||
let pi = if let Some(pi) = programming_interface {
|
||||
pi.get_register_value()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
registers[2] = u32::from(class_code.get_register_value()) << 24
|
||||
| u32::from(subclass.get_register_value()) << 16
|
||||
| u32::from(pi) << 8
|
||||
| u32::from(revision_id);
|
||||
writable_bits[3] = 0x0000_00ff; // Cacheline size (r/w)
|
||||
match header_type {
|
||||
PciHeaderType::Device => {
|
||||
registers[3] = 0x0000_0000; // Header type 0 (device)
|
||||
writable_bits[15] = 0x0000_00ff; // Interrupt line (r/w)
|
||||
}
|
||||
PciHeaderType::Bridge => {
|
||||
registers[3] = 0x0001_0000; // Header type 1 (bridge)
|
||||
writable_bits[9] = 0xfff0_fff0; // Memory base and limit
|
||||
writable_bits[15] = 0xffff_00ff; // Bridge control (r/w), interrupt line (r/w)
|
||||
}
|
||||
};
|
||||
registers[11] = u32::from(subsystem_id) << 16 | u32::from(subsystem_vendor_id);
|
||||
|
||||
(
|
||||
registers,
|
||||
writable_bits,
|
||||
[PciBar::default(); NUM_BAR_REGS],
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
0
|
||||
};
|
||||
registers[2] = u32::from(class_code.get_register_value()) << 24
|
||||
| u32::from(subclass.get_register_value()) << 16
|
||||
| u32::from(pi) << 8
|
||||
| u32::from(revision_id);
|
||||
writable_bits[3] = 0x0000_00ff; // Cacheline size (r/w)
|
||||
match header_type {
|
||||
PciHeaderType::Device => {
|
||||
registers[3] = 0x0000_0000; // Header type 0 (device)
|
||||
writable_bits[15] = 0x0000_00ff; // Interrupt line (r/w)
|
||||
}
|
||||
PciHeaderType::Bridge => {
|
||||
registers[3] = 0x0001_0000; // Header type 1 (bridge)
|
||||
writable_bits[9] = 0xfff0_fff0; // Memory base and limit
|
||||
writable_bits[15] = 0xffff_00ff; // Bridge control (r/w), interrupt line (r/w)
|
||||
}
|
||||
};
|
||||
registers[11] = u32::from(subsystem_id) << 16 | u32::from(subsystem_vendor_id);
|
||||
|
||||
let bars = [PciBar::default(); NUM_BAR_REGS];
|
||||
|
||||
PciConfiguration {
|
||||
registers,
|
||||
writable_bits,
|
||||
bars,
|
||||
rom_bar_addr,
|
||||
rom_bar_size,
|
||||
rom_bar_used,
|
||||
last_capability,
|
||||
msix_cap_reg_idx,
|
||||
rom_bar_addr: 0,
|
||||
rom_bar_size: 0,
|
||||
rom_bar_used: false,
|
||||
last_capability: None,
|
||||
msix_cap_reg_idx: None,
|
||||
msix_config,
|
||||
}
|
||||
}
|
||||
@@ -644,6 +610,18 @@ impl PciConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &PciConfigurationState) {
|
||||
self.registers.clone_from_slice(state.registers.as_slice());
|
||||
self.writable_bits
|
||||
.clone_from_slice(state.writable_bits.as_slice());
|
||||
self.bars.clone_from_slice(state.bars.as_slice());
|
||||
self.rom_bar_addr = state.rom_bar_addr;
|
||||
self.rom_bar_size = state.rom_bar_size;
|
||||
self.rom_bar_used = state.rom_bar_used;
|
||||
self.last_capability = state.last_capability;
|
||||
self.msix_cap_reg_idx = state.msix_cap_reg_idx;
|
||||
}
|
||||
|
||||
/// Reads a 32bit register from `reg_idx` in the register map.
|
||||
pub fn read_reg(&self, reg_idx: usize) -> u32 {
|
||||
*(self.registers.get(reg_idx).unwrap_or(&0xffff_ffff))
|
||||
@@ -1068,11 +1046,16 @@ impl Pausable for PciConfiguration {}
|
||||
|
||||
impl Snapshottable for PciConfiguration {
|
||||
fn id(&self) -> String {
|
||||
String::from(PCI_CONFIGURATION_ID)
|
||||
String::from("pci_configuration")
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
Snapshot::new_from_versioned_state(&self.state())
|
||||
Snapshot::new_from_versioned_state(&self.id(), &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id())?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1195,7 +1178,6 @@ mod tests {
|
||||
0xABCD,
|
||||
0x2468,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
// Add two capabilities with different contents.
|
||||
@@ -1252,7 +1234,6 @@ mod tests {
|
||||
0xABCD,
|
||||
0x2468,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
let class_reg = cfg.read_reg(2);
|
||||
|
||||
@@ -19,13 +19,12 @@ pub use self::configuration::{
|
||||
PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciCapability, PciCapabilityId,
|
||||
PciClassCode, PciConfiguration, PciExpressCapabilityId, PciHeaderType, PciMassStorageSubclass,
|
||||
PciNetworkControllerSubclass, PciProgrammingInterface, PciSerialBusSubClass, PciSubclass,
|
||||
PCI_CONFIGURATION_ID,
|
||||
};
|
||||
pub use self::device::{
|
||||
BarReprogrammingParams, DeviceRelocation, Error as PciDeviceError, PciDevice,
|
||||
};
|
||||
pub use self::msi::{msi_num_enabled_vectors, MsiCap, MsiConfig};
|
||||
pub use self::msix::{MsixCap, MsixConfig, MsixTableEntry, MSIX_CONFIG_ID, MSIX_TABLE_ENTRY_SIZE};
|
||||
pub use self::msix::{MsixCap, MsixConfig, MsixTableEntry, MSIX_TABLE_ENTRY_SIZE};
|
||||
pub use self::vfio::{VfioPciDevice, VfioPciError};
|
||||
pub use self::vfio_user::{VfioUserDmaMapping, VfioUserPciDevice, VfioUserPciDeviceError};
|
||||
use serde::de::Visitor;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
|
||||
//
|
||||
|
||||
use anyhow::anyhow;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
@@ -38,15 +39,13 @@ pub fn msi_num_enabled_vectors(msg_ctl: u16) -> usize {
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
enum Error {
|
||||
#[error("Failed enabling the interrupt route: {0}")]
|
||||
EnableInterruptRoute(io::Error),
|
||||
#[error("Failed updating the interrupt route: {0}")]
|
||||
UpdateInterruptRoute(io::Error),
|
||||
}
|
||||
|
||||
pub const MSI_CONFIG_ID: &str = "msi_config";
|
||||
|
||||
#[derive(Clone, Copy, Default, Versionize)]
|
||||
pub struct MsiCap {
|
||||
// Message Control Register
|
||||
@@ -173,7 +172,7 @@ impl MsiCap {
|
||||
}
|
||||
|
||||
#[derive(Versionize)]
|
||||
pub struct MsiConfigState {
|
||||
struct MsiConfigState {
|
||||
cap: MsiCap,
|
||||
}
|
||||
|
||||
@@ -185,53 +184,51 @@ pub struct MsiConfig {
|
||||
}
|
||||
|
||||
impl MsiConfig {
|
||||
pub fn new(
|
||||
msg_ctl: u16,
|
||||
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
|
||||
state: Option<MsiConfigState>,
|
||||
) -> Result<Self, Error> {
|
||||
let cap = if let Some(state) = state {
|
||||
if state.cap.enabled() {
|
||||
for idx in 0..state.cap.num_enabled_vectors() {
|
||||
let config = MsiIrqSourceConfig {
|
||||
high_addr: state.cap.msg_addr_hi,
|
||||
low_addr: state.cap.msg_addr_lo,
|
||||
data: state.cap.msg_data as u32,
|
||||
devid: 0,
|
||||
};
|
||||
|
||||
interrupt_source_group
|
||||
.update(
|
||||
idx as InterruptIndex,
|
||||
InterruptSourceConfig::MsiIrq(config),
|
||||
state.cap.vector_masked(idx),
|
||||
)
|
||||
.map_err(Error::UpdateInterruptRoute)?;
|
||||
}
|
||||
|
||||
interrupt_source_group
|
||||
.enable()
|
||||
.map_err(Error::EnableInterruptRoute)?;
|
||||
}
|
||||
|
||||
state.cap
|
||||
} else {
|
||||
MsiCap {
|
||||
msg_ctl,
|
||||
..Default::default()
|
||||
}
|
||||
pub fn new(msg_ctl: u16, interrupt_source_group: Arc<dyn InterruptSourceGroup>) -> Self {
|
||||
let cap = MsiCap {
|
||||
msg_ctl,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(MsiConfig {
|
||||
MsiConfig {
|
||||
cap,
|
||||
interrupt_source_group,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn state(&self) -> MsiConfigState {
|
||||
MsiConfigState { cap: self.cap }
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &MsiConfigState) -> Result<(), Error> {
|
||||
self.cap = state.cap;
|
||||
|
||||
if self.enabled() {
|
||||
for idx in 0..self.num_enabled_vectors() {
|
||||
let config = MsiIrqSourceConfig {
|
||||
high_addr: self.cap.msg_addr_hi,
|
||||
low_addr: self.cap.msg_addr_lo,
|
||||
data: self.cap.msg_data as u32,
|
||||
devid: 0,
|
||||
};
|
||||
|
||||
self.interrupt_source_group
|
||||
.update(
|
||||
idx as InterruptIndex,
|
||||
InterruptSourceConfig::MsiIrq(config),
|
||||
self.cap.vector_masked(idx),
|
||||
)
|
||||
.map_err(Error::UpdateInterruptRoute)?;
|
||||
}
|
||||
|
||||
self.interrupt_source_group
|
||||
.enable()
|
||||
.map_err(Error::EnableInterruptRoute)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn enabled(&self) -> bool {
|
||||
self.cap.enabled()
|
||||
}
|
||||
@@ -284,10 +281,21 @@ impl Pausable for MsiConfig {}
|
||||
|
||||
impl Snapshottable for MsiConfig {
|
||||
fn id(&self) -> String {
|
||||
String::from(MSI_CONFIG_ID)
|
||||
String::from("msi_config")
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
Snapshot::new_from_versioned_state(&self.state())
|
||||
Snapshot::new_from_versioned_state(&self.id(), &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id())?)
|
||||
.map_err(|e| {
|
||||
MigratableError::Restore(anyhow!(
|
||||
"Could not restore state for {}: {:?}",
|
||||
self.id(),
|
||||
e
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
116
pci/src/msix.rs
116
pci/src/msix.rs
@@ -4,6 +4,7 @@
|
||||
//
|
||||
|
||||
use crate::{PciCapability, PciCapabilityId};
|
||||
use anyhow::anyhow;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use std::io;
|
||||
use std::result;
|
||||
@@ -25,10 +26,9 @@ const MSIX_ENABLE_BIT: u8 = 15;
|
||||
const FUNCTION_MASK_MASK: u16 = (1 << FUNCTION_MASK_BIT) as u16;
|
||||
const MSIX_ENABLE_MASK: u16 = (1 << MSIX_ENABLE_BIT) as u16;
|
||||
pub const MSIX_TABLE_ENTRY_SIZE: usize = 16;
|
||||
pub const MSIX_CONFIG_ID: &str = "msix_config";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
enum Error {
|
||||
/// Failed enabling the interrupt route.
|
||||
EnableInterruptRoute(io::Error),
|
||||
/// Failed updating the interrupt route.
|
||||
@@ -61,7 +61,7 @@ impl Default for MsixTableEntry {
|
||||
}
|
||||
|
||||
#[derive(Versionize)]
|
||||
pub struct MsixConfigState {
|
||||
struct MsixConfigState {
|
||||
table_entries: Vec<MsixTableEntry>,
|
||||
pba_entries: Vec<u64>,
|
||||
masked: bool,
|
||||
@@ -84,62 +84,23 @@ impl MsixConfig {
|
||||
msix_vectors: u16,
|
||||
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
|
||||
devid: u32,
|
||||
state: Option<MsixConfigState>,
|
||||
) -> result::Result<Self, Error> {
|
||||
) -> Self {
|
||||
assert!(msix_vectors <= MAX_MSIX_VECTORS_PER_DEVICE);
|
||||
|
||||
let (table_entries, pba_entries, masked, enabled) = if let Some(state) = state {
|
||||
if state.enabled && !state.masked {
|
||||
for (idx, table_entry) in state.table_entries.iter().enumerate() {
|
||||
if table_entry.masked() {
|
||||
continue;
|
||||
}
|
||||
let mut table_entries: Vec<MsixTableEntry> = Vec::new();
|
||||
table_entries.resize_with(msix_vectors as usize, Default::default);
|
||||
let mut pba_entries: Vec<u64> = Vec::new();
|
||||
let num_pba_entries: usize = ((msix_vectors as usize) / BITS_PER_PBA_ENTRY) + 1;
|
||||
pba_entries.resize_with(num_pba_entries, Default::default);
|
||||
|
||||
let config = MsiIrqSourceConfig {
|
||||
high_addr: table_entry.msg_addr_hi,
|
||||
low_addr: table_entry.msg_addr_lo,
|
||||
data: table_entry.msg_data,
|
||||
devid,
|
||||
};
|
||||
|
||||
interrupt_source_group
|
||||
.update(
|
||||
idx as InterruptIndex,
|
||||
InterruptSourceConfig::MsiIrq(config),
|
||||
state.masked,
|
||||
)
|
||||
.map_err(Error::UpdateInterruptRoute)?;
|
||||
|
||||
interrupt_source_group
|
||||
.enable()
|
||||
.map_err(Error::EnableInterruptRoute)?;
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
state.table_entries,
|
||||
state.pba_entries,
|
||||
state.masked,
|
||||
state.enabled,
|
||||
)
|
||||
} else {
|
||||
let mut table_entries: Vec<MsixTableEntry> = Vec::new();
|
||||
table_entries.resize_with(msix_vectors as usize, Default::default);
|
||||
let mut pba_entries: Vec<u64> = Vec::new();
|
||||
let num_pba_entries: usize = ((msix_vectors as usize) / BITS_PER_PBA_ENTRY) + 1;
|
||||
pba_entries.resize_with(num_pba_entries, Default::default);
|
||||
|
||||
(table_entries, pba_entries, true, false)
|
||||
};
|
||||
|
||||
Ok(MsixConfig {
|
||||
MsixConfig {
|
||||
table_entries,
|
||||
pba_entries,
|
||||
devid,
|
||||
interrupt_source_group,
|
||||
masked,
|
||||
enabled,
|
||||
})
|
||||
masked: true,
|
||||
enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn state(&self) -> MsixConfigState {
|
||||
@@ -151,6 +112,42 @@ impl MsixConfig {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &MsixConfigState) -> result::Result<(), Error> {
|
||||
self.table_entries = state.table_entries.clone();
|
||||
self.pba_entries = state.pba_entries.clone();
|
||||
self.masked = state.masked;
|
||||
self.enabled = state.enabled;
|
||||
|
||||
if self.enabled && !self.masked {
|
||||
for (idx, table_entry) in self.table_entries.iter().enumerate() {
|
||||
if table_entry.masked() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let config = MsiIrqSourceConfig {
|
||||
high_addr: table_entry.msg_addr_hi,
|
||||
low_addr: table_entry.msg_addr_lo,
|
||||
data: table_entry.msg_data,
|
||||
devid: self.devid,
|
||||
};
|
||||
|
||||
self.interrupt_source_group
|
||||
.update(
|
||||
idx as InterruptIndex,
|
||||
InterruptSourceConfig::MsiIrq(config),
|
||||
self.masked,
|
||||
)
|
||||
.map_err(Error::UpdateInterruptRoute)?;
|
||||
|
||||
self.interrupt_source_group
|
||||
.enable()
|
||||
.map_err(Error::EnableInterruptRoute)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn masked(&self) -> bool {
|
||||
self.masked
|
||||
}
|
||||
@@ -429,11 +426,22 @@ impl Pausable for MsixConfig {}
|
||||
|
||||
impl Snapshottable for MsixConfig {
|
||||
fn id(&self) -> String {
|
||||
String::from(MSIX_CONFIG_ID)
|
||||
String::from("msix_config")
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
Snapshot::new_from_versioned_state(&self.state())
|
||||
Snapshot::new_from_versioned_state(&self.id(), &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id())?)
|
||||
.map_err(|e| {
|
||||
MigratableError::Restore(anyhow!(
|
||||
"Could not restore state for {}: {:?}",
|
||||
self.id(),
|
||||
e
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
247
pci/src/vfio.rs
247
pci/src/vfio.rs
@@ -3,13 +3,11 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
|
||||
//
|
||||
|
||||
use crate::msi::{MsiConfigState, MSI_CONFIG_ID};
|
||||
use crate::msix::MsixConfigState;
|
||||
use crate::{
|
||||
msi_num_enabled_vectors, BarReprogrammingParams, MsiCap, MsiConfig, MsixCap, MsixConfig,
|
||||
PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciBdf, PciCapabilityId,
|
||||
PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciExpressCapabilityId,
|
||||
PciHeaderType, PciSubclass, MSIX_CONFIG_ID, MSIX_TABLE_ENTRY_SIZE, PCI_CONFIGURATION_ID,
|
||||
PciHeaderType, PciSubclass, MSIX_TABLE_ENTRY_SIZE,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
@@ -38,8 +36,6 @@ use vm_migration::{
|
||||
};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
pub(crate) const VFIO_COMMON_ID: &str = "vfio_common";
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum VfioPciError {
|
||||
#[error("Failed to create user memory region: {0}")]
|
||||
@@ -62,14 +58,6 @@ pub enum VfioPciError {
|
||||
RegionAlignment,
|
||||
#[error("Invalid region size")]
|
||||
RegionSize,
|
||||
#[error("Failed to retrieve MsiConfigState: {0}")]
|
||||
RetrieveMsiConfigState(#[source] anyhow::Error),
|
||||
#[error("Failed to retrieve MsixConfigState: {0}")]
|
||||
RetrieveMsixConfigState(#[source] anyhow::Error),
|
||||
#[error("Failed to retrieve PciConfigurationState: {0}")]
|
||||
RetrievePciConfigurationState(#[source] anyhow::Error),
|
||||
#[error("Failed to retrieve VfioCommonState: {0}")]
|
||||
RetrieveVfioCommonState(#[source] anyhow::Error),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@@ -418,86 +406,6 @@ pub(crate) struct VfioCommon {
|
||||
}
|
||||
|
||||
impl VfioCommon {
|
||||
pub(crate) fn new(
|
||||
msi_interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
legacy_interrupt_group: Option<Arc<dyn InterruptSourceGroup>>,
|
||||
vfio_wrapper: Arc<dyn Vfio>,
|
||||
subclass: &dyn PciSubclass,
|
||||
bdf: PciBdf,
|
||||
snapshot: Option<Snapshot>,
|
||||
) -> Result<Self, VfioPciError> {
|
||||
let pci_configuration_state =
|
||||
vm_migration::versioned_state_from_id(snapshot.as_ref(), PCI_CONFIGURATION_ID)
|
||||
.map_err(|e| {
|
||||
VfioPciError::RetrievePciConfigurationState(anyhow!(
|
||||
"Failed to get PciConfigurationState from Snapshot: {}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
let configuration = PciConfiguration::new(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
PciClassCode::Other,
|
||||
subclass,
|
||||
None,
|
||||
PciHeaderType::Device,
|
||||
0,
|
||||
0,
|
||||
None,
|
||||
pci_configuration_state,
|
||||
);
|
||||
|
||||
let mut vfio_common = VfioCommon {
|
||||
mmio_regions: Vec::new(),
|
||||
configuration,
|
||||
interrupt: Interrupt {
|
||||
intx: None,
|
||||
msi: None,
|
||||
msix: None,
|
||||
},
|
||||
msi_interrupt_manager,
|
||||
legacy_interrupt_group,
|
||||
vfio_wrapper,
|
||||
patches: HashMap::new(),
|
||||
};
|
||||
|
||||
let state: Option<VfioCommonState> = snapshot
|
||||
.as_ref()
|
||||
.map(|s| s.to_versioned_state())
|
||||
.transpose()
|
||||
.map_err(|e| {
|
||||
VfioPciError::RetrieveVfioCommonState(anyhow!(
|
||||
"Failed to get VfioCommonState from Snapshot: {}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
let msi_state = vm_migration::versioned_state_from_id(snapshot.as_ref(), MSI_CONFIG_ID)
|
||||
.map_err(|e| {
|
||||
VfioPciError::RetrieveMsiConfigState(anyhow!(
|
||||
"Failed to get MsiConfigState from Snapshot: {}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
let msix_state = vm_migration::versioned_state_from_id(snapshot.as_ref(), MSIX_CONFIG_ID)
|
||||
.map_err(|e| {
|
||||
VfioPciError::RetrieveMsixConfigState(anyhow!(
|
||||
"Failed to get MsixConfigState from Snapshot: {}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
if let Some(state) = state.as_ref() {
|
||||
vfio_common.set_state(state, msi_state, msix_state)?;
|
||||
} else {
|
||||
vfio_common.parse_capabilities(bdf);
|
||||
vfio_common.initialize_legacy_interrupt()?;
|
||||
}
|
||||
|
||||
Ok(vfio_common)
|
||||
}
|
||||
|
||||
pub(crate) fn allocate_bars(
|
||||
&mut self,
|
||||
allocator: &Arc<Mutex<SystemAllocator>>,
|
||||
@@ -742,13 +650,7 @@ impl VfioCommon {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn initialize_msix(
|
||||
&mut self,
|
||||
msix_cap: MsixCap,
|
||||
cap_offset: u32,
|
||||
bdf: PciBdf,
|
||||
state: Option<MsixConfigState>,
|
||||
) {
|
||||
pub(crate) fn initialize_msix(&mut self, msix_cap: MsixCap, cap_offset: u32, bdf: PciBdf) {
|
||||
let interrupt_source_group = self
|
||||
.msi_interrupt_manager
|
||||
.create_group(MsiIrqGroupConfig {
|
||||
@@ -761,9 +663,7 @@ impl VfioCommon {
|
||||
msix_cap.table_size(),
|
||||
interrupt_source_group.clone(),
|
||||
bdf.into(),
|
||||
state,
|
||||
)
|
||||
.unwrap();
|
||||
);
|
||||
|
||||
self.interrupt.msix = Some(VfioMsix {
|
||||
bar: msix_config,
|
||||
@@ -777,12 +677,7 @@ impl VfioCommon {
|
||||
self.vfio_wrapper.read_config_word((cap + 2).into())
|
||||
}
|
||||
|
||||
pub(crate) fn initialize_msi(
|
||||
&mut self,
|
||||
msg_ctl: u16,
|
||||
cap_offset: u32,
|
||||
state: Option<MsiConfigState>,
|
||||
) {
|
||||
pub(crate) fn initialize_msi(&mut self, msg_ctl: u16, cap_offset: u32) {
|
||||
let interrupt_source_group = self
|
||||
.msi_interrupt_manager
|
||||
.create_group(MsiIrqGroupConfig {
|
||||
@@ -791,7 +686,7 @@ impl VfioCommon {
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let msi_config = MsiConfig::new(msg_ctl, interrupt_source_group.clone(), state).unwrap();
|
||||
let msi_config = MsiConfig::new(msg_ctl, interrupt_source_group.clone());
|
||||
|
||||
self.interrupt.msi = Some(VfioMsi {
|
||||
cfg: msi_config,
|
||||
@@ -818,7 +713,7 @@ impl VfioCommon {
|
||||
// Parse capability only if the VFIO device
|
||||
// supports MSI.
|
||||
let msg_ctl = self.parse_msi_capabilities(cap_next);
|
||||
self.initialize_msi(msg_ctl, cap_next as u32, None);
|
||||
self.initialize_msi(msg_ctl, cap_next as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -829,7 +724,7 @@ impl VfioCommon {
|
||||
// Parse capability only if the VFIO device
|
||||
// supports MSI-X.
|
||||
let msix_cap = self.parse_msix_capabilities(cap_next);
|
||||
self.initialize_msix(msix_cap, cap_next as u32, bdf, None);
|
||||
self.initialize_msix(msix_cap, cap_next as u32, bdf);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1196,12 +1091,7 @@ impl VfioCommon {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(
|
||||
&mut self,
|
||||
state: &VfioCommonState,
|
||||
msi_state: Option<MsiConfigState>,
|
||||
msix_state: Option<MsixConfigState>,
|
||||
) -> Result<(), VfioPciError> {
|
||||
fn set_state(&mut self, state: &VfioCommonState) -> Result<(), VfioPciError> {
|
||||
if let (Some(intx), Some(interrupt_source_group)) =
|
||||
(&state.intx_state, self.legacy_interrupt_group.clone())
|
||||
{
|
||||
@@ -1216,11 +1106,11 @@ impl VfioCommon {
|
||||
}
|
||||
|
||||
if let Some(msi) = &state.msi_state {
|
||||
self.initialize_msi(msi.cap.msg_ctl, msi.cap_offset, msi_state);
|
||||
self.initialize_msi(msi.cap.msg_ctl, msi.cap_offset);
|
||||
}
|
||||
|
||||
if let Some(msix) = &state.msix_state {
|
||||
self.initialize_msix(msix.cap, msix.cap_offset, msix.bdf.into(), msix_state);
|
||||
self.initialize_msix(msix.cap, msix.cap_offset, msix.bdf.into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -1231,27 +1121,73 @@ impl Pausable for VfioCommon {}
|
||||
|
||||
impl Snapshottable for VfioCommon {
|
||||
fn id(&self) -> String {
|
||||
String::from(VFIO_COMMON_ID)
|
||||
String::from("vfio_common")
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
let mut vfio_common_snapshot = Snapshot::new_from_versioned_state(&self.state())?;
|
||||
let mut vfio_common_snapshot =
|
||||
Snapshot::new_from_versioned_state(&self.id(), &self.state())?;
|
||||
|
||||
// Snapshot PciConfiguration
|
||||
vfio_common_snapshot.add_snapshot(self.configuration.id(), self.configuration.snapshot()?);
|
||||
vfio_common_snapshot.add_snapshot(self.configuration.snapshot()?);
|
||||
|
||||
// Snapshot MSI
|
||||
if let Some(msi) = &mut self.interrupt.msi {
|
||||
vfio_common_snapshot.add_snapshot(msi.cfg.id(), msi.cfg.snapshot()?);
|
||||
vfio_common_snapshot.add_snapshot(msi.cfg.snapshot()?);
|
||||
}
|
||||
|
||||
// Snapshot MSI-X
|
||||
if let Some(msix) = &mut self.interrupt.msix {
|
||||
vfio_common_snapshot.add_snapshot(msix.bar.id(), msix.bar.snapshot()?);
|
||||
vfio_common_snapshot.add_snapshot(msix.bar.snapshot()?);
|
||||
}
|
||||
|
||||
Ok(vfio_common_snapshot)
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
if let Some(vfio_common_section) = snapshot
|
||||
.snapshot_data
|
||||
.get(&format!("{}-section", self.id()))
|
||||
{
|
||||
// It has to be invoked first as we want Interrupt to be initialized
|
||||
// correctly before we try to restore MSI and MSI-X configurations.
|
||||
self.set_state(&vfio_common_section.to_versioned_state()?)
|
||||
.map_err(|e| {
|
||||
MigratableError::Restore(anyhow!("Could not restore VFIO_COMMON state {:?}", e))
|
||||
})?;
|
||||
|
||||
// Restore PciConfiguration
|
||||
if let Some(pci_config_snapshot) = snapshot.snapshots.get(&self.configuration.id()) {
|
||||
self.configuration.restore(*pci_config_snapshot.clone())?;
|
||||
}
|
||||
|
||||
// Restore MSI
|
||||
if let Some(msi) = &mut self.interrupt.msi {
|
||||
if let Some(msi_snapshot) = snapshot.snapshots.get(&msi.cfg.id()) {
|
||||
msi.cfg.restore(*msi_snapshot.clone())?;
|
||||
}
|
||||
if msi.cfg.enabled() {
|
||||
self.enable_msi().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Restore MSI-X
|
||||
if let Some(msix) = &mut self.interrupt.msix {
|
||||
if let Some(msix_snapshot) = snapshot.snapshots.get(&msix.bar.id()) {
|
||||
msix.bar.restore(*msix_snapshot.clone())?;
|
||||
}
|
||||
if msix.bar.enabled() {
|
||||
self.enable_msix().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(MigratableError::Restore(anyhow!(
|
||||
"Could not find VFIO_COMMON snapshot section"
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
/// VfioPciDevice represents a VFIO PCI device.
|
||||
@@ -1282,22 +1218,48 @@ impl VfioPciDevice {
|
||||
legacy_interrupt_group: Option<Arc<dyn InterruptSourceGroup>>,
|
||||
iommu_attached: bool,
|
||||
bdf: PciBdf,
|
||||
restoring: bool,
|
||||
memory_slot: Arc<dyn Fn() -> u32 + Send + Sync>,
|
||||
snapshot: Option<Snapshot>,
|
||||
) -> Result<Self, VfioPciError> {
|
||||
let device = Arc::new(device);
|
||||
device.reset();
|
||||
|
||||
let configuration = PciConfiguration::new(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
PciClassCode::Other,
|
||||
&PciVfioSubclass::VfioSubclass,
|
||||
None,
|
||||
PciHeaderType::Device,
|
||||
0,
|
||||
0,
|
||||
None,
|
||||
);
|
||||
|
||||
let vfio_wrapper = VfioDeviceWrapper::new(Arc::clone(&device));
|
||||
|
||||
let common = VfioCommon::new(
|
||||
let mut common = VfioCommon {
|
||||
mmio_regions: Vec::new(),
|
||||
configuration,
|
||||
interrupt: Interrupt {
|
||||
intx: None,
|
||||
msi: None,
|
||||
msix: None,
|
||||
},
|
||||
msi_interrupt_manager,
|
||||
legacy_interrupt_group,
|
||||
Arc::new(vfio_wrapper) as Arc<dyn Vfio>,
|
||||
&PciVfioSubclass::VfioSubclass,
|
||||
bdf,
|
||||
vm_migration::snapshot_from_id(snapshot.as_ref(), VFIO_COMMON_ID),
|
||||
)?;
|
||||
vfio_wrapper: Arc::new(vfio_wrapper) as Arc<dyn Vfio>,
|
||||
patches: HashMap::new(),
|
||||
};
|
||||
|
||||
// No need to parse capabilities from the device if on the restore path.
|
||||
// The initialization will be performed later when restore() will be
|
||||
// called.
|
||||
if !restoring {
|
||||
common.parse_capabilities(bdf);
|
||||
common.initialize_legacy_interrupt()?;
|
||||
}
|
||||
|
||||
let vfio_pci_device = VfioPciDevice {
|
||||
id,
|
||||
@@ -1460,7 +1422,6 @@ impl VfioPciDevice {
|
||||
)?;
|
||||
|
||||
for area in sparse_areas.iter() {
|
||||
// SAFETY: FFI call with correct arguments
|
||||
let host_addr = unsafe {
|
||||
libc::mmap(
|
||||
null_mut(),
|
||||
@@ -1527,7 +1488,6 @@ impl VfioPciDevice {
|
||||
error!("Could not remove the userspace memory region: {}", e);
|
||||
}
|
||||
|
||||
// SAFETY: FFI call with correct arguments
|
||||
let ret = unsafe {
|
||||
libc::munmap(
|
||||
user_memory_region.host_addr as *mut libc::c_void,
|
||||
@@ -1739,13 +1699,28 @@ impl Snapshottable for VfioPciDevice {
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
let mut vfio_pci_dev_snapshot = Snapshot::default();
|
||||
let mut vfio_pci_dev_snapshot = Snapshot::new(&self.id);
|
||||
|
||||
// Snapshot VfioCommon
|
||||
vfio_pci_dev_snapshot.add_snapshot(self.common.id(), self.common.snapshot()?);
|
||||
vfio_pci_dev_snapshot.add_snapshot(self.common.snapshot()?);
|
||||
|
||||
Ok(vfio_pci_dev_snapshot)
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
// Restore VfioCommon
|
||||
if let Some(vfio_common_snapshot) = snapshot.snapshots.get(&self.common.id()) {
|
||||
self.common.restore(*vfio_common_snapshot.clone())?;
|
||||
self.map_mmio_regions().map_err(|e| {
|
||||
MigratableError::Restore(anyhow!(
|
||||
"Could not map MMIO regions for VfioPciDevice on restore {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for VfioPciDevice {}
|
||||
impl Migratable for VfioPciDevice {}
|
||||
|
||||
@@ -3,11 +3,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use crate::vfio::{UserMemoryRegion, Vfio, VfioCommon, VfioError, VFIO_COMMON_ID};
|
||||
use crate::vfio::{Interrupt, UserMemoryRegion, Vfio, VfioCommon, VfioError};
|
||||
use crate::{BarReprogrammingParams, PciBarConfiguration, VfioPciError};
|
||||
use crate::{PciBdf, PciDevice, PciDeviceError, PciSubclass};
|
||||
use crate::{
|
||||
PciBdf, PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciSubclass,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use hypervisor::HypervisorVmError;
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::os::unix::prelude::AsRawFd;
|
||||
use std::ptr::null_mut;
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
@@ -47,8 +51,6 @@ pub enum VfioUserPciDeviceError {
|
||||
DmaUnmap(#[source] VfioUserError),
|
||||
#[error("Failed to initialize legacy interrupts: {0}")]
|
||||
InitializeLegacyInterrupts(#[source] VfioPciError),
|
||||
#[error("Failed to create VfioCommon: {0}")]
|
||||
CreateVfioCommon(#[source] VfioPciError),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@@ -71,9 +73,22 @@ impl VfioUserPciDevice {
|
||||
msi_interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
legacy_interrupt_group: Option<Arc<dyn InterruptSourceGroup>>,
|
||||
bdf: PciBdf,
|
||||
restoring: bool,
|
||||
memory_slot: Arc<dyn Fn() -> u32 + Send + Sync>,
|
||||
snapshot: Option<Snapshot>,
|
||||
) -> Result<Self, VfioUserPciDeviceError> {
|
||||
// This is used for the BAR and capabilities only
|
||||
let configuration = PciConfiguration::new(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
PciClassCode::Other,
|
||||
&PciVfioUserSubclass::VfioUserSubclass,
|
||||
None,
|
||||
PciHeaderType::Device,
|
||||
0,
|
||||
0,
|
||||
None,
|
||||
);
|
||||
let resettable = client.lock().unwrap().resettable();
|
||||
if resettable {
|
||||
client
|
||||
@@ -87,15 +102,29 @@ impl VfioUserPciDevice {
|
||||
client: client.clone(),
|
||||
};
|
||||
|
||||
let common = VfioCommon::new(
|
||||
let mut common = VfioCommon {
|
||||
mmio_regions: Vec::new(),
|
||||
configuration,
|
||||
interrupt: Interrupt {
|
||||
intx: None,
|
||||
msi: None,
|
||||
msix: None,
|
||||
},
|
||||
msi_interrupt_manager,
|
||||
legacy_interrupt_group,
|
||||
Arc::new(vfio_wrapper) as Arc<dyn Vfio>,
|
||||
&PciVfioUserSubclass::VfioUserSubclass,
|
||||
bdf,
|
||||
vm_migration::snapshot_from_id(snapshot.as_ref(), VFIO_COMMON_ID),
|
||||
)
|
||||
.map_err(VfioUserPciDeviceError::CreateVfioCommon)?;
|
||||
vfio_wrapper: Arc::new(vfio_wrapper) as Arc<dyn Vfio>,
|
||||
patches: HashMap::new(),
|
||||
};
|
||||
|
||||
// No need to parse capabilities from the device if on the restore path.
|
||||
// The initialization will be performed later when restore() will be
|
||||
// called.
|
||||
if !restoring {
|
||||
common.parse_capabilities(bdf);
|
||||
common
|
||||
.initialize_legacy_interrupt()
|
||||
.map_err(VfioUserPciDeviceError::InitializeLegacyInterrupts)?;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
id,
|
||||
@@ -152,7 +181,6 @@ impl VfioUserPciDevice {
|
||||
};
|
||||
|
||||
for s in mmaps.iter() {
|
||||
// SAFETY: FFI call with correct arguments
|
||||
let host_addr = unsafe {
|
||||
libc::mmap(
|
||||
null_mut(),
|
||||
@@ -219,7 +247,6 @@ impl VfioUserPciDevice {
|
||||
}
|
||||
|
||||
// Remove mmaps
|
||||
// SAFETY: FFI call with correct arguments
|
||||
let ret = unsafe {
|
||||
libc::munmap(
|
||||
user_memory_region.host_addr as *mut libc::c_void,
|
||||
@@ -535,13 +562,28 @@ impl Snapshottable for VfioUserPciDevice {
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
let mut vfio_pci_dev_snapshot = Snapshot::default();
|
||||
let mut vfio_pci_dev_snapshot = Snapshot::new(&self.id);
|
||||
|
||||
// Snapshot VfioCommon
|
||||
vfio_pci_dev_snapshot.add_snapshot(self.common.id(), self.common.snapshot()?);
|
||||
vfio_pci_dev_snapshot.add_snapshot(self.common.snapshot()?);
|
||||
|
||||
Ok(vfio_pci_dev_snapshot)
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
// Restore VfioCommon
|
||||
if let Some(vfio_common_snapshot) = snapshot.snapshots.get(&self.common.id()) {
|
||||
self.common.restore(*vfio_common_snapshot.clone())?;
|
||||
self.map_mmio_regions().map_err(|e| {
|
||||
MigratableError::Restore(anyhow!(
|
||||
"Could not map MMIO regions for VfioUserPciDevice on restore {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for VfioUserPciDevice {}
|
||||
impl Migratable for VfioUserPciDevice {}
|
||||
|
||||
@@ -6,10 +6,13 @@ edition = "2021"
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.0.32", features = ["wrap_help"] }
|
||||
clap = { version = "4.0.29", features = ["wrap_help","cargo"] }
|
||||
dirs = "4.0.0"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.89"
|
||||
test_infra = { path = "../test_infra" }
|
||||
thiserror = "1.0.38"
|
||||
thiserror = "1.0.37"
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "4.0.29", features = ["cargo"] }
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#[macro_use(crate_version)]
|
||||
extern crate clap;
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let mut git_human_readable = "v".to_owned() + env!("CARGO_PKG_VERSION");
|
||||
|
||||
let mut git_human_readable = "v".to_owned() + crate_version!();
|
||||
if let Ok(git_out) = Command::new("git").args(["describe", "--dirty"]).output() {
|
||||
if git_out.status.success() {
|
||||
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
// Custom harness to run performance tests
|
||||
extern crate test_infra;
|
||||
#[macro_use(crate_authors)]
|
||||
extern crate clap;
|
||||
|
||||
mod performance_tests;
|
||||
|
||||
@@ -127,8 +129,8 @@ pub struct PerformanceTestControl {
|
||||
test_iterations: u32,
|
||||
num_queues: Option<u32>,
|
||||
queue_size: Option<u32>,
|
||||
net_control: Option<(bool, bool)>, // First bool is for RX(true)/TX(false), second bool is for bandwidth or PPS
|
||||
fio_control: Option<(FioOps, bool)>, // Second parameter controls whether we want bandwidth or IOPS
|
||||
net_rx: Option<bool>,
|
||||
fio_ops: Option<FioOps>,
|
||||
num_boot_vcpus: Option<u8>,
|
||||
}
|
||||
|
||||
@@ -144,13 +146,11 @@ impl fmt::Display for PerformanceTestControl {
|
||||
if let Some(o) = self.queue_size {
|
||||
output = format!("{output}, queue_size = {o}");
|
||||
}
|
||||
if let Some(o) = self.net_control {
|
||||
let (rx, bw) = o;
|
||||
output = format!("{output}, rx = {rx}, bandwidth = {bw}");
|
||||
if let Some(o) = self.net_rx {
|
||||
output = format!("{output}, net_rx = {o}");
|
||||
}
|
||||
if let Some(o) = &self.fio_control {
|
||||
let (ops, bw) = o;
|
||||
output = format!("{output}, fio_ops = {ops}, bandwidth = {bw}");
|
||||
if let Some(o) = &self.fio_ops {
|
||||
output = format!("{output}, fio_ops = {o}");
|
||||
}
|
||||
|
||||
write!(f, "{output}")
|
||||
@@ -164,8 +164,8 @@ impl PerformanceTestControl {
|
||||
test_iterations: 5,
|
||||
num_queues: None,
|
||||
queue_size: None,
|
||||
net_control: None,
|
||||
fio_control: None,
|
||||
net_rx: None,
|
||||
fio_ops: None,
|
||||
num_boot_vcpus: Some(1),
|
||||
}
|
||||
}
|
||||
@@ -262,7 +262,7 @@ mod adjuster {
|
||||
}
|
||||
}
|
||||
|
||||
const TEST_LIST: [PerformanceTest; 29] = [
|
||||
const TEST_LIST: [PerformanceTest; 17] = [
|
||||
PerformanceTest {
|
||||
name: "boot_time_ms",
|
||||
func_ptr: performance_boot_time,
|
||||
@@ -321,7 +321,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(256),
|
||||
net_control: Some((true, true)),
|
||||
net_rx: Some(true),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::bps_to_gbps,
|
||||
@@ -332,7 +332,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(256),
|
||||
net_control: Some((false, true)),
|
||||
net_rx: Some(false),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::bps_to_gbps,
|
||||
@@ -343,7 +343,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(4),
|
||||
queue_size: Some(256),
|
||||
net_control: Some((true, true)),
|
||||
net_rx: Some(true),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::bps_to_gbps,
|
||||
@@ -354,62 +354,18 @@ const TEST_LIST: [PerformanceTest; 29] = [
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(4),
|
||||
queue_size: Some(256),
|
||||
net_control: Some((false, true)),
|
||||
net_rx: Some(false),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::bps_to_gbps,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "virtio_net_throughput_single_queue_rx_pps",
|
||||
func_ptr: performance_net_throughput,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(256),
|
||||
net_control: Some((true, false)),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::identity,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "virtio_net_throughput_single_queue_tx_pps",
|
||||
func_ptr: performance_net_throughput,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(256),
|
||||
net_control: Some((false, false)),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::identity,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "virtio_net_throughput_multi_queue_rx_pps",
|
||||
func_ptr: performance_net_throughput,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(4),
|
||||
queue_size: Some(256),
|
||||
net_control: Some((true, false)),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::identity,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "virtio_net_throughput_multi_queue_tx_pps",
|
||||
func_ptr: performance_net_throughput,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(4),
|
||||
queue_size: Some(256),
|
||||
net_control: Some((false, false)),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::identity,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "block_read_MiBps",
|
||||
func_ptr: performance_block_io,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(1),
|
||||
queue_size: Some(128),
|
||||
fio_control: Some((FioOps::Read, true)),
|
||||
fio_ops: Some(FioOps::Read),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::Bps_to_MiBps,
|
||||
@@ -420,7 +376,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(1),
|
||||
queue_size: Some(128),
|
||||
fio_control: Some((FioOps::Write, true)),
|
||||
fio_ops: Some(FioOps::Write),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::Bps_to_MiBps,
|
||||
@@ -431,7 +387,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(1),
|
||||
queue_size: Some(128),
|
||||
fio_control: Some((FioOps::RandomRead, true)),
|
||||
fio_ops: Some(FioOps::RandomRead),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::Bps_to_MiBps,
|
||||
@@ -442,7 +398,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(1),
|
||||
queue_size: Some(128),
|
||||
fio_control: Some((FioOps::RandomWrite, true)),
|
||||
fio_ops: Some(FioOps::RandomWrite),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::Bps_to_MiBps,
|
||||
@@ -453,7 +409,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(128),
|
||||
fio_control: Some((FioOps::Read, true)),
|
||||
fio_ops: Some(FioOps::Read),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::Bps_to_MiBps,
|
||||
@@ -464,7 +420,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(128),
|
||||
fio_control: Some((FioOps::Write, true)),
|
||||
fio_ops: Some(FioOps::Write),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::Bps_to_MiBps,
|
||||
@@ -475,7 +431,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(128),
|
||||
fio_control: Some((FioOps::RandomRead, true)),
|
||||
fio_ops: Some(FioOps::RandomRead),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::Bps_to_MiBps,
|
||||
@@ -486,99 +442,11 @@ const TEST_LIST: [PerformanceTest; 29] = [
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(128),
|
||||
fio_control: Some((FioOps::RandomWrite, true)),
|
||||
fio_ops: Some(FioOps::RandomWrite),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::Bps_to_MiBps,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "block_read_IOPS",
|
||||
func_ptr: performance_block_io,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(1),
|
||||
queue_size: Some(128),
|
||||
fio_control: Some((FioOps::Read, false)),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::identity,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "block_write_IOPS",
|
||||
func_ptr: performance_block_io,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(1),
|
||||
queue_size: Some(128),
|
||||
fio_control: Some((FioOps::Write, false)),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::identity,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "block_random_read_IOPS",
|
||||
func_ptr: performance_block_io,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(1),
|
||||
queue_size: Some(128),
|
||||
fio_control: Some((FioOps::RandomRead, false)),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::identity,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "block_random_write_IOPS",
|
||||
func_ptr: performance_block_io,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(1),
|
||||
queue_size: Some(128),
|
||||
fio_control: Some((FioOps::RandomWrite, false)),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::identity,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "block_multi_queue_read_IOPS",
|
||||
func_ptr: performance_block_io,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(128),
|
||||
fio_control: Some((FioOps::Read, false)),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::identity,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "block_multi_queue_write_IOPS",
|
||||
func_ptr: performance_block_io,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(128),
|
||||
fio_control: Some((FioOps::Write, false)),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::identity,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "block_multi_queue_random_read_IOPS",
|
||||
func_ptr: performance_block_io,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(128),
|
||||
fio_control: Some((FioOps::RandomRead, false)),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::identity,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "block_multi_queue_random_write_IOPS",
|
||||
func_ptr: performance_block_io,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(128),
|
||||
fio_control: Some((FioOps::RandomWrite, false)),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::identity,
|
||||
},
|
||||
];
|
||||
|
||||
fn run_test_with_timeout(
|
||||
@@ -629,7 +497,7 @@ fn date() -> String {
|
||||
fn main() {
|
||||
let cmd_arguments = ClapCommand::new("performance-metrics")
|
||||
.version(env!("GIT_HUMAN_READABLE"))
|
||||
.author(env!("CARGO_PKG_AUTHORS"))
|
||||
.author(crate_authors!())
|
||||
.about("Generate the performance metrics data for Cloud Hypervisor")
|
||||
.arg(
|
||||
Arg::new("test-filter")
|
||||
|
||||
@@ -78,7 +78,7 @@ fn direct_kernel_boot_path() -> PathBuf {
|
||||
|
||||
pub fn performance_net_throughput(control: &PerformanceTestControl) -> f64 {
|
||||
let test_timeout = control.test_timeout;
|
||||
let (rx, bandwidth) = control.net_control.unwrap();
|
||||
let rx = control.net_rx.unwrap();
|
||||
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = performance_test_new_guest(Box::new(focal));
|
||||
@@ -105,7 +105,7 @@ pub fn performance_net_throughput(control: &PerformanceTestControl) -> f64 {
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
measure_virtio_net_throughput(test_timeout, num_queues / 2, &guest, rx, bandwidth).unwrap()
|
||||
measure_virtio_net_throughput(test_timeout, num_queues / 2, &guest, rx).unwrap()
|
||||
});
|
||||
|
||||
let _ = child.kill();
|
||||
@@ -335,7 +335,7 @@ pub fn performance_boot_time_pmem(control: &PerformanceTestControl) -> f64 {
|
||||
pub fn performance_block_io(control: &PerformanceTestControl) -> f64 {
|
||||
let test_timeout = control.test_timeout;
|
||||
let num_queues = control.num_queues.unwrap();
|
||||
let (fio_ops, bandwidth) = control.fio_control.as_ref().unwrap();
|
||||
let fio_ops = control.fio_ops.as_ref().unwrap();
|
||||
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = performance_test_new_guest(Box::new(focal));
|
||||
@@ -388,11 +388,7 @@ pub fn performance_block_io(control: &PerformanceTestControl) -> f64 {
|
||||
.unwrap();
|
||||
|
||||
// Parse fio output
|
||||
if *bandwidth {
|
||||
parse_fio_output(&output, fio_ops, num_queues).unwrap()
|
||||
} else {
|
||||
parse_fio_output_iops(&output, fio_ops, num_queues).unwrap()
|
||||
}
|
||||
parse_fio_output(&output, fio_ops, num_queues).unwrap()
|
||||
});
|
||||
|
||||
let _ = child.kill();
|
||||
@@ -429,7 +425,7 @@ mod tests {
|
||||
}
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse_iperf3_output(output.as_bytes(), true, true).unwrap(),
|
||||
parse_iperf3_output(output.as_bytes(), true).unwrap(),
|
||||
23957198874.604115
|
||||
);
|
||||
|
||||
@@ -448,31 +444,9 @@ mod tests {
|
||||
}
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse_iperf3_output(output.as_bytes(), false, true).unwrap(),
|
||||
parse_iperf3_output(output.as_bytes(), false).unwrap(),
|
||||
39520744482.79
|
||||
);
|
||||
let output = r#"
|
||||
{
|
||||
"end": {
|
||||
"sum": {
|
||||
"start": 0,
|
||||
"end": 5.000036,
|
||||
"seconds": 5.000036,
|
||||
"bytes": 29944971264,
|
||||
"bits_per_second": 47911877363.396217,
|
||||
"jitter_ms": 0.0038609822983198556,
|
||||
"lost_packets": 16,
|
||||
"packets": 913848,
|
||||
"lost_percent": 0.0017508382137948542,
|
||||
"sender": true
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse_iperf3_output(output.as_bytes(), true, false).unwrap(),
|
||||
182765.08409139456
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -10,7 +10,7 @@ path = "src/qcow.rs"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.4.3"
|
||||
libc = "0.2.139"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
remain = "0.2.6"
|
||||
remain = "0.2.5"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
@@ -231,7 +231,6 @@ impl Write for RawFile {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
// SAFETY: tmp_ptr is at least rounded_len long
|
||||
let tmp_buf = unsafe { slice::from_raw_parts_mut(tmp_ptr, rounded_len) };
|
||||
|
||||
// This can eventually replaced with read_at once its interface
|
||||
|
||||
@@ -4,6 +4,6 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.139"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
@@ -343,7 +343,6 @@ impl RateLimiter {
|
||||
let timer_fd = TimerFd::new()?;
|
||||
// Note: vmm_sys_util::TimerFd::new() open the fd w/o O_NONBLOCK. We manually add this flag
|
||||
// so that `Self::event_handler` won't be blocked with `vmm_sys_util::TimerFd::wait()`.
|
||||
// SAFETY: FFI calls.
|
||||
let ret = unsafe {
|
||||
let fd = timer_fd.as_raw_fd();
|
||||
let mut flags = libc::fcntl(fd, libc::F_GETFL);
|
||||
|
||||
199
release-notes.md
199
release-notes.md
@@ -1,15 +1,4 @@
|
||||
- [v29.0](#v290)
|
||||
- [Release Binary Supports Both MSHV and KVM](#release-binary-supports-both-mshv-and-kvm)
|
||||
- [Snapshot/Restore and Live Migration Improvements](#snapshotrestore-and-live-migration-improvements)
|
||||
- [Heap Allocation Improvements](#heap-allocation-improvements)
|
||||
- [`ch-remote` Improvements](#ch-remote-improvements)
|
||||
- [`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)
|
||||
- [Removals](#removals)
|
||||
- [Deprecations](#deprecations)
|
||||
- [Contributors](#contributors)
|
||||
- [v28.2](#v282)
|
||||
- [v28.1](#v281)
|
||||
- [v28.0](#v280)
|
||||
- [Community Engagement (Reminder)](#community-engagement-reminder)
|
||||
@@ -17,9 +6,9 @@
|
||||
- [Virtualised TPM Support](#virtualised-tpm-support)
|
||||
- [Transparent Huge Page Support](#transparent-huge-page-support)
|
||||
- [README Quick Start Improved](#readme-quick-start-improved)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
||||
- [Removals](#removals-1)
|
||||
- [Contributors](#contributors-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||
- [Removals](#removals)
|
||||
- [Contributors](#contributors)
|
||||
- [v27.0](#v270)
|
||||
- [Community Engagement](#community-engagement)
|
||||
- [Prebuilt Packages](#prebuilt-packages)
|
||||
@@ -28,41 +17,41 @@
|
||||
- [Simplified Build Feature Flags](#simplified-build-feature-flags)
|
||||
- [Asynchronous Kernel Loading](#asynchronous-kernel-loading)
|
||||
- [GDB Support for AArch64](#gdb-support-for-aarch64)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
||||
- [Deprecations](#deprecations-1)
|
||||
- [Contributors](#contributors-2)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
||||
- [Deprecations](#deprecations)
|
||||
- [Contributors](#contributors-1)
|
||||
- [v26.0](#v260)
|
||||
- [SMBIOS Improvements via `--platform`](#smbios-improvements-via---platform)
|
||||
- [Unified Binary MSHV and KVM Support](#unified-binary-mshv-and-kvm-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
||||
- [Deprecations](#deprecations-1)
|
||||
- [Removals](#removals-1)
|
||||
- [Contributors](#contributors-2)
|
||||
- [v25.0](#v250)
|
||||
- [`ch-remote` Improvements](#ch-remote-improvements)
|
||||
- [VM "Coredump" Support](#vm-coredump-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-3)
|
||||
- [Deprecations](#deprecations-2)
|
||||
- [Removals](#removals-2)
|
||||
- [Contributors](#contributors-3)
|
||||
- [v25.0](#v250)
|
||||
- [`ch-remote` Improvements](#ch-remote-improvements-1)
|
||||
- [VM "Coredump" Support](#vm-coredump-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-4)
|
||||
- [Removals](#removals-3)
|
||||
- [Contributors](#contributors-4)
|
||||
- [v24.0](#v240)
|
||||
- [Bypass Mode for `virtio-iommu`](#bypass-mode-for-virtio-iommu)
|
||||
- [Ensure Identifiers Uniqueness](#ensure-identifiers-uniqueness)
|
||||
- [Sparse Mmap support](#sparse-mmap-support)
|
||||
- [Expose Platform Serial Number](#expose-platform-serial-number)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-5)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-4)
|
||||
- [Notable Improvements](#notable-improvements)
|
||||
- [Deprecations](#deprecations-3)
|
||||
- [Deprecations](#deprecations-2)
|
||||
- [New on the Website](#new-on-the-website)
|
||||
- [Contributors](#contributors-5)
|
||||
- [Contributors](#contributors-4)
|
||||
- [v23.1](#v231)
|
||||
- [v23.0](#v230)
|
||||
- [vDPA Support](#vdpa-support)
|
||||
- [Updated OS Support list](#updated-os-support-list)
|
||||
- [`AArch64` Memory Map Improvements](#aarch64-memory-map-improvements)
|
||||
- [`AMX` Support](#amx-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-6)
|
||||
- [Deprecations](#deprecations-4)
|
||||
- [Contributors](#contributors-6)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-5)
|
||||
- [Deprecations](#deprecations-3)
|
||||
- [Contributors](#contributors-5)
|
||||
- [v22.1](#v221)
|
||||
- [v22.0](#v220)
|
||||
- [GDB Debug Stub Support](#gdb-debug-stub-support)
|
||||
@@ -73,13 +62,13 @@
|
||||
- [PMU Support for AArch64](#pmu-support-for-aarch64)
|
||||
- [Documentation Under CC-BY-4.0 License](#documentation-under-cc-by-40-license)
|
||||
- [Deprecation of "Classic" `virtiofsd`](#deprecation-of-classic-virtiofsd)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-7)
|
||||
- [Contributors](#contributors-7)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-6)
|
||||
- [Contributors](#contributors-6)
|
||||
- [v21.0](#v210)
|
||||
- [Efficient Local Live Migration (for Live Upgrade)](#efficient-local-live-migration-for-live-upgrade)
|
||||
- [Recommended Kernel is Now 5.15](#recommended-kernel-is-now-515)
|
||||
- [Notable Bug fixes](#notable-bug-fixes-8)
|
||||
- [Contributors](#contributors-8)
|
||||
- [Notable Bug fixes](#notable-bug-fixes-7)
|
||||
- [Contributors](#contributors-7)
|
||||
- [v20.2](#v202)
|
||||
- [v20.1](#v201)
|
||||
- [v20.0](#v200)
|
||||
@@ -88,8 +77,8 @@
|
||||
- [Improved VFIO support](#improved-vfio-support)
|
||||
- [Safer code](#safer-code)
|
||||
- [Extended documentation](#extended-documentation)
|
||||
- [Notable bug fixes](#notable-bug-fixes-9)
|
||||
- [Contributors](#contributors-9)
|
||||
- [Notable bug fixes](#notable-bug-fixes-8)
|
||||
- [Contributors](#contributors-8)
|
||||
- [v19.0](#v190)
|
||||
- [Improved PTY handling for serial and `virtio-console`](#improved-pty-handling-for-serial-and-virtio-console)
|
||||
- [PCI boot time optimisations](#pci-boot-time-optimisations)
|
||||
@@ -97,8 +86,8 @@
|
||||
- [Live migration enhancements](#live-migration-enhancements)
|
||||
- [`virtio-mem` support with `vfio-user`](#virtio-mem-support-with-vfio-user)
|
||||
- [AArch64 for `virtio-iommu`](#aarch64-for-virtio-iommu)
|
||||
- [Notable bug fixes](#notable-bug-fixes-10)
|
||||
- [Contributors](#contributors-10)
|
||||
- [Notable bug fixes](#notable-bug-fixes-9)
|
||||
- [Contributors](#contributors-9)
|
||||
- [v18.0](#v180)
|
||||
- [Experimental User Device (`vfio-user`) support](#experimental-user-device-vfio-user-support)
|
||||
- [Migration support for `vhost-user` devices](#migration-support-for-vhost-user-devices)
|
||||
@@ -108,31 +97,31 @@
|
||||
- [Live migration on MSHV hypervisor](#live-migration-on-mshv-hypervisor)
|
||||
- [AArch64 CPU topology support](#aarch64-cpu-topology-support)
|
||||
- [Power button support on AArch64](#power-button-support-on-aarch64)
|
||||
- [Notable bug fixes](#notable-bug-fixes-11)
|
||||
- [Contributors](#contributors-11)
|
||||
- [Notable bug fixes](#notable-bug-fixes-10)
|
||||
- [Contributors](#contributors-10)
|
||||
- [v17.0](#v170)
|
||||
- [ARM64 NUMA support using ACPI](#arm64-numa-support-using-acpi)
|
||||
- [`Seccomp` support for MSHV backend](#seccomp-support-for-mshv-backend)
|
||||
- [Hotplug of `macvtap` devices](#hotplug-of-macvtap-devices)
|
||||
- [Improved SGX support](#improved-sgx-support)
|
||||
- [Inflight tracking for `vhost-user` devices](#inflight-tracking-for-vhost-user-devices)
|
||||
- [Notable bug fixes](#notable-bug-fixes-12)
|
||||
- [Contributors](#contributors-12)
|
||||
- [Notable bug fixes](#notable-bug-fixes-11)
|
||||
- [Contributors](#contributors-11)
|
||||
- [v16.0](#v160)
|
||||
- [Improved live migration support](#improved-live-migration-support)
|
||||
- [Improved `vhost-user` support](#improved-vhost-user-support)
|
||||
- [ARM64 ACPI and UEFI support](#arm64-acpi-and-uefi-support)
|
||||
- [Notable bug fixes](#notable-bug-fixes-13)
|
||||
- [Notable bug fixes](#notable-bug-fixes-12)
|
||||
- [Removed functionality](#removed-functionality)
|
||||
- [Contributors](#contributors-13)
|
||||
- [Contributors](#contributors-12)
|
||||
- [v15.0](#v150)
|
||||
- [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees)
|
||||
- [Network device rate limiting](#network-device-rate-limiting)
|
||||
- [Support for runtime control of `virtio-net` guest offload](#support-for-runtime-control-of-virtio-net-guest-offload)
|
||||
- [`--api-socket` supports file descriptor parameter](#--api-socket-supports-file-descriptor-parameter)
|
||||
- [Bug fixes](#bug-fixes)
|
||||
- [Deprecations](#deprecations-5)
|
||||
- [Contributors](#contributors-14)
|
||||
- [Deprecations](#deprecations-4)
|
||||
- [Contributors](#contributors-13)
|
||||
- [v0.14.1](#v0141)
|
||||
- [v0.14.0](#v0140)
|
||||
- [Structured event monitoring](#structured-event-monitoring)
|
||||
@@ -141,8 +130,8 @@
|
||||
- [Updated hotplug documentation](#updated-hotplug-documentation)
|
||||
- [PTY control for serial and `virtio-console`](#pty-control-for-serial-and-virtio-console)
|
||||
- [Block device rate limiting](#block-device-rate-limiting)
|
||||
- [Deprecations](#deprecations-6)
|
||||
- [Contributors](#contributors-15)
|
||||
- [Deprecations](#deprecations-5)
|
||||
- [Contributors](#contributors-14)
|
||||
- [v0.13.0](#v0130)
|
||||
- [Wider VFIO device support](#wider-vfio-device-support)
|
||||
- [Improved huge page support](#improved-huge-page-support)
|
||||
@@ -150,13 +139,13 @@
|
||||
- [VHD disk image support](#vhd-disk-image-support)
|
||||
- [Improved Virtio device threading](#improved-virtio-device-threading)
|
||||
- [Clean shutdown support via synthetic power button](#clean-shutdown-support-via-synthetic-power-button)
|
||||
- [Contributors](#contributors-16)
|
||||
- [Contributors](#contributors-15)
|
||||
- [v0.12.0](#v0120)
|
||||
- [ARM64 enhancements](#arm64-enhancements)
|
||||
- [Removal of `vhost-user-net` and `vhost-user-block` self spawning](#removal-of-vhost-user-net-and-vhost-user-block-self-spawning)
|
||||
- [Migration of `vhost-user-fs` backend](#migration-of-vhost-user-fs-backend)
|
||||
- [Enhanced "info" API](#enhanced-info-api)
|
||||
- [Contributors](#contributors-17)
|
||||
- [Contributors](#contributors-16)
|
||||
- [v0.11.0](#v0110)
|
||||
- [`io_uring` support by default for `virtio-block`](#io_uring-support-by-default-for-virtio-block)
|
||||
- [Windows Guest Support](#windows-guest-support)
|
||||
@@ -168,15 +157,15 @@
|
||||
- [Default Log Level Changed](#default-log-level-changed)
|
||||
- [New `--balloon` Parameter Added](#new---balloon-parameter-added)
|
||||
- [Experimental `virtio-watchdog` Support](#experimental-virtio-watchdog-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-14)
|
||||
- [Contributors](#contributors-18)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-13)
|
||||
- [Contributors](#contributors-17)
|
||||
- [v0.10.0](#v0100)
|
||||
- [`virtio-block` Support for Multiple Descriptors](#virtio-block-support-for-multiple-descriptors)
|
||||
- [Memory Zones](#memory-zones)
|
||||
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements)
|
||||
- [Preliminary KVM HyperV Emulation Control](#preliminary-kvm-hyperv-emulation-control)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-15)
|
||||
- [Contributors](#contributors-19)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-14)
|
||||
- [Contributors](#contributors-18)
|
||||
- [v0.9.0](#v090)
|
||||
- [`io_uring` Based Block Device Support](#io_uring-based-block-device-support)
|
||||
- [Block and Network Device Statistics](#block-and-network-device-statistics)
|
||||
@@ -189,17 +178,17 @@
|
||||
- [Enhancements to ARM64 Support](#enhancements-to-arm64-support)
|
||||
- [Intel SGX Support](#intel-sgx-support)
|
||||
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-16)
|
||||
- [Contributors](#contributors-20)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-15)
|
||||
- [Contributors](#contributors-19)
|
||||
- [v0.8.0](#v080)
|
||||
- [Experimental Snapshot and Restore Support](#experimental-snapshot-and-restore-support)
|
||||
- [Experimental ARM64 Support](#experimental-arm64-support)
|
||||
- [Support for Using 5-level Paging in Guests](#support-for-using-5-level-paging-in-guests)
|
||||
- [Virtio Device Interrupt Suppression for Network Devices](#virtio-device-interrupt-suppression-for-network-devices)
|
||||
- [`vhost_user_fs` Improvements](#vhost_user_fs-improvements)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-17)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-16)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes)
|
||||
- [Contributors](#contributors-21)
|
||||
- [Contributors](#contributors-20)
|
||||
- [v0.7.0](#v070)
|
||||
- [Block, Network, Persistent Memory (PMEM), VirtioFS and Vsock hotplug](#block-network-persistent-memory-pmem-virtiofs-and-vsock-hotplug)
|
||||
- [Alternative `libc` Support](#alternative-libc-support)
|
||||
@@ -209,14 +198,14 @@
|
||||
- [`Seccomp` Sandboxing](#seccomp-sandboxing)
|
||||
- [Updated Distribution Support](#updated-distribution-support)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes-1)
|
||||
- [Contributors](#contributors-22)
|
||||
- [Contributors](#contributors-21)
|
||||
- [v0.6.0](#v060)
|
||||
- [Directly Assigned Devices Hotplug](#directly-assigned-devices-hotplug)
|
||||
- [Shared Filesystem Improvements](#shared-filesystem-improvements)
|
||||
- [Block and Networking IO Self Offloading](#block-and-networking-io-self-offloading)
|
||||
- [Command Line Interface](#command-line-interface)
|
||||
- [PVH Boot](#pvh-boot)
|
||||
- [Contributors](#contributors-23)
|
||||
- [Contributors](#contributors-22)
|
||||
- [v0.5.1](#v051)
|
||||
- [v0.5.0](#v050)
|
||||
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
|
||||
@@ -224,7 +213,7 @@
|
||||
- [New Interrupt Management Framework](#new-interrupt-management-framework)
|
||||
- [Development Tools](#development-tools)
|
||||
- [Kata Containers Integration](#kata-containers-integration)
|
||||
- [Contributors](#contributors-24)
|
||||
- [Contributors](#contributors-23)
|
||||
- [v0.4.0](#v040)
|
||||
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
|
||||
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
|
||||
@@ -233,7 +222,7 @@
|
||||
- [Userspace IOAPIC by default](#userspace-ioapic-by-default)
|
||||
- [PCI BAR reprogramming](#pci-bar-reprogramming)
|
||||
- [New `cloud-hypervisor` organization](#new-cloud-hypervisor-organization)
|
||||
- [Contributors](#contributors-25)
|
||||
- [Contributors](#contributors-24)
|
||||
- [v0.3.0](#v030)
|
||||
- [Block device offloading](#block-device-offloading)
|
||||
- [Network device backend](#network-device-backend)
|
||||
@@ -260,93 +249,13 @@
|
||||
- [Unit testing](#unit-testing)
|
||||
- [Integration tests parallelization](#integration-tests-parallelization)
|
||||
|
||||
# v29.0
|
||||
# v28.2
|
||||
This is a bug fix release. The following issues have been addressed:
|
||||
|
||||
This release has been tracked in our [roadmap
|
||||
project](https://github.com/orgs/cloud-hypervisor/projects/6) as iteration
|
||||
v29.0. The following user visible changes have been made:
|
||||
|
||||
### Release Binary Supports Both MSHV and KVM
|
||||
|
||||
On `x86-64` the binary included in releases supports both the KVM and MSHV
|
||||
hypervisor with runtime detection to identify the correct hypervisor to use.
|
||||
|
||||
### Snapshot/Restore and Live Migration Improvements
|
||||
|
||||
Improvements have been made to the data structures used for both live migration
|
||||
and snapshot/restore. Unfortunately this has broken compatibility with older
|
||||
versions (support for migrating between major versions is not yet officially
|
||||
supported but has worked for some versions.)
|
||||
|
||||
### Heap Allocation Improvements
|
||||
|
||||
Improvements have been made to the volume of heap allocations when running with
|
||||
`virtio-block` devices along with a reduction in the peak heap size.
|
||||
|
||||
### `ch-remote` Improvements
|
||||
|
||||
Support for "pinging" the VMM and shutting the VMM down have been added to
|
||||
`ch-remote`.
|
||||
|
||||
### `AArch64` Documentation Integration
|
||||
|
||||
The documentation for `AArch64` support has been integrated into the main
|
||||
README.
|
||||
|
||||
### `virtio-block` Counters Enhancement
|
||||
|
||||
The counters for the `virtio-block` device has extended to include min/mean/max
|
||||
latency counters.
|
||||
|
||||
### TCP Offload Control
|
||||
|
||||
The `virtio-net` device has gained support for controlling the enabling of
|
||||
checksum and offloading. This allows the device to be used in environments
|
||||
where the hardware lacks support for the offloading.
|
||||
|
||||
### Notable Bug Fixes
|
||||
|
||||
* Update dependencies including a version of `linux-loader` that addresses an
|
||||
infinite loop issue ([details](https://github.com/rust-vmm/linux-loader/security/advisories/GHSA-52h2-m2cf-9jh6))
|
||||
* Fix bugs related to `virtio-net` including an integer overflow issue
|
||||
(#4924, #4949)
|
||||
* Use host `cpuid` information for L2 cache for older KVM on x86 (#4920)
|
||||
* Memory mapped into the guest is now marked as non-dumpable which prevents large core files (#5016)
|
||||
* Fix QCOW2 refcount table size (#5034)
|
||||
* Fix unpause support on MSHV in dual binary (#5037)
|
||||
* Threads inside `virtio` devices are now shutdown on reboot (#5095)
|
||||
|
||||
### Removals
|
||||
|
||||
No functionality has been removed in this release.
|
||||
|
||||
### Deprecations
|
||||
|
||||
* Support for specifying a directory with `MemoryZoneConfig::file` or
|
||||
`MemoryConfig::file` has been deprecated. This was originally used for
|
||||
supporting huge pages or shared memory backing which is now natively supported
|
||||
(#5085)
|
||||
|
||||
### Contributors
|
||||
|
||||
Many thanks to everyone who has contributed to our release:
|
||||
|
||||
* Bo Chen <chen.bo@intel.com>
|
||||
* Claudio Fontana <claudio.fontana@gmail.com>
|
||||
* Hao Xu <howeyxu@tencent.com>
|
||||
* Henry Wang <Henry.Wang@arm.com>
|
||||
* Jinank Jain <jinankjain@microsoft.com>
|
||||
* Michael Zhao <michael.zhao@arm.com>
|
||||
* Muminul Islam <muislam@microsoft.com>
|
||||
* Philipp Schuster <philipp.schuster@cyberus-technology.de>
|
||||
* Rob Bradford <robert.bradford@intel.com>
|
||||
* Sebastien Boeuf <sebastien.boeuf@intel.com>
|
||||
* Shuaiyi Zhang <zhangsy28@lenovo.com>
|
||||
* Wei Liu <liuwe@microsoft.com>
|
||||
* Yong He <alexyonghe@tencent.com>
|
||||
* Yuji Hagiwara <yuuzi41@gmail.com>
|
||||
|
||||
|
||||
# v28.1
|
||||
This is a bug fix release. The following issues have been addressed:
|
||||
|
||||
|
||||
@@ -208,6 +208,8 @@ if [[ "$hypervisor" = "mshv" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# For now these values are deafult for kvm
|
||||
features=""
|
||||
|
||||
# lock the workloads folder to avoid parallel updating by different containers
|
||||
(
|
||||
@@ -231,7 +233,11 @@ fi
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
cargo build --all --release --target $BUILD_TARGET
|
||||
# Test without ACPI
|
||||
cargo build --all --release $features --target $BUILD_TARGET
|
||||
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||
strip target/$BUILD_TARGET/release/vhost_user_net
|
||||
strip target/$BUILD_TARGET/release/ch-remote
|
||||
|
||||
# Enable KSM with some reasonable parameters so that it won't take too long
|
||||
# for the memory to be merged between two processes.
|
||||
@@ -244,13 +250,13 @@ echo 6144 | sudo tee /proc/sys/vm/nr_hugepages
|
||||
sudo chmod a+rwX /dev/hugepages
|
||||
|
||||
# Run all direct kernel boot (Device Tree) test cases in mod `parallel`
|
||||
time cargo test "common_parallel::$test_filter" --target $BUILD_TARGET -- ${test_binary_args[*]}
|
||||
time cargo test $features "common_parallel::$test_filter" --target $BUILD_TARGET -- ${test_binary_args[*]}
|
||||
RES=$?
|
||||
|
||||
# Run some tests in sequence since the result could be affected by other tests
|
||||
# running in parallel.
|
||||
if [ $RES -eq 0 ]; then
|
||||
time cargo test "common_sequential::$test_filter" --target $BUILD_TARGET -- --test-threads=1 ${test_binary_args[*]}
|
||||
time cargo test $features "common_sequential::$test_filter" --target $BUILD_TARGET -- --test-threads=1 ${test_binary_args[*]}
|
||||
RES=$?
|
||||
else
|
||||
exit $RES
|
||||
@@ -258,7 +264,7 @@ fi
|
||||
|
||||
# Run all ACPI test cases
|
||||
if [ $RES -eq 0 ]; then
|
||||
time cargo test "aarch64_acpi::$test_filter" --target $BUILD_TARGET -- ${test_binary_args[*]}
|
||||
time cargo test $features "aarch64_acpi::$test_filter" --target $BUILD_TARGET -- ${test_binary_args[*]}
|
||||
RES=$?
|
||||
else
|
||||
exit $RES
|
||||
@@ -266,14 +272,14 @@ fi
|
||||
|
||||
# Run all test cases related to live migration
|
||||
if [ $RES -eq 0 ]; then
|
||||
time cargo test "live_migration_parallel::$test_filter" --target $BUILD_TARGET -- ${test_binary_args[*]}
|
||||
time cargo test $features "live_migration_parallel::$test_filter" --target $BUILD_TARGET -- ${test_binary_args[*]}
|
||||
RES=$?
|
||||
else
|
||||
exit $RES
|
||||
fi
|
||||
|
||||
if [ $RES -eq 0 ]; then
|
||||
time cargo test "live_migration_sequential::$test_filter" --target $BUILD_TARGET -- --test-threads=1 ${test_binary_args[*]}
|
||||
time cargo test $features "live_migration_sequential::$test_filter" --target $BUILD_TARGET -- --test-threads=1 ${test_binary_args[*]}
|
||||
RES=$?
|
||||
else
|
||||
exit $RES
|
||||
|
||||
@@ -12,10 +12,10 @@ mkdir -p "$WORKLOADS_DIR"
|
||||
process_common_args "$@"
|
||||
|
||||
# For now these values are default for kvm
|
||||
test_features=""
|
||||
features=""
|
||||
|
||||
if [ "$hypervisor" = "mshv" ] ; then
|
||||
test_features="--no-default-features --features mshv"
|
||||
features="--no-default-features --features mshv"
|
||||
fi
|
||||
|
||||
cp scripts/sha1sums-x86_64 $WORKLOADS_DIR
|
||||
@@ -86,21 +86,24 @@ if [[ "${BUILD_TARGET}" == "x86_64-unknown-linux-musl" ]]; then
|
||||
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
|
||||
fi
|
||||
|
||||
cargo build --no-default-features --features "kvm,mshv" --all --release --target $BUILD_TARGET
|
||||
cargo build --all --release $features --target $BUILD_TARGET
|
||||
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||
strip target/$BUILD_TARGET/release/vhost_user_net
|
||||
strip target/$BUILD_TARGET/release/ch-remote
|
||||
|
||||
# Test ovs-dpdk relies on hugepages
|
||||
echo 6144 | sudo tee /proc/sys/vm/nr_hugepages
|
||||
sudo chmod a+rwX /dev/hugepages
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
time cargo test $test_features "live_migration_parallel::$test_filter" -- ${test_binary_args[*]}
|
||||
time cargo test $features "live_migration_parallel::$test_filter" -- ${test_binary_args[*]}
|
||||
RES=$?
|
||||
|
||||
# Run some tests in sequence since the result could be affected by other tests
|
||||
# running in parallel.
|
||||
if [ $RES -eq 0 ]; then
|
||||
export RUST_BACKTRACE=1
|
||||
time cargo test $test_features "live_migration_sequential::$test_filter" -- --test-threads=1 ${test_binary_args[*]}
|
||||
time cargo test $features "live_migration_sequential::$test_filter" -- --test-threads=1 ${test_binary_args[*]}
|
||||
RES=$?
|
||||
fi
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@ mkdir -p "$WORKLOADS_DIR"
|
||||
process_common_args "$@"
|
||||
|
||||
# For now these values are default for kvm
|
||||
test_features=""
|
||||
features=""
|
||||
|
||||
if [ "$hypervisor" = "mshv" ] ; then
|
||||
test_features="--no-default-features --features mshv"
|
||||
features="--no-default-features --features mshv"
|
||||
fi
|
||||
|
||||
cp scripts/sha1sums-x86_64 $WORKLOADS_DIR
|
||||
@@ -77,10 +77,13 @@ if [[ "${BUILD_TARGET}" == "x86_64-unknown-linux-musl" ]]; then
|
||||
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
|
||||
fi
|
||||
|
||||
cargo build --no-default-features --features "kvm,mshv" --all --release --target $BUILD_TARGET
|
||||
cargo build --all --release $features --target $BUILD_TARGET
|
||||
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||
strip target/$BUILD_TARGET/release/vhost_user_net
|
||||
strip target/$BUILD_TARGET/release/ch-remote
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
time cargo test $test_features "rate_limiter::$test_filter" -- --test-threads=1 ${test_binary_args[*]}
|
||||
time cargo test $features "rate_limiter::$test_filter" -- --test-threads=1 ${test_binary_args[*]}
|
||||
RES=$?
|
||||
|
||||
exit $RES
|
||||
|
||||
@@ -11,32 +11,8 @@ if [[ "$hypervisor" = "mshv" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WORKLOADS_DIR="$HOME/workloads"
|
||||
mkdir -p "$WORKLOADS_DIR"
|
||||
|
||||
FW_URL=$(curl --silent https://api.github.com/repos/cloud-hypervisor/rust-hypervisor-firmware/releases/latest | grep "browser_download_url" | grep -o 'https://.*[^ "]')
|
||||
FW="$WORKLOADS_DIR/hypervisor-fw"
|
||||
pushd $WORKLOADS_DIR
|
||||
rm -f $FW
|
||||
time wget --quiet $FW_URL || exit 1
|
||||
popd
|
||||
|
||||
JAMMY_OS_IMAGE_NAME="jammy-server-cloudimg-amd64-custom-20221118-1.qcow2"
|
||||
JAMMY_OS_IMAGE_URL="https://cloud-hypervisor.azureedge.net/$JAMMY_OS_IMAGE_NAME"
|
||||
JAMMY_OS_IMAGE="$WORKLOADS_DIR/$JAMMY_OS_IMAGE_NAME"
|
||||
if [ ! -f "$JAMMY_OS_IMAGE" ]; then
|
||||
pushd $WORKLOADS_DIR
|
||||
time wget --quiet $JAMMY_OS_IMAGE_URL || exit 1
|
||||
popd
|
||||
fi
|
||||
|
||||
JAMMY_OS_RAW_IMAGE_NAME="jammy-server-cloudimg-amd64-custom-20221118-1.raw"
|
||||
JAMMY_OS_RAW_IMAGE="$WORKLOADS_DIR/$JAMMY_OS_RAW_IMAGE_NAME"
|
||||
if [ ! -f "$JAMMY_OS_RAW_IMAGE" ]; then
|
||||
pushd $WORKLOADS_DIR
|
||||
time qemu-img convert -p -f qcow2 -O raw $JAMMY_OS_IMAGE_NAME $JAMMY_OS_RAW_IMAGE_NAME || exit 1
|
||||
popd
|
||||
fi
|
||||
# For now these values are default for kvm
|
||||
features=""
|
||||
|
||||
BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
|
||||
CFLAGS=""
|
||||
@@ -46,11 +22,12 @@ TARGET_CC="musl-gcc"
|
||||
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
|
||||
fi
|
||||
|
||||
cargo build --no-default-features --features "kvm,mshv" --all --release --target $BUILD_TARGET
|
||||
cargo build --all --release $features --target $BUILD_TARGET
|
||||
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
time cargo test "sgx::$test_filter" -- ${test_binary_args[*]}
|
||||
time cargo test $features "sgx::$test_filter" -- ${test_binary_args[*]}
|
||||
RES=$?
|
||||
|
||||
exit $RES
|
||||
|
||||
@@ -5,16 +5,14 @@ source $HOME/.cargo/env
|
||||
source $(dirname "$0")/test-util.sh
|
||||
|
||||
process_common_args "$@"
|
||||
# For now these values are default for kvm
|
||||
features=""
|
||||
|
||||
WORKLOADS_DIR="$HOME/workloads"
|
||||
FOCAL_OS_IMAGE_NAME="focal-server-cloudimg-amd64-custom-20210609-0.raw"
|
||||
FOCAL_OS_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_IMAGE_NAME"
|
||||
FW="$WORKLOADS_DIR/hypervisor-fw"
|
||||
|
||||
VMLINUX_IMAGE="$WORKLOADS_DIR/vmlinux"
|
||||
if [ ! -f "$VMLINUX_IMAGE" ]; then
|
||||
build_custom_linux
|
||||
fi
|
||||
|
||||
BLK_IMAGE="$WORKLOADS_DIR/blk.img"
|
||||
MNT_DIR="mount_image"
|
||||
@@ -45,7 +43,9 @@ TARGET_CC="musl-gcc"
|
||||
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
|
||||
fi
|
||||
|
||||
cargo build --no-default-features --features "kvm,mshv" --all --release --target $BUILD_TARGET
|
||||
cargo build --all --release $features --target $BUILD_TARGET
|
||||
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||
strip target/$BUILD_TARGET/release/ch-remote
|
||||
|
||||
# We always copy a fresh version of our binary for our L2 guest.
|
||||
cp target/$BUILD_TARGET/release/cloud-hypervisor $VFIO_DIR
|
||||
@@ -56,11 +56,11 @@ echo 6144 | sudo tee /proc/sys/vm/nr_hugepages
|
||||
sudo chmod a+rwX /dev/hugepages
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
time cargo test "vfio::test_vfio" -- ${test_binary_args[*]}
|
||||
time cargo test $features "vfio::test_vfio" -- ${test_binary_args[*]}
|
||||
RES=$?
|
||||
|
||||
if [ $RES -eq 0 ]; then
|
||||
time cargo test "vfio::test_nvidia" -- --test-threads=1 ${test_binary_args[*]}
|
||||
time cargo test $features "vfio::test_nvidia" -- --test-threads=1 ${test_binary_args[*]}
|
||||
RES=$?
|
||||
fi
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ source $(dirname "$0")/test-util.sh
|
||||
source $(dirname "$0")/common-aarch64.sh
|
||||
|
||||
process_common_args "$@"
|
||||
# For now these values are default for kvm
|
||||
features=""
|
||||
|
||||
# aarch64 not supported for MSHV
|
||||
if [[ "$hypervisor" = "mshv" ]]; then
|
||||
@@ -44,11 +46,12 @@ dmsetup mknodes
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
cargo build --all --release --target $BUILD_TARGET
|
||||
cargo build --all --release $features --target $BUILD_TARGET
|
||||
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||
|
||||
# Only run with 1 thread to avoid tests interfering with one another because
|
||||
# Windows has a static IP configured
|
||||
time cargo test "windows::$test_filter" --target $BUILD_TARGET -- ${test_binary_args[*]}
|
||||
time cargo test $features "windows::$test_filter" --target $BUILD_TARGET -- ${test_binary_args[*]}
|
||||
RES=$?
|
||||
|
||||
dmsetup remove_all -f
|
||||
|
||||
@@ -6,10 +6,10 @@ source $(dirname "$0")/test-util.sh
|
||||
|
||||
process_common_args "$@"
|
||||
# For now these values are default for kvm
|
||||
test_features=""
|
||||
features=""
|
||||
|
||||
if [ "$hypervisor" = "mshv" ] ; then
|
||||
test_features="--no-default-features --features mshv"
|
||||
features="--no-default-features --features mshv"
|
||||
fi
|
||||
WIN_IMAGE_FILE="/root/workloads/windows-server-2019.raw"
|
||||
|
||||
@@ -44,13 +44,14 @@ dmsetup mknodes
|
||||
dmsetup create windows-snapshot-base --table "0 $img_blk_size snapshot-origin /dev/mapper/windows-base"
|
||||
dmsetup mknodes
|
||||
|
||||
cargo build --no-default-features --features "kvm,mshv" --all --release --target $BUILD_TARGET
|
||||
cargo build --all --release $features --target $BUILD_TARGET
|
||||
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
# Only run with 1 thread to avoid tests interfering with one another because
|
||||
# Windows has a static IP configured
|
||||
time cargo test $test_features "windows::$test_filter" -- ${test_binary_args[*]}
|
||||
time cargo test $features "windows::$test_filter" -- ${test_binary_args[*]}
|
||||
RES=$?
|
||||
|
||||
dmsetup remove_all -f
|
||||
|
||||
@@ -12,10 +12,10 @@ mkdir -p "$WORKLOADS_DIR"
|
||||
process_common_args "$@"
|
||||
|
||||
# For now these values are default for kvm
|
||||
test_features=""
|
||||
features=""
|
||||
|
||||
if [ "$hypervisor" = "mshv" ] ; then
|
||||
test_features="--no-default-features --features mshv"
|
||||
features="--no-default-features --features mshv"
|
||||
fi
|
||||
|
||||
cp scripts/sha1sums-x86_64 $WORKLOADS_DIR
|
||||
@@ -36,6 +36,24 @@ if [ ! -f "$OVMF_FW" ]; then
|
||||
popd
|
||||
fi
|
||||
|
||||
BIONIC_OS_IMAGE_NAME="bionic-server-cloudimg-amd64.qcow2"
|
||||
BIONIC_OS_IMAGE_URL="https://cloud-hypervisor.azureedge.net/$BIONIC_OS_IMAGE_NAME"
|
||||
BIONIC_OS_IMAGE="$WORKLOADS_DIR/$BIONIC_OS_IMAGE_NAME"
|
||||
if [ ! -f "$BIONIC_OS_IMAGE" ]; then
|
||||
pushd $WORKLOADS_DIR
|
||||
time wget --quiet $BIONIC_OS_IMAGE_URL || exit 1
|
||||
popd
|
||||
fi
|
||||
|
||||
BIONIC_OS_RAW_IMAGE_NAME="bionic-server-cloudimg-amd64.raw"
|
||||
BIONIC_OS_RAW_IMAGE="$WORKLOADS_DIR/$BIONIC_OS_RAW_IMAGE_NAME"
|
||||
if [ ! -f "$BIONIC_OS_RAW_IMAGE" ]; then
|
||||
pushd $WORKLOADS_DIR
|
||||
time qemu-img convert -p -f qcow2 -O raw $BIONIC_OS_IMAGE_NAME $BIONIC_OS_RAW_IMAGE_NAME || exit 1
|
||||
popd
|
||||
fi
|
||||
|
||||
|
||||
FOCAL_OS_IMAGE_NAME="focal-server-cloudimg-amd64-custom-20210609-0.qcow2"
|
||||
FOCAL_OS_IMAGE_URL="https://cloud-hypervisor.azureedge.net/$FOCAL_OS_IMAGE_NAME"
|
||||
FOCAL_OS_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_IMAGE_NAME"
|
||||
@@ -53,7 +71,7 @@ if [ ! -f "$FOCAL_OS_RAW_IMAGE" ]; then
|
||||
popd
|
||||
fi
|
||||
|
||||
JAMMY_OS_IMAGE_NAME="jammy-server-cloudimg-amd64-custom-20221118-1.qcow2"
|
||||
JAMMY_OS_IMAGE_NAME="jammy-server-cloudimg-amd64-custom-20220329-0.qcow2"
|
||||
JAMMY_OS_IMAGE_URL="https://cloud-hypervisor.azureedge.net/$JAMMY_OS_IMAGE_NAME"
|
||||
JAMMY_OS_IMAGE="$WORKLOADS_DIR/$JAMMY_OS_IMAGE_NAME"
|
||||
if [ ! -f "$JAMMY_OS_IMAGE" ]; then
|
||||
@@ -62,7 +80,7 @@ if [ ! -f "$JAMMY_OS_IMAGE" ]; then
|
||||
popd
|
||||
fi
|
||||
|
||||
JAMMY_OS_RAW_IMAGE_NAME="jammy-server-cloudimg-amd64-custom-20221118-1.raw"
|
||||
JAMMY_OS_RAW_IMAGE_NAME="jammy-server-cloudimg-amd64-custom-20220329-0.raw"
|
||||
JAMMY_OS_RAW_IMAGE="$WORKLOADS_DIR/$JAMMY_OS_RAW_IMAGE_NAME"
|
||||
if [ ! -f "$JAMMY_OS_RAW_IMAGE" ]; then
|
||||
pushd $WORKLOADS_DIR
|
||||
@@ -156,7 +174,10 @@ cp $VMLINUX_IMAGE $VFIO_DIR || exit 1
|
||||
|
||||
BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
|
||||
|
||||
cargo build --no-default-features --features "kvm,mshv" --all --release --target $BUILD_TARGET
|
||||
cargo build --all --release $features --target $BUILD_TARGET
|
||||
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||
strip target/$BUILD_TARGET/release/vhost_user_net
|
||||
strip target/$BUILD_TARGET/release/ch-remote
|
||||
|
||||
# We always copy a fresh version of our binary for our L2 guest.
|
||||
cp target/$BUILD_TARGET/release/cloud-hypervisor $VFIO_DIR
|
||||
@@ -176,14 +197,14 @@ sudo chmod a+rwX /dev/hugepages
|
||||
ulimit -l unlimited
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
time cargo test $test_features "common_parallel::$test_filter" -- ${test_binary_args[*]}
|
||||
time cargo test $features "common_parallel::$test_filter" -- ${test_binary_args[*]}
|
||||
RES=$?
|
||||
|
||||
# Run some tests in sequence since the result could be affected by other tests
|
||||
# running in parallel.
|
||||
if [ $RES -eq 0 ]; then
|
||||
export RUST_BACKTRACE=1
|
||||
time cargo test $test_features "common_sequential::$test_filter" -- --test-threads=1 ${test_binary_args[*]}
|
||||
time cargo test $features "common_sequential::$test_filter" -- --test-threads=1 ${test_binary_args[*]}
|
||||
RES=$?
|
||||
fi
|
||||
|
||||
|
||||
@@ -28,6 +28,13 @@ build_fio() {
|
||||
|
||||
process_common_args "$@"
|
||||
|
||||
# For now these values are default for kvm
|
||||
features=""
|
||||
|
||||
if [ "$hypervisor" = "mshv" ]; then
|
||||
features="--no-default-features --features mshv"
|
||||
fi
|
||||
|
||||
cp scripts/sha1sums-${TEST_ARCH} $WORKLOADS_DIR
|
||||
|
||||
if [ ${TEST_ARCH} == "aarch64" ]; then
|
||||
@@ -92,7 +99,11 @@ if [[ "${BUILD_TARGET}" == "${TEST_ARCH}-unknown-linux-musl" ]]; then
|
||||
CFLAGS="-I /usr/include/${TEST_ARCH}-linux-musl/ -idirafter /usr/include/"
|
||||
fi
|
||||
|
||||
cargo build --no-default-features --features "kvm,mshv" --all --release --target $BUILD_TARGET
|
||||
cargo build --all --release $features --target $BUILD_TARGET
|
||||
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||
strip target/$BUILD_TARGET/release/vhost_user_net
|
||||
strip target/$BUILD_TARGET/release/ch-remote
|
||||
strip target/$BUILD_TARGET/release/performance-metrics
|
||||
|
||||
# setup hugepages
|
||||
echo 6144 | sudo tee /proc/sys/vm/nr_hugepages
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
27f3b17962ace69b51f0ddc2012095e3109e6ed8 bionic-server-cloudimg-amd64.qcow2
|
||||
8db9cc58b01452ce2d06c313177e6e74d8582d93 bionic-server-cloudimg-amd64.raw
|
||||
d4a44acc6014d5f83dea1c625c43d677a95fa75f alpine-minirootfs-x86_64.tar.gz
|
||||
f1eccdc5e1b515dbad294426ab081b47ebfb97c0 focal-server-cloudimg-amd64-custom-20210609-0.qcow2
|
||||
7f5a8358243a96adf61f5c20139b29f308f2c0e3 focal-server-cloudimg-amd64-custom-20210609-0.raw
|
||||
b12e52203bc78400b2f099420b411b49ef4be7b8 jammy-server-cloudimg-amd64-custom-20221118-1.qcow2
|
||||
d843d5b249e8db3c0158eaf56ade895ed10342b5 jammy-server-cloudimg-amd64-custom-20221118-1.raw
|
||||
272102ac243e544bc5c2303f383eeb217bd2e446 jammy-server-cloudimg-amd64-custom-20220329-0.qcow2
|
||||
0b5b4816a0976c458f8e8bab061e9d63ebc1b806 jammy-server-cloudimg-amd64-custom-20220329-0.raw
|
||||
@@ -3,9 +3,11 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#[macro_use(crate_authors)]
|
||||
extern crate clap;
|
||||
|
||||
use api_client::simple_api_command;
|
||||
use api_client::simple_api_command_with_fds;
|
||||
use api_client::simple_api_full_command;
|
||||
use api_client::Error as ApiClientError;
|
||||
use clap::{Arg, ArgAction, ArgMatches, Command};
|
||||
use option_parser::{ByteSized, ByteSizedParseError};
|
||||
@@ -336,11 +338,6 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
Some("counters") => {
|
||||
simple_api_command(&mut socket, "GET", "counters", None).map_err(Error::ApiClient)
|
||||
}
|
||||
Some("ping") => {
|
||||
simple_api_full_command(&mut socket, "GET", "vmm.ping", None).map_err(Error::ApiClient)
|
||||
}
|
||||
Some("shutdown-vmm") => simple_api_full_command(&mut socket, "PUT", "vmm.shutdown", None)
|
||||
.map_err(Error::ApiClient),
|
||||
Some("resize") => resize_api_command(
|
||||
&mut socket,
|
||||
matches
|
||||
@@ -503,7 +500,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||
|
||||
fn main() {
|
||||
let app = Command::new("ch-remote")
|
||||
.author(env!("CARGO_PKG_AUTHORS"))
|
||||
.author(crate_authors!())
|
||||
.subcommand_required(true)
|
||||
.about("Remotely control a cloud-hypervisor VMM.")
|
||||
.arg(
|
||||
@@ -678,9 +675,7 @@ fn main() {
|
||||
Command::new("create")
|
||||
.about("Create VM from a JSON configuration")
|
||||
.arg(Arg::new("path").index(1).default_value("-")),
|
||||
)
|
||||
.subcommand(Command::new("ping").about("Ping the VMM to check for API server availability"))
|
||||
.subcommand(Command::new("shutdown-vmm").about("Shutdown the VMM"));
|
||||
);
|
||||
|
||||
let matches = app.get_matches();
|
||||
|
||||
|
||||
12
src/main.rs
12
src/main.rs
@@ -3,6 +3,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#[macro_use(crate_authors)]
|
||||
extern crate clap;
|
||||
#[macro_use]
|
||||
extern crate event_monitor;
|
||||
|
||||
@@ -89,7 +91,7 @@ impl log::Log for Logger {
|
||||
if record.file().is_some() && record.line().is_some() {
|
||||
writeln!(
|
||||
*(*(self.output.lock().unwrap())),
|
||||
"cloud-hypervisor: {:.6?}: <{}> {}:{}:{} -- {}",
|
||||
"cloud-hypervisor: {:?}: <{}> {}:{}:{} -- {}",
|
||||
duration,
|
||||
std::thread::current().name().unwrap_or("anonymous"),
|
||||
record.level(),
|
||||
@@ -100,7 +102,7 @@ impl log::Log for Logger {
|
||||
} else {
|
||||
writeln!(
|
||||
*(*(self.output.lock().unwrap())),
|
||||
"cloud-hypervisor: {:.6?}: <{}> {}:{} -- {}",
|
||||
"cloud-hypervisor: {:?}: <{}> {}:{} -- {}",
|
||||
duration,
|
||||
std::thread::current().name().unwrap_or("anonymous"),
|
||||
record.level(),
|
||||
@@ -123,12 +125,11 @@ fn prepare_default_values() -> (String, String, String) {
|
||||
}
|
||||
|
||||
fn create_app(default_vcpus: String, default_memory: String, default_rng: String) -> Command {
|
||||
#[allow(clippy::let_and_return)]
|
||||
let app = Command::new("cloud-hypervisor")
|
||||
// 'BUILT_VERSION' is set by the build script 'build.rs' at
|
||||
// compile time
|
||||
.version(env!("BUILT_VERSION"))
|
||||
.author(env!("CARGO_PKG_AUTHORS"))
|
||||
.author(crate_authors!())
|
||||
.about("Launch a cloud-hypervisor VMM.")
|
||||
.group(ArgGroup::new("vm-config").multiple(true))
|
||||
.group(ArgGroup::new("vmm-config").multiple(true))
|
||||
@@ -452,7 +453,6 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
.convert("fd")
|
||||
.map_err(Error::ParsingEventMonitor)?
|
||||
.unwrap();
|
||||
// SAFETY: fd is valid
|
||||
unsafe { File::from_raw_fd(fd) }
|
||||
} else if parser.is_set("path") {
|
||||
std::fs::OpenOptions::new()
|
||||
@@ -592,7 +592,6 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
|
||||
fn main() {
|
||||
// Ensure all created files (.e.g sockets) are only accessible by this user
|
||||
// SAFETY: trivially safe
|
||||
let _ = unsafe { libc::umask(0o077) };
|
||||
|
||||
let (default_vcpus, default_memory, default_rng) = prepare_default_values();
|
||||
@@ -608,7 +607,6 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
// SAFETY: trivially safe
|
||||
let on_tty = unsafe { libc::isatty(libc::STDIN_FILENO) } != 0;
|
||||
if on_tty {
|
||||
// Don't forget to set the terminal in canonical mode
|
||||
|
||||
@@ -7,9 +7,9 @@ edition = "2021"
|
||||
[dependencies]
|
||||
dirs = "4.0.0"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.139"
|
||||
libc = "0.2.138"
|
||||
once_cell = "1.16.0"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.89"
|
||||
ssh2 = { version = "0.9.3", features = ["vendored-openssl"] }
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#![allow(clippy::undocumented_unsafe_blocks)]
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use serde_json::Value;
|
||||
use ssh2::Session;
|
||||
@@ -1052,7 +1050,27 @@ impl Guest {
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub fn check_nvidia_gpu(&self) {
|
||||
assert!(self.ssh_command("nvidia-smi").unwrap().contains("Tesla T4"));
|
||||
// Run CUDA sample to validate it can find the device
|
||||
let device_query_result = self
|
||||
.ssh_command("sudo /root/NVIDIA_CUDA-11.3_Samples/bin/x86_64/linux/release/deviceQuery")
|
||||
.unwrap();
|
||||
assert!(device_query_result.contains("Detected 1 CUDA Capable device"));
|
||||
assert!(device_query_result.contains("Device 0: \"NVIDIA Tesla T4\""));
|
||||
assert!(device_query_result.contains("Result = PASS"));
|
||||
|
||||
// Run NVIDIA DCGM Diagnostics to validate the device is functional
|
||||
self.ssh_command("sudo nv-hostengine").unwrap();
|
||||
|
||||
assert!(self
|
||||
.ssh_command("sudo dcgmi discovery -l")
|
||||
.unwrap()
|
||||
.contains("Name: NVIDIA Tesla T4"));
|
||||
assert_eq!(
|
||||
self.ssh_command("sudo dcgmi diag -r 'diagnostic' | grep Pass | wc -l")
|
||||
.unwrap()
|
||||
.trim(),
|
||||
"10"
|
||||
);
|
||||
}
|
||||
|
||||
pub fn reboot_linux(&self, current_reboot_count: u32, custom_timeout: Option<i32>) {
|
||||
@@ -1311,36 +1329,22 @@ pub fn clh_command(cmd: &str) -> String {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn parse_iperf3_output(output: &[u8], sender: bool, bandwidth: bool) -> Result<f64, Error> {
|
||||
pub fn parse_iperf3_output(output: &[u8], sender: bool) -> Result<f64, Error> {
|
||||
std::panic::catch_unwind(|| {
|
||||
let s = String::from_utf8_lossy(output);
|
||||
let v: Value = serde_json::from_str(&s).expect("'iperf3' parse error: invalid json output");
|
||||
|
||||
if bandwidth {
|
||||
if sender {
|
||||
v["end"]["sum_sent"]["bits_per_second"]
|
||||
.as_f64()
|
||||
.expect("'iperf3' parse error: missing entry 'end.sum_sent.bits_per_second'")
|
||||
} else {
|
||||
v["end"]["sum_received"]["bits_per_second"].as_f64().expect(
|
||||
"'iperf3' parse error: missing entry 'end.sum_received.bits_per_second'",
|
||||
)
|
||||
}
|
||||
let bps: f64 = if sender {
|
||||
v["end"]["sum_sent"]["bits_per_second"]
|
||||
.as_f64()
|
||||
.expect("'iperf3' parse error: missing entry 'end.sum_sent.bits_per_second'")
|
||||
} else {
|
||||
// iperf does not distinguish sent vs received in this case.
|
||||
v["end"]["sum_received"]["bits_per_second"]
|
||||
.as_f64()
|
||||
.expect("'iperf3' parse error: missing entry 'end.sum_received.bits_per_second'")
|
||||
};
|
||||
|
||||
let lost_packets = v["end"]["sum"]["lost_packets"]
|
||||
.as_f64()
|
||||
.expect("'iperf3' parse error: missing entry 'end.sum.lost_packets'");
|
||||
let packets = v["end"]["sum"]["packets"]
|
||||
.as_f64()
|
||||
.expect("'iperf3' parse error: missing entry 'end.sum.packets'");
|
||||
let seconds = v["end"]["sum"]["seconds"]
|
||||
.as_f64()
|
||||
.expect("'iperf3' parse error: missing entry 'end.sum.seconds'");
|
||||
|
||||
(packets - lost_packets) / seconds
|
||||
}
|
||||
bps
|
||||
})
|
||||
.map_err(|_| {
|
||||
eprintln!(
|
||||
@@ -1509,7 +1513,6 @@ pub fn measure_virtio_net_throughput(
|
||||
queue_pairs: u32,
|
||||
guest: &Guest,
|
||||
receive: bool,
|
||||
bandwidth: bool,
|
||||
) -> Result<f64, Error> {
|
||||
let default_port = 5201;
|
||||
|
||||
@@ -1532,19 +1535,12 @@ pub fn measure_virtio_net_throughput(
|
||||
&format!("{}", default_port + n),
|
||||
"-t",
|
||||
&format!("{test_timeout}"),
|
||||
"-i",
|
||||
"0",
|
||||
]);
|
||||
// For measuring the guest transmit throughput (as a sender),
|
||||
// use reverse mode of the iperf3 client on the host
|
||||
if !receive {
|
||||
cmd.args(["-R"]);
|
||||
}
|
||||
// Use UDP stream to measure packets per second. The bitrate is set to
|
||||
// 1T to make sure it saturates the link.
|
||||
if !bandwidth {
|
||||
cmd.args(["-u", "-b", "1T"]);
|
||||
}
|
||||
let client = cmd
|
||||
.stderr(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
@@ -1555,7 +1551,7 @@ pub fn measure_virtio_net_throughput(
|
||||
}
|
||||
|
||||
let mut err: Option<Error> = None;
|
||||
let mut results = Vec::new();
|
||||
let mut bps = Vec::new();
|
||||
let mut failed = false;
|
||||
for c in clients {
|
||||
let mut c = c;
|
||||
@@ -1567,7 +1563,7 @@ pub fn measure_virtio_net_throughput(
|
||||
if !failed {
|
||||
// Safe to unwrap as we know the child has terminated succesffully
|
||||
let output = c.wait_with_output().unwrap();
|
||||
results.push(parse_iperf3_output(&output.stdout, receive, bandwidth)?);
|
||||
bps.push(parse_iperf3_output(&output.stdout, receive)?);
|
||||
} else {
|
||||
let _ = c.kill();
|
||||
let output = c.wait_with_output().unwrap();
|
||||
@@ -1581,7 +1577,7 @@ pub fn measure_virtio_net_throughput(
|
||||
if let Some(e) = err {
|
||||
Err(e)
|
||||
} else {
|
||||
Ok(results.iter().sum())
|
||||
Ok(bps.iter().sum())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
#![allow(clippy::undocumented_unsafe_blocks)]
|
||||
|
||||
// When enabling the `mshv` feature, we skip quite some tests and
|
||||
// hence have known dead-code. This annotation silences dead-code
|
||||
// related warnings for our quality workflow to pass.
|
||||
@@ -32,12 +32,14 @@ use wait_timeout::ChildExt;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
mod x86_64 {
|
||||
pub const BIONIC_IMAGE_NAME: &str = "bionic-server-cloudimg-amd64.raw";
|
||||
pub const FOCAL_IMAGE_NAME: &str = "focal-server-cloudimg-amd64-custom-20210609-0.raw";
|
||||
pub const JAMMY_NVIDIA_IMAGE_NAME: &str = "jammy-server-cloudimg-amd64-nvidia.raw";
|
||||
pub const FOCAL_SGX_IMAGE_NAME: &str = "focal-server-cloudimg-amd64-sgx.raw";
|
||||
pub const HIRSUTE_NVIDIA_IMAGE_NAME: &str = "hirsute-server-cloudimg-amd64-nvidia.raw";
|
||||
pub const FOCAL_IMAGE_NAME_QCOW2: &str = "focal-server-cloudimg-amd64-custom-20210609-0.qcow2";
|
||||
pub const FOCAL_IMAGE_NAME_VHD: &str = "focal-server-cloudimg-amd64-custom-20210609-0.vhd";
|
||||
pub const FOCAL_IMAGE_NAME_VHDX: &str = "focal-server-cloudimg-amd64-custom-20210609-0.vhdx";
|
||||
pub const JAMMY_IMAGE_NAME: &str = "jammy-server-cloudimg-amd64-custom-20221118-1.raw";
|
||||
pub const JAMMY_IMAGE_NAME: &str = "jammy-server-cloudimg-amd64-custom-20220329-0.raw";
|
||||
pub const WINDOWS_IMAGE_NAME: &str = "windows-server-2019.raw";
|
||||
pub const OVMF_NAME: &str = "CLOUDHV.fd";
|
||||
pub const GREP_SERIAL_IRQ_CMD: &str = "grep -c 'IO-APIC.*ttyS0' /proc/interrupts || true";
|
||||
@@ -1887,12 +1889,24 @@ mod common_parallel {
|
||||
|
||||
use crate::*;
|
||||
|
||||
#[test]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn test_bionic_hypervisor_fw() {
|
||||
test_simple_launch(fw_path(FwType::RustHypervisorFirmware), BIONIC_IMAGE_NAME)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn test_focal_hypervisor_fw() {
|
||||
test_simple_launch(fw_path(FwType::RustHypervisorFirmware), FOCAL_IMAGE_NAME)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn test_bionic_ovmf() {
|
||||
test_simple_launch(fw_path(FwType::Ovmf), BIONIC_IMAGE_NAME)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn test_focal_ovmf() {
|
||||
@@ -1961,7 +1975,7 @@ mod common_parallel {
|
||||
let _ = guest.ssh_command("sudo systemctl stop snapd");
|
||||
|
||||
guest.ssh_command("sudo poweroff").unwrap();
|
||||
thread::sleep(std::time::Duration::new(20, 0));
|
||||
thread::sleep(std::time::Duration::new(10, 0));
|
||||
let latest_events = [
|
||||
&MetaEvent {
|
||||
event: "shutdown".to_string(),
|
||||
@@ -1987,10 +2001,8 @@ mod common_parallel {
|
||||
|
||||
#[test]
|
||||
fn test_multi_cpu() {
|
||||
let jammy_image = JAMMY_IMAGE_NAME.to_string();
|
||||
let jammy = UbuntuDiskConfig::new(jammy_image);
|
||||
let guest = Guest::new(Box::new(jammy));
|
||||
|
||||
let bionic = UbuntuDiskConfig::new(BIONIC_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(bionic));
|
||||
let mut cmd = GuestCommand::new(&guest);
|
||||
cmd.args(["--cpus", "boot=2,max=4"])
|
||||
.args(["--memory", "size=512M"])
|
||||
@@ -2007,11 +2019,18 @@ mod common_parallel {
|
||||
|
||||
assert_eq!(guest.get_cpu_count().unwrap_or_default(), 2);
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
assert_eq!(
|
||||
guest
|
||||
.ssh_command(
|
||||
r#"sudo dmesg | grep "smp: Brought up" | sed "s/\[\ *[0-9.]*\] //""#
|
||||
)
|
||||
.ssh_command(r#"dmesg | grep "smpboot: Allowing" | sed "s/\[\ *[0-9.]*\] //""#)
|
||||
.unwrap()
|
||||
.trim(),
|
||||
"smpboot: Allowing 4 CPUs, 2 hotplug CPUs"
|
||||
);
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
assert_eq!(
|
||||
guest
|
||||
.ssh_command(r#"dmesg | grep "smp: Brought up" | sed "s/\[\ *[0-9.]*\] //""#)
|
||||
.unwrap()
|
||||
.trim(),
|
||||
"smp: Brought up 1 node, 2 CPUs"
|
||||
@@ -5787,20 +5806,12 @@ mod common_parallel {
|
||||
event: "starting".to_string(),
|
||||
device_id: None,
|
||||
},
|
||||
&MetaEvent {
|
||||
event: "activated".to_string(),
|
||||
device_id: Some("__console".to_string()),
|
||||
},
|
||||
&MetaEvent {
|
||||
event: "activated".to_string(),
|
||||
device_id: Some("__rng".to_string()),
|
||||
},
|
||||
&MetaEvent {
|
||||
event: "restoring".to_string(),
|
||||
device_id: None,
|
||||
},
|
||||
];
|
||||
assert!(check_sequential_events(
|
||||
assert!(check_sequential_events_exact(
|
||||
&expected_events,
|
||||
&event_path_restored
|
||||
));
|
||||
@@ -7709,14 +7720,19 @@ mod sgx {
|
||||
|
||||
#[test]
|
||||
fn test_sgx() {
|
||||
let jammy_image = JAMMY_IMAGE_NAME.to_string();
|
||||
let jammy = UbuntuDiskConfig::new(jammy_image);
|
||||
let guest = Guest::new(Box::new(jammy));
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_SGX_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(focal));
|
||||
let mut workload_path = dirs::home_dir().unwrap();
|
||||
workload_path.push("workloads");
|
||||
|
||||
let mut kernel_path = workload_path;
|
||||
kernel_path.push("vmlinux_w_sgx");
|
||||
|
||||
let mut child = GuestCommand::new(&guest)
|
||||
.args(["--cpus", "boot=1"])
|
||||
.args(["--memory", "size=512M"])
|
||||
.args(["--kernel", fw_path(FwType::RustHypervisorFirmware).as_str()])
|
||||
.args(["--kernel", kernel_path.to_str().unwrap()])
|
||||
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.default_disks()
|
||||
.default_net()
|
||||
.args(["--sgx-epc", "id=epc0,size=64M"])
|
||||
@@ -7738,6 +7754,18 @@ mod sgx {
|
||||
.trim(),
|
||||
"0x0000000004000000"
|
||||
);
|
||||
|
||||
// Run a test relying on SGX enclaves and check if it runs
|
||||
// successfully.
|
||||
assert!(guest
|
||||
.ssh_command("cd /linux-sgx/SampleCode/LocalAttestation/bin/ && sudo ./app")
|
||||
.unwrap()
|
||||
.trim()
|
||||
.contains(
|
||||
"succeed to load enclaves.\nsucceed to \
|
||||
establish secure channel.\nSucceed to exchange \
|
||||
secure message...\nSucceed to close Session..."
|
||||
));
|
||||
});
|
||||
|
||||
let _ = child.kill();
|
||||
@@ -8014,8 +8042,8 @@ mod vfio {
|
||||
}
|
||||
|
||||
fn test_nvidia_card_memory_hotplug(hotplug_method: &str) {
|
||||
let jammy = UbuntuDiskConfig::new(JAMMY_NVIDIA_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(jammy));
|
||||
let hirsute = UbuntuDiskConfig::new(HIRSUTE_NVIDIA_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(hirsute));
|
||||
let api_socket = temp_api_path(&guest.tmp_dir);
|
||||
|
||||
let mut child = GuestCommand::new(&guest)
|
||||
@@ -8068,8 +8096,8 @@ mod vfio {
|
||||
|
||||
#[test]
|
||||
fn test_nvidia_card_pci_hotplug() {
|
||||
let jammy = UbuntuDiskConfig::new(JAMMY_NVIDIA_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(jammy));
|
||||
let hirsute = UbuntuDiskConfig::new(HIRSUTE_NVIDIA_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(hirsute));
|
||||
let api_socket = temp_api_path(&guest.tmp_dir);
|
||||
|
||||
let mut child = GuestCommand::new(&guest)
|
||||
@@ -8110,8 +8138,8 @@ mod vfio {
|
||||
|
||||
#[test]
|
||||
fn test_nvidia_card_reboot() {
|
||||
let jammy = UbuntuDiskConfig::new(JAMMY_NVIDIA_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(jammy));
|
||||
let hirsute = UbuntuDiskConfig::new(HIRSUTE_NVIDIA_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(hirsute));
|
||||
let api_socket = temp_api_path(&guest.tmp_dir);
|
||||
|
||||
let mut child = GuestCommand::new(&guest)
|
||||
@@ -9211,51 +9239,43 @@ mod live_migration {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_live_upgrade_basic() {
|
||||
_test_live_migration(true, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_live_upgrade_local() {
|
||||
_test_live_migration(true, true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_live_upgrade_numa() {
|
||||
_test_live_migration_numa(true, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_live_upgrade_numa_local() {
|
||||
_test_live_migration_numa(true, true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_live_upgrade_watchdog() {
|
||||
_test_live_migration_watchdog(true, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_live_upgrade_watchdog_local() {
|
||||
_test_live_migration_watchdog(true, true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_live_upgrade_balloon() {
|
||||
_test_live_migration_balloon(true, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_live_upgrade_balloon_local() {
|
||||
_test_live_migration_balloon(true, true)
|
||||
}
|
||||
@@ -9282,7 +9302,6 @@ mod live_migration {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_live_upgrade_ovs_dpdk() {
|
||||
@@ -9290,7 +9309,6 @@ mod live_migration {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_live_upgrade_ovs_dpdk_local() {
|
||||
@@ -9422,8 +9440,7 @@ mod rate_limiter {
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
let measured_bps =
|
||||
measure_virtio_net_throughput(test_timeout, num_queues / 2, &guest, rx, true)
|
||||
.unwrap();
|
||||
measure_virtio_net_throughput(test_timeout, num_queues / 2, &guest, rx).unwrap();
|
||||
assert!(check_rate_limit(measured_bps, limit_bps, 0.1));
|
||||
});
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ impl Emulator {
|
||||
let mut res: PtmResult = 0;
|
||||
|
||||
let mut fds = [-1, -1];
|
||||
// SAFETY: FFI calls and return value of the unsafe call is checked
|
||||
// Safe because return value of the unsafe call is checked
|
||||
unsafe {
|
||||
let ret = libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr());
|
||||
if ret == -1 {
|
||||
@@ -289,7 +289,7 @@ impl Emulator {
|
||||
/// Function to write to data socket and read the response from it
|
||||
fn unix_tx_bufs(&mut self) -> Result<()> {
|
||||
let isselftest: bool;
|
||||
// SAFETY: type "sockaddr_storage" is valid with an all-zero byte-pattern value
|
||||
// Safe as type "sockaddr_storage" is valid with an all-zero byte-pattern value
|
||||
let mut addr: sockaddr_storage = unsafe { mem::zeroed() };
|
||||
let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;
|
||||
|
||||
@@ -307,7 +307,7 @@ impl Emulator {
|
||||
iov_len: cmd.input.len(),
|
||||
}; 1];
|
||||
|
||||
// SAFETY: all zero values from the unsafe method are updated before usage
|
||||
// Safe because all zero values from the unsafe method are updated before usage
|
||||
let mut msghdr: libc::msghdr = unsafe { mem::zeroed() };
|
||||
msghdr.msg_name = ptr::null_mut();
|
||||
msghdr.msg_namelen = 0;
|
||||
@@ -316,7 +316,7 @@ impl Emulator {
|
||||
msghdr.msg_control = ptr::null_mut();
|
||||
msghdr.msg_controllen = 0;
|
||||
msghdr.msg_flags = 0;
|
||||
// SAFETY: FFI call and the return value of the unsafe method is checked
|
||||
// Safe as the return value of the unsafe method is checked
|
||||
unsafe {
|
||||
let ret = libc::sendmsg(self.data_fd, &msghdr, 0);
|
||||
if ret == -1 {
|
||||
@@ -328,7 +328,7 @@ impl Emulator {
|
||||
}
|
||||
|
||||
cmd.output.fill(0);
|
||||
// SAFETY: FFI calls and return value from unsafe method is checked
|
||||
// Safe as return value from unsafe method is checked
|
||||
unsafe {
|
||||
let ret = libc::recvfrom(
|
||||
self.data_fd,
|
||||
|
||||
@@ -5,10 +5,10 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.139"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
once_cell = "1.16.0"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
serde = { version = "1.0.150", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.89"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -30,7 +30,6 @@ impl Tracer {
|
||||
|
||||
fn end(&self) {
|
||||
let end = Instant::now();
|
||||
// SAFETY: FFI call
|
||||
let path = format!("cloud-hypervisor-{}.trace", unsafe { libc::getpid() });
|
||||
let mut file = File::create(&path).unwrap();
|
||||
|
||||
@@ -106,14 +105,11 @@ struct TraceEvent {
|
||||
|
||||
pub fn trace_point_log(event: &'static str) {
|
||||
let trace_event = TraceEvent {
|
||||
// SAFETY: start has been initialised as part of initialising the value of TRACER
|
||||
timestamp: Instant::now().duration_since(unsafe { TRACER.get().unwrap().start }),
|
||||
event,
|
||||
end_timestamp: None,
|
||||
// SAFETY: thread_depth accesses current thread only specific data
|
||||
depth: unsafe { TRACER.get().unwrap().thread_depth() },
|
||||
};
|
||||
// SAFETY: add_event accesses current thread only specific data
|
||||
unsafe {
|
||||
TRACER.get_mut().unwrap().add_event(trace_event);
|
||||
}
|
||||
@@ -126,7 +122,6 @@ pub struct TraceBlock {
|
||||
|
||||
impl TraceBlock {
|
||||
pub fn new(event: &'static str) -> Self {
|
||||
// SAFETY: increase_thread_depth accesses current thread only specific data
|
||||
unsafe {
|
||||
TRACER.get_mut().unwrap().increase_thread_depth();
|
||||
}
|
||||
@@ -139,16 +134,16 @@ impl TraceBlock {
|
||||
|
||||
impl Drop for TraceBlock {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: start has been initialised as part of initialising the value of TRACER
|
||||
let start = unsafe { TRACER.get().unwrap().start };
|
||||
let trace_event = TraceEvent {
|
||||
timestamp: self.start.duration_since(start),
|
||||
timestamp: self
|
||||
.start
|
||||
.duration_since(unsafe { TRACER.get().unwrap().start }),
|
||||
event: self.event,
|
||||
end_timestamp: Some(Instant::now().duration_since(start)),
|
||||
// SAFETY: thread_depth() returns a number local to the current thread
|
||||
end_timestamp: Some(
|
||||
Instant::now().duration_since(unsafe { TRACER.get().unwrap().start }),
|
||||
),
|
||||
depth: unsafe { TRACER.get().unwrap().thread_depth() },
|
||||
};
|
||||
// SAFETY: add_event and decrease_thread_depth access current thread only specific data
|
||||
unsafe {
|
||||
TRACER.get_mut().unwrap().add_event(trace_event);
|
||||
TRACER.get_mut().unwrap().decrease_thread_depth();
|
||||
@@ -171,11 +166,9 @@ macro_rules! trace_scoped {
|
||||
}
|
||||
|
||||
pub fn end() {
|
||||
// SAFETY: this is called after all other threads end
|
||||
unsafe { TRACER.get().unwrap().end() }
|
||||
}
|
||||
|
||||
pub fn start() {
|
||||
// SAFETY: this is called before other threads start
|
||||
unsafe { TRACER.set(Tracer::new()).unwrap() }
|
||||
}
|
||||
|
||||
@@ -5,12 +5,13 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.139"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
serde = { version = "1.0.151", features = ["rc"] }
|
||||
serde_derive = "1.0.149"
|
||||
serde = { version = "1.0.150", features = ["rc"] }
|
||||
serde_derive = "1.0.150"
|
||||
serde_json = "1.0.89"
|
||||
thiserror = "1.0.38"
|
||||
thiserror = "1.0.37"
|
||||
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main", features = ["fam-wrappers"] }
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = "0.11.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
|
||||
@@ -190,25 +190,16 @@ struct DeviceReset {
|
||||
header: Header,
|
||||
}
|
||||
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
// SAFETY: these data structures only contain a sereis of integers
|
||||
unsafe impl ByteValued for Header {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for Version {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for DmaMap {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for DmaUnmap {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for DeviceGetInfo {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for DeviceGetRegionInfo {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for RegionAccess {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for GetIrqInfo {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for SetIrqs {}
|
||||
// SAFETY: data structure only contain a sereis of integers
|
||||
unsafe impl ByteValued for DeviceReset {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
@@ -600,9 +591,8 @@ impl Client {
|
||||
break;
|
||||
}
|
||||
|
||||
// SAFETY: `cap_data_ptr` is valid and the `cap_offset` is checked above
|
||||
// Safe because the `cap_data_ptr` is valid and the `cap_offset` is checked above
|
||||
let cap_ptr = unsafe { cap_data_ptr.offset(cap_offset as isize) };
|
||||
// SAFETY: `cap_ptr` is valid
|
||||
let cap_header = unsafe { &*(cap_ptr as *const vfio_info_cap_header) };
|
||||
match cap_header.id as u32 {
|
||||
VFIO_REGION_INFO_CAP_SPARSE_MMAP => {
|
||||
@@ -614,7 +604,7 @@ impl Client {
|
||||
);
|
||||
break;
|
||||
}
|
||||
// SAFETY: `cap_ptr` is valid and its size is also checked above
|
||||
// Safe because the `cap_ptr` is valid and its size is also checked above
|
||||
let sparse_mmap = unsafe {
|
||||
&*(cap_ptr as *mut u8 as *const vfio_region_info_cap_sparse_mmap)
|
||||
};
|
||||
@@ -626,8 +616,8 @@ impl Client {
|
||||
cap_offset, mmap_cap_size, area_num, mmap_area_size, cap_size);
|
||||
break;
|
||||
}
|
||||
// Safe because the `sparse_mmap` is valid and its size is also checked above
|
||||
let areas =
|
||||
// SAFETY: `sparse_mmap` is valid and its size is also checked above
|
||||
unsafe { sparse_mmap.areas.as_slice(sparse_mmap.nr_areas as usize) };
|
||||
for area in areas.iter() {
|
||||
sparse_areas.push(*area);
|
||||
|
||||
@@ -8,8 +8,8 @@ license = "Apache-2.0"
|
||||
[dependencies]
|
||||
byteorder = "1.4.3"
|
||||
crc32c = "0.6.3"
|
||||
libc = "0.2.139"
|
||||
libc = "0.2.138"
|
||||
log = "0.4.17"
|
||||
remain = "0.2.6"
|
||||
thiserror = "1.0.38"
|
||||
remain = "0.2.5"
|
||||
thiserror = "1.0.37"
|
||||
uuid = { version = "1.2.2", features = ["v4"] }
|
||||
|
||||
@@ -136,7 +136,6 @@ impl Header {
|
||||
f.read_exact(&mut buffer)
|
||||
.map_err(VhdxHeaderError::ReadHeader)?;
|
||||
|
||||
// SAFETY: buffer is of correct size and has been successfully filled.
|
||||
let header = unsafe { *(buffer.as_ptr() as *mut Header) };
|
||||
if header.signature != HEADER_SIGN {
|
||||
return Err(VhdxHeaderError::InvalidHeaderSign);
|
||||
@@ -152,7 +151,6 @@ impl Header {
|
||||
|
||||
/// Converts the header structure into a buffer
|
||||
fn get_header_as_buffer(&self, buffer: &mut [u8; HEADER_SIZE as usize]) {
|
||||
// SAFETY: self is a valid header.
|
||||
let reference = unsafe {
|
||||
std::slice::from_raw_parts(self as *const Header as *const u8, HEADER_SIZE as usize)
|
||||
};
|
||||
@@ -223,7 +221,6 @@ impl RegionTableHeader {
|
||||
f.read_exact(&mut buffer)
|
||||
.map_err(VhdxHeaderError::ReadRegionTableHeader)?;
|
||||
|
||||
// SAFETY: buffer is of correct size and has been successfully filled.
|
||||
let region_table_header = unsafe { *(buffer.as_ptr() as *mut RegionTableHeader) };
|
||||
if region_table_header.signature != REGION_SIGN {
|
||||
return Err(VhdxHeaderError::InvalidRegionSign);
|
||||
@@ -340,8 +337,6 @@ pub struct RegionTableEntry {
|
||||
impl RegionTableEntry {
|
||||
/// Reads one Region Entry from a Region Table index that starts from 0
|
||||
pub fn new(buffer: &[u8]) -> Result<RegionTableEntry> {
|
||||
assert!(buffer.len() == std::mem::size_of::<RegionTableEntry>());
|
||||
// SAFETY: the assertion above makes sure the buffer size is correct.
|
||||
let mut region_table_entry = unsafe { *(buffer.as_ptr() as *mut RegionTableEntry) };
|
||||
|
||||
let uuid = crate::uuid_from_guid(buffer);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user