Compare commits

..

7 Commits
v27.0 ... v21.1

Author SHA1 Message Date
Rob Bradford
e01933c8ea build: Release v21.1 (bug fix release)
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2022-03-11 08:44:05 +00:00
Rob Bradford
c6307865eb virtio-devices: Enable F_EVENT_IDX on control queue if negotiated
With the VIRTIO_F_EVENT_IDX handling now conducted inside the
virtio-queue crate it is necessary to activate the functionality on
every queue if it is negotiatated. Otherwise this leads to a failure of
the guest to signal to the host that there is something in the available
queue as the queue's internal state has not been configured correctly.

Fixes: #3829

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
(cherry picked from commit 223d0cf787)
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2022-03-11 08:44:05 +00:00
Yi Wang
6275d4f87e vmm: interrupt: fix msi mask irq causing kernel panic on AMD
When mask a msi irq, we set the entry.masked to be true, so kvm
hypervisor will not pass the gsi to kernel through KVM_SET_GSI_ROUTING
ioctl which update kvm->irq_routing. This will trigger kernel
panic on AMD platform when the gsi is the largest one in kernel
kvm->irqfds.items:

crash> bt
PID: 22218  TASK: ffff951a6ad74980  CPU: 73  COMMAND: "vcpu8"
 #0 [ffffb1ba6707fa40] machine_kexec at ffffffff8565b397
 #1 [ffffb1ba6707fa90] __crash_kexec at ffffffff85788a6d
 #2 [ffffb1ba6707fb58] crash_kexec at ffffffff8578995d
 #3 [ffffb1ba6707fb70] oops_end at ffffffff85623c0d
 #4 [ffffb1ba6707fb90] no_context at ffffffff856692c9
 #5 [ffffb1ba6707fbf8] exc_page_fault at ffffffff85f95b51
 #6 [ffffb1ba6707fc50] asm_exc_page_fault at ffffffff86000ace
    [exception RIP: svm_update_pi_irte+227]
    RIP: ffffffffc0761b53  RSP: ffffb1ba6707fd08  RFLAGS: 00010086
    RAX: ffffb1ba6707fd78  RBX: ffffb1ba66d91000  RCX: 0000000000000001
    RDX: 00003c803f63f1c0  RSI: 000000000000019a  RDI: ffffb1ba66db2ab8
    RBP: 000000000000019a   R8: 0000000000000040   R9: ffff94ca41b82200
    R10: ffffffffffffffcf  R11: 0000000000000001  R12: 0000000000000001
    R13: 0000000000000001  R14: ffffffffffffffcf  R15: 000000000000005f
    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
 #7 [ffffb1ba6707fdb8] kvm_irq_routing_update at ffffffffc09f19a1 [kvm]
 #8 [ffffb1ba6707fde0] kvm_set_irq_routing at ffffffffc09f2133 [kvm]
 #9 [ffffb1ba6707fe18] kvm_vm_ioctl at ffffffffc09ef544 [kvm]
    RIP: 00007f143c36488b  RSP: 00007f143a4e04b8  RFLAGS: 00000246
    RAX: ffffffffffffffda  RBX: 00007f05780041d0  RCX: 00007f143c36488b
    RDX: 00007f05780041d0  RSI: 000000004008ae6a  RDI: 0000000000000020
    RBP: 00000000000004e8   R8: 0000000000000008   R9: 00007f05780041e0
    R10: 00007f0578004560  R11: 0000000000000246  R12: 00000000000004e0
    R13: 000000000000001a  R14: 00007f1424001c60  R15: 00007f0578003bc0
    ORIG_RAX: 0000000000000010  CS: 0033  SS: 002b

To solve this problem, move route.disable() before set_gsi_routes() to
remove the gsi from irqfds.items first.

This problem only exists on AMD platform, 'cause on Intel platform
kernel just return when update irte while it only prints a warning on
AMD.

Also, this patch adjusts the order of enable() and set_gsi_routes() in
unmask(), which should do no harm.

Signed-off-by: Yi Wang <wang.yi59@zte.com.cn>
(cherry picked from commit 5375b84e3b)
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2022-03-11 08:44:05 +00:00
Yi Wang
4dfe1ff77a vmm: interrupt: fix msi mask irq causing kernel panic on AMD
When mask a msi irq, we set the entry.masked to be true, so kvm
hypervisor will not pass the gsi to kernel through KVM_SET_GSI_ROUTING
ioctl which update kvm->irq_routing. This will trigger kernel
panic on AMD platform when the gsi is the largest one in kernel
kvm->irqfds.items:

crash> bt
PID: 22218  TASK: ffff951a6ad74980  CPU: 73  COMMAND: "vcpu8"
 #0 [ffffb1ba6707fa40] machine_kexec at ffffffff8565b397
 #1 [ffffb1ba6707fa90] __crash_kexec at ffffffff85788a6d
 #2 [ffffb1ba6707fb58] crash_kexec at ffffffff8578995d
 #3 [ffffb1ba6707fb70] oops_end at ffffffff85623c0d
 #4 [ffffb1ba6707fb90] no_context at ffffffff856692c9
 #5 [ffffb1ba6707fbf8] exc_page_fault at ffffffff85f95b51
 #6 [ffffb1ba6707fc50] asm_exc_page_fault at ffffffff86000ace
    [exception RIP: svm_update_pi_irte+227]
    RIP: ffffffffc0761b53  RSP: ffffb1ba6707fd08  RFLAGS: 00010086
    RAX: ffffb1ba6707fd78  RBX: ffffb1ba66d91000  RCX: 0000000000000001
    RDX: 00003c803f63f1c0  RSI: 000000000000019a  RDI: ffffb1ba66db2ab8
    RBP: 000000000000019a   R8: 0000000000000040   R9: ffff94ca41b82200
    R10: ffffffffffffffcf  R11: 0000000000000001  R12: 0000000000000001
    R13: 0000000000000001  R14: ffffffffffffffcf  R15: 000000000000005f
    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
 #7 [ffffb1ba6707fdb8] kvm_irq_routing_update at ffffffffc09f19a1 [kvm]
 #8 [ffffb1ba6707fde0] kvm_set_irq_routing at ffffffffc09f2133 [kvm]
 #9 [ffffb1ba6707fe18] kvm_vm_ioctl at ffffffffc09ef544 [kvm]
    RIP: 00007f143c36488b  RSP: 00007f143a4e04b8  RFLAGS: 00000246
    RAX: ffffffffffffffda  RBX: 00007f05780041d0  RCX: 00007f143c36488b
    RDX: 00007f05780041d0  RSI: 000000004008ae6a  RDI: 0000000000000020
    RBP: 00000000000004e8   R8: 0000000000000008   R9: 00007f05780041e0
    R10: 00007f0578004560  R11: 0000000000000246  R12: 00000000000004e0
    R13: 000000000000001a  R14: 00007f1424001c60  R15: 00007f0578003bc0
    ORIG_RAX: 0000000000000010  CS: 0033  SS: 002b

To solve this problem, move route.disable() before set_gsi_routes() to
remove the gsi from irqfds.items first.

This problem only exists on AMD platform, 'cause on Intel platform
kernel just return when update irte while it only prints a warning on
AMD.

Signed-off-by: Yi Wang <wang.yi59@zte.com.cn>
(cherry picked from commit db9e5e5a87)
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2022-03-11 08:44:05 +00:00
Rob Bradford
bbda07c388 pci: Support DWORD/4-byte writes to the MSI-X control register
The PCI spec does not specify that the access has to be of a specific
size.

Fixes: #3714

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
(cherry picked from commit 9c6e7c4a4b)
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2022-03-11 08:44:05 +00:00
Rob Bradford
366223c9f4 vmm: Ensure that PIO and MMIO exits complete before pausing
As per this kernel documentation:

      For KVM_EXIT_IO, KVM_EXIT_MMIO, KVM_EXIT_OSI, KVM_EXIT_PAPR, KVM_EXIT_XEN,
      KVM_EXIT_EPR, KVM_EXIT_X86_RDMSR and KVM_EXIT_X86_WRMSR the corresponding
      operations are complete (and guest state is consistent) only after userspace
      has re-entered the kernel with KVM_RUN.  The kernel side will first finish
      incomplete operations and then check for pending signals.

      The pending state of the operation is not preserved in state which is
      visible to userspace, thus userspace should ensure that the operation is
      completed before performing a live migration.  Userspace can re-enter the
      guest with an unmasked signal pending or with the immediate_exit field set
      to complete pending operations without allowing any further instructions
      to be executed.

Since we capture the state as part of the pause and override it as part
of the resume we must ensure the state is consistent otherwise we will
lose the results of the MMIO or PIO operation that caused the exit from
which we paused.

Fixes: #3658

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
(cherry picked from commit 507912385a)
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2022-03-11 08:44:05 +00:00
Rob Bradford
375356a097 virtio-devices: Add openat() syscall to seccomp filter
When freeing memory sometimes glibc will attempt to read
"/proc/sys/vm/overcommit_memory" to find out how it should release the
blocks. This happens sporadically with Cloud Hypervisor but has been
seen in use. It is not necessary to add the read() syscall to the list
as it is already included in the virtio devices common set. Similarly
the vCPU and vmm threads already have both these in the allowed list.

Fixes: #3609

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
(cherry picked from commit 53caa565bb)
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2022-03-11 08:44:05 +00:00
261 changed files with 17606 additions and 28575 deletions

View File

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

View File

@@ -13,7 +13,6 @@ jobs:
- stable
- beta
- nightly
- "1.60"
target:
- x86_64-unknown-linux-gnu
- x86_64-unknown-linux-musl
@@ -23,36 +22,35 @@ 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:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- name: Build (default features)
run: cargo rustc --locked --bin cloud-hypervisor -- -D warnings
- name: Debug Check (default features)
run: |
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 + tdx)
run: cargo rustc --bin cloud-hypervisor --features "tdx" -- -D warnings
- name: Build (acpi,kvm)
run: cargo rustc --bin cloud-hypervisor --no-default-features --features "acpi,kvm" -- -D warnings
- name: Build (kvm)
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "kvm" -- -D warnings
run: cargo rustc --bin cloud-hypervisor --no-default-features --features "kvm" -- -D warnings
- name: Build (default features + tdx)
run: cargo rustc --locked --bin cloud-hypervisor --features "tdx" -- -D warnings
- name: Build (default features + guest_debug)
run: cargo rustc --locked --bin cloud-hypervisor --features "guest_debug" -- -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 --locked --bin cloud-hypervisor --no-default-features --features "mshv" -- -D warnings
- name: Build (mshv + kvm)
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv,kvm" -- -D warnings
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)"

View File

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

61
.github/workflows/quality-aarch64.yaml vendored Normal file
View File

@@ -0,0 +1,61 @@
name: Cloud Hypervisor Quality Checks
on: [pull_request, create]
jobs:
build:
if: github.event_name == 'pull_request'
name: Quality (clippy, rustfmt)
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.experimental }}
strategy:
fail-fast: false
matrix:
rust:
- stable
target:
- aarch64-unknown-linux-gnu
experimental: [false]
include:
- rust: beta
target: aarch64-unknown-linux-gnu
experimental: true
steps:
- name: Code checkout
uses: actions/checkout@v2
- name: Install Rust toolchain (${{ matrix.rust }})
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
components: rustfmt, clippy
- name: Formatting (rustfmt)
run: cargo fmt -- --check
- name: Clippy (kvm)
uses: actions-rs/cargo@v1
with:
use-cross: true
command: clippy
args: --target=${{ matrix.target }} --all --no-default-features --features "kvm" -- -D warnings
- name: Clippy (kvm,acpi)
uses: actions-rs/cargo@v1
with:
use-cross: true
command: clippy
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

View File

@@ -14,21 +14,14 @@ jobs:
- stable
target:
- x86_64-unknown-linux-gnu
- aarch64-unknown-linux-gnu
experimental: [false]
include:
- rust: beta
target: x86_64-unknown-linux-gnu
experimental: true
- rust: beta
target: aarch64-unknown-linux-gnu
experimental: true
steps:
- name: Code checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
uses: actions/checkout@v2
- name: Install Rust toolchain (${{ matrix.rust }})
uses: actions-rs/toolchain@v1
with:
@@ -37,68 +30,32 @@ jobs:
override: true
components: rustfmt, clippy
- name: Debug Check (default features)
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
run: |
set -e
commits=$(git rev-list origin/${{ github.base_ref }}..${{ github.sha }})
for commit in $commits; do git checkout $commit; cargo check --tests --all --target=${{ matrix.target }}; done
git checkout ${{ github.sha }}
- name: Formatting (rustfmt)
run: cargo fmt -- --check
- name: Clippy (all features,kvm)
run: cargo clippy --all --all-targets --no-default-features --tests --features "common,kvm" -- -D warnings
- name: Clippy (all features,mshv)
run: cargo clippy --all --all-targets --no-default-features --tests --features "common,mshv" -- -D warnings
- name: Clippy (acpi,kvm)
run: cargo clippy --all --all-targets --no-default-features --tests --features "acpi,kvm" -- -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 (kvm)
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy
args: --locked --all --all-targets --no-default-features --tests --features "kvm" -- -D warnings
run: cargo clippy --all --all-targets --no-default-features --tests --features "kvm" -- -D warnings
- name: Clippy (default features)
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy
args: --locked --all --all-targets --tests -- -D warnings
- name: Clippy (default features + guest_debug)
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy
args: --locked --all --all-targets --tests --features "guest_debug" -- -D warnings
- name: Clippy (default features + tracing)
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy
args: --locked --all --all-targets --tests --features "tracing" -- -D warnings
- name: Clippy (acpi,mshv)
run: cargo clippy --all --all-targets --no-default-features --tests --features "acpi,mshv" -- -D warnings
- name: Clippy (mshv)
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy
args: --locked --all --all-targets --no-default-features --tests --features "mshv" -- -D warnings
run: cargo clippy --all --all-targets --no-default-features --tests --features "mshv" -- -D warnings
- name: Clippy (mshv + kvm)
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy
args: --locked --all --all-targets --no-default-features --tests --features "mshv,kvm" -- -D warnings
- name: Clippy (kvm + tdx)
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy
args: --locked --all --all-targets --no-default-features --tests --features "tdx,kvm" -- -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)"

View File

@@ -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.62"
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.62"
toolchain: stable
target: x86_64-unknown-linux-musl
- name: Build
uses: actions-rs/cargo@v1
with:
toolchain: "1.62"
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.62"
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.62"
toolchain: stable
target: aarch64-unknown-linux-musl
override: true
- name: Static Build (AArch64)

1
.gitignore vendored
View File

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

View File

@@ -1 +1 @@
edition = "2021"
edition = "2018"

View File

@@ -1,2 +0,0 @@
# Add the list of code owners here (using their GitHub username)
* @cloud-hypervisor/cloud-hypervisor-reviewers

View File

