mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e01933c8ea | ||
|
|
c6307865eb | ||
|
|
6275d4f87e | ||
|
|
4dfe1ff77a | ||
|
|
bbda07c388 | ||
|
|
366223c9f4 | ||
|
|
375356a097 |
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -4,7 +4,7 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 1
|
||||
open-pull-requests-limit: 3
|
||||
allow:
|
||||
- dependency-type: direct
|
||||
- dependency-type: indirect
|
||||
@@ -12,7 +12,7 @@ updates:
|
||||
directory: "/fuzz"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 1
|
||||
open-pull-requests-limit: 3
|
||||
allow:
|
||||
- dependency-type: direct
|
||||
- dependency-type: indirect
|
||||
|
||||
33
.github/workflows/build.yaml
vendored
33
.github/workflows/build.yaml
vendored
@@ -13,7 +13,6 @@ jobs:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
- "1.60"
|
||||
target:
|
||||
- x86_64-unknown-linux-gnu
|
||||
- x86_64-unknown-linux-musl
|
||||
@@ -23,9 +22,6 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install musl-gcc
|
||||
run: sudo apt install -y musl-tools
|
||||
|
||||
- name: Install Rust toolchain (${{ matrix.rust }})
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -35,29 +31,26 @@ jobs:
|
||||
|
||||
- name: Debug Check (default features)
|
||||
run: |
|
||||
git rev-list origin/main..$GITHUB_SHA | xargs -t -I % sh -c 'git checkout %; cargo check --tests --all --target=${{ matrix.target }}'
|
||||
git rev-list origin/main..$GITHUB_SHA | xargs -t -I % sh -c 'git checkout %; cargo check --all --target=${{ matrix.target }}'
|
||||
git checkout $GITHUB_SHA
|
||||
|
||||
- name: Build (default features)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor -- -D warnings
|
||||
- name: Build (default + tdx)
|
||||
run: cargo rustc --bin cloud-hypervisor --features "tdx" -- -D warnings
|
||||
|
||||
- name: Build (common + kvm)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "common,kvm" -- -D warnings
|
||||
- name: Build (acpi,kvm)
|
||||
run: cargo rustc --bin cloud-hypervisor --no-default-features --features "acpi,kvm" -- -D warnings
|
||||
|
||||
- name: Build (default features + tdx)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "tdx" -- -D warnings
|
||||
- name: Build (kvm)
|
||||
run: cargo rustc --bin cloud-hypervisor --no-default-features --features "kvm" -- -D warnings
|
||||
|
||||
- name: Build (default features + amx)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "amx" -- -D warnings
|
||||
|
||||
- name: Build (default features + gdb)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "gdb" -- -D warnings
|
||||
|
||||
- name: Build (common + mshv)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "common,mshv" -- -D warnings
|
||||
- name: Build (acpi,mshv)
|
||||
run: cargo rustc --bin cloud-hypervisor --no-default-features --features "acpi,mshv" -- -D warnings
|
||||
|
||||
- name: Build (mshv)
|
||||
run: cargo rustc --bin cloud-hypervisor --no-default-features --features "mshv" -- -D warnings
|
||||
|
||||
- name: Release Build (default features)
|
||||
run: cargo build --locked --all --release --target=${{ matrix.target }}
|
||||
run: cargo build --all --release --target=${{ matrix.target }}
|
||||
|
||||
- name: Check build did not modify any files
|
||||
run: test -z "$(git status --porcelain)"
|
||||
|
||||
15
.github/workflows/docker-image.yaml
vendored
15
.github/workflows/docker-image.yaml
vendored
@@ -27,17 +27,6 @@ jobs:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
# list of Docker images to use as base name for tags
|
||||
images: cloudhypervisor/dev
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=raw,value={{date 'YYYYMMDD'}}-0
|
||||
type=sha
|
||||
|
||||
- name: Build and push
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: docker/build-push-action@v2
|
||||
@@ -45,7 +34,7 @@ jobs:
|
||||
file: ./resources/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
tags: cloudhypervisor/dev:latest
|
||||
|
||||
- name: Build only
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
@@ -53,7 +42,7 @@ jobs:
|
||||
with:
|
||||
file: ./resources/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
tags: cloudhypervisor/dev:latest
|
||||
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
|
||||
22
.github/workflows/quality-aarch64.yaml
vendored
22
.github/workflows/quality-aarch64.yaml
vendored
@@ -32,16 +32,30 @@ jobs:
|
||||
- name: Formatting (rustfmt)
|
||||
run: cargo fmt -- --check
|
||||
|
||||
- name: Clippy (common + kvm)
|
||||
- name: Clippy (kvm)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: true
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --tests --all --no-default-features --features "common,kvm" -- -D warnings
|
||||
args: --target=${{ matrix.target }} --all --no-default-features --features "kvm" -- -D warnings
|
||||
|
||||
- name: Clippy (default features)
|
||||
- name: Clippy (kvm,acpi)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: true
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --tests --all -- -D warnings
|
||||
args: --target=${{ matrix.target }} --all --no-default-features --features "kvm,acpi" -- -D warnings
|
||||
|
||||
- name: Clippy (all features,kvm)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: true
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --all --no-default-features --features "common,kvm" -- -D warnings
|
||||
|
||||
- name: Clippy (default)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: true
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --all -- -D warnings
|
||||
|
||||
29
.github/workflows/quality.yaml
vendored
29
.github/workflows/quality.yaml
vendored
@@ -33,20 +33,29 @@ jobs:
|
||||
- name: Formatting (rustfmt)
|
||||
run: cargo fmt -- --check
|
||||
|
||||
- name: Clippy (common + kvm)
|
||||
run: cargo clippy --locked --all --all-targets --no-default-features --tests --features "common,kvm" -- -D warnings
|
||||
- name: Clippy (all features,kvm)
|
||||
run: cargo clippy --all --all-targets --no-default-features --tests --features "common,kvm" -- -D warnings
|
||||
|
||||
- name: Clippy (default features)
|
||||
run: cargo clippy --locked --all --all-targets --tests -- -D warnings
|
||||
- name: Clippy (all features,mshv)
|
||||
run: cargo clippy --all --all-targets --no-default-features --tests --features "common,mshv" -- -D warnings
|
||||
|
||||
- name: Clippy (default features + amx)
|
||||
run: cargo clippy --locked --all --all-targets --tests --features "amx" -- -D warnings
|
||||
- name: Clippy (acpi,kvm)
|
||||
run: cargo clippy --all --all-targets --no-default-features --tests --features "acpi,kvm" -- -D warnings
|
||||
|
||||
- name: Clippy (default features + gdb)
|
||||
run: cargo clippy --locked --all --all-targets --tests --features "gdb" -- -D warnings
|
||||
- name: Clippy (acpi,kvm,tdx)
|
||||
run: cargo clippy --all --all-targets --no-default-features --tests --features "acpi,kvm,tdx" -- -D warnings
|
||||
|
||||
- name: Clippy (common + mshv)
|
||||
run: cargo clippy --locked --all --all-targets --no-default-features --tests --features "common,mshv" -- -D warnings
|
||||
- name: Clippy (kvm)
|
||||
run: cargo clippy --all --all-targets --no-default-features --tests --features "kvm" -- -D warnings
|
||||
|
||||
- name: Clippy (acpi,mshv)
|
||||
run: cargo clippy --all --all-targets --no-default-features --tests --features "acpi,mshv" -- -D warnings
|
||||
|
||||
- name: Clippy (mshv)
|
||||
run: cargo clippy --all --all-targets --no-default-features --tests --features "mshv" -- -D warnings
|
||||
|
||||
- name: Clippy (integration tests)
|
||||
run: cargo clippy --all --all-targets --tests -- -D warnings
|
||||
|
||||
- name: Check build did not modify any files
|
||||
run: test -z "$(git status --porcelain)"
|
||||
|
||||
20
.github/workflows/release.yaml
vendored
20
.github/workflows/release.yaml
vendored
@@ -9,38 +9,28 @@ jobs:
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Install musl-gcc
|
||||
run: sudo apt install -y musl-tools
|
||||
- name: Create release directory
|
||||
run: rsync -rv --exclude=.git . ../cloud-hypervisor-${{ github.event.ref }}
|
||||
- name: Install Rust toolchain (x86_64-unknown-linux-gnu)
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "1.60"
|
||||
toolchain: stable
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- name: Install Rust toolchain (x86_64-unknown-linux-musl)
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "1.60"
|
||||
toolchain: stable
|
||||
target: x86_64-unknown-linux-musl
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
toolchain: "1.60"
|
||||
command: build
|
||||
args: --all --release --target=x86_64-unknown-linux-gnu
|
||||
run: cargo build --all --release --target=x86_64-unknown-linux-gnu
|
||||
- name: Static Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
toolchain: "1.60"
|
||||
command: build
|
||||
args: --all --release --target=x86_64-unknown-linux-musl
|
||||
run: cargo build --all --release --target=x86_64-unknown-linux-musl
|
||||
- name: Strip cloud-hypervisor binaries
|
||||
run: strip target/*/release/cloud-hypervisor
|
||||
- name: Install Rust toolchain (aarch64-unknown-linux-musl)
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "1.60"
|
||||
toolchain: stable
|
||||
target: aarch64-unknown-linux-musl
|
||||
override: true
|
||||
- name: Static Build (AArch64)
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,4 +4,3 @@
|
||||
**/*.rs.bk
|
||||
**/Cargo.lock
|
||||
**/rusty-tags.vi
|
||||
/rpm/SOURCES
|
||||
|
||||
@@ -1 +1 @@
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
@@ -1,2 +0,0 @@
|
||||
# Add the list of code owners here (using their GitHub username)
|
||||
* @cloud-hypervisor/cloud-hypervisor-reviewers
|
||||
@@ -2,14 +2,12 @@
|
||||
|
||||
Cloud Hypervisor is an open source project licensed under the [Apache v2
|
||||
License](https://opensource.org/licenses/Apache-2.0) and the [BSD 3
|
||||
Clause](https://opensource.org/licenses/BSD-3-Clause) license. Individual files
|
||||
contain details of their licensing and changes to that file are under the same
|
||||
license unless the contribution changes the license of the file. When importing
|
||||
code from a third party project (e.g. Firecracker or CrosVM) please respect the
|
||||
license of those projects.
|
||||
|
||||
New code should be under the [Apache v2
|
||||
License](https://opensource.org/licenses/Apache-2.0).
|
||||
Clause](https://opensource.org/licenses/BSD-3-Clause) license. Contributions
|
||||
can be made under either license or both. Individual files contain details of
|
||||
their licensing and changes to that file are under the same license unless the
|
||||
contribution changes the license of the file. When importing code from a third
|
||||
party project (e.g. Firecracker or CrosVM) please respect the license of those
|
||||
projects.
|
||||
|
||||
## Coding Style
|
||||
|
||||
@@ -65,9 +63,11 @@ you want to merge your changes to `cloud-hypervisor`:
|
||||
2. Within your fork, create a branch for your contribution.
|
||||
3. [Create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)
|
||||
against the main branch of the Cloud Hypervisor repository.
|
||||
4. To update your pull request amend existing commits whenever applicable and
|
||||
4. Add reviewers to your pull request and then work with your reviewers to address
|
||||
any comments and obtain minimum of 2 [maintainers](MAINTAINERS.md) approvals.
|
||||
To update your pull request amend existing commits whenever applicable and
|
||||
then push the new changes to your pull request branch.
|
||||
5. Once the pull request is approved it can be integrated.
|
||||
5. Once the pull request is approved, one of the maintainers will merge it.
|
||||
|
||||
## Issue tracking
|
||||
|
||||
|
||||
374
Cargo.lock
generated
374
Cargo.lock
generated
@@ -20,9 +20,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.57"
|
||||
version = "1.0.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
|
||||
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -50,6 +50,7 @@ dependencies = [
|
||||
"linux-loader",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
@@ -72,9 +73,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
@@ -118,9 +119,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
version = "1.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -130,38 +131,29 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.1.18"
|
||||
version = "3.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
|
||||
checksum = "7a30c3bf9ff12dfe5dae53f0a96e0febcd18420d1c0e7fad77796d9d5c4b5375"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"os_str_bytes",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"terminal_size",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "24.0.0"
|
||||
version = "21.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
"clap",
|
||||
"dirs",
|
||||
"dirs 4.0.0",
|
||||
"epoll",
|
||||
"event_monitor",
|
||||
"hypervisor",
|
||||
@@ -183,9 +175,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crc32c"
|
||||
version = "0.6.3"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfea2db42e9927a3845fb268a10a72faed6d416065f77873f05e411457c363e"
|
||||
checksum = "ee6b9c9389584bcba988bd0836086789b7f87ad91892d6a83d5291dbb24524b5"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
@@ -216,6 +208,15 @@ dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "4.0.0"
|
||||
@@ -227,9 +228,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.7"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
@@ -265,6 +266,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
@@ -274,35 +276,11 @@ version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b643857cf70949306b81d7e92cb9d47add673868edac9863c4a49c42feaf3f1e"
|
||||
|
||||
[[package]]
|
||||
name = "gdbstub"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa2ca5d6b045de372cef3991f873389b421b4cabcfbe52f7787fae8b8b37906"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"log",
|
||||
"managed",
|
||||
"num-traits",
|
||||
"paste",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gdbstub_arch"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51dc4b5718ac76d21e8605c0966dd32d80273b89b11e6cfef467b04e45934d37"
|
||||
dependencies = [
|
||||
"gdbstub",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.6"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
||||
checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@@ -351,6 +329,7 @@ dependencies = [
|
||||
"mshv-bindings",
|
||||
"mshv-ioctls",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"vm-memory",
|
||||
@@ -359,9 +338,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iced-x86"
|
||||
version = "1.17.0"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "158f5204401d08f91d19176112146d75e99b3cf745092e268fa7be33e09adcec"
|
||||
checksum = "46e977036f7f5139d580c7f19ad62df9cb8ebd8410bb569e73585226be80a86f"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"static_assertions",
|
||||
@@ -369,9 +348,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.8.1"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
|
||||
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
@@ -405,25 +384,16 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnetwork"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f84f1612606f3753f205a4e9a2efd6fe5b4c573a6269b2cc6c3003d44a0d127"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.2"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
||||
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||
|
||||
[[package]]
|
||||
name = "kvm-bindings"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.5.0-tdx#52e56d0e8ef0f6ea32fc0492e6a175b73617a49f"
|
||||
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.5.0#9c497710ba9968d8efe15dbae03991b16cf82e23"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -433,7 +403,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kvm-ioctls"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/rust-vmm/kvm-ioctls?branch=main#1e03e29cdfbb0cb108a98de7a78045a5a517f18e"
|
||||
source = "git+https://github.com/rust-vmm/kvm-ioctls?branch=main#d22ef1f51852dfb055da38004e1a4fed81246f81"
|
||||
dependencies = [
|
||||
"kvm-bindings",
|
||||
"libc",
|
||||
@@ -448,9 +418,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.126"
|
||||
version = "0.2.112"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
|
||||
|
||||
[[package]]
|
||||
name = "libssh2-sys"
|
||||
@@ -468,9 +438,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.6"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92e7e15d7610cce1d9752e137625f14e61a28cd45929b6e12e47b50fe154ee2e"
|
||||
checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -489,39 +459,41 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.7"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
|
||||
checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "managed"
|
||||
version = "0.8.0"
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
name = "memoffset"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#a730d86940081ad044cdfbc1285c1db6d3048392"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#0a58eb1ece68e326e68365c4297d0a7c08ecd9bc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"vmm-sys-util",
|
||||
@@ -530,7 +502,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mshv-bindings"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#67e13faa36989a9226ad21c9ff3947e1d5738a54"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#b8b69b655337292037219ab765d2c94c565a076e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"serde",
|
||||
@@ -542,7 +514,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mshv-ioctls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#67e13faa36989a9226ad21c9ff3947e1d5738a54"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#b8b69b655337292037219ab765d2c94c565a076e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mshv-bindings",
|
||||
@@ -579,40 +551,15 @@ dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "no-std-net"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "111.20.0+1.1.1o"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92892c4f87d56e376e469ace79f1128fdaded07646ddf73aa0be4706ff712dec"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.73"
|
||||
version = "0.9.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d5fd19fb3e0a8191c1e34935718976a3e70c112ab9a24af6d7cadccd9d90bc0"
|
||||
checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl-src",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
@@ -623,9 +570,12 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.0.1"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435"
|
||||
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
@@ -652,12 +602,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc"
|
||||
|
||||
[[package]]
|
||||
name = "pci"
|
||||
version = "0.1.0"
|
||||
@@ -667,7 +611,6 @@ dependencies = [
|
||||
"hypervisor",
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
@@ -681,32 +624,19 @@ dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "performance-metrics"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"dirs",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"test_infra",
|
||||
"thiserror",
|
||||
"wait-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.25"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
||||
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
|
||||
|
||||
[[package]]
|
||||
name = "pnet"
|
||||
version = "0.30.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5cc57672f576f6b95370277fb445738d4887195c6cf4192bdf4f44697e2389b"
|
||||
checksum = "8750e073f82219c01e771133c64718d7685aef922da8a0d430a46aed05b6341a"
|
||||
dependencies = [
|
||||
"ipnetwork 0.19.0",
|
||||
"ipnetwork",
|
||||
"pnet_base",
|
||||
"pnet_datalink",
|
||||
"pnet_packet",
|
||||
@@ -716,20 +646,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pnet_base"
|
||||
version = "0.30.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e88341c6c842f89bdc7287f7b1e26b6fa64fa11c7ea3756971e6f18cd2510c4"
|
||||
dependencies = [
|
||||
"no-std-net",
|
||||
]
|
||||
checksum = "8205fe084bd43a3af79b3155c19feddd62e733640498842e631a2ffe107d1538"
|
||||
|
||||
[[package]]
|
||||
name = "pnet_datalink"
|
||||
version = "0.30.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc6e55d71c51db73372db35bc54f43abd8460adff1c3a9b717804ca6416d5df2"
|
||||
checksum = "6f85aef5e52e22ff06b1b11f2eb6d52959a9e0ecad3cb3f5cc2d78cadc077f0e"
|
||||
dependencies = [
|
||||
"ipnetwork 0.18.0",
|
||||
"ipnetwork",
|
||||
"libc",
|
||||
"pnet_base",
|
||||
"pnet_sys",
|
||||
@@ -738,9 +665,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pnet_macros"
|
||||
version = "0.30.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebfcdc9c072966723026b3596a1f655fb8bbfe0142f9770f8d481aee4459d6b9"
|
||||
checksum = "98cc3af95fed6dc318dfede3e81320f96ad5e237c6f7c4688108b19c8e67432d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -750,18 +677,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pnet_macros_support"
|
||||
version = "0.30.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bba532f5a4b320c029d89e612671fb621851b3b07e972c53850d34130033a5cd"
|
||||
checksum = "feaba58ba96abb218ec584d6caf0d3ff48922df05dbbeb1560553c197091b29e"
|
||||
dependencies = [
|
||||
"pnet_base",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pnet_packet"
|
||||
version = "0.30.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7009716ac86091c1b6e2cdec95a2b028c880f516054c1ec11edd02f9f463cbde"
|
||||
checksum = "f246edaaf1aaf82072d4cd38ee18bcc5dfc0464093f9ca39e4ac5962d68cf9d4"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"pnet_base",
|
||||
@@ -771,9 +698,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pnet_sys"
|
||||
version = "0.30.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e2a05efbc55c22f664c0ea475fbc4ffc4d09346aff9068438279d7e3d431f6f"
|
||||
checksum = "028c87a5e3a48fc07df099a2025f2ef16add5993712e1494ba69a6707ee7ed06"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
@@ -781,9 +708,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pnet_transport"
|
||||
version = "0.30.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b8ff06f37863f7590183f7044ab2e8d4dae991ecea0c791e3c6dd61ed2913d"
|
||||
checksum = "950f2a7961e19d22e19e84ff0a6e0955013185fe149673499662633d02b41b7a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pnet_base",
|
||||
@@ -793,11 +720,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.39"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -813,9 +740,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.18"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
||||
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -831,29 +758,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.13"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
|
||||
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.3"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"redox_syscall",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.6"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -862,15 +788,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.26"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "remain"
|
||||
version = "0.2.3"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c35270ea384ac1762895831cc8acb96f171468e52cec82ed9186f9416209fa4"
|
||||
checksum = "70ba1e78fa68412cb93ef642fd4d20b9a941be49ee9333875ebaf13112673ea7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -888,9 +814,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.10"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
|
||||
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
@@ -909,24 +835,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.9"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd"
|
||||
checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.137"
|
||||
version = "1.0.133"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a"
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.137"
|
||||
version = "1.0.133"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
|
||||
checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -935,9 +858,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.81"
|
||||
version = "1.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
|
||||
checksum = "c059c05b48c5c0067d4b4b2b4f0732dd65feb52daf7e0ea09cd87e7dadc1af79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -946,9 +869,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.14"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
|
||||
checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
@@ -995,13 +918,13 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.95"
|
||||
version = "1.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
|
||||
checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1018,9 +941,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
@@ -1039,9 +962,8 @@ dependencies = [
|
||||
name = "test_infra"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"dirs 3.0.2",
|
||||
"epoll",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"ssh2",
|
||||
"vmm-sys-util",
|
||||
@@ -1050,50 +972,44 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.15.0"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
|
||||
dependencies = [
|
||||
"terminal_size",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.31"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.31"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.3"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.0.0"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cfcd319456c4d6ea10087ed423473267e1a071f3bc0aa89f80d60997843c6f0"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
@@ -1143,7 +1059,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio-ioctls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#f75a77c1ab6349c105bc1462a65508726b4c2e0f"
|
||||
source = "git+https://github.com/rust-vmm/vfio-ioctls?branch=main#19e5b83ddfad430109ce3e8d51f4a24796099127"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
@@ -1190,9 +1106,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vhost"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53567fd9ab820e4f3cc156f24146882fee3c365194c3e1dea74723265f27fc88"
|
||||
checksum = "3b56bf8f178fc500fe14505fca8b00dec76fc38f2304f461c8d9d7547982311d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -1201,16 +1117,17 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vhost-user-backend"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/rust-vmm/vhost-user-backend?rev=14f58eda14076e973704d4f904850be1146fbb05#14f58eda14076e973704d4f904850be1146fbb05"
|
||||
name = "vhost_user_backend"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"epoll",
|
||||
"libc",
|
||||
"log",
|
||||
"vhost",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
"vm-virtio",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
@@ -1227,7 +1144,7 @@ dependencies = [
|
||||
"option_parser",
|
||||
"qcow",
|
||||
"vhost",
|
||||
"vhost-user-backend",
|
||||
"vhost_user_backend",
|
||||
"virtio-bindings",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
@@ -1245,7 +1162,7 @@ dependencies = [
|
||||
"net_util",
|
||||
"option_parser",
|
||||
"vhost",
|
||||
"vhost-user-backend",
|
||||
"vhost_user_backend",
|
||||
"virtio-bindings",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
@@ -1276,8 +1193,8 @@ dependencies = [
|
||||
"rate_limiter",
|
||||
"seccompiler",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"vhost",
|
||||
@@ -1293,11 +1210,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88f2d73c184c18f8acc32dab77fcb6e3af92d53262538d3a68aa474810d6863c"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"memoffset",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
@@ -1316,8 +1232,8 @@ name = "vm-device"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hypervisor",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"vfio-ioctls",
|
||||
@@ -1328,13 +1244,13 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vm-fdt"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#ca35d96191f8232bd7ab8c3e72ec925bf04bd2e0"
|
||||
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#9cfa0c8d7c3032f79589c7b01c8722a37f4cf44f"
|
||||
|
||||
[[package]]
|
||||
name = "vm-memory"
|
||||
version = "0.8.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "767ed8aaebbff902e02e6d3749dc2baef55e46565f8a6414a065e5baee4b4a81"
|
||||
checksum = "339d4349c126fdcd87e034631d7274370cf19eb0e87b33166bcd956589fc72c5"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"libc",
|
||||
@@ -1347,6 +1263,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
@@ -1378,8 +1295,6 @@ dependencies = [
|
||||
"devices",
|
||||
"epoll",
|
||||
"event_monitor",
|
||||
"gdbstub",
|
||||
"gdbstub_arch",
|
||||
"hypervisor",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
@@ -1392,6 +1307,7 @@ dependencies = [
|
||||
"qcow",
|
||||
"seccompiler",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"signal-hook",
|
||||
"thiserror",
|
||||
|
||||
39
Cargo.toml
39
Cargo.toml
@@ -1,44 +1,41 @@
|
||||
[package]
|
||||
name = "cloud-hypervisor"
|
||||
version = "24.0.0"
|
||||
version = "21.1.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
default-run = "cloud-hypervisor"
|
||||
build = "build.rs"
|
||||
license = "LICENSE-APACHE & LICENSE-BSD-3-Clause"
|
||||
description = "Open source Virtual Machine Monitor (VMM) that runs on top of KVM"
|
||||
homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
||||
# Minimum buildable version:
|
||||
# Keep in sync with version in .github/workflows/build.yaml
|
||||
rust-version = "1.60"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.52"
|
||||
api_client = { path = "api_client" }
|
||||
clap = { version = "3.1.18", features = ["wrap_help","cargo"] }
|
||||
clap = { version = "3.0.10", features = ["wrap_help","cargo"] }
|
||||
epoll = "4.3.1"
|
||||
event_monitor = { path = "event_monitor" }
|
||||
hypervisor = { path = "hypervisor" }
|
||||
libc = "0.2.126"
|
||||
log = { version = "0.4.17", features = ["std"] }
|
||||
libc = "0.2.112"
|
||||
log = { version = "0.4.14", features = ["std"] }
|
||||
option_parser = { path = "option_parser" }
|
||||
seccompiler = "0.2.0"
|
||||
serde_json = "1.0.81"
|
||||
signal-hook = "0.3.14"
|
||||
thiserror = "1.0.31"
|
||||
serde_json = "1.0.75"
|
||||
signal-hook = "0.3.13"
|
||||
thiserror = "1.0.30"
|
||||
vmm = { path = "vmm" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
vm-memory = "0.8.0"
|
||||
vm-memory = "0.7.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.1.18", features = ["cargo"] }
|
||||
clap = { version = "3.0.10", features = ["wrap_help"] }
|
||||
|
||||
# List of patched crates
|
||||
[patch.crates-io]
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0-tdx" }
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0", features = ["with-serde", "fam-wrappers"] }
|
||||
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "main" }
|
||||
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
|
||||
|
||||
@@ -46,18 +43,17 @@ versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_deri
|
||||
dirs = "4.0.0"
|
||||
lazy_static= "1.4.0"
|
||||
net_util = { path = "net_util" }
|
||||
serde_json = "1.0.81"
|
||||
serde_json = "1.0.75"
|
||||
test_infra = { path = "test_infra" }
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
[features]
|
||||
default = ["common", "kvm"]
|
||||
# Common features for all hypervisors
|
||||
common = ["fwdebug"]
|
||||
amx = ["vmm/amx"]
|
||||
common = ["acpi", "cmos", "fwdebug"]
|
||||
acpi = ["vmm/acpi"]
|
||||
cmos = ["vmm/cmos"]
|
||||
fwdebug = ["vmm/fwdebug"]
|
||||
gdb = ["vmm/gdb"]
|
||||
kvm = ["vmm/kvm"]
|
||||
mshv = ["vmm/mshv"]
|
||||
tdx = ["vmm/tdx"]
|
||||
@@ -75,18 +71,19 @@ members = [
|
||||
"net_util",
|
||||
"option_parser",
|
||||
"pci",
|
||||
"performance-metrics",
|
||||
"qcow",
|
||||
"rate_limiter",
|
||||
"test_infra",
|
||||
"vfio_user",
|
||||
"vhdx",
|
||||
"vhost_user_backend",
|
||||
"vhost_user_block",
|
||||
"vhost_user_net",
|
||||
"virtio-devices",
|
||||
"virtio-queue",
|
||||
"vmm",
|
||||
"vm-allocator",
|
||||
"vm-device",
|
||||
"vm-migration",
|
||||
"vm-virtio"
|
||||
]
|
||||
exclude = ["test_infra"]
|
||||
|
||||
156
Jenkinsfile
vendored
156
Jenkinsfile
vendored
@@ -1,28 +1,9 @@
|
||||
def runWorkers = true
|
||||
pipeline{
|
||||
agent none
|
||||
stages {
|
||||
stage ('Early checks') {
|
||||
agent { node { label 'built-in' } }
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Check for documentation only changes') {
|
||||
when {
|
||||
expression {
|
||||
return docsFileOnly()
|
||||
}
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
runWorkers = false
|
||||
echo "Documentation only changes, no need to run the CI"
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Check for RFC/WIP builds') {
|
||||
when {
|
||||
changeRequest comparator: 'REGEXP', title: '.*(rfc|RFC|wip|WIP).*'
|
||||
@@ -41,14 +22,11 @@ pipeline{
|
||||
}
|
||||
}
|
||||
stage ('Build') {
|
||||
parallel {
|
||||
parallel {
|
||||
stage ('Worker build') {
|
||||
agent { node { label 'focal' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
agent { node { label 'hirsute' } }
|
||||
environment {
|
||||
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
@@ -56,11 +34,7 @@ pipeline{
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Prepare environment') {
|
||||
steps {
|
||||
sh "scripts/prepare_vdpa.sh"
|
||||
}
|
||||
}
|
||||
|
||||
stage ('Run OpenAPI tests') {
|
||||
steps {
|
||||
sh "scripts/run_openapi_tests.sh"
|
||||
@@ -84,12 +58,6 @@ pipeline{
|
||||
}
|
||||
stage ('AArch64 worker build') {
|
||||
agent { node { label 'bionic-arm64' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
@@ -98,7 +66,7 @@ pipeline{
|
||||
}
|
||||
stage ('Run unit tests') {
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --unit --libc musl"
|
||||
sh "scripts/dev_cli.sh tests --unit"
|
||||
}
|
||||
}
|
||||
stage ('Run integration tests') {
|
||||
@@ -107,7 +75,7 @@ pipeline{
|
||||
}
|
||||
steps {
|
||||
sh "sudo modprobe openvswitch"
|
||||
sh "scripts/dev_cli.sh tests --integration --libc musl"
|
||||
sh "scripts/dev_cli.sh tests --integration"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,12 +87,9 @@ pipeline{
|
||||
}
|
||||
}
|
||||
stage ('Worker build (musl)') {
|
||||
agent { node { label 'focal' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
agent { node { label 'hirsute' } }
|
||||
environment {
|
||||
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
@@ -132,11 +97,7 @@ pipeline{
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Prepare environment') {
|
||||
steps {
|
||||
sh "scripts/prepare_vdpa.sh"
|
||||
}
|
||||
}
|
||||
|
||||
stage ('Run unit tests for musl') {
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --unit --libc musl"
|
||||
@@ -153,14 +114,80 @@ pipeline{
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Worker build - Windows guest') {
|
||||
agent { node { label 'focal' } }
|
||||
stage ('Worker build SGX') {
|
||||
agent { node { label 'bionic-sgx' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
branch 'main'
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Run SGX integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-sgx"
|
||||
}
|
||||
}
|
||||
stage ('Run SGX integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-sgx --libc musl"
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||
deleteDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Worker build VFIO') {
|
||||
agent { node { label 'bionic-vfio' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
branch 'main'
|
||||
}
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage ('Run VFIO integration tests') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-vfio"
|
||||
}
|
||||
}
|
||||
stage ('Run VFIO integration tests for musl') {
|
||||
options {
|
||||
timeout(time: 1, unit: 'HOURS')
|
||||
}
|
||||
steps {
|
||||
sh "scripts/dev_cli.sh tests --integration-vfio --libc musl"
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||
deleteDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Worker build - Windows guest') {
|
||||
agent { node { label 'hirsute' } }
|
||||
environment {
|
||||
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
|
||||
}
|
||||
@@ -200,13 +227,7 @@ pipeline{
|
||||
}
|
||||
}
|
||||
stage ('Worker build - Live Migration') {
|
||||
agent { node { label 'focal-small' } }
|
||||
when {
|
||||
beforeAgent true
|
||||
expression {
|
||||
return runWorkers
|
||||
}
|
||||
}
|
||||
agent { node { label 'hirsute-small' } }
|
||||
stages {
|
||||
stage ('Checkout') {
|
||||
steps {
|
||||
@@ -272,18 +293,7 @@ def cancelPreviousBuilds() {
|
||||
def installAzureCli() {
|
||||
sh "sudo apt install -y ca-certificates curl apt-transport-https lsb-release gnupg"
|
||||
sh "curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null"
|
||||
sh "echo \"deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ focal main\" | sudo tee /etc/apt/sources.list.d/azure-cli.list"
|
||||
sh "echo \"deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ hirsute main\" | sudo tee /etc/apt/sources.list.d/azure-cli.list"
|
||||
sh "sudo apt update"
|
||||
sh "sudo apt install -y azure-cli"
|
||||
}
|
||||
|
||||
def boolean docsFileOnly() {
|
||||
if (env.CHANGE_TARGET == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return sh(
|
||||
returnStatus: true,
|
||||
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v '\\.md'"
|
||||
) != 0
|
||||
}
|
||||
|
||||
179
README.md
179
README.md
@@ -27,19 +27,12 @@
|
||||
|
||||
# 1. What is Cloud Hypervisor?
|
||||
|
||||
Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on
|
||||
top of [KVM](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt)
|
||||
hypervisor and Microsoft Hypervisor (MSHV).
|
||||
Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on top of [KVM](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt) and the MSHV hypervisors .
|
||||
|
||||
The project focuses on exclusively running modern, cloud workloads, on top of
|
||||
a limited set of hardware architectures and platforms. Cloud workloads refers
|
||||
to those that are usually run by customers inside a cloud provider. For our
|
||||
purposes this means modern operating systems with most I/O handled by
|
||||
paravirtualised devices (i.e. virtio), no requirement for legacy devices, and
|
||||
64-bit CPUs.
|
||||
The project focuses on exclusively running modern, cloud workloads, on top of a limited set of hardware architectures and platforms.
|
||||
Cloud workloads refers to those that are usually run by customers inside a cloud provider. For our purposes this means modern operating systems with most I/O handled by paravirtualised devices (i.e. virtio), no requirement for legacy devices, and 64-bit CPUs.
|
||||
|
||||
Cloud Hypervisor is implemented in [Rust](https://www.rust-lang.org/) and is
|
||||
based on the [rust-vmm](https://github.com/rust-vmm) crates.
|
||||
Cloud Hypervisor is implemented in [Rust](https://www.rust-lang.org/) and is based on the [rust-vmm](https://github.com/rust-vmm) crates.
|
||||
|
||||
## Objectives
|
||||
|
||||
@@ -58,9 +51,7 @@ based on the [rust-vmm](https://github.com/rust-vmm) crates.
|
||||
|
||||
### Architectures
|
||||
|
||||
Cloud Hypervisor supports the `x86-64` and `AArch64` architectures. There are
|
||||
some small differences in functionality between the two architectures
|
||||
(see [#1125](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/1125)).
|
||||
Cloud Hypervisor supports the `x86-64` and `AArch64` architectures. There are some small differences in functionality between the two architectures (see [#1125](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/1125)).
|
||||
|
||||
### Guest OS
|
||||
|
||||
@@ -83,15 +74,17 @@ $ mkdir $CLOUDH
|
||||
|
||||
## Install prerequisites
|
||||
|
||||
You need to install some prerequisite packages in order to build and test Cloud
|
||||
Hypervisor. Here, all the steps are based on Ubuntu, for other Linux
|
||||
distributions please replace the package manager and package name.
|
||||
You need to install some prerequisite packages in order to build and test Cloud Hypervisor.
|
||||
Here, all the steps are based on Ubuntu, for other Linux distributions please replace the
|
||||
package manager and package name.
|
||||
|
||||
```shell
|
||||
# Install build-essential, git, and qemu-img
|
||||
$ sudo apt install git build-essential qemu-img
|
||||
# Install git
|
||||
$ sudo apt install git
|
||||
# Install rust tool chain
|
||||
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
# Install build-essential
|
||||
$ sudo apt install build-essential
|
||||
# If you want to build statically linked binary please add musl target
|
||||
$ rustup target add x86_64-unknown-linux-musl
|
||||
```
|
||||
@@ -114,8 +107,7 @@ $ cargo build --release --target=x86_64-unknown-linux-musl --all
|
||||
$ popd
|
||||
```
|
||||
|
||||
This will build a `cloud-hypervisor` binary under
|
||||
`$CLOUDH/cloud-hypervisor/target/release/cloud-hypervisor`.
|
||||
This will build a `cloud-hypervisor` binary under `$CLOUDH/cloud-hypervisor/target/release/cloud-hypervisor`.
|
||||
|
||||
### Containerized builds and tests
|
||||
|
||||
@@ -147,26 +139,24 @@ development script commands and their related options.
|
||||
|
||||
## Run
|
||||
|
||||
You can run a guest VM by either using an existing cloud image or booting into
|
||||
your own kernel and disk image.
|
||||
You can run a guest VM by either using an existing cloud image or booting into your own kernel and disk image.
|
||||
|
||||
### Cloud image
|
||||
|
||||
Cloud Hypervisor supports booting disk images containing all needed
|
||||
components to run cloud workloads, a.k.a. cloud images. To do that we rely on
|
||||
the [Rust Hypervisor
|
||||
Firmware](https://github.com/cloud-hypervisor/rust-hypervisor-firmware) project
|
||||
to provide an ELF formatted KVM firmware for `cloud-hypervisor` to directly
|
||||
boot into.
|
||||
Firmware](https://github.com/cloud-hypervisor/rust-hypervisor-firmware) project to provide
|
||||
an ELF
|
||||
formatted KVM firmware for `cloud-hypervisor` to directly boot into.
|
||||
|
||||
We need to get the latest `rust-hypervisor-firmware` release and also a working
|
||||
cloud image. Here we will use a Ubuntu image:
|
||||
We need to get the latest `rust-hypervisor-firmware` release and also a working cloud image. Here we will use a Ubuntu image:
|
||||
|
||||
```shell
|
||||
$ pushd $CLOUDH
|
||||
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
|
||||
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-amd64.img focal-server-cloudimg-amd64.raw
|
||||
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.4.0/hypervisor-fw
|
||||
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.3.2/hypervisor-fw
|
||||
$ popd
|
||||
```
|
||||
|
||||
@@ -188,10 +178,7 @@ Multiple arguments can be given to the `--disk` parameter.
|
||||
|
||||
#### Building your kernel
|
||||
|
||||
Cloud Hypervisor also supports direct kernel boot into a `vmlinux` ELF kernel.
|
||||
In order to support virtio-watchdog we have our own development branch. You are
|
||||
of course able to use your own kernel but these instructions will continue with
|
||||
the version that we develop and test against.
|
||||
Cloud Hypervisor also supports direct kernel boot into a `vmlinux` ELF kernel. In order to support virtio-iommu we have our own development branch. You are of course able to use your own kernel but these instructions will continue with the version that we develop and test against.
|
||||
|
||||
To build the kernel:
|
||||
|
||||
@@ -208,13 +195,11 @@ $ KCFLAGS="-Wa,-mx86-used-note=no" make bzImage -j `nproc`
|
||||
$ popd
|
||||
```
|
||||
|
||||
The `vmlinux` kernel image will then be located at
|
||||
`linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin`.
|
||||
The `vmlinux` kernel image will then be located at `linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin`.
|
||||
|
||||
#### Disk image
|
||||
|
||||
For the disk image, we will use a Ubuntu cloud image that contains a root
|
||||
partition:
|
||||
For the disk image, we will use a Ubuntu cloud image that contains a root partition:
|
||||
|
||||
```shell
|
||||
$ pushd $CLOUDH
|
||||
@@ -225,8 +210,8 @@ $ popd
|
||||
|
||||
#### Booting the guest VM
|
||||
|
||||
Now we can directly boot into our custom kernel and make it use the Ubuntu root
|
||||
partition. If we want to have 4 vCPUs and 1024 MBytes of memory:
|
||||
Now we can directly boot into our custom kernel and make it use the Ubuntu root partition.
|
||||
If we want to have 4 vCPUs and 1024 MBytes of memory:
|
||||
|
||||
```shell
|
||||
$ pushd $CLOUDH
|
||||
@@ -261,114 +246,94 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||
|
||||
# 3. Status
|
||||
|
||||
Cloud Hypervisor is under active development. The following stability guarantees
|
||||
are currently made:
|
||||
Cloud Hypervisor is under active development. The following stability guarantees are currently made:
|
||||
|
||||
* The API (including command line options) will not be removed or changed in a
|
||||
breaking way without a minimum of 2 major releases notice. Where possible
|
||||
warnings will be given about the use of deprecated functionality and the
|
||||
deprecations will be documented in the release notes.
|
||||
|
||||
breaking way without a minimum of 2 releases notice. Where possible warnings
|
||||
will be given about the use of deprecated functionality and the deprecations
|
||||
will be documented in the release notes.
|
||||
* Point releases will be made between individual releases where there are
|
||||
substantial bug fixes or security issues that need to be fixed. These point
|
||||
releases will only include bug fixes.
|
||||
substantial bug fixes or security issues that need to be fixed.
|
||||
|
||||
Currently the following items are **not** guaranteed across updates:
|
||||
|
||||
* Snapshot/restore is not supported across different versions
|
||||
* Live migration is not supported across different versions
|
||||
* The following features are considered experimental and may change
|
||||
substantially between releases: TDX, vfio-user, vDPA.
|
||||
substantially between releases: TDX, SGX.
|
||||
|
||||
As of 2022-04-05, the following cloud images are supported:
|
||||
|
||||
As of 2021-04-29, the following cloud images are supported:
|
||||
|
||||
- [Ubuntu Bionic](https://cloud-images.ubuntu.com/bionic/current/) (cloudimg)
|
||||
- [Ubuntu Focal](https://cloud-images.ubuntu.com/focal/current/) (cloudimg)
|
||||
- [Ubuntu Jammy](https://cloud-images.ubuntu.com/jammy/current/) (cloudimg)
|
||||
- [Ubuntu Groovy](https://cloud-images.ubuntu.com/groovy/current/) (cloudimg)
|
||||
- [Ubuntu Hirsute](https://cloud-images.ubuntu.com/hirsute/current/) (cloudimg)
|
||||
|
||||
Direct kernel boot to userspace should work with a rootfs from most
|
||||
distributions.
|
||||
|
||||
Further details can be found in the [release documentation](docs/releases.md).
|
||||
Direct kernel boot to userspace should work with a rootfs from most distributions.
|
||||
|
||||
## Hot Plug
|
||||
|
||||
Cloud Hypervisor supports hotplug of CPUs, passthrough devices (VFIO),
|
||||
`virtio-{net,block,pmem,fs,vsock}` and memory resizing. This
|
||||
[document](docs/hotplug.md) details how to add devices to a running VM.
|
||||
Cloud Hypervisor supports hotplug of CPUs, passthrough devices (VFIO), `virtio-{net,block,pmem,fs,vsock}` and memory resizing. This [document](docs/hotplug.md) details how to add devices to
|
||||
a running VM.
|
||||
|
||||
## Device Model
|
||||
|
||||
Details of the device model can be found in this
|
||||
[documentation](docs/device_model.md).
|
||||
Details of the device model can be found in this [documentation](docs/device_model.md).
|
||||
|
||||
## TODO
|
||||
|
||||
We are not tracking the Cloud Hypervisor TODO list from a specific git tracked
|
||||
file but through
|
||||
[github issues](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/new)
|
||||
instead.
|
||||
We are not tracking the Cloud Hypervisor TODO list from a specific git tracked file but through
|
||||
[github issues](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/new) instead.
|
||||
|
||||
# 4. `rust-vmm` project dependency
|
||||
|
||||
In order to satisfy the design goal of having a high-performance,
|
||||
security-focused hypervisor the decision was made to use the
|
||||
[Rust](https://www.rust-lang.org/) programming language. The language's strong
|
||||
focus on memory and thread safety makes it an ideal candidate for implementing
|
||||
VMMs.
|
||||
In order to satisfy the design goal of having a high-performance, security-focused hypervisor the decision
|
||||
was made to use the [Rust](https://www.rust-lang.org/) programming language.
|
||||
The language's strong focus on memory and thread safety makes it an ideal candidate for implementing VMMs.
|
||||
|
||||
Instead of implementing the VMM components from scratch, Cloud Hypervisor is
|
||||
importing the [rust-vmm](https://github.com/rust-vmm) crates, and sharing code
|
||||
and architecture together with other VMMs like e.g. Amazon's
|
||||
[Firecracker](https://firecracker-microvm.github.io/) and Google's
|
||||
[crosvm](https://chromium.googlesource.com/chromiumos/platform/crosvm/).
|
||||
Instead of implementing the VMM components from scratch, Cloud Hypervisor is importing the [rust-vmm](https://github.com/rust-vmm)
|
||||
crates, and sharing code and architecture together with other VMMs like e.g. Amazon's [Firecracker](https://firecracker-microvm.github.io/)
|
||||
and Google's [crosvm](https://chromium.googlesource.com/chromiumos/platform/crosvm/).
|
||||
|
||||
Cloud Hypervisor embraces the rust-vmm project goals, which is to be able to
|
||||
share and re-use as many virtualization crates as possible. As such, the Cloud
|
||||
Hypervisor relationship with the rust-vmm project is twofold:
|
||||
Cloud Hypervisor embraces the rust-vmm project goals, which is to be able to share and re-use
|
||||
as many virtualization crates as possible. As such, the Cloud Hypervisor relationship with the rust-vmm
|
||||
project is twofold:
|
||||
|
||||
1. It will use as much of the rust-vmm code as possible. Any new rust-vmm crate
|
||||
that's relevant to the project goals will be integrated as soon as possible.
|
||||
2. As it is likely that the rust-vmm project will lack some of the features that
|
||||
Cloud Hypervisor needs (e.g. ACPI, VFIO, vhost-user, etc), we will be using
|
||||
the Cloud Hypervisor VMM to implement and test them, and contribute them back
|
||||
to the rust-vmm project.
|
||||
1. It will use as much of the rust-vmm code as possible. Any new rust-vmm crate that's relevant to the project
|
||||
goals will be integrated as soon as possible.
|
||||
2. As it is likely that the rust-vmm project will lack some of the features that Cloud Hypervisor needs (e.g. ACPI,
|
||||
VFIO, vhost-user, etc), we will be using the Cloud Hypervisor VMM to implement and test them, and contribute them
|
||||
back to the rust-vmm project.
|
||||
|
||||
## Firecracker and crosvm
|
||||
|
||||
A large part of the Cloud Hypervisor code is based on either the Firecracker or
|
||||
the crosvm projects implementations. Both of these are VMMs written in Rust with
|
||||
a focus on safety and security, like Cloud Hypervisor.
|
||||
A large part of the Cloud Hypervisor code is based on either the Firecracker or the crosvm projects implementations.
|
||||
Both of these are VMMs written in Rust with a focus on safety and security, like Cloud Hypervisor.
|
||||
|
||||
However we want to emphasize that the Cloud Hypervisor project is neither a fork
|
||||
nor a reimplementation of any of those projects. The goals and use cases we're
|
||||
trying to meet are different. We're aiming at supporting cloud workloads, i.e.
|
||||
those modern, full Linux distribution images currently being run by Cloud
|
||||
Service Provider (CSP) tenants.
|
||||
However we want to emphasize that the Cloud Hypervisor project is neither a fork nor a reimplementation of any of those
|
||||
projects. The goals and use cases we're trying to meet are different.
|
||||
We're aiming at supporting cloud workloads, i.e. those modern, full Linux distribution images currently being run by
|
||||
Cloud Service Provider (CSP) tenants.
|
||||
|
||||
Our primary target is not to support client or serverless use cases, and as such
|
||||
our code base already diverges from the crosvm and Firecracker ones. As we add
|
||||
more features to support our use cases, we believe that the divergence will
|
||||
increase while at the same time sharing as much of the fundamental
|
||||
virtualization code through the rust-vmm project crates as possible.
|
||||
Our primary target is not to support client or serverless use cases, and as such our code base already diverges
|
||||
from the crosvm and Firecracker ones. As we add more features to support our use cases, we believe that the divergence
|
||||
will increase while at the same time sharing as much of the fundamental virtualization code through the rust-vmm project
|
||||
crates as possible.
|
||||
|
||||
# 5. Community
|
||||
|
||||
The Cloud Hypervisor project follows the governance, and community guidelines
|
||||
described in the [Community](https://github.com/cloud-hypervisor/community)
|
||||
repository.
|
||||
The Cloud Hypervisor project follows the governance, and community guidelines described in
|
||||
the [Community](https://github.com/cloud-hypervisor/community) repository.
|
||||
|
||||
## Contribute
|
||||
|
||||
We are working on building a global, diverse and collaborative community around
|
||||
the Cloud Hypervisor project. Anyone who is interested in
|
||||
[contributing](CONTRIBUTING.md) to the project is welcome to participate.
|
||||
We are working on building a global, diverse and collaborative community around the Cloud Hypervisor project.
|
||||
Anyone who is interested in [contributing](CONTRIBUTING.md) to the project is welcome to participate.
|
||||
|
||||
We believe that contributing to a open source project like Cloud Hypervisor
|
||||
covers a lot more than just sending code. Testing, documentation, pull request
|
||||
reviews, bug reports, feature requests, project improvement suggestions, etc,
|
||||
are all equal and welcome means of contribution. See the
|
||||
[CONTRIBUTING](CONTRIBUTING.md) document for more details.
|
||||
We believe that contributing to a open source project like Cloud Hypervisor covers a lot more than just sending
|
||||
code. Testing, documentation, pull request reviews, bug reports, feature requests, project improvement suggestions,
|
||||
etc, are all equal and welcome means of contribution. See the [CONTRIBUTING](CONTRIBUTING.md) document for more details.
|
||||
|
||||
## Join us
|
||||
|
||||
@@ -377,4 +342,4 @@ and [join us on Slack](https://cloud-hypervisor.slack.com/).
|
||||
|
||||
## Security issues
|
||||
|
||||
Please contact the maintainers listed in the MAINTAINERS.md file with security issues.
|
||||
Please use the GitHub security advisories feature for reporting issues: https://github.com/cloud-hypervisor/cloud-hypervisor/security/advisories/new
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "acpi_tables"
|
||||
version = "0.1.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
vm-memory = "0.8.0"
|
||||
vm-memory = "0.7.0"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "api_client"
|
||||
version = "0.1.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
@@ -2,25 +2,27 @@
|
||||
name = "arch"
|
||||
version = "0.1.0"
|
||||
authors = ["The Chromium OS Authors"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
acpi = ["acpi_tables"]
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { path = "../acpi_tables" }
|
||||
anyhow = "1.0.57"
|
||||
acpi_tables = { path = "../acpi_tables", optional = true }
|
||||
anyhow = "1.0.52"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.126"
|
||||
libc = "0.2.112"
|
||||
linux-loader = { version = "0.4.0", features = ["elf", "bzimage", "pe"] }
|
||||
log = "0.4.17"
|
||||
serde = { version = "1.0.137", features = ["rc", "derive"] }
|
||||
thiserror = "1.0.31"
|
||||
log = "0.4.14"
|
||||
serde = { version = "1.0.133", features = ["rc"] }
|
||||
serde_derive = "1.0.133"
|
||||
thiserror = "1.0.30"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-bitmap"] }
|
||||
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-bitmap"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vmm-sys-util = { version = "0.9.0", features = ["with-serde"] }
|
||||
|
||||
|
||||
@@ -18,13 +18,14 @@ use std::str;
|
||||
use super::super::DeviceType;
|
||||
use super::super::GuestMemoryMmap;
|
||||
use super::super::InitramfsConfig;
|
||||
use super::get_fdt_addr;
|
||||
use super::gic::GicDevice;
|
||||
use super::layout::{
|
||||
IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_PCI_IO_SIZE, MEM_PCI_IO_START,
|
||||
PCI_HIGH_BASE, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
|
||||
};
|
||||
use vm_fdt::{FdtWriter, FdtWriterResult};
|
||||
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryRegion};
|
||||
use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestMemoryError, GuestMemoryRegion};
|
||||
|
||||
// This is a value for uniquely identifying the FDT node declaring the interrupt controller.
|
||||
const GIC_PHANDLE: u32 = 1;
|
||||
@@ -50,16 +51,11 @@ const SIZE_CELLS: u32 = 0x2;
|
||||
// Look for "The 1st cell..."
|
||||
const GIC_FDT_IRQ_TYPE_SPI: u32 = 0;
|
||||
const GIC_FDT_IRQ_TYPE_PPI: u32 = 1;
|
||||
const GIC_FDT_IRQ_PPI_CPU_SHIFT: u32 = 8;
|
||||
const GIC_FDT_IRQ_PPI_CPU_MASK: u32 = 0xff << GIC_FDT_IRQ_PPI_CPU_SHIFT;
|
||||
|
||||
// From https://elixir.bootlin.com/linux/v4.9.62/source/include/dt-bindings/interrupt-controller/irq.h#L17
|
||||
const IRQ_TYPE_EDGE_RISING: u32 = 1;
|
||||
const IRQ_TYPE_LEVEL_HI: u32 = 4;
|
||||
|
||||
// PMU PPI interrupt number
|
||||
pub const AARCH64_PMU_IRQ: u32 = 7;
|
||||
|
||||
// Keys and Buttons
|
||||
// System Power Down
|
||||
const KEY_POWER: u32 = 116;
|
||||
@@ -95,7 +91,6 @@ pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHash
|
||||
pci_space_info: &[PciSpaceInfo],
|
||||
numa_nodes: &NumaNodes,
|
||||
virtio_iommu_bdf: Option<u32>,
|
||||
pmu_supported: bool,
|
||||
) -> FdtWriterResult<Vec<u8>> {
|
||||
// Allocate stuff necessary for the holding the blob.
|
||||
let mut fdt = FdtWriter::new().unwrap();
|
||||
@@ -119,9 +114,6 @@ pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHash
|
||||
create_chosen_node(&mut fdt, cmdline, initrd)?;
|
||||
create_gic_node(&mut fdt, gic_device)?;
|
||||
create_timer_node(&mut fdt)?;
|
||||
if pmu_supported {
|
||||
create_pmu_node(&mut fdt, vcpu_mpidr.len())?;
|
||||
}
|
||||
create_clock_node(&mut fdt)?;
|
||||
create_psci_node(&mut fdt)?;
|
||||
create_devices_node(&mut fdt, device_info)?;
|
||||
@@ -140,8 +132,9 @@ pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHash
|
||||
|
||||
pub fn write_fdt_to_memory(fdt_final: Vec<u8>, guest_mem: &GuestMemoryMmap) -> Result<()> {
|
||||
// Write FDT to memory.
|
||||
let fdt_address = GuestAddress(get_fdt_addr());
|
||||
guest_mem
|
||||
.write_slice(fdt_final.as_slice(), super::layout::FDT_START)
|
||||
.write_slice(fdt_final.as_slice(), fdt_address)
|
||||
.map_err(Error::WriteFdtToMemory)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -159,6 +152,9 @@ fn create_cpu_nodes(
|
||||
fdt.property_u32("#size-cells", 0x0)?;
|
||||
|
||||
let num_cpus = vcpu_mpidr.len();
|
||||
let threads_per_core = vcpu_topology.unwrap_or_default().0 as u8;
|
||||
let cores_per_package = vcpu_topology.unwrap_or_default().1 as u8;
|
||||
let packages = vcpu_topology.unwrap_or_default().2 as u8;
|
||||
|
||||
for (cpu_id, mpidr) in vcpu_mpidr.iter().enumerate().take(num_cpus) {
|
||||
let cpu_name = format!("cpu@{:x}", cpu_id);
|
||||
@@ -187,9 +183,32 @@ fn create_cpu_nodes(
|
||||
fdt.end_node(cpu_node)?;
|
||||
}
|
||||
|
||||
if let Some(topology) = vcpu_topology {
|
||||
let (threads_per_core, cores_per_package, packages) = topology;
|
||||
// If there is a valid cpu topology config, create the cpu-map node.
|
||||
if (threads_per_core > 0)
|
||||
&& (cores_per_package > 0)
|
||||
&& (packages > 0)
|
||||
&& (num_cpus as u8 == threads_per_core * cores_per_package * packages)
|
||||
{
|
||||
let cpu_map_node = fdt.begin_node("cpu-map")?;
|
||||
// Create mappings between CPU index and cluster,core, and thread.
|
||||
let mut cluster_core_thread_to_cpuidx = HashMap::new();
|
||||
for cpu_idx in 0..num_cpus as u8 {
|
||||
if threads_per_core > 1 {
|
||||
cluster_core_thread_to_cpuidx.insert(
|
||||
(
|
||||
cpu_idx / (cores_per_package * threads_per_core),
|
||||
(cpu_idx / threads_per_core) % cores_per_package,
|
||||
cpu_idx % threads_per_core,
|
||||
),
|
||||
cpu_idx,
|
||||
);
|
||||
} else {
|
||||
cluster_core_thread_to_cpuidx.insert(
|
||||
(cpu_idx / cores_per_package, cpu_idx % cores_per_package, 0),
|
||||
cpu_idx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Create device tree nodes with regard of above mapping.
|
||||
for cluster_idx in 0..packages {
|
||||
@@ -200,16 +219,24 @@ fn create_cpu_nodes(
|
||||
let core_name = format!("core{:x}", core_idx);
|
||||
let core_node = fdt.begin_node(&core_name)?;
|
||||
|
||||
for thread_idx in 0..threads_per_core {
|
||||
let thread_name = format!("thread{:x}", thread_idx);
|
||||
let thread_node = fdt.begin_node(&thread_name)?;
|
||||
let cpu_idx = threads_per_core * cores_per_package * cluster_idx
|
||||
+ threads_per_core * core_idx
|
||||
+ thread_idx;
|
||||
fdt.property_u32("cpu", cpu_idx as u32 + FIRST_VCPU_PHANDLE)?;
|
||||
fdt.end_node(thread_node)?;
|
||||
}
|
||||
if threads_per_core > 1 {
|
||||
for thread_idx in 0..threads_per_core {
|
||||
let thread_name = format!("thread{:x}", thread_idx);
|
||||
let thread_node = fdt.begin_node(&thread_name)?;
|
||||
let cpu_idx = cluster_core_thread_to_cpuidx
|
||||
.get(&(cluster_idx, core_idx, thread_idx))
|
||||
.unwrap();
|
||||
|
||||
fdt.property_u32("cpu", *cpu_idx as u32 + FIRST_VCPU_PHANDLE)?;
|
||||
fdt.end_node(thread_node)?;
|
||||
}
|
||||
} else {
|
||||
let cpu_idx = cluster_core_thread_to_cpuidx
|
||||
.get(&(cluster_idx, core_idx, 0))
|
||||
.unwrap();
|
||||
|
||||
fdt.property_u32("cpu", *cpu_idx as u32 + FIRST_VCPU_PHANDLE)?;
|
||||
}
|
||||
fdt.end_node(core_node)?;
|
||||
}
|
||||
fdt.end_node(cluster_node)?;
|
||||
@@ -229,67 +256,40 @@ fn create_memory_node(
|
||||
guest_mem: &GuestMemoryMmap,
|
||||
numa_nodes: &NumaNodes,
|
||||
) -> FdtWriterResult<()> {
|
||||
// See https://github.com/torvalds/linux/blob/58ae0b51506802713aa0e9956d1853ba4c722c98/Documentation/devicetree/bindings/numa.txt
|
||||
// for NUMA setting in memory node.
|
||||
if numa_nodes.len() > 1 {
|
||||
for numa_node_idx in 0..numa_nodes.len() {
|
||||
let numa_node = numa_nodes.get(&(numa_node_idx as u32));
|
||||
let mut mem_reg_prop: Vec<u64> = Vec::new();
|
||||
let mut node_memory_addr: u64 = 0;
|
||||
// Each memory zone of numa will have its own memory node, but
|
||||
// different numa nodes should not share same memory zones.
|
||||
for memory_region in numa_node.unwrap().memory_regions.iter() {
|
||||
let memory_region_start_addr: u64 = memory_region.start_addr().raw_value();
|
||||
let memory_region_size: u64 = memory_region.size() as u64;
|
||||
mem_reg_prop.push(memory_region_start_addr);
|
||||
mem_reg_prop.push(memory_region_size);
|
||||
// Set the node address the first non-zero regison address
|
||||
if node_memory_addr == 0 {
|
||||
node_memory_addr = memory_region_start_addr;
|
||||
let mem_reg_prop = [memory_region_start_addr, memory_region_size];
|
||||
// With feature `acpi` enabled, RAM at 0-4M is for edk2 only
|
||||
// and should be hidden to the guest.
|
||||
#[cfg(feature = "acpi")]
|
||||
if memory_region_start_addr == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let memory_node_name = format!("memory@{:x}", memory_region_start_addr);
|
||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||
fdt.property_u32("numa-node-id", numa_node_idx as u32)?;
|
||||
fdt.end_node(memory_node)?;
|
||||
}
|
||||
let memory_node_name = format!("memory@{:x}", node_memory_addr);
|
||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||
fdt.property_u32("numa-node-id", numa_node_idx as u32)?;
|
||||
fdt.end_node(memory_node)?;
|
||||
}
|
||||
} else {
|
||||
let last_addr = guest_mem.last_addr().raw_value();
|
||||
if last_addr < super::layout::MEM_32BIT_RESERVED_START.raw_value() {
|
||||
// Case 1: all RAM is under the hole
|
||||
let mem_size = last_addr - super::layout::RAM_START.raw_value() + 1;
|
||||
let mem_reg_prop = [super::layout::RAM_START.raw_value() as u64, mem_size as u64];
|
||||
let memory_node = fdt.begin_node("memory")?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||
fdt.end_node(memory_node)?;
|
||||
} else {
|
||||
// Case 2: RAM is split by the hole
|
||||
// Region 1: RAM before the hole
|
||||
let mem_size = super::layout::MEM_32BIT_RESERVED_START.raw_value()
|
||||
- super::layout::RAM_START.raw_value();
|
||||
let mem_reg_prop = [super::layout::RAM_START.raw_value() as u64, mem_size as u64];
|
||||
let memory_node_name = format!("memory@{:x}", super::layout::RAM_START.raw_value());
|
||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||
fdt.end_node(memory_node)?;
|
||||
let mem_size = guest_mem.last_addr().raw_value() - super::layout::RAM_64BIT_START + 1;
|
||||
// See https://github.com/torvalds/linux/blob/master/Documentation/devicetree/booting-without-of.txt#L960
|
||||
// for an explanation of this.
|
||||
let mem_reg_prop = [super::layout::RAM_64BIT_START as u64, mem_size as u64];
|
||||
let memory_node = fdt.begin_node("memory")?;
|
||||
|
||||
// Region 2: RAM after the hole
|
||||
let mem_size = last_addr - super::layout::RAM_64BIT_START.raw_value() + 1;
|
||||
let mem_reg_prop = [
|
||||
super::layout::RAM_64BIT_START.raw_value() as u64,
|
||||
mem_size as u64,
|
||||
];
|
||||
let memory_node_name =
|
||||
format!("memory@{:x}", super::layout::RAM_64BIT_START.raw_value());
|
||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||
fdt.end_node(memory_node)?;
|
||||
}
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||
fdt.end_node(memory_node)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -540,24 +540,6 @@ fn create_devices_node<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::Buil
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_pmu_node(fdt: &mut FdtWriter, cpu_nums: usize) -> FdtWriterResult<()> {
|
||||
let num_cpus = cpu_nums as u64 as u32;
|
||||
let compatible = "arm,armv8-pmuv3";
|
||||
let cpu_mask: u32 =
|
||||
(((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK;
|
||||
let irq = [
|
||||
GIC_FDT_IRQ_TYPE_PPI,
|
||||
AARCH64_PMU_IRQ,
|
||||
cpu_mask | IRQ_TYPE_LEVEL_HI,
|
||||
];
|
||||
|
||||
let pmu_node = fdt.begin_node("pmu")?;
|
||||
fdt.property_string("compatible", compatible)?;
|
||||
fdt.property_array_u32("interrupts", &irq)?;
|
||||
fdt.end_node(pmu_node)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_pci_nodes(
|
||||
fdt: &mut FdtWriter,
|
||||
pci_device_info: &[PciSpaceInfo],
|
||||
@@ -573,19 +555,20 @@ fn create_pci_nodes(
|
||||
// could fall bellow 4G.
|
||||
// Here we cut off PCI device space below 8G in FDT to workaround the EDK2 check.
|
||||
// But the address written in ACPI is not impacted.
|
||||
let (pci_device_base_64bit, pci_device_size_64bit) =
|
||||
if pci_device_info_elem.pci_device_space_start < PCI_HIGH_BASE.raw_value() {
|
||||
(
|
||||
PCI_HIGH_BASE.raw_value(),
|
||||
pci_device_info_elem.pci_device_space_size
|
||||
- (PCI_HIGH_BASE.raw_value() - pci_device_info_elem.pci_device_space_start),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
pci_device_info_elem.pci_device_space_start,
|
||||
pci_device_info_elem.pci_device_space_size,
|
||||
)
|
||||
};
|
||||
let (pci_device_base_64bit, pci_device_size_64bit) = if cfg!(feature = "acpi")
|
||||
&& (pci_device_info_elem.pci_device_space_start < PCI_HIGH_BASE)
|
||||
{
|
||||
(
|
||||
PCI_HIGH_BASE,
|
||||
pci_device_info_elem.pci_device_space_size
|
||||
- (PCI_HIGH_BASE - pci_device_info_elem.pci_device_space_start),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
pci_device_info_elem.pci_device_space_start,
|
||||
pci_device_info_elem.pci_device_space_size,
|
||||
)
|
||||
};
|
||||
// There is no specific requirement of the 32bit MMIO range, and
|
||||
// therefore at least we can make these ranges 4K aligned.
|
||||
let pci_device_size_32bit: u64 =
|
||||
|
||||
@@ -22,7 +22,6 @@ pub mod kvm {
|
||||
use std::{boxed::Box, result};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use vm_memory::Address;
|
||||
use vm_migration::{
|
||||
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable,
|
||||
VersionMapped,
|
||||
@@ -84,7 +83,7 @@ pub mod kvm {
|
||||
|
||||
/// Get the address of the GIC distributor.
|
||||
pub fn get_dist_addr() -> u64 {
|
||||
layout::GIC_V3_DIST_START.raw_value()
|
||||
layout::GIC_V3_DIST_START
|
||||
}
|
||||
|
||||
/// Get the size of the GIC distributor.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//
|
||||
// Memory layout of AArch64 guest:
|
||||
// Memory layout of Aarch64 guest:
|
||||
//
|
||||
// Physical +---------------------------------------------------------------+
|
||||
// address | |
|
||||
@@ -20,13 +20,6 @@
|
||||
// | DRAM |
|
||||
// | |
|
||||
// | |
|
||||
// 4GB +---------------------------------------------------------------+
|
||||
// | 32-bit devices hole |
|
||||
// 4GB-64M +---------------------------------------------------------------+
|
||||
// | |
|
||||
// | |
|
||||
// | DRAM |
|
||||
// | |
|
||||
// | |
|
||||
// 1GB +---------------------------------------------------------------+
|
||||
// | |
|
||||
@@ -42,11 +35,11 @@
|
||||
// | Legacy devices space |
|
||||
// | |
|
||||
// 144 M +---------------------------------------------------------------|
|
||||
// | |
|
||||
// | Reserved (now GIC is here) |
|
||||
// 64 M +---------------------------------------------------------------+
|
||||
// | |
|
||||
// | UEFI space |
|
||||
// | |
|
||||
// 4 M +---------------------------------------------------------------+
|
||||
// | UEFI flash |
|
||||
// 0GB +---------------------------------------------------------------+
|
||||
//
|
||||
//
|
||||
@@ -55,16 +48,17 @@ use vm_memory::GuestAddress;
|
||||
|
||||
/// 0x0 ~ 0x40_0000 (4 MiB) is reserved to UEFI
|
||||
/// UEFI binary size is required less than 3 MiB, reserving 4 MiB is enough.
|
||||
pub const UEFI_START: GuestAddress = GuestAddress(0);
|
||||
pub const UEFI_START: u64 = 0x0;
|
||||
pub const MEM_UEFI_START: GuestAddress = GuestAddress(0);
|
||||
pub const UEFI_SIZE: u64 = 0x040_0000;
|
||||
|
||||
/// Below this address will reside the GIC, above this address will reside the MMIO devices.
|
||||
pub const MAPPED_IO_START: GuestAddress = GuestAddress(0x0900_0000);
|
||||
pub const MAPPED_IO_START: u64 = 0x0900_0000;
|
||||
|
||||
/// See kernel file arch/arm64/include/uapi/asm/kvm.h for the GIC related definitions.
|
||||
/// 0x08ff_0000 ~ 0x0900_0000 is reserved for GICv3 Distributor
|
||||
pub const GIC_V3_DIST_SIZE: u64 = 0x01_0000;
|
||||
pub const GIC_V3_DIST_START: GuestAddress = GuestAddress(MAPPED_IO_START.0 - GIC_V3_DIST_SIZE);
|
||||
pub const GIC_V3_DIST_START: u64 = MAPPED_IO_START - GIC_V3_DIST_SIZE;
|
||||
/// Below 0x08ff_0000 is reserved for GICv3 Redistributor.
|
||||
/// The size defined here is for each vcpu.
|
||||
/// The total size is 'number_of_vcpu * GIC_V3_REDIST_SIZE'
|
||||
@@ -73,9 +67,9 @@ pub const GIC_V3_REDIST_SIZE: u64 = 0x02_0000;
|
||||
pub const GIC_V3_ITS_SIZE: u64 = 0x02_0000;
|
||||
|
||||
/// Space 0x0900_0000 ~ 0x0905_0000 is reserved for legacy devices.
|
||||
pub const LEGACY_SERIAL_MAPPED_IO_START: GuestAddress = GuestAddress(0x0900_0000);
|
||||
pub const LEGACY_RTC_MAPPED_IO_START: GuestAddress = GuestAddress(0x0901_0000);
|
||||
pub const LEGACY_GPIO_MAPPED_IO_START: GuestAddress = GuestAddress(0x0902_0000);
|
||||
pub const LEGACY_SERIAL_MAPPED_IO_START: u64 = 0x0900_0000;
|
||||
pub const LEGACY_RTC_MAPPED_IO_START: u64 = 0x0901_0000;
|
||||
pub const LEGACY_GPIO_MAPPED_IO_START: u64 = 0x0902_0000;
|
||||
|
||||
/// Space 0x0905_0000 ~ 0x0906_0000 is reserved for pcie io address
|
||||
pub const MEM_PCI_IO_START: GuestAddress = GuestAddress(0x0905_0000);
|
||||
@@ -91,15 +85,8 @@ pub const PCI_MMCONFIG_SIZE: u64 = 256 << 20;
|
||||
// One bus with potentially 256 devices (32 slots x 8 functions).
|
||||
pub const PCI_MMIO_CONFIG_SIZE_PER_SEGMENT: u64 = 4096 * 256;
|
||||
|
||||
/// Start of RAM.
|
||||
pub const RAM_START: GuestAddress = GuestAddress(0x4000_0000);
|
||||
|
||||
/// 32-bit reserved area: 64MiB before 4GiB
|
||||
pub const MEM_32BIT_RESERVED_START: GuestAddress = GuestAddress(0xfc00_0000);
|
||||
pub const MEM_32BIT_RESERVED_SIZE: u64 = 0x0400_0000;
|
||||
|
||||
/// Start of 64-bit RAM.
|
||||
pub const RAM_64BIT_START: GuestAddress = GuestAddress(0x1_0000_0000);
|
||||
/// Start of RAM on 64 bit ARM.
|
||||
pub const RAM_64BIT_START: u64 = 0x4000_0000;
|
||||
|
||||
/// Kernel command line maximum size.
|
||||
/// As per `arch/arm64/include/uapi/asm/setup.h`.
|
||||
@@ -107,19 +94,19 @@ pub const CMDLINE_MAX_SIZE: usize = 2048;
|
||||
|
||||
/// FDT is at the beginning of RAM.
|
||||
/// Maximum size of the device tree blob as specified in https://www.kernel.org/doc/Documentation/arm64/booting.txt.
|
||||
pub const FDT_START: GuestAddress = RAM_START;
|
||||
pub const FDT_MAX_SIZE: u64 = 0x20_0000;
|
||||
pub const FDT_START: u64 = RAM_64BIT_START;
|
||||
pub const FDT_MAX_SIZE: usize = 0x20_0000;
|
||||
|
||||
/// Put ACPI table above dtb
|
||||
pub const ACPI_START: GuestAddress = GuestAddress(RAM_START.0 + FDT_MAX_SIZE);
|
||||
pub const ACPI_MAX_SIZE: u64 = 0x20_0000;
|
||||
pub const RSDP_POINTER: GuestAddress = ACPI_START;
|
||||
pub const ACPI_START: u64 = RAM_64BIT_START + FDT_MAX_SIZE as u64;
|
||||
pub const ACPI_MAX_SIZE: usize = 0x20_0000;
|
||||
pub const RSDP_POINTER: GuestAddress = GuestAddress(ACPI_START);
|
||||
|
||||
/// Kernel start after FDT and ACPI
|
||||
pub const KERNEL_START: GuestAddress = GuestAddress(ACPI_START.0 + ACPI_MAX_SIZE);
|
||||
pub const KERNEL_START: u64 = ACPI_START + ACPI_MAX_SIZE as u64;
|
||||
|
||||
/// Pci high memory base
|
||||
pub const PCI_HIGH_BASE: GuestAddress = GuestAddress(0x2_0000_0000);
|
||||
pub const PCI_HIGH_BASE: u64 = 0x2_0000_0000_u64;
|
||||
|
||||
// As per virt/kvm/arm/vgic/vgic-kvm-device.c we need
|
||||
// the number of interrupts our GIC will support to be:
|
||||
|
||||
@@ -43,14 +43,11 @@ pub enum Error {
|
||||
|
||||
/// Error configuring the MPIDR register
|
||||
VcpuRegMpidr(hypervisor::HypervisorCpuError),
|
||||
|
||||
/// Error initializing PMU for vcpu
|
||||
VcpuInitPmu,
|
||||
}
|
||||
|
||||
impl From<Error> for super::Error {
|
||||
fn from(e: Error) -> super::Error {
|
||||
super::Error::PlatformSpecific(e)
|
||||
super::Error::AArch64Setup(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,25 +61,45 @@ pub struct EntryPoint {
|
||||
|
||||
/// Configure the specified VCPU, and return its MPIDR.
|
||||
pub fn configure_vcpu(
|
||||
vcpu: &Arc<dyn hypervisor::Vcpu>,
|
||||
fd: &Arc<dyn hypervisor::Vcpu>,
|
||||
id: u8,
|
||||
kernel_entry_point: Option<EntryPoint>,
|
||||
) -> super::Result<u64> {
|
||||
if let Some(kernel_entry_point) = kernel_entry_point {
|
||||
regs::setup_regs(vcpu, id, kernel_entry_point.entry_addr.raw_value())
|
||||
regs::setup_regs(fd, id, kernel_entry_point.entry_addr.raw_value())
|
||||
.map_err(Error::RegsConfiguration)?;
|
||||
}
|
||||
|
||||
let mpidr = vcpu.read_mpidr().map_err(Error::VcpuRegMpidr)?;
|
||||
let mpidr = fd.read_mpidr().map_err(Error::VcpuRegMpidr)?;
|
||||
Ok(mpidr)
|
||||
}
|
||||
|
||||
pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> {
|
||||
let mut regions = vec![
|
||||
// 0 MiB ~ 256 MiB: UEFI, GIC and legacy devices
|
||||
// Normally UEFI should be loaded to a flash area at the beginning of memory.
|
||||
// But now flash memory type is not supported.
|
||||
// As a workaround, we take 4 MiB memory from the main RAM for UEFI.
|
||||
// As a result, the RAM that the guest can see is less than what has been
|
||||
// assigned in command line, when ACPI and UEFI is enabled.
|
||||
let ram_deduction = if cfg!(feature = "acpi") {
|
||||
layout::UEFI_SIZE
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
vec![
|
||||
// 0 ~ 4 MiB: Reserved for UEFI space
|
||||
#[cfg(feature = "acpi")]
|
||||
(GuestAddress(0), layout::UEFI_SIZE as usize, RegionType::Ram),
|
||||
#[cfg(not(feature = "acpi"))]
|
||||
(
|
||||
GuestAddress(0),
|
||||
layout::MEM_32BIT_DEVICES_START.0 as usize,
|
||||
layout::UEFI_SIZE as usize,
|
||||
RegionType::Reserved,
|
||||
),
|
||||
// 4 MiB ~ 256 MiB: Gic and legacy devices
|
||||
(
|
||||
GuestAddress(layout::UEFI_SIZE),
|
||||
(layout::MEM_32BIT_DEVICES_START.0 - layout::UEFI_SIZE) as usize,
|
||||
RegionType::Reserved,
|
||||
),
|
||||
// 256 MiB ~ 768 MiB: MMIO space
|
||||
@@ -97,39 +114,13 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
|
||||
layout::PCI_MMCONFIG_SIZE as usize,
|
||||
RegionType::Reserved,
|
||||
),
|
||||
];
|
||||
|
||||
let ram_32bit_space_size =
|
||||
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START);
|
||||
|
||||
// RAM space
|
||||
// Case1: guest memory fits before the gap
|
||||
if size as u64 <= ram_32bit_space_size {
|
||||
regions.push((layout::RAM_START, size as usize, RegionType::Ram));
|
||||
// Case2: guest memory extends beyond the gap
|
||||
} else {
|
||||
// Push memory before the gap
|
||||
regions.push((
|
||||
layout::RAM_START,
|
||||
ram_32bit_space_size as usize,
|
||||
// 1 GiB ~ : Ram
|
||||
(
|
||||
GuestAddress(layout::RAM_64BIT_START),
|
||||
(size - ram_deduction) as usize,
|
||||
RegionType::Ram,
|
||||
));
|
||||
// Other memory is placed after 4GiB
|
||||
regions.push((
|
||||
layout::RAM_64BIT_START,
|
||||
(size - ram_32bit_space_size) as usize,
|
||||
RegionType::Ram,
|
||||
));
|
||||
}
|
||||
|
||||
// Add the 32-bit reserved memory hole as a reserved region
|
||||
regions.push((
|
||||
layout::MEM_32BIT_RESERVED_START,
|
||||
layout::MEM_32BIT_RESERVED_SIZE as usize,
|
||||
RegionType::Reserved,
|
||||
));
|
||||
|
||||
regions
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
/// Configures the system and should be called once per vm before starting vcpu threads.
|
||||
@@ -145,7 +136,6 @@ pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::Bui
|
||||
virtio_iommu_bdf: Option<u32>,
|
||||
gic_device: &dyn GicDevice,
|
||||
numa_nodes: &NumaNodes,
|
||||
pmu_supported: bool,
|
||||
) -> super::Result<()> {
|
||||
let fdt_final = fdt::create_fdt(
|
||||
guest_mem,
|
||||
@@ -158,7 +148,6 @@ pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::Bui
|
||||
pci_space_info,
|
||||
numa_nodes,
|
||||
virtio_iommu_bdf,
|
||||
pmu_supported,
|
||||
)
|
||||
.map_err(|_| Error::SetupFdt)?;
|
||||
|
||||
@@ -185,13 +174,28 @@ pub fn initramfs_load_addr(
|
||||
if guest_mem.address_in_range(offset) {
|
||||
Ok(offset.raw_value())
|
||||
} else {
|
||||
Err(super::Error::PlatformSpecific(Error::InitramfsAddress))
|
||||
Err(super::Error::AArch64Setup(Error::InitramfsAddress))
|
||||
}
|
||||
}
|
||||
None => Err(super::Error::PlatformSpecific(Error::InitramfsAddress)),
|
||||
None => Err(super::Error::AArch64Setup(Error::InitramfsAddress)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the memory address where the kernel could be loaded.
|
||||
pub fn get_kernel_start() -> u64 {
|
||||
layout::KERNEL_START
|
||||
}
|
||||
|
||||
///Return guest memory address where the uefi should be loaded.
|
||||
pub fn get_uefi_start() -> u64 {
|
||||
layout::UEFI_START
|
||||
}
|
||||
|
||||
// Auxiliary function to get the address where the device tree blob is loaded.
|
||||
fn get_fdt_addr() -> u64 {
|
||||
layout::FDT_START
|
||||
}
|
||||
|
||||
pub fn get_host_cpu_phys_bits() -> u8 {
|
||||
// A dummy hypervisor created only for querying the host IPA size and will
|
||||
// be freed after the query.
|
||||
@@ -211,26 +215,11 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_arch_memory_regions_dram_2gb() {
|
||||
let regions = arch_memory_regions((1usize << 31) as u64); //2GB
|
||||
assert_eq!(5, regions.len());
|
||||
assert_eq!(layout::RAM_START, regions[3].0);
|
||||
assert_eq!((1usize << 31), regions[3].1);
|
||||
assert_eq!(RegionType::Ram, regions[3].2);
|
||||
assert_eq!(RegionType::Reserved, regions[4].2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arch_memory_regions_dram_4gb() {
|
||||
fn test_arch_memory_regions_dram() {
|
||||
let regions = arch_memory_regions((1usize << 32) as u64); //4GB
|
||||
let ram_32bit_space_size =
|
||||
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START) as usize;
|
||||
assert_eq!(6, regions.len());
|
||||
assert_eq!(layout::RAM_START, regions[3].0);
|
||||
assert_eq!(ram_32bit_space_size as usize, regions[3].1);
|
||||
assert_eq!(RegionType::Ram, regions[3].2);
|
||||
assert_eq!(RegionType::Reserved, regions[5].2);
|
||||
assert_eq!(5, regions.len());
|
||||
assert_eq!(GuestAddress(layout::RAM_64BIT_START), regions[4].0);
|
||||
assert_eq!(1usize << 32, regions[4].1);
|
||||
assert_eq!(RegionType::Ram, regions[4].2);
|
||||
assert_eq!(((1usize << 32) - ram_32bit_space_size), regions[4].1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the THIRD-PARTY file.
|
||||
|
||||
use super::get_fdt_addr;
|
||||
use hypervisor::kvm::kvm_bindings::{
|
||||
kvm_regs, user_pt_regs, KVM_REG_ARM64, KVM_REG_ARM_CORE, KVM_REG_SIZE_U64,
|
||||
};
|
||||
use hypervisor::{arm64_core_reg_id, offset__of};
|
||||
use std::sync::Arc;
|
||||
use std::{mem, result};
|
||||
use vm_memory::Address;
|
||||
|
||||
/// Errors thrown while setting aarch64 registers.
|
||||
#[derive(Debug)]
|
||||
@@ -67,7 +67,7 @@ pub fn setup_regs(vcpu: &Arc<dyn hypervisor::Vcpu>, cpu_id: u8, boot_ip: u64) ->
|
||||
let regs0 = offset__of!(user_pt_regs, regs) + kreg_off;
|
||||
vcpu.set_reg(
|
||||
arm64_core_reg_id!(KVM_REG_SIZE_U64, regs0),
|
||||
super::layout::FDT_START.raw_value(),
|
||||
get_fdt_addr() as u64,
|
||||
)
|
||||
.map_err(Error::SetCoreRegister)?;
|
||||
}
|
||||
|
||||
@@ -9,15 +9,15 @@
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::x86_64::SgxEpcSection;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
use versionize::{VersionMap, Versionize, VersionizeError, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use vm_migration::VersionMapped;
|
||||
@@ -26,27 +26,31 @@ type GuestMemoryMmap = vm_memory::GuestMemoryMmap<vm_memory::bitmap::AtomicBitma
|
||||
type GuestRegionMmap = vm_memory::GuestRegionMmap<vm_memory::bitmap::AtomicBitmap>;
|
||||
|
||||
/// Type for returning error code.
|
||||
#[derive(Debug, Error)]
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[error("Platform specific error (x86_64): {0:?}")]
|
||||
PlatformSpecific(x86_64::Error),
|
||||
/// X86_64 specific error triggered during system configuration.
|
||||
X86_64Setup(x86_64::Error),
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[error("Platform specific error (aarch64): {0:?}")]
|
||||
PlatformSpecific(aarch64::Error),
|
||||
#[error("The memory map table extends past the end of guest memory")]
|
||||
/// AArch64 specific error triggered during system configuration.
|
||||
AArch64Setup(aarch64::Error),
|
||||
/// The zero page extends past the end of guest_mem.
|
||||
ZeroPagePastRamEnd,
|
||||
/// Error writing the zero page of guest memory.
|
||||
ZeroPageSetup(vm_memory::GuestMemoryError),
|
||||
/// The memory map table extends past the end of guest memory.
|
||||
MemmapTablePastRamEnd,
|
||||
#[error("Error writing memory map table to guest memory")]
|
||||
/// Error writing memory map table to guest memory.
|
||||
MemmapTableSetup,
|
||||
#[error("The hvm_start_info structure extends past the end of guest memory")]
|
||||
/// The hvm_start_info structure extends past the end of guest memory.
|
||||
StartInfoPastRamEnd,
|
||||
#[error("Error writing hvm_start_info to guest memory")]
|
||||
/// Error writing hvm_start_info to guest memory.
|
||||
StartInfoSetup,
|
||||
#[error("Failed to compute initramfs address")]
|
||||
/// Failed to compute initramfs address.
|
||||
InitramfsAddress,
|
||||
#[error("Error writing module entry to guest memory: {0}")]
|
||||
ModlistSetup(#[source] vm_memory::GuestMemoryError),
|
||||
#[error("RSDP extends past the end of guest memory")]
|
||||
/// Error writing module entry to guest memory.
|
||||
ModlistSetup(vm_memory::GuestMemoryError),
|
||||
/// RSDP Beyond Guest Memory
|
||||
RsdpPastRamEnd,
|
||||
}
|
||||
|
||||
@@ -81,8 +85,8 @@ pub mod aarch64;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub use aarch64::{
|
||||
arch_memory_regions, configure_system, configure_vcpu, fdt::DeviceInfoForFdt,
|
||||
get_host_cpu_phys_bits, initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE,
|
||||
layout::IRQ_BASE, uefi, EntryPoint,
|
||||
get_host_cpu_phys_bits, get_kernel_start, get_uefi_start, initramfs_load_addr, layout,
|
||||
layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE, uefi, EntryPoint,
|
||||
};
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -154,7 +158,6 @@ impl fmt::Display for DeviceType {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub struct MmioDeviceInfo {
|
||||
pub addr: u64,
|
||||
pub len: u64,
|
||||
pub irq: u32,
|
||||
}
|
||||
|
||||
@@ -177,6 +180,6 @@ impl DeviceInfoForFdt for MmioDeviceInfo {
|
||||
self.irq
|
||||
}
|
||||
fn length(&self) -> u64 {
|
||||
self.len
|
||||
4096
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,6 @@ pub const LOW_RAM_START: GuestAddress = GuestAddress(0x0);
|
||||
|
||||
// == Fixed addresses within the "Low RAM" range: ==
|
||||
|
||||
// Location of EBDA address
|
||||
pub const EBDA_POINTER: GuestAddress = GuestAddress(0x40e);
|
||||
|
||||
// Initial GDT/IDT needed to boot kernel
|
||||
pub const BOOT_GDT_START: GuestAddress = GuestAddress(0x500);
|
||||
pub const BOOT_IDT_START: GuestAddress = GuestAddress(0x520);
|
||||
|
||||
@@ -15,8 +15,7 @@ pub mod regs;
|
||||
use crate::GuestMemoryMmap;
|
||||
use crate::InitramfsConfig;
|
||||
use crate::RegionType;
|
||||
use hypervisor::x86_64::{CpuId, CpuIdEntry, CPUID_FLAG_VALID_INDEX};
|
||||
use hypervisor::HypervisorError;
|
||||
use hypervisor::{CpuId, CpuIdEntry, HypervisorError, CPUID_FLAG_VALID_INDEX};
|
||||
use linux_loader::loader::bootparam::boot_params;
|
||||
use linux_loader::loader::elf::start_info::{
|
||||
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
|
||||
@@ -187,18 +186,11 @@ pub enum Error {
|
||||
|
||||
/// Error checking CPUID compatibility
|
||||
CpuidCheckCompatibility,
|
||||
|
||||
// Error writing EBDA address
|
||||
EbdaSetup(vm_memory::GuestMemoryError),
|
||||
|
||||
/// Error retrieving TDX capabilities through the hypervisor (kvm/mshv) API
|
||||
#[cfg(feature = "tdx")]
|
||||
TdxCapabilities(HypervisorError),
|
||||
}
|
||||
|
||||
impl From<Error> for super::Error {
|
||||
fn from(e: Error) -> super::Error {
|
||||
super::Error::PlatformSpecific(e)
|
||||
super::Error::X86_64Setup(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -605,39 +597,9 @@ pub fn generate_common_cpuid(
|
||||
update_cpuid_sgx(&mut cpuid, sgx_epc_sections)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
let tdx_capabilities = if tdx_enabled {
|
||||
let caps = hypervisor
|
||||
.tdx_capabilities()
|
||||
.map_err(Error::TdxCapabilities)?;
|
||||
info!("TDX capabilities {:#?}", caps);
|
||||
Some(caps)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Update some existing CPUID
|
||||
for entry in cpuid.as_mut_slice().iter_mut() {
|
||||
match entry.function {
|
||||
0xd =>
|
||||
{
|
||||
#[cfg(feature = "tdx")]
|
||||
if let Some(caps) = &tdx_capabilities {
|
||||
let xcr0_mask: u64 = 0x82ff;
|
||||
let xss_mask: u64 = !xcr0_mask;
|
||||
if entry.index == 0 {
|
||||
entry.eax &= (caps.xfam_fixed0 as u32) & (xcr0_mask as u32);
|
||||
entry.eax |= (caps.xfam_fixed1 as u32) & (xcr0_mask as u32);
|
||||
entry.edx &= ((caps.xfam_fixed0 & xcr0_mask) >> 32) as u32;
|
||||
entry.edx |= ((caps.xfam_fixed1 & xcr0_mask) >> 32) as u32;
|
||||
} else if entry.index == 1 {
|
||||
entry.ecx &= (caps.xfam_fixed0 as u32) & (xss_mask as u32);
|
||||
entry.ecx |= (caps.xfam_fixed1 as u32) & (xss_mask as u32);
|
||||
entry.edx &= ((caps.xfam_fixed0 & xss_mask) >> 32) as u32;
|
||||
entry.edx |= ((caps.xfam_fixed1 & xss_mask) >> 32) as u32;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set CPU physical bits
|
||||
0x8000_0008 => {
|
||||
entry.eax = (entry.eax & 0xffff_ff00) | (phys_bits as u32 & 0xff);
|
||||
@@ -744,7 +706,7 @@ pub fn generate_common_cpuid(
|
||||
}
|
||||
|
||||
pub fn configure_vcpu(
|
||||
vcpu: &Arc<dyn hypervisor::Vcpu>,
|
||||
fd: &Arc<dyn hypervisor::Vcpu>,
|
||||
id: u8,
|
||||
kernel_entry_point: Option<EntryPoint>,
|
||||
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
@@ -756,23 +718,23 @@ pub fn configure_vcpu(
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id));
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1f, None, CpuidReg::EDX, u32::from(id));
|
||||
|
||||
vcpu.set_cpuid2(&cpuid)
|
||||
fd.set_cpuid2(&cpuid)
|
||||
.map_err(|e| Error::SetSupportedCpusFailed(e.into()))?;
|
||||
|
||||
if kvm_hyperv {
|
||||
vcpu.enable_hyperv_synic().unwrap();
|
||||
fd.enable_hyperv_synic().unwrap();
|
||||
}
|
||||
|
||||
regs::setup_msrs(vcpu).map_err(Error::MsrsConfiguration)?;
|
||||
regs::setup_msrs(fd).map_err(Error::MsrsConfiguration)?;
|
||||
if let Some(kernel_entry_point) = kernel_entry_point {
|
||||
if let Some(entry_addr) = kernel_entry_point.entry_addr {
|
||||
// Safe to unwrap because this method is called after the VM is configured
|
||||
regs::setup_regs(vcpu, entry_addr.raw_value()).map_err(Error::RegsConfiguration)?;
|
||||
regs::setup_fpu(vcpu).map_err(Error::FpuConfiguration)?;
|
||||
regs::setup_sregs(&vm_memory.memory(), vcpu).map_err(Error::SregsConfiguration)?;
|
||||
regs::setup_regs(fd, entry_addr.raw_value()).map_err(Error::RegsConfiguration)?;
|
||||
regs::setup_fpu(fd).map_err(Error::FpuConfiguration)?;
|
||||
regs::setup_sregs(&vm_memory.memory(), fd).map_err(Error::SregsConfiguration)?;
|
||||
}
|
||||
}
|
||||
interrupts::set_lint(vcpu).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
|
||||
interrupts::set_lint(fd).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -839,14 +801,8 @@ pub fn configure_system(
|
||||
_num_cpus: u8,
|
||||
rsdp_addr: Option<GuestAddress>,
|
||||
sgx_epc_region: Option<SgxEpcRegion>,
|
||||
serial_number: Option<&str>,
|
||||
) -> super::Result<()> {
|
||||
// Write EBDA address to location where ACPICA expects to find it
|
||||
guest_mem
|
||||
.write_obj((layout::EBDA_START.0 >> 4) as u16, layout::EBDA_POINTER)
|
||||
.map_err(Error::EbdaSetup)?;
|
||||
|
||||
let size = smbios::setup_smbios(guest_mem, serial_number).map_err(Error::SmbiosSetup)?;
|
||||
let size = smbios::setup_smbios(guest_mem).map_err(Error::SmbiosSetup)?;
|
||||
|
||||
// Place the MP table after the SMIOS table aligned to 16 bytes
|
||||
let offset = GuestAddress(layout::SMBIOS_START).unchecked_add(size);
|
||||
@@ -1195,7 +1151,6 @@ mod tests {
|
||||
1,
|
||||
Some(layout::RSDP_POINTER),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
assert!(config_err.is_err());
|
||||
|
||||
@@ -1209,7 +1164,7 @@ mod tests {
|
||||
.collect();
|
||||
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
||||
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None, None).unwrap();
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None).unwrap();
|
||||
|
||||
// Now assigning some memory that is equal to the start of the 32bit memory hole.
|
||||
let mem_size = 3328 << 20;
|
||||
@@ -1220,9 +1175,9 @@ mod tests {
|
||||
.map(|r| (r.0, r.1))
|
||||
.collect();
|
||||
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None, None).unwrap();
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None).unwrap();
|
||||
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None, None).unwrap();
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None).unwrap();
|
||||
|
||||
// Now assigning some memory that falls after the 32bit memory hole.
|
||||
let mem_size = 3330 << 20;
|
||||
@@ -1233,9 +1188,9 @@ mod tests {
|
||||
.map(|r| (r.0, r.1))
|
||||
.collect();
|
||||
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None, None).unwrap();
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None).unwrap();
|
||||
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None, None).unwrap();
|
||||
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
use crate::layout::{BOOT_GDT_START, BOOT_IDT_START, PVH_INFO_START};
|
||||
use crate::GuestMemoryMmap;
|
||||
use hypervisor::arch::x86::gdt::{gdt_entry, segment_from_gdt};
|
||||
use hypervisor::arch::x86::regs::CR0_PE;
|
||||
use hypervisor::arch::x86::regs::*;
|
||||
use hypervisor::x86_64::{FpuState, SpecialRegisters, StandardRegisters};
|
||||
use std::sync::Arc;
|
||||
use std::{mem, result};
|
||||
|
||||
@@ -162,7 +162,7 @@ fn write_string(
|
||||
Ok(curptr)
|
||||
}
|
||||
|
||||
pub fn setup_smbios(mem: &GuestMemoryMmap, serial_number: Option<&str>) -> Result<u64> {
|
||||
pub fn setup_smbios(mem: &GuestMemoryMmap) -> Result<u64> {
|
||||
let physptr = GuestAddress(SMBIOS_START)
|
||||
.checked_add(mem::size_of::<Smbios30Entrypoint>() as u64)
|
||||
.ok_or(Error::NotEnoughMemory)?;
|
||||
@@ -195,15 +195,11 @@ pub fn setup_smbios(mem: &GuestMemoryMmap, serial_number: Option<&str>) -> Resul
|
||||
handle,
|
||||
manufacturer: 1, // First string written in this section
|
||||
product_name: 2, // Second string written in this section
|
||||
serial_number: serial_number.map(|_| 3).unwrap_or_default(), // 3rd string
|
||||
..Default::default()
|
||||
};
|
||||
curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
|
||||
curptr = write_string(mem, "Cloud Hypervisor", curptr)?;
|
||||
curptr = write_string(mem, "cloud-hypervisor", curptr)?;
|
||||
if let Some(serial_number) = serial_number {
|
||||
curptr = write_string(mem, serial_number, curptr)?;
|
||||
}
|
||||
curptr = write_and_incr(mem, 0u8, curptr)?;
|
||||
}
|
||||
|
||||
@@ -267,7 +263,7 @@ mod tests {
|
||||
fn entrypoint_checksum() {
|
||||
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap();
|
||||
|
||||
setup_smbios(&mem, None).unwrap();
|
||||
setup_smbios(&mem).unwrap();
|
||||
|
||||
let smbios_ep: Smbios30Entrypoint = mem.read_obj(GuestAddress(SMBIOS_START)).unwrap();
|
||||
|
||||
|
||||
@@ -52,9 +52,6 @@ pub enum TdvfSectionType {
|
||||
Cfv,
|
||||
TdHob,
|
||||
TempMem,
|
||||
PermMem,
|
||||
Payload,
|
||||
PayloadParam,
|
||||
Reserved = 0xffffffff,
|
||||
}
|
||||
|
||||
@@ -64,6 +61,57 @@ impl Default for TdvfSectionType {
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default, Debug)]
|
||||
pub struct TdVmmDataRegion {
|
||||
pub start_address: u64,
|
||||
pub length: u64,
|
||||
pub region_type: TdVmmDataRegionType,
|
||||
}
|
||||
|
||||
#[repr(u16)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum TdVmmDataRegionType {
|
||||
Signature = 0x0000,
|
||||
InterfaceVersion = 0x0001,
|
||||
SystemUuid = 0x0002,
|
||||
RamSize = 0x0003,
|
||||
GraphicsEnabled = 0x0004,
|
||||
SmpCpuCount = 0x0005,
|
||||
MachineId = 0x0006,
|
||||
KernelAddress = 0x0007,
|
||||
KernelSize = 0x0008,
|
||||
KernelCommandLine = 0x0009,
|
||||
InitrdAddress = 0x000a,
|
||||
InitrdSize = 0x000b,
|
||||
BootDevice = 0x000c,
|
||||
NumaData = 0x000d,
|
||||
BootMenu = 0x000e,
|
||||
MaximumCpuCount = 0x000f,
|
||||
KernelEntry = 0x0010,
|
||||
KernelData = 0x0011,
|
||||
InitrdData = 0x0012,
|
||||
CommandLineAddress = 0x0013,
|
||||
CommandLineSize = 0x0014,
|
||||
CommandLineData = 0x0015,
|
||||
KernelSetupAddress = 0x0016,
|
||||
KernelSetupSize = 0x0017,
|
||||
KernelSetupData = 0x0018,
|
||||
FileDir = 0x0019,
|
||||
AcpiTables = 0x8000,
|
||||
SmbiosTables = 0x8001,
|
||||
Irq0Override = 0x8002,
|
||||
E820Table = 0x8003,
|
||||
HpetData = 0x8004,
|
||||
Reserved = 0xffff,
|
||||
}
|
||||
|
||||
impl Default for TdVmmDataRegionType {
|
||||
fn default() -> Self {
|
||||
TdVmmDataRegionType::Reserved
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_tdvf_sections(file: &mut File) -> Result<Vec<TdvfSection>, TdvfError> {
|
||||
// The 32-bit offset to the TDVF metadata is located 32 bytes from
|
||||
// the end of the file.
|
||||
@@ -183,41 +231,20 @@ struct HobGuidType {
|
||||
name: EfiGuid,
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum PayloadImageType {
|
||||
ExecutablePayload,
|
||||
BzImage,
|
||||
RawVmLinux,
|
||||
}
|
||||
|
||||
impl Default for PayloadImageType {
|
||||
fn default() -> Self {
|
||||
PayloadImageType::ExecutablePayload
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
pub struct PayloadInfo {
|
||||
pub image_type: PayloadImageType,
|
||||
pub entry_point: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
struct TdPayload {
|
||||
struct TdVmmData {
|
||||
guid_type: HobGuidType,
|
||||
payload_info: PayloadInfo,
|
||||
region: TdVmmDataRegion,
|
||||
}
|
||||
|
||||
// SAFETY: These data structures only contain a series of integers
|
||||
unsafe impl ByteValued for TdVmmDataRegion {}
|
||||
unsafe impl ByteValued for HobHeader {}
|
||||
unsafe impl ByteValued for HobHandoffInfoTable {}
|
||||
unsafe impl ByteValued for HobResourceDescriptor {}
|
||||
unsafe impl ByteValued for HobGuidType {}
|
||||
unsafe impl ByteValued for PayloadInfo {}
|
||||
unsafe impl ByteValued for TdPayload {}
|
||||
unsafe impl ByteValued for TdVmmData {}
|
||||
|
||||
pub struct TdHob {
|
||||
start_offset: u64,
|
||||
@@ -324,10 +351,10 @@ impl TdHob {
|
||||
},
|
||||
/* TODO:
|
||||
* QEMU currently fills it in like this:
|
||||
* EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_TESTED
|
||||
* EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_ENCRYPTED | EFI_RESOURCE_ATTRIBUTE_TESTED
|
||||
* which differs from the spec (due to TDVF implementation issue?)
|
||||
*/
|
||||
0x7,
|
||||
0x04000007,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -349,90 +376,35 @@ impl TdHob {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn add_acpi_table(
|
||||
pub fn add_td_vmm_data(
|
||||
&mut self,
|
||||
mem: &GuestMemoryMmap,
|
||||
table_content: &[u8],
|
||||
region: TdVmmDataRegion,
|
||||
) -> Result<(), TdvfError> {
|
||||
// We already know the HobGuidType size is 8 bytes multiple, but we
|
||||
// need the total size to be 8 bytes multiple. That is why the ACPI
|
||||
// table size must be 8 bytes multiple as well.
|
||||
let length = std::mem::size_of::<HobGuidType>() as u16
|
||||
+ align_hob(table_content.len() as u64) as u16;
|
||||
let hob_guid_type = HobGuidType {
|
||||
header: HobHeader {
|
||||
r#type: HobType::GuidExtension,
|
||||
length,
|
||||
reserved: 0,
|
||||
},
|
||||
// ACPI_TABLE_HOB_GUID
|
||||
// 0x6a0c5870, 0xd4ed, 0x44f4, {0xa1, 0x35, 0xdd, 0x23, 0x8b, 0x6f, 0xc, 0x8d }
|
||||
name: EfiGuid {
|
||||
data1: 0x6a0c_5870,
|
||||
data2: 0xd4ed,
|
||||
data3: 0x44f4,
|
||||
data4: [0xa1, 0x35, 0xdd, 0x23, 0x8b, 0x6f, 0xc, 0x8d],
|
||||
},
|
||||
};
|
||||
info!(
|
||||
"Writing HOB ACPI table {:x} {:x?} {:x?}",
|
||||
self.current_offset, hob_guid_type, table_content
|
||||
);
|
||||
mem.write_obj(hob_guid_type, GuestAddress(self.current_offset))
|
||||
.map_err(TdvfError::GuestMemoryWriteHob)?;
|
||||
let current_offset = self.current_offset + std::mem::size_of::<HobGuidType>() as u64;
|
||||
|
||||
// In case the table is quite large, let's make sure we can handle
|
||||
// retrying until everything has been correctly copied.
|
||||
let mut offset: usize = 0;
|
||||
loop {
|
||||
let bytes_written = mem
|
||||
.write(
|
||||
&table_content[offset..],
|
||||
GuestAddress(current_offset + offset as u64),
|
||||
)
|
||||
.map_err(TdvfError::GuestMemoryWriteHob)?;
|
||||
offset += bytes_written;
|
||||
if offset >= table_content.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.current_offset += length as u64;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_payload(
|
||||
&mut self,
|
||||
mem: &GuestMemoryMmap,
|
||||
payload_info: PayloadInfo,
|
||||
) -> Result<(), TdvfError> {
|
||||
let payload = TdPayload {
|
||||
let td_vmm_data = TdVmmData {
|
||||
guid_type: HobGuidType {
|
||||
header: HobHeader {
|
||||
r#type: HobType::GuidExtension,
|
||||
length: std::mem::size_of::<TdPayload>() as u16,
|
||||
length: std::mem::size_of::<TdVmmData>() as u16,
|
||||
reserved: 0,
|
||||
},
|
||||
// HOB_PAYLOAD_INFO_GUID
|
||||
// 0xb96fa412, 0x461f, 0x4be3, {0x8c, 0xd, 0xad, 0x80, 0x5a, 0x49, 0x7a, 0xc0
|
||||
// TD_VMM_DATA_GUID CF2643E4-C0D3-46FF-0000-72EE623DDE38
|
||||
name: EfiGuid {
|
||||
data1: 0xb96f_a412,
|
||||
data2: 0x461f,
|
||||
data3: 0x4be3,
|
||||
data4: [0x8c, 0xd, 0xad, 0x80, 0x5a, 0x49, 0x7a, 0xc0],
|
||||
data1: 0xcf26_43e4,
|
||||
data2: 0xc0d3,
|
||||
data3: 0x46ff,
|
||||
data4: [0x00, 0x00, 0x72, 0xee, 0x62, 0x3d, 0xde, 0x38],
|
||||
},
|
||||
},
|
||||
payload_info,
|
||||
region,
|
||||
};
|
||||
info!(
|
||||
"Writing HOB TD_PAYLOAD {:x} {:x?}",
|
||||
self.current_offset, payload
|
||||
"Writing HOB TD_VMM_DATA {:x} {:x?}",
|
||||
self.current_offset, td_vmm_data
|
||||
);
|
||||
mem.write_obj(payload, GuestAddress(self.current_offset))
|
||||
mem.write_obj(td_vmm_data, GuestAddress(self.current_offset))
|
||||
.map_err(TdvfError::GuestMemoryWriteHob)?;
|
||||
self.update_offset::<TdPayload>();
|
||||
|
||||
self.update_offset::<TdVmmData>();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,23 @@
|
||||
name = "block_util"
|
||||
version = "0.1.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
io-uring = "0.5.2"
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.112"
|
||||
log = "0.4.14"
|
||||
qcow = { path = "../qcow" }
|
||||
thiserror = "1.0.31"
|
||||
thiserror = "1.0.30"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
vhdx = { path = "../vhdx" }
|
||||
virtio-bindings = { version = "0.1.0", features = ["virtio-v5_0_0"] }
|
||||
virtio-queue = "0.3.0"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
virtio-queue = { path = "../virtio-queue" }
|
||||
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ use std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
|
||||
use std::os::linux::fs::MetadataExt;
|
||||
use std::path::Path;
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
use std::sync::MutexGuard;
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
@@ -40,7 +39,6 @@ use vm_memory::{
|
||||
bitmap::AtomicBitmap, bitmap::Bitmap, ByteValued, Bytes, GuestAddress, GuestMemory,
|
||||
GuestMemoryError, GuestMemoryLoadGuard,
|
||||
};
|
||||
use vm_virtio::{AccessPlatform, Translatable};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
@@ -192,7 +190,6 @@ pub struct Request {
|
||||
impl Request {
|
||||
pub fn parse(
|
||||
desc_chain: &mut DescriptorChain<GuestMemoryLoadGuard<GuestMemoryMmap>>,
|
||||
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
||||
) -> result::Result<Request, Error> {
|
||||
let hdr_desc = desc_chain
|
||||
.next()
|
||||
@@ -207,13 +204,9 @@ impl Request {
|
||||
return Err(Error::UnexpectedWriteOnlyDescriptor);
|
||||
}
|
||||
|
||||
let hdr_desc_addr = hdr_desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, hdr_desc.len() as usize);
|
||||
|
||||
let mut req = Request {
|
||||
request_type: request_type(desc_chain.memory(), hdr_desc_addr)?,
|
||||
sector: sector(desc_chain.memory(), hdr_desc_addr)?,
|
||||
request_type: request_type(desc_chain.memory(), hdr_desc.addr())?,
|
||||
sector: sector(desc_chain.memory(), hdr_desc.addr())?,
|
||||
data_descriptors: Vec::new(),
|
||||
status_addr: GuestAddress(0),
|
||||
writeback: true,
|
||||
@@ -247,12 +240,7 @@ impl Request {
|
||||
if !desc.is_write_only() && req.request_type == RequestType::GetDeviceId {
|
||||
return Err(Error::UnexpectedReadOnlyDescriptor);
|
||||
}
|
||||
|
||||
req.data_descriptors.push((
|
||||
desc.addr()
|
||||
.translate_gva(access_platform, desc.len() as usize),
|
||||
desc.len(),
|
||||
));
|
||||
req.data_descriptors.push((desc.addr(), desc.len()));
|
||||
desc = desc_chain
|
||||
.next()
|
||||
.ok_or(Error::DescriptorChainTooShort)
|
||||
@@ -273,9 +261,7 @@ impl Request {
|
||||
return Err(Error::DescriptorLengthTooSmall);
|
||||
}
|
||||
|
||||
req.status_addr = status_desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, status_desc.len() as usize);
|
||||
req.status_addr = status_desc.addr();
|
||||
|
||||
Ok(req)
|
||||
}
|
||||
|
||||
@@ -2,25 +2,26 @@
|
||||
name = "devices"
|
||||
version = "0.1.0"
|
||||
authors = ["The Chromium OS Authors"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { path = "../acpi_tables" }
|
||||
anyhow = "1.0.57"
|
||||
acpi_tables = { path = "../acpi_tables", optional = true }
|
||||
anyhow = "1.0.52"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "1.3.2"
|
||||
byteorder = "1.4.3"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.112"
|
||||
log = "0.4.14"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = "0.8.0"
|
||||
vm-memory = "0.7.0"
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
acpi = ["acpi_tables"]
|
||||
cmos = []
|
||||
fwdebug = []
|
||||
|
||||
@@ -102,6 +102,7 @@ impl BusDevice for AcpiGedDevice {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "acpi")]
|
||||
impl Aml for AcpiGedDevice {
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
aml::Device::new(
|
||||
|
||||
@@ -76,7 +76,6 @@ impl InterruptController for Gic {
|
||||
.update(
|
||||
i as InterruptIndex,
|
||||
InterruptSourceConfig::LegacyIrq(config),
|
||||
false,
|
||||
)
|
||||
.map_err(Error::EnableInterrupt)?;
|
||||
}
|
||||
|
||||
@@ -367,13 +367,19 @@ impl Ioapic {
|
||||
};
|
||||
|
||||
self.interrupt_source_group
|
||||
.update(
|
||||
irq as InterruptIndex,
|
||||
InterruptSourceConfig::MsiIrq(config),
|
||||
interrupt_mask(entry) == 1,
|
||||
)
|
||||
.update(irq as InterruptIndex, InterruptSourceConfig::MsiIrq(config))
|
||||
.map_err(Error::UpdateInterrupt)?;
|
||||
|
||||
if interrupt_mask(entry) == 1 {
|
||||
self.interrupt_source_group
|
||||
.mask(irq as InterruptIndex)
|
||||
.map_err(Error::MaskInterrupt)?;
|
||||
} else {
|
||||
self.interrupt_source_group
|
||||
.unmask(irq as InterruptIndex)
|
||||
.map_err(Error::UnmaskInterrupt)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ use std::cmp::min;
|
||||
use std::mem;
|
||||
use std::sync::{Arc, Barrier};
|
||||
use vm_device::BusDevice;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
// https://github.com/rust-lang/libc/issues/1848
|
||||
#[cfg_attr(target_env = "musl", allow(deprecated))]
|
||||
@@ -22,14 +21,13 @@ const DATA_LEN: usize = 128;
|
||||
pub struct Cmos {
|
||||
index: u8,
|
||||
data: [u8; DATA_LEN],
|
||||
reset_evt: EventFd,
|
||||
}
|
||||
|
||||
impl Cmos {
|
||||
/// Constructs a CMOS/RTC device with initial data.
|
||||
/// `mem_below_4g` is the size of memory in bytes below the 32-bit gap.
|
||||
/// `mem_above_4g` is the size of memory in bytes above the 32-bit gap.
|
||||
pub fn new(mem_below_4g: u64, mem_above_4g: u64, reset_evt: EventFd) -> Cmos {
|
||||
pub fn new(mem_below_4g: u64, mem_above_4g: u64) -> Cmos {
|
||||
let mut data = [0u8; DATA_LEN];
|
||||
|
||||
// Extended memory from 16 MB to 4 GB in units of 64 KB
|
||||
@@ -46,11 +44,7 @@ impl Cmos {
|
||||
data[0x5c] = (high_mem >> 8) as u8;
|
||||
data[0x5d] = (high_mem >> 16) as u8;
|
||||
|
||||
Cmos {
|
||||
index: 0,
|
||||
data,
|
||||
reset_evt,
|
||||
}
|
||||
Cmos { index: 0, data }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,15 +56,8 @@ impl BusDevice for Cmos {
|
||||
}
|
||||
|
||||
match offset {
|
||||
INDEX_OFFSET => self.index = data[0],
|
||||
DATA_OFFSET => {
|
||||
if self.index == 0x8f && data[0] == 0 {
|
||||
info!("CMOS reset");
|
||||
self.reset_evt.write(1).unwrap();
|
||||
} else {
|
||||
self.data[(self.index & INDEX_MASK) as usize] = data[0]
|
||||
}
|
||||
}
|
||||
INDEX_OFFSET => self.index = data[0] & INDEX_MASK,
|
||||
DATA_OFFSET => self.data[self.index as usize] = data[0],
|
||||
o => warn!("bad write offset on CMOS device: {}", o),
|
||||
};
|
||||
None
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright © 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use std::fmt;
|
||||
use std::time::Instant;
|
||||
use vm_device::BusDevice;
|
||||
|
||||
/// Debug I/O port, see:
|
||||
/// https://www.intel.com/content/www/us/en/support/articles/000005500/boards-and-kits.html
|
||||
///
|
||||
/// Since we're not a physical platform, we can freely assign code ranges for
|
||||
/// debugging specific parts of our virtual platform.
|
||||
pub enum DebugIoPortRange {
|
||||
Firmware,
|
||||
Bootloader,
|
||||
Kernel,
|
||||
Userspace,
|
||||
Custom,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const DEBUG_IOPORT_PREFIX: &str = "Debug I/O port";
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
impl DebugIoPortRange {
|
||||
fn from_u8(value: u8) -> DebugIoPortRange {
|
||||
match value {
|
||||
0x00..=0x1f => DebugIoPortRange::Firmware,
|
||||
0x20..=0x3f => DebugIoPortRange::Bootloader,
|
||||
0x40..=0x5f => DebugIoPortRange::Kernel,
|
||||
0x60..=0x7f => DebugIoPortRange::Userspace,
|
||||
_ => DebugIoPortRange::Custom,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
impl fmt::Display for DebugIoPortRange {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
DebugIoPortRange::Firmware => write!(f, "{}: Firmware", DEBUG_IOPORT_PREFIX),
|
||||
DebugIoPortRange::Bootloader => write!(f, "{}: Bootloader", DEBUG_IOPORT_PREFIX),
|
||||
DebugIoPortRange::Kernel => write!(f, "{}: Kernel", DEBUG_IOPORT_PREFIX),
|
||||
DebugIoPortRange::Userspace => write!(f, "{}: Userspace", DEBUG_IOPORT_PREFIX),
|
||||
DebugIoPortRange::Custom => write!(f, "{}: Custom", DEBUG_IOPORT_PREFIX),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DebugPort {
|
||||
timestamp: Instant,
|
||||
}
|
||||
|
||||
impl DebugPort {
|
||||
pub fn new(timestamp: Instant) -> Self {
|
||||
Self { timestamp }
|
||||
}
|
||||
}
|
||||
|
||||
impl BusDevice for DebugPort {
|
||||
fn read(&mut self, _base: u64, _offset: u64, _data: &mut [u8]) {
|
||||
error!("Invalid read to debug port")
|
||||
}
|
||||
|
||||
fn write(
|
||||
&mut self,
|
||||
_base: u64,
|
||||
_offset: u64,
|
||||
data: &[u8],
|
||||
) -> Option<std::sync::Arc<std::sync::Barrier>> {
|
||||
let elapsed = self.timestamp.elapsed();
|
||||
|
||||
let code = data[0];
|
||||
warn!(
|
||||
"[{} code 0x{:x}] {}.{:>06} seconds",
|
||||
DebugIoPortRange::from_u8(code),
|
||||
code,
|
||||
elapsed.as_secs(),
|
||||
elapsed.as_micros()
|
||||
);
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -356,7 +356,6 @@ mod tests {
|
||||
&self,
|
||||
_index: InterruptIndex,
|
||||
_config: InterruptSourceConfig,
|
||||
_masked: bool,
|
||||
) -> result::Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE-BSD-3-Clause file.
|
||||
|
||||
#[cfg(feature = "cmos")]
|
||||
mod cmos;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
mod debug_port;
|
||||
#[cfg(feature = "fwdebug")]
|
||||
mod fwdebug;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
@@ -19,9 +18,8 @@ mod serial;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
mod uart_pl011;
|
||||
|
||||
#[cfg(feature = "cmos")]
|
||||
pub use self::cmos::Cmos;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use self::debug_port::DebugPort;
|
||||
#[cfg(feature = "fwdebug")]
|
||||
pub use self::fwdebug::FwDebugDevice;
|
||||
pub use self::i8042::I8042Device;
|
||||
|
||||
@@ -430,7 +430,6 @@ mod tests {
|
||||
&self,
|
||||
_index: InterruptIndex,
|
||||
_config: InterruptSourceConfig,
|
||||
_masked: bool,
|
||||
) -> result::Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -339,7 +339,6 @@ mod tests {
|
||||
&self,
|
||||
_index: InterruptIndex,
|
||||
_config: InterruptSourceConfig,
|
||||
_masked: bool,
|
||||
) -> result::Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ use crate::{read_le_u32, write_le_u32};
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::time::Instant;
|
||||
use std::{io, result};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
@@ -35,7 +34,6 @@ const UARTRIS: u64 = 15;
|
||||
const UARTMIS: u64 = 16;
|
||||
const UARTICR: u64 = 17;
|
||||
const UARTDMACR: u64 = 18;
|
||||
const UARTDEBUG: u64 = 0x3c0;
|
||||
|
||||
const PL011_INT_TX: u32 = 0x20;
|
||||
const PL011_INT_RX: u32 = 0x10;
|
||||
@@ -79,7 +77,6 @@ pub struct Pl011 {
|
||||
rsr: u32,
|
||||
cr: u32,
|
||||
dmacr: u32,
|
||||
debug: u32,
|
||||
int_enabled: u32,
|
||||
int_level: u32,
|
||||
read_fifo: VecDeque<u8>,
|
||||
@@ -91,7 +88,6 @@ pub struct Pl011 {
|
||||
read_trigger: u32,
|
||||
irq: Arc<dyn InterruptSourceGroup>,
|
||||
out: Option<Box<dyn io::Write + Send>>,
|
||||
timestamp: std::time::Instant,
|
||||
}
|
||||
|
||||
#[derive(Versionize)]
|
||||
@@ -101,7 +97,6 @@ pub struct Pl011State {
|
||||
rsr: u32,
|
||||
cr: u32,
|
||||
dmacr: u32,
|
||||
debug: u32,
|
||||
int_enabled: u32,
|
||||
int_level: u32,
|
||||
read_fifo: Vec<u8>,
|
||||
@@ -121,7 +116,6 @@ impl Pl011 {
|
||||
id: String,
|
||||
irq: Arc<dyn InterruptSourceGroup>,
|
||||
out: Option<Box<dyn io::Write + Send>>,
|
||||
timestamp: Instant,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
@@ -130,7 +124,6 @@ impl Pl011 {
|
||||
rsr: 0u32,
|
||||
cr: 0x300u32,
|
||||
dmacr: 0u32,
|
||||
debug: 0u32,
|
||||
int_enabled: 0u32,
|
||||
int_level: 0u32,
|
||||
read_fifo: VecDeque::new(),
|
||||
@@ -142,7 +135,6 @@ impl Pl011 {
|
||||
read_trigger: 1u32,
|
||||
irq,
|
||||
out,
|
||||
timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +149,6 @@ impl Pl011 {
|
||||
rsr: self.rsr,
|
||||
cr: self.cr,
|
||||
dmacr: self.dmacr,
|
||||
debug: self.debug,
|
||||
int_enabled: self.int_enabled,
|
||||
int_level: self.int_level,
|
||||
read_fifo: self.read_fifo.clone().into(),
|
||||
@@ -176,7 +167,6 @@ impl Pl011 {
|
||||
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();
|
||||
@@ -290,50 +280,13 @@ impl Pl011 {
|
||||
return Err(Error::DmaNotImplemented);
|
||||
}
|
||||
}
|
||||
UARTDEBUG => {
|
||||
self.debug = val;
|
||||
self.handle_debug();
|
||||
}
|
||||
off => {
|
||||
debug!("PL011: Bad write offset, offset: {}", off);
|
||||
return Err(Error::BadWriteOffset(off));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_debug(&self) {
|
||||
let elapsed = self.timestamp.elapsed();
|
||||
|
||||
match self.debug {
|
||||
0x00..=0x1f => info!(
|
||||
"[Debug I/O port: Firmware code: 0x{:x}] {}.{:>06} seconds",
|
||||
self.debug,
|
||||
elapsed.as_secs(),
|
||||
elapsed.as_micros()
|
||||
),
|
||||
0x20..=0x3f => info!(
|
||||
"[Debug I/O port: Bootloader code: 0x{:x}] {}.{:>06} seconds",
|
||||
self.debug,
|
||||
elapsed.as_secs(),
|
||||
elapsed.as_micros()
|
||||
),
|
||||
0x40..=0x5f => info!(
|
||||
"[Debug I/O port: Kernel code: 0x{:x}] {}.{:>06} seconds",
|
||||
self.debug,
|
||||
elapsed.as_secs(),
|
||||
elapsed.as_micros()
|
||||
),
|
||||
0x60..=0x7f => info!(
|
||||
"[Debug I/O port: Userspace code: 0x{:x}] {}.{:>06} seconds",
|
||||
self.debug,
|
||||
elapsed.as_secs(),
|
||||
elapsed.as_micros()
|
||||
),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn trigger_interrupt(&mut self) -> result::Result<(), io::Error> {
|
||||
self.irq.trigger(0)
|
||||
}
|
||||
@@ -374,7 +327,6 @@ impl BusDevice for Pl011 {
|
||||
UARTRIS => self.int_level,
|
||||
UARTMIS => (self.int_level & self.int_enabled),
|
||||
UARTDMACR => self.dmacr,
|
||||
UARTDEBUG => self.debug,
|
||||
_ => {
|
||||
read_ok = false;
|
||||
0
|
||||
@@ -452,7 +404,6 @@ mod tests {
|
||||
&self,
|
||||
_index: InterruptIndex,
|
||||
_config: InterruptSourceConfig,
|
||||
_masked: bool,
|
||||
) -> result::Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
@@ -497,7 +448,6 @@ mod tests {
|
||||
String::from(SERIAL_NAME),
|
||||
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||
Some(Box::new(pl011_out.clone())),
|
||||
Instant::now(),
|
||||
);
|
||||
|
||||
pl011.write(0, UARTDR as u64, &[b'x', b'y']);
|
||||
@@ -518,7 +468,6 @@ mod tests {
|
||||
String::from(SERIAL_NAME),
|
||||
Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
|
||||
Some(Box::new(pl011_out)),
|
||||
Instant::now(),
|
||||
);
|
||||
|
||||
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd
|
||||
|
||||
@@ -12,6 +12,7 @@ extern crate bitflags;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
#[cfg(feature = "acpi")]
|
||||
pub mod acpi;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub mod gic;
|
||||
@@ -20,6 +21,7 @@ pub mod interrupt_controller;
|
||||
pub mod ioapic;
|
||||
pub mod legacy;
|
||||
|
||||
#[cfg(feature = "acpi")]
|
||||
pub use self::acpi::{AcpiGedDevice, AcpiPmTimerDevice, AcpiShutdownDevice};
|
||||
|
||||
bitflags! {
|
||||
|
||||
@@ -1,399 +0,0 @@
|
||||
The documentation in this directory is covered by the following license:
|
||||
|
||||
Attribution 4.0 International
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||
does not provide legal services or legal advice. Distribution of
|
||||
Creative Commons public licenses does not create a lawyer-client or
|
||||
other relationship. Creative Commons makes its licenses and related
|
||||
information available on an "as-is" basis. Creative Commons gives no
|
||||
warranties regarding its licenses, any material licensed under their
|
||||
terms and conditions, or any related information. Creative Commons
|
||||
disclaims all liability for damages resulting from their use to the
|
||||
fullest extent possible.
|
||||
|
||||
Using Creative Commons Public Licenses
|
||||
|
||||
Creative Commons public licenses provide a standard set of terms and
|
||||
conditions that creators and other rights holders may use to share
|
||||
original works of authorship and other material subject to copyright
|
||||
and certain other rights specified in the public license below. The
|
||||
following considerations are for informational purposes only, are not
|
||||
exhaustive, and do not form part of our licenses.
|
||||
|
||||
Considerations for licensors: Our public licenses are
|
||||
intended for use by those authorized to give the public
|
||||
permission to use material in ways otherwise restricted by
|
||||
copyright and certain other rights. Our licenses are
|
||||
irrevocable. Licensors should read and understand the terms
|
||||
and conditions of the license they choose before applying it.
|
||||
Licensors should also secure all rights necessary before
|
||||
applying our licenses so that the public can reuse the
|
||||
material as expected. Licensors should clearly mark any
|
||||
material not subject to the license. This includes other CC-
|
||||
licensed material, or material used under an exception or
|
||||
limitation to copyright. More considerations for licensors:
|
||||
wiki.creativecommons.org/Considerations_for_licensors
|
||||
|
||||
Considerations for the public: By using one of our public
|
||||
licenses, a licensor grants the public permission to use the
|
||||
licensed material under specified terms and conditions. If
|
||||
the licensor's permission is not necessary for any reason--for
|
||||
example, because of any applicable exception or limitation to
|
||||
copyright--then that use is not regulated by the license. Our
|
||||
licenses grant only permissions under copyright and certain
|
||||
other rights that a licensor has authority to grant. Use of
|
||||
the licensed material may still be restricted for other
|
||||
reasons, including because others have copyright or other
|
||||
rights in the material. A licensor may make special requests,
|
||||
such as asking that all changes be marked or described.
|
||||
Although not required by our licenses, you are encouraged to
|
||||
respect those requests where reasonable. More considerations
|
||||
for the public:
|
||||
wiki.creativecommons.org/Considerations_for_licensees
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Attribution 4.0 International Public License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree
|
||||
to be bound by the terms and conditions of this Creative Commons
|
||||
Attribution 4.0 International Public License ("Public License"). To the
|
||||
extent this Public License may be interpreted as a contract, You are
|
||||
granted the Licensed Rights in consideration of Your acceptance of
|
||||
these terms and conditions, and the Licensor grants You such rights in
|
||||
consideration of benefits the Licensor receives from making the
|
||||
Licensed Material available under these terms and conditions.
|
||||
|
||||
|
||||
Section 1 -- Definitions.
|
||||
|
||||
a. Adapted Material means material subject to Copyright and Similar
|
||||
Rights that is derived from or based upon the Licensed Material
|
||||
and in which the Licensed Material is translated, altered,
|
||||
arranged, transformed, or otherwise modified in a manner requiring
|
||||
permission under the Copyright and Similar Rights held by the
|
||||
Licensor. For purposes of this Public License, where the Licensed
|
||||
Material is a musical work, performance, or sound recording,
|
||||
Adapted Material is always produced where the Licensed Material is
|
||||
synched in timed relation with a moving image.
|
||||
|
||||
b. Adapter's License means the license You apply to Your Copyright
|
||||
and Similar Rights in Your contributions to Adapted Material in
|
||||
accordance with the terms and conditions of this Public License.
|
||||
|
||||
c. Copyright and Similar Rights means copyright and/or similar rights
|
||||
closely related to copyright including, without limitation,
|
||||
performance, broadcast, sound recording, and Sui Generis Database
|
||||
Rights, without regard to how the rights are labeled or
|
||||
categorized. For purposes of this Public License, the rights
|
||||
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||
Rights.
|
||||
|
||||
d. Effective Technological Measures means those measures that, in the
|
||||
absence of proper authority, may not be circumvented under laws
|
||||
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||
Treaty adopted on December 20, 1996, and/or similar international
|
||||
agreements.
|
||||
|
||||
e. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||
any other exception or limitation to Copyright and Similar Rights
|
||||
that applies to Your use of the Licensed Material.
|
||||
|
||||
f. Licensed Material means the artistic or literary work, database,
|
||||
or other material to which the Licensor applied this Public
|
||||
License.
|
||||
|
||||
g. Licensed Rights means the rights granted to You subject to the
|
||||
terms and conditions of this Public License, which are limited to
|
||||
all Copyright and Similar Rights that apply to Your use of the
|
||||
Licensed Material and that the Licensor has authority to license.
|
||||
|
||||
h. Licensor means the individual(s) or entity(ies) granting rights
|
||||
under this Public License.
|
||||
|
||||
i. Share means to provide material to the public by any means or
|
||||
process that requires permission under the Licensed Rights, such
|
||||
as reproduction, public display, public performance, distribution,
|
||||
dissemination, communication, or importation, and to make material
|
||||
available to the public including in ways that members of the
|
||||
public may access the material from a place and at a time
|
||||
individually chosen by them.
|
||||
|
||||
j. Sui Generis Database Rights means rights other than copyright
|
||||
resulting from Directive 96/9/EC of the European Parliament and of
|
||||
the Council of 11 March 1996 on the legal protection of databases,
|
||||
as amended and/or succeeded, as well as other essentially
|
||||
equivalent rights anywhere in the world.
|
||||
|
||||
k. You means the individual or entity exercising the Licensed Rights
|
||||
under this Public License. Your has a corresponding meaning.
|
||||
|
||||
|
||||
Section 2 -- Scope.
|
||||
|
||||
a. License grant.
|
||||
|
||||
1. Subject to the terms and conditions of this Public License,
|
||||
the Licensor hereby grants You a worldwide, royalty-free,
|
||||
non-sublicensable, non-exclusive, irrevocable license to
|
||||
exercise the Licensed Rights in the Licensed Material to:
|
||||
|
||||
a. reproduce and Share the Licensed Material, in whole or
|
||||
in part; and
|
||||
|
||||
b. produce, reproduce, and Share Adapted Material.
|
||||
|
||||
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||
Exceptions and Limitations apply to Your use, this Public
|
||||
License does not apply, and You do not need to comply with
|
||||
its terms and conditions.
|
||||
|
||||
3. Term. The term of this Public License is specified in Section
|
||||
6(a).
|
||||
|
||||
4. Media and formats; technical modifications allowed. The
|
||||
Licensor authorizes You to exercise the Licensed Rights in
|
||||
all media and formats whether now known or hereafter created,
|
||||
and to make technical modifications necessary to do so. The
|
||||
Licensor waives and/or agrees not to assert any right or
|
||||
authority to forbid You from making technical modifications
|
||||
necessary to exercise the Licensed Rights, including
|
||||
technical modifications necessary to circumvent Effective
|
||||
Technological Measures. For purposes of this Public License,
|
||||
simply making modifications authorized by this Section 2(a)
|
||||
(4) never produces Adapted Material.
|
||||
|
||||
5. Downstream recipients.
|
||||
|
||||
a. Offer from the Licensor -- Licensed Material. Every
|
||||
recipient of the Licensed Material automatically
|
||||
receives an offer from the Licensor to exercise the
|
||||
Licensed Rights under the terms and conditions of this
|
||||
Public License.
|
||||
|
||||
b. No downstream restrictions. You may not offer or impose
|
||||
any additional or different terms or conditions on, or
|
||||
apply any Effective Technological Measures to, the
|
||||
Licensed Material if doing so restricts exercise of the
|
||||
Licensed Rights by any recipient of the Licensed
|
||||
Material.
|
||||
|
||||
6. No endorsement. Nothing in this Public License constitutes or
|
||||
may be construed as permission to assert or imply that You
|
||||
are, or that Your use of the Licensed Material is, connected
|
||||
with, or sponsored, endorsed, or granted official status by,
|
||||
the Licensor or others designated to receive attribution as
|
||||
provided in Section 3(a)(1)(A)(i).
|
||||
|
||||
b. Other rights.
|
||||
|
||||
1. Moral rights, such as the right of integrity, are not
|
||||
licensed under this Public License, nor are publicity,
|
||||
privacy, and/or other similar personality rights; however, to
|
||||
the extent possible, the Licensor waives and/or agrees not to
|
||||
assert any such rights held by the Licensor to the limited
|
||||
extent necessary to allow You to exercise the Licensed
|
||||
Rights, but not otherwise.
|
||||
|
||||
2. Patent and trademark rights are not licensed under this
|
||||
Public License.
|
||||
|
||||
3. To the extent possible, the Licensor waives any right to
|
||||
collect royalties from You for the exercise of the Licensed
|
||||
Rights, whether directly or through a collecting society
|
||||
under any voluntary or waivable statutory or compulsory
|
||||
licensing scheme. In all other cases the Licensor expressly
|
||||
reserves any right to collect such royalties.
|
||||
|
||||
|
||||
Section 3 -- License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to the
|
||||
following conditions.
|
||||
|
||||
a. Attribution.
|
||||
|
||||
1. If You Share the Licensed Material (including in modified
|
||||
form), You must:
|
||||
|
||||
a. retain the following if it is supplied by the Licensor
|
||||
with the Licensed Material:
|
||||
|
||||
i. identification of the creator(s) of the Licensed
|
||||
Material and any others designated to receive
|
||||
attribution, in any reasonable manner requested by
|
||||
the Licensor (including by pseudonym if
|
||||
designated);
|
||||
|
||||
ii. a copyright notice;
|
||||
|
||||
iii. a notice that refers to this Public License;
|
||||
|
||||
iv. a notice that refers to the disclaimer of
|
||||
warranties;
|
||||
|
||||
v. a URI or hyperlink to the Licensed Material to the
|
||||
extent reasonably practicable;
|
||||
|
||||
b. indicate if You modified the Licensed Material and
|
||||
retain an indication of any previous modifications; and
|
||||
|
||||
c. indicate the Licensed Material is licensed under this
|
||||
Public License, and include the text of, or the URI or
|
||||
hyperlink to, this Public License.
|
||||
|
||||
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||
reasonable manner based on the medium, means, and context in
|
||||
which You Share the Licensed Material. For example, it may be
|
||||
reasonable to satisfy the conditions by providing a URI or
|
||||
hyperlink to a resource that includes the required
|
||||
information.
|
||||
|
||||
3. If requested by the Licensor, You must remove any of the
|
||||
information required by Section 3(a)(1)(A) to the extent
|
||||
reasonably practicable.
|
||||
|
||||
4. If You Share Adapted Material You produce, the Adapter's
|
||||
License You apply must not prevent recipients of the Adapted
|
||||
Material from complying with this Public License.
|
||||
|
||||
|
||||
Section 4 -- Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that
|
||||
apply to Your use of the Licensed Material:
|
||||
|
||||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||
to extract, reuse, reproduce, and Share all or a substantial
|
||||
portion of the contents of the database;
|
||||
|
||||
b. if You include all or a substantial portion of the database
|
||||
contents in a database in which You have Sui Generis Database
|
||||
Rights, then the database in which You have Sui Generis Database
|
||||
Rights (but not its individual contents) is Adapted Material; and
|
||||
|
||||
c. You must comply with the conditions in Section 3(a) if You Share
|
||||
all or a substantial portion of the contents of the database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not
|
||||
replace Your obligations under this Public License where the Licensed
|
||||
Rights include other Copyright and Similar Rights.
|
||||
|
||||
|
||||
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||
|
||||
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||
|
||||
c. The disclaimer of warranties and limitation of liability provided
|
||||
above shall be interpreted in a manner that, to the extent
|
||||
possible, most closely approximates an absolute disclaimer and
|
||||
waiver of all liability.
|
||||
|
||||
|
||||
Section 6 -- Term and Termination.
|
||||
|
||||
a. This Public License applies for the term of the Copyright and
|
||||
Similar Rights licensed here. However, if You fail to comply with
|
||||
this Public License, then Your rights under this Public License
|
||||
terminate automatically.
|
||||
|
||||
b. Where Your right to use the Licensed Material has terminated under
|
||||
Section 6(a), it reinstates:
|
||||
|
||||
1. automatically as of the date the violation is cured, provided
|
||||
it is cured within 30 days of Your discovery of the
|
||||
violation; or
|
||||
|
||||
2. upon express reinstatement by the Licensor.
|
||||
|
||||
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||
right the Licensor may have to seek remedies for Your violations
|
||||
of this Public License.
|
||||
|
||||
c. For the avoidance of doubt, the Licensor may also offer the
|
||||
Licensed Material under separate terms or conditions or stop
|
||||
distributing the Licensed Material at any time; however, doing so
|
||||
will not terminate this Public License.
|
||||
|
||||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||
License.
|
||||
|
||||
|
||||
Section 7 -- Other Terms and Conditions.
|
||||
|
||||
a. The Licensor shall not be bound by any additional or different
|
||||
terms or conditions communicated by You unless expressly agreed.
|
||||
|
||||
b. Any arrangements, understandings, or agreements regarding the
|
||||
Licensed Material not stated herein are separate from and
|
||||
independent of the terms and conditions of this Public License.
|
||||
|
||||
|
||||
Section 8 -- Interpretation.
|
||||
|
||||
a. For the avoidance of doubt, this Public License does not, and
|
||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||
conditions on any use of the Licensed Material that could lawfully
|
||||
be made without permission under this Public License.
|
||||
|
||||
b. To the extent possible, if any provision of this Public License is
|
||||
deemed unenforceable, it shall be automatically reformed to the
|
||||
minimum extent necessary to make it enforceable. If the provision
|
||||
cannot be reformed, it shall be severed from this Public License
|
||||
without affecting the enforceability of the remaining terms and
|
||||
conditions.
|
||||
|
||||
c. No term or condition of this Public License will be waived and no
|
||||
failure to comply consented to unless expressly agreed to by the
|
||||
Licensor.
|
||||
|
||||
d. Nothing in this Public License constitutes or may be interpreted
|
||||
as a limitation upon, or waiver of, any privileges and immunities
|
||||
that apply to the Licensor or You, including from the legal
|
||||
processes of any jurisdiction or authority.
|
||||
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons is not a party to its public
|
||||
licenses. Notwithstanding, Creative Commons may elect to apply one of
|
||||
its public licenses to material it publishes and in those instances
|
||||
will be considered the “Licensor.” The text of the Creative Commons
|
||||
public licenses is dedicated to the public domain under the CC0 Public
|
||||
Domain Dedication. Except for the limited purpose of indicating that
|
||||
material is shared under a Creative Commons public license or as
|
||||
otherwise permitted by the Creative Commons policies published at
|
||||
creativecommons.org/policies, Creative Commons does not authorize the
|
||||
use of the trademark "Creative Commons" or any other trademark or logo
|
||||
of Creative Commons without its prior written consent including,
|
||||
without limitation, in connection with any unauthorized modifications
|
||||
to any of its public licenses or any other arrangements,
|
||||
understandings, or agreements concerning use of licensed material. For
|
||||
the avoidance of doubt, this paragraph does not form part of the
|
||||
public licenses.
|
||||
|
||||
Creative Commons may be contacted at creativecommons.org.
|
||||
|
||||
|
||||
@@ -88,8 +88,6 @@ Reboot the VM | `/vm.reboot` | N/A
|
||||
Trigger power button of the VM | `/vm.power-button` | N/A | N/A | The VM is booted
|
||||
Pause the VM | `/vm.pause` | N/A | N/A | The VM is booted
|
||||
Resume the VM | `/vm.resume` | N/A | N/A | The VM is paused
|
||||
Task a snapshot of the VM | `/vm.snapshot` | `/schemas/VmSnapshotConfig`| N/A | The VM is paused
|
||||
Restore the VM from a snapshot | `/vm.restore` | `/schemas/RestoreConfig` | N/A | The VM is created but not booted
|
||||
Add/remove CPUs to/from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
|
||||
Add/remove memory from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
|
||||
Add/remove memory from a zone | `/vm.resize-zone` | `/schemas/VmResizeZone` | N/A | The VM is booted
|
||||
@@ -100,7 +98,6 @@ Add fs device to the VM | `/vm.add-fs` | `/schemas/FsConfig`
|
||||
Add pmem device to the VM | `/vm.add-pmem` | `/schemas/PmemConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
||||
Add network device to the VM | `/vm.add-net` | `/schemas/NetConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
||||
Add userspace PCI device to the VM | `/vm.add-user-device`| `/schemas/VmAddUserDevice`| `/schemas/PciDeviceInfo` | The VM is booted
|
||||
Add vdpa device to the VM | `/vm.add-vdpa` | `/schemas/VdpaConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
||||
Add vsock device to the VM | `/vm.add-vsock` | `/schemas/VsockConfig` | `/schemas/PciDeviceInfo` | The VM is booted
|
||||
Remove device from the VM | `/vm.remove-device` | `/schemas/VmRemoveDevice` | N/A | The VM is booted
|
||||
Dump the VM counters | `/vm.counters` | N/A | `/schemas/VmCounters` | The VM is booted
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
# Balloon
|
||||
|
||||
Cloud Hypervisor implements a balloon device based on the VIRTIO specification.
|
||||
Its main purpose is to provide the host a way to reclaim memory by controlling
|
||||
the amount of memory visible to the guest. But it also provides some interesting
|
||||
features related to guest memory management.
|
||||
|
||||
## Parameters
|
||||
|
||||
`BalloonConfig` (known as `--balloon` from the CLI perspective) contains the
|
||||
list of parameters available for the balloon device.
|
||||
|
||||
```rust
|
||||
struct BalloonConfig {
|
||||
pub size: u64,
|
||||
pub deflate_on_oom: bool,
|
||||
pub free_page_reporting: bool,
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
--balloon <balloon> Balloon parameters "size=<balloon_size>,deflate_on_oom=on|off,free_page_reporting=on|off"
|
||||
```
|
||||
|
||||
### `size`
|
||||
|
||||
Size of the balloon device. It is subtracted from the VM's total size. For
|
||||
instance, if creating a VM with 4GiB of RAM, along with a balloon of 1GiB, the
|
||||
guest will be able to use 3GiB of accessible memory. The guest sees all the RAM
|
||||
and unless it is balloon enlightened is entitled to all of it.
|
||||
|
||||
This parameter is mandatory.
|
||||
|
||||
Value is an unsigned integer of 64 bits corresponding to the balloon size in
|
||||
bytes.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--balloon size=1G
|
||||
```
|
||||
|
||||
### `deflate_on_oom`
|
||||
|
||||
Allow the guest to deflate the balloon if running Out Of Memory (OOM). Assuming
|
||||
the balloon size is greater than 0, this means the guest is allowed to reduce
|
||||
the balloon size all the way down to 0 if this can help recover from the OOM
|
||||
event.
|
||||
|
||||
This parameter is optional.
|
||||
|
||||
Value is a boolean set to `off` by default.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--ballloon size=2G,deflate_on_oom=on
|
||||
```
|
||||
|
||||
### `free_page_reporting`
|
||||
|
||||
Allow the guest to report lists of free pages. This feature doesn't require the
|
||||
balloon to be of any specific size as it doesn't impact the balloon size. The
|
||||
guest can let the VMM know about pages that are free after they have been used.
|
||||
Based on this information, the VMM can advise the host that it doesn't need
|
||||
these pages anymore.
|
||||
|
||||
This parameter is optional.
|
||||
|
||||
Value is a boolean set to `off` by default.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--ballloon size=0,free_page_reporting=on
|
||||
```
|
||||
24
docs/cpu.md
24
docs/cpu.md
@@ -17,12 +17,11 @@ struct CpusConfig {
|
||||
kvm_hyperv: bool,
|
||||
max_phys_bits: u8,
|
||||
affinity: Option<Vec<CpuAffinity>>,
|
||||
features: CpuFeatures,
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
--cpus boot=<boot_vcpus>,max=<max_vcpus>,topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>,kvm_hyperv=on|off,max_phys_bits=<maximum_number_of_physical_bits>,affinity=<list_of_vcpus_with_their_associated_cpuset>,features=<list_of_features_to_enable>
|
||||
--cpus boot=<boot_vcpus>,max=<max_vcpus>,topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>,kvm_hyperv=on|off,max_phys_bits=<maximum_number_of_physical_bits>,affinity=<list_of_vcpus_with_their_associated_cpuset>
|
||||
```
|
||||
|
||||
### `boot`
|
||||
@@ -188,24 +187,3 @@ _Example_
|
||||
In this example, assuming the host has 4 CPUs, vCPU 0 will run exclusively on
|
||||
host CPUs 2 and 3, while vCPU 1 will run exclusively on host CPUs 0 and 1.
|
||||
Because nothing is defined for vCPU 2, it can run on any of the 4 host CPUs.
|
||||
|
||||
### `features`
|
||||
|
||||
Set of CPU features to enable.
|
||||
|
||||
This option allows the user to enable a set of CPU features that are disabled
|
||||
by default otherwise.
|
||||
|
||||
The currently available feature set is: `amx`.
|
||||
|
||||
The `amx` feature will enable the x86 extension adding hardware units for
|
||||
matrix operations (int and float dot products). The goal of the extension is to
|
||||
provide performance enhancements for these common operations.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--cpus features=amx
|
||||
```
|
||||
|
||||
In this example the amx CPU feature will be enabled for the VMM.
|
||||
|
||||
78
docs/fs.md
78
docs/fs.md
@@ -1,27 +1,23 @@
|
||||
# How to use virtio-fs
|
||||
|
||||
In the context of virtualization, it is always convenient to be able to share a
|
||||
directory from the host with the guest.
|
||||
In the context of virtualization, it is always convenient to be able to share a directory from the host with the guest.
|
||||
|
||||
__virtio-fs__, also known as __vhost-user-fs__ is a virtual device defined by
|
||||
the VIRTIO specification which allows any VMM to perform filesystem sharing.
|
||||
__virtio-fs__, also known as __vhost-user-fs__ is a virtual device defined by the VIRTIO specification which allows any VMM to perform filesystem sharing.
|
||||
|
||||
## Pre-requisites
|
||||
|
||||
### The daemon
|
||||
|
||||
This virtual device relies on the _vhost-user_ protocol, which assumes the
|
||||
backend (device emulation) is handled by a dedicated process running on the
|
||||
host. This daemon is called __virtiofsd__ and needs to be present on the host.
|
||||
This virtual device relies on the _vhost-user_ protocol, which assumes the backend (device emulation) is handled by a dedicated process running on the host. This daemon is called __virtiofsd__ and needs to be present on the host.
|
||||
|
||||
_Build virtiofsd_
|
||||
```bash
|
||||
git clone https://gitlab.com/virtio-fs/virtiofsd
|
||||
pushd virtiofsd
|
||||
cargo build --release
|
||||
sudo setcap cap_sys_admin+epi target/release/virtiofsd
|
||||
git clone --depth 1 "https://gitlab.com/virtio-fs/qemu.git" -b "qemu5.0-virtiofs-dax" $VIRTIOFSD_DIR
|
||||
cd $VIRTIOFSD_DIR
|
||||
./configure --prefix=$PWD --target-list=x86_64-softmmu
|
||||
make virtiofsd -j `nproc`
|
||||
sudo setcap cap_sys_admin+epi "virtiofsd"
|
||||
```
|
||||
|
||||
_Create shared directory_
|
||||
```bash
|
||||
mkdir /tmp/shared_dir
|
||||
@@ -31,39 +27,29 @@ _Run virtiofsd_
|
||||
./virtiofsd \
|
||||
-d \
|
||||
--socket-path=/tmp/virtiofs \
|
||||
--shared-dir=/tmp/shared_dir \
|
||||
--cache=never
|
||||
-o source=/tmp/shared_dir \
|
||||
-o cache=none
|
||||
```
|
||||
|
||||
The `cache=never` option is the default when using `virtiofsd` with
|
||||
Cloud Hypervisor. This prevents from using the host page cache, reducing the
|
||||
overall footprint on host memory. This increases the maximum density of virtual
|
||||
machines that can be launched on a single host.
|
||||
The `cache=none` option should be the default when using `virtiofsd` with the __cloud-hypervisor__ VMM. This prevents from using the guest page cache, which reduces the memory footprint of the guest. When running multiple virtual machines on the same host, this will let the host deal with page cache, which will increase the density of virtual machines which can be launched.
|
||||
|
||||
The `cache=always` option will allow the host page cache to be used, which can
|
||||
result in better performance for the guest's workload at the cost of increasing
|
||||
the footprint on host memory.
|
||||
The `cache=always` option will allow for the guest page cache to be used, which will increase the memory footprint of the guest. This option should be used only for specific use cases where a single VM is going to be running on a host.
|
||||
|
||||
### Kernel support
|
||||
|
||||
Modern Linux kernels (at least v5.10) have support for virtio-fs. Use of older
|
||||
kernels, with additional patches, are not supported.
|
||||
Modern Linux kernels starting (at least v5.10) have support for virtio-fs. Use
|
||||
of older kernels, with additional patches, are not supported.
|
||||
|
||||
## How to share directories with cloud-hypervisor
|
||||
|
||||
### Start the VM
|
||||
Once the daemon is running, the option `--fs` from __cloud-hypervisor__ needs to be used.
|
||||
|
||||
Once the daemon is running, the option `--fs` from Cloud Hypervisor needs
|
||||
to be used.
|
||||
Direct kernel boot is the preferred option, but we can boot from an EFI cloud image if it contains a recent enough kernel.
|
||||
|
||||
Both direct kernel boot and EFI firmware can be used to boot a VM with
|
||||
virtio-fs, given that the cloud image contains a recent enough kernel.
|
||||
Because _vhost-user_ expects a dedicated process (__virtiofsd__ in this case) to be able to access the guest RAM to communicate through the _virtqueues_ with the driver running in the guest, `--memory` option needs to be slightly modified. It must specify `shared=on` to share the memory pages so that an external process can access them.
|
||||
|
||||
Correct functioning of `--fs` requires `--memory shared=on` to facilitate
|
||||
interprocess memory sharing.
|
||||
|
||||
Assuming you have `focal-server-cloudimg-amd64.raw` and `vmlinux` on your
|
||||
system, here is the Cloud Hypervisor command you need to run:
|
||||
Assuming you have `focal-server-cloudimg-amd64.raw` and `vmlinux` on your system, here is the __cloud-hypervisor__ command you need to run:
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
--cpus boot=1 \
|
||||
@@ -74,20 +60,26 @@ system, here is the Cloud Hypervisor command you need to run:
|
||||
--fs tag=myfs,socket=/tmp/virtiofs,num_queues=1,queue_size=512
|
||||
```
|
||||
|
||||
### Mount the shared directory
|
||||
|
||||
The last step is to mount the shared directory inside the guest, using the
|
||||
`virtiofs` filesystem type.
|
||||
By default, DAX is enabled with a cache window of 8GiB. You can specify a custom size (let's say 4GiB for this example) for the cache by explicitly setting DAX and the cache size:
|
||||
|
||||
```bash
|
||||
mkdir mount_dir
|
||||
mount -t virtiofs myfs mount_dir/
|
||||
--fs tag=myfs,socket=/tmp/virtiofs,num_queues=1,queue_size=512,dax=on,cache_size=4G
|
||||
|
||||
```
|
||||
|
||||
The `tag` needs to be consistent with what has been provided through the
|
||||
Cloud Hypervisor command line, which happens to be `myfs` in this example.
|
||||
In case you don't want to use a shared window of cache to pass the shared files content, this means you will have to explicitly disable DAX with `dax=off`. Note that in this case, the `cache_size` parameter will be ignored.
|
||||
|
||||
## DAX feature
|
||||
```bash
|
||||
--fs tag=myfs,socket=/tmp/virtiofs,num_queues=1,queue_size=512,dax=off
|
||||
|
||||
Given the DAX feature is not stable yet from a daemon standpoint, it is not
|
||||
available in Cloud Hypervisor.
|
||||
```
|
||||
|
||||
### Mount the shared directory
|
||||
The last step is to mount the shared directory inside the guest, using the `virtiofs` filesystem type.
|
||||
```bash
|
||||
mkdir mount_dir
|
||||
mount -t virtiofs -o dax myfs mount_dir/
|
||||
```
|
||||
The `tag` needs to be consistent with what has been provided through the __cloud-hypervisor__ command line, which happens to be `myfs` in this example.
|
||||
|
||||
The `-o dax` option must be removed in case the shared cache region is not enabled from the VMM.
|
||||
|
||||
47
docs/gdb.md
47
docs/gdb.md
@@ -1,47 +0,0 @@
|
||||
# GDB Support
|
||||
|
||||
This feature allows remote guest debugging using GDB. Note that this feature is only supported on x86_64/KVM.
|
||||
|
||||
To enable debugging with GDB, build with the `gdb` feature enabled:
|
||||
|
||||
```bash
|
||||
cargo build --features gdb
|
||||
```
|
||||
|
||||
To use the `--gdb` option, specify the Unix Domain Socket with `--path` that Cloud Hypervisor will use to communicate with the host's GDB:
|
||||
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
--kernel hypervisor-fw \
|
||||
--disk path=bionic-server-cloudimg-amd64.raw \
|
||||
--cpus boot=1 \
|
||||
--memory size=1024M \
|
||||
--net "tap=,mac=,ip=,mask=" \
|
||||
--console off \
|
||||
--serial tty \
|
||||
--gdb path=/tmp/ch-gdb-sock
|
||||
```
|
||||
|
||||
Cloud Hypervisor will listen for GDB on the host side before starting the guest.
|
||||
On the host side, connect to the GDB remote server as follows:
|
||||
|
||||
```bash
|
||||
gdb -q
|
||||
(gdb) target remote /tmp/ch-gdb-sock
|
||||
Remote debugging using /tmp/ch-gdb-sock
|
||||
warning: No executable has been specified, and target does not support
|
||||
determining executable automatically. Try using the "file" command.
|
||||
0x000000000011217e in ?? ()
|
||||
```
|
||||
|
||||
You can set up to four hardware breakpoints using the x86 debug register:
|
||||
|
||||
```bash
|
||||
(gdb) hb *0x1121b7
|
||||
Hardware assisted breakpoint 1 at 0x1121b7
|
||||
(gdb) c
|
||||
Continuing.
|
||||
|
||||
Breakpoint 1, 0x00000000001121b7 in ?? ()
|
||||
(gdb)
|
||||
```
|
||||
@@ -15,20 +15,14 @@ the guest side can be found in the [Guest TDX tree](https://github.com/intel/tdx
|
||||
The TDVF firmware can be found in the
|
||||
[EDK2 staging project](https://github.com/tianocore/edk2-staging/tree/TDVF).
|
||||
|
||||
The TDShim firmware can be found in the
|
||||
[Confidential Containers project](https://github.com/confidential-containers/td-shim).
|
||||
|
||||
## Cloud Hypervisor support
|
||||
|
||||
First, you must be running on a machine with TDX enabled in hardware, and
|
||||
with the host OS compiled from the [KVM TDX tree](https://github.com/intel/tdx/tree/kvm).
|
||||
|
||||
Cloud Hypervisor can run TDX VM (Trust Domain) by loading a TD firmware,
|
||||
Cloud Hypervisor can run TDX VM (Trust Domain) by loading the TDVF firmware,
|
||||
which will then load the guest kernel from the image. The image must be custom
|
||||
as it must include a kernel built from the [Guest TDX tree](https://github.com/intel/tdx/tree/guest).
|
||||
|
||||
### TDVF
|
||||
|
||||
The firmware can be built as follows:
|
||||
|
||||
```bash
|
||||
@@ -55,9 +49,9 @@ cargo build --features tdx
|
||||
```
|
||||
|
||||
And run a TDX VM by providing the firmware previously built, along with the
|
||||
guest image containing the TDX enlightened kernel. The latest image
|
||||
`td-guest-rhel8.5.raw` contains `console=hvc0` on the kernel boot parameters,
|
||||
meaning it will be printing guest kernel logs to the `virtio-console` device.
|
||||
guest image containing the TDX enlightened kernel. Assuming the guest kernel
|
||||
command line contains `console=hvc0` (printing to the `virtio-console` device),
|
||||
run Cloud Hypervisor as follows:
|
||||
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
@@ -67,8 +61,8 @@ meaning it will be printing guest kernel logs to the `virtio-console` device.
|
||||
--disk path=tdx_guest_img
|
||||
```
|
||||
|
||||
And here is the alternative command when looking for debug logs from the
|
||||
firmware:
|
||||
And here is the alternative command when looking for debug logs (assuming the
|
||||
guest kernel command line contains `console=ttyS0`):
|
||||
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
@@ -76,35 +70,6 @@ firmware:
|
||||
--cpus boot=1 \
|
||||
--memory size=1G \
|
||||
--disk path=tdx_guest_img \
|
||||
--serial file=/tmp/ch_serial \
|
||||
--console tty
|
||||
--serial tty \
|
||||
--console off
|
||||
```
|
||||
|
||||
### TDShim
|
||||
|
||||
This is a lightweight version of the TDVF, written in Rust and designed for
|
||||
direct kernel boot, which is useful for containers use cases.
|
||||
|
||||
You can find the instructions for building the firmware directly from the
|
||||
project [documentation](https://github.com/confidential-containers/td-shim/tree/staging#how-to-build).
|
||||
|
||||
And run a TDX VM by providing the firmware previously built, along with a guest
|
||||
kernel built from the [Guest TDX tree](https://github.com/intel/tdx/tree/guest).
|
||||
The appropriate kernel boot options must be provided through the `--cmdline`
|
||||
option as well.
|
||||
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
--tdx firmware=tdshim \
|
||||
--kernel bzImage \
|
||||
--cmdline "root=/dev/vda3 console=hvc0 rw"
|
||||
--cpus boot=1 \
|
||||
--memory size=1G \
|
||||
--disk path=tdx_guest_img
|
||||
```
|
||||
|
||||
### Guest kernel disables serial ports
|
||||
|
||||
The latest guest kernel that can be found in the latest image
|
||||
`td-guest-rhel8.5.raw` disabled the support for serial ports. This means adding
|
||||
`console=ttyS0` will have no effect and will not print any log from the guest.
|
||||
@@ -24,8 +24,8 @@ bucket is unbounded in speed which allows for bursts bound in size by
|
||||
the amount of tokens available. Once the token bucket is empty,
|
||||
consumption speed is bound by the "refill-rate". Similarly, Cloud
|
||||
Hypervisor provides another three options for limiting I/O operations,
|
||||
i.e., `ops_size` (I/O operations), `ops_one_time_burst` (I/O operations),
|
||||
and `ops_refill_time` (ms).
|
||||
i.e., `ops_size` (I/O operations), `bw_one_time_burst` (I/O operations),
|
||||
and `bw_refill_time` (ms).
|
||||
|
||||
One caveat in the I/O throttling is that every-time the bucket gets
|
||||
empty, it will stop I/O operations for a fixed amount of time
|
||||
|
||||
@@ -231,38 +231,3 @@ Last thing is to start the L2 guest with the huge pages memory backend.
|
||||
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
|
||||
--device path=/sys/bus/pci/devices/0000:00:04.0
|
||||
```
|
||||
|
||||
### Dedicated IOMMU PCI segments
|
||||
|
||||
To facilitate hotplug of devices that require being behind an IOMMU it is
|
||||
possible to mark entire PCI segments as behind the IOMMU.
|
||||
|
||||
This is accomplished through `--platform
|
||||
num_pci_segments=<number_of_segments>,iommu_segments=<range of segments>` or
|
||||
via the equivalents in `PlatformConfig` for the API.
|
||||
|
||||
e.g.
|
||||
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
--api-socket=/tmp/api \
|
||||
--cpus boot=1 \
|
||||
--memory size=4G,hugepages=on \
|
||||
--disk path=focal-server-cloudimg-amd64.raw \
|
||||
--kernel custom-vmlinux \
|
||||
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
|
||||
--platform num_pci_segments=2,iommu_segments=1
|
||||
```
|
||||
|
||||
This adds a second PCI segment to the platform behind the IOMMU. A VFIO device
|
||||
requiring the IOMMU then may be hotplugged:
|
||||
|
||||
e.g.
|
||||
|
||||
```bash
|
||||
./ch-remote --api-socket=/tmp/api add-device path=/sys/bus/pci/devices/0000:00:04.0,iommu=on,pci_segment=1
|
||||
```
|
||||
|
||||
Devices that cannot be placed behind an IOMMU (e.g. lacking an `iommu=` option)
|
||||
cannot be placed on the IOMMU segments.
|
||||
|
||||
|
||||
209
docs/networking.md
Normal file
209
docs/networking.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# How to use networking
|
||||
|
||||
cloud-hypervisor can emulate one or more virtual network interfaces, represented at the hypervisor host by [tap devices](https://www.kernel.org/doc/Documentation/networking/tuntap.txt). This guide briefly describes, in a manual and distribution neutral way, how to setup and use networking with cloud-hypervisor.
|
||||
|
||||
## Multiple queue support for net devices
|
||||
|
||||
While multiple vcpus defined for guest, to gain the benefit of vcpu scalable to improve performance, it suggests to define multiple queue pairs for net devices, one Tx/Rx queue pair per one vcpu, that means the number of queue pairs at least is equal to the vcpu count. In that case, after virtnet driver set cpu affinity for virtqueues in guest kernel, vcpus could handle interrupt from different virtqueue pairs in parallel.
|
||||
|
||||
It will gain better performance for guest that has multiple queues defined for net devices while it has multiple net sessions running in userspace.
|
||||
|
||||
To enable multiple queue support in cloud-hypervisor, multiple queue pairs will be defined, while multiple tap fds will be opened for the same tap device, it will also have multiple threads started, each thread will monitor and handle the events from each virtqueue pairs and the associated tap fd.
|
||||
|
||||
Note:
|
||||
|
||||
- Currently, it does not support to use ethtool to change the combined queue numbers in guest.
|
||||
- Multiple queue is enabled for vhost-user-net backend in cloud-hypervisor, however, multiple thread is not added to handle mq, thus, the performance for vhost-user-net backend is not supposed to be improved. The multiple thread will be added for backend later.
|
||||
- Performance test for vhost-user-net will be covered once vhost-user-net backend has multiple thread supported.
|
||||
- Performance test for virtio-net is done by comparing 2 queue pairs with 1 queue pairs, that to run 2 iperf3 sessions in the same test environments, throughput is improved about 37%.
|
||||
|
||||
## Start cloud-hypervisor with net devices
|
||||
|
||||
Use one `--net` command-line argument from cloud-hypervisor to specify the emulation of one or more virtual NIC's. The example below instructs cloud-hypervisor to emulate for instance 2 virtual NIC's:
|
||||
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
--cpus boot=4 \
|
||||
--memory "size=512M" \
|
||||
--disk path=focal-server-cloudimg-amd64.raw \
|
||||
--kernel my-vmlinux.bin \
|
||||
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
|
||||
--net tap=ich0,mac=a4:a1:c2:00:00:01,ip=192.168.4.2,mask=255.255.255.0,num_queues=2,queue_size=256 \
|
||||
tap=ich1,mac=a4:a1:c2:00:00:02,ip=10.0.1.2,mask=255.255.255.0,num_queues=2,queue_size=256
|
||||
```
|
||||
|
||||
The `--net` argument takes 1 or more space-separated strings of key value pairs containing the following 4 keys or fields:
|
||||
|
||||
| Name | Purpose | Optional |
|
||||
| ---------- | ---------------------- | -------- |
|
||||
| tap | tap device name | Yes |
|
||||
| mac | vNIC mac address | Yes |
|
||||
| ip | tap IP IP address | yes |
|
||||
| mask | tap IP netmask | Yes |
|
||||
| num_queues | the number of queues | yes |
|
||||
| queue_size | the size of each queue | Yes |
|
||||
|
||||
num_queues is the total number of tx and rx queues, the default value is 2, and it could be increased by multiples of 2. Additionally, num_queues is suggested to be as 2 times of vcpu count. The default value for queue_size is 256.
|
||||
|
||||
If the tap device is pre-created on host before guest boot up. To use multiple queue support for net device in guest, the tap device should be opened like this from host.
|
||||
|
||||
```bash
|
||||
[root@localhost ~]# ip tuntap add name ich0 mode tap multi_queue
|
||||
```
|
||||
|
||||
And the `--net` device should specify support for multiple queues. `num_queues` must be a multiple of 2 starting at least from 4 since multiple queues really means multiple queue pairs. We need at least 2 pairs for this configuration to be correct:
|
||||
|
||||
```bash
|
||||
--net tap=ich0,mac=a4:a1:c2:00:00:01,ip=192.168.4.2,mask=255.255.255.0,num_queues=4,queue_size=256
|
||||
```
|
||||
|
||||
## Configure the tap devices
|
||||
|
||||
After starting cloud-hypervisor as shown above, 2 tap devices with state down will become available at the host:
|
||||
|
||||
```bash
|
||||
root@host:~# ip link show ich0
|
||||
78: ich0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
|
||||
link/ether 72:54:12:ff:ce:6f brd ff:ff:ff:ff:ff:ff
|
||||
root@host:~# ip link show ich1
|
||||
79: ich1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
|
||||
link/ether 06:7a:fc:1b:9a:67 brd ff:ff:ff:ff:ff:ff
|
||||
```
|
||||
|
||||
Set the tap devices to up state:
|
||||
|
||||
```bash
|
||||
root@host:~# ip link set up ich0
|
||||
root@host:~# ip link set up ich1
|
||||
|
||||
root@host:~# ip link show ich0
|
||||
78: ich0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 1000
|
||||
link/ether 72:54:12:ff:ce:6f brd ff:ff:ff:ff:ff:ff
|
||||
root@host:~# ip link show ich1
|
||||
79: ich1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 1000
|
||||
link/ether 06:7a:fc:1b:9a:67 brd ff:ff:ff:ff:ff:ff
|
||||
```
|
||||
|
||||
## Connect tap devices
|
||||
|
||||
Different networking models can be used to provide external connectivity. In this example we will
|
||||
use 2 linux bridges emulating 2 different networks. The integration bridge (ich-int) in this example will also be used
|
||||
for external connectivity.
|
||||
|
||||
Create the bridges and connect the cloud-hypervisor tap devices to the bridges:
|
||||
|
||||
```bash
|
||||
root@host:~# brctl addbr ich-int
|
||||
root@host:~# brctl addbr ich-dpl
|
||||
root@host:~# ip link set up ich-int
|
||||
root@host:~# ip link set up ich-dpl
|
||||
root@host:~# brctl addif ich-int ich0
|
||||
root@host:~# brctl addif ich-dpl ich1
|
||||
root@host:~# brctl show
|
||||
bridge name bridge id STP enabled interfaces
|
||||
ich-dpl 8000.067afc1b9a67 no ich1
|
||||
ich-int 8000.725412ffce6f no ich0
|
||||
```
|
||||
|
||||
This completes the layer 2 wiring: The cloud-hypervisor is now connected to the hypervisor host via the 2 linux bridges.
|
||||
|
||||
## IP (Layer 3) provisioning
|
||||
|
||||
### Hypervisor host
|
||||
|
||||
On the hypervisor host add the network gateway IP address of each network to the 2 linux bridges:
|
||||
|
||||
```bash
|
||||
root@host:~# ip addr add 192.168.4.1/24 dev ich-int
|
||||
root@host:~# ip addr add 10.0.1.1/24 dev ich-dpl
|
||||
```
|
||||
|
||||
The routing table of the hypervisor host should now also have corresponding routing entries:
|
||||
|
||||
```bash
|
||||
root@host:~# route -n
|
||||
Kernel IP routing table
|
||||
Destination Gateway Genmask Flags Metric Ref Use Iface
|
||||
0.0.0.0 192.168.178.1 0.0.0.0 UG 600 0 0 wlan1
|
||||
10.0.1.0 0.0.0.0 255.255.255.0 U 0 0 0 ich-dpl
|
||||
192.168.4.0 0.0.0.0 255.255.255.0 U 0 0 0 ich-int
|
||||
192.168.178.0 0.0.0.0 255.255.255.0 U 600 0 0 wlan1
|
||||
```
|
||||
|
||||
### Virtual Machine
|
||||
|
||||
Within the virtual machine set the vNIC's to up state and provision the corresponding IP addresses on the 2 vNIC's. The steps outlined below use the ip command as an example. Alternative distribution specific procedures can also apply.
|
||||
|
||||
```bash
|
||||
root@guest:~# ip link set up enp0s2
|
||||
root@guest:~# ip link set up enp0s3
|
||||
root@guest:~# ip addr add 192.168.4.2/24 dev enp0s2
|
||||
root@guest:~# ip addr add 10.0.1.2/24 dev enp0s3
|
||||
```
|
||||
|
||||
IP connectivity between the virtual machine and the hypervisor-host can be verified by sending
|
||||
ICMP requests to the hypervisor-host for the gateway IP address from within the virtual machine:
|
||||
|
||||
```bash
|
||||
root@guest:~# ping 192.168.4.1
|
||||
PING 192.168.4.1 (192.168.4.1) 56(84) bytes of data.
|
||||
64 bytes from 192.168.4.1: icmp_seq=1 ttl=64 time=0.456 ms
|
||||
64 bytes from 192.168.4.1: icmp_seq=2 ttl=64 time=0.226 ms
|
||||
root@guest:~# ping 10.0.1.1
|
||||
PING 10.0.1.1 (10.0.1.1) 56(84) bytes of data.
|
||||
64 bytes from 10.0.1.1: icmp_seq=1 ttl=64 time=0.449 ms
|
||||
64 bytes from 10.0.1.1: icmp_seq=2 ttl=64 time=0.393 ms
|
||||
```
|
||||
|
||||
The connection can now be used for instance to log into the virtual machine with
|
||||
ssh under the precondition that the machine has an ssh daemon provisioned:
|
||||
|
||||
```bash
|
||||
root@host:~# ssh root@192.168.4.2
|
||||
The authenticity of host '192.168.4.2 (192.168.4.2)' can't be established.
|
||||
ECDSA key fingerprint is SHA256:qNAUmTtDMW9pNuZARkpLQhfw+Yc1tqUDBrQp7aZGSjw.
|
||||
Are you sure you want to continue connecting (yes/no)? yes
|
||||
Warning: Permanently added '192.168.4.2' (ECDSA) to the list of known hosts.
|
||||
root@192.168.4.2's password:
|
||||
Linux cloud-hypervisor 5.2.0 #2 SMP Thu Jul 11 08:08:16 CEST 2019 x86_64
|
||||
|
||||
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
|
||||
permitted by applicable law.
|
||||
|
||||
|
||||
Last login: Fri Jul 12 13:27:56 2019 from 192.168.4.1
|
||||
root@guest:~#
|
||||
```
|
||||
|
||||
## Internet connectivity
|
||||
|
||||
To enable internet connectivity a default gw and a nameserver has to be set within
|
||||
the virtual machine:
|
||||
|
||||
```bash
|
||||
root@guest:~# ip route add default via 192.168.4.1
|
||||
root@guest:~# cat /etc/resolv.conf
|
||||
options timeout:2
|
||||
domain vallis.nl
|
||||
search vallis.nl
|
||||
nameserver 192.168.178.1
|
||||
```
|
||||
|
||||
make sure that the default gateway of the hypervisor host (in this example host 192.168.178.1 which is an adsl router) has an entry in the routing table for the 192.168.4.0/24 network otherwise IP connectivity will not work.
|
||||
|
||||
```bash
|
||||
root@guest:~# nslookup ftp.nl.debian.org
|
||||
Server: 192.168.178.1
|
||||
Address: 192.168.178.1#53
|
||||
|
||||
Non-authoritative answer:
|
||||
cdn-fastly.deb.debian.org canonical name = prod.debian.map.fastly.net.
|
||||
Name: prod.debian.map.fastly.net
|
||||
Address: 151.101.36.204
|
||||
|
||||
root@guest:~# apt-get update
|
||||
Ign:1 http://cdn-fastly.deb.debian.org/debian stretch InRelease
|
||||
Get:2 http://cdn-fastly.deb.debian.org/debian stretch Release [118 kB]
|
||||
Get:3 http://cdn-fastly.deb.debian.org/debian stretch Release.gpg [2434 B]
|
||||
Fetched 120 kB in 1s (110 kB/s)
|
||||
```
|
||||
@@ -1,29 +0,0 @@
|
||||
# Performance Metrics
|
||||
|
||||
Cloud Hypervisor provides a [performance metrics](https://github.com/cloud-hypervisor/cloud-hypervisor/tree/main/performance-metrics)
|
||||
binary for users to generate metrics data from their own
|
||||
environment. This document describes how to generate metrics data
|
||||
quickly by using Cloud Hypervisor's development script,
|
||||
e.g. `dev_cli.sh`. The only prerequisite is [Docker installation](https://docs.docker.com/engine/install/).
|
||||
Please note that upon its first invocation, this script will pull a
|
||||
fairly large container image.
|
||||
|
||||
To generate metrics data for all available performance tests (including
|
||||
boot time, block I/O throughput, and network throughput & latency) and
|
||||
output the result into a json file:
|
||||
|
||||
```
|
||||
$ ./scripts/dev_cli.sh tests --metrics -- -- --report-file /tmp/metrics.json
|
||||
```
|
||||
|
||||
To get a list of available performance tests:
|
||||
|
||||
```
|
||||
$ ./scripts/dev_cli.sh tests --metrics -- -- --list-tests
|
||||
```
|
||||
|
||||
To generate metrics data for selected performance tests, e.g. boot time only:
|
||||
|
||||
```
|
||||
$ ./scripts/dev_cli.sh tests --metrics -- -- --report-file /tmp/metrics.json --test-filter boot_time
|
||||
```
|
||||
103
docs/releases.md
103
docs/releases.md
@@ -1,103 +0,0 @@
|
||||
# Release Documentation
|
||||
|
||||
## Abstract
|
||||
|
||||
This document provides guidance to users, downstream maintainers and
|
||||
any other consumers of the Cloud Hypervisor project, this document
|
||||
describes the release process, release cadence, stability expectations and
|
||||
related topics.
|
||||
|
||||
## Basic Terms
|
||||
|
||||
### Stability
|
||||
|
||||
For Cloud Hypervisor the following areas are subject to stability guarantees:
|
||||
|
||||
- [REST API](api.md#rest-api)
|
||||
- [Command line options](api.md#command-line-interface)
|
||||
- [Device Model](device_model.md)
|
||||
- Device tree, device list, ACPI, Hyper-V enlightenments and any other
|
||||
features exposed to guest
|
||||
- KVM compatibility
|
||||
- Rust edition compatibility
|
||||
|
||||
This list is incomplete but this document serves as a best effort guide to stability
|
||||
across releases.
|
||||
|
||||
### Experimental features
|
||||
|
||||
Experimental features are under active development and no guarantees are made about their stability.
|
||||
|
||||
List of experimental features:
|
||||
|
||||
- TDX
|
||||
- vfio-user
|
||||
- vDPA
|
||||
|
||||
### Security
|
||||
|
||||
Security fixes should be included in a new point release.
|
||||
|
||||
For security issues an advisory will be published via the GitHub security advisory process along with the release. Watching the project on GitHub will notify you of those issues.
|
||||
|
||||
## Releases
|
||||
|
||||
### Versioning
|
||||
|
||||
The versioning scheme uses `MAJOR.POINT` pattern:
|
||||
|
||||
- `MAJOR` can introduce incompatible changes along with support for new features. Changes to the [API](api.md#rest-api),
|
||||
[CLI options](api.md#command-line-interface) and [device model](device_model.md)
|
||||
require a notice at least 2 releases in advance for the actual change to take
|
||||
place.
|
||||
- `POINT` contains bug fixes and/or security fixes.
|
||||
|
||||
### Major Release Cadence
|
||||
|
||||
Cloud Hypervisor is under active development. A new major release is issued approximately
|
||||
every 6 weeks. Point releases are issued on demand, when important bug fixes are in
|
||||
the queue. A major release would receive bug fixes for the next two cycles (~12 weeks)
|
||||
and then be considered EOL.
|
||||
|
||||
```
|
||||
+ - Active release support
|
||||
E - EOL
|
||||
|
||||
2021 2022 2023
|
||||
| | | | | | | | |
|
||||
18.0 | | | ++++++++E
|
||||
19.0 | | | |++++++++E
|
||||
20.0 | | | | ++++++++E
|
||||
21.0 | | | | | ++++++++E
|
||||
22.0 | | | | | +++++++++E
|
||||
23.0 | | | | | | +++++++++E
|
||||
|
||||
```
|
||||
|
||||
### Major Release Stability Considerations
|
||||
|
||||
Snapshot/restore support is not compatible across `MAJOR` versions.
|
||||
Live migration support is not compatible across `MAJOR` versions.
|
||||
|
||||
### LTS Release Cadence
|
||||
|
||||
A regular release is promoted to LTS every 12 months. An LTS release is supported for 18 months. This gives a 6 months window for users to move to the new LTS.
|
||||
|
||||
```
|
||||
+ - Active release support
|
||||
E - EOL
|
||||
|
||||
2022 2023 2024 2025 2026
|
||||
| | | | | | | | | | | | | | | | |
|
||||
23.0 | |+++++++++++++++++++++++++++++E
|
||||
43.0 | | | | | |+++++++++++++++++++++++++++++E
|
||||
63.0 | | | | | | | | | |+++++++++++++++++++++++++++++E
|
||||
|
||||
```
|
||||
|
||||
### LTS Stablity Considerations
|
||||
|
||||
An LTS release is just a `MAJOR` release for which point releases are made for
|
||||
longer following the same rules for what can be backported to a `POINT` release.
|
||||
The focus lays on critical and security bug fixes which are pulled at the
|
||||
maintainer's discretion.
|
||||
@@ -8,6 +8,9 @@ snapshot and creates the exact same virtual machine, restoring the previously
|
||||
saved states. The new virtual machine is restored in a paused state, as it was
|
||||
before the snapshot was performed.
|
||||
|
||||
This feature is important for the project as it establishes the first step
|
||||
towards the support for live migration.
|
||||
|
||||
## Snapshot a Cloud Hypervisor VM
|
||||
|
||||
First thing, we must run a Cloud Hypervisor VM:
|
||||
@@ -43,22 +46,24 @@ ll /home/foo/snapshot/
|
||||
total 4194536
|
||||
drwxrwxr-x 2 foo bar 4096 Jul 22 11:50 ./
|
||||
drwxr-xr-x 47 foo bar 4096 Jul 22 11:47 ../
|
||||
-rw------- 1 foo bar 1084 Jul 22 11:19 config.json
|
||||
-rw------- 1 foo bar 4294967296 Jul 22 11:19 memory-ranges
|
||||
-rw------- 1 foo bar 217853 Jul 22 11:19 state.json
|
||||
-rw------- 1 foo bar 3221225472 Jul 22 11:19 memory-region-0
|
||||
-rw------- 1 foo bar 1073741824 Jul 22 11:19 memory-region-1
|
||||
-rw------- 1 foo bar 217853 Jul 22 11:19 vm.json
|
||||
```
|
||||
|
||||
`config.json` contains the virtual machine configuration. It is used to create
|
||||
a similar virtual machine with the correct amount of CPUs, RAM, and other
|
||||
expected devices. It is stored in a human readable format so that it could be
|
||||
modified between the snapshot and restore phases to achieve some very special
|
||||
use cases. But for most cases, manually modifying the configuration should not
|
||||
be needed.
|
||||
In this particular example, we can observe that 2 memory region files were
|
||||
created. That is explained by the size of the guest RAM, which is 4GiB in this
|
||||
case. Because it exceeds 3GiB (which is where we can find a ~1GiB memory hole),
|
||||
Cloud Hypervisor needs 2 distinct memory regions to be created. Each memory
|
||||
region's content is stored through a dedicated file, which explains why we end
|
||||
up with 2 different files, the first one containing the guest RAM range 0-3GiB
|
||||
and the second one containing the guest RAM range 3-4GiB.
|
||||
|
||||
`memory-ranges` stores the content of the guest RAM.
|
||||
|
||||
`state.json` contains the virtual machine state. It is used to restore each
|
||||
component in the state it was left before the snapshot occurred.
|
||||
`vm.json` gathers all information related to the virtual machine configuration
|
||||
and state. The configuration bits are used to create a similar virtual machine
|
||||
with the correct amount of CPUs, RAM, and other expected devices. The state
|
||||
bits are used to restore each component in the state it was left before the
|
||||
snapshot occurred.
|
||||
|
||||
## Restore a Cloud Hypervisor VM
|
||||
|
||||
@@ -95,4 +100,13 @@ snapshot earlier.
|
||||
|
||||
## Limitations
|
||||
|
||||
VFIO devices and Intel SGX are out of scope.
|
||||
The support of snapshot/restore feature is still experimental, meaning one
|
||||
might still find some bugs associated with it.
|
||||
|
||||
Additionally, some devices and features don't support to be snapshot and
|
||||
restored yet:
|
||||
- `vhost-user` devices
|
||||
- `virtio-mem`
|
||||
- Intel SGX
|
||||
|
||||
VFIO devices are out of scope.
|
||||
|
||||
@@ -33,7 +33,11 @@ Any UEFI capable image can be booted using the Cloud Hypervisor specific firmwar
|
||||
|
||||
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.
|
||||
|
||||
The same firmware can be used with Cloud Hypervisor or with QEMU. This is particularly useful if using QEMU for the preparation phase.
|
||||
|
||||
# Links
|
||||
|
||||
- [OVMF wiki](https://github.com/tianocore/tianocore.github.io/wiki/OVMF)
|
||||
- [Cloud Hypervisor specific tree](https://github.com/cloud-hypervisor/edk2/tree/ch)
|
||||
- [Redhat OVMF Status Report](https://access.redhat.com/sites/default/files/attachments/ovmf-whtepaper-031815.pdf)
|
||||
- [SeaBIOS Build Overview](https://www.seabios.org/Build_overview#Build_as_a_UEFI_Compatibility_Support_Module_.28CSM.29)
|
||||
|
||||
154
docs/vdpa.md
154
docs/vdpa.md
@@ -1,154 +0,0 @@
|
||||
# Virtio Data Path Acceleration
|
||||
|
||||
vDPA aims at achieving bare-metal performance for devices passed into a virtual
|
||||
machine. It is an alternative to VFIO, as it provides a simpler solution for
|
||||
achieving migration.
|
||||
|
||||
It is a kernel framework introduced recently to handle devices complying with
|
||||
the VIRTIO specification on their data-path, while the control path is vendor
|
||||
specific. In practice, virtqueues are accessed directly through DMA mechanism
|
||||
between the hardware and the guest. The control path is accessed through the
|
||||
vDPA framework, being exposed through the vhost interface as a vhost-vdpa
|
||||
device.
|
||||
|
||||
Because DMA accesses between device and guest are going through virtqueues,
|
||||
migration can be achieved without requiring device's driver to implement any
|
||||
specific migration support. In case of VFIO, each vendor is expected to provide
|
||||
an implementation of the VFIO migration framework, complicating things as it
|
||||
must be done for each and every device's driver.
|
||||
|
||||
The official [website](https://vdpa-dev.gitlab.io/) contains some extensive
|
||||
documentation on the topic.
|
||||
|
||||
## Usage
|
||||
|
||||
`VdpaConfig` (known as `--vdpa` from the CLI perspective) contains the list of
|
||||
parameters available for the vDPA device.
|
||||
|
||||
```rust
|
||||
struct VdpaConfig {
|
||||
path: PathBuf,
|
||||
num_queues: usize,
|
||||
id: Option<String>,
|
||||
pci_segment: u16,
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
--vdpa <vdpa> vDPA device "path=<device_path>,num_queues=<number_of_queues>,iommu=on|off,id=<device_id>,pci_segment=<segment_id>"
|
||||
```
|
||||
|
||||
### `path`
|
||||
|
||||
Path of the vDPA device. Usually `/dev/vhost-vdpa-X`.
|
||||
|
||||
This parameter is mandatory.
|
||||
|
||||
Value is a string.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--vdpa path=/dev/vhost-vdpa-0
|
||||
```
|
||||
|
||||
### `num_queues`
|
||||
|
||||
Number of virtqueues supported by the vDPA device.
|
||||
|
||||
This parameter is optional.
|
||||
|
||||
Value is an unsigned integer set to `1` by default.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--vdpa path=/dev/vhost-vdpa-0,num_queues=2
|
||||
```
|
||||
|
||||
### `id`
|
||||
|
||||
Identifier of the vDPA device.
|
||||
|
||||
This parameter is optional. If provided, it must be unique across the entire
|
||||
virtual machine.
|
||||
|
||||
Value is a string.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--vdpa path=/dev/vhost-vdpa-0,id=vdpa0
|
||||
```
|
||||
|
||||
### `pci_segment`
|
||||
|
||||
PCI segment number to which the vDPA device should be attached to.
|
||||
|
||||
This parameter is optional.
|
||||
|
||||
Value is an unsigned integer of 16 bits set to `0` by default.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--vdpa path=/dev/vhost-vdpa-0,pci_segment=1
|
||||
```
|
||||
|
||||
## Example with vDPA block simulator
|
||||
|
||||
The vDPA framework provides a simulator with both `virtio-block` and
|
||||
`virtio-net` implementations. This is very useful for testing vDPA when we
|
||||
don't have access to the specific hardware.
|
||||
|
||||
Given the host kernel has the appropriate modules available, let's load them
|
||||
all:
|
||||
|
||||
```
|
||||
sudo modprobe vdpa
|
||||
sudo modprobe vhost_vdpa
|
||||
sudo modprobe vdpa_sim
|
||||
sudo modprobe vdpa_sim_blk
|
||||
```
|
||||
|
||||
Given you have the `iproute2/vdpa` tool installed, let's now create the
|
||||
`virtio-block` vDPA device:
|
||||
|
||||
```sh
|
||||
sudo vdpa dev add name vdpa-blk1 mgmtdev vdpasim_blk
|
||||
sudo chown $USER:$USER /dev/vhost-vdpa-0
|
||||
sudo chmod 660 /dev/vhost-vdpa-0
|
||||
```
|
||||
|
||||
Increase the maximum locked memory to ensure setting up IOMMU mappings will
|
||||
succeed:
|
||||
|
||||
```sh
|
||||
ulimit -l unlimited
|
||||
```
|
||||
|
||||
Start Cloud Hypervisor:
|
||||
|
||||
```sh
|
||||
cloud-hypervisor \
|
||||
--cpus boot=1 \
|
||||
--memory size=1G,hugepages=on \
|
||||
--disk path=focal-server-cloudimg-amd64.raw \
|
||||
--kernel vmlinux \
|
||||
--cmdline "root=/dev/vda1 console=hvc0" \
|
||||
--vdpa path=/dev/vhost-vdpa-0,num_queues=1
|
||||
```
|
||||
|
||||
The `virtio-block` device backed by the vDPA simulator can be found as
|
||||
`/dev/vdb` in the guest:
|
||||
|
||||
```
|
||||
cloud@cloud:~$ lsblk
|
||||
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
|
||||
nullb0 252:0 0 250G 0 disk
|
||||
vda 254:0 0 2.2G 0 disk
|
||||
├─vda1 254:1 0 2.1G 0 part /
|
||||
├─vda14 254:14 0 4M 0 part
|
||||
└─vda15 254:15 0 106M 0 part /boot/efi
|
||||
vdb 254:16 0 128M 0 disk
|
||||
```
|
||||
@@ -75,7 +75,8 @@ Here is an example how to create a bridge and add two DPDK ports to it
|
||||
ovs-vsctl add-br ovsbr0 -- set bridge ovsbr0 datapath_type=netdev
|
||||
# create two DPDK ports and add them to the bridge
|
||||
ovs-vsctl add-port ovsbr0 vhost-user1 -- set Interface vhost-user1 type=dpdkvhostuser
|
||||
ovs-vsctl add-port ovsbr0 vhost-user2 -- set Interface vhost-user2 type=dpdkvhostuser
|
||||
ovs-vsctl add-port ovsbr0 vhost-user2 -- set Interface vhost-user2
|
||||
type=dpdkvhostuser
|
||||
# set the number of rx queues
|
||||
ovs-vsctl set Interface vhost-user1 options:n_rxq=2
|
||||
ovs-vsctl set Interface vhost-user2 options:n_rxq=2
|
||||
|
||||
@@ -65,7 +65,7 @@ iface eth0 inet dhcp
|
||||
|
||||
```bash
|
||||
# starting in the directory above rootfs
|
||||
sudo virtiofsd --socket-path=$PWD/virtiofs-rootfs.sock --shared-dir=$PWD/rootfs --cache=never &
|
||||
sudo virtiofsd --socket-path=$PWD/virtiofs-rootfs.sock -o source=$PWD/rootfs -o cache=none &
|
||||
sudo cloud-hypervisor \
|
||||
--cpus boot=1,max=1 \
|
||||
--kernel vmlinux \
|
||||
|
||||
@@ -22,7 +22,7 @@ __Prerequisites__
|
||||
- QEMU, version >=5.0.0 is recommended.
|
||||
- Windows installation ISO. Obtained through MSDN, Visual Studio subscription, evaluation center, etc.
|
||||
- [VirtIO driver ISO](https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/)
|
||||
- Suitable firmware for Cloud Hypervisor (`CLOUDHV.fd`) and for QEMU (`OVMF.fd`)
|
||||
- Suitable [OVMF](uefi.md) firmware
|
||||
- With the suggested image size of 30G, there should be enough free disk space to hold the installation ISO and any other necessary files
|
||||
|
||||
This step currently requires QEMU to install Windows onto the guest. QEMU is only used at the preparation stage, the resulting image is then fully functional with Cloud Hypervisor.
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
name = "event_monitor"
|
||||
version = "0.1.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.126"
|
||||
serde = { version = "1.0.137", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.81"
|
||||
libc = "0.2.112"
|
||||
serde = { version = "1.0.133", features = ["rc"] }
|
||||
serde_derive = "1.0.133"
|
||||
serde_json = "1.0.75"
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use serde::Serialize;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
@@ -49,9 +50,6 @@ pub fn event_log(source: &str, event: &str, properties: Option<&HashMap<Cow<str>
|
||||
properties,
|
||||
};
|
||||
serde_json::to_writer_pretty(file, &e).ok();
|
||||
|
||||
let mut file = file;
|
||||
file.write_all(b"\n\n").ok();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
226
fuzz/Cargo.lock
generated
226
fuzz/Cargo.lock
generated
@@ -11,9 +11,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.57"
|
||||
version = "1.0.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
|
||||
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -24,9 +24,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.1.0"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c38b6b6b79f671c25e1a3e785b7b82d7562ffc9cd3efdc98627e5668a2472490"
|
||||
checksum = "510c76ecefdceada737ea728f4f9a84bd2e1ef29f1ba555e560940fe279954de"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
@@ -47,6 +47,7 @@ dependencies = [
|
||||
"linux-loader",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
@@ -69,9 +70,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
@@ -115,9 +116,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
version = "1.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -127,33 +128,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.1.18"
|
||||
version = "3.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
|
||||
checksum = "7a30c3bf9ff12dfe5dae53f0a96e0febcd18420d1c0e7fad77796d9d5c4b5375"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"os_str_bytes",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"terminal_size",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "23.0.0"
|
||||
version = "20.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -193,9 +185,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crc32c"
|
||||
version = "0.6.3"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfea2db42e9927a3845fb268a10a72faed6d416065f77873f05e411457c363e"
|
||||
checksum = "ee6b9c9389584bcba988bd0836086789b7f87ad91892d6a83d5291dbb24524b5"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
@@ -242,6 +234,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
@@ -251,35 +244,11 @@ version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b643857cf70949306b81d7e92cb9d47add673868edac9863c4a49c42feaf3f1e"
|
||||
|
||||
[[package]]
|
||||
name = "gdbstub"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa2ca5d6b045de372cef3991f873389b421b4cabcfbe52f7787fae8b8b37906"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"log",
|
||||
"managed",
|
||||
"num-traits",
|
||||
"paste",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gdbstub_arch"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51dc4b5718ac76d21e8605c0966dd32d80273b89b11e6cfef467b04e45934d37"
|
||||
dependencies = [
|
||||
"gdbstub",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.6"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
||||
checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@@ -313,6 +282,7 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"vm-memory",
|
||||
@@ -321,9 +291,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iced-x86"
|
||||
version = "1.17.0"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "158f5204401d08f91d19176112146d75e99b3cf745092e268fa7be33e09adcec"
|
||||
checksum = "46e977036f7f5139d580c7f19ad62df9cb8ebd8410bb569e73585226be80a86f"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"static_assertions",
|
||||
@@ -331,9 +301,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.8.1"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
|
||||
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
@@ -351,14 +321,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.2"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
||||
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||
|
||||
[[package]]
|
||||
name = "kvm-bindings"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.5.0-tdx#52e56d0e8ef0f6ea32fc0492e6a175b73617a49f"
|
||||
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.5.0#9c497710ba9968d8efe15dbae03991b16cf82e23"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -368,7 +338,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kvm-ioctls"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/rust-vmm/kvm-ioctls?branch=main#1e03e29cdfbb0cb108a98de7a78045a5a517f18e"
|
||||
source = "git+https://github.com/rust-vmm/kvm-ioctls?branch=main#d22ef1f51852dfb055da38004e1a4fed81246f81"
|
||||
dependencies = [
|
||||
"kvm-bindings",
|
||||
"libc",
|
||||
@@ -383,15 +353,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.126"
|
||||
version = "0.2.112"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.4.3"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "336244aaeab6a12df46480dc585802aa743a72d66b11937844c61bbca84c991d"
|
||||
checksum = "36a9a84a6e8b55dfefb04235e55edb2b9a2a18488fcae777a6bdaa6f06f1deb3"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"cc",
|
||||
@@ -409,18 +379,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "managed"
|
||||
version = "0.8.0"
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
@@ -458,20 +428,11 @@ dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.12.0"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
|
||||
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
|
||||
|
||||
[[package]]
|
||||
name = "option_parser"
|
||||
@@ -479,15 +440,12 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.0.1"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435"
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc"
|
||||
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pci"
|
||||
@@ -498,7 +456,6 @@ dependencies = [
|
||||
"hypervisor",
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
@@ -514,11 +471,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.39"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -534,9 +491,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.18"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
||||
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -552,9 +509,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "remain"
|
||||
version = "0.2.3"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c35270ea384ac1762895831cc8acb96f171468e52cec82ed9186f9416209fa4"
|
||||
checksum = "70ba1e78fa68412cb93ef642fd4d20b9a941be49ee9333875ebaf13112673ea7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -572,9 +529,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.10"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
|
||||
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||
|
||||
[[package]]
|
||||
name = "seccompiler"
|
||||
@@ -587,24 +544,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.9"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd"
|
||||
checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.137"
|
||||
version = "1.0.133"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a"
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.137"
|
||||
version = "1.0.133"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
|
||||
checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -613,9 +567,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.81"
|
||||
version = "1.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
|
||||
checksum = "c059c05b48c5c0067d4b4b2b4f0732dd65feb52daf7e0ea09cd87e7dadc1af79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -624,9 +578,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.14"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
|
||||
checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
@@ -655,20 +609,20 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.95"
|
||||
version = "1.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
|
||||
checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
@@ -685,27 +639,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.15.0"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
|
||||
dependencies = [
|
||||
"terminal_size",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.31"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.31"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -713,16 +667,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.0"
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.0.0"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cfcd319456c4d6ea10087ed423473267e1a071f3bc0aa89f80d60997843c6f0"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
@@ -766,14 +720,12 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio-ioctls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#4630612f2ff300e78b6d55be324fb5481aa84661"
|
||||
source = "git+https://github.com/rust-vmm/vfio-ioctls?branch=main#8f7f2210ebe71660938b05b2ed7eec9253478bc5"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
"kvm-ioctls",
|
||||
"libc",
|
||||
"log",
|
||||
"thiserror",
|
||||
"vfio-bindings",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
@@ -811,9 +763,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vhost"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53567fd9ab820e4f3cc156f24146882fee3c365194c3e1dea74723265f27fc88"
|
||||
checksum = "3b56bf8f178fc500fe14505fca8b00dec76fc38f2304f461c8d9d7547982311d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -846,8 +798,8 @@ dependencies = [
|
||||
"rate_limiter",
|
||||
"seccompiler",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"vhost",
|
||||
@@ -863,9 +815,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88f2d73c184c18f8acc32dab77fcb6e3af92d53262538d3a68aa474810d6863c"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"vm-memory",
|
||||
@@ -886,8 +836,8 @@ name = "vm-device"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hypervisor",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"vfio-ioctls",
|
||||
@@ -902,9 +852,9 @@ source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#720e48e435b791ec6cb
|
||||
|
||||
[[package]]
|
||||
name = "vm-memory"
|
||||
version = "0.8.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "767ed8aaebbff902e02e6d3749dc2baef55e46565f8a6414a065e5baee4b4a81"
|
||||
checksum = "339d4349c126fdcd87e034631d7274370cf19eb0e87b33166bcd956589fc72c5"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"libc",
|
||||
@@ -917,6 +867,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"versionize",
|
||||
@@ -948,8 +899,6 @@ dependencies = [
|
||||
"devices",
|
||||
"epoll",
|
||||
"event_monitor",
|
||||
"gdbstub",
|
||||
"gdbstub_arch",
|
||||
"hypervisor",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
@@ -962,6 +911,7 @@ dependencies = [
|
||||
"qcow",
|
||||
"seccompiler",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"signal-hook",
|
||||
"thiserror",
|
||||
|
||||
@@ -3,29 +3,29 @@ name = "cloud-hypervisor-fuzz"
|
||||
version = "0.0.0"
|
||||
authors = ["Automatically generated"]
|
||||
publish = false
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
[package.metadata]
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
block_util = { path = "../block_util" }
|
||||
libc = "0.2.126"
|
||||
libfuzzer-sys = "0.4.3"
|
||||
libc = "0.2.112"
|
||||
libfuzzer-sys = "0.4.2"
|
||||
qcow = { path = "../qcow" }
|
||||
seccompiler = "0.2.0"
|
||||
vhdx = { path = "../vhdx" }
|
||||
virtio-devices = { path = "../virtio-devices" }
|
||||
virtio-queue = "0.3.0"
|
||||
virtio-queue = { path = "../virtio-queue" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vm-memory = "0.8.0"
|
||||
vm-memory = "0.7.0"
|
||||
|
||||
[dependencies.cloud-hypervisor]
|
||||
path = ".."
|
||||
|
||||
[patch.crates-io]
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0-tdx" }
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0", features = ["with-serde", "fam-wrappers"] }
|
||||
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "main" }
|
||||
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
|
||||
|
||||
|
||||
@@ -139,7 +139,8 @@ pub struct NoopVirtioInterrupt {}
|
||||
impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||
fn trigger(
|
||||
&self,
|
||||
_int_type: VirtioInterruptType,
|
||||
_int_type: &VirtioInterruptType,
|
||||
_queue: Option<&Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
) -> std::result::Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "hypervisor"
|
||||
version = "0.1.0"
|
||||
authors = ["Microsoft Authors"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
license = "Apache-2.0 OR BSD-3-Clause"
|
||||
|
||||
[features]
|
||||
@@ -11,22 +11,23 @@ mshv = ["mshv-ioctls", "mshv-bindings"]
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.52"
|
||||
epoll = "4.3.1"
|
||||
thiserror = "1.0.31"
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
thiserror = "1.0.30"
|
||||
libc = "0.2.112"
|
||||
log = "0.4.14"
|
||||
kvm-ioctls = { version = "0.11.0", optional = true }
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0-tdx", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
mshv-bindings = { git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
mshv-bindings = {git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", branch = "main", optional = true}
|
||||
serde = { version = "1.0.137", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.81"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
serde = { version = "1.0.133", features = ["rc"] }
|
||||
serde_derive = "1.0.133"
|
||||
serde_json = "1.0.75"
|
||||
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = { version = "0.9.0", features = ["with-serde"] }
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
||||
version = "1.17.0"
|
||||
version = "1.15.0"
|
||||
default-features = false
|
||||
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]
|
||||
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
|
||||
use crate::arch::emulator::{EmulationError, EmulationResult, PlatformEmulator, PlatformError};
|
||||
use crate::arch::x86::emulator::instructions::*;
|
||||
use crate::arch::x86::regs::{CR0_PE, EFER_LMA};
|
||||
use crate::arch::x86::{segment_type_expand_down, segment_type_ro, Exception, SegmentRegisterOps};
|
||||
use crate::arch::x86::regs::*;
|
||||
use crate::arch::x86::*;
|
||||
use crate::arch::x86::{Exception, SegmentRegisterOps};
|
||||
use crate::x86_64::{SegmentRegister, SpecialRegisters, StandardRegisters};
|
||||
use anyhow::Context;
|
||||
use iced_x86::*;
|
||||
|
||||
@@ -12,12 +12,6 @@
|
||||
use crate::aarch64::VcpuInit;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::aarch64::{RegList, Register, StandardRegisters};
|
||||
#[cfg(feature = "tdx")]
|
||||
use crate::kvm::{TdxExitDetails, TdxExitStatus};
|
||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||
use crate::x86_64::SuspendRegisters;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::x86_64::Xsave;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::x86_64::{CpuId, LapicState};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -25,13 +19,15 @@ use crate::x86_64::{
|
||||
ExtendedControlRegisters, FpuState, MsrEntries, SpecialRegisters, StandardRegisters, VcpuEvents,
|
||||
};
|
||||
use crate::CpuState;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::DeviceAttr;
|
||||
#[cfg(feature = "kvm")]
|
||||
use crate::MpState;
|
||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||
use crate::SuspendRegisters;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::Xsave;
|
||||
#[cfg(feature = "mshv")]
|
||||
use mshv_bindings::*;
|
||||
use thiserror::Error;
|
||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
///
|
||||
@@ -227,27 +223,11 @@ pub enum HypervisorCpuError {
|
||||
#[error("Failed to translate GVA: {0}")]
|
||||
TranslateVirtualAddress(#[source] anyhow::Error),
|
||||
///
|
||||
/// Set cpu attribute error
|
||||
///
|
||||
#[error("Failed to set vcpu attribute: {0}")]
|
||||
SetVcpuAttribute(#[source] anyhow::Error),
|
||||
///
|
||||
/// Check if cpu has a certain attribute error
|
||||
///
|
||||
#[error("Failed to check if vcpu has attribute: {0}")]
|
||||
HasVcpuAttribute(#[source] anyhow::Error),
|
||||
///
|
||||
/// Failed to initialize TDX on CPU
|
||||
///
|
||||
#[cfg(feature = "tdx")]
|
||||
#[error("Failed to initialize TDX: {0}")]
|
||||
InitializeTdx(#[source] std::io::Error),
|
||||
///
|
||||
/// Unknown TDX VM call
|
||||
///
|
||||
#[cfg(feature = "tdx")]
|
||||
#[error("Unknown TDX VM call")]
|
||||
UnknownTdxVmCall,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -264,10 +244,6 @@ pub enum VmExit<'a> {
|
||||
Reset,
|
||||
Shutdown,
|
||||
Hyperv,
|
||||
#[cfg(feature = "tdx")]
|
||||
Tdx,
|
||||
#[cfg(feature = "kvm")]
|
||||
Debug,
|
||||
}
|
||||
|
||||
///
|
||||
@@ -283,16 +259,7 @@ pub trait Vcpu: Send + Sync {
|
||||
/// Returns the vCPU general purpose registers.
|
||||
///
|
||||
fn get_regs(&self) -> Result<StandardRegisters>;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
///
|
||||
/// Sets vcpu attribute
|
||||
///
|
||||
fn set_vcpu_attr(&self, attr: &DeviceAttr) -> Result<()>;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
///
|
||||
/// Check if vcpu has attribute.
|
||||
///
|
||||
fn has_vcpu_attr(&self, attr: &DeviceAttr) -> Result<()>;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the vCPU general purpose registers.
|
||||
@@ -401,11 +368,6 @@ pub trait Vcpu: Send + Sync {
|
||||
/// potential soft lockups when being resumed.
|
||||
///
|
||||
fn notify_guest_clock_paused(&self) -> Result<()>;
|
||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||
///
|
||||
/// Sets debug registers to set hardware breakpoints and/or enable single step.
|
||||
///
|
||||
fn set_guest_debug(&self, addrs: &[GuestAddress], singlestep: bool) -> Result<()>;
|
||||
///
|
||||
/// Sets the type of CPU to be exposed to the guest and optional features.
|
||||
///
|
||||
@@ -466,11 +428,11 @@ pub trait Vcpu: Send + Sync {
|
||||
/// Triggers the running of the current virtual CPU returning an exit reason.
|
||||
///
|
||||
fn run(&self) -> std::result::Result<VmExit, HypervisorCpuError>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||
///
|
||||
/// Translate guest virtual address to guest physical address
|
||||
///
|
||||
fn translate_gva(&self, gva: u64, flags: u64) -> Result<(u64, u32)>;
|
||||
fn translate_gva(&self, gva: u64, flags: u64) -> Result<(u64, hv_translate_gva_result)>;
|
||||
///
|
||||
/// Initialize TDX support on the vCPU
|
||||
///
|
||||
@@ -486,14 +448,4 @@ pub trait Vcpu: Send + Sync {
|
||||
/// Set the "immediate_exit" state
|
||||
///
|
||||
fn set_immediate_exit(&self, exit: bool);
|
||||
#[cfg(feature = "tdx")]
|
||||
///
|
||||
/// Returns the details about TDX exit reason
|
||||
///
|
||||
fn get_tdx_exit_details(&mut self) -> Result<TdxExitDetails>;
|
||||
#[cfg(feature = "tdx")]
|
||||
///
|
||||
/// Set the status code for TDX exit
|
||||
///
|
||||
fn set_tdx_status(&mut self, status: TdxExitStatus);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
// Copyright 2018-2019 CrowdStrike, Inc.
|
||||
//
|
||||
//
|
||||
#[cfg(feature = "tdx")]
|
||||
use crate::kvm::TdxCapabilities;
|
||||
use crate::vm::Vm;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::x86_64::CpuId;
|
||||
@@ -61,11 +59,6 @@ pub enum HypervisorError {
|
||||
///
|
||||
#[error("Checking extensions:{0}")]
|
||||
CheckExtensions(#[source] anyhow::Error),
|
||||
///
|
||||
/// Failed to retrieve TDX capabilities
|
||||
///
|
||||
#[error("Failed to retrieve TDX capabilities:{0}")]
|
||||
TdxCapabilities(#[source] anyhow::Error),
|
||||
}
|
||||
|
||||
///
|
||||
@@ -112,9 +105,4 @@ pub trait Hypervisor: Send + Sync {
|
||||
/// Retrieve AArch64 host maximum IPA size supported by KVM.
|
||||
///
|
||||
fn get_host_ipa_limit(&self) -> i32;
|
||||
///
|
||||
/// Retrieve TDX capabilities
|
||||
///
|
||||
#[cfg(feature = "tdx")]
|
||||
fn tdx_capabilities(&self) -> Result<TdxCapabilities>;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ use kvm_bindings::{
|
||||
pub use kvm_bindings::{
|
||||
kvm_one_reg as Register, kvm_regs as StandardRegisters, kvm_vcpu_init as VcpuInit, RegList,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
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
|
||||
|
||||
@@ -17,11 +17,11 @@ use crate::cpu;
|
||||
use crate::device;
|
||||
use crate::hypervisor;
|
||||
use crate::vec_with_array_field;
|
||||
use crate::vm::{self, InterruptSourceConfig, VmOps};
|
||||
use crate::vm::{self, VmmOps};
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::{arm64_core_reg_id, offset__of};
|
||||
use kvm_ioctls::{NoDatamatch, VcpuFd, VmFd};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use std::convert::TryInto;
|
||||
@@ -42,8 +42,7 @@ use crate::arch::x86::NUM_IOAPIC_PINS;
|
||||
use aarch64::{RegList, Register, StandardRegisters};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use kvm_bindings::{
|
||||
kvm_enable_cap, kvm_guest_debug, kvm_msr_entry, MsrList, KVM_CAP_HYPERV_SYNIC,
|
||||
KVM_CAP_SPLIT_IRQCHIP, KVM_GUESTDBG_ENABLE, KVM_GUESTDBG_SINGLESTEP, KVM_GUESTDBG_USE_HW_BP,
|
||||
kvm_enable_cap, kvm_msr_entry, MsrList, KVM_CAP_HYPERV_SYNIC, KVM_CAP_SPLIT_IRQCHIP,
|
||||
};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use x86_64::{check_required_kvm_extensions, FpuState, SpecialRegisters, StandardRegisters};
|
||||
@@ -82,7 +81,7 @@ pub use {
|
||||
kvm_bindings::kvm_clock_data as ClockData, kvm_bindings::kvm_create_device as CreateDevice,
|
||||
kvm_bindings::kvm_device_attr as DeviceAttr,
|
||||
kvm_bindings::kvm_irq_routing_entry as IrqRoutingEntry, kvm_bindings::kvm_mp_state as MpState,
|
||||
kvm_bindings::kvm_run, kvm_bindings::kvm_userspace_memory_region as MemoryRegion,
|
||||
kvm_bindings::kvm_userspace_memory_region as MemoryRegion,
|
||||
kvm_bindings::kvm_vcpu_events as VcpuEvents, kvm_ioctls::DeviceFd, kvm_ioctls::IoEventAddress,
|
||||
kvm_ioctls::VcpuExit,
|
||||
};
|
||||
@@ -90,23 +89,13 @@ pub use {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const KVM_CAP_SGX_ATTRIBUTE: u32 = 196;
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
const KVM_EXIT_TDX: u32 = 35;
|
||||
#[cfg(feature = "tdx")]
|
||||
const TDG_VP_VMCALL_GET_QUOTE: u64 = 0x10002;
|
||||
#[cfg(feature = "tdx")]
|
||||
const TDG_VP_VMCALL_SETUP_EVENT_NOTIFY_INTERRUPT: u64 = 0x10004;
|
||||
#[cfg(feature = "tdx")]
|
||||
const TDG_VP_VMCALL_SUCCESS: u64 = 0;
|
||||
#[cfg(feature = "tdx")]
|
||||
const TDG_VP_VMCALL_INVALID_OPERAND: u64 = 0x8000000000000000;
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
ioctl_iowr_nr!(KVM_MEMORY_ENCRYPT_OP, KVMIO, 0xba, std::os::raw::c_ulong);
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
#[repr(u32)]
|
||||
enum TdxCommand {
|
||||
#[allow(dead_code)]
|
||||
Capabilities = 0,
|
||||
InitVm,
|
||||
InitVcpu,
|
||||
@@ -114,46 +103,6 @@ enum TdxCommand {
|
||||
Finalize,
|
||||
}
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
pub enum TdxExitDetails {
|
||||
GetQuote,
|
||||
SetupEventNotifyInterrupt,
|
||||
}
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
pub enum TdxExitStatus {
|
||||
Success,
|
||||
InvalidOperand,
|
||||
}
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
const TDX_MAX_NR_CPUID_CONFIGS: usize = 6;
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TdxCpuidConfig {
|
||||
pub leaf: u32,
|
||||
pub sub_leaf: u32,
|
||||
pub eax: u32,
|
||||
pub ebx: u32,
|
||||
pub ecx: u32,
|
||||
pub edx: u32,
|
||||
}
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TdxCapabilities {
|
||||
pub attrs_fixed0: u64,
|
||||
pub attrs_fixed1: u64,
|
||||
pub xfam_fixed0: u64,
|
||||
pub xfam_fixed1: u64,
|
||||
pub nr_cpuid_configs: u32,
|
||||
pub padding: u32,
|
||||
pub cpuid_configs: [TdxCpuidConfig; TDX_MAX_NR_CPUID_CONFIGS],
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct KvmVmState {}
|
||||
|
||||
@@ -234,7 +183,7 @@ impl vm::Vm for KvmVm {
|
||||
fn create_vcpu(
|
||||
&self,
|
||||
id: u8,
|
||||
vm_ops: Option<Arc<dyn VmOps>>,
|
||||
vmmops: Option<Arc<dyn VmmOps>>,
|
||||
) -> vm::Result<Arc<dyn cpu::Vcpu>> {
|
||||
let vc = self
|
||||
.fd
|
||||
@@ -244,7 +193,7 @@ impl vm::Vm for KvmVm {
|
||||
fd: vc,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
msrs: self.msrs.clone(),
|
||||
vm_ops,
|
||||
vmmops,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
hyperv_synic: AtomicBool::new(false),
|
||||
};
|
||||
@@ -284,64 +233,6 @@ impl vm::Vm for KvmVm {
|
||||
.unregister_ioevent(fd, addr, NoDatamatch)
|
||||
.map_err(|e| vm::HypervisorVmError::UnregisterIoEvent(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Constructs a routing entry
|
||||
///
|
||||
fn make_routing_entry(
|
||||
&self,
|
||||
gsi: u32,
|
||||
config: &InterruptSourceConfig,
|
||||
) -> kvm_irq_routing_entry {
|
||||
match &config {
|
||||
InterruptSourceConfig::MsiIrq(cfg) => {
|
||||
let mut kvm_route = kvm_irq_routing_entry {
|
||||
gsi,
|
||||
type_: KVM_IRQ_ROUTING_MSI,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
kvm_route.u.msi.address_lo = cfg.low_addr;
|
||||
kvm_route.u.msi.address_hi = cfg.high_addr;
|
||||
kvm_route.u.msi.data = cfg.data;
|
||||
|
||||
if self.check_extension(crate::kvm::Cap::MsiDevid) {
|
||||
// On AArch64, there is limitation on the range of the 'devid',
|
||||
// it can not be greater than 65536 (the max of u16).
|
||||
//
|
||||
// BDF can not be used directly, because 'segment' is in high
|
||||
// 16 bits. The layout of the u32 BDF is:
|
||||
// |---- 16 bits ----|-- 8 bits --|-- 5 bits --|-- 3 bits --|
|
||||
// | segment | bus | device | function |
|
||||
//
|
||||
// Now that we support 1 bus only in a segment, we can build a
|
||||
// 'devid' by replacing the 'bus' bits with the low 8 bits of
|
||||
// 'segment' data.
|
||||
// This way we can resolve the range checking problem and give
|
||||
// different `devid` to all the devices. Limitation is that at
|
||||
// most 256 segments can be supported.
|
||||
//
|
||||
let modified_devid = (cfg.devid & 0x00ff_0000) >> 8 | cfg.devid & 0xff;
|
||||
|
||||
kvm_route.flags = KVM_MSI_VALID_DEVID;
|
||||
kvm_route.u.msi.__bindgen_anon_1.devid = modified_devid;
|
||||
}
|
||||
kvm_route
|
||||
}
|
||||
InterruptSourceConfig::LegacyIrq(cfg) => {
|
||||
let mut kvm_route = kvm_irq_routing_entry {
|
||||
gsi,
|
||||
type_: KVM_IRQ_ROUTING_IRQCHIP,
|
||||
..Default::default()
|
||||
};
|
||||
kvm_route.u.irqchip.irqchip = cfg.irqchip;
|
||||
kvm_route.u.irqchip.pin = cfg.pin;
|
||||
|
||||
kvm_route
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Sets the GSI routing table entries, overwriting any previously set
|
||||
/// entries, as per the `KVM_SET_GSI_ROUTING` ioctl.
|
||||
@@ -610,7 +501,7 @@ impl vm::Vm for KvmVm {
|
||||
let data = TdxInitVm {
|
||||
max_vcpus,
|
||||
tsc_khz: 0,
|
||||
attributes: 0,
|
||||
attributes: 1, // TDX1_TD_ATTRIBUTE_DEBUG,
|
||||
cpuid: cpuid.as_fam_struct_ptr() as u64,
|
||||
mrconfigid: [0; 6],
|
||||
mrowner: [0; 6],
|
||||
@@ -847,34 +738,13 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
||||
fn get_host_ipa_limit(&self) -> i32 {
|
||||
self.kvm.get_host_ipa_limit()
|
||||
}
|
||||
|
||||
///
|
||||
/// Retrieve TDX capabilities
|
||||
///
|
||||
#[cfg(feature = "tdx")]
|
||||
fn tdx_capabilities(&self) -> hypervisor::Result<TdxCapabilities> {
|
||||
let data = TdxCapabilities {
|
||||
nr_cpuid_configs: TDX_MAX_NR_CPUID_CONFIGS as u32,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
tdx_command(
|
||||
&self.kvm.as_raw_fd(),
|
||||
TdxCommand::Capabilities,
|
||||
0,
|
||||
&data as *const _ as u64,
|
||||
)
|
||||
.map_err(|e| hypervisor::HypervisorError::TdxCapabilities(e.into()))?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
/// Vcpu struct for KVM
|
||||
pub struct KvmVcpu {
|
||||
fd: VcpuFd,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
msrs: MsrEntries,
|
||||
vm_ops: Option<Arc<dyn vm::VmOps>>,
|
||||
vmmops: Option<Arc<dyn vm::VmmOps>>,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
hyperv_synic: AtomicBool,
|
||||
}
|
||||
@@ -907,27 +777,6 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.set_regs(regs)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetStandardRegs(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
///
|
||||
/// Set attribute for vcpu.
|
||||
///
|
||||
fn set_vcpu_attr(&self, attr: &DeviceAttr) -> cpu::Result<()> {
|
||||
self.fd
|
||||
.set_device_attr(attr)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetVcpuAttribute(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
///
|
||||
/// Check if vcpu has a certain attribute.
|
||||
///
|
||||
fn has_vcpu_attr(&self, attr: &DeviceAttr) -> cpu::Result<()> {
|
||||
self.fd
|
||||
.has_device_attr(attr)
|
||||
.map_err(|e| cpu::HypervisorCpuError::HasVcpuAttribute(e.into()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Returns the vCPU special registers.
|
||||
@@ -1088,24 +937,6 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.set_xcrs(xcrs)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetXcsr(e.into()))
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Translates guest virtual address to guest physical address using the `KVM_TRANSLATE` ioctl.
|
||||
///
|
||||
fn translate_gva(&self, gva: u64, _flags: u64) -> cpu::Result<(u64, u32)> {
|
||||
let tr = self
|
||||
.fd
|
||||
.translate_gva(gva)
|
||||
.map_err(|e| cpu::HypervisorCpuError::TranslateVirtualAddress(e.into()))?;
|
||||
// tr.valid is set if the GVA is mapped to valid GPA.
|
||||
match tr.valid {
|
||||
0 => Err(cpu::HypervisorCpuError::TranslateVirtualAddress(anyhow!(
|
||||
"Invalid GVA: {:#x}",
|
||||
gva
|
||||
))),
|
||||
_ => Ok((tr.physical_address, 0)),
|
||||
}
|
||||
}
|
||||
///
|
||||
/// Triggers the running of the current virtual CPU returning an exit reason.
|
||||
///
|
||||
@@ -1114,8 +945,8 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
Ok(run) => match run {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
VcpuExit::IoIn(addr, data) => {
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
return vm_ops
|
||||
if let Some(vmmops) = &self.vmmops {
|
||||
return vmmops
|
||||
.pio_read(addr.into(), data)
|
||||
.map(|_| cpu::VmExit::Ignore)
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()));
|
||||
@@ -1125,8 +956,8 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
VcpuExit::IoOut(addr, data) => {
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
return vm_ops
|
||||
if let Some(vmmops) = &self.vmmops {
|
||||
return vmmops
|
||||
.pio_write(addr.into(), data)
|
||||
.map(|_| cpu::VmExit::Ignore)
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()));
|
||||
@@ -1158,8 +989,8 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
}
|
||||
|
||||
VcpuExit::MmioRead(addr, data) => {
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
return vm_ops
|
||||
if let Some(vmmops) = &self.vmmops {
|
||||
return vmmops
|
||||
.mmio_read(addr, data)
|
||||
.map(|_| cpu::VmExit::Ignore)
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()));
|
||||
@@ -1168,8 +999,8 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
Ok(cpu::VmExit::MmioRead(addr, data))
|
||||
}
|
||||
VcpuExit::MmioWrite(addr, data) => {
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
return vm_ops
|
||||
if let Some(vmmops) = &self.vmmops {
|
||||
return vmmops
|
||||
.mmio_write(addr, data)
|
||||
.map(|_| cpu::VmExit::Ignore)
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()));
|
||||
@@ -1178,9 +1009,6 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
Ok(cpu::VmExit::MmioWrite(addr, data))
|
||||
}
|
||||
VcpuExit::Hyperv => Ok(cpu::VmExit::Hyperv),
|
||||
#[cfg(feature = "tdx")]
|
||||
VcpuExit::Unsupported(KVM_EXIT_TDX) => Ok(cpu::VmExit::Tdx),
|
||||
VcpuExit::Debug(_) => Ok(cpu::VmExit::Debug),
|
||||
|
||||
r => Err(cpu::HypervisorCpuError::RunVcpu(anyhow!(
|
||||
"Unexpected exit reason on vcpu run: {:?}",
|
||||
@@ -1234,45 +1062,6 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets debug registers to set hardware breakpoints and/or enable single step.
|
||||
///
|
||||
fn set_guest_debug(
|
||||
&self,
|
||||
addrs: &[vm_memory::GuestAddress],
|
||||
singlestep: bool,
|
||||
) -> cpu::Result<()> {
|
||||
if addrs.len() > 4 {
|
||||
return Err(cpu::HypervisorCpuError::SetDebugRegs(anyhow!(
|
||||
"Support 4 breakpoints at most but {} addresses are passed",
|
||||
addrs.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let mut dbg = kvm_guest_debug {
|
||||
control: KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP,
|
||||
..Default::default()
|
||||
};
|
||||
if singlestep {
|
||||
dbg.control |= KVM_GUESTDBG_SINGLESTEP;
|
||||
}
|
||||
|
||||
// Set bits 9 and 10.
|
||||
// bit 9: GE (global exact breakpoint enable) flag.
|
||||
// bit 10: always 1.
|
||||
dbg.arch.debugreg[7] = 0x0600;
|
||||
|
||||
for (i, addr) in addrs.iter().enumerate() {
|
||||
dbg.arch.debugreg[i] = addr.0;
|
||||
// Set global breakpoint enable flag
|
||||
dbg.arch.debugreg[7] |= 2 << (i * 2);
|
||||
}
|
||||
|
||||
self.fd
|
||||
.set_guest_debug(&dbg)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetDebugRegs(e.into()))
|
||||
}
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
fn vcpu_init(&self, kvi: &VcpuInit) -> cpu::Result<()> {
|
||||
self.fd
|
||||
@@ -1792,43 +1581,6 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
fn set_immediate_exit(&self, exit: bool) {
|
||||
self.fd.set_kvm_immediate_exit(exit.into());
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the details about TDX exit reason
|
||||
///
|
||||
#[cfg(feature = "tdx")]
|
||||
fn get_tdx_exit_details(&mut self) -> cpu::Result<TdxExitDetails> {
|
||||
let kvm_run = self.fd.get_kvm_run();
|
||||
let tdx_vmcall = unsafe { &mut kvm_run.__bindgen_anon_1.tdx.u.vmcall };
|
||||
|
||||
tdx_vmcall.status_code = TDG_VP_VMCALL_INVALID_OPERAND;
|
||||
|
||||
if tdx_vmcall.type_ != 0 {
|
||||
return Err(cpu::HypervisorCpuError::UnknownTdxVmCall);
|
||||
}
|
||||
|
||||
match tdx_vmcall.subfunction {
|
||||
TDG_VP_VMCALL_GET_QUOTE => Ok(TdxExitDetails::GetQuote),
|
||||
TDG_VP_VMCALL_SETUP_EVENT_NOTIFY_INTERRUPT => {
|
||||
Ok(TdxExitDetails::SetupEventNotifyInterrupt)
|
||||
}
|
||||
_ => Err(cpu::HypervisorCpuError::UnknownTdxVmCall),
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Set the status code for TDX exit
|
||||
///
|
||||
#[cfg(feature = "tdx")]
|
||||
fn set_tdx_status(&mut self, status: TdxExitStatus) {
|
||||
let kvm_run = self.fd.get_kvm_run();
|
||||
let tdx_vmcall = unsafe { &mut kvm_run.__bindgen_anon_1.tdx.u.vmcall };
|
||||
|
||||
tdx_vmcall.status_code = match status {
|
||||
TdxExitStatus::Success => TDG_VP_VMCALL_SUCCESS,
|
||||
TdxExitStatus::InvalidOperand => TDG_VP_VMCALL_INVALID_OPERAND,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Device struct for KVM
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
use crate::arch::x86::{msr_index, SegmentRegisterOps, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
||||
use crate::kvm::{Cap, Kvm, KvmError, KvmResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
///
|
||||
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
|
||||
|
||||
@@ -37,10 +37,10 @@ pub mod kvm;
|
||||
pub mod mshv;
|
||||
|
||||
/// Hypevisor related module
|
||||
mod hypervisor;
|
||||
pub mod hypervisor;
|
||||
|
||||
/// Vm related module
|
||||
mod vm;
|
||||
pub mod vm;
|
||||
|
||||
/// CPU related module
|
||||
mod cpu;
|
||||
@@ -48,32 +48,16 @@ mod cpu;
|
||||
/// Device related module
|
||||
mod device;
|
||||
|
||||
pub use crate::hypervisor::{Hypervisor, HypervisorError};
|
||||
pub use cpu::{HypervisorCpuError, Vcpu, VmExit};
|
||||
pub use device::{Device, HypervisorDeviceError};
|
||||
pub use hypervisor::{Hypervisor, HypervisorError};
|
||||
#[cfg(all(feature = "kvm", target_arch = "aarch64"))]
|
||||
pub use kvm::aarch64;
|
||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||
pub use kvm::x86_64;
|
||||
// Aliased types exposed from both hypervisors
|
||||
#[cfg(feature = "kvm")]
|
||||
pub use kvm::{
|
||||
ClockData, CpuState, CreateDevice, DeviceAttr, DeviceFd, IoEventAddress, IrqRoutingEntry,
|
||||
MemoryRegion, MpState, VcpuEvents, VcpuExit, VmState,
|
||||
};
|
||||
pub use kvm::*;
|
||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||
pub use mshv::x86_64;
|
||||
// Aliased types exposed from both hypervisors
|
||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||
pub use mshv::{
|
||||
CpuState, CreateDevice, DeviceAttr, DeviceFd, IoEventAddress, IrqRoutingEntry, MemoryRegion,
|
||||
MpState, VcpuEvents, VcpuExit, VmState,
|
||||
};
|
||||
pub use mshv::*;
|
||||
pub use vm::{DataMatch, HypervisorVmError, Vm};
|
||||
|
||||
use std::sync::Arc;
|
||||
pub use vm::{
|
||||
DataMatch, HypervisorVmError, InterruptSourceConfig, LegacyIrqSourceConfig, MsiIrqSourceConfig,
|
||||
Vm, VmOps,
|
||||
};
|
||||
|
||||
pub fn new() -> std::result::Result<Arc<dyn Hypervisor>, HypervisorError> {
|
||||
#[cfg(feature = "kvm")]
|
||||
|
||||
@@ -11,11 +11,11 @@ use crate::cpu;
|
||||
use crate::cpu::Vcpu;
|
||||
use crate::hypervisor;
|
||||
use crate::vec_with_array_field;
|
||||
use crate::vm::{self, InterruptSourceConfig, VmOps};
|
||||
use crate::vm::{self, VmmOps};
|
||||
pub use mshv_bindings::*;
|
||||
pub use mshv_ioctls::IoEventAddress;
|
||||
use mshv_ioctls::{set_registers_64, Mshv, NoDatamatch, VcpuFd, VmFd};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use vm::DataMatch;
|
||||
@@ -122,7 +122,7 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
fd: vm_fd,
|
||||
msrs,
|
||||
hv_state: hv_state_init(),
|
||||
vm_ops: None,
|
||||
vmmops: None,
|
||||
dirty_log_slots: Arc::new(RwLock::new(HashMap::new())),
|
||||
}))
|
||||
}
|
||||
@@ -134,7 +134,7 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Retrieve the list of MSRs supported by MSHV.
|
||||
/// Retrieve the list of MSRs supported by KVM.
|
||||
///
|
||||
fn get_msr_list(&self) -> hypervisor::Result<MsrList> {
|
||||
self.mshv
|
||||
@@ -151,7 +151,7 @@ pub struct MshvVcpu {
|
||||
cpuid: CpuId,
|
||||
msrs: MsrEntries,
|
||||
hv_state: Arc<RwLock<HvState>>, // Mshv State
|
||||
vm_ops: Option<Arc<dyn vm::VmOps>>,
|
||||
vmmops: Option<Arc<dyn vm::VmmOps>>,
|
||||
}
|
||||
|
||||
/// Implementation of Vcpu trait for Microsoft Hypervisor
|
||||
@@ -355,14 +355,14 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
|
||||
if is_write {
|
||||
let data = (info.rax as u32).to_le_bytes();
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
vm_ops
|
||||
if let Some(vmmops) = &self.vmmops {
|
||||
vmmops
|
||||
.pio_write(port.into(), &data[0..len])
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
|
||||
}
|
||||
} else {
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
vm_ops
|
||||
if let Some(vmmops) = &self.vmmops {
|
||||
vmmops
|
||||
.pio_read(port.into(), &mut data[0..len])
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
|
||||
}
|
||||
@@ -561,17 +561,13 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
///
|
||||
/// Translate guest virtual address to guest physical address
|
||||
///
|
||||
fn translate_gva(&self, gva: u64, flags: u64) -> cpu::Result<(u64, u32)> {
|
||||
fn translate_gva(&self, gva: u64, flags: u64) -> cpu::Result<(u64, hv_translate_gva_result)> {
|
||||
let r = self
|
||||
.fd
|
||||
.translate_gva(gva, flags)
|
||||
.map_err(|e| cpu::HypervisorCpuError::TranslateVirtualAddress(e.into()))?;
|
||||
|
||||
let gpa = r.0;
|
||||
// SAFETY: r is valid, otherwise this function will have returned
|
||||
let result_code = unsafe { r.1.__bindgen_anon_1.result_code };
|
||||
|
||||
Ok((gpa, result_code))
|
||||
Ok(r)
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
@@ -630,13 +626,15 @@ impl<'a> MshvEmulatorContext<'a> {
|
||||
// TODO: More fine-grained control for the flags
|
||||
let flags = HV_TRANSLATE_GVA_VALIDATE_READ | HV_TRANSLATE_GVA_VALIDATE_WRITE;
|
||||
|
||||
let (gpa, result_code) = self
|
||||
let r = self
|
||||
.vcpu
|
||||
.translate_gva(gva, flags.into())
|
||||
.map_err(|e| PlatformError::TranslateVirtualAddress(anyhow!(e)))?;
|
||||
|
||||
// SAFETY: r is valid, otherwise this function will have returned
|
||||
let result_code = unsafe { r.1.__bindgen_anon_1.result_code };
|
||||
match result_code {
|
||||
hv_translate_gva_result_code_HV_TRANSLATE_GVA_SUCCESS => Ok(gpa),
|
||||
hv_translate_gva_result_code_HV_TRANSLATE_GVA_SUCCESS => Ok(r.0),
|
||||
_ => Err(PlatformError::TranslateVirtualAddress(anyhow!(result_code))),
|
||||
}
|
||||
}
|
||||
@@ -655,9 +653,9 @@ impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
||||
gpa
|
||||
);
|
||||
|
||||
if let Some(vm_ops) = &self.vcpu.vm_ops {
|
||||
if vm_ops.guest_mem_read(gpa, data).is_err() {
|
||||
vm_ops
|
||||
if let Some(vmmops) = &self.vcpu.vmmops {
|
||||
if vmmops.guest_mem_read(gpa, data).is_err() {
|
||||
vmmops
|
||||
.mmio_read(gpa, data)
|
||||
.map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
|
||||
}
|
||||
@@ -675,9 +673,9 @@ impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
||||
gpa
|
||||
);
|
||||
|
||||
if let Some(vm_ops) = &self.vcpu.vm_ops {
|
||||
if vm_ops.guest_mem_write(gpa, data).is_err() {
|
||||
vm_ops
|
||||
if let Some(vmmops) = &self.vcpu.vmmops {
|
||||
if vmmops.guest_mem_write(gpa, data).is_err() {
|
||||
vmmops
|
||||
.mmio_write(gpa, data)
|
||||
.map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
|
||||
}
|
||||
@@ -746,7 +744,7 @@ pub struct MshvVm {
|
||||
msrs: MsrEntries,
|
||||
// Hypervisor State
|
||||
hv_state: Arc<RwLock<HvState>>,
|
||||
vm_ops: Option<Arc<dyn vm::VmOps>>,
|
||||
vmmops: Option<Arc<dyn vm::VmmOps>>,
|
||||
dirty_log_slots: Arc<RwLock<HashMap<u64, MshvDirtyLogSlot>>>,
|
||||
}
|
||||
|
||||
@@ -816,7 +814,7 @@ impl vm::Vm for MshvVm {
|
||||
fn create_vcpu(
|
||||
&self,
|
||||
id: u8,
|
||||
vm_ops: Option<Arc<dyn VmOps>>,
|
||||
vmmops: Option<Arc<dyn VmmOps>>,
|
||||
) -> vm::Result<Arc<dyn cpu::Vcpu>> {
|
||||
let vcpu_fd = self
|
||||
.fd
|
||||
@@ -828,7 +826,7 @@ impl vm::Vm for MshvVm {
|
||||
cpuid: CpuId::new(1).unwrap(),
|
||||
msrs: self.msrs.clone(),
|
||||
hv_state: self.hv_state.clone(),
|
||||
vm_ops,
|
||||
vmmops,
|
||||
};
|
||||
Ok(Arc::new(vcpu))
|
||||
}
|
||||
@@ -957,27 +955,6 @@ impl vm::Vm for MshvVm {
|
||||
.map_err(|e| vm::HypervisorVmError::CreatePassthroughDevice(e.into()))
|
||||
}
|
||||
|
||||
///
|
||||
/// Constructs a routing entry
|
||||
///
|
||||
fn make_routing_entry(
|
||||
&self,
|
||||
gsi: u32,
|
||||
config: &InterruptSourceConfig,
|
||||
) -> mshv_msi_routing_entry {
|
||||
match config {
|
||||
InterruptSourceConfig::MsiIrq(cfg) => mshv_msi_routing_entry {
|
||||
gsi,
|
||||
address_lo: cfg.low_addr,
|
||||
address_hi: cfg.high_addr,
|
||||
data: cfg.data,
|
||||
},
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> vm::Result<()> {
|
||||
let mut msi_routing =
|
||||
vec_with_array_field::<mshv_msi_routing, mshv_msi_routing_entry>(entries.len());
|
||||
@@ -1048,3 +1025,6 @@ impl vm::Vm for MshvVm {
|
||||
.map_err(|e| vm::HypervisorVmError::GetDirtyLog(e.into()))
|
||||
}
|
||||
}
|
||||
pub use hv_cpuid_entry as CpuIdEntry;
|
||||
|
||||
pub const CPUID_FLAG_VALID_INDEX: u32 = 0;
|
||||
|
||||
@@ -8,14 +8,13 @@
|
||||
//
|
||||
//
|
||||
use crate::arch::x86::{msr_index, SegmentRegisterOps, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
///
|
||||
/// Export generically-named wrappers of mshv_bindings for Unix-based platforms
|
||||
///
|
||||
pub use {
|
||||
mshv_bindings::hv_cpuid_entry as CpuIdEntry,
|
||||
mshv_bindings::mshv_user_mem_region as MemoryRegion, mshv_bindings::msr_entry as MsrEntry,
|
||||
mshv_bindings::CpuId, mshv_bindings::DebugRegisters,
|
||||
mshv_bindings::FloatingPointUnit as FpuState, mshv_bindings::LapicState,
|
||||
@@ -26,8 +25,6 @@ pub use {
|
||||
mshv_bindings::Xcrs as ExtendedControlRegisters,
|
||||
};
|
||||
|
||||
pub const CPUID_FLAG_VALID_INDEX: u32 = 0;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct VcpuMshvState {
|
||||
pub msrs: MsrEntries,
|
||||
|
||||
@@ -12,15 +12,15 @@
|
||||
use crate::aarch64::VcpuInit;
|
||||
use crate::cpu::Vcpu;
|
||||
use crate::device::Device;
|
||||
#[cfg(feature = "kvm")]
|
||||
use crate::kvm::KvmVmState as VmState;
|
||||
#[cfg(feature = "mshv")]
|
||||
use crate::mshv::HvState as VmState;
|
||||
#[cfg(feature = "tdx")]
|
||||
use crate::x86_64::CpuId;
|
||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||
use crate::ClockData;
|
||||
use crate::CreateDevice;
|
||||
#[cfg(feature = "mshv")]
|
||||
use crate::HvState as VmState;
|
||||
#[cfg(feature = "kvm")]
|
||||
use crate::KvmVmState as VmState;
|
||||
use crate::{IoEventAddress, IrqRoutingEntry, MemoryRegion};
|
||||
#[cfg(feature = "kvm")]
|
||||
use kvm_ioctls::Cap;
|
||||
@@ -216,39 +216,6 @@ pub enum HypervisorVmError {
|
||||
///
|
||||
pub type Result<T> = std::result::Result<T, HypervisorVmError>;
|
||||
|
||||
/// Configuration data for legacy interrupts.
|
||||
///
|
||||
/// On x86 platforms, legacy interrupts means those interrupts routed through PICs or IOAPICs.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct LegacyIrqSourceConfig {
|
||||
pub irqchip: u32,
|
||||
pub pin: u32,
|
||||
}
|
||||
|
||||
/// Configuration data for MSI/MSI-X interrupts.
|
||||
///
|
||||
/// On x86 platforms, these interrupts are vectors delivered directly to the LAPIC.
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct MsiIrqSourceConfig {
|
||||
/// High address to delivery message signaled interrupt.
|
||||
pub high_addr: u32,
|
||||
/// Low address to delivery message signaled interrupt.
|
||||
pub low_addr: u32,
|
||||
/// Data to write to delivery message signaled interrupt.
|
||||
pub data: u32,
|
||||
/// Unique ID of the device to delivery message signaled interrupt.
|
||||
pub devid: u32,
|
||||
}
|
||||
|
||||
/// Configuration data for an interrupt source.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum InterruptSourceConfig {
|
||||
/// Configuration data for Legacy interrupts.
|
||||
LegacyIrq(LegacyIrqSourceConfig),
|
||||
/// Configuration data for PciMsi, PciMsix and generic MSI interrupts.
|
||||
MsiIrq(MsiIrqSourceConfig),
|
||||
}
|
||||
|
||||
///
|
||||
/// Trait to represent a Vm
|
||||
///
|
||||
@@ -268,7 +235,7 @@ pub trait Vm: Send + Sync {
|
||||
/// Unregister an event that will, when signaled, trigger the `gsi` IRQ.
|
||||
fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>;
|
||||
/// Creates a new KVM vCPU file descriptor and maps the memory corresponding
|
||||
fn create_vcpu(&self, id: u8, vm_ops: Option<Arc<dyn VmOps>>) -> Result<Arc<dyn Vcpu>>;
|
||||
fn create_vcpu(&self, id: u8, vmmops: Option<Arc<dyn VmmOps>>) -> Result<Arc<dyn Vcpu>>;
|
||||
/// Registers an event to be signaled whenever a certain address is written to.
|
||||
fn register_ioevent(
|
||||
&self,
|
||||
@@ -278,8 +245,6 @@ pub trait Vm: Send + Sync {
|
||||
) -> Result<()>;
|
||||
/// Unregister an event from a certain address it has been previously registered to.
|
||||
fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> Result<()>;
|
||||
// Construct a routing entry
|
||||
fn make_routing_entry(&self, gsi: u32, config: &InterruptSourceConfig) -> IrqRoutingEntry;
|
||||
/// Sets the GSI routing table entries, overwriting any previously set
|
||||
fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> Result<()>;
|
||||
/// Creates a memory region structure that can be used with {create/remove}_user_memory_region
|
||||
@@ -344,7 +309,7 @@ pub trait Vm: Send + Sync {
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
pub trait VmOps: Send + Sync {
|
||||
pub trait VmmOps: Send + Sync {
|
||||
fn guest_mem_write(&self, gpa: u64, buf: &[u8]) -> Result<usize>;
|
||||
fn guest_mem_read(&self, gpa: u64, buf: &mut [u8]) -> Result<usize>;
|
||||
fn mmio_read(&self, gpa: u64, data: &mut [u8]) -> Result<()>;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "net_gen"
|
||||
version = "0.1.0"
|
||||
authors = ["The Chromium OS Authors"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
@@ -2,25 +2,25 @@
|
||||
name = "net_util"
|
||||
version = "0.1.0"
|
||||
authors = ["The Chromium OS Authors"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
epoll = "4.3.1"
|
||||
getrandom = "0.2"
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.112"
|
||||
log = "0.4.14"
|
||||
net_gen = { path = "../net_gen" }
|
||||
rate_limiter = { path = "../rate_limiter" }
|
||||
serde = "1.0.137"
|
||||
serde = "1.0.133"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
virtio-bindings = "0.1.0"
|
||||
virtio-queue = "0.3.0"
|
||||
vm-memory = { version = "0.8.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
virtio-queue = { path = "../virtio-queue" }
|
||||
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
[dev-dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
pnet = "0.30.0"
|
||||
serde_json = "1.0.81"
|
||||
pnet = "0.29.0"
|
||||
serde_json = "1.0.75"
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
use crate::GuestMemoryMmap;
|
||||
use crate::Tap;
|
||||
use libc::c_uint;
|
||||
use std::sync::Arc;
|
||||
use virtio_bindings::bindings::virtio_net::{
|
||||
VIRTIO_NET_CTRL_GUEST_OFFLOADS, VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, VIRTIO_NET_CTRL_MQ,
|
||||
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN,
|
||||
@@ -15,7 +14,6 @@ use virtio_bindings::bindings::virtio_net::{
|
||||
};
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::{ByteValued, Bytes, GuestMemoryAtomic, GuestMemoryError};
|
||||
use vm_virtio::{AccessPlatform, Translatable};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
@@ -59,104 +57,86 @@ impl CtrlQueue {
|
||||
pub fn process(
|
||||
&mut self,
|
||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
||||
) -> Result<()> {
|
||||
) -> Result<bool> {
|
||||
let mut used_desc_heads = Vec::new();
|
||||
loop {
|
||||
for mut desc_chain in queue.iter().map_err(Error::QueueIterator)? {
|
||||
let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?;
|
||||
for mut desc_chain in queue.iter().map_err(Error::QueueIterator)? {
|
||||
let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?;
|
||||
|
||||
let ctrl_hdr: ControlHeader = desc_chain
|
||||
.memory()
|
||||
.read_obj(
|
||||
ctrl_desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, ctrl_desc.len() as usize),
|
||||
)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
let data_desc = desc_chain.next().ok_or(Error::NoDataDescriptor)?;
|
||||
let ctrl_hdr: ControlHeader = desc_chain
|
||||
.memory()
|
||||
.read_obj(ctrl_desc.addr())
|
||||
.map_err(Error::GuestMemory)?;
|
||||
let data_desc = desc_chain.next().ok_or(Error::NoDataDescriptor)?;
|
||||
let status_desc = desc_chain.next().ok_or(Error::NoStatusDescriptor)?;
|
||||
|
||||
let data_desc_addr = data_desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, data_desc.len() as usize);
|
||||
|
||||
let status_desc = desc_chain.next().ok_or(Error::NoStatusDescriptor)?;
|
||||
|
||||
let ok = match u32::from(ctrl_hdr.class) {
|
||||
VIRTIO_NET_CTRL_MQ => {
|
||||
let queue_pairs = desc_chain
|
||||
.memory()
|
||||
.read_obj::<u16>(data_desc_addr)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET {
|
||||
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
||||
false
|
||||
} else if (queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN as u16)
|
||||
|| (queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX as u16)
|
||||
{
|
||||
warn!("Number of MQ pairs out of range: {}", queue_pairs);
|
||||
false
|
||||
} else {
|
||||
info!("Number of MQ pairs requested: {}", queue_pairs);
|
||||
true
|
||||
}
|
||||
}
|
||||
VIRTIO_NET_CTRL_GUEST_OFFLOADS => {
|
||||
let features = desc_chain
|
||||
.memory()
|
||||
.read_obj::<u64>(data_desc_addr)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET {
|
||||
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
||||
false
|
||||
} else {
|
||||
let mut ok = true;
|
||||
for tap in self.taps.iter_mut() {
|
||||
info!("Reprogramming tap offload with features: {}", features);
|
||||
tap.set_offload(virtio_features_to_tap_offload(features))
|
||||
.map_err(|e| {
|
||||
error!("Error programming tap offload: {:?}", e);
|
||||
ok = false
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
ok
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
warn!("Unsupported command {:?}", ctrl_hdr);
|
||||
let ok = match u32::from(ctrl_hdr.class) {
|
||||
VIRTIO_NET_CTRL_MQ => {
|
||||
let queue_pairs = desc_chain
|
||||
.memory()
|
||||
.read_obj::<u16>(data_desc.addr())
|
||||
.map_err(Error::GuestMemory)?;
|
||||
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET {
|
||||
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
||||
false
|
||||
} else if (queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN as u16)
|
||||
|| (queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX as u16)
|
||||
{
|
||||
warn!("Number of MQ pairs out of range: {}", queue_pairs);
|
||||
false
|
||||
} else {
|
||||
info!("Number of MQ pairs requested: {}", queue_pairs);
|
||||
true
|
||||
}
|
||||
};
|
||||
}
|
||||
VIRTIO_NET_CTRL_GUEST_OFFLOADS => {
|
||||
let features = desc_chain
|
||||
.memory()
|
||||
.read_obj::<u64>(data_desc.addr())
|
||||
.map_err(Error::GuestMemory)?;
|
||||
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET {
|
||||
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
||||
false
|
||||
} else {
|
||||
let mut ok = true;
|
||||
for tap in self.taps.iter_mut() {
|
||||
info!("Reprogramming tap offload with features: {}", features);
|
||||
tap.set_offload(virtio_features_to_tap_offload(features))
|
||||
.map_err(|e| {
|
||||
error!("Error programming tap offload: {:?}", e);
|
||||
ok = false
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
ok
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
warn!("Unsupported command {:?}", ctrl_hdr);
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_obj(
|
||||
if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8,
|
||||
status_desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, status_desc.len() as usize),
|
||||
)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
let len = ctrl_desc.len() + data_desc.len() + status_desc.len();
|
||||
used_desc_heads.push((desc_chain.head_index(), len));
|
||||
}
|
||||
|
||||
for (desc_index, len) in used_desc_heads.iter() {
|
||||
queue
|
||||
.add_used(*desc_index, *len)
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
}
|
||||
|
||||
if !queue
|
||||
.enable_notification()
|
||||
.map_err(Error::QueueEnableNotification)?
|
||||
{
|
||||
break;
|
||||
}
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_obj(
|
||||
if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8,
|
||||
status_desc.addr(),
|
||||
)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
let len = ctrl_desc.len() + data_desc.len() + status_desc.len();
|
||||
used_desc_heads.push((desc_chain.head_index(), len));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
for (desc_index, len) in used_desc_heads.iter() {
|
||||
queue
|
||||
.add_used(*desc_index, *len)
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
queue
|
||||
.enable_notification()
|
||||
.map_err(Error::QueueEnableNotification)?;
|
||||
}
|
||||
|
||||
Ok(!used_desc_heads.is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ fn create_sockaddr(ip_addr: net::Ipv4Addr) -> net_gen::sockaddr {
|
||||
unsafe { mem::transmute(addr_in) }
|
||||
}
|
||||
|
||||
fn create_inet_socket() -> Result<net::UdpSocket> {
|
||||
fn create_socket() -> Result<net::UdpSocket> {
|
||||
// This is safe since we check the return value.
|
||||
let sock = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
|
||||
if sock < 0 {
|
||||
@@ -88,17 +88,6 @@ fn create_inet_socket() -> Result<net::UdpSocket> {
|
||||
Ok(unsafe { net::UdpSocket::from_raw_fd(sock) })
|
||||
}
|
||||
|
||||
fn create_unix_socket() -> Result<net::UdpSocket> {
|
||||
// This is safe since we check the return value.
|
||||
let sock = unsafe { libc::socket(libc::AF_UNIX, libc::SOCK_DGRAM, 0) };
|
||||
if sock < 0 {
|
||||
return Err(Error::CreateSocket(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
// This is safe; nothing else will use or hold onto the raw sock fd.
|
||||
Ok(unsafe { net::UdpSocket::from_raw_fd(sock) })
|
||||
}
|
||||
|
||||
fn vnet_hdr_len() -> usize {
|
||||
std::mem::size_of::<virtio_net_hdr_v1>()
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::{Bytes, GuestMemory, GuestMemoryAtomic};
|
||||
use vm_virtio::{AccessPlatform, Translatable};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TxVirtio {
|
||||
@@ -39,7 +38,6 @@ impl TxVirtio {
|
||||
tap: &mut Tap,
|
||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
rate_limiter: &mut Option<RateLimiter>,
|
||||
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
||||
) -> Result<bool, NetQueuePairError> {
|
||||
let mut retry_write = false;
|
||||
let mut rate_limit_reached = false;
|
||||
@@ -60,13 +58,10 @@ impl TxVirtio {
|
||||
|
||||
let mut iovecs = Vec::new();
|
||||
while let Some(desc) = next_desc {
|
||||
let desc_addr = desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, desc.len() as usize);
|
||||
if !desc.is_write_only() && desc.len() > 0 {
|
||||
let buf = desc_chain
|
||||
.memory()
|
||||
.get_slice(desc_addr, desc.len() as usize)
|
||||
.get_slice(desc.addr(), desc.len() as usize)
|
||||
.map_err(NetQueuePairError::GuestMemory)?
|
||||
.as_ptr();
|
||||
let iovec = libc::iovec {
|
||||
@@ -77,7 +72,7 @@ impl TxVirtio {
|
||||
} else {
|
||||
error!(
|
||||
"Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}",
|
||||
desc_addr.0,
|
||||
desc.addr().0,
|
||||
desc.len(),
|
||||
desc.is_write_only()
|
||||
);
|
||||
@@ -132,12 +127,9 @@ impl TxVirtio {
|
||||
queue
|
||||
.add_used(used_desc_head.0, used_desc_head.1)
|
||||
.map_err(NetQueuePairError::QueueAddUsed)?;
|
||||
if !queue
|
||||
queue
|
||||
.enable_notification()
|
||||
.map_err(NetQueuePairError::QueueEnableNotification)?
|
||||
{
|
||||
break;
|
||||
}
|
||||
.map_err(NetQueuePairError::QueueEnableNotification)?;
|
||||
}
|
||||
|
||||
Ok(retry_write)
|
||||
@@ -169,7 +161,6 @@ impl RxVirtio {
|
||||
tap: &mut Tap,
|
||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
rate_limiter: &mut Option<RateLimiter>,
|
||||
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
||||
) -> Result<bool, NetQueuePairError> {
|
||||
let mut exhausted_descs = true;
|
||||
let mut rate_limit_reached = false;
|
||||
@@ -190,26 +181,15 @@ impl RxVirtio {
|
||||
let desc = desc_chain
|
||||
.next()
|
||||
.ok_or(NetQueuePairError::DescriptorChainTooShort)?;
|
||||
|
||||
let num_buffers_addr = desc_chain
|
||||
.memory()
|
||||
.checked_offset(
|
||||
desc.addr()
|
||||
.translate_gva(access_platform, desc.len() as usize),
|
||||
10,
|
||||
)
|
||||
.unwrap();
|
||||
let num_buffers_addr = desc_chain.memory().checked_offset(desc.addr(), 10).unwrap();
|
||||
let mut next_desc = Some(desc);
|
||||
|
||||
let mut iovecs = Vec::new();
|
||||
while let Some(desc) = next_desc {
|
||||
let desc_addr = desc
|
||||
.addr()
|
||||
.translate_gva(access_platform, desc.len() as usize);
|
||||
if desc.is_write_only() && desc.len() > 0 {
|
||||
let buf = desc_chain
|
||||
.memory()
|
||||
.get_slice(desc_addr, desc.len() as usize)
|
||||
.get_slice(desc.addr(), desc.len() as usize)
|
||||
.map_err(NetQueuePairError::GuestMemory)?
|
||||
.as_ptr();
|
||||
let iovec = libc::iovec {
|
||||
@@ -220,7 +200,7 @@ impl RxVirtio {
|
||||
} else {
|
||||
error!(
|
||||
"Invalid descriptor chain: address = 0x{:x} length = {} write_only = {}",
|
||||
desc_addr.0,
|
||||
desc.addr().0,
|
||||
desc.len(),
|
||||
desc.is_write_only()
|
||||
);
|
||||
@@ -283,12 +263,9 @@ impl RxVirtio {
|
||||
queue
|
||||
.add_used(used_desc_head.0, used_desc_head.1)
|
||||
.map_err(NetQueuePairError::QueueAddUsed)?;
|
||||
if !queue
|
||||
queue
|
||||
.enable_notification()
|
||||
.map_err(NetQueuePairError::QueueEnableNotification)?
|
||||
{
|
||||
break;
|
||||
}
|
||||
.map_err(NetQueuePairError::QueueEnableNotification)?;
|
||||
}
|
||||
|
||||
Ok(exhausted_descs)
|
||||
@@ -349,7 +326,6 @@ pub struct NetQueuePair {
|
||||
pub rx_desc_avail: bool,
|
||||
pub rx_rate_limiter: Option<RateLimiter>,
|
||||
pub tx_rate_limiter: Option<RateLimiter>,
|
||||
pub access_platform: Option<Arc<dyn AccessPlatform>>,
|
||||
}
|
||||
|
||||
impl NetQueuePair {
|
||||
@@ -357,12 +333,9 @@ impl NetQueuePair {
|
||||
&mut self,
|
||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
) -> Result<bool, NetQueuePairError> {
|
||||
let tx_tap_retry = self.tx.process_desc_chain(
|
||||
&mut self.tap,
|
||||
queue,
|
||||
&mut self.tx_rate_limiter,
|
||||
self.access_platform.as_ref(),
|
||||
)?;
|
||||
let tx_tap_retry =
|
||||
self.tx
|
||||
.process_desc_chain(&mut self.tap, queue, &mut self.tx_rate_limiter)?;
|
||||
|
||||
// We got told to try again when writing to the tap. Wait for the TAP to be writable
|
||||
if tx_tap_retry && !self.tx_tap_listening {
|
||||
@@ -405,12 +378,10 @@ impl NetQueuePair {
|
||||
&mut self,
|
||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
) -> Result<bool, NetQueuePairError> {
|
||||
self.rx_desc_avail = !self.rx.process_desc_chain(
|
||||
&mut self.tap,
|
||||
queue,
|
||||
&mut self.rx_rate_limiter,
|
||||
self.access_platform.as_ref(),
|
||||
)?;
|
||||
self.rx_desc_avail =
|
||||
!self
|
||||
.rx
|
||||
.process_desc_chain(&mut self.tap, queue, &mut self.rx_rate_limiter)?;
|
||||
let rate_limit_reached = self
|
||||
.rx_rate_limiter
|
||||
.as_ref()
|
||||
|
||||
@@ -5,10 +5,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the THIRD-PARTY file.
|
||||
|
||||
use super::{
|
||||
create_inet_socket, create_sockaddr, create_unix_socket, vnet_hdr_len, Error as NetUtilError,
|
||||
MacAddr,
|
||||
};
|
||||
use super::{create_sockaddr, create_socket, vnet_hdr_len, Error as NetUtilError, MacAddr};
|
||||
use crate::mac::MAC_ADDR_LEN;
|
||||
use std::fs::File;
|
||||
use std::io::{Error as IoError, Read, Result as IoResult, Write};
|
||||
@@ -199,7 +196,7 @@ impl Tap {
|
||||
|
||||
/// Set the host-side IP address for the tap interface.
|
||||
pub fn set_ip_addr(&self, ip_addr: net::Ipv4Addr) -> Result<()> {
|
||||
let sock = create_inet_socket().map_err(Error::NetUtil)?;
|
||||
let sock = create_socket().map_err(Error::NetUtil)?;
|
||||
let addr = create_sockaddr(ip_addr);
|
||||
|
||||
let mut ifreq = self.get_ifreq();
|
||||
@@ -227,7 +224,7 @@ impl Tap {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let sock = create_unix_socket().map_err(Error::NetUtil)?;
|
||||
let sock = create_socket().map_err(Error::NetUtil)?;
|
||||
|
||||
let mut ifreq = self.get_ifreq();
|
||||
|
||||
@@ -257,7 +254,7 @@ impl Tap {
|
||||
|
||||
/// Get mac addr for tap interface.
|
||||
pub fn get_mac_addr(&self) -> Result<MacAddr> {
|
||||
let sock = create_unix_socket().map_err(Error::NetUtil)?;
|
||||
let sock = create_socket().map_err(Error::NetUtil)?;
|
||||
|
||||
let ifreq = self.get_ifreq();
|
||||
|
||||
@@ -278,7 +275,7 @@ impl Tap {
|
||||
|
||||
/// Set the netmask for the subnet that the tap interface will exist on.
|
||||
pub fn set_netmask(&self, netmask: net::Ipv4Addr) -> Result<()> {
|
||||
let sock = create_inet_socket().map_err(Error::NetUtil)?;
|
||||
let sock = create_socket().map_err(Error::NetUtil)?;
|
||||
let addr = create_sockaddr(netmask);
|
||||
|
||||
let mut ifreq = self.get_ifreq();
|
||||
@@ -309,7 +306,7 @@ impl Tap {
|
||||
|
||||
/// Enable the tap interface.
|
||||
pub fn enable(&self) -> Result<()> {
|
||||
let sock = create_unix_socket().map_err(Error::NetUtil)?;
|
||||
let sock = create_socket().map_err(Error::NetUtil)?;
|
||||
|
||||
let mut ifreq = self.get_ifreq();
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
name = "option_parser"
|
||||
version = "0.1.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
@@ -77,7 +77,7 @@ impl OptionParser {
|
||||
}
|
||||
|
||||
for option in split_commas_outside_brackets(input)?.iter() {
|
||||
let parts: Vec<&str> = option.splitn(2, '=').collect();
|
||||
let parts: Vec<&str> = option.split('=').collect();
|
||||
|
||||
match self.options.get_mut(parts[0]) {
|
||||
None => return Err(OptionParserError::UnknownOption(parts[0].to_owned())),
|
||||
@@ -313,7 +313,6 @@ impl<S: FromStr, T: TupleValue> FromStr for Tuple<S, T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct StringList(pub Vec<String>);
|
||||
|
||||
pub enum StringListParseError {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "pci"
|
||||
version = "0.1.0"
|
||||
authors = ["Samuel Ortiz <sameo@linux.intel.com>"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
@@ -10,21 +10,20 @@ kvm = ["vfio-ioctls/kvm"]
|
||||
mshv = ["vfio-ioctls/mshv"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
anyhow = "1.0.52"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio-ioctls", branch = "main", default-features = false }
|
||||
vfio_user = { path = "../vfio_user" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
serde = { version="1.0.137", features=["derive"] }
|
||||
thiserror = "1.0.31"
|
||||
libc = "0.2.112"
|
||||
log = "0.4.14"
|
||||
thiserror = "1.0.30"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = "0.8.0"
|
||||
vm-memory = "0.7.0"
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
|
||||
[dependencies.vfio-bindings]
|
||||
|
||||
@@ -6,13 +6,13 @@ use crate::configuration::{
|
||||
PciBarRegionType, PciBridgeSubclass, PciClassCode, PciConfiguration, PciHeaderType,
|
||||
};
|
||||
use crate::device::{DeviceRelocation, Error as PciDeviceError, PciDevice};
|
||||
use crate::PciBarConfiguration;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::DerefMut;
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use vm_device::{Bus, BusDevice};
|
||||
use vm_memory::{Address, GuestAddress, GuestUsize};
|
||||
|
||||
const VENDOR_ID_INTEL: u16 = 0x8086;
|
||||
const DEVICE_ID_INTEL_VIRT_PCIE_HOST: u16 = 0x0d57;
|
||||
@@ -88,10 +88,6 @@ impl PciDevice for PciRoot {
|
||||
fn as_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PciBus {
|
||||
@@ -122,21 +118,21 @@ impl PciBus {
|
||||
dev: Arc<Mutex<dyn BusDevice>>,
|
||||
#[cfg(target_arch = "x86_64")] io_bus: &Bus,
|
||||
mmio_bus: &Bus,
|
||||
bars: Vec<PciBarConfiguration>,
|
||||
bars: Vec<(GuestAddress, GuestUsize, PciBarRegionType)>,
|
||||
) -> Result<()> {
|
||||
for bar in bars {
|
||||
match bar.region_type() {
|
||||
for (address, size, type_) in bars {
|
||||
match type_ {
|
||||
PciBarRegionType::IoRegion => {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
io_bus
|
||||
.insert(dev.clone(), bar.addr(), bar.size())
|
||||
.insert(dev.clone(), address.raw_value(), size)
|
||||
.map_err(PciRootError::PioInsert)?;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
error!("I/O region is not supported");
|
||||
}
|
||||
PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => {
|
||||
mmio_bus
|
||||
.insert(dev.clone(), bar.addr(), bar.size())
|
||||
.insert(dev.clone(), address.raw_value(), size)
|
||||
.map_err(PciRootError::MmioInsert)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ use std::fmt::{self, Display};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use versionize::{VersionMap, Versionize, VersionizeError, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use vm_device::PciBarType;
|
||||
use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable, VersionMapped};
|
||||
|
||||
// The number of 32bit registers in the config space, 4096 bytes.
|
||||
@@ -19,12 +18,9 @@ const STATUS_REG: usize = 1;
|
||||
const STATUS_REG_CAPABILITIES_USED_MASK: u32 = 0x0010_0000;
|
||||
const BAR0_REG: usize = 4;
|
||||
const ROM_BAR_REG: usize = 12;
|
||||
const ROM_BAR_IDX: usize = 6;
|
||||
const BAR_IO_ADDR_MASK: u32 = 0xffff_fffc;
|
||||
const BAR_MEM_ADDR_MASK: u32 = 0xffff_fff0;
|
||||
const ROM_BAR_ADDR_MASK: u32 = 0xffff_f800;
|
||||
const MSI_CAPABILITY_REGISTER_MASK: u32 = 0x0071_0000;
|
||||
const MSIX_CAPABILITY_REGISTER_MASK: u32 = 0xc000_0000;
|
||||
const NUM_BAR_REGS: usize = 6;
|
||||
const CAPABILITY_LIST_HEAD_OFFSET: usize = 0x34;
|
||||
const FIRST_CAPABILITY_OFFSET: usize = 0x40;
|
||||
@@ -328,48 +324,17 @@ pub enum PciBarRegionType {
|
||||
Memory64BitRegion = 0x04,
|
||||
}
|
||||
|
||||
impl From<PciBarType> for PciBarRegionType {
|
||||
fn from(type_: PciBarType) -> Self {
|
||||
match type_ {
|
||||
PciBarType::Io => PciBarRegionType::IoRegion,
|
||||
PciBarType::Mmio32 => PciBarRegionType::Memory32BitRegion,
|
||||
PciBarType::Mmio64 => PciBarRegionType::Memory64BitRegion,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::from_over_into)]
|
||||
impl Into<PciBarType> for PciBarRegionType {
|
||||
fn into(self) -> PciBarType {
|
||||
match self {
|
||||
PciBarRegionType::IoRegion => PciBarType::Io,
|
||||
PciBarRegionType::Memory32BitRegion => PciBarType::Mmio32,
|
||||
PciBarRegionType::Memory64BitRegion => PciBarType::Mmio64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum PciBarPrefetchable {
|
||||
NotPrefetchable = 0,
|
||||
Prefetchable = 0x08,
|
||||
}
|
||||
|
||||
#[allow(clippy::from_over_into)]
|
||||
impl Into<bool> for PciBarPrefetchable {
|
||||
fn into(self) -> bool {
|
||||
match self {
|
||||
PciBarPrefetchable::NotPrefetchable => false,
|
||||
PciBarPrefetchable::Prefetchable => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PciBarConfiguration {
|
||||
addr: u64,
|
||||
size: u64,
|
||||
idx: usize,
|
||||
reg_idx: usize,
|
||||
region_type: PciBarRegionType,
|
||||
prefetchable: PciBarPrefetchable,
|
||||
}
|
||||
@@ -589,23 +554,22 @@ impl PciConfiguration {
|
||||
|
||||
/// Adds a region specified by `config`. Configures the specified BAR(s) to
|
||||
/// report this region and size to the guest kernel. Enforces a few constraints
|
||||
/// (i.e, region size must be power of two, register not already used).
|
||||
pub fn add_pci_bar(&mut self, config: &PciBarConfiguration) -> Result<()> {
|
||||
let bar_idx = config.idx;
|
||||
let reg_idx = BAR0_REG + bar_idx;
|
||||
|
||||
if self.bars[bar_idx].used {
|
||||
return Err(Error::BarInUse(bar_idx));
|
||||
/// (i.e, region size must be power of two, register not already used). Returns 'None' on
|
||||
/// failure all, `Some(BarIndex)` on success.
|
||||
pub fn add_pci_bar(&mut self, config: &PciBarConfiguration) -> Result<usize> {
|
||||
if self.bars[config.reg_idx].used {
|
||||
return Err(Error::BarInUse(config.reg_idx));
|
||||
}
|
||||
|
||||
if config.size.count_ones() != 1 {
|
||||
return Err(Error::BarSizeInvalid(config.size));
|
||||
}
|
||||
|
||||
if bar_idx >= NUM_BAR_REGS {
|
||||
return Err(Error::BarInvalid(bar_idx));
|
||||
if config.reg_idx >= NUM_BAR_REGS {
|
||||
return Err(Error::BarInvalid(config.reg_idx));
|
||||
}
|
||||
|
||||
let bar_idx = BAR0_REG + config.reg_idx;
|
||||
let end_addr = config
|
||||
.addr
|
||||
.checked_add(config.size - 1)
|
||||
@@ -618,20 +582,20 @@ impl PciConfiguration {
|
||||
|
||||
// Encode the BAR size as expected by the software running in
|
||||
// the guest.
|
||||
self.bars[bar_idx].size =
|
||||
self.bars[config.reg_idx].size =
|
||||
encode_32_bits_bar_size(config.size as u32).ok_or(Error::Encode32BarSize)?;
|
||||
}
|
||||
PciBarRegionType::Memory64BitRegion => {
|
||||
if bar_idx + 1 >= NUM_BAR_REGS {
|
||||
return Err(Error::BarInvalid64(bar_idx));
|
||||
if config.reg_idx + 1 >= NUM_BAR_REGS {
|
||||
return Err(Error::BarInvalid64(config.reg_idx));
|
||||
}
|
||||
|
||||
if end_addr > u64::max_value() {
|
||||
return Err(Error::BarAddressInvalid(config.addr, config.size));
|
||||
}
|
||||
|
||||
if self.bars[bar_idx + 1].used {
|
||||
return Err(Error::BarInUse64(bar_idx));
|
||||
if self.bars[config.reg_idx + 1].used {
|
||||
return Err(Error::BarInUse64(config.reg_idx));
|
||||
}
|
||||
|
||||
// Encode the BAR size as expected by the software running in
|
||||
@@ -639,12 +603,12 @@ impl PciConfiguration {
|
||||
let (bar_size_hi, bar_size_lo) =
|
||||
encode_64_bits_bar_size(config.size).ok_or(Error::Encode64BarSize)?;
|
||||
|
||||
self.registers[reg_idx + 1] = (config.addr >> 32) as u32;
|
||||
self.writable_bits[reg_idx + 1] = 0xffff_ffff;
|
||||
self.bars[bar_idx + 1].addr = self.registers[reg_idx + 1];
|
||||
self.bars[bar_idx].size = bar_size_lo;
|
||||
self.bars[bar_idx + 1].size = bar_size_hi;
|
||||
self.bars[bar_idx + 1].used = true;
|
||||
self.registers[bar_idx + 1] = (config.addr >> 32) as u32;
|
||||
self.writable_bits[bar_idx + 1] = 0xffff_ffff;
|
||||
self.bars[config.reg_idx + 1].addr = self.registers[bar_idx + 1];
|
||||
self.bars[config.reg_idx].size = bar_size_lo;
|
||||
self.bars[config.reg_idx + 1].size = bar_size_hi;
|
||||
self.bars[config.reg_idx + 1].used = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,30 +620,26 @@ impl PciConfiguration {
|
||||
PciBarRegionType::IoRegion => (BAR_IO_ADDR_MASK, config.region_type as u32),
|
||||
};
|
||||
|
||||
self.registers[reg_idx] = ((config.addr as u32) & mask) | lower_bits;
|
||||
self.writable_bits[reg_idx] = mask;
|
||||
self.bars[bar_idx].addr = self.registers[reg_idx];
|
||||
self.bars[bar_idx].used = true;
|
||||
self.bars[bar_idx].r#type = Some(config.region_type);
|
||||
|
||||
Ok(())
|
||||
self.registers[bar_idx] = ((config.addr as u32) & mask) | lower_bits;
|
||||
self.writable_bits[bar_idx] = mask;
|
||||
self.bars[config.reg_idx].addr = self.registers[bar_idx];
|
||||
self.bars[config.reg_idx].used = true;
|
||||
self.bars[config.reg_idx].r#type = Some(config.region_type);
|
||||
Ok(config.reg_idx)
|
||||
}
|
||||
|
||||
/// Adds rom expansion BAR.
|
||||
pub fn add_pci_rom_bar(&mut self, config: &PciBarConfiguration, active: u32) -> Result<()> {
|
||||
let bar_idx = config.idx;
|
||||
let reg_idx = ROM_BAR_REG;
|
||||
|
||||
pub fn add_pci_rom_bar(&mut self, config: &PciBarConfiguration, active: u32) -> Result<usize> {
|
||||
if self.rom_bar_used {
|
||||
return Err(Error::RomBarInUse(bar_idx));
|
||||
return Err(Error::RomBarInUse(config.reg_idx));
|
||||
}
|
||||
|
||||
if config.size.count_ones() != 1 {
|
||||
return Err(Error::RomBarSizeInvalid(config.size));
|
||||
}
|
||||
|
||||
if bar_idx != ROM_BAR_IDX {
|
||||
return Err(Error::RomBarInvalid(bar_idx));
|
||||
if config.reg_idx != ROM_BAR_REG {
|
||||
return Err(Error::RomBarInvalid(config.reg_idx));
|
||||
}
|
||||
|
||||
let end_addr = config
|
||||
@@ -691,14 +651,13 @@ impl PciConfiguration {
|
||||
return Err(Error::RomBarAddressInvalid(config.addr, config.size));
|
||||
}
|
||||
|
||||
self.registers[reg_idx] = (config.addr as u32) | active;
|
||||
self.writable_bits[reg_idx] = ROM_BAR_ADDR_MASK;
|
||||
self.rom_bar_addr = self.registers[reg_idx];
|
||||
self.registers[config.reg_idx] = (config.addr as u32) | active;
|
||||
self.writable_bits[config.reg_idx] = ROM_BAR_ADDR_MASK;
|
||||
self.rom_bar_addr = self.registers[config.reg_idx];
|
||||
self.rom_bar_size =
|
||||
encode_32_bits_bar_size(config.size as u32).ok_or(Error::Encode32BarSize)?;
|
||||
self.rom_bar_used = true;
|
||||
|
||||
Ok(())
|
||||
Ok(config.reg_idx)
|
||||
}
|
||||
|
||||
/// Returns the address of the given BAR region.
|
||||
@@ -755,15 +714,8 @@ impl PciConfiguration {
|
||||
}
|
||||
self.last_capability = Some((cap_offset, total_len));
|
||||
|
||||
match cap_data.id() {
|
||||
PciCapabilityId::MessageSignalledInterrupts => {
|
||||
self.writable_bits[cap_offset / 4] = MSI_CAPABILITY_REGISTER_MASK;
|
||||
}
|
||||
PciCapabilityId::MsiX => {
|
||||
self.msix_cap_reg_idx = Some(cap_offset / 4);
|
||||
self.writable_bits[self.msix_cap_reg_idx.unwrap()] = MSIX_CAPABILITY_REGISTER_MASK;
|
||||
}
|
||||
_ => {}
|
||||
if cap_data.id() == PciCapabilityId::MsiX {
|
||||
self.msix_cap_reg_idx = Some(cap_offset / 4);
|
||||
}
|
||||
|
||||
Ok(cap_offset)
|
||||
@@ -950,7 +902,7 @@ impl Snapshottable for PciConfiguration {
|
||||
impl Default for PciBarConfiguration {
|
||||
fn default() -> Self {
|
||||
PciBarConfiguration {
|
||||
idx: 0,
|
||||
reg_idx: 0,
|
||||
addr: 0,
|
||||
size: 0,
|
||||
region_type: PciBarRegionType::Memory64BitRegion,
|
||||
@@ -961,13 +913,13 @@ impl Default for PciBarConfiguration {
|
||||
|
||||
impl PciBarConfiguration {
|
||||
pub fn new(
|
||||
idx: usize,
|
||||
reg_idx: usize,
|
||||
size: u64,
|
||||
region_type: PciBarRegionType,
|
||||
prefetchable: PciBarPrefetchable,
|
||||
) -> Self {
|
||||
PciBarConfiguration {
|
||||
idx,
|
||||
reg_idx,
|
||||
addr: 0,
|
||||
size,
|
||||
region_type,
|
||||
@@ -976,8 +928,8 @@ impl PciBarConfiguration {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn set_index(mut self, idx: usize) -> Self {
|
||||
self.idx = idx;
|
||||
pub fn set_register_index(mut self, reg_idx: usize) -> Self {
|
||||
self.reg_idx = reg_idx;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -993,31 +945,15 @@ impl PciBarConfiguration {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> u64 {
|
||||
self.size
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn set_region_type(mut self, region_type: PciBarRegionType) -> Self {
|
||||
self.region_type = region_type;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn idx(&self) -> usize {
|
||||
self.idx
|
||||
}
|
||||
|
||||
pub fn addr(&self) -> u64 {
|
||||
self.addr
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u64 {
|
||||
self.size
|
||||
}
|
||||
|
||||
pub fn region_type(&self) -> PciBarRegionType {
|
||||
self.region_type
|
||||
}
|
||||
|
||||
pub fn prefetchable(&self) -> PciBarPrefetchable {
|
||||
self.prefetchable
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
// found in the LICENSE-BSD-3-Clause file.
|
||||
|
||||
use crate::configuration::{self, PciBarRegionType};
|
||||
use crate::PciBarConfiguration;
|
||||
use std::any::Any;
|
||||
use std::fmt::{self, Display};
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use std::{self, io, result};
|
||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||
use vm_device::{BusDevice, Resource};
|
||||
use vm_device::BusDevice;
|
||||
use vm_memory::{GuestAddress, GuestUsize};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
@@ -19,8 +19,6 @@ pub enum Error {
|
||||
IoAllocationFailed(u64),
|
||||
/// Registering an IO BAR failed.
|
||||
IoRegistrationFailed(u64, configuration::Error),
|
||||
/// Expected resource not found.
|
||||
MissingResource,
|
||||
}
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
@@ -36,7 +34,6 @@ impl Display for Error {
|
||||
IoRegistrationFailed(addr, e) => {
|
||||
write!(f, "failed to register an IO BAR, addr={} err={}", addr, e)
|
||||
}
|
||||
MissingResource => write!(f, "failed to find expected resource"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,8 +53,7 @@ pub trait PciDevice: BusDevice {
|
||||
&mut self,
|
||||
_allocator: &Arc<Mutex<SystemAllocator>>,
|
||||
_mmio_allocator: &mut AddressAllocator,
|
||||
_resources: Option<Vec<Resource>>,
|
||||
) -> Result<Vec<PciBarConfiguration>> {
|
||||
) -> Result<Vec<(GuestAddress, GuestUsize, PciBarRegionType)>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
@@ -107,9 +103,6 @@ pub trait PciDevice: BusDevice {
|
||||
/// Provides a mutable reference to the Any trait. This is useful to let
|
||||
/// the caller have access to the underlying type behind the trait.
|
||||
fn as_any(&mut self) -> &mut dyn Any;
|
||||
|
||||
/// Optionally returns a unique identifier.
|
||||
fn id(&self) -> Option<String>;
|
||||
}
|
||||
|
||||
/// This trait defines a set of functions which can be triggered whenever a
|
||||
|
||||
@@ -27,10 +27,7 @@ pub use self::msi::{msi_num_enabled_vectors, MsiCap, MsiConfig};
|
||||
pub use self::msix::{MsixCap, MsixConfig, MsixTableEntry, MSIX_TABLE_ENTRY_SIZE};
|
||||
pub use self::vfio::{VfioPciDevice, VfioPciError};
|
||||
pub use self::vfio_user::{VfioUserDmaMapping, VfioUserPciDevice, VfioUserPciDeviceError};
|
||||
use serde::de::Visitor;
|
||||
use std::fmt::{self, Display};
|
||||
use std::num::ParseIntError;
|
||||
use std::str::FromStr;
|
||||
use std::fmt::Display;
|
||||
|
||||
/// PCI has four interrupt pins A->D.
|
||||
#[derive(Copy, Clone)]
|
||||
@@ -55,41 +52,6 @@ pub const PCI_CONFIG_IO_PORT_SIZE: u64 = 0x8;
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub struct PciBdf(u32);
|
||||
|
||||
struct PciBdfVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for PciBdfVisitor {
|
||||
type Value = PciBdf;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("struct PciBdf")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(v.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for PciBdf {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_str(PciBdfVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for PciBdf {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.collect_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl PciBdf {
|
||||
pub fn segment(&self) -> u16 {
|
||||
((self.0 >> 16) & 0xffff) as u16
|
||||
@@ -159,25 +121,3 @@ impl Display for PciBdf {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PciBdf {
|
||||
type Err = ParseIntError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let items: Vec<&str> = s.split('.').collect();
|
||||
assert_eq!(items.len(), 2);
|
||||
let function = u8::from_str_radix(items[1], 16)?;
|
||||
let items: Vec<&str> = items[0].split(':').collect();
|
||||
assert_eq!(items.len(), 3);
|
||||
let segment = u16::from_str_radix(items[0], 16)?;
|
||||
let bus = u8::from_str_radix(items[1], 16)?;
|
||||
let device = u8::from_str_radix(items[2], 16)?;
|
||||
Ok(PciBdf::new(segment, bus, device, function))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for PciBdf {
|
||||
fn from(bdf: &str) -> Self {
|
||||
Self::from_str(bdf).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,17 +3,11 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
|
||||
//
|
||||
|
||||
use anyhow::anyhow;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptSourceConfig, InterruptSourceGroup, MsiIrqSourceConfig,
|
||||
};
|
||||
use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable, VersionMapped};
|
||||
|
||||
// MSI control masks
|
||||
const MSI_CTL_ENABLE: u16 = 0x1;
|
||||
@@ -38,15 +32,7 @@ pub fn msi_num_enabled_vectors(msg_ctl: u16) -> usize {
|
||||
1 << field
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum Error {
|
||||
#[error("Failed enabling the interrupt route: {0}")]
|
||||
EnableInterruptRoute(io::Error),
|
||||
#[error("Failed updating the interrupt route: {0}")]
|
||||
UpdateInterruptRoute(io::Error),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, Versionize)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct MsiCap {
|
||||
// Message Control Register
|
||||
// 0: MSI enable.
|
||||
@@ -171,13 +157,6 @@ impl MsiCap {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Versionize)]
|
||||
struct MsiConfigState {
|
||||
cap: MsiCap,
|
||||
}
|
||||
|
||||
impl VersionMapped for MsiConfigState {}
|
||||
|
||||
pub struct MsiConfig {
|
||||
cap: MsiCap,
|
||||
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
|
||||
@@ -196,39 +175,6 @@ impl MsiConfig {
|
||||
}
|
||||
}
|
||||
|
||||
fn state(&self) -> MsiConfigState {
|
||||
MsiConfigState { cap: self.cap }
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &MsiConfigState) -> Result<(), Error> {
|
||||
self.cap = state.cap;
|
||||
|
||||
if self.enabled() {
|
||||
for idx in 0..self.num_enabled_vectors() {
|
||||
let config = MsiIrqSourceConfig {
|
||||
high_addr: self.cap.msg_addr_hi,
|
||||
low_addr: self.cap.msg_addr_lo,
|
||||
data: self.cap.msg_data as u32,
|
||||
devid: 0,
|
||||
};
|
||||
|
||||
self.interrupt_source_group
|
||||
.update(
|
||||
idx as InterruptIndex,
|
||||
InterruptSourceConfig::MsiIrq(config),
|
||||
self.cap.vector_masked(idx),
|
||||
)
|
||||
.map_err(Error::UpdateInterruptRoute)?;
|
||||
}
|
||||
|
||||
self.interrupt_source_group
|
||||
.enable()
|
||||
.map_err(Error::EnableInterruptRoute)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn enabled(&self) -> bool {
|
||||
self.cap.enabled()
|
||||
}
|
||||
@@ -255,13 +201,20 @@ impl MsiConfig {
|
||||
devid: 0,
|
||||
};
|
||||
|
||||
if let Err(e) = self.interrupt_source_group.update(
|
||||
idx as InterruptIndex,
|
||||
InterruptSourceConfig::MsiIrq(config),
|
||||
self.cap.vector_masked(idx),
|
||||
) {
|
||||
if let Err(e) = self
|
||||
.interrupt_source_group
|
||||
.update(idx as InterruptIndex, InterruptSourceConfig::MsiIrq(config))
|
||||
{
|
||||
error!("Failed updating vector: {:?}", e);
|
||||
}
|
||||
|
||||
if self.cap.vector_masked(idx) {
|
||||
if let Err(e) = self.interrupt_source_group.mask(idx as InterruptIndex) {
|
||||
error!("Failed masking vector: {:?}", e);
|
||||
}
|
||||
} else if let Err(e) = self.interrupt_source_group.unmask(idx as InterruptIndex) {
|
||||
error!("Failed unmasking vector: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
if !old_enabled {
|
||||
@@ -276,26 +229,3 @@ impl MsiConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Pausable for MsiConfig {}
|
||||
|
||||
impl Snapshottable for MsiConfig {
|
||||
fn id(&self) -> String {
|
||||
String::from("msi_config")
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
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
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,11 +132,7 @@ impl MsixConfig {
|
||||
};
|
||||
|
||||
self.interrupt_source_group
|
||||
.update(
|
||||
idx as InterruptIndex,
|
||||
InterruptSourceConfig::MsiIrq(config),
|
||||
self.masked,
|
||||
)
|
||||
.update(idx as InterruptIndex, InterruptSourceConfig::MsiIrq(config))
|
||||
.map_err(Error::UpdateInterruptRoute)?;
|
||||
|
||||
self.interrupt_source_group
|
||||
@@ -166,7 +162,6 @@ impl MsixConfig {
|
||||
// Update interrupt routing
|
||||
if old_masked != self.masked || old_enabled != self.enabled {
|
||||
if self.enabled && !self.masked {
|
||||
debug!("MSI-X enabled for device 0x{:x}", self.devid);
|
||||
for (idx, table_entry) in self.table_entries.iter().enumerate() {
|
||||
let config = MsiIrqSourceConfig {
|
||||
high_addr: table_entry.msg_addr_hi,
|
||||
@@ -175,16 +170,23 @@ impl MsixConfig {
|
||||
devid: self.devid,
|
||||
};
|
||||
|
||||
if let Err(e) = self.interrupt_source_group.update(
|
||||
idx as InterruptIndex,
|
||||
InterruptSourceConfig::MsiIrq(config),
|
||||
table_entry.masked(),
|
||||
) {
|
||||
if let Err(e) = self
|
||||
.interrupt_source_group
|
||||
.update(idx as InterruptIndex, InterruptSourceConfig::MsiIrq(config))
|
||||
{
|
||||
error!("Failed updating vector: {:?}", e);
|
||||
}
|
||||
|
||||
if table_entry.masked() {
|
||||
if let Err(e) = self.interrupt_source_group.mask(idx as InterruptIndex) {
|
||||
error!("Failed masking vector: {:?}", e);
|
||||
}
|
||||
} else if let Err(e) = self.interrupt_source_group.unmask(idx as InterruptIndex)
|
||||
{
|
||||
error!("Failed unmasking vector: {:?}", e);
|
||||
}
|
||||
}
|
||||
} else if old_enabled || !old_masked {
|
||||
debug!("MSI-X disabled for device 0x{:x}", self.devid);
|
||||
if let Err(e) = self.interrupt_source_group.disable() {
|
||||
error!("Failed disabling irq_fd: {:?}", e);
|
||||
}
|
||||
@@ -310,10 +312,17 @@ impl MsixConfig {
|
||||
if let Err(e) = self.interrupt_source_group.update(
|
||||
index as InterruptIndex,
|
||||
InterruptSourceConfig::MsiIrq(config),
|
||||
table_entry.masked(),
|
||||
) {
|
||||
error!("Failed updating vector: {:?}", e);
|
||||
}
|
||||
|
||||
if table_entry.masked() {
|
||||
if let Err(e) = self.interrupt_source_group.mask(index as InterruptIndex) {
|
||||
error!("Failed masking vector: {:?}", e);
|
||||
}
|
||||
} else if let Err(e) = self.interrupt_source_group.unmask(index as InterruptIndex) {
|
||||
error!("Failed unmasking vector: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// After the MSI-X table entry has been updated, it is necessary to
|
||||
|
||||
684
pci/src/vfio.rs
684
pci/src/vfio.rs
File diff suppressed because it is too large
Load Diff
@@ -3,8 +3,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use crate::vfio::{Interrupt, UserMemoryRegion, Vfio, VfioCommon, VfioError};
|
||||
use crate::{BarReprogrammingParams, PciBarConfiguration, VfioPciError};
|
||||
use crate::vfio::{Interrupt, Vfio, VfioCommon, VfioError};
|
||||
use crate::{BarReprogrammingParams, PciBarRegionType, VfioPciError};
|
||||
use crate::{
|
||||
PciBdf, PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciSubclass,
|
||||
};
|
||||
@@ -21,17 +21,18 @@ use vfio_user::{Client, Error as VfioUserError};
|
||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||
use vm_device::dma_mapping::ExternalDmaMapping;
|
||||
use vm_device::interrupt::{InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig};
|
||||
use vm_device::{BusDevice, Resource};
|
||||
use vm_device::BusDevice;
|
||||
use vm_memory::bitmap::AtomicBitmap;
|
||||
use vm_memory::{
|
||||
Address, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryRegion, GuestRegionMmap,
|
||||
GuestUsize,
|
||||
};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
pub struct VfioUserPciDevice {
|
||||
id: String,
|
||||
vm: Arc<dyn hypervisor::Vm>,
|
||||
client: Arc<Mutex<Client>>,
|
||||
vfio_wrapper: VfioUserClientWrapper,
|
||||
common: VfioCommon,
|
||||
}
|
||||
|
||||
@@ -62,10 +63,9 @@ impl PciSubclass for PciVfioUserSubclass {
|
||||
|
||||
impl VfioUserPciDevice {
|
||||
pub fn new(
|
||||
id: String,
|
||||
vm: &Arc<dyn hypervisor::Vm>,
|
||||
client: Arc<Mutex<Client>>,
|
||||
msi_interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
msi_interrupt_manager: &Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
legacy_interrupt_group: Option<Arc<dyn InterruptSourceGroup>>,
|
||||
bdf: PciBdf,
|
||||
) -> Result<Self, VfioUserPciDeviceError> {
|
||||
@@ -103,20 +103,17 @@ impl VfioUserPciDevice {
|
||||
msi: None,
|
||||
msix: None,
|
||||
},
|
||||
msi_interrupt_manager,
|
||||
legacy_interrupt_group,
|
||||
vfio_wrapper: Arc::new(vfio_wrapper) as Arc<dyn Vfio>,
|
||||
};
|
||||
|
||||
common.parse_capabilities(bdf);
|
||||
common.parse_capabilities(msi_interrupt_manager, &vfio_wrapper, bdf);
|
||||
common
|
||||
.initialize_legacy_interrupt()
|
||||
.initialize_legacy_interrupt(legacy_interrupt_group, &vfio_wrapper)
|
||||
.map_err(VfioUserPciDeviceError::InitializeLegacyInterrupts)?;
|
||||
|
||||
Ok(Self {
|
||||
id,
|
||||
vm: vm.clone(),
|
||||
client,
|
||||
vfio_wrapper,
|
||||
common,
|
||||
})
|
||||
}
|
||||
@@ -146,15 +143,6 @@ impl VfioUserPciDevice {
|
||||
.file_offset
|
||||
.clone();
|
||||
|
||||
let sparse_areas = self
|
||||
.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.region(mmio_region.index)
|
||||
.unwrap()
|
||||
.sparse_areas
|
||||
.clone();
|
||||
|
||||
if region_flags & VFIO_REGION_INFO_FLAG_MMAP != 0 {
|
||||
let mut prot = 0;
|
||||
if region_flags & VFIO_REGION_INFO_FLAG_READ != 0 {
|
||||
@@ -164,57 +152,41 @@ impl VfioUserPciDevice {
|
||||
prot |= libc::PROT_WRITE;
|
||||
}
|
||||
|
||||
let mmaps = if sparse_areas.is_empty() {
|
||||
vec![vfio_region_sparse_mmap_area {
|
||||
offset: 0,
|
||||
size: mmio_region.length,
|
||||
}]
|
||||
} else {
|
||||
sparse_areas
|
||||
let host_addr = unsafe {
|
||||
libc::mmap(
|
||||
null_mut(),
|
||||
mmio_region.length as usize,
|
||||
prot,
|
||||
libc::MAP_SHARED,
|
||||
file_offset.as_ref().unwrap().file().as_raw_fd(),
|
||||
file_offset.as_ref().unwrap().start() as libc::off_t,
|
||||
)
|
||||
};
|
||||
|
||||
for s in mmaps.iter() {
|
||||
let host_addr = unsafe {
|
||||
libc::mmap(
|
||||
null_mut(),
|
||||
s.size as usize,
|
||||
prot,
|
||||
libc::MAP_SHARED,
|
||||
file_offset.as_ref().unwrap().file().as_raw_fd(),
|
||||
file_offset.as_ref().unwrap().start() as libc::off_t
|
||||
+ s.offset as libc::off_t,
|
||||
)
|
||||
};
|
||||
|
||||
if host_addr == libc::MAP_FAILED {
|
||||
error!(
|
||||
"Could not mmap regions, error:{}",
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let user_memory_region = UserMemoryRegion {
|
||||
slot: mem_slot(),
|
||||
start: mmio_region.start.0 + s.offset,
|
||||
size: s.size,
|
||||
host_addr: host_addr as u64,
|
||||
};
|
||||
|
||||
mmio_region.user_memory_regions.push(user_memory_region);
|
||||
|
||||
let mem_region = vm.make_user_memory_region(
|
||||
user_memory_region.slot,
|
||||
user_memory_region.start,
|
||||
user_memory_region.size,
|
||||
user_memory_region.host_addr,
|
||||
false,
|
||||
false,
|
||||
if host_addr == libc::MAP_FAILED {
|
||||
error!(
|
||||
"Could not mmap regions, error:{}",
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
|
||||
vm.create_user_memory_region(mem_region)
|
||||
.map_err(VfioUserPciDeviceError::MapRegionGuest)?;
|
||||
continue;
|
||||
}
|
||||
|
||||
let slot = mem_slot();
|
||||
let mem_region = vm.make_user_memory_region(
|
||||
slot,
|
||||
mmio_region.start.0,
|
||||
mmio_region.length as u64,
|
||||
host_addr as u64,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
vm.create_user_memory_region(mem_region)
|
||||
.map_err(VfioUserPciDeviceError::MapRegionGuest)?;
|
||||
|
||||
mmio_region.mem_slot = Some(slot);
|
||||
mmio_region.host_addr = Some(host_addr as u64);
|
||||
mmio_region.mmap_size = Some(mmio_region.length as usize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,13 +195,17 @@ impl VfioUserPciDevice {
|
||||
|
||||
pub fn unmap_mmio_regions(&mut self) {
|
||||
for mmio_region in self.common.mmio_regions.iter() {
|
||||
for user_memory_region in mmio_region.user_memory_regions.iter() {
|
||||
if let (Some(host_addr), Some(mmap_size), Some(mem_slot)) = (
|
||||
mmio_region.host_addr,
|
||||
mmio_region.mmap_size,
|
||||
mmio_region.mem_slot,
|
||||
) {
|
||||
// Remove region
|
||||
let r = self.vm.make_user_memory_region(
|
||||
user_memory_region.slot,
|
||||
user_memory_region.start,
|
||||
user_memory_region.size,
|
||||
user_memory_region.host_addr,
|
||||
mem_slot,
|
||||
mmio_region.start.raw_value(),
|
||||
mmap_size as u64,
|
||||
host_addr as u64,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
@@ -238,13 +214,7 @@ impl VfioUserPciDevice {
|
||||
error!("Could not remove the userspace memory region: {}", e);
|
||||
}
|
||||
|
||||
// Remove mmaps
|
||||
let ret = unsafe {
|
||||
libc::munmap(
|
||||
user_memory_region.host_addr as *mut libc::c_void,
|
||||
user_memory_region.size as usize,
|
||||
)
|
||||
};
|
||||
let ret = unsafe { libc::munmap(host_addr as *mut libc::c_void, mmap_size) };
|
||||
if ret != 0 {
|
||||
error!(
|
||||
"Could not unmap region {}, error:{}",
|
||||
@@ -422,10 +392,9 @@ impl PciDevice for VfioUserPciDevice {
|
||||
&mut self,
|
||||
allocator: &Arc<Mutex<SystemAllocator>>,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
resources: Option<Vec<Resource>>,
|
||||
) -> Result<Vec<PciBarConfiguration>, PciDeviceError> {
|
||||
) -> Result<Vec<(GuestAddress, GuestUsize, PciBarRegionType)>, PciDeviceError> {
|
||||
self.common
|
||||
.allocate_bars(allocator, mmio_allocator, resources)
|
||||
.allocate_bars(allocator, mmio_allocator, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn free_bars(
|
||||
@@ -456,19 +425,22 @@ impl PciDevice for VfioUserPciDevice {
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
) -> Option<Arc<Barrier>> {
|
||||
self.common.write_config_register(reg_idx, offset, data)
|
||||
self.common
|
||||
.write_config_register(reg_idx, offset, data, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
|
||||
self.common.read_config_register(reg_idx)
|
||||
self.common
|
||||
.read_config_register(reg_idx, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn read_bar(&mut self, base: u64, offset: u64, data: &mut [u8]) {
|
||||
self.common.read_bar(base, offset, data)
|
||||
self.common.read_bar(base, offset, data, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn write_bar(&mut self, base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
self.common.write_bar(base, offset, data)
|
||||
self.common
|
||||
.write_bar(base, offset, data, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn move_bar(&mut self, old_base: u64, new_base: u64) -> Result<(), std::io::Error> {
|
||||
@@ -477,41 +449,35 @@ impl PciDevice for VfioUserPciDevice {
|
||||
if mmio_region.start.raw_value() == old_base {
|
||||
mmio_region.start = GuestAddress(new_base);
|
||||
|
||||
for user_memory_region in mmio_region.user_memory_regions.iter_mut() {
|
||||
// Remove old region
|
||||
let old_region = self.vm.make_user_memory_region(
|
||||
user_memory_region.slot,
|
||||
user_memory_region.start,
|
||||
user_memory_region.size,
|
||||
user_memory_region.host_addr,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
if let Some(mem_slot) = mmio_region.mem_slot {
|
||||
if let Some(host_addr) = mmio_region.host_addr {
|
||||
// Remove original region
|
||||
let old_region = self.vm.make_user_memory_region(
|
||||
mem_slot,
|
||||
old_base,
|
||||
mmio_region.length as u64,
|
||||
host_addr as u64,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
self.vm
|
||||
.remove_user_memory_region(old_region)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
self.vm
|
||||
.remove_user_memory_region(old_region)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
|
||||
// Update the user memory region with the correct start address.
|
||||
if new_base > old_base {
|
||||
user_memory_region.start += new_base - old_base;
|
||||
} else {
|
||||
user_memory_region.start -= old_base - new_base;
|
||||
let new_region = self.vm.make_user_memory_region(
|
||||
mem_slot,
|
||||
new_base,
|
||||
mmio_region.length as u64,
|
||||
host_addr as u64,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
self.vm
|
||||
.create_user_memory_region(new_region)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
}
|
||||
|
||||
// Insert new region
|
||||
let new_region = self.vm.make_user_memory_region(
|
||||
user_memory_region.slot,
|
||||
user_memory_region.start,
|
||||
user_memory_region.size,
|
||||
user_memory_region.host_addr,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
self.vm
|
||||
.create_user_memory_region(new_region)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
}
|
||||
info!("Moved bar 0x{:x} -> 0x{:x}", old_base, new_base);
|
||||
}
|
||||
@@ -519,10 +485,6 @@ impl PciDevice for VfioUserPciDevice {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
Some(self.id.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VfioUserPciDevice {
|
||||
@@ -531,22 +493,18 @@ impl Drop for VfioUserPciDevice {
|
||||
|
||||
if let Some(msix) = &self.common.interrupt.msix {
|
||||
if msix.bar.enabled() {
|
||||
self.common.disable_msix();
|
||||
self.common.disable_msix(&self.vfio_wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(msi) = &self.common.interrupt.msi {
|
||||
if msi.cfg.enabled() {
|
||||
self.common.disable_msi()
|
||||
self.common.disable_msi(&self.vfio_wrapper)
|
||||
}
|
||||
}
|
||||
|
||||
if self.common.interrupt.intx_in_use() {
|
||||
self.common.disable_intx();
|
||||
}
|
||||
|
||||
if let Err(e) = self.client.lock().unwrap().shutdown() {
|
||||
error!("Failed shutting down vfio-user client: {}", e);
|
||||
self.common.disable_intx(&self.vfio_wrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
[package]
|
||||
name = "performance-metrics"
|
||||
version = "0.1.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.1.18", features = ["wrap_help","cargo"] }
|
||||
dirs = "4.0.0"
|
||||
serde = { version = "1.0.137", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.81"
|
||||
test_infra = { path = "../test_infra" }
|
||||
thiserror = "1.0.31"
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.1.18", features = ["cargo"] }
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright © 2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#[macro_use(crate_version)]
|
||||
extern crate clap;
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let mut git_human_readable = "v".to_owned() + crate_version!();
|
||||
if let Ok(git_out) = Command::new("git").args(&["describe", "--dirty"]).output() {
|
||||
if git_out.status.success() {
|
||||
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {
|
||||
git_human_readable = git_out_str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This println!() has a special behavior, as it will set the environment
|
||||
// variable GIT_HUMAN_READABLE, so that it can be reused from the binary.
|
||||
// Particularly, this is used from the main.rs to display the exact
|
||||
// version information.
|
||||
println!("cargo:rustc-env=GIT_HUMAN_READABLE={}", git_human_readable);
|
||||
}
|
||||
@@ -1,588 +0,0 @@
|
||||
// Copyright © 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// Custom harness to run performance tests
|
||||
extern crate test_infra;
|
||||
#[macro_use(crate_authors)]
|
||||
extern crate clap;
|
||||
|
||||
mod performance_tests;
|
||||
|
||||
use clap::{Arg, Command as ClapCommand};
|
||||
use performance_tests::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
env, fmt,
|
||||
process::Command,
|
||||
sync::{mpsc::channel, Arc},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum Error {
|
||||
#[error("Error: test timed-out")]
|
||||
TestTimeout,
|
||||
#[error("Error: test failed")]
|
||||
TestFailed,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct PerformanceTestResult {
|
||||
name: String,
|
||||
mean: f64,
|
||||
std_dev: f64,
|
||||
max: f64,
|
||||
min: f64,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct MetricsReport {
|
||||
pub git_human_readable: String,
|
||||
pub git_revision: String,
|
||||
pub git_commit_date: String,
|
||||
pub date: String,
|
||||
pub results: Vec<PerformanceTestResult>,
|
||||
}
|
||||
|
||||
impl Default for MetricsReport {
|
||||
fn default() -> Self {
|
||||
let mut git_human_readable = "".to_string();
|
||||
if let Ok(git_out) = Command::new("git").args(&["describe", "--dirty"]).output() {
|
||||
if git_out.status.success() {
|
||||
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {
|
||||
git_human_readable = git_out_str.trim().to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut git_revision = "".to_string();
|
||||
if let Ok(git_out) = Command::new("git").args(&["rev-parse", "HEAD"]).output() {
|
||||
if git_out.status.success() {
|
||||
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {
|
||||
git_revision = git_out_str.trim().to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut git_commit_date = "".to_string();
|
||||
if let Ok(git_out) = Command::new("git")
|
||||
.args(&["show", "-s", "--format=%cd"])
|
||||
.output()
|
||||
{
|
||||
if git_out.status.success() {
|
||||
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {
|
||||
git_commit_date = git_out_str.trim().to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MetricsReport {
|
||||
git_human_readable,
|
||||
git_revision,
|
||||
git_commit_date,
|
||||
date: date(),
|
||||
results: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PerformanceTestOverrides {
|
||||
test_iterations: Option<u32>,
|
||||
}
|
||||
|
||||
impl fmt::Display for PerformanceTestOverrides {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if let Some(test_iterations) = self.test_iterations {
|
||||
write!(f, "test_iterations = {}", test_iterations)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PerformanceTestControl {
|
||||
test_timeout: u32,
|
||||
test_iterations: u32,
|
||||
num_queues: Option<u32>,
|
||||
queue_size: Option<u32>,
|
||||
net_rx: Option<bool>,
|
||||
fio_ops: Option<FioOps>,
|
||||
num_boot_vcpus: Option<u8>,
|
||||
}
|
||||
|
||||
impl fmt::Display for PerformanceTestControl {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut output = format!(
|
||||
"test_timeout = {}s, test_iterations = {}",
|
||||
self.test_timeout, self.test_iterations
|
||||
);
|
||||
if let Some(o) = self.num_queues {
|
||||
output = format!("{}, num_queues = {}", output, o);
|
||||
}
|
||||
if let Some(o) = self.queue_size {
|
||||
output = format!("{}, queue_size = {}", output, o);
|
||||
}
|
||||
if let Some(o) = self.net_rx {
|
||||
output = format!("{}, net_rx = {}", output, o);
|
||||
}
|
||||
if let Some(o) = &self.fio_ops {
|
||||
output = format!("{}, fio_ops = {}", output, o);
|
||||
}
|
||||
|
||||
write!(f, "{}", output)
|
||||
}
|
||||
}
|
||||
|
||||
impl PerformanceTestControl {
|
||||
const fn default() -> Self {
|
||||
Self {
|
||||
test_timeout: 10,
|
||||
test_iterations: 5,
|
||||
num_queues: None,
|
||||
queue_size: None,
|
||||
net_rx: None,
|
||||
fio_ops: None,
|
||||
num_boot_vcpus: Some(1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A performance test should finish within the a certain time-out and
|
||||
/// return a performance metrics number (including the average number and
|
||||
/// standard deviation)
|
||||
struct PerformanceTest {
|
||||
pub name: &'static str,
|
||||
pub func_ptr: fn(&PerformanceTestControl) -> f64,
|
||||
pub control: PerformanceTestControl,
|
||||
unit_adjuster: fn(f64) -> f64,
|
||||
}
|
||||
|
||||
impl PerformanceTest {
|
||||
pub fn run(&self, overrides: &PerformanceTestOverrides) -> PerformanceTestResult {
|
||||
let mut metrics = Vec::new();
|
||||
for _ in 0..overrides
|
||||
.test_iterations
|
||||
.unwrap_or(self.control.test_iterations)
|
||||
{
|
||||
metrics.push((self.func_ptr)(&self.control));
|
||||
}
|
||||
|
||||
let mean = (self.unit_adjuster)(mean(&metrics).unwrap());
|
||||
let std_dev = (self.unit_adjuster)(std_deviation(&metrics).unwrap());
|
||||
let max = (self.unit_adjuster)(metrics.clone().into_iter().reduce(f64::max).unwrap());
|
||||
let min = (self.unit_adjuster)(metrics.clone().into_iter().reduce(f64::min).unwrap());
|
||||
|
||||
PerformanceTestResult {
|
||||
name: self.name.to_string(),
|
||||
mean,
|
||||
std_dev,
|
||||
max,
|
||||
min,
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the timeout for each test
|
||||
// Note: To cover the setup/cleanup time, 20s is added for each iteration of the test
|
||||
pub fn calc_timeout(&self, test_iterations: &Option<u32>) -> u64 {
|
||||
((self.control.test_timeout + 20) * test_iterations.unwrap_or(self.control.test_iterations))
|
||||
as u64
|
||||
}
|
||||
}
|
||||
|
||||
fn mean(data: &[f64]) -> Option<f64> {
|
||||
let count = data.len();
|
||||
|
||||
if count > 0 {
|
||||
Some(data.iter().sum::<f64>() / count as f64)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn std_deviation(data: &[f64]) -> Option<f64> {
|
||||
let count = data.len();
|
||||
|
||||
if count > 0 {
|
||||
let mean = mean(data).unwrap();
|
||||
let variance = data
|
||||
.iter()
|
||||
.map(|value| {
|
||||
let diff = mean - *value;
|
||||
diff * diff
|
||||
})
|
||||
.sum::<f64>()
|
||||
/ count as f64;
|
||||
|
||||
Some(variance.sqrt())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
mod adjuster {
|
||||
pub fn identity(v: f64) -> f64 {
|
||||
v
|
||||
}
|
||||
|
||||
pub fn s_to_ms(v: f64) -> f64 {
|
||||
v * 1000.0
|
||||
}
|
||||
|
||||
pub fn bps_to_gbps(v: f64) -> f64 {
|
||||
v / (1_000_000_000_f64)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn Bps_to_MiBps(v: f64) -> f64 {
|
||||
v / (1 << 20) as f64
|
||||
}
|
||||
}
|
||||
|
||||
const TEST_LIST: [PerformanceTest; 17] = [
|
||||
PerformanceTest {
|
||||
name: "boot_time_ms",
|
||||
func_ptr: performance_boot_time,
|
||||
control: PerformanceTestControl {
|
||||
test_timeout: 2,
|
||||
test_iterations: 10,
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::s_to_ms,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "boot_time_pmem_ms",
|
||||
func_ptr: performance_boot_time_pmem,
|
||||
control: PerformanceTestControl {
|
||||
test_timeout: 2,
|
||||
test_iterations: 10,
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::s_to_ms,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "boot_time_16_vcpus_ms",
|
||||
func_ptr: performance_boot_time,
|
||||
control: PerformanceTestControl {
|
||||
test_timeout: 2,
|
||||
test_iterations: 10,
|
||||
num_boot_vcpus: Some(16),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::s_to_ms,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "boot_time_16_vcpus_pmem_ms",
|
||||
func_ptr: performance_boot_time_pmem,
|
||||
control: PerformanceTestControl {
|
||||
test_timeout: 2,
|
||||
test_iterations: 10,
|
||||
num_boot_vcpus: Some(16),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::s_to_ms,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "virtio_net_latency_us",
|
||||
func_ptr: performance_net_latency,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(256),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::identity,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "virtio_net_throughput_single_queue_rx_gbps",
|
||||
func_ptr: performance_net_throughput,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(256),
|
||||
net_rx: Some(true),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::bps_to_gbps,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "virtio_net_throughput_single_queue_tx_gbps",
|
||||
func_ptr: performance_net_throughput,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(256),
|
||||
net_rx: Some(false),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::bps_to_gbps,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "virtio_net_throughput_multi_queue_rx_gbps",
|
||||
func_ptr: performance_net_throughput,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(4),
|
||||
queue_size: Some(256),
|
||||
net_rx: Some(true),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::bps_to_gbps,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "virtio_net_throughput_multi_queue_tx_gbps",
|
||||
func_ptr: performance_net_throughput,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(4),
|
||||
queue_size: Some(256),
|
||||
net_rx: Some(false),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::bps_to_gbps,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "block_read_MiBps",
|
||||
func_ptr: performance_block_io,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(1),
|
||||
queue_size: Some(128),
|
||||
fio_ops: Some(FioOps::Read),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::Bps_to_MiBps,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "block_write_MiBps",
|
||||
func_ptr: performance_block_io,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(1),
|
||||
queue_size: Some(128),
|
||||
fio_ops: Some(FioOps::Write),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::Bps_to_MiBps,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "block_random_read_MiBps",
|
||||
func_ptr: performance_block_io,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(1),
|
||||
queue_size: Some(128),
|
||||
fio_ops: Some(FioOps::RandomRead),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::Bps_to_MiBps,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "block_random_write_MiBps",
|
||||
func_ptr: performance_block_io,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(1),
|
||||
queue_size: Some(128),
|
||||
fio_ops: Some(FioOps::RandomWrite),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::Bps_to_MiBps,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "block_multi_queue_read_MiBps",
|
||||
func_ptr: performance_block_io,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(128),
|
||||
fio_ops: Some(FioOps::Read),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::Bps_to_MiBps,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "block_multi_queue_write_MiBps",
|
||||
func_ptr: performance_block_io,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(128),
|
||||
fio_ops: Some(FioOps::Write),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::Bps_to_MiBps,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "block_multi_queue_random_read_MiBps",
|
||||
func_ptr: performance_block_io,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(128),
|
||||
fio_ops: Some(FioOps::RandomRead),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::Bps_to_MiBps,
|
||||
},
|
||||
PerformanceTest {
|
||||
name: "block_multi_queue_random_write_MiBps",
|
||||
func_ptr: performance_block_io,
|
||||
control: PerformanceTestControl {
|
||||
num_queues: Some(2),
|
||||
queue_size: Some(128),
|
||||
fio_ops: Some(FioOps::RandomWrite),
|
||||
..PerformanceTestControl::default()
|
||||
},
|
||||
unit_adjuster: adjuster::Bps_to_MiBps,
|
||||
},
|
||||
];
|
||||
|
||||
fn run_test_with_timeout(
|
||||
test: &'static PerformanceTest,
|
||||
overrides: &Arc<PerformanceTestOverrides>,
|
||||
) -> Result<PerformanceTestResult, Error> {
|
||||
let (sender, receiver) = channel::<Result<PerformanceTestResult, Error>>();
|
||||
let test_iterations = overrides.test_iterations;
|
||||
let overrides = overrides.clone();
|
||||
thread::spawn(move || {
|
||||
println!(
|
||||
"Test '{}' running .. (control: {}, overrides: {})",
|
||||
test.name, test.control, overrides
|
||||
);
|
||||
|
||||
let output = match std::panic::catch_unwind(|| test.run(&overrides)) {
|
||||
Ok(test_result) => {
|
||||
println!(
|
||||
"Test '{}' .. ok: mean = {}, std_dev = {}",
|
||||
test_result.name, test_result.mean, test_result.std_dev
|
||||
);
|
||||
Ok(test_result)
|
||||
}
|
||||
Err(_) => Err(Error::TestFailed),
|
||||
};
|
||||
|
||||
let _ = sender.send(output);
|
||||
});
|
||||
|
||||
// Todo: Need to cleanup/kill all hanging child processes
|
||||
let test_timeout = test.calc_timeout(&test_iterations);
|
||||
receiver
|
||||
.recv_timeout(Duration::from_secs(test_timeout))
|
||||
.map_err(|_| {
|
||||
eprintln!(
|
||||
"[Error] Test '{}' time-out after {} seconds",
|
||||
test.name, test_timeout
|
||||
);
|
||||
Error::TestTimeout
|
||||
})?
|
||||
}
|
||||
|
||||
fn date() -> String {
|
||||
let output = test_infra::exec_host_command_output("date");
|
||||
String::from_utf8_lossy(&output.stdout).trim().to_string()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let cmd_arguments = ClapCommand::new("performance-metrics")
|
||||
.version(env!("GIT_HUMAN_READABLE"))
|
||||
.author(crate_authors!())
|
||||
.about("Generate the performance metrics data for Cloud Hypervisor")
|
||||
.arg(
|
||||
Arg::new("test-filter")
|
||||
.long("test-filter")
|
||||
.help("Filter metrics tests to run based on provided keywords")
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("list-tests")
|
||||
.long("list-tests")
|
||||
.help("Print the list of availale metrics tests")
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(false)
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("report-file")
|
||||
.long("report-file")
|
||||
.help("Report file. Standard error is used if not specified")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("iterations")
|
||||
.long("iterations")
|
||||
.help("Override number of test iterations")
|
||||
.takes_value(true),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
// It seems that the tool (ethr) used for testing the virtio-net latency
|
||||
// is not stable on AArch64, and therefore the latency test is currently
|
||||
// skipped on AArch64.
|
||||
let test_list: Vec<&PerformanceTest> = TEST_LIST
|
||||
.iter()
|
||||
.filter(|t| !(cfg!(target_arch = "aarch64") && t.name == "virtio_net_latency_us"))
|
||||
.collect();
|
||||
|
||||
if cmd_arguments.is_present("list-tests") {
|
||||
for test in test_list.iter() {
|
||||
println!("\"{}\" ({})", test.name, test.control);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let test_filter = match cmd_arguments.values_of("test-filter") {
|
||||
Some(s) => s.collect(),
|
||||
None => Vec::new(),
|
||||
};
|
||||
|
||||
// Run performance tests sequentially and report results (in both readable/json format)
|
||||
let mut metrics_report: MetricsReport = Default::default();
|
||||
|
||||
init_tests();
|
||||
|
||||
let overrides = Arc::new(PerformanceTestOverrides {
|
||||
test_iterations: cmd_arguments
|
||||
.value_of("iterations")
|
||||
.map(|s| s.parse())
|
||||
.transpose()
|
||||
.unwrap_or_default(),
|
||||
});
|
||||
|
||||
for test in test_list.iter() {
|
||||
if test_filter.is_empty() || test_filter.iter().any(|&s| test.name.contains(s)) {
|
||||
match run_test_with_timeout(test, &overrides) {
|
||||
Ok(r) => {
|
||||
metrics_report.results.push(r);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Aborting test due to error: '{:?}'", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
cleanup_tests();
|
||||
|
||||
let mut report_file: Box<dyn std::io::Write + Send> =
|
||||
if let Some(file) = cmd_arguments.value_of("report-file") {
|
||||
Box::new(
|
||||
std::fs::File::create(std::path::Path::new(file))
|
||||
.map_err(|e| {
|
||||
eprintln!("Error opening report file: {}: {}", file, e);
|
||||
std::process::exit(1);
|
||||
})
|
||||
.unwrap(),
|
||||
)
|
||||
} else {
|
||||
Box::new(std::io::stdout())
|
||||
};
|
||||
|
||||
report_file
|
||||
.write_all(
|
||||
serde_json::to_string_pretty(&metrics_report)
|
||||
.unwrap()
|
||||
.as_bytes(),
|
||||
)
|
||||
.map_err(|e| {
|
||||
eprintln!("Error writing report file: {}", e);
|
||||
std::process::exit(1);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
@@ -1,846 +0,0 @@
|
||||
// Copyright © 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// Performance tests
|
||||
|
||||
use crate::{mean, PerformanceTestControl};
|
||||
use serde_json::Value;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use std::string::String;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use std::{fmt, fs};
|
||||
use test_infra::Error as InfraError;
|
||||
use test_infra::*;
|
||||
use wait_timeout::ChildExt;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub const FOCAL_IMAGE_NAME: &str = "focal-server-cloudimg-amd64-custom-20210609-0.raw";
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub const FOCAL_IMAGE_NAME: &str = "focal-server-cloudimg-arm64-custom-20210929-0-update-tool.raw";
|
||||
|
||||
#[derive(Debug)]
|
||||
enum WaitTimeoutError {
|
||||
Timedout,
|
||||
ExitStatus,
|
||||
General(std::io::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
BootTimeParse,
|
||||
EthrLogFile(std::io::Error),
|
||||
EthrLogParse,
|
||||
FioOutputParse,
|
||||
Iperf3Parse,
|
||||
Infra(InfraError),
|
||||
Spawn(std::io::Error),
|
||||
Scp(SshCommandError),
|
||||
WaitTimeout(WaitTimeoutError),
|
||||
}
|
||||
|
||||
impl From<InfraError> for Error {
|
||||
fn from(e: InfraError) -> Self {
|
||||
Self::Infra(e)
|
||||
}
|
||||
}
|
||||
|
||||
const BLK_IO_TEST_IMG: &str = "/var/tmp/ch-blk-io-test.img";
|
||||
|
||||
pub fn init_tests() {
|
||||
// The test image can not be created on tmpfs (e.g. /tmp) filesystem,
|
||||
// as tmpfs does not support O_DIRECT
|
||||
assert!(exec_host_command_output(&format!(
|
||||
"dd if=/dev/zero of={} bs=1M count=4096",
|
||||
BLK_IO_TEST_IMG
|
||||
))
|
||||
.status
|
||||
.success());
|
||||
}
|
||||
|
||||
pub fn cleanup_tests() {
|
||||
fs::remove_file(BLK_IO_TEST_IMG)
|
||||
.unwrap_or_else(|_| panic!("Failed to remove file '{}'.", BLK_IO_TEST_IMG));
|
||||
}
|
||||
|
||||
// Performance tests are expected to be executed sequentially, so we can
|
||||
// start VM guests with the same IP while putting them on a different
|
||||
// private network. The default constructor "Guest::new()" does not work
|
||||
// well, as we can easily create more than 256 VMs from repeating various
|
||||
// performance tests dozens times in a single run.
|
||||
fn performance_test_new_guest(disk_config: Box<dyn DiskConfig>) -> Guest {
|
||||
Guest::new_from_ip_range(disk_config, "172.19", 0)
|
||||
}
|
||||
|
||||
const DIRECT_KERNEL_BOOT_CMDLINE: &str =
|
||||
"root=/dev/vda1 console=hvc0 rw systemd.journald.forward_to_console=1";
|
||||
|
||||
// Creates the path for direct kernel boot and return the path.
|
||||
// For x86_64, this function returns the vmlinux kernel path.
|
||||
// For AArch64, this function returns the PE kernel path.
|
||||
fn direct_kernel_boot_path() -> PathBuf {
|
||||
let mut workload_path = dirs::home_dir().unwrap();
|
||||
workload_path.push("workloads");
|
||||
|
||||
let mut kernel_path = workload_path;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
kernel_path.push("vmlinux");
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
kernel_path.push("Image");
|
||||
|
||||
kernel_path
|
||||
}
|
||||
|
||||
// Wait the child process for a given timeout
|
||||
fn child_wait_timeout(child: &mut Child, timeout: u64) -> Result<(), WaitTimeoutError> {
|
||||
match child.wait_timeout(Duration::from_secs(timeout)) {
|
||||
Err(e) => {
|
||||
return Err(WaitTimeoutError::General(e));
|
||||
}
|
||||
Ok(s) => match s {
|
||||
None => {
|
||||
return Err(WaitTimeoutError::Timedout);
|
||||
}
|
||||
Some(s) => {
|
||||
if !s.success() {
|
||||
return Err(WaitTimeoutError::ExitStatus);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_iperf3_output(output: &[u8], sender: bool) -> Result<f64, Error> {
|
||||
std::panic::catch_unwind(|| {
|
||||
let s = String::from_utf8_lossy(output);
|
||||
let v: Value = serde_json::from_str(&s).expect("'iperf3' parse error: invalid json output");
|
||||
|
||||
let bps: f64 = if sender {
|
||||
v["end"]["sum_sent"]["bits_per_second"]
|
||||
.as_f64()
|
||||
.expect("'iperf3' parse error: missing entry 'end.sum_sent.bits_per_second'")
|
||||
} else {
|
||||
v["end"]["sum_received"]["bits_per_second"]
|
||||
.as_f64()
|
||||
.expect("'iperf3' parse error: missing entry 'end.sum_received.bits_per_second'")
|
||||
};
|
||||
|
||||
bps
|
||||
})
|
||||
.map_err(|_| {
|
||||
eprintln!(
|
||||
"=============== iperf3 output ===============\n\n{}\n\n===========end============\n\n",
|
||||
String::from_utf8_lossy(output)
|
||||
);
|
||||
Error::Iperf3Parse
|
||||
})
|
||||
}
|
||||
|
||||
fn measure_virtio_net_throughput(
|
||||
test_timeout: u32,
|
||||
queue_pairs: u32,
|
||||
guest: &Guest,
|
||||
receive: bool,
|
||||
) -> Result<f64, Error> {
|
||||
let default_port = 5201;
|
||||
|
||||
// 1. start the iperf3 server on the guest
|
||||
for n in 0..queue_pairs {
|
||||
guest
|
||||
.ssh_command(&format!("iperf3 -s -p {} -D", default_port + n))
|
||||
.map_err(InfraError::SshCommand)?;
|
||||
}
|
||||
|
||||
thread::sleep(Duration::new(1, 0));
|
||||
|
||||
// 2. start the iperf3 client on host to measure RX through-put
|
||||
let mut clients = Vec::new();
|
||||
for n in 0..queue_pairs {
|
||||
let mut cmd = Command::new("iperf3");
|
||||
cmd.args(&[
|
||||
"-J", // Output in JSON format
|
||||
"-c",
|
||||
&guest.network.guest_ip,
|
||||
"-p",
|
||||
&format!("{}", default_port + n),
|
||||
"-t",
|
||||
&format!("{}", test_timeout),
|
||||
]);
|
||||
// For measuring the guest transmit throughput (as a sender),
|
||||
// use reverse mode of the iperf3 client on the host
|
||||
if !receive {
|
||||
cmd.args(&["-R"]);
|
||||
}
|
||||
let client = cmd
|
||||
.stderr(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.map_err(Error::Spawn)?;
|
||||
|
||||
clients.push(client);
|
||||
}
|
||||
|
||||
let mut err: Option<Error> = None;
|
||||
let mut bps = Vec::new();
|
||||
let mut failed = false;
|
||||
for c in clients {
|
||||
let mut c = c;
|
||||
if let Err(e) = child_wait_timeout(&mut c, test_timeout as u64 + 5) {
|
||||
err = Some(Error::WaitTimeout(e));
|
||||
failed = true;
|
||||
}
|
||||
|
||||
if !failed {
|
||||
// Safe to unwrap as we know the child has terminated succesffully
|
||||
let output = c.wait_with_output().unwrap();
|
||||
bps.push(parse_iperf3_output(&output.stdout, receive)?);
|
||||
} else {
|
||||
let _ = c.kill();
|
||||
let output = c.wait_with_output().unwrap();
|
||||
println!(
|
||||
"=============== Client output [Error] ===============\n\n{}\n\n===========end============\n\n",
|
||||
String::from_utf8_lossy(&output.stdout)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(e) = err {
|
||||
Err(e)
|
||||
} else {
|
||||
Ok(bps.iter().sum())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn performance_net_throughput(control: &PerformanceTestControl) -> f64 {
|
||||
let test_timeout = control.test_timeout;
|
||||
let rx = control.net_rx.unwrap();
|
||||
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = performance_test_new_guest(Box::new(focal));
|
||||
|
||||
let num_queues = control.num_queues.unwrap();
|
||||
let queue_size = control.queue_size.unwrap();
|
||||
let net_params = format!(
|
||||
"tap=,mac={},ip={},mask=255.255.255.0,num_queues={},queue_size={}",
|
||||
guest.network.guest_mac, guest.network.host_ip, num_queues, queue_size,
|
||||
);
|
||||
|
||||
let mut child = GuestCommand::new(&guest)
|
||||
.args(&["--cpus", &format!("boot={}", num_queues)])
|
||||
.args(&["--memory", "size=4G"])
|
||||
.args(&["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(&["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.default_disks()
|
||||
.args(&["--net", net_params.as_str()])
|
||||
.capture_output()
|
||||
.verbosity(VerbosityLevel::Warn)
|
||||
.set_print_cmd(false)
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
measure_virtio_net_throughput(test_timeout, num_queues / 2, &guest, rx).unwrap()
|
||||
});
|
||||
|
||||
let _ = child.kill();
|
||||
let output = child.wait_with_output().unwrap();
|
||||
|
||||
match r {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
handle_child_output(Err(e), &output);
|
||||
panic!("test failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_ethr_latency_output(output: &[u8]) -> Result<Vec<f64>, Error> {
|
||||
std::panic::catch_unwind(|| {
|
||||
let s = String::from_utf8_lossy(output);
|
||||
let mut latency = Vec::new();
|
||||
for l in s.lines() {
|
||||
let v: Value = serde_json::from_str(l).expect("'ethr' parse error: invalid json line");
|
||||
// Skip header/summary lines
|
||||
if let Some(avg) = v["Avg"].as_str() {
|
||||
// Assume the latency unit is always "us"
|
||||
latency.push(
|
||||
avg.split("us").collect::<Vec<&str>>()[0]
|
||||
.parse::<f64>()
|
||||
.expect("'ethr' parse error: invalid 'Avg' entry"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
assert!(
|
||||
!latency.is_empty(),
|
||||
"'ethr' parse error: no valid latency data found"
|
||||
);
|
||||
|
||||
latency
|
||||
})
|
||||
.map_err(|_| {
|
||||
eprintln!(
|
||||
"=============== ethr output ===============\n\n{}\n\n===========end============\n\n",
|
||||
String::from_utf8_lossy(output)
|
||||
);
|
||||
Error::EthrLogParse
|
||||
})
|
||||
}
|
||||
|
||||
fn measure_virtio_net_latency(guest: &Guest, test_timeout: u32) -> Result<Vec<f64>, Error> {
|
||||
// copy the 'ethr' tool to the guest image
|
||||
let ethr_path = "/usr/local/bin/ethr";
|
||||
let ethr_remote_path = "/tmp/ethr";
|
||||
scp_to_guest(
|
||||
Path::new(ethr_path),
|
||||
Path::new(ethr_remote_path),
|
||||
&guest.network.guest_ip,
|
||||
//DEFAULT_SSH_RETRIES,
|
||||
1,
|
||||
DEFAULT_SSH_TIMEOUT,
|
||||
)
|
||||
.map_err(Error::Scp)?;
|
||||
|
||||
// Start the ethr server on the guest
|
||||
guest
|
||||
.ssh_command(&format!("{} -s &> /dev/null &", ethr_remote_path))
|
||||
.map_err(InfraError::SshCommand)?;
|
||||
|
||||
thread::sleep(Duration::new(1, 0));
|
||||
|
||||
// Start the ethr client on the host
|
||||
let log_file = guest
|
||||
.tmp_dir
|
||||
.as_path()
|
||||
.join("ethr.client.log")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
let mut c = Command::new(ethr_path)
|
||||
.args(&[
|
||||
"-c",
|
||||
&guest.network.guest_ip,
|
||||
"-t",
|
||||
"l",
|
||||
"-o",
|
||||
&log_file, // file output is JSON format
|
||||
"-d",
|
||||
&format!("{}s", test_timeout),
|
||||
])
|
||||
.stderr(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.map_err(Error::Spawn)?;
|
||||
|
||||
if let Err(e) = child_wait_timeout(&mut c, test_timeout as u64 + 5).map_err(Error::WaitTimeout)
|
||||
{
|
||||
let _ = c.kill();
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
// Parse the ethr latency test output
|
||||
let content = fs::read(log_file).map_err(Error::EthrLogFile)?;
|
||||
parse_ethr_latency_output(&content)
|
||||
}
|
||||
|
||||
pub fn performance_net_latency(control: &PerformanceTestControl) -> f64 {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = performance_test_new_guest(Box::new(focal));
|
||||
|
||||
let num_queues = control.num_queues.unwrap();
|
||||
let queue_size = control.queue_size.unwrap();
|
||||
let net_params = format!(
|
||||
"tap=,mac={},ip={},mask=255.255.255.0,num_queues={},queue_size={}",
|
||||
guest.network.guest_mac, guest.network.host_ip, num_queues, queue_size,
|
||||
);
|
||||
|
||||
let mut child = GuestCommand::new(&guest)
|
||||
.args(&["--cpus", &format!("boot={}", num_queues)])
|
||||
.args(&["--memory", "size=4G"])
|
||||
.args(&["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(&["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.default_disks()
|
||||
.args(&["--net", net_params.as_str()])
|
||||
.capture_output()
|
||||
.verbosity(VerbosityLevel::Warn)
|
||||
.set_print_cmd(false)
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
|
||||
// 'ethr' tool will measure the latency multiple times with provided test time
|
||||
let latency = measure_virtio_net_latency(&guest, control.test_timeout).unwrap();
|
||||
mean(&latency).unwrap()
|
||||
});
|
||||
|
||||
let _ = child.kill();
|
||||
let output = child.wait_with_output().unwrap();
|
||||
|
||||
match r {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
handle_child_output(Err(e), &output);
|
||||
panic!("test failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_boot_time_output(output: &[u8]) -> Result<f64, Error> {
|
||||
std::panic::catch_unwind(|| {
|
||||
let l: Vec<String> = String::from_utf8_lossy(output)
|
||||
.lines()
|
||||
.into_iter()
|
||||
.filter(|l| l.contains("Debug I/O port: Kernel code"))
|
||||
.map(|l| l.to_string())
|
||||
.collect();
|
||||
|
||||
assert_eq!(
|
||||
l.len(),
|
||||
2,
|
||||
"Expecting two matching lines for 'Debug I/O port: Kernel code'"
|
||||
);
|
||||
|
||||
let time_stamp_kernel_start = {
|
||||
let s = l[0].split("--").collect::<Vec<&str>>();
|
||||
assert_eq!(
|
||||
s.len(),
|
||||
2,
|
||||
"Expecting '--' for the matching line of 'Debug I/O port' output"
|
||||
);
|
||||
|
||||
// Sample output: "[Debug I/O port: Kernel code 0x40] 0.096537 seconds"
|
||||
assert!(
|
||||
s[1].contains("0x40"),
|
||||
"Expecting kernel code '0x40' for 'linux_kernel_start' time stamp output"
|
||||
);
|
||||
let t = s[1].split_whitespace().collect::<Vec<&str>>();
|
||||
assert_eq!(
|
||||
t.len(),
|
||||
8,
|
||||
"Expecting exact '8' words from the 'Debug I/O port' output"
|
||||
);
|
||||
assert!(
|
||||
t[7].eq("seconds"),
|
||||
"Expecting 'seconds' as the the last word of the 'Debug I/O port' output"
|
||||
);
|
||||
|
||||
t[6].parse::<f64>().unwrap()
|
||||
};
|
||||
|
||||
let time_stamp_user_start = {
|
||||
let s = l[1].split("--").collect::<Vec<&str>>();
|
||||
assert_eq!(
|
||||
s.len(),
|
||||
2,
|
||||
"Expecting '--' for the matching line of 'Debug I/O port' output"
|
||||
);
|
||||
|
||||
// Sample output: "Debug I/O port: Kernel code 0x41] 0.198980 seconds"
|
||||
assert!(
|
||||
s[1].contains("0x41"),
|
||||
"Expecting kernel code '0x41' for 'linux_kernel_start' time stamp output"
|
||||
);
|
||||
let t = s[1].split_whitespace().collect::<Vec<&str>>();
|
||||
assert_eq!(
|
||||
t.len(),
|
||||
8,
|
||||
"Expecting exact '8' words from the 'Debug I/O port' output"
|
||||
);
|
||||
assert!(
|
||||
t[7].eq("seconds"),
|
||||
"Expecting 'seconds' as the the last word of the 'Debug I/O port' output"
|
||||
);
|
||||
|
||||
t[6].parse::<f64>().unwrap()
|
||||
};
|
||||
|
||||
time_stamp_user_start - time_stamp_kernel_start
|
||||
})
|
||||
.map_err(|_| {
|
||||
eprintln!(
|
||||
"=============== boot-time output ===============\n\n{}\n\n===========end============\n\n",
|
||||
String::from_utf8_lossy(output)
|
||||
);
|
||||
Error::BootTimeParse
|
||||
})
|
||||
}
|
||||
|
||||
fn measure_boot_time(cmd: &mut GuestCommand, test_timeout: u32) -> Result<f64, Error> {
|
||||
let mut child = cmd
|
||||
.capture_output()
|
||||
.verbosity(VerbosityLevel::Warn)
|
||||
.set_print_cmd(false)
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
thread::sleep(Duration::new(test_timeout as u64, 0));
|
||||
let _ = child.kill();
|
||||
let output = child.wait_with_output().unwrap();
|
||||
|
||||
parse_boot_time_output(&output.stderr).map_err(|e| {
|
||||
eprintln!(
|
||||
"\n\n==== Start child stdout ====\n\n{}\n\n==== End child stdout ====",
|
||||
String::from_utf8_lossy(&output.stdout)
|
||||
);
|
||||
eprintln!(
|
||||
"\n\n==== Start child stderr ====\n\n{}\n\n==== End child stderr ====",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
|
||||
e
|
||||
})
|
||||
}
|
||||
|
||||
pub fn performance_boot_time(control: &PerformanceTestControl) -> f64 {
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = performance_test_new_guest(Box::new(focal));
|
||||
let mut cmd = GuestCommand::new(&guest);
|
||||
|
||||
let c = cmd
|
||||
.args(&[
|
||||
"--cpus",
|
||||
&format!("boot={}", control.num_boot_vcpus.unwrap_or(1)),
|
||||
])
|
||||
.args(&["--memory", "size=1G"])
|
||||
.args(&["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(&["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.args(&["--console", "off"])
|
||||
.default_disks();
|
||||
|
||||
measure_boot_time(c, control.test_timeout).unwrap()
|
||||
});
|
||||
|
||||
match r {
|
||||
Ok(r) => r,
|
||||
Err(_) => {
|
||||
panic!("test failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn performance_boot_time_pmem(control: &PerformanceTestControl) -> f64 {
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = performance_test_new_guest(Box::new(focal));
|
||||
let mut cmd = GuestCommand::new(&guest);
|
||||
let c = cmd
|
||||
.args(&[
|
||||
"--cpus",
|
||||
&format!("boot={}", control.num_boot_vcpus.unwrap_or(1)),
|
||||
])
|
||||
.args(&["--memory", "size=1G,hugepages=on"])
|
||||
.args(&["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(&["--cmdline", "root=/dev/pmem0p1 console=ttyS0 quiet rw"])
|
||||
.args(&["--console", "off"])
|
||||
.args(&[
|
||||
"--pmem",
|
||||
format!(
|
||||
"file={}",
|
||||
guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
]);
|
||||
|
||||
measure_boot_time(c, control.test_timeout).unwrap()
|
||||
});
|
||||
|
||||
match r {
|
||||
Ok(r) => r,
|
||||
Err(_) => {
|
||||
panic!("test failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum FioOps {
|
||||
Read,
|
||||
RandomRead,
|
||||
Write,
|
||||
RandomWrite,
|
||||
}
|
||||
|
||||
impl fmt::Display for FioOps {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
FioOps::Read => write!(f, "read"),
|
||||
FioOps::RandomRead => write!(f, "randread"),
|
||||
FioOps::Write => write!(f, "write"),
|
||||
FioOps::RandomWrite => write!(f, "randwrite"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_fio_output(output: &str, fio_ops: &FioOps, num_jobs: u32) -> Result<f64, Error> {
|
||||
std::panic::catch_unwind(|| {
|
||||
let v: Value =
|
||||
serde_json::from_str(output).expect("'fio' parse error: invalid json output");
|
||||
let jobs = v["jobs"]
|
||||
.as_array()
|
||||
.expect("'fio' parse error: missing entry 'jobs'");
|
||||
assert_eq!(
|
||||
jobs.len(),
|
||||
num_jobs as usize,
|
||||
"'fio' parse error: Unexpected number of 'fio' jobs."
|
||||
);
|
||||
|
||||
let read = match fio_ops {
|
||||
FioOps::Read | FioOps::RandomRead => true,
|
||||
FioOps::Write | FioOps::RandomWrite => false,
|
||||
};
|
||||
|
||||
let mut total_bps = 0_f64;
|
||||
for j in jobs {
|
||||
if read {
|
||||
let bytes = j["read"]["io_bytes"]
|
||||
.as_u64()
|
||||
.expect("'fio' parse error: missing entry 'read.io_bytes'");
|
||||
let runtime = j["read"]["runtime"]
|
||||
.as_u64()
|
||||
.expect("'fio' parse error: missing entry 'read.runtime'")
|
||||
as f64
|
||||
/ 1000_f64;
|
||||
total_bps += bytes as f64 / runtime as f64;
|
||||
} else {
|
||||
let bytes = j["write"]["io_bytes"]
|
||||
.as_u64()
|
||||
.expect("'fio' parse error: missing entry 'write.io_bytes'");
|
||||
let runtime = j["write"]["runtime"]
|
||||
.as_u64()
|
||||
.expect("'fio' parse error: missing entry 'write.runtime'")
|
||||
as f64
|
||||
/ 1000_f64;
|
||||
total_bps += bytes as f64 / runtime as f64;
|
||||
}
|
||||
}
|
||||
|
||||
total_bps
|
||||
})
|
||||
.map_err(|_| {
|
||||
eprintln!(
|
||||
"=============== Fio output ===============\n\n{}\n\n===========end============\n\n",
|
||||
output
|
||||
);
|
||||
Error::FioOutputParse
|
||||
})
|
||||
}
|
||||
|
||||
pub fn performance_block_io(control: &PerformanceTestControl) -> f64 {
|
||||
let test_timeout = control.test_timeout;
|
||||
let num_queues = control.num_queues.unwrap();
|
||||
let fio_ops = control.fio_ops.as_ref().unwrap();
|
||||
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = performance_test_new_guest(Box::new(focal));
|
||||
let api_socket = guest
|
||||
.tmp_dir
|
||||
.as_path()
|
||||
.join("cloud-hypervisor.sock")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
let mut child = GuestCommand::new(&guest)
|
||||
.args(&["--cpus", &format!("boot={}", num_queues)])
|
||||
.args(&["--memory", "size=4G"])
|
||||
.args(&["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(&["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.args(&[
|
||||
"--disk",
|
||||
format!(
|
||||
"path={}",
|
||||
guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
format!(
|
||||
"path={}",
|
||||
guest.disk_config.disk(DiskType::CloudInit).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
format!("path={}", BLK_IO_TEST_IMG).as_str(),
|
||||
])
|
||||
.default_net()
|
||||
.args(&["--api-socket", &api_socket])
|
||||
.capture_output()
|
||||
.verbosity(VerbosityLevel::Warn)
|
||||
.set_print_cmd(false)
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
|
||||
let fio_command = format!(
|
||||
"sudo fio --filename=/dev/vdc --name=test --output-format=json \
|
||||
--direct=1 --bs=4k --ioengine=io_uring --iodepth=64 \
|
||||
--rw={} --runtime={} --numjobs={}",
|
||||
fio_ops, test_timeout, num_queues
|
||||
);
|
||||
let output = guest
|
||||
.ssh_command(&fio_command)
|
||||
.map_err(InfraError::SshCommand)
|
||||
.unwrap();
|
||||
|
||||
// Parse fio output
|
||||
parse_fio_output(&output, fio_ops, num_queues).unwrap()
|
||||
});
|
||||
|
||||
let _ = child.kill();
|
||||
let output = child.wait_with_output().unwrap();
|
||||
|
||||
match r {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
handle_child_output(Err(e), &output);
|
||||
panic!("test failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_iperf3_output() {
|
||||
let output = r#"
|
||||
{
|
||||
"end": {
|
||||
"sum_sent": {
|
||||
"start": 0,
|
||||
"end": 5.000196,
|
||||
"seconds": 5.000196,
|
||||
"bytes": 14973836248,
|
||||
"bits_per_second": 23957198874.604115,
|
||||
"retransmits": 0,
|
||||
"sender": false
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse_iperf3_output(output.as_bytes(), true).unwrap(),
|
||||
23957198874.604115
|
||||
);
|
||||
|
||||
let output = r#"
|
||||
{
|
||||
"end": {
|
||||
"sum_received": {
|
||||
"start": 0,
|
||||
"end": 5.000626,
|
||||
"seconds": 5.000626,
|
||||
"bytes": 24703557800,
|
||||
"bits_per_second": 39520744482.79,
|
||||
"sender": true
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse_iperf3_output(output.as_bytes(), false).unwrap(),
|
||||
39520744482.79
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_ethr_latency_output() {
|
||||
let output = r#"{"Time":"2022-02-08T03:52:50Z","Title":"","Type":"INFO","Message":"Using destination: 192.168.249.2, ip: 192.168.249.2, port: 8888"}
|
||||
{"Time":"2022-02-08T03:52:51Z","Title":"","Type":"INFO","Message":"Running latency test: 1000, 1"}
|
||||
{"Time":"2022-02-08T03:52:51Z","Title":"","Type":"LatencyResult","RemoteAddr":"192.168.249.2","Protocol":"TCP","Avg":"80.712us","Min":"61.677us","P50":"257.014us","P90":"74.418us","P95":"107.283us","P99":"119.309us","P999":"142.100us","P9999":"216.341us","Max":"216.341us"}
|
||||
{"Time":"2022-02-08T03:52:52Z","Title":"","Type":"LatencyResult","RemoteAddr":"192.168.249.2","Protocol":"TCP","Avg":"79.826us","Min":"55.129us","P50":"598.996us","P90":"73.849us","P95":"106.552us","P99":"122.152us","P999":"142.459us","P9999":"474.280us","Max":"474.280us"}
|
||||
{"Time":"2022-02-08T03:52:53Z","Title":"","Type":"LatencyResult","RemoteAddr":"192.168.249.2","Protocol":"TCP","Avg":"78.239us","Min":"56.999us","P50":"396.820us","P90":"69.469us","P95":"115.421us","P99":"119.404us","P999":"130.158us","P9999":"258.686us","Max":"258.686us"}"#;
|
||||
|
||||
let ret = parse_ethr_latency_output(output.as_bytes()).unwrap();
|
||||
let reference = vec![80.712_f64, 79.826_f64, 78.239_f64];
|
||||
assert_eq!(ret, reference);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_boot_time_output() {
|
||||
let output = r#"
|
||||
cloud-hypervisor: 161.167103ms: <vcpu0> INFO:vmm/src/vm.rs:392 -- [Debug I/O port: Kernel code 0x40] 0.132 seconds
|
||||
cloud-hypervisor: 613.57361ms: <vcpu0> INFO:vmm/src/vm.rs:392 -- [Debug I/O port: Kernel code 0x41] 0.5845 seconds
|
||||
"#;
|
||||
|
||||
assert_eq!(parse_boot_time_output(output.as_bytes()).unwrap(), 0.4525);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fio_output() {
|
||||
let output = r#"
|
||||
{
|
||||
"jobs" : [
|
||||
{
|
||||
"read" : {
|
||||
"io_bytes" : 1965273088,
|
||||
"io_kbytes" : 1919212,
|
||||
"bw_bytes" : 392976022,
|
||||
"bw" : 383765,
|
||||
"iops" : 95941.411718,
|
||||
"runtime" : 5001,
|
||||
"total_ios" : 479803,
|
||||
"short_ios" : 0,
|
||||
"drop_ios" : 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"#;
|
||||
|
||||
let bps = 1965273088_f64 / (5001_f64 / 1000_f64);
|
||||
assert_eq!(
|
||||
parse_fio_output(output, &FioOps::RandomRead, 1).unwrap(),
|
||||
bps
|
||||
);
|
||||
assert_eq!(parse_fio_output(output, &FioOps::Read, 1).unwrap(), bps);
|
||||
|
||||
let output = r#"
|
||||
{
|
||||
"jobs" : [
|
||||
{
|
||||
"write" : {
|
||||
"io_bytes" : 1172783104,
|
||||
"io_kbytes" : 1145296,
|
||||
"bw_bytes" : 234462835,
|
||||
"bw" : 228967,
|
||||
"iops" : 57241.903239,
|
||||
"runtime" : 5002,
|
||||
"total_ios" : 286324,
|
||||
"short_ios" : 0,
|
||||
"drop_ios" : 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"write" : {
|
||||
"io_bytes" : 1172234240,
|
||||
"io_kbytes" : 1144760,
|
||||
"bw_bytes" : 234353106,
|
||||
"bw" : 228860,
|
||||
"iops" : 57215.113954,
|
||||
"runtime" : 5002,
|
||||
"total_ios" : 286190,
|
||||
"short_ios" : 0,
|
||||
"drop_ios" : 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"#;
|
||||
|
||||
let bps = 1172783104_f64 / (5002_f64 / 1000_f64) + 1172234240_f64 / (5002_f64 / 1000_f64);
|
||||
assert_eq!(
|
||||
parse_fio_output(output, &FioOps::RandomWrite, 2).unwrap(),
|
||||
bps
|
||||
);
|
||||
assert_eq!(parse_fio_output(output, &FioOps::Write, 2).unwrap(), bps);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "qcow"
|
||||
version = "0.1.0"
|
||||
authors = ["The Chromium OS Authors"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
license = "BSD-3-Clause"
|
||||
|
||||
[lib]
|
||||
@@ -10,7 +10,7 @@ path = "src/qcow.rs"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.4.3"
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
remain = "0.2.3"
|
||||
libc = "0.2.112"
|
||||
log = "0.4.14"
|
||||
remain = "0.2.2"
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
@@ -516,19 +516,6 @@ impl QcowFile {
|
||||
|
||||
let l2_entries = cluster_size / size_of::<u64>() as u64;
|
||||
|
||||
// Check for compressed blocks
|
||||
for l2_addr_disk in l1_table.get_values() {
|
||||
if *l2_addr_disk != 0 {
|
||||
if let Err(e) = Self::read_l2_cluster(&mut raw_file, *l2_addr_disk) {
|
||||
if let Some(os_error) = e.raw_os_error() {
|
||||
if os_error == ENOTSUP {
|
||||
return Err(Error::CompressedBlocksNotSupported);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut qcow = QcowFile {
|
||||
raw_file,
|
||||
header,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
[package]
|
||||
name = "rate_limiter"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.112"
|
||||
log = "0.4.14"
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user