Compare commits

..

17 Commits
v29.0 ... v28.2

Author SHA1 Message Date
Bo Chen
3b0d2e796b build: Release v28.2 (bug fix release)
Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-01-25 08:44:46 -08:00
Rob Bradford
42357c01f3 .github: Don't try and create releases for created branches
Dependabot will create a branch on the repo for it's updates this
triggers the release action (because it's the same event as a tag) which
will then fail leading to dependabot PRs not being automerged. Instead
only run the release check test on PRs or tag creation.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
(cherry picked from commit f6c058da56)
2023-01-24 06:04:01 -08:00
Rob Bradford
b7b5b9d7e6 .github: Re-order release steps to ensure binaries are available
Since we run "cargo clean" before running the aarch64 build we need to
create the release and upload the x86-64 assets before the clean.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
(cherry picked from commit 6e9172bf6f)
Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-01-24 06:04:01 -08:00
Rob Bradford
a63e064004 .github: Clean source tree before cross building release assets
This address issues with leaking symbols into the cross build.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
(cherry picked from commit 547230bb77)
Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-01-24 06:04:01 -08:00
Rob Bradford
62c1f39ea2 .github: Run release style builds on all PRs
Adjust the release workflow to move the conditional check on the tag
creation into the steps that create the release/upload the assets.

This allows us to ensure we're always in a releaseable state.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
(cherry picked from commit ef7e177df2)
Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-01-24 06:04:01 -08:00
Bo Chen
460ba718d4 github,Cargo.toml: Strip release binaries using toolchain
From Rust 1.59, the cargo command is now able to strip a binary [1].
This can be enabled in Cargo.toml by adding a `strip = "true"` to
the `[profile.release]` section.

Adding such binary stripping support in Cargo.toml of the project,
also change the stripping process in the release workflow to the one
using toolchain, so that the AArch64 release binaries can also
be stripped.

Fixes: https://github.com/cloud-hypervisor/cloud-hypervisor/issues/4916

[1] https://doc.rust-lang.org/beta/cargo/reference/profiles.html#strip

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
(cherry picked from commit 1ff0191b30)
Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-01-24 06:04:01 -08:00
Bo Chen
c91a8e1324 misc: Automatically fix cargo clippy issues added in 1.65 (stable)
The code of the stable branch diverges from the main branch, so we
can't directly backport the corresponding commit to fix the clippy
issues.

See: commit 5e52729453

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-01-19 09:12:38 -08:00
Philipp Schuster
1adfb7e9f8 virtio-devices: properly join all threads on Drop
This change is important to do a proper resource cleanup. We decided
to do this repetitive approach as VirtioCommon can't implement Drop
without major changes to the corresponding code. Also, devices such as
Net can't easily use the epoll_threads-abstraction from VirtioCommon as
it has multiple threads with different semantics.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
(cherry picked from commit ad6c0ee52b)
2023-01-19 09:12:38 -08:00
Muminul Islam
8dd4d42053 vmm: Ensure PIO/MMIO exits complete before pausing only for KVM
MSHV does not require to ensure MMIO/PIO exits complete
before pausing. This patch makes sure the above requirement
by checking the hypervisor type run-time.

Fixes #5037

Signed-off-by: Muminul Islam <muislam@microsoft.com>
(cherry picked from commit 4e3bc20f2c)
2023-01-19 09:12:38 -08:00
Sebastien Boeuf
3834b43878 qcow: Fix number of refcount table entries
The number of entries in the refcount table was incorrectly calculated
given there was no need for dividing the number of refblock clusters.
The number of refblock clusters is the number of entries in the refcount
table.

Suggested-by: lv_mz <lv.mengzhao@zte.com.cn>
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
(cherry picked from commit 0e7d5d2761)
2023-01-19 09:12:38 -08:00
Bo Chen
52605cc0e4 build: Release v28.1 (bug fix release)
Signed-off-by: Bo Chen <chen.bo@intel.com>
2022-12-13 13:02:57 -08:00
Rob Bradford
92beda1e32 README: Use consistent path to cloud-hypervisor binary
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
(cherry picked from commit 00becda899)
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2022-12-13 15:12:40 +00:00
Bo Chen
167fef382a net_util: queue_pair: Avoid integer overflow
This integer overflow was triggered with fuzzing on the virtio-net
device. The integer overflow is from the wrong assumption that the
packets read from or written to the tap device is always larger than the
size of a virtio-net header.

Signed-off-by: Bo Chen <chen.bo@intel.com>
(cherry picked from commit 559faa272a)
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2022-12-13 15:12:40 +00:00
Yuji Hagiwara
5126e9b26e docs: Fix a typo on the doc for tpm
swtpm accepts --tpmstate option

Signed-off-by: Yuji Hagiwara <yuuzi41@gmail.com>
(cherry picked from commit 47a7ebe434)
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2022-12-13 15:12:40 +00:00
Bo Chen
59bd682b1f net_util: queue_pair: Avoid panic and handle error properly
This panic was triggered with fuzzing on the virtio-net device. This
commits handles the error explicitly to avoid the panic, which also
makes the fuzzer happy (as panic is treated as bugs).

Signed-off-by: Bo Chen <chen.bo@intel.com>
(cherry picked from commit 4d9a2b17a7)
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2022-12-13 15:12:40 +00:00
Rob Bradford
8e3b351038 arch: x86_64: Use host cpuid information for L2 cache for older KVM
If the KVM version is too old (pre Linux 5.7) then fetch the CPUID
information from the host and use that in the guest. We prefer the KVM
version over the host version as that would use the CPUID for the
running CPU vs the CPU that runs this code which might be different due
to a hybrid topology.

Fixes: #4918

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
(cherry picked from commit 7c3110e6d5)
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2022-12-13 15:12:40 +00:00
Rob Bradford
3f8d06b47e build: Update dependencies in v28.x stable branch
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2022-12-13 14:29:22 +00:00
156 changed files with 2971 additions and 4137 deletions

View File

@@ -34,22 +34,22 @@ jobs:
override: true override: true
- name: Build (default features) - 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) - 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) - 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) - 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) - 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) - 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) - name: Release Build (default features)
run: cargo build --locked --all --release --target=${{ matrix.target }} run: cargo build --locked --all --release --target=${{ matrix.target }}

View File

@@ -13,24 +13,15 @@ jobs:
rust: rust:
- stable - stable
target: target:
- aarch64-unknown-linux-gnu
- aarch64-unknown-linux-musl
- x86_64-unknown-linux-gnu - x86_64-unknown-linux-gnu
- x86_64-unknown-linux-musl - aarch64-unknown-linux-gnu
experimental: [false] experimental: [false]
include: include:
- rust: beta
target: aarch64-unknown-linux-gnu
experimental: true
- rust: beta
target: aarch64-unknown-linux-musl
experimental: true
- rust: beta - rust: beta
target: x86_64-unknown-linux-gnu target: x86_64-unknown-linux-gnu
experimental: true experimental: true
- rust: beta - rust: beta
target: x86_64-unknown-linux-musl target: aarch64-unknown-linux-gnu
experimental: true experimental: true
steps: steps:
- name: Code checkout - name: Code checkout
@@ -62,28 +53,28 @@ jobs:
with: with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }} use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy 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) - name: Clippy (default features)
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }} use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy 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) - name: Clippy (default features + guest_debug)
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }} use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy 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) - name: Clippy (default features + tracing)
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }} use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy 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) - name: Clippy (mshv)
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }} if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
@@ -91,7 +82,7 @@ jobs:
with: with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }} use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy 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) - name: Clippy (mshv + kvm)
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }} if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
@@ -99,7 +90,7 @@ jobs:
with: with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }} use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy 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) - name: Clippy (kvm + tdx)
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }} if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
@@ -107,7 +98,7 @@ jobs:
with: with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }} use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy 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 - name: Check build did not modify any files
run: test -z "$(git status --porcelain)" run: test -z "$(git status --porcelain)"

View File

@@ -3,6 +3,7 @@ on: [pull_request, create]
jobs: jobs:
release: release:
if: (github.event_name == 'create' && github.event.ref_type == 'tag') || github.event_name == 'pull_request'
name: Release name: Release
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -25,15 +26,15 @@ jobs:
- name: Build - name: Build
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
toolchain: "1.62" toolchain: "1.62"
command: build command: build
args: --all --release --no-default-features --features "kvm,mshv" --target=x86_64-unknown-linux-gnu args: --all --release --target=x86_64-unknown-linux-gnu
- name: Static Build - name: Static Build
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
toolchain: "1.62" toolchain: "1.62"
command: build command: build
args: --all --release --no-default-features --features "kvm,mshv" --target=x86_64-unknown-linux-musl args: --all --release --target=x86_64-unknown-linux-musl
- name: Install Rust toolchain (aarch64-unknown-linux-musl) - name: Install Rust toolchain (aarch64-unknown-linux-musl)
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:

1
.gitignore vendored
View File

@@ -5,4 +5,3 @@
**/Cargo.lock **/Cargo.lock
**/rusty-tags.vi **/rusty-tags.vi
/rpm/SOURCES /rpm/SOURCES
/.vscode

94
Cargo.lock generated
View File