@@ -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
@@ -17,23 +15,6 @@ We follow the [Rust Style](https://github.com/rust-dev-tools/fmt-rfcs/blob/maste
convention and enforce it through the Continuous Integration (CI) process calling into `rustfmt`
for each submitted Pull Request (PR).
## Basic Checks
Please consider creating the following hook as `.git/hooks/pre-commit` in order
to ensure basic correctness of your code. You can extend this further if you
have specific features that you regularly develop against.
```sh
#!/bin/sh
cargo fmt -- --check || exit 1
cargo check --locked --all --all-targets --tests || exit 1
cargo clippy --locked --all --all-targets --tests -- -D warnings || exit 1
```
You will need to `chmod +x .git/hooks/pre-commit` to have it run on every
commit you make.
## Certificate of Origin
In order to get a clear contribution chain of trust we use the [signed-off-by language](https://01.org/community/signed-process)
@@ -82,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

635
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,65 +1,62 @@
[package]
name = "cloud-hypervisor"
version = "27.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
codegen-units = 1
opt-level = "s"
[dependencies]
anyhow = "1.0.65"
anyhow = "1.0.52"
api_client = { path = "api_client" }
clap = { version = "3.2.22", 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.133"
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.3.0"
serde_json = "1.0.85"
signal-hook = "0.3.14"
thiserror = "1.0.37"
tracer = { path = "tracer" }
seccompiler = "0.2.0"
serde_json = "1.0.75"
signal-hook = "0.3.13"
thiserror = "1.0.30"
vmm = { path = "vmm" }
vmm-sys-util = "0.10.0"
vm-memory = "0.9.0"
vmm-sys-util = "0.9.0"
vm-memory = "0.7.0"
[build-dependencies]
clap = { version = "3.2.22", 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" }
[dev-dependencies]
dirs = "4.0.0"
lazy_static= "1.4.0"
net_util = { path = "net_util" }
once_cell = "1.15.0"
serde_json = "1.0.85"
serde_json = "1.0.75"
test_infra = { path = "test_infra" }
wait-timeout = "0.2.0"
[features]
default = ["kvm"]
guest_debug = ["vmm/guest_debug"]
default = ["common", "kvm"]
# Common features for all hypervisors
common = ["acpi", "cmos", "fwdebug"]
acpi = ["vmm/acpi"]
cmos = ["vmm/cmos"]
fwdebug = ["vmm/fwdebug"]
kvm = ["vmm/kvm"]
mshv = ["vmm/mshv"]
tdx = ["vmm/tdx"]
tracing = ["vmm/tracing", "tracer/tracing"]
[workspace]
members = [
@@ -74,20 +71,19 @@ members = [
"net_util",
"option_parser",
"pci",
"performance-metrics",
"qcow",
"rate_limiter",
"serial_buffer",
"test_infra",
"tracer",
"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"]

227
Jenkinsfile vendored
View File

@@ -1,41 +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 fuzzer files only changes') {
when {
expression {
return fuzzFileOnly()
}
}
steps {
script {
runWorkers = false
echo "Fuzzer cargo files only changes, no need to run the CI"
}
}
}
stage ('Check for RFC/WIP builds') {
when {
changeRequest comparator: 'REGEXP', title: '.*(rfc|RFC|wip|WIP).*'
@@ -54,14 +22,11 @@ pipeline{
}
}
stage ('Build') {
parallel {
parallel {
stage ('Worker build') {
agent { node { label 'jammy' } }
when {
beforeAgent true
expression {
return runWorkers
}
agent { node { label 'hirsute' } }
environment {
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
}
stages {
stage ('Checkout') {
@@ -69,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"
@@ -97,15 +58,6 @@ pipeline{
}
stage ('AArch64 worker build') {
agent { node { label 'bionic-arm64' } }
when {
beforeAgent true
expression {
return runWorkers
}
}
environment {
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
}
stages {
stage ('Checkout') {
steps {
@@ -114,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') {
@@ -123,43 +75,7 @@ pipeline{
}
steps {
sh "sudo modprobe openvswitch"
sh "scripts/dev_cli.sh tests --integration --libc musl"
}
}
stage ('Install azure-cli') {
steps {
installAzureCli("bionic", "arm64")
}
}
stage ('Download Windows image') {
steps {
sh '''#!/bin/bash -x
IMG_BASENAME=windows-11-iot-enterprise-aarch64.raw
IMG_PATH=$HOME/workloads/$IMG_BASENAME
IMG_GZ_PATH=$HOME/workloads/$IMG_BASENAME.gz
IMG_GZ_BLOB_NAME=windows-11-iot-enterprise-aarch64-9-min.raw.gz
cp "scripts/$IMG_BASENAME.sha1" "$HOME/workloads/"
pushd "$HOME/workloads"
if sha1sum "$IMG_BASENAME.sha1" --check; then
exit
fi
popd
mkdir -p "$HOME/workloads"
az storage blob download \
--container-name private-images \
--file "$IMG_GZ_PATH" \
--name "$IMG_GZ_BLOB_NAME" \
--connection-string "$AZURE_CONNECTION_STRING"
gzip -d $IMG_GZ_PATH
'''
}
}
stage ('Run Windows guest integration tests') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh "scripts/dev_cli.sh tests --integration-windows --libc musl"
sh "scripts/dev_cli.sh tests --integration"
}
}
}
@@ -171,12 +87,9 @@ pipeline{
}
}
stage ('Worker build (musl)') {
agent { node { label 'jammy' } }
when {
beforeAgent true
expression {
return runWorkers
}
agent { node { label 'hirsute' } }
environment {
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
}
stages {
stage ('Checkout') {
@@ -184,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"
@@ -205,14 +114,80 @@ pipeline{
}
}
}
stage ('Worker build - Windows guest') {
agent { node { label 'jammy' } }
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')
}
@@ -224,7 +199,7 @@ pipeline{
}
stage ('Install azure-cli') {
steps {
installAzureCli("jammy", "amd64")
installAzureCli()
}
}
stage ('Download assets') {
@@ -252,13 +227,7 @@ pipeline{
}
}
stage ('Worker build - Live Migration') {
agent { node { label 'jammy' } }
when {
beforeAgent true
expression {
return runWorkers
}
}
agent { node { label 'hirsute-small' } }
stages {
stage ('Checkout') {
steps {
@@ -292,14 +261,14 @@ pipeline{
regression {
script {
if (env.BRANCH_NAME == 'main') {
slackSend (color: '#ff0000', message: '"main" branch build is now failing', channel: '#jenkins-ci')
slackSend (color: '#ff0000', message: '"main" branch build is now failing')
}
}
}
fixed {
script {
if (env.BRANCH_NAME == 'main') {
slackSend (color: '#00ff00', message: '"main" branch build is now fixed', channel: '#jenkins-ci')
slackSend (color: '#00ff00', message: '"main" branch build is now fixed')
}
}
}
@@ -321,32 +290,10 @@ def cancelPreviousBuilds() {
}
}
def installAzureCli(distro, arch) {
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=${arch}] https://packages.microsoft.com/repos/azure-cli/ ${distro} 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
}
def boolean fuzzFileOnly() {
if (env.CHANGE_TARGET == null) {
return false;
}
return sh(
returnStatus: true,
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v -E 'fuzz/'"
) != 0
}

213
README.md
View File

@@ -4,12 +4,10 @@
- [Architectures](#architectures)
- [Guest OS](#guest-os)
- [2. Getting Started](#2-getting-started)
- [Host OS](#host-os)
- [Preparation](#preparation)
- [Install prerequisites](#install-prerequisites)
- [Clone and build](#clone-and-build)
- [Containerized builds and tests](#containerized-builds-and-tests)
- [Use Prebuilt Binaries](#use-prebuilt-binaries)
- [Run](#run)
- [Cloud image](#cloud-image)
- [Custom kernel and disk image](#custom-kernel-and-disk-image)
@@ -20,30 +18,21 @@
- [Hot Plug](#hot-plug)
- [Device Model](#device-model)
- [TODO](#todo)
- [Roadmap](#roadmap)
- [4. `rust-vmm` project dependency](#4-rust-vmm-project-dependency)
- [Firecracker and crosvm](#firecracker-and-crosvm)
- [5. Community](#5-community)
- [Contribute](#contribute)
- [Slack](#slack)
- [Mailing list](#mailing-list)
- [Join us](#join-us)
- [Security issues](#security-issues)
# 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
@@ -62,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
@@ -76,12 +63,6 @@ Below sections describe how to build and run Cloud Hypervisor on the `x86_64`
platform. For getting started on the `AArch64` platform, please refer to the
[Arm64 documentation](docs/arm64.md).
## Host OS
Based on required KVM functionality the minimum host kernel version is 4.11.
For adequate peformance the minimum recommended host kernel vesion is 5.6. The
majority of the CI currently tests with kernel version 5.15.
## Preparation
We create a folder to build and run `cloud-hypervisor` at `$HOME/cloud-hypervisor`
@@ -93,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-utils
$ sudo apt install git build-essential qemu-utils
# 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
```
@@ -124,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
@@ -155,37 +137,26 @@ $ ./scripts/dev_cli.sh tests --unit
Run the `./scripts/dev_cli.sh --help` command to view all the supported
development script commands and their related options.
## Use Prebuilt Binaries
Cloud Hypervisor packages targeting some popular Linux distributions are available
thanks to the [Open Build Service](https://build.opensuse.org). The
[OBS README](https://github.com/cloud-hypervisor/obs-packaging) explains how to
enable the repository in a supported Linux distribution and install Cloud Hypervisor
and accompanying packages. Please report any packaging issues in the
[obs-packaging](https://github.com/cloud-hypervisor/obs-packaging) repository.
## 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.1/hypervisor-fw
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.3.2/hypervisor-fw
$ popd
```
@@ -207,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:
@@ -227,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
@@ -244,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
@@ -280,131 +246,100 @@ $ ./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.
## Roadmap
The project roadmap is tracked through a [GitHub project](https://github.com/orgs/cloud-hypervisor/projects/6).
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.
## Slack
## Join us
Get an [invite to our Slack channel](https://join.slack.com/t/cloud-hypervisor/shared_invite/enQtNjY3MTE3MDkwNDQ4LWQ1MTA1ZDVmODkwMWQ1MTRhYzk4ZGNlN2UwNTI3ZmFlODU0OTcwOWZjMTkwZDExYWE3YjFmNzgzY2FmNDAyMjI)
and [join us on Slack](https://cloud-hypervisor.slack.com/).
## Mailing list
Please report bugs using the [GitHub issue
tracker](https://github.com/cloud-hypervisor/cloud-hypervisor/issues) but for
broader community discussions you may use our [mailing
list](https://lists.cloudhypervisor.org/g/dev/).
## 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

View File

@@ -2,7 +2,7 @@
name = "acpi_tables"
version = "0.1.0"
authors = ["The Cloud Hypervisor Authors"]
edition = "2021"
edition = "2018"
[dependencies]
vm-memory = "0.9.0"
vm-memory = "0.7.0"

View File

@@ -68,7 +68,7 @@ impl Aml for Path {
};
for part in self.name_parts.clone().iter_mut() {
bytes.extend_from_slice(part.as_ref());
bytes.extend_from_slice(&part.to_vec());
}
}
}

View File

@@ -4,7 +4,6 @@
//
#[repr(packed)]
#[derive(Clone, Copy)]
pub struct GenericAddress {
pub address_space_id: u8,
pub register_bit_width: u8,

View File

@@ -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.10.0"
vmm-sys-util = "0.9.0"

View File

@@ -101,10 +101,6 @@ fn parse_http_response(socket: &mut dyn Read) -> Result<Option<String>, Error> {
loop {
let mut bytes = vec![0; 256];
let count = socket.read(&mut bytes).map_err(Error::Socket)?;
// If the return value is 0, the peer has performed an orderly shutdown.
if count == 0 {
break;
}
res.push_str(std::str::from_utf8(&bytes[0..count]).unwrap());
// End of headers
@@ -131,7 +127,7 @@ fn parse_http_response(socket: &mut dyn Read) -> Result<Option<String>, Error> {
}
}
}
let body_string = content_length.and(body_offset.map(|o| String::from(&res[o..])));
let body_string = content_length.and(Some(String::from(&res[body_offset.unwrap()..])));
let status_code = get_status_code(&res)?;
if status_code.is_server_error() {
@@ -141,20 +137,18 @@ fn parse_http_response(socket: &mut dyn Read) -> Result<Option<String>, Error> {
}
}
/// Make an API request using the fully qualified command name.
/// For example, full_command could be "vm.create" or "vmm.ping".
pub fn simple_api_full_command_with_fds_and_response<T: Read + Write + ScmSocket>(
pub fn simple_api_command_with_fds<T: Read + Write + ScmSocket>(
socket: &mut T,
method: &str,
full_command: &str,
c: &str,
request_body: Option<&str>,
request_fds: Vec<RawFd>,
) -> Result<Option<String>, Error> {
) -> Result<(), Error> {
socket
.send_with_fds(
&[format!(
"{} /api/v1/{} HTTP/1.1\r\nHost: localhost\r\nAccept: */*\r\n",
method, full_command
"{} /api/v1/vm.{} HTTP/1.1\r\nHost: localhost\r\nAccept: */*\r\n",
method, c
)
.as_bytes()],
&request_fds,
@@ -177,69 +171,12 @@ pub fn simple_api_full_command_with_fds_and_response<T: Read + Write + ScmSocket
socket.flush().map_err(Error::Socket)?;
parse_http_response(socket)
}
pub fn simple_api_full_command_with_fds<T: Read + Write + ScmSocket>(
socket: &mut T,
method: &str,
full_command: &str,
request_body: Option<&str>,
request_fds: Vec<RawFd>,
) -> Result<(), Error> {
let response = simple_api_full_command_with_fds_and_response(
socket,
method,
full_command,
request_body,
request_fds,
)?;
if response.is_some() {
println!("{}", response.unwrap());
if let Some(body) = parse_http_response(socket)? {
println!("{}", body);
}
Ok(())
}
pub fn simple_api_full_command<T: Read + Write + ScmSocket>(
socket: &mut T,
method: &str,
full_command: &str,
request_body: Option<&str>,
) -> Result<(), Error> {
simple_api_full_command_with_fds(socket, method, full_command, request_body, Vec::new())
}
pub fn simple_api_full_command_and_response<T: Read + Write + ScmSocket>(
socket: &mut T,
method: &str,
full_command: &str,
request_body: Option<&str>,
) -> Result<Option<String>, Error> {
simple_api_full_command_with_fds_and_response(
socket,
method,
full_command,
request_body,
Vec::new(),
)
}
pub fn simple_api_command_with_fds<T: Read + Write + ScmSocket>(
socket: &mut T,
method: &str,
c: &str,
request_body: Option<&str>,
request_fds: Vec<RawFd>,
) -> Result<(), Error> {
// Create the full VM command. For VMM commands, use
// simple_api_full_command().
let full_command = format!("vm.{}", c);
simple_api_full_command_with_fds(socket, method, &full_command, request_body, request_fds)
}
pub fn simple_api_command<T: Read + Write + ScmSocket>(
socket: &mut T,
method: &str,

View File

@@ -2,28 +2,30 @@
name = "arch"
version = "0.1.0"
authors = ["The Chromium OS Authors"]
edition = "2021"
edition = "2018"
[features]
default = []
acpi = ["acpi_tables"]
tdx = []
[dependencies]
anyhow = "1.0.65"
acpi_tables = { path = "../acpi_tables", optional = true }
anyhow = "1.0.52"
byteorder = "1.4.3"
hypervisor = { path = "../hypervisor" }
libc = "0.2.133"
linux-loader = { version = "0.6.0", features = ["elf", "bzimage", "pe"] }
log = "0.4.17"
serde = { version = "1.0.145", features = ["rc", "derive"] }
thiserror = "1.0.37"
uuid = "1.1.2"
libc = "0.2.112"
linux-loader = { version = "0.4.0", features = ["elf", "bzimage", "pe"] }
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.9.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.10.0", features = ["with-serde"] }
vmm-sys-util = { version = "0.9.0", features = ["with-serde"] }
[target.'cfg(target_arch = "aarch64")'.dependencies]
fdt_parser = { version = "0.1.4", package = "fdt" }
fdt_parser = { version = "0.1.3", package = 'fdt'}
vm-fdt = { git = "https://github.com/rust-vmm/vm-fdt", branch = "main" }

View File

@@ -8,24 +8,24 @@
use crate::{NumaNodes, PciSpaceInfo};
use byteorder::{BigEndian, ByteOrder};
use hypervisor::arch::aarch64::gic::Vgic;
use std::cmp;
use std::collections::HashMap;
use std::ffi::CStr;
use std::fmt::Debug;
use std::result;
use std::str;
use std::sync::{Arc, Mutex};
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;
@@ -51,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;
@@ -91,12 +86,11 @@ pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHash
vcpu_mpidr: Vec<u64>,
vcpu_topology: Option<(u8, u8, u8)>,
device_info: &HashMap<(DeviceType, String), T, S>,
gic_device: &Arc<Mutex<dyn Vgic>>,
gic_device: &dyn GicDevice,
initrd: &Option<InitramfsConfig>,
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();
@@ -120,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)?;
@@ -141,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(())
}
@@ -160,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);
@@ -188,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 {
@@ -201,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)?;
@@ -230,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(())
@@ -316,18 +315,18 @@ fn create_chosen_node(
Ok(())
}
fn create_gic_node(fdt: &mut FdtWriter, gic_device: &Arc<Mutex<dyn Vgic>>) -> FdtWriterResult<()> {
let gic_reg_prop = gic_device.lock().unwrap().device_properties();
fn create_gic_node(fdt: &mut FdtWriter, gic_device: &dyn GicDevice) -> FdtWriterResult<()> {
let gic_reg_prop = gic_device.device_properties();
let intc_node = fdt.begin_node("intc")?;
fdt.property_string("compatible", gic_device.lock().unwrap().fdt_compatibility())?;
fdt.property_string("compatible", gic_device.fdt_compatibility())?;
fdt.property_null("interrupt-controller")?;
// "interrupt-cells" field specifies the number of cells needed to encode an
// interrupt source. The type shall be a <u32> and the value shall be 3 if no PPI affinity description
// is required.
fdt.property_u32("#interrupt-cells", 3)?;
fdt.property_array_u64("reg", &gic_reg_prop)?;
fdt.property_array_u64("reg", gic_reg_prop)?;
fdt.property_u32("phandle", GIC_PHANDLE)?;
fdt.property_u32("#address-cells", 2)?;
fdt.property_u32("#size-cells", 2)?;
@@ -335,18 +334,18 @@ fn create_gic_node(fdt: &mut FdtWriter, gic_device: &Arc<Mutex<dyn Vgic>>) -> Fd
let gic_intr_prop = [
GIC_FDT_IRQ_TYPE_PPI,
gic_device.lock().unwrap().fdt_maint_irq(),
gic_device.fdt_maint_irq(),
IRQ_TYPE_LEVEL_HI,
];
fdt.property_array_u32("interrupts", &gic_intr_prop)?;
if gic_device.lock().unwrap().msi_compatible() {
if gic_device.msi_compatible() {
let msic_node = fdt.begin_node("msic")?;
fdt.property_string("compatible", gic_device.lock().unwrap().msi_compatibility())?;
fdt.property_string("compatible", gic_device.msi_compatibility())?;
fdt.property_null("msi-controller")?;
fdt.property_u32("phandle", MSI_PHANDLE)?;
let msi_reg_prop = gic_device.lock().unwrap().msi_properties();
fdt.property_array_u64("reg", &msi_reg_prop)?;
let msi_reg_prop = gic_device.msi_properties();
fdt.property_array_u64("reg", msi_reg_prop)?;
fdt.end_node(msic_node)?;
}
@@ -541,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],
@@ -574,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 =

View File

@@ -1,12 +1,12 @@
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use crate::arch::aarch64::gic::{Error, Result};
use crate::device::HypervisorDeviceError;
use crate::kvm::kvm_bindings::{
use super::{Error, Result};
use crate::layout::IRQ_BASE;
use hypervisor::kvm::kvm_bindings::{
kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
};
use kvm_ioctls::DeviceFd;
use std::sync::Arc;
/*
Distributor registers as detailed at page 456 from
@@ -77,7 +77,12 @@ static VGIC_DIST_REGS: &[DistReg] = &[
VGIC_DIST_REG!(GICD_IPRIORITYR, 8, 0),
];
fn dist_attr_access(gic: &DeviceFd, offset: u32, val: &u32, set: bool) -> Result<()> {
fn dist_attr_access(
gic: &Arc<dyn hypervisor::Device>,
offset: u32,
val: &u32,
set: bool,
) -> Result<()> {
let mut gic_dist_attr = kvm_device_attr {
group: KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
attr: offset as u64,
@@ -85,30 +90,28 @@ fn dist_attr_access(gic: &DeviceFd, offset: u32, val: &u32, set: bool) -> Result
flags: 0,
};
if set {
gic.set_device_attr(&gic_dist_attr).map_err(|e| {
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
})?;
gic.set_device_attr(&gic_dist_attr)
.map_err(Error::SetDeviceAttribute)?;
} else {
gic.get_device_attr(&mut gic_dist_attr).map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;
gic.get_device_attr(&mut gic_dist_attr)
.map_err(Error::GetDeviceAttribute)?;
}
Ok(())
}
/// Get the distributor control register.
pub fn read_ctlr(gic: &DeviceFd) -> Result<u32> {
pub fn read_ctlr(gic: &Arc<dyn hypervisor::Device>) -> Result<u32> {
let val: u32 = 0;
dist_attr_access(gic, GICD_CTLR, &val, false)?;
Ok(val)
}
/// Set the distributor control register.
pub fn write_ctlr(gic: &DeviceFd, val: u32) -> Result<()> {
pub fn write_ctlr(gic: &Arc<dyn hypervisor::Device>, val: u32) -> Result<()> {
dist_attr_access(gic, GICD_CTLR, &val, true)
}
fn get_interrupts_num(gic: &DeviceFd) -> Result<u32> {
fn get_interrupts_num(gic: &Arc<dyn hypervisor::Device>) -> Result<u32> {
let num_irq = 0;
let mut nr_irqs_attr = kvm_device_attr {
@@ -117,18 +120,12 @@ fn get_interrupts_num(gic: &DeviceFd) -> Result<u32> {
addr: &num_irq as *const u32 as u64,
flags: 0,
};
gic.get_device_attr(&mut nr_irqs_attr).map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;
gic.get_device_attr(&mut nr_irqs_attr)
.map_err(Error::GetDeviceAttribute)?;
Ok(num_irq)
}
fn compute_reg_len(gic: &DeviceFd, reg: &DistReg, base: u32) -> Result<u32> {
// FIXME:
// Redefine some GIC constants to avoid the dependency on `layout` crate.
// This is temporary solution, will be fixed in future refactoring.
const LAYOUT_IRQ_BASE: u32 = 32;
fn compute_reg_len(gic: &Arc<dyn hypervisor::Device>, reg: &DistReg, base: u32) -> Result<u32> {
let mut end = base;
let num_irq = get_interrupts_num(gic)?;
if reg.length > 0 {
@@ -141,8 +138,8 @@ fn compute_reg_len(gic: &DeviceFd, reg: &DistReg, base: u32) -> Result<u32> {
// This is the type of register that takes into account the number of interrupts
// that the model has. It is also the type of register where
// a register relates to multiple interrupts.
end = base + (reg.bpi as u32 * (num_irq - LAYOUT_IRQ_BASE) / 8);
if reg.bpi as u32 * (num_irq - LAYOUT_IRQ_BASE) % 8 > 0 {
end = base + (reg.bpi as u32 * (num_irq - IRQ_BASE) / 8);
if reg.bpi as u32 * (num_irq - IRQ_BASE) % 8 > 0 {
end += REG_SIZE as u32;
}
}
@@ -150,7 +147,7 @@ fn compute_reg_len(gic: &DeviceFd, reg: &DistReg, base: u32) -> Result<u32> {
}
/// Set distributor registers of the GIC.
pub fn set_dist_regs(gic: &DeviceFd, state: &[u32]) -> Result<()> {
pub fn set_dist_regs(gic: &Arc<dyn hypervisor::Device>, state: &[u32]) -> Result<()> {
let mut idx = 0;
for dreg in VGIC_DIST_REGS {
@@ -167,7 +164,7 @@ pub fn set_dist_regs(gic: &DeviceFd, state: &[u32]) -> Result<()> {
Ok(())
}
/// Get distributor registers of the GIC.
pub fn get_dist_regs(gic: &DeviceFd) -> Result<Vec<u32>> {
pub fn get_dist_regs(gic: &Arc<dyn hypervisor::Device>) -> Result<Vec<u32>> {
let mut state = Vec::new();
for dreg in VGIC_DIST_REGS {

View File

@@ -0,0 +1,261 @@
// Copyright 2021 Arm Limited (or its affiliates). All rights reserved.
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// This file implements the GicV3 device.
pub mod kvm {
use crate::aarch64::gic::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
use crate::aarch64::gic::icc_regs::{get_icc_regs, set_icc_regs};
use crate::aarch64::gic::kvm::{save_pending_tables, KvmGicDevice};
use crate::aarch64::gic::redist_regs::{
construct_gicr_typers, get_redist_regs, set_redist_regs,
};
use crate::aarch64::gic::GicDevice;
use crate::layout;
use anyhow::anyhow;
use hypervisor::kvm::kvm_bindings;
use hypervisor::CpuState;
use std::any::Any;
use std::convert::TryInto;
use std::sync::Arc;
use std::{boxed::Box, result};
use versionize::{VersionMap, Versionize, VersionizeResult};
use versionize_derive::Versionize;
use vm_migration::{
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable,
VersionMapped,
};
/// Errors thrown while saving/restoring the GICv3.
#[derive(Debug)]
pub enum Error {
/// Error in saving RDIST pending tables into guest RAM.
SavePendingTables(crate::aarch64::gic::Error),
/// Error in saving GIC distributor registers.
SaveDistributorRegisters(crate::aarch64::gic::Error),
/// Error in restoring GIC distributor registers.
RestoreDistributorRegisters(crate::aarch64::gic::Error),
/// Error in saving GIC distributor control registers.
SaveDistributorCtrlRegisters(crate::aarch64::gic::Error),
/// Error in restoring GIC distributor control registers.
RestoreDistributorCtrlRegisters(crate::aarch64::gic::Error),
/// Error in saving GIC redistributor registers.
SaveRedistributorRegisters(crate::aarch64::gic::Error),
/// Error in restoring GIC redistributor registers.
RestoreRedistributorRegisters(crate::aarch64::gic::Error),
/// Error in saving GIC CPU interface registers.
SaveIccRegisters(crate::aarch64::gic::Error),
/// Error in restoring GIC CPU interface registers.
RestoreIccRegisters(crate::aarch64::gic::Error),
}
type Result<T> = result::Result<T, Error>;
pub struct KvmGicV3 {
/// The hypervisor agnostic device for the GicV3
device: Arc<dyn hypervisor::Device>,
/// Vector holding values of GICR_TYPER for each vCPU
gicr_typers: Vec<u64>,
/// GIC device properties, to be used for setting up the fdt entry
properties: [u64; 4],
/// Number of CPUs handled by the device
vcpu_count: u64,
}
#[derive(Versionize)]
pub struct Gicv3State {
dist: Vec<u32>,
rdist: Vec<u32>,
icc: Vec<u32>,
// special register that enables interrupts and affinity routing
gicd_ctlr: u32,
}
impl VersionMapped for Gicv3State {}
impl KvmGicV3 {
// Device trees specific constants
pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
/// Get the address of the GIC distributor.
pub fn get_dist_addr() -> u64 {
layout::GIC_V3_DIST_START
}
/// Get the size of the GIC distributor.
pub fn get_dist_size() -> u64 {
layout::GIC_V3_DIST_SIZE
}
/// Get the address of the GIC redistributors.
pub fn get_redists_addr(vcpu_count: u64) -> u64 {
KvmGicV3::get_dist_addr() - KvmGicV3::get_redists_size(vcpu_count)
}
/// Get the size of the GIC redistributors.
pub fn get_redists_size(vcpu_count: u64) -> u64 {
vcpu_count * layout::GIC_V3_REDIST_SIZE
}
/// Save the state of GIC.
fn state(&self, gicr_typers: &[u64]) -> Result<Gicv3State> {
let gicd_ctlr =
read_ctlr(self.device()).map_err(Error::SaveDistributorCtrlRegisters)?;
let dist_state =
get_dist_regs(self.device()).map_err(Error::SaveDistributorRegisters)?;
let rdist_state = get_redist_regs(self.device(), gicr_typers)
.map_err(Error::SaveRedistributorRegisters)?;
let icc_state =
get_icc_regs(self.device(), gicr_typers).map_err(Error::SaveIccRegisters)?;
Ok(Gicv3State {
dist: dist_state,
rdist: rdist_state,
icc: icc_state,
gicd_ctlr,
})
}
/// Restore the state of GIC.
fn set_state(&mut self, gicr_typers: &[u64], state: &Gicv3State) -> Result<()> {
write_ctlr(self.device(), state.gicd_ctlr)
.map_err(Error::RestoreDistributorCtrlRegisters)?;
set_dist_regs(self.device(), &state.dist)
.map_err(Error::RestoreDistributorRegisters)?;
set_redist_regs(self.device(), gicr_typers, &state.rdist)
.map_err(Error::RestoreRedistributorRegisters)?;
set_icc_regs(self.device(), gicr_typers, &state.icc)
.map_err(Error::RestoreIccRegisters)?;
Ok(())
}
}
impl GicDevice for KvmGicV3 {
fn device(&self) -> &Arc<dyn hypervisor::Device> {
&self.device
}
fn fdt_compatibility(&self) -> &str {
"arm,gic-v3"
}
fn fdt_maint_irq(&self) -> u32 {
KvmGicV3::ARCH_GIC_V3_MAINT_IRQ
}
fn device_properties(&self) -> &[u64] {
&self.properties
}
fn vcpu_count(&self) -> u64 {
self.vcpu_count
}
fn set_its_device(&mut self, _its_device: Option<Arc<dyn hypervisor::Device>>) {}
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
let gicr_typers = construct_gicr_typers(vcpu_states);
self.gicr_typers = gicr_typers;
}
fn as_any_concrete_mut(&mut self) -> &mut dyn Any {
self
}
}
impl KvmGicDevice for KvmGicV3 {
fn version() -> u32 {
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3
}
fn create_device(
device: Arc<dyn hypervisor::Device>,
vcpu_count: u64,
) -> Box<dyn GicDevice> {
Box::new(KvmGicV3 {
device,
gicr_typers: vec![0; vcpu_count.try_into().unwrap()],
properties: [
KvmGicV3::get_dist_addr(),
KvmGicV3::get_dist_size(),
KvmGicV3::get_redists_addr(vcpu_count),
KvmGicV3::get_redists_size(vcpu_count),
],
vcpu_count,
})
}
fn init_device_attributes(
_vm: &Arc<dyn hypervisor::Vm>,
gic_device: &mut dyn GicDevice,
) -> crate::aarch64::gic::Result<()> {
/* Setting up the distributor attribute.
We are placing the GIC below 1GB so we need to substract the size of the distributor.
*/
Self::set_device_attribute(
gic_device.device(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST),
&KvmGicV3::get_dist_addr() as *const u64 as u64,
0,
)?;
/* Setting up the redistributors' attribute.
We are calculating here the start of the redistributors address. We have one per CPU.
*/
Self::set_device_attribute(
gic_device.device(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST),
&KvmGicV3::get_redists_addr(gic_device.vcpu_count()) as *const u64 as u64,
0,
)?;
Ok(())
}
}
pub const GIC_V3_SNAPSHOT_ID: &str = "gic-v3";
impl Snapshottable for KvmGicV3 {
fn id(&self) -> String {
GIC_V3_SNAPSHOT_ID.to_string()
}
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
let gicr_typers = self.gicr_typers.clone();
Snapshot::new_from_versioned_state(&self.id(), &self.state(&gicr_typers).unwrap())
}
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
let gicr_typers = self.gicr_typers.clone();
self.set_state(&gicr_typers, &snapshot.to_versioned_state(&self.id())?)
.map_err(|e| {
MigratableError::Restore(anyhow!("Could not restore GICv3 state {:?}", e))
})
}
}
impl Pausable for KvmGicV3 {
fn pause(&mut self) -> std::result::Result<(), MigratableError> {
// Flush redistributors pending tables to guest RAM.
save_pending_tables(self.device()).map_err(|e| {
MigratableError::Pause(anyhow!("Could not save GICv3 GIC pending tables {:?}", e))
})?;
Ok(())
}
}
impl Transportable for KvmGicV3 {}
impl Migratable for KvmGicV3 {}
}

View File

@@ -0,0 +1,514 @@
// Copyright 2021 Arm Limited (or its affiliates). All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//
// This file implements the GicV3 device with ITS (Virtual Interrupt Translation Service).
pub mod kvm {
use crate::aarch64::gic::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
use crate::aarch64::gic::icc_regs::{get_icc_regs, set_icc_regs};
use crate::aarch64::gic::redist_regs::{
construct_gicr_typers, get_redist_regs, set_redist_regs,
};
use crate::aarch64::gic::gicv3::kvm::KvmGicV3;
use crate::aarch64::gic::kvm::{save_pending_tables, KvmGicDevice};
use crate::aarch64::gic::GicDevice;
use crate::layout;
use anyhow::anyhow;
use hypervisor::kvm::kvm_bindings;
use hypervisor::CpuState;
use std::any::Any;
use std::convert::TryInto;
use std::sync::Arc;
use std::{boxed::Box, result};
use versionize::{VersionMap, Versionize, VersionizeResult};
use versionize_derive::Versionize;
use vm_migration::{
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable,
VersionMapped,
};
const GITS_CTLR: u32 = 0x0000;
const GITS_IIDR: u32 = 0x0004;
const GITS_CBASER: u32 = 0x0080;
const GITS_CWRITER: u32 = 0x0088;
const GITS_CREADR: u32 = 0x0090;
const GITS_BASER: u32 = 0x0100;
/// Errors thrown while saving/restoring the GICv3ITS.
#[derive(Debug)]
pub enum Error {
/// Error in saving RDIST pending tables into guest RAM.
SavePendingTables(crate::aarch64::gic::Error),
/// Error in saving GIC distributor registers.
SaveDistributorRegisters(crate::aarch64::gic::Error),
/// Error in restoring GIC distributor registers.
RestoreDistributorRegisters(crate::aarch64::gic::Error),
/// Error in saving GIC distributor control registers.
SaveDistributorCtrlRegisters(crate::aarch64::gic::Error),
/// Error in restoring GIC distributor control registers.
RestoreDistributorCtrlRegisters(crate::aarch64::gic::Error),
/// Error in saving GIC redistributor registers.
SaveRedistributorRegisters(crate::aarch64::gic::Error),
/// Error in restoring GIC redistributor registers.
RestoreRedistributorRegisters(crate::aarch64::gic::Error),
/// Error in saving GIC CPU interface registers.
SaveIccRegisters(crate::aarch64::gic::Error),
/// Error in restoring GIC CPU interface registers.
RestoreIccRegisters(crate::aarch64::gic::Error),
/// Error in saving GICv3ITS IIDR register.
SaveITSIIDR(crate::aarch64::gic::Error),
/// Error in restoring GICv3ITS IIDR register.
RestoreITSIIDR(crate::aarch64::gic::Error),
/// Error in saving GICv3ITS CBASER register.
SaveITSCBASER(crate::aarch64::gic::Error),
/// Error in restoring GICv3ITS CBASER register.
RestoreITSCBASER(crate::aarch64::gic::Error),
/// Error in saving GICv3ITS CREADR register.
SaveITSCREADR(crate::aarch64::gic::Error),
/// Error in restoring GICv3ITS CREADR register.
RestoreITSCREADR(crate::aarch64::gic::Error),
/// Error in saving GICv3ITS CWRITER register.
SaveITSCWRITER(crate::aarch64::gic::Error),
/// Error in restoring GICv3ITS CWRITER register.
RestoreITSCWRITER(crate::aarch64::gic::Error),
/// Error in saving GICv3ITS BASER register.
SaveITSBASER(crate::aarch64::gic::Error),
/// Error in restoring GICv3ITS BASER register.
RestoreITSBASER(crate::aarch64::gic::Error),
/// Error in saving GICv3ITS CTLR register.
SaveITSCTLR(crate::aarch64::gic::Error),
/// Error in restoring GICv3ITS CTLR register.
RestoreITSCTLR(crate::aarch64::gic::Error),
/// Error in saving GICv3ITS restore tables.
SaveITSTables(crate::aarch64::gic::Error),
/// Error in restoring GICv3ITS restore tables.
RestoreITSTables(crate::aarch64::gic::Error),
}
type Result<T> = result::Result<T, Error>;
/// Access an ITS device attribute.
///
/// This is a helper function to get/set the ITS device attribute depending
/// the bool parameter `set` provided.
pub fn gicv3_its_attr_access(
its_device: &Arc<dyn hypervisor::Device>,
group: u32,
attr: u32,
val: &u64,
set: bool,
) -> crate::aarch64::gic::Result<()> {
let mut gicv3_its_attr = kvm_bindings::kvm_device_attr {
group,
attr: attr as u64,
addr: val as *const u64 as u64,
flags: 0,
};
if set {
its_device
.set_device_attr(&gicv3_its_attr)
.map_err(crate::aarch64::gic::Error::SetDeviceAttribute)?;
} else {
its_device
.get_device_attr(&mut gicv3_its_attr)
.map_err(crate::aarch64::gic::Error::GetDeviceAttribute)?;
}
Ok(())
}
/// Function that saves/restores ITS tables into guest RAM.
///
/// The tables get flushed to guest RAM whenever the VM gets stopped.
pub fn gicv3_its_tables_access(
its_device: &Arc<dyn hypervisor::Device>,
save: bool,
) -> crate::aarch64::gic::Result<()> {
let attr = if save {
u64::from(kvm_bindings::KVM_DEV_ARM_ITS_SAVE_TABLES)
} else {
u64::from(kvm_bindings::KVM_DEV_ARM_ITS_RESTORE_TABLES)
};
let init_gic_attr = kvm_bindings::kvm_device_attr {
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
attr,
addr: 0,
flags: 0,
};
its_device
.set_device_attr(&init_gic_attr)
.map_err(crate::aarch64::gic::Error::SetDeviceAttribute)
}
pub struct KvmGicV3Its {
/// The hypervisor agnostic device for the GicV3
device: Arc<dyn hypervisor::Device>,
/// The hypervisor agnostic device for the Its device
its_device: Option<Arc<dyn hypervisor::Device>>,
/// Vector holding values of GICR_TYPER for each vCPU
gicr_typers: Vec<u64>,
/// GIC device properties, to be used for setting up the fdt entry
gic_properties: [u64; 4],
/// MSI device properties, to be used for setting up the fdt entry
msi_properties: [u64; 2],
/// Number of CPUs handled by the device
vcpu_count: u64,
}
#[derive(Versionize)]
pub struct Gicv3ItsState {
dist: Vec<u32>,
rdist: Vec<u32>,
icc: Vec<u32>,
// special register that enables interrupts and affinity routing
gicd_ctlr: u32,
its_ctlr: u64,
its_iidr: u64,
its_cbaser: u64,
its_cwriter: u64,
its_creadr: u64,
its_baser: [u64; 8],
}
impl VersionMapped for Gicv3ItsState {}
impl KvmGicV3Its {
fn get_msi_size() -> u64 {
layout::GIC_V3_ITS_SIZE
}
fn get_msi_addr(vcpu_count: u64) -> u64 {
KvmGicV3::get_redists_addr(vcpu_count) - KvmGicV3Its::get_msi_size()
}
/// Save the state of GICv3ITS.
fn state(&self, gicr_typers: &[u64]) -> Result<Gicv3ItsState> {
let gicd_ctlr =
read_ctlr(self.device()).map_err(Error::SaveDistributorCtrlRegisters)?;
let dist_state =
get_dist_regs(self.device()).map_err(Error::SaveDistributorRegisters)?;
let rdist_state = get_redist_regs(self.device(), gicr_typers)
.map_err(Error::SaveRedistributorRegisters)?;
let icc_state =
get_icc_regs(self.device(), gicr_typers).map_err(Error::SaveIccRegisters)?;
let its_baser_state: [u64; 8] = [0; 8];
for i in 0..8 {
gicv3_its_attr_access(
self.its_device().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_BASER + i * 8,
&its_baser_state[i as usize],
false,
)
.map_err(Error::SaveITSBASER)?;
}
let its_ctlr_state: u64 = 0;
gicv3_its_attr_access(
self.its_device().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CTLR,
&its_ctlr_state,
false,
)
.map_err(Error::SaveITSCTLR)?;
let its_cbaser_state: u64 = 0;
gicv3_its_attr_access(
self.its_device().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CBASER,
&its_cbaser_state,
false,
)
.map_err(Error::SaveITSCBASER)?;
let its_creadr_state: u64 = 0;
gicv3_its_attr_access(
self.its_device().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CREADR,
&its_creadr_state,
false,
)
.map_err(Error::SaveITSCREADR)?;
let its_cwriter_state: u64 = 0;
gicv3_its_attr_access(
self.its_device().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CWRITER,
&its_cwriter_state,
false,
)
.map_err(Error::SaveITSCWRITER)?;
let its_iidr_state: u64 = 0;
gicv3_its_attr_access(
self.its_device().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_IIDR,
&its_iidr_state,
false,
)
.map_err(Error::SaveITSIIDR)?;
Ok(Gicv3ItsState {
dist: dist_state,
rdist: rdist_state,
icc: icc_state,
gicd_ctlr,
its_ctlr: its_ctlr_state,
its_iidr: its_iidr_state,
its_cbaser: its_cbaser_state,
its_cwriter: its_cwriter_state,
its_creadr: its_creadr_state,
its_baser: its_baser_state,
})
}
/// Restore the state of GICv3ITS.
fn set_state(&mut self, gicr_typers: &[u64], state: &Gicv3ItsState) -> Result<()> {
write_ctlr(self.device(), state.gicd_ctlr)
.map_err(Error::RestoreDistributorCtrlRegisters)?;
set_dist_regs(self.device(), &state.dist)
.map_err(Error::RestoreDistributorRegisters)?;
set_redist_regs(self.device(), gicr_typers, &state.rdist)
.map_err(Error::RestoreRedistributorRegisters)?;
set_icc_regs(self.device(), gicr_typers, &state.icc)
.map_err(Error::RestoreIccRegisters)?;
//Restore GICv3ITS registers
gicv3_its_attr_access(
self.its_device().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_IIDR,
&state.its_iidr,
true,
)
.map_err(Error::RestoreITSIIDR)?;
gicv3_its_attr_access(
self.its_device().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CBASER,
&state.its_cbaser,
true,
)
.map_err(Error::RestoreITSCBASER)?;
gicv3_its_attr_access(
self.its_device().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CREADR,
&state.its_creadr,
true,
)
.map_err(Error::RestoreITSCREADR)?;
gicv3_its_attr_access(
self.its_device().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CWRITER,
&state.its_cwriter,
true,
)
.map_err(Error::RestoreITSCWRITER)?;
for i in 0..8 {
gicv3_its_attr_access(
self.its_device().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_BASER + i * 8,
&state.its_baser[i as usize],
true,
)
.map_err(Error::RestoreITSBASER)?;
}
// Restore ITS tables
gicv3_its_tables_access(self.its_device().unwrap(), false)
.map_err(Error::RestoreITSTables)?;
gicv3_its_attr_access(
self.its_device().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CTLR,
&state.its_ctlr,
true,
)
.map_err(Error::RestoreITSCTLR)?;
Ok(())
}
}
impl GicDevice for KvmGicV3Its {
fn device(&self) -> &Arc<dyn hypervisor::Device> {
&self.device
}
fn its_device(&self) -> Option<&Arc<dyn hypervisor::Device>> {
self.its_device.as_ref()
}
fn fdt_compatibility(&self) -> &str {
"arm,gic-v3"
}
fn msi_compatible(&self) -> bool {
true
}
fn msi_compatibility(&self) -> &str {
"arm,gic-v3-its"
}
fn fdt_maint_irq(&self) -> u32 {
KvmGicV3::ARCH_GIC_V3_MAINT_IRQ
}
fn msi_properties(&self) -> &[u64] {
&self.msi_properties
}
fn device_properties(&self) -> &[u64] {
&self.gic_properties
}
fn vcpu_count(&self) -> u64 {
self.vcpu_count
}
fn set_its_device(&mut self, its_device: Option<Arc<dyn hypervisor::Device>>) {
self.its_device = its_device;
}
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
let gicr_typers = construct_gicr_typers(vcpu_states);
self.gicr_typers = gicr_typers;
}
fn as_any_concrete_mut(&mut self) -> &mut dyn Any {
self
}
}
impl KvmGicDevice for KvmGicV3Its {
fn version() -> u32 {
KvmGicV3::version()
}
fn create_device(
device: Arc<dyn hypervisor::Device>,
vcpu_count: u64,
) -> Box<dyn GicDevice> {
Box::new(KvmGicV3Its {
device,
its_device: None,
gicr_typers: vec![0; vcpu_count.try_into().unwrap()],
gic_properties: [
KvmGicV3::get_dist_addr(),
KvmGicV3::get_dist_size(),
KvmGicV3::get_redists_addr(vcpu_count),
KvmGicV3::get_redists_size(vcpu_count),
],
msi_properties: [
KvmGicV3Its::get_msi_addr(vcpu_count),
KvmGicV3Its::get_msi_size(),
],
vcpu_count,
})
}
fn init_device_attributes(
vm: &Arc<dyn hypervisor::Vm>,
gic_device: &mut dyn GicDevice,
) -> crate::aarch64::gic::Result<()> {
KvmGicV3::init_device_attributes(vm, gic_device)?;
let mut its_device = kvm_bindings::kvm_create_device {
type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS,
fd: 0,
flags: 0,
};
let its_fd = vm
.create_device(&mut its_device)
.map_err(crate::aarch64::gic::Error::CreateGic)?;
Self::set_device_attribute(
&its_fd,
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE),
&KvmGicV3Its::get_msi_addr(gic_device.vcpu_count()) as *const u64 as u64,
0,
)?;
Self::set_device_attribute(
&its_fd,
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
0,
0,
)?;
gic_device.set_its_device(Some(its_fd));
Ok(())
}
}
pub const GIC_V3_ITS_SNAPSHOT_ID: &str = "gic-v3-its";
impl Snapshottable for KvmGicV3Its {
fn id(&self) -> String {
GIC_V3_ITS_SNAPSHOT_ID.to_string()
}
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
let gicr_typers = self.gicr_typers.clone();
Snapshot::new_from_versioned_state(&self.id(), &self.state(&gicr_typers).unwrap())
}
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
let gicr_typers = self.gicr_typers.clone();
self.set_state(&gicr_typers, &snapshot.to_versioned_state(&self.id())?)
.map_err(|e| {
MigratableError::Restore(anyhow!("Could not restore GICv3ITS state {:?}", e))
})
}
}
impl Pausable for KvmGicV3Its {
fn pause(&mut self) -> std::result::Result<(), MigratableError> {
// Flush redistributors pending tables to guest RAM.
save_pending_tables(self.device()).map_err(|e| {
MigratableError::Pause(anyhow!(
"Could not save GICv3ITS GIC pending tables {:?}",
e
))
})?;
// Flush ITS tables to guest RAM.
gicv3_its_tables_access(self.its_device().unwrap(), true).map_err(|e| {
MigratableError::Pause(anyhow!("Could not save GICv3ITS ITS tables {:?}", e))
})?;
Ok(())
}
}
impl Transportable for KvmGicV3Its {}
impl Migratable for KvmGicV3Its {}
}

View File

@@ -1,16 +1,14 @@
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use crate::arch::aarch64::gic::{Error, Result};
use crate::device::HypervisorDeviceError;
use crate::kvm::kvm_bindings::{
use super::{Error, Result};
use hypervisor::kvm::kvm_bindings::{
kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, KVM_REG_ARM64_SYSREG_CRM_MASK,
KVM_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRN_MASK, KVM_REG_ARM64_SYSREG_CRN_SHIFT,
KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP1_MASK,
KVM_REG_ARM64_SYSREG_OP1_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_SHIFT,
};
use kvm_ioctls::DeviceFd;
use std::sync::Arc;
const KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT: u32 = 32;
const KVM_DEV_ARM_VGIC_V3_MPIDR_MASK: u64 = 0xffffffff << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT as u64;
@@ -79,7 +77,13 @@ static VGIC_ICC_REGS: &[u64] = &[
SYS_ICC_AP1R3_EL1,
];
fn icc_attr_access(gic: &DeviceFd, offset: u64, typer: u64, val: &u32, set: bool) -> Result<()> {
fn icc_attr_access(
gic: &Arc<dyn hypervisor::Device>,
offset: u64,
typer: u64,
val: &u32,
set: bool,
) -> Result<()> {
let mut gic_icc_attr = kvm_device_attr {
group: KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
attr: ((typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | offset), // this needs the mpidr
@@ -87,19 +91,17 @@ fn icc_attr_access(gic: &DeviceFd, offset: u64, typer: u64, val: &u32, set: bool
flags: 0,
};
if set {
gic.set_device_attr(&gic_icc_attr).map_err(|e| {
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
})?;
gic.set_device_attr(&gic_icc_attr)
.map_err(Error::SetDeviceAttribute)?;
} else {
gic.get_device_attr(&mut gic_icc_attr).map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;
gic.get_device_attr(&mut gic_icc_attr)
.map_err(Error::GetDeviceAttribute)?;
}
Ok(())
}
/// Get ICC registers.
pub fn get_icc_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
pub fn get_icc_regs(gic: &Arc<dyn hypervisor::Device>, gicr_typer: &[u64]) -> Result<Vec<u32>> {
let mut state: Vec<u32> = Vec::new();
// We need this for the ICC_AP<m>R<n>_EL1 registers.
let mut num_priority_bits = 0;
@@ -152,7 +154,11 @@ pub fn get_icc_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
}
/// Set ICC registers.
pub fn set_icc_regs(gic: &DeviceFd, gicr_typer: &[u64], state: &[u32]) -> Result<()> {
pub fn set_icc_regs(
gic: &Arc<dyn hypervisor::Device>,
gicr_typer: &[u64],
state: &[u32],
) -> Result<()> {
let mut num_priority_bits = 0;
let mut idx = 0;
for ix in gicr_typer {

220
arch/src/aarch64/gic/mod.rs Normal file
View File

@@ -0,0 +1,220 @@
// Copyright 2021 Arm Limited (or its affiliates). All rights reserved.
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
pub mod dist_regs;
pub mod gicv3;
pub mod gicv3_its;
pub mod icc_regs;
pub mod redist_regs;
pub use self::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
pub use self::icc_regs::{get_icc_regs, set_icc_regs};
pub use self::redist_regs::{get_redist_regs, set_redist_regs};
use hypervisor::CpuState;
use std::any::Any;
use std::result;
use std::sync::Arc;
/// Errors thrown while setting up the GIC.
#[derive(Debug)]
pub enum Error {
/// Error while calling KVM ioctl for setting up the global interrupt controller.
CreateGic(hypervisor::HypervisorVmError),
/// Error while setting device attributes for the GIC.
SetDeviceAttribute(hypervisor::HypervisorDeviceError),
/// Error while getting device attributes for the GIC.
GetDeviceAttribute(hypervisor::HypervisorDeviceError),
}
type Result<T> = result::Result<T, Error>;
pub trait GicDevice: Send {
/// Returns the hypervisor agnostic Device of the GIC device
fn device(&self) -> &Arc<dyn hypervisor::Device>;
/// Returns the hypervisor agnostic Device of the ITS device
fn its_device(&self) -> Option<&Arc<dyn hypervisor::Device>> {
None
}
/// Returns the fdt compatibility property of the device
fn fdt_compatibility(&self) -> &str;
/// Returns the maint_irq fdt property of the device
fn fdt_maint_irq(&self) -> u32;
/// Returns an array with GIC device properties
fn device_properties(&self) -> &[u64];
/// Returns the number of vCPUs this GIC handles
fn vcpu_count(&self) -> u64;
/// Returns whether the GIC device is MSI compatible or not
fn msi_compatible(&self) -> bool {
false
}
/// Returns the MSI compatibility property of the device
fn msi_compatibility(&self) -> &str {
""
}
/// Returns the MSI reg property of the device
fn msi_properties(&self) -> &[u64] {
&[]
}
fn set_its_device(&mut self, its_device: Option<Arc<dyn hypervisor::Device>>);
/// Get the values of GICR_TYPER for each vCPU.
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]);
/// Downcast the trait object to its concrete type.
fn as_any_concrete_mut(&mut self) -> &mut dyn Any;
}
pub mod kvm {
use super::GicDevice;
use super::Result;
use crate::aarch64::gic::gicv3_its::kvm::KvmGicV3Its;
use crate::layout;
use hypervisor::kvm::kvm_bindings;
use std::boxed::Box;
use std::sync::Arc;
/// Trait for GIC devices.
pub trait KvmGicDevice: Send + Sync + GicDevice {
/// Returns the GIC version of the device
fn version() -> u32;
/// Create the GIC device object
fn create_device(
device: Arc<dyn hypervisor::Device>,
vcpu_count: u64,
) -> Box<dyn GicDevice>;
/// Setup the device-specific attributes
fn init_device_attributes(
vm: &Arc<dyn hypervisor::Vm>,
gic_device: &mut dyn GicDevice,
) -> Result<()>;
/// Initialize a GIC device
fn init_device(vm: &Arc<dyn hypervisor::Vm>) -> Result<Arc<dyn hypervisor::Device>> {
let mut gic_device = kvm_bindings::kvm_create_device {
type_: Self::version(),
fd: 0,
flags: 0,
};
vm.create_device(&mut gic_device)
.map_err(super::Error::CreateGic)
}
/// Set a GIC device attribute
fn set_device_attribute(
device: &Arc<dyn hypervisor::Device>,
group: u32,
attr: u64,
addr: u64,
flags: u32,
) -> Result<()> {
let attr = kvm_bindings::kvm_device_attr {
flags,
group,
attr,
addr,
};
device
.set_device_attr(&attr)
.map_err(super::Error::SetDeviceAttribute)?;
Ok(())
}
/// Get a GIC device attribute
fn get_device_attribute(
device: &Arc<dyn hypervisor::Device>,
group: u32,
attr: u64,
addr: u64,
flags: u32,
) -> Result<()> {
let mut attr = kvm_bindings::kvm_device_attr {
flags,
group,
attr,
addr,
};
device
.get_device_attr(&mut attr)
.map_err(super::Error::GetDeviceAttribute)?;
Ok(())
}
/// Finalize the setup of a GIC device
fn finalize_device(gic_device: &dyn GicDevice) -> Result<()> {
/* We need to tell the kernel how many irqs to support with this vgic.
* See the `layout` module for details.
*/
let nr_irqs: u32 = layout::IRQ_NUM;
let nr_irqs_ptr = &nr_irqs as *const u32;
Self::set_device_attribute(
gic_device.device(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
0,
nr_irqs_ptr as u64,
0,
)?;
/* Finalize the GIC.
* See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
*/
Self::set_device_attribute(
gic_device.device(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
0,
0,
)?;
Ok(())
}
/// Method to initialize the GIC device
#[allow(clippy::new_ret_no_self)]
fn new(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GicDevice>> {
let vgic_fd = Self::init_device(vm)?;
let mut device = Self::create_device(vgic_fd, vcpu_count);
Self::init_device_attributes(vm, &mut *device)?;
Self::finalize_device(&*device)?;
Ok(device)
}
}
/// Create a GICv3-ITS device.
///
pub fn create_gic(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GicDevice>> {
debug!("creating a GICv3-ITS");
KvmGicV3Its::new(vm, vcpu_count)
}
/// Function that saves RDIST pending tables into guest RAM.
///
/// The tables get flushed to guest RAM whenever the VM gets stopped.
pub fn save_pending_tables(gic: &Arc<dyn hypervisor::Device>) -> Result<()> {
let init_gic_attr = kvm_bindings::kvm_device_attr {
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES),
addr: 0,
flags: 0,
};
gic.set_device_attr(&init_gic_attr)
.map_err(super::Error::SetDeviceAttribute)
}
}

View File

@@ -1,18 +1,10 @@
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use crate::arch::aarch64::gic::{Error, Result};
use crate::device::HypervisorDeviceError;
use crate::kvm::kvm_bindings::{
kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, KVM_REG_ARM64, KVM_REG_ARM64_SYSREG,
KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK,
KVM_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_SIZE_U64,
};
use crate::kvm::Register;
use crate::kvm::VcpuKvmState;
use crate::CpuState;
use kvm_ioctls::DeviceFd;
use super::{Error, Result};
use hypervisor::kvm::kvm_bindings::{kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS};
use hypervisor::CpuState;
use std::sync::Arc;
// Relevant redistributor registers that we want to save/restore.
const GICR_CTLR: u32 = 0x0000;
@@ -39,12 +31,6 @@ const GICR_ICFGR0: u32 = GICR_SGI_OFFSET + 0x0C00;
const KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT: u32 = 32;
const KVM_DEV_ARM_VGIC_V3_MPIDR_MASK: u64 = 0xffffffff << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT as u64;
const KVM_ARM64_SYSREG_MPIDR_EL1: u64 = KVM_REG_ARM64 as u64
| KVM_REG_SIZE_U64 as u64
| KVM_REG_ARM64_SYSREG as u64
| (((3_u64) << KVM_REG_ARM64_SYSREG_OP0_SHIFT) & KVM_REG_ARM64_SYSREG_OP0_MASK as u64)
| (((5_u64) << KVM_REG_ARM64_SYSREG_OP2_SHIFT) & KVM_REG_ARM64_SYSREG_OP2_MASK as u64);
/// This is how we represent the registers of a distributor.
/// It is relrvant their offset from the base address of the
/// distributor.
@@ -96,27 +82,31 @@ static VGIC_SGI_REGS: &[RdistReg] = &[
VGIC_RDIST_REG!(GICR_IPRIORITYR0, 32),
];
fn redist_attr_access(gic: &DeviceFd, offset: u32, typer: u64, val: &u32, set: bool) -> Result<()> {
let mut gic_redist_attr = kvm_device_attr {
fn redist_attr_access(
gic: &Arc<dyn hypervisor::Device>,
offset: u32,
typer: u64,
val: &u32,
set: bool,
) -> Result<()> {
let mut gic_dist_attr = kvm_device_attr {
group: KVM_DEV_ARM_VGIC_GRP_REDIST_REGS,
attr: (typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | (offset as u64), // this needs the mpidr
addr: val as *const u32 as u64,
flags: 0,
};
if set {
gic.set_device_attr(&gic_redist_attr).map_err(|e| {
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
})?;
gic.set_device_attr(&gic_dist_attr)
.map_err(Error::SetDeviceAttribute)?;
} else {
gic.get_device_attr(&mut gic_redist_attr).map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;
gic.get_device_attr(&mut gic_dist_attr)
.map_err(Error::GetDeviceAttribute)?;
}
Ok(())
}
fn access_redists_aux(
gic: &DeviceFd,
gic: &Arc<dyn hypervisor::Device>,
gicr_typer: &[u64],
state: &mut Vec<u32>,
reg_list: &[RdistReg],
@@ -146,7 +136,7 @@ fn access_redists_aux(
}
/// Get redistributor registers.
pub fn get_redist_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
pub fn get_redist_regs(gic: &Arc<dyn hypervisor::Device>, gicr_typer: &[u64]) -> Result<Vec<u32>> {
let mut state = Vec::new();
let mut idx: usize = 0;
access_redists_aux(
@@ -163,7 +153,11 @@ pub fn get_redist_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
}
/// Set redistributor registers.
pub fn set_redist_regs(gic: &DeviceFd, gicr_typer: &[u64], state: &[u32]) -> Result<()> {
pub fn set_redist_regs(
gic: &Arc<dyn hypervisor::Device>,
gicr_typer: &[u64],
state: &[u32],
) -> Result<()> {
let mut idx: usize = 0;
let mut mut_state = state.to_owned();
access_redists_aux(
@@ -199,16 +193,15 @@ pub fn construct_gicr_typers(vcpu_states: &[CpuState]) -> Vec<u64> {
*/
let mut gicr_typers: Vec<u64> = Vec::new();
for (index, state) in vcpu_states.iter().enumerate() {
let state: VcpuKvmState = state.clone().into();
let last = (index == vcpu_states.len() - 1) as u64;
// state.sys_regs is a big collection of system registers, including MIPDR_EL1
let mpidr: Vec<Register> = state
.sys_regs
.into_iter()
.filter(|reg| reg.id == KVM_ARM64_SYSREG_MPIDR_EL1)
.collect();
let last = {
if index == vcpu_states.len() - 1 {
1
} else {
0
}
};
//calculate affinity
let mut cpu_affid = mpidr[0].addr & 1095233437695;
let mut cpu_affid = state.mpidr & 1095233437695;
cpu_affid = ((cpu_affid & 0xFF00000000) >> 8) | (cpu_affid & 0xFFFFFF);
gicr_typers.push((cpu_affid << 32) | (1 << 24) | (index as u64) << 8 | (last << 4));
}

View File

@@ -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.
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 = MAPPED_IO_START;
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:

View File

@@ -4,21 +4,23 @@
/// Module for the flattened device tree.
pub mod fdt;
/// Module for the global interrupt controller configuration.
pub mod gic;
/// Layout for this aarch64 system.
pub mod layout;
/// Module for system registers definition
/// Logic for configuring aarch64 registers.
pub mod regs;
/// Module for loading UEFI binary.
pub mod uefi;
pub use self::fdt::DeviceInfoForFdt;
use crate::{DeviceType, GuestMemoryMmap, NumaNodes, PciSpaceInfo, RegionType};
use hypervisor::arch::aarch64::gic::Vgic;
use gic::GicDevice;
use log::{log_enabled, Level};
use std::collections::HashMap;
use std::convert::TryInto;
use std::fmt::Debug;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use vm_memory::{Address, GuestAddress, GuestMemory, GuestUsize};
/// Errors thrown while configuring aarch64 system.
@@ -31,24 +33,21 @@ pub enum Error {
WriteFdtToMemory(fdt::Error),
/// Failed to create a GIC.
SetupGic,
SetupGic(gic::Error),
/// Failed to compute the initramfs address.
InitramfsAddress,
/// Error configuring the general purpose registers
RegsConfiguration(hypervisor::HypervisorCpuError),
RegsConfiguration(regs::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)
}
}
@@ -62,31 +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 {
vcpu.setup_regs(
id,
kernel_entry_point.entry_addr.raw_value(),
super::layout::FDT_START.raw_value(),
)
.map_err(Error::RegsConfiguration)?;
regs::setup_regs(fd, id, kernel_entry_point.entry_addr.raw_value())
.map_err(Error::RegsConfiguration)?;
}
let mpidr = vcpu
.get_sys_reg(regs::MPIDR_EL1)
.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
@@ -101,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.
@@ -147,9 +134,8 @@ pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::Bui
initrd: &Option<super::InitramfsConfig>,
pci_space_info: &[PciSpaceInfo],
virtio_iommu_bdf: Option<u32>,
gic_device: &Arc<Mutex<dyn Vgic>>,
gic_device: &dyn GicDevice,
numa_nodes: &NumaNodes,
pmu_supported: bool,
) -> super::Result<()> {
let fdt_final = fdt::create_fdt(
guest_mem,
@@ -162,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)?;
@@ -189,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.
@@ -215,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);
}
}

View File

@@ -1,44 +1,75 @@
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the THIRD-PARTY file.
///
/// AArch64 system register encoding:
/// See https://developer.arm.com/documentation/ddi0487 (chapter D12)
///
/// 31 22 21 20 19 18 16 15 12 11 8 7 5 4 0
/// +----------+---+-----+-----+-----+-----+-----+----+
/// |1101010100| L | op0 | op1 | CRn | CRm | op2 | Rt |
/// +----------+---+-----+-----+-----+-----+-----+----+
///
/// Notes:
/// - L and Rt are reserved as implementation defined fields, ignored.
///
const SYSREG_HEAD: u32 = 0b1101010100u32 << 22;
const SYSREG_OP0_SHIFT: u32 = 19;
const SYSREG_OP0_MASK: u32 = 0b11u32 << 19;
const SYSREG_OP1_SHIFT: u32 = 16;
const SYSREG_OP1_MASK: u32 = 0b111u32 << 16;
const SYSREG_CRN_SHIFT: u32 = 12;
const SYSREG_CRN_MASK: u32 = 0b1111u32 << 12;
const SYSREG_CRM_SHIFT: u32 = 8;
const SYSREG_CRM_MASK: u32 = 0b1111u32 << 8;
const SYSREG_OP2_SHIFT: u32 = 5;
const SYSREG_OP2_MASK: u32 = 0b111u32 << 5;
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};
/// Define the ID of system registers
#[macro_export]
macro_rules! arm64_sys_reg {
($name: tt, $op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => {
pub const $name: u32 = SYSREG_HEAD
| ((($op0 as u32) << SYSREG_OP0_SHIFT) & SYSREG_OP0_MASK as u32)
| ((($op1 as u32) << SYSREG_OP1_SHIFT) & SYSREG_OP1_MASK as u32)
| ((($crn as u32) << SYSREG_CRN_SHIFT) & SYSREG_CRN_MASK as u32)
| ((($crm as u32) << SYSREG_CRM_SHIFT) & SYSREG_CRM_MASK as u32)
| ((($op2 as u32) << SYSREG_OP2_SHIFT) & SYSREG_OP2_MASK as u32);
};
/// Errors thrown while setting aarch64 registers.
#[derive(Debug)]
pub enum Error {
/// Failed to set core register (PC, PSTATE or general purpose ones).
SetCoreRegister(hypervisor::HypervisorCpuError),
/// Failed to get a system register.
GetSysRegister(hypervisor::HypervisorCpuError),
}
type Result<T> = result::Result<T, Error>;
arm64_sys_reg!(MPIDR_EL1, 3, 0, 0, 0, 5);
arm64_sys_reg!(ID_AA64MMFR0_EL1, 3, 0, 0, 7, 0);
arm64_sys_reg!(TTBR1_EL1, 3, 0, 2, 0, 1);
arm64_sys_reg!(TCR_EL1, 3, 0, 2, 0, 2);
#[allow(non_upper_case_globals)]
// PSR (Processor State Register) bits.
// Taken from arch/arm64/include/uapi/asm/ptrace.h.
const PSR_MODE_EL1h: u64 = 0x0000_0005;
const PSR_F_BIT: u64 = 0x0000_0040;
const PSR_I_BIT: u64 = 0x0000_0080;
const PSR_A_BIT: u64 = 0x0000_0100;
const PSR_D_BIT: u64 = 0x0000_0200;
// Taken from arch/arm64/kvm/inject_fault.c.
const PSTATE_FAULT_BITS_64: u64 = PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT;
/// Configure core registers for a given CPU.
///
/// # Arguments
///
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
/// * `cpu_id` - Index of current vcpu.
/// * `boot_ip` - Starting instruction pointer.
/// * `mem` - Reserved DRAM for current VM.
pub fn setup_regs(vcpu: &Arc<dyn hypervisor::Vcpu>, cpu_id: u8, boot_ip: u64) -> Result<()> {
let kreg_off = offset__of!(kvm_regs, regs);
// Get the register index of the PSTATE (Processor State) register.
let pstate = offset__of!(user_pt_regs, pstate) + kreg_off;
vcpu.set_reg(
arm64_core_reg_id!(KVM_REG_SIZE_U64, pstate),
PSTATE_FAULT_BITS_64,
)
.map_err(Error::SetCoreRegister)?;
// Other vCPUs are powered off initially awaiting PSCI wakeup.
if cpu_id == 0 {
// Setting the PC (Processor Counter) to the current program address (kernel address).
let pc = offset__of!(user_pt_regs, pc) + kreg_off;
vcpu.set_reg(arm64_core_reg_id!(KVM_REG_SIZE_U64, pc), boot_ip as u64)
.map_err(Error::SetCoreRegister)?;
// Last mandatory thing to set -> the address pointing to the FDT (also called DTB).
// "The device tree blob (dtb) must be placed on an 8-byte boundary and must
// not exceed 2 megabytes in size." -> https://www.kernel.org/doc/Documentation/arm64/booting.txt.
// We are choosing to place it the end of DRAM. See `get_fdt_addr`.
let regs0 = offset__of!(user_pt_regs, regs) + kreg_off;
vcpu.set_reg(
arm64_core_reg_id!(KVM_REG_SIZE_U64, regs0),
get_fdt_addr() as u64,
)
.map_err(Error::SetCoreRegister)?;
}
Ok(())
}

View File

@@ -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,
}
@@ -54,7 +58,7 @@ pub enum Error {
pub type Result<T> = result::Result<T, Error>;
/// Type for memory region types.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize, Versionize)]
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize, Versionize)]
pub enum RegionType {
/// RAM type
Ram,
@@ -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
}
}

View File

@@ -5,6 +5,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use hypervisor::x86_64::LapicState;
use std::io::Cursor;
use std::mem;
use std::result;
use std::sync::Arc;
@@ -16,6 +20,32 @@ pub const APIC_LVT1: usize = 0x360;
pub const APIC_MODE_NMI: u32 = 0x4;
pub const APIC_MODE_EXTINT: u32 = 0x7;
pub fn get_klapic_reg(klapic: &LapicState, reg_offset: usize) -> u32 {
let sliceu8 = unsafe {
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
// Cursors are only readable on arrays of u8, not i8(c_char).
mem::transmute::<&[i8], &[u8]>(&klapic.regs[reg_offset..])
};
let mut reader = Cursor::new(sliceu8);
// Following call can't fail if the offsets defined above are correct.
reader
.read_u32::<LittleEndian>()
.expect("Failed to read klapic register")
}
pub fn set_klapic_reg(klapic: &mut LapicState, reg_offset: usize, value: u32) {
let sliceu8 = unsafe {
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
// Cursors are only readable on arrays of u8, not i8(c_char).
mem::transmute::<&mut [i8], &mut [u8]>(&mut klapic.regs[reg_offset..])
};
let mut writer = Cursor::new(sliceu8);
// Following call can't fail if the offsets defined above are correct.
writer
.write_u32::<LittleEndian>(value)
.expect("Failed to write klapic register")
}
pub fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 {
((reg) & !0x700) | ((mode) << 8)
}
@@ -27,13 +57,42 @@ pub fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 {
pub fn set_lint(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
let mut klapic = vcpu.get_lapic()?;
let lvt_lint0 = klapic.get_klapic_reg(APIC_LVT0);
klapic.set_klapic_reg(
let lvt_lint0 = get_klapic_reg(&klapic, APIC_LVT0);
set_klapic_reg(
&mut klapic,
APIC_LVT0,
set_apic_delivery_mode(lvt_lint0, APIC_MODE_EXTINT),
);
let lvt_lint1 = klapic.get_klapic_reg(APIC_LVT1);
klapic.set_klapic_reg(APIC_LVT1, set_apic_delivery_mode(lvt_lint1, APIC_MODE_NMI));
let lvt_lint1 = get_klapic_reg(&klapic, APIC_LVT1);
set_klapic_reg(
&mut klapic,
APIC_LVT1,
set_apic_delivery_mode(lvt_lint1, APIC_MODE_NMI),
);
vcpu.set_lapic(&klapic)
}
#[cfg(test)]
mod tests {
use super::*;
const KVM_APIC_REG_SIZE: usize = 0x400;
#[test]
fn test_set_and_get_klapic_reg() {
let reg_offset = 0x340;
let mut klapic = LapicState::default();
set_klapic_reg(&mut klapic, reg_offset, 3);
let value = get_klapic_reg(&klapic, reg_offset);
assert_eq!(value, 3);
}
#[test]
#[should_panic]
fn test_set_and_get_klapic_out_of_bounds() {
let reg_offset = KVM_APIC_REG_SIZE + 10;
let mut klapic = LapicState::default();
set_klapic_reg(&mut klapic, reg_offset, 3);
}
}

View File

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

View File

@@ -15,8 +15,7 @@ pub mod regs;
use crate::GuestMemoryMmap;
use crate::InitramfsConfig;
use crate::RegionType;
use hypervisor::arch::x86::{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,22 +186,15 @@ 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)
}
}
#[allow(clippy::upper_case_acronyms)]
#[allow(dead_code, clippy::upper_case_acronyms)]
#[derive(Copy, Clone, Debug)]
pub enum CpuidReg {
EAX,
@@ -223,15 +215,17 @@ pub struct CpuidPatch {
impl CpuidPatch {
pub fn set_cpuid_reg(
cpuid: &mut Vec<CpuIdEntry>,
cpuid: &mut CpuId,
function: u32,
index: Option<u32>,
reg: CpuidReg,
value: u32,
) {
let entries = cpuid.as_mut_slice();
let mut entry_found = false;
for entry in cpuid.iter_mut() {
if entry.function == function && (index.is_none() || index.unwrap() == entry.index) {
for entry in entries.iter_mut() {
if entry.function == function && (index == None || index.unwrap() == entry.index) {
entry_found = true;
match reg {
CpuidReg::EAX => {
@@ -277,12 +271,16 @@ impl CpuidPatch {
}
}
cpuid.push(entry);
if let Err(e) = cpuid.push(entry) {
error!("Failed adding new CPUID entry: {:?}", e);
}
}
}
pub fn patch_cpuid(cpuid: &mut [CpuIdEntry], patches: Vec<CpuidPatch>) {
for entry in cpuid {
pub fn patch_cpuid(cpuid: &mut CpuId, patches: Vec<CpuidPatch>) {
let entries = cpuid.as_mut_slice();
for entry in entries.iter_mut() {
for patch in patches.iter() {
if entry.function == patch.function && entry.index == patch.index {
if let Some(flags_bit) = patch.flags_bit {
@@ -306,15 +304,16 @@ impl CpuidPatch {
}
pub fn is_feature_enabled(
cpuid: &[CpuIdEntry],
cpuid: &CpuId,
function: u32,
index: u32,
reg: CpuidReg,
feature_bit: usize,
) -> bool {
let entries = cpuid.as_slice();
let mask = 1 << feature_bit;
for entry in cpuid {
for entry in entries.iter() {
if entry.function == function && entry.index == index {
let reg_val = match reg {
CpuidReg::EAX => entry.eax,
@@ -463,12 +462,12 @@ impl CpuidFeatureEntry {
}
fn get_features_from_cpuid(
cpuid: &[CpuIdEntry],
cpuid: &CpuId,
feature_entry_list: &[CpuidFeatureEntry],
) -> Vec<u32> {
let mut features = vec![0; feature_entry_list.len()];
for (i, feature_entry) in feature_entry_list.iter().enumerate() {
for cpuid_entry in cpuid {
for cpuid_entry in cpuid.as_slice().iter() {
if cpuid_entry.function == feature_entry.function
&& cpuid_entry.index == feature_entry.index
{
@@ -498,8 +497,8 @@ impl CpuidFeatureEntry {
// The function returns `Error` (a.k.a. "incompatible"), when the CPUID features from `src_vm_cpuid`
// is not a subset of those of the `dest_vm_cpuid`.
pub fn check_cpuid_compatibility(
src_vm_cpuid: &[CpuIdEntry],
dest_vm_cpuid: &[CpuIdEntry],
src_vm_cpuid: &CpuId,
dest_vm_cpuid: &CpuId,
) -> Result<(), Error> {
let feature_entry_list = &Self::checked_feature_entry_list();
let src_vm_features = Self::get_features_from_cpuid(src_vm_cpuid, feature_entry_list);
@@ -551,7 +550,7 @@ pub fn generate_common_cpuid(
phys_bits: u8,
kvm_hyperv: bool,
#[cfg(feature = "tdx")] tdx_enabled: bool,
) -> super::Result<Vec<CpuIdEntry>> {
) -> super::Result<CpuId> {
let cpuid_patches = vec![
// Patch tsc deadline timer bit
CpuidPatch {
@@ -598,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);
@@ -662,14 +631,16 @@ pub fn generate_common_cpuid(
for i in 0x8000_0002..=0x8000_0004 {
cpuid.retain(|c| c.function != i);
let leaf = unsafe { std::arch::x86_64::__cpuid(i) };
cpuid.push(CpuIdEntry {
function: i,
eax: leaf.eax,
ebx: leaf.ebx,
ecx: leaf.ecx,
edx: leaf.edx,
..Default::default()
});
cpuid
.push(CpuIdEntry {
function: i,
eax: leaf.eax,
ebx: leaf.ebx,
ecx: leaf.ecx,
edx: leaf.edx,
..Default::default()
})
.map_err(Error::CpuidIdentification)?;
}
if kvm_hyperv {
@@ -678,44 +649,56 @@ pub fn generate_common_cpuid(
cpuid.retain(|c| c.function != 0x4000_0001);
// See "Hypervisor Top Level Functional Specification" for details
// Compliance with "Hv#1" requires leaves up to 0x4000_000a
cpuid.push(CpuIdEntry {
function: 0x40000000,
eax: 0x4000000a, // Maximum cpuid leaf
ebx: 0x756e694c, // "Linu"
ecx: 0x564b2078, // "x KV"
edx: 0x7648204d, // "M Hv"
..Default::default()
});
cpuid.push(CpuIdEntry {
function: 0x40000001,
eax: 0x31237648, // "Hv#1"
..Default::default()
});
cpuid.push(CpuIdEntry {
function: 0x40000002,
eax: 0x3839, // "Build number"
ebx: 0xa0000, // "Version"
..Default::default()
});
cpuid.push(CpuIdEntry {
function: 0x4000_0003,
eax: 1 << 1 // AccessPartitionReferenceCounter
cpuid
.push(CpuIdEntry {
function: 0x40000000,
eax: 0x4000000a, // Maximum cpuid leaf
ebx: 0x756e694c, // "Linu"
ecx: 0x564b2078, // "x KV"
edx: 0x7648204d, // "M Hv"
..Default::default()
})
.map_err(Error::CpuidKvmHyperV)?;
cpuid
.push(CpuIdEntry {
function: 0x40000001,
eax: 0x31237648, // "Hv#1"
..Default::default()
})
.map_err(Error::CpuidKvmHyperV)?;
cpuid
.push(CpuIdEntry {
function: 0x40000002,
eax: 0x3839, // "Build number"
ebx: 0xa0000, // "Version"
..Default::default()
})
.map_err(Error::CpuidKvmHyperV)?;
cpuid
.push(CpuIdEntry {
function: 0x4000_0003,
eax: 1 << 1 // AccessPartitionReferenceCounter
| 1 << 2 // AccessSynicRegs
| 1 << 3 // AccessSyntheticTimerRegs
| 1 << 9, // AccessPartitionReferenceTsc
edx: 1 << 3, // CPU dynamic partitioning
..Default::default()
});
cpuid.push(CpuIdEntry {
function: 0x4000_0004,
eax: 1 << 5, // Recommend relaxed timing
..Default::default()
});
for i in 0x4000_0005..=0x4000_000a {
cpuid.push(CpuIdEntry {
function: i,
edx: 1 << 3, // CPU dynamic partitioning
..Default::default()
});
})
.map_err(Error::CpuidKvmHyperV)?;
cpuid
.push(CpuIdEntry {
function: 0x4000_0004,
eax: 1 << 5, // Recommend relaxed timing
..Default::default()
})
.map_err(Error::CpuidKvmHyperV)?;
for i in 0x4000_0005..=0x4000_000a {
cpuid
.push(CpuIdEntry {
function: i,
..Default::default()
})
.map_err(Error::CpuidKvmHyperV)?;
}
}
@@ -723,11 +706,11 @@ 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>,
cpuid: Vec<CpuIdEntry>,
cpuid: CpuId,
kvm_hyperv: bool,
) -> super::Result<()> {
// Per vCPU CPUID changes; common are handled via generate_common_cpuid()
@@ -735,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(())
}
@@ -818,17 +801,8 @@ pub fn configure_system(
_num_cpus: u8,
rsdp_addr: Option<GuestAddress>,
sgx_epc_region: Option<SgxEpcRegion>,
serial_number: Option<&str>,
uuid: Option<&str>,
oem_strings: 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, uuid, oem_strings)
.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);
@@ -1038,7 +1012,7 @@ pub fn get_host_cpu_phys_bits() -> u8 {
}
fn update_cpuid_topology(
cpuid: &mut Vec<CpuIdEntry>,
cpuid: &mut CpuId,
threads_per_core: u8,
cores_per_die: u8,
dies_per_package: u8,
@@ -1102,10 +1076,7 @@ fn update_cpuid_topology(
// The goal is to update the CPUID sub-leaves to reflect the number of EPC
// sections exposed to the guest.
fn update_cpuid_sgx(
cpuid: &mut Vec<CpuIdEntry>,
epc_sections: Vec<SgxEpcSection>,
) -> Result<(), Error> {
fn update_cpuid_sgx(cpuid: &mut CpuId, epc_sections: Vec<SgxEpcSection>) -> Result<(), Error> {
// Something's wrong if there's no EPC section.
if epc_sections.is_empty() {
return Err(Error::NoSgxEpcSection);
@@ -1180,9 +1151,6 @@ mod tests {
1,
Some(layout::RSDP_POINTER),
None,
None,
None,
None,
);
assert!(config_err.is_err());
@@ -1196,18 +1164,7 @@ mod tests {
.collect();
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
configure_system(
&gm,
GuestAddress(0),
&None,
no_vcpus,
None,
None,
None,
None,
None,
)
.unwrap();
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None).unwrap();
// Now assigning some memory that is equal to the start of the 32bit memory hole.
let mem_size = 3328 << 20;
@@ -1218,31 +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,
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,
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;
@@ -1253,31 +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,
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,
None,
None,
)
.unwrap();
configure_system(&gm, GuestAddress(0), &None, no_vcpus, None, None).unwrap();
}
#[test]

View File

@@ -9,8 +9,8 @@
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::{FpuState, SpecialRegisters, StandardRegisters};
use hypervisor::arch::x86::regs::*;
use hypervisor::x86_64::{FpuState, SpecialRegisters, StandardRegisters};
use std::sync::Arc;
use std::{mem, result};
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError};
@@ -66,7 +66,7 @@ pub fn setup_fpu(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
///
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
pub fn setup_msrs(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
vcpu.set_msrs(&vcpu.boot_msr_entries())
vcpu.set_msrs(&hypervisor::x86_64::boot_msr_entries())
.map_err(Error::SetModelSpecificRegisters)?;
Ok(())

View File

@@ -12,10 +12,10 @@ use std::fmt::{self, Display};
use std::mem;
use std::result;
use std::slice;
use uuid::Uuid;
use vm_memory::ByteValued;
use vm_memory::{Address, Bytes, GuestAddress};
#[allow(unused_variables)]
#[derive(Debug)]
pub enum Error {
/// There was too little guest memory to store the entire SMBIOS table.
@@ -28,8 +28,6 @@ pub enum Error {
WriteSmbiosEp,
/// Failure to write additional data to memory
WriteData,
/// Failure to parse uuid, uuid format may be error
ParseUuid(uuid::Error),
}
impl std::error::Error for Error {}
@@ -39,16 +37,11 @@ impl Display for Error {
use self::Error::*;
let description = match self {
NotEnoughMemory => {
"There was too little guest memory to store the SMBIOS table".to_string()
}
AddressOverflow => {
"The SMBIOS table has too little address space to be stored".to_string()
}
Clear => "Failure while zeroing out the memory for the SMBIOS table".to_string(),
WriteSmbiosEp => "Failure to write SMBIOS entrypoint structure".to_string(),
WriteData => "Failure to write additional data to memory".to_string(),
ParseUuid(e) => format!("Failure to parse uuid: {}", e),
NotEnoughMemory => "There was too little guest memory to store the SMBIOS table",
AddressOverflow => "The SMBIOS table has too little address space to be stored",
Clear => "Failure while zeroing out the memory for the SMBIOS table",
WriteSmbiosEp => "Failure to write SMBIOS entrypoint structure",
WriteData => "Failure to write additional data to memory",
};
write!(f, "SMBIOS error: {}", description)
@@ -61,7 +54,6 @@ pub type Result<T> = result::Result<T, Error>;
const SM3_MAGIC_IDENT: &[u8; 5usize] = b"_SM3_";
const BIOS_INFORMATION: u8 = 0;
const SYSTEM_INFORMATION: u8 = 1;
const OEM_STRINGS: u8 = 11;
const END_OF_TABLE: u8 = 127;
const PCI_SUPPORTED: u64 = 1 << 7;
const IS_VIRTUAL_MACHINE: u8 = 1 << 4;
@@ -76,81 +68,75 @@ fn compute_checksum<T: Copy>(v: &T) -> u8 {
(!checksum).wrapping_add(1)
}
#[repr(C)]
#[repr(packed)]
#[derive(Default, Copy, Clone)]
struct Smbios30Entrypoint {
signature: [u8; 5usize],
checksum: u8,
length: u8,
majorver: u8,
minorver: u8,
docrev: u8,
revision: u8,
reserved: u8,
max_size: u32,
physptr: u64,
#[derive(Default, Copy)]
pub struct Smbios30Entrypoint {
pub signature: [u8; 5usize],
pub checksum: u8,
pub length: u8,
pub majorver: u8,
pub minorver: u8,
pub docrev: u8,
pub revision: u8,
pub reserved: u8,
pub max_size: u32,
pub physptr: u64,
}
#[repr(C)]
#[repr(packed)]
#[derive(Default, Copy, Clone)]
struct SmbiosBiosInfo {
r#type: u8,
length: u8,
handle: u16,
vendor: u8,
version: u8,
start_addr: u16,
release_date: u8,
rom_size: u8,
characteristics: u64,
characteristics_ext1: u8,
characteristics_ext2: u8,
impl Clone for Smbios30Entrypoint {
fn clone(&self) -> Self {
*self
}
}
#[repr(C)]
#[repr(packed)]
#[derive(Default, Copy, Clone)]
struct SmbiosSysInfo {
r#type: u8,
length: u8,
handle: u16,
manufacturer: u8,
product_name: u8,
version: u8,
serial_number: u8,
uuid: [u8; 16usize],
wake_up_type: u8,
sku: u8,
family: u8,
#[derive(Default, Copy)]
pub struct SmbiosBiosInfo {
pub typ: u8,
pub length: u8,
pub handle: u16,
pub vendor: u8,
pub version: u8,
pub start_addr: u16,
pub release_date: u8,
pub rom_size: u8,
pub characteristics: u64,
pub characteristics_ext1: u8,
pub characteristics_ext2: u8,
}
#[repr(C)]
#[repr(packed)]
#[derive(Default, Copy, Clone)]
struct SmbiosOemStrings {
r#type: u8,
length: u8,
handle: u16,
count: u8,
impl Clone for SmbiosBiosInfo {
fn clone(&self) -> Self {
*self
}
}
#[repr(C)]
#[repr(packed)]
#[derive(Default, Copy, Clone)]
struct SmbiosEndOfTable {
r#type: u8,
length: u8,
handle: u16,
#[derive(Default, Copy)]
pub struct SmbiosSysInfo {
pub typ: u8,
pub length: u8,
pub handle: u16,
pub manufacturer: u8,
pub product_name: u8,
pub version: u8,
pub serial_number: u8,
pub uuid: [u8; 16usize],
pub wake_up_type: u8,
pub sku: u8,
pub family: u8,
}
impl Clone for SmbiosSysInfo {
fn clone(&self) -> Self {
*self
}
}
// SAFETY: These data structures only contain a series of integers
unsafe impl ByteValued for Smbios30Entrypoint {}
unsafe impl ByteValued for SmbiosBiosInfo {}
unsafe impl ByteValued for SmbiosSysInfo {}
unsafe impl ByteValued for SmbiosOemStrings {}
unsafe impl ByteValued for SmbiosEndOfTable {}
fn write_and_incr<T: ByteValued>(
mem: &GuestMemoryMmap,
@@ -176,12 +162,7 @@ fn write_string(
Ok(curptr)
}
pub fn setup_smbios(
mem: &GuestMemoryMmap,
serial_number: Option<&str>,
uuid: Option<&str>,
oem_strings: 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)?;
@@ -191,7 +172,7 @@ pub fn setup_smbios(
{
handle += 1;
let smbios_biosinfo = SmbiosBiosInfo {
r#type: BIOS_INFORMATION,
typ: BIOS_INFORMATION,
length: mem::size_of::<SmbiosBiosInfo>() as u8,
handle,
vendor: 1, // First string written in this section
@@ -208,59 +189,29 @@ pub fn setup_smbios(
{
handle += 1;
let uuid_number = uuid
.map(Uuid::parse_str)
.transpose()
.map_err(Error::ParseUuid)?
.unwrap_or(Uuid::nil());
let smbios_sysinfo = SmbiosSysInfo {
r#type: SYSTEM_INFORMATION,
typ: SYSTEM_INFORMATION,
length: mem::size_of::<SmbiosSysInfo>() as u8,
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
uuid: uuid_number.to_bytes_le(), // set uuid
..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)?;
}
if let Some(oem_strings) = oem_strings {
handle += 1;
let smbios_oemstrings = SmbiosOemStrings {
r#type: OEM_STRINGS,
length: mem::size_of::<SmbiosOemStrings>() as u8,
handle,
count: oem_strings.len() as u8,
};
curptr = write_and_incr(mem, smbios_oemstrings, curptr)?;
for s in oem_strings {
curptr = write_string(mem, s, curptr)?;
}
curptr = write_and_incr(mem, 0u8, curptr)?;
}
{
handle += 1;
let smbios_end = SmbiosEndOfTable {
r#type: END_OF_TABLE,
length: mem::size_of::<SmbiosEndOfTable>() as u8,
let smbios_sysinfo = SmbiosSysInfo {
typ: END_OF_TABLE,
length: mem::size_of::<SmbiosSysInfo>() as u8,
handle,
..Default::default()
};
curptr = write_and_incr(mem, smbios_end, curptr)?;
curptr = write_and_incr(mem, 0u8, curptr)?;
curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
curptr = write_and_incr(mem, 0u8, curptr)?;
}
@@ -282,7 +233,7 @@ pub fn setup_smbios(
.map_err(|_| Error::WriteSmbiosEp)?;
}
Ok(curptr.unchecked_offset_from(physptr) + std::mem::size_of::<Smbios30Entrypoint>() as u64)
Ok(curptr.unchecked_offset_from(physptr))
}
#[cfg(test)]
@@ -312,7 +263,7 @@ mod tests {
fn entrypoint_checksum() {
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap();
setup_smbios(&mem, None, None, None).unwrap();
setup_smbios(&mem).unwrap();
let smbios_ep: Smbios30Entrypoint = mem.read_obj(GuestAddress(SMBIOS_START)).unwrap();

View File

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

View File

@@ -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.6"
libc = "0.2.133"
log = "0.4.17"
io-uring = "0.5.2"
libc = "0.2.112"
log = "0.4.14"
qcow = { path = "../qcow" }
thiserror = "1.0.37"
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.6.1"
vm-memory = { version = "0.9.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.10.0"
vmm-sys-util = "0.9.0"

View File

@@ -8,7 +8,7 @@ use std::fs::File;
use std::os::unix::io::AsRawFd;
use thiserror::Error;
use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr};
use vmm_sys_util::{ioctl_expr, ioctl_io_nr, ioctl_ioc_nr};
#[derive(Error, Debug)]
pub enum DiskFileError {
@@ -104,7 +104,7 @@ impl DiskTopology {
pub type DiskFileResult<T> = std::result::Result<T, DiskFileError>;
pub trait DiskFile: Send {
pub trait DiskFile: Send + Sync {
fn size(&mut self) -> DiskFileResult<u64>;
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>>;
fn topology(&mut self) -> DiskTopology {
@@ -127,7 +127,7 @@ pub enum AsyncIoError {
pub type AsyncIoResult<T> = std::result::Result<T, AsyncIoError>;
pub trait AsyncIo: Send {
pub trait AsyncIo: Send + Sync {
fn notifier(&self) -> &EventFd;
fn read_vectored(
&mut self,

View File

@@ -30,9 +30,7 @@ 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 thiserror::Error;
use versionize::{VersionMap, Versionize, VersionizeResult};
use versionize_derive::Versionize;
use virtio_bindings::bindings::virtio_blk::*;
@@ -41,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>;
@@ -49,25 +46,25 @@ type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
const SECTOR_SHIFT: u8 = 9;
pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
#[derive(Error, Debug)]
#[derive(Debug)]
pub enum Error {
#[error("Guest gave us bad memory addresses.")]
/// Guest gave us bad memory addresses.
GuestMemory(GuestMemoryError),
#[error("Guest gave us offsets that would have overflowed a usize.")]
/// Guest gave us offsets that would have overflowed a usize.
CheckedOffset(GuestAddress, usize),
#[error("Guest gave us a write only descriptor that protocol says to read from.")]
/// Guest gave us a write only descriptor that protocol says to read from.
UnexpectedWriteOnlyDescriptor,
#[error("Guest gave us a read only descriptor that protocol says to write to.")]
/// Guest gave us a read only descriptor that protocol says to write to.
UnexpectedReadOnlyDescriptor,
#[error("Guest gave us too few descriptors in a descriptor chain.")]
/// Guest gave us too few descriptors in a descriptor chain.
DescriptorChainTooShort,
#[error("Guest gave us a descriptor that was too short to use.")]
/// Guest gave us a descriptor that was too short to use.
DescriptorLengthTooSmall,
#[error("Getting a block's metadata fails for any reason.")]
/// Getting a block's metadata fails for any reason.
GetFileMetadata,
#[error("The requested operation would cause a seek beyond disk end.")]
/// The requested operation would cause a seek beyond disk end.
InvalidOffset,
#[error("The requested operation does not support multiple descriptors.")]
/// The requested operation does not support multiple descriptors.
TooManyDescriptors,
}
@@ -103,31 +100,20 @@ pub fn build_disk_image_id(disk_path: &Path) -> Vec<u8> {
default_disk_image_id
}
#[derive(Error, Debug)]
#[derive(Debug)]
pub enum ExecuteError {
#[error("Bad request: {0}")]
BadRequest(Error),
#[error("Falied to flush: {0}")]
Flush(io::Error),
#[error("Failed to read: {0}")]
Read(GuestMemoryError),
#[error("Failed to seek: {0}")]
Seek(io::Error),
#[error("Failed to write: {0}")]
Write(GuestMemoryError),
#[error("Unsupported request: {0}")]
Unsupported(u32),
#[error("Failed to submit io uring: {0}")]
SubmitIoUring(io::Error),
#[error("Failed to get guest address: {0}")]
GetHostAddress(GuestMemoryError),
#[error("Failed to async read: {0}")]
AsyncRead(AsyncIoError),
#[error("Failed to async write: {0}")]
AsyncWrite(AsyncIoError),
#[error("failed to async flush: {0}")]
AsyncFlush(AsyncIoError),
#[error("Failed allocating a temporary buffer: {0}")]
/// Failed allocating a temporary buffer.
TemporaryBufferAllocation(io::Error),
}
@@ -150,7 +136,7 @@ impl ExecuteError {
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum RequestType {
In,
Out,
@@ -204,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()
@@ -219,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,
@@ -259,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)
@@ -285,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)
}

View File

@@ -11,7 +11,7 @@ use std::process::Command;
fn main() {
let mut version = "v".to_owned() + crate_version!();
if let Ok(git_out) = Command::new("git").args(["describe", "--dirty"]).output() {
if let Ok(git_out) = Command::new("git").args(&["describe", "--dirty"]).output() {
if git_out.status.success() {
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {
version = git_out_str;

View File

@@ -2,26 +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.65"
acpi_tables = { path = "../acpi_tables", optional = true }
anyhow = "1.0.52"
arch = { path = "../arch" }
bitflags = "1.3.2"
byteorder = "1.4.3"
hypervisor = { path = "../hypervisor" }
libc = "0.2.133"
log = "0.4.17"
epoll = "4.3.1"
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.9.0"
vm-memory = "0.7.0"
vm-migration = { path = "../vm-migration" }
vmm-sys-util = "0.10.0"
[target.'cfg(target_arch = "aarch64")'.dependencies]
arch = { path = "../arch" }
vmm-sys-util = "0.9.0"
[features]
default = []
acpi = ["acpi_tables"]
cmos = []
fwdebug = []

View File

@@ -102,20 +102,22 @@ impl BusDevice for AcpiGedDevice {
}
}
#[cfg(feature = "acpi")]
impl Aml for AcpiGedDevice {
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
aml::Device::new(
"_SB_.GEC_".into(),
"_SB_.GED_".into(),
vec![
&aml::Name::new("_HID".into(), &aml::EisaName::new("PNP0A06")),
&aml::Name::new("_UID".into(), &"Generic Event Controller"),
&aml::Name::new("_HID".into(), &"ACPI0013"),
&aml::Name::new("_UID".into(), &aml::ZERO),
&aml::Name::new(
"_CRS".into(),
&aml::ResourceTemplate::new(vec![&aml::AddressSpace::new_memory(
aml::AddressSpaceCachable::NotCacheable,
&aml::ResourceTemplate::new(vec![&aml::Interrupt::new(
true,
self.address.0 as u64,
self.address.0 + GED_DEVICE_ACPI_SIZE as u64 - 1,
true,
false,
false,
self.ged_irq,
)]),
),
&aml::OpRegion::new(
@@ -131,8 +133,8 @@ impl Aml for AcpiGedDevice {
vec![aml::FieldEntry::Named(*b"GDAT", 8)],
),
&aml::Method::new(
"ESCN".into(),
0,
"_EVT".into(),
1,
true,
vec![
&aml::Store::new(&aml::Local(0), &aml::Path::new("GDAT")),
@@ -163,30 +165,6 @@ impl Aml for AcpiGedDevice {
),
],
)
.append_aml_bytes(bytes);
aml::Device::new(
"_SB_.GED_".into(),
vec![
&aml::Name::new("_HID".into(), &"ACPI0013"),
&aml::Name::new("_UID".into(), &aml::ZERO),
&aml::Name::new(
"_CRS".into(),
&aml::ResourceTemplate::new(vec![&aml::Interrupt::new(
true,
true,
false,
false,
self.ged_irq,
)]),
),
&aml::Method::new(
"_EVT".into(),
1,
true,
vec![&aml::MethodCall::new("\\_SB_.GEC_.ESCN".into(), vec![])],
),
],
)
.append_aml_bytes(bytes)
}
}

View File

@@ -4,37 +4,30 @@
use super::interrupt_controller::{Error, InterruptController};
extern crate arch;
use anyhow::anyhow;
use arch::layout;
use hypervisor::{
arch::aarch64::gic::{Vgic, VgicConfig},
CpuState,
};
use arch::aarch64::gic::GicDevice;
use std::result;
use std::sync::{Arc, Mutex};
use vm_device::interrupt::{
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
LegacyIrqSourceConfig, MsiIrqGroupConfig,
};
use vm_memory::address::Address;
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
use vmm_sys_util::eventfd::EventFd;
type Result<T> = result::Result<T, Error>;
// Reserve 32 IRQs for legacy devices.
pub const IRQ_LEGACY_BASE: usize = layout::IRQ_BASE as usize;
// Reserve 32 IRQs for legacy device.
pub const IRQ_LEGACY_BASE: usize = arch::layout::IRQ_BASE as usize;
pub const IRQ_LEGACY_COUNT: usize = 32;
// Gic (Generic Interupt Controller) struct provides all the functionality of a
// GIC device. It wraps a hypervisor-emulated GIC device (Vgic) provided by the
// `hypervisor` crate.
// Gic struct also implements InterruptController to provide interrupt delivery
// service.
// This Gic struct implements InterruptController to provide interrupt delivery service.
// The Gic source files in arch/ folder maintain the Aarch64 specific Gic device.
// The 2 Gic instances could be merged together.
// Leave this refactoring to future. Two options may be considered:
// 1. Move Gic*.rs from arch/ folder here.
// 2. Move this file and ioapic.rs to arch/, as they are architecture specific.
pub struct Gic {
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
// The hypervisor agnostic virtual GIC
vgic: Option<Arc<Mutex<dyn Vgic>>>,
gic_device: Option<Arc<Mutex<Box<dyn GicDevice>>>>,
}
impl Gic {
@@ -51,39 +44,16 @@ impl Gic {
Ok(Gic {
interrupt_source_group,
vgic: None,
gic_device: None,
})
}
/// Default config implied by arch::layout
pub fn create_default_config(vcpu_count: u64) -> VgicConfig {
let redists_size = layout::GIC_V3_REDIST_SIZE * vcpu_count;
let redists_addr = layout::GIC_V3_DIST_START.raw_value() - redists_size;
VgicConfig {
vcpu_count,
dist_addr: layout::GIC_V3_DIST_START.raw_value(),
dist_size: layout::GIC_V3_DIST_SIZE,
redists_addr,
redists_size,
msi_addr: redists_addr - layout::GIC_V3_ITS_SIZE,
msi_size: layout::GIC_V3_ITS_SIZE,
nr_irqs: layout::IRQ_NUM,
}
pub fn set_gic_device(&mut self, gic_device: Arc<Mutex<Box<dyn GicDevice>>>) {
self.gic_device = Some(gic_device);
}
pub fn create_vgic(
&mut self,
vm: &Arc<dyn hypervisor::Vm>,
config: VgicConfig,
) -> Result<Arc<Mutex<dyn Vgic>>> {
let vgic = vm.create_vgic(config).map_err(Error::CreateGic)?;
self.vgic = Some(vgic.clone());
Ok(vgic.clone())
}
pub fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
let vgic = self.vgic.as_ref().unwrap().clone();
vgic.lock().unwrap().set_gicr_typers(vcpu_states);
pub fn get_gic_device(&self) -> Option<&Arc<Mutex<Box<dyn GicDevice>>>> {
self.gic_device.as_ref()
}
}
@@ -106,7 +76,6 @@ impl InterruptController for Gic {
.update(
i as InterruptIndex,
InterruptSourceConfig::LegacyIrq(config),
false,
)
.map_err(Error::EnableInterrupt)?;
}
@@ -127,43 +96,3 @@ impl InterruptController for Gic {
self.interrupt_source_group.notifier(irq as InterruptIndex)
}
}
pub const GIC_V3_ITS_SNAPSHOT_ID: &str = "gic-v3-its";
impl Snapshottable for Gic {
fn id(&self) -> String {
GIC_V3_ITS_SNAPSHOT_ID.to_string()
}
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
let vgic = self.vgic.as_ref().unwrap().clone();
let state = vgic.lock().unwrap().state().unwrap();
Snapshot::new_from_state(&self.id(), &state)
}
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
let vgic = self.vgic.as_ref().unwrap().clone();
vgic.lock()
.unwrap()
.set_state(&snapshot.to_state(&self.id())?)
.map_err(|e| {
MigratableError::Restore(anyhow!("Could not restore GICv3ITS state {:?}", e))
})?;
Ok(())
}
}
impl Pausable for Gic {
fn pause(&mut self) -> std::result::Result<(), MigratableError> {
// Flush tables to guest RAM
let vgic = self.vgic.as_ref().unwrap().clone();
vgic.lock().unwrap().save_data_tables().map_err(|e| {
MigratableError::Pause(anyhow!(
"Could not save GICv3ITS GIC pending tables {:?}",
e
))
})?;
Ok(())
}
}
impl Transportable for Gic {}
impl Migratable for Gic {}

View File

@@ -24,8 +24,6 @@ pub enum Error {
UpdateInterrupt(io::Error),
/// Failed enabling the interrupt.
EnableInterrupt(io::Error),
/// Failed creating GIC device.
CreateGic(hypervisor::HypervisorVmError),
}
type Result<T> = result::Result<T, Error>;

View File

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

View File

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

View File

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

View File

@@ -356,7 +356,6 @@ mod tests {
&self,
_index: InterruptIndex,
_config: InterruptSourceConfig,
_masked: bool,
) -> result::Result<(), std::io::Error> {
Ok(())
}

View File

@@ -5,10 +5,9 @@
// 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(target_arch = "x86_64")]
#[cfg(feature = "fwdebug")]
mod fwdebug;
#[cfg(target_arch = "aarch64")]
mod gpio_pl061;
@@ -19,10 +18,9 @@ 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(target_arch = "x86_64")]
#[cfg(feature = "fwdebug")]
pub use self::fwdebug::FwDebugDevice;
pub use self::i8042::I8042Device;
pub use self::serial::Serial;

View File

@@ -430,7 +430,6 @@ mod tests {
&self,
_index: InterruptIndex,
_config: InterruptSourceConfig,
_masked: bool,
) -> result::Result<(), std::io::Error> {
Ok(())
}

View File

@@ -339,7 +339,6 @@ mod tests {
&self,
_index: InterruptIndex,
_config: InterruptSourceConfig,
_masked: bool,
) -> result::Result<(), std::io::Error> {
Ok(())
}

View File

@@ -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 => warn!(
"[Debug I/O port: Firmware code: 0x{:x}] {}.{:>06} seconds",
self.debug,
elapsed.as_secs(),
elapsed.as_micros()
),
0x20..=0x3f => warn!(
"[Debug I/O port: Bootloader code: 0x{:x}] {}.{:>06} seconds",
self.debug,
elapsed.as_secs(),
elapsed.as_micros()
),
0x40..=0x5f => warn!(
"[Debug I/O port: Kernel code: 0x{:x}] {}.{:>06} seconds",
self.debug,
elapsed.as_secs(),
elapsed.as_micros()
),
0x60..=0x7f => warn!(
"[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)
}
@@ -372,9 +325,8 @@ impl BusDevice for Pl011 {
UARTIFLS => self.ifl,
UARTIMSC => self.int_enabled,
UARTRIS => self.int_level,
UARTMIS => self.int_level & self.int_enabled,
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

View File

@@ -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! {

View File

@@ -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.

View File

@@ -1,21 +1,21 @@
- [Cloud Hypervisor API](#cloud-hypervisor-api)
- [External API](#external-api)
- [REST API](#rest-api)
- [Location and availability](#location-and-availability)
- [Endpoints](#endpoints)
- [Virtual Machine Manager (VMM) Actions](#virtual-machine-manager-vmm-actions)
- [Virtual Machine (VM) Actions](#virtual-machine-vm-actions)
- [REST API Examples](#rest-api-examples)
- [Create a Virtual Machine](#create-a-virtual-machine)
- [Boot a Virtual Machine](#boot-a-virtual-machine)
- [Dump a Virtual Machine Information](#dump-a-virtual-machine-information)
- [Reboot a Virtual Machine](#reboot-a-virtual-machine)
- [Shut a Virtual Machine Down](#shut-a-virtual-machine-down)
- [Command Line Interface](#command-line-interface)
- [REST API and CLI Architectural Relationship](#rest-api-and-cli-architectural-relationship)
- [Internal API](#internal-api)
- [Goals and Design](#goals-and-design)
- [End to End Example](#end-to-end-example)
* [External API](#external-api)
+ [REST API](#rest-api)
- [Location and availability](#location-and-availability)
- [Endpoints](#endpoints)
* [Virtual Machine Manager (VMM) Actions](#virtual-machine-manager-vmm-actions)
* [Virtual Machine (VM) Actions](#virtual-machine-vm-actions)
- [REST API Examples](#rest-api-examples)
* [Create a Virtual Machine](#create-a-virtual-machine)
* [Boot a Virtual Machine](#boot-a-virtual-machine)
* [Dump a Virtual Machine Information](#dump-a-virtual-machine-information)
* [Reboot a Virtual Machine](#reboot-a-virtual-machine)
* [Shut a Virtual Machine Down](#shut-a-virtual-machine-down)
+ [Command Line Interface](#command-line-interface)
+ [REST API and CLI Architectural Relationship](#rest-api-and-cli-architectural-relationship)
* [Internal API](#internal-api)
+ [Goals and Design](#goals-and-design)
* [End to End Example](#end-to-end-example)
# Cloud Hypervisor API
@@ -71,40 +71,36 @@ The Cloud Hypervisor API exposes the following actions through its endpoints:
#### Virtual Machine Manager (VMM) Actions
| Action | Endpoint | Request Body | Response Body | Prerequisites |
| ----------------------------------- | --------------- | ------------ | -------------------------- | ------------------ |
| Check for the REST API availability | `/vmm.ping` | N/A | `/schemas/VmmPingResponse` | N/A |
| Shut the VMM down | `/vmm.shutdown` | N/A | N/A | The VMM is running |
Action | Endpoint | Request Body | Response Body | Prerequisites
------------------------------------|-----------------|--------------|----------------------------|---------------------------
Check for the REST API availability | `/vmm.ping` | N/A | `/schemas/VmmPingResponse` | N/A
Shut the VMM down | `/vmm.shutdown` | N/A | N/A | The VMM is running
#### Virtual Machine (VM) Actions
| Action | Endpoint | Request Body | Response Body | Prerequisites |
| ---------------------------------- | --------------------- | --------------------------- | ------------------------ | -------------------------------- |
| Create the VM | `/vm.create` | `/schemas/VmConfig` | N/A | The VM is not created yet |
| Delete the VM | `/vm.delete` | N/A | N/A | N/A |
| Boot the VM | `/vm.boot` | N/A | N/A | The VM is created but not booted |
| Shut the VM down | `/vm.shutdown` | N/A | N/A | The VM is booted |
| Reboot the VM | `/vm.reboot` | N/A | N/A | The VM is booted |
| Trigger power button of the VM | `/vm.power-button` | N/A | N/A | The VM is booted |
| Pause the VM | `/vm.pause` | N/A | N/A | The VM is booted |
| Resume the VM | `/vm.resume` | N/A | N/A | The VM is paused |
| Task a snapshot of the VM | `/vm.snapshot` | `/schemas/VmSnapshotConfig` | N/A | The VM is paused |
| Perform a coredump of the VM | `/vm.coredump` | `/schemas/VmCoredumpData` | N/A | The VM is paused |
| Restore the VM from a snapshot | `/vm.restore` | `/schemas/RestoreConfig` | N/A | The VM is created but not booted |
| Add/remove CPUs to/from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted |
| Add/remove memory from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted |
| Add/remove memory from a zone | `/vm.resize-zone` | `/schemas/VmResizeZone` | N/A | The VM is booted |
| Dump the VM information | `/vm.info` | N/A | `/schemas/VmInfo` | The VM is created |
| Add VFIO PCI device to the VM | `/vm.add-device` | `/schemas/VmAddDevice` | `/schemas/PciDeviceInfo` | The VM is booted |
| Add disk device to the VM | `/vm.add-disk` | `/schemas/DiskConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
| Add fs device to the VM | `/vm.add-fs` | `/schemas/FsConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
| Add pmem device to the VM | `/vm.add-pmem` | `/schemas/PmemConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
| Add network device to the VM | `/vm.add-net` | `/schemas/NetConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
| Add userspace PCI device to the VM | `/vm.add-user-device` | `/schemas/VmAddUserDevice` | `/schemas/PciDeviceInfo` | The VM is booted |
| Add vdpa device to the VM | `/vm.add-vdpa` | `/schemas/VdpaConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
| Add vsock device to the VM | `/vm.add-vsock` | `/schemas/VsockConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
| Remove device from the VM | `/vm.remove-device` | `/schemas/VmRemoveDevice` | N/A | The VM is booted |
| Dump the VM counters | `/vm.counters` | N/A | `/schemas/VmCounters` | The VM is booted |
Action | Endpoint | Request Body | Response Body | Prerequisites
-----------------------------------|----------------------|---------------------------|--------------------------|---------------------------
Create the VM | `/vm.create` | `/schemas/VmConfig` | N/A | The VM is not created yet
Delete the VM | `/vm.delete` | N/A | N/A | N/A
Boot the VM | `/vm.boot` | N/A | N/A | The VM is created but not booted
Shut the VM down | `/vm.shutdown` | N/A | N/A | The VM is booted
Reboot the VM | `/vm.reboot` | N/A | N/A | The VM is booted
Trigger power button of the VM | `/vm.power-button` | N/A | N/A | The VM is booted
Pause the VM | `/vm.pause` | N/A | N/A | The VM is booted
Resume the VM | `/vm.resume` | N/A | N/A | The VM is paused
Add/remove CPUs to/from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
Add/remove memory from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
Add/remove memory from a zone | `/vm.resize-zone` | `/schemas/VmResizeZone` | N/A | The VM is booted
Dump the VM information | `/vm.info` | N/A | `/schemas/VmInfo` | The VM is created
Add VFIO PCI device to the VM | `/vm.add-device` | `/schemas/VmAddDevice` | `/schemas/PciDeviceInfo` | The VM is booted
Add disk device to the VM | `/vm.add-disk` | `/schemas/DiskConfig` | `/schemas/PciDeviceInfo` | The VM is booted
Add fs device to the VM | `/vm.add-fs` | `/schemas/FsConfig` | `/schemas/PciDeviceInfo` | The VM is booted
Add pmem device to the VM | `/vm.add-pmem` | `/schemas/PmemConfig` | `/schemas/PciDeviceInfo` | The VM is booted
Add network device to the VM | `/vm.add-net` | `/schemas/NetConfig` | `/schemas/PciDeviceInfo` | The VM is booted
Add userspace PCI device to the VM | `/vm.add-user-device`| `/schemas/VmAddUserDevice`| `/schemas/PciDeviceInfo` | The VM is booted
Add 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
### REST API Examples
@@ -143,7 +139,8 @@ curl --unix-socket /tmp/cloud-hypervisor.sock -i \
-H 'Content-Type: application/json' \
-d '{
"cpus":{"boot_vcpus": 4, "max_vcpus": 4},
"payload":{"kernel":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu", "cmdline":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
"kernel":{"path":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu"},
"cmdline":{"args":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
"disks":[{"path":"/opt/clh/images/focal-server-cloudimg-amd64.raw"}],
"rng":{"src":"/dev/urandom"},
"net":[{"ip":"192.168.10.10", "mask":"255.255.255.0", "mac":"12:34:56:78:90:01"}]
@@ -310,7 +307,8 @@ APIs work together, let's look at a complete VM creation flow, from the
-H 'Content-Type: application/json' \
-d '{
"cpus":{"boot_vcpus": 4, "max_vcpus": 4},
"payload":{"kernel":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu", "cmdline":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
"kernel":{"path":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu"},
"cmdline":{"args":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
"disks":[{"path":"/opt/clh/images/focal-server-cloudimg-amd64.raw"}],
"rng":{"src":"/dev/urandom"},
"net":[{"ip":"192.168.10.10", "mask":"255.255.255.0", "mac":"12:34:56:78:90:01"}]

View File

@@ -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
```

View File

@@ -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.

View File

@@ -59,16 +59,6 @@ mkdir -p /mnt
sudo mount -o loop,offset=$((227328 * 512)) focal-server-cloudimg-amd64.raw /mnt
```
### Set up DNS
The next step describes changing the root directory to the rootfs contained by
the cloud image. For DNS to work in the root directory, you will need to first bind-mount
the host `/etc/resolv.conf` onto the mounted linux partition of the cloud image.
```bash
sudo mount -o bind /etc/resolv.conf /mnt/etc/resolv.conf
```
### Change root directory
Changing the root directory will allow us to install new packages to the rootfs
@@ -118,7 +108,6 @@ umount /dev/pts
umount /proc
history -c
exit
umount /mnt/etc/resolv.conf
umount /mnt
```

View File

@@ -6,22 +6,22 @@ This document describes the device model supported by `cloud-hypervisor`.
| Device | Build configurable | Enabled by default | Runtime configurable |
| :----: | :----: | :----: | :----: |
| Serial port | :x: | :x: | :heavy_check_mark: |
| RTC/CMOS | :heavy_check_mark: | :heavy_check_mark: | :x: |
| I/O APIC | :x: | :x: | :heavy_check_mark: |
| i8042 shutdown/reboot | :x: | :x: | :x: |
| ACPI shutdown/reboot | :x: | :heavy_check_mark: | :x: |
| virtio-blk | :x: | :x: | :heavy_check_mark: |
| virtio-console | :x: | :x: | :heavy_check_mark: |
| virtio-iommu | :x: | :x: | :heavy_check_mark: |
| virtio-net | :x: | :x: | :heavy_check_mark: |
| virtio-pmem | :x: | :x: | :heavy_check_mark: |
| virtio-rng | :x: | :x: | :heavy_check_mark: |
| virtio-vsock | :x: | :x: | :heavy_check_mark: |
| vhost-user-blk | :x: | :x: | :heavy_check_mark: |
| vhost-user-fs | :x: | :x: | :heavy_check_mark: |
| vhost-user-net | :x: | :x: | :heavy_check_mark: |
| VFIO | :heavy_check_mark: | :x: | :heavy_check_mark: |
| Serial port | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
| RTC/CMOS | :heavy_check_mark: | :heavy_check_mark: | :negative_squared_cross_mark: |
| I/O APIC | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
| i8042 shutdown/reboot | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :negative_squared_cross_mark: |
| ACPI shutdown/reboot | :negative_squared_cross_mark: | :heavy_check_mark: | :negative_squared_cross_mark: |
| virtio-blk | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
| virtio-console | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
| virtio-iommu | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
| virtio-net | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
| virtio-pmem | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
| virtio-rng | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
| virtio-vsock | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
| vhost-user-blk | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
| vhost-user-fs | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
| vhost-user-net | :negative_squared_cross_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
| VFIO | :heavy_check_mark: | :negative_squared_cross_mark: | :heavy_check_mark: |
## Legacy devices

View File

@@ -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
@@ -29,46 +25,31 @@ mkdir /tmp/shared_dir
_Run virtiofsd_
```bash
./virtiofsd \
--log-level debug \
-d \
--socket-path=/tmp/virtiofs \
--shared-dir=/tmp/shared_dir \
--cache=never \
--thread-pool-size=$N
-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 `thread-pool-size` option controls how many IO threads are spawned. For
very fast storage like NVMe spawning enough worker threads is critical to
getting an acceptable performance compared to native.
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 \
@@ -79,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.

View File

@@ -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 `guest_debug` feature enabled:
```bash
cargo build --features guest_debug
```
To use the `--gdb` option, specify the Unix Domain Socket with `--path` that Cloud Hypervisor will use to communicate with the host's GDB:
```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)
```

View File

@@ -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,69 +49,27 @@ 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 \
--platform tdx=on
--firmware edk2-staging/Build/OvmfCh/RELEASE_GCC5/FV/OVMF.fd \
--tdx firmware=edk2-staging/Build/OvmfCh/RELEASE_GCC5/FV/OVMF.fd \
--cpus boot=1 \
--memory size=1G \
--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 \
--platform tdx=on
--firmware edk2-staging/Build/OvmfCh/DEBUG_GCC5/FV/OVMF.fd \
--tdx firmware=edk2-staging/Build/OvmfCh/DEBUG_GCC5/FV/OVMF.fd \
--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 \
--platform tdx=on
--firmware tdshim \
--kernel bzImage \
--cmdline "root=/dev/vda3 console=hvc0 rw"
--cpus boot=1 \
--memory size=1G \
--disk path=tdx_guest_img
```
### Guest kernel limitations
#### Serial ports disabled
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.
#### PCI hotplug through ACPI
Unless you run the guest kernel with the parameter `tdx_disable_filter`, ACPI
devices responsible for handling PCI hotplug (PCI hotplug controller, PCI
Express Bus and Generic Event Device) will not be allowed, therefore the
corresponding drivers will not be loaded and the PCI hotplug feature will not
be supported.

View File

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

View File

@@ -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
View 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)
```

View File

@@ -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
```

View File

@@ -43,22 +43,3 @@ $ perf report -g
```
If profiling with a network device attached either the TAP device must be already created and configured or the profiling must be done as root so that the TAP device can be created.
## Userspace only profiling with LBR
The use of LBR (Last Branch Record; available since Haswell) offers lower
overhead if only userspace profiling is required. This lower overhead can allow
a higher frequency of sampling. This also removes the requirement to compile
with custom `RUSTFLAGS` however debug symbols should still be included:
e.g.
```
$ perf record --call-graph lbr --all-user --user-callchains -g target/release/cloud-hypervisor \
--kernel ~/src/linux/vmlinux \
--pmem file=~/workloads/focal.raw \
--cpus boot=1 --memory size=1G \
--cmdline "root=/dev/pmem0p1 console=ttyS0" \
--serial tty --console off \
--api-socket=/tmp/api1
```

View File

@@ -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.

View File

@@ -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.

View File

@@ -1,42 +0,0 @@
# Tracing
Cloud Hypervisor has a basic tracing infrastucture, particularly focussed on
the tracing of the initial VM setup.
## Usage
To enabling tracing, build with "tracing" feature. When compiled without the
feature the tracing is compiled out.
```bash
cargo build --features "tracing"
```
And then run Cloud Hypervisor as you wish, the trace will be written to the current directory as `cloud-hypervisor-<pid>.trace`. This is JSON file which you can inspect yourself.
Alternatively you can use the provided script in
`scripts/ch-trace-visualiser.py` to generate an SVG:
For example:
```bash
scripts/ch-trace-visualiser.py cloud-hypervisor-39466.trace output.svg
```
## Tracing in the codebase
There are existing tracepoints in the code base; extra ones can be added for
more detailed tracing.
The `tracer::trace_scoped!()` macro is used to add the current existing scope
appears as a block in the trace. Other than providing a useful name for the
event nothing else is required from the developer.
The `tracer::start()` and `tracer::end()` functions are already in place for
generating traces of the boot. These can be relocated for focus tracing on a
narrow part of the code base.
A `tracer::trace_point!()` macro is also provided for an instantaneous trace
point however this is not in use in the code base currently nor is handled by
the visualisation script due to the difficulty in representation in the SVG.

View File

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

View File

@@ -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
```

View File

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

View File

@@ -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 \

View File

@@ -1,84 +0,0 @@
# VSOCK support
VSOCK provides a way for guest and host to communicate through a socket. VSOCK sockets support both stream and datagram types.
The `virtio-vsock` is based on the [Firecracker](https://github.com/firecracker-microvm/firecracker/blob/main/docs/vsock.md) implementation, where additional details can be found.
## What is a CID?
CID is a 32-bit context identifier describing the source or destination. In combination with the port, the complete addressing can be achieved to describe multiple listeners running on the same machine.
The table below depicts the well known CID values:
| CID | Description |
|-----|-------------|
| -1 | Random CID |
| 0 | Hypervisor |
| 1 | Loopback |
| 2 | Host |
## Prerequisites
### Kernel Requirements
Host kernel: CONFIG_VHOST_VSOCK
Guest kernel: CONFIG_VIRTIO_VSOCKETS
### Nested VM support
Linux __v5.5__ or newer is required for the L1 VM.
### Loopback support
Linux __v5.6__ or newer is required.
## Establishing VSOCK Connection
VSOCK device becomes available with `--vsock` option passed by the VM start. Cloud Hypervisor can be invoked for instance as below:
```bash
cloud-hypervisor \
--cpus boot=1 \
--memory size=4G \
--firmware CLOUDHV.fd \
--disk path=jammy-server-cloudimg.raw \
--vsock cid=3,socket=/tmp/ch.vsock
```
The examples use __socat__ `>=1.7.4` to illustrate the VSOCK functionality. However, there are other tools supporting VSOCK, like [ncat](https://stefano-garzarella.github.io/posts/2019-11-08-kvmforum-2019-vsock/).
### Connecting from Host to Guest
The host starts to listen on the defined port:
`$ socat - VSOCK-LISTEN:1234`
Once the host is listening, the guest can send data:
`echo -e "CONNECT 1234\\nHello from host!" | socat - UNIX-CONNECT:/tmp/ch.vsock
Note the string `CONNECT <port>` prepended to the actual data. It is possible for the guest to start listening on different ports, thus the specific command is needed to instruct VSOCK to which listener the host wants to connect. It needs to be sent once per connection. Once the connection established, data transfers can take place directly.
### Connecting from Guest to Host
This first requires a listening UNIX socket on the host side. The UNIX socket path has to be constructed by using the socket path used at the VM launch time with appended `_` and the port number to be used on the guest side. As in the example above, if we'd intended to connect from the guest to the port `1234`, the Unix socket path on the host side would be `/tmp/ch.vsock_1234`.
Also note that the CID used on the guest side is the well known CID value `2`.
Listening on the host side:
`$ socat - UNIX-LISTEN:/tmp/ch.vsock_1234`
From the guest:
`$ echo -e "Hello from guest!" | socat - VSOCK-CONNECT:2:1234`
## Links
- [virtio-vsock in QEMU, Firecracker and Linux: Status, Performance and Challenges](https://kvmforum2019.sched.com/event/TmwK)
- [Leveraging virtio-vsock in the cloud and containers](https://archive.fosdem.org/2021/schedule/event/vai_virtio_vsock/)
- [VSOCK man page](https://manpages.ubuntu.com/manpages/focal/man7/vsock.7.html)
- [https://stefano-garzarella.github.io/posts/2020-02-20-vsock-nested-vms-loopback/](https://stefano-garzarella.github.io/posts/2020-02-20-vsock-nested-vms-loopback/)
- [https://github.com/firecracker-microvm/firecracker/blob/main/docs/vsock.md](https://github.com/firecracker-microvm/firecracker/blob/main/docs/vsock.md)

View File

@@ -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.

View File

@@ -2,9 +2,10 @@
name = "event_monitor"
version = "0.1.0"
authors = ["The Cloud Hypervisor Authors"]
edition = "2021"
edition = "2018"
[dependencies]
libc = "0.2.133"
serde = { version = "1.0.145", features = ["rc", "derive"] }
serde_json = "1.0.85"
libc = "0.2.112"
serde = { version = "1.0.133", features = ["rc"] }
serde_derive = "1.0.133"
serde_json = "1.0.75"

View File

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

442
fuzz/Cargo.lock generated
View File

@@ -11,9 +11,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.65"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
[[package]]
name = "api_client"
@@ -24,20 +24,21 @@ dependencies = [
[[package]]
name = "arbitrary"
version = "1.1.6"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f44124848854b941eafdb34f05b3bcf59472f643c7e151eba7c2b69daa469ed5"
checksum = "510c76ecefdceada737ea728f4f9a84bd2e1ef29f1ba555e560940fe279954de"
[[package]]
name = "arc-swap"
version = "1.5.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164"
checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f"
[[package]]
name = "arch"
version = "0.1.0"
dependencies = [
"acpi_tables",
"anyhow",
"byteorder",
"fdt",
@@ -46,8 +47,8 @@ dependencies = [
"linux-loader",
"log",
"serde",
"serde_derive",
"thiserror",
"uuid",
"versionize",
"versionize_derive",
"vm-fdt",
@@ -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,12 +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"
dependencies = [
"jobserver",
]
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
[[package]]
name = "cfg-if"
@@ -130,33 +128,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "3.2.22"
version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750"
checksum = "7a30c3bf9ff12dfe5dae53f0a96e0febcd18420d1c0e7fad77796d9d5c4b5375"
dependencies = [
"atty",
"bitflags",
"clap_lex",
"indexmap",
"once_cell",
"lazy_static",
"os_str_bytes",
"strsim",
"termcolor",
"terminal_size",
"textwrap",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "cloud-hypervisor"
version = "26.0.0"
version = "20.0.0"
dependencies = [
"anyhow",
"api_client",
@@ -171,7 +160,6 @@ dependencies = [
"serde_json",
"signal-hook",
"thiserror",
"tracer",
"vm-memory",
"vmm",
"vmm-sys-util",
@@ -183,29 +171,23 @@ version = "0.0.0"
dependencies = [
"block_util",
"cloud-hypervisor",
"devices",
"epoll",
"libc",
"libfuzzer-sys",
"micro_http",
"once_cell",
"qcow",
"seccompiler",
"vhdx",
"virtio-devices",
"virtio-queue",
"vm-device",
"vm-memory",
"vm-virtio",
"vmm",
"vmm-sys-util",
]
[[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,41 +198,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55626594feae15d266d52440b26ff77de0e22230cf0c113abe619084c1ddc910"
[[package]]
name = "darling"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "devices"
version = "0.1.0"
@@ -260,7 +207,7 @@ dependencies = [
"arch",
"bitflags",
"byteorder",
"hypervisor",
"epoll",
"libc",
"log",
"versionize",
@@ -281,53 +228,27 @@ dependencies = [
"libc",
]
[[package]]
name = "errno"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "event_monitor"
version = "0.1.0"
dependencies = [
"libc",
"serde",
"serde_derive",
"serde_json",
]
[[package]]
name = "fdt"
version = "0.1.4"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "964f5becd44d069dca0beea2b4bc05639ae7bf3b3f5369c295aff360bb57cca2"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
checksum = "b643857cf70949306b81d7e92cb9d47add673868edac9863c4a49c42feaf3f1e"
[[package]]
name = "getrandom"
version = "0.2.7"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
dependencies = [
"cfg-if",
"libc",
@@ -336,9 +257,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.12.3"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "hermit-abi"
@@ -354,57 +275,45 @@ name = "hypervisor"
version = "0.1.0"
dependencies = [
"anyhow",
"byteorder",
"epoll",
"iced-x86",
"kvm-bindings",
"kvm-ioctls",
"libc",
"log",
"serde",
"serde_with",
"serde_derive",
"serde_json",
"thiserror",
"vfio-ioctls",
"vm-memory",
"vmm-sys-util",
]
[[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",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "indexmap"
version = "1.9.1"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "io-lifetimes"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ea37f355c05dde75b84bba2d767906ad522e97cd9e2eef2be7a4ab7fb442c06"
[[package]]
name = "io-uring"
version = "0.5.6"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a645fd8e7048c2f54050f7d0d4de8f624ada437e043bc7d72609bd08d70120b"
checksum = "8d75829ed9377bab6c90039fe47b9d84caceb4b5063266142e21bcce6550cda8"
dependencies = [
"bitflags",
"libc",
@@ -412,23 +321,14 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.3"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
[[package]]
name = "jobserver"
version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
dependencies = [
"libc",
]
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",
@@ -438,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",
@@ -453,15 +353,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.133"
version = "0.2.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
[[package]]
name = "libfuzzer-sys"
version = "0.4.4"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae185684fe19814afd066da15a7cc41e126886c21282934225d9fc847582da58"
checksum = "36a9a84a6e8b55dfefb04235e55edb2b9a2a18488fcae777a6bdaa6f06f1deb3"
dependencies = [
"arbitrary",
"cc",
@@ -470,32 +370,32 @@ dependencies = [
[[package]]
name = "linux-loader"
version = "0.6.0"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62a2f912deca034ec34b0a43a390059ea98daac40e440ebe8bea88f3315fe168"
checksum = "8a5e77493808403a6bd56a301a64ea6b9342e36ea845044bf0dfdf56fe52fa08"
dependencies = [
"vm-memory",
]
[[package]]
name = "linux-raw-sys"
version = "0.0.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d"
[[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 = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "micro_http"
version = "0.1.0"
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#863b0370ba7e57f7df5b908ada9e5b44809ccae9"
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#9517a300370a158a7af0996b7eebf040d171e1a4"
dependencies = [
"libc",
"vmm-sys-util",
@@ -519,7 +419,6 @@ dependencies = [
"net_gen",
"rate_limiter",
"serde",
"thiserror",
"versionize",
"versionize_derive",
"virtio-bindings",
@@ -531,9 +430,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.15.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "option_parser"
@@ -541,9 +440,12 @@ version = "0.1.0"
[[package]]
name = "os_str_bytes"
version = "6.3.0"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
dependencies = [
"memchr",
]
[[package]]
name = "pci"
@@ -554,7 +456,6 @@ dependencies = [
"hypervisor",
"libc",
"log",
"serde",
"thiserror",
"versionize",
"versionize_derive",
@@ -570,11 +471,11 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.45"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3edcd08cf4fea98d1ae6c9ddd3b8ccb1acac7c3693d62625969a7daa04a2ae36"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
"unicode-ident",
"unicode-xid",
]
[[package]]
@@ -590,9 +491,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.21"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
dependencies = [
"proc-macro2",
]
@@ -608,9 +509,9 @@ dependencies = [
[[package]]
name = "remain"
version = "0.2.4"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06df529c0d271b27ac4a2c260f5ce2914b60bd44702cecec7b9f271adbdde23b"
checksum = "70ba1e78fa68412cb93ef642fd4d20b9a941be49ee9333875ebaf13112673ea7"
dependencies = [
"proc-macro2",
"quote",
@@ -626,55 +527,38 @@ dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.35.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbb2fda4666def1433b1b05431ab402e42a1084285477222b72d6c564c417cef"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "ryu"
version = "1.0.11"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]]
name = "seccompiler"
version = "0.3.0"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f6575e3c2b3a0fe2ef3e53855b6a8dead7c29f783da5e123d378c8c6a89017e"
checksum = "e01d1292a1131b22ccea49f30bd106f1238b5ddeec1a98d39268dcc31d540e68"
dependencies = [
"libc",
]
[[package]]
name = "semver"
version = "1.0.14"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
[[package]]
name = "serde"
version = "1.0.145"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
dependencies = [
"serde_derive",
]
checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a"
[[package]]
name = "serde_derive"
version = "1.0.145"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537"
dependencies = [
"proc-macro2",
"quote",
@@ -683,46 +567,20 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.85"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
checksum = "c059c05b48c5c0067d4b4b2b4f0732dd65feb52daf7e0ea09cd87e7dadc1af79"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_with"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368f2d60d049ea019a84dcd6687b0d1e0030fe663ae105039bdf967ed5e6a9a7"
dependencies = [
"serde",
"serde_with_macros",
]
[[package]]
name = "serde_with_macros"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ccadfacf6cf10faad22bbadf55986bdd0856edfb5d9210aa1dcf1f516e84e93"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serial_buffer"
version = "0.1.0"
[[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",
@@ -751,57 +609,57 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.101"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
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",
]
[[package]]
name = "terminal_size"
version = "0.2.1"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8440c860cf79def6164e4a0a983bcc2305d82419177a0e0c71930d049e3ac5a1"
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
dependencies = [
"rustix",
"windows-sys",
"libc",
"winapi",
]
[[package]]
name = "textwrap"
version = "0.15.1"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
dependencies = [
"terminal_size",
]
[[package]]
name = "thiserror"
version = "1.0.37"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.37"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [
"proc-macro2",
"quote",
@@ -809,27 +667,16 @@ dependencies = [
]
[[package]]
name = "tracer"
version = "0.1.0"
dependencies = [
"libc",
"log",
"once_cell",
"serde",
"serde_json",
]
[[package]]
name = "unicode-ident"
version = "1.0.4"
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "uuid"
version = "1.1.2"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
dependencies = [
"getrandom",
]
@@ -873,14 +720,12 @@ dependencies = [
[[package]]
name = "vfio-ioctls"
version = "0.1.0"
source = "git+https://github.com/rust-vmm/vfio?branch=main#bf7c9b997b4ad0ca1ab4bfe96dc1b10d4bc5be24"
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",
@@ -890,6 +735,7 @@ dependencies = [
name = "vfio_user"
version = "0.1.0"
dependencies = [
"anyhow",
"libc",
"log",
"serde",
@@ -912,13 +758,14 @@ dependencies = [
"remain",
"thiserror",
"uuid",
"vmm-sys-util",
]
[[package]]
name = "vhost"
version = "0.5.0"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79243657c76e5c90dcbf60187c842614f6dfc7123972c55bb3bcc446792aca93"
checksum = "3b56bf8f178fc500fe14505fca8b00dec76fc38f2304f461c8d9d7547982311d"
dependencies = [
"bitflags",
"libc",
@@ -951,9 +798,8 @@ dependencies = [
"rate_limiter",
"seccompiler",
"serde",
"serde_derive",
"serde_json",
"serial_buffer",
"thiserror",
"versionize",
"versionize_derive",
"vhost",
@@ -969,12 +815,9 @@ dependencies = [
[[package]]
name = "virtio-queue"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2b7bea73248006630c9611a61ac3b8e44eece09342801fe1faec583b7a9f02"
version = "0.1.0"
dependencies = [
"log",
"virtio-bindings",
"vm-memory",
"vmm-sys-util",
]
@@ -993,8 +836,9 @@ name = "vm-device"
version = "0.1.0"
dependencies = [
"anyhow",
"hypervisor",
"serde",
"serde_derive",
"serde_json",
"thiserror",
"vfio-ioctls",
"vm-memory",
@@ -1008,9 +852,9 @@ source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#720e48e435b791ec6cb
[[package]]
name = "vm-memory"
version = "0.9.0"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "583f213899e8a5eea23d9c507252d4bed5bc88f0ecbe0783262f80034630744b"
checksum = "339d4349c126fdcd87e034631d7274370cf19eb0e87b33166bcd956589fc72c5"
dependencies = [
"arc-swap",
"libc",
@@ -1023,6 +867,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"serde",
"serde_derive",
"serde_json",
"thiserror",
"versionize",
@@ -1035,6 +880,7 @@ name = "vm-virtio"
version = "0.1.0"
dependencies = [
"log",
"virtio-bindings",
"virtio-queue",
"vm-memory",
]
@@ -1054,22 +900,21 @@ dependencies = [
"epoll",
"event_monitor",
"hypervisor",
"lazy_static",
"libc",
"linux-loader",
"log",
"micro_http",
"net_util",
"once_cell",
"option_parser",
"pci",
"qcow",
"seccompiler",
"serde",
"serde_derive",
"serde_json",
"serial_buffer",
"signal-hook",
"thiserror",
"tracer",
"uuid",
"versionize",
"versionize_derive",
@@ -1088,9 +933,9 @@ dependencies = [
[[package]]
name = "vmm-sys-util"
version = "0.10.0"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08604d7be03eb26e33b3cee3ed4aef2bf550b305d1cca60e84da5d28d3790b62"
checksum = "733537bded03aaa93543f785ae997727b30d1d9f4a03b7861d23290474242e11"
dependencies = [
"bitflags",
"libc",
@@ -1100,9 +945,9 @@ dependencies = [
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "winapi"
@@ -1134,46 +979,3 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"

View File

@@ -3,35 +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" }
devices = { path = "../devices" }
epoll = "4.3.1"
libc = "0.2.133"
libfuzzer-sys = "0.4.4"
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
once_cell = "1.15.0"
libc = "0.2.112"
libfuzzer-sys = "0.4.2"
qcow = { path = "../qcow" }
seccompiler = "0.3.0"
seccompiler = "0.2.0"
vhdx = { path = "../vhdx" }
virtio-devices = { path = "../virtio-devices" }
virtio-queue = "0.6.1"
vmm = { path = "../vmm" }
vmm-sys-util = "0.10.0"
vm-memory = "0.9.0"
vm-device = { path = "../vm-device" }
virtio-queue = { path = "../virtio-queue" }
vmm-sys-util = "0.9.0"
vm-virtio = { path = "../vm-virtio" }
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" }
@@ -40,8 +34,8 @@ versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_deri
members = ["."]
[[bin]]
name = "balloon"
path = "fuzz_targets/balloon.rs"
name = "qcow"
path = "fuzz_targets/qcow.rs"
test = false
doc = false
@@ -51,50 +45,8 @@ path = "fuzz_targets/block.rs"
test = false
doc = false
[[bin]]
name = "cmos"
path = "fuzz_targets/cmos.rs"
test = false
doc = false
[[bin]]
name = "http_api"
path = "fuzz_targets/http_api.rs"
test = false
doc = false
[[bin]]
name = "pmem"
path = "fuzz_targets/pmem.rs"
test = false
doc = false
[[bin]]
name = "qcow"
path = "fuzz_targets/qcow.rs"
test = false
doc = false
[[bin]]
name = "rng"
path = "fuzz_targets/rng.rs"
test = false
doc = false
[[bin]]
name = "serial"
path = "fuzz_targets/serial.rs"
test = false
doc = false
[[bin]]
name = "vhdx"
path = "fuzz_targets/vhdx.rs"
test = false
doc = false
[[bin]]
name = "watchdog"
path = "fuzz_targets/watchdog.rs"
test = false
doc = false

View File

@@ -1,152 +0,0 @@
// Copyright © 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
#![no_main]
use libfuzzer_sys::fuzz_target;
use seccompiler::SeccompAction;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::Arc;
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
const QUEUE_DATA_SIZE: usize = 4;
const MEM_SIZE: usize = 1 * 1024 * 1024;
const BALLOON_SIZE: u64 = 512 * 1024;
// Number of queues
const QUEUE_NUM: usize = 3;
// Max entries in the queue.
const QUEUE_SIZE: u16 = 256;
// Descriptor table alignment
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
// Avalable ring alignment
const AVAIL_RING_ALIGN_SIZE: u64 = 2;
// Used ring alignment
const USED_RING_ALIGN_SIZE: u64 = 4;
// Descriptor table size
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
// Available ring size
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
// Used ring size
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
fuzz_target!(|bytes| {
if bytes.len() < QUEUE_DATA_SIZE * QUEUE_NUM
|| bytes.len() > (QUEUE_DATA_SIZE * QUEUE_NUM + MEM_SIZE)
{
return;
}
let mut balloon = virtio_devices::Balloon::new(
"fuzzer_balloon".to_owned(),
BALLOON_SIZE,
true,
true,
SeccompAction::Allow,
EventFd::new(EFD_NONBLOCK).unwrap(),
)
.unwrap();
let queue_data = &bytes[..QUEUE_DATA_SIZE * QUEUE_NUM];
let mem_bytes = &bytes[QUEUE_DATA_SIZE * QUEUE_NUM..];
// Setup the guest memory with the input bytes
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return;
}
let guest_memory = GuestMemoryAtomic::new(mem);
// Setup the virt queues with the input bytes
let mut queues = setup_virt_queues(
&[
&queue_data[..QUEUE_DATA_SIZE].try_into().unwrap(),
&queue_data[QUEUE_DATA_SIZE..QUEUE_DATA_SIZE * 2]
.try_into()
.unwrap(),
&queue_data[QUEUE_DATA_SIZE * 2..QUEUE_DATA_SIZE * 3]
.try_into()
.unwrap(),
],
0,
);
let inflate_q = queues.remove(0);
let inflate_evt = EventFd::new(0).unwrap();
let inflate_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(inflate_evt.as_raw_fd())) };
let deflate_q = queues.remove(0);
let deflate_evt = EventFd::new(0).unwrap();
let deflate_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(deflate_evt.as_raw_fd())) };
let reporting_q = queues.remove(0);
let reporting_evt = EventFd::new(0).unwrap();
let reporting_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(reporting_evt.as_raw_fd())) };
// Kick the 'queue' events before activate the balloon device
inflate_queue_evt.write(1).unwrap();
deflate_queue_evt.write(1).unwrap();
reporting_queue_evt.write(1).unwrap();
balloon
.activate(
guest_memory,
Arc::new(NoopVirtioInterrupt {}),
vec![
(0, inflate_q, inflate_evt),
(1, deflate_q, deflate_evt),
(2, reporting_q, reporting_evt),
],
)
.ok();
// Wait for the events to finish and balloon device worker thread to return
balloon.wait_for_epoll_threads();
});
pub struct NoopVirtioInterrupt {}
impl VirtioInterrupt for NoopVirtioInterrupt {
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
Ok(())
}
}
macro_rules! align {
($n:expr, $align:expr) => {{
(($n + $align - 1) / $align) * $align
}};
}
fn setup_virt_queues(bytes: &[&[u8; QUEUE_DATA_SIZE]], base_addr: u64) -> Vec<Queue> {
let mut queues = Vec::new();
let mut base_addr = base_addr;
for b in bytes {
let mut q = Queue::new(QUEUE_SIZE).unwrap();
let desc_table_addr = align!(base_addr, DESC_TABLE_ALIGN_SIZE);
let avail_ring_addr = align!(desc_table_addr + DESC_TABLE_SIZE, AVAIL_RING_ALIGN_SIZE);
let used_ring_addr = align!(avail_ring_addr + AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE);
q.try_set_desc_table_address(GuestAddress(desc_table_addr))
.unwrap();
q.try_set_avail_ring_address(GuestAddress(avail_ring_addr))
.unwrap();
q.try_set_used_ring_address(GuestAddress(used_ring_addr))
.unwrap();
q.set_next_avail(b[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
q.set_next_used(b[1] as u16);
q.set_event_idx(b[2] % 2 != 0);
q.set_size(b[3] as u16 % QUEUE_SIZE);
q.set_ready(true);
queues.push(q);
base_addr = used_ring_addr + USED_RING_SIZE;
}
queues
}

View File

@@ -1,10 +1,6 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Copyright © 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
#![no_main]
@@ -13,42 +9,89 @@ use libfuzzer_sys::fuzz_target;
use seccompiler::SeccompAction;
use std::ffi;
use std::fs::File;
use std::io;
use std::io::{self, Cursor, Read, Seek, SeekFrom};
use std::mem::size_of;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::path::PathBuf;
use std::sync::Arc;
use virtio_devices::{Block, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
use virtio_queue::{Queue, QueueState};
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
const QUEUE_DATA_SIZE: usize = 4;
const MEM_SIZE: usize = 256 * 1024 * 1024;
// Max entries in the queue.
const QUEUE_SIZE: u16 = 256;
// Guest physical address for descriptor table.
const DESC_TABLE_ADDR: u64 = 0;
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
// Guest physical address for available ring
const AVAIL_RING_ADDR: u64 = DESC_TABLE_ADDR + DESC_TABLE_SIZE;
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
// Guest physical address for used ring (requires to 4-bytes aligned)
const USED_RING_ADDR: u64 = (AVAIL_RING_ADDR + AVAIL_RING_SIZE + 3) & !3_u64;
const MEM_SIZE: u64 = 256 * 1024 * 1024;
const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor.
const QUEUE_SIZE: u16 = 16; // Max entries in the queue.
const CMD_SIZE: usize = 16; // Bytes in the command.
fuzz_target!(|bytes| {
if bytes.len() < QUEUE_DATA_SIZE || bytes.len() > (QUEUE_DATA_SIZE + MEM_SIZE) {
let size_u64 = size_of::<u64>();
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE as usize)]).unwrap();
// The fuzz data is interpreted as:
// starting index 8 bytes
// command location 8 bytes
// command 16 bytes
// descriptors circular buffer 16 bytes * 3
if bytes.len() < 4 * size_u64 {
// Need an index to start.
return;
}
let queue_data = &bytes[..QUEUE_DATA_SIZE];
let mem_bytes = &bytes[QUEUE_DATA_SIZE..];
let mut data_image = Cursor::new(bytes);
let first_index = read_u64(&mut data_image);
if first_index > MEM_SIZE / DESC_SIZE {
return;
}
let first_offset = first_index * DESC_SIZE;
if first_offset as usize + size_u64 > bytes.len() {
return;
}
let command_addr = read_u64(&mut data_image);
if command_addr > MEM_SIZE - CMD_SIZE as u64 {
return;
}
if mem
.write_slice(
&bytes[2 * size_u64..(2 * size_u64) + CMD_SIZE],
GuestAddress(command_addr as u64),
)
.is_err()
{
return;
}
data_image.seek(SeekFrom::Start(first_offset)).unwrap();
let desc_table = read_u64(&mut data_image);
if mem
.write_slice(&bytes[32..], GuestAddress(desc_table as u64))
.is_err()
{
return;
}
let guest_memory = GuestMemoryAtomic::new(mem);
let mut q = Queue::<
GuestMemoryAtomic<GuestMemoryMmap>,
QueueState,
>::new(guest_memory.clone(), QUEUE_SIZE);
q.state.ready = true;
q.state.size = QUEUE_SIZE / 2;
let queue_evts: Vec<EventFd> = vec![EventFd::new(0).unwrap()];
let queue_fd = queue_evts[0].as_raw_fd();
let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(queue_fd)) };
// Create a virtio-block device backed by a synchronous raw file
let shm = memfd_create(&ffi::CString::new("fuzz").unwrap(), 0).unwrap();
let disk_file: File = unsafe { File::from_raw_fd(shm) };
let qcow_disk = Box::new(RawFileDiskSync::new(disk_file)) as Box<dyn DiskFile>;
let mut block = Block::new(
"tmp".to_owned(),
qcow_disk,
@@ -63,34 +106,24 @@ fuzz_target!(|bytes| {
)
.unwrap();
// Setup the virt queue with the input bytes
let q = setup_virt_queue(queue_data.try_into().unwrap());
// Setup the guest memory with the input bytes
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return;
}
let guest_memory = GuestMemoryAtomic::new(mem);
let evt = EventFd::new(0).unwrap();
let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) };
// Kick the 'queue' event before activate the block device
queue_evt.write(1).unwrap();
block
.activate(
guest_memory,
Arc::new(NoopVirtioInterrupt {}),
vec![(0, q, evt)],
vec![q],
queue_evts,
)
.ok();
// Wait for the events to finish and block device worker thread to return
block.wait_for_epoll_threads();
queue_evt.write(77).unwrap(); // Rings the doorbell, any byte will do.
});
fn read_u64<T: Read>(readable: &mut T) -> u64 {
let mut buf = [0u8; size_of::<u64>()];
readable.read_exact(&mut buf[..]).unwrap();
u64::from_le_bytes(buf)
}
fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> {
let res = unsafe { libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags) };
@@ -104,25 +137,11 @@ fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> {
pub struct NoopVirtioInterrupt {}
impl VirtioInterrupt for NoopVirtioInterrupt {
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
fn trigger(
&self,
_int_type: &VirtioInterruptType,
_queue: Option<&Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
) -> std::result::Result<(), std::io::Error> {
Ok(())
}
}
fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue {
let mut q = Queue::new(QUEUE_SIZE).unwrap();
q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
q.set_next_used(bytes[1] as u16);
q.set_event_idx(bytes[2] % 2 != 0);
q.set_size(bytes[3] as u16 % QUEUE_SIZE);
q.try_set_desc_table_address(GuestAddress(DESC_TABLE_ADDR))
.unwrap();
q.try_set_avail_ring_address(GuestAddress(AVAIL_RING_ADDR))
.unwrap();
q.try_set_used_ring_address(GuestAddress(USED_RING_ADDR))
.unwrap();
q.set_ready(true);
q
}

View File

@@ -1,48 +0,0 @@
// Copyright © 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
#![no_main]
use devices::legacy::Cmos;
use libc::EFD_NONBLOCK;
use libfuzzer_sys::fuzz_target;
use vm_device::BusDevice;
use vmm_sys_util::eventfd::EventFd;
fuzz_target!(|bytes| {
// Need at least 16 bytes for the test
if bytes.len() < 16 {
return;
}
let mut below_4g = [0u8; 8];
let mut above_4g = [0u8; 8];
below_4g.copy_from_slice(&bytes[0..8]);
above_4g.copy_from_slice(&bytes[8..16]);
let mut cmos = Cmos::new(
u64::from_le_bytes(below_4g),
u64::from_le_bytes(above_4g),
EventFd::new(EFD_NONBLOCK).unwrap(),
);
let mut i = 16;
while i < bytes.len() {
let read = bytes.get(i).unwrap_or(&0) % 2 == 0;
i += 1;
if read {
let offset = (bytes.get(i).unwrap_or(&0) % 2) as u64;
i += 1;
let mut out_bytes = vec![0];
cmos.read(0, offset, &mut out_bytes);
} else {
let offset = (bytes.get(i).unwrap_or(&0) % 2) as u64;
i += 1;
let data = vec![*bytes.get(i).unwrap_or(&0)];
i += 1;
cmos.write(0, offset, &data);
}
}
});

View File

@@ -1,191 +0,0 @@
// Copyright © 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
#![no_main]
use libfuzzer_sys::fuzz_target;
use micro_http::Request;
use once_cell::sync::Lazy;
use std::os::unix::io::AsRawFd;
use std::sync::mpsc::{channel, Receiver};
use std::thread;
use vmm::api::{http::*, ApiRequest, ApiResponsePayload};
use vmm::{EpollContext, EpollDispatch};
use vmm_sys_util::eventfd::EventFd;
// Need to be ordered for test case reproducibility
static ROUTES: Lazy<Vec<&Box<dyn EndpointHandler + Sync + Send>>> =
Lazy::new(|| HTTP_ROUTES.routes.values().collect());
fuzz_target!(|bytes| {
if bytes.len() < 2 {
return;
}
let route = ROUTES[bytes[0] as usize % ROUTES.len()];
if let Some(request) = generate_request(&bytes[1..]) {
let exit_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
let api_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
let (api_sender, api_receiver) = channel();
let http_receiver_thread = {
let exit_evt = exit_evt.try_clone().unwrap();
let api_evt = api_evt.try_clone().unwrap();
thread::Builder::new()
.name("http_receiver".to_string())
.spawn(move || {
http_receiver_stub(exit_evt, api_evt, api_receiver);
})
.unwrap()
};
route.handle_request(&request, api_evt, api_sender);
exit_evt.write(1).ok();
http_receiver_thread.join().unwrap();
};
});
fn generate_request(bytes: &[u8]) -> Option<Request> {
let req_method = match bytes[0] % 5 {
0 => "GET",
1 => "PUT",
2 => "PATCH",
3 => "POST",
_ => "INVALID",
};
let request_line = format!("{} http://localhost/home HTTP/1.1\r\n", req_method);
let req_body = &bytes[1..];
let request = if req_body.len() > 0 {
[
format!("{}Content-Length: {}\r\n", request_line, req_body.len()).as_bytes(),
req_body,
]
.concat()
} else {
format!("{}\r\n", request_line).as_bytes().to_vec()
};
Request::try_from(&request, None).ok()
}
fn http_receiver_stub(exit_evt: EventFd, api_evt: EventFd, api_receiver: Receiver<ApiRequest>) {
let mut epoll = EpollContext::new().unwrap();
epoll.add_event(&exit_evt, EpollDispatch::Exit).unwrap();
epoll.add_event(&api_evt, EpollDispatch::Api).unwrap();
let epoll_fd = epoll.as_raw_fd();
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); 2];
let num_events;
loop {
num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) {
Ok(num_events) => num_events,
Err(e) => match e.raw_os_error() {
Some(libc::EAGAIN) | Some(libc::EINTR) => continue,
_ => panic!("Unexpected epoll::wait error!"),
},
};
break;
}
for event in events.iter().take(num_events) {
let dispatch_event: EpollDispatch = event.data.into();
match dispatch_event {
EpollDispatch::Exit => {
break;
}
EpollDispatch::Api => {
for _ in 0..api_evt.read().unwrap() {
let api_request = api_receiver.recv().unwrap();
match api_request {
ApiRequest::VmCreate(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmDelete(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmBoot(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmShutdown(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmReboot(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmInfo(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmmPing(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmPause(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmResume(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmSnapshot(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmRestore(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmmShutdown(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmResize(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmResizeZone(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmAddDevice(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmAddUserDevice(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmRemoveDevice(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmAddDisk(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmAddFs(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmAddPmem(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmAddNet(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmAddVdpa(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmAddVsock(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmCounters(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmReceiveMigration(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmSendMigration(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmPowerButton(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
}
}
}
_ => {
panic!("Unexpected Epoll event");
}
}
}
}

View File

@@ -1,150 +0,0 @@
// Copyright © 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
#![no_main]
use libc::{MAP_NORESERVE, MAP_PRIVATE, PROT_READ, PROT_WRITE};
use libfuzzer_sys::fuzz_target;
use seccompiler::SeccompAction;
use std::ffi;
use std::fs::File;
use std::io;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::sync::Arc;
use virtio_devices::{Pmem, UserspaceMapping, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
use vm_memory::guest_memory::FileOffset;
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic, MmapRegion};
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
const QUEUE_DATA_SIZE: usize = 4;
const MEM_SIZE: usize = 256 * 1024 * 1024;
const PMEM_FILE_SIZE: usize = 128 * 1024 * 1024;
// Max entries in the queue.
const QUEUE_SIZE: u16 = 256;
// Guest physical address for descriptor table.
const DESC_TABLE_ADDR: u64 = 0;
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
// Guest physical address for available ring
const AVAIL_RING_ADDR: u64 = DESC_TABLE_ADDR + DESC_TABLE_SIZE;
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
// Guest physical address for used ring (requires to 4-bytes aligned)
const USED_RING_ADDR: u64 = (AVAIL_RING_ADDR + AVAIL_RING_SIZE + 3) & !3_u64;
fuzz_target!(|bytes| {
if bytes.len() < QUEUE_DATA_SIZE || bytes.len() > (QUEUE_DATA_SIZE + MEM_SIZE) {
return;
}
let mut pmem = create_dummy_pmem();
let queue_data = &bytes[..QUEUE_DATA_SIZE];
let mem_bytes = &bytes[QUEUE_DATA_SIZE..];
// Setup the virt queue with the input bytes
let q = setup_virt_queue(queue_data.try_into().unwrap());
// Setup the guest memory with the input bytes
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return;
}
let guest_memory = GuestMemoryAtomic::new(mem);
let evt = EventFd::new(0).unwrap();
let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) };
// Kick the 'queue' event before activate the pmem device
queue_evt.write(1).unwrap();
pmem.activate(
guest_memory,
Arc::new(NoopVirtioInterrupt {}),
vec![(0, q, evt)],
)
.ok();
// Wait for the events to finish and pmem device worker thread to return
pmem.wait_for_epoll_threads();
});
fn memfd_create_with_size(name: &ffi::CStr, flags: u32, size: usize) -> Result<RawFd, io::Error> {
let fd = unsafe { libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags) };
if fd < 0 {
return Err(io::Error::last_os_error());
}
let res = unsafe { libc::syscall(libc::SYS_ftruncate, fd, size) };
if res < 0 {
return Err(io::Error::last_os_error());
}
Ok(fd as RawFd)
}
pub struct NoopVirtioInterrupt {}
impl VirtioInterrupt for NoopVirtioInterrupt {
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
Ok(())
}
}
// Create a dummy virtio-pmem device for fuzzing purpose only
fn create_dummy_pmem() -> Pmem {
let shm =
memfd_create_with_size(&ffi::CString::new("fuzz").unwrap(), 0, PMEM_FILE_SIZE).unwrap();
let file: File = unsafe { File::from_raw_fd(shm) };
// The fuzzer is focusing on the virtio-pmem code that processes guest inputs (e.g. virt queues),
// so dummy mappings (both mmap and user mapping) does the job and is faster (using smaller amount of memory).
let dummy_mapping_size = 1;
let cloned_file = file.try_clone().unwrap();
let dummy_mmap_region = MmapRegion::build(
Some(FileOffset::new(cloned_file, 0)),
dummy_mapping_size,
PROT_READ | PROT_WRITE,
MAP_NORESERVE | MAP_PRIVATE,
)
.unwrap();
let guest_addr = GuestAddress(0);
let dummy_user_mapping = UserspaceMapping {
host_addr: dummy_mmap_region.as_ptr() as u64,
mem_slot: 0,
addr: guest_addr,
len: dummy_mapping_size as u64,
mergeable: false,
};
Pmem::new(
"tmp".to_owned(),
file,
guest_addr,
dummy_user_mapping,
dummy_mmap_region,
false,
SeccompAction::Allow,
EventFd::new(EFD_NONBLOCK).unwrap(),
)
.unwrap()
}
fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue {
let mut q = Queue::new(QUEUE_SIZE).unwrap();
q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
q.set_next_used(bytes[1] as u16);
q.set_event_idx(bytes[2] % 2 != 0);
q.set_size(bytes[3] as u16 % QUEUE_SIZE);
q.try_set_desc_table_address(GuestAddress(DESC_TABLE_ADDR))
.unwrap();
q.try_set_avail_ring_address(GuestAddress(AVAIL_RING_ADDR))
.unwrap();
q.try_set_used_ring_address(GuestAddress(USED_RING_ADDR))
.unwrap();
q.set_ready(true);
q
}

View File

@@ -1,134 +0,0 @@
// Copyright © 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
#![no_main]
use libfuzzer_sys::fuzz_target;
use seccompiler::SeccompAction;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::Arc;
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
macro_rules! align {
($n:expr, $align:expr) => {{
(($n + $align - 1) / $align) * $align
}};
}
const QUEUE_DATA_SIZE: usize = 4;
const MEM_SIZE: usize = 1 * 1024 * 1024;
// Max entries in the queue.
const QUEUE_SIZE: u16 = 256;
// Descriptor table alignment
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
// Avalable ring alignment
const AVAIL_RING_ALIGN_SIZE: u64 = 2;
// Used ring alignment
const USED_RING_ALIGN_SIZE: u64 = 4;
// Descriptor table size
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
// Available ring size
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
// Used ring size
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
// Guest memory gap
const GUEST_MEM_GAP: u64 = 1 * 1024 * 1024;
// Guest physical address for descriptor table.
const DESC_TABLE_ADDR: u64 = align!(MEM_SIZE as u64 + GUEST_MEM_GAP, DESC_TABLE_ALIGN_SIZE);
// Guest physical address for available ring
const AVAIL_RING_ADDR: u64 = align!(DESC_TABLE_ADDR + DESC_TABLE_SIZE, AVAIL_RING_ALIGN_SIZE);
// Guest physical address for used ring
const USED_RING_ADDR: u64 = align!(AVAIL_RING_ADDR + AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE);
// Virtio-queue size in bytes
const QUEUE_BYTES_SIZE: usize = (USED_RING_ADDR + USED_RING_SIZE - DESC_TABLE_ADDR) as usize;
fuzz_target!(|bytes| {
if bytes.len() < (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE)
|| bytes.len() > (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE + MEM_SIZE)
{
return;
}
let mut rng = virtio_devices::Rng::new(
"fuzzer_rng".to_owned(),
"/dev/urandom",
false,
SeccompAction::Allow,
EventFd::new(EFD_NONBLOCK).unwrap(),
)
.unwrap();
let queue_data = &bytes[..QUEUE_DATA_SIZE];
let queue_bytes = &bytes[QUEUE_DATA_SIZE..QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE];
let mem_bytes = &bytes[QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE..];
// Setup the virt queue with the input bytes
let q = setup_virt_queue(queue_data.try_into().unwrap());
// Setup the guest memory with the input bytes
let mem = GuestMemoryMmap::from_ranges(&[
(GuestAddress(0), MEM_SIZE),
(GuestAddress(DESC_TABLE_ADDR), QUEUE_BYTES_SIZE),
])
.unwrap();
if mem
.write_slice(queue_bytes, GuestAddress(DESC_TABLE_ADDR))
.is_err()
{
return;
}
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return;
}
let guest_memory = GuestMemoryAtomic::new(mem);
let evt = EventFd::new(0).unwrap();
let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) };
// Kick the 'queue' event before activate the rng device
queue_evt.write(1).unwrap();
rng.activate(
guest_memory,
Arc::new(NoopVirtioInterrupt {}),
vec![(0, q, evt)],
)
.ok();
// Wait for the events to finish and rng device worker thread to return
rng.wait_for_epoll_threads();
});
pub struct NoopVirtioInterrupt {}
impl VirtioInterrupt for NoopVirtioInterrupt {
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
Ok(())
}
}
fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue {
let mut q = Queue::new(QUEUE_SIZE).unwrap();
q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
q.set_next_used(bytes[1] as u16);
q.set_event_idx(bytes[2] % 2 != 0);
q.set_size(bytes[3] as u16 % QUEUE_SIZE);
q.try_set_desc_table_address(GuestAddress(DESC_TABLE_ADDR))
.unwrap();
q.try_set_avail_ring_address(GuestAddress(AVAIL_RING_ADDR))
.unwrap();
q.try_set_used_ring_address(GuestAddress(USED_RING_ADDR))
.unwrap();
q.set_ready(true);
q
}

View File

@@ -1,73 +0,0 @@
// Copyright © 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
#![no_main]
use devices::legacy::Serial;
use libc::EFD_NONBLOCK;
use libfuzzer_sys::fuzz_target;
use std::sync::Arc;
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig, InterruptSourceGroup};
use vm_device::BusDevice;
use vmm_sys_util::eventfd::EventFd;
fuzz_target!(|bytes| {
let mut serial = Serial::new_sink(
"serial".into(),
Arc::new(TestInterrupt::new(EventFd::new(EFD_NONBLOCK).unwrap())),
);
let mut i = 0;
while i < bytes.len() {
let choice = bytes.get(i).unwrap_or(&0) % 3;
i += 1;
match choice {
0 => {
let offset = (bytes.get(i).unwrap_or(&0) % 8) as u64;
i += 1;
let mut out_bytes = vec![0];
serial.read(0, offset, &mut out_bytes);
}
1 => {
let offset = (bytes.get(i).unwrap_or(&0) % 8) as u64;
i += 1;
let data = vec![*bytes.get(i).unwrap_or(&0)];
i += 1;
serial.write(0, offset, &data);
}
_ => {
let data = vec![*bytes.get(i).unwrap_or(&0)];
i += 1;
serial.queue_input_bytes(&data).ok();
}
}
}
});
struct TestInterrupt {
event_fd: EventFd,
}
impl InterruptSourceGroup for TestInterrupt {
fn trigger(&self, _index: InterruptIndex) -> Result<(), std::io::Error> {
self.event_fd.write(1)
}
fn update(
&self,
_index: InterruptIndex,
_config: InterruptSourceConfig,
_masked: bool,
) -> Result<(), std::io::Error> {
Ok(())
}
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
Some(self.event_fd.try_clone().unwrap())
}
}
impl TestInterrupt {
fn new(event_fd: EventFd) -> Self {
TestInterrupt { event_fd }
}
}

View File

@@ -1,99 +0,0 @@
// Copyright © 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
#![no_main]
use libfuzzer_sys::fuzz_target;
use seccompiler::SeccompAction;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::Arc;
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
const QUEUE_DATA_SIZE: usize = 4;
const MEM_SIZE: usize = 256 * 1024 * 1024;
// Max entries in the queue.
const QUEUE_SIZE: u16 = 256;
// Guest physical address for descriptor table.
const DESC_TABLE_ADDR: u64 = 0;
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
// Guest physical address for available ring
const AVAIL_RING_ADDR: u64 = DESC_TABLE_ADDR + DESC_TABLE_SIZE;
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
// Guest physical address for used ring (requires to 4-bytes aligned)
const USED_RING_ADDR: u64 = (AVAIL_RING_ADDR + AVAIL_RING_SIZE + 3) & !3_u64;
fuzz_target!(|bytes| {
if bytes.len() < QUEUE_DATA_SIZE || bytes.len() > (QUEUE_DATA_SIZE + MEM_SIZE) {
return;
}
let mut watchdog = virtio_devices::Watchdog::new(
"fuzzer_watchdog".to_owned(),
EventFd::new(EFD_NONBLOCK).unwrap(),
SeccompAction::Allow,
EventFd::new(EFD_NONBLOCK).unwrap(),
)
.unwrap();
let queue_data = &bytes[..QUEUE_DATA_SIZE];
let mem_bytes = &bytes[QUEUE_DATA_SIZE..];
// Setup the virt queue with the input bytes
let q = setup_virt_queue(queue_data.try_into().unwrap());
// Setup the guest memory with the input bytes
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return;
}
let guest_memory = GuestMemoryAtomic::new(mem);
let evt = EventFd::new(0).unwrap();
let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) };
// Kick the 'queue' event before activate the watchdog device
queue_evt.write(1).unwrap();
watchdog
.activate(
guest_memory,
Arc::new(NoopVirtioInterrupt {}),
vec![(0, q, evt)],
)
.ok();
// Wait for the events to finish and watchdog device worker thread to return
watchdog.wait_for_epoll_threads();
});
pub struct NoopVirtioInterrupt {}
impl VirtioInterrupt for NoopVirtioInterrupt {
fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
Ok(())
}
}
fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue {
let mut q = Queue::new(QUEUE_SIZE).unwrap();
q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
q.set_next_used(bytes[1] as u16);
q.set_event_idx(bytes[2] % 2 != 0);
q.set_size(bytes[3] as u16 % QUEUE_SIZE);
q.try_set_desc_table_address(GuestAddress(DESC_TABLE_ADDR))
.unwrap();
q.try_set_avail_ring_address(GuestAddress(AVAIL_RING_ADDR))
.unwrap();
q.try_set_used_ring_address(GuestAddress(USED_RING_ADDR))
.unwrap();
q.set_ready(true);
q
}

View File

@@ -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,25 +11,25 @@ mshv = ["mshv-ioctls", "mshv-bindings"]
tdx = []
[dependencies]
anyhow = "1.0.65"
byteorder = "1.4.3"
thiserror = "1.0.37"
libc = "0.2.133"
log = "0.4.17"
anyhow = "1.0.52"
epoll = "4.3.1"
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.145", features = ["rc", "derive"] }
serde_with = { version = "2.0.1", default-features = false, features = ["macros"] }
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
vm-memory = { version = "0.9.0", features = ["backend-mmap", "backend-atomic"] }
vmm-sys-util = { version = "0.10.0", features = ["with-serde"] }
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"]
[dev-dependencies]
env_logger = "0.9.1"
env_logger = "0.9.0"

View File

@@ -1,68 +0,0 @@
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
use crate::{CpuState, GicState, HypervisorDeviceError, HypervisorVmError};
use std::any::Any;
use std::result;
/// Errors thrown while setting up the VGIC.
#[derive(Debug)]
pub enum Error {
/// Error while calling KVM ioctl for setting up the global interrupt controller.
CreateGic(HypervisorVmError),
/// Error while setting device attributes for the GIC.
SetDeviceAttribute(HypervisorDeviceError),
/// Error while getting device attributes for the GIC.
GetDeviceAttribute(HypervisorDeviceError),
}
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug)]
pub struct VgicConfig {
pub vcpu_count: u64,
pub dist_addr: u64,
pub dist_size: u64,
pub redists_addr: u64,
pub redists_size: u64,
pub msi_addr: u64,
pub msi_size: u64,
pub nr_irqs: u32,
}
/// Hypervisor agnostic interface for a virtualized GIC
pub trait Vgic: Send + Sync {
/// Returns the fdt compatibility property of the device
fn fdt_compatibility(&self) -> &str;
/// Returns the maint_irq fdt property of the device
fn fdt_maint_irq(&self) -> u32;
/// Returns an array with GIC device properties
fn device_properties(&self) -> [u64; 4];
/// Returns the number of vCPUs this GIC handles
fn vcpu_count(&self) -> u64;
/// Returns whether the GIC device is MSI compatible or not
fn msi_compatible(&self) -> bool;
/// Returns the MSI compatibility property of the device
fn msi_compatibility(&self) -> &str;
/// Returns the MSI reg property of the device
fn msi_properties(&self) -> [u64; 2];
/// Get the values of GICR_TYPER for each vCPU.
fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]);
/// Downcast the trait object to its concrete type.
fn as_any_concrete_mut(&mut self) -> &mut dyn Any;
/// Save the state of GICv3ITS.
fn state(&self) -> Result<GicState>;
/// Restore the state of GICv3ITS.
fn set_state(&mut self, state: &GicState) -> Result<()>;
/// Saves GIC internal data tables into RAM.
fn save_data_tables(&self) -> Result<()>;
}

View File

@@ -1,3 +0,0 @@
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
pub mod gic;

View File

@@ -99,7 +99,7 @@ pub enum EmulationError<T: Debug> {
/// A CpuState is an architecture specific type, representing a CPU state.
/// The emulator and its instruction handlers modify a given CPU state and
/// eventually ask the platform to commit it back through `set_cpu_state`.
pub trait PlatformEmulator {
pub trait PlatformEmulator: Send + Sync {
type CpuState: Clone;
/// Read guest memory into a u8 slice.

View File

@@ -10,14 +10,9 @@
//
// Copyright © 2020, Microsoft Corporation
//
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
//
pub mod emulator;
#[cfg(target_arch = "x86_64")]
#[macro_use]
pub mod x86;
#[cfg(target_arch = "aarch64")]
pub mod aarch64;

View File

@@ -38,7 +38,11 @@ fn calc_rflags_cpazso(op0: u64, op1: u64, op_size: usize) -> u64 {
// AF cares about the lowest 4 bits (nibble). msb_shift is 3 in this case.
let af = ((cout >> 3) & 0x1) << AF_SHIFT;
let zf = u64::from(result & (!0u64 >> (63 - msb_shift)) == 0) << ZF_SHIFT;
let zf = if result & (!0u64 >> (63 - msb_shift)) == 0 {
1
} else {
0
} << ZF_SHIFT;
let sf = ((result >> msb_shift) & 0x1) << SF_SHIFT;

View File

@@ -28,11 +28,14 @@ fn get_op<T: CpuStateManager>(
)));
}
if !matches!(op_size, 1 | 2 | 4 | 8) {
return Err(PlatformError::InvalidOperand(anyhow!(
"Invalid operand size {:?}",
op_size
)));
match op_size {
1 | 2 | 4 | 8 => {}
_ => {
return Err(PlatformError::InvalidOperand(anyhow!(
"Invalid operand size {:?}",
op_size
)))
}
}
let value = match insn
@@ -78,11 +81,14 @@ fn set_op<T: CpuStateManager>(
)));
}
if !matches!(op_size, 1 | 2 | 4 | 8) {
return Err(PlatformError::InvalidOperand(anyhow!(
"Invalid operand size {:?}",
op_size
)));
match op_size {
1 | 2 | 4 | 8 => {}
_ => {
return Err(PlatformError::InvalidOperand(anyhow!(
"Invalid operand size {:?}",
op_size
)))
}
}
match insn

View File

@@ -6,11 +6,10 @@
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, SegmentRegister, SpecialRegisters,
StandardRegisters,
};
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::*;
@@ -18,7 +17,7 @@ use iced_x86::*;
mod instructions;
/// x86 CPU modes
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq)]
pub enum CpuMode {
/// Real mode
Real,

View File

@@ -8,7 +8,7 @@
// found in the LICENSE-BSD-3-Clause file.
// For GDT details see arch/x86/include/asm/segment.h
use crate::arch::x86::SegmentRegister;
use crate::x86_64::SegmentRegister;
/// Constructor for a conventional segment GDT (or LDT) entry. Derived from the kernel's segment.h.
pub fn gdt_entry(flags: u16, base: u32, limit: u32) -> u64 {
@@ -51,7 +51,7 @@ fn get_limit(entry: u64) -> u32 {
// Perform manual limit scaling if G flag is set
match get_g(entry) {
0 => limit,
_ => (limit << 12) | 0xFFF, // G flag is either 0 or 1
_ => ((limit << 12) | 0xFFF), // G flag is either 0 or 1
}
}
@@ -106,6 +106,7 @@ pub fn segment_from_gdt(entry: u64, table_index: u8) -> SegmentRegister {
l: get_l(entry),
g: get_g(entry),
avl: get_avl(entry),
padding: 0,
unusable: match get_p(entry) {
0 => 1,
_ => 0,

View File

@@ -13,6 +13,7 @@
pub mod emulator;
pub mod gdt;
#[allow(non_upper_case_globals)]
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
#[allow(non_upper_case_globals)]
@@ -26,7 +27,7 @@ pub const MTRR_MEM_TYPE_WB: u64 = 0x6;
pub const NUM_IOAPIC_PINS: usize = 24;
// X86 Exceptions
#[allow(clippy::upper_case_acronyms)]
#[allow(dead_code, clippy::upper_case_acronyms)]
#[derive(Clone, Debug)]
pub enum Exception {
DE = 0, // Divide Error
@@ -52,86 +53,40 @@ pub enum Exception {
pub mod regs;
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "with-serde", derive(Deserialize, Serialize))]
pub struct SegmentRegister {
pub base: u64,
pub limit: u32,
pub selector: u16,
pub type_: u8,
pub present: u8,
pub dpl: u8,
pub db: u8,
pub s: u8,
pub l: u8,
pub g: u8,
pub avl: u8,
pub unusable: u8,
}
// Abstracted segment register ops.
// Each x86 hypervisor should implement those.
pub trait SegmentRegisterOps {
// Segment type
fn segment_type(&self) -> u8;
fn set_segment_type(&mut self, val: u8);
impl SegmentRegister {
pub fn segment_type(&self) -> u8 {
self.type_
}
pub fn set_segment_type(&mut self, val: u8) {
self.type_ = val;
}
// Descriptor Privilege Level (DPL)
fn dpl(&self) -> u8;
fn set_dpl(&mut self, val: u8);
pub fn dpl(&self) -> u8 {
self.dpl
}
// Granularity
fn granularity(&self) -> u8;
fn set_granularity(&mut self, val: u8);
pub fn set_dpl(&mut self, val: u8) {
self.dpl = val;
}
// Memory Presence
fn present(&self) -> u8;
fn set_present(&mut self, val: u8);
pub fn present(&self) -> u8 {
self.present
}
// Long mode
fn long(&self) -> u8;
fn set_long(&mut self, val: u8);
pub fn set_present(&mut self, val: u8) {
self.present = val;
}
// Available for system use (AVL)
fn avl(&self) -> u8;
fn set_avl(&mut self, val: u8);
pub fn long(&self) -> u8 {
self.l
}
// Descriptor type (System or code/data)
fn desc_type(&self) -> u8;
fn set_desc_type(&mut self, val: u8);
pub fn set_long(&mut self, val: u8) {
self.l = val;
}
pub fn avl(&self) -> u8 {
self.avl
}
pub fn set_avl(&mut self, val: u8) {
self.avl = val;
}
pub fn desc_type(&self) -> u8 {
self.s
}
pub fn set_desc_type(&mut self, val: u8) {
self.s = val;
}
pub fn granularity(&self) -> u8 {
self.g
}
pub fn set_granularity(&mut self, val: u8) {
self.g = val;
}
pub fn db(&self) -> u8 {
self.db
}
pub fn set_db(&mut self, val: u8) {
self.db = val;
}
// D/B
fn db(&self) -> u8;
fn set_db(&mut self, val: u8);
}
// Code segment
@@ -160,6 +115,7 @@ macro_rules! msr {
MsrEntry {
index: $msr,
data: 0x0,
..Default::default()
}
};
}
@@ -169,143 +125,7 @@ macro_rules! msr_data {
MsrEntry {
index: $msr,
data: $data,
..Default::default()
}
};
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "with-serde", derive(Deserialize, Serialize))]
pub struct StandardRegisters {
pub rax: u64,
pub rbx: u64,
pub rcx: u64,
pub rdx: u64,
pub rsi: u64,
pub rdi: u64,
pub rsp: u64,
pub rbp: u64,
pub r8: u64,
pub r9: u64,
pub r10: u64,
pub r11: u64,
pub r12: u64,
pub r13: u64,
pub r14: u64,
pub r15: u64,
pub rip: u64,
pub rflags: u64,
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "with-serde", derive(Deserialize, Serialize))]
pub struct DescriptorTable {
pub base: u64,
pub limit: u16,
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "with-serde", derive(Deserialize, Serialize))]
pub struct SpecialRegisters {
pub cs: SegmentRegister,
pub ds: SegmentRegister,
pub es: SegmentRegister,
pub fs: SegmentRegister,
pub gs: SegmentRegister,
pub ss: SegmentRegister,
pub tr: SegmentRegister,
pub ldt: SegmentRegister,
pub gdt: DescriptorTable,
pub idt: DescriptorTable,
pub cr0: u64,
pub cr2: u64,
pub cr3: u64,
pub cr4: u64,
pub cr8: u64,
pub efer: u64,
pub apic_base: u64,
pub interrupt_bitmap: [u64; 4usize],
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
pub struct CpuIdEntry {
pub function: u32,
pub index: u32,
pub flags: u32,
pub eax: u32,
pub ebx: u32,
pub ecx: u32,
pub edx: u32,
}
pub const CPUID_FLAG_VALID_INDEX: u32 = 1;
#[derive(Default, Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct FpuState {
pub fpr: [[u8; 16usize]; 8usize],
pub fcw: u16,
pub fsw: u16,
pub ftwx: u8,
pub last_opcode: u16,
pub last_ip: u64,
pub last_dp: u64,
pub xmm: [[u8; 16usize]; 16usize],
pub mxcsr: u32,
}
#[serde_with::serde_as]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct LapicState {
#[serde_as(as = "[_; 1024usize]")]
pub(crate) regs: [::std::os::raw::c_char; 1024usize],
}
impl Default for LapicState {
fn default() -> Self {
// SAFETY: this is plain old data structure
unsafe { ::std::mem::zeroed() }
}
}
impl LapicState {
pub fn get_klapic_reg(&self, reg_offset: usize) -> u32 {
use byteorder::{LittleEndian, ReadBytesExt};
use std::io::Cursor;
use std::mem;
let sliceu8 = unsafe {
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
// Cursors are only readable on arrays of u8, not i8(c_char).
mem::transmute::<&[i8], &[u8]>(&self.regs[reg_offset..])
};
let mut reader = Cursor::new(sliceu8);
// Following call can't fail if the offsets defined above are correct.
reader
.read_u32::<LittleEndian>()
.expect("Failed to read klapic register")
}
pub fn set_klapic_reg(&mut self, reg_offset: usize, value: u32) {
use byteorder::{LittleEndian, WriteBytesExt};
use std::io::Cursor;
use std::mem;
let sliceu8 = unsafe {
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
// Cursors are only readable on arrays of u8, not i8(c_char).
mem::transmute::<&mut [i8], &mut [u8]>(&mut self.regs[reg_offset..])
};
let mut writer = Cursor::new(sliceu8);
// Following call can't fail if the offsets defined above are correct.
writer
.write_u32::<LittleEndian>(value)
.expect("Failed to write klapic register")
}
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
pub struct MsrEntry {
pub index: u32,
pub data: u64,
}

View File

@@ -9,17 +9,25 @@
//
#[cfg(target_arch = "aarch64")]
use crate::aarch64::{RegList, StandardRegisters, VcpuInit};
use crate::aarch64::VcpuInit;
#[cfg(target_arch = "aarch64")]
use crate::aarch64::{RegList, Register, StandardRegisters};
#[cfg(target_arch = "x86_64")]
use crate::arch::x86::{
CpuIdEntry, FpuState, LapicState, MsrEntry, SpecialRegisters, StandardRegisters,
use crate::x86_64::{CpuId, LapicState};
#[cfg(target_arch = "x86_64")]
use crate::x86_64::{
ExtendedControlRegisters, FpuState, MsrEntries, SpecialRegisters, StandardRegisters, VcpuEvents,
};
#[cfg(feature = "tdx")]
use crate::kvm::{TdxExitDetails, TdxExitStatus};
use crate::CpuState;
#[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;
use vm_memory::GuestAddress;
#[derive(Error, Debug)]
///
@@ -36,6 +44,11 @@ pub enum HypervisorCpuError {
#[error("Failed to get standard registers: {0}")]
GetStandardRegs(#[source] anyhow::Error),
///
/// Getting suspend registers error
///
#[error("Failed to get suspend registers: {0}")]
GetSuspendRegs(#[source] anyhow::Error),
///
/// Setting special register error
///
#[error("Failed to set special registers: {0}")]
@@ -210,33 +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,
#[cfg(target_arch = "aarch64")]
///
/// Failed to intialize PMU
///
#[error("Failed to initialize PMU")]
InitializePmu,
}
#[derive(Debug)]
@@ -253,10 +244,6 @@ pub enum VmExit<'a> {
Reset,
Shutdown,
Hyperv,
#[cfg(feature = "tdx")]
Tdx,
#[cfg(feature = "kvm")]
Debug,
}
///
@@ -267,10 +254,13 @@ pub type Result<T> = anyhow::Result<T, HypervisorCpuError>;
/// Trait to represent a generic Vcpu
///
pub trait Vcpu: Send + Sync {
#[cfg(target_arch = "x86_64")]
///
/// Returns the vCPU general purpose registers.
///
fn get_regs(&self) -> Result<StandardRegisters>;
#[cfg(target_arch = "x86_64")]
///
/// Sets the vCPU general purpose registers.
///
@@ -299,7 +289,7 @@ pub trait Vcpu: Send + Sync {
///
/// X86 specific call to setup the CPUID registers.
///
fn set_cpuid2(&self, cpuid: &[CpuIdEntry]) -> Result<()>;
fn set_cpuid2(&self, cpuid: &CpuId) -> Result<()>;
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call to enable HyperV SynIC
@@ -309,7 +299,7 @@ pub trait Vcpu: Send + Sync {
///
/// X86 specific call to retrieve the CPUID registers.
///
fn get_cpuid2(&self, num_entries: usize) -> Result<Vec<CpuIdEntry>>;
fn get_cpuid2(&self, num_entries: usize) -> Result<CpuId>;
#[cfg(target_arch = "x86_64")]
///
/// Returns the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
@@ -324,65 +314,106 @@ pub trait Vcpu: Send + Sync {
///
/// Returns the model-specific registers (MSR) for this vCPU.
///
fn get_msrs(&self, msrs: &mut Vec<MsrEntry>) -> Result<usize>;
fn get_msrs(&self, msrs: &mut MsrEntries) -> Result<usize>;
#[cfg(target_arch = "x86_64")]
///
/// Setup the model-specific registers (MSR) for this vCPU.
///
fn set_msrs(&self, msrs: &[MsrEntry]) -> Result<usize>;
fn set_msrs(&self, msrs: &MsrEntries) -> Result<usize>;
#[cfg(feature = "kvm")]
///
/// Returns the vcpu's current "multiprocessing state".
///
fn get_mp_state(&self) -> Result<MpState>;
#[cfg(feature = "kvm")]
///
/// Sets the vcpu's current "multiprocessing state".
///
fn set_mp_state(&self, mp_state: MpState) -> Result<()>;
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call that returns the vcpu's current "xsave struct".
///
fn get_xsave(&self) -> Result<Xsave>;
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call that sets the vcpu's current "xsave struct".
///
fn set_xsave(&self, xsave: &Xsave) -> Result<()>;
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call that returns the vcpu's current "xcrs".
///
fn get_xcrs(&self) -> Result<ExtendedControlRegisters>;
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call that sets the vcpu's current "xcrs".
///
fn set_xcrs(&self, xcrs: &ExtendedControlRegisters) -> Result<()>;
#[cfg(target_arch = "x86_64")]
///
/// Returns currently pending exceptions, interrupts, and NMIs as well as related
/// states of the vcpu.
///
fn get_vcpu_events(&self) -> Result<VcpuEvents>;
#[cfg(target_arch = "x86_64")]
///
/// Sets pending exceptions, interrupts, and NMIs as well as related states
/// of the vcpu.
///
fn set_vcpu_events(&self, events: &VcpuEvents) -> Result<()>;
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
///
/// Let the guest know that it has been paused, which prevents from
/// potential soft lockups when being resumed.
///
fn notify_guest_clock_paused(&self) -> Result<()> {
Ok(())
}
///
/// Sets debug registers to set hardware breakpoints and/or enable single step.
///
fn set_guest_debug(&self, _addrs: &[GuestAddress], _singlestep: bool) -> Result<()> {
Err(HypervisorCpuError::SetDebugRegs(anyhow!("unimplemented")))
}
fn notify_guest_clock_paused(&self) -> Result<()>;
///
/// Sets the type of CPU to be exposed to the guest and optional features.
///
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn vcpu_init(&self, kvi: &VcpuInit) -> Result<()>;
///
/// Sets the value of one register for this vCPU.
///
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn set_reg(&self, reg_id: u64, data: u64) -> Result<()>;
///
/// Sets the value of one register for this vCPU.
///
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn get_reg(&self, reg_id: u64) -> Result<u64>;
///
/// Gets a list of the guest registers that are supported for the
/// KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
///
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn get_reg_list(&self, reg_list: &mut RegList) -> Result<()>;
///
/// Gets the value of a system register
/// Save the state of the core registers.
///
#[cfg(target_arch = "aarch64")]
fn get_sys_reg(&self, sys_reg: u32) -> Result<u64>;
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn core_registers(&self, state: &mut StandardRegisters) -> Result<()>;
///
/// Configure core registers for a given CPU.
/// Restore the state of the core registers.
///
#[cfg(target_arch = "aarch64")]
fn setup_regs(&self, cpu_id: u8, boot_ip: u64, fdt_start: u64) -> Result<()>;
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn set_core_registers(&self, state: &StandardRegisters) -> Result<()>;
///
/// Check if the CPU supports PMU
/// Save the state of the system registers.
///
#[cfg(target_arch = "aarch64")]
fn has_pmu_support(&self) -> bool;
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn system_registers(&self, state: &mut Vec<Register>) -> Result<()>;
///
/// Initialize PMU
/// Restore the state of the system registers.
///
#[cfg(target_arch = "aarch64")]
fn init_pmu(&self, irq: u32) -> Result<()>;
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn set_system_registers(&self, state: &[Register]) -> Result<()>;
///
/// Read the MPIDR - Multiprocessor Affinity Register.
///
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn read_mpidr(&self) -> Result<u64>;
///
/// Retrieve the vCPU state.
/// This function is necessary to snapshot the VM
@@ -397,39 +428,24 @@ 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
///
#[cfg(feature = "tdx")]
fn tdx_init(&self, _hob_address: u64) -> Result<()> {
unimplemented!()
}
fn tdx_init(&self, hob_address: u64) -> Result<()>;
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
///
/// Return suspend registers(explicit and intercept suspend registers)
///
fn get_suspend_regs(&self) -> Result<SuspendRegisters>;
#[cfg(feature = "kvm")]
///
/// 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> {
unimplemented!()
}
#[cfg(feature = "tdx")]
///
/// Set the status code for TDX exit
///
fn set_tdx_status(&mut self, _status: TdxExitStatus) {
unimplemented!()
}
#[cfg(target_arch = "x86_64")]
///
/// Return the list of initial MSR entries for a VCPU
///
fn boot_msr_entries(&self) -> Vec<MsrEntry>;
fn set_immediate_exit(&self, exit: bool);
}

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