Compare commits

..

246 Commits
v42.0 ... v45.0

Author SHA1 Message Date
Bo Chen
3d88996e5b build: Release v45.0
Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-03-29 16:44:09 +00:00
Ruoqing He
3899cd0e30 docs: Add guide for direct boot VM on riscv64
Add `riscv.md` to guide developers/users on preparing guest kernel,
image, and direct boot VM on riscv64 platforms; document support status
and known limitations.

Co-authored-by: Bo Chen <bchen@crusoe.ai>
Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-03-29 03:58:12 +00:00
Jinrong Liang
9d93df4c8f vm-migration: Optimize downtime by moving stop_dirty_log()
The larger the VM memory, the greater the memory pressure, and the
greater the stop_dirty_log() overhead. Moving stop_dirty_log() outside
the downtime period can reduce downtime.

Signed-off-by: Jinrong Liang <cloudliang@tencent.com>
2025-03-27 21:08:33 +00:00
Jinrong Liang
4c27572b87 vm-migration: Avoid stop_dirty_log() during local migration
Starting and stopping logging dirty pages only occurs during cross-host
migrations.

Signed-off-by: Jinrong Liang <cloudliang@tencent.com>
2025-03-27 21:08:33 +00:00
Ruoqing He
19fea1ad88 hypervisor: Simplify riscv64 set_regs implementation
Use `riscv64_set_one_reg_from_vcpu!` macro to simplify `set_regs` for
riscv64.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-03-27 00:27:05 +00:00
Ruoqing He
9a96ea44be hypervisor: Introduce riscv64_set_one_reg_to_vcpu macro
`riscv64_set_one_reg_to_vcpu` macro is used to set value of specific
RISC-V `$reg_name` stored in `state` to KVM Vcpu.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-03-27 00:27:05 +00:00
Ruoqing He
e11b9d6449 hypervisor: Simplify riscv64 get_regs implementation
Use `riscv64_get_one_reg_from_vcpu!` macro to simplify `get_regs` for
riscv64.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-03-27 00:27:05 +00:00
Ruoqing He
ce5fe7f89d hypervisor: Introduce riscv64_get_one_reg_from_vcpu macro
`riscv64_get_one_reg_from_vcpu` macro is used to extract RISC-V register
data from KVM Vcpu according to `$reg_name` provided to `state`.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-03-27 00:27:05 +00:00
Ruoqing He
0dd0364bf8 build: Centralize igvm crates to workspace
`igvm` crates are referenced by multiple components, centralize them to
workspace to better manage those crates.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-03-24 18:01:03 +00:00
Jinank Jain
3081d01fc3 vmm: Fix compilation on aarch64 MSHV
Certain MSHV ioctls are only available on x86 architecture. Thus,
conditionally compile seccomp filter for x86 and in general enable
seccomp filters when compiling for MSHV on aarch64.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-03-21 03:50:59 +00:00
Philipp Schuster
fa58b725cb vmm: alphabetically sort CLI options in --help output
The CLI has grown to a big variety of options. clap prints them in the
help message (--help) in the order they were defined. We now are at a
point where grouping things logically together doesn't work well.
Further, there is no support by clap for logical grouping and the
current code base wasn't consistent. Therefore, this commit introduces
two changes:

- a new structure to define arguments (all in an array)
- an alphabetical ordering of the arguments

No other changes have been made. No options have been altered.

This significantly improves:
- code maintainability and extensibility
- readability of the --help output

A unit test ensures they stay sorted. A better approach to check if the
list of arguments (known at build time) is sorted would be a compile
time check (`const`), but this currently isn't possible in stable Rust.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-03-20 08:43:09 +00:00
Andrew Consroe
a38b4c7f17 vmm: tighten landlock rule for PmemConfig
when discard_writes is true, only grant read access in landlock

Signed-off-by: Andrew Consroe <aconz2@gmail.com>
2025-03-20 01:58:10 +00:00
Philipp Schuster
5aa1540c5d ci: gitlint now ignores long lines from links
This helps to prevent annoying CI failures when one adds useful resources into
a commit message, such as [0].

One can test this locally using: `gitlint --commits HEAD~1..HEAD`

[0]
https://example.com/?lorem-ipsum-lorem-ipsum-lorem-ipsum-lorem-ipsum-lorem-ipsum-lorem-ipsum-lorem-ipsum-lorem-ipsum-lorem-ipsum-lorem-ipsum-lorem-ipsum-lorem-ipsum-lorem-ipsum-lorem-ipsum

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-03-19 16:11:26 +00:00
Jinank Jain
7da8ae9c0f hypervisor: Fix MSHV compilation on aarch64
Use the definitions from the rust-vmm/mshv crate for various
datastructures such as StandardRegisters, RegList, VcpuInit etc.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-03-19 06:58:52 +00:00
Wei Liu
108e8f9dff hypervisor: mshv: handle cross-page access in emulator
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-03-17 21:40:24 +00:00
dependabot[bot]
5ab2feae70 build: Bump anstyle-query from 1.1.1 to 1.1.2
Bumps [anstyle-query](https://github.com/rust-cli/anstyle) from 1.1.1 to 1.1.2.
- [Commits](https://github.com/rust-cli/anstyle/compare/anstyle-query-v1.1.1...anstyle-query-v1.1.2)

---
updated-dependencies:
- dependency-name: anstyle-query
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-15 00:15:13 +00:00
Ruoqing He
c99660a8f9 vmm: Introduce riscv64 architecture support
Integrate all works done previously to enable booting riscv linux on
riscv platforms, example command:

```console
./target/debug/cloud-hypervisor \
        --kernel path/to/kernel \
        --disk path=path/to/disk \
        --cmdline "console=hvc0 root=/dev/vda rw" \
        --cpus boot=1 \
        --memory size=1024M \
        --seccomp false
```

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-03-14 17:00:57 +00:00
Ruoqing He
08c1bb4b00 ci: Enable retry for Link Check
Link check is likely to fail due to connectivity reasons, retry three
times before it fails.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-03-14 16:56:12 +00:00
Bo Chen
5bd177dde8 ci: Move metrics and rate-limiter workers
We are moving the metrics and rate-limiter workers to a bare-metal
system, so that we can have more consistent results (particularly for
the block device metrics)

Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-03-14 10:15:21 +00:00
dependabot[bot]
52ed1082fb build: Bump igvm from 0.3.3 to 0.3.4
Bumps [igvm](https://github.com/microsoft/igvm) from 0.3.3 to 0.3.4.
- [Release notes](https://github.com/microsoft/igvm/releases)
- [Commits](https://github.com/microsoft/igvm/compare/igvm-v0.3.3...igvm-v0.3.4)

---
updated-dependencies:
- dependency-name: igvm
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-14 00:28:53 +00:00
dependabot[bot]
03a23309b9 build: Bump async-channel from 2.3.0 to 2.3.1
Bumps [async-channel](https://github.com/smol-rs/async-channel) from 2.3.0 to 2.3.1.
- [Release notes](https://github.com/smol-rs/async-channel/releases)
- [Changelog](https://github.com/smol-rs/async-channel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/async-channel/compare/v2.3.0...v2.3.1)

---
updated-dependencies:
- dependency-name: async-channel
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-13 01:45:20 +00:00
dependabot[bot]
fa0965a11a build: Bump indexmap from 2.7.1 to 2.8.0
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.7.1 to 2.8.0.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.7.1...2.8.0)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: indirect
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-12 00:02:27 +00:00
Anatol Belski
524f26abef vmm: api: Expose SEV-SNP and igvm related options
This involves the platform  and VM config.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2025-03-11 17:22:20 +00:00
dependabot[bot]
846d0976e3 build: Bump unicode-ident from 1.0.12 to 1.0.18
Bumps [unicode-ident](https://github.com/dtolnay/unicode-ident) from 1.0.12 to 1.0.18.
- [Release notes](https://github.com/dtolnay/unicode-ident/releases)
- [Commits](https://github.com/dtolnay/unicode-ident/compare/1.0.12...1.0.18)

---
updated-dependencies:
- dependency-name: unicode-ident
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-11 00:33:12 +00:00
Ruoqing He
4718dc1b72 build: Manually bump seccompiler to 0.5.0
v0.5.0 of `seccompiler` has supported riscv64, let's bump from v0.4.0 to
v0.5.0 to capture that support.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-03-10 17:38:56 +00:00
dependabot[bot]
0a9895e009 build: Bump ryu from 1.0.18 to 1.0.20
Bumps [ryu](https://github.com/dtolnay/ryu) from 1.0.18 to 1.0.20.
- [Release notes](https://github.com/dtolnay/ryu/releases)
- [Commits](https://github.com/dtolnay/ryu/compare/1.0.18...1.0.20)

---
updated-dependencies:
- dependency-name: ryu
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-10 09:43:34 +00:00
Bo Chen
297b41d615 pci, vmm: vfio: Report device path on host with DMA map/unmap errors
In addition to the BDF information on the guest, reporting the device
path of the VFIO device on the host is more useful when it comes to
debug DMA map/unmap errors particularly ones caused by failing hardware
on the host.

Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-03-08 10:00:34 +00:00
Ruoqing He
396aba7a52 tests: Skip test_snapshot_restore_with_fd on aarch64
`test_snapshot_restore_with_fd` uses unsafe file descriptors and with
rust 1.82.0 it errors with:

```
fatal runtime error: IO Safety violation: owned file descriptor already
closed
```

so has been skipped for now.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-03-07 15:09:14 +00:00
Ruoqing He
35aaa1333a misc: Fix clippy - more concise repeat().take()
Reported by 1.86.0-beta.1 (f0cb41030 2025-02-17).

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-03-07 15:09:14 +00:00
Ruoqing He
6768a13d95 build: Bump MSRV to 1.82.0
We are having complains from Rust 1.86.0-beta.1 (f0cb41030 2025-02-17)'
clippy, which suggests us to replace `repeat().take()` with
`repeat_n()`. While `repeat_n()` is stablized in Rust 1.82.0.

Update image to 20250307-2 because MSRV in Dockerfile is updated.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-03-07 15:09:14 +00:00
Ruoqing He
0d50d0b257 build: Add force-non-host to install minimal toolchain
While executing `rustup toolchain add --profile minimal
1.82.0-x86_64-unknown-linux-musl`, it errors out with:

```
error: toolchain '1.82.0-x86_64-unknown-linux-musl' may not be able to
run on this system
note: add the `--force-non-host` flag to install the toolchain anyway
```

Add `--force-non-host` to install minimal musl toolchain for x86_64.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-03-07 15:09:14 +00:00
Jinank Jain
f5a2f8473b hypervisor: Add a basic implementation of MshvVcpuState for aarch64
Currently we are just storing the StandardRegisters in the Vcpu state
which would be required for saving and restoring the ARM64 guest on
MSHV.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-03-06 11:14:16 +00:00
Stefan Kober
03bb59db69 seccomp: Allow clock_gettime() on http api thread
Signed-off-by: Stefan Kober <stefan.kober@cyberus-technology.de>
2025-03-05 15:35:37 +00:00
Ruoqing He
9e1fb3bfa1 misc: Fix clippy - manual implementation of ok
Reported by 1.86.0-beta.1 (f0cb41030 2025-02-17).

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-03-01 01:02:17 +00:00
Ruoqing He
4de422ad69 misc: Fix clippy - manually reimplementing div_ceil
Reported by 1.86.0-beta.1 (f0cb41030 2025-02-17).

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-03-01 01:02:17 +00:00
Ruoqing He
c441bb2968 misc: Fix clippy - doc list item overindented
Reported by 1.86.0-beta.1 (f0cb41030 2025-02-17).

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-03-01 01:02:17 +00:00
Ruoqing He
5cb5115456 build: Fix spdk in linux/arm64 image
The reason `test_vfio_user` fails is as @likebreath pointed: our ARM
host does not support SVE, while the nvme_tgt binary built from the
container image requires it. As a result, we encountered a SIGILL when
running the nvme_tgt binary. This also explains why this is not
happening when the container is built on the same host itself.

And quote from @rbradford:

When a job is run on one of the workers it looks to see if there is a
container locally matching the name as specified in the dev_cli.sh
script - if there is then it uses it. Otherwise it will try and download
it from the container registry - if that fails then it will built
locally. For the x86-64 workers started dynamically it will never have a
local version as they are a fresh VM. But on the ARM64 builder is a
local container image cache.

This can lead to an issue where if the image is build with one version
(a handcrafted datestamp) and then the Dockerfile is changed without
changing the timestamp then an old version may be fetched from the cache
or server. It is there for essential to always bump the datestamp (there
is a number after the - that can be used for this.)

However there is also the added complexity that image that is build and
uploaded to the container registry is not the same as the built locally
and thus used for the initial testing of the Dockerfile change. This
leads to the issue we have seen where different CPU compiler flags (from
-march=native) from the QEMU cross build in the hosted GHA action and
the local ARM64 build. Resulting in a binary in the remotely built
container not working locally.

We end up specifying TARGET_ARCHITECTURE="armv8.2-a" for building spdk,
and put built `python/spdk/` folder into `/usr/local/bin/spdk-nvme`.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-28 18:34:23 +00:00
Ruoqing He
0fbba66b21 scripts: Remove SPDK build in aarch64 test script
We already build `SPDK` for `linux/arm64` in our `Dockerfile`, no need
to build it here anymore.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-24 14:41:46 +00:00
Ruoqing He
5388fa1ced build: Build SPDK for arm64 docker image
Enable `SPDK` build (with DPDK for `generic` arm64 platform) for
`linux/arm64` image.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-24 14:41:46 +00:00
Ruoqing He
655d512523 build: Upgrade to 24.04 in Dockerfile
`arm64` build in ubuntu:22.04 errors out with `error processing package
libc-bin`. This issue is a known issue between the binfmt (running
different architectures via QEMU) and the libc ldconfig binary running
in container. We're "suddenly" having issues as ubuntu-latest (which is
the OS version we run the GH action container with) was recently changed
from 22.04 to 24.04 and hence why upgrading the container userspace from
22.04 to 24.04 solves the problem.

Removed deprecated package `python3-distutils`.

Update image name from `20250111-0` to `20250222-0`.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-24 14:41:46 +00:00
Ruoqing He
7d45473ff7 ci: Update docker actions to latest
Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-24 14:41:46 +00:00
Ruoqing He
294d5fbb08 misc: Fix clippy - operator precedence can trip the unwary
Reported by 1.85.0-stable (4d91de4e4 2025-02-17), fix accordingly.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-21 23:15:04 +00:00
Jinank Jain
8c796e6d5d hypervisor: Add MSHV implementation of VcpuInit
Extend the VcpuInit interface to accomodate changes for MSHV on aarch64.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-02-19 23:23:45 +00:00
Jinank Jain
630f5c1f14 hypervisor: Add MSHV implementation of RegList
Extend the RegList interface to accomodate changes for MSHV on aarch64.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-02-19 23:23:45 +00:00
Jinank Jain
5cbf907200 hypervisor: Add implementation to fetch host IPA limit on MSHV
This fixes an compilation error when we try to compile CloudHypervisor
for MSHV on aarch64.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-02-19 09:39:21 +00:00
dependabot[bot]
bfeab76059 build: Bump async-trait from 0.1.85 to 0.1.86
Bumps [async-trait](https://github.com/dtolnay/async-trait) from 0.1.85 to 0.1.86.
- [Release notes](https://github.com/dtolnay/async-trait/releases)
- [Commits](https://github.com/dtolnay/async-trait/compare/0.1.85...0.1.86)

---
updated-dependencies:
- dependency-name: async-trait
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-19 04:30:55 +00:00
dependabot[bot]
12143ca72f build: Bump proc-macro2 from 1.0.92 to 1.0.93
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.92 to 1.0.93.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.92...1.0.93)

---
updated-dependencies:
- dependency-name: proc-macro2
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-18 00:27:36 +00:00
dependabot[bot]
ba05bdd085 build: Bump proc-macro-crate from 3.1.0 to 3.2.0
Bumps [proc-macro-crate](https://github.com/bkchr/proc-macro-crate) from 3.1.0 to 3.2.0.
- [Release notes](https://github.com/bkchr/proc-macro-crate/releases)
- [Commits](https://github.com/bkchr/proc-macro-crate/commits/v3.2.0)

---
updated-dependencies:
- dependency-name: proc-macro-crate
  dependency-type: indirect
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-17 12:07:24 +00:00
Jinank Jain
171b28ce52 hypervisor, vmm: Avoid leaking hypervisor specific data structure
Currently a bunch of KVM specific interfaces are leaked into the vmm
crate which should ideally does not contain any hypervisor specific data
structures.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-02-12 23:08:43 +00:00
Jinank Jain
5b929cb277 hypervisor: Implement hypervisor agnostic variant of VcpuInit
This will help in fixing the build issue for MSHV on ARM64.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-02-12 23:08:43 +00:00
Jinank Jain
ee0b0d43d8 hypervisor: Implement hypervisor agnostic variant of RegList
This helps in unification of RegList across different platforms.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-02-12 23:08:43 +00:00
Jinank Jain
061482340e hypervisor: Implement hypervisor agnostic Register interface
This will help in fixing the build issue for MSHV on ARM64.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-02-12 23:08:43 +00:00
dependabot[bot]
c43ae1dc9d build: Bump dirs from 5.0.1 to 6.0.0
Bumps [dirs](https://github.com/soc/dirs-rs) from 5.0.1 to 6.0.0.
- [Commits](https://github.com/soc/dirs-rs/commits)

---
updated-dependencies:
- dependency-name: dirs
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-11 23:57:37 +00:00
Jinank Jain
7db3002e1d build: Bump mshv crate to latest version
Move mshv crates from v0.3.2 to v0.3.3

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-02-11 14:31:48 +00:00
Ruoqing He
07b7457c2e build: Bump getrandom from 0.2.15 to 0.3.1
Manually bump `getrandom` due to API changes.

Bumps [getrandom](https://github.com/rust-random/getrandom) from 0.2.15 to 0.3.1.
- [Changelog](https://github.com/rust-random/getrandom/blob/master/CHANGELOG.md)
- [Commits](rust-random/getrandom@v0.2.15...v0.3.1)

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-11 07:40:16 +08:00
Ruoqing He
dc9142f86c ci: Introduce lychee to check links
Use `lychee` to check availability of links in cloud-hypervisor.

The urls explictly excluded in config file are manually checked.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-08 22:11:19 +08:00
Ruoqing He
6fa7c84d2e misc: Update link in release-note.md
We have lost track to releases before v27.0 since these projects no
longer exists. Delete links to those projects.

Update links to a detailed view specific to each group of release.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-08 17:32:54 +08:00
Ruoqing He
cc734e2e44 docs: Fix broken to fedora 36 artifacts
Content of fedora 36 have been moved to fedora archives [1], update
accordingly.

Format `README.md` using `mdformat` with GitHub Flavored Markdown
(GFM).

[1] http://archives.fedoraproject.org/pub/archive/fedora/

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-08 16:47:14 +08:00
Ruoqing He
e3e83362d6 docs: Fix broken link in CONTRIBUTING.md
Previous repo hosts details of `Rust Style` is removed, point `Rust
Style` to `style-guide` in `rust-lang/rust` repo.

Link to provide illustration on `signed-off-by language` is also
removed, use a snapshot found in web archive [1] instead.

Format `CONTRIBUTING.md` using `mdformat` with GitHub Flavored Markdown
(GFM).

[1] https://web.archive.org

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-08 16:40:57 +08:00
Ruoqing He
730cf1e944 docs: Fix broken link to intel 0x80 debug port
Previous link to provide details of `0x80 debug port` is removed, which
could no longer be found on intel site [1]. Use snapshot found in web
archive [2] to fix this link.

Format `debug-port.md` using `mdformat` with GitHub Flavored Markdown
(GFM).

[1] https://www.intel.com/content/www/us/en/homepage.html
[2] https://web.archive.org

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-08 16:40:44 +08:00
Ruoqing He
1caa69d0cc docs: Update outdated intel_sgx.md
TDX homepage was moved to elsewhere, and `sgx` support is upstreamed
since v5.11 kernel.

Format `intel_sgx.md` using `mdformat` with GitHub Flavored Markdown
(GFM).

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-08 16:39:16 +08:00
Ruoqing He
abcbf43433 docs: Update outdated intel_tdx.md
TDX homepage was moved to elsewhere, and `tdx-tools` repo was removed.

Provide a valid link of TDX homepage and change all reference to
`tdx-tools` to `tdx-linux`.

Format `intel_tdx.md` using `mdformat` with GitHub Flavored Markdown
(GFM).

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-08 16:38:46 +08:00
Ruoqing He
af74de2779 docs: Fix broken link in io_throttling.md
There is a link referencing `rate-limiter` module of `firecracker`, but
that module no longer exsits.

Point the link to a commit with the same date in `firecracker` when this
commit was merged to `cloud-hypervisor`.

Format `io_throttling.md` using `mdformat` with GitHub Flavored Markdown
(GFM).

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-08 16:38:22 +08:00
Ruoqing He
dfebc8929d docs: Fix broken link to docs/arm64.md
`docs/arm64.md` was removed and splited into `README.md`, `building.md`
and `uefi.md` in #4991.

Let's point it to
`8ab15b9a98/docs/arm64.md`
the commit right before `docs/arm64.md` was removed in main branch.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-08 16:37:50 +08:00
Ruoqing He
9988379f6b docs: Fix link to docs/logging.md
Previous link `docs/logging` is not valid, replacing `docs/logging` with
`docs/logging.md`.

Format `logging.md` using `mdformat` with GitHub Flavored Markdown
(GFM).

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-08 16:37:03 +08:00
Ruoqing He
4d12192490 docs: Fix broken link in amd_sev_snp.md
Previous link to details of SNP is now broken, pointing that section to
a valid link [1].

Format `amd_sev_snp.md` using `mdformat` with GitHub Flavored Markdown
(GFM).

[1] https://www.amd.com/content/dam/amd/en/documents/epyc-business-docs/solution-briefs/amd-secure-encrypted-virtualization-solution-brief.pdf

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-02-08 16:36:48 +08:00
Wei Liu
9f9cfeb5be build: Provide CH_EXTRA_VERSION
Use this to add any extra versioning information to the binary. It is
useful when packaging Cloud Hypervisor.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-02-04 19:33:10 +00:00
Bo Chen
3fa1e77adc build: Release v44.0
Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-02-03 18:09:27 +00:00
dependabot[bot]
b8424ea506 build: Bump colorchoice from 1.0.2 to 1.0.3
Bumps [colorchoice](https://github.com/rust-cli/anstyle) from 1.0.2 to 1.0.3.
- [Commits](https://github.com/rust-cli/anstyle/compare/colorchoice-v1.0.2...colorchoice-v1.0.3)

---
updated-dependencies:
- dependency-name: colorchoice
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-24 23:56:13 +00:00
Ruoqing He
0c2fe29f61 arch: riscv64: Fix interrupt-controller FDT property
This is a typo here, `interrupt-controller` attribute of each CPU node
should not have a `#` prepended.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-01-24 12:24:02 +00:00
dependabot[bot]
b445d219ca build: Bump uuid from 1.8.0 to 1.12.1
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.8.0 to 1.12.1.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.8.0...1.12.1)

---
updated-dependencies:
- dependency-name: uuid
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-24 00:40:22 +00:00
dependabot[bot]
6de283f42c build: Bump micro_http from ef43cef to ef96f62
Bumps [micro_http](https://github.com/firecracker-microvm/micro-http) from `ef43cef` to `ef96f62`.
- [Commits](ef43cef716...ef96f623c4)

---
updated-dependencies:
- dependency-name: micro_http
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-22 23:49:15 +00:00
Rob Bradford
f892789481 docs: Update documentation for new kernel configuration
Replace the use of a reference kernel configuration file from this
repository with the use of a defconfig from the linux fork.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-22 17:45:32 +00:00
dependabot[bot]
f96554cbe9 build: Bump clap_lex from 0.7.2 to 0.7.4
Bumps [clap_lex](https://github.com/clap-rs/clap) from 0.7.2 to 0.7.4.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_lex-v0.7.2...clap_lex-v0.7.4)

---
updated-dependencies:
- dependency-name: clap_lex
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-21 23:48:31 +00:00
Ruoqing He
3509b5bf4a hypervisor: Create vcpu before initialize AIA
Create a corresponding `vcpu` in `test_create_aia`  to capture wrongly
configured RISC-V IMSIC attr.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-01-21 12:53:54 +00:00
Ruoqing He
cf463b88b7 hypervisor: Fix AIA IMSIC attr calculation
The IMSIC attr of RISC-V AIA is wrongly configured to start from 0, which
would error out with `os error 22` (invalid argument).

```console
Error booting VM: VmBoot(DeviceManager(CreateInterruptController(CreateAia(CreateVaia(Vaia error SetDeviceAttribute(SetDeviceAttribute(Invalid argument (os error 22))))))))
```

`riscv_imsic_attr_of` should shift `cpu_index` by 1 here to produce
correct IMSIC attr.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-01-21 12:53:54 +00:00
dependabot[bot]
ca8eda5399 build: Bump autocfg from 1.3.0 to 1.4.0
Bumps [autocfg](https://github.com/cuviper/autocfg) from 1.3.0 to 1.4.0.
- [Commits](https://github.com/cuviper/autocfg/compare/1.3.0...1.4.0)

---
updated-dependencies:
- dependency-name: autocfg
  dependency-type: indirect
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-21 00:08:19 +00:00
dependabot[bot]
45b770ea9f build: Bump fastrand from 2.2.0 to 2.3.0
Bumps [fastrand](https://github.com/smol-rs/fastrand) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/smol-rs/fastrand/releases)
- [Changelog](https://github.com/smol-rs/fastrand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/fastrand/compare/v2.2.0...v2.3.0)

---
updated-dependencies:
- dependency-name: fastrand
  dependency-type: indirect
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-19 09:51:20 +00:00
dependabot[bot]
d2c4ceb99a build: Bump uuid-macro-internal from 1.10.0 to 1.12.0
Bumps [uuid-macro-internal](https://github.com/uuid-rs/uuid) from 1.10.0 to 1.12.0.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.10.0...1.12.0)

---
updated-dependencies:
- dependency-name: uuid-macro-internal
  dependency-type: indirect
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-17 00:32:04 +00:00
dependabot[bot]
caf7cae2fa build: Bump async-executor from 1.11.0 to 1.13.1
Bumps [async-executor](https://github.com/smol-rs/async-executor) from 1.11.0 to 1.13.1.
- [Release notes](https://github.com/smol-rs/async-executor/releases)
- [Changelog](https://github.com/smol-rs/async-executor/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/async-executor/compare/v1.11.0...v1.13.1)

---
updated-dependencies:
- dependency-name: async-executor
  dependency-type: indirect
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-16 00:08:08 +00:00
Arvind Vasudev
81eca69b40 pci: Adding support for printing the device's address on a DMA map and unmap errors
Signed-off-by: Arvind Vasudev <avasudev@crusoeenergy.com>
2025-01-15 18:14:35 +00:00
Arvind Vasudev
8c07294691 pci: Implementing Debug for PciBdf to aid with debugging
Signed-off-by: Arvind Vasudev <avasudev@crusoeenergy.com>
2025-01-15 18:14:35 +00:00
Rob Bradford
2f9436bc12 build: Switch to named released kernel binary
For more control over updating the guest kernel use a fixed tag name
rather than fetching the latest.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-15 09:21:16 +00:00
dependabot[bot]
62fade97d1 build: Bump tempfile from 3.11.0 to 3.12.0
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.11.0 to 3.12.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.11.0...v3.12.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: indirect
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-14 23:49:04 +00:00
Nikolay Edigaryev
5a8df3622c tests: ensure that IOMMU maximum address width in bits is respected
Signed-off-by: Nikolay Edigaryev <edigaryev@gmail.com>
2025-01-14 21:31:47 +00:00
Nikolay Edigaryev
27fda753e1 virtio-devices: iommu: allow limiting maximum address width in bits
Currently, Cloud Hypervisor does not set a VIRTIO_IOMMU_F_INPUT_RANGE
feature bit for the VirtIO IOMMU device, which, according to spec[1],
means that the guest may use the whole 64-bit address space is for
IOMMU purposes:

>If the feature is not offered, virtual mappings span over the whole
>64-bit address space (start = 0, end = 0xffffffff ffffffff)

As far as I am aware, there are currently no host platforms on
the market capable of addressing the whole 64-bit address space.

For example, I am currently working with a host platform that reports
39-bit address space for IOMMU purposes:

>DMAR: Host address width 39

When running a VFIO pass-through guest on such a platform, NVIDIA
driver in guest gets DMA mapping failures when working with large data,
and this results in Cloud Hypervisor exiting with the following error:

>cloud-hypervisor: 1501.220535s: <__iommu>
>ERROR:virtio-devices/src/thread_helper.rs:53 -- Error running worker:
>HandleEvent(Failed to process request queue : ExternalMapping(Custom
>{ kind: Other, error: "failed to map memory for VFIO container, iova
>0x7fff00000000, gpa 0x24ce25000, size 0x1000: IommuDmaMap(Error(22))"
>}))

Passing "--platform iommu_address_width=39" to Cloud Hypervisor built
with this change fixes this.

[1]: https://docs.oasis-open.org/virtio/virtio/v1.3/csd01/
virtio-v1.3-csd01.html#x1-5420006

Signed-off-by: Nikolay Edigaryev <edigaryev@gmail.com>
2025-01-14 21:31:47 +00:00
Nikolay Edigaryev
74ca38f7a9 vmm: introduce platform option to limit maximum IOMMU address width
Signed-off-by: Nikolay Edigaryev <edigaryev@gmail.com>
2025-01-14 21:31:47 +00:00
Rob Bradford
fa686fdfc7 tests: Bump OVMF version
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-14 17:53:43 +00:00
Wei Liu
c827a1c009 fuzz: build x86emul by default
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-14 17:37:57 +00:00
dependabot[bot]
eefd7fcaaf build: Bump linux-raw-sys from 0.4.14 to 0.4.15
Bumps [linux-raw-sys](https://github.com/sunfishcode/linux-raw-sys) from 0.4.14 to 0.4.15.
- [Commits](https://github.com/sunfishcode/linux-raw-sys/compare/v0.4.14...v0.4.15)

---
updated-dependencies:
- dependency-name: linux-raw-sys
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-14 16:01:22 +00:00
Rob Bradford
62303d8c33 arch: x86_64: Log the cpuid per vCPU
This will aid debugging issues related to CPUID.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-14 12:52:55 +00:00
Rob Bradford
293cf90e3d hypervisor: Implement fmt::Display for CpuIdEntry
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-14 12:52:55 +00:00
Bo Chen
8f98fabd0b vmm: Cleanup VM being created when VM restore fails
When VM restore fails, the VMM state is left with some side-effects,
such as a VM being created. It would prevent the VMM from creating and
booting a new VM or restoring from a VM snapshot.

To fix this issue, this patch explicitly handles the side effects to the
VMM state when VM restore fails, e.g. clear the VmConfig and shutdown
the VM being created.

Fixes: #6869

Signed-off-by: Bo Chen <bo.arvin.chen@gmail.com>
2025-01-14 00:45:03 +00:00
Bo Chen
07821c08c0 vmm: Always re-generate the 'console_info' with VM restore
With VM restore, the VMM is always re-creating a VM based on the
restored `VmConfig`. We should always re-generate the 'console_info'
from the `Vmm` struct to stay consistent with the new VM being created.

Signed-off-by: Bo Chen <bo.arvin.chen@gmail.com>
2025-01-14 00:45:03 +00:00
Wei Liu
0cb2c86ff4 fuzz: introduce a virtio vsock fuzzer
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-14 00:26:01 +00:00
Wei Liu
d359c8cdce virtio-devices: vsock: allow fuzzer to use TestBackend
Instead of reinventing this mock infrastructure in the upcoming fuzzer,
reuse the one that is already available.

However this change makes Clippy complain that TestBackend and
TestContext don't implement Default. This is just test code, we can
suppress Clippy in this case.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-14 00:26:01 +00:00
Rob Bradford
c801929fd1 tests: Update interrupt group name on ARM
This has changed with kernel 6.12.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-13 21:46:23 +00:00
Rob Bradford
66da3b9970 scripts: Temporarily build kernel as part of CI
Updating the kernel to v6.12 has shown up a flaw in the workflow for our
binary kernel releases. The CI job that builds the binary kernel in the
cloud-hypervisor/linux repository fetches the config from the main
branch of the cloud-hypervisor/cloud-hypervisor repository. However the
CI job to update the kernel version to use is in the cloud-hypervisor
repository.

As a workaround - update the kernel config and version in the
cloud-hypervisor repository to point to v6.12 and use the ability to
build the kernel during the CI run. Once merged to main a new release
can be made in the linux respository which will build a binary asset
using the new config. After that release the CI jobs on the
cloud-hypervisor repository can changed back to using the binary kernel
assets.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-13 21:46:23 +00:00
Rob Bradford
6ddbd60d9d build: Update kernel to v6.12
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-13 21:46:23 +00:00
Rob Bradford
2fc4de6c65 virtio-devices: iommu: Use hex formatting in log messages
This means that the the addresses are more easily readable.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-13 21:46:23 +00:00
Rob Bradford
03eeb36b74 virtio-devices: iommu: Search full range for GVA conversion
Remove an erroneous optimisation that used the page size mask to reduce
the range to iterate through on the set of mappings. This doesn't work
as the virtio-iommu ranges are larger than a single page. This may have
worked in the past when the mappings were limited to a single page.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-13 21:46:23 +00:00
Rob Bradford
2fe7f54ece build: Bump version number of Docker image
No change to the Dockerfile but I observed that the 20251022-0 image was
not available in the repository.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-11 15:03:01 +00:00
Rob Bradford
1cc540c880 build: Update github artifact action version
The v3 version is now deprecated. Tested by creating a dummy tag and
validating the results.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-09 22:25:35 +00:00
Wei Liu
a1af4238ae virtio-devices: make ioeventfds() return an iterator
MSHV's SEV-SNP implementation calls ioeventfds whenever there is an
event.

This change removes the need frequent allocation and deallocation of a
vector, while at the same time makes sure other call sites are
unaffected.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-09 21:28:46 +00:00
Wei Liu
d2e798944a virtio-devices: rename two variables
They are used. No need to start their names with an underscore.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-09 21:28:46 +00:00
Wei Liu
d99f294281 pci: rename as_any to as_any_mut
That trait function returns a mutable reference. Rename it to follow
Rust's convention.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-09 21:28:46 +00:00
dependabot[bot]
44c9ce598d build: Bump anstyle-wincon from 3.0.4 to 3.0.6
Bumps [anstyle-wincon](https://github.com/rust-cli/anstyle) from 3.0.4 to 3.0.6.
- [Commits](https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v3.0.4...anstyle-wincon-v3.0.6)

---
updated-dependencies:
- dependency-name: anstyle-wincon
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-09 13:57:10 +00:00
Wei Liu
5b42aa0bcf hypervisor: kvm: fix an operator precedence clippy warning
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-09 13:51:42 +00:00
Wei Liu
703e0cab04 vmm: use C ABI-qualification for packed structures
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-09 13:51:42 +00:00
Wei Liu
abb88cf47f arch: tdx: use proper repr qualifications
They should be packed and use C ABI.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-09 13:51:42 +00:00
Wei Liu
2e22b8bc3a pci: use C ABI-qualification for a packed structure
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-09 13:51:42 +00:00
Wei Liu
778c05d678 virtio-devices: use C ABI-qualification for packed structures
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-09 13:51:42 +00:00
Wei Liu
5b29eba636 block: vhdx: use C ABI-qualification for packed structures
This fixes the following issue from beta Clippy:

error: item uses `packed` representation without ABI-qualification
Error:    --> block/src/vhdx/vhdx_header.rs:333:1
    |
331 |   #[repr(packed)]
    |          ------ `packed` representation set here
332 |   #[derive(Clone, Copy, Debug)]
333 | / pub struct RegionTableEntry {
334 | |     pub guid: Uuid,
335 | |     pub file_offset: u64,
336 | |     pub length: u32,
337 | |     pub required: u32,
338 | | }
    | |_^
    |
    = warning: unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI
    = help: qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#repr_packed_without_abi

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-09 13:51:42 +00:00
Wei Liu
e6e78e5986 block: vhdx: reject zero size virtual disk
Some calculation down the road depends on that value not being zero.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-08 22:45:58 +00:00
Wei Liu
a2df4d7660 fuzz: explicitly keep or reject input for vhdx fuzzer
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-08 22:45:58 +00:00
Wei Liu
bc4a1fd16c block: vhdx: use checked_add in read and write functions
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-08 22:45:58 +00:00
Wei Liu
463c9b8e56 block: vhdx: hoist a check out of IO loops
This reduces one indentation level in the IO loops in the read and write
functions.

No functional change.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-08 22:45:58 +00:00
dependabot[bot]
835847bea5 build: Bump async-trait from 0.1.83 to 0.1.85
Bumps [async-trait](https://github.com/dtolnay/async-trait) from 0.1.83 to 0.1.85.
- [Release notes](https://github.com/dtolnay/async-trait/releases)
- [Commits](https://github.com/dtolnay/async-trait/compare/0.1.83...0.1.85)

---
updated-dependencies:
- dependency-name: async-trait
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-08 15:17:36 +00:00
Rob Bradford
3e4ed5621e vmm: Automatically fix operator precedence clippy warning
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-07 17:44:41 +00:00
Rob Bradford
2624f17ffe virtio-devices: Automatically fix operator precedence clippy warning
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-07 17:44:41 +00:00
Rob Bradford
21f05ebb4f vhost_user_block: Automatically fix operator precedence clippy warning
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-07 17:44:41 +00:00
Rob Bradford
1485e17268 vhost_user_net: Automatically fix operator precedence clippy warning
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-07 17:44:41 +00:00
Rob Bradford
b57cc3d79f pci: Automatically fix operator precedence clippy warning
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-07 17:44:41 +00:00
Rob Bradford
a322e2d6f4 hypervisor: Automatically fix operator precedence clippy warning
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-07 17:44:41 +00:00
Rob Bradford
dd0b95ba5c devices: Automatically fix operator precedence clippy warning
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-07 17:44:41 +00:00
Rob Bradford
b6667f948e arch: Fix operator precedence clippy warning
With manual (to ensure comment preservation) and automatic fixes.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-07 17:44:41 +00:00
dependabot[bot]
4f22f67307 build: Bump proc-macro2 from 1.0.86 to 1.0.92
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.86 to 1.0.92.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.86...1.0.92)

---
updated-dependencies:
- dependency-name: proc-macro2
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-07 00:15:47 +00:00
Rob Bradford
eeae63b459 build: Bump thiserror version
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-06 17:39:45 +00:00
Rob Bradford
abf5748f64 block: qcow: Use constant in error message
This avoids ambiguity of parameters:

error: ambiguous reference to positional arguments by number in a tuple variant; change this to a named argument
  --> block/src/qcow/mod.rs:48:48
   |
48 |     #[error("File larger than max of {}: {0}", MAX_QCOW_FILE_SIZE)]
   |                                                ^^^^^^^^^^^^^^^^^^

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-01-06 17:39:45 +00:00
dependabot[bot]
23b5f7b1c5 build: Bump futures from 0.3.30 to 0.3.31
Bumps [futures](https://github.com/rust-lang/futures-rs) from 0.3.30 to 0.3.31.
- [Release notes](https://github.com/rust-lang/futures-rs/releases)
- [Changelog](https://github.com/rust-lang/futures-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/futures-rs/compare/0.3.30...0.3.31)

---
updated-dependencies:
- dependency-name: futures
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-06 15:05:55 +00:00
Wei Liu
a66fef407c block: vhdx: properly account for bytes read and written
The counter value in vhdx_io::{read,write} should've been accumulated
over the loop.

Fixes: #6897
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-03 10:25:53 +00:00
Wei Liu
7c39f37855 fuzz: introduce an x86 instruction emulator fuzzer
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-02 22:43:59 +00:00
Wei Liu
fe24a7a24f hypervisor: introduce an mshv_emulator feature
This will become useful when we build the fuzzing target for the
instruction emulator, because there is no need to pull in the rest of
the hypervisor crate in that situation.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-02 22:43:59 +00:00
Wei Liu
73e1451a12 hypervisor: emulator: use wrapping arithmetic
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-02 22:43:59 +00:00
Wei Liu
1180f757b3 hypervisor: emulator: adjust iced-x86 feature flags
The fastfmt feature and VEX support use techniques that appear to leak
memory in the eye of LLVM's address sanitizer.

While at it, disable a bunch of instruction set decoding support we
never intend to support.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-02 22:43:59 +00:00
Wei Liu
2932658acd fuzz: update Cargo.lock
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-02 22:43:59 +00:00
Wei Liu
1f7b809619 block: increase the size of temporary vectors
The size was set to one because without VIRTIO_BLK_F_SEG_MAX, the guest
only used one data descriptor per request.

The value 32 is empirically derived from booting a guest. This value
eliminates all SmallVec allocations observable by DHAT.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-01 18:50:39 +00:00
Wei Liu
32482f6634 block: make available VIRTIO_BLK_F_SEG_MAX
This allows the guest to put in more than one segment per request. It
can improve the throughput of the system.

Introduce a new check to make sure the queue size configured by the user
is large enough to hold at least one segment.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-01-01 18:50:39 +00:00
Wei Liu
6fd5b0f696 fuzz: explicitly keep or reject fuzzer corpus
When the main fuzzer function returns (), it is equivalent to
returning Corpus::Keep.

In some of the return paths, we want to reject the input so that the
libfuzzer won't spend more time mutating them.

The should make fuzzing more efficient. No functional change intended.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-12-31 19:49:48 +00:00
Wei Liu
ef88b2778e block: vhdx: advance file offset after read and write
This is needed to handle multiple reads or writes in a loop.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-12-28 23:59:59 +00:00
Wei Liu
8b55d1e2d4 block: vhdx: rename Header::get_header_as_buffer
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-12-28 23:59:59 +00:00
Wei Liu
2df285effd block: vhdx: use calculate_checksum in Header::update_header
It doesn't make sense to open code the same calculation when there is
already a helper function.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-12-28 23:59:59 +00:00
Wei Liu
4ea4391f45 block: vhdx: adjust calculate_checksum
It doesn't need to be public. None of its code path returns an error.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-12-28 23:59:59 +00:00
Wei Liu
8f8d3c0139 block: vhdx: fix checksum calculation
The checksum field in the original buffer should be zeroed.

The code was zeroing a temporary buffer. That's wrong.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-12-28 23:59:59 +00:00
Wei Liu
408467a05f block: vhdx: do not update the VHDX header during parsing
The original code was buggy. It always attempted to update the header,
even when the file was opened as read-only. That led to an error.

The specification states that the headers should be updated when the
first user visible write happens. We can just drop the incorrect code.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-12-28 23:59:59 +00:00
Wei Liu
f27b028c18 block: vhdx: drop pub keyword from update_header
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-12-28 23:59:59 +00:00
dependabot[bot]
eaa2194699 build: Bump kvm-ioctls from 0.19.0 to 0.19.1
Bumps [kvm-ioctls](https://github.com/rust-vmm/kvm-ioctls) from 0.19.0 to 0.19.1.
- [Release notes](https://github.com/rust-vmm/kvm-ioctls/releases)
- [Changelog](https://github.com/rust-vmm/kvm/blob/v0.19.1/CHANGELOG.md)
- [Commits](https://github.com/rust-vmm/kvm-ioctls/compare/v0.19.0...v0.19.1)

---
updated-dependencies:
- dependency-name: kvm-ioctls
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-23 20:18:18 +00:00
Wei Liu
9973d28849 block: drop unused function create_disk_file
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-12-21 20:20:29 +00:00
dependabot[bot]
1cdb91a40f build: Bump vfio-ioctls from e71efe4 to bdbb1cd
Bumps [vfio-ioctls](https://github.com/rust-vmm/vfio) from `e71efe4` to `bdbb1cd`.
- [Release notes](https://github.com/rust-vmm/vfio/releases)
- [Commits](e71efe41d5...bdbb1cd486)

---
updated-dependencies:
- dependency-name: vfio-ioctls
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-21 11:51:17 +00:00
Wei Liu
57e331db0e block: drop the ASYNC flag from io_uring entries
The ASYNC flag forces requests to go to worker threads. Worker threads
are expensive. Let the kernel decide what to do.

With this change, I no longer see an excessive amount of io_uring worker
threads.

Quote from the manual for io_uring_sqe_set_flags(3):

```
   IOSQE_ASYNC
          Normal operation for io_uring is to try and issue an sqe
          as non-blocking first, and if that fails, execute it in an
          async manner. To support more efficient overlapped
          operation of requests that the application knows/assumes
          will always (or most of the time) block, the application
          can ask for an sqe to be issued async from the start. Note
          that this flag immediately causes the SQE to be offloaded
          to an async helper thread with no initial non-blocking
          attempt.  This may be less efficient and should not be
          used liberally or without understanding the performance
          and efficiency tradeoffs.
```

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-12-21 10:47:29 +00:00
Wei Liu
090fcebfec block: do not ignore failures when pushing to io_uring submission queue
Instead of silently ignoring the error, return an error to the callers.

This in practice should never happen, because the submission queue size
(ring depth) is the same as the virtio queue size. Virtio queue won't
push more requests than there are submission queue entries.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-12-21 10:47:29 +00:00
Wei Liu
b2f40afc69 block: fix short read and short write in AsyncAdaptor
The original code relied on the default `read_vectored` or
`write_vectored` implementations from the standard library.

The default implementation of those functions only uses the first
non-empty buffer. That's not correct when there are more than one
buffers.

Fixes: #6876
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-12-21 10:46:16 +00:00
Rob Bradford
72452707ee scripts: Reduce number of parallel jobs on ARM64 CI
This system is erroring out on jobs due to insufficient memory - reduce
parallelism to allow CI jobs to complete.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2024-12-20 10:36:13 +00:00
Rob Bradford
49a389ff12 build: Release v43.0
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2024-12-18 17:28:48 +00:00
dependabot[bot]
2a9b60dcce build: Bump object from 0.36.1 to 0.36.5
Bumps [object](https://github.com/gimli-rs/object) from 0.36.1 to 0.36.5.
- [Changelog](https://github.com/gimli-rs/object/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gimli-rs/object/compare/0.36.1...0.36.5)

---
updated-dependencies:
- dependency-name: object
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-18 16:16:59 +00:00
Jinrong Liang
bbd8d3b71d tests: live-migration: Add integration test for tcp live migration
Add integration test for tcp live migration to ensure live migration
functions as expected.

Signed-off-by: Jinrong Liang <cloudliang@tencent.com>
2024-12-18 14:02:38 +00:00
Jinrong Liang
b9163bf431 docs: Add documentation for cross-host TCP live migration
Updated the live migration documentation to include instructions for
performing cross-host live migrations over TCP connections.

Signed-off-by: Jinrong Liang <cloudliang@tencent.com>
2024-12-18 14:02:38 +00:00
Jinrong Liang
909e1bc3f0 vmm: Add support for cross-host live migration over TCP
Add support for cross-host live migration over TCP, which
significantly improves performance in cross-host live migration
scenarios compared to the Unix socket forwarding method.

Signed-off-by: Jinrong Liang <cloudliang@tencent.com>
2024-12-18 14:02:38 +00:00
Jinrong Liang
4f18476cca build: Bump vm-memory from 0.16.0 to 0.16.1
Bump vm-memory from 0.16.0 to 0.16.1 to include the implementations of
ReadVolatile and WriteVolatile for TcpStream.

Signed-off-by: Jinrong Liang <cloudliang@tencent.com>
2024-12-18 14:02:38 +00:00
dependabot[bot]
3d7661664d build: Bump getrandom from 0.2.14 to 0.2.15
Bumps [getrandom](https://github.com/rust-random/getrandom) from 0.2.14 to 0.2.15.
- [Changelog](https://github.com/rust-random/getrandom/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/getrandom/compare/v0.2.14...v0.2.15)

---
updated-dependencies:
- dependency-name: getrandom
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-14 15:13:09 +00:00
Alyssa Ross
50bac1694f vmm: support PCI I/O regions on all architectures
While non-Intel CPU architectures don't have a special concept of IO
address space, support for PCI I/O regions is still needed to be able
to handle PCI devices that use them.

With this change, I'm able to pass through an e1000e device from QEMU
to a cloud-hypervisor VM on aarch64 and use it in the cloud-hypervisor
guest.  Previously, it would hit the unimplemented!().

Signed-off-by: Alyssa Ross <hi@alyssa.is>
2024-12-14 14:12:00 +00:00
Alyssa Ross
dd8a5a7de8 vmm: remove outdated comment
Fixes: def98faf ("vmm, vm-allocator: Introduce an allocator for platform devices")
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2024-12-14 14:12:00 +00:00
dependabot[bot]
dba92c28f7 build: Bump event-listener-strategy from 0.5.1 to 0.5.3
Bumps [event-listener-strategy](https://github.com/smol-rs/event-listener-strategy) from 0.5.1 to 0.5.3.
- [Release notes](https://github.com/smol-rs/event-listener-strategy/releases)
- [Changelog](https://github.com/smol-rs/event-listener-strategy/blob/main/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/event-listener-strategy/compare/v0.5.1...v0.5.3)

---
updated-dependencies:
- dependency-name: event-listener-strategy
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-13 01:55:30 +00:00
Muminul Islam
4e298d1abf hypervisor: maintain a bitmap for SEV-SNP VM on MSHV
Add a bitmap to MshvVM struct for caching the pages
that the VMM got shared access from the guest.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2024-12-12 03:32:22 +00:00
Muminul Islam
149c342867 vmm: pass memory size during VM creation
Pass memory size from vmm crate to hypervisor VM
creation API so use for creating Bitmap for SEV-SNP
guest.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2024-12-12 03:32:22 +00:00
Muminul Islam
4054a49e2d hypervisor: use memory size during VM creation
For SEV-SNP VM on MSHV we need to request page access during
IO, we want to avoid such request for the page that have already
been requested. In order to maintain the bitmap we need the memory size
during bitmap creation.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2024-12-12 03:32:22 +00:00
dependabot[bot]
1001d807ff build: Bump version_check from 0.9.4 to 0.9.5
Bumps [version_check](https://github.com/SergioBenitez/version_check) from 0.9.4 to 0.9.5.
- [Commits](https://github.com/SergioBenitez/version_check/compare/v0.9.4...v0.9.5)

---
updated-dependencies:
- dependency-name: version_check
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-12 02:56:45 +00:00
Muminul Islam
e4a5219f53 hypervisor: mshv: modify GuestRequest handling for CVM
VMM needs to handle VMG exit for guest request. This patch
removes the old method that uses gpa_read/write(IOCTL and hypercall),
which is expensive and update the GHCB page using
mapped(root partition) struct.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2024-12-11 21:06:06 +00:00
Muminul Islam
a458351d8b hypervisor: mshv: modify MMIO exit handling for CVM
VMM needs to handle VMG exit for MMIO. This patch
removes the old method that uses gpa_read/write(IOCTL and hypercall),
which is expensive and update the GHCB page using
mapped(root partition) struct.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2024-12-11 21:06:06 +00:00
Muminul Islam
5a27bf878c hypervisor: mshv: modify IoPort handling for CVM
VMM needs to handle VMG exit for IO Port. This patch
removes the old method that uses gpa_read/write(IOCTL and hypercall),
which is expensive and update the GHCB page using
mapped(root partition) struct.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2024-12-11 21:06:06 +00:00
Muminul Islam
310dafb4d7 hypervisor: mshv: modify doorbell page handling for CVM
VMM needs to handle VMG exit for doorbell page. This patch
removes the old method that uses gpa_read/write(IOCTL and hypercall),
which is expensive and update the GHCB page using
mapped(root partition) struct.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2024-12-11 21:06:06 +00:00
Muminul Islam
78895dcc37 hypervisor: mshv: clear exitinfo1 using mapped ghcb address
After handling the VMG exit vmm needs to clear the exitinfo1
into the GHCB page. This patch replaces the old
method(gpa_write) and clear the exitinfo1 using mapped GHCB struct.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2024-12-11 21:06:06 +00:00
Muminul Islam
1757d83db3 hypervisor: Keep GHCB mapped address for each VCPU on MSHV
For confidential VM on MSHV, GHCB page is the communication
method between guest and host. All the CVM exits, VMM
needs to read and write to the GHCB page. MSHV provides
an option to remap the page to the root partition. This
way VMM could directly read and write to the page and
skip extra IOCTL and hypercall. This improvement makes
the IO 10% faster.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2024-12-11 21:06:06 +00:00
Muminul Islam
9cef779cc7 build: use latest version of mshv and vfio crates
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2024-12-11 21:06:06 +00:00
dependabot[bot]
a10d1ed78e build: Bump openssl-sys from 0.9.102 to 0.9.104
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.102 to 0.9.104.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.102...openssl-sys-v0.9.104)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-10 23:57:33 +00:00
dependabot[bot]
dc7f0a42bc build: Bump regex from 1.10.5 to 1.11.1
Bumps [regex](https://github.com/rust-lang/regex) from 1.10.5 to 1.11.1.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.10.5...1.11.1)

---
updated-dependencies:
- dependency-name: regex
  dependency-type: indirect
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-10 00:39:08 +00:00
dependabot[bot]
53098ae40f build: Bump backtrace from 0.3.73 to 0.3.74
Bumps [backtrace](https://github.com/rust-lang/backtrace-rs) from 0.3.73 to 0.3.74.
- [Release notes](https://github.com/rust-lang/backtrace-rs/releases)
- [Commits](https://github.com/rust-lang/backtrace-rs/compare/0.3.73...0.3.74)

---
updated-dependencies:
- dependency-name: backtrace
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-09 11:48:05 +00:00
Ruoqing He
51cf346828 ci: Enable riscv64 CI of devices module
Enable build, clippy and unit-test on devices module.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-12-06 16:24:41 +00:00
Ruoqing He
0042447fb9 devices: Disable tpm module for riscv64
TPM support on RISC-V is not scheduled in this stage, we will come back
when it's ready and we have workloads to verify it on RISC-V.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-12-06 16:24:41 +00:00
Ruoqing He
e8c330e220 devices: Introduce RISC-V AIA interrupt device
Introduce definitions, implementations and error variants of RISC-V AIA
(Advance Interrupt Architecture) interrupt controller.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-12-06 16:24:41 +00:00
Ruoqing He
ba78e331c6 devices: Enable build with kvm feature
Currently `devices` module cannot be solely built, by specifying
`arch/kvm` to turn on the features required for its dependency - `arch`
crate to build. Thus enabling `devices` crate to be built and tested
with command:

```sh
cargo build -p devices --features kvm
```

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-12-06 16:24:41 +00:00
Ruoqing He
b6d2ac2c2e devices: Enable arch module for riscv64
`arch` module are required for devices module to build. Enabling `arch`
module in target specific dependencies section.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-12-06 16:24:41 +00:00
dependabot[bot]
f6fb442fa3 build: Bump cpufeatures from 0.2.12 to 0.2.16
Bumps [cpufeatures](https://github.com/RustCrypto/utils) from 0.2.12 to 0.2.16.
- [Commits](https://github.com/RustCrypto/utils/compare/cpufeatures-v0.2.12...cpufeatures-v0.2.16)

---
updated-dependencies:
- dependency-name: cpufeatures
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-06 00:47:03 +00:00
dependabot[bot]
0c2f2d3ec1 build: Bump anyhow from 1.0.87 to 1.0.94
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.87 to 1.0.94.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.87...1.0.94)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-05 00:30:01 +00:00
Ruoqing He
584c088094 ci: Enable riscv64 CI of vm-allocator module
Enable build, clippy, doc-test and unit-test on vm-allocator module.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-12-04 09:29:56 +00:00
Ruoqing He
5b715f483d vm-allocator: Introduce RISC-V architecture
Provide implementation for GSI allocator to work on riscv64
architecture, and doc comment for riscv64 as well.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-12-04 09:29:56 +00:00
Ruoqing He
1e602bd9a6 vm-allocator: Enable build with kvm feature
Currently `vm-allocator` module cannot be solely built, by add
`features` section and specifying `arch/kvm` to turn on the features
required for its dependency - `arch` module to build. Thus enabling
`vm-allocator` crate to be built and tested with command:

```sh
cargo build -p vm-allocator --features kvm
```

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-12-04 09:29:56 +00:00
Ruoqing He
6b57f301db vm-allocator: Enable arch module for riscv64
`arch` module are required to provide definitions for riscv64 system irq
and memroy allocation, enabling `arch` module in target specific
dependencies section.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-12-04 09:29:56 +00:00
dependabot[bot]
cd0db3146f build: Bump async-signal from 0.2.6 to 0.2.10
Bumps [async-signal](https://github.com/smol-rs/async-signal) from 0.2.6 to 0.2.10.
- [Release notes](https://github.com/smol-rs/async-signal/releases)
- [Changelog](https://github.com/smol-rs/async-signal/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/async-signal/compare/v0.2.6...v0.2.10)

---
updated-dependencies:
- dependency-name: async-signal
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-04 00:10:04 +00:00
Ruoqing He
ac3ef0849e ci: Enable riscv64 CI of arch module
Enable build, clippy and unit-test on arch module.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-12-03 22:13:37 +00:00
Ruoqing He
bd8db86b0c arch: Introduce RISC-V architecture
Integrate fdt setup and layout for `riscv64` to `arch` crate, provide
definitions of `riscv64` platform specific error and reuse
`MmioDeviceInfo`, `PciSpaceInfo` structures and `DeviceInfoForFdt` impl
block for `riscv64`.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-12-03 22:13:37 +00:00
Ruoqing He
7b5f06788a arch: Introduce fdt setup for riscv64
Introduce `cpu`, `memory`, `chosen`, `aia` and `pci` node setup for
`riscv64`.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-12-03 22:13:37 +00:00
Ruoqing He
9a7f278716 arch: Introduce RISC-V 64-bit layout
Reused layout set for `aarch64` as much as possible to provide a concise
and compatible (for devices) layout for RISC-V 64-bit.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-12-03 22:13:37 +00:00
Ruoqing He
fb457954bb arch: Enable fdt_parser and vm-fdt for riscv64
`fdt_parser` and `vm-fdt` crates are required to setup fdt used to boot
riscv64 virtual machines, enabling the two crates in target specific
dependencies section.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-12-03 22:13:37 +00:00
Ruoqing He
c4063d26be hypervisor: Set pc and a1 for all vcpu
It turns out we need to setup `a0`, `pc` and `a1` for all vcpus before
we run them, remove predicates used to set `pc` and `a1` for `vcpu0`.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-12-03 22:13:37 +00:00
Ruoqing He
9006013c60 hypervisor: Tune Vaia trait to work with fdt setup
Previous `Vaia` implementation uses types as it is, thus the property
string generated for fdt setup requires additional type conversion.
Change the types used in the methods of `Vaia` trait to provide a
concise fdt setup process.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-12-03 22:13:37 +00:00
dependabot[bot]
30cf1eed5e build: Bump libc from 0.2.158 to 0.2.167
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.158 to 0.2.167.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Changelog](https://github.com/rust-lang/libc/blob/0.2.167/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.158...0.2.167)

---
updated-dependencies:
- dependency-name: libc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-03 01:15:36 +00:00
dependabot[bot]
c6b023e58d build: Bump async-process from 2.2.2 to 2.3.0
Bumps [async-process](https://github.com/smol-rs/async-process) from 2.2.2 to 2.3.0.
- [Release notes](https://github.com/smol-rs/async-process/releases)
- [Changelog](https://github.com/smol-rs/async-process/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/async-process/compare/v2.2.2...v2.3.0)

---
updated-dependencies:
- dependency-name: async-process
  dependency-type: indirect
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-02 15:10:37 +00:00
Bo Chen
46f0e0d14a ci: Increase rate-limiter worker timeout
The rate-limiter worker was moved to use small Azure VMs (#6731) and now
requires more time to complete.

Increasing its timeout to stablize this worker.

Signed-off-by: Bo Chen <bo.arvin.chen@gmail.com>
2024-12-02 15:11:24 +00:00
Ruoqing He
261bfac4d4 ci: Constrain FW_URL to x86_64 one
With 0.5.0 release of `rust-hypervisor-firmware`, `aarch64` binary were
added to assets, which causes the `FW_URL` to have multiple download url
separated by a white space, thus our integration tests would fail.

Constrain `FW_URL` to `hypervisor-fw` to resolve this.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-12-02 14:14:57 +00:00
dependabot[bot]
ec9b9ab542 build: Bump async-broadcast from 0.7.0 to 0.7.1
Bumps [async-broadcast](https://github.com/smol-rs/async-broadcast) from 0.7.0 to 0.7.1.
- [Release notes](https://github.com/smol-rs/async-broadcast/releases)
- [Changelog](https://github.com/smol-rs/async-broadcast/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/async-broadcast/compare/0.7.0...v0.7.1)

---
updated-dependencies:
- dependency-name: async-broadcast
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-29 16:10:28 +00:00
Julian Stecklina
8c9b34c9f1 docs: rewrite cross-host migration docs
The previous docs were very developer centric and have led several
people to believe that cross-machine migration is not supported at
all.

Signed-off-by: Julian Stecklina <julian.stecklina@cyberus-technology.de>
2024-11-29 15:42:23 +00:00
Julian Stecklina
5b822191c0 docs: apply style fixes to live migration docs
I've added newlines between paragraphs and code blocks for easier
reading. I've also changed the code blocks to use the correct
highlighting.

Signed-off-by: Julian Stecklina <julian.stecklina@cyberus-technology.de>
2024-11-29 15:42:23 +00:00
Ruoqing He
ab7b294688 misc: Replace map_or on false with is_some_and
Replace `map_or()` on false condition with `is_some_and` to provide
better readability, as suggestted by v1.84.0-beta.1 `cargo clippy`.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-29 12:44:33 +00:00
dependabot[bot]
7e419784cd build: Bump fastrand from 2.1.1 to 2.2.0
Bumps [fastrand](https://github.com/smol-rs/fastrand) from 2.1.1 to 2.2.0.
- [Release notes](https://github.com/smol-rs/fastrand/releases)
- [Changelog](https://github.com/smol-rs/fastrand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/fastrand/compare/v2.1.1...v2.2.0)

---
updated-dependencies:
- dependency-name: fastrand
  dependency-type: indirect
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-28 15:03:52 +00:00
dependabot[bot]
48bed965c0 build: Bump syn from 2.0.66 to 2.0.87
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.66 to 2.0.87.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.66...2.0.87)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-26 22:10:11 +00:00
Rob Bradford
0d6cef4521 pci: vfio: Release memory slots upon unmap
This prevents starvation of the limited set of memory slots in the
kernel.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2024-11-24 10:45:15 +00:00
Rob Bradford
81f8a27ef6 pci: vfio: Use MemorySlotAllocator for allocating memory slots
Adapt the existing code to replace the closure with the new of the new
MemorySlotAllocator.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2024-11-24 10:45:15 +00:00
Rob Bradford
e3bd5e9b35 vmm: memory_manager: Adapt for new MemorySlotAllocator
Adapt the existing code to transparently the MemorySlotAllocator. The
MemoryManager is the canonical holder of the these values with them
turned into a MemoryAllocator on demand.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2024-11-24 10:45:15 +00:00
Rob Bradford
80b2c98a68 vm-allocator: Add an allocator KVM memory slots
Add an allocator for KVM memory slots, a finite resource in the kernel
- these need to be manipulated across different crates with a common
dependency on this crate.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2024-11-24 10:45:15 +00:00
Ruoqing He
3c05626ad1 scripts: Replace download_linux with prepare_linux
`prepare_linux` is capable of determining whether we need to invoke
`build_custom_linux` for building linux from source or `download_linux`
for downloading pre-built.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-12 16:27:20 +00:00
Ruoqing He
906580ee92 scripts: Add prepare_linux function
`prepare_linux` checks if a `--build-guest-kernel` option is present,
and build kernel from `cloud-hypervisor/linux.git`. Otherwise, it will
invoke `download_linux` to use pre-built kernel.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-12 16:27:20 +00:00
dependabot[bot]
c6889388a3 build: Bump anstyle-parse from 0.2.3 to 0.2.6
Bumps [anstyle-parse](https://github.com/rust-cli/anstyle) from 0.2.3 to 0.2.6.
- [Commits](https://github.com/rust-cli/anstyle/compare/anstyle-parse-v0.2.3...anstyle-parse-v0.2.6)

---
updated-dependencies:
- dependency-name: anstyle-parse
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-11 10:47:03 +00:00
Rob Bradford
f38fc94115 build: Increase ARM64 CI build timeouts
The builds are flaking due to reaching the timeouts.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2024-11-11 10:45:58 +00:00
dependabot[bot]
6ecca29aa6 build: Bump pkg-config from 0.3.30 to 0.3.31
Bumps [pkg-config](https://github.com/rust-lang/pkg-config-rs) from 0.3.30 to 0.3.31.
- [Changelog](https://github.com/rust-lang/pkg-config-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/pkg-config-rs/compare/0.3.30...0.3.31)

---
updated-dependencies:
- dependency-name: pkg-config
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-08 00:50:27 +00:00
Rob Bradford
2ef04671be main: Place --tpm in the correct argument group
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2024-11-08 00:12:23 +00:00
Rob Bradford
453bc31994 main: Require a payload to boot when any VM argument provided
If any VM argument (e.g. --disk) is provided require some payload (e.g.
--kernel or --firmware) when parsing the command line arguments.

See: #6831

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2024-11-08 00:12:23 +00:00
dependabot[bot]
6e6966df8f build: Bump xdg-home from 1.2.0 to 1.3.0
Bumps [xdg-home](https://github.com/zeenix/xdg-home) from 1.2.0 to 1.3.0.
- [Release notes](https://github.com/zeenix/xdg-home/releases)
- [Commits](https://github.com/zeenix/xdg-home/compare/1.2.0...1.3.0)

---
updated-dependencies:
- dependency-name: xdg-home
  dependency-type: indirect
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-07 09:23:49 +00:00
dependabot[bot]
f32c2638f6 build: Bump futures-core from 0.3.30 to 0.3.31
Bumps [futures-core](https://github.com/rust-lang/futures-rs) from 0.3.30 to 0.3.31.
- [Release notes](https://github.com/rust-lang/futures-rs/releases)
- [Changelog](https://github.com/rust-lang/futures-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/futures-rs/compare/0.3.30...0.3.31)

---
updated-dependencies:
- dependency-name: futures-core
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-06 15:14:55 +00:00
Ruoqing He
fbe1cd6404 hypervisor: kvm: Add g/set_regs unit-test on riscv64
Add unit-test to make sure get_regs and set_regs on riscv64 architecture
work as expected, effectively avoiding typos in register names.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-06 14:32:39 +00:00
Ruoqing He
778a72f52f ci: Enable riscv64 CI of hypervisor module
Integrate machine provided by @ISRC-CAS to run build, clippy and
unit-test on hypervisor module.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-06 14:32:39 +00:00
Ruoqing He
76256230d6 hypervisor: kvm: Complement create_standard_regs
Complement `create_standard_regs` implementation on RISC-V platform to
work with `From` trait of `kvm_riscv_core`.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-06 14:32:39 +00:00
Ruoqing He
8cd80ea36b hypervisor: Introduce RISC-V architecture
Introduce cpu, vm, kvm, arch module RISC-V platform support. Add macro
definitions to implement methods interacting with RISC-V registers.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-06 14:32:39 +00:00
Ruoqing He
5e937c8b88 hypervisor: vm: Introduce RISC-V Vm trait
Introduce RISC-V specific Vm traits and error variant, disable
`create_irq_chip` on RISC-V platform.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-06 14:32:39 +00:00
Ruoqing He
710535343b hypervisor: cpu: Introduce RISC-V Vcpu trait
Add RISC-V specific Vcpu trait. Disable `set_guest_debug` on RISC-V
platform.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-06 14:32:39 +00:00
Ruoqing He
59c5b0a1cd hypervisor: kvm: Integrate riscv64 regs & AIA
Incorporates riscv64 register interaction and AIA creation to kvm
module. Complete `Vcpu` trait on RISC-V platform.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-06 14:32:39 +00:00
Ruoqing He
c13019d5b5 hypervisor: cpu: Introduce RISC-V specific error
Add error variants specific to RISC-V architecture.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-06 14:32:39 +00:00
Ruoqing He
c5774685f4 hypervisor: kvm: Introduce AIA creation
Implement definition required to work with KVM in-kernel AIA device,
construction procedure of AIA.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-06 14:32:39 +00:00
Ruoqing He
2df8d2fa1e hypervisor: kvm: Introduce riscv64 register g/set
Implement macros to calculate register ID on riscv64, definition of
RISC-V `VcpuKvmState`.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-06 14:32:39 +00:00
Ruoqing He
dd00bd6ef2 hypervisor: arch: Integrate riscv64 to arch module
Integrate `aia` module into `riscv64` module, and enable `riscv64`
module if target architecture is RISC-V 64-bit.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-06 14:32:39 +00:00
Ruoqing He
2752149bb0 hypervisor: arch: Introduce riscv64 AIA
Introduce definitions, traits relate to RISC-V AIA (Advanced Interrupt
Architecutre) construction.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-06 14:32:39 +00:00
Andrew Consroe
1bc74e43f8 docs: remove old --api-socket behavior
default --api-socket removed in 78f9ddc6be

Signed-off-by: Andrew Consroe <aconz2@gmail.com>
2024-11-06 08:33:27 +00:00
Wojtek Czekalski
8acaeb5c82 vmm: Add serial option to the disk config doc
Signed-off-by: Wojtek Czekalski <wczekalski@me.com>
2024-11-05 19:48:25 +00:00
dependabot[bot]
5eaf510f90 build: Bump async-trait from 0.1.80 to 0.1.83
Bumps [async-trait](https://github.com/dtolnay/async-trait) from 0.1.80 to 0.1.83.
- [Release notes](https://github.com/dtolnay/async-trait/releases)
- [Commits](https://github.com/dtolnay/async-trait/compare/0.1.80...0.1.83)

---
updated-dependencies:
- dependency-name: async-trait
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-05 15:44:55 +00:00
Rob Bradford
df1d6eaaee virtio-devices: Enable VIRTIO_RING_F_INDIRECT_DESC
This improves sequential write performance using fio (2888MiB/s ->
3293MiB/s)

VM config: cloud-hypervisor --disk path=~/workloads/jammy.raw,direct=on path=~/workloads/big-disk.img,direct=on --cpus boot=1 --memory size=2G,shared=on --serial tty --console off --seccomp log --kernel ~/workloads/hypervisor-fw

Host: fio --filename=big-disk.img --direct=1 --rw=write --bs=256k --ioengine=libaio --iodepth=64 --runtime=120 --numjobs=1 --time_based --group_reporting --name=throughput-test-job --eta-newline=1

VM:  fio --filename=/dev/vdb --direct=1 --rw=write --bs=256k --ioengine=libaio --iodepth=64 --runtime=120 --numjobs=1 --time_based --group_reporting --name=throughput-test-job --eta-newline=1

Baseline (file on filesystem on host used as backing store for block
device):

throughput-test-job: (groupid=0, jobs=1): err= 0: pid=10169: Tue Nov  5 09:31:55 2024
  write: IOPS=13.5k, BW=3385MiB/s (3549MB/s)(397GiB/120008msec); 0 zone resets
    slat (usec): min=4, max=10222, avg=20.25, stdev=29.01
    clat (usec): min=984, max=45599, avg=4706.01, stdev=2278.11
     lat (usec): min=1002, max=45610, avg=4726.27, stdev=2278.77
    clat percentiles (usec):
     |  1.00th=[ 3195],  5.00th=[ 3228], 10.00th=[ 3261], 20.00th=[ 3261],
     | 30.00th=[ 3261], 40.00th=[ 3261], 50.00th=[ 3294], 60.00th=[ 3916],
     | 70.00th=[ 5014], 80.00th=[ 7308], 90.00th=[ 7635], 95.00th=[ 7898],
     | 99.00th=[ 8586], 99.50th=[ 8979], 99.90th=[36439], 99.95th=[36963],
     | 99.99th=[43779]
   bw (  MiB/s): min= 1934, max= 4821, per=100.00%, avg=3391.67, stdev=1266.42, samples=239
   iops        : min= 7738, max=19286, avg=13566.67, stdev=5065.65, samples=239
  lat (usec)   : 1000=0.01%
  lat (msec)   : 2=0.03%, 4=61.10%, 10=38.62%, 20=0.11%, 50=0.15%
  cpu          : usr=17.13%, sys=14.38%, ctx=1352501, majf=0, minf=11
  IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.1%, >=64=0.0%
     issued rwts: total=0,1624829,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=64

Run status group 0 (all jobs):
  WRITE: bw=3385MiB/s (3549MB/s), 3385MiB/s-3385MiB/s (3549MB/s-3549MB/s), io=397GiB (426GB), run=120008-120008msec

Disk stats (read/write):
    dm-2: ios=129/1624787, sectors=1872/831364040, merge=0/0, ticks=185/6960387, in_queue=6960572, util=100.00%, aggrios=130/1626025, aggsectors=1880/831915888, aggrmerge=0/0, aggrticks=194/6967818, aggrin_queue=6968012, aggrutil=99.97%
    dm-0: ios=130/1626025, sectors=1880/831915888, merge=0/0, ticks=194/6967818, in_queue=6968012, util=99.97%, aggrios=130/1606095, aggsectors=1880/831915888, aggrmerge=0/19930, aggrticks=204/6634488, aggrin_queue=6635288, aggrutil=58.59%
  nvme0n1: ios=130/1606095, sectors=1880/831915888, merge=0/19930, ticks=204/6634488, in_queue=6635288, util=58.59%

On block device in VM:

throughput-test-job: (groupid=0, jobs=1): err= 0: pid=667: Tue Nov  5 09:53:19 2024
  write: IOPS=13.2k, BW=3293MiB/s (3453MB/s)(386GiB/120008msec); 0 zone resets
    slat (usec): min=4, max=3518, avg=27.77, stdev=35.32
    clat (usec): min=723, max=44252, avg=4829.82, stdev=2222.41
     lat (usec): min=735, max=44270, avg=4857.85, stdev=2223.45
    clat percentiles (usec):
     |  1.00th=[ 3097],  5.00th=[ 3195], 10.00th=[ 3195], 20.00th=[ 3228],
     | 30.00th=[ 3261], 40.00th=[ 3294], 50.00th=[ 3621], 60.00th=[ 4555],
     | 70.00th=[ 5997], 80.00th=[ 7242], 90.00th=[ 7570], 95.00th=[ 7898],
     | 99.00th=[ 8586], 99.50th=[ 8848], 99.90th=[36439], 99.95th=[36963],
     | 99.99th=[40633]
   bw (  MiB/s): min= 1914, max= 4857, per=100.00%, avg=3299.46, stdev=1180.81, samples=239
   iops        : min= 7658, max=19430, avg=13197.77, stdev=4723.22, samples=239
  lat (usec)   : 750=0.01%, 1000=0.01%
  lat (msec)   : 2=0.01%, 4=52.79%, 10=46.95%, 20=0.10%, 50=0.14%
  cpu          : usr=25.95%, sys=16.71%, ctx=1111821, majf=0, minf=10
  IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.1%, >=64=0.0%
     issued rwts: total=0,1580693,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=64

Run status group 0 (all jobs):
  WRITE: bw=3293MiB/s (3453MB/s), 3293MiB/s-3293MiB/s (3453MB/s-3453MB/s), io=386GiB (414GB), run=120008-120008msec

Disk stats (read/write):
  vdb: ios=60/1953213, merge=0/0, ticks=14/8229134, in_queue=8229149, util=100.00%

Prior to change:

throughput-test-job: (groupid=0, jobs=1): err= 0: pid=667: Tue Nov  5 09:37:45 2024
  write: IOPS=11.6k, BW=2888MiB/s (3028MB/s)(338GiB/120008msec); 0 zone resets
    slat (usec): min=3, max=3200, avg=18.48, stdev=24.54
    clat (usec): min=1237, max=46575, avg=5521.41, stdev=2641.99
     lat (usec): min=1249, max=46591, avg=5540.06, stdev=2643.54
    clat percentiles (usec):
     |  1.00th=[ 2999],  5.00th=[ 3163], 10.00th=[ 3195], 20.00th=[ 3261],
     | 30.00th=[ 3294], 40.00th=[ 3359], 50.00th=[ 6063], 60.00th=[ 7111],
     | 70.00th=[ 7373], 80.00th=[ 7570], 90.00th=[ 7832], 95.00th=[ 8094],
     | 99.00th=[ 8717], 99.50th=[ 9241], 99.90th=[36963], 99.95th=[37487],
     | 99.99th=[41157]
   bw (  MiB/s): min= 1936, max= 4826, per=100.00%, avg=2892.43, stdev=1202.99, samples=239
   iops        : min= 7746, max=19306, avg=11569.68, stdev=4811.98, samples=239
  lat (msec)   : 2=0.01%, 4=46.26%, 10=53.38%, 20=0.09%, 50=0.26%
  cpu          : usr=14.20%, sys=8.59%, ctx=1246257, majf=0, minf=12
  IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.1%, >=64=0.0%
     issued rwts: total=0,1386102,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=64

Run status group 0 (all jobs):
  WRITE: bw=2888MiB/s (3028MB/s), 2888MiB/s-2888MiB/s (3028MB/s-3028MB/s), io=338GiB (363GB), run=120008-120008msec

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2024-11-05 15:44:41 +00:00
Ruoqing He
a6a3d247da ci: Enable consistency check for fuzz workspace
`fuzz` is a separate workspace, enable consistency check on rust-vmm
crates in `fuzz` to keep stuffs in sync with root workspace.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-04 11:08:29 +00:00
Ruoqing He
838a857311 hypervisor: Wrap get_device_attr with unsafe
`get_device_attr` in 0.19.0 kvm-ioctls is marked as unsafe, wrap
invokation of `get_device_attr` with `unsafe` block.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-04 11:08:29 +00:00
Ruoqing He
95229b24ab build: Bump rust-vmm crates
- Bump kvm-bindings from 0.9.1 to 0.10.0
- Bump kvm-ioctls from 0.18.0 to 0.19.0
- Bump vm-memory from 0.15.0 to 0.16.0
- Bump linux-loader from 0.12.0 to 0.13.0
- Bump virtio-bindings from 0.2.1 to 0.2.4
- Bump virtio-queue from 0.13.0 to 0.14.0
- Pin mshv-bindings to 0.3.1
- Pin mshv-ioctls to 0.3.1
- Pin vhost to rev "d983ae0"
- Pin vhost-user-backend to rev "d983ae0"

Since vhost 0.12.0 and vhost-user-backend 0.16.0 are going to be yanked,
temporarily pin these two to "d983ae0", which are expected to be
replaced by 0.13.0 vhost and 0.17.0 vhost-user-backend after released.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-04 11:08:29 +00:00
Ruoqing He
71b0eb3c21 ci: Enable consistency check for rust-vmm packages
Enable `package-consistency` check to prevent contributor or dependabot
accidentally break `rust-vmm` consistency.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-02 16:09:09 +00:00
Ruoqing He
337cbf3d33 scripts: Add consistency check script
Add `package-consistency-check.py` script to prevent #6809 and #6815
from happening. This script takes a string present in the repository
field of packages to identify pacakges from a specific source.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-02 16:09:09 +00:00
dependabot[bot]
675140f627 build: Bump lock_api from 0.4.11 to 0.4.12
Bumps [lock_api](https://github.com/Amanieu/parking_lot) from 0.4.11 to 0.4.12.
- [Changelog](https://github.com/Amanieu/parking_lot/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Amanieu/parking_lot/compare/lock_api-0.4.11...lock_api-0.4.12)

---
updated-dependencies:
- dependency-name: lock_api
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-02 10:14:51 +00:00
Ruoqing He
894a4dee6e build: Centralize vhost-user-backend to workspace
`vhost-user-backend` is left behind in #6764, move it to root workspace.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2024-11-02 08:08:51 +00:00
Rob Bradford
f57d00cd66 build: Revert "build: Bump vfio_user from bf7d7e8 to 3febcdd"
This reverts commit 39ab10244b.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2024-11-02 08:57:36 +00:00
dependabot[bot]
59a1719b6e build: Bump flume from 0.11.0 to 0.11.1
Bumps [flume](https://github.com/zesterer/flume) from 0.11.0 to 0.11.1.
- [Changelog](https://github.com/zesterer/flume/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zesterer/flume/commits)

---
updated-dependencies:
- dependency-name: flume
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-30 00:41:24 +00:00
dependabot[bot]
39ab10244b build: Bump vfio_user from bf7d7e8 to 3febcdd
Bumps [vfio_user](https://github.com/rust-vmm/vfio-user) from `bf7d7e8` to `3febcdd`.
- [Commits](bf7d7e851b...3febcdd3fa)

---
updated-dependencies:
- dependency-name: vfio_user
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-29 00:35:41 +00:00
dependabot[bot]
b5d856b31a build: Bump pin-project-lite from 0.2.14 to 0.2.15
Bumps [pin-project-lite](https://github.com/taiki-e/pin-project-lite) from 0.2.14 to 0.2.15.
- [Release notes](https://github.com/taiki-e/pin-project-lite/releases)
- [Changelog](https://github.com/taiki-e/pin-project-lite/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/pin-project-lite/compare/v0.2.14...v0.2.15)

---
updated-dependencies:
- dependency-name: pin-project-lite
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-26 00:01:51 +00:00
163 changed files with 6429 additions and 8705 deletions

View File

@@ -15,7 +15,7 @@ jobs:
- stable
- beta
- nightly
- "1.77.0"
- "1.82.0"
target:
- x86_64-unknown-linux-gnu
- x86_64-unknown-linux-musl

View File

@@ -21,13 +21,13 @@ jobs:
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v3
- name: Login to ghcr
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
@@ -36,17 +36,17 @@ jobs:
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# generate Docker tags based on the following events/attributes
tags: |
type=raw,value=20241022-0
type=raw,value=20250307-2
type=sha
- name: Build and push
if: ${{ github.event_name == 'push' }}
uses: docker/build-push-action@v2
uses: docker/build-push-action@v6
with:
file: ./resources/Dockerfile
platforms: linux/amd64,linux/arm64
@@ -55,7 +55,7 @@ jobs:
- name: Build only
if: ${{ github.event_name == 'pull_request' }}
uses: docker/build-push-action@v2
uses: docker/build-push-action@v6
with:
file: ./resources/Dockerfile
platforms: linux/amd64,linux/arm64

View File

@@ -6,7 +6,7 @@ concurrency:
jobs:
build:
timeout-minutes: 60
timeout-minutes: 120
name: Tests (ARM64)
runs-on: bookworm-arm64
steps:
@@ -21,7 +21,7 @@ jobs:
- name: Load openvswitch module
run: sudo modprobe openvswitch
- name: Run integration tests (musl)
timeout-minutes: 30
timeout-minutes: 60
run: scripts/dev_cli.sh tests --integration --libc musl
- name: Install Azure CLI
if: ${{ github.event_name != 'pull_request' }}

View File

@@ -7,7 +7,7 @@ on:
jobs:
build:
name: Tests (Metrics)
runs-on: garm-jammy-16
runs-on: bare-metal-9950x
env:
METRICS_PUBLISH_KEY: ${{ secrets.METRICS_PUBLISH_KEY }}
steps:
@@ -15,15 +15,6 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Docker
run: |
sudo apt-get update
sudo apt-get -y install ca-certificates curl gnupg
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
sudo chmod a+r /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt install -y docker-ce docker-ce-cli
- name: Run metrics tests
timeout-minutes: 60
run: scripts/dev_cli.sh tests --metrics -- -- --report-file /root/workloads/metrics.json

View File

@@ -7,7 +7,7 @@ concurrency:
jobs:
build:
name: Tests (Rate-Limiter)
runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'garm-jammy-16' }}
runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'bare-metal-9950x' }}
env:
AUTH_DOWNLOAD_TOKEN: ${{ secrets.AUTH_DOWNLOAD_TOKEN }}
steps:
@@ -16,19 +16,9 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Docker
if: ${{ github.event_name != 'pull_request' }}
run: |
sudo apt-get update
sudo apt-get -y install ca-certificates curl gnupg
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
sudo chmod a+r /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt install -y docker-ce docker-ce-cli
- name: Run rate-limiter integration tests
if: ${{ github.event_name != 'pull_request' }}
timeout-minutes: 10
timeout-minutes: 20
run: scripts/dev_cli.sh tests --integration-rate-limiter
- name: Skipping build for PR
if: ${{ github.event_name == 'pull_request' }}

16
.github/workflows/lychee.yaml vendored Normal file
View File

@@ -0,0 +1,16 @@
name: Link Check (lychee)
on:
pull_request
jobs:
link_check:
name: Link Check
runs-on: ubuntu-latest
steps:
- name: Code checkout
uses: actions/checkout@v4
- name: Link Availability Check
uses: lycheeverse/lychee-action@master
with:
args: --verbose --config .lychee.toml .

View File

@@ -0,0 +1,32 @@
name: Cloud Hypervisor Consistency
on: [pull_request, merge_group]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Rust VMM Consistency Check
runs-on: ubuntu-latest
steps:
- name: Code checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
run: sudo apt install -y python3
- name: Install Rust toolchain stable
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- name: Check Rust VMM Package Consistency of root Workspace
run: python3 scripts/package-consistency-check.py github.com/rust-vmm
- name: Check Rust VMM Package Consistency of fuzz Workspace
run: |
pushd fuzz
python3 ../scripts/package-consistency-check.py github.com/rust-vmm
popd

39
.github/workflows/preview-riscv64.yaml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: Cloud Hypervisor RISC-V 64-bit Preview
on: [pull_request, merge_group]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Cargo
runs-on: riscv64-qemu-host
strategy:
fail-fast: false
matrix:
module:
- hypervisor
- arch
- vm-allocator
- devices
steps:
- name: Code checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Rust toolchain
run: /opt/scripts/exec-in-qemu.sh rustup default 1.82.0
- name: Build ${{ matrix.module }} Module (kvm)
run: /opt/scripts/exec-in-qemu.sh cargo rustc --locked -p ${{ matrix.module }} --no-default-features --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
- name: Clippy ${{ matrix.module }} Module (kvm)
run: /opt/scripts/exec-in-qemu.sh cargo clippy --locked -p ${{ matrix.module }} --no-default-features --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
- name: Test ${{ matrix.module }} Module (kvm)
run: /opt/scripts/exec-in-qemu.sh cargo test --locked -p ${{ matrix.module }} --no-default-features --features "kvm"
- name: Check no files were modified
run: test -z "$(git status --porcelain)"

View File

@@ -45,7 +45,7 @@ jobs:
target: ${{ matrix.platform.target }}
args: ${{ matrix.platform.args }}
strip: true
toolchain: "1.77.0"
toolchain: "1.82.0"
- name: Copy Release Binaries
if: github.event_name == 'create' && github.event.ref_type == 'tag'
shell: bash
@@ -54,7 +54,7 @@ jobs:
cp target/${{ matrix.platform.target }}/release/ch-remote ./${{ matrix.platform.name_ch_remote }}
- name: Upload Release Artifacts
if: github.event_name == 'create' && github.event.ref_type == 'tag'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Artifacts for ${{ matrix.platform.target }}
path: |
@@ -80,7 +80,7 @@ jobs:
github.event_name == 'create' && github.event.ref_type == 'tag' &&
matrix.platform.target == 'x86_64-unknown-linux-gnu'
id: upload-release-cloud-hypervisor-vendored-sources
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
path: cloud-hypervisor-${{ github.event.ref }}.tar.xz
name: cloud-hypervisor-${{ github.event.ref }}.tar.xz

View File

@@ -13,3 +13,8 @@ line-length=72
# default 80
[body-max-line-length]
line-length=72
# Allow developers to add long links to useful resources
[ignore-by-body]
regex=^https?:\/\/
ignore=body-max-line-length

18
.lychee.toml Normal file
View File

@@ -0,0 +1,18 @@
verbose = "info"
exclude = [
# Availability of links below should be manually verified.
# Page for intel SGX support, returns 403 while querying.
'^https://www.intel.com/content/www/us/en/developer/tools/software-guard-extensions/linux-overview.html',
# Page for intel TDX support, returns 403 while querying.
'^https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/overview.html',
# Page for TPM, returns 403 while querying.
'^https://trustedcomputinggroup.org/wp-content/uploads/PC-Client-Specific-Platform-TPM-Profile-for-TPM-2p0-v1p05p_r14_pub.pdf',
# GitHub user smibarber referenced in `CREDITS.md` no longer exist
'^https://github.com/smibarber',
]
max_retries = 3
retry_wait_time = 5

View File

@@ -13,7 +13,7 @@ License](https://opensource.org/licenses/Apache-2.0).
## Coding Style
We follow the [Rust Style](https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/guide.md)
We follow the [Rust Style](https://github.com/rust-lang/rust/tree/HEAD/src/doc/style-guide/src)
convention and enforce it through the Continuous Integration (CI) process calling into `rustfmt`
for each submitted Pull Request (PR).
@@ -36,7 +36,7 @@ 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)
In order to get a clear contribution chain of trust we use the [signed-off-by language](https://web.archive.org/web/20230406041855/https://01.org/community/signed-process)
used by the Linux kernel project.
## Patch format
@@ -79,12 +79,12 @@ you want to merge your changes to `cloud-hypervisor`:
1. Fork the [cloud-hypervisor](https://github.com/cloud-hypervisor/cloud-hypervisor) project
into your github organization.
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/)
1. Within your fork, create a branch for your contribution.
1. [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
1. 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.
1. Once the pull request is approved it can be integrated.
## Issue tracking

535
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ edition = "2021"
homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
license = "Apache-2.0 AND BSD-3-Clause"
name = "cloud-hypervisor"
version = "42.0.0"
version = "45.0.0"
# Minimum buildable version:
# Keep in sync with version in .github/workflows/build.yaml
# Policy on MSRV (see #4318):
@@ -15,7 +15,7 @@ version = "42.0.0"
# a.) A dependency requires it,
# b.) If we want to use a new feature and that MSRV is at least 6 months old,
# c.) There is a security issue that is addressed by the toolchain update.
rust-version = "1.77.0"
rust-version = "1.82.0"
[profile.release]
codegen-units = 1
@@ -29,20 +29,20 @@ inherits = "release"
strip = false
[dependencies]
anyhow = "1.0.87"
anyhow = "1.0.94"
api_client = { path = "api_client" }
clap = { version = "4.5.13", features = ["string"] }
dhat = { version = "0.3.3", optional = true }
epoll = "4.3.3"
event_monitor = { path = "event_monitor" }
hypervisor = { path = "hypervisor" }
libc = "0.2.158"
libc = "0.2.167"
log = { version = "0.4.22", features = ["std"] }
option_parser = { path = "option_parser" }
seccompiler = { workspace = true }
serde_json = "1.0.120"
signal-hook = "0.3.17"
thiserror = "1.0.62"
thiserror = "2.0.6"
tpm = { path = "tpm" }
tracer = { path = "tracer" }
vm-memory = { workspace = true }
@@ -51,7 +51,7 @@ vmm-sys-util = { workspace = true }
zbus = { version = "4.4.0", optional = true }
[dev-dependencies]
dirs = "5.0.1"
dirs = "6.0.0"
net_util = { path = "net_util" }
once_cell = "1.20.2"
serde_json = "1.0.120"
@@ -102,19 +102,25 @@ members = [
]
[workspace.dependencies]
# rust-vmm crates
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
kvm-bindings = "0.9.1"
kvm-ioctls = "0.18.0"
linux-loader = "0.12.0"
mshv-bindings = { git = "https://github.com/rust-vmm/mshv", tag = "v0.3.0" }
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", tag = "v0.3.0" }
seccompiler = "0.4.0"
kvm-bindings = "0.10.0"
kvm-ioctls = "0.19.1"
linux-loader = "0.13.0"
mshv-bindings = "0.3.3"
mshv-ioctls = "0.3.3"
seccompiler = "0.5.0"
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main" }
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
vfio_user = { git = "https://github.com/rust-vmm/vfio-user", branch = "main" }
vhost = "0.12.0"
virtio-bindings = "0.2.2"
virtio-queue = "0.13.0"
vhost = { git = "https://github.com/rust-vmm/vhost", rev = "d983ae0" }
vhost-user-backend = { git = "https://github.com/rust-vmm/vhost", rev = "d983ae0" }
virtio-bindings = "0.2.4"
virtio-queue = "0.14.0"
vm-fdt = { git = "https://github.com/rust-vmm/vm-fdt", branch = "main" }
vm-memory = "0.15.0"
vm-memory = "0.16.1"
vmm-sys-util = "0.12.1"
# igvm crates
igvm = "0.3.4"
igvm_defs = "0.3.1"

View File

@@ -59,9 +59,13 @@ based on the [Rust VMM](https://github.com/rust-vmm) crates.
### Architectures
Cloud Hypervisor supports the `x86-64` and `AArch64` architectures. There are
minor differences in functionality between the two architectures
(see [#1125](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/1125)).
Cloud Hypervisor supports the `x86-64`, `AArch64` and `riscv64`
architectures, with functionality varying across these platforms. The
functionality differences between `x86-64` and `AArch64` are documented
in [#1125](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/1125).
The `riscv64` architecture support is experimental and offers limited
functionality. For more details and instructions, please refer to [riscv
documentation](docs/riscv.md).
### Guest OS
@@ -181,14 +185,9 @@ To build the kernel:
```shell
# Clone the Cloud Hypervisor Linux branch
$ git clone --depth 1 https://github.com/cloud-hypervisor/linux.git -b ch-6.2 linux-cloud-hypervisor
$ git clone --depth 1 https://github.com/cloud-hypervisor/linux.git -b ch-6.12.8 linux-cloud-hypervisor
$ pushd linux-cloud-hypervisor
# Use the x86-64 cloud-hypervisor kernel config to build your kernel for x86-64
$ wget https://raw.githubusercontent.com/cloud-hypervisor/cloud-hypervisor/main/resources/linux-config-x86_64
# Use the AArch64 cloud-hypervisor kernel config to build your kernel for AArch64
$ wget https://raw.githubusercontent.com/cloud-hypervisor/cloud-hypervisor/main/resources/linux-config-aarch64
$ cp linux-config-x86_64 .config # x86-64
$ cp linux-config-aarch64 .config # AArch64
$ make ch_defconfig
# Do native build of the x86-64 kernel
$ KCFLAGS="-Wa,-mx86-used-note=no" make bzImage -j `nproc`
# Do native build of the AArch64 kernel
@@ -303,7 +302,7 @@ As of 2023-01-03, the following cloud images are supported:
- [Ubuntu Focal](https://cloud-images.ubuntu.com/focal/current/) (focal-server-cloudimg-{amd64,arm64}.img)
- [Ubuntu Jammy](https://cloud-images.ubuntu.com/jammy/current/) (jammy-server-cloudimg-{amd64,arm64}.img )
- [Fedora 36](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/) ([Fedora-Cloud-Base-36-1.5.x86_64.raw.xz](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/x86_64/images/) / [Fedora-Cloud-Base-36-1.5.aarch64.raw.xz](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/aarch64/images/))
- [Fedora 36](https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/36/Cloud/) ([Fedora-Cloud-Base-36-1.5.x86_64.raw.xz](https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/36/Cloud/x86_64/images/) / [Fedora-Cloud-Base-36-1.5.aarch64.raw.xz](https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/36/Cloud/aarch64/images/))
Direct kernel boot to userspace should work with a rootfs from most
distributions although you may need to enable exotic filesystem types in the

View File

@@ -5,5 +5,5 @@ name = "api_client"
version = "0.1.0"
[dependencies]
thiserror = "1.0.62"
thiserror = "2.0.6"
vmm-sys-util = { workspace = true }

View File

@@ -11,19 +11,19 @@ sev_snp = []
tdx = []
[dependencies]
anyhow = "1.0.87"
anyhow = "1.0.94"
byteorder = "1.5.0"
hypervisor = { path = "../hypervisor" }
libc = "0.2.158"
libc = "0.2.167"
linux-loader = { workspace = true, features = ["bzimage", "elf", "pe"] }
log = "0.4.22"
serde = { version = "1.0.208", features = ["derive", "rc"] }
thiserror = "1.0.62"
uuid = "1.8.0"
thiserror = "2.0.6"
uuid = "1.12.1"
vm-memory = { workspace = true, features = ["backend-bitmap", "backend-mmap"] }
vm-migration = { path = "../vm-migration" }
vmm-sys-util = { workspace = true, features = ["with-serde"] }
[target.'cfg(target_arch = "aarch64")'.dependencies]
[target.'cfg(any(target_arch = "aarch64", target_arch = "riscv64"))'.dependencies]
fdt_parser = { version = "0.1.5", package = "fdt" }
vm-fdt = { workspace = true }

View File

@@ -1109,10 +1109,7 @@ fn print_node(node: fdt_parser::node::FdtNode<'_, '_>, n_spaces: usize) {
// - At first, try to convert it to CStr and print,
// - If failed, print it as u32 array.
let value_result = match CStr::from_bytes_with_nul(value) {
Ok(value_cstr) => match value_cstr.to_str() {
Ok(value_str) => Some(value_str),
Err(_e) => None,
},
Ok(value_cstr) => value_cstr.to_str().ok(),
Err(_e) => None,
};

View File

@@ -1,3 +1,4 @@
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
// Copyright 2020 Arm Limited (or its affiliates). All rights reserved.
// Copyright © 2020, Oracle and/or its affiliates.
//
@@ -5,7 +6,7 @@
// SPDX-License-Identifier: Apache-2.0
//! Implements platform specific functionality.
//! Supported platforms: x86_64, aarch64.
//! Supported platforms: x86_64, aarch64, riscv64.
#[macro_use]
extern crate log;
@@ -32,6 +33,9 @@ pub enum Error {
#[cfg(target_arch = "aarch64")]
#[error("Platform specific error (aarch64): {0:?}")]
PlatformSpecific(aarch64::Error),
#[cfg(target_arch = "riscv64")]
#[error("Platform specific error (riscv64): {0:?}")]
PlatformSpecific(riscv64::Error),
#[error("The memory map table extends past the end of guest memory")]
MemmapTablePastRamEnd,
#[error("Error writing memory map table to guest memory")]
@@ -85,6 +89,17 @@ pub use aarch64::{
layout::IRQ_BASE, uefi, EntryPoint, _NSIG,
};
/// Module for riscv64 related functionality.
#[cfg(target_arch = "riscv64")]
pub mod riscv64;
#[cfg(target_arch = "riscv64")]
pub use riscv64::{
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, EntryPoint, _NSIG,
};
#[cfg(target_arch = "x86_64")]
pub mod x86_64;
@@ -132,7 +147,7 @@ pub enum DeviceType {
/// Device Type: Virtio.
Virtio(u32),
/// Device Type: Serial.
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
Serial,
/// Device Type: RTC.
#[cfg(target_arch = "aarch64")]
@@ -153,7 +168,7 @@ impl fmt::Display for DeviceType {
/// Structure to describe MMIO device information
#[derive(Clone, Debug)]
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
pub struct MmioDeviceInfo {
pub addr: u64,
pub len: u64,
@@ -162,7 +177,7 @@ pub struct MmioDeviceInfo {
/// Structure to describe PCI space information
#[derive(Clone, Debug)]
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
pub struct PciSpaceInfo {
pub pci_segment_id: u16,
pub mmio_config_address: u64,
@@ -170,7 +185,7 @@ pub struct PciSpaceInfo {
pub pci_device_space_size: u64,
}
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
impl DeviceInfoForFdt for MmioDeviceInfo {
fn addr(&self) -> u64 {
self.addr

484
arch/src/riscv64/fdt.rs Normal file
View File

@@ -0,0 +1,484 @@
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
// Copyright 2020 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.
use std::collections::HashMap;
use std::ffi::CStr;
use std::fmt::Debug;
use std::sync::{Arc, Mutex};
use std::{cmp, result, str};
use byteorder::{BigEndian, ByteOrder};
use hypervisor::arch::riscv64::aia::Vaia;
use thiserror::Error;
use vm_fdt::{FdtWriter, FdtWriterResult};
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryRegion};
use super::super::{DeviceType, GuestMemoryMmap, InitramfsConfig};
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 crate::PciSpaceInfo;
const AIA_APLIC_PHANDLE: u32 = 1;
const AIA_IMSIC_PHANDLE: u32 = 2;
const CPU_INTC_BASE_PHANDLE: u32 = 3;
const CPU_BASE_PHANDLE: u32 = 256 + CPU_INTC_BASE_PHANDLE;
// Read the documentation specified when appending the root node to the FDT.
const ADDRESS_CELLS: u32 = 0x2;
const SIZE_CELLS: u32 = 0x2;
// From https://elixir.bootlin.com/linux/v6.10/source/include/dt-bindings/interrupt-controller/irq.h#L14
const _IRQ_TYPE_EDGE_RISING: u32 = 1;
const IRQ_TYPE_LEVEL_HI: u32 = 4;
const S_MODE_EXT_IRQ: u32 = 9;
/// Trait for devices to be added to the Flattened Device Tree.
pub trait DeviceInfoForFdt {
/// Returns the address where this device will be loaded.
fn addr(&self) -> u64;
/// Returns the associated interrupt for this device.
fn irq(&self) -> u32;
/// Returns the amount of memory that needs to be reserved for this device.
fn length(&self) -> u64;
}
/// Errors thrown while configuring the Flattened Device Tree for riscv64.
#[derive(Debug, Error)]
pub enum Error {
/// Failure in writing FDT in memory.
#[error("Failure in writing FDT in memory: {0}")]
WriteFdtToMemory(GuestMemoryError),
}
type Result<T> = result::Result<T, Error>;
/// Creates the flattened device tree for this riscv64 VM.
#[allow(clippy::too_many_arguments)]
pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
guest_mem: &GuestMemoryMmap,
cmdline: &str,
num_vcpu: u32,
device_info: &HashMap<(DeviceType, String), T, S>,
aia_device: &Arc<Mutex<dyn Vaia>>,
initrd: &Option<InitramfsConfig>,
pci_space_info: &[PciSpaceInfo],
) -> FdtWriterResult<Vec<u8>> {
// Allocate stuff necessary for the holding the blob.
let mut fdt = FdtWriter::new()?;
// For an explanation why these nodes were introduced in the blob take a look at
// https://github.com/devicetree-org/devicetree-specification/releases/tag/v0.4
// In chapter 3.
// Header or the root node as per above mentioned documentation.
let root_node = fdt.begin_node("")?;
fdt.property_string("compatible", "linux,dummy-virt")?;
// For info on #address-cells and size-cells resort to Table 3.1 Root Node
// Properties
fdt.property_u32("#address-cells", ADDRESS_CELLS)?;
fdt.property_u32("#size-cells", SIZE_CELLS)?;
create_cpu_nodes(&mut fdt, num_vcpu)?;
create_memory_node(&mut fdt, guest_mem)?;
create_chosen_node(&mut fdt, cmdline, initrd)?;
create_aia_node(&mut fdt, aia_device)?;
create_devices_node(&mut fdt, device_info)?;
create_pci_nodes(&mut fdt, pci_space_info)?;
// End Header node.
fdt.end_node(root_node)?;
let fdt_final = fdt.finish()?;
Ok(fdt_final)
}
pub fn write_fdt_to_memory(fdt_final: Vec<u8>, guest_mem: &GuestMemoryMmap) -> Result<()> {
// Write FDT to memory.
guest_mem
.write_slice(fdt_final.as_slice(), super::layout::FDT_START)
.map_err(Error::WriteFdtToMemory)?;
Ok(())
}
// Following are the auxiliary function for creating the different nodes that we append to our FDT.
fn create_cpu_nodes(fdt: &mut FdtWriter, num_cpus: u32) -> FdtWriterResult<()> {
// See https://elixir.bootlin.com/linux/v6.10/source/Documentation/devicetree/bindings/riscv/cpus.yaml
let cpus = fdt.begin_node("cpus")?;
// As per documentation, on RISC-V 64-bit systems value should be set to 1.
fdt.property_u32("#address-cells", 0x01)?;
fdt.property_u32("#size-cells", 0x0)?;
// TODO: Retrieve CPU frequency from cpu timer regs
let timebase_frequency: u32 = 0x989680;
fdt.property_u32("timebase-frequency", timebase_frequency)?;
for cpu_index in 0..num_cpus {
let cpu = fdt.begin_node(&format!("cpu@{:x}", cpu_index))?;
fdt.property_string("device_type", "cpu")?;
fdt.property_string("compatible", "riscv")?;
fdt.property_string("mmu-type", "sv48")?;
fdt.property_string("riscv,isa", "rv64imafdc_smaia_ssaia")?;
fdt.property_string("status", "okay")?;
fdt.property_u32("reg", cpu_index)?;
fdt.property_u32("phandle", CPU_BASE_PHANDLE + cpu_index)?;
// interrupt controller node
let intc_node = fdt.begin_node("interrupt-controller")?;
fdt.property_string("compatible", "riscv,cpu-intc")?;
fdt.property_u32("#interrupt-cells", 1u32)?;
fdt.property_null("interrupt-controller")?;
fdt.property_u32("phandle", CPU_INTC_BASE_PHANDLE + cpu_index)?;
fdt.end_node(intc_node)?;
fdt.end_node(cpu)?;
}
fdt.end_node(cpus)?;
Ok(())
}
fn create_memory_node(fdt: &mut FdtWriter, guest_mem: &GuestMemoryMmap) -> FdtWriterResult<()> {
// Note: memory regions from "GuestMemory" are sorted and non-zero sized.
let ram_regions = {
let mut ram_regions = Vec::new();
let mut current_start = guest_mem
.iter()
.next()
.map(GuestMemoryRegion::start_addr)
.expect("GuestMemory must have one memory region at least")
.raw_value();
let mut current_end = current_start;
for (start, size) in guest_mem
.iter()
.map(|m| (m.start_addr().raw_value(), m.len()))
{
if current_end == start {
// This zone is continuous with the previous one.
current_end += size;
} else {
ram_regions.push((current_start, current_end));
current_start = start;
current_end = start + size;
}
}
ram_regions.push((current_start, current_end));
ram_regions
};
let mut mem_reg_property = Vec::new();
for region in ram_regions {
let mem_size = region.1 - region.0;
mem_reg_property.push(region.0);
mem_reg_property.push(mem_size);
}
let ram_start = super::layout::RAM_START.raw_value();
let memory_node_name = format!("memory@{:x}", ram_start);
let memory_node = fdt.begin_node(&memory_node_name)?;
fdt.property_string("device_type", "memory")?;
fdt.property_array_u64("reg", &mem_reg_property)?;
fdt.end_node(memory_node)?;
Ok(())
}
fn create_chosen_node(
fdt: &mut FdtWriter,
cmdline: &str,
initrd: &Option<InitramfsConfig>,
) -> FdtWriterResult<()> {
let chosen_node = fdt.begin_node("chosen")?;
fdt.property_string("bootargs", cmdline)?;
if let Some(initrd_config) = initrd {
let initrd_start = initrd_config.address.raw_value();
let initrd_end = initrd_config.address.raw_value() + initrd_config.size as u64;
fdt.property_u64("linux,initrd-start", initrd_start)?;
fdt.property_u64("linux,initrd-end", initrd_end)?;
}
fdt.end_node(chosen_node)?;
Ok(())
}
fn create_aia_node(fdt: &mut FdtWriter, aia_device: &Arc<Mutex<dyn Vaia>>) -> FdtWriterResult<()> {
// IMSIC
if aia_device.lock().unwrap().msi_compatible() {
use super::layout::IMSIC_START;
let imsic_name = format!("imsics@{:x}", IMSIC_START.0);
let imsic_node = fdt.begin_node(&imsic_name)?;
fdt.property_string(
"compatible",
aia_device.lock().unwrap().imsic_compatibility(),
)?;
let imsic_reg_prop = aia_device.lock().unwrap().imsic_properties();
fdt.property_array_u32("reg", &imsic_reg_prop)?;
fdt.property_u32("#interrupt-cells", 0u32)?;
fdt.property_null("interrupt-controller")?;
fdt.property_null("msi-controller")?;
// TODO complete num-ids
fdt.property_u32("riscv,num-ids", 2047u32)?;
fdt.property_u32("phandle", AIA_IMSIC_PHANDLE)?;
let mut irq_cells = Vec::new();
let num_cpus = aia_device.lock().unwrap().vcpu_count();
for i in 0..num_cpus {
irq_cells.push(CPU_INTC_BASE_PHANDLE + i);
irq_cells.push(S_MODE_EXT_IRQ);
}
fdt.property_array_u32("interrupts-extended", &irq_cells)?;
fdt.end_node(imsic_node)?;
}
// APLIC
use super::layout::APLIC_START;
let aplic_name = format!("aplic@{:x}", APLIC_START.0);
let aplic_node = fdt.begin_node(&aplic_name)?;
fdt.property_string(
"compatible",
aia_device.lock().unwrap().aplic_compatibility(),
)?;
let reg_cells = aia_device.lock().unwrap().aplic_properties();
fdt.property_array_u32("reg", &reg_cells)?;
fdt.property_u32("#interrupt-cells", 2u32)?;
fdt.property_null("interrupt-controller")?;
// TODO complete num-srcs
fdt.property_u32("riscv,num-sources", 96u32)?;
fdt.property_u32("phandle", AIA_APLIC_PHANDLE)?;
fdt.property_u32("msi-parent", AIA_IMSIC_PHANDLE)?;
fdt.end_node(aplic_node)?;
Ok(())
}
fn create_serial_node<T: DeviceInfoForFdt + Clone + Debug>(
fdt: &mut FdtWriter,
dev_info: &T,
) -> FdtWriterResult<()> {
let serial_reg_prop = [dev_info.addr(), dev_info.length()];
let irq = [dev_info.irq() - IRQ_BASE, IRQ_TYPE_LEVEL_HI];
let serial_node = fdt.begin_node(&format!("serial@{:x}", dev_info.addr()))?;
fdt.property_string("compatible", "ns16550a")?;
fdt.property_array_u64("reg", &serial_reg_prop)?;
fdt.property_u32("clock-frequency", 3686400)?;
fdt.property_u32("interrupt-parent", AIA_APLIC_PHANDLE)?;
fdt.property_array_u32("interrupts", &irq)?;
fdt.end_node(serial_node)?;
Ok(())
}
fn create_devices_node<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
fdt: &mut FdtWriter,
dev_info: &HashMap<(DeviceType, String), T, S>,
) -> FdtWriterResult<()> {
for ((device_type, _device_id), info) in dev_info {
match device_type {
DeviceType::Serial => create_serial_node(fdt, info)?,
DeviceType::Virtio(_) => unreachable!(),
}
}
Ok(())
}
fn create_pci_nodes(fdt: &mut FdtWriter, pci_device_info: &[PciSpaceInfo]) -> FdtWriterResult<()> {
// Add node for PCIe controller.
// See Documentation/devicetree/bindings/pci/host-generic-pci.txt in the kernel
// and https://elinux.org/Device_Tree_Usage.
// In multiple PCI segments setup, each PCI segment needs a PCI node.
for pci_device_info_elem in pci_device_info.iter() {
// EDK2 requires the PCIe high space above 4G address.
// The actual space in CLH follows the RAM. If the RAM space is small, the PCIe high space
// could fall below 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,
)
};
// 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 =
MEM_32BIT_DEVICES_SIZE / ((1 << 12) * pci_device_info.len() as u64) * (1 << 12);
let pci_device_base_32bit: u64 = MEM_32BIT_DEVICES_START.0
+ pci_device_size_32bit * pci_device_info_elem.pci_segment_id as u64;
let ranges = [
// io addresses. Since AArch64 will not use IO address,
// we can set the same IO address range for every segment.
0x1000000,
0_u32,
0_u32,
(MEM_PCI_IO_START.0 >> 32) as u32,
MEM_PCI_IO_START.0 as u32,
(MEM_PCI_IO_SIZE >> 32) as u32,
MEM_PCI_IO_SIZE as u32,
// mmio addresses
0x2000000, // (ss = 10: 32-bit memory space)
(pci_device_base_32bit >> 32) as u32, // PCI address
pci_device_base_32bit as u32,
(pci_device_base_32bit >> 32) as u32, // CPU address
pci_device_base_32bit as u32,
(pci_device_size_32bit >> 32) as u32, // size
pci_device_size_32bit as u32,
// device addresses
0x3000000, // (ss = 11: 64-bit memory space)
(pci_device_base_64bit >> 32) as u32, // PCI address
pci_device_base_64bit as u32,
(pci_device_base_64bit >> 32) as u32, // CPU address
pci_device_base_64bit as u32,
(pci_device_size_64bit >> 32) as u32, // size
pci_device_size_64bit as u32,
];
let bus_range = [0, 0]; // Only bus 0
let reg = [
pci_device_info_elem.mmio_config_address,
PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
];
// See kernel document Documentation/devicetree/bindings/pci/pci-msi.txt
let msi_map = [
// rid-base: A single cell describing the first RID matched by the entry.
0x0,
// msi-controller: A single phandle to an MSI controller.
AIA_IMSIC_PHANDLE,
// msi-base: An msi-specifier describing the msi-specifier produced for the
// first RID matched by the entry.
(pci_device_info_elem.pci_segment_id as u32) << 8,
// length: A single cell describing how many consecutive RIDs are matched
// following the rid-base.
0x100,
];
let pci_node_name = format!("pci@{:x}", pci_device_info_elem.mmio_config_address);
let pci_node = fdt.begin_node(&pci_node_name)?;
fdt.property_string("compatible", "pci-host-ecam-generic")?;
fdt.property_string("device_type", "pci")?;
fdt.property_array_u32("ranges", &ranges)?;
fdt.property_array_u32("bus-range", &bus_range)?;
fdt.property_u32(
"linux,pci-domain",
pci_device_info_elem.pci_segment_id as u32,
)?;
fdt.property_u32("#address-cells", 3)?;
fdt.property_u32("#size-cells", 2)?;
fdt.property_array_u64("reg", &reg)?;
fdt.property_u32("#interrupt-cells", 1)?;
fdt.property_null("interrupt-map")?;
fdt.property_null("interrupt-map-mask")?;
fdt.property_null("dma-coherent")?;
fdt.property_array_u32("msi-map", &msi_map)?;
fdt.property_u32("msi-parent", AIA_IMSIC_PHANDLE)?;
fdt.end_node(pci_node)?;
}
Ok(())
}
// Parse the DTB binary and print for debugging
pub fn print_fdt(dtb: &[u8]) {
match fdt_parser::Fdt::new(dtb) {
Ok(fdt) => {
if let Some(root) = fdt.find_node("/") {
debug!("Printing the FDT:");
print_node(root, 0);
} else {
debug!("Failed to find root node in FDT for debugging.");
}
}
Err(_) => debug!("Failed to parse FDT for debugging."),
}
}
fn print_node(node: fdt_parser::node::FdtNode<'_, '_>, n_spaces: usize) {
debug!("{:indent$}{}/", "", node.name, indent = n_spaces);
for property in node.properties() {
let name = property.name;
// If the property is 'compatible', its value requires special handling.
// The u8 array could contain multiple null-terminated strings.
// We copy the original array and simply replace all 'null' characters with spaces.
let value = if name == "compatible" {
let mut compatible = vec![0u8; 256];
let handled_value = property
.value
.iter()
.map(|&c| if c == 0 { b' ' } else { c })
.collect::<Vec<_>>();
let len = cmp::min(255, handled_value.len());
compatible[..len].copy_from_slice(&handled_value[..len]);
compatible[..(len + 1)].to_vec()
} else {
property.value.to_vec()
};
let value = &value;
// Now the value can be either:
// - A null-terminated C string, or
// - Binary data
// We follow a very simple logic to present the value:
// - At first, try to convert it to CStr and print,
// - If failed, print it as u32 array.
let value_result = match CStr::from_bytes_with_nul(value) {
Ok(value_cstr) => match value_cstr.to_str() {
Ok(value_str) => Some(value_str),
Err(_e) => None,
},
Err(_e) => None,
};
if let Some(value_str) = value_result {
debug!(
"{:indent$}{} : {:#?}",
"",
name,
value_str,
indent = (n_spaces + 2)
);
} else {
let mut array = Vec::with_capacity(256);
array.resize(value.len() / 4, 0u32);
BigEndian::read_u32_into(value, &mut array);
debug!(
"{:indent$}{} : {:X?}",
"",
name,
array,
indent = (n_spaces + 2)
);
};
}
// Print children nodes if there is any
for child in node.children() {
print_node(child, n_spaces + 2);
}
}

105
arch/src/riscv64/layout.rs Normal file
View File

@@ -0,0 +1,105 @@
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
// Copyright 2020 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
//
// Memory layout of RISC-V 64-bit guest:
//
// Physical +---------------------------------------------------------------+
// address | |
// end | |
// ~ ~ ~ ~
// | |
// | Highmem PCI MMIO space |
// | |
// RAM end +---------------------------------------------------------------+
// (dynamic, | |
// including | |
// hotplug ~ ~ ~ ~
// memory) | |
// | DRAM |
// | |
// | |
// | |
// | |
// 1 GB +---------------------------------------------------------------+
// | |
// | PCI MMCONFIG space |
// | |
// 768 MB +---------------------------------------------------------------+
// | |
// | |
// | PCI MMIO space |
// | |
// 256 MB +---------------------------------------------------------------|
// | |
// | Legacy devices space |
// | |
// 128 MB +---------------------------------------------------------------|
// | |
// | IMSICs |
// | |
// 64 MB +---------------------------------------------------------------+
// | |
// | APLICs |
// | |
// 0 GB +---------------------------------------------------------------+
//
//
use vm_memory::GuestAddress;
/// AIA related devices
/// See https://elixir.bootlin.com/linux/v6.10/source/arch/riscv/include/uapi/asm/kvm.h
/// 0x0 ~ 0x0400_0000 (64 MiB) resides APLICs
pub const APLIC_START: GuestAddress = GuestAddress(0);
pub const APLIC_SIZE: u64 = 0x4000;
/// 0x0400_0000 ~ 0x0800_0000 (64 MiB) resides IMSICs
pub const IMSIC_START: GuestAddress = GuestAddress(0x0400_0000);
pub const IMSIC_SIZE: u64 = 0x1000;
/// Below this address will reside the AIA, above this address will reside the MMIO devices.
const MAPPED_IO_START: GuestAddress = GuestAddress(0x0800_0000);
/// Space 0x0800_0000 ~ 0x1000_0000 is reserved for legacy devices.
pub const LEGACY_SERIAL_MAPPED_IO_START: GuestAddress = MAPPED_IO_START;
/// Space 0x0905_0000 ~ 0x0906_0000 is reserved for pcie io address
pub const MEM_PCI_IO_START: GuestAddress = GuestAddress(0x0905_0000);
pub const MEM_PCI_IO_SIZE: u64 = 0x1_0000;
/// Starting from 0x1000_0000 (256MiB) to 0x3000_0000 (768MiB) is used for PCIE MMIO
pub const MEM_32BIT_DEVICES_START: GuestAddress = GuestAddress(0x1000_0000);
pub const MEM_32BIT_DEVICES_SIZE: u64 = 0x2000_0000;
/// PCI MMCONFIG space (start: after the device space at 768MiB, length: 256MiB)
pub const PCI_MMCONFIG_START: GuestAddress = GuestAddress(0x3000_0000);
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);
/// Kernel command line maximum size on RISC-V.
/// See https://elixir.bootlin.com/linux/v6.10/source/arch/riscv/include/uapi/asm/setup.h
pub const CMDLINE_MAX_SIZE: usize = 1024;
/// FDT is at the beginning of RAM.
pub const FDT_START: GuestAddress = RAM_START;
pub const FDT_MAX_SIZE: u64 = 0x1_0000;
/// Kernel start after FDT
pub const KERNEL_START: GuestAddress = GuestAddress(RAM_START.0 + FDT_MAX_SIZE);
/// Pci high memory base
pub const PCI_HIGH_BASE: GuestAddress = GuestAddress(0x2_0000_0000);
/// First usable interrupt on riscv64
pub const IRQ_BASE: u32 = 0;
// As per https://elixir.bootlin.com/linux/v6.10/source/arch/riscv/include/asm/kvm_host.h#L31
/// Number of supported interrupts
pub const IRQ_NUM: u32 = 1023;

173
arch/src/riscv64/mod.rs Normal file
View File

@@ -0,0 +1,173 @@
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
// Copyright 2020 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
/// Module for the flattened device tree.
pub mod fdt;
/// Layout for this riscv64 system.
pub mod layout;
use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::{Arc, Mutex};
use hypervisor::arch::riscv64::aia::Vaia;
use log::{log_enabled, Level};
use thiserror::Error;
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic};
pub use self::fdt::DeviceInfoForFdt;
use crate::{DeviceType, GuestMemoryMmap, PciSpaceInfo, RegionType};
pub const _NSIG: i32 = 65;
/// Errors thrown while configuring riscv64 system.
#[derive(Debug, Error)]
pub enum Error {
/// Failed to create a FDT.
#[error("Failed to create a FDT")]
SetupFdt,
/// Failed to write FDT to memory.
#[error("Failed to write FDT to memory: {0}")]
WriteFdtToMemory(fdt::Error),
/// Failed to create a AIA.
#[error("Failed to create a AIA")]
SetupAia,
/// Failed to compute the initramfs address.
#[error("Failed to compute the initramfs address")]
InitramfsAddress,
/// Error configuring the general purpose registers
#[error("Error configuring the general purpose registers: {0}")]
RegsConfiguration(hypervisor::HypervisorCpuError),
}
impl From<Error> for super::Error {
fn from(e: Error) -> super::Error {
super::Error::PlatformSpecific(e)
}
}
#[derive(Debug, Copy, Clone)]
/// Specifies the entry point address where the guest must start
/// executing code.
pub struct EntryPoint {
/// Address in guest memory where the guest must start execution
pub entry_addr: GuestAddress,
}
/// Configure the specified VCPU, and return its MPIDR.
pub fn configure_vcpu(
vcpu: &Arc<dyn hypervisor::Vcpu>,
id: u8,
boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>,
) -> super::Result<()> {
if let Some((kernel_entry_point, _guest_memory)) = boot_setup {
vcpu.setup_regs(
id,
kernel_entry_point.entry_addr.raw_value(),
layout::FDT_START.raw_value(),
)
.map_err(Error::RegsConfiguration)?;
}
Ok(())
}
pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> {
vec![
// 0 MiB ~ 256 MiB: AIA and legacy devices
(
GuestAddress(0),
layout::MEM_32BIT_DEVICES_START.0 as usize,
RegionType::Reserved,
),
// 256 MiB ~ 768 MiB: MMIO space
(
layout::MEM_32BIT_DEVICES_START,
layout::MEM_32BIT_DEVICES_SIZE as usize,
RegionType::SubRegion,
),
// 768 MiB ~ 1 GiB: reserved. The leading 256M for PCIe MMCONFIG space
(
layout::PCI_MMCONFIG_START,
layout::PCI_MMCONFIG_SIZE as usize,
RegionType::Reserved,
),
// 1GiB ~ inf: RAM
(layout::RAM_START, usize::MAX, RegionType::Ram),
]
}
/// Configures the system and should be called once per vm before starting vcpu threads.
#[allow(clippy::too_many_arguments)]
pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
guest_mem: &GuestMemoryMmap,
cmdline: &str,
num_vcpu: u32,
device_info: &HashMap<(DeviceType, String), T, S>,
initrd: &Option<super::InitramfsConfig>,
pci_space_info: &[PciSpaceInfo],
aia_device: &Arc<Mutex<dyn Vaia>>,
) -> super::Result<()> {
let fdt_final = fdt::create_fdt(
guest_mem,
cmdline,
num_vcpu,
device_info,
aia_device,
initrd,
pci_space_info,
)
.map_err(|_| Error::SetupFdt)?;
if log_enabled!(Level::Debug) {
fdt::print_fdt(&fdt_final);
}
fdt::write_fdt_to_memory(fdt_final, guest_mem).map_err(Error::WriteFdtToMemory)?;
Ok(())
}
/// Returns the memory address where the initramfs could be loaded.
pub fn initramfs_load_addr(
guest_mem: &GuestMemoryMmap,
initramfs_size: usize,
) -> super::Result<u64> {
let round_to_pagesize = |size| (size + (super::PAGE_SIZE - 1)) & !(super::PAGE_SIZE - 1);
match guest_mem
.last_addr()
.checked_sub(round_to_pagesize(initramfs_size) as u64 - 1)
{
Some(offset) => {
if guest_mem.address_in_range(offset) {
Ok(offset.raw_value())
} else {
Err(super::Error::PlatformSpecific(Error::InitramfsAddress))
}
}
None => Err(super::Error::PlatformSpecific(Error::InitramfsAddress)),
}
}
pub fn get_host_cpu_phys_bits(_hypervisor: &Arc<dyn hypervisor::Hypervisor>) -> u8 {
40
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arch_memory_regions_dram() {
let regions = arch_memory_regions();
assert_eq!(4, regions.len());
assert_eq!(layout::RAM_START, regions[3].0);
assert_eq!(RegionType::Ram, regions[3].2);
}
}

View File

@@ -601,7 +601,7 @@ pub fn generate_common_cpuid(
config: &CpuidConfig,
) -> super::Result<Vec<CpuIdEntry>> {
// SAFETY: cpuid called with valid leaves
if unsafe { x86_64::__cpuid(1) }.ecx & 1 << HYPERVISOR_ECX_BIT == 1 << HYPERVISOR_ECX_BIT {
if unsafe { x86_64::__cpuid(1) }.ecx & (1 << HYPERVISOR_ECX_BIT) == 1 << HYPERVISOR_ECX_BIT {
// SAFETY: cpuid called with valid leaves
let hypervisor_cpuid = unsafe { x86_64::__cpuid(0x4000_0000) };
@@ -689,7 +689,7 @@ pub fn generate_common_cpuid(
// Clear AMX related bits if the AMX feature is not enabled
0x7 => {
if !config.amx && entry.index == 0 {
entry.edx &= !(1 << AMX_BF16 | 1 << AMX_TILE | 1 << AMX_INT8)
entry.edx &= !((1 << AMX_BF16) | (1 << AMX_TILE) | (1 << AMX_INT8))
}
}
0xd =>
@@ -747,12 +747,12 @@ pub fn generate_common_cpuid(
// These features are not supported by TDX
#[cfg(feature = "tdx")]
if config.tdx {
entry.eax &= !(1 << KVM_FEATURE_CLOCKSOURCE_BIT
| 1 << KVM_FEATURE_CLOCKSOURCE2_BIT
| 1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT
| 1 << KVM_FEATURE_ASYNC_PF_BIT
| 1 << KVM_FEATURE_ASYNC_PF_VMEXIT_BIT
| 1 << KVM_FEATURE_STEAL_TIME_BIT)
entry.eax &= !((1 << KVM_FEATURE_CLOCKSOURCE_BIT)
| (1 << KVM_FEATURE_CLOCKSOURCE2_BIT)
| (1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT)
| (1 << KVM_FEATURE_ASYNC_PF_BIT)
| (1 << KVM_FEATURE_ASYNC_PF_VMEXIT_BIT)
| (1 << KVM_FEATURE_STEAL_TIME_BIT))
}
}
_ => {}
@@ -801,10 +801,10 @@ pub fn generate_common_cpuid(
});
cpuid.push(CpuIdEntry {
function: 0x4000_0003,
eax: 1 << 1 // AccessPartitionReferenceCounter
| 1 << 2 // AccessSynicRegs
| 1 << 3 // AccessSyntheticTimerRegs
| 1 << 9, // AccessPartitionReferenceTsc
eax: (1 << 1) // AccessPartitionReferenceCounter
| (1 << 2) // AccessSynicRegs
| (1 << 3) // AccessSyntheticTimerRegs
| (1 << 9), // AccessPartitionReferenceTsc
edx: 1 << 3, // CPU dynamic partitioning
..Default::default()
});
@@ -887,6 +887,10 @@ pub fn configure_vcpu(
}
}
for c in &cpuid {
info!("{}", c);
}
vcpu.set_cpuid2(&cpuid)
.map_err(|e| Error::SetSupportedCpusFailed(e.into()))?;
@@ -1380,7 +1384,7 @@ fn update_cpuid_topology(
let mut cpu_ebx = CpuidPatch::get_cpuid_reg(cpuid, 0x1, None, CpuidReg::EBX).unwrap_or(0);
cpu_ebx |= ((dies_per_package as u32) * (cores_per_die as u32) * (threads_per_core as u32))
& 0xff << 16;
& (0xff << 16);
CpuidPatch::set_cpuid_reg(cpuid, 0x1, None, CpuidReg::EBX, cpu_ebx);
let mut cpu_edx = CpuidPatch::get_cpuid_reg(cpuid, 0x1, None, CpuidReg::EDX).unwrap_or(0);

View File

@@ -35,7 +35,7 @@ const TABLE_FOOTER_GUID: &str = "96b582de-1fb2-45f7-baea-a366c55a082d";
const TDVF_METADATA_OFFSET_GUID: &str = "e47a6535-984a-4798-865e-4685a7bf8ec2";
// TDVF_DESCRIPTOR
#[repr(packed)]
#[repr(C, packed)]
#[derive(Default)]
pub struct TdvfDescriptor {
signature: [u8; 4],
@@ -45,7 +45,7 @@ pub struct TdvfDescriptor {
}
// TDVF_SECTION
#[repr(packed)]
#[repr(C, packed)]
#[derive(Clone, Copy, Default, Debug)]
pub struct TdvfSection {
pub data_offset: u32,
@@ -209,7 +209,7 @@ enum HobType {
EndOfHobList = 0xffff,
}
#[repr(C)]
#[repr(C, packed)]
#[derive(Copy, Clone, Default, Debug)]
struct HobHeader {
r#type: HobType,
@@ -217,7 +217,7 @@ struct HobHeader {
reserved: u32,
}
#[repr(C)]
#[repr(C, packed)]
#[derive(Copy, Clone, Default, Debug)]
struct HobHandoffInfoTable {
header: HobHeader,
@@ -230,7 +230,7 @@ struct HobHandoffInfoTable {
efi_end_of_hob_list: u64,
}
#[repr(C)]
#[repr(C, packed)]
#[derive(Copy, Clone, Default, Debug)]
struct EfiGuid {
data1: u32,
@@ -239,7 +239,7 @@ struct EfiGuid {
data4: [u8; 8],
}
#[repr(C)]
#[repr(C, packed)]
#[derive(Copy, Clone, Default, Debug)]
struct HobResourceDescriptor {
header: HobHeader,
@@ -250,7 +250,7 @@ struct HobResourceDescriptor {
resource_length: u64,
}
#[repr(C)]
#[repr(C, packed)]
#[derive(Copy, Clone, Default, Debug)]
struct HobGuidType {
header: HobHeader,
@@ -266,14 +266,14 @@ pub enum PayloadImageType {
RawVmLinux,
}
#[repr(C)]
#[repr(C, packed)]
#[derive(Copy, Clone, Default, Debug)]
pub struct PayloadInfo {
pub image_type: PayloadImageType,
pub entry_point: u64,
}
#[repr(C)]
#[repr(C, packed)]
#[derive(Copy, Clone, Default, Debug)]
struct TdPayload {
guid_type: HobGuidType,
@@ -299,7 +299,7 @@ pub struct TdHob {
}
fn align_hob(v: u64) -> u64 {
(v + 7) / 8 * 8
v.div_ceil(8) * 8
}
impl TdHob {

View File

@@ -12,13 +12,13 @@ io_uring = ["dep:io-uring"]
byteorder = "1.5.0"
crc-any = "2.4.4"
io-uring = { version = "0.6.4", optional = true }
libc = "0.2.158"
libc = "0.2.167"
log = "0.4.22"
remain = "0.2.14"
serde = { version = "1.0.208", features = ["derive"] }
smallvec = "1.13.2"
thiserror = "1.0.62"
uuid = { version = "1.8.0", features = ["v4"] }
thiserror = "2.0.6"
uuid = { version = "1.12.1", features = ["v4"] }
virtio-bindings = { workspace = true, features = ["virtio-v5_0_0"] }
virtio-queue = { workspace = true }
vm-memory = { workspace = true, features = [

View File

@@ -59,9 +59,7 @@ use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::{aio, ioctl_io_nr, ioctl_ioc_nr};
use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult};
use crate::fixed_vhd::FixedVhd;
use crate::qcow::{QcowFile, RawFile};
use crate::vhdx::{Vhdx, VhdxError};
use crate::vhdx::VhdxError;
const SECTOR_SHIFT: u8 = 9;
pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
@@ -220,6 +218,8 @@ fn sector<B: Bitmap + 'static>(
mem.read_obj(addr).map_err(Error::GuestMemory)
}
const DEFAULT_DESCRIPTOR_VEC_SIZE: usize = 32;
#[derive(Debug)]
pub struct AlignedOperation {
origin_ptr: u64,
@@ -232,10 +232,10 @@ pub struct AlignedOperation {
pub struct Request {
pub request_type: RequestType,
pub sector: u64,
pub data_descriptors: SmallVec<[(GuestAddress, u32); 1]>,
pub data_descriptors: SmallVec<[(GuestAddress, u32); DEFAULT_DESCRIPTOR_VEC_SIZE]>,
pub status_addr: GuestAddress,
pub writeback: bool,
pub aligned_operations: SmallVec<[AlignedOperation; 1]>,
pub aligned_operations: SmallVec<[AlignedOperation; DEFAULT_DESCRIPTOR_VEC_SIZE]>,
pub start: Instant,
}
@@ -263,10 +263,10 @@ impl Request {
let mut req = Request {
request_type: request_type(desc_chain.memory(), hdr_desc_addr)?,
sector: sector(desc_chain.memory(), hdr_desc_addr)?,
data_descriptors: SmallVec::with_capacity(1),
data_descriptors: SmallVec::with_capacity(DEFAULT_DESCRIPTOR_VEC_SIZE),
status_addr: GuestAddress(0),
writeback: true,
aligned_operations: SmallVec::with_capacity(1),
aligned_operations: SmallVec::with_capacity(DEFAULT_DESCRIPTOR_VEC_SIZE),
start: Instant::now(),
};
@@ -398,7 +398,7 @@ impl Request {
let request_type = self.request_type;
let offset = (sector << SECTOR_SHIFT) as libc::off_t;
let mut iovecs: SmallVec<[libc::iovec; 1]> =
let mut iovecs: SmallVec<[libc::iovec; DEFAULT_DESCRIPTOR_VEC_SIZE]> =
SmallVec::with_capacity(self.data_descriptors.len());
for (data_addr, data_len) in &self.data_descriptors {
if *data_len == 0 {
@@ -653,7 +653,8 @@ where
completion_list: &mut VecDeque<(u64, i32)>,
) -> AsyncIoResult<()> {
// Convert libc::iovec into IoSliceMut
let mut slices: SmallVec<[IoSliceMut; 1]> = SmallVec::with_capacity(iovecs.len());
let mut slices: SmallVec<[IoSliceMut; DEFAULT_DESCRIPTOR_VEC_SIZE]> =
SmallVec::with_capacity(iovecs.len());
for iovec in iovecs.iter() {
// SAFETY: on Linux IoSliceMut wraps around libc::iovec
slices.push(IoSliceMut::new(unsafe {
@@ -668,9 +669,11 @@ where
file.seek(SeekFrom::Start(offset as u64))
.map_err(AsyncIoError::ReadVectored)?;
// Read vectored
file.read_vectored(slices.as_mut_slice())
.map_err(AsyncIoError::ReadVectored)?
let mut r = 0;
for b in slices.iter_mut() {
r += file.read(b).map_err(AsyncIoError::ReadVectored)?;
}
r
};
completion_list.push_back((user_data, result as i32));
@@ -688,7 +691,8 @@ where
completion_list: &mut VecDeque<(u64, i32)>,
) -> AsyncIoResult<()> {
// Convert libc::iovec into IoSlice
let mut slices: SmallVec<[IoSlice; 1]> = SmallVec::with_capacity(iovecs.len());
let mut slices: SmallVec<[IoSlice; DEFAULT_DESCRIPTOR_VEC_SIZE]> =
SmallVec::with_capacity(iovecs.len());
for iovec in iovecs.iter() {
// SAFETY: on Linux IoSlice wraps around libc::iovec
slices.push(IoSlice::new(unsafe {
@@ -703,9 +707,11 @@ where
file.seek(SeekFrom::Start(offset as u64))
.map_err(AsyncIoError::WriteVectored)?;
// Write vectored
file.write_vectored(slices.as_slice())
.map_err(AsyncIoError::WriteVectored)?
let mut r = 0;
for b in slices.iter() {
r += file.write(b).map_err(AsyncIoError::WriteVectored)?;
}
r
};
completion_list.push_back((user_data, result as i32));
@@ -789,25 +795,6 @@ pub trait BlockBackend: Read + Write + Seek + Send + Debug {
fn size(&self) -> Result<u64, Error>;
}
/// Inspect the image file type and create an appropriate disk file to match it.
pub fn create_disk_file(mut file: File, direct_io: bool) -> Result<Box<dyn BlockBackend>, Error> {
let image_type = detect_image_type(&mut file).map_err(Error::DetectImageType)?;
Ok(match image_type {
ImageType::Qcow2 => {
Box::new(QcowFile::from(RawFile::new(file, direct_io)).map_err(Error::QcowError)?)
as Box<dyn BlockBackend>
}
ImageType::FixedVhd => {
Box::new(FixedVhd::new(file).map_err(Error::FixedVhdError)?) as Box<dyn BlockBackend>
}
ImageType::Vhdx => {
Box::new(Vhdx::new(file).map_err(Error::VhdxError)?) as Box<dyn BlockBackend>
}
ImageType::Raw => Box::new(RawFile::new(file, direct_io)) as Box<dyn BlockBackend>,
})
}
#[derive(Debug)]
pub struct DiskTopology {
pub logical_block_size: u64,

View File

@@ -45,7 +45,7 @@ pub enum Error {
CompressedBlocksNotSupported,
#[error("Failed to evict cache: {0}")]
EvictingCache(io::Error),
#[error("File larger than max of {}: {0}", MAX_QCOW_FILE_SIZE)]
#[error("File larger than max of {MAX_QCOW_FILE_SIZE}: {0}")]
FileTooBig(u64),
#[error("Failed to get file size: {0}")]
GettingFileSize(io::Error),
@@ -502,7 +502,7 @@ impl QcowFile {
if refcount_bits != 16 {
return Err(Error::UnsupportedRefcountOrder);
}
let refcount_bytes = (refcount_bits + 7) / 8;
let refcount_bytes = refcount_bits.div_ceil(8);
// Need at least one refcount cluster
if header.refcount_table_clusters == 0 {

View File

@@ -3,10 +3,10 @@
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use std::fs::File;
use std::io::{Seek, SeekFrom};
use std::io::{Error, Seek, SeekFrom};
use std::os::unix::io::{AsRawFd, RawFd};
use io_uring::{opcode, squeue, types, IoUring};
use io_uring::{opcode, types, IoUring};
use vmm_sys_util::eventfd::EventFd;
use crate::async_io::{
@@ -86,14 +86,14 @@ impl AsyncIo for RawFileAsync {
// SAFETY: we know the file descriptor is valid and we
// relied on vm-memory to provide the buffer address.
let _ = unsafe {
unsafe {
sq.push(
&opcode::Readv::new(types::Fd(self.fd), iovecs.as_ptr(), iovecs.len() as u32)
.offset(offset.try_into().unwrap())
.build()
.flags(squeue::Flags::ASYNC)
.user_data(user_data),
)
.map_err(|_| AsyncIoError::ReadVectored(Error::other("Submission queue is full")))?
};
// Update the submission queue and submit new operations to the
@@ -114,14 +114,14 @@ impl AsyncIo for RawFileAsync {
// SAFETY: we know the file descriptor is valid and we
// relied on vm-memory to provide the buffer address.
let _ = unsafe {
unsafe {
sq.push(
&opcode::Writev::new(types::Fd(self.fd), iovecs.as_ptr(), iovecs.len() as u32)
.offset(offset.try_into().unwrap())
.build()
.flags(squeue::Flags::ASYNC)
.user_data(user_data),
)
.map_err(|_| AsyncIoError::WriteVectored(Error::other("Submission queue is full")))?
};
// Update the submission queue and submit new operations to the
@@ -137,13 +137,13 @@ impl AsyncIo for RawFileAsync {
let (submitter, mut sq, _) = self.io_uring.split();
// SAFETY: we know the file descriptor is valid.
let _ = unsafe {
unsafe {
sq.push(
&opcode::Fsync::new(types::Fd(self.fd))
.build()
.flags(squeue::Flags::ASYNC)
.user_data(user_data),
)
.map_err(|_| AsyncIoError::Fsync(Error::other("Submission queue is full")))?
};
// Update the submission queue and submit new operations to the

View File

@@ -102,7 +102,7 @@ impl Read for Vhdx {
let sector_count = (buf.len() as u64).div_ceil(self.disk_spec.logical_sector_size as u64);
let sector_index = self.current_offset / self.disk_spec.logical_sector_size as u64;
vhdx_io::read(
let result = vhdx_io::read(
&mut self.file,
buf,
&self.disk_spec,
@@ -117,7 +117,11 @@ impl Read for Vhdx {
"Failed reading {sector_count} sectors from VHDx at index {sector_index}: {e}"
),
)
})
})?;
self.current_offset = self.current_offset.checked_add(result as u64).unwrap();
Ok(result)
}
}
@@ -142,7 +146,7 @@ impl Write for Vhdx {
})?;
}
vhdx_io::write(
let result = vhdx_io::write(
&mut self.file,
buf,
&mut self.disk_spec,
@@ -158,7 +162,11 @@ impl Write for Vhdx {
"Failed writing {sector_count} sectors on VHDx at index {sector_index}: {e}"
),
)
})
})?;
self.current_offset = self.current_offset.checked_add(result as u64).unwrap();
Ok(result)
}
}

View File

@@ -110,7 +110,7 @@ impl FileTypeIdentifier {
}
}
#[repr(packed)]
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
pub struct Header {
pub signature: u32,
@@ -142,7 +142,7 @@ impl Header {
return Err(VhdxHeaderError::InvalidHeaderSign);
}
let new_checksum = calculate_checksum(&mut buffer, size_of::<u32>())?;
let new_checksum = calculate_checksum(&mut buffer, size_of::<u32>());
if header.checksum != new_checksum {
return Err(VhdxHeaderError::InvalidChecksum(String::from("Header")));
}
@@ -151,7 +151,7 @@ impl Header {
}
/// Converts the header structure into a buffer
fn get_header_as_buffer(&self, buffer: &mut [u8; HEADER_SIZE as usize]) {
fn write_to_buffer(&self, buffer: &mut [u8; HEADER_SIZE as usize]) {
// SAFETY: self is a valid header.
let reference = unsafe {
std::slice::from_raw_parts(self as *const Header as *const u8, HEADER_SIZE as usize)
@@ -160,7 +160,7 @@ impl Header {
}
/// Creates and returns new updated header from the provided current header
pub fn update_header(
fn update_header(
f: &mut File,
current_header: &Header,
change_data_guid: bool,
@@ -191,11 +191,9 @@ impl Header {
log_offset: current_header.log_offset,
};
new_header.get_header_as_buffer(&mut buffer);
let mut crc = crc_any::CRC::crc32c();
crc.digest(&buffer);
new_header.checksum = crc.get_crc() as u32;
new_header.get_header_as_buffer(&mut buffer);
new_header.write_to_buffer(&mut buffer);
new_header.checksum = calculate_checksum(&mut buffer, size_of::<u32>());
new_header.write_to_buffer(&mut buffer);
f.seek(SeekFrom::Start(start))
.map_err(VhdxHeaderError::SeekHeader)?;
@@ -205,7 +203,7 @@ impl Header {
}
}
#[repr(packed)]
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
struct RegionTableHeader {
pub signature: u32,
@@ -231,7 +229,7 @@ impl RegionTableHeader {
return Err(VhdxHeaderError::InvalidRegionSign);
}
let new_checksum = calculate_checksum(&mut buffer, size_of::<u32>())?;
let new_checksum = calculate_checksum(&mut buffer, size_of::<u32>());
if region_table_header.checksum != new_checksum {
return Err(VhdxHeaderError::InvalidChecksum(String::from("Region")));
}
@@ -330,7 +328,7 @@ impl RegionInfo {
}
}
#[repr(packed)]
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
pub struct RegionTableEntry {
pub guid: Uuid,
@@ -371,22 +369,10 @@ pub struct VhdxHeader {
impl VhdxHeader {
/// Creates a VhdxHeader from a reference to a file
pub fn new(f: &mut File) -> Result<VhdxHeader> {
let _file_type_identifier: FileTypeIdentifier = FileTypeIdentifier::new(f)?;
let header_1 = Header::new(f, HEADER_1_START);
let header_2 = Header::new(f, HEADER_2_START);
let mut file_write_guid: u128 = 0;
let metadata = f.metadata().map_err(VhdxHeaderError::ReadMetadata)?;
if !metadata.permissions().readonly() {
file_write_guid = Uuid::new_v4().as_u128();
}
let (header_1, header_2) =
VhdxHeader::update_headers(f, header_1, header_2, file_write_guid)?;
Ok(VhdxHeader {
_file_type_identifier,
header_1,
header_2,
_file_type_identifier: FileTypeIdentifier::new(f)?,
header_1: Header::new(f, HEADER_1_START)?,
header_2: Header::new(f, HEADER_2_START)?,
region_table_1: RegionTableHeader::new(f, REGION_TABLE_1_START)?,
_region_table_2: RegionTableHeader::new(f, REGION_TABLE_2_START)?,
})
@@ -468,13 +454,11 @@ impl VhdxHeader {
/// Therefore, before calculating, the existing checksum is retrieved and the
/// corresponding field is made zero. After the calculation, the existing checksum
/// is put back to the buffer.
pub fn calculate_checksum(buffer: &mut [u8], csum_offset: usize) -> Result<u32> {
// Read the checksum into a mutable slice
let csum_buf = &mut buffer[csum_offset..csum_offset + 4];
// Convert the checksum chunk into a u32 integer
let orig_csum = LittleEndian::read_u32(csum_buf);
fn calculate_checksum(buffer: &mut [u8], csum_offset: usize) -> u32 {
// Read the original checksum from the buffer
let orig_csum = LittleEndian::read_u32(&buffer[csum_offset..csum_offset + 4]);
// Zero the checksum in the buffer
LittleEndian::write_u32(csum_buf, 0);
LittleEndian::write_u32(&mut buffer[csum_offset..csum_offset + 4], 0);
// Calculate the checksum on the resulting buffer
let mut crc = crc_any::CRC::crc32c();
crc.digest(&buffer);
@@ -483,5 +467,5 @@ pub fn calculate_checksum(buffer: &mut [u8], csum_offset: usize) -> Result<u32>
// Put back the original checksum in the buffer
LittleEndian::write_u32(&mut buffer[csum_offset..csum_offset + 4], orig_csum);
Ok(new_csum)
new_csum
}

View File

@@ -95,46 +95,45 @@ pub fn read(
mut sector_index: u64,
mut sector_count: u64,
) -> Result<usize> {
if disk_spec.has_parent {
return Err(VhdxIoError::UnsupportedMode);
}
let mut read_count: usize = 0;
while sector_count > 0 {
if disk_spec.has_parent {
return Err(VhdxIoError::UnsupportedMode);
} else {
let sector = Sector::new(disk_spec, bat, sector_index, sector_count)?;
let sector = Sector::new(disk_spec, bat, sector_index, sector_count)?;
let bat_entry = match bat.get(sector.bat_index as usize) {
Some(entry) => entry.0,
None => {
return Err(VhdxIoError::InvalidBatIndex);
}
};
match bat_entry & vhdx_bat::BAT_STATE_BIT_MASK {
vhdx_bat::PAYLOAD_BLOCK_NOT_PRESENT
| vhdx_bat::PAYLOAD_BLOCK_UNDEFINED
| vhdx_bat::PAYLOAD_BLOCK_UNMAPPED
| vhdx_bat::PAYLOAD_BLOCK_ZERO => {}
vhdx_bat::PAYLOAD_BLOCK_FULLY_PRESENT => {
f.seek(SeekFrom::Start(sector.file_offset))
.map_err(VhdxIoError::ReadSectorBlock)?;
f.read_exact(
&mut buf[read_count
..(read_count + (sector.free_sectors * SECTOR_SIZE) as usize)],
)
.map_err(VhdxIoError::ReadSectorBlock)?;
}
vhdx_bat::PAYLOAD_BLOCK_PARTIALLY_PRESENT => {
return Err(VhdxIoError::UnsupportedMode);
}
_ => {
return Err(VhdxIoError::InvalidBatEntryState);
}
};
sector_count -= sector.free_sectors;
sector_index += sector.free_sectors;
read_count = sector.free_bytes as usize;
let bat_entry = match bat.get(sector.bat_index as usize) {
Some(entry) => entry.0,
None => {
return Err(VhdxIoError::InvalidBatIndex);
}
};
match bat_entry & vhdx_bat::BAT_STATE_BIT_MASK {
vhdx_bat::PAYLOAD_BLOCK_NOT_PRESENT
| vhdx_bat::PAYLOAD_BLOCK_UNDEFINED
| vhdx_bat::PAYLOAD_BLOCK_UNMAPPED
| vhdx_bat::PAYLOAD_BLOCK_ZERO => {}
vhdx_bat::PAYLOAD_BLOCK_FULLY_PRESENT => {
f.seek(SeekFrom::Start(sector.file_offset))
.map_err(VhdxIoError::ReadSectorBlock)?;
f.read_exact(
&mut buf
[read_count..(read_count + (sector.free_sectors * SECTOR_SIZE) as usize)],
)
.map_err(VhdxIoError::ReadSectorBlock)?;
}
vhdx_bat::PAYLOAD_BLOCK_PARTIALLY_PRESENT => {
return Err(VhdxIoError::UnsupportedMode);
}
_ => {
return Err(VhdxIoError::InvalidBatEntryState);
}
};
sector_count -= sector.free_sectors;
sector_index += sector.free_sectors;
read_count += sector.free_bytes as usize;
}
Ok(read_count)
}
@@ -150,77 +149,73 @@ pub fn write(
mut sector_index: u64,
mut sector_count: u64,
) -> Result<usize> {
if disk_spec.has_parent {
return Err(VhdxIoError::UnsupportedMode);
}
let mut write_count: usize = 0;
while sector_count > 0 {
if disk_spec.has_parent {
return Err(VhdxIoError::UnsupportedMode);
} else {
let sector = Sector::new(disk_spec, bat, sector_index, sector_count)?;
let sector = Sector::new(disk_spec, bat, sector_index, sector_count)?;
let bat_entry = match bat.get(sector.bat_index as usize) {
Some(entry) => entry.0,
None => {
return Err(VhdxIoError::InvalidBatIndex);
}
};
match bat_entry & vhdx_bat::BAT_STATE_BIT_MASK {
vhdx_bat::PAYLOAD_BLOCK_NOT_PRESENT
| vhdx_bat::PAYLOAD_BLOCK_UNDEFINED
| vhdx_bat::PAYLOAD_BLOCK_UNMAPPED
| vhdx_bat::PAYLOAD_BLOCK_ZERO => {
let file_offset =
align!(disk_spec.image_size, vhdx_metadata::BLOCK_SIZE_MIN as u64);
let new_size = file_offset
.checked_add(disk_spec.block_size as u64)
.ok_or(VhdxIoError::InvalidDiskSize)?;
f.set_len(new_size).map_err(VhdxIoError::ResizeFile)?;
disk_spec.image_size = new_size;
let new_bat_entry = file_offset
| (vhdx_bat::PAYLOAD_BLOCK_FULLY_PRESENT & vhdx_bat::BAT_STATE_BIT_MASK);
bat[sector.bat_index as usize] = BatEntry(new_bat_entry);
BatEntry::write_bat_entries(f, bat_offset, bat)
.map_err(VhdxIoError::WriteBat)?;
if file_offset < vhdx_metadata::BLOCK_SIZE_MIN as u64 {
break;
}
f.seek(SeekFrom::Start(file_offset))
.map_err(VhdxIoError::ReadSectorBlock)?;
f.write_all(
&buf[write_count
..(write_count + (sector.free_sectors * SECTOR_SIZE) as usize)],
)
.map_err(VhdxIoError::ReadSectorBlock)?;
}
vhdx_bat::PAYLOAD_BLOCK_FULLY_PRESENT => {
if sector.file_offset < vhdx_metadata::BLOCK_SIZE_MIN as u64 {
break;
}
f.seek(SeekFrom::Start(sector.file_offset))
.map_err(VhdxIoError::ReadSectorBlock)?;
f.write_all(
&buf[write_count
..(write_count + (sector.free_sectors * SECTOR_SIZE) as usize)],
)
.map_err(VhdxIoError::ReadSectorBlock)?;
}
vhdx_bat::PAYLOAD_BLOCK_PARTIALLY_PRESENT => {
return Err(VhdxIoError::UnsupportedMode);
}
_ => {
return Err(VhdxIoError::InvalidBatEntryState);
}
};
sector_count -= sector.free_sectors;
sector_index += sector.free_sectors;
write_count = sector.free_bytes as usize;
let bat_entry = match bat.get(sector.bat_index as usize) {
Some(entry) => entry.0,
None => {
return Err(VhdxIoError::InvalidBatIndex);
}
};
match bat_entry & vhdx_bat::BAT_STATE_BIT_MASK {
vhdx_bat::PAYLOAD_BLOCK_NOT_PRESENT
| vhdx_bat::PAYLOAD_BLOCK_UNDEFINED
| vhdx_bat::PAYLOAD_BLOCK_UNMAPPED
| vhdx_bat::PAYLOAD_BLOCK_ZERO => {
let file_offset =
align!(disk_spec.image_size, vhdx_metadata::BLOCK_SIZE_MIN as u64);
let new_size = file_offset
.checked_add(disk_spec.block_size as u64)
.ok_or(VhdxIoError::InvalidDiskSize)?;
f.set_len(new_size).map_err(VhdxIoError::ResizeFile)?;
disk_spec.image_size = new_size;
let new_bat_entry = file_offset
| (vhdx_bat::PAYLOAD_BLOCK_FULLY_PRESENT & vhdx_bat::BAT_STATE_BIT_MASK);
bat[sector.bat_index as usize] = BatEntry(new_bat_entry);
BatEntry::write_bat_entries(f, bat_offset, bat).map_err(VhdxIoError::WriteBat)?;
if file_offset < vhdx_metadata::BLOCK_SIZE_MIN as u64 {
break;
}
f.seek(SeekFrom::Start(file_offset))
.map_err(VhdxIoError::ReadSectorBlock)?;
f.write_all(
&buf[write_count..(write_count + (sector.free_sectors * SECTOR_SIZE) as usize)],
)
.map_err(VhdxIoError::ReadSectorBlock)?;
}
vhdx_bat::PAYLOAD_BLOCK_FULLY_PRESENT => {
if sector.file_offset < vhdx_metadata::BLOCK_SIZE_MIN as u64 {
break;
}
f.seek(SeekFrom::Start(sector.file_offset))
.map_err(VhdxIoError::ReadSectorBlock)?;
f.write_all(
&buf[write_count..(write_count + (sector.free_sectors * SECTOR_SIZE) as usize)],
)
.map_err(VhdxIoError::ReadSectorBlock)?;
}
vhdx_bat::PAYLOAD_BLOCK_PARTIALLY_PRESENT => {
return Err(VhdxIoError::UnsupportedMode);
}
_ => {
return Err(VhdxIoError::InvalidBatEntryState);
}
};
sector_count -= sector.free_sectors;
sector_index += sector.free_sectors;
write_count += sector.free_bytes as usize;
}
Ok(write_count)
}

View File

@@ -55,6 +55,8 @@ const METADATA_LENGTH_MAX: u32 = 1 << 20; // 1 MiB
pub enum VhdxMetadataError {
#[error("Invalid block size count")]
InvalidBlockSize,
#[error("Invalid disk size {0}")]
InvalidDiskSize(u64),
#[error("Invalid metadata entry count")]
InvalidEntryCount,
#[error("Invalid logical sector size")]
@@ -214,6 +216,14 @@ impl DiskSpec {
if metadata_presence != METADATA_ALL_PRESENT {
return Err(VhdxMetadataError::MissingMetadata);
}
// Make sure virtual disk size is not zero
if (metadata_presence & METADATA_VIRTUAL_DISK_SIZE_PRESENT != 0)
&& disk_spec.virtual_disk_size == 0
{
return Err(VhdxMetadataError::InvalidDiskSize(
disk_spec.virtual_disk_size,
));
}
// Check if the virtual disk size is a multiple of the logical sector
// size.
if ((metadata_presence & METADATA_LOGICAL_SECTOR_SIZE_PRESENT) != 0)
@@ -257,7 +267,7 @@ impl DiskSpec {
}
}
#[repr(packed)]
#[repr(C, packed)]
#[derive(Default, Debug, Clone, Copy)]
struct MetadataTableHeader {
signature: u64,
@@ -288,7 +298,7 @@ impl MetadataTableHeader {
}
}
#[repr(packed)]
#[repr(C, packed)]
#[derive(Default, Debug, Clone, Copy)]
pub struct MetadataTableEntry {
item_id: Uuid,

View File

@@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
//
use std::env;
use std::process::Command;
fn main() {
@@ -12,10 +13,18 @@ fn main() {
if git_out.status.success() {
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {
version = git_out_str;
// Pop the trailing newline.
version.pop();
}
}
}
// Append CH_EXTRA_VERSION to version if it is set.
if let Ok(extra_version) = env::var("CH_EXTRA_VERSION") {
println!("cargo:rerun-if-env-changed=CH_EXTRA_VERSION");
version.push_str(&format!("-{}", extra_version));
}
// This println!() has a special behavior, as it will set the environment
// variable BUILD_VERSION, so that it can be reused from the binary.
// Particularly, this is used from src/main.rs to display the exact

View File

@@ -6,18 +6,18 @@ version = "0.1.0"
[dependencies]
acpi_tables = { workspace = true }
anyhow = "1.0.87"
anyhow = "1.0.94"
arch = { path = "../arch" }
bitflags = "2.6.0"
byteorder = "1.5.0"
event_monitor = { path = "../event_monitor" }
hypervisor = { path = "../hypervisor" }
libc = "0.2.158"
libc = "0.2.167"
log = "0.4.22"
num_enum = "0.7.2"
pci = { path = "../pci" }
serde = { version = "1.0.208", features = ["derive"] }
thiserror = "1.0.62"
thiserror = "2.0.6"
tpm = { path = "../tpm" }
vm-allocator = { path = "../vm-allocator" }
vm-device = { path = "../vm-device" }
@@ -29,9 +29,10 @@ vm-memory = { workspace = true, features = [
vm-migration = { path = "../vm-migration" }
vmm-sys-util = { workspace = true }
[target.'cfg(target_arch = "aarch64")'.dependencies]
[target.'cfg(any(target_arch = "aarch64", target_arch = "riscv64"))'.dependencies]
arch = { path = "../arch" }
[features]
default = []
kvm = ["arch/kvm"]
pvmemcontrol = []

144
devices/src/aia.rs Normal file
View File

@@ -0,0 +1,144 @@
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
// Copyright 2020, ARM Limited.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use super::interrupt_controller::{Error, InterruptController};
extern crate arch;
use std::result;
use std::sync::{Arc, Mutex};
use arch::layout;
use hypervisor::arch::riscv64::aia::{Vaia, VaiaConfig};
use hypervisor::{AiaState, CpuState};
use vm_device::interrupt::{
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
LegacyIrqSourceConfig, MsiIrqGroupConfig,
};
use vm_memory::address::Address;
use vm_migration::{Migratable, Pausable, 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;
pub const IRQ_LEGACY_COUNT: usize = 32;
// TODO: AIA snapshotting is not yet completed.
pub const _AIA_SNAPSHOT_ID: &str = "";
// Aia (Advance Interrupt Architecture) struct provides all the functionality of a
// AIA device. It wraps a hypervisor-emulated AIA device (Vaia) provided by the
// `hypervisor` crate.
// Aia struct also implements InterruptController to provide interrupt delivery
// service.
pub struct Aia {
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
// The hypervisor agnostic virtual AIA
vaia: Arc<Mutex<dyn Vaia>>,
}
impl Aia {
pub fn new(
vcpu_count: u8,
interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
vm: Arc<dyn hypervisor::Vm>,
) -> Result<Aia> {
let interrupt_source_group = interrupt_manager
.create_group(MsiIrqGroupConfig {
base: IRQ_LEGACY_BASE as InterruptIndex,
count: IRQ_LEGACY_COUNT as InterruptIndex,
})
.map_err(Error::CreateInterruptSourceGroup)?;
let vaia = vm
.create_vaia(Aia::create_default_config(vcpu_count as u64))
.map_err(Error::CreateAia)?;
let aia = Aia {
interrupt_source_group,
vaia,
};
aia.enable()?;
Ok(aia)
}
pub fn restore_vaia(
&mut self,
state: Option<AiaState>,
_saved_vcpu_states: &[CpuState],
) -> Result<()> {
self.vaia
.clone()
.lock()
.unwrap()
.set_state(&state.unwrap())
.map_err(Error::RestoreAia)
}
fn enable(&self) -> Result<()> {
// Set irqfd for legacy interrupts
self.interrupt_source_group
.enable()
.map_err(Error::EnableInterrupt)?;
// Set irq_routing for legacy interrupts.
// irqchip: Hardcode to 0 as we support only 1 APLIC
// pin: Use irq number as pin
for i in IRQ_LEGACY_BASE..(IRQ_LEGACY_BASE + IRQ_LEGACY_COUNT) {
let config = LegacyIrqSourceConfig {
irqchip: 0,
pin: (i - IRQ_LEGACY_BASE) as u32,
};
self.interrupt_source_group
.update(
i as InterruptIndex,
InterruptSourceConfig::LegacyIrq(config),
false,
false,
)
.map_err(Error::EnableInterrupt)?;
}
self.interrupt_source_group
.set_gsi()
.map_err(Error::EnableInterrupt)?;
Ok(())
}
/// Default config implied by arch::layout
pub fn create_default_config(vcpu_count: u64) -> VaiaConfig {
VaiaConfig {
vcpu_count: vcpu_count as u32,
aplic_addr: layout::APLIC_START.raw_value(),
imsic_addr: layout::IMSIC_START.raw_value(),
nr_irqs: layout::IRQ_NUM,
}
}
pub fn get_vaia(&mut self) -> Result<Arc<Mutex<dyn Vaia>>> {
Ok(self.vaia.clone())
}
}
impl InterruptController for Aia {
// This should be called anytime an interrupt needs to be injected into the
// running guest.
fn service_irq(&mut self, irq: usize) -> Result<()> {
self.interrupt_source_group
.trigger(irq as InterruptIndex)
.map_err(Error::TriggerInterrupt)?;
Ok(())
}
fn notifier(&self, irq: usize) -> Option<EventFd> {
self.interrupt_source_group.notifier(irq as InterruptIndex)
}
}
impl Snapshottable for Aia {}
impl Pausable for Aia {}
impl Transportable for Aia {}
impl Migratable for Aia {}

View File

@@ -1,3 +1,4 @@
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
// Copyright 2020, ARM Limited.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
@@ -41,6 +42,14 @@ pub enum Error {
/// Failed restoring GIC device.
#[error("Failed restoring GIC device: {0}")]
RestoreGic(hypervisor::arch::aarch64::gic::Error),
#[cfg(target_arch = "riscv64")]
/// Failed creating AIA device.
#[error("Failed creating AIA device: {0}")]
CreateAia(hypervisor::HypervisorVmError),
#[cfg(target_arch = "riscv64")]
/// Failed restoring AIA device.
#[error("Failed restoring AIA device: {0}")]
RestoreAia(hypervisor::arch::riscv64::aia::Error),
}
type Result<T> = result::Result<T, Error>;
@@ -67,7 +76,7 @@ pub struct MsiMessage {
// Introduce trait InterruptController to uniform the interrupt
// service provided for devices.
// Device manager uses this trait without caring whether it is a
// IOAPIC (X86) or GIC (Arm).
// IOAPIC (X86), GIC (Arm) or AIA (RISC-V).
pub trait InterruptController: Send {
fn service_irq(&mut self, irq: usize) -> Result<()>;
#[cfg(target_arch = "x86_64")]

View File

@@ -346,9 +346,9 @@ impl Ioapic {
// Generate MSI message address
let low_addr: u32 = self.apic_address.0 as u32
| u32::from(destination_id) << 12
| u32::from(redirection_hint) << 3
| u32::from(destination_mode) << 2;
| (u32::from(destination_id) << 12)
| (u32::from(redirection_hint) << 3)
| (u32::from(destination_mode) << 2);
// Validate Trigger Mode value
let trigger_mode = trigger_mode(entry);
@@ -372,9 +372,9 @@ impl Ioapic {
}
// Generate MSI message data
let data: u32 = u32::from(trigger_mode) << 15
| u32::from(remote_irr(entry)) << 14
| u32::from(delivery_mode) << 8
let data: u32 = (u32::from(trigger_mode) << 15)
| (u32::from(remote_irr(entry)) << 14)
| (u32::from(delivery_mode) << 8)
| u32::from(vector(entry));
let config = MsiIrqSourceConfig {

View File

@@ -152,7 +152,7 @@ impl BusDevice for Cmos {
0x08 => to_bcd(month as u8),
0x09 => to_bcd((year % 100) as u8),
// Bit 5 for 32kHz clock. Bit 7 for Update in Progress
0x0a => 1 << 5 | (update_in_progress as u8) << 7,
0x0a => (1 << 5) | ((update_in_progress as u8) << 7),
// Bit 0-6 are reserved and must be 0.
// Bit 7 must be 1 (CMOS has power)
0x0d => 1 << 7,

View File

@@ -15,6 +15,8 @@ extern crate event_monitor;
extern crate log;
pub mod acpi;
#[cfg(target_arch = "riscv64")]
pub mod aia;
#[cfg(target_arch = "x86_64")]
pub mod debug_console;
#[cfg(target_arch = "aarch64")]
@@ -26,6 +28,8 @@ pub mod legacy;
#[cfg(feature = "pvmemcontrol")]
pub mod pvmemcontrol;
pub mod pvpanic;
// TODO: TPM is not yet supported
#[cfg(not(target_arch = "riscv64"))]
pub mod tpm;
pub use self::acpi::{AcpiGedDevice, AcpiPmTimerDevice, AcpiShutdownDevice};

View File

@@ -708,7 +708,7 @@ impl PciDevice for PvmemcontrolPciDevice {
self.configuration.read_config_register(reg_idx)
}
fn as_any(&mut self) -> &mut dyn std::any::Any {
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}

View File

@@ -237,7 +237,7 @@ impl PciDevice for PvPanicDevice {
data[0] = self.events;
}
fn as_any(&mut self) -> &mut dyn Any {
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}

View File

@@ -1,6 +1,7 @@
# AMD SEV-SNP
### WARNING
This feature is only currently supported on MSHV.
AMD Secure Encrypted Virtualization & Secure Nested Paging (SEV-SNP) is an AMD
@@ -9,8 +10,8 @@ malicious hypervisor-based attacks like data replay, memory-remapping and more
in order to create an isolated execution environment. Here are some useful
links:
* [SNP Homepage] (https://www.amd.com/en/processors/amd-secure-encrypted-virtualization)
more information about SEV-SNP technical aspects, design and specification.
- [SNP Homepage](https://www.amd.com/content/dam/amd/en/documents/epyc-business-docs/solution-briefs/amd-secure-encrypted-virtualization-solution-brief.pdf):
more information about SEV-SNP technical aspects, design and specification.
## Cloud Hypervisor support

View File

@@ -50,17 +50,12 @@ for more details about the API payloads and responses.
#### REST API Location and availability
The REST API is available as soon as the Cloud Hypervisor binary is started,
through a local UNIX socket.
By default, it is located at `/run/user/{user ID}/cloud-hypervisor.{Cloud Hypervisor PID}`.
For example, if you launched Cloud Hypervisor as user ID 1000 and its PID is
123456, the Cloud Hypervisor REST API will be available at `/run/user/1000/cloud-hypervisor.123456`.
The REST API default URL can be overridden through the Cloud Hypervisor
option `--api-socket`:
The REST API, if enabled, is available as soon as the Cloud Hypervisor binary is started,
through either a local UNIX socket as given in the Cloud Hypervisor option `--api-socket path=...`
or a fd with `--api-socket fd=...`.
```
$ ./target/debug/cloud-hypervisor --api-socket /tmp/cloud-hypervisor.sock
$ ./target/debug/cloud-hypervisor --api-socket path=/tmp/cloud-hypervisor.sock
Cloud Hypervisor Guest
API server: /tmp/cloud-hypervisor.sock
vCPUs: 1

View File

@@ -1,8 +1,9 @@
# `cloud-hypervisor` debug IO ports
When running x86 guests, `cloud-hypervisor` provides different kinds of debug ports:
- [`0x80` debug port](https://www.intel.com/content/www/us/en/support/articles/000005500/boards-and-kits.html)
- Debug console (by default at `0xe9`).
- [`0x80` debug port](https://web.archive.org/web/20211028033025/https://www.intel.com/content/www/us/en/support/articles/000005500/boards-and-kits.html)
- Debug console (by default at `0xe9`).
- Firmware debug port at `0x402`.
All of them can be used to trace user-defined guest events and all of them can
@@ -25,13 +26,13 @@ for debugging specific components of the guest software stack. When logging a
write of one of those codes to the debug port, `cloud-hypervisor` adds a
pre-defined string to the logs.
| Code Range | Component | Log string |
| ---------------- | ----------- | ------------ |
| `0x00` to `0x1f` | Firmware | `Firmware` |
| `0x20` to `0x3f` | Bootloader | `Bootloader` |
| `0x40` to `0x5f` | Kernel | `Kernel` |
| `0x60` to `0x7f` | Userspace | `Userspace` |
| `0x80` to `0xff` | Custom | `Custom` |
| Code Range | Component | Log string |
| ---------------- | ---------- | ------------ |
| `0x00` to `0x1f` | Firmware | `Firmware` |
| `0x20` to `0x3f` | Bootloader | `Bootloader` |
| `0x40` to `0x5f` | Kernel | `Kernel` |
| `0x60` to `0x7f` | Userspace | `Userspace` |
| `0x80` to `0xff` | Custom | `Custom` |
One typical use case is guest boot time measurement and tracing. By writing
different values to the debug I/O port at different boot process steps, the
@@ -71,8 +72,8 @@ cloud-hypervisor: 403.499628ms: DEBUG:vmm/src/vm.rs:510 -- [Debug I/O port: Firm
### Debug console port
The debug console is inspired by QEMU and Bochs, which have a similar feature.
By default, the I/O port `0xe9` is used. This port can be configured like a
The debug console is inspired by QEMU and Bochs, which have a similar feature.
By default, the I/O port `0xe9` is used. This port can be configured like a
console. Thus, it can print to a tty, a file, or a pty, for example.
### Firmware debug port
@@ -83,7 +84,7 @@ it. The firmware debug port only prints to stdout.
## When do I need these ports?
The ports are on the one hand interesting for firmware or kernel developers, as
they provide an easy way to print debug information from within a guest.
they provide an easy way to print debug information from within a guest.
Furthermore, you can patch "normal" software to measure certain events, such as
the boot time of a guest.
@@ -91,6 +92,6 @@ the boot time of a guest.
The `0x80` debug port and the port of the firmware debug device are always
available. The debug console must be activated via the command line, but
provides more configuration options.
provides more configuration options.
You can use different ports for different aspect of your logging messages.
You can use different ports for different aspect of your logging messages.

View File

@@ -8,11 +8,12 @@ the host kernel. The required Linux and KVM changes can be found in the
[KVM SGX Tree](https://github.com/intel/kvm-sgx).
Utilizing SGX in the guest requires a kernel/OS with SGX support, e.g. a kernel
built using the [SGX Linux Development Tree](https://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-sgx.git)
since release 5.11, see
[here](https://www.intel.com/content/www/us/en/developer/tools/software-guard-extensions/linux-overview.html)
or the [KVM SGX Tree](https://github.com/intel/kvm-sgx). Running KVM SGX as the
guest kernel allows nested virtualization of SGX.
For more information about SGX, please refer to the [SGX Homepage](https://software.intel.com/sgx).
For more information about SGX, please refer to the [SGX Homepage](https://www.intel.com/content/www/us/en/developer/tools/software-guard-extensions/linux-overview.html).
For more information about SGX SDK and how to test SGX, please refer to the
following [instructions](https://github.com/intel/linux-sgx).

View File

@@ -4,21 +4,21 @@ Intel® Trust Domain Extensions (Intel® TDX) is an Intel technology designed to
isolate virtual machines from the VMM, hypervisor and any other software on the
host platform. Here are some useful links:
* [TDX Homepage](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-trust-domain-extensions.html):
more information about TDX technical aspects, design and specification
- [TDX Homepage](https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/overview.html):
more information about TDX technical aspects, design and specification
* [KVM TDX tree](https://github.com/intel/tdx/tree/kvm): the required
- [KVM TDX tree](https://github.com/intel/tdx/tree/kvm): the required
Linux kernel changes for the host side
* [Guest TDX tree](https://github.com/intel/tdx/tree/guest): the Linux
- [Guest TDX tree](https://github.com/intel/tdx/tree/guest): the Linux
kernel changes for the guest side
* [EDK2 project](https://github.com/tianocore/edk2): the TDVF firmware
- [EDK2 project](https://github.com/tianocore/edk2): the TDVF firmware
* [Confidential Containers project](https://github.com/confidential-containers/td-shim):
- [Confidential Containers project](https://github.com/confidential-containers/td-shim):
the TDShim firmware
* [TDX Tools](https://github.com/intel/tdx-tools): a collection of tools
- [TDX Linux](https://github.com/intel/tdx-linux): a collection of tools
and scripts to setup TDX environment for testing purpose (such as
installing required packages on the host, creating guest images, and
building the custom Linux kernel for TDX host and guest)
@@ -27,17 +27,13 @@ more information about TDX technical aspects, design and specification
It is required to use 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).
The host environment can also be setup with the [TDX Tools](https://github.com/intel/tdx-tools).
The host environment can also be setup with the [TDX Linux](https://github.com/intel/tdx-linux).
Cloud Hypervisor can run TDX VM (Trust Domain) by loading a TD firmware ([TDVF](https://github.com/tianocore/edk2)),
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).
Cloud Hypervisor can also boot a TDX VM with direct kernel boot using [TDshim](https://github.com/confidential-containers/td-shim).
The custom Linux kernel for the guest can be built with the [TDX Tools](https://github.com/intel/tdx-tools).
> **Note**
> The latest version of custom host and guest kernel being tested is
> from [TDX Tools - 2023ww01](https://github.com/intel/tdx-tools/commits/2023ww01).
The custom Linux kernel for the guest can be built with the [TDX Linux](https://github.com/intel/tdx-linux).
### TDVF
@@ -110,6 +106,7 @@ direct kernel boot, which is useful for containers use cases.
To build TDShim from source, it is required to install `Rust`, `NASM`,
and `LLVM` first. The TDshim can be build as follows:
```bash
git clone https://github.com/confidential-containers/td-shim
cd td-shim
@@ -126,13 +123,14 @@ cargo image --release
If debug logs from the TDShim is needed, here are the alternative
commands:
```bash
cargo image
```
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)
or the [TDX Tools](https://github.com/intel/tdx-tools).
or the [TDX Linux](https://github.com/intel/tdx-linux).
The appropriate kernel boot options must be provided through the `--cmdline`
option as well.

View File

@@ -1,7 +1,7 @@
# I/O Throttling
Cloud Hypervisor now supports I/O throttling on virtio-block and virtio-net
devices. This support is based on the [`rate-limiter` module](https://github.com/firecracker-microvm/firecracker/tree/master/src/rate_limiter)
devices. This support is based on the [`rate-limiter` module](https://github.com/firecracker-microvm/firecracker/tree/7a1231b141e958d15d5b2c079dd5e0880528b4b0/src/rate_limiter)
from Firecracker. This document explains the user interface of this
feature, and highlights some internal implementations that can help users
better understand the expected behavior of I/O throttling in practice.
@@ -44,10 +44,12 @@ generally advisable to keep `bw/ops_refill_time` larger than `100 ms`
expectation ("refill-rate").
## Rate Limit Groups
It is possible to throttle the aggregate bandwidth or operations
of multiple virtio-blk devices using a `rate_limit_group`. virtio-blk devices may be
dynamically added and removed from a `rate_limit_group`. The following example
demonstrates how to throttle the aggregate bandwidth of two disks to 10 MiB/s.
```
--disk path=disk0.raw,rate_limit_group=group0 \
path=disk1.raw,rate_limit_group=group0 \

View File

@@ -1,16 +1,19 @@
# Live Migration
This document gives two examples of how to use the live migration
support in Cloud Hypervisor:
This document gives examples of how to use the live migration support
in Cloud Hypervisor:
1. local migration - migrating between two VMs running on the same
machine;
1. nested-vm migration - migrating between two nested VMs whose host VMs
are running on the same machine.
1. local migration - migrating a VM from one Cloud Hypervisor instance to another on the same machine;
1. remote migration - migrating a VM between two machines;
> :warning: These examples place sockets /tmp. This is done for
> simplicity and should not be done in production.
## Local Migration (Suitable for Live Upgrade of VMM)
Launch the source VM (on the host machine):
```bash
```console
$ target/release/cloud-hypervisor
--kernel ~/workloads/vmlinux \
--disk path=~/workloads/focal.raw \
@@ -20,17 +23,20 @@ $ target/release/cloud-hypervisor
```
Launch the destination VM from the same directory (on the host machine):
```bash
```console
$ target/release/cloud-hypervisor --api-socket=/tmp/api2
```
Get ready for receiving migration for the destination VM (on the host machine):
```bash
```console
$ target/release/ch-remote --api-socket=/tmp/api2 receive-migration unix:/tmp/sock
```
Start to send migration for the source VM (on the host machine):
```bash
```console
$ target/release/ch-remote --api-socket=/tmp/api1 send-migration --local unix:/tmp/sock
```
@@ -38,102 +44,139 @@ When the above commands completed, the source VM should be successfully
migrated to the destination VM. Now the destination VM is running while
the source VM is terminated gracefully.
## Nested-VM Migration
## Remote Migration
Launch VM 1 (on the host machine) with an extra virtio-blk device for
exposing a guest image for the nested source VM:
> Note: the example below also attached an additional virtio-blk device
> with a dummy image for testing purpose (which is optional).
```bash
$ head -c 1M < /dev/urandom > tmp.img # create a dummy image for testing
$ sudo /target/release/cloud-hypervisor \
In this example, we will migrate a VM from one machine (`src`) to
another (`dst`) across the network. To keep it simple, we will use a
minimal VM setup without storage.
### Preparation
Make sure that `src` and `dst` can reach each other via the
network. You should be able to ping each machine. Also each machine
should have an open TCP port.
You will need a kernel and initramfs for a minimal Linux system. For
this example, we will use the Debian netboot image.
Place the kernel and initramfs into the _same directory_ on both
machines. This is important for the migration to succeed. We will use
`/var/images`:
```console
src $ export DEBIAN=https://ftp.debian.org/debian/dists/stable/main/installer-amd64/current/images/netboot/debian-installer/amd64
src $ mkdir -p /var/images
src $ curl $DEBIAN/linux > /var/images/linux
src $ curl $DEBIAN/initrd.gz > /var/images/initrd
```
Repeat the above steps on the destination host.
### Unix Socket Migration
If Unix socket is selected for migration, we can tunnel traffic through "socat".
#### Starting the Receiver VM
On the receiver side, we prepare an empty VM:
```console
dst $ cloud-hypervisor --api-socket /tmp/api
```
In a different terminal, configure the VM as a migration target:
```console
dst $ ch-remote --api-socket=/tmp/api receive-migration unix:/tmp/sock
```
In yet another terminal, forward TCP connections to the Unix domain socket:
```console
dst $ socat TCP-LISTEN:{port},reuseaddr UNIX-CLIENT:/tmp/sock
```
#### Starting the Sender VM
Let's start the VM on the source machine:
```console
src $ cloud-hypervisor \
--serial tty --console off \
--cpus boot=1 --memory size=512M \
--kernel vmlinux \
--cmdline "root=/dev/vda1 console=ttyS0" \
--disk path=focal-1.raw path=focal-nested.raw path=tmp.img\
--net ip=192.168.101.1
--cpus boot=2 --memory size=4G \
--kernel /var/images/linux \
--initramfs /var/images/initrd \
--cmdline "console=ttyS0" \
--api-socket /tmp/api
```
Launch VM 2 (on the host machine) with an extra virtio-blk device for
exposing the same guest image for the nested destination VM:
```bash
$ sudo /target/release/cloud-hypervisor \
After a few seconds the VM should be up and you can interact with it.
#### Performing the Migration
First, we start `socat`:
```console
src $ socat UNIX-LISTEN:/tmp/sock,reuseaddr TCP:{dst}:{port}
```
> Replace {dst}:{port} with the actual IP address and port of your destination host.
Then we kick-off the migration itself:
```console
src $ ch-remote --api-socket=/tmp/api send-migration unix:/tmp/sock
```
When the above commands completed, the VM should be successfully
migrated to the destination machine without interrupting the workload.
### TCP Socket Migration
If TCP socket is selected for migration, we need to consider migrating
in a trusted network.
#### Starting the Receiver VM
On the receiver side, we prepare an empty VM:
```console
dst $ cloud-hypervisor --api-socket /tmp/api
```
In a different terminal, prepare to receive the migration:
```console
dst $ ch-remote --api-socket=/tmp/api receive-migration tcp:0.0.0.0:{port}
```
#### Starting the Sender VM
Let's start the VM on the source machine:
```console
src $ cloud-hypervisor \
--serial tty --console off \
--cpus boot=1 --memory size=512M \
--kernel vmlinux \
--cmdline "root=/dev/vda1 console=ttyS0" \
--disk path=focal-2.raw path=focal-nested.raw path=tmp.img\
--net ip=192.168.102.1
--cpus boot=2 --memory size=4G \
--kernel /var/images/linux \
--initramfs /var/images/initrd \
--cmdline "console=ttyS0" \
--api-socket /tmp/api
```
Launch the nested source VM (inside the guest OS of the VM 1) :
```bash
vm-1:~$ sudo ./cloud-hypervisor \
--serial tty --console off \
--memory size=128M \
--kernel vmlinux \
--cmdline "console=ttyS0 root=/dev/vda1" \
--disk path=/dev/vdb path=/dev/vdc \
--api-socket=/tmp/api1 \
--net ip=192.168.100.1
vm-1:~$ # setup the guest network if needed
vm-1:~$ sudo ip addr add 192.168.101.2/24 dev ens4
vm-1:~$ sudo ip link set up dev ens4
vm-1:~$ sudo ip r add default via 192.168.101.1
```
Optional: Run the guest workload below (on the guest OS of the nested source VM),
which performs intensive virtio-blk operations. Now the console of the nested
source VM should repeatedly print `"equal"`, and our goal is migrating
this VM and the running workload without interruption.
```bash
#/bin/bash
After a few seconds the VM should be up and you can interact with it.
# On the guest OS of the nested source VM
#### Performing the Migration
input="/dev/vdb"
result=$(md5sum $input)
tmp=$(md5sum $input)
Initiate the Migration over TCP:
while [[ "$result" == "$tmp" ]]
do
echo "equal"
tmp=$(md5sum $input)
done
echo "not equal"
echo "result = $result"
echo "tmp = $tmp"
```console
src $ ch-remote --api-socket=/tmp/api send-migration tcp:{dst}:{port}
```
Launch the nested destination VM (inside the guest OS of the VM 2):
```bash
vm-2:~$ sudo ./cloud-hypervisor --api-socket=/tmp/api2
vm-2:~$ # setup the guest network with the following commands if needed
vm-2:~$ sudo ip addr add 192.168.102.2/24 dev ens4
vm-2:~$ sudo ip link set up dev ens4
vm-2:~$ sudo ip r add default via 192.168.102.1
vm-2:~$ ping 192.168.101.2 # This should succeed
```
> Note: If the above ping failed, please check the iptables rule on the
> host machine, e.g. whether the policy for the `FORWARD` chain is set
> to `DROP` (which is the default setting configured by Docker).
> Replace {dst}:{port} with the actual IP address and port of your destination host.
Get ready for receiving migration for the nested destination VM (inside
the guest OS of the VM 2):
```bash
vm-2:~$ sudo ./ch-remote --api-socket=/tmp/api2 receive-migration unix:/tmp/sock2
vm-2:~$ sudo socat TCP-LISTEN:6000,reuseaddr UNIX-CLIENT:/tmp/sock2
```
Start to send migration for the nested source VM (inside the guest OS of
the VM 1):
```bash
vm-1:~$ sudo socat UNIX-LISTEN:/tmp/sock1,reuseaddr TCP:192.168.102.2:6000
vm-1:~$ sudo ./ch-remote --api-socket=/tmp/api1 send-migration unix:/tmp/sock1
```
When the above commands completed, the source VM should be successfully
migrated to the destination VM without interrupting our testing guest
workload. Now the destination VM is running the testing guest workload
while the source VM is terminated gracefully.
After completing the above commands, the source VM will be migrated to
the destination host and continue running there. The source VM instance
will terminate normally. All ongoing processes and connections within
the VM should remain intact after the migration.

View File

@@ -2,8 +2,8 @@
The target audience of this document is both:
* Developers who want to understand what log level to use and when,
* Users who want to debug issues with running their workloads in Cloud Hypervisor
- Developers who want to understand what log level to use and when,
- Users who want to debug issues with running their workloads in Cloud Hypervisor
## Control
@@ -29,7 +29,6 @@ A typical example of where this level of message should be generated is during a
The user should investigate the meaning of this warning and take steps to ensure the correct functionality.
### `info!()`
Use `-v` to enable.
@@ -40,4 +39,4 @@ This level is for the benefit of developers. It should be used for sporadic and
Use `-vv` to enable.
For the most verbose of logging messages. It is acceptable to "spam" the log with repeated invocations of the same message. This level of logging would be combined with `--log-file`.
For the most verbose of logging messages. It is acceptable to "spam" the log with repeated invocations of the same message. This level of logging would be combined with `--log-file`.

88
docs/riscv.md Normal file
View File

@@ -0,0 +1,88 @@
# How to build and test Cloud Hypervisor on riscv64
This document introduces how to build and test Cloud Hypervisor on `riscv64`.
All instructions here are tested with Ubuntu 24.04.2 as the host OS.
## Hardware requirements
- riscv64 servers (recommended) or development boards equipped with the AIA
(Advance Interrupt Architecture) interrupt controller.
## Getting started
We create a folder to build and run Cloud Hypervisor at `$HOME/cloud-hypervisor`
```console
export CLOUDH=$HOME/cloud-hypervisor
mkdir $CLOUDH
```
## Prerequisites
You need to install some prerequisite packages to build and test Cloud Hypervisor.
### Tools
```console
# Install rust tool chain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Install the tools used for building guest kernel, EDK2 and converting guest disk
sudo apt-get update
sudo apt-get install git build-essential m4 bison flex uuid-dev qemu-utils
```
### Building Cloud Hypervisor
```console
pushd $CLOUDH
git clone https://github.com/cloud-hypervisor/cloud-hypervisor.git
cd cloud-hypervisor
cargo build
popd
```
### Disk image
Download the Ubuntu cloud image and convert the image type.
```console
pushd $CLOUDH
wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-riscv64.img
qemu-img convert -p -f qcow2 -O raw jammy-server-cloudimg-riscv64.img jammy-server-cloudimg-riscv64.raw
popd
```
## Direct-kernel booting
### Building kernel
```console
pushd $CLOUDH
git clone --depth 1 "https://github.com/cloud-hypervisor/linux.git" -b ch-6.12.8
cd linux
make ch_defconfig
make -j `nproc`
popd
```
### Booting the guest VM
```console
pushd $CLOUDH
sudo $CLOUDH/cloud-hypervisor/target/debug/cloud-hypervisor \
--kernel $CLOUDH/linux/arch/riscv64/boot/Image \
--disk path=jammy-server-cloudimg-riscv64.raw \
--cmdline "console=hvc0 root=/dev/vda rw" \
--cpus boot=1 \
--memory size=1024M \
--seccomp false \
--log-file boot.log -vv
popd
```
## Known limitations
- Direct kernel boot only
- `64-bit Linux` guest OS only
- For more details, see
[here](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/6978).

View File

@@ -5,8 +5,8 @@ name = "event_monitor"
version = "0.1.0"
[dependencies]
flume = "0.11.0"
libc = "0.2.158"
flume = "0.11.1"
libc = "0.2.167"
once_cell = "1.20.2"
serde = { version = "1.0.208", features = ["derive", "rc"] }
serde_json = "1.0.120"

256
fuzz/Cargo.lock generated
View File

@@ -1,13 +1,13 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "acpi_tables"
version = "0.1.0"
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#ca1a473fe73cdd8eb49c1449faad7aaac06f32c2"
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#849d5950196f66dd10f2b2606d8fe8c7cb39ec24"
dependencies = [
"zerocopy",
"zerocopy 0.7.35",
]
[[package]]
@@ -61,15 +61,15 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.87"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8"
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
[[package]]
name = "arbitrary"
version = "1.3.2"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
[[package]]
name = "arc-swap"
@@ -89,7 +89,7 @@ dependencies = [
"linux-loader",
"log",
"serde",
"thiserror",
"thiserror 2.0.9",
"uuid",
"vm-fdt",
"vm-memory",
@@ -126,7 +126,7 @@ dependencies = [
"remain",
"serde",
"smallvec",
"thiserror",
"thiserror 2.0.9",
"uuid",
"virtio-bindings",
"virtio-queue",
@@ -195,13 +195,16 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
name = "cloud-hypervisor-fuzz"
version = "0.0.0"
dependencies = [
"arbitrary",
"block",
"devices",
"epoll",
"hypervisor",
"libc",
"libfuzzer-sys",
"linux-loader",
"micro_http",
"mshv-bindings",
"net_util",
"once_cell",
"seccompiler",
@@ -297,7 +300,7 @@ dependencies = [
"num_enum",
"pci",
"serde",
"thiserror",
"thiserror 2.0.9",
"tpm",
"vm-allocator",
"vm-device",
@@ -361,9 +364,9 @@ checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67"
[[package]]
name = "flume"
version = "0.11.0"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
dependencies = [
"futures-core",
"futures-sink",
@@ -422,10 +425,22 @@ dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi",
"wasi 0.11.0+wasi-snapshot-preview1",
"wasm-bindgen",
]
[[package]]
name = "getrandom"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
dependencies = [
"cfg-if",
"libc",
"wasi 0.13.3+wasi-0.2.2",
"windows-targets",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
@@ -437,21 +452,33 @@ name = "hypervisor"
version = "0.1.0"
dependencies = [
"anyhow",
"arc-swap",
"byteorder",
"cfg-if",
"concat-idents",
"iced-x86",
"kvm-bindings",
"kvm-ioctls",
"libc",
"log",
"mshv-bindings",
"serde",
"serde_with",
"thiserror",
"thiserror 2.0.9",
"vfio-ioctls",
"vm-memory",
"vmm-sys-util",
]
[[package]]
name = "iced-x86"
version = "1.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c447cff8c7f384a7d4f741cfcff32f75f3ad02b406432e8d6c878d56b1edf6b"
dependencies = [
"lazy_static",
]
[[package]]
name = "ident_case"
version = "1.0.1"
@@ -500,20 +527,20 @@ dependencies = [
[[package]]
name = "kvm-bindings"
version = "0.9.1"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2efe3f1a4437bffe000e6297a593b98184213cd27486776c335f95ab53d48e3a"
checksum = "fa4933174d0cc4b77b958578cd45784071cc5ae212c2d78fbd755aaaa6dfa71a"
dependencies = [
"serde",
"vmm-sys-util",
"zerocopy",
"zerocopy 0.7.35",
]
[[package]]
name = "kvm-ioctls"
version = "0.18.0"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92c2176b91f68903b54ac8c6185bada7d607ca6110998976ff15c032f88a7d39"
checksum = "e013ae7fcd2c6a8f384104d16afe7ea02969301ea2bb2a56e44b011ebc907cab"
dependencies = [
"bitflags 2.6.0",
"kvm-bindings",
@@ -529,14 +556,20 @@ checksum = "18738c5d4c7fae6727a96adb94722ef7ce82f3eafea0a11777e258a93816537e"
dependencies = [
"enumflags2",
"libc",
"thiserror",
"thiserror 1.0.64",
]
[[package]]
name = "libc"
version = "0.2.158"
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "libfuzzer-sys"
@@ -551,9 +584,9 @@ dependencies = [
[[package]]
name = "linux-loader"
version = "0.12.0"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d379d0089d0fbf4161c35a4fdfd76125923f1a93632c49195f5372b4c0b1472"
checksum = "870c3814345f050991f99869417779f6062542bcf4ed81db7a1b926ad1306638"
dependencies = [
"vm-memory",
]
@@ -595,13 +628,27 @@ dependencies = [
"vmm-sys-util",
]
[[package]]
name = "mshv-bindings"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9c369385758f81ca937414dc2147737c92032e4fb399669f287abf94d89252d"
dependencies = [
"libc",
"num_enum",
"serde",
"serde_derive",
"vmm-sys-util",
"zerocopy 0.8.14",
]
[[package]]
name = "nanorand"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
dependencies = [
"getrandom",
"getrandom 0.2.15",
]
[[package]]
@@ -616,13 +663,13 @@ name = "net_util"
version = "0.1.0"
dependencies = [
"epoll",
"getrandom",
"getrandom 0.3.1",
"libc",
"log",
"net_gen",
"rate_limiter",
"serde",
"thiserror",
"thiserror 2.0.9",
"virtio-bindings",
"virtio-queue",
"vm-memory",
@@ -662,9 +709,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.19.0"
version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "option_parser"
@@ -686,7 +733,7 @@ dependencies = [
"libc",
"log",
"serde",
"thiserror",
"thiserror 2.0.9",
"vfio-bindings",
"vfio-ioctls",
"vfio_user",
@@ -703,7 +750,7 @@ version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
"zerocopy",
"zerocopy 0.7.35",
]
[[package]]
@@ -717,9 +764,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.86"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
dependencies = [
"unicode-ident",
]
@@ -735,20 +782,20 @@ dependencies = [
[[package]]
name = "rand"
version = "0.8.5"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"zerocopy 0.8.14",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
@@ -756,11 +803,11 @@ dependencies = [
[[package]]
name = "rand_core"
version = "0.6.4"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom",
"getrandom 0.3.1",
]
[[package]]
@@ -770,7 +817,7 @@ dependencies = [
"epoll",
"libc",
"log",
"thiserror",
"thiserror 2.0.9",
"vmm-sys-util",
]
@@ -799,9 +846,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "seccompiler"
version = "0.4.0"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "345a3e4dddf721a478089d4697b83c6c0a8f5bf16086f6c13397e4534eb6e2e5"
checksum = "a4ae55de56877481d112a559bbc12667635fdaf5e005712fd4e2b2fa50ffc884"
dependencies = [
"libc",
]
@@ -913,9 +960,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.77"
version = "2.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a"
dependencies = [
"proc-macro2",
"quote",
@@ -928,7 +975,16 @@ version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
dependencies = [
"thiserror-impl",
"thiserror-impl 1.0.64",
]
[[package]]
name = "thiserror"
version = "2.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
dependencies = [
"thiserror-impl 2.0.9",
]
[[package]]
@@ -942,6 +998,17 @@ dependencies = [
"syn",
]
[[package]]
name = "thiserror-impl"
version = "2.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml_datetime"
version = "0.6.8"
@@ -968,7 +1035,7 @@ dependencies = [
"libc",
"log",
"net_gen",
"thiserror",
"thiserror 2.0.9",
"vmm-sys-util",
]
@@ -997,20 +1064,20 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.10.0"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
dependencies = [
"getrandom",
"getrandom 0.3.1",
"rand",
"uuid-macro-internal",
]
[[package]]
name = "uuid-macro-internal"
version = "1.10.0"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee1cd046f83ea2c4e920d6ee9f7c3537ef928d75dce5d84a87c2c5d6b3999a3a"
checksum = "9521621447c21497fac206ffe6e9f642f977c4f82eeba9201055f64884d9cb01"
dependencies = [
"proc-macro2",
"quote",
@@ -1020,7 +1087,7 @@ dependencies = [
[[package]]
name = "vfio-bindings"
version = "0.4.0"
source = "git+https://github.com/rust-vmm/vfio?branch=main#a51a4746b0d317bfc21fa49d40f9287f3b8137fd"
source = "git+https://github.com/rust-vmm/vfio?branch=main#b135b8305c2cc8ec333e0cf77a780445cc98dcee"
dependencies = [
"vmm-sys-util",
]
@@ -1028,14 +1095,14 @@ dependencies = [
[[package]]
name = "vfio-ioctls"
version = "0.2.0"
source = "git+https://github.com/rust-vmm/vfio?branch=main#a51a4746b0d317bfc21fa49d40f9287f3b8137fd"
source = "git+https://github.com/rust-vmm/vfio?branch=main#b135b8305c2cc8ec333e0cf77a780445cc98dcee"
dependencies = [
"byteorder",
"kvm-bindings",
"kvm-ioctls",
"libc",
"log",
"thiserror",
"thiserror 1.0.64",
"vfio-bindings",
"vm-memory",
"vmm-sys-util",
@@ -1044,7 +1111,7 @@ dependencies = [
[[package]]
name = "vfio_user"
version = "0.1.0"
source = "git+https://github.com/rust-vmm/vfio-user?branch=main#bf7d7e851b604d8414a7960fb9137b59fc42421d"
source = "git+https://github.com/rust-vmm/vfio-user?branch=main#3febcdd3fa2531623865663ca1721e1962ed9979"
dependencies = [
"bitflags 1.3.2",
"libc",
@@ -1052,7 +1119,7 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
"thiserror",
"thiserror 1.0.64",
"vfio-bindings",
"vm-memory",
"vmm-sys-util",
@@ -1060,9 +1127,8 @@ dependencies = [
[[package]]
name = "vhost"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c1c4c6c9f79fbe3150d9a403008ca416d34c489897effdda28b646f09900aad"
version = "0.12.1"
source = "git+https://github.com/rust-vmm/vhost?rev=d983ae0#d983ae07f78663b7d24059667376992460b571a2"
dependencies = [
"bitflags 2.6.0",
"libc",
@@ -1073,9 +1139,9 @@ dependencies = [
[[package]]
name = "virtio-bindings"
version = "0.2.3"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68d0df4f5ad79b1dc81b5913ac737e24a84dcd5100f36ed953a1faec18aba241"
checksum = "1711e61c00f8cb450bd15368152a1e37a12ef195008ddc7d0f4812f9e2b30a68"
[[package]]
name = "virtio-devices"
@@ -1098,7 +1164,7 @@ dependencies = [
"serde_json",
"serde_with",
"serial_buffer",
"thiserror",
"thiserror 2.0.9",
"vhost",
"virtio-bindings",
"virtio-queue",
@@ -1112,9 +1178,9 @@ dependencies = [
[[package]]
name = "virtio-queue"
version = "0.13.0"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb1761348d3b5e82131379b9373435b48dc8333100bff3f1cdf9cc541a0ad83"
checksum = "872e2f3fbd70a7e6f01689720cce3d5c2c5efe52b484dd07b674246ada0e9a8d"
dependencies = [
"log",
"virtio-bindings",
@@ -1138,7 +1204,7 @@ dependencies = [
"anyhow",
"hypervisor",
"serde",
"thiserror",
"thiserror 2.0.9",
"vfio-ioctls",
"vm-memory",
"vmm-sys-util",
@@ -1147,17 +1213,17 @@ dependencies = [
[[package]]
name = "vm-fdt"
version = "0.3.0"
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#982fb8d9c8cd7f53520d7e304b39ff307fa3a641"
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#ef5bd734f5f66fb07722d766981adbc915f0d941"
[[package]]
name = "vm-memory"
version = "0.15.0"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a320fc11792e063174402ff444aa3c80363cbf1e31c47b5ef74124406c334ce6"
checksum = "f1720e7240cdc739f935456eb77f370d7e9b2a3909204da1e2b47bef1137a013"
dependencies = [
"arc-swap",
"libc",
"thiserror",
"thiserror 1.0.64",
"winapi",
]
@@ -1168,7 +1234,7 @@ dependencies = [
"anyhow",
"serde",
"serde_json",
"thiserror",
"thiserror 2.0.9",
"vm-memory",
]
@@ -1215,7 +1281,7 @@ dependencies = [
"serde_json",
"serial_buffer",
"signal-hook",
"thiserror",
"thiserror 2.0.9",
"tracer",
"uuid",
"vfio-ioctls",
@@ -1228,7 +1294,7 @@ dependencies = [
"vm-migration",
"vm-virtio",
"vmm-sys-util",
"zerocopy",
"zerocopy 0.7.35",
]
[[package]]
@@ -1249,6 +1315,15 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.13.3+wasi-0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.93"
@@ -1408,6 +1483,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [
"bitflags 2.6.0",
]
[[package]]
name = "zerocopy"
version = "0.7.35"
@@ -1415,7 +1499,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"byteorder",
"zerocopy-derive",
"zerocopy-derive 0.7.35",
]
[[package]]
name = "zerocopy"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468"
dependencies = [
"zerocopy-derive 0.8.14",
]
[[package]]
@@ -1428,3 +1521,14 @@ dependencies = [
"quote",
"syn",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View File

@@ -9,24 +9,29 @@ version = "0.0.0"
cargo-fuzz = true
[features]
default = ["mshv_emulator"]
igvm = []
mshv_emulator = ["hypervisor/mshv_emulator"]
pvmemcontrol = []
[dependencies]
arbitrary = "1.4.1"
block = { path = "../block" }
devices = { path = "../devices" }
epoll = "4.3.3"
hypervisor = { path = "../hypervisor", features = ["mshv_emulator"] }
libc = "0.2.155"
libfuzzer-sys = "0.4.7"
linux-loader = { version = "0.12.0", features = ["bzimage", "elf", "pe"] }
linux-loader = { version = "0.13.0", features = ["bzimage", "elf", "pe"] }
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
mshv-bindings = "0.3.2"
net_util = { path = "../net_util" }
once_cell = "1.19.0"
seccompiler = "0.4.0"
seccompiler = "0.5.0"
virtio-devices = { path = "../virtio-devices" }
virtio-queue = "0.13.0"
virtio-queue = "0.14.0"
vm-device = { path = "../vm-device" }
vm-memory = "0.15.0"
vm-memory = "0.16.0"
vm-migration = { path = "../vm-migration" }
vm-virtio = { path = "../vm-virtio" }
vmm = { path = "../vmm", features = ["guest_debug"] }
@@ -126,8 +131,21 @@ name = "vhdx"
path = "fuzz_targets/vhdx.rs"
test = false
[[bin]]
doc = false
name = "vsock"
path = "fuzz_targets/vsock.rs"
test = false
[[bin]]
doc = false
name = "watchdog"
path = "fuzz_targets/watchdog.rs"
test = false
[[bin]]
doc = false
name = "x86emul"
path = "fuzz_targets/x86emul.rs"
required-features = ["mshv_emulator"]
test = false

View File

@@ -7,7 +7,7 @@
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::Arc;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@@ -37,11 +37,11 @@ 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| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < QUEUE_DATA_SIZE * QUEUE_NUM
|| bytes.len() > (QUEUE_DATA_SIZE * QUEUE_NUM + MEM_SIZE)
{
return;
return Corpus::Reject;
}
let mut balloon = virtio_devices::Balloon::new(
@@ -61,7 +61,7 @@ fuzz_target!(|bytes| {
// 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;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@@ -108,6 +108,8 @@ fuzz_target!(|bytes| {
// Wait for the events to finish and balloon device worker thread to return
balloon.wait_for_epoll_threads();
Corpus::Keep
});
pub struct NoopVirtioInterrupt {}

View File

@@ -17,7 +17,7 @@ use std::{ffi, io};
use block::async_io::DiskFile;
use block::raw_sync::RawFileDiskSync;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{Block, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@@ -40,9 +40,9 @@ 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| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < QUEUE_DATA_SIZE || bytes.len() > (QUEUE_DATA_SIZE + MEM_SIZE) {
return;
return Corpus::Reject;
}
let queue_data = &bytes[..QUEUE_DATA_SIZE];
@@ -76,7 +76,7 @@ fuzz_target!(|bytes| {
// 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;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@@ -96,6 +96,8 @@ fuzz_target!(|bytes| {
// Wait for the events to finish and block device worker thread to return
block.wait_for_epoll_threads();
Corpus::Keep
});
fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> {

View File

@@ -5,14 +5,14 @@
#![no_main]
use devices::legacy::Cmos;
use libc::EFD_NONBLOCK;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use vm_device::BusDevice;
use vmm_sys_util::eventfd::EventFd;
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
// Need at least 16 bytes for the test
if bytes.len() < 16 {
return;
return Corpus::Reject;
}
let mut below_4g = [0u8; 8];
@@ -46,4 +46,6 @@ fuzz_target!(|bytes| {
cmos.write(0, offset, &data);
}
}
Corpus::Keep
});

View File

@@ -9,7 +9,7 @@ use std::io::Write;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::sync::Arc;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@@ -54,12 +54,12 @@ const QUEUE_BYTES_SIZE: usize = align!(
DESC_TABLE_ALIGN_SIZE
) as usize;
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + CONSOLE_INPUT_SIZE
|| bytes.len()
> (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + CONSOLE_INPUT_SIZE + MEM_SIZE
{
return;
return Corpus::Reject;
}
let (pipe_rx, mut pipe_tx) = create_pipe().unwrap();
@@ -108,10 +108,10 @@ fuzz_target!(|bytes| {
.write_slice(queue_bytes, GuestAddress(BASE_VIRT_QUEUE_ADDR))
.is_err()
{
return;
return Corpus::Reject;
}
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@@ -137,6 +137,8 @@ fuzz_target!(|bytes| {
// Wait for the events to finish and console device worker thread to return
console.wait_for_epoll_threads();
Corpus::Keep
});
pub struct NoopVirtioInterrupt {}

View File

@@ -8,7 +8,7 @@ use std::path::PathBuf;
use std::sync::mpsc::{channel, Receiver};
use std::thread;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use micro_http::Request;
use once_cell::sync::Lazy;
use vm_migration::MigratableError;
@@ -27,9 +27,9 @@ use vmm_sys_util::eventfd::EventFd;
static ROUTES: Lazy<Vec<&Box<dyn EndpointHandler + Sync + Send>>> =
Lazy::new(|| HTTP_ROUTES.routes.values().collect());
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < 2 {
return;
return Corpus::Reject;
}
let route = ROUTES[bytes[0] as usize % ROUTES.len()];
@@ -53,6 +53,8 @@ fuzz_target!(|bytes| {
exit_evt.write(1).ok();
http_receiver_thread.join().unwrap();
};
Corpus::Keep
});
fn generate_request(bytes: &[u8]) -> Option<Request> {

View File

@@ -7,7 +7,7 @@
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::Arc;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@@ -54,11 +54,11 @@ const USED_RING_ADDR: u64 = align!(AVAIL_RING_ADDR + AVAIL_RING_SIZE, USED_RING_
// Virtio-queue size in bytes
const QUEUE_BYTES_SIZE: usize = (USED_RING_ADDR + USED_RING_SIZE - DESC_TABLE_ADDR) as usize;
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE)
|| bytes.len() > (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE + MEM_SIZE)
{
return;
return Corpus::Reject;
}
let (mut iommu, _) = virtio_devices::Iommu::new(
@@ -66,6 +66,7 @@ fuzz_target!(|bytes| {
SeccompAction::Allow,
EventFd::new(EFD_NONBLOCK).unwrap(),
((MEM_SIZE - IOVA_SPACE_SIZE) as u64, (MEM_SIZE - 1) as u64),
64,
None,
)
.unwrap();
@@ -91,10 +92,10 @@ fuzz_target!(|bytes| {
.write_slice(queue_bytes, GuestAddress(DESC_TABLE_ADDR))
.is_err()
{
return;
return Corpus::Reject;
}
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@@ -118,6 +119,8 @@ fuzz_target!(|bytes| {
// Wait for the events to finish and vIOMMU device worker thread to return
iommu.wait_for_epoll_threads();
Corpus::Keep
});
pub struct NoopVirtioInterrupt {}

View File

@@ -8,7 +8,7 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use vm_memory::bitmap::AtomicBitmap;
use vm_memory::GuestAddress;
@@ -18,7 +18,7 @@ const MEM_SIZE: usize = 256 * 1024 * 1024;
// From 'arch::x86_64::layout::CMDLINE_START'
const CMDLINE_START: GuestAddress = GuestAddress(0x20000);
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
let payload_config = vmm::vm_config::PayloadConfig {
firmware: None,
kernel: None,
@@ -29,9 +29,11 @@ fuzz_target!(|bytes| {
};
let kernel_cmdline = match vmm::vm::Vm::generate_cmdline(&payload_config) {
Ok(cmdline) => cmdline,
_ => return,
_ => return Corpus::Reject,
};
let guest_memory = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
linux_loader::loader::load_cmdline(&guest_memory, CMDLINE_START, &kernel_cmdline).ok();
Corpus::Keep
});

View File

@@ -7,7 +7,7 @@
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::{Arc, Mutex};
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{BlocksState, Mem, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@@ -57,11 +57,11 @@ const USED_RING_ADDR: u64 = align!(AVAIL_RING_ADDR + AVAIL_RING_SIZE, USED_RING_
// Virtio-queue size in bytes
const QUEUE_BYTES_SIZE: usize = (USED_RING_ADDR + USED_RING_SIZE - DESC_TABLE_ADDR) as usize;
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE
|| bytes.len() > (VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE + MEM_SIZE)
{
return;
return Corpus::Reject;
}
let virtio_mem_data = &bytes[..VIRTIO_MEM_DATA_SIZE];
@@ -86,7 +86,7 @@ fuzz_target!(|bytes| {
.write_slice(queue_bytes, GuestAddress(DESC_TABLE_ADDR))
.is_err()
{
return;
return Corpus::Reject;
}
// Add the memory region for the virtio-mem device
let mem = mem.insert_region(virtio_mem_region).unwrap();
@@ -94,7 +94,7 @@ fuzz_target!(|bytes| {
.write_slice(mem_bytes, GuestAddress(VIRTIO_MEM_REGION_ADDRESS))
.is_err()
{
return;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@@ -114,6 +114,8 @@ fuzz_target!(|bytes| {
// Wait for the events to finish and virtio-mem device worker thread to return
virtio_mem.wait_for_epoll_threads();
return Corpus::Keep;
});
pub struct NoopVirtioInterrupt {}

View File

@@ -9,7 +9,7 @@ use std::io::{Read, Write};
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::Arc;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@@ -55,12 +55,12 @@ const QUEUE_BYTES_SIZE: usize = align!(
DESC_TABLE_ALIGN_SIZE
) as usize;
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < TAP_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM
|| bytes.len()
> TAP_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + MEM_SIZE
{
return;
return Corpus::Reject;
}
let (dummy_tap_frontend, dummy_tap_backend) = create_socketpair().unwrap();
@@ -111,10 +111,10 @@ fuzz_target!(|bytes| {
.write_slice(queue_bytes, GuestAddress(BASE_VIRT_QUEUE_ADDR))
.is_err()
{
return;
return Corpus::Reject;
}
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@@ -155,6 +155,8 @@ fuzz_target!(|bytes| {
// Terminate the thread for the dummy tap backend
exit_evt.write(1).ok();
tap_backend_thread.join().unwrap();
return Corpus::Keep;
});
pub struct NoopVirtioInterrupt {}

View File

@@ -10,7 +10,7 @@ use std::sync::Arc;
use std::{ffi, io};
use libc::{MAP_NORESERVE, MAP_PRIVATE, PROT_READ, PROT_WRITE};
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{Pmem, UserspaceMapping, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@@ -35,9 +35,9 @@ 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| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < QUEUE_DATA_SIZE || bytes.len() > (QUEUE_DATA_SIZE + MEM_SIZE) {
return;
return Corpus::Reject;
}
let mut pmem = create_dummy_pmem();
@@ -50,7 +50,7 @@ fuzz_target!(|bytes| {
// 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;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@@ -69,6 +69,8 @@ fuzz_target!(|bytes| {
// Wait for the events to finish and pmem device worker thread to return
pmem.wait_for_epoll_threads();
Corpus::Keep
});
fn memfd_create_with_size(name: &ffi::CStr, flags: u32, size: usize) -> Result<RawFd, io::Error> {

View File

@@ -12,14 +12,14 @@ use std::mem::size_of;
use std::os::unix::io::{FromRawFd, RawFd};
use block::qcow::{QcowFile, RawFile};
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
// Take the first 64 bits of data as an address and the next 64 bits as data to
// store there. The rest of the data is used as a qcow image.
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < 16 {
// Need an address and data, each are 8 bytes.
return;
return Corpus::Reject;
}
let mut disk_image = Cursor::new(bytes);
let addr = read_u64(&mut disk_image);
@@ -33,6 +33,8 @@ fuzz_target!(|bytes| {
let _ = qcow.write_all(&value.to_le_bytes());
}
}
Corpus::Keep
});
fn read_u64<T: Read>(readable: &mut T) -> u64 {

View File

@@ -7,7 +7,7 @@
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::Arc;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@@ -52,11 +52,11 @@ const USED_RING_ADDR: u64 = align!(AVAIL_RING_ADDR + AVAIL_RING_SIZE, USED_RING_
// Virtio-queue size in bytes
const QUEUE_BYTES_SIZE: usize = (USED_RING_ADDR + USED_RING_SIZE - DESC_TABLE_ADDR) as usize;
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE)
|| bytes.len() > (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE + MEM_SIZE)
{
return;
return Corpus::Reject;
}
let mut rng = virtio_devices::Rng::new(
@@ -86,10 +86,10 @@ fuzz_target!(|bytes| {
.write_slice(queue_bytes, GuestAddress(DESC_TABLE_ADDR))
.is_err()
{
return;
return Corpus::Reject;
}
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@@ -108,6 +108,8 @@ fuzz_target!(|bytes| {
// Wait for the events to finish and rng device worker thread to return
rng.wait_for_epoll_threads();
Corpus::Keep
});
pub struct NoopVirtioInterrupt {}

View File

@@ -9,38 +9,47 @@ use std::io::{self, Read, Seek, SeekFrom, Write};
use std::os::unix::io::{FromRawFd, RawFd};
use block::vhdx::Vhdx;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
// Populate the corpus directory with a test file:
// truncate -s 16M /tmp/source
// qemu-img convert -O vhdx /tmp/source fuzz/corpus/vhdx/test.vhdx
// Run with:
// cargo fuzz run vhdx -j 32 -- -max_len=16777216
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
let shm = memfd_create(&ffi::CString::new("fuzz").unwrap(), 0).unwrap();
let mut disk_file: File = unsafe { File::from_raw_fd(shm) };
disk_file.write_all(&bytes[..]).unwrap();
disk_file.seek(SeekFrom::Start(0)).unwrap();
if let Ok(mut vhdx) = Vhdx::new(disk_file) {
if vhdx.seek(SeekFrom::Start(0)).is_ok() {
let mut offset = 0;
while offset < bytes.len() {
let mut data = vec![0; 8192];
vhdx.read_exact(&mut data).ok();
offset += data.len();
}
}
let mut vhdx = match Vhdx::new(disk_file) {
Ok(vhdx) => vhdx,
Err(_) => return Corpus::Reject,
};
if vhdx.seek(SeekFrom::Start(0)).is_ok() {
let mut offset = 0;
while offset < bytes.len() {
let data = vec![0; 8192];
vhdx.write_all(&data).ok();
offset += data.len();
}
}
if matches!(vhdx.seek(SeekFrom::Start(0)).is_err(), true) {
return Corpus::Reject;
};
let mut offset = 0;
while offset < bytes.len() {
let mut data = vec![0; 8192];
vhdx.read_exact(&mut data).ok();
offset += data.len();
}
if matches!(vhdx.seek(SeekFrom::Start(0)).is_err(), true) {
return Corpus::Reject;
};
offset = 0;
while offset < bytes.len() {
let data = vec![0; 8192];
vhdx.write_all(&data).ok();
offset += data.len();
}
Corpus::Keep
});
fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> {

148
fuzz/fuzz_targets/vsock.rs Normal file
View File

@@ -0,0 +1,148 @@
// Copyright © 2025 Microsoft
//
// SPDX-License-Identifier: Apache-2.0
#![no_main]
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::Arc;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::vsock::tests::TestBackend;
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
use vm_memory::bitmap::AtomicBitmap;
use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic};
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
use vmm_sys_util::tempdir::TempDir;
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
macro_rules! align {
($n:expr, $align:expr) => {{
$n.div_ceil($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;
// Available 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: &[u8]| -> Corpus {
if bytes.len() < (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE)
|| bytes.len() > (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE + MEM_SIZE)
{
return Corpus::Reject;
}
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..];
let q = setup_virt_queue(queue_data.try_into().unwrap());
let tmp_dir = TempDir::new_with_prefix("/tmp/fuzz_virtio_vsock").unwrap();
let vsock_path = tmp_dir.as_path().join("vsock.sock");
let backend = TestBackend::new();
// 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 Corpus::Reject;
}
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return Corpus::Reject;
}
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();
let mut vsock = virtio_devices::Vsock::new(
"fuzzer_vsock".to_owned(),
0,
vsock_path,
backend,
false,
SeccompAction::Allow,
EventFd::new(EFD_NONBLOCK).unwrap(),
None,
)
.unwrap();
vsock
.activate(
guest_memory,
Arc::new(NoopVirtioInterrupt {}),
vec![(0, q, evt)],
)
.ok();
// Wait for the events to finish and vsock device worker thread to return
vsock.wait_for_epoll_threads();
Corpus::Keep
});
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

@@ -7,7 +7,7 @@
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::Arc;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@@ -30,9 +30,9 @@ 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| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < QUEUE_DATA_SIZE || bytes.len() > (QUEUE_DATA_SIZE + MEM_SIZE) {
return;
return Corpus::Reject;
}
let mut watchdog = virtio_devices::Watchdog::new(
@@ -53,7 +53,7 @@ fuzz_target!(|bytes| {
// 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;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@@ -73,6 +73,8 @@ fuzz_target!(|bytes| {
// Wait for the events to finish and watchdog device worker thread to return
watchdog.wait_for_epoll_threads();
Corpus::Keep
});
pub struct NoopVirtioInterrupt {}

View File

@@ -0,0 +1,153 @@
// Copyright © 2025 Microsoft Corporation
//
// SPDX-License-Identifier: Apache-2.0
#![no_main]
use hypervisor::arch::emulator::{PlatformEmulator, PlatformError};
use hypervisor::arch::x86::emulator::{Emulator, EmulatorCpuState};
use hypervisor::arch::x86::{DescriptorTable, SegmentRegister, SpecialRegisters};
use hypervisor::StandardRegisters;
use libfuzzer_sys::{fuzz_target, Corpus};
#[derive(Debug)]
struct EmulatorContext {
state: EmulatorCpuState,
memory: [u8; 8],
}
impl PlatformEmulator for EmulatorContext {
type CpuState = EmulatorCpuState;
fn read_memory(&self, _gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
data.copy_from_slice(&self.memory[..data.len()]);
Ok(())
}
fn write_memory(&mut self, _gva: u64, _data: &[u8]) -> Result<(), PlatformError> {
// Discard writes
Ok(())
}
fn cpu_state(&self, _cpu_id: usize) -> Result<Self::CpuState, PlatformError> {
Ok(self.state.clone())
}
fn set_cpu_state(&self, _cpu_id: usize, _state: Self::CpuState) -> Result<(), PlatformError> {
// Ignore
Ok(())
}
fn fetch(&self, _ip: u64, _data: &mut [u8]) -> Result<(), PlatformError> {
// The fuzzer already provides 16 bytes of data, we don't need to fetch anything
panic!("fetch should not be called");
}
}
fuzz_target!(|bytes: &[u8]| -> Corpus {
let (mut ctx, insn) = match generate_context_and_instruction(bytes) {
Ok((ctx, insn)) => (ctx, insn),
Err(_) => return Corpus::Reject,
};
let mut e = Emulator::new(&mut ctx);
if e.emulate_first_insn(0, &insn).is_err() {
return Corpus::Reject;
}
Corpus::Keep
});
// Helper functions to generate structures from fuzzer input below
fn generate_segment_register(
u: &mut arbitrary::Unstructured<'_>,
) -> arbitrary::Result<SegmentRegister> {
Ok(SegmentRegister {
base: u.arbitrary()?,
limit: u.arbitrary()?,
selector: u.arbitrary()?,
avl: u.arbitrary()?,
dpl: u.arbitrary()?,
db: u.arbitrary()?,
g: u.arbitrary()?,
l: u.arbitrary()?,
present: u.arbitrary()?,
s: u.arbitrary()?,
type_: u.arbitrary()?,
unusable: u.arbitrary()?,
})
}
fn generate_descriptor_table(
u: &mut arbitrary::Unstructured<'_>,
) -> arbitrary::Result<DescriptorTable> {
Ok(DescriptorTable {
base: u.arbitrary()?,
limit: u.arbitrary()?,
})
}
fn generate_context_and_instruction(
bytes: &[u8],
) -> arbitrary::Result<(EmulatorContext, [u8; 16])> {
let mut u = arbitrary::Unstructured::new(bytes);
let mut regs = mshv_bindings::StandardRegisters {
rax: u.arbitrary()?,
rbx: u.arbitrary()?,
rcx: u.arbitrary()?,
rdx: u.arbitrary()?,
rsi: u.arbitrary()?,
rdi: u.arbitrary()?,
rsp: u.arbitrary()?,
rbp: u.arbitrary()?,
r8: u.arbitrary()?,
r9: u.arbitrary()?,
r10: u.arbitrary()?,
r11: u.arbitrary()?,
r12: u.arbitrary()?,
r13: u.arbitrary()?,
r14: u.arbitrary()?,
r15: u.arbitrary()?,
rip: u.arbitrary()?,
rflags: u.arbitrary()?,
};
// Cap RCX to avoid looping for too long for reps instructions.
regs.rcx &= 0xFFFFu64;
let regs = StandardRegisters::Mshv(regs);
let sregs = SpecialRegisters {
cs: generate_segment_register(&mut u)?,
ds: generate_segment_register(&mut u)?,
es: generate_segment_register(&mut u)?,
fs: generate_segment_register(&mut u)?,
gs: generate_segment_register(&mut u)?,
ss: generate_segment_register(&mut u)?,
tr: generate_segment_register(&mut u)?,
ldt: generate_segment_register(&mut u)?,
gdt: generate_descriptor_table(&mut u)?,
idt: generate_descriptor_table(&mut u)?,
cr0: u.arbitrary()?,
cr2: u.arbitrary()?,
cr3: u.arbitrary()?,
cr4: u.arbitrary()?,
cr8: u.arbitrary()?,
efer: u.arbitrary()?,
apic_base: u.arbitrary()?,
interrupt_bitmap: u.arbitrary()?,
};
let memory = u.arbitrary::<[u8; 8]>()?;
let insn = u.arbitrary::<[u8; 16]>()?;
let ctx = EmulatorContext {
state: EmulatorCpuState { regs, sregs },
memory,
};
Ok((ctx, insn))
}

View File

@@ -7,20 +7,22 @@ version = "0.1.0"
[features]
kvm = ["kvm-bindings", "kvm-ioctls", "vfio-ioctls/kvm"]
mshv = ["iced-x86", "mshv-bindings", "mshv-ioctls", "vfio-ioctls/mshv"]
mshv = ["mshv-bindings", "mshv-ioctls", "mshv_emulator", "vfio-ioctls/mshv"]
mshv_emulator = ["iced-x86", "mshv-bindings"]
sev_snp = ["igvm", "igvm_defs"]
tdx = []
[dependencies]
anyhow = "1.0.87"
anyhow = "1.0.94"
arc-swap = "1.7.1"
byteorder = "1.5.0"
cfg-if = "1.0.0"
concat-idents = "1.1.5"
igvm = { version = "0.3.3", optional = true }
igvm_defs = { version = "0.3.1", optional = true }
igvm = { workspace = true, optional = true }
igvm_defs = { workspace = true, optional = true }
kvm-bindings = { workspace = true, optional = true, features = ["serde"] }
kvm-ioctls = { workspace = true, optional = true }
libc = "0.2.158"
libc = "0.2.167"
log = "0.4.22"
mshv-bindings = { workspace = true, features = [
"fam-wrappers",
@@ -31,14 +33,27 @@ serde = { version = "1.0.208", features = ["derive", "rc"] }
serde_with = { version = "3.9.0", default-features = false, features = [
"macros",
] }
thiserror = "1.0.62"
thiserror = "2.0.6"
vfio-ioctls = { workspace = true, default-features = false }
vm-memory = { workspace = true, features = ["backend-atomic", "backend-mmap"] }
vm-memory = { workspace = true, features = [
"backend-atomic",
"backend-bitmap",
"backend-mmap",
] }
vmm-sys-util = { workspace = true, features = ["with-serde"] }
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
default-features = false
features = ["decoder", "fast_fmt", "instr_info", "op_code_info", "std"]
features = [
"decoder",
"instr_info",
"no_d3now",
"no_evex",
"no_vex",
"no_xop",
"op_code_info",
"std",
]
optional = true
version = "1.21.0"

View File

@@ -21,3 +21,6 @@ pub mod x86;
#[cfg(target_arch = "aarch64")]
pub mod aarch64;
#[cfg(target_arch = "riscv64")]
pub mod riscv64;

View File

@@ -0,0 +1,63 @@
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
use std::any::Any;
use std::result;
use thiserror::Error;
use crate::{AiaState, HypervisorDeviceError, HypervisorVmError};
/// Errors thrown while setting up the VAIA.
#[derive(Debug, Error)]
pub enum Error {
/// Error while calling KVM ioctl for setting up the global interrupt controller.
#[error("Failed creating AIA device: {0}")]
CreateAia(HypervisorVmError),
/// Error while setting device attributes for the AIA.
#[error("Failed setting device attributes for the AIA: {0}")]
SetDeviceAttribute(HypervisorDeviceError),
/// Error while getting device attributes for the AIA.
#[error("Failed getting device attributes for the AIA: {0}")]
GetDeviceAttribute(HypervisorDeviceError),
}
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug)]
pub struct VaiaConfig {
pub vcpu_count: u32,
pub aplic_addr: u64,
pub imsic_addr: u64,
pub nr_irqs: u32,
}
/// Hypervisor agnostic interface for a virtualized AIA
pub trait Vaia: Send + Sync {
/// Returns the compatibility property of APLIC
fn aplic_compatibility(&self) -> &str;
/// Returns an array with APLIC device properties
fn aplic_properties(&self) -> [u32; 4];
/// Returns the compatibility property of IMSIC
fn imsic_compatibility(&self) -> &str;
/// Returns an array with IMSIC device properties
fn imsic_properties(&self) -> [u32; 4];
/// Returns the number of vCPUs this AIA handles
fn vcpu_count(&self) -> u32;
/// Returns whether the AIA device is MSI compatible or not
fn msi_compatible(&self) -> bool;
/// Downcast the trait object to its concrete type.
fn as_any_concrete_mut(&mut self) -> &mut dyn Any;
/// Save the state of AiaImsics.
fn state(&self) -> Result<AiaState>;
/// Restore the state of AiaImsics.
fn set_state(&mut self, state: &AiaState) -> Result<()>;
}

View File

@@ -0,0 +1,5 @@
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
pub mod aia;

View File

@@ -116,14 +116,14 @@ fn memory_operand_address<T: CpuStateManager>(
if insn.memory_base() != iced_x86::Register::None {
let base: u64 = state.read_reg(insn.memory_base())?;
address += base;
address = address.wrapping_add(base);
}
if insn.memory_index() != iced_x86::Register::None {
let mut index: u64 = state.read_reg(insn.memory_index())?;
index *= insn.memory_index_scale() as u64;
index = index.wrapping_mul(insn.memory_index_scale() as u64);
address += index;
address = address.wrapping_add(index);
}
address = address.wrapping_add(insn.memory_displacement64());
@@ -140,16 +140,3 @@ pub trait InstructionHandler<T: CpuStateManager> {
platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<(), EmulationError<Exception>>;
}
macro_rules! insn_format {
($insn:ident) => {{
let mut output = String::new();
let mut formatter = FastFormatter::new();
formatter
.options_mut()
.set_space_after_operand_separator(true);
formatter.format(&$insn, &mut output);
output
}};
}

View File

@@ -185,7 +185,7 @@ pub trait CpuStateManager: Clone {
)));
}
Ok(logical_addr + segment_register.base)
Ok(logical_addr.wrapping_add(segment_register.base))
}
_ => Err(PlatformError::UnsupportedCpuMode(anyhow!("{:?}", mode))),
@@ -599,8 +599,8 @@ impl<T: CpuStateManager> Emulator<'_, T> {
decoder.decode_out(&mut insn);
if decoder.last_error() != DecoderError::None {
return Err(EmulationError::InstructionFetchingError(anyhow!(
"{:#x?}",
insn_format!(insn)
"{:?}",
insn.code()
)));
}
}
@@ -609,14 +609,17 @@ impl<T: CpuStateManager> Emulator<'_, T> {
Emulator::get_handler(insn.code())
.ok_or_else(|| {
EmulationError::UnsupportedInstruction(anyhow!(
"{:#x?} {:?} {:?}",
insn_format!(insn),
insn.mnemonic(),
insn.code()
"{:?} {:x?}",
insn.code(),
insn_stream
))
})?
.emulate(&insn, &mut state, self.platform)
.context(anyhow!("Failed to emulate {:#x?}", insn_format!(insn)))?;
.context(anyhow!(
"Failed to emulate {:?} {:x?}",
insn.code(),
insn_stream
))?;
last_decoded_ip = decoder.ip();
num_insn_emulated += 1;

View File

@@ -11,7 +11,9 @@
// Copyright © 2020, Microsoft Corporation
//
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
use core::fmt;
#[cfg(all(feature = "mshv_emulator", target_arch = "x86_64"))]
pub mod emulator;
pub mod gdt;
#[allow(non_camel_case_types)]
@@ -211,6 +213,22 @@ pub struct CpuIdEntry {
pub edx: u32,
}
impl fmt::Display for CpuIdEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"function = 0x{:08x} \
index = 0x{:08x} \
eax = 0x{:08x} \
ebx = 0x{:08x} \
ecx = 0x{:08x} \
edx = 0x{:08x} \
flags = 0x{:08x}",
self.function, self.index, self.eax, self.ebx, self.ecx, self.edx, self.flags
)
}
}
pub const CPUID_FLAG_VALID_INDEX: u32 = 1;
#[derive(Default, Debug, Clone, serde::Deserialize, serde::Serialize)]

View File

@@ -1,3 +1,5 @@
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
//
// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
@@ -8,15 +10,21 @@
//
//
#[cfg(target_arch = "aarch64")]
use std::sync::Arc;
use thiserror::Error;
#[cfg(not(target_arch = "riscv64"))]
use vm_memory::GuestAddress;
#[cfg(target_arch = "aarch64")]
use crate::aarch64::{RegList, VcpuInit};
#[cfg(target_arch = "x86_64")]
use crate::arch::x86::{CpuIdEntry, FpuState, LapicState, MsrEntry, SpecialRegisters};
#[cfg(feature = "tdx")]
use crate::kvm::{TdxExitDetails, TdxExitStatus};
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
use crate::RegList;
#[cfg(target_arch = "aarch64")]
use crate::VcpuInit;
use crate::{CpuState, MpState, StandardRegisters};
#[cfg(target_arch = "x86_64")]
@@ -216,6 +224,16 @@ pub enum HypervisorCpuError {
#[error("Failed to set aarch64 core register: {0}")]
SetAarchCoreRegister(#[source] anyhow::Error),
///
/// Getting RISC-V 64-bit core register error
///
#[error("Failed to get riscv64 core register: {0}")]
GetRiscvCoreRegister(#[source] anyhow::Error),
///
/// Setting RISC-V 64-bit core register error
///
#[error("Failed to set riscv64 core register: {0}")]
SetRiscvCoreRegister(#[source] anyhow::Error),
///
/// Getting registers list error
///
#[error("Failed to retrieve list of registers: {0}")]
@@ -231,6 +249,16 @@ pub enum HypervisorCpuError {
#[error("Failed to set system register: {0}")]
SetSysRegister(#[source] anyhow::Error),
///
/// Getting RISC-V 64-bit non-core register error
///
#[error("Failed to get non-core register: {0}")]
GetNonCoreRegister(#[source] anyhow::Error),
///
/// Setting RISC-V 64-bit non-core register error
///
#[error("Failed to set non-core register: {0}")]
SetNonCoreRegister(#[source] anyhow::Error),
///
/// GVA translation error
///
#[error("Failed to translate GVA: {0}")]
@@ -412,6 +440,7 @@ pub trait Vcpu: Send + Sync {
///
/// Sets debug registers to set hardware breakpoints and/or enable single step.
///
#[cfg(not(target_arch = "riscv64"))]
fn set_guest_debug(&self, _addrs: &[GuestAddress], _singlestep: bool) -> Result<()> {
Err(HypervisorCpuError::SetDebugRegs(anyhow!("unimplemented")))
}
@@ -423,12 +452,31 @@ pub trait Vcpu: Send + Sync {
#[cfg(target_arch = "aarch64")]
fn vcpu_finalize(&self, feature: i32) -> Result<()>;
///
/// Gets the features that have been finalized for a given CPU.
///
#[cfg(target_arch = "aarch64")]
fn vcpu_get_finalized_features(&self) -> i32;
///
/// Sets processor features for a given CPU.
///
#[cfg(target_arch = "aarch64")]
fn vcpu_set_processor_features(
&self,
vm: &Arc<dyn crate::Vm>,
kvi: &mut VcpuInit,
id: u8,
) -> Result<()>;
///
/// Returns VcpuInit with default value set
///
#[cfg(target_arch = "aarch64")]
fn create_vcpu_init(&self) -> VcpuInit;
///
/// 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 = "aarch64", target_arch = "riscv64"))]
fn get_reg_list(&self, reg_list: &mut RegList) -> Result<()>;
///
/// Gets the value of a system register
@@ -436,9 +484,14 @@ pub trait Vcpu: Send + Sync {
#[cfg(target_arch = "aarch64")]
fn get_sys_reg(&self, sys_reg: u32) -> Result<u64>;
///
/// Gets the value of a non-core register on RISC-V 64-bit
///
#[cfg(target_arch = "riscv64")]
fn get_non_core_reg(&self, non_core_reg: u32) -> Result<u64>;
///
/// Configure core registers for a given CPU.
///
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
fn setup_regs(&self, cpu_id: u8, boot_ip: u64, fdt_start: u64) -> Result<()>;
///
/// Check if the CPU supports PMU

View File

@@ -118,6 +118,17 @@ pub trait Hypervisor: Send + Sync {
fn create_vm_with_type(&self, _vm_type: u64) -> Result<Arc<dyn Vm>> {
unreachable!()
}
///
/// Create a Vm of a specific type using the underlying hypervisor, passing memory size
/// Return a hypervisor-agnostic Vm trait object
///
fn create_vm_with_type_and_memory(
&self,
_vm_type: u64,
#[cfg(feature = "sev_snp")] _mem_size: u64,
) -> Result<Arc<dyn Vm>> {
unreachable!()
}
#[cfg(target_arch = "x86_64")]
///
/// Get the supported CpuID

View File

@@ -103,9 +103,8 @@ fn dist_attr_get(gic: &DeviceFd, offset: u32) -> Result<u32> {
flags: 0,
};
// get_device_attr should be marked as unsafe, and will be in future.
// SAFETY: gic_dist_attr.addr is safe to write to.
gic.get_device_attr(&mut gic_dist_attr).map_err(|e| {
unsafe { gic.get_device_attr(&mut gic_dist_attr) }.map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;
@@ -131,9 +130,8 @@ fn get_interrupts_num(gic: &DeviceFd) -> Result<u32> {
addr: &mut num_irq as *mut u32 as u64,
flags: 0,
};
// get_device_attr should be marked as unsafe, and will be in future.
// SAFETY: nr_irqs_attr.addr is safe to write to.
gic.get_device_attr(&mut nr_irqs_attr).map_err(|e| {
unsafe { gic.get_device_attr(&mut nr_irqs_attr) }.map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;
Ok(num_irq)

View File

@@ -105,9 +105,8 @@ fn icc_attr_get(gic: &DeviceFd, offset: u64, typer: u64) -> Result<u32> {
flags: 0,
};
// get_device_attr should be marked as unsafe, and will be in future.
// SAFETY: gic_icc_attr.addr is safe to write to.
gic.get_device_attr(&mut gic_icc_attr).map_err(|e| {
unsafe { gic.get_device_attr(&mut gic_icc_attr) }.map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;

View File

@@ -49,13 +49,10 @@ fn gicv3_its_attr_get(its_device: &DeviceFd, group: u32, attr: u32) -> Result<u6
flags: 0,
};
// get_device_attr should be marked as unsafe, and will be in future.
// SAFETY: gicv3_its_attr.addr is safe to write to.
its_device
.get_device_attr(&mut gicv3_its_attr)
.map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;
unsafe { its_device.get_device_attr(&mut gicv3_its_attr) }.map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;
Ok(val)
}

View File

@@ -7,11 +7,11 @@ use kvm_ioctls::DeviceFd;
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,
kvm_device_attr, kvm_one_reg, 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, VcpuKvmState};
use crate::kvm::VcpuKvmState;
use crate::CpuState;
// Relevant redistributor registers that we want to save/restore.
@@ -121,9 +121,8 @@ fn redist_attr_get(gic: &DeviceFd, offset: u32, typer: u64) -> Result<u32> {
flags: 0,
};
// get_device_attr should be marked as unsafe, and will be in future.
// SAFETY: gic_redist_attr.addr is safe to write to.
gic.get_device_attr(&mut gic_redist_attr).map_err(|e| {
unsafe { gic.get_device_attr(&mut gic_redist_attr) }.map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;
@@ -214,7 +213,7 @@ pub fn construct_gicr_typers(vcpu_states: &[CpuState]) -> Vec<u64> {
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
let mpidr: Vec<kvm_one_reg> = state
.sys_regs
.into_iter()
.filter(|reg| reg.id == KVM_ARM64_SYSREG_MPIDR_EL1)
@@ -222,7 +221,7 @@ pub fn construct_gicr_typers(vcpu_states: &[CpuState]) -> Vec<u64> {
//calculate affinity
let mut cpu_affid = mpidr[0].addr & 1095233437695;
cpu_affid = ((cpu_affid & 0xFF00000000) >> 8) | (cpu_affid & 0xFFFFFF);
gicr_typers.push((cpu_affid << 32) | (1 << 24) | (index as u64) << 8 | (last << 4));
gicr_typers.push((cpu_affid << 32) | (1 << 24) | ((index as u64) << 8) | (last << 4));
}
gicr_typers

View File

@@ -14,7 +14,6 @@ use kvm_bindings::{
kvm_mp_state, kvm_one_reg, kvm_regs, KVM_REG_ARM_COPROC_MASK, KVM_REG_ARM_CORE,
KVM_REG_SIZE_MASK, KVM_REG_SIZE_U32, KVM_REG_SIZE_U64,
};
pub use kvm_bindings::{kvm_one_reg as Register, kvm_vcpu_init as VcpuInit, RegList};
pub use kvm_ioctls::{Cap, Kvm};
use serde::{Deserialize, Serialize};

View File

@@ -1,3 +1,5 @@
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
//
// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
@@ -28,20 +30,28 @@ use vmm_sys_util::eventfd::EventFd;
use crate::aarch64::gic::KvmGicV3Its;
#[cfg(target_arch = "aarch64")]
pub use crate::aarch64::{
check_required_kvm_extensions, gic::Gicv3ItsState as GicState, is_system_register, VcpuInit,
VcpuKvmState,
check_required_kvm_extensions, gic::Gicv3ItsState as GicState, is_system_register, VcpuKvmState,
};
#[cfg(target_arch = "aarch64")]
use crate::arch::aarch64::gic::{Vgic, VgicConfig};
#[cfg(target_arch = "riscv64")]
use crate::arch::riscv64::aia::{Vaia, VaiaConfig};
#[cfg(target_arch = "riscv64")]
use crate::riscv64::aia::KvmAiaImsics;
#[cfg(target_arch = "riscv64")]
pub use crate::riscv64::{
aia::AiaImsicsState as AiaState, check_required_kvm_extensions, is_non_core_register,
VcpuKvmState,
};
use crate::vm::{self, InterruptSourceConfig, VmOps};
#[cfg(target_arch = "aarch64")]
use crate::{arm64_core_reg_id, offset_of};
use crate::{cpu, hypervisor, vec_with_array_field, HypervisorType};
#[cfg(target_arch = "riscv64")]
use crate::{offset_of, riscv64_reg_id};
// x86_64 dependencies
#[cfg(target_arch = "x86_64")]
pub mod x86_64;
#[cfg(target_arch = "aarch64")]
use aarch64::{RegList, Register};
#[cfg(target_arch = "x86_64")]
use kvm_bindings::{
kvm_enable_cap, kvm_msr_entry, MsrList, KVM_CAP_HYPERV_SYNIC, KVM_CAP_SPLIT_IRQCHIP,
@@ -65,12 +75,21 @@ use crate::{
// aarch64 dependencies
#[cfg(target_arch = "aarch64")]
pub mod aarch64;
// riscv64 dependencies
#[cfg(target_arch = "riscv64")]
pub mod riscv64;
#[cfg(target_arch = "aarch64")]
use std::mem;
///
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
///
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
pub use kvm_bindings::kvm_vcpu_events as VcpuEvents;
pub use kvm_bindings::{
kvm_clock_data, kvm_create_device, kvm_device_type_KVM_DEV_TYPE_VFIO, kvm_guest_debug,
kvm_irq_routing, kvm_irq_routing_entry, kvm_mp_state, kvm_userspace_memory_region,
kvm_clock_data, kvm_create_device, kvm_create_device as CreateDevice,
kvm_device_attr as DeviceAttr, kvm_device_type_KVM_DEV_TYPE_VFIO, kvm_guest_debug,
kvm_irq_routing, kvm_irq_routing_entry, kvm_mp_state, kvm_run, kvm_userspace_memory_region,
KVM_GUESTDBG_ENABLE, KVM_GUESTDBG_SINGLESTEP, KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI,
KVM_MEM_LOG_DIRTY_PAGES, KVM_MEM_READONLY, KVM_MSI_VALID_DEVID,
};
@@ -81,21 +100,19 @@ use kvm_bindings::{
KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP1_MASK, KVM_REG_ARM64_SYSREG_OP2_MASK,
KVM_REG_ARM_CORE, KVM_REG_SIZE_U128, KVM_REG_SIZE_U32, KVM_REG_SIZE_U64,
};
#[cfg(target_arch = "riscv64")]
use kvm_bindings::{kvm_riscv_core, user_regs_struct, KVM_REG_RISCV_CORE};
#[cfg(feature = "tdx")]
use kvm_bindings::{kvm_run__bindgen_ty_1, KVMIO};
pub use kvm_ioctls::{Cap, Kvm};
pub use kvm_ioctls::{Cap, Kvm, VcpuExit};
use thiserror::Error;
use vfio_ioctls::VfioDeviceFd;
#[cfg(feature = "tdx")]
use vmm_sys_util::{ioctl::ioctl_with_val, ioctl_ioc_nr, ioctl_iowr_nr};
pub use {kvm_bindings, kvm_ioctls};
///
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
///
pub use {
kvm_bindings::kvm_create_device as CreateDevice, kvm_bindings::kvm_device_attr as DeviceAttr,
kvm_bindings::kvm_run, kvm_bindings::kvm_vcpu_events as VcpuEvents, kvm_ioctls::VcpuExit,
};
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
use crate::RegList;
#[cfg(target_arch = "x86_64")]
const KVM_CAP_SGX_ATTRIBUTE: u32 = 196;
@@ -334,12 +351,69 @@ impl From<ClockData> for kvm_clock_data {
}
}
impl From<kvm_bindings::kvm_one_reg> for crate::Register {
fn from(s: kvm_bindings::kvm_one_reg) -> Self {
crate::Register::Kvm(s)
}
}
impl From<crate::Register> for kvm_bindings::kvm_one_reg {
fn from(e: crate::Register) -> Self {
match e {
crate::Register::Kvm(e) => e,
/* Needed in case other hypervisors are enabled */
#[allow(unreachable_patterns)]
_ => panic!("Register is not valid"),
}
}
}
#[cfg(target_arch = "aarch64")]
impl From<kvm_bindings::kvm_vcpu_init> for crate::VcpuInit {
fn from(s: kvm_bindings::kvm_vcpu_init) -> Self {
crate::VcpuInit::Kvm(s)
}
}
#[cfg(target_arch = "aarch64")]
impl From<crate::VcpuInit> for kvm_bindings::kvm_vcpu_init {
fn from(e: crate::VcpuInit) -> Self {
match e {
crate::VcpuInit::Kvm(e) => e,
/* Needed in case other hypervisors are enabled */
#[allow(unreachable_patterns)]
_ => panic!("VcpuInit is not valid"),
}
}
}
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
impl From<kvm_bindings::RegList> for crate::RegList {
fn from(s: kvm_bindings::RegList) -> Self {
crate::RegList::Kvm(s)
}
}
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
impl From<crate::RegList> for kvm_bindings::RegList {
fn from(e: crate::RegList) -> Self {
match e {
crate::RegList::Kvm(e) => e,
/* Needed in case other hypervisors are enabled */
#[allow(unreachable_patterns)]
_ => panic!("RegList is not valid"),
}
}
}
#[cfg(not(target_arch = "riscv64"))]
impl From<kvm_bindings::kvm_regs> for crate::StandardRegisters {
fn from(s: kvm_bindings::kvm_regs) -> Self {
crate::StandardRegisters::Kvm(s)
}
}
#[cfg(not(target_arch = "riscv64"))]
impl From<crate::StandardRegisters> for kvm_bindings::kvm_regs {
fn from(e: crate::StandardRegisters) -> Self {
match e {
@@ -351,6 +425,25 @@ impl From<crate::StandardRegisters> for kvm_bindings::kvm_regs {
}
}
#[cfg(target_arch = "riscv64")]
impl From<kvm_bindings::kvm_riscv_core> for crate::StandardRegisters {
fn from(s: kvm_bindings::kvm_riscv_core) -> Self {
crate::StandardRegisters::Kvm(s)
}
}
#[cfg(target_arch = "riscv64")]
impl From<crate::StandardRegisters> for kvm_bindings::kvm_riscv_core {
fn from(e: crate::StandardRegisters) -> Self {
match e {
crate::StandardRegisters::Kvm(e) => e,
/* Needed in case other hypervisors are enabled */
#[allow(unreachable_patterns)]
_ => panic!("StandardRegisters are not valid"),
}
}
}
impl From<kvm_irq_routing_entry> for IrqRoutingEntry {
fn from(s: kvm_irq_routing_entry) -> Self {
IrqRoutingEntry::Kvm(s)
@@ -433,6 +526,7 @@ impl vm::Vm for KvmVm {
.map_err(|e| vm::HypervisorVmError::SetTssAddress(e.into()))
}
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
///
/// Creates an in-kernel interrupt controller.
///
@@ -493,6 +587,16 @@ impl vm::Vm for KvmVm {
Ok(Arc::new(Mutex::new(gic_device)))
}
#[cfg(target_arch = "riscv64")]
///
/// Creates a virtual AIA device.
///
fn create_vaia(&self, config: VaiaConfig) -> vm::Result<Arc<Mutex<dyn Vaia>>> {
let aia_device = KvmAiaImsics::new(self, config)
.map_err(|e| vm::HypervisorVmError::CreateVaia(anyhow!("Vaia error {:?}", e)))?;
Ok(Arc::new(Mutex::new(aia_device)))
}
///
/// Registers an event to be signaled whenever a certain address is written to.
///
@@ -563,7 +667,7 @@ impl vm::Vm for KvmVm {
// different `devid` to all the devices. Limitation is that at
// most 256 segments can be supported.
//
let modified_devid = (cfg.devid & 0x00ff_0000) >> 8 | cfg.devid & 0xff;
let modified_devid = ((cfg.devid & 0x00ff_0000) >> 8) | cfg.devid & 0xff;
kvm_route.flags = KVM_MSI_VALID_DEVID;
kvm_route.u.msi.__bindgen_anon_1.devid = modified_devid;
@@ -703,10 +807,13 @@ impl vm::Vm for KvmVm {
/// Returns the preferred CPU target type which can be emulated by KVM on underlying host.
///
#[cfg(target_arch = "aarch64")]
fn get_preferred_target(&self, kvi: &mut VcpuInit) -> vm::Result<()> {
fn get_preferred_target(&self, kvi: &mut crate::VcpuInit) -> vm::Result<()> {
let mut kvm_kvi: kvm_bindings::kvm_vcpu_init = (*kvi).into();
self.fd
.get_preferred_target(kvi)
.map_err(|e| vm::HypervisorVmError::GetPreferredTarget(e.into()))
.get_preferred_target(&mut kvm_kvi)
.map_err(|e| vm::HypervisorVmError::GetPreferredTarget(e.into()))?;
*kvi = kvm_kvi.into();
Ok(())
}
#[cfg(target_arch = "x86_64")]
@@ -1025,6 +1132,26 @@ impl hypervisor::Hypervisor for KvmHypervisor {
HypervisorType::Kvm
}
///
/// Create a Vm of a specific type using the underlying hypervisor, passing memory size
/// Return a hypervisor-agnostic Vm trait object
///
/// # Examples
///
/// ```
/// # use hypervisor::kvm::KvmHypervisor;
/// use hypervisor::kvm::KvmVm;
/// let hypervisor = KvmHypervisor::new().unwrap();
/// let vm = hypervisor.create_vm_with_type_and_memory(0).unwrap();
/// ```
fn create_vm_with_type_and_memory(
&self,
vm_type: u64,
#[cfg(feature = "sev_snp")] _mem_size: u64,
) -> hypervisor::Result<Arc<dyn vm::Vm>> {
self.create_vm_with_type(vm_type)
}
/// Create a KVM vm object of a specific VM type and return the object as Vm trait object
///
/// # Examples
@@ -1078,7 +1205,7 @@ impl hypervisor::Hypervisor for KvmHypervisor {
}))
}
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
{
Ok(Arc::new(KvmVm {
fd: vm_fd,
@@ -1161,6 +1288,7 @@ impl hypervisor::Hypervisor for KvmHypervisor {
Ok(data)
}
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
///
/// Get the number of supported hardware breakpoints
///
@@ -1208,7 +1336,14 @@ impl cpu::Vcpu for KvmVcpu {
/// Returns StandardRegisters with default value set
///
fn create_standard_regs(&self) -> StandardRegisters {
kvm_bindings::kvm_regs::default().into()
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
{
kvm_bindings::kvm_regs::default().into()
}
#[cfg(target_arch = "riscv64")]
{
kvm_bindings::kvm_riscv_core::default().into()
}
}
#[cfg(target_arch = "x86_64")]
///
@@ -1349,6 +1484,77 @@ impl cpu::Vcpu for KvmVcpu {
Ok(state.into())
}
#[cfg(target_arch = "riscv64")]
///
/// Returns the RISC-V vCPU core registers.
/// The `KVM_GET_REGS` ioctl is not available on RISC-V 64-bit,
/// `KVM_GET_ONE_REG` is used to get registers one by one.
///
fn get_regs(&self) -> cpu::Result<StandardRegisters> {
let mut state = kvm_riscv_core::default();
/// Macro used to extract RISC-V register data from KVM Vcpu according
/// to `$reg_name` provided to `state`.
macro_rules! riscv64_get_one_reg_from_vcpu {
(mode) => {
let off = offset_of!(kvm_riscv_core, mode);
let mut bytes = [0_u8; 8];
self.fd
.lock()
.unwrap()
.get_one_reg(riscv64_reg_id!(KVM_REG_RISCV_CORE, off), &mut bytes)
.map_err(|e| cpu::HypervisorCpuError::GetRiscvCoreRegister(e.into()))?;
state.mode = u64::from_le_bytes(bytes);
};
($reg_name:ident) => {
let off = offset_of!(kvm_riscv_core, regs, user_regs_struct, $reg_name);
let mut bytes = [0_u8; 8];
self.fd
.lock()
.unwrap()
.get_one_reg(riscv64_reg_id!(KVM_REG_RISCV_CORE, off), &mut bytes)
.map_err(|e| cpu::HypervisorCpuError::GetRiscvCoreRegister(e.into()))?;
state.regs.$reg_name = u64::from_le_bytes(bytes);
};
}
riscv64_get_one_reg_from_vcpu!(pc);
riscv64_get_one_reg_from_vcpu!(ra);
riscv64_get_one_reg_from_vcpu!(sp);
riscv64_get_one_reg_from_vcpu!(gp);
riscv64_get_one_reg_from_vcpu!(tp);
riscv64_get_one_reg_from_vcpu!(t0);
riscv64_get_one_reg_from_vcpu!(t1);
riscv64_get_one_reg_from_vcpu!(t2);
riscv64_get_one_reg_from_vcpu!(s0);
riscv64_get_one_reg_from_vcpu!(s1);
riscv64_get_one_reg_from_vcpu!(a0);
riscv64_get_one_reg_from_vcpu!(a1);
riscv64_get_one_reg_from_vcpu!(a2);
riscv64_get_one_reg_from_vcpu!(a3);
riscv64_get_one_reg_from_vcpu!(a4);
riscv64_get_one_reg_from_vcpu!(a5);
riscv64_get_one_reg_from_vcpu!(a6);
riscv64_get_one_reg_from_vcpu!(a7);
riscv64_get_one_reg_from_vcpu!(s2);
riscv64_get_one_reg_from_vcpu!(s3);
riscv64_get_one_reg_from_vcpu!(s4);
riscv64_get_one_reg_from_vcpu!(s5);
riscv64_get_one_reg_from_vcpu!(s6);
riscv64_get_one_reg_from_vcpu!(s7);
riscv64_get_one_reg_from_vcpu!(s8);
riscv64_get_one_reg_from_vcpu!(s9);
riscv64_get_one_reg_from_vcpu!(s10);
riscv64_get_one_reg_from_vcpu!(s11);
riscv64_get_one_reg_from_vcpu!(t3);
riscv64_get_one_reg_from_vcpu!(t4);
riscv64_get_one_reg_from_vcpu!(t5);
riscv64_get_one_reg_from_vcpu!(t6);
riscv64_get_one_reg_from_vcpu!(mode);
Ok(state.into())
}
#[cfg(target_arch = "x86_64")]
///
/// Sets the vCPU general purpose registers using the `KVM_SET_REGS` ioctl.
@@ -1483,6 +1689,81 @@ impl cpu::Vcpu for KvmVcpu {
Ok(())
}
#[cfg(target_arch = "riscv64")]
///
/// Sets the RISC-V vCPU core registers.
/// The `KVM_SET_REGS` ioctl is not available on RISC-V 64-bit,
/// `KVM_SET_ONE_REG` is used to set registers one by one.
///
fn set_regs(&self, state: &StandardRegisters) -> cpu::Result<()> {
// The function follows the exact identical order from `state`. Look there
// for some additional info on registers.
let kvm_regs_state: kvm_riscv_core = (*state).into();
/// Macro used to set value of specific RISC-V `$reg_name` stored in
/// `state` to KVM Vcpu.
macro_rules! riscv64_set_one_reg_to_vcpu {
(mode) => {
let off = offset_of!(kvm_riscv_core, mode);
self.fd
.lock()
.unwrap()
.set_one_reg(
riscv64_reg_id!(KVM_REG_RISCV_CORE, off),
&kvm_regs_state.mode.to_le_bytes(),
)
.map_err(|e| cpu::HypervisorCpuError::SetRiscvCoreRegister(e.into()))?;
};
($reg_name:ident) => {
let off = offset_of!(kvm_riscv_core, regs, user_regs_struct, $reg_name);
self.fd
.lock()
.unwrap()
.set_one_reg(
riscv64_reg_id!(KVM_REG_RISCV_CORE, off),
&kvm_regs_state.regs.$reg_name.to_le_bytes(),
)
.map_err(|e| cpu::HypervisorCpuError::SetRiscvCoreRegister(e.into()))?;
};
}
riscv64_set_one_reg_to_vcpu!(pc);
riscv64_set_one_reg_to_vcpu!(ra);
riscv64_set_one_reg_to_vcpu!(sp);
riscv64_set_one_reg_to_vcpu!(gp);
riscv64_set_one_reg_to_vcpu!(tp);
riscv64_set_one_reg_to_vcpu!(t0);
riscv64_set_one_reg_to_vcpu!(t1);
riscv64_set_one_reg_to_vcpu!(t2);
riscv64_set_one_reg_to_vcpu!(s0);
riscv64_set_one_reg_to_vcpu!(s1);
riscv64_set_one_reg_to_vcpu!(a0);
riscv64_set_one_reg_to_vcpu!(a1);
riscv64_set_one_reg_to_vcpu!(a2);
riscv64_set_one_reg_to_vcpu!(a3);
riscv64_set_one_reg_to_vcpu!(a4);
riscv64_set_one_reg_to_vcpu!(a5);
riscv64_set_one_reg_to_vcpu!(a6);
riscv64_set_one_reg_to_vcpu!(a7);
riscv64_set_one_reg_to_vcpu!(s2);
riscv64_set_one_reg_to_vcpu!(s3);
riscv64_set_one_reg_to_vcpu!(s4);
riscv64_set_one_reg_to_vcpu!(s5);
riscv64_set_one_reg_to_vcpu!(s6);
riscv64_set_one_reg_to_vcpu!(s7);
riscv64_set_one_reg_to_vcpu!(s8);
riscv64_set_one_reg_to_vcpu!(s9);
riscv64_set_one_reg_to_vcpu!(s10);
riscv64_set_one_reg_to_vcpu!(s11);
riscv64_set_one_reg_to_vcpu!(t3);
riscv64_set_one_reg_to_vcpu!(t4);
riscv64_set_one_reg_to_vcpu!(t5);
riscv64_set_one_reg_to_vcpu!(t6);
riscv64_set_one_reg_to_vcpu!(mode);
Ok(())
}
#[cfg(target_arch = "x86_64")]
///
/// Returns the vCPU special registers.
@@ -1812,6 +2093,7 @@ impl cpu::Vcpu for KvmVcpu {
Ok(())
}
#[cfg(not(target_arch = "riscv64"))]
///
/// Sets debug registers to set hardware breakpoints and/or enable single step.
///
@@ -1869,11 +2151,70 @@ impl cpu::Vcpu for KvmVcpu {
}
#[cfg(target_arch = "aarch64")]
fn vcpu_init(&self, kvi: &VcpuInit) -> cpu::Result<()> {
fn vcpu_get_finalized_features(&self) -> i32 {
kvm_bindings::KVM_ARM_VCPU_SVE as i32
}
#[cfg(target_arch = "aarch64")]
fn vcpu_set_processor_features(
&self,
vm: &Arc<dyn crate::Vm>,
kvi: &mut crate::VcpuInit,
id: u8,
) -> cpu::Result<()> {
use std::arch::is_aarch64_feature_detected;
#[allow(clippy::nonminimal_bool)]
let sve_supported =
is_aarch64_feature_detected!("sve") || is_aarch64_feature_detected!("sve2");
let mut kvm_kvi: kvm_bindings::kvm_vcpu_init = (*kvi).into();
// We already checked that the capability is supported.
kvm_kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PSCI_0_2;
if vm
.as_any()
.downcast_ref::<crate::kvm::KvmVm>()
.unwrap()
.check_extension(Cap::ArmPmuV3)
{
kvm_kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PMU_V3;
}
if sve_supported
&& vm
.as_any()
.downcast_ref::<crate::kvm::KvmVm>()
.unwrap()
.check_extension(Cap::ArmSve)
{
kvm_kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_SVE;
}
// Non-boot cpus are powered off initially.
if id > 0 {
kvm_kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_POWER_OFF;
}
*kvi = kvm_kvi.into();
Ok(())
}
///
/// Return VcpuInit with default value set
///
#[cfg(target_arch = "aarch64")]
fn create_vcpu_init(&self) -> crate::VcpuInit {
kvm_bindings::kvm_vcpu_init::default().into()
}
#[cfg(target_arch = "aarch64")]
fn vcpu_init(&self, kvi: &crate::VcpuInit) -> cpu::Result<()> {
let kvm_kvi: kvm_bindings::kvm_vcpu_init = (*kvi).into();
self.fd
.lock()
.unwrap()
.vcpu_init(kvi)
.vcpu_init(&kvm_kvi)
.map_err(|e| cpu::HypervisorCpuError::VcpuInit(e.into()))
}
@@ -1886,17 +2227,20 @@ impl cpu::Vcpu for KvmVcpu {
.map_err(|e| cpu::HypervisorCpuError::VcpuFinalize(e.into()))
}
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
///
/// 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")]
fn get_reg_list(&self, reg_list: &mut RegList) -> cpu::Result<()> {
let mut kvm_reg_list: kvm_bindings::RegList = reg_list.clone().into();
self.fd
.lock()
.unwrap()
.get_reg_list(reg_list)
.map_err(|e| cpu::HypervisorCpuError::GetRegList(e.into()))
.get_reg_list(&mut kvm_reg_list)
.map_err(|e: kvm_ioctls::Error| cpu::HypervisorCpuError::GetRegList(e.into()))?;
*reg_list = kvm_reg_list.into();
Ok(())
}
///
@@ -1934,6 +2278,14 @@ impl cpu::Vcpu for KvmVcpu {
Ok(u64::from_le_bytes(bytes))
}
///
/// Gets the value of a non-core register
///
#[cfg(target_arch = "riscv64")]
fn get_non_core_reg(&self, _non_core_reg: u32) -> cpu::Result<u64> {
unimplemented!()
}
///
/// Configure core registers for a given CPU.
///
@@ -1994,6 +2346,49 @@ impl cpu::Vcpu for KvmVcpu {
Ok(())
}
#[cfg(target_arch = "riscv64")]
///
/// Configure registers for a given RISC-V CPU.
///
fn setup_regs(&self, cpu_id: u8, boot_ip: u64, fdt_start: u64) -> cpu::Result<()> {
// Setting the A0 () to the hartid of this CPU.
let a0 = offset_of!(kvm_riscv_core, regs, user_regs_struct, a0);
self.fd
.lock()
.unwrap()
.set_one_reg(
riscv64_reg_id!(KVM_REG_RISCV_CORE, a0),
&u64::from(cpu_id).to_le_bytes(),
)
.map_err(|e| cpu::HypervisorCpuError::SetRiscvCoreRegister(e.into()))?;
// Setting the PC (Processor Counter) to the current program address (kernel address).
let pc = offset_of!(kvm_riscv_core, regs, user_regs_struct, pc);
self.fd
.lock()
.unwrap()
.set_one_reg(
riscv64_reg_id!(KVM_REG_RISCV_CORE, pc),
&boot_ip.to_le_bytes(),
)
.map_err(|e| cpu::HypervisorCpuError::SetRiscvCoreRegister(e.into()))?;
// 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 64 kilobytes in size." -> https://www.kernel.org/doc/Documentation/arch/riscv/boot.txt.
let a1 = offset_of!(kvm_riscv_core, regs, user_regs_struct, a1);
self.fd
.lock()
.unwrap()
.set_one_reg(
riscv64_reg_id!(KVM_REG_RISCV_CORE, a1),
&fdt_start.to_le_bytes(),
)
.map_err(|e| cpu::HypervisorCpuError::SetRiscvCoreRegister(e.into()))?;
Ok(())
}
#[cfg(target_arch = "x86_64")]
///
/// Get the current CPU state
@@ -2133,8 +2528,8 @@ impl cpu::Vcpu for KvmVcpu {
// Get systerm register
// Call KVM_GET_REG_LIST to get all registers available to the guest.
// For ArmV8 there are around 500 registers.
let mut sys_regs: Vec<Register> = Vec::new();
let mut reg_list = RegList::new(500).unwrap();
let mut sys_regs: Vec<kvm_bindings::kvm_one_reg> = Vec::new();
let mut reg_list = kvm_bindings::RegList::new(500).unwrap();
self.fd
.lock()
.unwrap()
@@ -2171,6 +2566,66 @@ impl cpu::Vcpu for KvmVcpu {
Ok(state.into())
}
#[cfg(target_arch = "riscv64")]
///
/// Get the current RISC-V 64-bit CPU state
///
fn state(&self) -> cpu::Result<CpuState> {
let mut state = VcpuKvmState {
mp_state: self.get_mp_state()?.into(),
..Default::default()
};
// Get core registers
state.core_regs = self.get_regs()?.into();
// Get non-core register
// Call KVM_GET_REG_LIST to get all registers available to the guest.
// For RISC-V 64-bit there are around 200 registers.
let mut sys_regs: Vec<kvm_bindings::kvm_one_reg> = Vec::new();
let mut reg_list = kvm_bindings::RegList::new(200).unwrap();
self.fd
.lock()
.unwrap()
.get_reg_list(&mut reg_list)
.map_err(|e| cpu::HypervisorCpuError::GetRegList(e.into()))?;
// At this point reg_list should contain:
// - core registers
// - config registers
// - timer registers
// - control and status registers
// - AIA control and status registers
// - smstateen control and status registers
// - sbi_sta control and status registers.
//
// The register list contains the number of registers and their ids. We
// will be needing to call KVM_GET_ONE_REG on each id in order to save
// all of them. We carve out from the list the core registers which are
// represented in the kernel by `kvm_riscv_core` structure and for which
// we can calculate the id based on the offset in the structure.
reg_list.retain(|regid| is_non_core_register(*regid));
// Now, for the rest of the registers left in the previously fetched
// register list, we are simply calling KVM_GET_ONE_REG.
let indices = reg_list.as_slice();
for index in indices.iter() {
let mut bytes = [0_u8; 8];
self.fd
.lock()
.unwrap()
.get_one_reg(*index, &mut bytes)
.map_err(|e| cpu::HypervisorCpuError::GetSysRegister(e.into()))?;
sys_regs.push(kvm_bindings::kvm_one_reg {
id: *index,
addr: u64::from_le_bytes(bytes),
});
}
state.non_core_regs = sys_regs;
Ok(state.into())
}
#[cfg(target_arch = "x86_64")]
///
/// Restore the previously saved CPU state
@@ -2283,6 +2738,28 @@ impl cpu::Vcpu for KvmVcpu {
Ok(())
}
#[cfg(target_arch = "riscv64")]
///
/// Restore the previously saved RISC-V 64-bit CPU state
///
fn set_state(&self, state: &CpuState) -> cpu::Result<()> {
let state: VcpuKvmState = state.clone().into();
// Set core registers
self.set_regs(&state.core_regs.into())?;
// Set system registers
for reg in &state.non_core_regs {
self.fd
.lock()
.unwrap()
.set_one_reg(reg.id, &reg.addr.to_le_bytes())
.map_err(|e| cpu::HypervisorCpuError::SetSysRegister(e.into()))?;
}
self.set_mp_state(state.mp_state.into())?;
Ok(())
}
///
/// Initialize TDX for this CPU
///
@@ -2548,3 +3025,58 @@ impl KvmVcpu {
.map_err(|e| cpu::HypervisorCpuError::SetVcpuEvents(e.into()))
}
}
#[cfg(test)]
mod tests {
#[test]
#[cfg(target_arch = "riscv64")]
fn test_get_and_set_regs() {
use super::*;
let kvm = KvmHypervisor::new().unwrap();
let hypervisor = Arc::new(kvm);
let vm = hypervisor.create_vm().expect("new VM fd creation failed");
let vcpu0 = vm.create_vcpu(0, None).unwrap();
let core_regs = StandardRegisters::from(kvm_riscv_core {
regs: user_regs_struct {
pc: 0x00,
ra: 0x01,
sp: 0x02,
gp: 0x03,
tp: 0x04,
t0: 0x05,
t1: 0x06,
t2: 0x07,
s0: 0x08,
s1: 0x09,
a0: 0x0a,
a1: 0x0b,
a2: 0x0c,
a3: 0x0d,
a4: 0x0e,
a5: 0x0f,
a6: 0x10,
a7: 0x11,
s2: 0x12,
s3: 0x13,
s4: 0x14,
s5: 0x15,
s6: 0x16,
s7: 0x17,
s8: 0x18,
s9: 0x19,
s10: 0x1a,
s11: 0x1b,
t3: 0x1c,
t4: 0x1d,
t5: 0x1e,
t6: 0x1f,
},
mode: 0x00,
});
vcpu0.set_regs(&core_regs).unwrap();
assert_eq!(vcpu0.get_regs().unwrap(), core_regs);
}
}

View File

@@ -0,0 +1,275 @@
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
use std::any::Any;
use kvm_ioctls::DeviceFd;
use serde::{Deserialize, Serialize};
use crate::arch::riscv64::aia::{Error, Result, Vaia, VaiaConfig};
use crate::device::HypervisorDeviceError;
use crate::kvm::KvmVm;
use crate::Vm;
pub struct KvmAiaImsics {
/// The KVM device for the Aia
device: DeviceFd,
/// AIA APLIC address
aplic_addr: u64,
/// AIA IMSIC address
imsic_addr: u64,
/// Number of CPUs handled by the device
vcpu_count: u32,
}
#[derive(Clone, Default, Serialize, Deserialize)]
pub struct AiaImsicsState {}
impl KvmAiaImsics {
/// Device trees specific constants
fn version() -> u32 {
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_RISCV_AIA
}
/// Setup the device-specific attributes
fn init_device_attributes(&mut self, nr_irqs: u32) -> Result<()> {
// AIA part attributes
// Getting the working mode of RISC-V AIA, defaults to EMUL, passible
// variants are EMUL, HW_ACCL, AUTO
let mut aia_mode = kvm_bindings::KVM_DEV_RISCV_AIA_MODE_EMUL;
Self::get_device_attribute(
&self.device,
kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CONFIG,
u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CONFIG_MODE),
&mut aia_mode as *mut u32 as u64,
0,
)?;
// Report AIA MODE
// Setting up the number of wired interrupt sources
Self::set_device_attribute(
&self.device,
kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CONFIG,
u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CONFIG_SRCS),
&nr_irqs as *const u32 as u64,
0,
)?;
// Getting the number of ids
let mut aia_nr_ids: u32 = 0;
Self::get_device_attribute(
&self.device,
kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CONFIG,
u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CONFIG_IDS),
&mut aia_nr_ids as *mut u32 as u64,
0,
)?;
// Report NR_IDS
// Setting up hart_bits
let max_hart_index = self.vcpu_count as u64 - 1;
let hart_bits = std::cmp::max(64 - max_hart_index.leading_zeros(), 1);
Self::set_device_attribute(
&self.device,
kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CONFIG,
u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CONFIG_HART_BITS),
&hart_bits as *const u32 as u64,
0,
)?;
// Designate addresses of APLIC and IMSICS
// Setting up RISC-V APLIC
Self::set_device_attribute(
&self.device,
kvm_bindings::KVM_DEV_RISCV_AIA_GRP_ADDR,
u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_ADDR_APLIC),
&self.aplic_addr as *const u64 as u64,
0,
)?;
// Helpers to calculate address and attribute of IMSIC of each vCPU
let riscv_imsic_addr_of = |cpu_index: u32| -> u64 {
self.imsic_addr + (cpu_index * kvm_bindings::KVM_DEV_RISCV_IMSIC_SIZE) as u64
};
let riscv_imsic_attr_of = |cpu_index: u32| -> u64 { cpu_index as u64 + 1 };
// Setting up RISC-V IMSICs
for cpu_index in 0..self.vcpu_count {
let cpu_imsic_addr = riscv_imsic_addr_of(cpu_index);
Self::set_device_attribute(
&self.device,
kvm_bindings::KVM_DEV_RISCV_AIA_GRP_ADDR,
riscv_imsic_attr_of(cpu_index),
&cpu_imsic_addr as *const u64 as u64,
0,
)?;
}
// Finalizing the AIA device
Self::set_device_attribute(
&self.device,
kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CTRL,
u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CTRL_INIT),
0,
0,
)
}
/// Create a KVM Vaia device
fn create_device(vm: &KvmVm) -> Result<DeviceFd> {
let mut aia_device = kvm_bindings::kvm_create_device {
type_: Self::version(),
fd: 0,
flags: 0,
};
let device_fd = vm
.create_device(&mut aia_device)
.map_err(Error::CreateAia)?;
// We know for sure this is a KVM fd
Ok(device_fd.to_kvm().unwrap())
}
/// Get an AIA device attribute
fn get_device_attribute(
device: &DeviceFd,
group: u32,
attr: u64,
addr: u64,
flags: u32,
) -> Result<()> {
let mut attr = kvm_bindings::kvm_device_attr {
flags,
group,
attr,
addr,
};
// SAFETY: attr.addr is safe to write to.
unsafe {
device.get_device_attr(&mut attr).map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})
}
}
/// Set an AIA device attribute
fn set_device_attribute(
device: &DeviceFd,
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(|e| {
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
})
}
/// Method to initialize the AIA device
pub fn new(vm: &dyn Vm, config: VaiaConfig) -> Result<KvmAiaImsics> {
// This is inside KVM module
let vm = vm.as_any().downcast_ref::<KvmVm>().expect("Wrong VM type?");
let vaia = Self::create_device(vm)?;
let mut aia_device = KvmAiaImsics {
device: vaia,
vcpu_count: config.vcpu_count,
aplic_addr: config.aplic_addr,
imsic_addr: config.imsic_addr,
};
aia_device.init_device_attributes(config.nr_irqs)?;
Ok(aia_device)
}
}
impl Vaia for KvmAiaImsics {
fn aplic_compatibility(&self) -> &str {
"riscv,aplic"
}
fn aplic_properties(&self) -> [u32; 4] {
[
0,
self.aplic_addr as u32,
0,
kvm_bindings::KVM_DEV_RISCV_APLIC_SIZE,
]
}
fn imsic_compatibility(&self) -> &str {
"riscv,imsics"
}
fn imsic_properties(&self) -> [u32; 4] {
[
0,
self.imsic_addr as u32,
0,
kvm_bindings::KVM_DEV_RISCV_IMSIC_SIZE * self.vcpu_count,
]
}
fn vcpu_count(&self) -> u32 {
self.vcpu_count
}
fn msi_compatible(&self) -> bool {
true
}
fn as_any_concrete_mut(&mut self) -> &mut dyn Any {
self
}
/// Save the state of AIA.
fn state(&self) -> Result<AiaImsicsState> {
unimplemented!()
}
/// Restore the state of AIA_IMSICs.
fn set_state(&mut self, _state: &AiaImsicsState) -> Result<()> {
unimplemented!()
}
}
#[cfg(test)]
mod tests {
use crate::arch::riscv64::aia::VaiaConfig;
use crate::kvm::KvmAiaImsics;
fn create_test_vaia_config() -> VaiaConfig {
VaiaConfig {
vcpu_count: 1,
aplic_addr: 0xd000000,
imsic_addr: 0x2800000,
nr_irqs: 256,
}
}
#[test]
fn test_create_aia() {
let hv = crate::new().unwrap();
let vm = hv.create_vm().unwrap();
let _vcpu = vm.create_vcpu(0, None).unwrap();
assert!(KvmAiaImsics::new(&*vm, create_test_vaia_config()).is_ok());
}
}

View File

@@ -0,0 +1,162 @@
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
pub mod aia;
use kvm_bindings::{
kvm_mp_state, kvm_one_reg, kvm_riscv_core, KVM_REG_RISCV_CORE, KVM_REG_RISCV_TYPE_MASK,
KVM_REG_SIZE_MASK, KVM_REG_SIZE_U64,
};
pub use kvm_ioctls::{Cap, Kvm};
use serde::{Deserialize, Serialize};
use crate::kvm::{KvmError, KvmResult};
// This macro gets the offset of a structure (i.e `str`) member (i.e `field`) without having
// an instance of that structure.
#[macro_export]
macro_rules! _offset_of {
($str:ty, $field:ident) => {{
let tmp: std::mem::MaybeUninit<$str> = std::mem::MaybeUninit::uninit();
let base = tmp.as_ptr();
// Avoid warnings when nesting `unsafe` blocks.
#[allow(unused_unsafe)]
// SAFETY: The pointer is valid and aligned, just not initialised. Using `addr_of` ensures
// that we don't actually read from `base` (which would be UB) nor create an intermediate
// reference.
let member = unsafe { core::ptr::addr_of!((*base).$field) } as *const u8;
// Avoid warnings when nesting `unsafe` blocks.
#[allow(unused_unsafe)]
// SAFETY: The two pointers are within the same allocated object `tmp`. All requirements
// from offset_from are upheld.
unsafe {
member.offset_from(base as *const u8) as usize
}
}};
}
#[macro_export]
macro_rules! offset_of {
($reg_struct:ty, $field:ident) => {
$crate::_offset_of!($reg_struct, $field)
};
($outer_reg_struct:ty, $outer_field:ident, $($inner_reg_struct:ty, $inner_field:ident), +) => {
$crate::_offset_of!($outer_reg_struct, $outer_field) + offset_of!($($inner_reg_struct, $inner_field), +)
};
}
// Following are macros that help with getting the ID of a riscv64 register, including config registers, core registers and timer registers.
// The register of core registers are wrapped in the `user_regs_struct` structure. See:
// https://elixir.bootlin.com/linux/v6.10/source/arch/riscv/include/uapi/asm/kvm.h#L62
// Get the ID of a register
#[macro_export]
macro_rules! riscv64_reg_id {
($reg_type: tt, $offset: tt) => {
// The core registers of an riscv64 machine are represented
// in kernel by the `kvm_riscv_core` structure:
//
// struct kvm_riscv_core {
// struct user_regs_struct regs;
// unsigned long mode;
// };
//
// struct user_regs_struct {
// unsigned long pc;
// unsigned long ra;
// unsigned long sp;
// unsigned long gp;
// unsigned long tp;
// unsigned long t0;
// unsigned long t1;
// unsigned long t2;
// unsigned long s0;
// unsigned long s1;
// unsigned long a0;
// unsigned long a1;
// unsigned long a2;
// unsigned long a3;
// unsigned long a4;
// unsigned long a5;
// unsigned long a6;
// unsigned long a7;
// unsigned long s2;
// unsigned long s3;
// unsigned long s4;
// unsigned long s5;
// unsigned long s6;
// unsigned long s7;
// unsigned long s8;
// unsigned long s9;
// unsigned long s10;
// unsigned long s11;
// unsigned long t3;
// unsigned long t4;
// unsigned long t5;
// unsigned long t6;
// };
// The id of a core register can be obtained like this: offset = id &
// ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_RISCV_CORE). Thus,
// id = KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_CORE | offset
//
// To generalize, the id of a register can be obtained by:
// id = KVM_REG_RISCV | KVM_REG_SIZE_U64 |
// KVM_REG_RISCV_CORE/KVM_REG_RISCV_CONFIG/KVM_REG_RISCV_TIMER |
// offset
kvm_bindings::KVM_REG_RISCV as u64
| u64::from($reg_type)
| u64::from(kvm_bindings::KVM_REG_SIZE_U64)
| (($offset / std::mem::size_of::<u64>()) as u64)
};
}
/// Specifies whether a particular register is a core register or not.
///
/// # Arguments
///
/// * `regid` - The index of the register we are checking.
pub fn is_non_core_register(regid: u64) -> bool {
if (regid & KVM_REG_RISCV_TYPE_MASK as u64) == KVM_REG_RISCV_CORE as u64 {
return false;
}
let size = regid & KVM_REG_SIZE_MASK;
assert!(
size == KVM_REG_SIZE_U64,
"Unexpected register size for system register {size}"
);
true
}
pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
macro_rules! check_extension {
($cap:expr) => {
if !kvm.check_extension($cap) {
return Err(KvmError::CapabilityMissing($cap));
}
};
}
// SetGuestDebug is required but some kernels have it implemented without the capability flag.
check_extension!(Cap::ImmediateExit);
check_extension!(Cap::Ioeventfd);
check_extension!(Cap::Irqchip);
check_extension!(Cap::Irqfd);
check_extension!(Cap::IrqRouting);
check_extension!(Cap::MpState);
check_extension!(Cap::OneReg);
check_extension!(Cap::UserMemory);
Ok(())
}
#[derive(Clone, Default, Serialize, Deserialize)]
pub struct VcpuKvmState {
pub mp_state: kvm_mp_state,
pub core_regs: kvm_riscv_core,
pub non_core_regs: Vec<kvm_one_reg>,
}

View File

@@ -1,3 +1,5 @@
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
//
// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
@@ -16,6 +18,7 @@
//!
//! - x86_64
//! - arm64
//! - riscv64 (experimental)
//!
#[macro_use]
@@ -57,6 +60,8 @@ pub use cpu::{HypervisorCpuError, Vcpu, VmExit};
pub use device::HypervisorDeviceError;
#[cfg(all(feature = "kvm", target_arch = "aarch64"))]
pub use kvm::{aarch64, GicState};
#[cfg(all(feature = "kvm", target_arch = "riscv64"))]
pub use kvm::{riscv64, AiaState};
pub use vm::{
DataMatch, HypervisorVmError, InterruptSourceConfig, LegacyIrqSourceConfig, MsiIrqSourceConfig,
Vm, VmOps,
@@ -158,7 +163,7 @@ pub enum IoEventAddress {
pub enum CpuState {
#[cfg(feature = "kvm")]
Kvm(kvm::VcpuKvmState),
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
#[cfg(feature = "mshv")]
Mshv(mshv::VcpuMshvState),
}
@@ -192,10 +197,34 @@ pub enum IrqRoutingEntry {
}
#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum StandardRegisters {
pub enum VcpuInit {
#[cfg(all(feature = "kvm", target_arch = "aarch64"))]
Kvm(kvm_bindings::kvm_vcpu_init),
#[cfg(all(feature = "mshv", target_arch = "aarch64"))]
Mshv(mshv_bindings::MshvVcpuInit),
}
#[derive(Debug, Clone, PartialEq)]
pub enum RegList {
#[cfg(all(feature = "kvm", any(target_arch = "aarch64", target_arch = "riscv64")))]
Kvm(kvm_bindings::RegList),
#[cfg(all(feature = "mshv", target_arch = "aarch64"))]
Mshv(mshv_bindings::MshvRegList),
}
pub enum Register {
#[cfg(feature = "kvm")]
Kvm(kvm_bindings::kvm_one_reg),
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum StandardRegisters {
#[cfg(all(feature = "kvm", not(target_arch = "riscv64")))]
Kvm(kvm_bindings::kvm_regs),
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
#[cfg(all(feature = "kvm", target_arch = "riscv64"))]
Kvm(kvm_bindings::kvm_riscv_core),
#[cfg(any(feature = "mshv", feature = "mshv_emulator"))]
Mshv(mshv_bindings::StandardRegisters),
}
@@ -208,7 +237,7 @@ macro_rules! set_x86_64_reg {
match self {
#[cfg(feature = "kvm")]
StandardRegisters::Kvm(s) => s.$reg_name = val,
#[cfg(feature = "mshv")]
#[cfg(any(feature = "mshv", feature = "mshv_emulator"))]
StandardRegisters::Mshv(s) => s.$reg_name = val,
}
}
@@ -226,7 +255,7 @@ macro_rules! get_x86_64_reg {
match self {
#[cfg(feature = "kvm")]
StandardRegisters::Kvm(s) => s.$reg_name,
#[cfg(feature = "mshv")]
#[cfg(any(feature = "mshv", feature = "mshv_emulator"))]
StandardRegisters::Mshv(s) => s.$reg_name,
}
}
@@ -282,6 +311,8 @@ macro_rules! set_aarch64_reg {
match self {
#[cfg(feature = "kvm")]
StandardRegisters::Kvm(s) => s.regs.$reg_name = val,
#[cfg(feature = "mshv")]
StandardRegisters::Mshv(s) => s.$reg_name = val,
}
}
}
@@ -298,6 +329,8 @@ macro_rules! get_aarch64_reg {
match self {
#[cfg(feature = "kvm")]
StandardRegisters::Kvm(s) => s.regs.$reg_name,
#[cfg(feature = "mshv")]
StandardRegisters::Mshv(s) => s.$reg_name,
}
}
}
@@ -314,3 +347,125 @@ get_aarch64_reg!(regs, [u64; 31usize]);
get_aarch64_reg!(sp, u64);
get_aarch64_reg!(pc, u64);
get_aarch64_reg!(pstate, u64);
macro_rules! set_riscv64_reg {
(mode) => {
#[cfg(target_arch = "riscv64")]
impl StandardRegisters {
pub fn set_mode(&mut self, val: u64) {
match self {
#[cfg(feature = "kvm")]
StandardRegisters::Kvm(s) => s.mode = val,
}
}
}
};
($reg_name:ident) => {
concat_idents!(method_name = "set_", $reg_name {
#[cfg(target_arch = "riscv64")]
impl StandardRegisters {
pub fn method_name(&mut self, val: u64) {
match self {
#[cfg(feature = "kvm")]
StandardRegisters::Kvm(s) => s.regs.$reg_name = val,
}
}
}
});
}
}
macro_rules! get_riscv64_reg {
(mode) => {
#[cfg(target_arch = "riscv64")]
impl StandardRegisters {
pub fn get_mode(&self) -> u64 {
match self {
#[cfg(feature = "kvm")]
StandardRegisters::Kvm(s) => s.mode,
}
}
}
};
($reg_name:ident) => {
concat_idents!(method_name = "get_", $reg_name {
#[cfg(target_arch = "riscv64")]
impl StandardRegisters {
pub fn method_name(&self) -> u64 {
match self {
#[cfg(feature = "kvm")]
StandardRegisters::Kvm(s) => s.regs.$reg_name,
}
}
}
});
}
}
set_riscv64_reg!(pc);
set_riscv64_reg!(ra);
set_riscv64_reg!(sp);
set_riscv64_reg!(gp);
set_riscv64_reg!(tp);
set_riscv64_reg!(t0);
set_riscv64_reg!(t1);
set_riscv64_reg!(t2);
set_riscv64_reg!(s0);
set_riscv64_reg!(s1);
set_riscv64_reg!(a0);
set_riscv64_reg!(a1);
set_riscv64_reg!(a2);
set_riscv64_reg!(a3);
set_riscv64_reg!(a4);
set_riscv64_reg!(a5);
set_riscv64_reg!(a6);
set_riscv64_reg!(a7);
set_riscv64_reg!(s2);
set_riscv64_reg!(s3);
set_riscv64_reg!(s4);
set_riscv64_reg!(s5);
set_riscv64_reg!(s6);
set_riscv64_reg!(s7);
set_riscv64_reg!(s8);
set_riscv64_reg!(s9);
set_riscv64_reg!(s10);
set_riscv64_reg!(s11);
set_riscv64_reg!(t3);
set_riscv64_reg!(t4);
set_riscv64_reg!(t5);
set_riscv64_reg!(t6);
set_riscv64_reg!(mode);
get_riscv64_reg!(pc);
get_riscv64_reg!(ra);
get_riscv64_reg!(sp);
get_riscv64_reg!(gp);
get_riscv64_reg!(tp);
get_riscv64_reg!(t0);
get_riscv64_reg!(t1);
get_riscv64_reg!(t2);
get_riscv64_reg!(s0);
get_riscv64_reg!(s1);
get_riscv64_reg!(a0);
get_riscv64_reg!(a1);
get_riscv64_reg!(a2);
get_riscv64_reg!(a3);
get_riscv64_reg!(a4);
get_riscv64_reg!(a5);
get_riscv64_reg!(a6);
get_riscv64_reg!(a7);
get_riscv64_reg!(s2);
get_riscv64_reg!(s3);
get_riscv64_reg!(s4);
get_riscv64_reg!(s5);
get_riscv64_reg!(s6);
get_riscv64_reg!(s7);
get_riscv64_reg!(s8);
get_riscv64_reg!(s9);
get_riscv64_reg!(s10);
get_riscv64_reg!(s11);
get_riscv64_reg!(t3);
get_riscv64_reg!(t4);
get_riscv64_reg!(t5);
get_riscv64_reg!(t6);
get_riscv64_reg!(mode);

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
// Copyright © 2025, Microsoft Corporation
//
use std::fmt;
///
/// Export generically-named wrappers of mshv_bindings for Unix-based platforms
///
pub use mshv_bindings::StandardRegisters as MshvStandardRegisters;
use serde::{Deserialize, Serialize};
#[derive(Clone, Serialize, Deserialize)]
pub struct VcpuMshvState {
pub regs: MshvStandardRegisters,
}
impl fmt::Display for VcpuMshvState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Standard registers: {:?}", self.regs)
}
}

View File

@@ -5,16 +5,26 @@
use std::any::Any;
use std::collections::HashMap;
#[cfg(feature = "sev_snp")]
use std::num::NonZeroUsize;
use std::sync::{Arc, RwLock};
#[cfg(feature = "sev_snp")]
use arc_swap::ArcSwap;
use mshv_bindings::*;
use mshv_ioctls::{set_registers_64, InterruptRequest, Mshv, NoDatamatch, VcpuFd, VmFd, VmType};
#[cfg(target_arch = "x86_64")]
use mshv_ioctls::{set_registers_64, InterruptRequest};
use mshv_ioctls::{Mshv, NoDatamatch, VcpuFd, VmFd, VmType};
use vfio_ioctls::VfioDeviceFd;
use vm::DataMatch;
#[cfg(feature = "sev_snp")]
use vm_memory::bitmap::AtomicBitmap;
#[cfg(target_arch = "x86_64")]
use crate::arch::emulator::PlatformEmulator;
#[cfg(target_arch = "x86_64")]
use crate::arch::x86::emulator::Emulator;
#[cfg(target_arch = "x86_64")]
use crate::mshv::emulator::MshvEmulatorContext;
use crate::vm::{self, InterruptSourceConfig, VmOps};
use crate::{cpu, hypervisor, vec_with_array_field, HypervisorType};
@@ -23,10 +33,17 @@ mod snp_constants;
// x86_64 dependencies
#[cfg(target_arch = "x86_64")]
pub mod x86_64;
// aarch64 dependencies
#[cfg(target_arch = "aarch64")]
pub mod aarch64;
#[cfg(target_arch = "x86_64")]
use std::fs::File;
use std::os::unix::io::AsRawFd;
#[cfg(target_arch = "aarch64")]
use std::sync::Mutex;
#[cfg(target_arch = "aarch64")]
pub use aarch64::VcpuMshvState;
#[cfg(feature = "sev_snp")]
use igvm_defs::IGVM_VHS_SNP_ID_BLOCK;
#[cfg(feature = "sev_snp")]
@@ -44,6 +61,8 @@ pub use {
mshv_bindings::mshv_device_attr as DeviceAttr, mshv_ioctls, mshv_ioctls::DeviceFd,
};
#[cfg(target_arch = "aarch64")]
use crate::arch::aarch64::gic::{Vgic, VgicConfig};
#[cfg(target_arch = "x86_64")]
use crate::arch::x86::{CpuIdEntry, FpuState, MsrEntry};
#[cfg(target_arch = "x86_64")]
@@ -185,6 +204,44 @@ impl From<IrqRoutingEntry> for mshv_user_irq_entry {
}
}
#[cfg(target_arch = "aarch64")]
impl From<mshv_bindings::MshvRegList> for crate::RegList {
fn from(s: mshv_bindings::MshvRegList) -> Self {
crate::RegList::Mshv(s)
}
}
#[cfg(target_arch = "aarch64")]
impl From<crate::RegList> for mshv_bindings::MshvRegList {
fn from(e: crate::RegList) -> Self {
match e {
crate::RegList::Mshv(e) => e,
/* Needed in case other hypervisors are enabled */
#[allow(unreachable_patterns)]
_ => panic!("RegList is not valid"),
}
}
}
#[cfg(target_arch = "aarch64")]
impl From<mshv_bindings::MshvVcpuInit> for crate::VcpuInit {
fn from(s: mshv_bindings::MshvVcpuInit) -> Self {
crate::VcpuInit::Mshv(s)
}
}
#[cfg(target_arch = "aarch64")]
impl From<crate::VcpuInit> for mshv_bindings::MshvVcpuInit {
fn from(e: crate::VcpuInit) -> Self {
match e {
crate::VcpuInit::Mshv(e) => e,
/* Needed in case other hypervisors are enabled */
#[allow(unreachable_patterns)]
_ => panic!("VcpuInit is not valid"),
}
}
}
struct MshvDirtyLogSlot {
guest_pfn: u64,
memory_size: u64,
@@ -205,48 +262,12 @@ impl MshvHypervisor {
.get_msr_index_list()
.map_err(|e| hypervisor::HypervisorError::GetMsrList(e.into()))
}
}
impl MshvHypervisor {
/// Create a hypervisor based on Mshv
#[allow(clippy::new_ret_no_self)]
pub fn new() -> hypervisor::Result<Arc<dyn hypervisor::Hypervisor>> {
let mshv_obj =
Mshv::new().map_err(|e| hypervisor::HypervisorError::HypervisorCreate(e.into()))?;
Ok(Arc::new(MshvHypervisor { mshv: mshv_obj }))
}
/// Check if the hypervisor is available
pub fn is_available() -> hypervisor::Result<bool> {
match std::fs::metadata("/dev/mshv") {
Ok(_) => Ok(true),
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(false),
Err(err) => Err(hypervisor::HypervisorError::HypervisorAvailableCheck(
err.into(),
)),
}
}
}
/// Implementation of Hypervisor trait for Mshv
///
/// # Examples
///
/// ```
/// # use hypervisor::mshv::MshvHypervisor;
/// # use std::sync::Arc;
/// let mshv = MshvHypervisor::new().unwrap();
/// let hypervisor = Arc::new(mshv);
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
/// ```
impl hypervisor::Hypervisor for MshvHypervisor {
///
/// Returns the type of the hypervisor
///
fn hypervisor_type(&self) -> HypervisorType {
HypervisorType::Mshv
}
fn create_vm_with_type(&self, vm_type: u64) -> hypervisor::Result<Arc<dyn crate::Vm>> {
fn create_vm_with_type_and_memory_int(
&self,
vm_type: u64,
#[cfg(feature = "sev_snp")] _mem_size: Option<u64>,
) -> hypervisor::Result<Arc<dyn crate::Vm>> {
let mshv_vm_type: VmType = match VmType::try_from(vm_type) {
Ok(vm_type) => vm_type,
Err(_) => return Err(hypervisor::HypervisorError::UnsupportedVmType()),
@@ -339,6 +360,14 @@ impl hypervisor::Hypervisor for MshvHypervisor {
dirty_log_slots: Arc::new(RwLock::new(HashMap::new())),
#[cfg(feature = "sev_snp")]
sev_snp_enabled: mshv_vm_type == VmType::Snp,
#[cfg(feature = "sev_snp")]
host_access_pages: ArcSwap::new(
AtomicBitmap::new(
_mem_size.unwrap_or_default() as usize,
NonZeroUsize::new(HV_PAGE_SIZE).unwrap(),
)
.into(),
),
}))
}
@@ -350,6 +379,78 @@ impl hypervisor::Hypervisor for MshvHypervisor {
}))
}
}
}
impl MshvHypervisor {
/// Create a hypervisor based on Mshv
#[allow(clippy::new_ret_no_self)]
pub fn new() -> hypervisor::Result<Arc<dyn hypervisor::Hypervisor>> {
let mshv_obj =
Mshv::new().map_err(|e| hypervisor::HypervisorError::HypervisorCreate(e.into()))?;
Ok(Arc::new(MshvHypervisor { mshv: mshv_obj }))
}
/// Check if the hypervisor is available
pub fn is_available() -> hypervisor::Result<bool> {
match std::fs::metadata("/dev/mshv") {
Ok(_) => Ok(true),
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(false),
Err(err) => Err(hypervisor::HypervisorError::HypervisorAvailableCheck(
err.into(),
)),
}
}
}
/// Implementation of Hypervisor trait for Mshv
///
/// # Examples
///
/// ```
/// use hypervisor::mshv::MshvHypervisor;
/// use std::sync::Arc;
/// let mshv = MshvHypervisor::new().unwrap();
/// let hypervisor = Arc::new(mshv);
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
/// ```
impl hypervisor::Hypervisor for MshvHypervisor {
///
/// Returns the type of the hypervisor
///
fn hypervisor_type(&self) -> HypervisorType {
HypervisorType::Mshv
}
///
/// Create a Vm of a specific type using the underlying hypervisor, passing memory size
/// Return a hypervisor-agnostic Vm trait object
///
/// # Examples
///
/// ```
/// use hypervisor::kvm::KvmHypervisor;
/// use hypervisor::kvm::KvmVm;
/// let hypervisor = KvmHypervisor::new().unwrap();
/// let vm = hypervisor.create_vm_with_type(0, 512*1024*1024).unwrap();
/// ```
fn create_vm_with_type_and_memory(
&self,
vm_type: u64,
#[cfg(feature = "sev_snp")] _mem_size: u64,
) -> hypervisor::Result<Arc<dyn vm::Vm>> {
self.create_vm_with_type_and_memory_int(
vm_type,
#[cfg(feature = "sev_snp")]
Some(_mem_size),
)
}
fn create_vm_with_type(&self, vm_type: u64) -> hypervisor::Result<Arc<dyn crate::Vm>> {
self.create_vm_with_type_and_memory_int(
vm_type,
#[cfg(feature = "sev_snp")]
None,
)
}
/// Create a mshv vm object and return the object as Vm trait object
///
@@ -357,7 +458,7 @@ impl hypervisor::Hypervisor for MshvHypervisor {
///
/// ```
/// # extern crate hypervisor;
/// # use hypervisor::mshv::MshvHypervisor;
/// use hypervisor::mshv::MshvHypervisor;
/// use hypervisor::mshv::MshvVm;
/// let hypervisor = MshvHypervisor::new().unwrap();
/// let vm = hypervisor.create_vm().unwrap();
@@ -393,8 +494,39 @@ impl hypervisor::Hypervisor for MshvHypervisor {
fn get_guest_debug_hw_bps(&self) -> usize {
0
}
#[cfg(target_arch = "aarch64")]
///
/// Retrieve AArch64 host maximum IPA size supported by MSHV.
///
fn get_host_ipa_limit(&self) -> i32 {
let host_ipa = self.mshv.get_host_partition_property(
hv_partition_property_code_HV_PARTITION_PROPERTY_PHYSICAL_ADDRESS_WIDTH as u64,
);
match host_ipa {
Ok(ipa) => ipa,
Err(e) => {
panic!("Failed to get host IPA limit: {:?}", e);
}
}
}
}
#[cfg(feature = "sev_snp")]
struct Ghcb(*mut svm_ghcb_base);
#[cfg(feature = "sev_snp")]
// SAFETY: struct is based on GHCB page in the hypervisor,
// safe to Send across threads
unsafe impl Send for Ghcb {}
#[cfg(feature = "sev_snp")]
// SAFETY: struct is based on GHCB page in the hypervisor,
// safe to Sync across threads as this is only required for Vcpu trait
// functionally not used anyway
unsafe impl Sync for Ghcb {}
/// Vcpu struct for Microsoft Hypervisor
pub struct MshvVcpu {
fd: VcpuFd,
@@ -405,6 +537,10 @@ pub struct MshvVcpu {
msrs: Vec<MsrEntry>,
vm_ops: Option<Arc<dyn vm::VmOps>>,
vm_fd: Arc<VmFd>,
#[cfg(feature = "sev_snp")]
ghcb: Option<Ghcb>,
#[cfg(feature = "sev_snp")]
host_access_pages: ArcSwap<AtomicBitmap>,
}
/// Implementation of Vcpu trait for Microsoft Hypervisor
@@ -412,8 +548,8 @@ pub struct MshvVcpu {
/// # Examples
///
/// ```
/// # use hypervisor::mshv::MshvHypervisor;
/// # use std::sync::Arc;
/// use hypervisor::mshv::MshvHypervisor;
/// use std::sync::Arc;
/// let mshv = MshvHypervisor::new().unwrap();
/// let hypervisor = Arc::new(mshv);
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
@@ -732,6 +868,12 @@ impl cpu::Vcpu for MshvVcpu {
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(anyhow!(
"Unhandled VCPU exit: attribute intercept - couldn't modify host access {}", e
)))?;
// Guest is revoking the shared access, so we need to update the bitmap
self.host_access_pages.rcu(|_bitmap| {
let bm = self.host_access_pages.load().as_ref().clone();
bm.reset_addr_range(gpa_start as usize, gfn_count as usize);
bm
});
Ok(cpu::VmExit::Ignore)
}
#[cfg(target_arch = "x86_64")]
@@ -789,6 +931,10 @@ impl cpu::Vcpu for MshvVcpu {
let ghcb_msr = svm_ghcb_msr {
as_uint64: info.ghcb_msr,
};
// Safe to use unwrap, for sev_snp guest we already have the
// GHCB pointer wrapped in the option, otherwise this place is not reached.
let ghcb = self.ghcb.as_ref().unwrap().0;
// SAFETY: Accessing a union element from bindgen generated bindings.
let ghcb_op = unsafe { ghcb_msr.__bindgen_anon_2.ghcb_info() as u32 };
// Sanity check on the header fields before handling other operations.
@@ -887,9 +1033,7 @@ impl cpu::Vcpu for MshvVcpu {
GHCB_INFO_NORMAL => {
let exit_code =
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_code as u32;
// SAFETY: Accessing a union element from bindgen generated bindings.
let pfn = unsafe { ghcb_msr.__bindgen_anon_2.gpa_page_number() };
let ghcb_gpa = pfn << GHCB_INFO_BIT_WIDTH;
match exit_code {
SVM_EXITCODE_HV_DOORBELL_PAGE => {
let exit_info1 =
@@ -898,10 +1042,11 @@ impl cpu::Vcpu for MshvVcpu {
SVM_NAE_HV_DOORBELL_PAGE_GET_PREFERRED => {
// Hypervisor does not have any preference for doorbell GPA.
let preferred_doorbell_gpa: u64 = 0xFFFFFFFFFFFFFFFF;
self.gpa_write(
ghcb_gpa + GHCB_SW_EXITINFO2_OFFSET,
&preferred_doorbell_gpa.to_le_bytes(),
)?;
set_svm_field_u64_ptr!(
ghcb,
exit_info2,
preferred_doorbell_gpa
);
}
SVM_NAE_HV_DOORBELL_PAGE_SET => {
let exit_info2 = info
@@ -928,13 +1073,10 @@ impl cpu::Vcpu for MshvVcpu {
cpu::HypervisorCpuError::SetRegister(e.into())
})?;
self.gpa_write(
ghcb_gpa + GHCB_SW_EXITINFO2_OFFSET,
&exit_info2.to_le_bytes(),
)?;
set_svm_field_u64_ptr!(ghcb, exit_info2, exit_info2);
// Clear the SW_EXIT_INFO1 register to indicate no error
self.clear_swexit_info1(ghcb_gpa)?;
self.clear_swexit_info1()?;
}
SVM_NAE_HV_DOORBELL_PAGE_QUERY => {
let mut reg_assocs = [ hv_register_assoc {
@@ -945,19 +1087,13 @@ impl cpu::Vcpu for MshvVcpu {
// SAFETY: Accessing a union element from bindgen generated bindings.
let doorbell_gpa = unsafe { reg_assocs[0].value.reg64 };
self.gpa_write(
ghcb_gpa + GHCB_SW_EXITINFO2_OFFSET,
&doorbell_gpa.to_le_bytes(),
)?;
set_svm_field_u64_ptr!(ghcb, exit_info2, doorbell_gpa);
// Clear the SW_EXIT_INFO1 register to indicate no error
self.clear_swexit_info1(ghcb_gpa)?;
self.clear_swexit_info1()?;
}
SVM_NAE_HV_DOORBELL_PAGE_CLEAR => {
self.gpa_write(
ghcb_gpa + GHCB_SW_EXITINFO2_OFFSET,
&[0; 8],
)?;
set_svm_field_u64_ptr!(ghcb, exit_info2, 0);
}
_ => {
panic!(
@@ -991,9 +1127,8 @@ impl cpu::Vcpu for MshvVcpu {
let is_write =
// SAFETY: Accessing a union element from bindgen generated bindings.
unsafe { port_info.__bindgen_anon_1.access_type() == 0 };
let mut data = [0; 8];
self.gpa_read(ghcb_gpa + GHCB_RAX_OFFSET, &mut data)?;
// SAFETY: Accessing the field from a mapped address
let mut data = unsafe { (*ghcb).rax.to_le_bytes() };
if is_write {
if let Some(vm_ops) = &self.vm_ops {
@@ -1009,17 +1144,15 @@ impl cpu::Vcpu for MshvVcpu {
cpu::HypervisorCpuError::RunVcpu(e.into())
})?;
}
self.gpa_write(ghcb_gpa + GHCB_RAX_OFFSET, &data)?;
set_svm_field_u64_ptr!(ghcb, rax, u64::from_le_bytes(data));
}
// Clear the SW_EXIT_INFO1 register to indicate no error
self.clear_swexit_info1(ghcb_gpa)?;
self.clear_swexit_info1()?;
}
SVM_EXITCODE_MMIO_READ => {
let src_gpa =
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info1;
let dst_gpa = info.__bindgen_anon_2.__bindgen_anon_1.sw_scratch;
let data_len =
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info2
as usize;
@@ -1032,16 +1165,18 @@ impl cpu::Vcpu for MshvVcpu {
cpu::HypervisorCpuError::RunVcpu(e.into())
})?;
}
self.gpa_write(dst_gpa, &data)?;
// Copy the data to the shared buffer of the GHCB page
let mut buffer_data = [0; 8];
buffer_data[..data_len].copy_from_slice(&data[..data_len]);
// SAFETY: Updating the value of mapped area
unsafe { (*ghcb).shared[0] = u64::from_le_bytes(buffer_data) };
// Clear the SW_EXIT_INFO1 register to indicate no error
self.clear_swexit_info1(ghcb_gpa)?;
self.clear_swexit_info1()?;
}
SVM_EXITCODE_MMIO_WRITE => {
let dst_gpa =
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info1;
let src_gpa = info.__bindgen_anon_2.__bindgen_anon_1.sw_scratch;
let data_len =
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info2
as usize;
@@ -1049,7 +1184,10 @@ impl cpu::Vcpu for MshvVcpu {
assert!(data_len <= 0x8);
let mut data = vec![0; data_len];
self.gpa_read(src_gpa, &mut data)?;
// SAFETY: Accessing data from a mapped address
let bytes_shared_ghcb =
unsafe { (*ghcb).shared[0].to_le_bytes() };
data.copy_from_slice(&bytes_shared_ghcb[..data_len]);
if let Some(vm_ops) = &self.vm_ops {
vm_ops.mmio_write(dst_gpa, &data).map_err(|e| {
@@ -1058,7 +1196,7 @@ impl cpu::Vcpu for MshvVcpu {
}
// Clear the SW_EXIT_INFO1 register to indicate no error
self.clear_swexit_info1(ghcb_gpa)?;
self.clear_swexit_info1()?;
}
SVM_EXITCODE_SNP_GUEST_REQUEST
| SVM_EXITCODE_SNP_EXTENDED_GUEST_REQUEST => {
@@ -1067,15 +1205,16 @@ impl cpu::Vcpu for MshvVcpu {
// We don't support extended guest request, so we just write empty data.
// This matches the behavior of KVM in Linux 6.11.
// Read RAX & RBX from the GHCB.
let mut data = [0; 8];
self.gpa_read(ghcb_gpa + GHCB_RAX_OFFSET, &mut data)?;
let data_gpa = u64::from_le_bytes(data);
self.gpa_read(ghcb_gpa + GHCB_RBX_OFFSET, &mut data)?;
let data_npages = u64::from_le_bytes(data);
// Read RBX from the GHCB.
// SAFETY: Accessing data from a mapped address
let data_gpa = unsafe { (*ghcb).rax };
// SAFETY: Accessing data from a mapped address
let data_npages = unsafe { (*ghcb).rbx };
if data_npages > 0 {
// The certificates are terminated by 24 zero bytes.
// TODO: Need to check if data_gpa is the address of the shared buffer in the GHCB page
// in that case we should clear the shared buffer(24 bytes)
self.gpa_write(data_gpa, &[0; 24])?;
}
}
@@ -1096,7 +1235,7 @@ impl cpu::Vcpu for MshvVcpu {
req_gpa, rsp_gpa
);
self.gpa_write(ghcb_gpa + GHCB_SW_EXITINFO2_OFFSET, &[0; 8])?;
set_svm_field_u64_ptr!(ghcb, exit_info2, 0);
}
SVM_EXITCODE_SNP_AP_CREATION => {
let vmsa_gpa =
@@ -1117,7 +1256,7 @@ impl cpu::Vcpu for MshvVcpu {
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
// Clear the SW_EXIT_INFO1 register to indicate no error
self.clear_swexit_info1(ghcb_gpa)?;
self.clear_swexit_info1()?;
}
_ => panic!(
"GHCB_INFO_NORMAL: Unhandled exit code: {:0x}",
@@ -1167,25 +1306,50 @@ impl cpu::Vcpu for MshvVcpu {
}
#[cfg(target_arch = "aarch64")]
fn get_reg_list(&self, reg_list: &mut RegList) -> cpu::Result<()> {
fn get_reg_list(&self, _reg_list: &mut crate::RegList) -> cpu::Result<()> {
unimplemented!()
}
#[cfg(target_arch = "aarch64")]
fn vcpu_init(&self, kvi: &VcpuInit) -> cpu::Result<()> {
fn vcpu_init(&self, _kvi: &crate::VcpuInit) -> cpu::Result<()> {
unimplemented!()
}
#[cfg(target_arch = "aarch64")]
fn set_regs(&self, regs: &StandardRegisters) -> cpu::Result<()> {
fn set_regs(&self, _regs: &crate::StandardRegisters) -> cpu::Result<()> {
unimplemented!()
}
#[cfg(target_arch = "aarch64")]
fn get_regs(&self) -> cpu::Result<StandardRegisters> {
fn get_regs(&self) -> cpu::Result<crate::StandardRegisters> {
unimplemented!()
}
#[cfg(target_arch = "aarch64")]
fn vcpu_finalize(&self, _feature: i32) -> cpu::Result<()> {
unimplemented!()
}
#[cfg(target_arch = "aarch64")]
fn vcpu_get_finalized_features(&self) -> i32 {
unimplemented!()
}
#[cfg(target_arch = "aarch64")]
fn vcpu_set_processor_features(
&self,
_vm: &Arc<dyn crate::Vm>,
_kvi: &mut crate::VcpuInit,
_id: u8,
) -> cpu::Result<()> {
unimplemented!()
}
#[cfg(target_arch = "aarch64")]
fn create_vcpu_init(&self) -> crate::VcpuInit {
unimplemented!();
}
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call to setup the CPUID registers.
@@ -1495,37 +1659,16 @@ impl MshvVcpu {
/// Clear SW_EXIT_INFO1 register for SEV-SNP guests.
///
#[cfg(feature = "sev_snp")]
fn clear_swexit_info1(
&self,
ghcb_gpa: u64,
) -> std::result::Result<cpu::VmExit, cpu::HypervisorCpuError> {
fn clear_swexit_info1(&self) -> std::result::Result<cpu::VmExit, cpu::HypervisorCpuError> {
// Clear the SW_EXIT_INFO1 register to indicate no error
self.gpa_write(ghcb_gpa + GHCB_SW_EXITINFO1_OFFSET, &[0; 4])?;
// Safe to use unwrap, for sev_snp guest we already have the
// GHCB pointer wrapped in the option, otherwise this place is not reached.
let ghcb = self.ghcb.as_ref().unwrap().0;
set_svm_field_u64_ptr!(ghcb, exit_info1, 0);
Ok(cpu::VmExit::Ignore)
}
#[cfg(feature = "sev_snp")]
fn gpa_read(&self, gpa: u64, data: &mut [u8]) -> cpu::Result<()> {
for (gpa, chunk) in (gpa..)
.step_by(HV_READ_WRITE_GPA_MAX_SIZE as usize)
.zip(data.chunks_mut(HV_READ_WRITE_GPA_MAX_SIZE as usize))
{
let mut rw_gpa_arg = mshv_bindings::mshv_read_write_gpa {
base_gpa: gpa,
byte_count: chunk.len() as u32,
..Default::default()
};
self.fd
.gpa_read(&mut rw_gpa_arg)
.map_err(|e| cpu::HypervisorCpuError::GpaRead(e.into()))?;
chunk.copy_from_slice(&rw_gpa_arg.data[..chunk.len()]);
}
Ok(())
}
#[cfg(feature = "sev_snp")]
fn gpa_write(&self, gpa: u64, data: &[u8]) -> cpu::Result<()> {
for (gpa, chunk) in (gpa..)
@@ -1558,6 +1701,8 @@ pub struct MshvVm {
dirty_log_slots: Arc<RwLock<HashMap<u64, MshvDirtyLogSlot>>>,
#[cfg(feature = "sev_snp")]
sev_snp_enabled: bool,
#[cfg(feature = "sev_snp")]
host_access_pages: ArcSwap<AtomicBitmap>,
}
impl MshvVm {
@@ -1580,9 +1725,9 @@ impl MshvVm {
/// # Examples
///
/// ```
/// # extern crate hypervisor;
/// # use hypervisor::mshv::MshvHypervisor;
/// # use std::sync::Arc;
/// extern crate hypervisor;
/// use hypervisor::mshv::MshvHypervisor;
/// use std::sync::Arc;
/// let mshv = MshvHypervisor::new().unwrap();
/// let hypervisor = Arc::new(mshv);
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
@@ -1649,6 +1794,34 @@ impl vm::Vm for MshvVm {
.fd
.create_vcpu(id)
.map_err(|e| vm::HypervisorVmError::CreateVcpu(e.into()))?;
/* Map the GHCB page to the VMM(root) address space
* The map is available after the vcpu creation. This address is mapped
* to the overlay ghcb page of the Microsoft Hypervisor, don't have
* to worry about the scenario when a guest changes the GHCB mapping.
*/
#[cfg(feature = "sev_snp")]
let ghcb = if self.sev_snp_enabled {
// SAFETY: Safe to call as VCPU has this map already available upon creation
let addr = unsafe {
libc::mmap(
std::ptr::null_mut(),
HV_PAGE_SIZE,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
vcpu_fd.as_raw_fd(),
MSHV_VP_MMAP_OFFSET_GHCB as i64 * libc::sysconf(libc::_SC_PAGE_SIZE),
)
};
if addr == libc::MAP_FAILED {
// No point of continuing, without this mmap VMGEXIT will fail anyway
// Return error
return Err(vm::HypervisorVmError::MmapToRoot);
}
Some(Ghcb(addr as *mut svm_ghcb_base))
} else {
None
};
let vcpu = MshvVcpu {
fd: vcpu_fd,
vp_index: id,
@@ -1658,6 +1831,10 @@ impl vm::Vm for MshvVm {
msrs: self.msrs.clone(),
vm_ops,
vm_fd: self.fd.clone(),
#[cfg(feature = "sev_snp")]
ghcb,
#[cfg(feature = "sev_snp")]
host_access_pages: ArcSwap::new(self.host_access_pages.load().clone()),
};
Ok(Arc::new(vcpu))
}
@@ -1784,7 +1961,7 @@ impl vm::Vm for MshvVm {
fn create_passthrough_device(&self) -> vm::Result<VfioDeviceFd> {
let mut vfio_dev = mshv_create_device {
type_: mshv_device_type_MSHV_DEV_TYPE_VFIO,
type_: MSHV_DEV_TYPE_VFIO,
fd: 0,
flags: 0,
};
@@ -2011,7 +2188,7 @@ impl vm::Vm for MshvVm {
}
#[cfg(target_arch = "aarch64")]
fn get_preferred_target(&self, kvi: &mut VcpuInit) -> vm::Result<()> {
fn get_preferred_target(&self, _kvi: &mut crate::VcpuInit) -> vm::Result<()> {
unimplemented!()
}
@@ -2050,6 +2227,7 @@ impl vm::Vm for MshvVm {
#[cfg(feature = "sev_snp")]
fn gain_page_access(&self, gpa: u64, size: u32) -> vm::Result<()> {
use mshv_ioctls::set_bits;
const ONE_GB: usize = 1024 * 1024 * 1024;
if !self.sev_snp_enabled {
return Ok(());
@@ -2058,7 +2236,25 @@ impl vm::Vm for MshvVm {
let start_gpfn: u64 = gpa >> PAGE_SHIFT;
let end_gpfn: u64 = (gpa + size as u64 - 1) >> PAGE_SHIFT;
let gpas: Vec<u64> = (start_gpfn..=end_gpfn).map(|x| x << PAGE_SHIFT).collect();
// Enlarge the bitmap if the PFN is greater than the bitmap length
if end_gpfn >= self.host_access_pages.load().as_ref().len() as u64 {
self.host_access_pages.rcu(|bitmap| {
let mut bm = bitmap.as_ref().clone();
bm.enlarge(ONE_GB);
bm
});
}
let gpas: Vec<u64> = (start_gpfn..=end_gpfn)
.filter(|x| {
!self
.host_access_pages
.load()
.as_ref()
.is_bit_set(*x as usize)
})
.map(|x| x << PAGE_SHIFT)
.collect();
if !gpas.is_empty() {
let mut gpa_list = vec_with_array_field::<mshv_modify_gpa_host_access, u64>(gpas.len());
@@ -2081,6 +2277,14 @@ impl vm::Vm for MshvVm {
self.fd
.modify_gpa_host_access(&gpa_list[0])
.map_err(|e| vm::HypervisorVmError::ModifyGpaHostAccess(e.into()))?;
for acquired_gpa in gpas {
self.host_access_pages.rcu(|bitmap| {
let bm = bitmap.clone();
bm.set_bit((acquired_gpa >> PAGE_SHIFT) as usize);
bm
});
}
}
Ok(())

View File

@@ -16,10 +16,3 @@ pub const ECDSA_SIG_X_COMPONENT_END: usize =
pub const ECDSA_SIG_Y_COMPONENT_START: usize = ECDSA_SIG_X_COMPONENT_END;
pub const ECDSA_SIG_Y_COMPONENT_END: usize =
ECDSA_SIG_X_COMPONENT_END + ECDSA_SIG_Y_COMPONENT_SIZE_IN_BYTES;
// These constants are derived from GHCB spec Sect. 2.6 Table 3 GHCB Layout
// Link: https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/56421.pdf
pub const GHCB_RAX_OFFSET: u64 = 0x01F8;
pub const GHCB_RBX_OFFSET: u64 = 0x0318;
pub const GHCB_SW_EXITINFO1_OFFSET: u64 = 0x398;
pub const GHCB_SW_EXITINFO2_OFFSET: u64 = 0x3A0;

View File

@@ -35,12 +35,7 @@ impl MshvEmulatorContext<'_> {
}
}
fn read_memory_flags(
&self,
gva: u64,
data: &mut [u8],
flags: u32,
) -> Result<(), PlatformError> {
fn r(&self, gva: u64, data: &mut [u8], flags: u32) -> Result<(), PlatformError> {
let gpa = self.translate(gva, flags)?;
debug!(
"mshv emulator: memory read {} bytes from [{:#x} -> {:#x}]",
@@ -59,17 +54,38 @@ impl MshvEmulatorContext<'_> {
Ok(())
}
}
/// Platform emulation for Hyper-V
impl PlatformEmulator for MshvEmulatorContext<'_> {
type CpuState = EmulatorCpuState;
fn read_memory_flags(
&self,
gva: u64,
data: &mut [u8],
flags: u32,
) -> Result<(), PlatformError> {
let mut len = data.len() as u64;
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
self.read_memory_flags(gva, data, HV_TRANSLATE_GVA_VALIDATE_READ)
// Compare the page number of the first and last byte. If they are different, this is a
// cross-page access.
let pg1 = gva >> HV_HYP_PAGE_SHIFT;
let pg2 = (gva + len - 1) >> HV_HYP_PAGE_SHIFT;
let cross_page = pg1 != pg2;
if cross_page {
// We only handle one page cross-page access
assert!(pg1 + 1 == pg2);
let n = (gva + len) & HV_HYP_PAGE_MASK as u64;
len -= n;
}
self.r(gva, &mut data[..len as usize], flags)?;
if cross_page {
self.r(gva + len, &mut data[len as usize..], flags)?;
}
Ok(())
}
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
fn w(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
let gpa = self.translate(gva, HV_TRANSLATE_GVA_VALIDATE_WRITE)?;
debug!(
"mshv emulator: memory write {} bytes at [{:#x} -> {:#x}]",
@@ -88,6 +104,40 @@ impl PlatformEmulator for MshvEmulatorContext<'_> {
Ok(())
}
}
/// Platform emulation for Hyper-V
impl PlatformEmulator for MshvEmulatorContext<'_> {
type CpuState = EmulatorCpuState;
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
self.read_memory_flags(gva, data, HV_TRANSLATE_GVA_VALIDATE_READ)
}
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
let mut len = data.len() as u64;
// Compare the page number of the first and last byte. If they are different, this is a
// cross-page access.
let pg1 = gva >> HV_HYP_PAGE_SHIFT;
let pg2 = (gva + len - 1) >> HV_HYP_PAGE_SHIFT;
let cross_page = pg1 != pg2;
if cross_page {
// We only handle one page cross-page access
assert!(pg1 + 1 == pg2);
let n = (gva + len) & HV_HYP_PAGE_MASK as u64;
len -= n;
}
self.w(gva, &data[..len as usize])?;
if cross_page {
self.w(gva + len, &data[len as usize..])?;
}
Ok(())
}
fn cpu_state(&self, cpu_id: usize) -> Result<Self::CpuState, PlatformError> {
if cpu_id != self.vcpu.vp_index as usize {

View File

@@ -1,3 +1,5 @@
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
//
// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
@@ -12,7 +14,7 @@ use std::any::Any;
#[cfg(target_arch = "x86_64")]
use std::fs::File;
use std::sync::Arc;
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
use std::sync::Mutex;
#[cfg(feature = "sev_snp")]
@@ -20,10 +22,10 @@ use igvm_defs::IGVM_VHS_SNP_ID_BLOCK;
use thiserror::Error;
use vmm_sys_util::eventfd::EventFd;
#[cfg(target_arch = "aarch64")]
use crate::aarch64::VcpuInit;
#[cfg(target_arch = "aarch64")]
use crate::arch::aarch64::gic::{Vgic, VgicConfig};
#[cfg(target_arch = "riscv64")]
use crate::arch::riscv64::aia::{Vaia, VaiaConfig};
#[cfg(feature = "tdx")]
use crate::arch::x86::CpuIdEntry;
use crate::cpu::Vcpu;
@@ -224,6 +226,11 @@ pub enum HypervisorVmError {
#[error("Failed to create Vgic: {0}")]
CreateVgic(#[source] anyhow::Error),
///
/// Create Vaia error
///
#[error("Failed to create Vaia: {0}")]
CreateVaia(#[source] anyhow::Error),
///
/// Import isolated pages error
///
#[error("Failed to import isolated pages: {0}")]
@@ -242,6 +249,12 @@ pub enum HypervisorVmError {
#[cfg(feature = "sev_snp")]
#[error("Failed to modify GPA host access: {0}")]
ModifyGpaHostAccess(#[source] anyhow::Error),
///
/// Failed to mmap
///
#[cfg(feature = "sev_snp")]
#[error("Failed to mmap:")]
MmapToRoot,
}
///
/// Result type for returning from a function
@@ -293,6 +306,7 @@ pub trait Vm: Send + Sync + Any {
#[cfg(target_arch = "x86_64")]
/// Sets the address of the three-page region in the VM's address space.
fn set_tss_address(&self, offset: usize) -> Result<()>;
#[cfg(not(target_arch = "riscv64"))]
/// Creates an in-kernel interrupt controller.
fn create_irq_chip(&self) -> Result<()>;
/// Registers an event that will, when signaled, trigger the `gsi` IRQ.
@@ -303,6 +317,8 @@ pub trait Vm: Send + Sync + Any {
fn create_vcpu(&self, id: u8, vm_ops: Option<Arc<dyn VmOps>>) -> Result<Arc<dyn Vcpu>>;
#[cfg(target_arch = "aarch64")]
fn create_vgic(&self, config: VgicConfig) -> Result<Arc<Mutex<dyn Vgic>>>;
#[cfg(target_arch = "riscv64")]
fn create_vaia(&self, config: VaiaConfig) -> Result<Arc<Mutex<dyn Vaia>>>;
/// Registers an event to be signaled whenever a certain address is written to.
fn register_ioevent(
@@ -333,7 +349,7 @@ pub trait Vm: Send + Sync + Any {
fn remove_user_memory_region(&self, user_memory_region: UserMemoryRegion) -> Result<()>;
/// Returns the preferred CPU target type which can be emulated by KVM on underlying host.
#[cfg(target_arch = "aarch64")]
fn get_preferred_target(&self, kvi: &mut VcpuInit) -> Result<()>;
fn get_preferred_target(&self, kvi: &mut crate::VcpuInit) -> Result<()>;
/// Enable split Irq capability
#[cfg(target_arch = "x86_64")]
fn enable_split_irq(&self) -> Result<()>;

View File

@@ -6,13 +6,13 @@ version = "0.1.0"
[dependencies]
epoll = "4.3.3"
getrandom = "0.2.14"
libc = "0.2.158"
getrandom = "0.3.1"
libc = "0.2.167"
log = "0.4.22"
net_gen = { path = "../net_gen" }
rate_limiter = { path = "../rate_limiter" }
serde = { version = "1.0.208", features = ["derive"] }
thiserror = "1.0.62"
thiserror = "2.0.6"
virtio-bindings = { workspace = true }
virtio-queue = { workspace = true }
vm-memory = { workspace = true, features = [

View File

@@ -80,7 +80,7 @@ impl MacAddr {
pub fn local_random() -> MacAddr {
// Generate a fully random MAC
let mut random_bytes = [0u8; MAC_ADDR_LEN];
if let Err(e) = getrandom::getrandom(&mut random_bytes) {
if let Err(e) = getrandom::fill(&mut random_bytes) {
error!(
"Error populating MAC address with random data: {}",
e.to_string()

View File

@@ -75,9 +75,7 @@ pub fn open_tap(
let mut ifname: String = String::new();
let vnet_hdr_size = vnet_hdr_len() as i32;
// Check if the given interface exists before we create it.
let tap_existed = if_name.map_or(false, |n| {
Path::new(&format!("/sys/class/net/{n}")).exists()
});
let tap_existed = if_name.is_some_and(|n| Path::new(&format!("/sys/class/net/{n}")).exists());
// In case the tap interface already exists, check if the number of
// queues is appropriate. The tap might not support multiqueue while

View File

@@ -468,7 +468,7 @@ impl NetQueuePair {
let rate_limit_reached = self
.rx_rate_limiter
.as_ref()
.map_or(false, |r| r.is_blocked());
.is_some_and(|r| r.is_blocked());
// Stop listening on the `RX_TAP_EVENT` when:
// 1) there is no available describes, or

View File

@@ -10,13 +10,13 @@ kvm = ["vfio-ioctls/kvm"]
mshv = ["vfio-ioctls/mshv"]
[dependencies]
anyhow = "1.0.87"
anyhow = "1.0.94"
byteorder = "1.5.0"
hypervisor = { path = "../hypervisor" }
libc = "0.2.158"
libc = "0.2.167"
log = "0.4.22"
serde = { version = "1.0.208", features = ["derive"] }
thiserror = "1.0.62"
thiserror = "2.0.6"
vfio-bindings = { workspace = true, features = ["fam-wrappers"] }
vfio-ioctls = { workspace = true, default-features = false }
vfio_user = { workspace = true }

View File

@@ -90,7 +90,7 @@ impl PciDevice for PciRoot {
self.config.read_reg(reg_idx)
}
fn as_any(&mut self) -> &mut dyn Any {
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@@ -125,19 +125,16 @@ impl PciBus {
pub fn register_mapping(
&self,
dev: Arc<dyn BusDeviceSync>,
#[cfg(target_arch = "x86_64")] io_bus: &Bus,
io_bus: &Bus,
mmio_bus: &Bus,
bars: Vec<PciBarConfiguration>,
) -> Result<()> {
for bar in bars {
match bar.region_type() {
PciBarRegionType::IoRegion => {
#[cfg(target_arch = "x86_64")]
io_bus
.insert(dev.clone(), bar.addr(), bar.size())
.map_err(PciRootError::PioInsert)?;
#[cfg(not(target_arch = "x86_64"))]
error!("I/O region is not supported");
}
PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => {
mmio_bus
@@ -294,7 +291,7 @@ impl PciConfigIo {
),
2 => (
0x0000_ffff << (offset * 16),
(u32::from(data[1]) << 8 | u32::from(data[0])) << (offset * 16),
((u32::from(data[1]) << 8) | u32::from(data[0])) << (offset * 16),
),
4 => (0xffff_ffff, LittleEndian::read_u32(data)),
_ => return,

View File

@@ -582,7 +582,7 @@ impl PciConfiguration {
} else {
let mut registers = [0u32; NUM_CONFIGURATION_REGISTERS];
let mut writable_bits = [0u32; NUM_CONFIGURATION_REGISTERS];
registers[0] = u32::from(device_id) << 16 | u32::from(vendor_id);
registers[0] = (u32::from(device_id) << 16) | u32::from(vendor_id);
// TODO(dverkamp): Status should be write-1-to-clear
writable_bits[1] = 0x0000_ffff; // Status (r/o), command (r/w)
let pi = if let Some(pi) = programming_interface {
@@ -590,9 +590,9 @@ impl PciConfiguration {
} else {
0
};
registers[2] = u32::from(class_code.get_register_value()) << 24
| u32::from(subclass.get_register_value()) << 16
| u32::from(pi) << 8
registers[2] = (u32::from(class_code.get_register_value()) << 24)
| (u32::from(subclass.get_register_value()) << 16)
| (u32::from(pi) << 8)
| u32::from(revision_id);
writable_bits[3] = 0x0000_00ff; // Cacheline size (r/w)
match header_type {
@@ -606,7 +606,7 @@ impl PciConfiguration {
writable_bits[15] = 0xffff_00ff; // Bridge control (r/w), interrupt line (r/w)
}
};
registers[11] = u32::from(subsystem_id) << 16 | u32::from(subsystem_vendor_id);
registers[11] = (u32::from(subsystem_id) << 16) | u32::from(subsystem_vendor_id);
(
registers,
@@ -933,7 +933,7 @@ impl PciConfiguration {
1 => self.write_byte(reg_idx * 4 + offset as usize, data[0]),
2 => self.write_word(
reg_idx * 4 + offset as usize,
u16::from(data[0]) | u16::from(data[1]) << 8,
u16::from(data[0]) | (u16::from(data[1]) << 8),
),
4 => self.write_reg(reg_idx, LittleEndian::read_u32(data)),
_ => (),
@@ -1009,9 +1009,9 @@ impl PciConfiguration {
"Detected BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}",
reg_idx, self.registers[reg_idx], value
);
let old_base = u64::from(self.bars[bar_idx].addr & mask) << 32
let old_base = (u64::from(self.bars[bar_idx].addr & mask) << 32)
| u64::from(self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1]);
let new_base = u64::from(value & mask) << 32
let new_base = (u64::from(value & mask) << 32)
| u64::from(self.registers[reg_idx - 1] & self.writable_bits[reg_idx - 1]);
let len =
decode_64_bits_bar_size(self.bars[bar_idx].size, self.bars[bar_idx - 1].size)
@@ -1159,7 +1159,7 @@ mod tests {
use super::*;
#[repr(packed)]
#[repr(C, packed)]
#[derive(Clone, Copy, Default)]
#[allow(dead_code)]
struct TestCap {

View File

@@ -115,7 +115,7 @@ pub trait PciDevice: Send {
}
/// Provides a mutable reference to the Any trait. This is useful to let
/// the caller have access to the underlying type behind the trait.
fn as_any(&mut self) -> &mut dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
/// Optionally returns a unique identifier.
fn id(&self) -> Option<String>;

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