@@ -20,9 +20,9 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.68" version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
[[package]] [[package]]
name = "api_client" name = "api_client"
@@ -88,7 +88,6 @@ dependencies = [
"libc", "libc",
"log", "log",
"qcow", "qcow",
"smallvec",
"thiserror", "thiserror",
"versionize", "versionize",
"versionize_derive", "versionize_derive",
@@ -108,9 +107,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.78" version = "1.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@@ -120,9 +119,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.0.32" version = "4.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39" checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"clap_lex", "clap_lex",
@@ -144,7 +143,7 @@ dependencies = [
[[package]] [[package]]
name = "cloud-hypervisor" name = "cloud-hypervisor"
version = "29.0.0" version = "28.2.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"api_client", "api_client",
@@ -409,11 +408,12 @@ dependencies = [
[[package]] [[package]]
name = "iced-x86" name = "iced-x86"
version = "1.18.0" version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd04b950d75b3498320253b17fb92745b2cc79ead8814aede2f7c1bab858bec" checksum = "158f5204401d08f91d19176112146d75e99b3cf745092e268fa7be33e09adcec"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"static_assertions",
] ]
[[package]] [[package]]
@@ -443,9 +443,9 @@ dependencies = [
[[package]] [[package]]
name = "io-uring" name = "io-uring"
version = "0.5.11" version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9ddf18110cedc09617f5a965bdb51adb0c0fb02c791e4ab22f7a21bb25269b0" checksum = "7ba34abb5175052fc1a2227a10d2275b7386c9990167de9786c0b88d8b062330"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"libc", "libc",
@@ -462,9 +462,9 @@ dependencies = [
[[package]] [[package]]
name = "is-terminal" name = "is-terminal"
version = "0.4.2" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi",
"io-lifetimes", "io-lifetimes",
@@ -474,9 +474,9 @@ dependencies = [
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.5" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
[[package]] [[package]]
name = "kvm-bindings" name = "kvm-bindings"
@@ -507,9 +507,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.139" version = "0.2.138"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
[[package]] [[package]]
name = "libssh2-sys" name = "libssh2-sys"
@@ -548,9 +548,9 @@ dependencies = [
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.1.4" version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
@@ -595,7 +595,7 @@ dependencies = [
[[package]] [[package]]
name = "mshv-bindings" name = "mshv-bindings"
version = "0.1.1" 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 = [ dependencies = [
"libc", "libc",
"serde", "serde",
@@ -607,7 +607,7 @@ dependencies = [
[[package]] [[package]]
name = "mshv-ioctls" name = "mshv-ioctls"
version = "0.1.1" 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 = [ dependencies = [
"libc", "libc",
"mshv-bindings", "mshv-bindings",
@@ -678,9 +678,9 @@ dependencies = [
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.80" version = "0.9.79"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" checksum = "5454462c0eced1e97f2ec09036abc8da362e66802f66fd20f86854d9d8cbcbc4"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"cc", "cc",
@@ -713,9 +713,9 @@ dependencies = [
[[package]] [[package]]
name = "parking_lot_core" name = "parking_lot_core"
version = "0.8.5" version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"instant", "instant",
@@ -727,9 +727,9 @@ dependencies = [
[[package]] [[package]]
name = "paste" name = "paste"
version = "1.0.11" version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" checksum = "cf1c2c742266c2f1041c914ba65355a83ae8747b05f208319784083583494b4b"
[[package]] [[package]]
name = "pci" name = "pci"
@@ -926,9 +926,9 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.23" version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@@ -996,9 +996,9 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]] [[package]]
name = "remain" name = "remain"
version = "0.2.6" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5704e2cda92fd54202f05430725317ba0ea7d0c96b246ca0a92e45177127ba3b" checksum = "1a81bc6a3aaeb180f767bd2bda8c03bac5b93b3b69a783c66f35d16a4df276cf"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1051,24 +1051,24 @@ dependencies = [
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.16" version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.151" version = "1.0.150"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0" checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.151" version = "1.0.150"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8" checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1155,6 +1155,12 @@ dependencies = [
"parking_lot", "parking_lot",
] ]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.10.0" version = "0.10.0"
@@ -1208,18 +1214,18 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.38" version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.38" version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1251,9 +1257,9 @@ dependencies = [
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.6" version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
[[package]] [[package]]
name = "uuid" name = "uuid"

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "cloud-hypervisor" name = "cloud-hypervisor"
version = "29.0.0" version = "28.2.0"
authors = ["The Cloud Hypervisor Authors"] authors = ["The Cloud Hypervisor Authors"]
edition = "2021" edition = "2021"
default-run = "cloud-hypervisor" 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" homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
# Minimum buildable version: # Minimum buildable version:
# Keep in sync with version in .github/workflows/build.yaml # 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" rust-version = "1.60"
[profile.release] [profile.release]
@@ -24,25 +19,28 @@ opt-level = "s"
strip = true strip = true
[dependencies] [dependencies]
anyhow = "1.0.68" anyhow = "1.0.66"
api_client = { path = "api_client" } 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" epoll = "4.3.1"
event_monitor = { path = "event_monitor" } event_monitor = { path = "event_monitor" }
hypervisor = { path = "hypervisor" } hypervisor = { path = "hypervisor" }
libc = "0.2.139" libc = "0.2.138"
log = { version = "0.4.17", features = ["std"] } log = { version = "0.4.17", features = ["std"] }
option_parser = { path = "option_parser" } option_parser = { path = "option_parser" }
seccompiler = "0.3.0" seccompiler = "0.3.0"
serde_json = "1.0.89" serde_json = "1.0.89"
signal-hook = "0.3.14" signal-hook = "0.3.14"
thiserror = "1.0.38" thiserror = "1.0.37"
tpm = { path = "tpm"} tpm = { path = "tpm"}
tracer = { path = "tracer" } tracer = { path = "tracer" }
vmm = { path = "vmm" } vmm = { path = "vmm" }
vmm-sys-util = "0.11.0" vmm-sys-util = "0.11.0"
vm-memory = "0.10.0" vm-memory = "0.10.0"
[build-dependencies]
clap = { version = "4.0.29", features = ["cargo"] }
# List of patched crates # List of patched crates
[patch.crates-io] [patch.crates-io]
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx" } kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx" }

153
Jenkinsfile vendored
View File

@@ -10,16 +10,29 @@ pipeline {
checkout scm checkout scm
} }
} }
stage('Check if worker build can be skipped') { stage('Check for documentation only changes') {
when { when {
expression { expression {
return skipWorkerBuild() return docsFileOnly()
} }
} }
steps { steps {
script { script {
runWorkers = false 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' sh 'sudo apt install -y azure-cli'
} }
def boolean skipWorkerBuild() { def boolean docsFileOnly() {
if (env.CHANGE_TARGET == null) { if (env.CHANGE_TARGET == null) {
return false return false
} }
if (sh( return sh(
returnStatus: true, returnStatus: true,
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v '\\.md'" script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v '\\.md'"
) != 0) { ) != 0
return true }
def boolean fuzzFileOnly() {
if (env.CHANGE_TARGET == null) {
return false
} }
if (sh( return sh(
returnStatus: true, returnStatus: true,
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v -E 'fuzz/'" script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v -E 'fuzz/'"
) != 0) { ) != 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
} }

View File

@@ -69,12 +69,10 @@ Cloud Hypervisor supports `64-bit Linux` and Windows 10/Windows Server 2019.
# 2. Getting Started # 2. Getting Started
The following sections describe how to build and run Cloud Hypervisor. 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
## Prerequisites for AArch64 to the
[AArch64 documentation](docs/arm64.md).
- AArch64 servers (recommended) or development boards equipped with the GICv3
interrupt controller.
## Host OS ## Host OS
@@ -107,10 +105,13 @@ do not wish to use the pre-built binaries.
## Booting Linux ## Booting Linux
Cloud Hypervisor supports direct kernel boot (the x86-64 kernel requires the kernel The instructions below are for the `x86-64` platform. For `AArch64` please see
built with PVH support) or booting via a firmware (either [Rust Hypervisor 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 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 Binary builds of the firmware files are available for the latest release of
[Rust Hyperivor [Rust Hyperivor
@@ -172,7 +173,7 @@ $ ./cloud-hypervisor \
#### Building your Kernel #### 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: To build the kernel:
@@ -180,23 +181,15 @@ To build the kernel:
# Clone the Cloud Hypervisor Linux branch # Clone the Cloud Hypervisor Linux branch
$ git clone --depth 1 https://github.com/cloud-hypervisor/linux.git -b ch-5.15.12 linux-cloud-hypervisor $ git clone --depth 1 https://github.com/cloud-hypervisor/linux.git -b ch-5.15.12 linux-cloud-hypervisor
$ pushd 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 $ 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 $ cp linux-config-x86_64 .config
$ 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
$ KCFLAGS="-Wa,-mx86-used-note=no" make bzImage -j `nproc` $ KCFLAGS="-Wa,-mx86-used-note=no" make bzImage -j `nproc`
# Do native build of the AArch64 kernel
$ make -j `nproc`
$ popd $ 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`. `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 #### 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. an `ext4` root filesystem.
```shell ```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-amd64.img
$ 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
$ 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
``` ```
#### Booting the guest VM #### 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 These sample commands boot the disk image using the custom kernel whilst also
supplying the desired kernel command line. supplying the desired kernel command line.
- x86-64
```shell ```shell
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor $ sudo setcap cap_net_admin+ep ./cloud-hypervisor
$ ./create-cloud-init.sh $ ./create-cloud-init.sh
@@ -229,26 +218,9 @@ $ ./cloud-hypervisor \
--net "tap=,mac=,ip=,mask=" --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`. If earlier kernel messages are required the serial console should be used instead of `virtio-console`.
- x86-64 ```./cloud-hypervisor \
```shell
$ ./cloud-hypervisor \
--kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \ --kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
--console off \ --console off \
--serial tty \ --serial tty \
@@ -259,20 +231,6 @@ $ ./cloud-hypervisor \
--net "tap=,mac=,ip=,mask=" --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 # 3. Status
Cloud Hypervisor is under active development. The following stability 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). 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 Bionic](https://cloud-images.ubuntu.com/bionic/current/) (bionic-server-cloudimg-amd64.img)
- [Ubuntu Jammy](https://cloud-images.ubuntu.com/jammy/current/) (jammy-server-cloudimg-{amd64,arm64}.img ) - [Ubuntu Focal](https://cloud-images.ubuntu.com/focal/current/) (focal-server-cloudimg-amd64.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 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 Direct kernel boot to userspace should work with a rootfs from most
distributions although you may need to enable exotic filesystem types in the distributions although you may need to enable exotic filesystem types in the

View File

@@ -38,6 +38,7 @@ pub struct Sdt {
data: Vec<u8>, data: Vec<u8>,
} }
#[allow(clippy::len_without_is_empty)]
impl Sdt { impl Sdt {
pub fn new( pub fn new(
signature: [u8; 4], signature: [u8; 4],
@@ -97,7 +98,6 @@ impl Sdt {
/// Write a value at the given offset /// Write a value at the given offset
pub fn write<T>(&mut self, offset: usize, value: T) { pub fn write<T>(&mut self, offset: usize, value: T) {
assert!((offset + (std::mem::size_of::<T>() - 1)) < self.data.len()); 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 { unsafe {
*(((self.data.as_mut_ptr() as usize) + offset) as *mut T) = value; *(((self.data.as_mut_ptr() as usize) + offset) as *mut T) = value;
} }
@@ -123,10 +123,6 @@ impl Sdt {
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.data.len() self.data.len()
} }
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -9,14 +9,14 @@ default = []
tdx = [] tdx = []
[dependencies] [dependencies]
anyhow = "1.0.68" anyhow = "1.0.66"
byteorder = "1.4.3" byteorder = "1.4.3"
hypervisor = { path = "../hypervisor" } hypervisor = { path = "../hypervisor" }
libc = "0.2.139" libc = "0.2.138"
linux-loader = { version = "0.8.1", features = ["elf", "bzimage", "pe"] } linux-loader = { version = "0.8.1", features = ["elf", "bzimage", "pe"] }
log = "0.4.17" log = "0.4.17"
serde = { version = "1.0.151", features = ["rc", "derive"] } serde = { version = "1.0.150", features = ["rc", "derive"] }
thiserror = "1.0.38" thiserror = "1.0.37"
uuid = "1.2.2" uuid = "1.2.2"
versionize = "0.1.9" versionize = "0.1.9"
versionize_derive = "0.1.4" versionize_derive = "0.1.4"

View File

@@ -259,7 +259,7 @@ fn create_memory_node(
if last_addr < super::layout::MEM_32BIT_RESERVED_START.raw_value() { if last_addr < super::layout::MEM_32BIT_RESERVED_START.raw_value() {
// Case 1: all RAM is under the hole // Case 1: all RAM is under the hole
let mem_size = last_addr - super::layout::RAM_START.raw_value() + 1; 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")?; let memory_node = fdt.begin_node("memory")?;
fdt.property_string("device_type", "memory")?; fdt.property_string("device_type", "memory")?;
fdt.property_array_u64("reg", &mem_reg_prop)?; fdt.property_array_u64("reg", &mem_reg_prop)?;
@@ -269,7 +269,7 @@ fn create_memory_node(
// Region 1: RAM before the hole // Region 1: RAM before the hole
let mem_size = super::layout::MEM_32BIT_RESERVED_START.raw_value() let mem_size = super::layout::MEM_32BIT_RESERVED_START.raw_value()
- super::layout::RAM_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_name = format!("memory@{:x}", super::layout::RAM_START.raw_value());
let memory_node = fdt.begin_node(&memory_node_name)?; let memory_node = fdt.begin_node(&memory_node_name)?;
fdt.property_string("device_type", "memory")?; fdt.property_string("device_type", "memory")?;
@@ -278,7 +278,10 @@ fn create_memory_node(
// Region 2: RAM after the hole // Region 2: RAM after the hole
let mem_size = last_addr - super::layout::RAM_64BIT_START.raw_value() + 1; 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 = let memory_node_name =
format!("memory@{:x}", super::layout::RAM_64BIT_START.raw_value()); format!("memory@{:x}", super::layout::RAM_64BIT_START.raw_value());
let memory_node = fdt.begin_node(&memory_node_name)?; let memory_node = fdt.begin_node(&memory_node_name)?;
@@ -300,7 +303,7 @@ fn create_chosen_node(
fdt.property_string("bootargs", cmdline)?; fdt.property_string("bootargs", cmdline)?;
if let Some(initrd_config) = initrd { 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; 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-start", initrd_start)?;
fdt.property_u64("linux,initrd-end", initrd_end)?; fdt.property_u64("linux,initrd-end", initrd_end)?;

View File

@@ -19,7 +19,7 @@ use std::collections::HashMap;
use std::convert::TryInto; use std::convert::TryInto;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::{Arc, Mutex}; 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. /// Errors thrown while configuring aarch64 system.
#[derive(Debug)] #[derive(Debug)]
@@ -64,9 +64,9 @@ pub struct EntryPoint {
pub fn configure_vcpu( pub fn configure_vcpu(
vcpu: &Arc<dyn hypervisor::Vcpu>, vcpu: &Arc<dyn hypervisor::Vcpu>,
id: u8, id: u8,
boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>, kernel_entry_point: Option<EntryPoint>,
) -> super::Result<u64> { ) -> 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( vcpu.setup_regs(
id, id,
kernel_entry_point.entry_addr.raw_value(), kernel_entry_point.entry_addr.raw_value(),
@@ -108,7 +108,7 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
// RAM space // RAM space
// Case1: guest memory fits before the gap // 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)); regions.push((layout::RAM_START, size as usize, RegionType::Ram));
// Case2: guest memory extends beyond the gap // Case2: guest memory extends beyond the gap
} else { } else {
@@ -231,7 +231,7 @@ mod tests {
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START) as usize; layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START) as usize;
assert_eq!(6, regions.len()); assert_eq!(6, regions.len());
assert_eq!(layout::RAM_START, regions[3].0); 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::Ram, regions[3].2);
assert_eq!(RegionType::Reserved, regions[5].2); assert_eq!(RegionType::Reserved, regions[5].2);
assert_eq!(RegionType::Ram, regions[4].2); assert_eq!(RegionType::Ram, regions[4].2);

View File

@@ -99,7 +99,7 @@ pub use x86_64::{
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
#[inline(always)] #[inline(always)]
fn pagesize() -> usize { fn pagesize() -> usize {
// SAFETY: Trivially safe // Trivially safe
unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
} }

View File

@@ -125,11 +125,9 @@ struct MemmapTableEntryWrapper(hvm_memmap_table_entry);
#[derive(Copy, Clone, Default)] #[derive(Copy, Clone, Default)]
struct ModlistEntryWrapper(hvm_modlist_entry); 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 {} unsafe impl ByteValued for StartInfoWrapper {}
// SAFETY: data structure only contain a series of integers
unsafe impl ByteValued for MemmapTableEntryWrapper {} unsafe impl ByteValued for MemmapTableEntryWrapper {}
// SAFETY: data structure only contain a series of integers
unsafe impl ByteValued for ModlistEntryWrapper {} unsafe impl ByteValued for ModlistEntryWrapper {}
// This is a workaround to the Rust enforcement specifying that any implementation of a foreign // 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( pub fn generate_common_cpuid(
hypervisor: &Arc<dyn hypervisor::Hypervisor>, hypervisor: Arc<dyn hypervisor::Hypervisor>,
topology: Option<(u8, u8, u8)>, topology: Option<(u8, u8, u8)>,
sgx_epc_sections: Option<Vec<SgxEpcSection>>, sgx_epc_sections: Option<Vec<SgxEpcSection>>,
phys_bits: u8, phys_bits: u8,
kvm_hyperv: bool, kvm_hyperv: bool,
#[cfg(feature = "tdx")] tdx_enabled: bool, #[cfg(feature = "tdx")] tdx_enabled: bool,
) -> super::Result<Vec<CpuIdEntry>> { ) -> 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![ let cpuid_patches = vec![
// Patch tsc deadline timer bit // Patch tsc deadline timer bit
CpuidPatch { CpuidPatch {
@@ -694,7 +675,6 @@ pub fn generate_common_cpuid(
// Copy CPU identification string // Copy CPU identification string
for i in 0x8000_0002..=0x8000_0004 { for i in 0x8000_0002..=0x8000_0004 {
cpuid.retain(|c| c.function != i); cpuid.retain(|c| c.function != i);
// SAFETY: call cpuid with valid leaves
let leaf = unsafe { std::arch::x86_64::__cpuid(i) }; let leaf = unsafe { std::arch::x86_64::__cpuid(i) };
cpuid.push(CpuIdEntry { cpuid.push(CpuIdEntry {
function: i, function: i,
@@ -759,7 +739,8 @@ pub fn generate_common_cpuid(
pub fn configure_vcpu( pub fn configure_vcpu(
vcpu: &Arc<dyn hypervisor::Vcpu>, vcpu: &Arc<dyn hypervisor::Vcpu>,
id: u8, id: u8,
boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>, kernel_entry_point: Option<EntryPoint>,
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
cpuid: Vec<CpuIdEntry>, cpuid: Vec<CpuIdEntry>,
kvm_hyperv: bool, kvm_hyperv: bool,
) -> super::Result<()> { ) -> super::Result<()> {
@@ -776,12 +757,12 @@ pub fn configure_vcpu(
} }
regs::setup_msrs(vcpu).map_err(Error::MsrsConfiguration)?; 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 { if let Some(entry_addr) = kernel_entry_point.entry_addr {
// Safe to unwrap because this method is called after the VM is configured // 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_regs(vcpu, entry_addr.raw_value()).map_err(Error::RegsConfiguration)?;
regs::setup_fpu(vcpu).map_err(Error::FpuConfiguration)?; 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()))?; 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 { pub fn get_host_cpu_phys_bits() -> u8 {
// SAFETY: call cpuid with valid leaves
unsafe { unsafe {
let leaf = x86_64::__cpuid(0x8000_0000); 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 // Get host CPUID for leaf 0x12, subleaf 0x2. This is to retrieve EPC
// properties such as confidentiality and integrity. // properties such as confidentiality and integrity.
// SAFETY: call cpuid with valid leaves
let leaf = unsafe { std::arch::x86_64::__cpuid_count(0x12, 0x2) }; let leaf = unsafe { std::arch::x86_64::__cpuid_count(0x12, 0x2) };
for (i, epc_section) in epc_sections.iter().enumerate() { for (i, epc_section) in epc_sections.iter().enumerate() {

View File

@@ -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. // SAFETY: These `mpspec` wrapper types are only data, reading them from data is a safe initialization.
unsafe impl ByteValued for MpcBusWrapper {} unsafe impl ByteValued for MpcBusWrapper {}
// SAFETY: see above
unsafe impl ByteValued for MpcCpuWrapper {} unsafe impl ByteValued for MpcCpuWrapper {}
// SAFETY: see above
unsafe impl ByteValued for MpcIntsrcWrapper {} unsafe impl ByteValued for MpcIntsrcWrapper {}
// SAFETY: see above
unsafe impl ByteValued for MpcIoapicWrapper {} unsafe impl ByteValued for MpcIoapicWrapper {}
// SAFETY: see above
unsafe impl ByteValued for MpcTableWrapper {} unsafe impl ByteValued for MpcTableWrapper {}
// SAFETY: see above
unsafe impl ByteValued for MpcLintsrcWrapper {} unsafe impl ByteValued for MpcLintsrcWrapper {}
// SAFETY: see above
unsafe impl ByteValued for MpfIntelWrapper {} unsafe impl ByteValued for MpfIntelWrapper {}
#[derive(Debug)] #[derive(Debug)]
@@ -101,7 +95,7 @@ const CPU_FEATURE_APIC: u32 = 0x200;
const CPU_FEATURE_FPU: u32 = 0x001; const CPU_FEATURE_FPU: u32 = 0x001;
fn compute_checksum<T: Copy>(v: &T) -> u8 { 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 v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
let mut checksum: u8 = 0; let mut checksum: u8 = 0;
for i in v_slice.iter() { for i in v_slice.iter() {

View File

@@ -67,7 +67,7 @@ const PCI_SUPPORTED: u64 = 1 << 7;
const IS_VIRTUAL_MACHINE: u8 = 1 << 4; const IS_VIRTUAL_MACHINE: u8 = 1 << 4;
fn compute_checksum<T: Copy>(v: &T) -> u8 { 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 v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
let mut checksum: u8 = 0; let mut checksum: u8 = 0;
for i in v_slice.iter() { for i in v_slice.iter() {
@@ -145,15 +145,11 @@ struct SmbiosEndOfTable {
handle: u16, 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 {} unsafe impl ByteValued for Smbios30Entrypoint {}
// SAFETY: data structure only contain a series of integers
unsafe impl ByteValued for SmbiosBiosInfo {} unsafe impl ByteValued for SmbiosBiosInfo {}
// SAFETY: data structure only contain a series of integers
unsafe impl ByteValued for SmbiosSysInfo {} unsafe impl ByteValued for SmbiosSysInfo {}
// SAFETY: data structure only contain a series of integers
unsafe impl ByteValued for SmbiosOemStrings {} unsafe impl ByteValued for SmbiosOemStrings {}
// SAFETY: data structure only contain a series of integers
unsafe impl ByteValued for SmbiosEndOfTable {} unsafe impl ByteValued for SmbiosEndOfTable {}
fn write_and_incr<T: ByteValued>( fn write_and_incr<T: ByteValued>(

View File

@@ -4,9 +4,7 @@
use crate::GuestMemoryMmap; use crate::GuestMemoryMmap;
use std::fs::File; use std::fs::File;
use std::io::{Read, Seek, SeekFrom}; use std::io::{Read, Seek, SeekFrom};
use std::str::FromStr;
use thiserror::Error; use thiserror::Error;
use uuid::Uuid;
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemoryError}; use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemoryError};
#[derive(Error, Debug)] #[derive(Error, Debug)]
@@ -15,8 +13,6 @@ pub enum TdvfError {
ReadDescriptor(#[source] std::io::Error), ReadDescriptor(#[source] std::io::Error),
#[error("Failed read TDVF descriptor offset: {0}")] #[error("Failed read TDVF descriptor offset: {0}")]
ReadDescriptorOffset(#[source] std::io::Error), ReadDescriptorOffset(#[source] std::io::Error),
#[error("Failed read GUID table: {0}")]
ReadGuidTable(#[source] std::io::Error),
#[error("Invalid descriptor signature")] #[error("Invalid descriptor signature")]
InvalidDescriptorSignature, InvalidDescriptorSignature,
#[error("Invalid descriptor size")] #[error("Invalid descriptor size")]
@@ -25,13 +21,8 @@ pub enum TdvfError {
InvalidDescriptorVersion, InvalidDescriptorVersion,
#[error("Failed to write HOB details to guest memory: {0}")] #[error("Failed to write HOB details to guest memory: {0}")]
GuestMemoryWriteHob(#[source] GuestMemoryError), 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 // TDVF_DESCRIPTOR
#[repr(packed)] #[repr(packed)]
#[derive(Default)] #[derive(Default)]
@@ -73,72 +64,7 @@ impl Default for TdvfSectionType {
} }
} }
fn tdvf_descriptor_offset(file: &mut File) -> Result<(SeekFrom, bool), TdvfError> { pub fn parse_tdvf_sections(file: &mut File) -> Result<Vec<TdvfSection>, 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.
// The 32-bit offset to the TDVF metadata is located 32 bytes from // The 32-bit offset to the TDVF metadata is located 32 bytes from
// the end of the file. // the end of the file.
// See "TDVF Metadata Pointer" in "TDX Virtual Firmware Design Guide // 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]; let mut descriptor_offset: [u8; 4] = [0; 4];
file.read_exact(&mut descriptor_offset) file.read_exact(&mut descriptor_offset)
.map_err(TdvfError::ReadDescriptorOffset)?; .map_err(TdvfError::ReadDescriptorOffset)?;
let descriptor_offset = u32::from_le_bytes(descriptor_offset) as u64;
Ok(( file.seek(SeekFrom::Start(descriptor_offset))
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)
.map_err(TdvfError::ReadDescriptor)?; .map_err(TdvfError::ReadDescriptor)?;
let mut descriptor: TdvfDescriptor = Default::default(); 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 { file.read_exact(unsafe {
std::slice::from_raw_parts_mut( std::slice::from_raw_parts_mut(
&mut descriptor as *mut _ as *mut u8, &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(); let mut sections = Vec::new();
sections.resize_with(descriptor.num_sections as usize, TdvfSection::default); 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 { file.read_exact(unsafe {
std::slice::from_raw_parts_mut( std::slice::from_raw_parts_mut(
sections.as_mut_ptr() as *mut u8, 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)?; .map_err(TdvfError::ReadDescriptor)?;
Ok((sections, guid_found)) Ok(sections)
} }
#[repr(u16)] #[repr(u16)]
@@ -293,17 +211,12 @@ struct TdPayload {
payload_info: PayloadInfo, 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 {} unsafe impl ByteValued for HobHeader {}
// SAFETY: data structure only contain a series of integers
unsafe impl ByteValued for HobHandoffInfoTable {} unsafe impl ByteValued for HobHandoffInfoTable {}
// SAFETY: data structure only contain a series of integers
unsafe impl ByteValued for HobResourceDescriptor {} unsafe impl ByteValued for HobResourceDescriptor {}
// SAFETY: data structure only contain a series of integers
unsafe impl ByteValued for HobGuidType {} unsafe impl ByteValued for HobGuidType {}
// SAFETY: data structure only contain a series of integers
unsafe impl ByteValued for PayloadInfo {} unsafe impl ByteValued for PayloadInfo {}
// SAFETY: data structure only contain a series of integers
unsafe impl ByteValued for TdPayload {} unsafe impl ByteValued for TdPayload {}
pub struct TdHob { pub struct TdHob {
@@ -399,19 +312,12 @@ impl TdHob {
physical_start: u64, physical_start: u64,
resource_length: u64, resource_length: u64,
ram: bool, ram: bool,
guid_found: bool,
) -> Result<(), TdvfError> { ) -> Result<(), TdvfError> {
self.add_resource( self.add_resource(
mem, mem,
physical_start, physical_start,
resource_length, resource_length,
if ram { 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 */ 0 /* EFI_RESOURCE_SYSTEM_MEMORY */
} else { } else {
0x5 /*EFI_RESOURCE_MEMORY_RESERVED */ 0x5 /*EFI_RESOURCE_MEMORY_RESERVED */
@@ -539,7 +445,7 @@ mod tests {
#[ignore] #[ignore]
fn test_parse_tdvf_sections() { fn test_parse_tdvf_sections() {
let mut f = std::fs::File::open("tdvf.fd").unwrap(); 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 { for section in sections {
eprintln!("{section:x?}") eprintln!("{section:x?}")
} }

View File

@@ -8,12 +8,11 @@ edition = "2021"
default = [] default = []
[dependencies] [dependencies]
io-uring = "0.5.11" io-uring = "0.5.9"
libc = "0.2.139" libc = "0.2.138"
log = "0.4.17" log = "0.4.17"
qcow = { path = "../qcow" } qcow = { path = "../qcow" }
smallvec = "1.10.0" thiserror = "1.0.37"
thiserror = "1.0.38"
versionize = "0.1.9" versionize = "0.1.9"
versionize_derive = "0.1.4" versionize_derive = "0.1.4"
vhdx = { path = "../vhdx" } vhdx = { path = "../vhdx" }

View File

@@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use libc::{ioctl, S_IFBLK, S_IFMT}; use libc::{ioctl, S_IFBLK, S_IFMT};
use std::convert::TryInto;
use std::fs::File; use std::fs::File;
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
use thiserror::Error; use thiserror::Error;
@@ -53,21 +54,19 @@ enum BlockSize {
impl DiskTopology { impl DiskTopology {
fn is_block_device(f: &mut File) -> std::io::Result<bool> { fn is_block_device(f: &mut File) -> std::io::Result<bool> {
let mut stat = std::mem::MaybeUninit::<libc::stat>::uninit(); 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()) }; let ret = unsafe { libc::fstat(f.as_raw_fd(), stat.as_mut_ptr()) };
if ret != 0 { if ret != 0 {
return Err(std::io::Error::last_os_error()); 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 }; let is_block = unsafe { (*stat.as_ptr()).st_mode & S_IFMT == S_IFBLK };
Ok(is_block) Ok(is_block)
} }
// libc::ioctl() takes different types on different architectures // 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> { fn query_block_size(f: &mut File, block_size_type: BlockSize) -> std::io::Result<u64> {
let mut block_size = 0; let mut block_size = 0;
// SAFETY: FFI call with correct arguments
let ret = unsafe { let ret = unsafe {
ioctl( ioctl(
f.as_raw_fd(), f.as_raw_fd(),
@@ -76,7 +75,9 @@ impl DiskTopology {
BlockSize::PhysicalBlock => BLKPBSZGET(), BlockSize::PhysicalBlock => BLKPBSZGET(),
BlockSize::MinimumIo => BLKIOMIN(), BlockSize::MinimumIo => BLKIOMIN(),
BlockSize::OptimalIo => BLKIOOPT(), BlockSize::OptimalIo => BLKIOOPT(),
} as _, }
.try_into()
.unwrap(),
&mut block_size, &mut block_size,
) )
}; };
@@ -131,15 +132,15 @@ pub trait AsyncIo: Send {
fn read_vectored( fn read_vectored(
&mut self, &mut self,
offset: libc::off_t, offset: libc::off_t,
iovecs: &[libc::iovec], iovecs: Vec<libc::iovec>,
user_data: u64, user_data: u64,
) -> AsyncIoResult<()>; ) -> AsyncIoResult<()>;
fn write_vectored( fn write_vectored(
&mut self, &mut self,
offset: libc::off_t, offset: libc::off_t,
iovecs: &[libc::iovec], iovecs: Vec<libc::iovec>,
user_data: u64, user_data: u64,
) -> AsyncIoResult<()>; ) -> AsyncIoResult<()>;
fn fsync(&mut self, user_data: Option<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)>;
} }

View File

@@ -64,7 +64,7 @@ impl AsyncIo for FixedVhdAsync {
fn read_vectored( fn read_vectored(
&mut self, &mut self,
offset: libc::off_t, offset: libc::off_t,
iovecs: &[libc::iovec], iovecs: Vec<libc::iovec>,
user_data: u64, user_data: u64,
) -> AsyncIoResult<()> { ) -> AsyncIoResult<()> {
if offset as u64 >= self.size { if offset as u64 >= self.size {
@@ -83,7 +83,7 @@ impl AsyncIo for FixedVhdAsync {
fn write_vectored( fn write_vectored(
&mut self, &mut self,
offset: libc::off_t, offset: libc::off_t,
iovecs: &[libc::iovec], iovecs: Vec<libc::iovec>,
user_data: u64, user_data: u64,
) -> AsyncIoResult<()> { ) -> AsyncIoResult<()> {
if offset as u64 >= self.size { if offset as u64 >= self.size {
@@ -104,7 +104,7 @@ impl AsyncIo for FixedVhdAsync {
self.raw_file_async.fsync(user_data) self.raw_file_async.fsync(user_data)
} }
fn next_completed_request(&mut self) -> Option<(u64, i32)> { fn complete(&mut self) -> Vec<(u64, i32)> {
self.raw_file_async.next_completed_request() self.raw_file_async.complete()
} }
} }

View File

@@ -62,7 +62,7 @@ impl AsyncIo for FixedVhdSync {
fn read_vectored( fn read_vectored(
&mut self, &mut self,
offset: libc::off_t, offset: libc::off_t,
iovecs: &[libc::iovec], iovecs: Vec<libc::iovec>,
user_data: u64, user_data: u64,
) -> AsyncIoResult<()> { ) -> AsyncIoResult<()> {
if offset as u64 >= self.size { if offset as u64 >= self.size {
@@ -81,7 +81,7 @@ impl AsyncIo for FixedVhdSync {
fn write_vectored( fn write_vectored(
&mut self, &mut self,
offset: libc::off_t, offset: libc::off_t,
iovecs: &[libc::iovec], iovecs: Vec<libc::iovec>,
user_data: u64, user_data: u64,
) -> AsyncIoResult<()> { ) -> AsyncIoResult<()> {
if offset as u64 >= self.size { if offset as u64 >= self.size {
@@ -101,7 +101,7 @@ impl AsyncIo for FixedVhdSync {
self.raw_file_sync.fsync(user_data) self.raw_file_sync.fsync(user_data)
} }
fn next_completed_request(&mut self) -> Option<(u64, i32)> { fn complete(&mut self) -> Vec<(u64, i32)> {
self.raw_file_sync.next_completed_request() self.raw_file_sync.complete()
} }
} }

View File

@@ -22,10 +22,8 @@ pub mod vhdx_sync;
use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult}; use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult};
use io_uring::{opcode, IoUring, Probe}; use io_uring::{opcode, IoUring, Probe};
use smallvec::SmallVec;
use std::alloc::{alloc_zeroed, dealloc, Layout}; use std::alloc::{alloc_zeroed, dealloc, Layout};
use std::cmp; use std::cmp;
use std::collections::VecDeque;
use std::convert::TryInto; use std::convert::TryInto;
use std::fs::File; use std::fs::File;
use std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; use std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
@@ -34,7 +32,6 @@ use std::path::Path;
use std::result; use std::result;
use std::sync::Arc; use std::sync::Arc;
use std::sync::MutexGuard; use std::sync::MutexGuard;
use std::time::Instant;
use thiserror::Error; use thiserror::Error;
use versionize::{VersionMap, Versionize, VersionizeResult}; use versionize::{VersionMap, Versionize, VersionizeResult};
use versionize_derive::Versionize; use versionize_derive::Versionize;
@@ -198,11 +195,10 @@ pub struct AlignedOperation {
pub struct Request { pub struct Request {
pub request_type: RequestType, pub request_type: RequestType,
pub sector: u64, pub sector: u64,
pub data_descriptors: SmallVec<[(GuestAddress, u32); 1]>, pub data_descriptors: Vec<(GuestAddress, u32)>,
pub status_addr: GuestAddress, pub status_addr: GuestAddress,
pub writeback: bool, pub writeback: bool,
pub aligned_operations: Vec<AlignedOperation>, pub aligned_operations: Vec<AlignedOperation>,
pub start: Instant,
} }
impl Request { impl Request {
@@ -230,11 +226,10 @@ impl Request {
let mut req = Request { let mut req = Request {
request_type: request_type(desc_chain.memory(), hdr_desc_addr)?, request_type: request_type(desc_chain.memory(), hdr_desc_addr)?,
sector: sector(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), status_addr: GuestAddress(0),
writeback: true, writeback: true,
aligned_operations: Vec::new(), aligned_operations: Vec::new(),
start: Instant::now(),
}; };
let status_desc; let status_desc;
@@ -254,7 +249,6 @@ impl Request {
return Err(Error::DescriptorChainTooShort); return Err(Error::DescriptorChainTooShort);
} }
} else { } else {
req.data_descriptors.reserve_exact(1);
while desc.has_next() { while desc.has_next() {
if desc.is_write_only() && req.request_type == RequestType::Out { if desc.is_write_only() && req.request_type == RequestType::Out {
return Err(Error::UnexpectedWriteOnlyDescriptor); return Err(Error::UnexpectedWriteOnlyDescriptor);
@@ -359,8 +353,7 @@ impl Request {
let request_type = self.request_type; let request_type = self.request_type;
let offset = (sector << SECTOR_SHIFT) as libc::off_t; let offset = (sector << SECTOR_SHIFT) as libc::off_t;
let mut iovecs: SmallVec<[libc::iovec; 1]> = let mut iovecs = Vec::new();
SmallVec::with_capacity(self.data_descriptors.len());
for (data_addr, data_len) in &self.data_descriptors { for (data_addr, data_len) in &self.data_descriptors {
if *data_len == 0 { if *data_len == 0 {
continue; continue;
@@ -388,7 +381,7 @@ impl Request {
let iov_base = if (origin_ptr as u64) % SECTOR_SIZE != 0 { let iov_base = if (origin_ptr as u64) % SECTOR_SIZE != 0 {
let layout = let layout =
Layout::from_size_align(*data_len as usize, SECTOR_SIZE as usize).unwrap(); 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) }; let aligned_ptr = unsafe { alloc_zeroed(layout) };
if aligned_ptr.is_null() { if aligned_ptr.is_null() {
return Err(ExecuteError::TemporaryBufferAllocation( return Err(ExecuteError::TemporaryBufferAllocation(
@@ -399,7 +392,7 @@ impl Request {
// We need to perform the copy beforehand in case we're writing // We need to perform the copy beforehand in case we're writing
// data out. // data out.
if request_type == RequestType::Out { if request_type == RequestType::Out {
// SAFETY: destination buffer has been allocated with // Safe because destination buffer has been allocated with
// the proper size. // the proper size.
unsafe { unsafe {
std::ptr::copy(origin_ptr as *const u8, aligned_ptr, *data_len as usize) 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); .mark_dirty(0, *data_len as usize);
} }
disk_image disk_image
.read_vectored(offset, &iovecs, user_data) .read_vectored(offset, iovecs, user_data)
.map_err(ExecuteError::AsyncRead)?; .map_err(ExecuteError::AsyncRead)?;
} }
RequestType::Out => { RequestType::Out => {
disk_image disk_image
.write_vectored(offset, &iovecs, user_data) .write_vectored(offset, iovecs, user_data)
.map_err(ExecuteError::AsyncWrite)?; .map_err(ExecuteError::AsyncWrite)?;
} }
RequestType::Flush => { RequestType::Flush => {
@@ -474,7 +467,7 @@ impl Request {
// We need to perform the copy after the data has been read inside // We need to perform the copy after the data has been read inside
// the aligned buffer in case we're reading data in. // the aligned buffer in case we're reading data in.
if self.request_type == RequestType::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. // proper size.
unsafe { unsafe {
std::ptr::copy( std::ptr::copy(
@@ -486,7 +479,7 @@ impl Request {
} }
// Free the temporary aligned buffer. // 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 // layout
unsafe { unsafe {
dealloc( dealloc(
@@ -535,9 +528,8 @@ pub struct VirtioBlockGeometry {
pub sectors: u8, 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 {} unsafe impl ByteValued for VirtioBlockConfig {}
// SAFETY: data structure only contain a series of integers
unsafe impl ByteValued for VirtioBlockGeometry {} unsafe impl ByteValued for VirtioBlockGeometry {}
/// Check if io_uring for block device can be used on the current system, as /// Check if io_uring for block device can be used on the current system, as
@@ -596,15 +588,14 @@ where
fn read_vectored_sync( fn read_vectored_sync(
&mut self, &mut self,
offset: libc::off_t, offset: libc::off_t,
iovecs: &[libc::iovec], iovecs: Vec<libc::iovec>,
user_data: u64, user_data: u64,
eventfd: &EventFd, eventfd: &EventFd,
completion_list: &mut VecDeque<(u64, i32)>, completion_list: &mut Vec<(u64, i32)>,
) -> AsyncIoResult<()> { ) -> AsyncIoResult<()> {
// Convert libc::iovec into IoSliceMut // Convert libc::iovec into IoSliceMut
let mut slices = Vec::new(); let mut slices = Vec::new();
for iovec in iovecs.iter() { for iovec in iovecs.iter() {
// SAFETY: on Linux IoSliceMut wraps around libc::iovec
slices.push(IoSliceMut::new(unsafe { std::mem::transmute(*iovec) })); slices.push(IoSliceMut::new(unsafe { std::mem::transmute(*iovec) }));
} }
@@ -620,7 +611,7 @@ where
.map_err(AsyncIoError::ReadVectored)? .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(); eventfd.write(1).unwrap();
Ok(()) Ok(())
@@ -629,15 +620,14 @@ where
fn write_vectored_sync( fn write_vectored_sync(
&mut self, &mut self,
offset: libc::off_t, offset: libc::off_t,
iovecs: &[libc::iovec], iovecs: Vec<libc::iovec>,
user_data: u64, user_data: u64,
eventfd: &EventFd, eventfd: &EventFd,
completion_list: &mut VecDeque<(u64, i32)>, completion_list: &mut Vec<(u64, i32)>,
) -> AsyncIoResult<()> { ) -> AsyncIoResult<()> {
// Convert libc::iovec into IoSlice // Convert libc::iovec into IoSlice
let mut slices = Vec::new(); let mut slices = Vec::new();
for iovec in iovecs.iter() { for iovec in iovecs.iter() {
// SAFETY: on Linux IoSliceMut wraps around libc::iovec
slices.push(IoSlice::new(unsafe { std::mem::transmute(*iovec) })); slices.push(IoSlice::new(unsafe { std::mem::transmute(*iovec) }));
} }
@@ -653,7 +643,7 @@ where
.map_err(AsyncIoError::WriteVectored)? .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(); eventfd.write(1).unwrap();
Ok(()) Ok(())
@@ -663,7 +653,7 @@ where
&mut self, &mut self,
user_data: Option<u64>, user_data: Option<u64>,
eventfd: &EventFd, eventfd: &EventFd,
completion_list: &mut VecDeque<(u64, i32)>, completion_list: &mut Vec<(u64, i32)>,
) -> AsyncIoResult<()> { ) -> AsyncIoResult<()> {
let result: i32 = { let result: i32 = {
let mut file = self.file(); let mut file = self.file();
@@ -675,7 +665,7 @@ where
}; };
if let Some(user_data) = user_data { if let Some(user_data) = user_data {
completion_list.push_back((user_data, result)); completion_list.push((user_data, result));
eventfd.write(1).unwrap(); eventfd.write(1).unwrap();
} }

View File

@@ -5,7 +5,6 @@
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult}; use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult};
use crate::AsyncAdaptor; use crate::AsyncAdaptor;
use qcow::{QcowFile, RawFile, Result as QcowResult}; use qcow::{QcowFile, RawFile, Result as QcowResult};
use std::collections::VecDeque;
use std::fs::File; use std::fs::File;
use std::io::{Seek, SeekFrom}; use std::io::{Seek, SeekFrom};
use std::sync::{Arc, Mutex, MutexGuard}; use std::sync::{Arc, Mutex, MutexGuard};
@@ -38,7 +37,7 @@ impl DiskFile for QcowDiskSync {
pub struct QcowSync { pub struct QcowSync {
qcow_file: Arc<Mutex<QcowFile>>, qcow_file: Arc<Mutex<QcowFile>>,
eventfd: EventFd, eventfd: EventFd,
completion_list: VecDeque<(u64, i32)>, completion_list: Vec<(u64, i32)>,
} }
impl QcowSync { impl QcowSync {
@@ -47,7 +46,7 @@ impl QcowSync {
qcow_file, qcow_file,
eventfd: EventFd::new(libc::EFD_NONBLOCK) eventfd: EventFd::new(libc::EFD_NONBLOCK)
.expect("Failed creating EventFd for QcowSync"), .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( fn read_vectored(
&mut self, &mut self,
offset: libc::off_t, offset: libc::off_t,
iovecs: &[libc::iovec], iovecs: Vec<libc::iovec>,
user_data: u64, user_data: u64,
) -> AsyncIoResult<()> { ) -> AsyncIoResult<()> {
self.qcow_file.read_vectored_sync( self.qcow_file.read_vectored_sync(
@@ -81,7 +80,7 @@ impl AsyncIo for QcowSync {
fn write_vectored( fn write_vectored(
&mut self, &mut self,
offset: libc::off_t, offset: libc::off_t,
iovecs: &[libc::iovec], iovecs: Vec<libc::iovec>,
user_data: u64, user_data: u64,
) -> AsyncIoResult<()> { ) -> AsyncIoResult<()> {
self.qcow_file.write_vectored_sync( self.qcow_file.write_vectored_sync(
@@ -98,7 +97,7 @@ impl AsyncIo for QcowSync {
.fsync_sync(user_data, &self.eventfd, &mut self.completion_list) .fsync_sync(user_data, &self.eventfd, &mut self.completion_list)
} }
fn next_completed_request(&mut self) -> Option<(u64, i32)> { fn complete(&mut self) -> Vec<(u64, i32)> {
self.completion_list.pop_front() self.completion_list.drain(..).collect()
} }
} }

View File

@@ -76,12 +76,12 @@ impl AsyncIo for RawFileAsync {
fn read_vectored( fn read_vectored(
&mut self, &mut self,
offset: libc::off_t, offset: libc::off_t,
iovecs: &[libc::iovec], iovecs: Vec<libc::iovec>,
user_data: u64, user_data: u64,
) -> AsyncIoResult<()> { ) -> AsyncIoResult<()> {
let (submitter, mut sq, _) = self.io_uring.split(); 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. // relied on vm-memory to provide the buffer address.
let _ = unsafe { let _ = unsafe {
sq.push( sq.push(
@@ -104,12 +104,12 @@ impl AsyncIo for RawFileAsync {
fn write_vectored( fn write_vectored(
&mut self, &mut self,
offset: libc::off_t, offset: libc::off_t,
iovecs: &[libc::iovec], iovecs: Vec<libc::iovec>,
user_data: u64, user_data: u64,
) -> AsyncIoResult<()> { ) -> AsyncIoResult<()> {
let (submitter, mut sq, _) = self.io_uring.split(); 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. // relied on vm-memory to provide the buffer address.
let _ = unsafe { let _ = unsafe {
sq.push( sq.push(
@@ -133,7 +133,7 @@ impl AsyncIo for RawFileAsync {
if let Some(user_data) = user_data { if let Some(user_data) = user_data {
let (submitter, mut sq, _) = self.io_uring.split(); 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 { let _ = unsafe {
sq.push( sq.push(
&opcode::Fsync::new(types::Fd(self.fd)) &opcode::Fsync::new(types::Fd(self.fd))
@@ -148,17 +148,20 @@ impl AsyncIo for RawFileAsync {
sq.sync(); sq.sync();
submitter.submit().map_err(AsyncIoError::Fsync)?; submitter.submit().map_err(AsyncIoError::Fsync)?;
} else { } else {
// SAFETY: FFI call with a valid fd
unsafe { libc::fsync(self.fd) }; unsafe { libc::fsync(self.fd) };
} }
Ok(()) Ok(())
} }
fn next_completed_request(&mut self) -> Option<(u64, i32)> { fn complete(&mut self) -> Vec<(u64, i32)> {
self.io_uring let mut completion_list = Vec::new();
.completion()
.next() let cq = self.io_uring.completion();
.map(|entry| (entry.user_data(), entry.result())) for cq_entry in cq {
completion_list.push((cq_entry.user_data(), cq_entry.result()));
}
completion_list
} }
} }

View File

@@ -5,7 +5,6 @@
use crate::async_io::{ use crate::async_io::{
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult, DiskTopology, AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult, DiskTopology,
}; };
use std::collections::VecDeque;
use std::fs::File; use std::fs::File;
use std::io::{Seek, SeekFrom}; use std::io::{Seek, SeekFrom};
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
@@ -45,7 +44,7 @@ impl DiskFile for RawFileDiskSync {
pub struct RawFileSync { pub struct RawFileSync {
fd: RawFd, fd: RawFd,
eventfd: EventFd, eventfd: EventFd,
completion_list: VecDeque<(u64, i32)>, completion_list: Vec<(u64, i32)>,
} }
impl RawFileSync { impl RawFileSync {
@@ -53,7 +52,7 @@ impl RawFileSync {
RawFileSync { RawFileSync {
fd, fd,
eventfd: EventFd::new(libc::EFD_NONBLOCK).expect("Failed creating EventFd for RawFile"), 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( fn read_vectored(
&mut self, &mut self,
offset: libc::off_t, offset: libc::off_t,
iovecs: &[libc::iovec], iovecs: Vec<libc::iovec>,
user_data: u64, user_data: u64,
) -> AsyncIoResult<()> { ) -> AsyncIoResult<()> {
// SAFETY: FFI call with valid arguments
let result = unsafe { let result = unsafe {
libc::preadv( libc::preadv(
self.fd as libc::c_int, self.fd as libc::c_int,
@@ -82,7 +80,7 @@ impl AsyncIo for RawFileSync {
return Err(AsyncIoError::ReadVectored(std::io::Error::last_os_error())); 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(); self.eventfd.write(1).unwrap();
Ok(()) Ok(())
@@ -91,10 +89,9 @@ impl AsyncIo for RawFileSync {
fn write_vectored( fn write_vectored(
&mut self, &mut self,
offset: libc::off_t, offset: libc::off_t,
iovecs: &[libc::iovec], iovecs: Vec<libc::iovec>,
user_data: u64, user_data: u64,
) -> AsyncIoResult<()> { ) -> AsyncIoResult<()> {
// SAFETY: FFI call with valid arguments
let result = unsafe { let result = unsafe {
libc::pwritev( libc::pwritev(
self.fd as libc::c_int, self.fd as libc::c_int,
@@ -107,28 +104,27 @@ impl AsyncIo for RawFileSync {
return Err(AsyncIoError::WriteVectored(std::io::Error::last_os_error())); 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(); self.eventfd.write(1).unwrap();
Ok(()) Ok(())
} }
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> { fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
// SAFETY: FFI call
let result = unsafe { libc::fsync(self.fd as libc::c_int) }; let result = unsafe { libc::fsync(self.fd as libc::c_int) };
if result < 0 { if result < 0 {
return Err(AsyncIoError::Fsync(std::io::Error::last_os_error())); return Err(AsyncIoError::Fsync(std::io::Error::last_os_error()));
} }
if let Some(user_data) = user_data { 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(); self.eventfd.write(1).unwrap();
} }
Ok(()) Ok(())
} }
fn next_completed_request(&mut self) -> Option<(u64, i32)> { fn complete(&mut self) -> Vec<(u64, i32)> {
self.completion_list.pop_front() self.completion_list.drain(..).collect()
} }
} }

View File

@@ -4,7 +4,6 @@
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult}; use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult};
use crate::AsyncAdaptor; use crate::AsyncAdaptor;
use std::collections::VecDeque;
use std::fs::File; use std::fs::File;
use std::sync::{Arc, Mutex, MutexGuard}; use std::sync::{Arc, Mutex, MutexGuard};
use vhdx::vhdx::{Result as VhdxResult, Vhdx}; use vhdx::vhdx::{Result as VhdxResult, Vhdx};
@@ -38,7 +37,7 @@ impl DiskFile for VhdxDiskSync {
pub struct VhdxSync { pub struct VhdxSync {
vhdx_file: Arc<Mutex<Vhdx>>, vhdx_file: Arc<Mutex<Vhdx>>,
eventfd: EventFd, eventfd: EventFd,
completion_list: VecDeque<(u64, i32)>, completion_list: Vec<(u64, i32)>,
} }
impl VhdxSync { impl VhdxSync {
@@ -46,7 +45,7 @@ impl VhdxSync {
Ok(VhdxSync { Ok(VhdxSync {
vhdx_file, vhdx_file,
eventfd: EventFd::new(libc::EFD_NONBLOCK)?, 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( fn read_vectored(
&mut self, &mut self,
offset: libc::off_t, offset: libc::off_t,
iovecs: &[libc::iovec], iovecs: Vec<libc::iovec>,
user_data: u64, user_data: u64,
) -> AsyncIoResult<()> { ) -> AsyncIoResult<()> {
self.vhdx_file.read_vectored_sync( self.vhdx_file.read_vectored_sync(
@@ -80,7 +79,7 @@ impl AsyncIo for VhdxSync {
fn write_vectored( fn write_vectored(
&mut self, &mut self,
offset: libc::off_t, offset: libc::off_t,
iovecs: &[libc::iovec], iovecs: Vec<libc::iovec>,
user_data: u64, user_data: u64,
) -> AsyncIoResult<()> { ) -> AsyncIoResult<()> {
self.vhdx_file.write_vectored_sync( self.vhdx_file.write_vectored_sync(
@@ -97,7 +96,7 @@ impl AsyncIo for VhdxSync {
.fsync_sync(user_data, &self.eventfd, &mut self.completion_list) .fsync_sync(user_data, &self.eventfd, &mut self.completion_list)
} }
fn next_completed_request(&mut self) -> Option<(u64, i32)> { fn complete(&mut self) -> Vec<(u64, i32)> {
self.completion_list.pop_front() self.completion_list.drain(..).collect()
} }
} }

View File

@@ -3,10 +3,13 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// //
#[macro_use(crate_version)]
extern crate clap;
use std::process::Command; use std::process::Command;
fn main() { 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 let Ok(git_out) = Command::new("git").args(["describe", "--dirty"]).output() {
if git_out.status.success() { if git_out.status.success() {

View File

@@ -6,15 +6,15 @@ edition = "2021"
[dependencies] [dependencies]
acpi_tables = { path = "../acpi_tables" } acpi_tables = { path = "../acpi_tables" }
anyhow = "1.0.68" anyhow = "1.0.66"
arch = { path = "../arch" } arch = { path = "../arch" }
bitflags = "1.3.2" bitflags = "1.3.2"
byteorder = "1.4.3" byteorder = "1.4.3"
hypervisor = { path = "../hypervisor" } hypervisor = { path = "../hypervisor" }
libc = "0.2.139" libc = "0.2.138"
log = "0.4.17" log = "0.4.17"
phf = { version = "0.11.1", features = ["macros"] } phf = { version = "0.11.1", features = ["macros"] }
thiserror = "1.0.38" thiserror = "1.0.37"
tpm = { path = "../tpm" } tpm = { path = "../tpm" }
versionize = "0.1.9" versionize = "0.1.9"
versionize_derive = "0.1.4" versionize_derive = "0.1.4"

View File

@@ -8,7 +8,7 @@ use anyhow::anyhow;
use arch::layout; use arch::layout;
use hypervisor::{ use hypervisor::{
arch::aarch64::gic::{Vgic, VgicConfig}, arch::aarch64::gic::{Vgic, VgicConfig},
CpuState, GicState, CpuState,
}; };
use std::result; use std::result;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@@ -25,7 +25,6 @@ type Result<T> = result::Result<T, Error>;
// Reserve 32 IRQs for legacy devices. // Reserve 32 IRQs for legacy devices.
pub const IRQ_LEGACY_BASE: usize = layout::IRQ_BASE as usize; pub const IRQ_LEGACY_BASE: usize = layout::IRQ_BASE as usize;
pub const IRQ_LEGACY_COUNT: usize = 32; pub const IRQ_LEGACY_COUNT: usize = 32;
pub const GIC_SNAPSHOT_ID: &str = "gic-v3-its";
// Gic (Generic Interupt Controller) struct provides all the functionality of a // Gic (Generic Interupt Controller) struct provides all the functionality of a
// GIC device. It wraps a hypervisor-emulated GIC device (Vgic) provided by the // GIC device. It wraps a hypervisor-emulated GIC device (Vgic) provided by the
@@ -40,9 +39,8 @@ pub struct Gic {
impl Gic { impl Gic {
pub fn new( pub fn new(
vcpu_count: u8, _vcpu_count: u8,
interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>, interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
vm: Arc<dyn hypervisor::Vm>,
) -> Result<Gic> { ) -> Result<Gic> {
let interrupt_source_group = interrupt_manager let interrupt_source_group = interrupt_manager
.create_group(MsiIrqGroupConfig { .create_group(MsiIrqGroupConfig {
@@ -51,34 +49,45 @@ impl Gic {
}) })
.map_err(Error::CreateInterruptSourceGroup)?; .map_err(Error::CreateInterruptSourceGroup)?;
let vgic = vm Ok(Gic {
.create_vgic(Gic::create_default_config(vcpu_count as u64))
.map_err(Error::CreateGic)?;
let gic = Gic {
interrupt_source_group, interrupt_source_group,
vgic: Some(vgic), vgic: None,
}; })
gic.enable()?;
Ok(gic)
} }
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, &mut self,
state: Option<GicState>, vm: &Arc<dyn hypervisor::Vm>,
saved_vcpu_states: &[CpuState], config: VgicConfig,
) -> Result<()> { ) -> Result<Arc<Mutex<dyn Vgic>>> {
self.set_gicr_typers(saved_vcpu_states); let vgic = vm.create_vgic(config).map_err(Error::CreateGic)?;
self.vgic self.vgic = Some(vgic.clone());
.clone() Ok(vgic.clone())
.unwrap()
.lock()
.unwrap()
.set_state(&state.unwrap())
.map_err(Error::RestoreGic)
} }
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<()> { fn enable(&self) -> Result<()> {
// Set irqfd for legacy interrupts // Set irqfd for legacy interrupts
self.interrupt_source_group self.interrupt_source_group
@@ -104,33 +113,6 @@ impl Gic {
Ok(()) 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 // This should be called anytime an interrupt needs to be injected into the
// running guest. // running guest.
fn service_irq(&mut self, irq: usize) -> Result<()> { 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 { impl Snapshottable for Gic {
fn id(&self) -> String { 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> { fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
let vgic = self.vgic.as_ref().unwrap().clone(); let vgic = self.vgic.as_ref().unwrap().clone();
let state = vgic.lock().unwrap().state().unwrap(); 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(())
} }
} }

View File

@@ -24,12 +24,8 @@ pub enum Error {
UpdateInterrupt(io::Error), UpdateInterrupt(io::Error),
/// Failed enabling the interrupt. /// Failed enabling the interrupt.
EnableInterrupt(io::Error), EnableInterrupt(io::Error),
#[cfg(target_arch = "aarch64")]
/// Failed creating GIC device. /// Failed creating GIC device.
CreateGic(hypervisor::HypervisorVmError), CreateGic(hypervisor::HypervisorVmError),
#[cfg(target_arch = "aarch64")]
/// Failed restoring GIC device.
RestoreGic(hypervisor::arch::aarch64::gic::Error),
} }
type Result<T> = result::Result<T, Error>; type Result<T> = result::Result<T, Error>;
@@ -59,6 +55,8 @@ pub struct MsiMessage {
// IOAPIC (X86) or GIC (Arm). // IOAPIC (X86) or GIC (Arm).
pub trait InterruptController: Send { pub trait InterruptController: Send {
fn service_irq(&mut self, irq: usize) -> Result<()>; fn service_irq(&mut self, irq: usize) -> Result<()>;
#[cfg(target_arch = "aarch64")]
fn enable(&self) -> Result<()>;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
fn end_of_interrupt(&mut self, vec: u8); fn end_of_interrupt(&mut self, vec: u8);
fn notifier(&self, irq: usize) -> Option<EventFd>; fn notifier(&self, irq: usize) -> Option<EventFd>;

View File

@@ -10,6 +10,7 @@
// See https://pdos.csail.mit.edu/6.828/2016/readings/ia32/ioapic.pdf for a specification. // See https://pdos.csail.mit.edu/6.828/2016/readings/ia32/ioapic.pdf for a specification.
use super::interrupt_controller::{Error, InterruptController}; use super::interrupt_controller::{Error, InterruptController};
use anyhow::anyhow;
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
use std::result; use std::result;
use std::sync::{Arc, Barrier}; use std::sync::{Arc, Barrier};
@@ -193,7 +194,6 @@ impl Ioapic {
id: String, id: String,
apic_address: GuestAddress, apic_address: GuestAddress,
interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>, interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
state: Option<IoapicState>,
) -> Result<Ioapic> { ) -> Result<Ioapic> {
let interrupt_source_group = interrupt_manager let interrupt_source_group = interrupt_manager
.create_group(MsiIrqGroupConfig { .create_group(MsiIrqGroupConfig {
@@ -202,47 +202,17 @@ impl Ioapic {
}) })
.map_err(Error::CreateInterruptSourceGroup)?; .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 // The IOAPIC is created with entries already masked. The guest will be
// in charge of unmasking them if/when necessary. // in charge of unmasking them if/when necessary.
let ioapic = Ioapic { Ok(Ioapic {
id, id,
id_reg, id_reg: 0,
reg_sel, reg_sel: 0,
reg_entries, reg_entries: [0x10000; NUM_IOAPIC_PINS],
used_entries, used_entries: [false; NUM_IOAPIC_PINS],
apic_address, apic_address,
interrupt_source_group, 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) { 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<()> { fn update_entry(&self, irq: usize) -> Result<()> {
let entry = self.reg_entries[irq]; let entry = self.reg_entries[irq];
@@ -438,7 +423,18 @@ impl Snapshottable for Ioapic {
} }
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 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
))
})
} }
} }

View File

@@ -97,7 +97,7 @@ impl BusDevice for Cmos {
let day; let day;
let month; let month;
let year; 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 // 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. // the tm and timespec struct because it contains only plain data.
let update_in_progress = unsafe { let update_in_progress = unsafe {

View File

@@ -106,39 +106,18 @@ impl VersionMapped for GpioState {}
impl Gpio { impl Gpio {
/// Constructs an PL061 GPIO device. /// Constructs an PL061 GPIO device.
pub fn new( pub fn new(id: String, interrupt: Arc<dyn InterruptSourceGroup>) -> Self {
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)
};
Self { Self {
id, id,
data, data: 0,
old_in_data, old_in_data: 0,
dir, dir: 0,
isense, isense: 0,
ibe, ibe: 0,
iev, iev: 0,
im, im: 0,
istate, istate: 0,
afsel, afsel: 0,
interrupt, 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) { fn pl061_internal_update(&mut self) {
// FIXME: // FIXME:
// Missing Output Interrupt Emulation. // Missing Output Interrupt Emulation.
// Input Edging 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 { if changed > 0 {
self.old_in_data = self.data; self.old_in_data = self.data;
for i in 0..N_GPIOS { for i in 0..N_GPIOS {
@@ -328,7 +319,12 @@ impl Snapshottable for Gpio {
} }
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 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( let mut gpio = Gpio::new(
String::from(GPIO_NAME), String::from(GPIO_NAME),
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())), Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
None,
); );
let mut data = [0; 4]; let mut data = [0; 4];

View File

@@ -61,10 +61,12 @@ pub enum ClockType {
/// Equivalent to `libc::CLOCK_MONOTONIC`. /// Equivalent to `libc::CLOCK_MONOTONIC`.
Monotonic, Monotonic,
/// Equivalent to `libc::CLOCK_REALTIME`. /// Equivalent to `libc::CLOCK_REALTIME`.
#[allow(dead_code)]
Real, Real,
/// Equivalent to `libc::CLOCK_PROCESS_CPUTIME_ID`. /// Equivalent to `libc::CLOCK_PROCESS_CPUTIME_ID`.
ProcessCpu, ProcessCpu,
/// Equivalent to `libc::CLOCK_THREAD_CPUTIME_ID`. /// Equivalent to `libc::CLOCK_THREAD_CPUTIME_ID`.
#[allow(dead_code)]
ThreadCpu, ThreadCpu,
} }
@@ -99,7 +101,7 @@ pub struct LocalTime {
impl LocalTime { impl LocalTime {
/// Returns the [LocalTime](struct.LocalTime.html) structure for the calling moment. /// Returns the [LocalTime](struct.LocalTime.html) structure for the calling moment.
#[cfg(test)] #[allow(dead_code)]
pub fn now() -> LocalTime { pub fn now() -> LocalTime {
let mut timespec = libc::timespec { let mut timespec = libc::timespec {
tv_sec: 0, tv_sec: 0,
@@ -119,7 +121,7 @@ impl LocalTime {
tm_zone: std::ptr::null(), tm_zone: std::ptr::null(),
}; };
// SAFETY: the parameters are valid. // Safe because the parameters are valid.
unsafe { unsafe {
libc::clock_gettime(libc::CLOCK_REALTIME, &mut timespec); libc::clock_gettime(libc::CLOCK_REALTIME, &mut timespec);
libc::localtime_r(&timespec.tv_sec, &mut tm); libc::localtime_r(&timespec.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. /// Returns a timestamp in nanoseconds based on the provided clock type.
/// ///
/// # Arguments /// # Arguments
@@ -181,7 +199,7 @@ pub fn get_time(clock_type: ClockType) -> u64 {
tv_sec: 0, tv_sec: 0,
tv_nsec: 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) }; 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) 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_name: ident, $write_fn_name: ident, $read_fn_name: ident, $is_be: expr, $data_type: ty) => {
#[test] #[test]
fn $test_name() { fn $test_name() {
#[allow(overflowing_literals)]
let test_cases = [ let test_cases = [
( (
0x0123_4567_89AB_CDEF as u64, 0x0123_4567_89AB_CDEF as u64,

View File

@@ -63,6 +63,7 @@ pub struct Serial {
id: String, id: String,
interrupt_enable: u8, interrupt_enable: u8,
interrupt_identification: u8, interrupt_identification: u8,
interrupt: Arc<dyn InterruptSourceGroup>,
line_control: u8, line_control: u8,
line_status: u8, line_status: u8,
modem_control: u8, modem_control: u8,
@@ -70,7 +71,6 @@ pub struct Serial {
scratch: u8, scratch: u8,
baud_divisor: u16, baud_divisor: u16,
in_buffer: VecDeque<u8>, in_buffer: VecDeque<u8>,
interrupt: Arc<dyn InterruptSourceGroup>,
out: Option<Box<dyn io::Write + Send>>, out: Option<Box<dyn io::Write + Send>>,
} }
@@ -93,56 +93,19 @@ impl Serial {
id: String, id: String,
interrupt: Arc<dyn InterruptSourceGroup>, interrupt: Arc<dyn InterruptSourceGroup>,
out: Option<Box<dyn io::Write + Send>>, out: Option<Box<dyn io::Write + Send>>,
state: Option<SerialState>,
) -> Serial { ) -> 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 { Serial {
id, id,
interrupt_enable, interrupt_enable: 0,
interrupt_identification, interrupt_identification: DEFAULT_INTERRUPT_IDENTIFICATION,
line_control,
line_status,
modem_control,
modem_status,
scratch,
baud_divisor,
in_buffer,
interrupt, 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, out,
} }
} }
@@ -152,18 +115,13 @@ impl Serial {
id: String, id: String,
interrupt: Arc<dyn InterruptSourceGroup>, interrupt: Arc<dyn InterruptSourceGroup>,
out: Box<dyn io::Write + Send>, out: Box<dyn io::Write + Send>,
state: Option<SerialState>,
) -> Serial { ) -> Serial {
Self::new(id, interrupt, Some(out), state) Self::new(id, interrupt, Some(out))
} }
/// Constructs a Serial port with no connected output. /// Constructs a Serial port with no connected output.
pub fn new_sink( pub fn new_sink(id: String, interrupt: Arc<dyn InterruptSourceGroup>) -> Serial {
id: String, Self::new(id, interrupt, None)
interrupt: Arc<dyn InterruptSourceGroup>,
state: Option<SerialState>,
) -> Serial {
Self::new(id, interrupt, None, state)
} }
pub fn set_out(&mut self, out: Box<dyn io::Write + Send>) { 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(), 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 { impl BusDevice for Serial {
@@ -334,7 +304,12 @@ impl Snapshottable for Serial {
} }
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 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), String::from(SERIAL_NAME),
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())), Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
Box::new(serial_out.clone()), Box::new(serial_out.clone()),
None,
); );
serial.write(0, DATA as u64, &[b'x', b'y']); serial.write(0, DATA as u64, &[b'x', b'y']);
@@ -430,7 +404,6 @@ mod tests {
String::from(SERIAL_NAME), String::from(SERIAL_NAME),
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())), Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
Box::new(serial_out), Box::new(serial_out),
None,
); );
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd // 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( let mut serial = Serial::new_sink(
String::from(SERIAL_NAME), String::from(SERIAL_NAME),
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())), 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 // 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( let mut serial = Serial::new_sink(
String::from(SERIAL_NAME), String::from(SERIAL_NAME),
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())), Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
None,
); );
serial.write(0, LCR as u64, &[LCR_DLAB_BIT]); serial.write(0, LCR as u64, &[LCR_DLAB_BIT]);
@@ -512,7 +483,6 @@ mod tests {
let mut serial = Serial::new_sink( let mut serial = Serial::new_sink(
String::from(SERIAL_NAME), String::from(SERIAL_NAME),
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())), Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
None,
); );
serial.write(0, MCR as u64, &[MCR_LOOP_BIT]); serial.write(0, MCR as u64, &[MCR_LOOP_BIT]);
@@ -539,7 +509,6 @@ mod tests {
let mut serial = Serial::new_sink( let mut serial = Serial::new_sink(
String::from(SERIAL_NAME), String::from(SERIAL_NAME),
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())), Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
None,
); );
serial.write(0, SCR as u64, &[0x12]); serial.write(0, SCR as u64, &[0x12]);

View File

@@ -122,79 +122,24 @@ impl Pl011 {
irq: Arc<dyn InterruptSourceGroup>, irq: Arc<dyn InterruptSourceGroup>,
out: Option<Box<dyn io::Write + Send>>, out: Option<Box<dyn io::Write + Send>>,
timestamp: Instant, timestamp: Instant,
state: Option<Pl011State>,
) -> Self { ) -> 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 { Self {
id, id,
flags, flags: 0x90u32,
lcr, lcr: 0u32,
rsr, rsr: 0u32,
cr, cr: 0x300u32,
dmacr, dmacr: 0u32,
debug, debug: 0u32,
int_enabled, int_enabled: 0u32,
int_level, int_level: 0u32,
read_fifo, read_fifo: VecDeque::new(),
ilpr, ilpr: 0u32,
ibrd, ibrd: 0u32,
fbrd, fbrd: 0u32,
ifl, ifl: 0x12u32,
read_count, read_count: 0u32,
read_trigger, read_trigger: 1u32,
irq, irq,
out, out,
timestamp, 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 /// 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<()> { pub fn queue_input_bytes(&mut self, c: &[u8]) -> vmm_sys_util::errno::Result<()> {
self.read_fifo.extend(c); self.read_fifo.extend(c);
@@ -454,7 +417,12 @@ impl Snapshottable for Pl011 {
} }
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 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())), Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
Some(Box::new(pl011_out.clone())), Some(Box::new(pl011_out.clone())),
Instant::now(), Instant::now(),
None,
); );
pl011.write(0, UARTDR, &[b'x', b'y']); pl011.write(0, UARTDR as u64, &[b'x', b'y']);
pl011.write(0, UARTDR, &[b'a']); pl011.write(0, UARTDR as u64, &[b'a']);
pl011.write(0, UARTDR, &[b'b']); pl011.write(0, UARTDR as u64, &[b'b']);
pl011.write(0, UARTDR, &[b'c']); pl011.write(0, UARTDR as u64, &[b'c']);
assert_eq!( assert_eq!(
pl011_out.buf.lock().unwrap().as_slice(), pl011_out.buf.lock().unwrap().as_slice(),
&[b'x', b'a', b'b', b'c'] &[b'x', b'a', b'b', b'c']
@@ -552,7 +519,6 @@ mod tests {
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())), Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
Some(Box::new(pl011_out)), Some(Box::new(pl011_out)),
Instant::now(), Instant::now(),
None,
); );
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd // 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); assert_eq!(intr_evt.read().unwrap(), 2);
let mut data = [0u8]; let mut data = [0u8];
pl011.read(0, UARTDR, &mut data); pl011.read(0, UARTDR as u64, &mut data);
assert_eq!(data[0], b'a'); 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'); 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'); assert_eq!(data[0], b'c');
} }
} }

View File

@@ -33,9 +33,11 @@ bitflags! {
} }
} }
#[allow(unused_macros)]
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
macro_rules! generate_read_fn { macro_rules! generate_read_fn {
($fn_name: ident, $data_type: ty, $byte_type: ty, $type_size: expr, $endian_type: ident) => { ($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 { pub fn $fn_name(input: &[$byte_type]) -> $data_type {
assert!($type_size == std::mem::size_of::<$data_type>()); assert!($type_size == std::mem::size_of::<$data_type>());
let mut array = [0u8; $type_size]; let mut array = [0u8; $type_size];
@@ -47,9 +49,11 @@ macro_rules! generate_read_fn {
}; };
} }
#[allow(unused_macros)]
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
macro_rules! generate_write_fn { macro_rules! generate_write_fn {
($fn_name: ident, $data_type: ty, $byte_type: ty, $endian_type: ident) => { ($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) { pub fn $fn_name(buf: &mut [$byte_type], n: $data_type) {
for (byte, read) in buf for (byte, read) in buf
.iter_mut() .iter_mut()

152
docs/arm64.md Normal file
View 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
```

View File

@@ -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. distributions please replace the package manager and package name.
```shell ```shell
# Install basic packages needed. For a package list targeting for more # Install build-essential, git, and qemu-utils
# functionalities for example the test, please see resources/Dockerfile. $ sudo apt install git build-essential qemu-utils
$ sudo apt-get update
$ sudo apt install git build-essential m4 bison flex uuid-dev qemu-utils
# Install rust tool chain # Install rust tool chain
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh $ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# If you want to build statically linked binary please add musl target # 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 x86_64-unknown-linux-musl
$ rustup target add aarch64-unknown-linux-musl # AArch64
``` ```
## Clone and build ## Clone and build
@@ -49,8 +46,7 @@ $ cargo build --release
$ sudo setcap cap_net_admin+ep ./target/release/cloud-hypervisor $ sudo setcap cap_net_admin+ep ./target/release/cloud-hypervisor
# If you want to build statically linked binary # If you want to build statically linked binary
$ cargo build --release --target=x86_64-unknown-linux-musl --all # x86-64 $ cargo build --release --target=x86_64-unknown-linux-musl --all
$ cargo build --release --target=aarch64-unknown-linux-musl --all # AArch64
$ popd $ popd
``` ```

View File

@@ -88,7 +88,7 @@ Ubuntu distributions.
```bash ```bash
apt update apt update
apt install fio iperf iperf3 socat stress cpuid apt install fio iperf iperf3 socat stress
``` ```
### Remove counterproductive packages ### 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. `/dev/vda1` with the appropriate partition number.
Update all references to the previous image name to the new one. 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
```

View File

@@ -2,19 +2,17 @@
Cloud Hypervisor supports UEFI boot through the utilization of the EDK II based UEFI firmware. 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. 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. The commands below will compile an OVMF firmware suitable for Cloud Hypervisor.
```shell ```shell
sudo apt-get update sudo apt-get update
sudo apt-get install uuid-dev nasm iasl build-essential python3-distutils git 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 cd edk2
. edksetup.sh . edksetup.sh
git submodule update --init 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. 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 ## 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. 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 # Links

View File

@@ -5,6 +5,6 @@ authors = ["The Cloud Hypervisor Authors"]
edition = "2021" edition = "2021"
[dependencies] [dependencies]
libc = "0.2.139" libc = "0.2.138"
serde = { version = "1.0.151", features = ["rc", "derive"] } serde = { version = "1.0.150", features = ["rc", "derive"] }
serde_json = "1.0.89" serde_json = "1.0.89"

View File

@@ -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 /// This function must only be called once from the main process before any threads
/// are created to avoid race conditions /// are created to avoid race conditions
pub fn set_monitor(file: File) -> Result<(), std::io::Error> { 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() }); assert!(unsafe { MONITOR.is_none() });
let fd = file.as_raw_fd(); let fd = file.as_raw_fd();
// SAFETY: FFI call to configure the fd
let ret = unsafe { let ret = unsafe {
let mut flags = libc::fcntl(fd, libc::F_GETFL); let mut flags = libc::fcntl(fd, libc::F_GETFL);
flags |= libc::O_NONBLOCK; flags |= libc::O_NONBLOCK;
@@ -28,7 +26,6 @@ pub fn set_monitor(file: File) -> Result<(), std::io::Error> {
if ret < 0 { if ret < 0 {
return Err(std::io::Error::last_os_error()); return Err(std::io::Error::last_os_error());
} }
// SAFETY: MONITOR is None. Nobody else can hold a reference to it.
unsafe { unsafe {
MONITOR = Some((file, Instant::now())); 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>>>) { 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() } { if let Some((file, start)) = unsafe { MONITOR.as_ref() } {
let e = Event { let e = Event {
timestamp: start.elapsed(), timestamp: start.elapsed(),

92
fuzz/Cargo.lock generated
View File

@@ -11,9 +11,9 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.68" version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
[[package]] [[package]]
name = "api_client" name = "api_client"
@@ -79,7 +79,6 @@ dependencies = [
"libc", "libc",
"log", "log",
"qcow", "qcow",
"smallvec",
"thiserror", "thiserror",
"versionize", "versionize",
"versionize_derive", "versionize_derive",
@@ -99,9 +98,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.78" version = "1.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
dependencies = [ dependencies = [
"jobserver", "jobserver",
] ]
@@ -114,9 +113,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.0.32" version = "4.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39" checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"clap_lex", "clap_lex",
@@ -170,9 +169,7 @@ dependencies = [
"epoll", "epoll",
"libc", "libc",
"libfuzzer-sys", "libfuzzer-sys",
"linux-loader",
"micro_http", "micro_http",
"net_util",
"once_cell", "once_cell",
"qcow", "qcow",
"seccompiler", "seccompiler",
@@ -352,11 +349,12 @@ dependencies = [
[[package]] [[package]]
name = "iced-x86" name = "iced-x86"
version = "1.18.0" version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd04b950d75b3498320253b17fb92745b2cc79ead8814aede2f7c1bab858bec" checksum = "158f5204401d08f91d19176112146d75e99b3cf745092e268fa7be33e09adcec"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"static_assertions",
] ]
[[package]] [[package]]
@@ -377,9 +375,9 @@ dependencies = [
[[package]] [[package]]
name = "io-uring" name = "io-uring"
version = "0.5.11" version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9ddf18110cedc09617f5a965bdb51adb0c0fb02c791e4ab22f7a21bb25269b0" checksum = "7ba34abb5175052fc1a2227a10d2275b7386c9990167de9786c0b88d8b062330"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"libc", "libc",
@@ -387,9 +385,9 @@ dependencies = [
[[package]] [[package]]
name = "is-terminal" name = "is-terminal"
version = "0.4.2" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi",
"io-lifetimes", "io-lifetimes",
@@ -399,9 +397,9 @@ dependencies = [
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.5" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
[[package]] [[package]]
name = "jobserver" name = "jobserver"
@@ -441,9 +439,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.139" version = "0.2.138"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
[[package]] [[package]]
name = "libfuzzer-sys" name = "libfuzzer-sys"
@@ -467,9 +465,9 @@ dependencies = [
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.1.4" version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
[[package]] [[package]]
name = "log" name = "log"
@@ -483,7 +481,7 @@ dependencies = [
[[package]] [[package]]
name = "micro_http" name = "micro_http"
version = "0.1.0" 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 = [ dependencies = [
"libc", "libc",
"vmm-sys-util", "vmm-sys-util",
@@ -519,9 +517,9 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.17.0" version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
[[package]] [[package]]
name = "option_parser" name = "option_parser"
@@ -600,9 +598,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.49" version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -653,9 +651,9 @@ dependencies = [
[[package]] [[package]]
name = "remain" name = "remain"
version = "0.2.6" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5704e2cda92fd54202f05430725317ba0ea7d0c96b246ca0a92e45177127ba3b" checksum = "1a81bc6a3aaeb180f767bd2bda8c03bac5b93b3b69a783c66f35d16a4df276cf"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -687,9 +685,9 @@ dependencies = [
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.12" version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]] [[package]]
name = "seccompiler" name = "seccompiler"
@@ -702,24 +700,24 @@ dependencies = [
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.16" version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.152" version = "1.0.150"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.152" version = "1.0.150"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -728,9 +726,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.91" version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@@ -749,9 +747,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_with_macros" name = "serde_with_macros"
version = "2.2.0" version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1966009f3c05f095697c537312f5415d1e3ed31ce0a56942bac4c771c5c335e" checksum = "e3452b4c0f6c1e357f73fdb87cd1efabaa12acf328c7a528e252893baeb3f4aa"
dependencies = [ dependencies = [
"darling", "darling",
"proc-macro2", "proc-macro2",
@@ -789,10 +787,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]] [[package]]
name = "smallvec" name = "static_assertions"
version = "1.10.0" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]] [[package]]
name = "strsim" name = "strsim"
@@ -802,9 +800,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.107" version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -875,9 +873,9 @@ dependencies = [
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.6" version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
[[package]] [[package]]
name = "uuid" name = "uuid"

View File

@@ -12,12 +12,10 @@ cargo-fuzz = true
block_util = { path = "../block_util" } block_util = { path = "../block_util" }
devices = { path = "../devices" } devices = { path = "../devices" }
epoll = "4.3.1" epoll = "4.3.1"
libc = "0.2.138" libc = "0.2.135"
libfuzzer-sys = "0.4.5" 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" } micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
net_util = { path = "../net_util" } once_cell = "1.16.0"
once_cell = "1.17.0"
qcow = { path = "../qcow" } qcow = { path = "../qcow" }
seccompiler = "0.3.0" seccompiler = "0.3.0"
vhdx = { path = "../vhdx" } vhdx = { path = "../vhdx" }
@@ -76,30 +74,12 @@ path = "fuzz_targets/iommu.rs"
test = false test = false
doc = 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]] [[bin]]
name = "mem" name = "mem"
path = "fuzz_targets/mem.rs" path = "fuzz_targets/mem.rs"
test = false test = false
doc = false doc = false
[[bin]]
name = "net"
path = "fuzz_targets/net.rs"
test = false
doc = false
[[bin]] [[bin]]
name = "pmem" name = "pmem"
path = "fuzz_targets/pmem.rs" path = "fuzz_targets/pmem.rs"

View File

@@ -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)
}
}

View File

@@ -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();
});

View File

@@ -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");
}
}
}
}
}

View File

@@ -15,7 +15,6 @@ fuzz_target!(|bytes| {
let mut serial = Serial::new_sink( let mut serial = Serial::new_sink(
"serial".into(), "serial".into(),
Arc::new(TestInterrupt::new(EventFd::new(EFD_NONBLOCK).unwrap())), Arc::new(TestInterrupt::new(EventFd::new(EFD_NONBLOCK).unwrap())),
None,
); );
let mut i = 0; let mut i = 0;

View File

@@ -11,23 +11,23 @@ mshv = ["mshv-ioctls", "mshv-bindings"]
tdx = [] tdx = []
[dependencies] [dependencies]
anyhow = "1.0.68" anyhow = "1.0.66"
byteorder = "1.4.3" byteorder = "1.4.3"
thiserror = "1.0.38" thiserror = "1.0.37"
libc = "0.2.139" libc = "0.2.138"
log = "0.4.17" log = "0.4.17"
kvm-ioctls = { version = "0.12.0", optional = true } kvm-ioctls = { version = "0.12.0", optional = true }
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx", features = ["with-serde", "fam-wrappers"], optional = true } 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-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} 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"] } 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 } 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"] } vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic"] }
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] } vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86] [target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
version = "1.18.0" version = "1.17.0"
default-features = false default-features = false
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"] features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]

View File

@@ -272,7 +272,6 @@ impl LapicState {
use std::io::Cursor; use std::io::Cursor;
use std::mem; use std::mem;
// SAFETY: plain old data type
let sliceu8 = unsafe { let sliceu8 = unsafe {
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array. // 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). // Cursors are only readable on arrays of u8, not i8(c_char).
@@ -291,7 +290,6 @@ impl LapicState {
use std::io::Cursor; use std::io::Cursor;
use std::mem; use std::mem;
// SAFETY: plain old data type
let sliceu8 = unsafe { let sliceu8 = unsafe {
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array. // 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). // Cursors are only readable on arrays of u8, not i8(c_char).

View File

@@ -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_SHIFT: u32 = 32;
const KVM_DEV_ARM_VGIC_V3_MPIDR_MASK: u64 = 0xffffffff << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT as u64; 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 const KVM_ARM64_SYSREG_MPIDR_EL1: u64 = KVM_REG_ARM64 as u64
| KVM_REG_SIZE_U64 | KVM_REG_SIZE_U64 as u64
| KVM_REG_ARM64_SYSREG as u64 | KVM_REG_ARM64_SYSREG as u64
| (((3_u64) << KVM_REG_ARM64_SYSREG_OP0_SHIFT) & KVM_REG_ARM64_SYSREG_OP0_MASK 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); | (((5_u64) << KVM_REG_ARM64_SYSREG_OP2_SHIFT) & KVM_REG_ARM64_SYSREG_OP2_MASK as u64);

View File

@@ -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 // This macro gets the offset of a structure (i.e `str`) member (i.e `field`) without having
// an instance of that structure. // an instance of that structure.
#[macro_export] #[macro_export]
macro_rules! offset_of { macro_rules! offset__of {
($str:ty, $field:ident) => {{ ($str:ty, $($field:ident)+) => ({
let tmp: std::mem::MaybeUninit<$str> = std::mem::MaybeUninit::uninit(); let tmp: std::mem::MaybeUninit<$str> = std::mem::MaybeUninit::uninit();
let base = tmp.as_ptr(); 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 // 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 // that we don't actually read from `base` (which would be UB) nor create an intermediate
// reference. // 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. // Avoid warnings when nesting `unsafe` blocks.
#[allow(unused_unsafe)] #[allow(unused_unsafe)]
// SAFETY: The two pointers are within the same allocated object `tmp`. All requirements // SAFETY: The two pointers are within the same allocated object `tmp`. All requirements
// from offset_from are upheld. // from offset_from are upheld.
unsafe { unsafe { member.offset_from(base as *const u8) as usize }
member.offset_from(base as *const u8) as usize });
}
}};
} }
// Following are macros that help with getting the ID of a aarch64 core register. // Following are macros that help with getting the ID of a aarch64 core register.

View File

@@ -23,7 +23,7 @@ use crate::vec_with_array_field;
use crate::vm::{self, InterruptSourceConfig, VmOps}; use crate::vm::{self, InterruptSourceConfig, VmOps};
use crate::HypervisorType; use crate::HypervisorType;
#[cfg(target_arch = "aarch64")] #[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 kvm_ioctls::{NoDatamatch, VcpuFd, VmFd};
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
@@ -1114,7 +1114,7 @@ impl cpu::Vcpu for KvmVcpu {
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
fn get_regs(&self) -> cpu::Result<StandardRegisters> { fn get_regs(&self) -> cpu::Result<StandardRegisters> {
let mut state: StandardRegisters = kvm_regs::default(); 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: // There are 31 user_pt_regs:
// https://elixir.free-electrons.com/linux/v4.14.174/source/arch/arm64/include/uapi/asm/ptrace.h#L72 // 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 // 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. // We are now entering the "Other register" section of the ARMv8-a architecture.
// First one, stack pointer. // First one, stack pointer.
let off = offset_of!(user_pt_regs, sp); let off = offset__of!(user_pt_regs, sp);
state.regs.sp = self state.regs.sp = self
.fd .fd
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off)) .get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off))
@@ -1140,7 +1140,7 @@ impl cpu::Vcpu for KvmVcpu {
.unwrap(); .unwrap();
// Second one, the program counter. // 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 state.regs.pc = self
.fd .fd
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off)) .get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off))
@@ -1149,7 +1149,7 @@ impl cpu::Vcpu for KvmVcpu {
.unwrap(); .unwrap();
// Next is the processor state. // 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 state.regs.pstate = self
.fd .fd
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off)) .get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off))
@@ -1158,7 +1158,7 @@ impl cpu::Vcpu for KvmVcpu {
.unwrap(); .unwrap();
// The stack pointer associated with EL1 // 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 state.sp_el1 = self
.fd .fd
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off)) .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 // Exception Link Register for EL1, when taking an exception to EL1, this register
// holds the address to which to return afterwards. // 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 state.elr_el1 = self
.fd .fd
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off)) .get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, off))
@@ -1177,7 +1177,7 @@ impl cpu::Vcpu for KvmVcpu {
.unwrap(); .unwrap();
// Saved Program Status Registers, there are 5 of them used in the kernel. // 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 { for i in 0..KVM_NR_SPSR as usize {
state.spsr[i] = self state.spsr[i] = self
.fd .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: // 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 // 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 { for i in 0..32 {
state.fp_regs.vregs[i] = self state.fp_regs.vregs[i] = self
.fd .fd
@@ -1200,7 +1200,7 @@ impl cpu::Vcpu for KvmVcpu {
} }
// Floating-point Status Register // 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 state.fp_regs.fpsr = self
.fd .fd
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U32, off)) .get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U32, off))
@@ -1209,7 +1209,7 @@ impl cpu::Vcpu for KvmVcpu {
.unwrap(); .unwrap();
// Floating-point Control Register // 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 state.fp_regs.fpcr = self
.fd .fd
.get_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U32, off)) .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<()> { fn set_regs(&self, state: &StandardRegisters) -> cpu::Result<()> {
// The function follows the exact identical order from `state`. Look there // The function follows the exact identical order from `state`. Look there
// for some additional info on registers. // 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 { for i in 0..31 {
self.fd self.fd
.set_one_reg( .set_one_reg(
@@ -1249,7 +1249,7 @@ impl cpu::Vcpu for KvmVcpu {
off += std::mem::size_of::<u64>(); off += std::mem::size_of::<u64>();
} }
let off = offset_of!(user_pt_regs, sp); let off = offset__of!(user_pt_regs, sp);
self.fd self.fd
.set_one_reg( .set_one_reg(
arm64_core_reg_id!(KVM_REG_SIZE_U64, off), 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()))?; .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 self.fd
.set_one_reg( .set_one_reg(
arm64_core_reg_id!(KVM_REG_SIZE_U64, off), 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()))?; .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 self.fd
.set_one_reg( .set_one_reg(
arm64_core_reg_id!(KVM_REG_SIZE_U64, off), 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()))?; .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 self.fd
.set_one_reg( .set_one_reg(
arm64_core_reg_id!(KVM_REG_SIZE_U64, off), 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()))?; .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 self.fd
.set_one_reg( .set_one_reg(
arm64_core_reg_id!(KVM_REG_SIZE_U64, off), 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()))?; .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 { for i in 0..KVM_NR_SPSR as usize {
self.fd self.fd
.set_one_reg( .set_one_reg(
@@ -1300,7 +1300,7 @@ impl cpu::Vcpu for KvmVcpu {
off += std::mem::size_of::<u64>(); 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 { for i in 0..32 {
self.fd self.fd
.set_one_reg( .set_one_reg(
@@ -1311,7 +1311,7 @@ impl cpu::Vcpu for KvmVcpu {
off += mem::size_of::<u128>(); 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 self.fd
.set_one_reg( .set_one_reg(
arm64_core_reg_id!(KVM_REG_SIZE_U32, off), 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()))?; .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 self.fd
.set_one_reg( .set_one_reg(
arm64_core_reg_id!(KVM_REG_SIZE_U32, off), 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 // it to the corresponding KVM ID, and call `KVM_GET_ONE_REG` API to
// get the value of the system parameter. // get the value of the system parameter.
// //
let id: u64 = KVM_REG_ARM64 let id: u64 = KVM_REG_ARM64 as u64
| KVM_REG_SIZE_U64 | KVM_REG_SIZE_U64 as u64
| KVM_REG_ARM64_SYSREG as u64 | KVM_REG_ARM64_SYSREG as u64
| ((((sys_reg) >> 5) | ((((sys_reg) >> 5)
& (KVM_REG_ARM64_SYSREG_OP0_MASK & (KVM_REG_ARM64_SYSREG_OP0_MASK
@@ -1733,10 +1733,10 @@ impl cpu::Vcpu for KvmVcpu {
const PSTATE_FAULT_BITS_64: u64 = const PSTATE_FAULT_BITS_64: u64 =
PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT; 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. // 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 self.fd
.set_one_reg( .set_one_reg(
arm64_core_reg_id!(KVM_REG_SIZE_U64, pstate), 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. // Other vCPUs are powered off initially awaiting PSCI wakeup.
if cpu_id == 0 { if cpu_id == 0 {
// Setting the PC (Processor Counter) to the current program address (kernel address). // 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 self.fd
.set_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, pc), boot_ip.into()) .set_one_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, pc), boot_ip.into())
.map_err(|e| cpu::HypervisorCpuError::SetCoreRegister(e.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 // "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. // 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`. // 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 self.fd
.set_one_reg( .set_one_reg(
arm64_core_reg_id!(KVM_REG_SIZE_U64, regs0), arm64_core_reg_id!(KVM_REG_SIZE_U64, regs0),
@@ -2066,7 +2066,6 @@ impl cpu::Vcpu for KvmVcpu {
#[cfg(feature = "tdx")] #[cfg(feature = "tdx")]
fn get_tdx_exit_details(&mut self) -> cpu::Result<TdxExitDetails> { fn get_tdx_exit_details(&mut self) -> cpu::Result<TdxExitDetails> {
let kvm_run = self.fd.get_kvm_run(); 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 }; let tdx_vmcall = unsafe { &mut kvm_run.__bindgen_anon_1.tdx.u.vmcall };
tdx_vmcall.status_code = TDG_VP_VMCALL_INVALID_OPERAND; tdx_vmcall.status_code = TDG_VP_VMCALL_INVALID_OPERAND;
@@ -2090,7 +2089,6 @@ impl cpu::Vcpu for KvmVcpu {
#[cfg(feature = "tdx")] #[cfg(feature = "tdx")]
fn set_tdx_status(&mut self, status: TdxExitStatus) { fn set_tdx_status(&mut self, status: TdxExitStatus) {
let kvm_run = self.fd.get_kvm_run(); 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 }; let tdx_vmcall = unsafe { &mut kvm_run.__bindgen_anon_1.tdx.u.vmcall };
tdx_vmcall.status_code = match status { tdx_vmcall.status_code = match status {

View File

@@ -18,6 +18,8 @@
//! - arm64 //! - arm64
//! //!
#![allow(clippy::significant_drop_in_scrutinee)]
#[macro_use] #[macro_use]
extern crate anyhow; extern crate anyhow;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
@@ -59,11 +61,9 @@ pub use vm::{
Vm, VmOps, Vm, VmOps,
}; };
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum HypervisorType { pub enum HypervisorType {
#[cfg(feature = "kvm")]
Kvm, Kvm,
#[cfg(feature = "mshv")]
Mshv, Mshv,
} }

View File

@@ -446,10 +446,10 @@ impl cpu::Vcpu for MshvVcpu {
/* Advance RIP and update RAX */ /* Advance RIP and update RAX */
let arr_reg_name_value = [ 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, 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) set_registers_64!(self.fd, arr_reg_name_value)
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?; .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!( assert!(
// SAFETY: access_info is valid, otherwise we won't be here
(unsafe { access_info.__bindgen_anon_1.string_op() } != 1), (unsafe { access_info.__bindgen_anon_1.string_op() } != 1),
"String IN/OUT not supported" "String IN/OUT not supported"
); );
assert!( assert!(
// SAFETY: access_info is valid, otherwise we won't be here
(unsafe { access_info.__bindgen_anon_1.rep_prefix() } != 1), (unsafe { access_info.__bindgen_anon_1.rep_prefix() } != 1),
"Rep IN/OUT not supported" "Rep IN/OUT not supported"
); );
@@ -495,10 +494,10 @@ impl cpu::Vcpu for MshvVcpu {
/* Advance RIP and update RAX */ /* Advance RIP and update RAX */
let arr_reg_name_value = [ 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, 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) set_registers_64!(self.fd, arr_reg_name_value)
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?; .map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;

View File

@@ -1187,7 +1187,6 @@ pub struct ifreq {
impl Default for ifreq { impl Default for ifreq {
fn default() -> Self { fn default() -> Self {
// SAFETY: all zeros is a valid pattern for this data type
unsafe { std::mem::zeroed() } unsafe { std::mem::zeroed() }
} }
} }

View File

@@ -7,12 +7,12 @@ edition = "2021"
[dependencies] [dependencies]
epoll = "4.3.1" epoll = "4.3.1"
getrandom = "0.2.8" getrandom = "0.2.8"
libc = "0.2.139" libc = "0.2.138"
log = "0.4.17" log = "0.4.17"
net_gen = { path = "../net_gen" } net_gen = { path = "../net_gen" }
rate_limiter = { path = "../rate_limiter" } rate_limiter = { path = "../rate_limiter" }
serde = "1.0.151" serde = "1.0.150"
thiserror = "1.0.38" thiserror = "1.0.37"
versionize = "0.1.9" versionize = "0.1.9"
versionize_derive = "0.1.4" versionize_derive = "0.1.4"
virtio-bindings = "0.1.0" virtio-bindings = "0.1.0"

View File

@@ -66,34 +66,32 @@ fn create_sockaddr(ip_addr: net::Ipv4Addr) -> net_gen::sockaddr {
let addr_in = net_gen::sockaddr_in { let addr_in = net_gen::sockaddr_in {
sin_family: net_gen::AF_INET as u16, sin_family: net_gen::AF_INET as u16,
sin_port: 0, sin_port: 0,
// SAFETY: ip_addr can be safely transmute to in_addr
sin_addr: unsafe { mem::transmute(ip_addr.octets()) }, sin_addr: unsafe { mem::transmute(ip_addr.octets()) },
__pad: [0; 8usize], __pad: [0; 8usize],
}; };
// SAFETY: addr_in can be safely transmute to sockaddr
unsafe { mem::transmute(addr_in) } unsafe { mem::transmute(addr_in) }
} }
fn create_inet_socket() -> Result<net::UdpSocket> { 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) }; let sock = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
if sock < 0 { if sock < 0 {
return Err(Error::CreateSocket(IoError::last_os_error())); 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) }) Ok(unsafe { net::UdpSocket::from_raw_fd(sock) })
} }
fn create_unix_socket() -> Result<net::UdpSocket> { 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) }; let sock = unsafe { libc::socket(libc::AF_UNIX, libc::SOCK_DGRAM, 0) };
if sock < 0 { if sock < 0 {
return Err(Error::CreateSocket(IoError::last_os_error())); 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) }) Ok(unsafe { net::UdpSocket::from_raw_fd(sock) })
} }

View File

@@ -83,7 +83,6 @@ impl TxVirtio {
} }
let len = if !iovecs.is_empty() { let len = if !iovecs.is_empty() {
// SAFETY: FFI call with correct arguments
let result = unsafe { let result = unsafe {
libc::writev( libc::writev(
tap.as_raw_fd() as libc::c_int, tap.as_raw_fd() as libc::c_int,
@@ -222,7 +221,6 @@ impl RxVirtio {
} }
let len = if !iovecs.is_empty() { let len = if !iovecs.is_empty() {
// SAFETY: FFI call with correct arguments
let result = unsafe { let result = unsafe {
libc::readv( libc::readv(
tap.as_raw_fd() as libc::c_int, tap.as_raw_fd() as libc::c_int,

View File

@@ -28,8 +28,8 @@ pub enum Error {
GetFeatures(IoError), GetFeatures(IoError),
#[error("Missing multiqueue support in the kernel")] #[error("Missing multiqueue support in the kernel")]
MultiQueueKernelSupport, MultiQueueKernelSupport,
#[error("ioctl ({0}) failed: {1}")] #[error("ioctl failed: {0}")]
IoctlError(c_ulong, IoError), IoctlError(IoError),
#[error("Failed to create a socket: {0}")] #[error("Failed to create a socket: {0}")]
NetUtil(NetUtilError), NetUtil(NetUtilError),
#[error("Invalid interface name")] #[error("Invalid interface name")]
@@ -87,37 +87,9 @@ fn build_terminated_if_name(if_name: &str) -> Result<Vec<u8>> {
} }
impl Tap { 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> { 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)?; let terminated_if_name = build_terminated_if_name(if_name)?;
// SAFETY: FFI call
let fd = unsafe { let fd = unsafe {
// Open calls are safe because we give a constant null-terminated // Open calls are safe because we give a constant null-terminated
// string and verify the result. // string and verify the result.
@@ -130,14 +102,13 @@ impl Tap {
return Err(Error::OpenTun(IoError::last_os_error())); 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 tuntap = unsafe { File::from_raw_fd(fd) };
// Let's validate some features before going any further. // 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 // ioctl is safe since we call it with a valid tap fd and check the return
// value. // value.
let mut features = 0; let mut features = 0;
// SAFETY: IOCTL with correct arguments
let ret = unsafe { ioctl_with_mut_ref(&tuntap, net_gen::TUNGETFEATURES(), &mut features) }; let ret = unsafe { ioctl_with_mut_ref(&tuntap, net_gen::TUNGETFEATURES(), &mut features) };
if ret < 0 { if ret < 0 {
return Err(Error::GetFeatures(IoError::last_os_error())); 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 // don't call as_mut on the same union field more than once, this block
// is safe. // is safe.
let mut ifreq: net_gen::ifreq = Default::default(); let mut ifreq: net_gen::ifreq = Default::default();
// SAFETY: see the comment above.
unsafe { unsafe {
let ifrn_name = ifreq.ifr_ifrn.ifrn_name.as_mut(); let ifrn_name = ifreq.ifr_ifrn.ifrn_name.as_mut();
let name_slice = &mut ifrn_name[..terminated_if_name.len()]; 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. // value.
let ret = unsafe { ioctl_with_mut_ref(&tuntap, net_gen::TUNSETIFF(), &mut ifreq) }; let ret = unsafe { ioctl_with_mut_ref(&tuntap, net_gen::TUNSETIFF(), &mut ifreq) };
if ret < 0 { if ret < 0 {
return Err(Error::ConfigureTap(IoError::last_os_error())); 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(); let mut if_name = unsafe { ifreq.ifr_ifrn.ifrn_name }.to_vec();
if_name.truncate(terminated_if_name.len() - 1); if_name.truncate(terminated_if_name.len() - 1);
// Safe since only the name is accessed, and it's cloned out.
Ok(Tap { Ok(Tap {
tap_file: tuntap, tap_file: tuntap,
if_name, if_name,
@@ -188,7 +158,6 @@ impl Tap {
pub fn from_tap_fd(fd: RawFd, num_queue_pairs: usize) -> Result<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 // Ensure that the file is opened non-blocking, this is particularly
// needed when opened via the shell for macvtap. // needed when opened via the shell for macvtap.
// SAFETY: FFI call
let ret = unsafe { let ret = unsafe {
let mut flags = libc::fcntl(fd, libc::F_GETFL); let mut flags = libc::fcntl(fd, libc::F_GETFL);
flags |= libc::O_NONBLOCK; flags |= libc::O_NONBLOCK;
@@ -198,20 +167,19 @@ impl Tap {
return Err(Error::ConfigureTap(IoError::last_os_error())); return Err(Error::ConfigureTap(IoError::last_os_error()));
} }
// SAFETY: fd is a tap fd
let tap_file = unsafe { File::from_raw_fd(fd) }; let tap_file = unsafe { File::from_raw_fd(fd) };
let mut ifreq: net_gen::ifreq = Default::default(); let mut ifreq: net_gen::ifreq = Default::default();
// Get current config including name // Get current config including name
// SAFETY: IOCTL with correct arugments let ret = unsafe { ioctl_with_mut_ref(&tap_file, net_gen::TUNGETIFF(), &mut ifreq) };
unsafe { Self::ioctl_with_mut_ref(&tap_file, net_gen::TUNGETIFF(), &mut 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 if_name = unsafe { ifreq.ifr_ifrn.ifrn_name }.to_vec(); let if_name = unsafe { ifreq.ifr_ifrn.ifrn_name }.to_vec();
// Try and update flags. Depending on how the tap was created (macvtap // 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. // or via open_named()) this might return -EEXIST so we just ignore that.
// SAFETY: access union fields
unsafe { unsafe {
ifreq.ifr_ifru.ifru_flags = ifreq.ifr_ifru.ifru_flags =
(net_gen::IFF_TAP | net_gen::IFF_NO_PI | net_gen::IFF_VNET_HDR) as c_short; (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; 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) }; 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 { if ret < 0 && IoError::last_os_error().raw_os_error().unwrap() != libc::EEXIST {
return Err(Error::ConfigureTap(IoError::last_os_error())); return Err(Error::ConfigureTap(IoError::last_os_error()));
@@ -241,8 +208,14 @@ impl Tap {
ifreq.ifr_ifru.ifru_addr = addr; ifreq.ifr_ifru.ifru_addr = addr;
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return. // 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) } 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. /// Set mac addr for tap interface.
@@ -260,10 +233,13 @@ impl Tap {
let mut ifreq = self.get_ifreq(); let mut ifreq = self.get_ifreq();
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return. // 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)? }; let ret =
unsafe { ioctl_with_ref(&sock, net_gen::sockios::SIOCGIFHWADDR as c_ulong, &ifreq) };
// SAFETY: We only access one field of the ifru union 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 { unsafe {
let ifru_hwaddr = &mut ifreq.ifr_ifru.ifru_hwaddr; let ifru_hwaddr = &mut ifreq.ifr_ifru.ifru_hwaddr;
for (i, v) in addr.get_bytes().iter().enumerate() { 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. // 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) } 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. /// Get mac addr for tap interface.
@@ -281,10 +263,14 @@ impl Tap {
let ifreq = self.get_ifreq(); let ifreq = self.get_ifreq();
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return. // 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)? }; 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 { let addr = unsafe {
MacAddr::from_bytes(&ifreq.ifr_ifru.ifru_hwaddr.sa_data[0..MAC_ADDR_LEN]) MacAddr::from_bytes(&ifreq.ifr_ifru.ifru_hwaddr.sa_data[0..MAC_ADDR_LEN])
.map_err(Error::MacParsing)? .map_err(Error::MacParsing)?
@@ -301,45 +287,57 @@ impl Tap {
ifreq.ifr_ifru.ifru_addr = addr; ifreq.ifr_ifru.ifru_addr = addr;
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return. // 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) } 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> { pub fn mtu(&self) -> Result<i32> {
let sock = create_unix_socket().map_err(Error::NetUtil)?; let sock = create_unix_socket().map_err(Error::NetUtil)?;
let ifreq = self.get_ifreq(); let ifreq = self.get_ifreq();
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return. // 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)? }; 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 }; let mtu = unsafe { ifreq.ifr_ifru.ifru_mtu };
Ok(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<()> { pub fn set_mtu(&self, mtu: i32) -> Result<()> {
let sock = create_unix_socket().map_err(Error::NetUtil)?; let sock = create_unix_socket().map_err(Error::NetUtil)?;
let mut ifreq = self.get_ifreq(); let mut ifreq = self.get_ifreq();
ifreq.ifr_ifru.ifru_mtu = mtu; ifreq.ifr_ifru.ifru_mtu = mtu;
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return. // 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) } 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. /// Set the offload flags for the tap interface.
pub fn set_offload(&self, flags: c_uint) -> Result<()> { pub fn set_offload(&self, flags: c_uint) -> Result<()> {
// SAFETY: ioctl is safe. Called with a valid tap fd, and we check the return. // 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) } 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. /// Enable the tap interface.
@@ -348,11 +346,13 @@ impl Tap {
let mut ifreq = self.get_ifreq(); let mut ifreq = self.get_ifreq();
// SAFETY: IOCTL with correct arguments let ret =
unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCGIFFLAGS as c_ulong, &ifreq)? }; 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 // 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 }; let ifru_flags = unsafe { ifreq.ifr_ifru.ifru_flags };
if ifru_flags & net_gen::net_device_flags_IFF_UP as i16 if ifru_flags & net_gen::net_device_flags_IFF_UP as i16
== 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; 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. // 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) } 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. /// Set the size of the vnet hdr.
pub fn set_vnet_hdr_size(&self, size: c_int) -> Result<()> { 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. // 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) } 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 { 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 // This sets the name of the interface, which is the only entry
// in a single-field union. // in a single-field union.
// SAFETY: access union fields and we're sure the copy is okay.
unsafe { unsafe {
let ifrn_name = ifreq.ifr_ifrn.ifrn_name.as_mut(); let ifrn_name = ifreq.ifr_ifrn.ifrn_name.as_mut();
let name_slice = &mut ifrn_name[..self.if_name.len()]; let name_slice = &mut ifrn_name[..self.if_name.len()];
@@ -390,11 +400,6 @@ impl Tap {
pub fn get_if_name(&self) -> Vec<u8> { pub fn get_if_name(&self) -> Vec<u8> {
self.if_name.clone() 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 { impl Read for Tap {

View File

@@ -10,17 +10,17 @@ kvm = ["vfio-ioctls/kvm"]
mshv = ["vfio-ioctls/mshv"] mshv = ["vfio-ioctls/mshv"]
[dependencies] [dependencies]
anyhow = "1.0.68" anyhow = "1.0.66"
byteorder = "1.4.3" byteorder = "1.4.3"
hypervisor = { path = "../hypervisor" } hypervisor = { path = "../hypervisor" }
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main", features = ["fam-wrappers"] } 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-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
vfio_user = { path = "../vfio_user" } vfio_user = { path = "../vfio_user" }
vmm-sys-util = "0.11.0" vmm-sys-util = "0.11.0"
libc = "0.2.139" libc = "0.2.138"
log = "0.4.17" log = "0.4.17"
serde = { version = "1.0.151", features = ["derive"] } serde = { version = "1.0.150", features = ["derive"] }
thiserror = "1.0.38" thiserror = "1.0.37"
versionize = "0.1.9" versionize = "0.1.9"
versionize_derive = "0.1.4" versionize_derive = "0.1.4"
vm-allocator = { path = "../vm-allocator" } vm-allocator = { path = "../vm-allocator" }

View File

@@ -62,7 +62,6 @@ impl PciRoot {
0, 0,
0, 0,
None, None,
None,
), ),
} }
} }

View File

@@ -32,8 +32,6 @@ const CAPABILITY_MAX_OFFSET: usize = 192;
const INTERRUPT_LINE_PIN_REG: usize = 15; 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. /// Represents the types of PCI headers allowed in the configuration registers.
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum PciHeaderType { pub enum PciHeaderType {
@@ -396,7 +394,7 @@ fn decode_64_bits_bar_size(bar_size_hi: u32, bar_size_lo: u32) -> Option<u64> {
None None
} }
#[derive(Debug, Default, Clone, Copy, Versionize)] #[derive(Default, Clone, Copy, Versionize)]
struct PciBar { struct PciBar {
addr: u32, addr: u32,
size: u32, size: u32,
@@ -405,7 +403,7 @@ struct PciBar {
} }
#[derive(Versionize)] #[derive(Versionize)]
pub struct PciConfigurationState { struct PciConfigurationState {
registers: Vec<u32>, registers: Vec<u32>,
writable_bits: Vec<u32>, writable_bits: Vec<u32>,
bars: Vec<PciBar>, bars: Vec<PciBar>,
@@ -555,78 +553,46 @@ impl PciConfiguration {
subsystem_vendor_id: u16, subsystem_vendor_id: u16,
subsystem_id: u16, subsystem_id: u16,
msix_config: Option<Arc<Mutex<MsixConfig>>>, msix_config: Option<Arc<Mutex<MsixConfig>>>,
state: Option<PciConfigurationState>,
) -> Self { ) -> Self {
let ( let mut registers = [0u32; NUM_CONFIGURATION_REGISTERS];
registers, let mut writable_bits = [0u32; NUM_CONFIGURATION_REGISTERS];
writable_bits, registers[0] = u32::from(device_id) << 16 | u32::from(vendor_id);
bars, // TODO(dverkamp): Status should be write-1-to-clear
rom_bar_addr, writable_bits[1] = 0x0000_ffff; // Status (r/o), command (r/w)
rom_bar_size, let pi = if let Some(pi) = programming_interface {
rom_bar_used, pi.get_register_value()
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,
)
} else { } else {
let mut registers = [0u32; NUM_CONFIGURATION_REGISTERS]; 0
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,
)
}; };
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 { PciConfiguration {
registers, registers,
writable_bits, writable_bits,
bars, bars,
rom_bar_addr, rom_bar_addr: 0,
rom_bar_size, rom_bar_size: 0,
rom_bar_used, rom_bar_used: false,
last_capability, last_capability: None,
msix_cap_reg_idx, msix_cap_reg_idx: None,
msix_config, 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. /// Reads a 32bit register from `reg_idx` in the register map.
pub fn read_reg(&self, reg_idx: usize) -> u32 { pub fn read_reg(&self, reg_idx: usize) -> u32 {
*(self.registers.get(reg_idx).unwrap_or(&0xffff_ffff)) *(self.registers.get(reg_idx).unwrap_or(&0xffff_ffff))
@@ -1068,11 +1046,16 @@ impl Pausable for PciConfiguration {}
impl Snapshottable for PciConfiguration { impl Snapshottable for PciConfiguration {
fn id(&self) -> String { fn id(&self) -> String {
String::from(PCI_CONFIGURATION_ID) String::from("pci_configuration")
} }
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 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, 0xABCD,
0x2468, 0x2468,
None, None,
None,
); );
// Add two capabilities with different contents. // Add two capabilities with different contents.
@@ -1252,7 +1234,6 @@ mod tests {
0xABCD, 0xABCD,
0x2468, 0x2468,
None, None,
None,
); );
let class_reg = cfg.read_reg(2); let class_reg = cfg.read_reg(2);

View File

@@ -19,13 +19,12 @@ pub use self::configuration::{
PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciCapability, PciCapabilityId, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciCapability, PciCapabilityId,
PciClassCode, PciConfiguration, PciExpressCapabilityId, PciHeaderType, PciMassStorageSubclass, PciClassCode, PciConfiguration, PciExpressCapabilityId, PciHeaderType, PciMassStorageSubclass,
PciNetworkControllerSubclass, PciProgrammingInterface, PciSerialBusSubClass, PciSubclass, PciNetworkControllerSubclass, PciProgrammingInterface, PciSerialBusSubClass, PciSubclass,
PCI_CONFIGURATION_ID,
}; };
pub use self::device::{ pub use self::device::{
BarReprogrammingParams, DeviceRelocation, Error as PciDeviceError, PciDevice, BarReprogrammingParams, DeviceRelocation, Error as PciDeviceError, PciDevice,
}; };
pub use self::msi::{msi_num_enabled_vectors, MsiCap, MsiConfig}; 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::{VfioPciDevice, VfioPciError};
pub use self::vfio_user::{VfioUserDmaMapping, VfioUserPciDevice, VfioUserPciDeviceError}; pub use self::vfio_user::{VfioUserDmaMapping, VfioUserPciDevice, VfioUserPciDeviceError};
use serde::de::Visitor; use serde::de::Visitor;

View File

@@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
// //
use anyhow::anyhow;
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
use std::io; use std::io;
use std::sync::Arc; use std::sync::Arc;
@@ -38,15 +39,13 @@ pub fn msi_num_enabled_vectors(msg_ctl: u16) -> usize {
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum Error { enum Error {
#[error("Failed enabling the interrupt route: {0}")] #[error("Failed enabling the interrupt route: {0}")]
EnableInterruptRoute(io::Error), EnableInterruptRoute(io::Error),
#[error("Failed updating the interrupt route: {0}")] #[error("Failed updating the interrupt route: {0}")]
UpdateInterruptRoute(io::Error), UpdateInterruptRoute(io::Error),
} }
pub const MSI_CONFIG_ID: &str = "msi_config";
#[derive(Clone, Copy, Default, Versionize)] #[derive(Clone, Copy, Default, Versionize)]
pub struct MsiCap { pub struct MsiCap {
// Message Control Register // Message Control Register
@@ -173,7 +172,7 @@ impl MsiCap {
} }
#[derive(Versionize)] #[derive(Versionize)]
pub struct MsiConfigState { struct MsiConfigState {
cap: MsiCap, cap: MsiCap,
} }
@@ -185,53 +184,51 @@ pub struct MsiConfig {
} }
impl MsiConfig { impl MsiConfig {
pub fn new( pub fn new(msg_ctl: u16, interrupt_source_group: Arc<dyn InterruptSourceGroup>) -> Self {
msg_ctl: u16, let cap = MsiCap {
interrupt_source_group: Arc<dyn InterruptSourceGroup>, msg_ctl,
state: Option<MsiConfigState>, ..Default::default()
) -> 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()
}
}; };
Ok(MsiConfig { MsiConfig {
cap, cap,
interrupt_source_group, interrupt_source_group,
}) }
} }
fn state(&self) -> MsiConfigState { fn state(&self) -> MsiConfigState {
MsiConfigState { cap: self.cap } 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 { pub fn enabled(&self) -> bool {
self.cap.enabled() self.cap.enabled()
} }
@@ -284,10 +281,21 @@ impl Pausable for MsiConfig {}
impl Snapshottable for MsiConfig { impl Snapshottable for MsiConfig {
fn id(&self) -> String { fn id(&self) -> String {
String::from(MSI_CONFIG_ID) String::from("msi_config")
} }
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 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
))
})
} }
} }

View File

@@ -4,6 +4,7 @@
// //
use crate::{PciCapability, PciCapabilityId}; use crate::{PciCapability, PciCapabilityId};
use anyhow::anyhow;
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
use std::io; use std::io;
use std::result; 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 FUNCTION_MASK_MASK: u16 = (1 << FUNCTION_MASK_BIT) as u16;
const MSIX_ENABLE_MASK: u16 = (1 << MSIX_ENABLE_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_TABLE_ENTRY_SIZE: usize = 16;
pub const MSIX_CONFIG_ID: &str = "msix_config";
#[derive(Debug)] #[derive(Debug)]
pub enum Error { enum Error {
/// Failed enabling the interrupt route. /// Failed enabling the interrupt route.
EnableInterruptRoute(io::Error), EnableInterruptRoute(io::Error),
/// Failed updating the interrupt route. /// Failed updating the interrupt route.
@@ -61,7 +61,7 @@ impl Default for MsixTableEntry {
} }
#[derive(Versionize)] #[derive(Versionize)]
pub struct MsixConfigState { struct MsixConfigState {
table_entries: Vec<MsixTableEntry>, table_entries: Vec<MsixTableEntry>,
pba_entries: Vec<u64>, pba_entries: Vec<u64>,
masked: bool, masked: bool,
@@ -84,62 +84,23 @@ impl MsixConfig {
msix_vectors: u16, msix_vectors: u16,
interrupt_source_group: Arc<dyn InterruptSourceGroup>, interrupt_source_group: Arc<dyn InterruptSourceGroup>,
devid: u32, devid: u32,
state: Option<MsixConfigState>, ) -> Self {
) -> result::Result<Self, Error> {
assert!(msix_vectors <= MAX_MSIX_VECTORS_PER_DEVICE); assert!(msix_vectors <= MAX_MSIX_VECTORS_PER_DEVICE);
let (table_entries, pba_entries, masked, enabled) = if let Some(state) = state { let mut table_entries: Vec<MsixTableEntry> = Vec::new();
if state.enabled && !state.masked { table_entries.resize_with(msix_vectors as usize, Default::default);
for (idx, table_entry) in state.table_entries.iter().enumerate() { let mut pba_entries: Vec<u64> = Vec::new();
if table_entry.masked() { let num_pba_entries: usize = ((msix_vectors as usize) / BITS_PER_PBA_ENTRY) + 1;
continue; pba_entries.resize_with(num_pba_entries, Default::default);
}
let config = MsiIrqSourceConfig { MsixConfig {
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 {
table_entries, table_entries,
pba_entries, pba_entries,
devid, devid,
interrupt_source_group, interrupt_source_group,
masked, masked: true,
enabled, enabled: false,
}) }
} }
fn state(&self) -> MsixConfigState { 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 { pub fn masked(&self) -> bool {
self.masked self.masked
} }
@@ -429,11 +426,22 @@ impl Pausable for MsixConfig {}
impl Snapshottable for MsixConfig { impl Snapshottable for MsixConfig {
fn id(&self) -> String { fn id(&self) -> String {
String::from(MSIX_CONFIG_ID) String::from("msix_config")
} }
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 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
))
})
} }
} }

View File

@@ -3,13 +3,11 @@
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
// //
use crate::msi::{MsiConfigState, MSI_CONFIG_ID};
use crate::msix::MsixConfigState;
use crate::{ use crate::{
msi_num_enabled_vectors, BarReprogrammingParams, MsiCap, MsiConfig, MsixCap, MsixConfig, msi_num_enabled_vectors, BarReprogrammingParams, MsiCap, MsiConfig, MsixCap, MsixConfig,
PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciBdf, PciCapabilityId, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciBdf, PciCapabilityId,
PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciExpressCapabilityId, 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 anyhow::anyhow;
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
@@ -38,8 +36,6 @@ use vm_migration::{
}; };
use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::eventfd::EventFd;
pub(crate) const VFIO_COMMON_ID: &str = "vfio_common";
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum VfioPciError { pub enum VfioPciError {
#[error("Failed to create user memory region: {0}")] #[error("Failed to create user memory region: {0}")]
@@ -62,14 +58,6 @@ pub enum VfioPciError {
RegionAlignment, RegionAlignment,
#[error("Invalid region size")] #[error("Invalid region size")]
RegionSize, 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)] #[derive(Copy, Clone)]
@@ -418,86 +406,6 @@ pub(crate) struct VfioCommon {
} }
impl 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( pub(crate) fn allocate_bars(
&mut self, &mut self,
allocator: &Arc<Mutex<SystemAllocator>>, allocator: &Arc<Mutex<SystemAllocator>>,
@@ -742,13 +650,7 @@ impl VfioCommon {
} }
} }
pub(crate) fn initialize_msix( pub(crate) fn initialize_msix(&mut self, msix_cap: MsixCap, cap_offset: u32, bdf: PciBdf) {
&mut self,
msix_cap: MsixCap,
cap_offset: u32,
bdf: PciBdf,
state: Option<MsixConfigState>,
) {
let interrupt_source_group = self let interrupt_source_group = self
.msi_interrupt_manager .msi_interrupt_manager
.create_group(MsiIrqGroupConfig { .create_group(MsiIrqGroupConfig {
@@ -761,9 +663,7 @@ impl VfioCommon {
msix_cap.table_size(), msix_cap.table_size(),
interrupt_source_group.clone(), interrupt_source_group.clone(),
bdf.into(), bdf.into(),
state, );
)
.unwrap();
self.interrupt.msix = Some(VfioMsix { self.interrupt.msix = Some(VfioMsix {
bar: msix_config, bar: msix_config,
@@ -777,12 +677,7 @@ impl VfioCommon {
self.vfio_wrapper.read_config_word((cap + 2).into()) self.vfio_wrapper.read_config_word((cap + 2).into())
} }
pub(crate) fn initialize_msi( pub(crate) fn initialize_msi(&mut self, msg_ctl: u16, cap_offset: u32) {
&mut self,
msg_ctl: u16,
cap_offset: u32,
state: Option<MsiConfigState>,
) {
let interrupt_source_group = self let interrupt_source_group = self
.msi_interrupt_manager .msi_interrupt_manager
.create_group(MsiIrqGroupConfig { .create_group(MsiIrqGroupConfig {
@@ -791,7 +686,7 @@ impl VfioCommon {
}) })
.unwrap(); .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 { self.interrupt.msi = Some(VfioMsi {
cfg: msi_config, cfg: msi_config,
@@ -818,7 +713,7 @@ impl VfioCommon {
// Parse capability only if the VFIO device // Parse capability only if the VFIO device
// supports MSI. // supports MSI.
let msg_ctl = self.parse_msi_capabilities(cap_next); 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 // Parse capability only if the VFIO device
// supports MSI-X. // supports MSI-X.
let msix_cap = self.parse_msix_capabilities(cap_next); 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( fn set_state(&mut self, state: &VfioCommonState) -> Result<(), VfioPciError> {
&mut self,
state: &VfioCommonState,
msi_state: Option<MsiConfigState>,
msix_state: Option<MsixConfigState>,
) -> Result<(), VfioPciError> {
if let (Some(intx), Some(interrupt_source_group)) = if let (Some(intx), Some(interrupt_source_group)) =
(&state.intx_state, self.legacy_interrupt_group.clone()) (&state.intx_state, self.legacy_interrupt_group.clone())
{ {
@@ -1216,11 +1106,11 @@ impl VfioCommon {
} }
if let Some(msi) = &state.msi_state { 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 { 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(()) Ok(())
@@ -1231,27 +1121,73 @@ impl Pausable for VfioCommon {}
impl Snapshottable for VfioCommon { impl Snapshottable for VfioCommon {
fn id(&self) -> String { fn id(&self) -> String {
String::from(VFIO_COMMON_ID) String::from("vfio_common")
} }
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 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 // Snapshot PciConfiguration
vfio_common_snapshot.add_snapshot(self.configuration.id(), self.configuration.snapshot()?); vfio_common_snapshot.add_snapshot(self.configuration.snapshot()?);
// Snapshot MSI // Snapshot MSI
if let Some(msi) = &mut self.interrupt.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 // Snapshot MSI-X
if let Some(msix) = &mut self.interrupt.msix { 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) 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. /// VfioPciDevice represents a VFIO PCI device.
@@ -1282,22 +1218,48 @@ impl VfioPciDevice {
legacy_interrupt_group: Option<Arc<dyn InterruptSourceGroup>>, legacy_interrupt_group: Option<Arc<dyn InterruptSourceGroup>>,
iommu_attached: bool, iommu_attached: bool,
bdf: PciBdf, bdf: PciBdf,
restoring: bool,
memory_slot: Arc<dyn Fn() -> u32 + Send + Sync>, memory_slot: Arc<dyn Fn() -> u32 + Send + Sync>,
snapshot: Option<Snapshot>,
) -> Result<Self, VfioPciError> { ) -> Result<Self, VfioPciError> {
let device = Arc::new(device); let device = Arc::new(device);
device.reset(); 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 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, msi_interrupt_manager,
legacy_interrupt_group, legacy_interrupt_group,
Arc::new(vfio_wrapper) as Arc<dyn Vfio>, vfio_wrapper: Arc::new(vfio_wrapper) as Arc<dyn Vfio>,
&PciVfioSubclass::VfioSubclass, patches: HashMap::new(),
bdf, };
vm_migration::snapshot_from_id(snapshot.as_ref(), VFIO_COMMON_ID),
)?; // 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 { let vfio_pci_device = VfioPciDevice {
id, id,
@@ -1460,7 +1422,6 @@ impl VfioPciDevice {
)?; )?;
for area in sparse_areas.iter() { for area in sparse_areas.iter() {
// SAFETY: FFI call with correct arguments
let host_addr = unsafe { let host_addr = unsafe {
libc::mmap( libc::mmap(
null_mut(), null_mut(),
@@ -1527,7 +1488,6 @@ impl VfioPciDevice {
error!("Could not remove the userspace memory region: {}", e); error!("Could not remove the userspace memory region: {}", e);
} }
// SAFETY: FFI call with correct arguments
let ret = unsafe { let ret = unsafe {
libc::munmap( libc::munmap(
user_memory_region.host_addr as *mut libc::c_void, 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> { 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 // 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) 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 Transportable for VfioPciDevice {}
impl Migratable for VfioPciDevice {} impl Migratable for VfioPciDevice {}

View File

@@ -3,11 +3,15 @@
// SPDX-License-Identifier: Apache-2.0 // 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::{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 hypervisor::HypervisorVmError;
use std::any::Any; use std::any::Any;
use std::collections::HashMap;
use std::os::unix::prelude::AsRawFd; use std::os::unix::prelude::AsRawFd;
use std::ptr::null_mut; use std::ptr::null_mut;
use std::sync::{Arc, Barrier, Mutex}; use std::sync::{Arc, Barrier, Mutex};
@@ -47,8 +51,6 @@ pub enum VfioUserPciDeviceError {
DmaUnmap(#[source] VfioUserError), DmaUnmap(#[source] VfioUserError),
#[error("Failed to initialize legacy interrupts: {0}")] #[error("Failed to initialize legacy interrupts: {0}")]
InitializeLegacyInterrupts(#[source] VfioPciError), InitializeLegacyInterrupts(#[source] VfioPciError),
#[error("Failed to create VfioCommon: {0}")]
CreateVfioCommon(#[source] VfioPciError),
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@@ -71,9 +73,22 @@ impl VfioUserPciDevice {
msi_interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>, msi_interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
legacy_interrupt_group: Option<Arc<dyn InterruptSourceGroup>>, legacy_interrupt_group: Option<Arc<dyn InterruptSourceGroup>>,
bdf: PciBdf, bdf: PciBdf,
restoring: bool,
memory_slot: Arc<dyn Fn() -> u32 + Send + Sync>, memory_slot: Arc<dyn Fn() -> u32 + Send + Sync>,
snapshot: Option<Snapshot>,
) -> Result<Self, VfioUserPciDeviceError> { ) -> 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(); let resettable = client.lock().unwrap().resettable();
if resettable { if resettable {
client client
@@ -87,15 +102,29 @@ impl VfioUserPciDevice {
client: client.clone(), 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, msi_interrupt_manager,
legacy_interrupt_group, legacy_interrupt_group,
Arc::new(vfio_wrapper) as Arc<dyn Vfio>, vfio_wrapper: Arc::new(vfio_wrapper) as Arc<dyn Vfio>,
&PciVfioUserSubclass::VfioUserSubclass, patches: HashMap::new(),
bdf, };
vm_migration::snapshot_from_id(snapshot.as_ref(), VFIO_COMMON_ID),
) // No need to parse capabilities from the device if on the restore path.
.map_err(VfioUserPciDeviceError::CreateVfioCommon)?; // 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 { Ok(Self {
id, id,
@@ -152,7 +181,6 @@ impl VfioUserPciDevice {
}; };
for s in mmaps.iter() { for s in mmaps.iter() {
// SAFETY: FFI call with correct arguments
let host_addr = unsafe { let host_addr = unsafe {
libc::mmap( libc::mmap(
null_mut(), null_mut(),
@@ -219,7 +247,6 @@ impl VfioUserPciDevice {
} }
// Remove mmaps // Remove mmaps
// SAFETY: FFI call with correct arguments
let ret = unsafe { let ret = unsafe {
libc::munmap( libc::munmap(
user_memory_region.host_addr as *mut libc::c_void, 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> { 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 // 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) 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 Transportable for VfioUserPciDevice {}
impl Migratable for VfioUserPciDevice {} impl Migratable for VfioUserPciDevice {}

View File

@@ -6,10 +6,13 @@ edition = "2021"
build = "build.rs" build = "build.rs"
[dependencies] [dependencies]
clap = { version = "4.0.32", features = ["wrap_help"] } clap = { version = "4.0.29", features = ["wrap_help","cargo"] }
dirs = "4.0.0" 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" serde_json = "1.0.89"
test_infra = { path = "../test_infra" } test_infra = { path = "../test_infra" }
thiserror = "1.0.38" thiserror = "1.0.37"
wait-timeout = "0.2.0" wait-timeout = "0.2.0"
[build-dependencies]
clap = { version = "4.0.29", features = ["cargo"] }

View File

@@ -3,11 +3,13 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// //
#[macro_use(crate_version)]
extern crate clap;
use std::process::Command; use std::process::Command;
fn main() { 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 let Ok(git_out) = Command::new("git").args(["describe", "--dirty"]).output() {
if git_out.status.success() { if git_out.status.success() {
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) { if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {

View File

@@ -5,6 +5,8 @@
// Custom harness to run performance tests // Custom harness to run performance tests
extern crate test_infra; extern crate test_infra;
#[macro_use(crate_authors)]
extern crate clap;
mod performance_tests; mod performance_tests;
@@ -127,8 +129,8 @@ pub struct PerformanceTestControl {
test_iterations: u32, test_iterations: u32,
num_queues: Option<u32>, num_queues: Option<u32>,
queue_size: 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 net_rx: Option<bool>,
fio_control: Option<(FioOps, bool)>, // Second parameter controls whether we want bandwidth or IOPS fio_ops: Option<FioOps>,
num_boot_vcpus: Option<u8>, num_boot_vcpus: Option<u8>,
} }
@@ -144,13 +146,11 @@ impl fmt::Display for PerformanceTestControl {
if let Some(o) = self.queue_size { if let Some(o) = self.queue_size {
output = format!("{output}, queue_size = {o}"); output = format!("{output}, queue_size = {o}");
} }
if let Some(o) = self.net_control { if let Some(o) = self.net_rx {
let (rx, bw) = o; output = format!("{output}, net_rx = {o}");
output = format!("{output}, rx = {rx}, bandwidth = {bw}");
} }
if let Some(o) = &self.fio_control { if let Some(o) = &self.fio_ops {
let (ops, bw) = o; output = format!("{output}, fio_ops = {o}");
output = format!("{output}, fio_ops = {ops}, bandwidth = {bw}");
} }
write!(f, "{output}") write!(f, "{output}")
@@ -164,8 +164,8 @@ impl PerformanceTestControl {
test_iterations: 5, test_iterations: 5,
num_queues: None, num_queues: None,
queue_size: None, queue_size: None,
net_control: None, net_rx: None,
fio_control: None, fio_ops: None,
num_boot_vcpus: Some(1), num_boot_vcpus: Some(1),
} }
} }
@@ -262,7 +262,7 @@ mod adjuster {
} }
} }
const TEST_LIST: [PerformanceTest; 29] = [ const TEST_LIST: [PerformanceTest; 17] = [
PerformanceTest { PerformanceTest {
name: "boot_time_ms", name: "boot_time_ms",
func_ptr: performance_boot_time, func_ptr: performance_boot_time,
@@ -321,7 +321,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
control: PerformanceTestControl { control: PerformanceTestControl {
num_queues: Some(2), num_queues: Some(2),
queue_size: Some(256), queue_size: Some(256),
net_control: Some((true, true)), net_rx: Some(true),
..PerformanceTestControl::default() ..PerformanceTestControl::default()
}, },
unit_adjuster: adjuster::bps_to_gbps, unit_adjuster: adjuster::bps_to_gbps,
@@ -332,7 +332,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
control: PerformanceTestControl { control: PerformanceTestControl {
num_queues: Some(2), num_queues: Some(2),
queue_size: Some(256), queue_size: Some(256),
net_control: Some((false, true)), net_rx: Some(false),
..PerformanceTestControl::default() ..PerformanceTestControl::default()
}, },
unit_adjuster: adjuster::bps_to_gbps, unit_adjuster: adjuster::bps_to_gbps,
@@ -343,7 +343,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
control: PerformanceTestControl { control: PerformanceTestControl {
num_queues: Some(4), num_queues: Some(4),
queue_size: Some(256), queue_size: Some(256),
net_control: Some((true, true)), net_rx: Some(true),
..PerformanceTestControl::default() ..PerformanceTestControl::default()
}, },
unit_adjuster: adjuster::bps_to_gbps, unit_adjuster: adjuster::bps_to_gbps,
@@ -354,62 +354,18 @@ const TEST_LIST: [PerformanceTest; 29] = [
control: PerformanceTestControl { control: PerformanceTestControl {
num_queues: Some(4), num_queues: Some(4),
queue_size: Some(256), queue_size: Some(256),
net_control: Some((false, true)), net_rx: Some(false),
..PerformanceTestControl::default() ..PerformanceTestControl::default()
}, },
unit_adjuster: adjuster::bps_to_gbps, 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 { PerformanceTest {
name: "block_read_MiBps", name: "block_read_MiBps",
func_ptr: performance_block_io, func_ptr: performance_block_io,
control: PerformanceTestControl { control: PerformanceTestControl {
num_queues: Some(1), num_queues: Some(1),
queue_size: Some(128), queue_size: Some(128),
fio_control: Some((FioOps::Read, true)), fio_ops: Some(FioOps::Read),
..PerformanceTestControl::default() ..PerformanceTestControl::default()
}, },
unit_adjuster: adjuster::Bps_to_MiBps, unit_adjuster: adjuster::Bps_to_MiBps,
@@ -420,7 +376,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
control: PerformanceTestControl { control: PerformanceTestControl {
num_queues: Some(1), num_queues: Some(1),
queue_size: Some(128), queue_size: Some(128),
fio_control: Some((FioOps::Write, true)), fio_ops: Some(FioOps::Write),
..PerformanceTestControl::default() ..PerformanceTestControl::default()
}, },
unit_adjuster: adjuster::Bps_to_MiBps, unit_adjuster: adjuster::Bps_to_MiBps,
@@ -431,7 +387,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
control: PerformanceTestControl { control: PerformanceTestControl {
num_queues: Some(1), num_queues: Some(1),
queue_size: Some(128), queue_size: Some(128),
fio_control: Some((FioOps::RandomRead, true)), fio_ops: Some(FioOps::RandomRead),
..PerformanceTestControl::default() ..PerformanceTestControl::default()
}, },
unit_adjuster: adjuster::Bps_to_MiBps, unit_adjuster: adjuster::Bps_to_MiBps,
@@ -442,7 +398,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
control: PerformanceTestControl { control: PerformanceTestControl {
num_queues: Some(1), num_queues: Some(1),
queue_size: Some(128), queue_size: Some(128),
fio_control: Some((FioOps::RandomWrite, true)), fio_ops: Some(FioOps::RandomWrite),
..PerformanceTestControl::default() ..PerformanceTestControl::default()
}, },
unit_adjuster: adjuster::Bps_to_MiBps, unit_adjuster: adjuster::Bps_to_MiBps,
@@ -453,7 +409,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
control: PerformanceTestControl { control: PerformanceTestControl {
num_queues: Some(2), num_queues: Some(2),
queue_size: Some(128), queue_size: Some(128),
fio_control: Some((FioOps::Read, true)), fio_ops: Some(FioOps::Read),
..PerformanceTestControl::default() ..PerformanceTestControl::default()
}, },
unit_adjuster: adjuster::Bps_to_MiBps, unit_adjuster: adjuster::Bps_to_MiBps,
@@ -464,7 +420,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
control: PerformanceTestControl { control: PerformanceTestControl {
num_queues: Some(2), num_queues: Some(2),
queue_size: Some(128), queue_size: Some(128),
fio_control: Some((FioOps::Write, true)), fio_ops: Some(FioOps::Write),
..PerformanceTestControl::default() ..PerformanceTestControl::default()
}, },
unit_adjuster: adjuster::Bps_to_MiBps, unit_adjuster: adjuster::Bps_to_MiBps,
@@ -475,7 +431,7 @@ const TEST_LIST: [PerformanceTest; 29] = [
control: PerformanceTestControl { control: PerformanceTestControl {
num_queues: Some(2), num_queues: Some(2),
queue_size: Some(128), queue_size: Some(128),
fio_control: Some((FioOps::RandomRead, true)), fio_ops: Some(FioOps::RandomRead),
..PerformanceTestControl::default() ..PerformanceTestControl::default()
}, },
unit_adjuster: adjuster::Bps_to_MiBps, unit_adjuster: adjuster::Bps_to_MiBps,
@@ -486,99 +442,11 @@ const TEST_LIST: [PerformanceTest; 29] = [
control: PerformanceTestControl { control: PerformanceTestControl {
num_queues: Some(2), num_queues: Some(2),
queue_size: Some(128), queue_size: Some(128),
fio_control: Some((FioOps::RandomWrite, true)), fio_ops: Some(FioOps::RandomWrite),
..PerformanceTestControl::default() ..PerformanceTestControl::default()
}, },
unit_adjuster: adjuster::Bps_to_MiBps, 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( fn run_test_with_timeout(
@@ -629,7 +497,7 @@ fn date() -> String {
fn main() { fn main() {
let cmd_arguments = ClapCommand::new("performance-metrics") let cmd_arguments = ClapCommand::new("performance-metrics")
.version(env!("GIT_HUMAN_READABLE")) .version(env!("GIT_HUMAN_READABLE"))
.author(env!("CARGO_PKG_AUTHORS")) .author(crate_authors!())
.about("Generate the performance metrics data for Cloud Hypervisor") .about("Generate the performance metrics data for Cloud Hypervisor")
.arg( .arg(
Arg::new("test-filter") Arg::new("test-filter")

View File

@@ -78,7 +78,7 @@ fn direct_kernel_boot_path() -> PathBuf {
pub fn performance_net_throughput(control: &PerformanceTestControl) -> f64 { pub fn performance_net_throughput(control: &PerformanceTestControl) -> f64 {
let test_timeout = control.test_timeout; 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 focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = performance_test_new_guest(Box::new(focal)); 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(|| { let r = std::panic::catch_unwind(|| {
guest.wait_vm_boot(None).unwrap(); 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(); let _ = child.kill();
@@ -335,7 +335,7 @@ pub fn performance_boot_time_pmem(control: &PerformanceTestControl) -> f64 {
pub fn performance_block_io(control: &PerformanceTestControl) -> f64 { pub fn performance_block_io(control: &PerformanceTestControl) -> f64 {
let test_timeout = control.test_timeout; let test_timeout = control.test_timeout;
let num_queues = control.num_queues.unwrap(); 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 focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = performance_test_new_guest(Box::new(focal)); let guest = performance_test_new_guest(Box::new(focal));
@@ -388,11 +388,7 @@ pub fn performance_block_io(control: &PerformanceTestControl) -> f64 {
.unwrap(); .unwrap();
// Parse fio output // Parse fio output
if *bandwidth { parse_fio_output(&output, fio_ops, num_queues).unwrap()
parse_fio_output(&output, fio_ops, num_queues).unwrap()
} else {
parse_fio_output_iops(&output, fio_ops, num_queues).unwrap()
}
}); });
let _ = child.kill(); let _ = child.kill();
@@ -429,7 +425,7 @@ mod tests {
} }
"#; "#;
assert_eq!( assert_eq!(
parse_iperf3_output(output.as_bytes(), true, true).unwrap(), parse_iperf3_output(output.as_bytes(), true).unwrap(),
23957198874.604115 23957198874.604115
); );
@@ -448,31 +444,9 @@ mod tests {
} }
"#; "#;
assert_eq!( assert_eq!(
parse_iperf3_output(output.as_bytes(), false, true).unwrap(), parse_iperf3_output(output.as_bytes(), false).unwrap(),
39520744482.79 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] #[test]

View File

@@ -10,7 +10,7 @@ path = "src/qcow.rs"
[dependencies] [dependencies]
byteorder = "1.4.3" byteorder = "1.4.3"
libc = "0.2.139" libc = "0.2.138"
log = "0.4.17" log = "0.4.17"
remain = "0.2.6" remain = "0.2.5"
vmm-sys-util = "0.11.0" vmm-sys-util = "0.11.0"

View File

@@ -231,7 +231,6 @@ impl Write for RawFile {
return Err(io::Error::last_os_error()); 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) }; let tmp_buf = unsafe { slice::from_raw_parts_mut(tmp_ptr, rounded_len) };
// This can eventually replaced with read_at once its interface // This can eventually replaced with read_at once its interface

View File

@@ -4,6 +4,6 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
libc = "0.2.139" libc = "0.2.138"
log = "0.4.17" log = "0.4.17"
vmm-sys-util = "0.11.0" vmm-sys-util = "0.11.0"

View File

@@ -343,7 +343,6 @@ impl RateLimiter {
let timer_fd = TimerFd::new()?; let timer_fd = TimerFd::new()?;
// Note: vmm_sys_util::TimerFd::new() open the fd w/o O_NONBLOCK. We manually add this flag // 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()`. // so that `Self::event_handler` won't be blocked with `vmm_sys_util::TimerFd::wait()`.
// SAFETY: FFI calls.
let ret = unsafe { let ret = unsafe {
let fd = timer_fd.as_raw_fd(); let fd = timer_fd.as_raw_fd();
let mut flags = libc::fcntl(fd, libc::F_GETFL); let mut flags = libc::fcntl(fd, libc::F_GETFL);

View File

@@ -1,15 +1,4 @@
- [v29.0](#v290) - [v28.2](#v282)
- [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.1](#v281) - [v28.1](#v281)
- [v28.0](#v280) - [v28.0](#v280)
- [Community Engagement (Reminder)](#community-engagement-reminder) - [Community Engagement (Reminder)](#community-engagement-reminder)
@@ -17,9 +6,9 @@
- [Virtualised TPM Support](#virtualised-tpm-support) - [Virtualised TPM Support](#virtualised-tpm-support)
- [Transparent Huge Page Support](#transparent-huge-page-support) - [Transparent Huge Page Support](#transparent-huge-page-support)
- [README Quick Start Improved](#readme-quick-start-improved) - [README Quick Start Improved](#readme-quick-start-improved)
- [Notable Bug Fixes](#notable-bug-fixes-1) - [Notable Bug Fixes](#notable-bug-fixes)
- [Removals](#removals-1) - [Removals](#removals)
- [Contributors](#contributors-1) - [Contributors](#contributors)
- [v27.0](#v270) - [v27.0](#v270)
- [Community Engagement](#community-engagement) - [Community Engagement](#community-engagement)
- [Prebuilt Packages](#prebuilt-packages) - [Prebuilt Packages](#prebuilt-packages)
@@ -28,41 +17,41 @@
- [Simplified Build Feature Flags](#simplified-build-feature-flags) - [Simplified Build Feature Flags](#simplified-build-feature-flags)
- [Asynchronous Kernel Loading](#asynchronous-kernel-loading) - [Asynchronous Kernel Loading](#asynchronous-kernel-loading)
- [GDB Support for AArch64](#gdb-support-for-aarch64) - [GDB Support for AArch64](#gdb-support-for-aarch64)
- [Notable Bug Fixes](#notable-bug-fixes-2) - [Notable Bug Fixes](#notable-bug-fixes-1)
- [Deprecations](#deprecations-1) - [Deprecations](#deprecations)
- [Contributors](#contributors-2) - [Contributors](#contributors-1)
- [v26.0](#v260) - [v26.0](#v260)
- [SMBIOS Improvements via `--platform`](#smbios-improvements-via---platform) - [SMBIOS Improvements via `--platform`](#smbios-improvements-via---platform)
- [Unified Binary MSHV and KVM Support](#unified-binary-mshv-and-kvm-support) - [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) - [Notable Bug Fixes](#notable-bug-fixes-3)
- [Deprecations](#deprecations-2)
- [Removals](#removals-2) - [Removals](#removals-2)
- [Contributors](#contributors-3) - [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) - [v24.0](#v240)
- [Bypass Mode for `virtio-iommu`](#bypass-mode-for-virtio-iommu) - [Bypass Mode for `virtio-iommu`](#bypass-mode-for-virtio-iommu)
- [Ensure Identifiers Uniqueness](#ensure-identifiers-uniqueness) - [Ensure Identifiers Uniqueness](#ensure-identifiers-uniqueness)
- [Sparse Mmap support](#sparse-mmap-support) - [Sparse Mmap support](#sparse-mmap-support)
- [Expose Platform Serial Number](#expose-platform-serial-number) - [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) - [Notable Improvements](#notable-improvements)
- [Deprecations](#deprecations-3) - [Deprecations](#deprecations-2)
- [New on the Website](#new-on-the-website) - [New on the Website](#new-on-the-website)
- [Contributors](#contributors-5) - [Contributors](#contributors-4)
- [v23.1](#v231) - [v23.1](#v231)
- [v23.0](#v230) - [v23.0](#v230)
- [vDPA Support](#vdpa-support) - [vDPA Support](#vdpa-support)
- [Updated OS Support list](#updated-os-support-list) - [Updated OS Support list](#updated-os-support-list)
- [`AArch64` Memory Map Improvements](#aarch64-memory-map-improvements) - [`AArch64` Memory Map Improvements](#aarch64-memory-map-improvements)
- [`AMX` Support](#amx-support) - [`AMX` Support](#amx-support)
- [Notable Bug Fixes](#notable-bug-fixes-6) - [Notable Bug Fixes](#notable-bug-fixes-5)
- [Deprecations](#deprecations-4) - [Deprecations](#deprecations-3)
- [Contributors](#contributors-6) - [Contributors](#contributors-5)
- [v22.1](#v221) - [v22.1](#v221)
- [v22.0](#v220) - [v22.0](#v220)
- [GDB Debug Stub Support](#gdb-debug-stub-support) - [GDB Debug Stub Support](#gdb-debug-stub-support)
@@ -73,13 +62,13 @@
- [PMU Support for AArch64](#pmu-support-for-aarch64) - [PMU Support for AArch64](#pmu-support-for-aarch64)
- [Documentation Under CC-BY-4.0 License](#documentation-under-cc-by-40-license) - [Documentation Under CC-BY-4.0 License](#documentation-under-cc-by-40-license)
- [Deprecation of "Classic" `virtiofsd`](#deprecation-of-classic-virtiofsd) - [Deprecation of "Classic" `virtiofsd`](#deprecation-of-classic-virtiofsd)
- [Notable Bug Fixes](#notable-bug-fixes-7) - [Notable Bug Fixes](#notable-bug-fixes-6)
- [Contributors](#contributors-7) - [Contributors](#contributors-6)
- [v21.0](#v210) - [v21.0](#v210)
- [Efficient Local Live Migration (for Live Upgrade)](#efficient-local-live-migration-for-live-upgrade) - [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) - [Recommended Kernel is Now 5.15](#recommended-kernel-is-now-515)
- [Notable Bug fixes](#notable-bug-fixes-8) - [Notable Bug fixes](#notable-bug-fixes-7)
- [Contributors](#contributors-8) - [Contributors](#contributors-7)
- [v20.2](#v202) - [v20.2](#v202)
- [v20.1](#v201) - [v20.1](#v201)
- [v20.0](#v200) - [v20.0](#v200)
@@ -88,8 +77,8 @@
- [Improved VFIO support](#improved-vfio-support) - [Improved VFIO support](#improved-vfio-support)
- [Safer code](#safer-code) - [Safer code](#safer-code)
- [Extended documentation](#extended-documentation) - [Extended documentation](#extended-documentation)
- [Notable bug fixes](#notable-bug-fixes-9) - [Notable bug fixes](#notable-bug-fixes-8)
- [Contributors](#contributors-9) - [Contributors](#contributors-8)
- [v19.0](#v190) - [v19.0](#v190)
- [Improved PTY handling for serial and `virtio-console`](#improved-pty-handling-for-serial-and-virtio-console) - [Improved PTY handling for serial and `virtio-console`](#improved-pty-handling-for-serial-and-virtio-console)
- [PCI boot time optimisations](#pci-boot-time-optimisations) - [PCI boot time optimisations](#pci-boot-time-optimisations)
@@ -97,8 +86,8 @@
- [Live migration enhancements](#live-migration-enhancements) - [Live migration enhancements](#live-migration-enhancements)
- [`virtio-mem` support with `vfio-user`](#virtio-mem-support-with-vfio-user) - [`virtio-mem` support with `vfio-user`](#virtio-mem-support-with-vfio-user)
- [AArch64 for `virtio-iommu`](#aarch64-for-virtio-iommu) - [AArch64 for `virtio-iommu`](#aarch64-for-virtio-iommu)
- [Notable bug fixes](#notable-bug-fixes-10) - [Notable bug fixes](#notable-bug-fixes-9)
- [Contributors](#contributors-10) - [Contributors](#contributors-9)
- [v18.0](#v180) - [v18.0](#v180)
- [Experimental User Device (`vfio-user`) support](#experimental-user-device-vfio-user-support) - [Experimental User Device (`vfio-user`) support](#experimental-user-device-vfio-user-support)
- [Migration support for `vhost-user` devices](#migration-support-for-vhost-user-devices) - [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) - [Live migration on MSHV hypervisor](#live-migration-on-mshv-hypervisor)
- [AArch64 CPU topology support](#aarch64-cpu-topology-support) - [AArch64 CPU topology support](#aarch64-cpu-topology-support)
- [Power button support on AArch64](#power-button-support-on-aarch64) - [Power button support on AArch64](#power-button-support-on-aarch64)
- [Notable bug fixes](#notable-bug-fixes-11) - [Notable bug fixes](#notable-bug-fixes-10)
- [Contributors](#contributors-11) - [Contributors](#contributors-10)
- [v17.0](#v170) - [v17.0](#v170)
- [ARM64 NUMA support using ACPI](#arm64-numa-support-using-acpi) - [ARM64 NUMA support using ACPI](#arm64-numa-support-using-acpi)
- [`Seccomp` support for MSHV backend](#seccomp-support-for-mshv-backend) - [`Seccomp` support for MSHV backend](#seccomp-support-for-mshv-backend)
- [Hotplug of `macvtap` devices](#hotplug-of-macvtap-devices) - [Hotplug of `macvtap` devices](#hotplug-of-macvtap-devices)
- [Improved SGX support](#improved-sgx-support) - [Improved SGX support](#improved-sgx-support)
- [Inflight tracking for `vhost-user` devices](#inflight-tracking-for-vhost-user-devices) - [Inflight tracking for `vhost-user` devices](#inflight-tracking-for-vhost-user-devices)
- [Notable bug fixes](#notable-bug-fixes-12) - [Notable bug fixes](#notable-bug-fixes-11)
- [Contributors](#contributors-12) - [Contributors](#contributors-11)
- [v16.0](#v160) - [v16.0](#v160)
- [Improved live migration support](#improved-live-migration-support) - [Improved live migration support](#improved-live-migration-support)
- [Improved `vhost-user` support](#improved-vhost-user-support) - [Improved `vhost-user` support](#improved-vhost-user-support)
- [ARM64 ACPI and UEFI support](#arm64-acpi-and-uefi-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) - [Removed functionality](#removed-functionality)
- [Contributors](#contributors-13) - [Contributors](#contributors-12)
- [v15.0](#v150) - [v15.0](#v150)
- [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees) - [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees)
- [Network device rate limiting](#network-device-rate-limiting) - [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) - [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) - [`--api-socket` supports file descriptor parameter](#--api-socket-supports-file-descriptor-parameter)
- [Bug fixes](#bug-fixes) - [Bug fixes](#bug-fixes)
- [Deprecations](#deprecations-5) - [Deprecations](#deprecations-4)
- [Contributors](#contributors-14) - [Contributors](#contributors-13)
- [v0.14.1](#v0141) - [v0.14.1](#v0141)
- [v0.14.0](#v0140) - [v0.14.0](#v0140)
- [Structured event monitoring](#structured-event-monitoring) - [Structured event monitoring](#structured-event-monitoring)
@@ -141,8 +130,8 @@
- [Updated hotplug documentation](#updated-hotplug-documentation) - [Updated hotplug documentation](#updated-hotplug-documentation)
- [PTY control for serial and `virtio-console`](#pty-control-for-serial-and-virtio-console) - [PTY control for serial and `virtio-console`](#pty-control-for-serial-and-virtio-console)
- [Block device rate limiting](#block-device-rate-limiting) - [Block device rate limiting](#block-device-rate-limiting)
- [Deprecations](#deprecations-6) - [Deprecations](#deprecations-5)
- [Contributors](#contributors-15) - [Contributors](#contributors-14)
- [v0.13.0](#v0130) - [v0.13.0](#v0130)
- [Wider VFIO device support](#wider-vfio-device-support) - [Wider VFIO device support](#wider-vfio-device-support)
- [Improved huge page support](#improved-huge-page-support) - [Improved huge page support](#improved-huge-page-support)
@@ -150,13 +139,13 @@
- [VHD disk image support](#vhd-disk-image-support) - [VHD disk image support](#vhd-disk-image-support)
- [Improved Virtio device threading](#improved-virtio-device-threading) - [Improved Virtio device threading](#improved-virtio-device-threading)
- [Clean shutdown support via synthetic power button](#clean-shutdown-support-via-synthetic-power-button) - [Clean shutdown support via synthetic power button](#clean-shutdown-support-via-synthetic-power-button)
- [Contributors](#contributors-16) - [Contributors](#contributors-15)
- [v0.12.0](#v0120) - [v0.12.0](#v0120)
- [ARM64 enhancements](#arm64-enhancements) - [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) - [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) - [Migration of `vhost-user-fs` backend](#migration-of-vhost-user-fs-backend)
- [Enhanced "info" API](#enhanced-info-api) - [Enhanced "info" API](#enhanced-info-api)
- [Contributors](#contributors-17) - [Contributors](#contributors-16)
- [v0.11.0](#v0110) - [v0.11.0](#v0110)
- [`io_uring` support by default for `virtio-block`](#io_uring-support-by-default-for-virtio-block) - [`io_uring` support by default for `virtio-block`](#io_uring-support-by-default-for-virtio-block)
- [Windows Guest Support](#windows-guest-support) - [Windows Guest Support](#windows-guest-support)
@@ -168,15 +157,15 @@
- [Default Log Level Changed](#default-log-level-changed) - [Default Log Level Changed](#default-log-level-changed)
- [New `--balloon` Parameter Added](#new---balloon-parameter-added) - [New `--balloon` Parameter Added](#new---balloon-parameter-added)
- [Experimental `virtio-watchdog` Support](#experimental-virtio-watchdog-support) - [Experimental `virtio-watchdog` Support](#experimental-virtio-watchdog-support)
- [Notable Bug Fixes](#notable-bug-fixes-14) - [Notable Bug Fixes](#notable-bug-fixes-13)
- [Contributors](#contributors-18) - [Contributors](#contributors-17)
- [v0.10.0](#v0100) - [v0.10.0](#v0100)
- [`virtio-block` Support for Multiple Descriptors](#virtio-block-support-for-multiple-descriptors) - [`virtio-block` Support for Multiple Descriptors](#virtio-block-support-for-multiple-descriptors)
- [Memory Zones](#memory-zones) - [Memory Zones](#memory-zones)
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements) - [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements)
- [Preliminary KVM HyperV Emulation Control](#preliminary-kvm-hyperv-emulation-control) - [Preliminary KVM HyperV Emulation Control](#preliminary-kvm-hyperv-emulation-control)
- [Notable Bug Fixes](#notable-bug-fixes-15) - [Notable Bug Fixes](#notable-bug-fixes-14)
- [Contributors](#contributors-19) - [Contributors](#contributors-18)
- [v0.9.0](#v090) - [v0.9.0](#v090)
- [`io_uring` Based Block Device Support](#io_uring-based-block-device-support) - [`io_uring` Based Block Device Support](#io_uring-based-block-device-support)
- [Block and Network Device Statistics](#block-and-network-device-statistics) - [Block and Network Device Statistics](#block-and-network-device-statistics)
@@ -189,17 +178,17 @@
- [Enhancements to ARM64 Support](#enhancements-to-arm64-support) - [Enhancements to ARM64 Support](#enhancements-to-arm64-support)
- [Intel SGX Support](#intel-sgx-support) - [Intel SGX Support](#intel-sgx-support)
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements-1) - [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements-1)
- [Notable Bug Fixes](#notable-bug-fixes-16) - [Notable Bug Fixes](#notable-bug-fixes-15)
- [Contributors](#contributors-20) - [Contributors](#contributors-19)
- [v0.8.0](#v080) - [v0.8.0](#v080)
- [Experimental Snapshot and Restore Support](#experimental-snapshot-and-restore-support) - [Experimental Snapshot and Restore Support](#experimental-snapshot-and-restore-support)
- [Experimental ARM64 Support](#experimental-arm64-support) - [Experimental ARM64 Support](#experimental-arm64-support)
- [Support for Using 5-level Paging in Guests](#support-for-using-5-level-paging-in-guests) - [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) - [Virtio Device Interrupt Suppression for Network Devices](#virtio-device-interrupt-suppression-for-network-devices)
- [`vhost_user_fs` Improvements](#vhost_user_fs-improvements) - [`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) - [Command Line and API Changes](#command-line-and-api-changes)
- [Contributors](#contributors-21) - [Contributors](#contributors-20)
- [v0.7.0](#v070) - [v0.7.0](#v070)
- [Block, Network, Persistent Memory (PMEM), VirtioFS and Vsock hotplug](#block-network-persistent-memory-pmem-virtiofs-and-vsock-hotplug) - [Block, Network, Persistent Memory (PMEM), VirtioFS and Vsock hotplug](#block-network-persistent-memory-pmem-virtiofs-and-vsock-hotplug)
- [Alternative `libc` Support](#alternative-libc-support) - [Alternative `libc` Support](#alternative-libc-support)
@@ -209,14 +198,14 @@
- [`Seccomp` Sandboxing](#seccomp-sandboxing) - [`Seccomp` Sandboxing](#seccomp-sandboxing)
- [Updated Distribution Support](#updated-distribution-support) - [Updated Distribution Support](#updated-distribution-support)
- [Command Line and API Changes](#command-line-and-api-changes-1) - [Command Line and API Changes](#command-line-and-api-changes-1)
- [Contributors](#contributors-22) - [Contributors](#contributors-21)
- [v0.6.0](#v060) - [v0.6.0](#v060)
- [Directly Assigned Devices Hotplug](#directly-assigned-devices-hotplug) - [Directly Assigned Devices Hotplug](#directly-assigned-devices-hotplug)
- [Shared Filesystem Improvements](#shared-filesystem-improvements) - [Shared Filesystem Improvements](#shared-filesystem-improvements)
- [Block and Networking IO Self Offloading](#block-and-networking-io-self-offloading) - [Block and Networking IO Self Offloading](#block-and-networking-io-self-offloading)
- [Command Line Interface](#command-line-interface) - [Command Line Interface](#command-line-interface)
- [PVH Boot](#pvh-boot) - [PVH Boot](#pvh-boot)
- [Contributors](#contributors-23) - [Contributors](#contributors-22)
- [v0.5.1](#v051) - [v0.5.1](#v051)
- [v0.5.0](#v050) - [v0.5.0](#v050)
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing) - [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
@@ -224,7 +213,7 @@
- [New Interrupt Management Framework](#new-interrupt-management-framework) - [New Interrupt Management Framework](#new-interrupt-management-framework)
- [Development Tools](#development-tools) - [Development Tools](#development-tools)
- [Kata Containers Integration](#kata-containers-integration) - [Kata Containers Integration](#kata-containers-integration)
- [Contributors](#contributors-24) - [Contributors](#contributors-23)
- [v0.4.0](#v040) - [v0.4.0](#v040)
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition) - [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation) - [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
@@ -233,7 +222,7 @@
- [Userspace IOAPIC by default](#userspace-ioapic-by-default) - [Userspace IOAPIC by default](#userspace-ioapic-by-default)
- [PCI BAR reprogramming](#pci-bar-reprogramming) - [PCI BAR reprogramming](#pci-bar-reprogramming)
- [New `cloud-hypervisor` organization](#new-cloud-hypervisor-organization) - [New `cloud-hypervisor` organization](#new-cloud-hypervisor-organization)
- [Contributors](#contributors-25) - [Contributors](#contributors-24)
- [v0.3.0](#v030) - [v0.3.0](#v030)
- [Block device offloading](#block-device-offloading) - [Block device offloading](#block-device-offloading)
- [Network device backend](#network-device-backend) - [Network device backend](#network-device-backend)
@@ -260,93 +249,13 @@
- [Unit testing](#unit-testing) - [Unit testing](#unit-testing)
- [Integration tests parallelization](#integration-tests-parallelization) - [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 QCOW2 refcount table size (#5034)
* Fix unpause support on MSHV in dual binary (#5037) * Fix unpause support on MSHV in dual binary (#5037)
* Threads inside `virtio` devices are now shutdown on reboot (#5095) * 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 # v28.1
This is a bug fix release. The following issues have been addressed: This is a bug fix release. The following issues have been addressed:

View File

@@ -208,6 +208,8 @@ if [[ "$hypervisor" = "mshv" ]]; then
exit 1 exit 1
fi fi
# For now these values are deafult for kvm
features=""
# lock the workloads folder to avoid parallel updating by different containers # lock the workloads folder to avoid parallel updating by different containers
( (
@@ -231,7 +233,11 @@ fi
export RUST_BACKTRACE=1 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 # Enable KSM with some reasonable parameters so that it won't take too long
# for the memory to be merged between two processes. # 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 sudo chmod a+rwX /dev/hugepages
# Run all direct kernel boot (Device Tree) test cases in mod `parallel` # 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=$? RES=$?
# Run some tests in sequence since the result could be affected by other tests # Run some tests in sequence since the result could be affected by other tests
# running in parallel. # running in parallel.
if [ $RES -eq 0 ]; then 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=$? RES=$?
else else
exit $RES exit $RES
@@ -258,7 +264,7 @@ fi
# Run all ACPI test cases # Run all ACPI test cases
if [ $RES -eq 0 ]; then 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=$? RES=$?
else else
exit $RES exit $RES
@@ -266,14 +272,14 @@ fi
# Run all test cases related to live migration # Run all test cases related to live migration
if [ $RES -eq 0 ]; then 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=$? RES=$?
else else
exit $RES exit $RES
fi fi
if [ $RES -eq 0 ]; then 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=$? RES=$?
else else
exit $RES exit $RES

View File

@@ -12,10 +12,10 @@ mkdir -p "$WORKLOADS_DIR"
process_common_args "$@" process_common_args "$@"
# For now these values are default for kvm # For now these values are default for kvm
test_features="" features=""
if [ "$hypervisor" = "mshv" ] ; then if [ "$hypervisor" = "mshv" ] ; then
test_features="--no-default-features --features mshv" features="--no-default-features --features mshv"
fi fi
cp scripts/sha1sums-x86_64 $WORKLOADS_DIR 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/" CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
fi 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 # Test ovs-dpdk relies on hugepages
echo 6144 | sudo tee /proc/sys/vm/nr_hugepages echo 6144 | sudo tee /proc/sys/vm/nr_hugepages
sudo chmod a+rwX /dev/hugepages sudo chmod a+rwX /dev/hugepages
export RUST_BACKTRACE=1 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=$? RES=$?
# Run some tests in sequence since the result could be affected by other tests # Run some tests in sequence since the result could be affected by other tests
# running in parallel. # running in parallel.
if [ $RES -eq 0 ]; then if [ $RES -eq 0 ]; then
export RUST_BACKTRACE=1 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=$? RES=$?
fi fi

View File

@@ -12,10 +12,10 @@ mkdir -p "$WORKLOADS_DIR"
process_common_args "$@" process_common_args "$@"
# For now these values are default for kvm # For now these values are default for kvm
test_features="" features=""
if [ "$hypervisor" = "mshv" ] ; then if [ "$hypervisor" = "mshv" ] ; then
test_features="--no-default-features --features mshv" features="--no-default-features --features mshv"
fi fi
cp scripts/sha1sums-x86_64 $WORKLOADS_DIR 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/" CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
fi 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 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=$? RES=$?
exit $RES exit $RES

View File

@@ -11,32 +11,8 @@ if [[ "$hypervisor" = "mshv" ]]; then
exit 1 exit 1
fi fi
WORKLOADS_DIR="$HOME/workloads" # For now these values are default for kvm
mkdir -p "$WORKLOADS_DIR" features=""
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
BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}" BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
CFLAGS="" CFLAGS=""
@@ -46,11 +22,12 @@ TARGET_CC="musl-gcc"
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/" CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
fi 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 export RUST_BACKTRACE=1
time cargo test "sgx::$test_filter" -- ${test_binary_args[*]} time cargo test $features "sgx::$test_filter" -- ${test_binary_args[*]}
RES=$? RES=$?
exit $RES exit $RES

View File

@@ -5,16 +5,14 @@ source $HOME/.cargo/env
source $(dirname "$0")/test-util.sh source $(dirname "$0")/test-util.sh
process_common_args "$@" process_common_args "$@"
# For now these values are default for kvm
features=""
WORKLOADS_DIR="$HOME/workloads" WORKLOADS_DIR="$HOME/workloads"
FOCAL_OS_IMAGE_NAME="focal-server-cloudimg-amd64-custom-20210609-0.raw" FOCAL_OS_IMAGE_NAME="focal-server-cloudimg-amd64-custom-20210609-0.raw"
FOCAL_OS_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_IMAGE_NAME" FOCAL_OS_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_IMAGE_NAME"
FW="$WORKLOADS_DIR/hypervisor-fw" FW="$WORKLOADS_DIR/hypervisor-fw"
VMLINUX_IMAGE="$WORKLOADS_DIR/vmlinux" VMLINUX_IMAGE="$WORKLOADS_DIR/vmlinux"
if [ ! -f "$VMLINUX_IMAGE" ]; then
build_custom_linux
fi
BLK_IMAGE="$WORKLOADS_DIR/blk.img" BLK_IMAGE="$WORKLOADS_DIR/blk.img"
MNT_DIR="mount_image" MNT_DIR="mount_image"
@@ -45,7 +43,9 @@ TARGET_CC="musl-gcc"
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/" CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
fi 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. # We always copy a fresh version of our binary for our L2 guest.
cp target/$BUILD_TARGET/release/cloud-hypervisor $VFIO_DIR 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 sudo chmod a+rwX /dev/hugepages
export RUST_BACKTRACE=1 export RUST_BACKTRACE=1
time cargo test "vfio::test_vfio" -- ${test_binary_args[*]} time cargo test $features "vfio::test_vfio" -- ${test_binary_args[*]}
RES=$? RES=$?
if [ $RES -eq 0 ]; then 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=$? RES=$?
fi fi

View File

@@ -6,6 +6,8 @@ source $(dirname "$0")/test-util.sh
source $(dirname "$0")/common-aarch64.sh source $(dirname "$0")/common-aarch64.sh
process_common_args "$@" process_common_args "$@"
# For now these values are default for kvm
features=""
# aarch64 not supported for MSHV # aarch64 not supported for MSHV
if [[ "$hypervisor" = "mshv" ]]; then if [[ "$hypervisor" = "mshv" ]]; then
@@ -44,11 +46,12 @@ dmsetup mknodes
export RUST_BACKTRACE=1 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 # Only run with 1 thread to avoid tests interfering with one another because
# Windows has a static IP configured # 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=$? RES=$?
dmsetup remove_all -f dmsetup remove_all -f

View File

@@ -6,10 +6,10 @@ source $(dirname "$0")/test-util.sh
process_common_args "$@" process_common_args "$@"
# For now these values are default for kvm # For now these values are default for kvm
test_features="" features=""
if [ "$hypervisor" = "mshv" ] ; then if [ "$hypervisor" = "mshv" ] ; then
test_features="--no-default-features --features mshv" features="--no-default-features --features mshv"
fi fi
WIN_IMAGE_FILE="/root/workloads/windows-server-2019.raw" 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 create windows-snapshot-base --table "0 $img_blk_size snapshot-origin /dev/mapper/windows-base"
dmsetup mknodes 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 export RUST_BACKTRACE=1
# Only run with 1 thread to avoid tests interfering with one another because # Only run with 1 thread to avoid tests interfering with one another because
# Windows has a static IP configured # 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=$? RES=$?
dmsetup remove_all -f dmsetup remove_all -f

View File

@@ -12,10 +12,10 @@ mkdir -p "$WORKLOADS_DIR"
process_common_args "$@" process_common_args "$@"
# For now these values are default for kvm # For now these values are default for kvm
test_features="" features=""
if [ "$hypervisor" = "mshv" ] ; then if [ "$hypervisor" = "mshv" ] ; then
test_features="--no-default-features --features mshv" features="--no-default-features --features mshv"
fi fi
cp scripts/sha1sums-x86_64 $WORKLOADS_DIR cp scripts/sha1sums-x86_64 $WORKLOADS_DIR
@@ -36,6 +36,24 @@ if [ ! -f "$OVMF_FW" ]; then
popd popd
fi 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_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_URL="https://cloud-hypervisor.azureedge.net/$FOCAL_OS_IMAGE_NAME"
FOCAL_OS_IMAGE="$WORKLOADS_DIR/$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 popd
fi 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_URL="https://cloud-hypervisor.azureedge.net/$JAMMY_OS_IMAGE_NAME"
JAMMY_OS_IMAGE="$WORKLOADS_DIR/$JAMMY_OS_IMAGE_NAME" JAMMY_OS_IMAGE="$WORKLOADS_DIR/$JAMMY_OS_IMAGE_NAME"
if [ ! -f "$JAMMY_OS_IMAGE" ]; then if [ ! -f "$JAMMY_OS_IMAGE" ]; then
@@ -62,7 +80,7 @@ if [ ! -f "$JAMMY_OS_IMAGE" ]; then
popd popd
fi 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" JAMMY_OS_RAW_IMAGE="$WORKLOADS_DIR/$JAMMY_OS_RAW_IMAGE_NAME"
if [ ! -f "$JAMMY_OS_RAW_IMAGE" ]; then if [ ! -f "$JAMMY_OS_RAW_IMAGE" ]; then
pushd $WORKLOADS_DIR pushd $WORKLOADS_DIR
@@ -156,7 +174,10 @@ cp $VMLINUX_IMAGE $VFIO_DIR || exit 1
BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}" 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. # We always copy a fresh version of our binary for our L2 guest.
cp target/$BUILD_TARGET/release/cloud-hypervisor $VFIO_DIR cp target/$BUILD_TARGET/release/cloud-hypervisor $VFIO_DIR
@@ -176,14 +197,14 @@ sudo chmod a+rwX /dev/hugepages
ulimit -l unlimited ulimit -l unlimited
export RUST_BACKTRACE=1 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=$? RES=$?
# Run some tests in sequence since the result could be affected by other tests # Run some tests in sequence since the result could be affected by other tests
# running in parallel. # running in parallel.
if [ $RES -eq 0 ]; then if [ $RES -eq 0 ]; then
export RUST_BACKTRACE=1 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=$? RES=$?
fi fi

View File

@@ -28,6 +28,13 @@ build_fio() {
process_common_args "$@" 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 cp scripts/sha1sums-${TEST_ARCH} $WORKLOADS_DIR
if [ ${TEST_ARCH} == "aarch64" ]; then 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/" CFLAGS="-I /usr/include/${TEST_ARCH}-linux-musl/ -idirafter /usr/include/"
fi 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 # setup hugepages
echo 6144 | sudo tee /proc/sys/vm/nr_hugepages echo 6144 | sudo tee /proc/sys/vm/nr_hugepages

View File

@@ -1,5 +1,7 @@
27f3b17962ace69b51f0ddc2012095e3109e6ed8 bionic-server-cloudimg-amd64.qcow2
8db9cc58b01452ce2d06c313177e6e74d8582d93 bionic-server-cloudimg-amd64.raw
d4a44acc6014d5f83dea1c625c43d677a95fa75f alpine-minirootfs-x86_64.tar.gz d4a44acc6014d5f83dea1c625c43d677a95fa75f alpine-minirootfs-x86_64.tar.gz
f1eccdc5e1b515dbad294426ab081b47ebfb97c0 focal-server-cloudimg-amd64-custom-20210609-0.qcow2 f1eccdc5e1b515dbad294426ab081b47ebfb97c0 focal-server-cloudimg-amd64-custom-20210609-0.qcow2
7f5a8358243a96adf61f5c20139b29f308f2c0e3 focal-server-cloudimg-amd64-custom-20210609-0.raw 7f5a8358243a96adf61f5c20139b29f308f2c0e3 focal-server-cloudimg-amd64-custom-20210609-0.raw
b12e52203bc78400b2f099420b411b49ef4be7b8 jammy-server-cloudimg-amd64-custom-20221118-1.qcow2 272102ac243e544bc5c2303f383eeb217bd2e446 jammy-server-cloudimg-amd64-custom-20220329-0.qcow2
d843d5b249e8db3c0158eaf56ade895ed10342b5 jammy-server-cloudimg-amd64-custom-20221118-1.raw 0b5b4816a0976c458f8e8bab061e9d63ebc1b806 jammy-server-cloudimg-amd64-custom-20220329-0.raw

View File

@@ -3,9 +3,11 @@
// SPDX-License-Identifier: Apache-2.0 // 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;
use api_client::simple_api_command_with_fds; use api_client::simple_api_command_with_fds;
use api_client::simple_api_full_command;
use api_client::Error as ApiClientError; use api_client::Error as ApiClientError;
use clap::{Arg, ArgAction, ArgMatches, Command}; use clap::{Arg, ArgAction, ArgMatches, Command};
use option_parser::{ByteSized, ByteSizedParseError}; use option_parser::{ByteSized, ByteSizedParseError};
@@ -336,11 +338,6 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
Some("counters") => { Some("counters") => {
simple_api_command(&mut socket, "GET", "counters", None).map_err(Error::ApiClient) 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( Some("resize") => resize_api_command(
&mut socket, &mut socket,
matches matches
@@ -503,7 +500,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
fn main() { fn main() {
let app = Command::new("ch-remote") let app = Command::new("ch-remote")
.author(env!("CARGO_PKG_AUTHORS")) .author(crate_authors!())
.subcommand_required(true) .subcommand_required(true)
.about("Remotely control a cloud-hypervisor VMM.") .about("Remotely control a cloud-hypervisor VMM.")
.arg( .arg(
@@ -678,9 +675,7 @@ fn main() {
Command::new("create") Command::new("create")
.about("Create VM from a JSON configuration") .about("Create VM from a JSON configuration")
.arg(Arg::new("path").index(1).default_value("-")), .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(); let matches = app.get_matches();

View File

@@ -3,6 +3,8 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// //
#[macro_use(crate_authors)]
extern crate clap;
#[macro_use] #[macro_use]
extern crate event_monitor; extern crate event_monitor;
@@ -89,7 +91,7 @@ impl log::Log for Logger {
if record.file().is_some() && record.line().is_some() { if record.file().is_some() && record.line().is_some() {
writeln!( writeln!(
*(*(self.output.lock().unwrap())), *(*(self.output.lock().unwrap())),
"cloud-hypervisor: {:.6?}: <{}> {}:{}:{} -- {}", "cloud-hypervisor: {:?}: <{}> {}:{}:{} -- {}",
duration, duration,
std::thread::current().name().unwrap_or("anonymous"), std::thread::current().name().unwrap_or("anonymous"),
record.level(), record.level(),
@@ -100,7 +102,7 @@ impl log::Log for Logger {
} else { } else {
writeln!( writeln!(
*(*(self.output.lock().unwrap())), *(*(self.output.lock().unwrap())),
"cloud-hypervisor: {:.6?}: <{}> {}:{} -- {}", "cloud-hypervisor: {:?}: <{}> {}:{} -- {}",
duration, duration,
std::thread::current().name().unwrap_or("anonymous"), std::thread::current().name().unwrap_or("anonymous"),
record.level(), 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 { fn create_app(default_vcpus: String, default_memory: String, default_rng: String) -> Command {
#[allow(clippy::let_and_return)]
let app = Command::new("cloud-hypervisor") let app = Command::new("cloud-hypervisor")
// 'BUILT_VERSION' is set by the build script 'build.rs' at // 'BUILT_VERSION' is set by the build script 'build.rs' at
// compile time // compile time
.version(env!("BUILT_VERSION")) .version(env!("BUILT_VERSION"))
.author(env!("CARGO_PKG_AUTHORS")) .author(crate_authors!())
.about("Launch a cloud-hypervisor VMM.") .about("Launch a cloud-hypervisor VMM.")
.group(ArgGroup::new("vm-config").multiple(true)) .group(ArgGroup::new("vm-config").multiple(true))
.group(ArgGroup::new("vmm-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") .convert("fd")
.map_err(Error::ParsingEventMonitor)? .map_err(Error::ParsingEventMonitor)?
.unwrap(); .unwrap();
// SAFETY: fd is valid
unsafe { File::from_raw_fd(fd) } unsafe { File::from_raw_fd(fd) }
} else if parser.is_set("path") { } else if parser.is_set("path") {
std::fs::OpenOptions::new() std::fs::OpenOptions::new()
@@ -592,7 +592,6 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
fn main() { fn main() {
// Ensure all created files (.e.g sockets) are only accessible by this user // Ensure all created files (.e.g sockets) are only accessible by this user
// SAFETY: trivially safe
let _ = unsafe { libc::umask(0o077) }; let _ = unsafe { libc::umask(0o077) };
let (default_vcpus, default_memory, default_rng) = prepare_default_values(); 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; let on_tty = unsafe { libc::isatty(libc::STDIN_FILENO) } != 0;
if on_tty { if on_tty {
// Don't forget to set the terminal in canonical mode // Don't forget to set the terminal in canonical mode

View File

@@ -7,9 +7,9 @@ edition = "2021"
[dependencies] [dependencies]
dirs = "4.0.0" dirs = "4.0.0"
epoll = "4.3.1" epoll = "4.3.1"
libc = "0.2.139" libc = "0.2.138"
once_cell = "1.16.0" 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" serde_json = "1.0.89"
ssh2 = { version = "0.9.3", features = ["vendored-openssl"] } ssh2 = { version = "0.9.3", features = ["vendored-openssl"] }
vmm-sys-util = "0.11.0" vmm-sys-util = "0.11.0"

View File

@@ -3,8 +3,6 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// //
#![allow(clippy::undocumented_unsafe_blocks)]
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use serde_json::Value; use serde_json::Value;
use ssh2::Session; use ssh2::Session;
@@ -1052,7 +1050,27 @@ impl Guest {
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
pub fn check_nvidia_gpu(&self) { 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>) { 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(|| { std::panic::catch_unwind(|| {
let s = String::from_utf8_lossy(output); let s = String::from_utf8_lossy(output);
let v: Value = serde_json::from_str(&s).expect("'iperf3' parse error: invalid json output"); let v: Value = serde_json::from_str(&s).expect("'iperf3' parse error: invalid json output");
if bandwidth { let bps: f64 = if sender {
if sender { v["end"]["sum_sent"]["bits_per_second"]
v["end"]["sum_sent"]["bits_per_second"] .as_f64()
.as_f64() .expect("'iperf3' parse error: missing entry 'end.sum_sent.bits_per_second'")
.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'",
)
}
} else { } 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"] bps
.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
}
}) })
.map_err(|_| { .map_err(|_| {
eprintln!( eprintln!(
@@ -1509,7 +1513,6 @@ pub fn measure_virtio_net_throughput(
queue_pairs: u32, queue_pairs: u32,
guest: &Guest, guest: &Guest,
receive: bool, receive: bool,
bandwidth: bool,
) -> Result<f64, Error> { ) -> Result<f64, Error> {
let default_port = 5201; let default_port = 5201;
@@ -1532,19 +1535,12 @@ pub fn measure_virtio_net_throughput(
&format!("{}", default_port + n), &format!("{}", default_port + n),
"-t", "-t",
&format!("{test_timeout}"), &format!("{test_timeout}"),
"-i",
"0",
]); ]);
// For measuring the guest transmit throughput (as a sender), // For measuring the guest transmit throughput (as a sender),
// use reverse mode of the iperf3 client on the host // use reverse mode of the iperf3 client on the host
if !receive { if !receive {
cmd.args(["-R"]); 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 let client = cmd
.stderr(Stdio::piped()) .stderr(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
@@ -1555,7 +1551,7 @@ pub fn measure_virtio_net_throughput(
} }
let mut err: Option<Error> = None; let mut err: Option<Error> = None;
let mut results = Vec::new(); let mut bps = Vec::new();
let mut failed = false; let mut failed = false;
for c in clients { for c in clients {
let mut c = c; let mut c = c;
@@ -1567,7 +1563,7 @@ pub fn measure_virtio_net_throughput(
if !failed { if !failed {
// Safe to unwrap as we know the child has terminated succesffully // Safe to unwrap as we know the child has terminated succesffully
let output = c.wait_with_output().unwrap(); 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 { } else {
let _ = c.kill(); let _ = c.kill();
let output = c.wait_with_output().unwrap(); let output = c.wait_with_output().unwrap();
@@ -1581,7 +1577,7 @@ pub fn measure_virtio_net_throughput(
if let Some(e) = err { if let Some(e) = err {
Err(e) Err(e)
} else { } else {
Ok(results.iter().sum()) Ok(bps.iter().sum())
} }
} }

View File

@@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// //
#![allow(clippy::undocumented_unsafe_blocks)]
// When enabling the `mshv` feature, we skip quite some tests and // When enabling the `mshv` feature, we skip quite some tests and
// hence have known dead-code. This annotation silences dead-code // hence have known dead-code. This annotation silences dead-code
// related warnings for our quality workflow to pass. // related warnings for our quality workflow to pass.
@@ -32,12 +32,14 @@ use wait_timeout::ChildExt;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
mod 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 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_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_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 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 WINDOWS_IMAGE_NAME: &str = "windows-server-2019.raw";
pub const OVMF_NAME: &str = "CLOUDHV.fd"; pub const OVMF_NAME: &str = "CLOUDHV.fd";
pub const GREP_SERIAL_IRQ_CMD: &str = "grep -c 'IO-APIC.*ttyS0' /proc/interrupts || true"; pub const GREP_SERIAL_IRQ_CMD: &str = "grep -c 'IO-APIC.*ttyS0' /proc/interrupts || true";
@@ -1887,12 +1889,24 @@ mod common_parallel {
use crate::*; use crate::*;
#[test]
#[cfg(target_arch = "x86_64")]
fn test_bionic_hypervisor_fw() {
test_simple_launch(fw_path(FwType::RustHypervisorFirmware), BIONIC_IMAGE_NAME)
}
#[test] #[test]
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
fn test_focal_hypervisor_fw() { fn test_focal_hypervisor_fw() {
test_simple_launch(fw_path(FwType::RustHypervisorFirmware), FOCAL_IMAGE_NAME) 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] #[test]
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
fn test_focal_ovmf() { fn test_focal_ovmf() {
@@ -1961,7 +1975,7 @@ mod common_parallel {
let _ = guest.ssh_command("sudo systemctl stop snapd"); let _ = guest.ssh_command("sudo systemctl stop snapd");
guest.ssh_command("sudo poweroff").unwrap(); 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 = [ let latest_events = [
&MetaEvent { &MetaEvent {
event: "shutdown".to_string(), event: "shutdown".to_string(),
@@ -1987,10 +2001,8 @@ mod common_parallel {
#[test] #[test]
fn test_multi_cpu() { fn test_multi_cpu() {
let jammy_image = JAMMY_IMAGE_NAME.to_string(); let bionic = UbuntuDiskConfig::new(BIONIC_IMAGE_NAME.to_string());
let jammy = UbuntuDiskConfig::new(jammy_image); let guest = Guest::new(Box::new(bionic));
let guest = Guest::new(Box::new(jammy));
let mut cmd = GuestCommand::new(&guest); let mut cmd = GuestCommand::new(&guest);
cmd.args(["--cpus", "boot=2,max=4"]) cmd.args(["--cpus", "boot=2,max=4"])
.args(["--memory", "size=512M"]) .args(["--memory", "size=512M"])
@@ -2007,11 +2019,18 @@ mod common_parallel {
assert_eq!(guest.get_cpu_count().unwrap_or_default(), 2); assert_eq!(guest.get_cpu_count().unwrap_or_default(), 2);
#[cfg(target_arch = "x86_64")]
assert_eq!( assert_eq!(
guest guest
.ssh_command( .ssh_command(r#"dmesg | grep "smpboot: Allowing" | sed "s/\[\ *[0-9.]*\] //""#)
r#"sudo dmesg | grep "smp: Brought up" | 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() .unwrap()
.trim(), .trim(),
"smp: Brought up 1 node, 2 CPUs" "smp: Brought up 1 node, 2 CPUs"
@@ -5787,20 +5806,12 @@ mod common_parallel {
event: "starting".to_string(), event: "starting".to_string(),
device_id: None, 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 { &MetaEvent {
event: "restoring".to_string(), event: "restoring".to_string(),
device_id: None, device_id: None,
}, },
]; ];
assert!(check_sequential_events( assert!(check_sequential_events_exact(
&expected_events, &expected_events,
&event_path_restored &event_path_restored
)); ));
@@ -7709,14 +7720,19 @@ mod sgx {
#[test] #[test]
fn test_sgx() { fn test_sgx() {
let jammy_image = JAMMY_IMAGE_NAME.to_string(); let focal = UbuntuDiskConfig::new(FOCAL_SGX_IMAGE_NAME.to_string());
let jammy = UbuntuDiskConfig::new(jammy_image); let guest = Guest::new(Box::new(focal));
let guest = Guest::new(Box::new(jammy)); 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) let mut child = GuestCommand::new(&guest)
.args(["--cpus", "boot=1"]) .args(["--cpus", "boot=1"])
.args(["--memory", "size=512M"]) .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_disks()
.default_net() .default_net()
.args(["--sgx-epc", "id=epc0,size=64M"]) .args(["--sgx-epc", "id=epc0,size=64M"])
@@ -7738,6 +7754,18 @@ mod sgx {
.trim(), .trim(),
"0x0000000004000000" "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(); let _ = child.kill();
@@ -8014,8 +8042,8 @@ mod vfio {
} }
fn test_nvidia_card_memory_hotplug(hotplug_method: &str) { fn test_nvidia_card_memory_hotplug(hotplug_method: &str) {
let jammy = UbuntuDiskConfig::new(JAMMY_NVIDIA_IMAGE_NAME.to_string()); let hirsute = UbuntuDiskConfig::new(HIRSUTE_NVIDIA_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(jammy)); let guest = Guest::new(Box::new(hirsute));
let api_socket = temp_api_path(&guest.tmp_dir); let api_socket = temp_api_path(&guest.tmp_dir);
let mut child = GuestCommand::new(&guest) let mut child = GuestCommand::new(&guest)
@@ -8068,8 +8096,8 @@ mod vfio {
#[test] #[test]
fn test_nvidia_card_pci_hotplug() { fn test_nvidia_card_pci_hotplug() {
let jammy = UbuntuDiskConfig::new(JAMMY_NVIDIA_IMAGE_NAME.to_string()); let hirsute = UbuntuDiskConfig::new(HIRSUTE_NVIDIA_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(jammy)); let guest = Guest::new(Box::new(hirsute));
let api_socket = temp_api_path(&guest.tmp_dir); let api_socket = temp_api_path(&guest.tmp_dir);
let mut child = GuestCommand::new(&guest) let mut child = GuestCommand::new(&guest)
@@ -8110,8 +8138,8 @@ mod vfio {
#[test] #[test]
fn test_nvidia_card_reboot() { fn test_nvidia_card_reboot() {
let jammy = UbuntuDiskConfig::new(JAMMY_NVIDIA_IMAGE_NAME.to_string()); let hirsute = UbuntuDiskConfig::new(HIRSUTE_NVIDIA_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(jammy)); let guest = Guest::new(Box::new(hirsute));
let api_socket = temp_api_path(&guest.tmp_dir); let api_socket = temp_api_path(&guest.tmp_dir);
let mut child = GuestCommand::new(&guest) let mut child = GuestCommand::new(&guest)
@@ -9211,51 +9239,43 @@ mod live_migration {
} }
#[test] #[test]
#[ignore]
fn test_live_upgrade_basic() { fn test_live_upgrade_basic() {
_test_live_migration(true, false) _test_live_migration(true, false)
} }
#[test] #[test]
#[ignore]
fn test_live_upgrade_local() { fn test_live_upgrade_local() {
_test_live_migration(true, true) _test_live_migration(true, true)
} }
#[test] #[test]
#[ignore]
#[cfg(not(feature = "mshv"))] #[cfg(not(feature = "mshv"))]
fn test_live_upgrade_numa() { fn test_live_upgrade_numa() {
_test_live_migration_numa(true, false) _test_live_migration_numa(true, false)
} }
#[test] #[test]
#[ignore]
#[cfg(not(feature = "mshv"))] #[cfg(not(feature = "mshv"))]
fn test_live_upgrade_numa_local() { fn test_live_upgrade_numa_local() {
_test_live_migration_numa(true, true) _test_live_migration_numa(true, true)
} }
#[test] #[test]
#[ignore]
fn test_live_upgrade_watchdog() { fn test_live_upgrade_watchdog() {
_test_live_migration_watchdog(true, false) _test_live_migration_watchdog(true, false)
} }
#[test] #[test]
#[ignore]
fn test_live_upgrade_watchdog_local() { fn test_live_upgrade_watchdog_local() {
_test_live_migration_watchdog(true, true) _test_live_migration_watchdog(true, true)
} }
#[test] #[test]
#[ignore]
fn test_live_upgrade_balloon() { fn test_live_upgrade_balloon() {
_test_live_migration_balloon(true, false) _test_live_migration_balloon(true, false)
} }
#[test] #[test]
#[ignore]
fn test_live_upgrade_balloon_local() { fn test_live_upgrade_balloon_local() {
_test_live_migration_balloon(true, true) _test_live_migration_balloon(true, true)
} }
@@ -9282,7 +9302,6 @@ mod live_migration {
} }
#[test] #[test]
#[ignore]
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
#[cfg(not(feature = "mshv"))] #[cfg(not(feature = "mshv"))]
fn test_live_upgrade_ovs_dpdk() { fn test_live_upgrade_ovs_dpdk() {
@@ -9290,7 +9309,6 @@ mod live_migration {
} }
#[test] #[test]
#[ignore]
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
#[cfg(not(feature = "mshv"))] #[cfg(not(feature = "mshv"))]
fn test_live_upgrade_ovs_dpdk_local() { fn test_live_upgrade_ovs_dpdk_local() {
@@ -9422,8 +9440,7 @@ mod rate_limiter {
let r = std::panic::catch_unwind(|| { let r = std::panic::catch_unwind(|| {
guest.wait_vm_boot(None).unwrap(); guest.wait_vm_boot(None).unwrap();
let measured_bps = let measured_bps =
measure_virtio_net_throughput(test_timeout, num_queues / 2, &guest, rx, true) measure_virtio_net_throughput(test_timeout, num_queues / 2, &guest, rx).unwrap();
.unwrap();
assert!(check_rate_limit(measured_bps, limit_bps, 0.1)); assert!(check_rate_limit(measured_bps, limit_bps, 0.1));
}); });

View File

@@ -133,7 +133,7 @@ impl Emulator {
let mut res: PtmResult = 0; let mut res: PtmResult = 0;
let mut fds = [-1, -1]; 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 { unsafe {
let ret = libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()); let ret = libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr());
if ret == -1 { if ret == -1 {
@@ -289,7 +289,7 @@ impl Emulator {
/// Function to write to data socket and read the response from it /// Function to write to data socket and read the response from it
fn unix_tx_bufs(&mut self) -> Result<()> { fn unix_tx_bufs(&mut self) -> Result<()> {
let isselftest: bool; 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 addr: sockaddr_storage = unsafe { mem::zeroed() };
let mut len = mem::size_of::<sockaddr_storage>() as socklen_t; let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;
@@ -307,7 +307,7 @@ impl Emulator {
iov_len: cmd.input.len(), iov_len: cmd.input.len(),
}; 1]; }; 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() }; let mut msghdr: libc::msghdr = unsafe { mem::zeroed() };
msghdr.msg_name = ptr::null_mut(); msghdr.msg_name = ptr::null_mut();
msghdr.msg_namelen = 0; msghdr.msg_namelen = 0;
@@ -316,7 +316,7 @@ impl Emulator {
msghdr.msg_control = ptr::null_mut(); msghdr.msg_control = ptr::null_mut();
msghdr.msg_controllen = 0; msghdr.msg_controllen = 0;
msghdr.msg_flags = 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 { unsafe {
let ret = libc::sendmsg(self.data_fd, &msghdr, 0); let ret = libc::sendmsg(self.data_fd, &msghdr, 0);
if ret == -1 { if ret == -1 {
@@ -328,7 +328,7 @@ impl Emulator {
} }
cmd.output.fill(0); 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 { unsafe {
let ret = libc::recvfrom( let ret = libc::recvfrom(
self.data_fd, self.data_fd,

View File

@@ -5,10 +5,10 @@ authors = ["The Cloud Hypervisor Authors"]
edition = "2021" edition = "2021"
[dependencies] [dependencies]
libc = "0.2.139" libc = "0.2.138"
log = "0.4.17" log = "0.4.17"
once_cell = "1.16.0" 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" serde_json = "1.0.89"
[features] [features]

View File

@@ -30,7 +30,6 @@ impl Tracer {
fn end(&self) { fn end(&self) {
let end = Instant::now(); let end = Instant::now();
// SAFETY: FFI call
let path = format!("cloud-hypervisor-{}.trace", unsafe { libc::getpid() }); let path = format!("cloud-hypervisor-{}.trace", unsafe { libc::getpid() });
let mut file = File::create(&path).unwrap(); let mut file = File::create(&path).unwrap();
@@ -106,14 +105,11 @@ struct TraceEvent {
pub fn trace_point_log(event: &'static str) { pub fn trace_point_log(event: &'static str) {
let trace_event = TraceEvent { 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 }), timestamp: Instant::now().duration_since(unsafe { TRACER.get().unwrap().start }),
event, event,
end_timestamp: None, end_timestamp: None,
// SAFETY: thread_depth accesses current thread only specific data
depth: unsafe { TRACER.get().unwrap().thread_depth() }, depth: unsafe { TRACER.get().unwrap().thread_depth() },
}; };
// SAFETY: add_event accesses current thread only specific data
unsafe { unsafe {
TRACER.get_mut().unwrap().add_event(trace_event); TRACER.get_mut().unwrap().add_event(trace_event);
} }
@@ -126,7 +122,6 @@ pub struct TraceBlock {
impl TraceBlock { impl TraceBlock {
pub fn new(event: &'static str) -> Self { pub fn new(event: &'static str) -> Self {
// SAFETY: increase_thread_depth accesses current thread only specific data
unsafe { unsafe {
TRACER.get_mut().unwrap().increase_thread_depth(); TRACER.get_mut().unwrap().increase_thread_depth();
} }
@@ -139,16 +134,16 @@ impl TraceBlock {
impl Drop for TraceBlock { impl Drop for TraceBlock {
fn drop(&mut self) { 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 { let trace_event = TraceEvent {
timestamp: self.start.duration_since(start), timestamp: self
.start
.duration_since(unsafe { TRACER.get().unwrap().start }),
event: self.event, event: self.event,
end_timestamp: Some(Instant::now().duration_since(start)), end_timestamp: Some(
// SAFETY: thread_depth() returns a number local to the current thread Instant::now().duration_since(unsafe { TRACER.get().unwrap().start }),
),
depth: unsafe { TRACER.get().unwrap().thread_depth() }, depth: unsafe { TRACER.get().unwrap().thread_depth() },
}; };
// SAFETY: add_event and decrease_thread_depth access current thread only specific data
unsafe { unsafe {
TRACER.get_mut().unwrap().add_event(trace_event); TRACER.get_mut().unwrap().add_event(trace_event);
TRACER.get_mut().unwrap().decrease_thread_depth(); TRACER.get_mut().unwrap().decrease_thread_depth();
@@ -171,11 +166,9 @@ macro_rules! trace_scoped {
} }
pub fn end() { pub fn end() {
// SAFETY: this is called after all other threads end
unsafe { TRACER.get().unwrap().end() } unsafe { TRACER.get().unwrap().end() }
} }
pub fn start() { pub fn start() {
// SAFETY: this is called before other threads start
unsafe { TRACER.set(Tracer::new()).unwrap() } unsafe { TRACER.set(Tracer::new()).unwrap() }
} }

View File

@@ -5,12 +5,13 @@ authors = ["The Cloud Hypervisor Authors"]
edition = "2021" edition = "2021"
[dependencies] [dependencies]
libc = "0.2.139" libc = "0.2.138"
log = "0.4.17" log = "0.4.17"
serde = { version = "1.0.151", features = ["rc"] } serde = { version = "1.0.150", features = ["rc"] }
serde_derive = "1.0.149" serde_derive = "1.0.150"
serde_json = "1.0.89" 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"] } 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"] } vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic"] }
vmm-sys-util = "0.11.0" vmm-sys-util = "0.11.0"

View File

@@ -190,25 +190,16 @@ struct DeviceReset {
header: Header, 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 {} unsafe impl ByteValued for Header {}
// SAFETY: data structure only contain a sereis of integers
unsafe impl ByteValued for Version {} unsafe impl ByteValued for Version {}
// SAFETY: data structure only contain a sereis of integers
unsafe impl ByteValued for DmaMap {} unsafe impl ByteValued for DmaMap {}
// SAFETY: data structure only contain a sereis of integers
unsafe impl ByteValued for DmaUnmap {} unsafe impl ByteValued for DmaUnmap {}
// SAFETY: data structure only contain a sereis of integers
unsafe impl ByteValued for DeviceGetInfo {} unsafe impl ByteValued for DeviceGetInfo {}
// SAFETY: data structure only contain a sereis of integers
unsafe impl ByteValued for DeviceGetRegionInfo {} unsafe impl ByteValued for DeviceGetRegionInfo {}
// SAFETY: data structure only contain a sereis of integers
unsafe impl ByteValued for RegionAccess {} unsafe impl ByteValued for RegionAccess {}
// SAFETY: data structure only contain a sereis of integers
unsafe impl ByteValued for GetIrqInfo {} unsafe impl ByteValued for GetIrqInfo {}
// SAFETY: data structure only contain a sereis of integers
unsafe impl ByteValued for SetIrqs {} unsafe impl ByteValued for SetIrqs {}
// SAFETY: data structure only contain a sereis of integers
unsafe impl ByteValued for DeviceReset {} unsafe impl ByteValued for DeviceReset {}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@@ -600,9 +591,8 @@ impl Client {
break; 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) }; 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) }; let cap_header = unsafe { &*(cap_ptr as *const vfio_info_cap_header) };
match cap_header.id as u32 { match cap_header.id as u32 {
VFIO_REGION_INFO_CAP_SPARSE_MMAP => { VFIO_REGION_INFO_CAP_SPARSE_MMAP => {
@@ -614,7 +604,7 @@ impl Client {
); );
break; 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 { let sparse_mmap = unsafe {
&*(cap_ptr as *mut u8 as *const vfio_region_info_cap_sparse_mmap) &*(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); cap_offset, mmap_cap_size, area_num, mmap_area_size, cap_size);
break; break;
} }
// Safe because the `sparse_mmap` is valid and its size is also checked above
let areas = 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) }; unsafe { sparse_mmap.areas.as_slice(sparse_mmap.nr_areas as usize) };
for area in areas.iter() { for area in areas.iter() {
sparse_areas.push(*area); sparse_areas.push(*area);

View File

@@ -8,8 +8,8 @@ license = "Apache-2.0"
[dependencies] [dependencies]
byteorder = "1.4.3" byteorder = "1.4.3"
crc32c = "0.6.3" crc32c = "0.6.3"
libc = "0.2.139" libc = "0.2.138"
log = "0.4.17" log = "0.4.17"
remain = "0.2.6" remain = "0.2.5"
thiserror = "1.0.38" thiserror = "1.0.37"
uuid = { version = "1.2.2", features = ["v4"] } uuid = { version = "1.2.2", features = ["v4"] }

View File

@@ -136,7 +136,6 @@ impl Header {
f.read_exact(&mut buffer) f.read_exact(&mut buffer)
.map_err(VhdxHeaderError::ReadHeader)?; .map_err(VhdxHeaderError::ReadHeader)?;
// SAFETY: buffer is of correct size and has been successfully filled.
let header = unsafe { *(buffer.as_ptr() as *mut Header) }; let header = unsafe { *(buffer.as_ptr() as *mut Header) };
if header.signature != HEADER_SIGN { if header.signature != HEADER_SIGN {
return Err(VhdxHeaderError::InvalidHeaderSign); return Err(VhdxHeaderError::InvalidHeaderSign);
@@ -152,7 +151,6 @@ impl Header {
/// Converts the header structure into a buffer /// Converts the header structure into a buffer
fn get_header_as_buffer(&self, buffer: &mut [u8; HEADER_SIZE as usize]) { fn get_header_as_buffer(&self, buffer: &mut [u8; HEADER_SIZE as usize]) {
// SAFETY: self is a valid header.
let reference = unsafe { let reference = unsafe {
std::slice::from_raw_parts(self as *const Header as *const u8, HEADER_SIZE as usize) 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) f.read_exact(&mut buffer)
.map_err(VhdxHeaderError::ReadRegionTableHeader)?; .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) }; let region_table_header = unsafe { *(buffer.as_ptr() as *mut RegionTableHeader) };
if region_table_header.signature != REGION_SIGN { if region_table_header.signature != REGION_SIGN {
return Err(VhdxHeaderError::InvalidRegionSign); return Err(VhdxHeaderError::InvalidRegionSign);
@@ -340,8 +337,6 @@ pub struct RegionTableEntry {
impl RegionTableEntry { impl RegionTableEntry {
/// Reads one Region Entry from a Region Table index that starts from 0 /// Reads one Region Entry from a Region Table index that starts from 0
pub fn new(buffer: &[u8]) -> Result<RegionTableEntry> { 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 mut region_table_entry = unsafe { *(buffer.as_ptr() as *mut RegionTableEntry) };
let uuid = crate::uuid_from_guid(buffer); let uuid = crate::uuid_from_guid(buffer);

Some files were not shown because too many files have changed in this diff Show More