Compare commits

...

178 Commits
v31.1 ... v33.0

Author SHA1 Message Date
Bo Chen
6e0e632361 build: Release v33.0
Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-06-29 09:55:24 -07:00
Ravi kumar Veeramally
1c9df58440 net_gen: Use more specific clippy lint controls
Remove clippy:all and update it with correct clippy lint,
so that it gives proper information.

Signed-off-by: Ravi kumar Veeramally <ravikumar.veeramally@intel.com>
2023-06-29 08:09:20 +01:00
dependabot[bot]
1917e86206 build: Bump bitflags from 2.3.2 to 2.3.3
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.3.2 to 2.3.3.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.3.2...2.3.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-28 18:11:01 -07:00
dependabot[bot]
787f5085b6 build: Bump proc-macro2 from 1.0.60 to 1.0.63 in /fuzz
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.60 to 1.0.63.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.60...1.0.63)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-29 00:14:32 +00:00
dependabot[bot]
fec39ccf51 build: Bump serde from 1.0.163 to 1.0.164
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.163 to 1.0.164.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.163...v1.0.164)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-28 00:44:59 +00:00
dependabot[bot]
115085eb9e build: Bump libc from 0.2.146 to 0.2.147 in /fuzz
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.146 to 0.2.147.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.146...0.2.147)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-28 00:26:11 +00:00
Ravi kumar Veeramally
802e9009b9 tests: Remove "useless_conversion" clippy
Between musl and glibc there is a difference in the signature of the
ioctl libc function. Use an anonymous cast to force the type coversion.

Signed-off-by: Ravi kumar Veeramally <ravikumar.veeramally@intel.com>
2023-06-27 09:53:41 -07:00
dependabot[bot]
37fbece885 build: Bump toml_datetime from 0.6.1 to 0.6.3
Bumps [toml_datetime](https://github.com/toml-rs/toml) from 0.6.1 to 0.6.3.
- [Commits](https://github.com/toml-rs/toml/compare/toml_datetime-v0.6.1...toml_datetime-v0.6.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-27 00:44:56 +00:00
dependabot[bot]
ec6e87479f build: Bump serde_json from 1.0.97 to 1.0.99 in /fuzz
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.97 to 1.0.99.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.97...v1.0.99)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-27 00:12:53 +00:00
Ravi kumar Veeramally
5b51024ef7 pci: Remove "from_over_into" clippy
According the std docs implementing From<..> is preferred since it
gives you Into<..> for free where the reverse isn’t true.

Signed-off-by: Ravi kumar Veeramally <ravikumar.veeramally@intel.com>
2023-06-26 10:37:58 -07:00
Ravi kumar Veeramally
99a98f270d vm-allicator: Remove "new_without_default" clippy
Add a Default implementation that delegates to new, so that clippy
can be removed.

Signed-off-by: Ravi kumar Veeramally <ravikumar.veeramally@intel.com>
2023-06-26 06:07:15 -07:00
dependabot[bot]
c0af33a4c9 build: Bump serde_with from 2.3.3 to 3.0.0
Bumps [serde_with](https://github.com/jonasbb/serde_with) from 2.3.3 to 3.0.0.
- [Release notes](https://github.com/jonasbb/serde_with/releases)
- [Commits](https://github.com/jonasbb/serde_with/compare/v2.3.3...v3.0.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-24 00:44:50 +00:00
dependabot[bot]
0cf868b5c1 build: Bump tracing-attributes from 0.1.25 to 0.1.26
Bumps [tracing-attributes](https://github.com/tokio-rs/tracing) from 0.1.25 to 0.1.26.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-attributes-0.1.25...tracing-attributes-0.1.26)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-23 00:45:10 +00:00
dependabot[bot]
3da60dfa5e build: Bump tracing-attributes from 0.1.24 to 0.1.25
Bumps [tracing-attributes](https://github.com/tokio-rs/tracing) from 0.1.24 to 0.1.25.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-attributes-0.1.24...tracing-attributes-0.1.25)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-22 00:44:07 +00:00
dependabot[bot]
1c8a91940e build: Bump openssl-sys from 0.9.87 to 0.9.90
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.87 to 0.9.90.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.87...openssl-sys-v0.9.90)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-21 18:57:44 +00:00
Ravi kumar Veeramally
8c117a8117 vmm: Remove identity_op audit clippy
Avoid identity operations with 0.

Signed-off-by: Ravi kumar Veeramally <ravikumar.veeramally@intel.com>
2023-06-20 14:25:54 -07:00
Ruslan Mstoi
7d0aa1fd02 scripts: dev_cli: Bump container version
Signed-off-by: Ruslan Mstoi <ruslan.mstoi@intel.com>
2023-06-20 12:39:18 -07:00
Ruslan Mstoi
a0190b16aa Add Hadolint Dockerfile Linter to CI
Fixes #5332

Signed-off-by: Ruslan Mstoi <ruslan.mstoi@intel.com>
2023-06-20 12:39:18 -07:00
Ruslan Mstoi
1bd8eb68ff Dockerfile: fix hadolint warnings
Fix these warnings:
SC2086 info: Double quote to prevent globbing and word splitting.
DL3047 info: Avoid use of wget without progress bar. Use `wget --progress=dot:giga <url>`. Or consider using `-q` or `-nv` (shorthands for `--quiet` or `--no-verbose`).
SC2006 style: Use $(...) notation instead of legacy backticks `...`.

Ignore these warning cause they break the build or they do not apply:
DL3008 warning: Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
DL3003 warning: Use WORKDIR to switch to a directory
SC2016 info: Expressions don't expand in single quotes, use double quotes for that.
SC2046 warning: Quote this to prevent word splitting.
DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check
SC2155 warning: Declare and assign separately to avoid masking return values.

Signed-off-by: Ruslan Mstoi <ruslan.mstoi@intel.com>
2023-06-20 12:39:18 -07:00
Ruslan Mstoi
8363eddd97 Dockerfile: fix hadolint error, do not install recommended packages
hadolint error:
DL3015 info: Avoid additional packages by specifying `--no-install-recommends`

Add required packages ca-certificates unzip iproute2 dbus

Without these packages build or integration tests fail if
--no-install-recommends is specified. Previously these packages were
installed as part of recommended dependency packages.

Signed-off-by: Ruslan Mstoi <ruslan.mstoi@intel.com>
2023-06-20 12:39:18 -07:00
Ruslan Mstoi
70aa5bc231 Dockerfile: remove unneeded files to free space
Remove temporary, unneeded files in the docker image to save
about 2.0M of space.

Signed-off-by: Ruslan Mstoi <ruslan.mstoi@intel.com>
2023-06-20 12:39:18 -07:00
Ruslan Mstoi
b8ce1816e9 Dockerfile: convert DEBIAN_FRONTEND to environment variable
Setting environment variable
DEBIAN_FRONTEND=noninteractive
Makes unnecessary setting it in all apt-get install commands

Signed-off-by: Ruslan Mstoi <ruslan.mstoi@intel.com>
2023-06-20 12:39:18 -07:00
Ruslan Mstoi
43eed4861f Dockerfile: untabify
Some parts of Dockerfile are indented with spaces others with tabs.
Unify indentation to use spaces.

Signed-off-by: Ruslan Mstoi <ruslan.mstoi@intel.com>
2023-06-20 12:39:18 -07:00
Bo Chen
268c4ea214 tests: disable test_vfio test
Details: https://github.com/cloud-hypervisor/cloud-hypervisor/issues/5190#issuecomment-1599357074

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-06-20 12:14:03 -07:00
Ruslan Mstoi
29a0ddc58e tests: Revert disable test_vfio test
This reverts commit 51c1738d55.

Signed-off-by: Ruslan Mstoi <ruslan.mstoi@intel.com>
2023-06-20 16:39:21 +01:00
Ruslan Mstoi
4079e4f1ad tests: Fix test_vfio test
Test was failing due to regression caused by commit
d5558aea2a

Failing command:
sudo /mnt/ch-remote --api-socket /tmp/ch_api.sock resize --memory=1073741824"

Fixes #5190

Signed-off-by: Ruslan Mstoi <ruslan.mstoi@intel.com>
2023-06-20 16:39:21 +01:00
Alyssa Ross
fba0b5f93c vmm: ignore and warn TAP FDs send in vm.create
This does the same thing as df2a7c17 ("vmm: Ignore and warn TAP FDs
sent via the HTTP request body"), but for the vm.create endpoint,
which also previously would accept file descriptors in the body, and
try to use whatever fd occupied that number as a TAP device.

Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-06-20 15:45:43 +01:00
Alyssa Ross
05cc5f596c vmm: ignore and warn TAP FDs sent via the D-Bus request body
Port of df2a7c17 ("vmm: Ignore and warn TAP FDs sent via the HTTP
request body"), but for the vm.create endpoint, which would previously
accept file descriptors in the body, and try to use whatever fd
occupied that number as a TAP device.

Since I had to move the wrapping of the net config in an Arc until
after it was modified, I made the same change to all other endpoints,
so the style stays consistent.

Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-06-20 15:45:43 +01:00
zhongbingnan
c1b33329db aarch64/fdt: Forward host cache layout to guest
Using the data from sysfs forward the host host cache layout to the
guest using the FDT tables.

TEST=The host cache layout (from sysfs) can be seen in inside the guest
using lscpu.

Signed-off-by: zhongbingnan <zhongbingnan@bytedance.com>
2023-06-20 15:45:15 +01:00
dependabot[bot]
402a24a688 build: Bump iced-x86 from 1.18.0 to 1.19.0
Bumps [iced-x86](https://github.com/icedland/iced) from 1.18.0 to 1.19.0.
- [Release notes](https://github.com/icedland/iced/releases)
- [Commits](https://github.com/icedland/iced/compare/v1.18.0...v1.19.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-20 09:13:24 +00:00
Alyssa Ross
0809e7f675 vmm: allow restart_syscall() in PTY process
This can be triggered by debugging cloud-hypervisor using gdb, or
probably if the process is suspended and restarted.

Fixes: https://github.com/cloud-hypervisor/cloud-hypervisor/issues/5489
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-06-19 14:26:08 +01:00
Jianyong Wu
a718716831 vfio: fix vfio device fail to initialize issue for 64k page size
Currently, vfio device fails to initialize as the msix-cap region in BAR
is mapped as RW region.

To resolve the initialization issue, this commit avoids mapping the
msix-cap region in the BAR. However, this solution introduces another
problem where aligning the msix table offset in the BAR to the page
size may cause overlap with the MMIO RW region, leading to reduced
performance. By enlarging the entire region in the BAR and relocating
the msix table to achieve page size alignment, this problem can be
overcomed effectively.

Fixes: #5292
Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
2023-06-19 10:29:23 +08:00
Jianyong Wu
5a9dd7489c vm-allocator: Add page size related functions
To avoid code duplication extract page related functions to their
own module and add utility functions for manipulating addresses
related to page sizes

Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
2023-06-19 10:29:23 +08:00
Anatol Belski
dec8d619d4 Revert "build: Temporarily disable Windows guest tests"
This reverts commit 83d57d3cce.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2023-06-18 09:19:38 +01:00
dependabot[bot]
c39cdb3c1f build: Bump rustix from 0.37.19 to 0.37.20
Bumps [rustix](https://github.com/bytecodealliance/rustix) from 0.37.19 to 0.37.20.
- [Release notes](https://github.com/bytecodealliance/rustix/releases)
- [Commits](https://github.com/bytecodealliance/rustix/compare/v0.37.19...v0.37.20)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-17 00:43:48 +00:00
dependabot[bot]
26ae8b6d5c build: Bump serde_json from 1.0.96 to 1.0.97 in /fuzz
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.96 to 1.0.97.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.96...v1.0.97)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-17 00:24:06 +00:00
Bo Chen
de31b3fadc vmm: Clarify memory regions are required to be page-size aligned
Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-06-16 14:15:03 -07:00
Bo Chen
b06ad85604 arch: Refactor the way of creating memory mapping
This patch clarifies the assumptions we have regarding the guest address
space layout while creating memory mapping in E820 on x86_64 and fdt on
aarch64. It also explicitly checks on these assumptions and report
errors if these assumptions do not hold.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-06-16 14:15:03 -07:00
Yu Li
8d89736c68 vmm: memory_manager: align down the rest space of ram_region
This commit renames `ram_region_sub_size` to `ram_region_available_size`
and make its value align down to the default page size or hugepage
size of the current memory zone, which can prevent the memory zone from
being split into misaligned parts.  And if the available size of ram
region is zero, this region will be marked as consumed even it has
unused space.

Note that there is two methods to use hugepages.

1. Specify `hugepages` for `memory` or `memory-zone`, if the
   `hugepage_size` is not specified, the value can be got by `statfs`
   for `/dev/hugepages`.
2. Specify a `file` in hugetlbfs for `memory-zone`, the hugepage size
   can also be got by `statfs` for the file.

The value for alignment will be the hugepage size if this memory zone
is using hugepages, otherwise the value will be default page size of
system.

Fixes: #5463

Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
2023-06-16 14:15:03 -07:00
Yu Li
55ee8eb482 arch: let arch_memory_regions return all available regions
The previous `arch_memory_regions` function will provide some memory
regions with the specified memory size and fill all the previous
regions before using the next one, but sometimes there may be no need
to fill up the previous one, e.g., the previous one should be aligned
with hugepage size.

This commit make `arch_memory_regions` function not take any
parameters and return the max available regions, the memory manager
can use them on demand.

Fixes: #5463

Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
2023-06-16 14:15:03 -07:00
Yu Li
1017157bb6 arch: create memory mapping by the actual memory info
The original codes did not consider that the previous memory region
might not be full and always set it to the maximum size.

This commit fixes this problem by creating memory mappings based on
the actual memory details in both E820 on x86_64 and fdt on aarch64.

Fixes: #5463

Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
2023-06-16 14:15:03 -07:00
Yu Li
499989fb17 logger: use write with \r\n instead of writeln
The device manager will set tty or pty to raw mode, all the `\n` will
be LF without CR, which makes the output difficult to read.

This commit solves it by using `write` with `\r\n` instead of
`writeln`, which can print CR and LF explicitly.

Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
2023-06-16 14:15:03 -07:00
Yu Li
ce0f30bb54 vmm: use unwrap_or instead of match for prefault
Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
2023-06-16 14:15:03 -07:00
Ravi kumar Veeramally
fce202a90c virtio-devices: Remove repeated suffix from enum names
Remove "enum_variant_names" clippy. Enumeration variant names should
specify their variant, not repeat the enumeration name.

Signed-off-by: Ravi kumar Veeramally <ravikumar.veeramally@intel.com>
2023-06-16 14:13:47 -07:00
dependabot[bot]
95626ae564 build: Bump bitflags from 1.3.2 to 2.3.2
Bumps [bitflags](https://github.com/bitflags/bitflags) from 1.3.2 to 2.3.2.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/1.3.2...2.3.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-16 01:41:45 +00:00
dependabot[bot]
1e1012c31b build: Bump uuid from 1.3.3 to 1.3.4 in /fuzz
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.3.3 to 1.3.4.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.3.3...1.3.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-16 01:14:22 +00:00
Ruslan Mstoi
56207dc3a0 tests: fix issue running vfio tests with empty workloads directory
Download firmware and focal image to fix issue of running vfio tests
with empty worlkoads directory:

$ scripts/dev_cli.sh tests --integration-vfio
+ cp /root/workloads/focal-server-cloudimg-amd64-custom-20210609-0.raw /root/workloads/vfio
cp: cannot stat '/root/workloads/focal-server-cloudimg-amd64-custom-20210609-0.raw': No such file or directory
+ cp /root/workloads/hypervisor-fw /root/workloads/vfio
cp: cannot stat '/root/workloads/hypervisor-fw': No such file or directory

Signed-off-by: Ruslan Mstoi <ruslan.mstoi@intel.com>
2023-06-15 15:54:43 -07:00
Jianyong Wu
57fdaa3a39 arch: x86_64: Populate the APIC Id
Program the APIC ID (CPUID leaf 0x1 EBX) with the CPU id. This resolves
an issue where the EDKII firmware expects the APIC ID to vary per-CPU.

Fixes: #5475
Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
2023-06-15 13:50:38 -07:00
dependabot[bot]
19c5c0af9f build: Bump uuid from 1.3.3 to 1.3.4
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.3.3 to 1.3.4.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.3.3...1.3.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-15 00:43:16 +00:00
dependabot[bot]
e26e7b4d72 build: Bump serde from 1.0.163 to 1.0.164 in /fuzz
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.163 to 1.0.164.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.163...v1.0.164)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-15 00:14:27 +00:00
Ravi kumar Veeramally
e8d6c9ecb0 test_data: Simplify default configuration for local testing
Script `create-cloud-init.sh" uses the same cloud init data as the the CI
but this means it is somewhat overloaded with unhelpful network configuration
entries and scripts that are only needed for the CI.

For local testing data kept in test_data/cloud-init/ubuntu/local folder.
This contains minimum configuration data for user to test it locally.
Script assigns default IP address using "network-config" details
with --net "tap=" option. The default network interface will be "ens4".

Signed-off-by: Ravi kumar Veeramally <ravikumar.veeramally@intel.com>
2023-06-14 10:23:52 -07:00
Ravi kumar Veeramally
0712027eb1 ci: Move CI related test data to separate folder
Script `create-cloud-init.sh" uses the same cloud init data as the the CI
but this means it is somewhat overloaded with unhelpful network configuration
entries and scripts that are only needed for the CI.

CI related data moved to test_data/cloud-init/ubuntu/ci folder.

Fixes: #4773

Signed-off-by: Ravi kumar Veeramally <ravikumar.veeramally@intel.com>
2023-06-14 10:23:52 -07:00
dependabot[bot]
7ea5d7a416 build: Bump crossbeam-utils from 0.8.15 to 0.8.16
Bumps [crossbeam-utils](https://github.com/crossbeam-rs/crossbeam) from 0.8.15 to 0.8.16.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-utils-0.8.15...crossbeam-utils-0.8.16)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-14 00:44:53 +00:00
dependabot[bot]
7023d4ff84 build: Bump log from 0.4.18 to 0.4.19 in /fuzz
Bumps [log](https://github.com/rust-lang/log) from 0.4.18 to 0.4.19.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.18...0.4.19)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-14 00:19:49 +00:00
dependabot[bot]
5f31ad319e build: Bump lock_api from 0.4.9 to 0.4.10
Bumps [lock_api](https://github.com/Amanieu/parking_lot) from 0.4.9 to 0.4.10.
- [Changelog](https://github.com/Amanieu/parking_lot/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Amanieu/parking_lot/compare/lock_api-0.4.9...lock_api-0.4.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-13 18:30:33 +00:00
dependabot[bot]
5083549afe build: Bump getrandom from 0.2.9 to 0.2.10 in /fuzz
Bumps [getrandom](https://github.com/rust-random/getrandom) from 0.2.9 to 0.2.10.
- [Changelog](https://github.com/rust-random/getrandom/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/getrandom/compare/v0.2.9...v0.2.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-13 18:27:56 +00:00
Alyssa Ross
beed5e5d6d vmm, virtio-devices: allow mremap for consoles
SerialBuffer uses VecDeque::extend, which calls realloc, which a
maximum buffer size of 1 MiB.  Starting at allocation sizes of
128 KiB, musl's mallocng allocator will use mremap for the allocation.
Since this was not permitted by the seccomp rules, heavy write load
could crash cloud-hypervisor with a seccomp failure.  (Encountered
using virtio-console, but I don't see any reason it wouldn't happen
for the legacy serial device too.)

Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-06-13 11:23:15 -07:00
dependabot[bot]
7feb25daf0 build: Bump mshv-ioctls from dcd4612 to 04f5adb
Bumps [mshv-ioctls](https://github.com/rust-vmm/mshv) from `dcd4612` to `04f5adb`.
- [Commits](dcd4612656...04f5adbf64)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-12 16:51:48 +00:00
dependabot[bot]
f2574897cd build: Bump proc-macro2 from 1.0.59 to 1.0.60 in /fuzz
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.59 to 1.0.60.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.59...1.0.60)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-10 00:17:39 +00:00
dependabot[bot]
2698924c0d build: Bump aho-corasick from 1.0.1 to 1.0.2
Bumps [aho-corasick](https://github.com/BurntSushi/aho-corasick) from 1.0.1 to 1.0.2.
- [Commits](https://github.com/BurntSushi/aho-corasick/compare/1.0.1...1.0.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-09 00:45:14 +00:00
dependabot[bot]
6bd4927d11 build: Bump libc from 0.2.145 to 0.2.146 in /fuzz
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.145 to 0.2.146.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.145...0.2.146)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-09 00:14:36 +00:00
Rob Bradford
f485922b78 build: Bump acpi_tables from cb5f06c to 05a6091
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2023-06-08 17:28:02 +00:00
Bo Chen
1faaa2cd0a tests: Enable "dbus_api" feature build only for dbus_api tests
In this way, our integration tests exercise the same set of build
features (e.g. "kvm,mshv") being used for releases.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-06-08 13:18:08 +01:00
Bo Chen
6c630f4908 build: Use upstream vhost for vDPA regression fix
The latest vhost release v0.7.0 has a vDPA regression [1]. It has been
fixed in the vhost upstream [2], but no new releases are available [3].
To unblock the bulk upgrades on rust-vmm depdencies, let's use the
upstream vhost for now.

[1] https://github.com/rust-vmm/vhost/issues/164
[2] https://github.com/rust-vmm/vhost/pull/165
[3] https://github.com/rust-vmm/vhost/issues/166

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-06-08 13:15:25 +01:00
Bo Chen
3b39c41a01 build: Bulk update rust-vmm dependencies
Bump to the latest rust-vmm crates, including vm-memory, vfio,
vfio-bindings, vfio-user, virtio-bindings, virtio-queue, linux-loader,
vhost, and vhost-user-backend,

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-06-08 13:15:25 +01:00
dependabot[bot]
309e346c4c build: Bump once_cell from 1.17.2 to 1.18.0 in /fuzz
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.17.2 to 1.18.0.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.17.2...v1.18.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-08 00:14:51 +00:00
dependabot[bot]
3f6833b315 build: Bump iced-x86 from 1.18.0 to 1.19.0 in /fuzz
Bumps [iced-x86](https://github.com/icedland/iced) from 1.18.0 to 1.19.0.
- [Release notes](https://github.com/icedland/iced/releases)
- [Commits](https://github.com/icedland/iced/compare/v1.18.0...v1.19.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-07 00:23:37 +00:00
dependabot[bot]
a862c804ba build: Bump openssl-src from 111.25.0+1.1.1t to 111.26.0+1.1.1u
Bumps [openssl-src](https://github.com/alexcrichton/openssl-src-rs) from 111.25.0+1.1.1t to 111.26.0+1.1.1u.
- [Release notes](https://github.com/alexcrichton/openssl-src-rs/releases)
- [Commits](https://github.com/alexcrichton/openssl-src-rs/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-06 18:04:54 +00:00
Omer Faruk Bayram
83914d80a2 docs: api: document the D-Bus API
Signed-off-by: Omer Faruk Bayram <omer.faruk@sartura.hr>
2023-06-06 10:18:26 -07:00
Omer Faruk Bayram
a7aecb5eee tests: refactor test_api* to support the D-Bus API and add a new test
Implemented a `TargetApi` enum to make the process of implementing
tests for the D-Bus and HTTP API more convenient.

Refactored `test_api_{create_boot, shutdown, pause_resume, delete}` tests
with the `TargetApi` enum to also implement tests for the D-Bus API.

Added a new test named `test_api_dbus_and_http_interleaved` that uses
both the HTTP and D-Bus API at the same time.

Modified integration test scripts to enable the `dbus_api` feature when
compiling and start a dbus-session when integration tests are run.

Signed-off-by: Omer Faruk Bayram <omer.faruk@sartura.hr>
2023-06-06 10:18:26 -07:00
Omer Faruk Bayram
a64d27f841 ch-remote: full support for calling the D-Bus API
Support calling into the D-Bus API in a non-breaking way.

Signed-off-by: Omer Faruk Bayram <omer.faruk@sartura.hr>
2023-06-06 10:18:26 -07:00
Omer Faruk Bayram
7a458d85d1 main: cli: add D-Bus API related CLI options
Introduces three new CLI options, `dbus-service-name`,
`dbus-object-path` and `dbus-system-bus` to configure the DBus API.

Signed-off-by: Omer Faruk Bayram <omer.faruk@sartura.hr>
2023-06-06 10:18:26 -07:00
Omer Faruk Bayram
a92d852848 vmm: dbus: apply seccomp filter
This commit applies the previously created seccomp filter
to the `DbusApi` thread.

Also encloses the main loop of the `DBusApi` thread using
`std::panic::catch_unwind` and `AssertUnwindSafe` in order to mirror
the behavior of the HTTP API.

Signed-off-by: Omer Faruk Bayram <omer.faruk@sartura.hr>
2023-06-06 10:18:26 -07:00
Omer Faruk Bayram
0664647109 vmm: seccomp: add new seccomp filter for the DBusApi thread
Signed-off-by: Omer Faruk Bayram <omer.faruk@sartura.hr>
2023-06-06 10:18:26 -07:00
Omer Faruk Bayram
f2c813e1cf vmm: seccomp: rename Thread::Api to Thread::HttpApi
Signed-off-by: Omer Faruk Bayram <omer.faruk@sartura.hr>
2023-06-06 10:18:26 -07:00
Omer Faruk Bayram
f00df25d40 vmm: dbus: graceful shutdown of the DBusApi thread
This commit adds support for graceful shutdown of the DBusApi thread
using `futures::channel::oneshot` channels. By using oneshot channels,
we ensure that the thread has enough time to send a response to the
`VmmShutdown` method call before it is terminated. Without this step,
the thread may be terminated before it can send a response, resulting
in an error message on the client side stating that the message
recipient disconnected from the message bus without providing a reply.

Also changes the default values for DBus service name, object path
and interface name.

Signed-off-by: Omer Faruk Bayram <omer.faruk@sartura.hr>
2023-06-06 10:18:26 -07:00
Omer Faruk Bayram
c016a0d4d3 vmm: dbus: implement the D-Bus API
This commit introduces three new dependencies: `zbus`, `futures`
and `blocking`. `blocking` is used to call the Internal API in zbus'
async context which is driven by `futures::executor`. They are all
behind the `dbus_api` feature flag.

The D-Bus API implementation is behind the same `dbus_api` feature
flag as well.

Signed-off-by: Omer Faruk Bayram <omer.faruk@sartura.hr>
2023-06-06 10:18:26 -07:00
Omer Faruk Bayram
5c96fbb19b vmm: move the http api into its own submodule
This commits moves the http API code into its own
submodule.

Signed-off-by: Omer Faruk Bayram <omer.faruk@sartura.hr>
2023-06-06 10:18:26 -07:00
Rob Bradford
b955f09b80 build: Bump MSRV to 1.64
This is required for openssl-src crate as it now uses functionality
first released in this version.

See: https://github.com/alexcrichton/openssl-src-rs/pull/184

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2023-06-06 17:34:22 +01:00
dependabot[bot]
79ad6f8536 build: Bump libc from 0.2.144 to 0.2.145 in /fuzz
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.144 to 0.2.145.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.144...0.2.145)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-06 00:27:33 +00:00
dependabot[bot]
be4f04a906 build: Bump proc-macro2 from 1.0.52 to 1.0.59
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.52 to 1.0.59.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.52...1.0.59)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-03 00:38:18 +00:00
Rafael Mendonca
f8415debe9 docs: Fix broken link to macvtap doc in custom-image.md
We get a "404 - page not found" when clicking on this link on github.

Signed-off-by: Rafael Mendonca <rafaelmendsr@gmail.com>
2023-06-02 14:57:24 -07:00
dependabot[bot]
9014a5e59c build: Bump serde from 1.0.156 to 1.0.163
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.156 to 1.0.163.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.156...v1.0.163)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-02 00:38:59 +00:00
Ravi kumar Veeramally
612621bb46 vmm: Improve "cloud-hypervisor --help" view format
Rust documentation comments follows 80 char per line. But some of
these help options doesn't have space in between. Longlines of
text is starting from beginning of the next line instead of
proper format.

Before fix:

Options:
  --cpus            boot=<boot_vcpus>,max=<max_vcpus>,topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>,kvm_hyperv=on|off,max_phys_bits=<maximum_number_of_physical_bits>,affinity=<list_of_vcpus_with_their_associated_cpuset>,features=<list_of_features_to_enable>

After fix:

Options:
  --cpus            boot=<boot_vcpus>, max=<max_vcpus>,
                    topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>,
                    kvm_hyperv=on|off,
                    max_phys_bits=<maximum_number_of_physical_bits>,
                    affinity=<list_of_vcpus_with_their_associated_cpuset>,
                    features=<list_of_features_to_enable>

Signed-off-by: Ravi kumar Veeramally <ravikumar.veeramally@intel.com>
2023-06-01 13:46:57 +01:00
Ruslan Mstoi
18e1d2caf5 tests: fix sha1sum check warning
Remove empty line to fix warning:
"sha1sum: WARNING: 1 line is improperly formatted"

Signed-off-by: Ruslan Mstoi <ruslan.mstoi@intel.com>
2023-06-01 13:45:47 +01:00
dependabot[bot]
4be406a308 build: Bump pkg-config from 0.3.26 to 0.3.27
Bumps [pkg-config](https://github.com/rust-lang/pkg-config-rs) from 0.3.26 to 0.3.27.
- [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.26...0.3.27)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-01 02:35:49 +00:00
Anatol Belski
7bf2a2c382 vmm: arch: Make phys_bits functionality use CPU vendor API
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2023-05-31 23:54:33 +02:00
Anatol Belski
7df80220ec hyperivsor: Add infrastructure to determine CPU vendor
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2023-05-31 23:54:33 +02:00
dependabot[bot]
84d2511de0 build: Bump regex from 1.7.1 to 1.8.3
Bumps [regex](https://github.com/rust-lang/regex) from 1.7.1 to 1.8.3.
- [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.7.1...1.8.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-31 00:39:15 +00:00
dependabot[bot]
374263ddac build: Bump once_cell from 1.17.1 to 1.17.2 in /fuzz
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.17.1 to 1.17.2.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.17.1...v1.17.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-31 00:15:58 +00:00
dependabot[bot]
7b76b6b61e build: Bump acpi_tables from 98dcb03 to cb5f06c
Bumps [acpi_tables](https://github.com/rust-vmm/acpi_tables) from `98dcb03` to `cb5f06c`.
- [Commits](98dcb0309d...cb5f06c747)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-30 10:20:37 -07:00
Rob Bradford
e06f12673b build: Bump to latest version of versionize_derive
This version includes a patch to correctly specify the version
dependencies.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2023-05-30 08:27:25 -07:00
Rob Bradford
89e658d9ff misc: Update for beta clippy failures on x86-64
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2023-05-30 07:18:17 -07:00
dependabot[bot]
5ac68534b1 build: Bump log from 0.4.17 to 0.4.18 in /fuzz
Bumps [log](https://github.com/rust-lang/log) from 0.4.17 to 0.4.18.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.17...0.4.18)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-30 00:12:45 +00:00
dependabot[bot]
6acceaa87c build: Bump quote from 1.0.27 to 1.0.28 in /fuzz
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.27 to 1.0.28.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.27...1.0.28)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-27 00:20:10 +00:00
dependabot[bot]
a3898fd8e1 build: Bump mshv-ioctls from c1230f2 to dcd4612
Bumps [mshv-ioctls](https://github.com/rust-vmm/mshv) from `c1230f2` to `dcd4612`.
- [Commits](c1230f282c...dcd4612656)

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

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2023-05-26 05:00:14 +00:00
dependabot[bot]
7026622c24 build: Bump proc-macro2 from 1.0.58 to 1.0.59 in /fuzz
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.58 to 1.0.59.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.58...1.0.59)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-26 00:16:06 +00:00
dependabot[bot]
41e5384f15 build: Bump unicode-ident from 1.0.8 to 1.0.9 in /fuzz
Bumps [unicode-ident](https://github.com/dtolnay/unicode-ident) from 1.0.8 to 1.0.9.
- [Release notes](https://github.com/dtolnay/unicode-ident/releases)
- [Commits](https://github.com/dtolnay/unicode-ident/compare/1.0.8...1.0.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-25 00:17:02 +00:00
dependabot[bot]
681a30bd15 build: Bump thiserror from 1.0.39 to 1.0.40
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.39 to 1.0.40.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.39...1.0.40)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-24 00:39:08 +00:00
Rafael Mendonca
1976157761 main: Fix error propagation if starting the VM fails
Commit 21d40d7 ("main: reset tty if starting the VM fails") changed
start_vmm() to join the vmm thread if an error happens after the vmm
thread is started. The implementation put all the error-prone code that
is run after the vmm is started in a closure, to be able to always join
the vmm thread, regardless of any error happening. However, it missed
propagating the error that might happen inside the closure back to the
main function, after joining the vmm thread.

For some cmd line options, the above issue inhibits proper error
reporting when starting a VM with invalid commands, as many parameters
are parsed after the vmm is started, thus if such parsing fails, no
error will be reported back to the user.

See: #5435
Fixes: 21d40d7 ("main: reset tty if starting the VM fails")
Signed-off-by: Rafael Mendonca <rafaelmendsr@gmail.com>
2023-05-23 09:45:08 -07:00
Bo Chen
0b1e626fe3 vmm: Allocate guest memory address space before TDX initialization
The refactoring on deferring address space allocation (#5169) broke TDX,
as TDX initialization needs to access guest memory for encryption and
measurement of guest pages.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-05-23 09:00:00 -07:00
dependabot[bot]
79bc42f3c2 build: Bump anyhow from 1.0.70 to 1.0.71
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.70 to 1.0.71.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.70...1.0.71)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-23 00:42:11 +00:00
Jianyong Wu
eca75dcfc9 vfio: align memory region size and address to PAGE_SIZE
In current implementation, memory region used in vfio is assumed to
align to 4k which may cause error when the PAGE_SIZE is not 4k, like on
Arm, it can be 16k and 64k.

Remove this assumption and align memory resource used by vfio to
PAGE_SIZE then vfio can run on host with 64k PAGE_SIZE.

Fixes: #5292
Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
2023-05-22 13:25:52 +01:00
dependabot[bot]
600d1664ee build: Bump vfio_user from e75c941 to eef6bec
Bumps [vfio_user](https://github.com/rust-vmm/vfio-user) from `e75c941` to `eef6bec`.
- [Commits](e75c9415d9...eef6bec4d4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-20 00:36:54 +00:00
dependabot[bot]
e6208d1c0e build: Bump uuid from 1.3.0 to 1.3.3
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.3.0 to 1.3.3.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.3.0...1.3.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-19 00:44:43 +00:00
dependabot[bot]
cf635c64d7 build: Bump proc-macro2 from 1.0.57 to 1.0.58 in /fuzz
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.57 to 1.0.58.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.57...1.0.58)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-19 00:19:07 +00:00
Rob Bradford
42ca292dce build: Release v32.0
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2023-05-18 10:09:15 -07:00
dependabot[bot]
b7338c96eb build: Bump serde from 1.0.152 to 1.0.156
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.152 to 1.0.156.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.152...v1.0.156)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-18 13:54:52 +01:00
Rob Bradford
bf4af14e73 tests: Temporarily disable test_macvtap{_hotplug} on aarch64
See: #5443

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2023-05-18 13:54:35 +01:00
dependabot[bot]
24a3505f0b build: Bump proc-macro2 from 1.0.56 to 1.0.57 in /fuzz
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.56 to 1.0.57.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.56...1.0.57)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-17 00:17:34 +00:00
dependabot[bot]
20f3691474 build: Bump uuid from 1.3.2 to 1.3.3 in /fuzz
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.3.2 to 1.3.3.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.3.2...1.3.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-16 00:19:41 +00:00
Hao Xu
c56a3ce59a vmm: reduce memory copy when BFT device tree
The current implementation of breadth first traversal for device tree
uses a temporary vector, therefore causes unnecessary memory copy.
Remove it and do it within vector nodes.

Signed-off-by: Hao Xu <howeyxu@tencent.com>
2023-05-15 17:19:48 +01:00
dependabot[bot]
18f7a37b48 build: Bump serde from 1.0.162 to 1.0.163 in /fuzz
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.162 to 1.0.163.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.162...v1.0.163)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-12 00:13:33 +00:00
dependabot[bot]
98496434b9 build: Bump quote from 1.0.26 to 1.0.27 in /fuzz
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.26 to 1.0.27.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.26...1.0.27)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-10 00:12:21 +00:00
dependabot[bot]
0b2994a6e3 build: Bump libc from 0.2.142 to 0.2.144 in /fuzz
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.142 to 0.2.144.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.142...0.2.144)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-09 00:13:07 +00:00
Anatol Belski
553e6857bd tests: Enable topology integration tests under mshv
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2023-05-08 08:50:09 -07:00
Anatol Belski
083ce323c0 seccomp: Add filter entry for MSHV_VP_REGISTER_INTERCEPT_RESULT
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2023-05-08 08:50:09 -07:00
Anatol Belski
8fff4c1af3 mshv: Pass topology explicitly while constructing cpuid
Unlike KVM, there's no internal handling for topoolgy under MSHV. Thus,
if no topology has been passed during the CH launch, use the boot CPUs
count to construct the topology struct.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2023-05-08 08:50:09 -07:00
Anatol Belski
35ecfb6ec5 hypervisor: mshv: Implement set_cpuid2 call
Passing the CPUID leafs with the topology is integrated into the common
mechanism of setting and patching CPUID in Cloud Hypervisor. All the
CPUID values will be passed to the hypervisor through the register
intercept call.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2023-05-08 08:50:09 -07:00
dependabot[bot]
a61a013311 build: Bump serde from 1.0.160 to 1.0.162 in /fuzz
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.160 to 1.0.162.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.160...1.0.162)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-06 00:12:52 +00:00
dependabot[bot]
b509fd94cd build: Bump darling from 0.20.0 to 0.20.1 in /fuzz
Bumps [darling](https://github.com/TedDriggs/darling) from 0.20.0 to 0.20.1.
- [Release notes](https://github.com/TedDriggs/darling/releases)
- [Changelog](https://github.com/TedDriggs/darling/blob/master/CHANGELOG.md)
- [Commits](https://github.com/TedDriggs/darling/compare/v0.20.0...v0.20.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-05 00:14:55 +00:00
dependabot[bot]
631af8a4f7 build: Bump anyhow from 1.0.70 to 1.0.71 in /fuzz
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.70 to 1.0.71.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.70...1.0.71)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-03 00:12:19 +00:00
Wei Liu
aa14fe214a pci: bump the number of supported PCI segments
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2023-05-02 09:34:05 +01:00
Wei Liu
45e3f49bba vmm: use MAX_NUM_PCI_SEGMENTS in test cases
No functional change.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2023-05-02 09:34:05 +01:00
Wei Liu
03db406645 tests: use a constant for maximum number of PCI segments
No functional change.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2023-05-02 09:34:05 +01:00
Wei Liu
ba1e89139a pci: aml: support up to 256 PCI segments
Originally the AML only accepted one hex number for PCI segment
numbering. Change it to accept two numbers. That makes it possible to
add up to 256 PCI segments.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2023-05-02 09:34:05 +01:00
Alyssa Ross
21d40d7489 main: reset tty if starting the VM fails
When I refactored this to centralise resetting the tty into
DeviceManager::drop, I tested that the tty was reset if an error
happened on the vmm thread, but not on the main thread.  It turns out
that if an error happened on the main thread, the process would just
exit, so drop handlers on other threads wouldn't get run.

To fix this, I've changed start_vmm() to write to the VMM's exit
eventfd and then join the thread if an error happens after the vmm
thread is started.

Fixes: b6feae0a ("vmm: only touch the tty flags if it's being used")
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-05-02 09:33:53 +01:00
Alyssa Ross
c90a0ffff6 vmm: reset to the original termios
Previously, we used two different functions for configuring ttys.
vmm_sys_util::terminal::Terminal::set_raw_mode() was used to configure
stdio ttys, and cfmakeraw() was used to configure ptys created by
cloud-hypervisor.  When I centralized the stdio tty cleanup, I also
switched to using cfmakeraw() everywhere, to avoid duplication.

cfmakeraw sets the OPOST flag, but when we later reset the ttys, we
used vmm_sys_util::terminal::Terminal::set_canon_mode(), which does
not unset this flag.  This meant that the terminal was getting mostly,
but not fully, reset.

To fix this without depending on the implementation of cfmakeraw(),
let's just store the original termios for stdio terminals, and restore
them to exactly the state we found them in when cloud-hypervisor exits.

Fixes: b6feae0a ("vmm: only touch the tty flags if it's being used")
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-05-02 09:33:53 +01:00
dependabot[bot]
dc7931f592 build: Bump uuid from 1.3.1 to 1.3.2 in /fuzz
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.3.1 to 1.3.2.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.3.1...1.3.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-02 00:15:30 +00:00
dependabot[bot]
841627f10a build: Bump serde_with from 2.3.2 to 2.3.3 in /fuzz
Bumps [serde_with](https://github.com/jonasbb/serde_with) from 2.3.2 to 2.3.3.
- [Release notes](https://github.com/jonasbb/serde_with/releases)
- [Commits](https://github.com/jonasbb/serde_with/compare/v2.3.2...v2.3.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-28 00:14:31 +00:00
dependabot[bot]
97fdb65012 build: Bump anyhow from 1.0.69 to 1.0.70
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.69 to 1.0.70.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.69...1.0.70)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-27 00:39:57 +00:00
Rob Bradford
d17d70fae1 vmm: Update for new acpi_tables version
In particular the Std::write() API requires that the value implements
AsBytes and copies the slice representation into the table data. This
avoids unaligned writes which can cause a panic with the updated
toolchain.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2023-04-26 23:25:57 +01:00
Rob Bradford
71d1296d09 vmm: Implemented zerocopy::AsBytes for SDT structures
For structures that are used in SDT ACPI tables it is necessary for them
to implement this trait for the newly safe Std::write() API.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2023-04-26 23:25:57 +01:00
dependabot[bot]
0d58e51a3a build: Bump rustc-demangle from 0.1.21 to 0.1.23
Bumps [rustc-demangle](https://github.com/alexcrichton/rustc-demangle) from 0.1.21 to 0.1.23.
- [Release notes](https://github.com/alexcrichton/rustc-demangle/releases)
- [Commits](https://github.com/alexcrichton/rustc-demangle/compare/0.1.21...0.1.23)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-26 00:38:49 +00:00
dependabot[bot]
97012c511d build: Bump serde_json from 1.0.95 to 1.0.96
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.95 to 1.0.96.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.95...v1.0.96)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-25 08:45:18 +00:00
dependabot[bot]
c10e2a79b1 build: Bump libc from 0.2.141 to 0.2.142 in /fuzz
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.141 to 0.2.142.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.141...0.2.142)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-25 00:22:46 +00:00
Ruslan Mstoi
f3870ae638 scripts: indent bash if statement
This commit indents unindented if statements in scripts

Signed-off-by: Ruslan Mstoi <ruslan.mstoi@intel.com>
2023-04-24 13:21:36 +01:00
Alyssa Ross
3c0b389c82 vmm: allow getdents64 in seccomp filter
This is used on older kernels where close_range() is not available.

Signed-off-by: Alyssa Ross <hi@alyssa.is>
Fixes: 505f4dfa ("vmm: close all unused fds in sigwinch listener")
2023-04-22 11:40:17 +01:00
dependabot[bot]
9881c94f84 build: Bump vfio_user from afbbd57 to e75c941
Bumps [vfio_user](https://github.com/rust-vmm/vfio-user) from `afbbd57` to `e75c941`.
- [Release notes](https://github.com/rust-vmm/vfio-user/releases)
- [Commits](afbbd57228...e75c9415d9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-22 10:14:39 +00:00
Rob Bradford
ceb8151747 hypervisor, vmm: Limit max number of vCPUs to hypervisor maximum
On KVM this is provided by an ioctl, on MSHV this is constant. Although
there is a HV_MAXIMUM_PROCESSORS constant the MSHV ioctl API is limited
to u8.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2023-04-22 10:35:39 +01:00
Rob Bradford
2554f2a8d4 qcow: use std::mem::size_of_val()
Rather than manually calculate the size of the slice in bytes. This
fixes a beta clippy issue.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2023-04-22 10:33:46 +01:00
Rob Bradford
036af673e6 virtio-devices: Avoid clashing names in imports
Don't import via glob to avoid (unused) objects colliding in the
namespace. This fixes a beta clippy issue.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2023-04-22 10:33:46 +01:00
Rob Bradford
990362e314 net_gen: Avoid clashing names in imports
Remove use of glob imports to fix an issue detected by clippy.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2023-04-22 10:33:46 +01:00
Rob Bradford
83d57d3cce build: Temporarily disable Windows guest tests
Windows guests tests are failing consistently.

See: #5400

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2023-04-22 09:49:07 +01:00
Rob Bradford
a0babfb9c1 tests: Remove "initial apicid" check from test_simple_launch
This fixes the following tests that have been consistently failing on
the CI:

[2023-04-22T07:00:53.760Z] failures:
[2023-04-22T07:00:53.760Z]     common_parallel::test_focal_hypervisor_fw
[2023-04-22T07:00:53.760Z]     common_parallel::test_focal_ovmf

I'm not sure of the origin of this check but it obviously dependent on
the underlying platform as the guest OS has not changed. Since it
depends on the host environment it doesn't make sense to assert for it.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2023-04-22 09:16:27 +01:00
dependabot[bot]
3394a59466 build: Bump serde_with from 2.3.1 to 2.3.2
Bumps [serde_with](https://github.com/jonasbb/serde_with) from 2.3.1 to 2.3.2.
- [Release notes](https://github.com/jonasbb/serde_with/releases)
- [Commits](https://github.com/jonasbb/serde_with/compare/v2.3.1...v2.3.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-20 00:37:26 +00:00
dependabot[bot]
f8e0376f6d build: Bump linux-raw-sys from 0.3.1 to 0.3.2
Bumps [linux-raw-sys](https://github.com/sunfishcode/linux-raw-sys) from 0.3.1 to 0.3.2.
- [Release notes](https://github.com/sunfishcode/linux-raw-sys/releases)
- [Commits](https://github.com/sunfishcode/linux-raw-sys/compare/v0.3.1...v0.3.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-19 01:14:24 +00:00
Rafael Mendonca
6379074264 misc: Remove unnecessary clippy directives
Clippy passes fine without these.

Signed-off-by: Rafael Mendonca <rafaelmendsr@gmail.com>
2023-04-18 10:48:31 -07:00
dependabot[bot]
5dbc72948f build: Bump is-terminal from 0.4.5 to 0.4.7
Bumps [is-terminal](https://github.com/sunfishcode/is-terminal) from 0.4.5 to 0.4.7.
- [Release notes](https://github.com/sunfishcode/is-terminal/releases)
- [Commits](https://github.com/sunfishcode/is-terminal/compare/v0.4.5...v0.4.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-18 05:22:06 +00:00
Bo Chen
04d111ee15 tests: Extend '_test_macvtap()' with reboot
In this way, we can cover the scenario where a VM with hotplugged net
device using FDs can work properly with reboot.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-04-17 16:33:29 +01:00
Bo Chen
a9623c7a28 vmm: Add valid FDs for TAP devices to 'VmConfig::preserved_fds'
In this way, valid FDs for TAP devices will be closed when the holding
VmConfig instance is destroyed.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-04-17 16:33:29 +01:00
Bo Chen
4baf85857a vmm: Add unit test for 'VmConfig::preserved_fds'
Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-04-17 16:33:29 +01:00
Bo Chen
e3d2917d5f vmm: Implement Clone and Drop for VmConfig
The custom 'clone' duplicates 'preserved_fds' so that the validation
logic can be safely carried out on the clone of the VmConfig.

The custom 'drop' ensures 'preserved_fds' are safely closed when the
holding VmConfig instance is destroyed.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-04-17 16:33:29 +01:00
Bo Chen
a84b540b65 vmm: config: Extend 'VmConfig' with 'preserved_fds'
Preserved FDs are the ones that share the same life-time as its holding
VmConfig instance, such as FDs for creating TAP devices.

Preserved FDs will stay open as long as the holding VmConfig instance is
valid, and will be closed when the holding VmConfig instance is destroyed.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-04-17 16:33:29 +01:00
Bo Chen
8eb162e3d7 Revert "vmm: config: Implement Clone for NetConfig"
This reverts commit ea4a95c4f6.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-04-17 16:33:29 +01:00
Bo Chen
2804608a1c Revert "vmm: config: Close FDs for TAP devices that are provided to VM"
This reverts commit b14427540b.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-04-17 16:33:29 +01:00
Bo Chen
e0125653b1 Revert "vmm: config: Don't close reserved FDs from NetConfig::drop()"
This reverts commit 0110fb4edc.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-04-17 16:33:29 +01:00
Bo Chen
e431a48201 Revert "vmm: config: Avoid closing invalid FDs from 'test_net_parsing()'"
This reverts commit 0567def931.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-04-17 16:33:29 +01:00
Bo Chen
c143cb3af0 Revert "vmm: config: Replace use of memfd_create with fd pointing to /dev/null"
This reverts commit 46066d6ae1.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-04-17 16:33:29 +01:00
Alyssa Ross
b6feae0ace vmm: only touch the tty flags if it's being used
When neither serial nor console are connected to the tty,
cloud-hypervisor shouldn't touch the tty at all.  One way in which
this is annoying is that if I am running cloud-hypervisor without it
using my terminal, I expect to be able to suspend it with ^Z like any
other process, but that doesn't work if it's put the terminal into raw
mode.

Instead of putting the tty into raw mode when a VM is created or
restored, do it when a serial or console device is created.  Since we
now know it can't be put into raw mode until the Vm object is created,
we can move setting it back to canon mode into the drop handler for
that object, which should always be run in normal operation.  We still
also put the tty into canon mode in the SIGTERM / SIGINT handler, but
check whether the tty was actually used, rather than whether stdin is
a tty.  This requires passing on_tty around as an atomic boolean.

I explored more of an abstraction over the tty — having an object that
encapsulated stdout and put the tty into raw mode when initialized and
into canon mode when dropped — but it wasn't practical, mostly due to
the special requirements of the signal handler.  I also investigated
whether the SIGWINCH listener process could be used here, which I
think would have worked but I'm hesitant to involve it in serial
handling as well as conosle handling.

There's no longer a check for whether the file descriptor is a tty
before setting it into canon mode — it's redundant, because if it's
not a tty it just won't respond to the ioctl.

Tested by shutting down through the API, SIGTERM, and an error
injected after setting raw mode.

Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-04-17 16:33:17 +01:00
Alyssa Ross
520aff2efc vmm: don't redundantly set the TTY to canon mode
If the VM is shut down, either it's going to be started again, in
which case we still want to be in raw mode, or the process is about to
exit, in which case canon mode will be set at the end of main.

Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-04-17 16:33:17 +01:00
dependabot[bot]
41880919a0 build: Bump io-lifetimes from 1.0.4 to 1.0.10
Bumps [io-lifetimes](https://github.com/sunfishcode/io-lifetimes) from 1.0.4 to 1.0.10.
- [Release notes](https://github.com/sunfishcode/io-lifetimes/releases)
- [Commits](https://github.com/sunfishcode/io-lifetimes/compare/1.0.4...v1.0.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-15 00:37:15 +00:00
dependabot[bot]
865291d840 build: Bump rustix from 0.36.8 to 0.36.12
Bumps [rustix](https://github.com/bytecodealliance/rustix) from 0.36.8 to 0.36.12.
- [Release notes](https://github.com/bytecodealliance/rustix/releases)
- [Commits](https://github.com/bytecodealliance/rustix/compare/v0.36.8...v0.36.12)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-14 21:34:46 +00:00
Omer Faruk Bayram
346ee09e6b vmm: api: include BUILD_VERSION and CH pid in VmmPingResponse
Signed-off-by: Omer Faruk Bayram <omer.faruk@sartura.hr>
2023-04-14 12:13:46 -07:00
Omer Faruk Bayram
59012ccc6e build: rename BUILT_VERSION to BUILD_VERSION
Signed-off-by: Omer Faruk Bayram <omer.faruk@sartura.hr>
2023-04-14 12:13:46 -07:00
Alyssa Ross
3f17f4657b virtio-devices: remove incorrect "blk" references
This code is shared between all vhost-user devices, not just
vhost-user-blk.

Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-04-13 01:27:56 -07:00
Alyssa Ross
086ca9b935 virtio-devices: fix formatting of error message
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-04-12 18:55:18 -07:00
Alyssa Ross
9b724303ac vmm: only use KVM_ARM_VCPU_PMU_V3 if available
Having PMU in guests isn't critical, and not all hardware supports
it (e.g. Apple Silicon).

CpuManager::init_pmu already has a fallback for if PMU is not
supported by the VCPU, but we weren't getting that far, because we
would always try to initialise the VCPU with KVM_ARM_VCPU_PMU_V3, and
then bail when it returned with EINVAL.

Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-04-13 09:02:55 +08:00
dependabot[bot]
948573db0d build: Bump dirs from 4.0.0 to 5.0.0
Bumps [dirs](https://github.com/soc/dirs-rs) from 4.0.0 to 5.0.0.
- [Release notes](https://github.com/soc/dirs-rs/releases)
- [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>
2023-04-13 00:37:35 +00:00
dependabot[bot]
0352846af2 build: Bump serde_json from 1.0.95 to 1.0.96 in /fuzz
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.95 to 1.0.96.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.95...v1.0.96)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-13 00:18:59 +00:00
Alyssa Ross
8b3f0043b0 virtio-devices: seccomp: add vhost-user syscalls
Cloud Hypervisor's vhost-user implementation will reconnect if it gets
disconnected from the backend.  That means connections happen inside
the vhost-user seccomp sandbox, so all syscalls used in reconnecting
have to be allowed in that sandbox.

clock_nanosleep is used by Glibc, and nanosleep is used by musl.

Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-04-12 09:27:07 -07:00
dependabot[bot]
9d532acab6 build: Bump serde from 1.0.159 to 1.0.160 in /fuzz
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.159 to 1.0.160.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.159...v1.0.160)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-12 00:11:33 +00:00
Bo Chen
df2a7c1764 vmm: Ignore and warn TAP FDs sent via the HTTP request body
Valid FDs can only be sent from another process via `SCM_RIGHTS`.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-04-11 09:34:14 -07:00
dependabot[bot]
3f5dac988f build: Bump uuid from 1.3.0 to 1.3.1 in /fuzz
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.3.0...1.3.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-11 00:15:36 +00:00
Muminul Islam
3096f1d42f hypervisor: Fix few register names on MSHV
Recently generated mshv-bindings has most of the registers
renamed. This patch renames some of the MSHV registers.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2023-04-10 14:28:41 -07:00
Muminul Islam
42a4e593ce build: update to latest mshv-{bindings, ioctls)
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2023-04-10 14:28:41 -07:00
smit-gardhariya
b56da6ee24 Export RUST_BACKTRACE when it is not set explicitly
Currently we are overwriting the RUST_BACKTRACE if set
explicitly by user while running performance metrics tests
using dev_cli.sh

This change will allow user to set the RUST_BACKTRACE
while running performance metrics tests with dev_cli.sh
which invokes run_metrics.sh to run the performance binary.
We will set RUST_BACKTRACE to 1 if not set explicitly.

Signed-off-by: smit-gardhariya <gardhariya.smit@gmail.com>
2023-04-10 14:08:21 -07:00
dependabot[bot]
90dcbc3232 build: Bump getrandom from 0.2.8 to 0.2.9 in /fuzz
Bumps [getrandom](https://github.com/rust-random/getrandom) from 0.2.8 to 0.2.9.
- [Release notes](https://github.com/rust-random/getrandom/releases)
- [Changelog](https://github.com/rust-random/getrandom/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/getrandom/compare/v0.2.8...v0.2.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-07 00:14:24 +00:00
103 changed files with 4450 additions and 1715 deletions

View File

@@ -13,7 +13,7 @@ jobs:
- stable - stable
- beta - beta
- nightly - nightly
- "1.62" - "1.64"
target: target:
- x86_64-unknown-linux-gnu - x86_64-unknown-linux-gnu
- x86_64-unknown-linux-musl - x86_64-unknown-linux-musl
@@ -42,6 +42,9 @@ jobs:
- name: Build (default features + tdx) - name: Build (default features + tdx)
run: cargo rustc --locked --bin cloud-hypervisor --features "tdx" -- -D warnings -D clippy::undocumented_unsafe_blocks run: cargo rustc --locked --bin cloud-hypervisor --features "tdx" -- -D warnings -D clippy::undocumented_unsafe_blocks
- name: Build (default features + dbus_api)
run: cargo rustc --locked --bin cloud-hypervisor --features "dbus_api" -- -D warnings -D clippy::undocumented_unsafe_blocks
- name: Build (default features + guest_debug) - name: Build (default features + guest_debug)
run: cargo rustc --locked --bin cloud-hypervisor --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks run: cargo rustc --locked --bin cloud-hypervisor --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks

26
.github/workflows/hadolint.yaml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: Lint Dockerfile
on:
push:
paths:
- resources/Dockerfile
pull_request:
paths:
- resources/Dockerfile
jobs:
hadolint:
name: Run Hadolint Dockerfile Linter
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Lint Dockerfile
uses: hadolint/hadolint-action@master
with:
dockerfile: ./resources/Dockerfile
format: tty
no-fail: false
verbose: true
failure-threshold: info

1192
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "cloud-hypervisor" name = "cloud-hypervisor"
version = "31.0.0" version = "33.0.0"
authors = ["The Cloud Hypervisor Authors"] authors = ["The Cloud Hypervisor Authors"]
edition = "2021" edition = "2021"
default-run = "cloud-hypervisor" default-run = "cloud-hypervisor"
@@ -15,7 +15,7 @@ homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
# a.) A dependency requires it, # a.) A dependency requires it,
# b.) If we want to use a new feature and that MSRV is at least 6 months old, # 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. # c.) There is a security issue that is addressed by the toolchain update.
rust-version = "1.62" rust-version = "1.64"
[profile.release] [profile.release]
lto = true lto = true
@@ -29,7 +29,7 @@ strip = false
debug = true debug = true
[dependencies] [dependencies]
anyhow = "1.0.69" anyhow = "1.0.71"
api_client = { path = "api_client" } api_client = { path = "api_client" }
argh = "0.1.9" argh = "0.1.9"
dhat = { version = "0.3.2", optional = true } dhat = { version = "0.3.2", optional = true }
@@ -40,30 +40,34 @@ libc = "0.2.139"
log = { version = "0.4.17", features = ["std"] } log = { version = "0.4.17", features = ["std"] }
option_parser = { path = "option_parser" } option_parser = { path = "option_parser" }
seccompiler = "0.3.0" seccompiler = "0.3.0"
serde_json = "1.0.95" serde_json = "1.0.96"
signal-hook = "0.3.15" signal-hook = "0.3.15"
thiserror = "1.0.39" thiserror = "1.0.40"
tpm = { path = "tpm"} tpm = { path = "tpm"}
tracer = { path = "tracer" } tracer = { path = "tracer" }
vmm = { path = "vmm" } vmm = { path = "vmm" }
vmm-sys-util = "0.11.0" vmm-sys-util = "0.11.0"
vm-memory = "0.10.0" vm-memory = "0.11.0"
zbus = { version = "3.11.1", optional = true }
# List of patched crates # List of patched crates
[patch.crates-io] [patch.crates-io]
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx" } kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx" }
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "main" }
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" } versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
vhost = { git = "https://github.com/rust-vmm/vhost", branch = "main" }
[dev-dependencies] [dev-dependencies]
dirs = "4.0.0" dirs = "5.0.0"
net_util = { path = "net_util" } net_util = { path = "net_util" }
once_cell = "1.17.1" once_cell = "1.17.1"
serde_json = "1.0.95" serde_json = "1.0.96"
test_infra = { path = "test_infra" } test_infra = { path = "test_infra" }
wait-timeout = "0.2.0" wait-timeout = "0.2.0"
[features] [features]
default = ["kvm"] default = ["kvm"]
dbus_api = ["zbus", "vmm/dbus_api"]
dhat-heap = ["dhat"] # For heap profiling dhat-heap = ["dhat"] # For heap profiling
guest_debug = ["vmm/guest_debug"] guest_debug = ["vmm/guest_debug"]
kvm = ["vmm/kvm"] kvm = ["vmm/kvm"]

View File

@@ -140,7 +140,10 @@ The Ubuntu cloud images do not ship with a default password so it necessary to
use a `cloud-init` disk image to customise the image on the first boot. A basic use a `cloud-init` disk image to customise the image on the first boot. A basic
`cloud-init` image is generated by this [script](scripts/create-cloud-init.sh). `cloud-init` image is generated by this [script](scripts/create-cloud-init.sh).
This seeds the image with a default username/password of `cloud/cloud123`. It This seeds the image with a default username/password of `cloud/cloud123`. It
is only necessary to add this disk image on the first boot. is only necessary to add this disk image on the first boot. Script also assigns
default IP address using `test_data/cloud-init/ubuntu/local/network-config` details
with `--net "mac=12:34:56:78:90:ab,tap="` option. Then the matching mac address
interface will be enabled as per `network-config` details.
```shell ```shell
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor $ sudo setcap cap_net_admin+ep ./cloud-hypervisor

View File

@@ -9,18 +9,18 @@ default = []
tdx = [] tdx = []
[dependencies] [dependencies]
anyhow = "1.0.69" anyhow = "1.0.71"
byteorder = "1.4.3" byteorder = "1.4.3"
hypervisor = { path = "../hypervisor" } hypervisor = { path = "../hypervisor" }
libc = "0.2.139" libc = "0.2.139"
linux-loader = { version = "0.8.1", features = ["elf", "bzimage", "pe"] } linux-loader = { version = "0.9.0", features = ["elf", "bzimage", "pe"] }
log = "0.4.17" log = "0.4.17"
serde = { version = "1.0.151", features = ["rc", "derive"] } serde = { version = "1.0.164", features = ["rc", "derive"] }
thiserror = "1.0.39" thiserror = "1.0.40"
uuid = "1.3.0" uuid = "1.3.4"
versionize = "0.1.10" versionize = "0.1.10"
versionize_derive = "0.1.4" versionize_derive = "0.1.4"
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-bitmap"] } vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-bitmap"] }
vm-migration = { path = "../vm-migration" } vm-migration = { path = "../vm-migration" }
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] } vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }

View File

@@ -24,6 +24,8 @@ use super::layout::{
IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_PCI_IO_SIZE, MEM_PCI_IO_START, 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, PCI_HIGH_BASE, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
}; };
use std::fs;
use std::path::Path;
use vm_fdt::{FdtWriter, FdtWriterResult}; use vm_fdt::{FdtWriter, FdtWriterResult};
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryRegion}; use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryRegion};
@@ -40,8 +42,12 @@ const VIRTIO_IOMMU_PHANDLE: u32 = 5;
// NOTE: Keep FIRST_VCPU_PHANDLE the last PHANDLE defined. // NOTE: Keep FIRST_VCPU_PHANDLE the last PHANDLE defined.
// This is a value for uniquely identifying the FDT node containing the first vCPU. // This is a value for uniquely identifying the FDT node containing the first vCPU.
// The last number of vCPU phandle depends on the number of vCPUs. // The last number of vCPU phandle depends on the number of vCPUs.
const FIRST_VCPU_PHANDLE: u32 = 6; const FIRST_VCPU_PHANDLE: u32 = 8;
// This is a value for uniquely identifying the FDT node containing the L2 cache info
const L2_CACHE_PHANDLE: u32 = 6;
// This is a value for uniquely identifying the FDT node containing the L3 cache info
const L3_CACHE_PHANDLE: u32 = 7;
// Read the documentation specified when appending the root node to the FDT. // Read the documentation specified when appending the root node to the FDT.
const ADDRESS_CELLS: u32 = 0x2; const ADDRESS_CELLS: u32 = 0x2;
const SIZE_CELLS: u32 = 0x2; const SIZE_CELLS: u32 = 0x2;
@@ -81,6 +87,97 @@ pub enum Error {
} }
type Result<T> = result::Result<T, Error>; type Result<T> = result::Result<T, Error>;
pub enum CacheLevel {
/// L1 data cache
L1D = 0,
/// L1 instruction cache
L1I = 1,
/// L2 cache
L2 = 2,
/// L3 cache
L3 = 3,
}
/// NOTE: CACHE SIZE file directory example,
/// "/sys/devices/system/cpu/cpu0/cache/index0/size".
pub fn get_cache_size(cache_level: CacheLevel) -> u32 {
let mut file_directory: String = "/sys/devices/system/cpu/cpu0/cache".to_string();
match cache_level {
CacheLevel::L1D => file_directory += "/index0/size",
CacheLevel::L1I => file_directory += "/index1/size",
CacheLevel::L2 => file_directory += "/index2/size",
CacheLevel::L3 => file_directory += "/index3/size",
}
let file_path = Path::new(&file_directory);
if !file_path.exists() {
error!("File: {} not exist.", file_directory);
0
} else {
info!("File: {} exist.", file_directory);
let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted.");
// The content of the file is as simple as a size, like: "32K"
let src = src.trim();
let src_digits: u32 = src[0..src.len() - 1].parse().unwrap();
let src_unit = &src[src.len() - 1..];
src_digits
* match src_unit {
"K" => 1024,
"M" => 1024u32.pow(2),
"G" => 1024u32.pow(3),
_ => 1,
}
}
}
/// NOTE: CACHE COHERENCY LINE SIZE file directory example,
/// "/sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size".
pub fn get_cache_coherency_line_size(cache_level: CacheLevel) -> u32 {
let mut file_directory: String = "/sys/devices/system/cpu/cpu0/cache".to_string();
match cache_level {
CacheLevel::L1D => file_directory += "/index0/coherency_line_size",
CacheLevel::L1I => file_directory += "/index1/coherency_line_size",
CacheLevel::L2 => file_directory += "/index2/coherency_line_size",
CacheLevel::L3 => file_directory += "/index3/coherency_line_size",
}
let file_path = Path::new(&file_directory);
if !file_path.exists() {
error!("File: {} not exist.", file_directory);
0
} else {
info!("File: {} exist.", file_directory);
let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted.");
src.trim().parse::<u32>().unwrap()
}
}
/// NOTE: CACHE NUMBER OF SETS file directory example,
/// "/sys/devices/system/cpu/cpu0/cache/index0/number_of_sets".
pub fn get_cache_number_of_sets(cache_level: CacheLevel) -> u32 {
let mut file_directory: String = "/sys/devices/system/cpu/cpu0/cache".to_string();
match cache_level {
CacheLevel::L1D => file_directory += "/index0/number_of_sets",
CacheLevel::L1I => file_directory += "/index1/number_of_sets",
CacheLevel::L2 => file_directory += "/index2/number_of_sets",
CacheLevel::L3 => file_directory += "/index3/number_of_sets",
}
let file_path = Path::new(&file_directory);
if !file_path.exists() {
error!("File: {} not exist.", file_directory);
0
} else {
info!("File: {} exist.", file_directory);
let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted.");
src.trim().parse::<u32>().unwrap()
}
}
/// Creates the flattened device tree for this aarch64 VM. /// Creates the flattened device tree for this aarch64 VM.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>( pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
@@ -159,6 +256,54 @@ fn create_cpu_nodes(
let num_cpus = vcpu_mpidr.len(); let num_cpus = vcpu_mpidr.len();
// Add cache info.
// L1 Data Cache Info.
let mut l1_d_cache_size: u32 = 0;
let mut l1_d_cache_line_size: u32 = 0;
let mut l1_d_cache_sets: u32 = 0;
// L1 Instruction Cache Info.
let mut l1_i_cache_size: u32 = 0;
let mut l1_i_cache_line_size: u32 = 0;
let mut l1_i_cache_sets: u32 = 0;
// L2 Cache Info.
let mut l2_cache_size: u32 = 0;
let mut l2_cache_line_size: u32 = 0;
let mut l2_cache_sets: u32 = 0;
// L3 Cache Info.
let mut l3_cache_size: u32 = 0;
let mut l3_cache_line_size: u32 = 0;
let mut l3_cache_sets: u32 = 0;
let cache_path = Path::new("/sys/devices/system/cpu/cpu0/cache");
let cache_exist: bool = cache_path.exists();
if !cache_exist {
error!("cache sysfs system does not exist.");
} else {
info!("cache sysfs system exists.");
// L1 Data Cache Info.
l1_d_cache_size = get_cache_size(CacheLevel::L1D);
l1_d_cache_line_size = get_cache_coherency_line_size(CacheLevel::L1D);
l1_d_cache_sets = get_cache_number_of_sets(CacheLevel::L1D);
// L1 Instruction Cache Info.
l1_i_cache_size = get_cache_size(CacheLevel::L1I);
l1_i_cache_line_size = get_cache_coherency_line_size(CacheLevel::L1I);
l1_i_cache_sets = get_cache_number_of_sets(CacheLevel::L1I);
// L2 Cache Info.
l2_cache_size = get_cache_size(CacheLevel::L2);
l2_cache_line_size = get_cache_coherency_line_size(CacheLevel::L2);
l2_cache_sets = get_cache_number_of_sets(CacheLevel::L2);
// L3 Cache Info.
l3_cache_size = get_cache_size(CacheLevel::L3);
l3_cache_line_size = get_cache_coherency_line_size(CacheLevel::L3);
l3_cache_sets = get_cache_number_of_sets(CacheLevel::L3);
}
for (cpu_id, mpidr) in vcpu_mpidr.iter().enumerate().take(num_cpus) { for (cpu_id, mpidr) in vcpu_mpidr.iter().enumerate().take(num_cpus) {
let cpu_name = format!("cpu@{cpu_id:x}"); let cpu_name = format!("cpu@{cpu_id:x}");
let cpu_node = fdt.begin_node(&cpu_name)?; let cpu_node = fdt.begin_node(&cpu_name)?;
@@ -173,6 +318,21 @@ fn create_cpu_nodes(
fdt.property_u32("reg", (mpidr & 0x7FFFFF) as u32)?; fdt.property_u32("reg", (mpidr & 0x7FFFFF) as u32)?;
fdt.property_u32("phandle", cpu_id as u32 + FIRST_VCPU_PHANDLE)?; fdt.property_u32("phandle", cpu_id as u32 + FIRST_VCPU_PHANDLE)?;
if cache_exist && l1_d_cache_size != 0 && l1_i_cache_size != 0 {
// Add cache info.
fdt.property_u32("d-cache-size", l1_d_cache_size)?;
fdt.property_u32("d-cache-line-size", l1_d_cache_line_size)?;
fdt.property_u32("d-cache-sets", l1_d_cache_sets)?;
fdt.property_u32("i-cache-size", l1_i_cache_size)?;
fdt.property_u32("i-cache-line-size", l1_i_cache_line_size)?;
fdt.property_u32("i-cache-sets", l1_i_cache_sets)?;
if l2_cache_size != 0 {
fdt.property_u32("next-level-cache", L2_CACHE_PHANDLE)?;
}
}
// Add `numa-node-id` property if there is any numa config. // Add `numa-node-id` property if there is any numa config.
if numa_nodes.len() > 1 { if numa_nodes.len() > 1 {
for numa_node_idx in 0..numa_nodes.len() { for numa_node_idx in 0..numa_nodes.len() {
@@ -186,6 +346,36 @@ fn create_cpu_nodes(
fdt.end_node(cpu_node)?; fdt.end_node(cpu_node)?;
} }
if cache_exist && l2_cache_size != 0 {
let l2_cache_name = "l2-cache0";
let l2_cache_node = fdt.begin_node(l2_cache_name)?;
fdt.property_u32("phandle", L2_CACHE_PHANDLE)?;
fdt.property_string("compatible", "cache")?;
fdt.property_u32("cache-size", l2_cache_size)?;
fdt.property_u32("cache-line-size", l2_cache_line_size)?;
fdt.property_u32("cache-sets", l2_cache_sets)?;
fdt.property_u32("cache-level", 2)?;
if l3_cache_size != 0 {
fdt.property_u32("next-level-cache", L3_CACHE_PHANDLE)?;
}
fdt.end_node(l2_cache_node)?;
}
if cache_exist && l3_cache_size != 0 {
let l3_cache_name = "l3-cache0";
let l3_cache_node = fdt.begin_node(l3_cache_name)?;
fdt.property_u32("phandle", L3_CACHE_PHANDLE)?;
fdt.property_string("compatible", "cache")?;
fdt.property_null("cache-unified")?;
fdt.property_u32("cache-size", l3_cache_size)?;
fdt.property_u32("cache-line-size", l3_cache_line_size)?;
fdt.property_u32("cache-sets", l3_cache_sets)?;
fdt.property_u32("cache-level", 3)?;
fdt.end_node(l3_cache_node)?;
}
if let Some(topology) = vcpu_topology { if let Some(topology) = vcpu_topology {
let (threads_per_core, cores_per_package, packages) = topology; let (threads_per_core, cores_per_package, packages) = topology;
let cpu_map_node = fdt.begin_node("cpu-map")?; let cpu_map_node = fdt.begin_node("cpu-map")?;
@@ -260,32 +450,86 @@ fn create_memory_node(
fdt.end_node(memory_node)?; fdt.end_node(memory_node)?;
} }
} else { } else {
let last_addr = guest_mem.last_addr().raw_value(); // Note: memory regions from "GuestMemory" are sorted and non-zero sized.
if last_addr < super::layout::MEM_32BIT_RESERVED_START.raw_value() { let ram_regions = {
// Case 1: all RAM is under the hole let mut ram_regions = Vec::new();
let mem_size = last_addr - super::layout::RAM_START.raw_value() + 1; let mut current_start = guest_mem
let mem_reg_prop = [super::layout::RAM_START.raw_value(), mem_size]; .iter()
let memory_node = fdt.begin_node("memory")?; .next()
fdt.property_string("device_type", "memory")?; .map(GuestMemoryRegion::start_addr)
fdt.property_array_u64("reg", &mem_reg_prop)?; .expect("GuestMemory must have one memory region at least")
fdt.end_node(memory_node)?; .raw_value();
} else { let mut current_end = current_start;
// Case 2: RAM is split by the hole
// Region 1: RAM before the hole for (start, size) in guest_mem
let mem_size = super::layout::MEM_32BIT_RESERVED_START.raw_value() .iter()
- super::layout::RAM_START.raw_value(); .map(|m| (m.start_addr().raw_value(), m.len()))
let mem_reg_prop = [super::layout::RAM_START.raw_value(), mem_size]; {
let memory_node_name = format!("memory@{:x}", super::layout::RAM_START.raw_value()); 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
};
if ram_regions.len() > 2 {
panic!(
"There should be up to two non-continuous regions, devidided by the
gap at the end of 32bit address space."
);
}
// Create the memory node for memory region before the gap
{
let (first_region_start, first_region_end) = ram_regions
.first()
.expect("There should be at last one memory region");
let ram_start = super::layout::RAM_START.raw_value();
let mem_32bit_reserved_start = super::layout::MEM_32BIT_RESERVED_START.raw_value();
if !((first_region_start <= &ram_start)
&& (first_region_end > &ram_start)
&& (first_region_end <= &mem_32bit_reserved_start))
{
panic!(
"Unexpected first memory region layout: (start: 0x{:08x}, end: 0x{:08x}).
ram_start: 0x{:08x}, mem_32bit_reserved_start: 0x{:08x}",
first_region_start, first_region_end, ram_start, mem_32bit_reserved_start
);
}
let mem_size = first_region_end - ram_start;
let mem_reg_prop = [ram_start, mem_size];
let memory_node_name = format!("memory@{:x}", ram_start);
let memory_node = fdt.begin_node(&memory_node_name)?; let memory_node = fdt.begin_node(&memory_node_name)?;
fdt.property_string("device_type", "memory")?; fdt.property_string("device_type", "memory")?;
fdt.property_array_u64("reg", &mem_reg_prop)?; fdt.property_array_u64("reg", &mem_reg_prop)?;
fdt.end_node(memory_node)?; fdt.end_node(memory_node)?;
}
// Region 2: RAM after the hole // Create the memory map entry for memory region after the gap if any
let mem_size = last_addr - super::layout::RAM_64BIT_START.raw_value() + 1; if let Some((second_region_start, second_region_end)) = ram_regions.get(1) {
let mem_reg_prop = [super::layout::RAM_64BIT_START.raw_value(), mem_size]; let ram_64bit_start = super::layout::RAM_64BIT_START.raw_value();
let memory_node_name =
format!("memory@{:x}", super::layout::RAM_64BIT_START.raw_value()); if second_region_start != &ram_64bit_start {
panic!(
"Unexpected second memory region layout: start: 0x{:08x}, ram_64bit_start: 0x{:08x}",
second_region_start, ram_64bit_start
);
}
let mem_size = second_region_end - ram_64bit_start;
let mem_reg_prop = [ram_64bit_start, mem_size];
let memory_node_name = format!("memory@{:x}", ram_64bit_start);
let memory_node = fdt.begin_node(&memory_node_name)?; let memory_node = fdt.begin_node(&memory_node_name)?;
fdt.property_string("device_type", "memory")?; fdt.property_string("device_type", "memory")?;
fdt.property_array_u64("reg", &mem_reg_prop)?; fdt.property_array_u64("reg", &mem_reg_prop)?;

View File

@@ -19,7 +19,7 @@ use std::collections::HashMap;
use std::convert::TryInto; use std::convert::TryInto;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic, GuestUsize}; use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic};
pub const _NSIG: i32 = 65; pub const _NSIG: i32 = 65;
@@ -83,8 +83,8 @@ pub fn configure_vcpu(
Ok(mpidr) Ok(mpidr)
} }
pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> { pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> {
let mut regions = vec![ vec![
// 0 MiB ~ 256 MiB: UEFI, GIC and legacy devices // 0 MiB ~ 256 MiB: UEFI, GIC and legacy devices
( (
GuestAddress(0), GuestAddress(0),
@@ -103,39 +103,21 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
layout::PCI_MMCONFIG_SIZE as usize, layout::PCI_MMCONFIG_SIZE as usize,
RegionType::Reserved, RegionType::Reserved,
), ),
]; // 1GiB ~ 4032 MiB: RAM before the gap
(
let ram_32bit_space_size =
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START);
// RAM space
// Case1: guest memory fits before the gap
if size <= ram_32bit_space_size {
regions.push((layout::RAM_START, size as usize, RegionType::Ram));
// Case2: guest memory extends beyond the gap
} else {
// Push memory before the gap
regions.push((
layout::RAM_START, layout::RAM_START,
ram_32bit_space_size as usize, layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START) as usize,
RegionType::Ram, RegionType::Ram,
)); ),
// Other memory is placed after 4GiB // 4GiB ~ inf: RAM after the gap
regions.push(( (layout::RAM_64BIT_START, usize::MAX, RegionType::Ram),
layout::RAM_64BIT_START, // Add the 32-bit reserved memory hole as a reserved region
(size - ram_32bit_space_size) as usize, (
RegionType::Ram, layout::MEM_32BIT_RESERVED_START,
)); layout::MEM_32BIT_RESERVED_SIZE as usize,
} RegionType::Reserved,
),
// Add the 32-bit reserved memory hole as a reserved region ]
regions.push((
layout::MEM_32BIT_RESERVED_START,
layout::MEM_32BIT_RESERVED_SIZE as usize,
RegionType::Reserved,
));
regions
} }
/// Configures the system and should be called once per vm before starting vcpu threads. /// Configures the system and should be called once per vm before starting vcpu threads.
@@ -198,11 +180,8 @@ pub fn initramfs_load_addr(
} }
} }
pub fn get_host_cpu_phys_bits() -> u8 { pub fn get_host_cpu_phys_bits(hypervisor: &Arc<dyn hypervisor::Hypervisor>) -> u8 {
// A dummy hypervisor created only for querying the host IPA size and will let host_cpu_phys_bits = hypervisor.get_host_ipa_limit().try_into().unwrap();
// be freed after the query.
let hv = hypervisor::new().unwrap();
let host_cpu_phys_bits = hv.get_host_ipa_limit().try_into().unwrap();
if host_cpu_phys_bits == 0 { if host_cpu_phys_bits == 0 {
// Host kernel does not support `get_host_ipa_limit`, // Host kernel does not support `get_host_ipa_limit`,
// we return the default value 40 here. // we return the default value 40 here.
@@ -217,26 +196,12 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn test_arch_memory_regions_dram_2gb() { fn test_arch_memory_regions_dram() {
let regions = arch_memory_regions((1usize << 31) as u64); //2GB let regions = arch_memory_regions();
assert_eq!(5, regions.len());
assert_eq!(layout::RAM_START, regions[3].0);
assert_eq!((1usize << 31), regions[3].1);
assert_eq!(RegionType::Ram, regions[3].2);
assert_eq!(RegionType::Reserved, regions[4].2);
}
#[test]
fn test_arch_memory_regions_dram_4gb() {
let regions = arch_memory_regions((1usize << 32) as u64); //4GB
let ram_32bit_space_size =
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START) as usize;
assert_eq!(6, regions.len()); assert_eq!(6, regions.len());
assert_eq!(layout::RAM_START, regions[3].0); assert_eq!(layout::RAM_START, regions[3].0);
assert_eq!(ram_32bit_space_size, regions[3].1);
assert_eq!(RegionType::Ram, regions[3].2); assert_eq!(RegionType::Ram, regions[3].2);
assert_eq!(RegionType::Reserved, regions[5].2); assert_eq!(RegionType::Reserved, regions[5].2);
assert_eq!(RegionType::Ram, regions[4].2); assert_eq!(RegionType::Ram, regions[4].2);
assert_eq!(((1usize << 32) - ram_32bit_space_size), regions[4].1);
} }
} }

View File

@@ -16,7 +16,7 @@ use crate::GuestMemoryMmap;
use crate::InitramfsConfig; use crate::InitramfsConfig;
use crate::RegionType; use crate::RegionType;
use hypervisor::arch::x86::{CpuIdEntry, CPUID_FLAG_VALID_INDEX}; use hypervisor::arch::x86::{CpuIdEntry, CPUID_FLAG_VALID_INDEX};
use hypervisor::{HypervisorCpuError, HypervisorError}; use hypervisor::{CpuVendor, HypervisorCpuError, HypervisorError};
use linux_loader::loader::bootparam::boot_params; use linux_loader::loader::bootparam::boot_params;
use linux_loader::loader::elf::start_info::{ use linux_loader::loader::elf::start_info::{
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info, hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
@@ -210,7 +210,6 @@ impl From<Error> for super::Error {
} }
} }
#[allow(clippy::upper_case_acronyms)]
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum CpuidReg { pub enum CpuidReg {
EAX, EAX,
@@ -776,6 +775,13 @@ pub fn configure_vcpu(
CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id)); CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id));
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1f, None, CpuidReg::EDX, u32::from(id)); CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1f, None, CpuidReg::EDX, u32::from(id));
// Set ApicId in cpuid for each vcpu
// SAFETY: get host cpuid when eax=1
let mut cpu_ebx = unsafe { core::arch::x86_64::__cpuid(1) }.ebx;
cpu_ebx &= 0xffffff;
cpu_ebx |= (id as u32) << 24;
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1, None, CpuidReg::EBX, cpu_ebx);
// The TSC frequency CPUID leaf should not be included when running with HyperV emulation // The TSC frequency CPUID leaf should not be included when running with HyperV emulation
if !kvm_hyperv { if !kvm_hyperv {
if let Some(tsc_khz) = vcpu.tsc_khz().map_err(Error::GetTscFrequency)? { if let Some(tsc_khz) = vcpu.tsc_khz().map_err(Error::GetTscFrequency)? {
@@ -828,47 +834,29 @@ pub fn configure_vcpu(
/// These should be used to configure the GuestMemory structure for the platform. /// These should be used to configure the GuestMemory structure for the platform.
/// For x86_64 all addresses are valid from the start of the kernel except a /// For x86_64 all addresses are valid from the start of the kernel except a
/// carve out at the end of 32bit address space. /// carve out at the end of 32bit address space.
pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> { pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> {
let reserved_memory_gap_start = layout::MEM_32BIT_RESERVED_START vec![
.checked_add(layout::MEM_32BIT_DEVICES_SIZE) // 0 GiB ~ 3GiB: memory before the gap
.expect("32-bit reserved region is too large"); (
let requested_memory_size = GuestAddress(size);
let mut regions = Vec::new();
// case1: guest memory fits before the gap
if size <= layout::MEM_32BIT_RESERVED_START.raw_value() {
regions.push((GuestAddress(0), size as usize, RegionType::Ram));
// case2: guest memory extends beyond the gap
} else {
// push memory before the gap
regions.push((
GuestAddress(0), GuestAddress(0),
layout::MEM_32BIT_RESERVED_START.raw_value() as usize, layout::MEM_32BIT_RESERVED_START.raw_value() as usize,
RegionType::Ram, RegionType::Ram,
)); ),
regions.push(( // 4 GiB ~ inf: memory after the gap
layout::RAM_64BIT_START, (layout::RAM_64BIT_START, usize::MAX, RegionType::Ram),
requested_memory_size.unchecked_offset_from(layout::MEM_32BIT_RESERVED_START) as usize, // 3 GiB ~ 3712 MiB: 32-bit device memory hole
RegionType::Ram, (
)); layout::MEM_32BIT_RESERVED_START,
} layout::MEM_32BIT_DEVICES_SIZE as usize,
RegionType::SubRegion,
// Add the 32-bit device memory hole as a sub region. ),
regions.push(( // 3712 MiB ~ 3968 MiB: 32-bit reserved memory hole
layout::MEM_32BIT_RESERVED_START, (
layout::MEM_32BIT_DEVICES_SIZE as usize, layout::MEM_32BIT_RESERVED_START.unchecked_add(layout::MEM_32BIT_DEVICES_SIZE),
RegionType::SubRegion, (layout::MEM_32BIT_RESERVED_SIZE - layout::MEM_32BIT_DEVICES_SIZE) as usize,
)); RegionType::Reserved,
),
// Add the 32-bit reserved memory hole as a sub region. ]
regions.push((
reserved_memory_gap_start,
(layout::MEM_32BIT_RESERVED_SIZE - layout::MEM_32BIT_DEVICES_SIZE) as usize,
RegionType::Reserved,
));
regions
} }
/// Configures the system and should be called once per vm before starting vcpu threads. /// Configures the system and should be called once per vm before starting vcpu threads.
@@ -966,30 +954,102 @@ fn configure_pvh(
// Create the memory map entries. // Create the memory map entries.
add_memmap_entry(&mut memmap, 0, layout::EBDA_START.raw_value(), E820_RAM); add_memmap_entry(&mut memmap, 0, layout::EBDA_START.raw_value(), E820_RAM);
let mem_end = guest_mem.last_addr(); // Merge continuous memory regions into one region.
// 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;
if mem_end < layout::MEM_32BIT_RESERVED_START { for (start, size) in guest_mem
add_memmap_entry( .iter()
&mut memmap, .map(|m| (m.start_addr().raw_value(), m.len()))
layout::HIGH_RAM_START.raw_value(), {
mem_end.unchecked_offset_from(layout::HIGH_RAM_START) + 1, if current_end == start {
E820_RAM, // This zone is continuous with the previous one.
); current_end += size;
} else { } else {
add_memmap_entry( ram_regions.push((current_start, current_end));
&mut memmap,
layout::HIGH_RAM_START.raw_value(), current_start = start;
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::HIGH_RAM_START), current_end = start + size;
E820_RAM, }
);
if mem_end > layout::RAM_64BIT_START {
add_memmap_entry(
&mut memmap,
layout::RAM_64BIT_START.raw_value(),
mem_end.unchecked_offset_from(layout::RAM_64BIT_START) + 1,
E820_RAM,
);
} }
ram_regions.push((current_start, current_end));
ram_regions
};
if ram_regions.len() > 2 {
error!(
"There should be up to two non-continuous regions, devidided by the
gap at the end of 32bit address space (e.g. between 3G and 4G)."
);
return Err(super::Error::MemmapTableSetup);
}
// Create the memory map entry for memory region before the gap
{
let (first_region_start, first_region_end) =
ram_regions.first().ok_or(super::Error::MemmapTableSetup)?;
let high_ram_start = layout::HIGH_RAM_START.raw_value();
let mem_32bit_reserved_start = layout::MEM_32BIT_RESERVED_START.raw_value();
if !((first_region_start <= &high_ram_start)
&& (first_region_end > &high_ram_start)
&& (first_region_end <= &mem_32bit_reserved_start))
{
error!(
"Unexpected first memory region layout: (start: 0x{:08x}, end: 0x{:08x}).
high_ram_start: 0x{:08x}, mem_32bit_reserved_start: 0x{:08x}",
first_region_start, first_region_end, high_ram_start, mem_32bit_reserved_start
);
return Err(super::Error::MemmapTableSetup);
}
info!(
"create_memmap_entry, start: 0x{:08x}, end: 0x{:08x})",
high_ram_start, first_region_end
);
add_memmap_entry(
&mut memmap,
high_ram_start,
first_region_end - high_ram_start,
E820_RAM,
);
}
// Create the memory map entry for memory region after the gap if any
if let Some((second_region_start, second_region_end)) = ram_regions.get(1) {
let ram_64bit_start = layout::RAM_64BIT_START.raw_value();
if second_region_start != &ram_64bit_start {
error!(
"Unexpected second memory region layout: start: 0x{:08x}, ram_64bit_start: 0x{:08x}",
second_region_start, ram_64bit_start
);
return Err(super::Error::MemmapTableSetup);
}
info!(
"create_memmap_entry, start: 0x{:08x}, end: 0x{:08x})",
ram_64bit_start, second_region_end
);
add_memmap_entry(
&mut memmap,
ram_64bit_start,
second_region_end - ram_64bit_start,
E820_RAM,
);
} }
add_memmap_entry( add_memmap_entry(
@@ -1079,7 +1139,7 @@ pub fn initramfs_load_addr(
Ok(aligned_addr) Ok(aligned_addr)
} }
pub fn get_host_cpu_phys_bits() -> u8 { pub fn get_host_cpu_phys_bits(hypervisor: &Arc<dyn hypervisor::Hypervisor>) -> u8 {
// SAFETY: call cpuid with valid leaves // SAFETY: call cpuid with valid leaves
unsafe { unsafe {
let leaf = x86_64::__cpuid(0x8000_0000); let leaf = x86_64::__cpuid(0x8000_0000);
@@ -1088,9 +1148,7 @@ pub fn get_host_cpu_phys_bits() -> u8 {
// Some physical address bits may become reserved when the feature is enabled. // Some physical address bits may become reserved when the feature is enabled.
// See AMD64 Architecture Programmer's Manual Volume 2, Section 7.10.1 // See AMD64 Architecture Programmer's Manual Volume 2, Section 7.10.1
let reduced = if leaf.eax >= 0x8000_001f let reduced = if leaf.eax >= 0x8000_001f
&& leaf.ebx == 0x6874_7541 // Vendor ID: AuthenticAMD && matches!(hypervisor.get_cpu_vendor(), CpuVendor::AMD)
&& leaf.ecx == 0x444d_4163
&& leaf.edx == 0x6974_6e65
&& x86_64::__cpuid(0x8000_001f).eax & 0x1 != 0 && x86_64::__cpuid(0x8000_001f).eax & 0x1 != 0
{ {
(x86_64::__cpuid(0x8000_001f).ebx >> 6) & 0x3f (x86_64::__cpuid(0x8000_001f).ebx >> 6) & 0x3f
@@ -1225,16 +1283,8 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn regions_lt_4gb() { fn regions_base_addr() {
let regions = arch_memory_regions(1 << 29); let regions = arch_memory_regions();
assert_eq!(3, regions.len());
assert_eq!(GuestAddress(0), regions[0].0);
assert_eq!(1usize << 29, regions[0].1);
}
#[test]
fn regions_gt_4gb() {
let regions = arch_memory_regions((1 << 32) + 0x8000);
assert_eq!(4, regions.len()); assert_eq!(4, regions.len());
assert_eq!(GuestAddress(0), regions[0].0); assert_eq!(GuestAddress(0), regions[0].0);
assert_eq!(GuestAddress(1 << 32), regions[1].0); assert_eq!(GuestAddress(1 << 32), regions[1].0);
@@ -1258,11 +1308,10 @@ mod tests {
assert!(config_err.is_err()); assert!(config_err.is_err());
// Now assigning some memory that falls before the 32bit memory hole. // Now assigning some memory that falls before the 32bit memory hole.
let mem_size = 128 << 20; let arch_mem_regions = arch_memory_regions();
let arch_mem_regions = arch_memory_regions(mem_size);
let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions
.iter() .iter()
.filter(|r| r.2 == RegionType::Ram) .filter(|r| r.2 == RegionType::Ram && r.1 != usize::MAX)
.map(|r| (r.0, r.1)) .map(|r| (r.0, r.1))
.collect(); .collect();
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap(); let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
@@ -1280,48 +1329,18 @@ mod tests {
) )
.unwrap(); .unwrap();
// Now assigning some memory that is equal to the start of the 32bit memory hole.
let mem_size = 3328 << 20;
let arch_mem_regions = arch_memory_regions(mem_size);
let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions
.iter()
.filter(|r| r.2 == RegionType::Ram)
.map(|r| (r.0, r.1))
.collect();
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
configure_system(
&gm,
GuestAddress(0),
&None,
no_vcpus,
None,
None,
None,
None,
None,
)
.unwrap();
configure_system(
&gm,
GuestAddress(0),
&None,
no_vcpus,
None,
None,
None,
None,
None,
)
.unwrap();
// Now assigning some memory that falls after the 32bit memory hole. // Now assigning some memory that falls after the 32bit memory hole.
let mem_size = 3330 << 20; let arch_mem_regions = arch_memory_regions();
let arch_mem_regions = arch_memory_regions(mem_size);
let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions
.iter() .iter()
.filter(|r| r.2 == RegionType::Ram) .filter(|r| r.2 == RegionType::Ram)
.map(|r| (r.0, r.1)) .map(|r| {
if r.1 == usize::MAX {
(r.0, 128 << 20)
} else {
(r.0, r.1)
}
})
.collect(); .collect();
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap(); let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
configure_system( configure_system(

View File

@@ -13,13 +13,13 @@ libc = "0.2.139"
log = "0.4.17" log = "0.4.17"
qcow = { path = "../qcow" } qcow = { path = "../qcow" }
smallvec = "1.10.0" smallvec = "1.10.0"
thiserror = "1.0.39" thiserror = "1.0.40"
versionize = "0.1.10" versionize = "0.1.10"
versionize_derive = "0.1.4" versionize_derive = "0.1.4"
vhdx = { path = "../vhdx" } vhdx = { path = "../vhdx" }
virtio-bindings = { version = "0.2.0", features = ["virtio-v5_0_0"] } virtio-bindings = { version = "0.2.0", features = ["virtio-v5_0_0"] }
virtio-queue = "0.7.1" virtio-queue = "0.8.0"
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] } vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
vm-virtio = { path = "../vm-virtio" } vm-virtio = { path = "../vm-virtio" }
vmm-sys-util = "0.11.0" vmm-sys-util = "0.11.0"

View File

@@ -17,8 +17,8 @@ fn main() {
} }
// This println!() has a special behavior, as it will set the environment // This println!() has a special behavior, as it will set the environment
// variable BUILT_VERSION, so that it can be reused from the binary. // variable BUILD_VERSION, so that it can be reused from the binary.
// Particularly, this is used from src/main.rs to display the exact // Particularly, this is used from src/main.rs to display the exact
// version. // version.
println!("cargo:rustc-env=BUILT_VERSION={version}"); println!("cargo:rustc-env=BUILD_VERSION={version}");
} }

View File

@@ -6,19 +6,19 @@ edition = "2021"
[dependencies] [dependencies]
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" } acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
anyhow = "1.0.69" anyhow = "1.0.71"
arch = { path = "../arch" } arch = { path = "../arch" }
bitflags = "1.3.2" bitflags = "2.3.3"
byteorder = "1.4.3" byteorder = "1.4.3"
hypervisor = { path = "../hypervisor" } hypervisor = { path = "../hypervisor" }
libc = "0.2.139" libc = "0.2.139"
log = "0.4.17" log = "0.4.17"
thiserror = "1.0.39" thiserror = "1.0.40"
tpm = { path = "../tpm" } tpm = { path = "../tpm" }
versionize = "0.1.10" versionize = "0.1.10"
versionize_derive = "0.1.4" versionize_derive = "0.1.4"
vm-device = { path = "../vm-device" } vm-device = { path = "../vm-device" }
vm-memory = "0.10.0" vm-memory = "0.11.0"
vm-migration = { path = "../vm-migration" } vm-migration = { path = "../vm-migration" }
vmm-sys-util = "0.11.0" vmm-sys-util = "0.11.0"

View File

@@ -116,6 +116,7 @@ impl Aml for AcpiGedDevice {
true, true,
self.address.0, self.address.0,
self.address.0 + GED_DEVICE_ACPI_SIZE as u64 - 1, self.address.0 + GED_DEVICE_ACPI_SIZE as u64 - 1,
None,
)]), )]),
), ),
&aml::OpRegion::new( &aml::OpRegion::new(

View File

@@ -1,18 +1,21 @@
- [Cloud Hypervisor API](#cloud-hypervisor-api) - [Cloud Hypervisor API](#cloud-hypervisor-api)
- [External API](#external-api) - [External API](#external-api)
- [REST API](#rest-api) - [REST API](#rest-api)
- [Location and availability](#location-and-availability) - [REST API Location and availability](#rest-api-location-and-availability)
- [Endpoints](#endpoints) - [REST API Endpoints](#rest-api-endpoints)
- [Virtual Machine Manager (VMM) Actions](#virtual-machine-manager-vmm-actions) - [Virtual Machine Manager (VMM) Actions](#virtual-machine-manager-vmm-actions)
- [Virtual Machine (VM) Actions](#virtual-machine-vm-actions) - [Virtual Machine (VM) Actions](#virtual-machine-vm-actions)
- [REST API Examples](#rest-api-examples) - [REST API Examples](#rest-api-examples)
- [Create a Virtual Machine](#create-a-virtual-machine) - [Create a Virtual Machine](#create-a-virtual-machine)
- [Boot a Virtual Machine](#boot-a-virtual-machine) - [Boot a Virtual Machine](#boot-a-virtual-machine)
- [Dump a Virtual Machine Information](#dump-a-virtual-machine-information) - [Dump a Virtual Machine Information](#dump-a-virtual-machine-information)
- [Reboot a Virtual Machine](#reboot-a-virtual-machine) - [Reboot a Virtual Machine](#reboot-a-virtual-machine)
- [Shut a Virtual Machine Down](#shut-a-virtual-machine-down) - [Shut a Virtual Machine Down](#shut-a-virtual-machine-down)
- [D-Bus API](#d-bus-api)
- [D-Bus API Location and availability](#d-bus-api-location-and-availability)
- [D-Bus API Interface](#d-bus-api-interface)
- [Command Line Interface](#command-line-interface) - [Command Line Interface](#command-line-interface)
- [REST API and CLI Architectural Relationship](#rest-api-and-cli-architectural-relationship) - [REST API, D-Bus API and CLI Architectural Relationship](#rest-api-and-cli-architectural-relationship)
- [Internal API](#internal-api) - [Internal API](#internal-api)
- [Goals and Design](#goals-and-design) - [Goals and Design](#goals-and-design)
- [End to End Example](#end-to-end-example) - [End to End Example](#end-to-end-example)
@@ -21,9 +24,11 @@
The Cloud Hypervisor API is made of 2 distinct interfaces: The Cloud Hypervisor API is made of 2 distinct interfaces:
1. **The external API**. This is the user facing API. Users and operators can 1. **The External API** This is the user facing API. Users and operators
control and manage Cloud Hypervisor through either a REST API or a Command can control and manage the Cloud Hypervisor through various options
Line Interface (CLI). including a REST API, a Command Line Interface (CLI) or a D-Bus based API,
which is not compiled into Cloud Hypervisor by default.
1. **The internal API**, based on [rust's Multi-Producer, Single-Consumer (MPSC)](https://doc.rust-lang.org/std/sync/mpsc/) 1. **The internal API**, based on [rust's Multi-Producer, Single-Consumer (MPSC)](https://doc.rust-lang.org/std/sync/mpsc/)
module. This API is used internally by the Cloud Hypervisor threads to module. This API is used internally by the Cloud Hypervisor threads to
communicate between each others. communicate between each others.
@@ -40,10 +45,10 @@ API triggers VM and VMM specific actions, and as such it is designed as a
collection of RPC-style, static methods. collection of RPC-style, static methods.
The API is [OpenAPI 3.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md) The API is [OpenAPI 3.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md)
compliant. Please consult the [Cloud Hypervisor API](https://raw.githubusercontent.com/cloud-hypervisor/cloud-hypervisor/master/vmm/src/api/openapi/cloud-hypervisor.yaml) compliant. Please consult the [Cloud Hypervisor OpenAPI Document](https://raw.githubusercontent.com/cloud-hypervisor/cloud-hypervisor/master/vmm/src/api/openapi/cloud-hypervisor.yaml)
document for more details about the API payloads and responses. for more details about the API payloads and responses.
### Location and availability #### REST API Location and availability
The REST API is available as soon as the Cloud Hypervisor binary is started, The REST API is available as soon as the Cloud Hypervisor binary is started,
through a local UNIX socket. through a local UNIX socket.
@@ -65,18 +70,18 @@ Cloud Hypervisor Guest
Disk(s): None Disk(s): None
``` ```
### Endpoints #### REST API Endpoints
The Cloud Hypervisor API exposes the following actions through its endpoints: The Cloud Hypervisor API exposes the following actions through its endpoints:
#### Virtual Machine Manager (VMM) Actions ##### Virtual Machine Manager (VMM) Actions
| Action | Endpoint | Request Body | Response Body | Prerequisites | | Action | Endpoint | Request Body | Response Body | Prerequisites |
| ----------------------------------- | --------------- | ------------ | -------------------------- | ------------------ | | ----------------------------------- | --------------- | ------------ | -------------------------- | ------------------ |
| Check for the REST API availability | `/vmm.ping` | N/A | `/schemas/VmmPingResponse` | N/A | | Check for the REST API availability | `/vmm.ping` | N/A | `/schemas/VmmPingResponse` | N/A |
| Shut the VMM down | `/vmm.shutdown` | N/A | N/A | The VMM is running | | Shut the VMM down | `/vmm.shutdown` | N/A | N/A | The VMM is running |
#### Virtual Machine (VM) Actions ##### Virtual Machine (VM) Actions
| Action | Endpoint | Request Body | Response Body | Prerequisites | | Action | Endpoint | Request Body | Response Body | Prerequisites |
| ---------------------------------- | ----------------------- | ------------------------------- | ------------------------ | ------------------------------------------------------ | | ---------------------------------- | ----------------------- | ------------------------------- | ------------------------ | ------------------------------------------------------ |
@@ -110,10 +115,10 @@ The Cloud Hypervisor API exposes the following actions through its endpoints:
* The `vmcoredump` action is available exclusively for the `x86_64` * The `vmcoredump` action is available exclusively for the `x86_64`
architecture and can be executed only when the `guest_debug` feature is architecture and can be executed only when the `guest_debug` feature is
enabled. Without this feature, the corresponding REST API endpoint is not enabled. Without this feature, the corresponding [REST API](#rest-api) or
available. [D-Bus API](#d-bus-api) endpoints are not available.
### REST API Examples #### REST API Examples
For the following set of examples, we assume Cloud Hypervisor is started with For the following set of examples, we assume Cloud Hypervisor is started with
the REST API available at `/tmp/cloud-hypervisor.sock`: the REST API available at `/tmp/cloud-hypervisor.sock`:
@@ -129,7 +134,7 @@ Cloud Hypervisor Guest
Disk(s): None Disk(s): None
``` ```
#### Create a Virtual Machine ##### Create a Virtual Machine
We want to create a virtual machine with the following characteristics: We want to create a virtual machine with the following characteristics:
@@ -157,7 +162,7 @@ curl --unix-socket /tmp/cloud-hypervisor.sock -i \
}' }'
``` ```
#### Boot a Virtual Machine ##### Boot a Virtual Machine
Once the VM is created, we can boot it: Once the VM is created, we can boot it:
@@ -167,7 +172,7 @@ Once the VM is created, we can boot it:
curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.boot' curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.boot'
``` ```
#### Dump a Virtual Machine Information ##### Dump a Virtual Machine Information
We can fetch information about any VM, as soon as it's created: We can fetch information about any VM, as soon as it's created:
@@ -179,7 +184,7 @@ curl --unix-socket /tmp/cloud-hypervisor.sock -i \
-H 'Accept: application/json' -H 'Accept: application/json'
``` ```
#### Reboot a Virtual Machine ##### Reboot a Virtual Machine
We can reboot a VM that's already booted: We can reboot a VM that's already booted:
@@ -189,7 +194,7 @@ We can reboot a VM that's already booted:
curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.reboot' curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.reboot'
``` ```
#### Shut a Virtual Machine Down ##### Shut a Virtual Machine Down
Once booted, we can shut a VM down from the REST API: Once booted, we can shut a VM down from the REST API:
@@ -199,6 +204,50 @@ Once booted, we can shut a VM down from the REST API:
curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.shutdown' curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.shutdown'
``` ```
### D-Bus API
Cloud Hypervisor offers a D-Bus API as an alternative to its REST API. As of
writing this document, the D-Bus API mirrors the functionality of the REST
API and shares the same set of endpoints, meaning that it supports every call
that is supported by the REST API and can be a drop-in replacement since it
also consumes/produces JSON.
#### D-Bus API Location and availability
This feature is not compiled into Cloud Hypervisor by default. Users who
wish to use the D-Bus API, must explicitly enable it with the `dbus_api`
feature flag when compiling Cloud Hypervisor.
```sh
$ ./scripts/dev_cli.sh build --release --libc musl -- --features dbus_api
```
Once this feature is enabled, it can be configured with the following
CLI options:
```
--dbus-service-name
well known name of the service
--dbus-object-path
object path to serve the dbus interface
--dbus-system-bus use the system bus instead of a session bus
```
Example invocation:
```sh
$ ./cloud-hypervisor --dbus-service-name "org.cloudhypervisor.DBusApi" \
--dbus-object-path "/org/cloudhypervisor/DBusApi"
```
This will start serving a service with the name `org.cloudhypervisor.DBusApi1`
which in turn can be used to control and manage Cloud Hypervisor.
#### D-Bus API Interface
Please refer to the [REST API](#rest-api) documentation. As previously
mentioned, the D-Bus API currently mirrors the behaviour of the REST API.
### Command Line Interface ### Command Line Interface
The Cloud Hypervisor Command Line Interface (CLI) can only be used for launching The Cloud Hypervisor Command Line Interface (CLI) can only be used for launching
@@ -206,22 +255,26 @@ the Cloud Hypervisor binary, i.e. it can not be used for controlling the VMM or
the launched VM once they're up and running. the launched VM once they're up and running.
If you want to inspect the VMM, or control the VM after launching Cloud If you want to inspect the VMM, or control the VM after launching Cloud
Hypervisor from the CLI, you must use the [REST API](#rest-api). Hypervisor from the CLI, you must use either the [REST API](#rest-api)
or the [D-Bus API](#d-bus-api).
From the CLI, one can either: From the CLI, one can:
1. Create and boot a complete virtual machine by using the CLI options to build 1. Create and boot a complete virtual machine by using the CLI options to build
the VM config. Run `cloud-hypervisor --help` for a complete list of CLI the VM config. Run `cloud-hypervisor --help` for a complete list of CLI
options. As soon as the `cloud-hypervisor` binary is launched, the options. As soon as the `cloud-hypervisor` binary is launched, contrary
[REST API](#rest-api) is available for controlling and managing the VM. to the [D-Bus API](#d-bus-api), the [REST API](#rest-api) is available
1. Start the [REST API](#rest-api) server only, by not passing any VM for controlling and managing the VM. The [D-Bus API](#d-bus-api) doesn't start
configuration options. The VM can then be asynchronously created and booted automatically and needs to be explicitly configured in order to be run.
by sending HTTP commands to the [REST API](#rest-api). Check the 1. Start either the REST API, D-Bus API or both simultaneously without passing
[REST API examples](#rest-api-examples) section for more details. any VM configuration options. The VM can then be asynchronously created and
booted by calling API methods of choice. It should be noted that one external
API does not exclude another; it is possible to have both the REST and D-Bus
APIs running simultaneously.
### REST API and CLI Architectural Relationship ### REST API, D-Bus API and CLI Architectural Relationship
The REST API and the CLI both rely on a common, [internal API](#internal-api). The REST API, D-Bus API and the CLI all rely on a common, [internal API](#internal-api).
The CLI options are parsed by the The CLI options are parsed by the
[argh crate](https://docs.rs/argh/latest/argh/) and then translated into [argh crate](https://docs.rs/argh/latest/argh/) and then translated into
@@ -232,7 +285,11 @@ The REST API is processed by an HTTP thread using the
crate. As with the CLI, the HTTP requests eventually get translated into crate. As with the CLI, the HTTP requests eventually get translated into
[internal API](#internal-api) commands. [internal API](#internal-api) commands.
As a summary, the REST API and the CLI are essentially frontends for the The D-Bus API is implemented using the [zbus](https://github.com/dbus2/zbus)
crate and runs in its own thread. Whenever it needs to call the [internal API](#internal-api),
the [blocking](https://github.com/smol-rs/blocking) crate is used perform the call in zbus' async context.
As a summary, the REST API, the D-Bus API and the CLI are essentially frontends for the
[internal API](#internal-api): [internal API](#internal-api):
``` ```
@@ -243,11 +300,11 @@ As a summary, the REST API and the CLI are essentially frontends for the
| +------------------+ | | +------------------+ |
| | +------------------------+ | | +------------------------+
| | | | | | | |
+------------+ | | | | +------------+ | +----------+ | | |
| | | | | +--------------+ | | | | D-Bus API | | | | +--------------+ |
| User +---------+ +------> | Internal API | | | User +---------+----------->+ zbus +--------------+------> | Internal API | |
| | | | | +--------------+ | | | | | | | | +--------------+ |
+------------+ | | | | +------------+ | +----------+ | | |
| | | | | | | |
| | +------------------------+ | | +------------------------+
| +----------+ | VMM | +----------+ | VMM
@@ -262,22 +319,23 @@ As a summary, the REST API and the CLI are essentially frontends for the
## Internal API ## Internal API
The Cloud Hypervisor internal API, as its name suggests, is used internally The Cloud Hypervisor internal API, as its name suggests, is used internally
by the different Cloud Hypervisor threads (VMM, HTTP, control loop, etc) to by the different Cloud Hypervisor threads (VMM, HTTP, D-Bus, control loop,
send commands and responses to each others. etc) to send commands and responses to each others.
It is based on [rust's Multi-Producer, Single-Consumer (MPSC)](https://doc.rust-lang.org/std/sync/mpsc/), It is based on [rust's Multi-Producer, Single-Consumer (MPSC)](https://doc.rust-lang.org/std/sync/mpsc/),
and the single consumer (a.k.a. the API receiver) is the Cloud Hypervisor and the single consumer (a.k.a. the API receiver) is the Cloud Hypervisor
control loop. control loop.
API producers are the HTTP thread handling the [REST API](#rest-api) and the API producers are the HTTP thread handling the [REST API](#rest-api), the
main thread that initially parses the [CLI](#command-line-interface). D-Bus thread handling the [D-Bus API](#d-bus-api) and the main thread that
initially parses the [CLI](#command-line-interface).
### Goals and Design ### Goals and Design
The internal API is designed for controlling, managing and inspecting a Cloud The internal API is designed for controlling, managing and inspecting a Cloud
Hypervisor VMM and its guest. It is a backend for handling external, user Hypervisor VMM and its guest. It is a backend for handling external, user
visible requests through either the [REST API](#rest-api) or the visible requests through the [REST API](#rest-api), the [D-Bus API](#d-bus-api)
[CLI](#command-line-interface) interfaces. or the [CLI](#command-line-interface) interfaces.
The API follows a command-response scheme that closely maps the [REST API](#rest-api). The API follows a command-response scheme that closely maps the [REST API](#rest-api).
Any command must be replied to with a response. Any command must be replied to with a response.

View File

@@ -225,7 +225,7 @@ Number Start End Size File system Name Flags
### Create a macvtap interface ### Create a macvtap interface
Rely on the following [documentation](docs/macvtap-bridge.md) to set up a Rely on the following [documentation](macvtap-bridge.md) to set up a
macvtap interface to provide your VM with proper connectivity. macvtap interface to provide your VM with proper connectivity.
### Boot the image ### Boot the image

View File

@@ -6,5 +6,5 @@ edition = "2021"
[dependencies] [dependencies]
libc = "0.2.139" libc = "0.2.139"
serde = { version = "1.0.151", features = ["rc", "derive"] } serde = { version = "1.0.164", features = ["rc", "derive"] }
serde_json = "1.0.95" serde_json = "1.0.96"

151
fuzz/Cargo.lock generated
View File

@@ -5,16 +5,16 @@ version = 3
[[package]] [[package]]
name = "acpi_tables" name = "acpi_tables"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#0b892e2c2053c4ecfac8d9e5a52babae75114702" source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#05a609136387cc1cc9b499cee4320020325c263f"
dependencies = [ dependencies = [
"zerocopy", "zerocopy",
] ]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.70" version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]] [[package]]
name = "api_client" name = "api_client"
@@ -100,6 +100,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded"
[[package]] [[package]]
name = "block_util" name = "block_util"
version = "0.1.0" version = "0.1.0"
@@ -113,7 +119,7 @@ dependencies = [
"versionize", "versionize",
"versionize_derive", "versionize_derive",
"vhdx", "vhdx",
"virtio-bindings 0.2.0", "virtio-bindings",
"virtio-queue", "virtio-queue",
"vm-memory", "vm-memory",
"vm-virtio", "vm-virtio",
@@ -143,7 +149,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "cloud-hypervisor" name = "cloud-hypervisor"
version = "30.0.0" version = "32.0.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"api_client", "api_client",
@@ -208,9 +214,9 @@ checksum = "55626594feae15d266d52440b26ff77de0e22230cf0c113abe619084c1ddc910"
[[package]] [[package]]
name = "darling" name = "darling"
version = "0.14.4" version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"darling_macro", "darling_macro",
@@ -218,27 +224,27 @@ dependencies = [
[[package]] [[package]]
name = "darling_core" name = "darling_core"
version = "0.14.4" version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb"
dependencies = [ dependencies = [
"fnv", "fnv",
"ident_case", "ident_case",
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim",
"syn 1.0.109", "syn 2.0.15",
] ]
[[package]] [[package]]
name = "darling_macro" name = "darling_macro"
version = "0.14.4" version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn 1.0.109", "syn 2.0.15",
] ]
[[package]] [[package]]
@@ -248,7 +254,7 @@ dependencies = [
"acpi_tables", "acpi_tables",
"anyhow", "anyhow",
"arch", "arch",
"bitflags", "bitflags 2.3.2",
"byteorder", "byteorder",
"hypervisor", "hypervisor",
"libc", "libc",
@@ -269,7 +275,7 @@ version = "4.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20df693c700404f7e19d4d6fae6b15215d2913c27955d2b9d6f2c0f537511cd0" checksum = "20df693c700404f7e19d4d6fae6b15215d2913c27955d2b9d6f2c0f537511cd0"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"libc", "libc",
] ]
@@ -296,9 +302,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.8" version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
@@ -326,9 +332,9 @@ dependencies = [
[[package]] [[package]]
name = "iced-x86" name = "iced-x86"
version = "1.18.0" version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd04b950d75b3498320253b17fb92745b2cc79ead8814aede2f7c1bab858bec" checksum = "b7cc8d38244d84278262c8ebe6930cc44283d194cbabae2651f6112103802fb5"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
] ]
@@ -345,7 +351,7 @@ version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd1e1a01cfb924fd8c5c43b6827965db394f5a3a16c599ce03452266e1cf984c" checksum = "dd1e1a01cfb924fd8c5c43b6827965db394f5a3a16c599ce03452266e1cf984c"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"libc", "libc",
] ]
@@ -393,9 +399,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.141" version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]] [[package]]
name = "libfuzzer-sys" name = "libfuzzer-sys"
@@ -410,21 +416,18 @@ dependencies = [
[[package]] [[package]]
name = "linux-loader" name = "linux-loader"
version = "0.8.1" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9259ddbfbb52cc918f6bbc60390004ddd0228cf1d85f402009ff2b3d95de83f" checksum = "8d3adb7b28e189741eca3b1a4a27de0bf15e0907c9d4b0c74bd2d7d84ef72e08"
dependencies = [ dependencies = [
"vm-memory", "vm-memory",
] ]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.17" version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "micro_http" name = "micro_http"
@@ -456,7 +459,7 @@ dependencies = [
"thiserror", "thiserror",
"versionize", "versionize",
"versionize_derive", "versionize_derive",
"virtio-bindings 0.2.0", "virtio-bindings",
"virtio-queue", "virtio-queue",
"vm-memory", "vm-memory",
"vm-virtio", "vm-virtio",
@@ -465,9 +468,9 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.17.1" version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]] [[package]]
name = "option_parser" name = "option_parser"
@@ -498,9 +501,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.56" version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -518,9 +521,9 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.26" version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@@ -542,7 +545,7 @@ checksum = "e13cca257d068dd3a390d04b2c3009a3fad2ee5048dfa8f239d048372810470c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.4", "syn 2.0.15",
] ]
[[package]] [[package]]
@@ -577,29 +580,29 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.159" version = "1.0.164"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.159" version = "1.0.164"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.4", "syn 2.0.15",
] ]
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.95" version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@@ -608,9 +611,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_with" name = "serde_with"
version = "2.3.2" version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "331bb8c3bf9b92457ab7abecf07078c13f7d270ba490103e84e8b014490cd0b0" checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513"
dependencies = [ dependencies = [
"serde", "serde",
"serde_with_macros", "serde_with_macros",
@@ -618,14 +621,14 @@ dependencies = [
[[package]] [[package]]
name = "serde_with_macros" name = "serde_with_macros"
version = "2.3.2" version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "859011bddcc11f289f07f467cc1fe01c7a941daa4d8f6c40d4d1c92eb6d9319c" checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070"
dependencies = [ dependencies = [
"darling", "darling",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn 2.0.15",
] ]
[[package]] [[package]]
@@ -676,9 +679,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.4" version = "2.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c622ae390c9302e214c31013517c2061ecb2699935882c60a9b37f82f8625ae" checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -702,7 +705,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.4", "syn 2.0.15",
] ]
[[package]] [[package]]
@@ -731,15 +734,15 @@ dependencies = [
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.8" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.3.0" version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" checksum = "0fa2982af2eec27de306107c027578ff7f423d65f7250e40ce0fea8f45248b81"
dependencies = [ dependencies = [
"getrandom", "getrandom",
] ]
@@ -774,7 +777,7 @@ dependencies = [
[[package]] [[package]]
name = "vfio-bindings" name = "vfio-bindings"
version = "0.4.0" version = "0.4.0"
source = "git+https://github.com/rust-vmm/vfio?branch=main#43439e056ddfa84a4f7906ee7f2f58be70505c08" source = "git+https://github.com/rust-vmm/vfio?branch=main#89f8e77dd1a2829197ecde65b686bafcc8a1def4"
dependencies = [ dependencies = [
"vmm-sys-util", "vmm-sys-util",
] ]
@@ -782,7 +785,7 @@ dependencies = [
[[package]] [[package]]
name = "vfio-ioctls" name = "vfio-ioctls"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/rust-vmm/vfio?branch=main#43439e056ddfa84a4f7906ee7f2f58be70505c08" source = "git+https://github.com/rust-vmm/vfio?branch=main#89f8e77dd1a2829197ecde65b686bafcc8a1def4"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"kvm-bindings", "kvm-bindings",
@@ -798,9 +801,9 @@ dependencies = [
[[package]] [[package]]
name = "vfio_user" name = "vfio_user"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/rust-vmm/vfio-user?branch=main#afbbd5722885e961ce12baea12efe01d52ce14b0" source = "git+https://github.com/rust-vmm/vfio-user?branch=main#eef6bec4d421f08ed1688fe67c5ea33aabbf5069"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"libc", "libc",
"log", "log",
"serde", "serde",
@@ -827,22 +830,16 @@ dependencies = [
[[package]] [[package]]
name = "vhost" name = "vhost"
version = "0.6.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9b791c5b0717a0558888a4cf7240cea836f39a99cb342e12ce633dcaa078072" checksum = "84f81f436bca4541f4d33172e1202882c9d437db34ed17fc6d84c8ff2bde21f5"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"libc", "libc",
"vm-memory", "vm-memory",
"vmm-sys-util", "vmm-sys-util",
] ]
[[package]]
name = "virtio-bindings"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff512178285488516ed85f15b5d0113a7cdb89e9e8a760b269ae4f02b84bd6b"
[[package]] [[package]]
name = "virtio-bindings" name = "virtio-bindings"
version = "0.2.0" version = "0.2.0"
@@ -874,7 +871,7 @@ dependencies = [
"versionize", "versionize",
"versionize_derive", "versionize_derive",
"vhost", "vhost",
"virtio-bindings 0.2.0", "virtio-bindings",
"virtio-queue", "virtio-queue",
"vm-allocator", "vm-allocator",
"vm-device", "vm-device",
@@ -886,12 +883,12 @@ dependencies = [
[[package]] [[package]]
name = "virtio-queue" name = "virtio-queue"
version = "0.7.1" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ba81e2bcc21c0d2fc5e6683e79367e26ad219197423a498df801d79d5ba77bd" checksum = "91aebb1df33db33cbf04d4c2445e4f78d0b0c8e65acfd16a4ee95ef63ca252f8"
dependencies = [ dependencies = [
"log", "log",
"virtio-bindings 0.1.0", "virtio-bindings",
"vm-memory", "vm-memory",
"vmm-sys-util", "vmm-sys-util",
] ]
@@ -925,9 +922,9 @@ source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#c5a99ab71b130435927
[[package]] [[package]]
name = "vm-memory" name = "vm-memory"
version = "0.10.0" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "688a70366615b45575a424d9c665561c1b5ab2224d494f706b6a6812911a827c" checksum = "9d6ea57fe00f9086c59eeeb68e102dd611686bc3c28520fa465996d4d4bdce07"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"libc", "libc",
@@ -964,7 +961,7 @@ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
"arch", "arch",
"bitflags", "bitflags 2.3.2",
"block_util", "block_util",
"devices", "devices",
"epoll", "epoll",
@@ -1009,7 +1006,7 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd64fe09d8e880e600c324e7d664760a17f56e9672b7495a86381b49e4f72f46" checksum = "dd64fe09d8e880e600c324e7d664760a17f56e9672b7495a86381b49e4f72f46"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"libc", "libc",
"serde", "serde",
"serde_derive", "serde_derive",

View File

@@ -12,20 +12,20 @@ cargo-fuzz = true
block_util = { path = "../block_util" } block_util = { path = "../block_util" }
devices = { path = "../devices" } devices = { path = "../devices" }
epoll = "4.3.1" epoll = "4.3.1"
libc = "0.2.141" libc = "0.2.147"
libfuzzer-sys = "0.4.6" libfuzzer-sys = "0.4.6"
linux-loader = { version = "0.8.1", features = ["elf", "bzimage", "pe"] } linux-loader = { version = "0.9.0", features = ["elf", "bzimage", "pe"] }
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" } micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
net_util = { path = "../net_util" } net_util = { path = "../net_util" }
once_cell = "1.17.1" once_cell = "1.18.0"
qcow = { path = "../qcow" } qcow = { path = "../qcow" }
seccompiler = "0.3.0" seccompiler = "0.3.0"
vhdx = { path = "../vhdx" } vhdx = { path = "../vhdx" }
virtio-devices = { path = "../virtio-devices" } virtio-devices = { path = "../virtio-devices" }
virtio-queue = "0.7.1" virtio-queue = "0.8.0"
vmm = { path = "../vmm" } vmm = { path = "../vmm" }
vmm-sys-util = "0.11.1" vmm-sys-util = "0.11.1"
vm-memory = "0.10.0" vm-memory = "0.11.0"
vm-device = { path = "../vm-device" } vm-device = { path = "../vm-device" }
vm-virtio = { path = "../vm-virtio" } vm-virtio = { path = "../vm-virtio" }

View File

@@ -11,23 +11,23 @@ mshv = ["mshv-ioctls", "mshv-bindings", "vfio-ioctls/mshv"]
tdx = [] tdx = []
[dependencies] [dependencies]
anyhow = "1.0.69" anyhow = "1.0.71"
byteorder = "1.4.3" byteorder = "1.4.3"
thiserror = "1.0.39" thiserror = "1.0.40"
libc = "0.2.139" libc = "0.2.139"
log = "0.4.17" log = "0.4.17"
kvm-ioctls = { version = "0.13.0", optional = true } kvm-ioctls = { version = "0.13.0", optional = true }
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx", features = ["with-serde", "fam-wrappers"], optional = true } kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx", features = ["with-serde", "fam-wrappers"], optional = true }
mshv-bindings = { git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true } mshv-bindings = { git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true }
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", branch = "main", optional = true} mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", branch = "main", optional = true}
serde = { version = "1.0.151", features = ["rc", "derive"] } serde = { version = "1.0.164", features = ["rc", "derive"] }
serde_with = { version = "2.3.1", default-features = false, features = ["macros"] } serde_with = { version = "3.0.0", default-features = false, features = ["macros"] }
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false } vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic"] } vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic"] }
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] } vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86] [target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
version = "1.18.0" version = "1.19.0"
default-features = false default-features = false
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"] features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]

View File

@@ -26,7 +26,6 @@ pub const MTRR_MEM_TYPE_WB: u64 = 0x6;
pub const NUM_IOAPIC_PINS: usize = 24; pub const NUM_IOAPIC_PINS: usize = 24;
// X86 Exceptions // X86 Exceptions
#[allow(clippy::upper_case_acronyms)]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Exception { pub enum Exception {
DE = 0, // Divide Error DE = 0, // Divide Error

View File

@@ -21,6 +21,15 @@ use crate::MpState;
use thiserror::Error; use thiserror::Error;
use vm_memory::GuestAddress; use vm_memory::GuestAddress;
#[cfg(target_arch = "x86_64")]
#[derive(Copy, Clone, Default)]
pub enum CpuVendor {
#[default]
Unknown,
Intel,
AMD,
}
#[derive(Error, Debug)] #[derive(Error, Debug)]
/// ///
/// Enum for CPU error /// Enum for CPU error

View File

@@ -9,10 +9,14 @@
// //
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
use crate::arch::x86::CpuIdEntry; use crate::arch::x86::CpuIdEntry;
#[cfg(target_arch = "x86_64")]
use crate::cpu::CpuVendor;
#[cfg(feature = "tdx")] #[cfg(feature = "tdx")]
use crate::kvm::TdxCapabilities; use crate::kvm::TdxCapabilities;
use crate::vm::Vm; use crate::vm::Vm;
use crate::HypervisorType; use crate::HypervisorType;
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64;
use std::sync::Arc; use std::sync::Arc;
use thiserror::Error; use thiserror::Error;
@@ -75,6 +79,11 @@ pub enum HypervisorError {
/// ///
#[error("Failed to set partition property:{0}")] #[error("Failed to set partition property:{0}")]
SetPartitionProperty(#[source] anyhow::Error), SetPartitionProperty(#[source] anyhow::Error),
///
/// Running on an unsupported CPU
///
#[error("Unsupported CPU:{0}")]
UnsupportedCpu(#[source] anyhow::Error),
} }
/// ///
@@ -133,4 +142,30 @@ pub trait Hypervisor: Send + Sync {
fn get_guest_debug_hw_bps(&self) -> usize { fn get_guest_debug_hw_bps(&self) -> usize {
unimplemented!() unimplemented!()
} }
/// Get maximum number of vCPUs
fn get_max_vcpus(&self) -> u32;
#[cfg(target_arch = "x86_64")]
///
/// Determine CPU vendor
///
fn get_cpu_vendor(&self) -> CpuVendor {
// SAFETY: call cpuid with valid leaves
unsafe {
let leaf = x86_64::__cpuid(0x0);
if leaf.ebx == 0x756e_6547 && leaf.ecx == 0x6c65_746e && leaf.edx == 0x4965_6e69 {
// Vendor string GenuineIntel
CpuVendor::Intel
} else if leaf.ebx == 0x6874_7541 && leaf.ecx == 0x444d_4163 && leaf.edx == 0x6974_6e65
{
// Vendor string AuthenticAMD
CpuVendor::AMD
} else {
// Not known yet, the corresponding manufacturer manual should contain the
// necesssary info. See also https://wiki.osdev.org/CPUID#CPU_Vendor_ID_String
CpuVendor::default()
}
}
}
} }

View File

@@ -243,7 +243,6 @@ impl KvmGicV3Its {
} }
/// Method to initialize the GIC device /// Method to initialize the GIC device
#[allow(clippy::new_ret_no_self)]
pub fn new(vm: &dyn Vm, config: VgicConfig) -> Result<KvmGicV3Its> { pub fn new(vm: &dyn Vm, config: VgicConfig) -> Result<KvmGicV3Its> {
// This is inside KVM module // This is inside KVM module
let vm = vm.as_any().downcast_ref::<KvmVm>().expect("Wrong VM type?"); let vm = vm.as_any().downcast_ref::<KvmVm>().expect("Wrong VM type?");

View File

@@ -330,7 +330,7 @@ impl KvmVm {
Ok(VfioDeviceFd::new_from_kvm(device_fd)) Ok(VfioDeviceFd::new_from_kvm(device_fd))
} }
/// Checks if a particular `Cap` is available. /// Checks if a particular `Cap` is available.
fn check_extension(&self, c: Cap) -> bool { pub fn check_extension(&self, c: Cap) -> bool {
self.fd.check_extension(c) self.fd.check_extension(c)
} }
} }
@@ -1084,6 +1084,11 @@ impl hypervisor::Hypervisor for KvmHypervisor {
self.kvm.get_guest_debug_hw_bps() as usize self.kvm.get_guest_debug_hw_bps() as usize
} }
} }
/// Get maximum number of vCPUs
fn get_max_vcpus(&self) -> u32 {
self.kvm.get_max_vcpus().min(u32::MAX as usize) as u32
}
} }
/// Vcpu struct for KVM /// Vcpu struct for KVM
pub struct KvmVcpu { pub struct KvmVcpu {

View File

@@ -49,6 +49,8 @@ mod cpu;
mod device; mod device;
pub use crate::hypervisor::{Hypervisor, HypervisorError}; pub use crate::hypervisor::{Hypervisor, HypervisorError};
#[cfg(target_arch = "x86_64")]
pub use cpu::CpuVendor;
pub use cpu::{HypervisorCpuError, Vcpu, VmExit}; pub use cpu::{HypervisorCpuError, Vcpu, VmExit};
pub use device::HypervisorDeviceError; pub use device::HypervisorDeviceError;
#[cfg(all(feature = "kvm", target_arch = "aarch64"))] #[cfg(all(feature = "kvm", target_arch = "aarch64"))]

View File

@@ -279,6 +279,13 @@ impl hypervisor::Hypervisor for MshvHypervisor {
fn get_supported_cpuid(&self) -> hypervisor::Result<Vec<CpuIdEntry>> { fn get_supported_cpuid(&self) -> hypervisor::Result<Vec<CpuIdEntry>> {
Ok(Vec::new()) Ok(Vec::new())
} }
/// Get maximum number of vCPUs
fn get_max_vcpus(&self) -> u32 {
// TODO: Using HV_MAXIMUM_PROCESSORS would be better
// but the ioctl API is limited to u8
256
}
} }
/// Vcpu struct for Microsoft Hypervisor /// Vcpu struct for Microsoft Hypervisor
@@ -454,10 +461,10 @@ impl cpu::Vcpu for MshvVcpu {
/* Advance RIP and update RAX */ /* Advance RIP and update RAX */
let arr_reg_name_value = [ let arr_reg_name_value = [
( (
hv_x64_register_name_HV_X64_REGISTER_RIP, hv_register_name_HV_X64_REGISTER_RIP,
info.header.rip + insn_len, info.header.rip + insn_len,
), ),
(hv_x64_register_name_HV_X64_REGISTER_RAX, ret_rax), (hv_register_name_HV_X64_REGISTER_RAX, ret_rax),
]; ];
set_registers_64!(self.fd, arr_reg_name_value) set_registers_64!(self.fd, arr_reg_name_value)
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?; .map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
@@ -503,10 +510,10 @@ impl cpu::Vcpu for MshvVcpu {
/* Advance RIP and update RAX */ /* Advance RIP and update RAX */
let arr_reg_name_value = [ let arr_reg_name_value = [
( (
hv_x64_register_name_HV_X64_REGISTER_RIP, hv_register_name_HV_X64_REGISTER_RIP,
info.header.rip + insn_len, info.header.rip + insn_len,
), ),
(hv_x64_register_name_HV_X64_REGISTER_RAX, ret_rax), (hv_register_name_HV_X64_REGISTER_RAX, ret_rax),
]; ];
set_registers_64!(self.fd, arr_reg_name_value) set_registers_64!(self.fd, arr_reg_name_value)
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?; .map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
@@ -576,8 +583,14 @@ impl cpu::Vcpu for MshvVcpu {
/// ///
/// X86 specific call to setup the CPUID registers. /// X86 specific call to setup the CPUID registers.
/// ///
fn set_cpuid2(&self, _cpuid: &[CpuIdEntry]) -> cpu::Result<()> { fn set_cpuid2(&self, cpuid: &[CpuIdEntry]) -> cpu::Result<()> {
Ok(()) let cpuid: Vec<mshv_bindings::hv_cpuid_entry> = cpuid.iter().map(|e| (*e).into()).collect();
let mshv_cpuid = <CpuId>::from_entries(&cpuid)
.map_err(|_| cpu::HypervisorCpuError::SetCpuid(anyhow!("failed to create CpuId")))?;
self.fd
.register_intercept_result_cpuid(&mshv_cpuid)
.map_err(|e| cpu::HypervisorCpuError::SetCpuid(e.into()))
} }
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
/// ///

View File

@@ -10,6 +10,7 @@
#[repr(C)] #[repr(C)]
#[derive(Default)] #[derive(Default)]
pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]); pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]);
#[allow(clippy::missing_safety_doc)]
impl<T> __IncompleteArrayField<T> { impl<T> __IncompleteArrayField<T> {
#[inline] #[inline]
pub const fn new() -> Self { pub const fn new() -> Self {

View File

@@ -10,6 +10,7 @@
#[repr(C)] #[repr(C)]
#[derive(Default)] #[derive(Default)]
pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]); pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]);
#[allow(clippy::missing_safety_doc)]
impl<T> __IncompleteArrayField<T> { impl<T> __IncompleteArrayField<T> {
#[inline] #[inline]
pub const fn new() -> Self { pub const fn new() -> Self {

View File

@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the THIRD-PARTY file. // found in the THIRD-PARTY file.
#![allow(clippy::all)]
#![allow(non_upper_case_globals)] #![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![allow(non_snake_case)] #![allow(non_snake_case)]
@@ -28,10 +27,12 @@ pub mod inn;
// generated with bindgen /usr/include/linux/sockios.h --no-unstable-rust // generated with bindgen /usr/include/linux/sockios.h --no-unstable-rust
// --constified-enum '*' --with-derive-default // --constified-enum '*' --with-derive-default
pub mod sockios; pub mod sockios;
pub use if_tun::*; pub use if_tun::{
pub use iff::*; sock_fprog, IFF_MULTI_QUEUE, IFF_NO_PI, IFF_TAP, IFF_VNET_HDR, TUN_F_CSUM, TUN_F_TSO4,
pub use inn::*; TUN_F_TSO6, TUN_F_TSO_ECN, TUN_F_UFO,
pub use sockios::*; };
pub use iff::{ifreq, net_device_flags_IFF_UP, setsockopt, sockaddr, AF_INET};
pub use inn::sockaddr_in;
pub const TUNTAP: ::std::os::raw::c_uint = 84; pub const TUNTAP: ::std::os::raw::c_uint = 84;

View File

@@ -11,13 +11,13 @@ libc = "0.2.139"
log = "0.4.17" log = "0.4.17"
net_gen = { path = "../net_gen" } net_gen = { path = "../net_gen" }
rate_limiter = { path = "../rate_limiter" } rate_limiter = { path = "../rate_limiter" }
serde = "1.0.151" serde = "1.0.164"
thiserror = "1.0.39" thiserror = "1.0.40"
versionize = "0.1.10" versionize = "0.1.10"
versionize_derive = "0.1.4" versionize_derive = "0.1.4"
virtio-bindings = "0.2.0" virtio-bindings = "0.2.0"
virtio-queue = "0.7.1" virtio-queue = "0.8.0"
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] } vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
vm-virtio = { path = "../vm-virtio" } vm-virtio = { path = "../vm-virtio" }
vmm-sys-util = "0.11.0" vmm-sys-util = "0.11.0"
@@ -25,4 +25,4 @@ vmm-sys-util = "0.11.0"
once_cell = "1.17.1" once_cell = "1.17.1"
pnet = "0.33.0" pnet = "0.33.0"
pnet_datalink = "0.33.0" pnet_datalink = "0.33.0"
serde_json = "1.0.95" serde_json = "1.0.96"

View File

@@ -10,7 +10,7 @@ kvm = ["vfio-ioctls/kvm"]
mshv = ["vfio-ioctls/mshv"] mshv = ["vfio-ioctls/mshv"]
[dependencies] [dependencies]
anyhow = "1.0.69" anyhow = "1.0.71"
byteorder = "1.4.3" byteorder = "1.4.3"
hypervisor = { path = "../hypervisor" } hypervisor = { path = "../hypervisor" }
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main", features = ["fam-wrappers"] } vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main", features = ["fam-wrappers"] }
@@ -20,11 +20,11 @@ vfio_user = { git = "https://github.com/rust-vmm/vfio-user", branch = "main" }
vmm-sys-util = "0.11.0" vmm-sys-util = "0.11.0"
libc = "0.2.139" libc = "0.2.139"
log = "0.4.17" log = "0.4.17"
serde = { version = "1.0.151", features = ["derive"] } serde = { version = "1.0.164", features = ["derive"] }
thiserror = "1.0.39" thiserror = "1.0.40"
versionize = "0.1.10" versionize = "0.1.10"
versionize_derive = "0.1.4" versionize_derive = "0.1.4"
vm-allocator = { path = "../vm-allocator" } vm-allocator = { path = "../vm-allocator" }
vm-device = { path = "../vm-device" } vm-device = { path = "../vm-device" }
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] } vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
vm-migration = { path = "../vm-migration" } vm-migration = { path = "../vm-migration" }

View File

@@ -452,10 +452,9 @@ impl From<PciBarType> for PciBarRegionType {
} }
} }
#[allow(clippy::from_over_into)] impl From<PciBarRegionType> for PciBarType {
impl Into<PciBarType> for PciBarRegionType { fn from(val: PciBarRegionType) -> Self {
fn into(self) -> PciBarType { match val {
match self {
PciBarRegionType::IoRegion => PciBarType::Io, PciBarRegionType::IoRegion => PciBarType::Io,
PciBarRegionType::Memory32BitRegion => PciBarType::Mmio32, PciBarRegionType::Memory32BitRegion => PciBarType::Mmio32,
PciBarRegionType::Memory64BitRegion => PciBarType::Mmio64, PciBarRegionType::Memory64BitRegion => PciBarType::Mmio64,
@@ -469,10 +468,9 @@ pub enum PciBarPrefetchable {
Prefetchable = 0x08, Prefetchable = 0x08,
} }
#[allow(clippy::from_over_into)] impl From<PciBarPrefetchable> for bool {
impl Into<bool> for PciBarPrefetchable { fn from(val: PciBarPrefetchable) -> Self {
fn into(self) -> bool { match val {
match self {
PciBarPrefetchable::NotPrefetchable => false, PciBarPrefetchable::NotPrefetchable => false,
PciBarPrefetchable::Prefetchable => true, PciBarPrefetchable::Prefetchable => true,
} }

View File

@@ -511,6 +511,16 @@ impl MsixCap {
self.pba & 0xffff_fff8 self.pba & 0xffff_fff8
} }
pub fn table_set_offset(&mut self, addr: u32) {
self.table &= 0x7;
self.table += addr;
}
pub fn pba_set_offset(&mut self, addr: u32) {
self.pba &= 0x7;
self.pba += addr;
}
pub fn table_bir(&self) -> u32 { pub fn table_bir(&self) -> u32 {
self.table & 0x7 self.table & 0x7
} }

View File

@@ -14,6 +14,7 @@ use crate::{
use anyhow::anyhow; use anyhow::anyhow;
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
use hypervisor::HypervisorVmError; use hypervisor::HypervisorVmError;
use libc::{sysconf, _SC_PAGESIZE};
use std::any::Any; use std::any::Any;
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::io; use std::io;
@@ -27,6 +28,9 @@ use vfio_bindings::bindings::vfio::*;
use vfio_ioctls::{ use vfio_ioctls::{
VfioContainer, VfioDevice, VfioIrq, VfioRegionInfoCap, VfioRegionSparseMmapArea, VfioContainer, VfioDevice, VfioIrq, VfioRegionInfoCap, VfioRegionSparseMmapArea,
}; };
use vm_allocator::page_size::{
align_page_size_down, align_page_size_up, is_4k_aligned, is_4k_multiple, is_page_size_aligned,
};
use vm_allocator::{AddressAllocator, SystemAllocator}; use vm_allocator::{AddressAllocator, SystemAllocator};
use vm_device::interrupt::{ use vm_device::interrupt::{
InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig, InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig,
@@ -498,6 +502,29 @@ impl VfioCommon {
Ok(vfio_common) Ok(vfio_common)
} }
/// In case msix table offset is not page size aligned, we need do some fixup to achive it.
/// Becuse we don't want the MMIO RW region and trap region overlap each other.
fn fixup_msix_region(&mut self, bar_id: u32, region_size: u64) -> u64 {
let msix = self.interrupt.msix.as_mut().unwrap();
let msix_cap = &mut msix.cap;
// Suppose table_bir equals to pba_bir here. Am I right?
let (table_offset, table_size) = msix_cap.table_range();
if is_page_size_aligned(table_offset) || msix_cap.table_bir() != bar_id {
return region_size;
}
let (pba_offset, pba_size) = msix_cap.pba_range();
let msix_sz = align_page_size_up(table_size + pba_size);
// Expand region to hold RW and trap region which both page size aligned
let size = std::cmp::max(region_size * 2, msix_sz * 2);
// let table starts from the middle of the region
msix_cap.table_set_offset((size / 2) as u32);
msix_cap.pba_set_offset((size / 2 + pba_offset - table_offset) as u32);
size
}
pub(crate) fn allocate_bars( pub(crate) fn allocate_bars(
&mut self, &mut self,
allocator: &Arc<Mutex<SystemAllocator>>, allocator: &Arc<Mutex<SystemAllocator>>,
@@ -661,9 +688,16 @@ impl VfioCommon {
.ok_or(PciDeviceError::IoAllocationFailed(region_size))? .ok_or(PciDeviceError::IoAllocationFailed(region_size))?
} }
PciBarRegionType::Memory64BitRegion => { PciBarRegionType::Memory64BitRegion => {
// BAR allocation must be naturally aligned // We need do some fixup to keep MMIO RW region and msix cap region page size
// aligned.
region_size = self.fixup_msix_region(bar_id, region_size);
mmio_allocator mmio_allocator
.allocate(restored_bar_addr, region_size, Some(region_size)) .allocate(
restored_bar_addr,
region_size,
// SAFETY: FFI call. Trivially safe.
Some(unsafe { sysconf(_SC_PAGESIZE) as GuestUsize }),
)
.ok_or(PciDeviceError::IoAllocationFailed(region_size))? .ok_or(PciDeviceError::IoAllocationFailed(region_size))?
} }
}; };
@@ -800,6 +834,23 @@ impl VfioCommon {
}); });
} }
pub(crate) fn get_msix_cap_idx(&self) -> Option<usize> {
let mut cap_next = self
.vfio_wrapper
.read_config_byte(PCI_CONFIG_CAPABILITY_OFFSET);
while cap_next != 0 {
let cap_id = self.vfio_wrapper.read_config_byte(cap_next.into());
if PciCapabilityId::from(cap_id) == PciCapabilityId::MsiX {
return Some(cap_next as usize);
} else {
cap_next = self.vfio_wrapper.read_config_byte((cap_next + 1).into());
}
}
None
}
pub(crate) fn parse_capabilities(&mut self, bdf: PciBdf) { pub(crate) fn parse_capabilities(&mut self, bdf: PciBdf) {
let mut cap_next = self let mut cap_next = self
.vfio_wrapper .vfio_wrapper
@@ -1154,6 +1205,15 @@ impl VfioCommon {
return self.configuration.read_reg(reg_idx); return self.configuration.read_reg(reg_idx);
} }
if let Some(id) = self.get_msix_cap_idx() {
let msix = self.interrupt.msix.as_mut().unwrap();
if reg_idx * 4 == id + 4 {
return msix.cap.table;
} else if reg_idx * 4 == id + 8 {
return msix.cap.pba;
}
}
// Since we don't support passing multi-functions devices, we should // Since we don't support passing multi-functions devices, we should
// mask the multi-function bit, bit 7 of the Header Type byte on the // mask the multi-function bit, bit 7 of the Header Type byte on the
// register 3. // register 3.
@@ -1316,18 +1376,6 @@ impl VfioPciDevice {
self.iommu_attached self.iommu_attached
} }
fn align_4k(address: u64) -> u64 {
(address + 0xfff) & 0xffff_ffff_ffff_f000
}
fn is_4k_aligned(address: u64) -> bool {
(address & 0xfff) == 0
}
fn is_4k_multiple(size: u64) -> bool {
(size & 0xfff) == 0
}
fn generate_sparse_areas( fn generate_sparse_areas(
caps: &[VfioRegionInfoCap], caps: &[VfioRegionInfoCap],
region_index: u32, region_index: u32,
@@ -1339,14 +1387,14 @@ impl VfioPciDevice {
match cap { match cap {
VfioRegionInfoCap::SparseMmap(sparse_mmap) => return Ok(sparse_mmap.areas.clone()), VfioRegionInfoCap::SparseMmap(sparse_mmap) => return Ok(sparse_mmap.areas.clone()),
VfioRegionInfoCap::MsixMappable => { VfioRegionInfoCap::MsixMappable => {
if !Self::is_4k_aligned(region_start) { if !is_4k_aligned(region_start) {
error!( error!(
"Region start address 0x{:x} must be at least aligned on 4KiB", "Region start address 0x{:x} must be at least aligned on 4KiB",
region_start region_start
); );
return Err(VfioPciError::RegionAlignment); return Err(VfioPciError::RegionAlignment);
} }
if !Self::is_4k_multiple(region_size) { if !is_4k_multiple(region_size) {
error!( error!(
"Region size 0x{:x} must be at least a multiple of 4KiB", "Region size 0x{:x} must be at least a multiple of 4KiB",
region_size region_size
@@ -1358,7 +1406,8 @@ impl VfioPciDevice {
// the MSI-X PBA table, we must calculate the subregions // the MSI-X PBA table, we must calculate the subregions
// around them, leading to a list of sparse areas. // around them, leading to a list of sparse areas.
// We want to make sure we will still trap MMIO accesses // We want to make sure we will still trap MMIO accesses
// to these MSI-X specific ranges. // to these MSI-X specific ranges. If these region don't align
// with pagesize, we can achive it by enlarging its range.
// //
// Using a BtreeMap as the list provided through the iterator is sorted // Using a BtreeMap as the list provided through the iterator is sorted
// by key. This ensures proper split of the whole region. // by key. This ensures proper split of the whole region.
@@ -1366,10 +1415,14 @@ impl VfioPciDevice {
if let Some(msix) = vfio_msix { if let Some(msix) = vfio_msix {
if region_index == msix.cap.table_bir() { if region_index == msix.cap.table_bir() {
let (offset, size) = msix.cap.table_range(); let (offset, size) = msix.cap.table_range();
let offset = align_page_size_down(offset);
let size = align_page_size_up(size);
inter_ranges.insert(offset, size); inter_ranges.insert(offset, size);
} }
if region_index == msix.cap.pba_bir() { if region_index == msix.cap.pba_bir() {
let (offset, size) = msix.cap.pba_range(); let (offset, size) = msix.cap.pba_range();
let offset = align_page_size_down(offset);
let size = align_page_size_up(size);
inter_ranges.insert(offset, size); inter_ranges.insert(offset, size);
} }
} }
@@ -1383,8 +1436,7 @@ impl VfioPciDevice {
size: range_offset - current_offset, size: range_offset - current_offset,
}); });
} }
current_offset = align_page_size_down(range_offset + range_size);
current_offset = Self::align_4k(range_offset + range_size);
} }
if region_size > current_offset { if region_size > current_offset {
@@ -1482,6 +1534,15 @@ impl VfioPciDevice {
return Err(VfioPciError::MmapArea); return Err(VfioPciError::MmapArea);
} }
if !is_page_size_aligned(area.size) || !is_page_size_aligned(area.offset) {
warn!(
"Could not mmap sparse area that is not page size aligned (offset = 0x{:x}, size = 0x{:x})",
area.offset,
area.size,
);
return Ok(());
}
let user_memory_region = UserMemoryRegion { let user_memory_region = UserMemoryRegion {
slot: (self.memory_slot)(), slot: (self.memory_slot)(),
start: region.start.0 + area.offset, start: region.start.0 + area.offset,

View File

@@ -7,9 +7,9 @@ build = "../build.rs"
[dependencies] [dependencies]
argh = "0.1.9" argh = "0.1.9"
dirs = "4.0.0" dirs = "5.0.0"
serde = { version = "1.0.151", features = ["rc", "derive"] } serde = { version = "1.0.164", features = ["rc", "derive"] }
serde_json = "1.0.95" serde_json = "1.0.96"
test_infra = { path = "../test_infra" } test_infra = { path = "../test_infra" }
thiserror = "1.0.39" thiserror = "1.0.40"
wait-timeout = "0.2.0" wait-timeout = "0.2.0"

View File

@@ -671,7 +671,7 @@ fn main() {
let opts: Options = argh::from_env(); let opts: Options = argh::from_env();
if opts.version { if opts.version {
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILT_VERSION")); println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILD_VERSION"));
return; return;
} }

View File

@@ -66,7 +66,7 @@ impl QcowRawFile {
non_zero_flags: u64, non_zero_flags: u64,
) -> io::Result<()> { ) -> io::Result<()> {
self.file.seek(SeekFrom::Start(offset))?; self.file.seek(SeekFrom::Start(offset))?;
let mut buffer = BufWriter::with_capacity(table.len() * size_of::<u64>(), &mut self.file); let mut buffer = BufWriter::with_capacity(std::mem::size_of_val(table), &mut self.file);
for addr in table { for addr in table {
let val = if *addr == 0 { let val = if *addr == 0 {
0 0
@@ -91,7 +91,8 @@ impl QcowRawFile {
/// Writes a refcount block to the file. /// Writes a refcount block to the file.
pub fn write_refcount_block(&mut self, offset: u64, table: &[u16]) -> io::Result<()> { pub fn write_refcount_block(&mut self, offset: u64, table: &[u16]) -> io::Result<()> {
self.file.seek(SeekFrom::Start(offset))?; self.file.seek(SeekFrom::Start(offset))?;
let mut buffer = BufWriter::with_capacity(table.len() * size_of::<u16>(), &mut self.file); let mut buffer = BufWriter::with_capacity(std::mem::size_of_val(table), &mut self.file);
for count in table { for count in table {
buffer.write_u16::<BigEndian>(*count)?; buffer.write_u16::<BigEndian>(*count)?;
} }

View File

@@ -1,18 +1,29 @@
- [v33.0](#v330)
- [D-Bus based API](#d-bus-based-api)
- [Passthrough CPU cache information for AArch64](#passthrough-cpu-cache-information-for-aarch64)
- [Notable Bug Fixes](#notable-bug-fixes)
- [Contributors](#contributors)
- [v32.0](#v320)
- [Increased PCI Segment Limit](#increased-pci-segment-limit)
- [API Changes](#api-changes)
- [Notable Bug Fixes](#notable-bug-fixes-1)
- [Contributors](#contributors-1)
- [v31.1](#v311)
- [v31.0](#v310) - [v31.0](#v310)
- [Update to Latest `acpi_tables`](#update-to-latest-acpi_tables) - [Update to Latest `acpi_tables`](#update-to-latest-acpi_tables)
- [Update Reference Kernel to 6.2](#update-reference-kernel-to-62) - [Update Reference Kernel to 6.2](#update-reference-kernel-to-62)
- [Improvements on Console `SIGWINCH` Handler](#improvements-on-console-sigwinch-handler) - [Improvements on Console `SIGWINCH` Handler](#improvements-on-console-sigwinch-handler)
- [Remove Directory Support from `MemoryZoneConfig::file`](#remove-directory-support-from-memoryzoneconfigfile) - [Remove Directory Support from `MemoryZoneConfig::file`](#remove-directory-support-from-memoryzoneconfigfile)
- [Documentation Improvements](#documentation-improvements) - [Documentation Improvements](#documentation-improvements)
- [Notable Bug Fixes](#notable-bug-fixes) - [Notable Bug Fixes](#notable-bug-fixes-2)
- [Contributors](#contributors) - [Contributors](#contributors-2)
- [v30.0](#v300) - [v30.0](#v300)
- [Command Line Changes for Reduced Binary Size](#command-line-changes-for-reduced-binary-size) - [Command Line Changes for Reduced Binary Size](#command-line-changes-for-reduced-binary-size)
- [Basic vfio-user Server Support](#basic-vfio-user-server-support) - [Basic vfio-user Server Support](#basic-vfio-user-server-support)
- [Heap Profiling Support](#heap-profiling-support) - [Heap Profiling Support](#heap-profiling-support)
- [Documentation Improvements](#documentation-improvements-1) - [Documentation Improvements](#documentation-improvements-1)
- [Notable Bug Fixes](#notable-bug-fixes-1) - [Notable Bug Fixes](#notable-bug-fixes-3)
- [Contributors](#contributors-1) - [Contributors](#contributors-3)
- [v28.2](#v282) - [v28.2](#v282)
- [v29.0](#v290) - [v29.0](#v290)
- [Release Binary Supports Both MSHV and KVM](#release-binary-supports-both-mshv-and-kvm) - [Release Binary Supports Both MSHV and KVM](#release-binary-supports-both-mshv-and-kvm)
@@ -22,10 +33,10 @@
- [`AArch64` Documentation Integration](#aarch64-documentation-integration) - [`AArch64` Documentation Integration](#aarch64-documentation-integration)
- [`virtio-block` Counters Enhancement](#virtio-block-counters-enhancement) - [`virtio-block` Counters Enhancement](#virtio-block-counters-enhancement)
- [TCP Offload Control](#tcp-offload-control) - [TCP Offload Control](#tcp-offload-control)
- [Notable Bug Fixes](#notable-bug-fixes-2) - [Notable Bug Fixes](#notable-bug-fixes-4)
- [Removals](#removals) - [Removals](#removals)
- [Deprecations](#deprecations) - [Deprecations](#deprecations)
- [Contributors](#contributors-2) - [Contributors](#contributors-4)
- [v28.1](#v281) - [v28.1](#v281)
- [v28.0](#v280) - [v28.0](#v280)
- [Community Engagement (Reminder)](#community-engagement-reminder) - [Community Engagement (Reminder)](#community-engagement-reminder)
@@ -33,9 +44,9 @@
- [Virtualised TPM Support](#virtualised-tpm-support) - [Virtualised TPM Support](#virtualised-tpm-support)
- [Transparent Huge Page Support](#transparent-huge-page-support) - [Transparent Huge Page Support](#transparent-huge-page-support)
- [README Quick Start Improved](#readme-quick-start-improved) - [README Quick Start Improved](#readme-quick-start-improved)
- [Notable Bug Fixes](#notable-bug-fixes-3) - [Notable Bug Fixes](#notable-bug-fixes-5)
- [Removals](#removals-1) - [Removals](#removals-1)
- [Contributors](#contributors-3) - [Contributors](#contributors-5)
- [v27.0](#v270) - [v27.0](#v270)
- [Community Engagement](#community-engagement) - [Community Engagement](#community-engagement)
- [Prebuilt Packages](#prebuilt-packages) - [Prebuilt Packages](#prebuilt-packages)
@@ -44,41 +55,41 @@
- [Simplified Build Feature Flags](#simplified-build-feature-flags) - [Simplified Build Feature Flags](#simplified-build-feature-flags)
- [Asynchronous Kernel Loading](#asynchronous-kernel-loading) - [Asynchronous Kernel Loading](#asynchronous-kernel-loading)
- [GDB Support for AArch64](#gdb-support-for-aarch64) - [GDB Support for AArch64](#gdb-support-for-aarch64)
- [Notable Bug Fixes](#notable-bug-fixes-4) - [Notable Bug Fixes](#notable-bug-fixes-6)
- [Deprecations](#deprecations-1) - [Deprecations](#deprecations-1)
- [Contributors](#contributors-4) - [Contributors](#contributors-6)
- [v26.0](#v260) - [v26.0](#v260)
- [SMBIOS Improvements via `--platform`](#smbios-improvements-via---platform) - [SMBIOS Improvements via `--platform`](#smbios-improvements-via---platform)
- [Unified Binary MSHV and KVM Support](#unified-binary-mshv-and-kvm-support) - [Unified Binary MSHV and KVM Support](#unified-binary-mshv-and-kvm-support)
- [Notable Bug Fixes](#notable-bug-fixes-5) - [Notable Bug Fixes](#notable-bug-fixes-7)
- [Deprecations](#deprecations-2) - [Deprecations](#deprecations-2)
- [Removals](#removals-2) - [Removals](#removals-2)
- [Contributors](#contributors-5) - [Contributors](#contributors-7)
- [v25.0](#v250) - [v25.0](#v250)
- [`ch-remote` Improvements](#ch-remote-improvements-1) - [`ch-remote` Improvements](#ch-remote-improvements-1)
- [VM "Coredump" Support](#vm-coredump-support) - [VM "Coredump" Support](#vm-coredump-support)
- [Notable Bug Fixes](#notable-bug-fixes-6) - [Notable Bug Fixes](#notable-bug-fixes-8)
- [Removals](#removals-3) - [Removals](#removals-3)
- [Contributors](#contributors-6) - [Contributors](#contributors-8)
- [v24.0](#v240) - [v24.0](#v240)
- [Bypass Mode for `virtio-iommu`](#bypass-mode-for-virtio-iommu) - [Bypass Mode for `virtio-iommu`](#bypass-mode-for-virtio-iommu)
- [Ensure Identifiers Uniqueness](#ensure-identifiers-uniqueness) - [Ensure Identifiers Uniqueness](#ensure-identifiers-uniqueness)
- [Sparse Mmap support](#sparse-mmap-support) - [Sparse Mmap support](#sparse-mmap-support)
- [Expose Platform Serial Number](#expose-platform-serial-number) - [Expose Platform Serial Number](#expose-platform-serial-number)
- [Notable Bug Fixes](#notable-bug-fixes-7) - [Notable Bug Fixes](#notable-bug-fixes-9)
- [Notable Improvements](#notable-improvements) - [Notable Improvements](#notable-improvements)
- [Deprecations](#deprecations-3) - [Deprecations](#deprecations-3)
- [New on the Website](#new-on-the-website) - [New on the Website](#new-on-the-website)
- [Contributors](#contributors-7) - [Contributors](#contributors-9)
- [v23.1](#v231) - [v23.1](#v231)
- [v23.0](#v230) - [v23.0](#v230)
- [vDPA Support](#vdpa-support) - [vDPA Support](#vdpa-support)
- [Updated OS Support list](#updated-os-support-list) - [Updated OS Support list](#updated-os-support-list)
- [`AArch64` Memory Map Improvements](#aarch64-memory-map-improvements) - [`AArch64` Memory Map Improvements](#aarch64-memory-map-improvements)
- [`AMX` Support](#amx-support) - [`AMX` Support](#amx-support)
- [Notable Bug Fixes](#notable-bug-fixes-8) - [Notable Bug Fixes](#notable-bug-fixes-10)
- [Deprecations](#deprecations-4) - [Deprecations](#deprecations-4)
- [Contributors](#contributors-8) - [Contributors](#contributors-10)
- [v22.1](#v221) - [v22.1](#v221)
- [v22.0](#v220) - [v22.0](#v220)
- [GDB Debug Stub Support](#gdb-debug-stub-support) - [GDB Debug Stub Support](#gdb-debug-stub-support)
@@ -89,13 +100,13 @@
- [PMU Support for AArch64](#pmu-support-for-aarch64) - [PMU Support for AArch64](#pmu-support-for-aarch64)
- [Documentation Under CC-BY-4.0 License](#documentation-under-cc-by-40-license) - [Documentation Under CC-BY-4.0 License](#documentation-under-cc-by-40-license)
- [Deprecation of "Classic" `virtiofsd`](#deprecation-of-classic-virtiofsd) - [Deprecation of "Classic" `virtiofsd`](#deprecation-of-classic-virtiofsd)
- [Notable Bug Fixes](#notable-bug-fixes-9) - [Notable Bug Fixes](#notable-bug-fixes-11)
- [Contributors](#contributors-9) - [Contributors](#contributors-11)
- [v21.0](#v210) - [v21.0](#v210)
- [Efficient Local Live Migration (for Live Upgrade)](#efficient-local-live-migration-for-live-upgrade) - [Efficient Local Live Migration (for Live Upgrade)](#efficient-local-live-migration-for-live-upgrade)
- [Recommended Kernel is Now 5.15](#recommended-kernel-is-now-515) - [Recommended Kernel is Now 5.15](#recommended-kernel-is-now-515)
- [Notable Bug fixes](#notable-bug-fixes-10) - [Notable Bug fixes](#notable-bug-fixes-12)
- [Contributors](#contributors-10) - [Contributors](#contributors-12)
- [v20.2](#v202) - [v20.2](#v202)
- [v20.1](#v201) - [v20.1](#v201)
- [v20.0](#v200) - [v20.0](#v200)
@@ -104,8 +115,8 @@
- [Improved VFIO support](#improved-vfio-support) - [Improved VFIO support](#improved-vfio-support)
- [Safer code](#safer-code) - [Safer code](#safer-code)
- [Extended documentation](#extended-documentation) - [Extended documentation](#extended-documentation)
- [Notable bug fixes](#notable-bug-fixes-11) - [Notable bug fixes](#notable-bug-fixes-13)
- [Contributors](#contributors-11) - [Contributors](#contributors-13)
- [v19.0](#v190) - [v19.0](#v190)
- [Improved PTY handling for serial and `virtio-console`](#improved-pty-handling-for-serial-and-virtio-console) - [Improved PTY handling for serial and `virtio-console`](#improved-pty-handling-for-serial-and-virtio-console)
- [PCI boot time optimisations](#pci-boot-time-optimisations) - [PCI boot time optimisations](#pci-boot-time-optimisations)
@@ -113,8 +124,8 @@
- [Live migration enhancements](#live-migration-enhancements) - [Live migration enhancements](#live-migration-enhancements)
- [`virtio-mem` support with `vfio-user`](#virtio-mem-support-with-vfio-user) - [`virtio-mem` support with `vfio-user`](#virtio-mem-support-with-vfio-user)
- [AArch64 for `virtio-iommu`](#aarch64-for-virtio-iommu) - [AArch64 for `virtio-iommu`](#aarch64-for-virtio-iommu)
- [Notable bug fixes](#notable-bug-fixes-12) - [Notable bug fixes](#notable-bug-fixes-14)
- [Contributors](#contributors-12) - [Contributors](#contributors-14)
- [v18.0](#v180) - [v18.0](#v180)
- [Experimental User Device (`vfio-user`) support](#experimental-user-device-vfio-user-support) - [Experimental User Device (`vfio-user`) support](#experimental-user-device-vfio-user-support)
- [Migration support for `vhost-user` devices](#migration-support-for-vhost-user-devices) - [Migration support for `vhost-user` devices](#migration-support-for-vhost-user-devices)
@@ -124,23 +135,23 @@
- [Live migration on MSHV hypervisor](#live-migration-on-mshv-hypervisor) - [Live migration on MSHV hypervisor](#live-migration-on-mshv-hypervisor)
- [AArch64 CPU topology support](#aarch64-cpu-topology-support) - [AArch64 CPU topology support](#aarch64-cpu-topology-support)
- [Power button support on AArch64](#power-button-support-on-aarch64) - [Power button support on AArch64](#power-button-support-on-aarch64)
- [Notable bug fixes](#notable-bug-fixes-13) - [Notable bug fixes](#notable-bug-fixes-15)
- [Contributors](#contributors-13) - [Contributors](#contributors-15)
- [v17.0](#v170) - [v17.0](#v170)
- [ARM64 NUMA support using ACPI](#arm64-numa-support-using-acpi) - [ARM64 NUMA support using ACPI](#arm64-numa-support-using-acpi)
- [`Seccomp` support for MSHV backend](#seccomp-support-for-mshv-backend) - [`Seccomp` support for MSHV backend](#seccomp-support-for-mshv-backend)
- [Hotplug of `macvtap` devices](#hotplug-of-macvtap-devices) - [Hotplug of `macvtap` devices](#hotplug-of-macvtap-devices)
- [Improved SGX support](#improved-sgx-support) - [Improved SGX support](#improved-sgx-support)
- [Inflight tracking for `vhost-user` devices](#inflight-tracking-for-vhost-user-devices) - [Inflight tracking for `vhost-user` devices](#inflight-tracking-for-vhost-user-devices)
- [Notable bug fixes](#notable-bug-fixes-14) - [Notable bug fixes](#notable-bug-fixes-16)
- [Contributors](#contributors-14) - [Contributors](#contributors-16)
- [v16.0](#v160) - [v16.0](#v160)
- [Improved live migration support](#improved-live-migration-support) - [Improved live migration support](#improved-live-migration-support)
- [Improved `vhost-user` support](#improved-vhost-user-support) - [Improved `vhost-user` support](#improved-vhost-user-support)
- [ARM64 ACPI and UEFI support](#arm64-acpi-and-uefi-support) - [ARM64 ACPI and UEFI support](#arm64-acpi-and-uefi-support)
- [Notable bug fixes](#notable-bug-fixes-15) - [Notable bug fixes](#notable-bug-fixes-17)
- [Removed functionality](#removed-functionality) - [Removed functionality](#removed-functionality)
- [Contributors](#contributors-15) - [Contributors](#contributors-17)
- [v15.0](#v150) - [v15.0](#v150)
- [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees) - [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees)
- [Network device rate limiting](#network-device-rate-limiting) - [Network device rate limiting](#network-device-rate-limiting)
@@ -148,7 +159,7 @@
- [`--api-socket` supports file descriptor parameter](#--api-socket-supports-file-descriptor-parameter) - [`--api-socket` supports file descriptor parameter](#--api-socket-supports-file-descriptor-parameter)
- [Bug fixes](#bug-fixes) - [Bug fixes](#bug-fixes)
- [Deprecations](#deprecations-5) - [Deprecations](#deprecations-5)
- [Contributors](#contributors-16) - [Contributors](#contributors-18)
- [v0.14.1](#v0141) - [v0.14.1](#v0141)
- [v0.14.0](#v0140) - [v0.14.0](#v0140)
- [Structured event monitoring](#structured-event-monitoring) - [Structured event monitoring](#structured-event-monitoring)
@@ -158,7 +169,7 @@
- [PTY control for serial and `virtio-console`](#pty-control-for-serial-and-virtio-console) - [PTY control for serial and `virtio-console`](#pty-control-for-serial-and-virtio-console)
- [Block device rate limiting](#block-device-rate-limiting) - [Block device rate limiting](#block-device-rate-limiting)
- [Deprecations](#deprecations-6) - [Deprecations](#deprecations-6)
- [Contributors](#contributors-17) - [Contributors](#contributors-19)
- [v0.13.0](#v0130) - [v0.13.0](#v0130)
- [Wider VFIO device support](#wider-vfio-device-support) - [Wider VFIO device support](#wider-vfio-device-support)
- [Improved huge page support](#improved-huge-page-support) - [Improved huge page support](#improved-huge-page-support)
@@ -166,13 +177,13 @@
- [VHD disk image support](#vhd-disk-image-support) - [VHD disk image support](#vhd-disk-image-support)
- [Improved Virtio device threading](#improved-virtio-device-threading) - [Improved Virtio device threading](#improved-virtio-device-threading)
- [Clean shutdown support via synthetic power button](#clean-shutdown-support-via-synthetic-power-button) - [Clean shutdown support via synthetic power button](#clean-shutdown-support-via-synthetic-power-button)
- [Contributors](#contributors-18) - [Contributors](#contributors-20)
- [v0.12.0](#v0120) - [v0.12.0](#v0120)
- [ARM64 enhancements](#arm64-enhancements) - [ARM64 enhancements](#arm64-enhancements)
- [Removal of `vhost-user-net` and `vhost-user-block` self spawning](#removal-of-vhost-user-net-and-vhost-user-block-self-spawning) - [Removal of `vhost-user-net` and `vhost-user-block` self spawning](#removal-of-vhost-user-net-and-vhost-user-block-self-spawning)
- [Migration of `vhost-user-fs` backend](#migration-of-vhost-user-fs-backend) - [Migration of `vhost-user-fs` backend](#migration-of-vhost-user-fs-backend)
- [Enhanced "info" API](#enhanced-info-api) - [Enhanced "info" API](#enhanced-info-api)
- [Contributors](#contributors-19) - [Contributors](#contributors-21)
- [v0.11.0](#v0110) - [v0.11.0](#v0110)
- [`io_uring` support by default for `virtio-block`](#io_uring-support-by-default-for-virtio-block) - [`io_uring` support by default for `virtio-block`](#io_uring-support-by-default-for-virtio-block)
- [Windows Guest Support](#windows-guest-support) - [Windows Guest Support](#windows-guest-support)
@@ -184,15 +195,15 @@
- [Default Log Level Changed](#default-log-level-changed) - [Default Log Level Changed](#default-log-level-changed)
- [New `--balloon` Parameter Added](#new---balloon-parameter-added) - [New `--balloon` Parameter Added](#new---balloon-parameter-added)
- [Experimental `virtio-watchdog` Support](#experimental-virtio-watchdog-support) - [Experimental `virtio-watchdog` Support](#experimental-virtio-watchdog-support)
- [Notable Bug Fixes](#notable-bug-fixes-16) - [Notable Bug Fixes](#notable-bug-fixes-18)
- [Contributors](#contributors-20) - [Contributors](#contributors-22)
- [v0.10.0](#v0100) - [v0.10.0](#v0100)
- [`virtio-block` Support for Multiple Descriptors](#virtio-block-support-for-multiple-descriptors) - [`virtio-block` Support for Multiple Descriptors](#virtio-block-support-for-multiple-descriptors)
- [Memory Zones](#memory-zones) - [Memory Zones](#memory-zones)
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements) - [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements)
- [Preliminary KVM HyperV Emulation Control](#preliminary-kvm-hyperv-emulation-control) - [Preliminary KVM HyperV Emulation Control](#preliminary-kvm-hyperv-emulation-control)
- [Notable Bug Fixes](#notable-bug-fixes-17) - [Notable Bug Fixes](#notable-bug-fixes-19)
- [Contributors](#contributors-21) - [Contributors](#contributors-23)
- [v0.9.0](#v090) - [v0.9.0](#v090)
- [`io_uring` Based Block Device Support](#io_uring-based-block-device-support) - [`io_uring` Based Block Device Support](#io_uring-based-block-device-support)
- [Block and Network Device Statistics](#block-and-network-device-statistics) - [Block and Network Device Statistics](#block-and-network-device-statistics)
@@ -205,17 +216,17 @@
- [Enhancements to ARM64 Support](#enhancements-to-arm64-support) - [Enhancements to ARM64 Support](#enhancements-to-arm64-support)
- [Intel SGX Support](#intel-sgx-support) - [Intel SGX Support](#intel-sgx-support)
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements-1) - [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements-1)
- [Notable Bug Fixes](#notable-bug-fixes-18) - [Notable Bug Fixes](#notable-bug-fixes-20)
- [Contributors](#contributors-22) - [Contributors](#contributors-24)
- [v0.8.0](#v080) - [v0.8.0](#v080)
- [Experimental Snapshot and Restore Support](#experimental-snapshot-and-restore-support) - [Experimental Snapshot and Restore Support](#experimental-snapshot-and-restore-support)
- [Experimental ARM64 Support](#experimental-arm64-support) - [Experimental ARM64 Support](#experimental-arm64-support)
- [Support for Using 5-level Paging in Guests](#support-for-using-5-level-paging-in-guests) - [Support for Using 5-level Paging in Guests](#support-for-using-5-level-paging-in-guests)
- [Virtio Device Interrupt Suppression for Network Devices](#virtio-device-interrupt-suppression-for-network-devices) - [Virtio Device Interrupt Suppression for Network Devices](#virtio-device-interrupt-suppression-for-network-devices)
- [`vhost_user_fs` Improvements](#vhost_user_fs-improvements) - [`vhost_user_fs` Improvements](#vhost_user_fs-improvements)
- [Notable Bug Fixes](#notable-bug-fixes-19) - [Notable Bug Fixes](#notable-bug-fixes-21)
- [Command Line and API Changes](#command-line-and-api-changes) - [Command Line and API Changes](#command-line-and-api-changes)
- [Contributors](#contributors-23) - [Contributors](#contributors-25)
- [v0.7.0](#v070) - [v0.7.0](#v070)
- [Block, Network, Persistent Memory (PMEM), VirtioFS and Vsock hotplug](#block-network-persistent-memory-pmem-virtiofs-and-vsock-hotplug) - [Block, Network, Persistent Memory (PMEM), VirtioFS and Vsock hotplug](#block-network-persistent-memory-pmem-virtiofs-and-vsock-hotplug)
- [Alternative `libc` Support](#alternative-libc-support) - [Alternative `libc` Support](#alternative-libc-support)
@@ -225,14 +236,14 @@
- [`Seccomp` Sandboxing](#seccomp-sandboxing) - [`Seccomp` Sandboxing](#seccomp-sandboxing)
- [Updated Distribution Support](#updated-distribution-support) - [Updated Distribution Support](#updated-distribution-support)
- [Command Line and API Changes](#command-line-and-api-changes-1) - [Command Line and API Changes](#command-line-and-api-changes-1)
- [Contributors](#contributors-24) - [Contributors](#contributors-26)
- [v0.6.0](#v060) - [v0.6.0](#v060)
- [Directly Assigned Devices Hotplug](#directly-assigned-devices-hotplug) - [Directly Assigned Devices Hotplug](#directly-assigned-devices-hotplug)
- [Shared Filesystem Improvements](#shared-filesystem-improvements) - [Shared Filesystem Improvements](#shared-filesystem-improvements)
- [Block and Networking IO Self Offloading](#block-and-networking-io-self-offloading) - [Block and Networking IO Self Offloading](#block-and-networking-io-self-offloading)
- [Command Line Interface](#command-line-interface) - [Command Line Interface](#command-line-interface)
- [PVH Boot](#pvh-boot) - [PVH Boot](#pvh-boot)
- [Contributors](#contributors-25) - [Contributors](#contributors-27)
- [v0.5.1](#v051) - [v0.5.1](#v051)
- [v0.5.0](#v050) - [v0.5.0](#v050)
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing) - [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
@@ -240,7 +251,7 @@
- [New Interrupt Management Framework](#new-interrupt-management-framework) - [New Interrupt Management Framework](#new-interrupt-management-framework)
- [Development Tools](#development-tools) - [Development Tools](#development-tools)
- [Kata Containers Integration](#kata-containers-integration) - [Kata Containers Integration](#kata-containers-integration)
- [Contributors](#contributors-26) - [Contributors](#contributors-28)
- [v0.4.0](#v040) - [v0.4.0](#v040)
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition) - [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation) - [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
@@ -249,7 +260,7 @@
- [Userspace IOAPIC by default](#userspace-ioapic-by-default) - [Userspace IOAPIC by default](#userspace-ioapic-by-default)
- [PCI BAR reprogramming](#pci-bar-reprogramming) - [PCI BAR reprogramming](#pci-bar-reprogramming)
- [New `cloud-hypervisor` organization](#new-cloud-hypervisor-organization) - [New `cloud-hypervisor` organization](#new-cloud-hypervisor-organization)
- [Contributors](#contributors-27) - [Contributors](#contributors-29)
- [v0.3.0](#v030) - [v0.3.0](#v030)
- [Block device offloading](#block-device-offloading) - [Block device offloading](#block-device-offloading)
- [Network device backend](#network-device-backend) - [Network device backend](#network-device-backend)
@@ -276,6 +287,103 @@
- [Unit testing](#unit-testing) - [Unit testing](#unit-testing)
- [Integration tests parallelization](#integration-tests-parallelization) - [Integration tests parallelization](#integration-tests-parallelization)
# v33.0
This release has been tracked in our [roadmap
project](https://github.com/orgs/cloud-hypervisor/projects/6) as iteration
v33.0. The following user visible changes have been made:
### D-Bus based API
A D-Bus based API has been added as an alternative to the existing REST
API. This feature is gated by the `dbus_api` feature. Details can be
found in the [API documentation](docs/api.md).
### Expose Host CPU Cache Details for AArch64
Now the CPU cache information on the host is properly exposed to the
guest on AArch64.
### Notable Bug Fixes
* Report errors explicitly to users when VM failed to boot (#5453)
* Fix VFIO on platforms with non-4k page size (#5450, #5469)
* Fix TDX initialization (#5454)
* Ensure all guest memory regions are page-size aligned (#5496)
* Fix seccomp filter lists related to virtio-console, serial and pty
(#5506, #5524)
* Populate APIC ID properly (#5512)
* Ignore and warn TAP FDs in more situations (#5522)
### Contributors
Many thanks to everyone who has contributed to our release:
* Alyssa Ross <hi@alyssa.is>
* Anatol Belski <anbelski@linux.microsoft.com>
* Bo Chen <chen.bo@intel.com>
* Jianyong Wu <jianyong.wu@arm.com>
* Omer Faruk Bayram <omer.faruk@sartura.hr>
* Rafael Mendonca <rafaelmendsr@gmail.com>
* Ravi kumar Veeramally <ravikumar.veeramally@intel.com>
* Rob Bradford <rbradford@rivosinc.com>
* Ruslan Mstoi <ruslan.mstoi@intel.com>
* Yu Li <liyu.yukiteru@bytedance.com>
* zhongbingnan <zhongbingnan@bytedance.com>
# v32.0
This release has been tracked in our [roadmap
project](https://github.com/orgs/cloud-hypervisor/projects/6) as iteration
v32.0. The following user visible changes have been made:
### Increased PCI Segment Limit
The maximum number of PCI segments that can be used is now 96 (up from 16).
### API Changes
* The VmmPingResponse now includes the PID as well as the build details.
(#5348)
### Notable Bug Fixes
* Ignore and warn TAP FDs sent via the HTTP request body (#5350)
* Properly preserve and close valid FDs for TAP devices (#5373)
* Only use `KVM_ARM_VCPU_PMU_V3` if available (#5360)
* Only touch the tty flags if it's being used (#5343)
* Fix seccomp filter lists for vhost-user devices (#5361)
* The number of vCPUs is capped at the hypervisor maximum (#5357)
* Fixes for TTY reset (#5414)
* CPU topology fixes on MSHV (#5325)
* Seccomp fixes for older distributions (#5397)
### Contributors
Many thanks to everyone who has contributed to our release:
* Alyssa Ross <hi@alyssa.is>
* Anatol Belski <anbelski@linux.microsoft.com>
* Bo Chen <chen.bo@intel.com>
* Hao Xu <howeyxu@tencent.com>
* Muminul Islam <muislam@microsoft.com>
* Omer Faruk Bayram <omer.faruk@sartura.hr>
* Rafael Mendonca <rafaelmendsr@gmail.com>
* Rob Bradford <rbradford@rivosinc.com>
* Ruslan Mstoi <ruslan.mstoi@intel.com>
* Smit Gardhariya <gardhariya.smit@gmail.com>
* Wei Liu <liuwe@microsoft.com>
# v31.1
This is a bug fix release. The following issues have been addressed:
* Ignore and warn TAP FDs sent via the HTTP request body (#5350)
* Properly preserve and close valid FDs for TAP devices (#5373)
* Only use `KVM_ARM_VCPU_PMU_V3` if available (#5360)
* Only touch the tty flags if it's being used (#5343)
* Fix seccomp filter lists for vhost-user devices (#5361)
# v31.0 # v31.0
This release has been tracked in our [roadmap This release has been tracked in our [roadmap

View File

@@ -15,58 +15,67 @@ ARG CARGO_GIT_REGISTRY_DIR="$CLH_BUILD_DIR/cargo_git_registry"
ENV CARGO_HOME=/usr/local/rust ENV CARGO_HOME=/usr/local/rust
ENV RUSTUP_HOME=$CARGO_HOME ENV RUSTUP_HOME=$CARGO_HOME
ENV PATH="$PATH:$CARGO_HOME/bin" ENV PATH="$PATH:$CARGO_HOME/bin"
ENV DEBIAN_FRONTEND=noninteractive
# Install all CI dependencies # Install all CI dependencies
# DL3015 ignored cause not installing openvswitch-switch-dpdk recommended packages breaks ovs_dpdk test
# hadolint ignore=DL3008,DL3015
RUN apt-get update \ RUN apt-get update \
&& apt-get -yq upgrade \ && apt-get -yq upgrade \
&& DEBIAN_FRONTEND=noninteractive apt-get install -yq \ && apt-get install --no-install-recommends -yq \
build-essential \ build-essential \
bc \ bc \
curl \ curl \
wget \ wget \
sudo \ sudo \
mtools \ mtools \
musl-tools \ musl-tools \
libssl-dev \ libssl-dev \
pkg-config \ pkg-config \
flex \ flex \
bison \ bison \
libelf-dev \ libelf-dev \
qemu-utils \ qemu-utils \
libglib2.0-dev \ libglib2.0-dev \
libpixman-1-dev \ libpixman-1-dev \
libseccomp-dev \ libseccomp-dev \
libcap-ng-dev \ libcap-ng-dev \
socat \ socat \
dosfstools \ dosfstools \
cpio \ cpio \
python \ python \
python3 \ python3 \
python3-setuptools \ python3-setuptools \
ntfs-3g \ ntfs-3g \
openvswitch-switch-dpdk \ python3-distutils \
python3-distutils \ uuid-dev \
uuid-dev \ iperf3 \
iperf3 \ zip \
zip \ git-core \
git-core \ dnsmasq \
dnsmasq \ dmsetup \
dmsetup \ ca-certificates \
unzip \
iproute2 \
dbus \
&& apt-get install openvswitch-switch-dpdk -yq \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/* /var/log/*log /var/log/apt/* /var/lib/dpkg/*-old /var/cache/debconf/*-old
RUN update-alternatives --set ovs-vswitchd /usr/lib/openvswitch-switch-dpdk/ovs-vswitchd-dpdk RUN update-alternatives --set ovs-vswitchd /usr/lib/openvswitch-switch-dpdk/ovs-vswitchd-dpdk
# hadolint ignore=DL3008
RUN if [ "$TARGETARCH" = "amd64" ]; then \ RUN if [ "$TARGETARCH" = "amd64" ]; then \
apt-get update \ apt-get update \
&& apt-get -yq upgrade \ && apt-get -yq upgrade \
&& DEBIAN_FRONTEND=noninteractive apt-get install -yq gcc-multilib gawk \ && apt-get install --no-install-recommends -yq gcc-multilib gawk \
libtool expect gnutls-dev gnutls-bin libfuse-dev \ libtool expect gnutls-dev gnutls-bin libfuse-dev \
libjson-glib-dev libgmp-dev libtasn1-dev python3-twisted \ libjson-glib-dev libgmp-dev libtasn1-dev python3-twisted \
net-tools softhsm2 \ net-tools softhsm2 \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/*; fi && rm -rf /var/lib/apt/lists/* /var/log/*log /var/log/apt/* /var/lib/dpkg/*-old /var/cache/debconf/*-old; fi
# hadolint ignore=DL3008
RUN if [ "$TARGETARCH" = "arm64" ]; then \ RUN if [ "$TARGETARCH" = "arm64" ]; then \
# On AArch64, `setcap` binary should be installed via `libcap2-bin`. # On AArch64, `setcap` binary should be installed via `libcap2-bin`.
# The `setcap` binary is used in integration tests. # The `setcap` binary is used in integration tests.
@@ -74,7 +83,7 @@ RUN if [ "$TARGETARCH" = "arm64" ]; then \
# kernel (any version) image in `/boot` and modules in `/lib/modules`. # kernel (any version) image in `/boot` and modules in `/lib/modules`.
apt-get update \ apt-get update \
&& apt-get -yq upgrade \ && apt-get -yq upgrade \
&& DEBIAN_FRONTEND=noninteractive apt-get install -yq \ && apt-get install --no-install-recommends -yq \
libcap2-bin \ libcap2-bin \
libguestfs-tools \ libguestfs-tools \
linux-image-generic \ linux-image-generic \
@@ -84,11 +93,12 @@ RUN if [ "$TARGETARCH" = "arm64" ]; then \
perl \ perl \
texinfo \ texinfo \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/*; fi && rm -rf /var/lib/apt/lists/* /var/log/*log /var/log/apt/* /var/lib/dpkg/*-old /var/cache/debconf/*-old; fi
# Fix the libssl-dev install # Fix the libssl-dev install
# hadolint ignore=SC2155
RUN export ARCH="$(uname -m)" \ RUN export ARCH="$(uname -m)" \
&& cp /usr/include/$ARCH-linux-gnu/openssl/opensslconf.h /usr/include/openssl/ && cp /usr/include/"$ARCH"-linux-gnu/openssl/opensslconf.h /usr/include/openssl/
ENV X86_64_UNKNOWN_LINUX_GNU_OPENSSL_LIB_DIR=/usr/lib/x86_64-linux-gnu/ ENV X86_64_UNKNOWN_LINUX_GNU_OPENSSL_LIB_DIR=/usr/lib/x86_64-linux-gnu/
ENV X86_64_UNKNOWN_LINUX_MUSL_OPENSSL_LIB_DIR=/usr/lib/x86_64-linux-gnu/ ENV X86_64_UNKNOWN_LINUX_MUSL_OPENSSL_LIB_DIR=/usr/lib/x86_64-linux-gnu/
ENV AARCH64_UNKNOWN_LINUX_GNU_OPENSSL_LIB_DIR=/usr/lib/aarch64-linux-gnu/ ENV AARCH64_UNKNOWN_LINUX_GNU_OPENSSL_LIB_DIR=/usr/lib/aarch64-linux-gnu/
@@ -96,9 +106,10 @@ ENV AARCH64_UNKNOWN_LINUX_MUSL_OPENSSL_LIB_DIR=/usr/lib/aarch64-linux-gnu/
ENV OPENSSL_INCLUDE_DIR=/usr/include/ ENV OPENSSL_INCLUDE_DIR=/usr/include/
# Install the rust toolchain # Install the rust toolchain
# hadolint ignore=DL4006,SC2155
RUN export ARCH="$(uname -m)" \ RUN export ARCH="$(uname -m)" \
&& nohup curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain "$RUST_TOOLCHAIN" \ && nohup curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain "$RUST_TOOLCHAIN" \
&& rustup target add $ARCH-unknown-linux-musl --toolchain "$RUST_TOOLCHAIN" \ && rustup target add "$ARCH"-unknown-linux-musl --toolchain "$RUST_TOOLCHAIN" \
&& if [ "$TARGETARCH" = "amd64" ]; then rustup toolchain add --profile minimal $RUST_TOOLCHAIN-x86_64-unknown-linux-musl; fi \ && if [ "$TARGETARCH" = "amd64" ]; then rustup toolchain add --profile minimal $RUST_TOOLCHAIN-x86_64-unknown-linux-musl; fi \
&& if [ "$TARGETARCH" = "amd64" ]; then rustup component add rustfmt; fi \ && if [ "$TARGETARCH" = "amd64" ]; then rustup component add rustfmt; fi \
&& if [ "$TARGETARCH" = "amd64" ]; then rustup component add clippy; fi \ && if [ "$TARGETARCH" = "amd64" ]; then rustup component add clippy; fi \
@@ -108,12 +119,14 @@ RUN export ARCH="$(uname -m)" \
&& ln -s "$CARGO_GIT_REGISTRY_DIR" "$CARGO_HOME/git" && ln -s "$CARGO_GIT_REGISTRY_DIR" "$CARGO_HOME/git"
# Set the rust environment # Set the rust environment
RUN echo 'source $CARGO_HOME/env' >> $HOME/.bashrc \ # hadolint ignore=SC2016
&& mkdir $HOME/.cargo \ RUN echo 'source $CARGO_HOME/env' >> "$HOME"/.bashrc \
&& ln -s $CARGO_HOME/env $HOME/.cargo/env && mkdir "$HOME"/.cargo \
&& ln -s $CARGO_HOME/env "$HOME"/.cargo/env
# install SPDK NVMe # install SPDK NVMe
# only for 'x86_64' platform images as 'docker buildx' can't build 'spdk' # only for 'x86_64' platform images as 'docker buildx' can't build 'spdk'
# hadolint ignore=DL3003,SC2046
RUN if [ "$TARGETARCH" = "amd64" ]; then \ RUN if [ "$TARGETARCH" = "amd64" ]; then \
git clone https://github.com/spdk/spdk \ git clone https://github.com/spdk/spdk \
&& cd spdk \ && cd spdk \
@@ -123,7 +136,7 @@ RUN if [ "$TARGETARCH" = "amd64" ]; then \
&& ./scripts/pkgdep.sh \ && ./scripts/pkgdep.sh \
&& apt-get clean \ && apt-get clean \
&& ./configure --with-vfio-user \ && ./configure --with-vfio-user \
&& make -j `nproc` \ && make -j $(nproc) \
&& mkdir /usr/local/bin/spdk-nvme \ && mkdir /usr/local/bin/spdk-nvme \
&& cp ./build/bin/nvmf_tgt /usr/local/bin/spdk-nvme \ && cp ./build/bin/nvmf_tgt /usr/local/bin/spdk-nvme \
&& cp ./scripts/rpc.py /usr/local/bin/spdk-nvme \ && cp ./scripts/rpc.py /usr/local/bin/spdk-nvme \
@@ -131,6 +144,7 @@ RUN if [ "$TARGETARCH" = "amd64" ]; then \
&& cd .. && rm -rf spdk; fi && cd .. && rm -rf spdk; fi
# install swtpm only for x86_64 arch # install swtpm only for x86_64 arch
# hadolint ignore=DL3003
RUN if [ "$TARGETARCH" = "amd64" ]; then \ RUN if [ "$TARGETARCH" = "amd64" ]; then \
git clone https://github.com/stefanberger/libtpms libtpms_build \ git clone https://github.com/stefanberger/libtpms libtpms_build \
&& cd libtpms_build \ && cd libtpms_build \
@@ -151,7 +165,7 @@ RUN if [ "$TARGETARCH" = "amd64" ]; then \
# install ethr tool for performance tests # install ethr tool for performance tests
RUN if [ "$TARGETARCH" = "amd64" ]; then \ RUN if [ "$TARGETARCH" = "amd64" ]; then \
wget https://github.com/microsoft/ethr/releases/latest/download/ethr_linux.zip \ wget -nv https://github.com/microsoft/ethr/releases/latest/download/ethr_linux.zip \
&& unzip ethr_linux.zip \ && unzip ethr_linux.zip \
&& cp ethr /usr/local/bin \ && cp ethr /usr/local/bin \
&& rm ethr_linux.zip; fi && rm ethr_linux.zip; fi

View File

@@ -3,7 +3,7 @@ set -x
rm -f /tmp/ubuntu-cloudinit.img rm -f /tmp/ubuntu-cloudinit.img
mkdosfs -n CIDATA -C /tmp/ubuntu-cloudinit.img 8192 mkdosfs -n CIDATA -C /tmp/ubuntu-cloudinit.img 8192
mcopy -oi /tmp/ubuntu-cloudinit.img -s test_data/cloud-init/ubuntu/user-data :: mcopy -oi /tmp/ubuntu-cloudinit.img -s test_data/cloud-init/ubuntu/local/user-data ::
mcopy -oi /tmp/ubuntu-cloudinit.img -s test_data/cloud-init/ubuntu/meta-data :: mcopy -oi /tmp/ubuntu-cloudinit.img -s test_data/cloud-init/ubuntu/local/meta-data ::
mcopy -oi /tmp/ubuntu-cloudinit.img -s test_data/cloud-init/ubuntu/network-config :: mcopy -oi /tmp/ubuntu-cloudinit.img -s test_data/cloud-init/ubuntu/local/network-config ::

View File

@@ -7,7 +7,7 @@
CLI_NAME="Cloud Hypervisor" CLI_NAME="Cloud Hypervisor"
CTR_IMAGE_TAG="ghcr.io/cloud-hypervisor/cloud-hypervisor" CTR_IMAGE_TAG="ghcr.io/cloud-hypervisor/cloud-hypervisor"
CTR_IMAGE_VERSION="20230316-0" CTR_IMAGE_VERSION="20230620-0"
CTR_IMAGE="${CTR_IMAGE_TAG}:${CTR_IMAGE_VERSION}" CTR_IMAGE="${CTR_IMAGE_TAG}:${CTR_IMAGE_VERSION}"
DOCKER_RUNTIME="docker" DOCKER_RUNTIME="docker"
@@ -426,7 +426,7 @@ cmd_tests() {
--env USER="root" \ --env USER="root" \
--env CH_LIBC="${libc}" \ --env CH_LIBC="${libc}" \
"$CTR_IMAGE" \ "$CTR_IMAGE" \
./scripts/run_integration_tests_"$(uname -m)".sh "$@" || fix_dir_perms $? || exit $? dbus-run-session ./scripts/run_integration_tests_"$(uname -m)".sh "$@" || fix_dir_perms $? || exit $?
fi fi
if [ "$integration_sgx" = true ]; then if [ "$integration_sgx" = true ]; then
@@ -539,6 +539,7 @@ cmd_tests() {
--volume "$CLH_INTEGRATION_WORKLOADS:$CTR_CLH_INTEGRATION_WORKLOADS" \ --volume "$CLH_INTEGRATION_WORKLOADS:$CTR_CLH_INTEGRATION_WORKLOADS" \
--env USER="root" \ --env USER="root" \
--env CH_LIBC="${libc}" \ --env CH_LIBC="${libc}" \
--env RUST_BACKTRACE="${RUST_BACKTRACE}" \
"$CTR_IMAGE" \ "$CTR_IMAGE" \
./scripts/run_metrics.sh "$@" || fix_dir_perms $? || exit $? ./scripts/run_metrics.sh "$@" || fix_dir_perms $? || exit $?
fi fi

View File

@@ -225,8 +225,8 @@ fi
BUILD_TARGET="aarch64-unknown-linux-${CH_LIBC}" BUILD_TARGET="aarch64-unknown-linux-${CH_LIBC}"
if [[ "${BUILD_TARGET}" == "aarch64-unknown-linux-musl" ]]; then if [[ "${BUILD_TARGET}" == "aarch64-unknown-linux-musl" ]]; then
export TARGET_CC="musl-gcc" export TARGET_CC="musl-gcc"
export RUSTFLAGS="-C link-arg=-lgcc -C link_arg=-specs -C link_arg=/usr/lib/aarch64-linux-musl/musl-gcc.specs" export RUSTFLAGS="-C link-arg=-lgcc -C link_arg=-specs -C link_arg=/usr/lib/aarch64-linux-musl/musl-gcc.specs"
fi fi
export RUST_BACKTRACE=1 export RUST_BACKTRACE=1
@@ -281,4 +281,12 @@ else
exit $RES exit $RES
fi fi
# Run tests on dbus_api
if [ $RES -eq 0 ]; then
cargo build --features "dbus_api" --all --release --target $BUILD_TARGET
export RUST_BACKTRACE=1
time cargo test "dbus_api::$test_filter" --target $BUILD_TARGET -- ${test_binary_args[*]}
RES=$?
fi
exit $RES exit $RES

View File

@@ -42,8 +42,8 @@ BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
CFLAGS="" CFLAGS=""
TARGET_CC="" TARGET_CC=""
if [[ "${BUILD_TARGET}" == "x86_64-unknown-linux-musl" ]]; then if [[ "${BUILD_TARGET}" == "x86_64-unknown-linux-musl" ]]; then
TARGET_CC="musl-gcc" TARGET_CC="musl-gcc"
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/" CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
fi fi
cargo build --no-default-features --features "kvm,mshv" --all --release --target $BUILD_TARGET cargo build --no-default-features --features "kvm,mshv" --all --release --target $BUILD_TARGET

View File

@@ -7,9 +7,42 @@ source $(dirname "$0")/test-util.sh
process_common_args "$@" process_common_args "$@"
WORKLOADS_DIR="$HOME/workloads" WORKLOADS_DIR="$HOME/workloads"
FOCAL_OS_IMAGE_NAME="focal-server-cloudimg-amd64-custom-20210609-0.raw" mkdir -p "$WORKLOADS_DIR"
FOCAL_OS_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_IMAGE_NAME"
cp scripts/sha1sums-x86_64 $WORKLOADS_DIR
FW_URL=$(curl --silent https://api.github.com/repos/cloud-hypervisor/rust-hypervisor-firmware/releases/latest | grep "browser_download_url" | grep -o 'https://.*[^ "]')
FW="$WORKLOADS_DIR/hypervisor-fw" FW="$WORKLOADS_DIR/hypervisor-fw"
if [ ! -f "$FW" ]; then
pushd $WORKLOADS_DIR
time wget --quiet $FW_URL || exit 1
popd
fi
FOCAL_OS_IMAGE_NAME="focal-server-cloudimg-amd64-custom-20210609-0.qcow2"
FOCAL_OS_IMAGE_URL="https://cloud-hypervisor.azureedge.net/$FOCAL_OS_IMAGE_NAME"
FOCAL_OS_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_IMAGE_NAME"
if [ ! -f "$FOCAL_OS_IMAGE" ]; then
pushd $WORKLOADS_DIR
time wget --quiet $FOCAL_OS_IMAGE_URL || exit 1
popd
fi
FOCAL_OS_RAW_IMAGE_NAME="focal-server-cloudimg-amd64-custom-20210609-0.raw"
FOCAL_OS_RAW_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_RAW_IMAGE_NAME"
if [ ! -f "$FOCAL_OS_RAW_IMAGE" ]; then
pushd $WORKLOADS_DIR
time qemu-img convert -p -f qcow2 -O raw $FOCAL_OS_IMAGE_NAME $FOCAL_OS_RAW_IMAGE_NAME || exit 1
popd
fi
pushd $WORKLOADS_DIR
sha1sum sha1sums-x86_64 --check --ignore-missing
if [ $? -ne 0 ]; then
echo "sha1sum validation of images failed, remove invalid images to fix the issue."
exit 1
fi
popd
VMLINUX_IMAGE="$WORKLOADS_DIR/vmlinux" VMLINUX_IMAGE="$WORKLOADS_DIR/vmlinux"
build_custom_linux build_custom_linux
@@ -31,7 +64,7 @@ VFIO_DIR="$WORKLOADS_DIR/vfio"
VFIO_DISK_IMAGE="$WORKLOADS_DIR/vfio.img" VFIO_DISK_IMAGE="$WORKLOADS_DIR/vfio.img"
rm -rf $VFIO_DIR $VFIO_DISK_IMAGE rm -rf $VFIO_DIR $VFIO_DISK_IMAGE
mkdir -p $VFIO_DIR mkdir -p $VFIO_DIR
cp $FOCAL_OS_IMAGE $VFIO_DIR cp $FOCAL_OS_RAW_IMAGE $VFIO_DIR
cp $FW $VFIO_DIR cp $FW $VFIO_DIR
cp $VMLINUX_IMAGE $VFIO_DIR || exit 1 cp $VMLINUX_IMAGE $VFIO_DIR || exit 1

View File

@@ -24,8 +24,8 @@ BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
CFLAGS="" CFLAGS=""
TARGET_CC="" TARGET_CC=""
if [[ "${BUILD_TARGET}" == "aarch64-unknown-linux-musl" ]]; then if [[ "${BUILD_TARGET}" == "aarch64-unknown-linux-musl" ]]; then
export TARGET_CC="musl-gcc" export TARGET_CC="musl-gcc"
export RUSTFLAGS="-C link-arg=-lgcc -C link_arg=-specs -C link_arg=/usr/lib/aarch64-linux-musl/musl-gcc.specs" export RUSTFLAGS="-C link-arg=-lgcc -C link_arg=-specs -C link_arg=/usr/lib/aarch64-linux-musl/musl-gcc.specs"
fi fi
# Check if the images are present # Check if the images are present

View File

@@ -26,8 +26,8 @@ BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
CFLAGS="" CFLAGS=""
TARGET_CC="" TARGET_CC=""
if [[ "${BUILD_TARGET}" == "x86_64-unknown-linux-musl" ]]; then if [[ "${BUILD_TARGET}" == "x86_64-unknown-linux-musl" ]]; then
TARGET_CC="musl-gcc" TARGET_CC="musl-gcc"
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/" CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
fi fi
# Check if the images are present # Check if the images are present

View File

@@ -189,4 +189,13 @@ if [ $RES -eq 0 ]; then
RES=$? RES=$?
fi fi
# Run tests on dbus_api
if [ $RES -eq 0 ]; then
cargo build --no-default-features --features "kvm,mshv,dbus_api" --all --release --target $BUILD_TARGET
export RUST_BACKTRACE=1
# integration tests now do not reply on build feature "dbus_api"
time cargo test $test_features "dbus_api::$test_filter" -- ${test_binary_args[*]}
RES=$?
fi
exit $RES exit $RES

View File

@@ -107,7 +107,12 @@ fi
# Ensure that git commands can be run in this directory (for metrics report) # Ensure that git commands can be run in this directory (for metrics report)
git config --global --add safe.directory $PWD git config --global --add safe.directory $PWD
export RUST_BACKTRACE=1 RUST_BACKTRACE_VALUE=`echo $RUST_BACKTRACE`
if [ -z $RUST_BACKTRACE_VALUE ];then
export RUST_BACKTRACE=1
else
echo "RUST_BACKTRACE is set to: $RUST_BACKTRACE_VALUE"
fi
time target/$BUILD_TARGET/release/performance-metrics ${test_binary_args[*]} time target/$BUILD_TARGET/release/performance-metrics ${test_binary_args[*]}
RES=$? RES=$?

View File

@@ -3,4 +3,3 @@ f1eccdc5e1b515dbad294426ab081b47ebfb97c0 focal-server-cloudimg-amd64-custom-2021
7f5a8358243a96adf61f5c20139b29f308f2c0e3 focal-server-cloudimg-amd64-custom-20210609-0.raw 7f5a8358243a96adf61f5c20139b29f308f2c0e3 focal-server-cloudimg-amd64-custom-20210609-0.raw
864c074e2f1bd753667a35188b510d83a1d62793 jammy-server-cloudimg-amd64-custom-20230119-0.qcow2 864c074e2f1bd753667a35188b510d83a1d62793 jammy-server-cloudimg-amd64-custom-20230119-0.qcow2
24358ee053f94e7f710ea4ce9e7a63eff2a1eb25 jammy-server-cloudimg-amd64-custom-20230119-0.raw 24358ee053f94e7f710ea4ce9e7a63eff2a1eb25 jammy-server-cloudimg-amd64-custom-20230119-0.raw

View File

@@ -11,13 +11,19 @@ use argh::FromArgs;
use option_parser::{ByteSized, ByteSizedParseError}; use option_parser::{ByteSized, ByteSizedParseError};
use std::fmt; use std::fmt;
use std::io::Read; use std::io::Read;
use std::marker::PhantomData;
use std::os::unix::net::UnixStream; use std::os::unix::net::UnixStream;
use std::process; use std::process;
#[cfg(feature = "dbus_api")]
use zbus::{dbus_proxy, zvariant::Optional};
type ApiResult = Result<(), Error>;
#[derive(Debug)] #[derive(Debug)]
enum Error { enum Error {
Connect(std::io::Error), HttpApiClient(ApiClientError),
ApiClient(ApiClientError), #[cfg(feature = "dbus_api")]
DBusApiClient(zbus::Error),
InvalidMemorySize(ByteSizedParseError), InvalidMemorySize(ByteSizedParseError),
InvalidBalloonSize(ByteSizedParseError), InvalidBalloonSize(ByteSizedParseError),
AddDeviceConfig(vmm::config::Error), AddDeviceConfig(vmm::config::Error),
@@ -37,8 +43,9 @@ impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*; use Error::*;
match self { match self {
ApiClient(e) => e.fmt(f), HttpApiClient(e) => e.fmt(f),
Connect(e) => write!(f, "Error opening HTTP socket: {e}"), #[cfg(feature = "dbus_api")]
DBusApiClient(e) => write!(f, "Error D-Bus proxy: {e}"),
InvalidMemorySize(e) => write!(f, "Error parsing memory size: {e:?}"), InvalidMemorySize(e) => write!(f, "Error parsing memory size: {e:?}"),
InvalidBalloonSize(e) => write!(f, "Error parsing balloon size: {e:?}"), InvalidBalloonSize(e) => write!(f, "Error parsing balloon size: {e:?}"),
AddDeviceConfig(e) => write!(f, "Error parsing device syntax: {e}"), AddDeviceConfig(e) => write!(f, "Error parsing device syntax: {e}"),
@@ -56,12 +63,430 @@ impl fmt::Display for Error {
} }
} }
fn resize_api_command( enum TargetApi<'a> {
socket: &mut UnixStream, HttpApi(UnixStream, PhantomData<&'a ()>),
#[cfg(feature = "dbus_api")]
DBusApi(DBusApi1ProxyBlocking<'a>),
}
#[cfg(feature = "dbus_api")]
#[dbus_proxy(name = "org.cloudhypervisor.DBusApi1", assume_defaults = false)]
trait DBusApi1 {
fn vmm_ping(&self) -> zbus::Result<String>;
fn vmm_shutdown(&self) -> zbus::Result<()>;
fn vm_add_device(&self, device_config: &str) -> zbus::Result<Optional<String>>;
fn vm_add_disk(&self, disk_config: &str) -> zbus::Result<Optional<String>>;
fn vm_add_fs(&self, fs_config: &str) -> zbus::Result<Optional<String>>;
fn vm_add_net(&self, net_config: &str) -> zbus::Result<Optional<String>>;
fn vm_add_pmem(&self, pmem_config: &str) -> zbus::Result<Optional<String>>;
fn vm_add_user_device(&self, vm_add_user_device: &str) -> zbus::Result<Optional<String>>;
fn vm_add_vdpa(&self, vdpa_config: &str) -> zbus::Result<Optional<String>>;
fn vm_add_vsock(&self, vsock_config: &str) -> zbus::Result<Optional<String>>;
fn vm_boot(&self) -> zbus::Result<()>;
fn vm_coredump(&self, vm_coredump_data: &str) -> zbus::Result<()>;
fn vm_counters(&self) -> zbus::Result<Optional<String>>;
fn vm_create(&self, vm_config: &str) -> zbus::Result<()>;
fn vm_delete(&self) -> zbus::Result<()>;
fn vm_info(&self) -> zbus::Result<String>;
fn vm_pause(&self) -> zbus::Result<()>;
fn vm_power_button(&self) -> zbus::Result<()>;
fn vm_reboot(&self) -> zbus::Result<()>;
fn vm_remove_device(&self, vm_remove_device: &str) -> zbus::Result<()>;
fn vm_resize(&self, vm_resize: &str) -> zbus::Result<()>;
fn vm_resize_zone(&self, vm_resize_zone: &str) -> zbus::Result<()>;
fn vm_restore(&self, restore_config: &str) -> zbus::Result<()>;
fn vm_receive_migration(&self, receive_migration_data: &str) -> zbus::Result<()>;
fn vm_send_migration(&self, receive_migration_data: &str) -> zbus::Result<()>;
fn vm_resume(&self) -> zbus::Result<()>;
fn vm_shutdown(&self) -> zbus::Result<()>;
fn vm_snapshot(&self, vm_snapshot_config: &str) -> zbus::Result<()>;
}
#[cfg(feature = "dbus_api")]
impl<'a> DBusApi1ProxyBlocking<'a> {
fn new_connection(name: &'a str, path: &'a str, system_bus: bool) -> Result<Self, zbus::Error> {
let connection = if system_bus {
zbus::blocking::Connection::system()?
} else {
zbus::blocking::Connection::session()?
};
Self::builder(&connection)
.destination(name)?
.path(path)?
.build()
}
fn print_response(&self, result: zbus::Result<Optional<String>>) -> ApiResult {
result
.map(|ret| {
if let Some(ref output) = *ret {
println!("{output}");
}
})
.map_err(Error::DBusApiClient)
}
fn api_vmm_ping(&self) -> ApiResult {
self.vmm_ping()
.map(|ping| println!("{ping}"))
.map_err(Error::DBusApiClient)
}
fn api_vmm_shutdown(&self) -> ApiResult {
self.vmm_shutdown().map_err(Error::DBusApiClient)
}
fn api_vm_add_device(&self, device_config: &str) -> ApiResult {
self.print_response(self.vm_add_device(device_config))
}
fn api_vm_add_disk(&self, disk_config: &str) -> ApiResult {
self.print_response(self.vm_add_disk(disk_config))
}
fn api_vm_add_fs(&self, fs_config: &str) -> ApiResult {
self.print_response(self.vm_add_fs(fs_config))
}
fn api_vm_add_net(&self, net_config: &str) -> ApiResult {
self.print_response(self.vm_add_net(net_config))
}
fn api_vm_add_pmem(&self, pmem_config: &str) -> ApiResult {
self.print_response(self.vm_add_pmem(pmem_config))
}
fn api_vm_add_user_device(&self, vm_add_user_device: &str) -> ApiResult {
self.print_response(self.vm_add_user_device(vm_add_user_device))
}
fn api_vm_add_vdpa(&self, vdpa_config: &str) -> ApiResult {
self.print_response(self.vm_add_vdpa(vdpa_config))
}
fn api_vm_add_vsock(&self, vsock_config: &str) -> ApiResult {
self.print_response(self.vm_add_vsock(vsock_config))
}
fn api_vm_boot(&self) -> ApiResult {
self.vm_boot().map_err(Error::DBusApiClient)
}
fn api_vm_coredump(&self, vm_coredump_data: &str) -> ApiResult {
self.vm_coredump(vm_coredump_data)
.map_err(Error::DBusApiClient)
}
fn api_vm_counters(&self) -> ApiResult {
self.print_response(self.vm_counters())
}
fn api_vm_create(&self, vm_config: &str) -> ApiResult {
self.vm_create(vm_config).map_err(Error::DBusApiClient)
}
fn api_vm_delete(&self) -> ApiResult {
self.vm_delete().map_err(Error::DBusApiClient)
}
fn api_vm_info(&self) -> ApiResult {
self.vm_info()
.map(|info| println!("{info}"))
.map_err(Error::DBusApiClient)
}
fn api_vm_pause(&self) -> ApiResult {
self.vm_pause().map_err(Error::DBusApiClient)
}
fn api_vm_power_button(&self) -> ApiResult {
self.vm_power_button().map_err(Error::DBusApiClient)
}
fn api_vm_reboot(&self) -> ApiResult {
self.vm_reboot().map_err(Error::DBusApiClient)
}
fn api_vm_remove_device(&self, vm_remove_device: &str) -> ApiResult {
self.vm_remove_device(vm_remove_device)
.map_err(Error::DBusApiClient)
}
fn api_vm_resize(&self, vm_resize: &str) -> ApiResult {
self.vm_resize(vm_resize).map_err(Error::DBusApiClient)
}
fn api_vm_resize_zone(&self, vm_resize_zone: &str) -> ApiResult {
self.vm_resize_zone(vm_resize_zone)
.map_err(Error::DBusApiClient)
}
fn api_vm_restore(&self, restore_config: &str) -> ApiResult {
self.vm_restore(restore_config)
.map_err(Error::DBusApiClient)
}
fn api_vm_receive_migration(&self, receive_migration_data: &str) -> ApiResult {
self.vm_receive_migration(receive_migration_data)
.map_err(Error::DBusApiClient)
}
fn api_vm_send_migration(&self, send_migration_data: &str) -> ApiResult {
self.vm_send_migration(send_migration_data)
.map_err(Error::DBusApiClient)
}
fn api_vm_resume(&self) -> ApiResult {
self.vm_resume().map_err(Error::DBusApiClient)
}
fn api_vm_shutdown(&self) -> ApiResult {
self.vm_shutdown().map_err(Error::DBusApiClient)
}
fn api_vm_snapshot(&self, vm_snapshot_config: &str) -> ApiResult {
self.vm_snapshot(vm_snapshot_config)
.map_err(Error::DBusApiClient)
}
}
impl<'a> TargetApi<'a> {
fn do_command(&mut self, toplevel: &TopLevel) -> ApiResult {
match self {
Self::HttpApi(api_socket, _) => rest_api_do_command(toplevel, api_socket),
#[cfg(feature = "dbus_api")]
Self::DBusApi(proxy) => dbus_api_do_command(toplevel, proxy),
}
}
}
fn rest_api_do_command(toplevel: &TopLevel, socket: &mut UnixStream) -> ApiResult {
match toplevel.command {
SubCommandEnum::Boot(_) => {
simple_api_command(socket, "PUT", "boot", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::Delete(_) => {
simple_api_command(socket, "PUT", "delete", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::ShutdownVmm(_) => {
simple_api_full_command(socket, "PUT", "vmm.shutdown", None)
.map_err(Error::HttpApiClient)
}
SubCommandEnum::Resume(_) => {
simple_api_command(socket, "PUT", "resume", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::PowerButton(_) => {
simple_api_command(socket, "PUT", "power-button", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::Reboot(_) => {
simple_api_command(socket, "PUT", "reboot", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::Pause(_) => {
simple_api_command(socket, "PUT", "pause", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::Info(_) => {
simple_api_command(socket, "GET", "info", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::Counters(_) => {
simple_api_command(socket, "GET", "counters", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::Ping(_) => {
simple_api_full_command(socket, "GET", "vmm.ping", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::Shutdown(_) => {
simple_api_command(socket, "PUT", "shutdown", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::Resize(ref config) => {
let resize = resize_config(config.cpus, &config.memory, &config.balloon)?;
simple_api_command(socket, "PUT", "resize", Some(&resize)).map_err(Error::HttpApiClient)
}
SubCommandEnum::ResizeZone(ref config) => {
let resize_zone = resize_zone_config(&config.id, &config.size)?;
simple_api_command(socket, "PUT", "resize-zone", Some(&resize_zone))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::AddDevice(ref config) => {
let device_config = add_device_config(&config.device_config)?;
simple_api_command(socket, "PUT", "add-device", Some(&device_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::RemoveDevice(ref config) => {
let remove_device_data = remove_device_config(&config.device_config);
simple_api_command(socket, "PUT", "remove-device", Some(&remove_device_data))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::AddDisk(ref config) => {
let disk_config = add_disk_config(&config.disk_config)?;
simple_api_command(socket, "PUT", "add-disk", Some(&disk_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::AddFs(ref config) => {
let fs_config = add_fs_config(&config.fs_config)?;
simple_api_command(socket, "PUT", "add-fs", Some(&fs_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::AddPmem(ref config) => {
let pmem_config = add_pmem_config(&config.pmem_config)?;
simple_api_command(socket, "PUT", "add-pmem", Some(&pmem_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::AddNet(ref config) => {
let (net_config, fds) = add_net_config(&config.net_config)?;
simple_api_command_with_fds(socket, "PUT", "add-net", Some(&net_config), fds)
.map_err(Error::HttpApiClient)
}
SubCommandEnum::AddUserDevice(ref config) => {
let device_config = add_user_device_config(&config.device_config)?;
simple_api_command(socket, "PUT", "add-user-device", Some(&device_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::AddVdpa(ref config) => {
let vdpa_config = add_vdpa_config(&config.vdpa_config)?;
simple_api_command(socket, "PUT", "add-vdpa", Some(&vdpa_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::AddVsock(ref config) => {
let vsock_config = add_vsock_config(&config.vsock_config)?;
simple_api_command(socket, "PUT", "add-vsock", Some(&vsock_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::Snapshot(ref config) => {
let snapshot_config = snapshot_api_config(&config.snapshot_config);
simple_api_command(socket, "PUT", "snapshot", Some(&snapshot_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::Restore(ref config) => {
let restore_config = restore_config(&config.restore_config)?;
simple_api_command(socket, "PUT", "restore", Some(&restore_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::Coredump(ref config) => {
let coredump_config = coredump_config(&config.coredump_config);
simple_api_command(socket, "PUT", "coredump", Some(&coredump_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::SendMigration(ref config) => {
let send_migration_data =
send_migration_data(&config.send_migration_config, config.send_migration_local);
simple_api_command(socket, "PUT", "send-migration", Some(&send_migration_data))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::ReceiveMigration(ref config) => {
let receive_migration_data = receive_migration_data(&config.receive_migration_config);
simple_api_command(
socket,
"PUT",
"receive-migration",
Some(&receive_migration_data),
)
.map_err(Error::HttpApiClient)
}
SubCommandEnum::Create(ref config) => {
let data = create_data(&config.vm_config)?;
simple_api_command(socket, "PUT", "create", Some(&data)).map_err(Error::HttpApiClient)
}
SubCommandEnum::Version(_) => {
// Already handled outside of this function
panic!()
}
}
}
#[cfg(feature = "dbus_api")]
fn dbus_api_do_command(toplevel: &TopLevel, proxy: &mut DBusApi1ProxyBlocking<'_>) -> ApiResult {
match toplevel.command {
SubCommandEnum::Boot(_) => proxy.api_vm_boot(),
SubCommandEnum::Delete(_) => proxy.api_vm_delete(),
SubCommandEnum::ShutdownVmm(_) => proxy.api_vmm_shutdown(),
SubCommandEnum::Resume(_) => proxy.api_vm_resume(),
SubCommandEnum::PowerButton(_) => proxy.api_vm_power_button(),
SubCommandEnum::Reboot(_) => proxy.api_vm_reboot(),
SubCommandEnum::Pause(_) => proxy.api_vm_pause(),
SubCommandEnum::Info(_) => proxy.api_vm_info(),
SubCommandEnum::Counters(_) => proxy.api_vm_counters(),
SubCommandEnum::Ping(_) => proxy.api_vmm_ping(),
SubCommandEnum::Shutdown(_) => proxy.api_vm_shutdown(),
SubCommandEnum::Resize(ref config) => {
let resize = resize_config(config.cpus, &config.memory, &config.balloon)?;
proxy.api_vm_resize(&resize)
}
SubCommandEnum::ResizeZone(ref config) => {
let resize_zone = resize_zone_config(&config.id, &config.size)?;
proxy.api_vm_resize_zone(&resize_zone)
}
SubCommandEnum::AddDevice(ref config) => {
let device_config = add_device_config(&config.device_config)?;
proxy.api_vm_add_device(&device_config)
}
SubCommandEnum::RemoveDevice(ref config) => {
let remove_device_data = remove_device_config(&config.device_config);
proxy.api_vm_remove_device(&remove_device_data)
}
SubCommandEnum::AddDisk(ref config) => {
let disk_config = add_disk_config(&config.disk_config)?;
proxy.api_vm_add_disk(&disk_config)
}
SubCommandEnum::AddFs(ref config) => {
let fs_config = add_fs_config(&config.fs_config)?;
proxy.api_vm_add_fs(&fs_config)
}
SubCommandEnum::AddPmem(ref config) => {
let pmem_config = add_pmem_config(&config.pmem_config)?;
proxy.api_vm_add_pmem(&pmem_config)
}
SubCommandEnum::AddNet(ref config) => {
let (net_config, _fds) = add_net_config(&config.net_config)?;
proxy.api_vm_add_net(&net_config)
}
SubCommandEnum::AddUserDevice(ref config) => {
let device_config = add_user_device_config(&config.device_config)?;
proxy.api_vm_add_user_device(&device_config)
}
SubCommandEnum::AddVdpa(ref config) => {
let vdpa_config = add_vdpa_config(&config.vdpa_config)?;
proxy.api_vm_add_vdpa(&vdpa_config)
}
SubCommandEnum::AddVsock(ref config) => {
let vsock_config = add_vsock_config(&config.vsock_config)?;
proxy.api_vm_add_vsock(&vsock_config)
}
SubCommandEnum::Snapshot(ref config) => {
let snapshot_config = snapshot_api_config(&config.snapshot_config);
proxy.api_vm_snapshot(&snapshot_config)
}
SubCommandEnum::Restore(ref config) => {
let restore_config = restore_config(&config.restore_config)?;
proxy.api_vm_restore(&restore_config)
}
SubCommandEnum::Coredump(ref config) => {
let coredump_config = coredump_config(&config.coredump_config);
proxy.api_vm_coredump(&coredump_config)
}
SubCommandEnum::SendMigration(ref config) => {
let send_migration_data =
send_migration_data(&config.send_migration_config, config.send_migration_local);
proxy.api_vm_send_migration(&send_migration_data)
}
SubCommandEnum::ReceiveMigration(ref config) => {
let receive_migration_data = receive_migration_data(&config.receive_migration_config);
proxy.api_vm_receive_migration(&receive_migration_data)
}
SubCommandEnum::Create(ref config) => {
let data = create_data(&config.vm_config)?;
proxy.api_vm_create(&data)
}
SubCommandEnum::Version(_) => {
// Already handled outside of this function
panic!()
}
}
}
fn resize_config(
desired_vcpus: Option<u8>, desired_vcpus: Option<u8>,
memory: &Option<String>, memory: &Option<String>,
balloon: &Option<String>, balloon: &Option<String>,
) -> Result<(), Error> { ) -> Result<String, Error> {
let desired_ram: Option<u64> = if let Some(memory) = memory { let desired_ram: Option<u64> = if let Some(memory) = memory {
Some( Some(
memory memory
@@ -90,16 +515,10 @@ fn resize_api_command(
desired_balloon, desired_balloon,
}; };
simple_api_command( Ok(serde_json::to_string(&resize).unwrap())
socket,
"PUT",
"resize",
Some(&serde_json::to_string(&resize).unwrap()),
)
.map_err(Error::ApiClient)
} }
fn resize_zone_api_command(socket: &mut UnixStream, id: &str, size: &str) -> Result<(), Error> { fn resize_zone_config(id: &str, size: &str) -> Result<String, Error> {
let resize_zone = vmm::api::VmResizeZoneData { let resize_zone = vmm::api::VmResizeZoneData {
id: id.to_owned(), id: id.to_owned(),
desired_ram: size desired_ram: size
@@ -108,89 +527,52 @@ fn resize_zone_api_command(socket: &mut UnixStream, id: &str, size: &str) -> Res
.0, .0,
}; };
simple_api_command( Ok(serde_json::to_string(&resize_zone).unwrap())
socket,
"PUT",
"resize-zone",
Some(&serde_json::to_string(&resize_zone).unwrap()),
)
.map_err(Error::ApiClient)
} }
fn add_device_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { fn add_device_config(config: &str) -> Result<String, Error> {
let device_config = vmm::config::DeviceConfig::parse(config).map_err(Error::AddDeviceConfig)?; let device_config = vmm::config::DeviceConfig::parse(config).map_err(Error::AddDeviceConfig)?;
let device_config = serde_json::to_string(&device_config).unwrap();
simple_api_command( Ok(device_config)
socket,
"PUT",
"add-device",
Some(&serde_json::to_string(&device_config).unwrap()),
)
.map_err(Error::ApiClient)
} }
fn add_user_device_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { fn add_user_device_config(config: &str) -> Result<String, Error> {
let device_config = let device_config =
vmm::config::UserDeviceConfig::parse(config).map_err(Error::AddUserDeviceConfig)?; vmm::config::UserDeviceConfig::parse(config).map_err(Error::AddUserDeviceConfig)?;
let device_config = serde_json::to_string(&device_config).unwrap();
simple_api_command( Ok(device_config)
socket,
"PUT",
"add-user-device",
Some(&serde_json::to_string(&device_config).unwrap()),
)
.map_err(Error::ApiClient)
} }
fn remove_device_api_command(socket: &mut UnixStream, id: &str) -> Result<(), Error> { fn remove_device_config(id: &str) -> String {
let remove_device_data = vmm::api::VmRemoveDeviceData { id: id.to_owned() }; let remove_device_data = vmm::api::VmRemoveDeviceData { id: id.to_owned() };
simple_api_command( serde_json::to_string(&remove_device_data).unwrap()
socket,
"PUT",
"remove-device",
Some(&serde_json::to_string(&remove_device_data).unwrap()),
)
.map_err(Error::ApiClient)
} }
fn add_disk_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { fn add_disk_config(config: &str) -> Result<String, Error> {
let disk_config = vmm::config::DiskConfig::parse(config).map_err(Error::AddDiskConfig)?; let disk_config = vmm::config::DiskConfig::parse(config).map_err(Error::AddDiskConfig)?;
let disk_config = serde_json::to_string(&disk_config).unwrap();
simple_api_command( Ok(disk_config)
socket,
"PUT",
"add-disk",
Some(&serde_json::to_string(&disk_config).unwrap()),
)
.map_err(Error::ApiClient)
} }
fn add_fs_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { fn add_fs_config(config: &str) -> Result<String, Error> {
let fs_config = vmm::config::FsConfig::parse(config).map_err(Error::AddFsConfig)?; let fs_config = vmm::config::FsConfig::parse(config).map_err(Error::AddFsConfig)?;
let fs_config = serde_json::to_string(&fs_config).unwrap();
simple_api_command( Ok(fs_config)
socket,
"PUT",
"add-fs",
Some(&serde_json::to_string(&fs_config).unwrap()),
)
.map_err(Error::ApiClient)
} }
fn add_pmem_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { fn add_pmem_config(config: &str) -> Result<String, Error> {
let pmem_config = vmm::config::PmemConfig::parse(config).map_err(Error::AddPmemConfig)?; let pmem_config = vmm::config::PmemConfig::parse(config).map_err(Error::AddPmemConfig)?;
let pmem_config = serde_json::to_string(&pmem_config).unwrap();
simple_api_command( Ok(pmem_config)
socket,
"PUT",
"add-pmem",
Some(&serde_json::to_string(&pmem_config).unwrap()),
)
.map_err(Error::ApiClient)
} }
fn add_net_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { fn add_net_config(config: &str) -> Result<(String, Vec<i32>), Error> {
let mut net_config = vmm::config::NetConfig::parse(config).map_err(Error::AddNetConfig)?; let mut net_config = vmm::config::NetConfig::parse(config).map_err(Error::AddNetConfig)?;
// NetConfig is modified on purpose here by taking the list of file // NetConfig is modified on purpose here by taking the list of file
@@ -198,113 +580,66 @@ fn add_net_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Erro
// process would not make any sense since the file descriptor may be // process would not make any sense since the file descriptor may be
// represented with different values. // represented with different values.
let fds = net_config.fds.take().unwrap_or_default(); let fds = net_config.fds.take().unwrap_or_default();
let net_config = serde_json::to_string(&net_config).unwrap();
simple_api_command_with_fds( Ok((net_config, fds))
socket,
"PUT",
"add-net",
Some(&serde_json::to_string(&net_config).unwrap()),
fds,
)
.map_err(Error::ApiClient)
} }
fn add_vdpa_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { fn add_vdpa_config(config: &str) -> Result<String, Error> {
let vdpa_config = vmm::config::VdpaConfig::parse(config).map_err(Error::AddVdpaConfig)?; let vdpa_config = vmm::config::VdpaConfig::parse(config).map_err(Error::AddVdpaConfig)?;
let vdpa_config = serde_json::to_string(&vdpa_config).unwrap();
simple_api_command( Ok(vdpa_config)
socket,
"PUT",
"add-vdpa",
Some(&serde_json::to_string(&vdpa_config).unwrap()),
)
.map_err(Error::ApiClient)
} }
fn add_vsock_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { fn add_vsock_config(config: &str) -> Result<String, Error> {
let vsock_config = vmm::config::VsockConfig::parse(config).map_err(Error::AddVsockConfig)?; let vsock_config = vmm::config::VsockConfig::parse(config).map_err(Error::AddVsockConfig)?;
let vsock_config = serde_json::to_string(&vsock_config).unwrap();
simple_api_command( Ok(vsock_config)
socket,
"PUT",
"add-vsock",
Some(&serde_json::to_string(&vsock_config).unwrap()),
)
.map_err(Error::ApiClient)
} }
fn snapshot_api_command(socket: &mut UnixStream, url: &str) -> Result<(), Error> { fn snapshot_api_config(url: &str) -> String {
let snapshot_config = vmm::api::VmSnapshotConfig { let snapshot_config = vmm::api::VmSnapshotConfig {
destination_url: String::from(url), destination_url: String::from(url),
}; };
simple_api_command( serde_json::to_string(&snapshot_config).unwrap()
socket,
"PUT",
"snapshot",
Some(&serde_json::to_string(&snapshot_config).unwrap()),
)
.map_err(Error::ApiClient)
} }
fn restore_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { fn restore_config(config: &str) -> Result<String, Error> {
let restore_config = vmm::config::RestoreConfig::parse(config).map_err(Error::Restore)?; let restore_config = vmm::config::RestoreConfig::parse(config).map_err(Error::Restore)?;
let restore_config = serde_json::to_string(&restore_config).unwrap();
simple_api_command( Ok(restore_config)
socket,
"PUT",
"restore",
Some(&serde_json::to_string(&restore_config).unwrap()),
)
.map_err(Error::ApiClient)
} }
fn coredump_api_command(socket: &mut UnixStream, destination_url: &str) -> Result<(), Error> { fn coredump_config(destination_url: &str) -> String {
let coredump_config = vmm::api::VmCoredumpData { let coredump_config = vmm::api::VmCoredumpData {
destination_url: String::from(destination_url), destination_url: String::from(destination_url),
}; };
simple_api_command( serde_json::to_string(&coredump_config).unwrap()
socket,
"PUT",
"coredump",
Some(&serde_json::to_string(&coredump_config).unwrap()),
)
.map_err(Error::ApiClient)
} }
fn receive_migration_api_command(socket: &mut UnixStream, url: &str) -> Result<(), Error> { fn receive_migration_data(url: &str) -> String {
let receive_migration_data = vmm::api::VmReceiveMigrationData { let receive_migration_data = vmm::api::VmReceiveMigrationData {
receiver_url: url.to_owned(), receiver_url: url.to_owned(),
}; };
simple_api_command(
socket, serde_json::to_string(&receive_migration_data).unwrap()
"PUT",
"receive-migration",
Some(&serde_json::to_string(&receive_migration_data).unwrap()),
)
.map_err(Error::ApiClient)
} }
fn send_migration_api_command( fn send_migration_data(url: &str, local: bool) -> String {
socket: &mut UnixStream,
url: &str,
local: bool,
) -> Result<(), Error> {
let send_migration_data = vmm::api::VmSendMigrationData { let send_migration_data = vmm::api::VmSendMigrationData {
destination_url: url.to_owned(), destination_url: url.to_owned(),
local, local,
}; };
simple_api_command(
socket, serde_json::to_string(&send_migration_data).unwrap()
"PUT",
"send-migration",
Some(&serde_json::to_string(&send_migration_data).unwrap()),
)
.map_err(Error::ApiClient)
} }
fn create_api_command(socket: &mut UnixStream, path: &str) -> Result<(), Error> { fn create_data(path: &str) -> Result<String, Error> {
let mut data = String::default(); let mut data = String::default();
if path == "-" { if path == "-" {
std::io::stdin() std::io::stdin()
@@ -314,100 +649,7 @@ fn create_api_command(socket: &mut UnixStream, path: &str) -> Result<(), Error>
data = std::fs::read_to_string(path).map_err(Error::ReadingFile)?; data = std::fs::read_to_string(path).map_err(Error::ReadingFile)?;
} }
simple_api_command(socket, "PUT", "create", Some(&data)).map_err(Error::ApiClient) Ok(data)
}
fn do_command(toplevel: &TopLevel) -> Result<(), Error> {
let mut socket =
UnixStream::connect(toplevel.api_socket.as_deref().unwrap()).map_err(Error::Connect)?;
match toplevel.command {
SubCommandEnum::Boot(_) => {
simple_api_command(&mut socket, "PUT", "boot", None).map_err(Error::ApiClient)
}
SubCommandEnum::Delete(_) => {
simple_api_command(&mut socket, "PUT", "delete", None).map_err(Error::ApiClient)
}
SubCommandEnum::ShutdownVmm(_) => {
simple_api_full_command(&mut socket, "PUT", "vmm.shutdown", None)
.map_err(Error::ApiClient)
}
SubCommandEnum::Resume(_) => {
simple_api_command(&mut socket, "PUT", "resume", None).map_err(Error::ApiClient)
}
SubCommandEnum::PowerButton(_) => {
simple_api_command(&mut socket, "PUT", "power-button", None).map_err(Error::ApiClient)
}
SubCommandEnum::Reboot(_) => {
simple_api_command(&mut socket, "PUT", "reboot", None).map_err(Error::ApiClient)
}
SubCommandEnum::Pause(_) => {
simple_api_command(&mut socket, "PUT", "pause", None).map_err(Error::ApiClient)
}
SubCommandEnum::Info(_) => {
simple_api_command(&mut socket, "GET", "info", None).map_err(Error::ApiClient)
}
SubCommandEnum::Counters(_) => {
simple_api_command(&mut socket, "GET", "counters", None).map_err(Error::ApiClient)
}
SubCommandEnum::Ping(_) => {
simple_api_full_command(&mut socket, "GET", "vmm.ping", None).map_err(Error::ApiClient)
}
SubCommandEnum::Shutdown(_) => {
simple_api_command(&mut socket, "PUT", "shutdown", None).map_err(Error::ApiClient)
}
SubCommandEnum::Resize(ref config) => {
resize_api_command(&mut socket, config.cpus, &config.memory, &config.balloon)
}
SubCommandEnum::ResizeZone(ref config) => {
resize_zone_api_command(&mut socket, &config.id, &config.size)
}
SubCommandEnum::AddDevice(ref config) => {
add_device_api_command(&mut socket, &config.device_config)
}
SubCommandEnum::RemoveDevice(ref config) => {
remove_device_api_command(&mut socket, &config.device_config)
}
SubCommandEnum::AddDisk(ref config) => {
add_disk_api_command(&mut socket, &config.disk_config)
}
SubCommandEnum::AddFs(ref config) => add_fs_api_command(&mut socket, &config.fs_config),
SubCommandEnum::AddPmem(ref config) => {
add_pmem_api_command(&mut socket, &config.pmem_config)
}
SubCommandEnum::AddNet(ref config) => add_net_api_command(&mut socket, &config.net_config),
SubCommandEnum::AddUserDevice(ref config) => {
add_user_device_api_command(&mut socket, &config.device_config)
}
SubCommandEnum::AddVdpa(ref config) => {
add_vdpa_api_command(&mut socket, &config.vdpa_config)
}
SubCommandEnum::AddVsock(ref config) => {
add_vsock_api_command(&mut socket, &config.vsock_config)
}
SubCommandEnum::Snapshot(ref config) => {
snapshot_api_command(&mut socket, &config.snapshot_config)
}
SubCommandEnum::Restore(ref config) => {
restore_api_command(&mut socket, &config.restore_config)
}
SubCommandEnum::Coredump(ref config) => {
coredump_api_command(&mut socket, &config.coredump_config)
}
SubCommandEnum::SendMigration(ref config) => send_migration_api_command(
&mut socket,
&config.send_migration_config,
config.send_migration_local,
),
SubCommandEnum::ReceiveMigration(ref config) => {
receive_migration_api_command(&mut socket, &config.receive_migration_config)
}
SubCommandEnum::Create(ref config) => create_api_command(&mut socket, &config.vm_config),
SubCommandEnum::Version(_) => {
// Already handled outside of this function
panic!()
}
}
} }
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
@@ -419,6 +661,21 @@ struct TopLevel {
#[argh(option, long = "api-socket")] #[argh(option, long = "api-socket")]
/// HTTP API socket path (UNIX domain socket) /// HTTP API socket path (UNIX domain socket)
api_socket: Option<String>, api_socket: Option<String>,
#[cfg(feature = "dbus_api")]
#[argh(option, long = "dbus-service-name")]
/// well known name of the dbus service
dbus_name: Option<String>,
#[cfg(feature = "dbus_api")]
#[argh(option, long = "dbus-object-path")]
/// object path which the interface is being served at
dbus_path: Option<String>,
#[cfg(feature = "dbus_api")]
#[argh(switch, long = "dbus-system-bus")]
/// use the system bus instead of a session bus
dbus_system_bus: bool,
} }
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
@@ -688,16 +945,56 @@ fn main() {
let toplevel: TopLevel = argh::from_env(); let toplevel: TopLevel = argh::from_env();
if matches!(toplevel.command, SubCommandEnum::Version(_)) { if matches!(toplevel.command, SubCommandEnum::Version(_)) {
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILT_VERSION")); println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILD_VERSION"));
return; return;
} }
if toplevel.api_socket.is_none() { let mut target_api = match (
println!("Please specify --api-socket"); &toplevel.api_socket,
process::exit(1) #[cfg(feature = "dbus_api")]
} &toplevel.dbus_name,
#[cfg(feature = "dbus_api")]
&toplevel.dbus_path,
) {
#[cfg(not(feature = "dbus_api"))]
(Some(ref api_socket),) => TargetApi::HttpApi(
UnixStream::connect(api_socket).unwrap_or_else(|e| {
eprintln!("Error opening HTTP socket: {e}");
process::exit(1)
}),
PhantomData,
),
#[cfg(feature = "dbus_api")]
(Some(ref api_socket), None, None) => TargetApi::HttpApi(
UnixStream::connect(api_socket).unwrap_or_else(|e| {
eprintln!("Error opening HTTP socket: {e}");
process::exit(1)
}),
PhantomData,
),
#[cfg(feature = "dbus_api")]
(None, Some(ref dbus_name), Some(ref dbus_path)) => TargetApi::DBusApi(
DBusApi1ProxyBlocking::new_connection(dbus_name, dbus_path, toplevel.dbus_system_bus)
.map_err(Error::DBusApiClient)
.unwrap_or_else(|e| {
eprintln!("Error creating D-Bus proxy: {e}");
process::exit(1)
}),
),
#[cfg(feature = "dbus_api")]
(Some(_), Some(_) | None, Some(_) | None) => {
println!(
"`api-socket` and (dbus-service-name or dbus-object-path) are mutually exclusive"
);
process::exit(1);
}
_ => {
println!("Please either provide the api-socket option or dbus-service-name and dbus-object-path options");
process::exit(1);
}
};
if let Err(e) = do_command(&toplevel) { if let Err(e) = target_api.do_command(&toplevel) {
eprintln!("Error running command: {e}"); eprintln!("Error running command: {e}");
process::exit(1) process::exit(1)
}; };

View File

@@ -8,7 +8,7 @@ extern crate event_monitor;
use argh::FromArgs; use argh::FromArgs;
use libc::EFD_NONBLOCK; use libc::EFD_NONBLOCK;
use log::LevelFilter; use log::{warn, LevelFilter};
use option_parser::OptionParser; use option_parser::OptionParser;
use seccompiler::SeccompAction; use seccompiler::SeccompAction;
use signal_hook::consts::SIGSYS; use signal_hook::consts::SIGSYS;
@@ -18,10 +18,11 @@ use std::os::unix::io::{FromRawFd, RawFd};
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use thiserror::Error; use thiserror::Error;
#[cfg(feature = "dbus_api")]
use vmm::api::dbus::{dbus_api_graceful_shutdown, DBusApiOptions};
use vmm::config; use vmm::config;
use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::signal::block_signal; use vmm_sys_util::signal::block_signal;
use vmm_sys_util::terminal::Terminal;
#[cfg(feature = "dhat-heap")] #[cfg(feature = "dhat-heap")]
#[global_allocator] #[global_allocator]
@@ -34,6 +35,8 @@ enum Error {
#[cfg(feature = "guest_debug")] #[cfg(feature = "guest_debug")]
#[error("Failed to create Debug EventFd: {0}")] #[error("Failed to create Debug EventFd: {0}")]
CreateDebugEventFd(#[source] std::io::Error), CreateDebugEventFd(#[source] std::io::Error),
#[error("Failed to create exit EventFd: {0}")]
CreateExitEventFd(#[source] std::io::Error),
#[error("Failed to open hypervisor interface (is hypervisor interface available?): {0}")] #[error("Failed to open hypervisor interface (is hypervisor interface available?): {0}")]
CreateHypervisor(#[source] hypervisor::HypervisorError), CreateHypervisor(#[source] hypervisor::HypervisorError),
#[error("Failed to start the VMM thread: {0}")] #[error("Failed to start the VMM thread: {0}")]
@@ -56,6 +59,12 @@ enum Error {
ParsingApiSocket(std::num::ParseIntError), ParsingApiSocket(std::num::ParseIntError),
#[error("Error parsing --event-monitor: {0}")] #[error("Error parsing --event-monitor: {0}")]
ParsingEventMonitor(option_parser::OptionParserError), ParsingEventMonitor(option_parser::OptionParserError),
#[cfg(feature = "dbus_api")]
#[error("`--dbus-object-path` option isn't provided")]
MissingDBusObjectPath,
#[cfg(feature = "dbus_api")]
#[error("`--dbus-service-name` option isn't provided")]
MissingDBusServiceName,
#[error("Error parsing --event-monitor: path or fd required")] #[error("Error parsing --event-monitor: path or fd required")]
BareEventMonitor, BareEventMonitor,
#[error("Error doing event monitor I/O: {0}")] #[error("Error doing event monitor I/O: {0}")]
@@ -91,9 +100,9 @@ impl log::Log for Logger {
let duration = now.duration_since(self.start); let duration = now.duration_since(self.start);
if record.file().is_some() && record.line().is_some() { if record.file().is_some() && record.line().is_some() {
writeln!( write!(
*(*(self.output.lock().unwrap())), *(*(self.output.lock().unwrap())),
"cloud-hypervisor: {:.6?}: <{}> {}:{}:{} -- {}", "cloud-hypervisor: {:.6?}: <{}> {}:{}:{} -- {}\r\n",
duration, duration,
std::thread::current().name().unwrap_or("anonymous"), std::thread::current().name().unwrap_or("anonymous"),
record.level(), record.level(),
@@ -102,9 +111,9 @@ impl log::Log for Logger {
record.args() record.args()
) )
} else { } else {
writeln!( write!(
*(*(self.output.lock().unwrap())), *(*(self.output.lock().unwrap())),
"cloud-hypervisor: {:.6?}: <{}> {}:{} -- {}", "cloud-hypervisor: {:.6?}: <{}> {}:{} -- {}\r\n",
duration, duration,
std::thread::current().name().unwrap_or("anonymous"), std::thread::current().name().unwrap_or("anonymous"),
record.level(), record.level(),
@@ -137,19 +146,19 @@ fn default_rng() -> String {
/// Launch a cloud-hypervisor VMM. /// Launch a cloud-hypervisor VMM.
pub struct TopLevel { pub struct TopLevel {
#[argh(option, long = "cpus", default = "default_vcpus()")] #[argh(option, long = "cpus", default = "default_vcpus()")]
/// boot=<boot_vcpus>,max=<max_vcpus>,topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>,kvm_hyperv=on|off,max_phys_bits=<maximum_number_of_physical_bits>,affinity=<list_of_vcpus_with_their_associated_cpuset>,features=<list_of_features_to_enable> /// boot=<boot_vcpus>, max=<max_vcpus>, topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>, kvm_hyperv=on|off, max_phys_bits=<maximum_number_of_physical_bits>, affinity=<list_of_vcpus_with_their_associated_cpuset>, features=<list_of_features_to_enable>
cpus: String, cpus: String,
#[argh(option, long = "platform")] #[argh(option, long = "platform")]
/// num_pci_segments=<num_pci_segments>,iommu_segments=<list_of_segments>,serial_number=<dmi_device_serial_number>,uuid=<dmi_device_uuid>,oem_strings=<list_of_strings> /// num_pci_segments=<num_pci_segments>, iommu_segments=<list_of_segments>, serial_number=<dmi_device_serial_number>, uuid=<dmi_device_uuid>, oem_strings=<list_of_strings>
platform: Option<String>, platform: Option<String>,
#[argh(option, long = "memory", default = "default_memory()")] #[argh(option, long = "memory", default = "default_memory()")]
/// size=<guest_memory_size>,mergeable=on|off,shared=on|off,hugepages=on|off,hugepage_size=<hugepage_size>,hotplug_method=acpi|virtio-mem,hotplug_size=<hotpluggable_memory_size>,hotplugged_size=<hotplugged_memory_size>,prefault=on|off,thp=on|off /// size=<guest_memory_size>, mergeable=on|off, shared=on|off, hugepages=on|off, hugepage_size=<hugepage_size>, hotplug_method=acpi|virtio-mem, hotplug_size=<hotpluggable_memory_size>, hotplugged_size=<hotplugged_memory_size>, prefault=on|off, thp=on|off
memory: String, memory: String,
#[argh(option, long = "memory-zone")] #[argh(option, long = "memory-zone")]
/// size=<guest_memory_region_size>,file=<backing_file>,shared=on|off,hugepages=on|off,hugepage_size=<hugepage_size>,host_numa_node=<node_id>,id=<zone_identifier>,hotplug_size=<hotpluggable_memory_size>,hotplugged_size=<hotplugged_memory_size>,prefault=on|off /// size=<guest_memory_region_size>, file=<backing_file>, shared=on|off, hugepages=on|off, hugepage_size=<hugepage_size>, host_numa_node=<node_id>, id=<zone_identifier>, hotplug_size=<hotpluggable_memory_size>, hotplugged_size=<hotplugged_memory_size>, prefault=on|off
memory_zone: Vec<String>, memory_zone: Vec<String>,
#[argh(option, long = "firmware")] #[argh(option, long = "firmware")]
@@ -169,27 +178,27 @@ pub struct TopLevel {
cmdline: Option<String>, cmdline: Option<String>,
#[argh(option, long = "disk")] #[argh(option, long = "disk")]
/// path=<disk_image_path>,readonly=on|off,direct=on|off,iommu=on|off,num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,vhost_user=on|off,socket=<vhost_user_socket_path>,bw_size=<bytes>,bw_one_time_burst=<bytes>,bw_refill_time=<ms>,ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>,id=<device_id>,pci_segment=<segment_id> /// path=<disk_image_path>, readonly=on|off, direct=on|off, iommu=on|off, num_queues=<number_of_queues>, queue_size=<size_of_each_queue>, vhost_user=on|off, socket=<vhost_user_socket_path>, bw_size=<bytes>, bw_one_time_burst=<bytes>, bw_refill_time=<ms>, ops_size=<io_ops>, ops_one_time_burst=<io_ops>, ops_refill_time=<ms>, id=<device_id>, pci_segment=<segment_id>
disk: Vec<String>, disk: Vec<String>,
#[argh(option, long = "net")] #[argh(option, long = "net")]
/// tap=<if_name>,ip=<ip_addr>,mask=<net_mask>,mac=<mac_addr>,fd=<fd1,fd2...>,iommu=on|off,num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,id=<device_id>,vhost_user=<vhost_user_enable>,socket=<vhost_user_socket_path>,vhost_mode=client|server,bw_size=<bytes>,bw_one_time_burst=<bytes>,bw_refill_time=<ms>,ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>,pci_segment=<segment_id>offload_tso=on|off,offload_ufo=on|off,offload_csum=on|off /// tap=<if_name>, ip=<ip_addr>, mask=<net_mask>, mac=<mac_addr>, fd=<fd1,fd2...>, iommu=on|off, num_queues=<number_of_queues>, queue_size=<size_of_each_queue>, id=<device_id>, vhost_user=<vhost_user_enable>, socket=<vhost_user_socket_path>, vhost_mode=client|server, bw_size=<bytes>, bw_one_time_burst=<bytes>, bw_refill_time=<ms>, ops_size=<io_ops>, ops_one_time_burst=<io_ops>, ops_refill_time=<ms>, pci_segment=<segment_id>offload_tso=on|off, offload_ufo=on|off, offload_csum=on|off
net: Vec<String>, net: Vec<String>,
#[argh(option, long = "rng", default = "default_rng()")] #[argh(option, long = "rng", default = "default_rng()")]
/// src=<entropy_source_path>,iommu=on|off /// src=<entropy_source_path>, iommu=on|off
rng: String, rng: String,
#[argh(option, long = "balloon")] #[argh(option, long = "balloon")]
/// size=<balloon_size>,deflate_on_oom=on|off,free_page_reporting=on|off /// size=<balloon_size>, deflate_on_oom=on|off, free_page_reporting=on|off
balloon: Option<String>, balloon: Option<String>,
#[argh(option, long = "fs")] #[argh(option, long = "fs")]
/// tag=<tag_name>,socket=<socket_path>,num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,id=<device_id>,pci_segment=<segment_id> /// tag=<tag_name>, socket=<socket_path>, num_queues=<number_of_queues>, queue_size=<size_of_each_queue>, id=<device_id>, pci_segment=<segment_id>
fs: Vec<String>, fs: Vec<String>,
#[argh(option, long = "pmem")] #[argh(option, long = "pmem")]
/// file=<backing_file_path>,size=<persistent_memory_size>,iommu=on|off,discard_writes=on|off,id=<device_id>,pci_segment=<segment_id> /// file=<backing_file_path>, size=<persistent_memory_size>, iommu=on|off, discard_writes=on|off, id=<device_id>, pci_segment=<segment_id>
pmem: Vec<String>, pmem: Vec<String>,
#[argh(option, long = "serial", default = "String::from(\"null\")")] #[argh(option, long = "serial", default = "String::from(\"null\")")]
@@ -197,27 +206,27 @@ pub struct TopLevel {
serial: String, serial: String,
#[argh(option, long = "console", default = "String::from(\"tty\")")] #[argh(option, long = "console", default = "String::from(\"tty\")")]
/// off|null|pty|tty|file=/path/to/a/file,iommu=on|off /// off|null|pty|tty|file=/path/to/a/file, iommu=on|off
console: String, console: String,
#[argh(option, long = "device")] #[argh(option, long = "device")]
/// path=<device_path>,iommu=on|off,id=<device_id>,pci_segment=<segment_id> /// path=<device_path>, iommu=on|off, id=<device_id>, pci_segment=<segment_id>
device: Vec<String>, device: Vec<String>,
#[argh(option, long = "user-device")] #[argh(option, long = "user-device")]
/// socket=<socket_path>,id=<device_id>,pci_segment=<segment_id> /// socket=<socket_path>, id=<device_id>, pci_segment=<segment_id>
user_device: Vec<String>, user_device: Vec<String>,
#[argh(option, long = "vdpa")] #[argh(option, long = "vdpa")]
/// path=<device_path>,num_queues=<number_of_queues>,iommu=on|off,id=<device_id>,pci_segment=<segment_id> /// path=<device_path>, num_queues=<number_of_queues>, iommu=on|off, id=<device_id>, pci_segment=<segment_id>
vdpa: Vec<String>, vdpa: Vec<String>,
#[argh(option, long = "vsock")] #[argh(option, long = "vsock")]
/// cid=<context_id>,socket=<socket_path>,iommu=on|off,id=<device_id>,pci_segment=<segment_id> /// cid=<context_id>, socket=<socket_path>, iommu=on|off, id=<device_id>, pci_segment=<segment_id>
vsock: Option<String>, vsock: Option<String>,
#[argh(option, long = "numa")] #[argh(option, long = "numa")]
/// guest_numa_id=<node_id>,cpus=<cpus_id>,distances=<list_of_distances_to_destination_nodes>,memory_zones=<list_of_memory_zones>,sgx_epc_sections=<list_of_sgx_epc_sections> /// guest_numa_id=<node_id>, cpus=<cpus_id>, distances=<list_of_distances_to_destination_nodes>, memory_zones=<list_of_memory_zones>, sgx_epc_sections=<list_of_sgx_epc_sections>
numa: Vec<String>, numa: Vec<String>,
#[argh(switch, long = "watchdog")] #[argh(switch, long = "watchdog")]
@@ -236,12 +245,27 @@ pub struct TopLevel {
/// path=<path/to/a/file>|fd=<fd> /// path=<path/to/a/file>|fd=<fd>
api_socket: Option<String>, api_socket: Option<String>,
#[cfg(feature = "dbus_api")]
#[argh(option, long = "dbus-service-name")]
/// well known name of the service
dbus_name: Option<String>,
#[cfg(feature = "dbus_api")]
#[argh(option, long = "dbus-object-path")]
/// object path to serve the dbus interface
dbus_path: Option<String>,
#[cfg(feature = "dbus_api")]
#[argh(switch, long = "dbus-system-bus")]
/// use the system bus instead of a session bus
dbus_system_bus: bool,
#[argh(option, long = "event-monitor")] #[argh(option, long = "event-monitor")]
/// path=<path/to/a/file>|fd=<fd> /// path=<path/to/a/file>|fd=<fd>
event_monitor: Option<String>, event_monitor: Option<String>,
#[argh(option, long = "restore")] #[argh(option, long = "restore")]
/// source_url=<source_url>,prefault=on|off /// source_url=<source_url>, prefault=on|off
restore: Option<String>, restore: Option<String>,
#[argh(option, long = "seccomp", default = "String::from(\"true\")")] #[argh(option, long = "seccomp", default = "String::from(\"true\")")]
@@ -254,7 +278,7 @@ pub struct TopLevel {
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
#[argh(option, long = "sgx-epc")] #[argh(option, long = "sgx-epc")]
/// id=<epc_section_identifier>,size=<epc_section_size>,prefault=on|off /// id=<epc_section_identifier>, size=<epc_section_size>, prefault=on|off
sgx_epc: Vec<String>, sgx_epc: Vec<String>,
#[cfg(feature = "guest_debug")] #[cfg(feature = "guest_debug")]
@@ -415,6 +439,18 @@ fn start_vmm(toplevel: TopLevel) -> Result<Option<String>, Error> {
(None, None) (None, None)
}; };
#[cfg(feature = "dbus_api")]
let dbus_options = match (&toplevel.dbus_name, &toplevel.dbus_path) {
(Some(ref name), Some(ref path)) => Ok(Some(DBusApiOptions {
service_name: name.to_owned(),
object_path: path.to_owned(),
system_bus: toplevel.dbus_system_bus,
})),
(Some(_), None) => Err(Error::MissingDBusObjectPath),
(None, Some(_)) => Err(Error::MissingDBusServiceName),
(None, None) => Ok(None),
}?;
if let Some(ref monitor_config) = toplevel.event_monitor { if let Some(ref monitor_config) = toplevel.event_monitor {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser.add("path").add("fd"); parser.add("path").add("fd");
@@ -444,7 +480,7 @@ fn start_vmm(toplevel: TopLevel) -> Result<Option<String>, Error> {
let (api_request_sender, api_request_receiver) = channel(); let (api_request_sender, api_request_receiver) = channel();
let api_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateApiEventFd)?; let api_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateApiEventFd)?;
let http_sender = api_request_sender.clone(); let api_request_sender_clone = api_request_sender.clone();
let seccomp_action = match &toplevel.seccomp as &str { let seccomp_action = match &toplevel.seccomp as &str {
"true" => SeccompAction::Trap, "true" => SeccompAction::Trap,
"false" => SeccompAction::Allow, "false" => SeccompAction::Allow,
@@ -510,12 +546,16 @@ fn start_vmm(toplevel: TopLevel) -> Result<Option<String>, Error> {
#[cfg(feature = "guest_debug")] #[cfg(feature = "guest_debug")]
let vm_debug_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateDebugEventFd)?; let vm_debug_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateDebugEventFd)?;
let vmm_thread = vmm::start_vmm_thread( let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateExitEventFd)?;
env!("CARGO_PKG_VERSION").to_string(),
let vmm_thread_handle = vmm::start_vmm_thread(
vmm::VmmVersionInfo::new(env!("BUILD_VERSION"), env!("CARGO_PKG_VERSION")),
&api_socket_path, &api_socket_path,
api_socket_fd, api_socket_fd,
#[cfg(feature = "dbus_api")]
dbus_options,
api_evt.try_clone().unwrap(), api_evt.try_clone().unwrap(),
http_sender, api_request_sender_clone,
api_request_receiver, api_request_receiver,
#[cfg(feature = "guest_debug")] #[cfg(feature = "guest_debug")]
gdb_socket_path, gdb_socket_path,
@@ -523,41 +563,60 @@ fn start_vmm(toplevel: TopLevel) -> Result<Option<String>, Error> {
debug_evt.try_clone().unwrap(), debug_evt.try_clone().unwrap(),
#[cfg(feature = "guest_debug")] #[cfg(feature = "guest_debug")]
vm_debug_evt.try_clone().unwrap(), vm_debug_evt.try_clone().unwrap(),
exit_evt.try_clone().unwrap(),
&seccomp_action, &seccomp_action,
hypervisor, hypervisor,
) )
.map_err(Error::StartVmmThread)?; .map_err(Error::StartVmmThread)?;
let payload_present = toplevel.kernel.is_some() || toplevel.firmware.is_some(); let r: Result<(), Error> = (|| {
let payload_present = toplevel.kernel.is_some() || toplevel.firmware.is_some();
if payload_present { if payload_present {
let vm_params = toplevel.to_vm_params(); let vm_params = toplevel.to_vm_params();
let vm_config = config::VmConfig::parse(vm_params).map_err(Error::ParsingConfig)?; let vm_config = config::VmConfig::parse(vm_params).map_err(Error::ParsingConfig)?;
// Create and boot the VM based off the VM config we just built. // Create and boot the VM based off the VM config we just built.
let sender = api_request_sender.clone(); let sender = api_request_sender.clone();
vmm::api::vm_create( vmm::api::vm_create(
api_evt.try_clone().unwrap(), api_evt.try_clone().unwrap(),
api_request_sender, api_request_sender,
Arc::new(Mutex::new(vm_config)), Arc::new(Mutex::new(vm_config)),
) )
.map_err(Error::VmCreate)?; .map_err(Error::VmCreate)?;
vmm::api::vm_boot(api_evt.try_clone().unwrap(), sender).map_err(Error::VmBoot)?; vmm::api::vm_boot(api_evt.try_clone().unwrap(), sender).map_err(Error::VmBoot)?;
} else if let Some(restore_params) = toplevel.restore { } else if let Some(restore_params) = toplevel.restore {
vmm::api::vm_restore( vmm::api::vm_restore(
api_evt.try_clone().unwrap(), api_evt.try_clone().unwrap(),
api_request_sender, api_request_sender,
Arc::new(config::RestoreConfig::parse(&restore_params).map_err(Error::ParsingRestore)?), Arc::new(
) config::RestoreConfig::parse(&restore_params).map_err(Error::ParsingRestore)?,
.map_err(Error::VmRestore)?; ),
)
.map_err(Error::VmRestore)?;
}
Ok(())
})();
if r.is_err() {
if let Err(e) = exit_evt.write(1) {
warn!("writing to exit EventFd: {e}");
}
} }
vmm_thread vmm_thread_handle
.thread_handle
.join() .join()
.map_err(Error::ThreadJoin)? .map_err(Error::ThreadJoin)?
.map_err(Error::VmmThread)?; .map_err(Error::VmmThread)?;
Ok(api_socket_path) #[cfg(feature = "dbus_api")]
if let Some(chs) = vmm_thread_handle.dbus_shutdown_chs {
dbus_api_graceful_shutdown(chs);
}
r.map(|_| api_socket_path)
} }
fn main() { fn main() {
@@ -571,7 +630,7 @@ fn main() {
let toplevel: TopLevel = argh::from_env(); let toplevel: TopLevel = argh::from_env();
if toplevel.version { if toplevel.version {
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILT_VERSION")); println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILD_VERSION"));
return; return;
} }
@@ -586,14 +645,6 @@ fn main() {
} }
}; };
// SAFETY: trivially safe
let on_tty = unsafe { libc::isatty(libc::STDIN_FILENO) } != 0;
if on_tty {
// Don't forget to set the terminal in canonical mode
// before to exit.
std::io::stdin().lock().set_canon_mode().unwrap();
}
#[cfg(feature = "dhat-heap")] #[cfg(feature = "dhat-heap")]
drop(_profiler); drop(_profiler);
@@ -731,6 +782,7 @@ mod unit_tests {
gdb: false, gdb: false,
platform: None, platform: None,
tpm: None, tpm: None,
preserved_fds: None,
}; };
assert_eq!(expected_vm_config, result_vm_config); assert_eq!(expected_vm_config, result_vm_config);

View File

@@ -0,0 +1,2 @@
instance-id: cloud
local-hostname: cloud

View File

@@ -0,0 +1,7 @@
version: 2
ethernets:
ens4:
match:
macaddress: 12:34:56:78:90:ab
addresses: [192.168.249.2/24]
gateway4: 192.168.249.1

View File

@@ -0,0 +1,10 @@
#cloud-config
users:
- name: cloud
passwd: $6$7125787751a8d18a$sHwGySomUA1PawiNFWVCKYQN.Ec.Wzz0JtPPL1MvzFrkwmop2dq7.4CYf03A5oemPQ4pOFCCrtCelvFBEle/K.
sudo: ALL=(ALL) NOPASSWD:ALL
lock_passwd: False
inactive: False
shell: /bin/bash
ssh_pwauth: True

View File

@@ -5,12 +5,12 @@ authors = ["The Cloud Hypervisor Authors"]
edition = "2021" edition = "2021"
[dependencies] [dependencies]
dirs = "4.0.0" dirs = "5.0.0"
epoll = "4.3.1" epoll = "4.3.1"
libc = "0.2.139" libc = "0.2.139"
once_cell = "1.17.1" once_cell = "1.17.1"
serde = { version = "1.0.151", features = ["rc", "derive"] } serde = { version = "1.0.164", features = ["rc", "derive"] }
serde_json = "1.0.95" serde_json = "1.0.96"
ssh2 = { version = "0.9.4", features = ["vendored-openssl"] } ssh2 = { version = "0.9.4", features = ["vendored-openssl"] }
vmm-sys-util = "0.11.0" vmm-sys-util = "0.11.0"
wait-timeout = "0.2.0" wait-timeout = "0.2.0"

View File

@@ -249,7 +249,8 @@ impl DiskConfig for UbuntuDiskConfig {
.unwrap() .unwrap()
.join("test_data") .join("test_data")
.join("cloud-init") .join("cloud-init")
.join("ubuntu"); .join("ubuntu")
.join("ci");
vec!["meta-data"].iter().for_each(|x| { vec!["meta-data"].iter().for_each(|x| {
rate_limited_copy(source_file_dir.join(x), cloud_init_directory.join(x)) rate_limited_copy(source_file_dir.join(x), cloud_init_directory.join(x))
@@ -868,14 +869,6 @@ impl Guest {
.map_err(Error::Parsing) .map_err(Error::Parsing)
} }
#[cfg(target_arch = "x86_64")]
pub fn get_initial_apicid(&self) -> Result<u32, Error> {
self.ssh_command("grep \"initial apicid\" /proc/cpuinfo | grep -o \"[0-9]*\"")?
.trim()
.parse()
.map_err(Error::Parsing)
}
pub fn get_total_memory(&self) -> Result<u32, Error> { pub fn get_total_memory(&self) -> Result<u32, Error> {
self.ssh_command("grep MemTotal /proc/meminfo | grep -o \"[0-9]*\"")? self.ssh_command("grep MemTotal /proc/meminfo | grep -o \"[0-9]*\"")?
.trim() .trim()

View File

@@ -10,8 +10,6 @@
extern crate test_infra; extern crate test_infra;
use api_client::simple_api_command;
use api_client::simple_api_full_command;
use net_util::MacAddr; use net_util::MacAddr;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs; use std::fs;
@@ -32,6 +30,9 @@ use test_infra::*;
use vmm_sys_util::{tempdir::TempDir, tempfile::TempFile}; use vmm_sys_util::{tempdir::TempDir, tempfile::TempFile};
use wait_timeout::ChildExt; use wait_timeout::ChildExt;
// Constant taken from the VMM crate.
const MAX_NUM_PCI_SEGMENTS: u16 = 96;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
mod x86_64 { mod x86_64 {
pub const FOCAL_IMAGE_NAME: &str = "focal-server-cloudimg-amd64-custom-20210609-0.raw"; pub const FOCAL_IMAGE_NAME: &str = "focal-server-cloudimg-amd64-custom-20210609-0.raw";
@@ -72,6 +73,324 @@ const DIRECT_KERNEL_BOOT_CMDLINE: &str =
const CONSOLE_TEST_STRING: &str = "Started OpenBSD Secure Shell server"; const CONSOLE_TEST_STRING: &str = "Started OpenBSD Secure Shell server";
// This enum exists to make it more convenient to
// implement test for both D-Bus and REST APIs.
enum TargetApi {
// API socket
HttpApi(String),
// well known service name, object path
DBusApi(String, String),
}
impl TargetApi {
fn new_http_api(tmp_dir: &TempDir) -> Self {
Self::HttpApi(temp_api_path(tmp_dir))
}
fn new_dbus_api(tmp_dir: &TempDir) -> Self {
// `tmp_dir` is in the form of "/tmp/chXXXXXX"
// and we take the `chXXXXXX` part as a unique identifier for the guest
let id = tmp_dir.as_path().file_name().unwrap().to_str().unwrap();
Self::DBusApi(
format!("org.cloudhypervisor.{id}"),
format!("/org/cloudhypervisor/{id}"),
)
}
fn guest_args(&self) -> Vec<&str> {
match self {
TargetApi::HttpApi(api_socket) => {
vec!["--api-socket", api_socket.as_str()]
}
TargetApi::DBusApi(service_name, object_path) => {
vec![
"--dbus-service-name",
service_name.as_str(),
"--dbus-object-path",
object_path.as_str(),
]
}
}
}
fn remote_args(&self) -> Vec<&str> {
// `guest_args` and `remote_args` are consistent with each other
self.guest_args()
}
fn remote_command(&self, command: &str, arg: Option<&str>) -> bool {
let mut cmd = Command::new(clh_command("ch-remote"));
cmd.args(self.remote_args());
cmd.arg(command);
if let Some(arg) = arg {
cmd.arg(arg);
}
let output = cmd.output().unwrap();
if output.status.success() {
true
} else {
eprintln!("Error running ch-remote command: {:?}", &cmd);
let stderr = String::from_utf8_lossy(&output.stderr);
eprintln!("stderr: {stderr}");
false
}
}
}
// Start cloud-hypervisor with no VM parameters, only the API server running.
// From the API: Create a VM, boot it and check that it looks as expected.
fn _test_api_create_boot(target_api: TargetApi, guest: Guest) {
let mut child = GuestCommand::new(&guest)
.args(target_api.guest_args())
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
// Verify API server is running
assert!(target_api.remote_command("ping", None));
// Create the VM first
let cpu_count: u8 = 4;
let request_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
let temp_config_path = guest.tmp_dir.as_path().join("config");
std::fs::write(&temp_config_path, request_body).unwrap();
let create_config = temp_config_path.as_os_str().to_str().unwrap();
assert!(target_api.remote_command("create", Some(create_config),));
// Then boot it
assert!(target_api.remote_command("boot", None));
thread::sleep(std::time::Duration::new(20, 0));
let r = std::panic::catch_unwind(|| {
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
}
// Start cloud-hypervisor with no VM parameters, only the API server running.
// From the API: Create a VM, boot it and check it can be shutdown and then
// booted again
fn _test_api_shutdown(target_api: TargetApi, guest: Guest) {
let mut child = GuestCommand::new(&guest)
.args(target_api.guest_args())
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
// Verify API server is running
assert!(target_api.remote_command("ping", None));
// Create the VM first
let cpu_count: u8 = 4;
let request_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
let temp_config_path = guest.tmp_dir.as_path().join("config");
std::fs::write(&temp_config_path, request_body).unwrap();
let create_config = temp_config_path.as_os_str().to_str().unwrap();
let r = std::panic::catch_unwind(|| {
assert!(target_api.remote_command("create", Some(create_config)));
// Then boot it
assert!(target_api.remote_command("boot", None));
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
// Sync and shutdown without powering off to prevent filesystem
// corruption.
guest.ssh_command("sync").unwrap();
guest.ssh_command("sudo shutdown -H now").unwrap();
// Wait for the guest to be fully shutdown
thread::sleep(std::time::Duration::new(20, 0));
// Then shut it down
assert!(target_api.remote_command("shutdown", None));
// Then boot it again
assert!(target_api.remote_command("boot", None));
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
}
// Start cloud-hypervisor with no VM parameters, only the API server running.
// From the API: Create a VM, boot it and check it can be deleted and then recreated
// booted again.
fn _test_api_delete(target_api: TargetApi, guest: Guest) {
let mut child = GuestCommand::new(&guest)
.args(target_api.guest_args())
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
// Verify API server is running
assert!(target_api.remote_command("ping", None));
// Create the VM first
let cpu_count: u8 = 4;
let request_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
let temp_config_path = guest.tmp_dir.as_path().join("config");
std::fs::write(&temp_config_path, request_body).unwrap();
let create_config = temp_config_path.as_os_str().to_str().unwrap();
let r = std::panic::catch_unwind(|| {
assert!(target_api.remote_command("create", Some(create_config)));
// Then boot it
assert!(target_api.remote_command("boot", None));
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
// Sync and shutdown without powering off to prevent filesystem
// corruption.
guest.ssh_command("sync").unwrap();
guest.ssh_command("sudo shutdown -H now").unwrap();
// Wait for the guest to be fully shutdown
thread::sleep(std::time::Duration::new(20, 0));
// Then delete it
assert!(target_api.remote_command("delete", None));
assert!(target_api.remote_command("create", Some(create_config)));
// Then boot it again
assert!(target_api.remote_command("boot", None));
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
}
// Start cloud-hypervisor with no VM parameters, only the API server running.
// From the API: Create a VM, boot it and check that it looks as expected.
// Then we pause the VM, check that it's no longer available.
// Finally we resume the VM and check that it's available.
fn _test_api_pause_resume(target_api: TargetApi, guest: Guest) {
let mut child = GuestCommand::new(&guest)
.args(target_api.guest_args())
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
// Verify API server is running
assert!(target_api.remote_command("ping", None));
// Create the VM first
let cpu_count: u8 = 4;
let request_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
let temp_config_path = guest.tmp_dir.as_path().join("config");
std::fs::write(&temp_config_path, request_body).unwrap();
let create_config = temp_config_path.as_os_str().to_str().unwrap();
assert!(target_api.remote_command("create", Some(create_config)));
// Then boot it
assert!(target_api.remote_command("boot", None));
thread::sleep(std::time::Duration::new(20, 0));
let r = std::panic::catch_unwind(|| {
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
// We now pause the VM
assert!(target_api.remote_command("pause", None));
// Check pausing again fails
assert!(!target_api.remote_command("pause", None));
thread::sleep(std::time::Duration::new(2, 0));
// SSH into the VM should fail
assert!(ssh_command_ip(
"grep -c processor /proc/cpuinfo",
&guest.network.guest_ip,
2,
5
)
.is_err());
// Resume the VM
assert!(target_api.remote_command("resume", None));
// Check resuming again fails
assert!(!target_api.remote_command("resume", None));
thread::sleep(std::time::Duration::new(2, 0));
// Now we should be able to SSH back in and get the right number of CPUs
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
}
fn prepare_virtiofsd(tmp_dir: &TempDir, shared_dir: &str) -> (std::process::Child, String) { fn prepare_virtiofsd(tmp_dir: &TempDir, shared_dir: &str) -> (std::process::Child, String) {
let mut workload_path = dirs::home_dir().unwrap(); let mut workload_path = dirs::home_dir().unwrap();
workload_path.push("workloads"); workload_path.push("workloads");
@@ -1206,7 +1525,10 @@ fn _test_virtio_fs(
.default_net() .default_net()
.args(["--api-socket", &api_socket]); .args(["--api-socket", &api_socket]);
if pci_segment.is_some() { if pci_segment.is_some() {
guest_command.args(["--platform", "num_pci_segments=16"]); guest_command.args([
"--platform",
&format!("num_pci_segments={MAX_NUM_PCI_SEGMENTS}"),
]);
} }
let fs_params = format!( let fs_params = format!(
@@ -1904,7 +2226,7 @@ fn enable_guest_watchdog(guest: &Guest, watchdog_sec: u32) {
} }
mod common_parallel { mod common_parallel {
use std::{fs::OpenOptions, io::SeekFrom, os::unix::net::UnixStream}; use std::{fs::OpenOptions, io::SeekFrom};
use crate::*; use crate::*;
@@ -1942,7 +2264,6 @@ mod common_parallel {
guest.wait_vm_boot(Some(120)).unwrap(); guest.wait_vm_boot(Some(120)).unwrap();
assert_eq!(guest.get_cpu_count().unwrap_or_default(), 1); assert_eq!(guest.get_cpu_count().unwrap_or_default(), 1);
assert_eq!(guest.get_initial_apicid().unwrap_or(1), 0);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000); assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
assert_eq!(guest.get_pci_bridge_class().unwrap_or_default(), "0x060000"); assert_eq!(guest.get_pci_bridge_class().unwrap_or_default(), "0x060000");
@@ -2046,19 +2367,16 @@ mod common_parallel {
} }
#[test] #[test]
#[cfg(not(feature = "mshv"))]
fn test_cpu_topology_421() { fn test_cpu_topology_421() {
test_cpu_topology(4, 2, 1, false); test_cpu_topology(4, 2, 1, false);
} }
#[test] #[test]
#[cfg(not(feature = "mshv"))]
fn test_cpu_topology_142() { fn test_cpu_topology_142() {
test_cpu_topology(1, 4, 2, false); test_cpu_topology(1, 4, 2, false);
} }
#[test] #[test]
#[cfg(not(feature = "mshv"))]
fn test_cpu_topology_262() { fn test_cpu_topology_262() {
test_cpu_topology(2, 6, 2, false); test_cpu_topology(2, 6, 2, false);
} }
@@ -2319,7 +2637,10 @@ mod common_parallel {
.args(["--memory", "size=512M"]) .args(["--memory", "size=512M"])
.args(["--kernel", direct_kernel_boot_path().to_str().unwrap()]) .args(["--kernel", direct_kernel_boot_path().to_str().unwrap()])
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE]) .args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
.args(["--platform", "num_pci_segments=16,iommu_segments=[1]"]) .args([
"--platform",
&format!("num_pci_segments={MAX_NUM_PCI_SEGMENTS},iommu_segments=[1]"),
])
.default_disks() .default_disks()
.capture_output() .capture_output()
.default_net(); .default_net();
@@ -2475,7 +2796,10 @@ mod common_parallel {
.args(["--memory", "size=512M"]) .args(["--memory", "size=512M"])
.args(["--kernel", direct_kernel_boot_path().to_str().unwrap()]) .args(["--kernel", direct_kernel_boot_path().to_str().unwrap()])
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE]) .args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
.args(["--platform", "num_pci_segments=16"]) .args([
"--platform",
&format!("num_pci_segments={MAX_NUM_PCI_SEGMENTS}"),
])
.args([ .args([
"--disk", "--disk",
format!( format!(
@@ -2502,15 +2826,15 @@ mod common_parallel {
let grep_cmd = "lspci | grep \"Host bridge\" | wc -l"; let grep_cmd = "lspci | grep \"Host bridge\" | wc -l";
let r = std::panic::catch_unwind(|| { let r = std::panic::catch_unwind(|| {
// There should be 16 PCI host bridges in the guest. // There should be MAX_NUM_PCI_SEGMENTS PCI host bridges in the guest.
assert_eq!( assert_eq!(
guest guest
.ssh_command(grep_cmd) .ssh_command(grep_cmd)
.unwrap() .unwrap()
.trim() .trim()
.parse::<u32>() .parse::<u16>()
.unwrap_or_default(), .unwrap_or_default(),
16 MAX_NUM_PCI_SEGMENTS
); );
// Check both if /dev/vdc exists and if the block size is 4M. // Check both if /dev/vdc exists and if the block size is 4M.
@@ -3971,7 +4295,7 @@ mod common_parallel {
.ssh_command_l1( .ssh_command_l1(
"sudo /mnt/ch-remote \ "sudo /mnt/ch-remote \
--api-socket /tmp/ch_api.sock \ --api-socket /tmp/ch_api.sock \
resize --memory=1073741824", resize --memory 1073741824",
) )
.unwrap(); .unwrap();
assert!(guest.get_total_memory_l2().unwrap_or_default() > 960_000); assert!(guest.get_total_memory_l2().unwrap_or_default() > 960_000);
@@ -4030,279 +4354,35 @@ mod common_parallel {
} }
#[test] #[test]
// Start cloud-hypervisor with no VM parameters, only the API server running. fn test_api_http_shutdown() {
// From the API: Create a VM, boot it and check that it looks as expected.
fn test_api_create_boot() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string()); let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal)); let guest = Guest::new(Box::new(focal));
let api_socket = temp_api_path(&guest.tmp_dir); _test_api_shutdown(TargetApi::new_http_api(&guest.tmp_dir), guest)
let mut child = GuestCommand::new(&guest)
.args(["--api-socket", &api_socket])
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
let mut socket = UnixStream::connect(&api_socket).unwrap();
// Verify API server is running
simple_api_full_command(&mut socket, "GET", "vmm.ping", None).unwrap();
// Create the VM first
let cpu_count: u8 = 4;
let http_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
let temp_config_path = guest.tmp_dir.as_path().join("config");
std::fs::write(&temp_config_path, http_body).unwrap();
remote_command(
&api_socket,
"create",
Some(temp_config_path.as_os_str().to_str().unwrap()),
);
// Then boot it
remote_command(&api_socket, "boot", None);
thread::sleep(std::time::Duration::new(20, 0));
let r = std::panic::catch_unwind(|| {
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
} }
#[test] #[test]
// Start cloud-hypervisor with no VM parameters, only the API server running. fn test_api_http_delete() {
// From the API: Create a VM, boot it and check it can be shutdown and then
// booted again
fn test_api_shutdown() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string()); let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal)); let guest = Guest::new(Box::new(focal));
let api_socket = temp_api_path(&guest.tmp_dir); _test_api_delete(TargetApi::new_http_api(&guest.tmp_dir), guest);
let mut child = GuestCommand::new(&guest)
.args(["--api-socket", &api_socket])
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
let mut socket = UnixStream::connect(&api_socket).unwrap();
// Verify API server is running
simple_api_full_command(&mut socket, "GET", "vmm.ping", None).unwrap();
// Create the VM first
let cpu_count: u8 = 4;
let http_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
let r = std::panic::catch_unwind(|| {
// socket has to be created again inside catch_unwind block to avoid errors
let mut socket = UnixStream::connect(&api_socket).unwrap();
simple_api_command(&mut socket, "PUT", "create", Some(&http_body)).unwrap();
// Then boot it
simple_api_command(&mut socket, "PUT", "boot", None).unwrap();
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
// Sync and shutdown without powering off to prevent filesystem
// corruption.
guest.ssh_command("sync").unwrap();
guest.ssh_command("sudo shutdown -H now").unwrap();
// Wait for the guest to be fully shutdown
thread::sleep(std::time::Duration::new(20, 0));
// Then shut it down
simple_api_command(&mut socket, "PUT", "shutdown", None).unwrap();
// Then boot it again
simple_api_command(&mut socket, "PUT", "boot", None).unwrap();
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
} }
#[test] #[test]
// Start cloud-hypervisor with no VM parameters, only the API server running. fn test_api_http_pause_resume() {
// From the API: Create a VM, boot it and check it can be deleted and then recreated
// booted again.
fn test_api_delete() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string()); let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal)); let guest = Guest::new(Box::new(focal));
let api_socket = temp_api_path(&guest.tmp_dir); _test_api_pause_resume(TargetApi::new_http_api(&guest.tmp_dir), guest)
let mut child = GuestCommand::new(&guest)
.args(["--api-socket", &api_socket])
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
let mut socket = UnixStream::connect(&api_socket).unwrap();
// Verify API server is running
simple_api_full_command(&mut socket, "GET", "vmm.ping", None).unwrap();
// Create the VM first
let cpu_count: u8 = 4;
let http_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
let r = std::panic::catch_unwind(|| {
// socket has to be created again inside catch_unwind block to avoid errors
let mut socket = UnixStream::connect(&api_socket).unwrap();
simple_api_command(&mut socket, "PUT", "create", Some(&http_body)).unwrap();
// Then boot it
simple_api_command(&mut socket, "PUT", "boot", None).unwrap();
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
// Sync and shutdown without powering off to prevent filesystem
// corruption.
guest.ssh_command("sync").unwrap();
guest.ssh_command("sudo shutdown -H now").unwrap();
// Wait for the guest to be fully shutdown
thread::sleep(std::time::Duration::new(20, 0));
// Then delete it
simple_api_command(&mut socket, "PUT", "delete", None).unwrap();
simple_api_command(&mut socket, "PUT", "create", Some(&http_body)).unwrap();
// Then boot it again
simple_api_command(&mut socket, "PUT", "boot", None).unwrap();
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
} }
#[test] #[test]
// Start cloud-hypervisor with no VM parameters, only the API server running. fn test_api_http_create_boot() {
// From the API: Create a VM, boot it and check that it looks as expected.
// Then we pause the VM, check that it's no longer available.
// Finally we resume the VM and check that it's available.
fn test_api_pause_resume() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string()); let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal)); let guest = Guest::new(Box::new(focal));
let api_socket = temp_api_path(&guest.tmp_dir); _test_api_create_boot(TargetApi::new_http_api(&guest.tmp_dir), guest)
let mut child = GuestCommand::new(&guest)
.args(["--api-socket", &api_socket])
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
let mut socket = UnixStream::connect(&api_socket).unwrap();
// Verify API server is running
simple_api_full_command(&mut socket, "GET", "vmm.ping", None).unwrap();
// Create the VM first
let cpu_count: u8 = 4;
let http_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
simple_api_command(&mut socket, "PUT", "create", Some(&http_body)).unwrap();
// Then boot it
simple_api_command(&mut socket, "PUT", "boot", None).unwrap();
thread::sleep(std::time::Duration::new(20, 0));
let r = std::panic::catch_unwind(|| {
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
// We now pause the VM
assert!(remote_command(&api_socket, "pause", None));
// Check pausing again fails
assert!(!remote_command(&api_socket, "pause", None));
thread::sleep(std::time::Duration::new(2, 0));
// SSH into the VM should fail
assert!(ssh_command_ip(
"grep -c processor /proc/cpuinfo",
&guest.network.guest_ip,
2,
5
)
.is_err());
// Resume the VM
assert!(remote_command(&api_socket, "resume", None));
// Check resuming again fails
assert!(!remote_command(&api_socket, "resume", None));
thread::sleep(std::time::Duration::new(2, 0));
// Now we should be able to SSH back in and get the right number of CPUs
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
} }
#[test] #[test]
@@ -4916,7 +4996,6 @@ mod common_parallel {
handle_child_output(r, &output); handle_child_output(r, &output);
} }
#[allow(clippy::useless_conversion)]
fn create_loop_device(backing_file_path: &str, block_size: u32, num_retries: usize) -> String { fn create_loop_device(backing_file_path: &str, block_size: u32, num_retries: usize) -> String {
const LOOP_CONFIGURE: u64 = 0x4c0a; const LOOP_CONFIGURE: u64 = 0x4c0a;
const LOOP_CTL_GET_FREE: u64 = 0x4c82; const LOOP_CTL_GET_FREE: u64 = 0x4c82;
@@ -4977,12 +5056,9 @@ mod common_parallel {
.unwrap(); .unwrap();
// Request a free loop device // Request a free loop device
let loop_device_number = unsafe { let loop_device_number =
libc::ioctl( unsafe { libc::ioctl(loop_ctl_file.as_raw_fd(), LOOP_CTL_GET_FREE as _) };
loop_ctl_file.as_raw_fd(),
LOOP_CTL_GET_FREE.try_into().unwrap(),
)
};
if loop_device_number < 0 { if loop_device_number < 0 {
panic!("Couldn't find a free loop device"); panic!("Couldn't find a free loop device");
} }
@@ -5014,7 +5090,7 @@ mod common_parallel {
let ret = unsafe { let ret = unsafe {
libc::ioctl( libc::ioctl(
loop_device_file.as_raw_fd(), loop_device_file.as_raw_fd(),
LOOP_CONFIGURE.try_into().unwrap(), LOOP_CONFIGURE as _,
&loop_config, &loop_config,
) )
}; };
@@ -5299,7 +5375,10 @@ mod common_parallel {
.capture_output(); .capture_output();
if pci_segment.is_some() { if pci_segment.is_some() {
cmd.args(["--platform", "num_pci_segments=16"]); cmd.args([
"--platform",
&format!("num_pci_segments={MAX_NUM_PCI_SEGMENTS}"),
]);
} }
let mut child = cmd.spawn().unwrap(); let mut child = cmd.spawn().unwrap();
@@ -5435,7 +5514,10 @@ mod common_parallel {
.capture_output(); .capture_output();
if pci_segment.is_some() { if pci_segment.is_some() {
cmd.args(["--platform", "num_pci_segments=16"]); cmd.args([
"--platform",
&format!("num_pci_segments={MAX_NUM_PCI_SEGMENTS}"),
]);
} }
let mut child = cmd.spawn().unwrap(); let mut child = cmd.spawn().unwrap();
@@ -6238,6 +6320,18 @@ mod common_parallel {
.unwrap_or_default(), .unwrap_or_default(),
2 2
); );
guest.reboot_linux(0, None);
assert_eq!(
guest
.ssh_command("ip -o link | wc -l")
.unwrap()
.trim()
.parse::<u32>()
.unwrap_or_default(),
2
);
}); });
let _ = child.kill(); let _ = child.kill();
@@ -6251,11 +6345,13 @@ mod common_parallel {
} }
#[test] #[test]
#[cfg_attr(target_arch = "aarch64", ignore = "See #5443")]
fn test_macvtap() { fn test_macvtap() {
_test_macvtap(false, "guestmacvtap0", "hostmacvtap0") _test_macvtap(false, "guestmacvtap0", "hostmacvtap0")
} }
#[test] #[test]
#[cfg_attr(target_arch = "aarch64", ignore = "See #5443")]
fn test_macvtap_hotplug() { fn test_macvtap_hotplug() {
_test_macvtap(true, "guestmacvtap1", "hostmacvtap1") _test_macvtap(true, "guestmacvtap1", "hostmacvtap1")
} }
@@ -6765,6 +6861,116 @@ mod common_parallel {
} }
} }
mod dbus_api {
use crate::*;
// Start cloud-hypervisor with no VM parameters, running both the HTTP
// and DBus APIs. Alternate calls to the external APIs (HTTP and DBus)
// to create a VM, boot it, and verify that it can be shut down and then
// booted again.
#[test]
fn test_api_dbus_and_http_interleaved() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal));
let dbus_api = TargetApi::new_dbus_api(&guest.tmp_dir);
let http_api = TargetApi::new_http_api(&guest.tmp_dir);
let mut child = GuestCommand::new(&guest)
.args(dbus_api.guest_args())
.args(http_api.guest_args())
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
// Verify API servers are running
assert!(dbus_api.remote_command("ping", None));
assert!(http_api.remote_command("ping", None));
// Create the VM first
let cpu_count: u8 = 4;
let request_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
let temp_config_path = guest.tmp_dir.as_path().join("config");
std::fs::write(&temp_config_path, request_body).unwrap();
let create_config = temp_config_path.as_os_str().to_str().unwrap();
let r = std::panic::catch_unwind(|| {
// Create the VM
assert!(dbus_api.remote_command("create", Some(create_config),));
// Then boot it
assert!(http_api.remote_command("boot", None));
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
// Sync and shutdown without powering off to prevent filesystem
// corruption.
guest.ssh_command("sync").unwrap();
guest.ssh_command("sudo shutdown -H now").unwrap();
// Wait for the guest to be fully shutdown
thread::sleep(std::time::Duration::new(20, 0));
// Then shutdown the VM
assert!(dbus_api.remote_command("shutdown", None));
// Then boot it again
assert!(http_api.remote_command("boot", None));
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
}
#[test]
fn test_api_dbus_create_boot() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal));
_test_api_create_boot(TargetApi::new_dbus_api(&guest.tmp_dir), guest)
}
#[test]
fn test_api_dbus_shutdown() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal));
_test_api_shutdown(TargetApi::new_dbus_api(&guest.tmp_dir), guest)
}
#[test]
fn test_api_dbus_delete() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal));
_test_api_delete(TargetApi::new_dbus_api(&guest.tmp_dir), guest);
}
#[test]
fn test_api_dbus_pause_resume() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal));
_test_api_pause_resume(TargetApi::new_dbus_api(&guest.tmp_dir), guest)
}
}
mod common_sequential { mod common_sequential {
#[cfg(not(feature = "mshv"))] #[cfg(not(feature = "mshv"))]
use crate::*; use crate::*;
@@ -8076,7 +8282,7 @@ mod vfio {
.ssh_command_l1( .ssh_command_l1(
"sudo /mnt/ch-remote \ "sudo /mnt/ch-remote \
--api-socket /tmp/ch_api.sock \ --api-socket /tmp/ch_api.sock \
resize --memory=1073741824", resize --memory 1073741824",
) )
.unwrap(); .unwrap();
assert!(guest.get_total_memory_l2().unwrap_or_default() > 960_000); assert!(guest.get_total_memory_l2().unwrap_or_default() > 960_000);

View File

@@ -309,11 +309,7 @@ impl Emulator {
} }
self.established_flag_cached = true; self.established_flag_cached = true;
if est.resp.bit != 0 { self.established_flag = est.resp.bit == 0;
self.established_flag = false;
} else {
self.established_flag = true;
}
self.established_flag self.established_flag
} }

View File

@@ -8,8 +8,8 @@ edition = "2021"
libc = "0.2.139" libc = "0.2.139"
log = "0.4.17" log = "0.4.17"
once_cell = "1.17.1" once_cell = "1.17.1"
serde = { version = "1.0.151", features = ["rc", "derive"] } serde = { version = "1.0.164", features = ["rc", "derive"] }
serde_json = "1.0.95" serde_json = "1.0.96"
[features] [features]
tracing = [] tracing = []

View File

@@ -11,5 +11,5 @@ crc32c = "0.6.3"
libc = "0.2.139" libc = "0.2.139"
log = "0.4.17" log = "0.4.17"
remain = "0.2.6" remain = "0.2.6"
thiserror = "1.0.39" thiserror = "1.0.40"
uuid = { version = "1.3.0", features = ["v4"] } uuid = { version = "1.3.4", features = ["v4"] }

View File

@@ -145,11 +145,7 @@ impl DiskSpec {
let bits = f let bits = f
.read_u32::<LittleEndian>() .read_u32::<LittleEndian>()
.map_err(VhdxMetadataError::ReadMetadata)?; .map_err(VhdxMetadataError::ReadMetadata)?;
if bits & BLOCK_HAS_PARENT != 0 { disk_spec.has_parent = bits & BLOCK_HAS_PARENT != 0;
disk_spec.has_parent = true;
} else {
disk_spec.has_parent = false;
}
metadata_presence |= METADATA_FILE_PARAMETER_PRESENT; metadata_presence |= METADATA_FILE_PARAMETER_PRESENT;
} else if metadata_entry.item_id } else if metadata_entry.item_id

View File

@@ -14,10 +14,10 @@ libc = "0.2.139"
log = "0.4.17" log = "0.4.17"
option_parser = { path = "../option_parser" } option_parser = { path = "../option_parser" }
qcow = { path = "../qcow" } qcow = { path = "../qcow" }
vhost = { version = "0.6.0", features = ["vhost-user-slave"] } vhost = { version = "0.7.0", features = ["vhost-user-slave"] }
vhost-user-backend = "0.8.0" vhost-user-backend = "0.9.0"
virtio-bindings = "0.2.0" virtio-bindings = "0.2.0"
virtio-queue = "0.7.1" virtio-queue = "0.8.0"
vm-memory = "0.10.0" vm-memory = "0.11.0"
vmm-sys-util = "0.11.0" vmm-sys-util = "0.11.0"

View File

@@ -32,7 +32,7 @@ fn main() {
let toplevel: TopLevel = argh::from_env(); let toplevel: TopLevel = argh::from_env();
if toplevel.version { if toplevel.version {
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILT_VERSION")); println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILD_VERSION"));
return; return;
} }

View File

@@ -13,9 +13,9 @@ libc = "0.2.139"
log = "0.4.17" log = "0.4.17"
net_util = { path = "../net_util" } net_util = { path = "../net_util" }
option_parser = { path = "../option_parser" } option_parser = { path = "../option_parser" }
vhost = { version = "0.6.0", features = ["vhost-user-slave"] } vhost = { version = "0.7.0", features = ["vhost-user-slave"] }
vhost-user-backend = "0.8.0" vhost-user-backend = "0.9.0"
virtio-bindings = "0.2.0" virtio-bindings = "0.2.0"
vm-memory = "0.10.0" vm-memory = "0.11.0"
vmm-sys-util = "0.11.0" vmm-sys-util = "0.11.0"

View File

@@ -28,7 +28,7 @@ fn main() {
let toplevel: TopLevel = argh::from_env(); let toplevel: TopLevel = argh::from_env();
if toplevel.version { if toplevel.version {
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILT_VERSION")); println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILD_VERSION"));
return; return;
} }

View File

@@ -8,7 +8,7 @@ edition = "2021"
default = [] default = []
[dependencies] [dependencies]
anyhow = "1.0.69" anyhow = "1.0.71"
arc-swap = "1.5.1" arc-swap = "1.5.1"
block_util = { path = "../block_util" } block_util = { path = "../block_util" }
byteorder = "1.4.3" byteorder = "1.4.3"
@@ -22,18 +22,18 @@ net_util = { path = "../net_util" }
pci = { path = "../pci" } pci = { path = "../pci" }
rate_limiter = { path = "../rate_limiter" } rate_limiter = { path = "../rate_limiter" }
seccompiler = "0.3.0" seccompiler = "0.3.0"
serde = { version = "1.0.151", features = ["derive"] } serde = { version = "1.0.164", features = ["derive"] }
serde_json = "1.0.95" serde_json = "1.0.96"
serial_buffer = { path = "../serial_buffer" } serial_buffer = { path = "../serial_buffer" }
thiserror = "1.0.39" thiserror = "1.0.40"
versionize = "0.1.10" versionize = "0.1.10"
versionize_derive = "0.1.4" versionize_derive = "0.1.4"
vhost = { version = "0.6.0", features = ["vhost-user-master", "vhost-user-slave", "vhost-kern", "vhost-vdpa"] } vhost = { version = "0.7.0", features = ["vhost-user-master", "vhost-user-slave", "vhost-kern", "vhost-vdpa"] }
virtio-bindings = { version = "0.2.0", features = ["virtio-v5_0_0"] } virtio-bindings = { version = "0.2.0", features = ["virtio-v5_0_0"] }
virtio-queue = "0.7.1" virtio-queue = "0.8.0"
vm-allocator = { path = "../vm-allocator" } vm-allocator = { path = "../vm-allocator" }
vm-device = { path = "../vm-device" } vm-device = { path = "../vm-device" }
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] } vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
vm-migration = { path = "../vm-migration" } vm-migration = { path = "../vm-migration" }
vm-virtio = { path = "../vm-virtio" } vm-virtio = { path = "../vm-virtio" }
vmm-sys-util = "0.11.0" vmm-sys-util = "0.11.0"

View File

@@ -39,19 +39,24 @@ pub mod vhost_user;
pub mod vsock; pub mod vsock;
pub mod watchdog; pub mod watchdog;
pub use self::balloon::*; pub use self::balloon::Balloon;
pub use self::block::*; pub use self::block::{Block, BlockState};
pub use self::console::*; pub use self::console::{Console, ConsoleResizer, Endpoint};
pub use self::device::*; pub use self::device::{
pub use self::epoll_helper::*; DmaRemapping, UserspaceMapping, VirtioCommon, VirtioDevice, VirtioInterrupt,
pub use self::iommu::*; VirtioInterruptType, VirtioSharedMemoryList,
pub use self::mem::*; };
pub use self::net::*; pub use self::epoll_helper::{
pub use self::pmem::*; EpollHelper, EpollHelperError, EpollHelperHandler, EPOLL_HELPER_EVENT_LAST,
pub use self::rng::*; };
pub use self::vdpa::*; pub use self::iommu::{AccessPlatformMapping, Iommu, IommuMapping};
pub use self::vsock::*; pub use self::mem::{BlocksState, Mem, VirtioMemMappingSource, VIRTIO_MEM_ALIGN_SIZE};
pub use self::watchdog::*; pub use self::net::{Net, NetCtrlEpollHandler};
pub use self::pmem::Pmem;
pub use self::rng::Rng;
pub use self::vdpa::{Vdpa, VdpaDmaMapping};
pub use self::vsock::Vsock;
pub use self::watchdog::Watchdog;
use vm_memory::{bitmap::AtomicBitmap, GuestAddress, GuestMemory}; use vm_memory::{bitmap::AtomicBitmap, GuestAddress, GuestMemory};
use vm_virtio::VirtioDeviceType; use vm_virtio::VirtioDeviceType;
@@ -86,8 +91,8 @@ pub enum ActivateError {
ThreadSpawn(std::io::Error), ThreadSpawn(std::io::Error),
#[error("Failed to setup vhost-user-fs daemon: {0}")] #[error("Failed to setup vhost-user-fs daemon: {0}")]
VhostUserFsSetup(vhost_user::Error), VhostUserFsSetup(vhost_user::Error),
#[error("Failed to setup vhost-user-blk daemon: {0}")] #[error("Failed to setup vhost-user daemon: {0}")]
VhostUserBlkSetup(vhost_user::Error), VhostUserSetup(vhost_user::Error),
#[error("Failed to create seccomp filter: {0}")] #[error("Failed to create seccomp filter: {0}")]
CreateSeccompFilter(seccompiler::Error), CreateSeccompFilter(seccompiler::Error),
#[error("Failed to create rate limiter: {0}")] #[error("Failed to create rate limiter: {0}")]

View File

@@ -151,6 +151,7 @@ fn virtio_rng_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
fn virtio_vhost_fs_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> { fn virtio_vhost_fs_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
vec![ vec![
(libc::SYS_clock_nanosleep, vec![]),
(libc::SYS_connect, vec![]), (libc::SYS_connect, vec![]),
(libc::SYS_nanosleep, vec![]), (libc::SYS_nanosleep, vec![]),
(libc::SYS_pread64, vec![]), (libc::SYS_pread64, vec![]),
@@ -170,8 +171,11 @@ fn virtio_vhost_net_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
vec![ vec![
(libc::SYS_accept4, vec![]), (libc::SYS_accept4, vec![]),
(libc::SYS_bind, vec![]), (libc::SYS_bind, vec![]),
(libc::SYS_clock_nanosleep, vec![]),
(libc::SYS_connect, vec![]),
(libc::SYS_getcwd, vec![]), (libc::SYS_getcwd, vec![]),
(libc::SYS_listen, vec![]), (libc::SYS_listen, vec![]),
(libc::SYS_nanosleep, vec![]),
(libc::SYS_recvmsg, vec![]), (libc::SYS_recvmsg, vec![]),
(libc::SYS_sendmsg, vec![]), (libc::SYS_sendmsg, vec![]),
(libc::SYS_sendto, vec![]), (libc::SYS_sendto, vec![]),
@@ -184,7 +188,14 @@ fn virtio_vhost_net_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
} }
fn virtio_vhost_block_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> { fn virtio_vhost_block_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
vec![] vec![
(libc::SYS_clock_nanosleep, vec![]),
(libc::SYS_connect, vec![]),
(libc::SYS_nanosleep, vec![]),
(libc::SYS_recvmsg, vec![]),
(libc::SYS_sendmsg, vec![]),
(libc::SYS_socket, vec![]),
]
} }
fn create_vsock_ioctl_seccomp_rule() -> Vec<SeccompRule> { fn create_vsock_ioctl_seccomp_rule() -> Vec<SeccompRule> {
@@ -248,6 +259,7 @@ fn virtio_thread_common() -> Vec<(i64, Vec<SeccompRule>)> {
(libc::SYS_madvise, vec![]), (libc::SYS_madvise, vec![]),
(libc::SYS_mmap, vec![]), (libc::SYS_mmap, vec![]),
(libc::SYS_mprotect, vec![]), (libc::SYS_mprotect, vec![]),
(libc::SYS_mremap, vec![]),
(libc::SYS_munmap, vec![]), (libc::SYS_munmap, vec![]),
(libc::SYS_openat, vec![]), (libc::SYS_openat, vec![]),
(libc::SYS_read, vec![]), (libc::SYS_read, vec![]),

View File

@@ -48,14 +48,13 @@ use super::pci_common_config::VirtioPciCommonConfigState;
/// Vector value used to disable MSI for a queue. /// Vector value used to disable MSI for a queue.
const VIRTQ_MSI_NO_VECTOR: u16 = 0xffff; const VIRTQ_MSI_NO_VECTOR: u16 = 0xffff;
#[allow(clippy::enum_variant_names)]
enum PciCapabilityType { enum PciCapabilityType {
CommonConfig = 1, Common = 1,
NotifyConfig = 2, Notify = 2,
IsrConfig = 3, Isr = 3,
DeviceConfig = 4, Device = 4,
PciConfig = 5, Pci = 5,
SharedMemoryConfig = 8, SharedMemory = 8,
} }
// This offset represents the 2 bytes omitted from the VirtioPciCap structure // This offset represents the 2 bytes omitted from the VirtioPciCap structure
@@ -210,7 +209,7 @@ impl PciCapability for VirtioPciCfgCap {
impl VirtioPciCfgCap { impl VirtioPciCfgCap {
fn new() -> Self { fn new() -> Self {
VirtioPciCfgCap { VirtioPciCfgCap {
cap: VirtioPciCap::new(PciCapabilityType::PciConfig, 0, 0, 0), cap: VirtioPciCap::new(PciCapabilityType::Pci, 0, 0, 0),
..Default::default() ..Default::default()
} }
} }
@@ -667,7 +666,7 @@ impl VirtioPciDevice {
) -> std::result::Result<(), PciDeviceError> { ) -> std::result::Result<(), PciDeviceError> {
// Add pointers to the different configuration structures from the PCI capabilities. // Add pointers to the different configuration structures from the PCI capabilities.
let common_cap = VirtioPciCap::new( let common_cap = VirtioPciCap::new(
PciCapabilityType::CommonConfig, PciCapabilityType::Common,
settings_bar, settings_bar,
COMMON_CONFIG_BAR_OFFSET as u32, COMMON_CONFIG_BAR_OFFSET as u32,
COMMON_CONFIG_SIZE as u32, COMMON_CONFIG_SIZE as u32,
@@ -677,7 +676,7 @@ impl VirtioPciDevice {
.map_err(PciDeviceError::CapabilitiesSetup)?; .map_err(PciDeviceError::CapabilitiesSetup)?;
let isr_cap = VirtioPciCap::new( let isr_cap = VirtioPciCap::new(
PciCapabilityType::IsrConfig, PciCapabilityType::Isr,
settings_bar, settings_bar,
ISR_CONFIG_BAR_OFFSET as u32, ISR_CONFIG_BAR_OFFSET as u32,
ISR_CONFIG_SIZE as u32, ISR_CONFIG_SIZE as u32,
@@ -688,7 +687,7 @@ impl VirtioPciDevice {
// TODO(dgreid) - set based on device's configuration size? // TODO(dgreid) - set based on device's configuration size?
let device_cap = VirtioPciCap::new( let device_cap = VirtioPciCap::new(
PciCapabilityType::DeviceConfig, PciCapabilityType::Device,
settings_bar, settings_bar,
DEVICE_CONFIG_BAR_OFFSET as u32, DEVICE_CONFIG_BAR_OFFSET as u32,
DEVICE_CONFIG_SIZE as u32, DEVICE_CONFIG_SIZE as u32,
@@ -698,7 +697,7 @@ impl VirtioPciDevice {
.map_err(PciDeviceError::CapabilitiesSetup)?; .map_err(PciDeviceError::CapabilitiesSetup)?;
let notify_cap = VirtioPciNotifyCap::new( let notify_cap = VirtioPciNotifyCap::new(
PciCapabilityType::NotifyConfig, PciCapabilityType::Notify,
settings_bar, settings_bar,
NOTIFICATION_BAR_OFFSET as u32, NOTIFICATION_BAR_OFFSET as u32,
NOTIFICATION_SIZE as u32, NOTIFICATION_SIZE as u32,
@@ -1057,7 +1056,7 @@ impl PciDevice for VirtioPciDevice {
for (idx, shm) in shm_list.region_list.iter().enumerate() { for (idx, shm) in shm_list.region_list.iter().enumerate() {
let shm_cap = VirtioPciCap64::new( let shm_cap = VirtioPciCap64::new(
PciCapabilityType::SharedMemoryConfig, PciCapabilityType::SharedMemory,
VIRTIO_SHM_BAR_INDEX as u8, VIRTIO_SHM_BAR_INDEX as u8,
idx as u8, idx as u8,
shm.offset, shm.offset,

View File

@@ -605,7 +605,7 @@ impl VirtioDevice for Fs {
&mut self, &mut self,
shm_regions: VirtioSharedMemoryList, shm_regions: VirtioSharedMemoryList,
) -> std::result::Result<(), crate::Error> { ) -> std::result::Result<(), crate::Error> {
if let Some(mut cache) = self.cache.as_mut() { if let Some(cache) = self.cache.as_mut() {
cache.0 = shm_regions; cache.0 = shm_regions;
Ok(()) Ok(())
} else { } else {

View File

@@ -237,7 +237,7 @@ impl<S: VhostUserMasterReqHandler> VhostUserEpollHandler<S> {
.map_err(|e| { .map_err(|e| {
EpollHelperError::IoError(std::io::Error::new( EpollHelperError::IoError(std::io::Error::new(
std::io::ErrorKind::Other, std::io::ErrorKind::Other,
format!("failed reconnecting vhost-user backend{e:?}"), format!("failed reconnecting vhost-user backend: {e:?}"),
)) ))
})?; })?;
@@ -340,7 +340,7 @@ impl VhostUserCommon {
&slave_req_handler, &slave_req_handler,
inflight.as_mut(), inflight.as_mut(),
) )
.map_err(ActivateError::VhostUserBlkSetup)?; .map_err(ActivateError::VhostUserSetup)?;
Ok(VhostUserEpollHandler { Ok(VhostUserEpollHandler {
vu: vu.clone(), vu: vu.clone(),
@@ -413,7 +413,7 @@ impl VhostUserCommon {
pub fn pause(&mut self) -> std::result::Result<(), MigratableError> { pub fn pause(&mut self) -> std::result::Result<(), MigratableError> {
if let Some(vu) = &self.vu { if let Some(vu) = &self.vu {
vu.lock().unwrap().pause_vhost_user().map_err(|e| { vu.lock().unwrap().pause_vhost_user().map_err(|e| {
MigratableError::Pause(anyhow!("Error pausing vhost-user-blk backend: {:?}", e)) MigratableError::Pause(anyhow!("Error pausing vhost-user backend: {:?}", e))
}) })
} else { } else {
Ok(()) Ok(())
@@ -423,7 +423,7 @@ impl VhostUserCommon {
pub fn resume(&mut self) -> std::result::Result<(), MigratableError> { pub fn resume(&mut self) -> std::result::Result<(), MigratableError> {
if let Some(vu) = &self.vu { if let Some(vu) = &self.vu {
vu.lock().unwrap().resume_vhost_user().map_err(|e| { vu.lock().unwrap().resume_vhost_user().map_err(|e| {
MigratableError::Resume(anyhow!("Error resuming vhost-user-blk backend: {:?}", e)) MigratableError::Resume(anyhow!("Error resuming vhost-user backend: {:?}", e))
}) })
} else { } else {
Ok(()) Ok(())

View File

@@ -436,7 +436,6 @@ mod tests {
} }
#[test] #[test]
#[allow(clippy::cognitive_complexity)]
fn test_tx_packet_assembly() { fn test_tx_packet_assembly() {
// Test case: successful TX packet assembly. // Test case: successful TX packet assembly.
{ {
@@ -581,7 +580,6 @@ mod tests {
} }
#[test] #[test]
#[allow(clippy::cognitive_complexity)]
fn test_packet_hdr_accessors() { fn test_packet_hdr_accessors() {
const SRC_CID: u64 = 1; const SRC_CID: u64 = 1;
const DST_CID: u64 = 2; const DST_CID: u64 = 2;

View File

@@ -6,7 +6,7 @@ edition = "2021"
[dependencies] [dependencies]
libc = "0.2.139" libc = "0.2.139"
vm-memory = "0.10.0" vm-memory = "0.11.0"
[target.'cfg(target_arch = "aarch64")'.dependencies] [target.'cfg(target_arch = "aarch64")'.dependencies]
arch = { path = "../arch" } arch = { path = "../arch" }

View File

@@ -63,7 +63,6 @@ impl GsiAllocator {
} }
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
#[allow(clippy::new_without_default)]
/// New GSI allocator /// New GSI allocator
pub fn new() -> Self { pub fn new() -> Self {
GsiAllocator { GsiAllocator {
@@ -106,3 +105,10 @@ impl GsiAllocator {
Ok(irq) Ok(irq)
} }
} }
#[cfg(target_arch = "aarch64")]
impl Default for GsiAllocator {
fn default() -> Self {
GsiAllocator::new()
}
}

View File

@@ -12,6 +12,8 @@
mod address; mod address;
mod gsi; mod gsi;
/// page size related utility funtions
pub mod page_size;
mod system; mod system;
pub use crate::address::AddressAllocator; pub use crate::address::AddressAllocator;

View File

@@ -0,0 +1,38 @@
// Copyright 2023 Arm Limited (or its affiliates). All rights reserved.
// SPDX-License-Identifier: Apache-2.0
use libc::{sysconf, _SC_PAGESIZE};
/// get host page size
pub fn get_page_size() -> u64 {
// SAFETY: FFI call. Trivially safe.
unsafe { sysconf(_SC_PAGESIZE) as u64 }
}
/// round up address to let it align page size
pub fn align_page_size_up(address: u64) -> u64 {
let page_size = get_page_size();
(address + page_size - 1) & !(page_size - 1)
}
/// round down address to let it align page size
pub fn align_page_size_down(address: u64) -> u64 {
let page_size = get_page_size();
address & !(page_size - 1)
}
/// Test if address is 4k aligned
pub fn is_4k_aligned(address: u64) -> bool {
(address & 0xfff) == 0
}
/// Test if size is 4k aligned
pub fn is_4k_multiple(size: u64) -> bool {
(size & 0xfff) == 0
}
/// Test if address is page size aligned
pub fn is_page_size_aligned(address: u64) -> bool {
let page_size = get_page_size();
address & (page_size - 1) == 0
}

View File

@@ -14,14 +14,7 @@ use crate::gsi::GsiAllocator;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
use crate::gsi::GsiApic; use crate::gsi::GsiApic;
use libc::{sysconf, _SC_PAGESIZE}; use crate::page_size::get_page_size;
/// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
#[inline(always)]
fn pagesize() -> usize {
// SAFETY: FFI call. Trivially safe.
unsafe { sysconf(_SC_PAGESIZE) as usize }
}
/// Manages allocating system resources such as address space and interrupt numbers. /// Manages allocating system resources such as address space and interrupt numbers.
/// ///
@@ -126,7 +119,7 @@ impl SystemAllocator {
self.platform_mmio_address_space.allocate( self.platform_mmio_address_space.allocate(
address, address,
size, size,
Some(align_size.unwrap_or(pagesize() as u64)), Some(align_size.unwrap_or(get_page_size())),
) )
} }
@@ -140,7 +133,7 @@ impl SystemAllocator {
self.mmio_hole_address_space.allocate( self.mmio_hole_address_space.allocate(
address, address,
size, size,
Some(align_size.unwrap_or(pagesize() as u64)), Some(align_size.unwrap_or(get_page_size())),
) )
} }

View File

@@ -10,11 +10,11 @@ kvm = ["vfio-ioctls/kvm"]
mshv = ["vfio-ioctls/mshv"] mshv = ["vfio-ioctls/mshv"]
[dependencies] [dependencies]
anyhow = "1.0.69" anyhow = "1.0.71"
hypervisor = { path = "../hypervisor" } hypervisor = { path = "../hypervisor" }
thiserror = "1.0.39" thiserror = "1.0.40"
serde = { version = "1.0.151", features = ["rc", "derive"] } serde = { version = "1.0.164", features = ["rc", "derive"] }
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false } vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
vm-memory = { version = "0.10.0", features = ["backend-mmap"] } vm-memory = { version = "0.11.0", features = ["backend-mmap"] }
vmm-sys-util = "0.11.0" vmm-sys-util = "0.11.0"

View File

@@ -110,8 +110,7 @@ impl Bus {
let devices = self.devices.read().unwrap(); let devices = self.devices.read().unwrap();
let (range, dev) = devices let (range, dev) = devices
.range(..=BusRange { base: addr, len: 1 }) .range(..=BusRange { base: addr, len: 1 })
.rev() .next_back()?;
.next()?;
dev.upgrade().map(|d| (*range, d.clone())) dev.upgrade().map(|d| (*range, d.clone()))
} }

View File

@@ -5,10 +5,10 @@ authors = ["The Cloud Hypervisor Authors"]
edition = "2021" edition = "2021"
[dependencies] [dependencies]
anyhow = "1.0.69" anyhow = "1.0.71"
thiserror = "1.0.39" thiserror = "1.0.40"
serde = { version = "1.0.151", features = ["rc", "derive"] } serde = { version = "1.0.164", features = ["rc", "derive"] }
serde_json = "1.0.95" serde_json = "1.0.96"
versionize = "0.1.10" versionize = "0.1.10"
versionize_derive = "0.1.4" versionize_derive = "0.1.4"
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic"] } vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic"] }

View File

@@ -12,7 +12,7 @@ use versionize::{VersionMap, Versionize};
pub mod protocol; pub mod protocol;
/// Global VMM version for versioning /// Global VMM version for versioning
const MAJOR_VERSION: u16 = 31; const MAJOR_VERSION: u16 = 33;
const MINOR_VERSION: u16 = 0; const MINOR_VERSION: u16 = 0;
const VMM_VERSION: u16 = MAJOR_VERSION << 12 | MINOR_VERSION & 0b1111; const VMM_VERSION: u16 = MAJOR_VERSION << 12 | MINOR_VERSION & 0b1111;

View File

@@ -9,5 +9,5 @@ default = []
[dependencies] [dependencies]
log = "0.4.17" log = "0.4.17"
virtio-queue = "0.7.1" virtio-queue = "0.8.0"
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] } vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }

View File

@@ -6,6 +6,7 @@ edition = "2021"
[features] [features]
default = [] default = []
dbus_api = ["blocking", "futures", "zbus"]
guest_debug = ["kvm", "gdbstub", "gdbstub_arch"] guest_debug = ["kvm", "gdbstub", "gdbstub_arch"]
kvm = ["hypervisor/kvm", "vfio-ioctls/kvm", "vm-device/kvm", "pci/kvm"] kvm = ["hypervisor/kvm", "vfio-ioctls/kvm", "vm-device/kvm", "pci/kvm"]
mshv = ["hypervisor/mshv", "vfio-ioctls/mshv", "vm-device/mshv", "pci/mshv"] mshv = ["hypervisor/mshv", "vfio-ioctls/mshv", "vm-device/mshv", "pci/mshv"]
@@ -14,19 +15,21 @@ tracing = ["tracer/tracing"]
[dependencies] [dependencies]
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" } acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
anyhow = "1.0.69" anyhow = "1.0.71"
arc-swap = "1.5.1" arc-swap = "1.5.1"
arch = { path = "../arch" } arch = { path = "../arch" }
bitflags = "1.3.2" bitflags = "2.3.3"
block_util = { path = "../block_util" } block_util = { path = "../block_util" }
blocking = { version = "1.3.0", optional = true }
devices = { path = "../devices" } devices = { path = "../devices" }
epoll = "4.3.1" epoll = "4.3.1"
event_monitor = { path = "../event_monitor" } event_monitor = { path = "../event_monitor" }
futures = { version = "0.3.27", optional = true }
gdbstub = { version = "0.6.4", optional = true } gdbstub = { version = "0.6.4", optional = true }
gdbstub_arch = { version = "0.2.4", optional = true } gdbstub_arch = { version = "0.2.4", optional = true }
hypervisor = { path = "../hypervisor" } hypervisor = { path = "../hypervisor" }
libc = "0.2.139" libc = "0.2.139"
linux-loader = { version = "0.8.1", features = ["elf", "bzimage", "pe"] } linux-loader = { version = "0.9.0", features = ["elf", "bzimage", "pe"] }
log = "0.4.17" log = "0.4.17"
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" } micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
net_util = { path = "../net_util" } net_util = { path = "../net_util" }
@@ -35,24 +38,25 @@ option_parser = { path = "../option_parser" }
pci = { path = "../pci" } pci = { path = "../pci" }
qcow = { path = "../qcow" } qcow = { path = "../qcow" }
seccompiler = "0.3.0" seccompiler = "0.3.0"
serde = { version = "1.0.151", features = ["rc", "derive"] } serde = { version = "1.0.164", features = ["rc", "derive"] }
serde_json = "1.0.95" serde_json = "1.0.96"
serial_buffer = { path = "../serial_buffer" } serial_buffer = { path = "../serial_buffer" }
signal-hook = "0.3.15" signal-hook = "0.3.15"
thiserror = "1.0.39" thiserror = "1.0.40"
tracer = { path = "../tracer" } tracer = { path = "../tracer" }
uuid = "1.3.0" uuid = "1.3.4"
versionize = "0.1.10" versionize = "0.1.10"
versionize_derive = "0.1.4" versionize_derive = "0.1.4"
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false } vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
vfio_user = { git = "https://github.com/rust-vmm/vfio-user", branch = "main" } vfio_user = { git = "https://github.com/rust-vmm/vfio-user", branch = "main" }
vhdx = { path = "../vhdx" } vhdx = { path = "../vhdx" }
virtio-devices = { path = "../virtio-devices" } virtio-devices = { path = "../virtio-devices" }
virtio-queue = "0.7.1" virtio-queue = "0.8.0"
vm-allocator = { path = "../vm-allocator" } vm-allocator = { path = "../vm-allocator" }
vm-device = { path = "../vm-device" } vm-device = { path = "../vm-device" }
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] } vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
vm-migration = { path = "../vm-migration" } vm-migration = { path = "../vm-migration" }
vm-virtio = { path = "../vm-virtio" } vm-virtio = { path = "../vm-virtio" }
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] } vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }
zerocopy = "0.6.1" zbus = { version = "3.11.1", optional = true }
zerocopy = "0.6.1"

View File

@@ -41,7 +41,7 @@ pub const ACPI_APIC_GENERIC_TRANSLATOR: u8 = 15;
#[allow(dead_code)] #[allow(dead_code)]
#[repr(packed)] #[repr(packed)]
#[derive(Default)] #[derive(Default, AsBytes)]
struct PciRangeEntry { struct PciRangeEntry {
pub base_address: u64, pub base_address: u64,
pub segment: u16, pub segment: u16,
@@ -52,7 +52,7 @@ struct PciRangeEntry {
#[allow(dead_code)] #[allow(dead_code)]
#[repr(packed)] #[repr(packed)]
#[derive(Default)] #[derive(Default, AsBytes)]
struct MemoryAffinity { struct MemoryAffinity {
pub type_: u8, pub type_: u8,
pub length: u8, pub length: u8,
@@ -69,7 +69,7 @@ struct MemoryAffinity {
#[allow(dead_code)] #[allow(dead_code)]
#[repr(packed)] #[repr(packed)]
#[derive(Default)] #[derive(Default, AsBytes)]
struct ProcessorLocalX2ApicAffinity { struct ProcessorLocalX2ApicAffinity {
pub type_: u8, pub type_: u8,
pub length: u8, pub length: u8,
@@ -83,7 +83,7 @@ struct ProcessorLocalX2ApicAffinity {
#[allow(dead_code)] #[allow(dead_code)]
#[repr(packed)] #[repr(packed)]
#[derive(Default)] #[derive(Default, AsBytes)]
struct ProcessorGiccAffinity { struct ProcessorGiccAffinity {
pub type_: u8, pub type_: u8,
pub length: u8, pub length: u8,
@@ -143,7 +143,7 @@ impl MemoryAffinity {
#[allow(dead_code)] #[allow(dead_code)]
#[repr(packed)] #[repr(packed)]
#[derive(Default)] #[derive(Default, AsBytes)]
struct ViotVirtioPciNode { struct ViotVirtioPciNode {
pub type_: u8, pub type_: u8,
_reserved: u8, _reserved: u8,
@@ -155,7 +155,7 @@ struct ViotVirtioPciNode {
#[allow(dead_code)] #[allow(dead_code)]
#[repr(packed)] #[repr(packed)]
#[derive(Default)] #[derive(Default, AsBytes)]
struct ViotPciRangeNode { struct ViotPciRangeNode {
pub type_: u8, pub type_: u8,
_reserved: u8, _reserved: u8,
@@ -239,7 +239,7 @@ fn create_facp_table(dsdt_offset: GuestAddress, device_manager: &Arc<Mutex<Devic
// X_DSDT // X_DSDT
facp.write(140, dsdt_offset.0); facp.write(140, dsdt_offset.0);
// Hypervisor Vendor Identity // Hypervisor Vendor Identity
facp.write(268, b"CLOUDHYP"); facp.write_bytes(268, b"CLOUDHYP");
facp.update_checksum(); facp.update_checksum();

377
vmm/src/api/dbus/mod.rs Normal file
View File

@@ -0,0 +1,377 @@
// Copyright © 2023 Sartura Ltd.
//
// SPDX-License-Identifier: Apache-2.0
//
use super::{ApiRequest, VmAction};
use crate::seccomp_filters::{get_seccomp_filter, Thread};
use crate::{Error as VmmError, Result as VmmResult};
use crate::{NetConfig, VmConfig};
use futures::channel::oneshot;
use futures::{executor, FutureExt};
use hypervisor::HypervisorType;
use seccompiler::{apply_filter, SeccompAction};
use std::panic::AssertUnwindSafe;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex};
use std::thread;
use vmm_sys_util::eventfd::EventFd;
use zbus::fdo::{self, Result};
use zbus::zvariant::Optional;
use zbus::{dbus_interface, ConnectionBuilder};
pub type DBusApiShutdownChannels = (oneshot::Sender<()>, oneshot::Receiver<()>);
pub struct DBusApiOptions {
pub service_name: String,
pub object_path: String,
pub system_bus: bool,
}
pub struct DBusApi {
api_notifier: EventFd,
api_sender: futures::lock::Mutex<Sender<ApiRequest>>,
}
fn api_error(error: impl std::fmt::Debug) -> fdo::Error {
fdo::Error::Failed(format!("{error:?}"))
}
// This method is intended to ensure that the DBusApi thread has enough time to
// send a response to the VmmShutdown method call before it is terminated. If
// this step is omitted, the thread may be terminated before it can send a
// response, resulting in an error message stating that the message recipient
// disconnected from the message bus without providing a reply.
pub fn dbus_api_graceful_shutdown(ch: DBusApiShutdownChannels) {
let (send_shutdown, mut recv_done) = ch;
// send the shutdown signal and return
// if it errors out
if send_shutdown.send(()).is_err() {
return;
}
// loop until `recv_err` errors out
// or as long as the return value indicates
// "immediately stale" (None)
while let Ok(None) = recv_done.try_recv() {}
}
impl DBusApi {
pub fn new(api_notifier: EventFd, api_sender: Sender<ApiRequest>) -> Self {
Self {
api_notifier,
api_sender: futures::lock::Mutex::new(api_sender),
}
}
async fn clone_api_sender(&self) -> Sender<ApiRequest> {
// lock the async mutex, clone the `Sender` and then immediately
// drop the MutexGuard so that other tasks can clone the
// `Sender` as well
self.api_sender.lock().await.clone()
}
fn clone_api_notifier(&self) -> Result<EventFd> {
self.api_notifier
.try_clone()
.map_err(|err| fdo::Error::IOError(format!("{err:?}")))
}
async fn vm_action(&self, action: VmAction) -> Result<Optional<String>> {
let api_sender = self.clone_api_sender().await;
let api_notifier = self.clone_api_notifier()?;
let result = blocking::unblock(move || super::vm_action(api_notifier, api_sender, action))
.await
.map_err(api_error)?
// We're using `from_utf8_lossy` here to not deal with the
// error case of `from_utf8` as we know that `b.body` is valid JSON.
.map(|b| String::from_utf8_lossy(&b.body).to_string());
Ok(result.into())
}
}
#[dbus_interface(name = "org.cloudhypervisor.DBusApi1")]
impl DBusApi {
async fn vmm_ping(&self) -> Result<String> {
let api_sender = self.clone_api_sender().await;
let api_notifier = self.clone_api_notifier()?;
let result = blocking::unblock(move || super::vmm_ping(api_notifier, api_sender))
.await
.map_err(api_error)?;
serde_json::to_string(&result).map_err(api_error)
}
async fn vmm_shutdown(&self) -> Result<()> {
let api_sender = self.clone_api_sender().await;
let api_notifier = self.clone_api_notifier()?;
blocking::unblock(move || super::vmm_shutdown(api_notifier, api_sender))
.await
.map_err(api_error)
}
async fn vm_add_device(&self, device_config: String) -> Result<Optional<String>> {
let device_config = serde_json::from_str(&device_config).map_err(api_error)?;
self.vm_action(VmAction::AddDevice(Arc::new(device_config)))
.await
}
async fn vm_add_disk(&self, disk_config: String) -> Result<Optional<String>> {
let disk_config = serde_json::from_str(&disk_config).map_err(api_error)?;
self.vm_action(VmAction::AddDisk(Arc::new(disk_config)))
.await
}
async fn vm_add_fs(&self, fs_config: String) -> Result<Optional<String>> {
let fs_config = serde_json::from_str(&fs_config).map_err(api_error)?;
self.vm_action(VmAction::AddFs(Arc::new(fs_config))).await
}
async fn vm_add_net(&self, net_config: String) -> Result<Optional<String>> {
let mut net_config: NetConfig = serde_json::from_str(&net_config).map_err(api_error)?;
if net_config.fds.is_some() {
warn!("Ignoring FDs sent via the D-Bus request body");
net_config.fds = None;
}
self.vm_action(VmAction::AddNet(Arc::new(net_config))).await
}
async fn vm_add_pmem(&self, pmem_config: String) -> Result<Optional<String>> {
let pmem_config = serde_json::from_str(&pmem_config).map_err(api_error)?;
self.vm_action(VmAction::AddPmem(Arc::new(pmem_config)))
.await
}
async fn vm_add_user_device(&self, vm_add_user_device: String) -> Result<Optional<String>> {
let vm_add_user_device = serde_json::from_str(&vm_add_user_device).map_err(api_error)?;
self.vm_action(VmAction::AddUserDevice(Arc::new(vm_add_user_device)))
.await
}
async fn vm_add_vdpa(&self, vdpa_config: String) -> Result<Optional<String>> {
let vdpa_config = serde_json::from_str(&vdpa_config).map_err(api_error)?;
self.vm_action(VmAction::AddVdpa(Arc::new(vdpa_config)))
.await
}
async fn vm_add_vsock(&self, vsock_config: String) -> Result<Optional<String>> {
let vsock_config = serde_json::from_str(&vsock_config).map_err(api_error)?;
self.vm_action(VmAction::AddVsock(Arc::new(vsock_config)))
.await
}
async fn vm_boot(&self) -> Result<()> {
self.vm_action(VmAction::Boot).await.map(|_| ())
}
#[allow(unused_variables)]
// zbus doesn't support cfg attributes on interface methods
// as a workaround, we make the *call to the internal API* conditionally
// compile and return an error on unsupported platforms.
async fn vm_coredump(&self, vm_coredump_data: String) -> Result<()> {
#[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
{
let vm_coredump_data = serde_json::from_str(&vm_coredump_data).map_err(api_error)?;
self.vm_action(VmAction::Coredump(Arc::new(vm_coredump_data)))
.await
.map(|_| ())
}
#[cfg(not(all(target_arch = "x86_64", feature = "guest_debug")))]
Err(api_error(
"VmCoredump only works on x86_64 with the `guest_debug` feature enabled",
))
}
async fn vm_counters(&self) -> Result<Optional<String>> {
self.vm_action(VmAction::Counters).await
}
async fn vm_create(&self, vm_config: String) -> Result<()> {
let api_sender = self.clone_api_sender().await;
let api_notifier = self.clone_api_notifier()?;
let mut vm_config: VmConfig = serde_json::from_str(&vm_config).map_err(api_error)?;
if let Some(ref mut nets) = vm_config.net {
if nets.iter().any(|net| net.fds.is_some()) {
warn!("Ignoring FDs sent via the D-Bus request body");
}
for net in nets {
net.fds = None;
}
}
blocking::unblock(move || {
super::vm_create(api_notifier, api_sender, Arc::new(Mutex::new(vm_config)))
})
.await
.map_err(api_error)?;
Ok(())
}
async fn vm_delete(&self) -> Result<()> {
self.vm_action(VmAction::Delete).await.map(|_| ())
}
async fn vm_info(&self) -> Result<String> {
let api_sender = self.clone_api_sender().await;
let api_notifier = self.clone_api_notifier()?;
let result = blocking::unblock(move || super::vm_info(api_notifier, api_sender))
.await
.map_err(api_error)?;
serde_json::to_string(&result).map_err(api_error)
}
async fn vm_pause(&self) -> Result<()> {
self.vm_action(VmAction::Pause).await.map(|_| ())
}
async fn vm_power_button(&self) -> Result<()> {
self.vm_action(VmAction::PowerButton).await.map(|_| ())
}
async fn vm_reboot(&self) -> Result<()> {
self.vm_action(VmAction::Reboot).await.map(|_| ())
}
async fn vm_remove_device(&self, vm_remove_device: String) -> Result<()> {
let vm_remove_device = serde_json::from_str(&vm_remove_device).map_err(api_error)?;
self.vm_action(VmAction::RemoveDevice(Arc::new(vm_remove_device)))
.await
.map(|_| ())
}
async fn vm_resize(&self, vm_resize: String) -> Result<()> {
let vm_resize = serde_json::from_str(&vm_resize).map_err(api_error)?;
self.vm_action(VmAction::Resize(Arc::new(vm_resize)))
.await
.map(|_| ())
}
async fn vm_resize_zone(&self, vm_resize_zone: String) -> Result<()> {
let vm_resize_zone = serde_json::from_str(&vm_resize_zone).map_err(api_error)?;
self.vm_action(VmAction::ResizeZone(Arc::new(vm_resize_zone)))
.await
.map(|_| ())
}
async fn vm_restore(&self, restore_config: String) -> Result<()> {
let restore_config = serde_json::from_str(&restore_config).map_err(api_error)?;
self.vm_action(VmAction::Restore(Arc::new(restore_config)))
.await
.map(|_| ())
}
async fn vm_receive_migration(&self, receive_migration_data: String) -> Result<()> {
let receive_migration_data =
serde_json::from_str(&receive_migration_data).map_err(api_error)?;
self.vm_action(VmAction::ReceiveMigration(Arc::new(receive_migration_data)))
.await
.map(|_| ())
}
async fn vm_send_migration(&self, send_migration_data: String) -> Result<()> {
let send_migration_data = serde_json::from_str(&send_migration_data).map_err(api_error)?;
self.vm_action(VmAction::SendMigration(Arc::new(send_migration_data)))
.await
.map(|_| ())
}
async fn vm_resume(&self) -> Result<()> {
self.vm_action(VmAction::Resume).await.map(|_| ())
}
async fn vm_shutdown(&self) -> Result<()> {
self.vm_action(VmAction::Shutdown).await.map(|_| ())
}
async fn vm_snapshot(&self, vm_snapshot_config: String) -> Result<()> {
let vm_snapshot_config = serde_json::from_str(&vm_snapshot_config).map_err(api_error)?;
self.vm_action(VmAction::Snapshot(Arc::new(vm_snapshot_config)))
.await
.map(|_| ())
}
}
pub fn start_dbus_thread(
dbus_options: DBusApiOptions,
api_notifier: EventFd,
api_sender: Sender<ApiRequest>,
seccomp_action: &SeccompAction,
exit_evt: EventFd,
hypervisor_type: HypervisorType,
) -> VmmResult<(thread::JoinHandle<VmmResult<()>>, DBusApiShutdownChannels)> {
let dbus_iface = DBusApi::new(api_notifier, api_sender);
let connection = executor::block_on(async move {
let conn_builder = if dbus_options.system_bus {
ConnectionBuilder::system()?
} else {
ConnectionBuilder::session()?
};
conn_builder
.internal_executor(false)
.name(dbus_options.service_name)?
.serve_at(dbus_options.object_path, dbus_iface)?
.build()
.await
})
.map_err(VmmError::CreateDBusSession)?;
let (send_shutdown, recv_shutdown) = oneshot::channel::<()>();
let (send_done, recv_done) = oneshot::channel::<()>();
// Retrieve seccomp filter for API thread
let api_seccomp_filter = get_seccomp_filter(seccomp_action, Thread::DBusApi, hypervisor_type)
.map_err(VmmError::CreateSeccompFilter)?;
let thread_join_handle = thread::Builder::new()
.name("dbus-thread".to_string())
.spawn(move || {
// Apply seccomp filter for API thread.
if !api_seccomp_filter.is_empty() {
apply_filter(&api_seccomp_filter)
.map_err(VmmError::ApplySeccompFilter)
.map_err(|e| {
error!("Error applying seccomp filter: {:?}", e);
exit_evt.write(1).ok();
e
})?;
}
std::panic::catch_unwind(AssertUnwindSafe(move || {
executor::block_on(async move {
let recv_shutdown = recv_shutdown.fuse();
let executor_tick = futures::future::Fuse::terminated();
futures::pin_mut!(recv_shutdown, executor_tick);
executor_tick.set(connection.executor().tick().fuse());
loop {
futures::select! {
_ = executor_tick => executor_tick.set(connection.executor().tick().fuse()),
_ = recv_shutdown => {
send_done.send(()).ok();
break;
},
}
}
})
}))
.map_err(|_| {
error!("dbus-api thread panicked");
exit_evt.write(1).ok()
})
.ok();
Ok(())
})
.map_err(VmmError::DBusThreadSpawn)?;
Ok((thread_join_handle, (send_shutdown, recv_done)))
}

View File

@@ -36,13 +36,22 @@ impl EndpointHandler for VmCreate {
match &req.body { match &req.body {
Some(body) => { Some(body) => {
// Deserialize into a VmConfig // Deserialize into a VmConfig
let vm_config: VmConfig = match serde_json::from_slice(body.raw()) let mut vm_config: VmConfig = match serde_json::from_slice(body.raw())
.map_err(HttpError::SerdeJsonDeserialize) .map_err(HttpError::SerdeJsonDeserialize)
{ {
Ok(config) => config, Ok(config) => config,
Err(e) => return error_response(e, StatusCode::BadRequest), Err(e) => return error_response(e, StatusCode::BadRequest),
}; };
if let Some(ref mut nets) = vm_config.net {
if nets.iter().any(|net| net.fds.is_some()) {
warn!("Ignoring FDs sent via the HTTP request body");
}
for net in nets {
net.fds = None;
}
}
// Call vm_create() // Call vm_create()
match vm_create(api_notifier, api_sender, Arc::new(Mutex::new(vm_config))) match vm_create(api_notifier, api_sender, Arc::new(Mutex::new(vm_config)))
.map_err(HttpError::ApiError) .map_err(HttpError::ApiError)
@@ -105,6 +114,10 @@ impl EndpointHandler for VmActionHandler {
), ),
AddNet(_) => { AddNet(_) => {
let mut net_cfg: NetConfig = serde_json::from_slice(body.raw())?; let mut net_cfg: NetConfig = serde_json::from_slice(body.raw())?;
if net_cfg.fds.is_some() {
warn!("Ignoring FDs sent via the HTTP request body");
net_cfg.fds = None;
}
// Update network config with optional files that might have // Update network config with optional files that might have
// been sent through control message. // been sent through control message.
if !files.is_empty() { if !files.is_empty() {

View File

@@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// //
use crate::api::http_endpoint::{VmActionHandler, VmCreate, VmInfo, VmmPing, VmmShutdown}; use self::http_endpoint::{VmActionHandler, VmCreate, VmInfo, VmmPing, VmmShutdown};
use crate::api::{ApiError, ApiRequest, VmAction}; use crate::api::{ApiError, ApiRequest, VmAction};
use crate::seccomp_filters::{get_seccomp_filter, Thread}; use crate::seccomp_filters::{get_seccomp_filter, Thread};
use crate::{Error as VmmError, Result}; use crate::{Error as VmmError, Result};
@@ -23,6 +23,8 @@ use std::sync::Arc;
use std::thread; use std::thread;
use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::eventfd::EventFd;
pub mod http_endpoint;
/// Errors associated with VMM management /// Errors associated with VMM management
#[derive(Debug)] #[derive(Debug)]
pub enum HttpError { pub enum HttpError {
@@ -282,7 +284,7 @@ fn start_http_thread(
hypervisor_type: HypervisorType, hypervisor_type: HypervisorType,
) -> Result<thread::JoinHandle<Result<()>>> { ) -> Result<thread::JoinHandle<Result<()>>> {
// Retrieve seccomp filter for API thread // Retrieve seccomp filter for API thread
let api_seccomp_filter = get_seccomp_filter(seccomp_action, Thread::Api, hypervisor_type) let api_seccomp_filter = get_seccomp_filter(seccomp_action, Thread::HttpApi, hypervisor_type)
.map_err(VmmError::CreateSeccompFilter)?; .map_err(VmmError::CreateSeccompFilter)?;
thread::Builder::new() thread::Builder::new()

View File

@@ -28,12 +28,15 @@
//! response channel Receiver. //! response channel Receiver.
//! 5. The thread handles the response and forwards potential errors. //! 5. The thread handles the response and forwards potential errors.
#[cfg(feature = "dbus_api")]
pub mod dbus;
pub mod http;
#[cfg(feature = "dbus_api")]
pub use self::dbus::start_dbus_thread;
pub use self::http::start_http_fd_thread; pub use self::http::start_http_fd_thread;
pub use self::http::start_http_path_thread; pub use self::http::start_http_path_thread;
pub mod http;
pub mod http_endpoint;
use crate::config::{ use crate::config::{
DeviceConfig, DiskConfig, FsConfig, NetConfig, PmemConfig, RestoreConfig, UserDeviceConfig, DeviceConfig, DiskConfig, FsConfig, NetConfig, PmemConfig, RestoreConfig, UserDeviceConfig,
VdpaConfig, VmConfig, VsockConfig, VdpaConfig, VmConfig, VsockConfig,
@@ -165,7 +168,9 @@ pub struct VmInfo {
#[derive(Clone, Deserialize, Serialize)] #[derive(Clone, Deserialize, Serialize)]
pub struct VmmPingResponse { pub struct VmmPingResponse {
pub build_version: String,
pub version: String, pub version: String,
pub pid: i64,
} }
#[derive(Clone, Deserialize, Serialize, Default, Debug)] #[derive(Clone, Deserialize, Serialize, Default, Debug)]
@@ -230,7 +235,6 @@ pub enum ApiResponsePayload {
/// This is the response sent by the VMM API server through the mpsc channel. /// This is the response sent by the VMM API server through the mpsc channel.
pub type ApiResponse = std::result::Result<ApiResponsePayload, ApiError>; pub type ApiResponse = std::result::Result<ApiResponsePayload, ApiError>;
#[allow(clippy::large_enum_variant)]
#[derive(Debug)] #[derive(Debug)]
pub enum ApiRequest { pub enum ApiRequest {
/// Create the virtual machine. This request payload is a VM configuration /// Create the virtual machine. This request payload is a VM configuration

View File

@@ -438,8 +438,13 @@ components:
- version - version
type: object type: object
properties: properties:
build_version:
type: string
version: version:
type: string type: string
pid:
type: integer
format: int64
description: Virtual Machine Monitor information description: Virtual Machine Monitor information
VmInfo: VmInfo:

View File

@@ -17,7 +17,7 @@ use std::str::FromStr;
use thiserror::Error; use thiserror::Error;
use virtio_devices::{RateLimiterConfig, TokenBucketConfig}; use virtio_devices::{RateLimiterConfig, TokenBucketConfig};
const MAX_NUM_PCI_SEGMENTS: u16 = 16; const MAX_NUM_PCI_SEGMENTS: u16 = 96;
/// Errors associated with VM configuration parameters. /// Errors associated with VM configuration parameters.
#[derive(Debug, Error)] #[derive(Debug, Error)]
@@ -1134,38 +1134,6 @@ impl NetConfig {
} }
} }
impl Clone for NetConfig {
fn clone(&self) -> Self {
NetConfig {
tap: self.tap.clone(),
vhost_socket: self.vhost_socket.clone(),
id: self.id.clone(),
fds: self
.fds
.as_ref()
// SAFETY: We have been handed these FDs through the API
.map(|fds| fds.iter().map(|fd| unsafe { libc::dup(*fd) }).collect()),
..*self
}
}
}
impl Drop for NetConfig {
fn drop(&mut self) {
if let Some(mut fds) = self.fds.take() {
for fd in fds.drain(..) {
// Skip reserved FDs
if fd <= 2 {
continue;
}
// SAFETY: Safe as the fd was given to the config by the API
unsafe { libc::close(fd) };
}
}
}
}
impl RngConfig { impl RngConfig {
pub fn parse(rng: &str) -> Result<Self> { pub fn parse(rng: &str) -> Result<Self> {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
@@ -2125,23 +2093,83 @@ impl VmConfig {
gdb, gdb,
platform, platform,
tpm, tpm,
preserved_fds: None,
}; };
config.validate().map_err(Error::Validation)?; config.validate().map_err(Error::Validation)?;
Ok(config) Ok(config)
} }
/// # Safety
/// To use this safely, the caller must guarantee that the input
/// fds are all valid.
pub unsafe fn add_preserved_fds(&mut self, mut fds: Vec<i32>) {
if fds.is_empty() {
return;
}
if let Some(preserved_fds) = &self.preserved_fds {
fds.append(&mut preserved_fds.clone());
}
self.preserved_fds = Some(fds);
}
#[cfg(feature = "tdx")] #[cfg(feature = "tdx")]
pub fn is_tdx_enabled(&self) -> bool { pub fn is_tdx_enabled(&self) -> bool {
self.platform.as_ref().map(|p| p.tdx).unwrap_or(false) self.platform.as_ref().map(|p| p.tdx).unwrap_or(false)
} }
} }
impl Clone for VmConfig {
fn clone(&self) -> Self {
VmConfig {
cpus: self.cpus.clone(),
memory: self.memory.clone(),
payload: self.payload.clone(),
disks: self.disks.clone(),
net: self.net.clone(),
rng: self.rng.clone(),
balloon: self.balloon.clone(),
fs: self.fs.clone(),
pmem: self.pmem.clone(),
serial: self.serial.clone(),
console: self.console.clone(),
devices: self.devices.clone(),
user_devices: self.user_devices.clone(),
vdpa: self.vdpa.clone(),
vsock: self.vsock.clone(),
#[cfg(target_arch = "x86_64")]
sgx_epc: self.sgx_epc.clone(),
numa: self.numa.clone(),
platform: self.platform.clone(),
tpm: self.tpm.clone(),
preserved_fds: self
.preserved_fds
.as_ref()
// SAFETY: FFI call with valid FDs
.map(|fds| fds.iter().map(|fd| unsafe { libc::dup(*fd) }).collect()),
..*self
}
}
}
impl Drop for VmConfig {
fn drop(&mut self) {
if let Some(mut fds) = self.preserved_fds.take() {
for fd in fds.drain(..) {
// SAFETY: FFI call with valid FDs
unsafe { libc::close(fd) };
}
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{fs::File, os::fd::AsRawFd};
use super::*; use super::*;
use net_util::MacAddr; use net_util::MacAddr;
use std::fs::File;
use std::os::unix::io::AsRawFd;
#[test] #[test]
fn test_cpu_parsing() -> Result<()> { fn test_cpu_parsing() -> Result<()> {
@@ -2361,10 +2389,6 @@ mod tests {
NetConfig { NetConfig {
mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(), mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(),
host_mac: Some(MacAddr::parse_str("12:34:de:ad:be:ef").unwrap()), host_mac: Some(MacAddr::parse_str("12:34:de:ad:be:ef").unwrap()),
fds: None,
id: None,
tap: None,
vhost_socket: None,
..Default::default() ..Default::default()
} }
); );
@@ -2375,9 +2399,6 @@ mod tests {
mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(), mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(),
host_mac: Some(MacAddr::parse_str("12:34:de:ad:be:ef").unwrap()), host_mac: Some(MacAddr::parse_str("12:34:de:ad:be:ef").unwrap()),
id: Some("mynet0".to_owned()), id: Some("mynet0".to_owned()),
fds: None,
tap: None,
vhost_socket: None,
..Default::default() ..Default::default()
} }
); );
@@ -2392,9 +2413,6 @@ mod tests {
tap: Some("tap0".to_owned()), tap: Some("tap0".to_owned()),
ip: "192.168.100.1".parse().unwrap(), ip: "192.168.100.1".parse().unwrap(),
mask: "255.255.255.128".parse().unwrap(), mask: "255.255.255.128".parse().unwrap(),
fds: None,
id: None,
vhost_socket: None,
..Default::default() ..Default::default()
} }
); );
@@ -2408,9 +2426,6 @@ mod tests {
host_mac: Some(MacAddr::parse_str("12:34:de:ad:be:ef").unwrap()), host_mac: Some(MacAddr::parse_str("12:34:de:ad:be:ef").unwrap()),
vhost_user: true, vhost_user: true,
vhost_socket: Some("/tmp/sock".to_owned()), vhost_socket: Some("/tmp/sock".to_owned()),
fds: None,
id: None,
tap: None,
..Default::default() ..Default::default()
} }
); );
@@ -2423,31 +2438,18 @@ mod tests {
num_queues: 4, num_queues: 4,
queue_size: 1024, queue_size: 1024,
iommu: true, iommu: true,
fds: None,
id: None,
tap: None,
vhost_socket: None,
..Default::default() ..Default::default()
} }
); );
// SAFETY: Safe as the file was just opened
let fd1 = unsafe { libc::dup(File::open("/dev/null").unwrap().as_raw_fd()) };
// SAFETY: Safe as the file was just opened
let fd2 = unsafe { libc::dup(File::open("/dev/null").unwrap().as_raw_fd()) };
assert_eq!( assert_eq!(
&format!( NetConfig::parse("mac=de:ad:be:ef:12:34,fd=[3,7],num_queues=4")?,
"{:?}", NetConfig {
NetConfig::parse(&format!( mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(),
"mac=de:ad:be:ef:12:34,fd=[{fd1},{fd2}],num_queues=4" fds: Some(vec![3, 7]),
))? num_queues: 4,
), ..Default::default()
&format!("NetConfig {{ tap: None, ip: 192.168.249.1, mask: 255.255.255.0, \ }
mac: MacAddr {{ bytes: [222, 173, 190, 239, 18, 52] }}, host_mac: None, mtu: None, \
iommu: false, num_queues: 4, queue_size: 256, vhost_user: false, vhost_socket: None, \
vhost_mode: Client, id: None, fds: Some([{fd1}, {fd2}]), \
rate_limiter_config: None, pci_segment: 0, offload_tso: true, offload_ufo: true, offload_csum: true }}")
); );
Ok(()) Ok(())
@@ -2774,6 +2776,7 @@ mod tests {
gdb: false, gdb: false,
platform: None, platform: None,
tpm: None, tpm: None,
preserved_fds: None,
}; };
assert!(valid_config.validate().is_ok()); assert!(valid_config.validate().is_ok());
@@ -2868,10 +2871,6 @@ mod tests {
let mut invalid_config = valid_config.clone(); let mut invalid_config = valid_config.clone();
invalid_config.net = Some(vec![NetConfig { invalid_config.net = Some(vec![NetConfig {
vhost_user: true, vhost_user: true,
fds: None,
id: None,
tap: None,
vhost_socket: None,
..Default::default() ..Default::default()
}]); }]);
assert_eq!( assert_eq!(
@@ -2883,9 +2882,6 @@ mod tests {
still_valid_config.net = Some(vec![NetConfig { still_valid_config.net = Some(vec![NetConfig {
vhost_user: true, vhost_user: true,
vhost_socket: Some("/path/to/sock".to_owned()), vhost_socket: Some("/path/to/sock".to_owned()),
fds: None,
id: None,
tap: None,
..Default::default() ..Default::default()
}]); }]);
still_valid_config.memory.shared = true; still_valid_config.memory.shared = true;
@@ -2894,9 +2890,6 @@ mod tests {
let mut invalid_config = valid_config.clone(); let mut invalid_config = valid_config.clone();
invalid_config.net = Some(vec![NetConfig { invalid_config.net = Some(vec![NetConfig {
fds: Some(vec![0]), fds: Some(vec![0]),
id: None,
tap: None,
vhost_socket: None,
..Default::default() ..Default::default()
}]); }]);
assert_eq!( assert_eq!(
@@ -2907,10 +2900,6 @@ mod tests {
let mut invalid_config = valid_config.clone(); let mut invalid_config = valid_config.clone();
invalid_config.net = Some(vec![NetConfig { invalid_config.net = Some(vec![NetConfig {
offload_csum: false, offload_csum: false,
fds: None,
id: None,
tap: None,
vhost_socket: None,
..Default::default() ..Default::default()
}]); }]);
assert_eq!( assert_eq!(
@@ -2958,24 +2947,26 @@ mod tests {
let mut still_valid_config = valid_config.clone(); let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig { still_valid_config.platform = Some(PlatformConfig {
num_pci_segments: 16, num_pci_segments: MAX_NUM_PCI_SEGMENTS,
..Default::default() ..Default::default()
}); });
assert!(still_valid_config.validate().is_ok()); assert!(still_valid_config.validate().is_ok());
let mut invalid_config = valid_config.clone(); let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig { invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 17, num_pci_segments: MAX_NUM_PCI_SEGMENTS + 1,
..Default::default() ..Default::default()
}); });
assert_eq!( assert_eq!(
invalid_config.validate(), invalid_config.validate(),
Err(ValidationError::InvalidNumPciSegments(17)) Err(ValidationError::InvalidNumPciSegments(
MAX_NUM_PCI_SEGMENTS + 1
))
); );
let mut still_valid_config = valid_config.clone(); let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig { still_valid_config.platform = Some(PlatformConfig {
num_pci_segments: 16, num_pci_segments: MAX_NUM_PCI_SEGMENTS,
iommu_segments: Some(vec![1, 2, 3]), iommu_segments: Some(vec![1, 2, 3]),
..Default::default() ..Default::default()
}); });
@@ -2983,18 +2974,18 @@ mod tests {
let mut invalid_config = valid_config.clone(); let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig { invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16, num_pci_segments: MAX_NUM_PCI_SEGMENTS,
iommu_segments: Some(vec![17, 18]), iommu_segments: Some(vec![MAX_NUM_PCI_SEGMENTS + 1, MAX_NUM_PCI_SEGMENTS + 2]),
..Default::default() ..Default::default()
}); });
assert_eq!( assert_eq!(
invalid_config.validate(), invalid_config.validate(),
Err(ValidationError::InvalidPciSegment(17)) Err(ValidationError::InvalidPciSegment(MAX_NUM_PCI_SEGMENTS + 1))
); );
let mut still_valid_config = valid_config.clone(); let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig { still_valid_config.platform = Some(PlatformConfig {
num_pci_segments: 16, num_pci_segments: MAX_NUM_PCI_SEGMENTS,
iommu_segments: Some(vec![1, 2, 3]), iommu_segments: Some(vec![1, 2, 3]),
..Default::default() ..Default::default()
}); });
@@ -3007,24 +2998,20 @@ mod tests {
let mut still_valid_config = valid_config.clone(); let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig { still_valid_config.platform = Some(PlatformConfig {
num_pci_segments: 16, num_pci_segments: MAX_NUM_PCI_SEGMENTS,
iommu_segments: Some(vec![1, 2, 3]), iommu_segments: Some(vec![1, 2, 3]),
..Default::default() ..Default::default()
}); });
still_valid_config.net = Some(vec![NetConfig { still_valid_config.net = Some(vec![NetConfig {
iommu: true, iommu: true,
pci_segment: 1, pci_segment: 1,
fds: None,
id: None,
tap: None,
vhost_socket: None,
..Default::default() ..Default::default()
}]); }]);
assert!(still_valid_config.validate().is_ok()); assert!(still_valid_config.validate().is_ok());
let mut still_valid_config = valid_config.clone(); let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig { still_valid_config.platform = Some(PlatformConfig {
num_pci_segments: 16, num_pci_segments: MAX_NUM_PCI_SEGMENTS,
iommu_segments: Some(vec![1, 2, 3]), iommu_segments: Some(vec![1, 2, 3]),
..Default::default() ..Default::default()
}); });
@@ -3037,7 +3024,7 @@ mod tests {
let mut still_valid_config = valid_config.clone(); let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig { still_valid_config.platform = Some(PlatformConfig {
num_pci_segments: 16, num_pci_segments: MAX_NUM_PCI_SEGMENTS,
iommu_segments: Some(vec![1, 2, 3]), iommu_segments: Some(vec![1, 2, 3]),
..Default::default() ..Default::default()
}); });
@@ -3050,7 +3037,7 @@ mod tests {
let mut still_valid_config = valid_config.clone(); let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig { still_valid_config.platform = Some(PlatformConfig {
num_pci_segments: 16, num_pci_segments: MAX_NUM_PCI_SEGMENTS,
iommu_segments: Some(vec![1, 2, 3]), iommu_segments: Some(vec![1, 2, 3]),
..Default::default() ..Default::default()
}); });
@@ -3063,7 +3050,7 @@ mod tests {
let mut invalid_config = valid_config.clone(); let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig { invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16, num_pci_segments: MAX_NUM_PCI_SEGMENTS,
iommu_segments: Some(vec![1, 2, 3]), iommu_segments: Some(vec![1, 2, 3]),
..Default::default() ..Default::default()
}); });
@@ -3079,17 +3066,13 @@ mod tests {
let mut invalid_config = valid_config.clone(); let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig { invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16, num_pci_segments: MAX_NUM_PCI_SEGMENTS,
iommu_segments: Some(vec![1, 2, 3]), iommu_segments: Some(vec![1, 2, 3]),
..Default::default() ..Default::default()
}); });
invalid_config.net = Some(vec![NetConfig { invalid_config.net = Some(vec![NetConfig {
iommu: false, iommu: false,
pci_segment: 1, pci_segment: 1,
fds: None,
id: None,
tap: None,
vhost_socket: None,
..Default::default() ..Default::default()
}]); }]);
assert_eq!( assert_eq!(
@@ -3099,7 +3082,7 @@ mod tests {
let mut invalid_config = valid_config.clone(); let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig { invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16, num_pci_segments: MAX_NUM_PCI_SEGMENTS,
iommu_segments: Some(vec![1, 2, 3]), iommu_segments: Some(vec![1, 2, 3]),
..Default::default() ..Default::default()
}); });
@@ -3115,7 +3098,7 @@ mod tests {
let mut invalid_config = valid_config.clone(); let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig { invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16, num_pci_segments: MAX_NUM_PCI_SEGMENTS,
iommu_segments: Some(vec![1, 2, 3]), iommu_segments: Some(vec![1, 2, 3]),
..Default::default() ..Default::default()
}); });
@@ -3131,7 +3114,7 @@ mod tests {
let mut invalid_config = valid_config.clone(); let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig { invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16, num_pci_segments: MAX_NUM_PCI_SEGMENTS,
iommu_segments: Some(vec![1, 2, 3]), iommu_segments: Some(vec![1, 2, 3]),
..Default::default() ..Default::default()
}); });
@@ -3148,7 +3131,7 @@ mod tests {
let mut invalid_config = valid_config.clone(); let mut invalid_config = valid_config.clone();
invalid_config.memory.shared = true; invalid_config.memory.shared = true;
invalid_config.platform = Some(PlatformConfig { invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16, num_pci_segments: MAX_NUM_PCI_SEGMENTS,
iommu_segments: Some(vec![1, 2, 3]), iommu_segments: Some(vec![1, 2, 3]),
..Default::default() ..Default::default()
}); });
@@ -3163,7 +3146,7 @@ mod tests {
let mut invalid_config = valid_config.clone(); let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig { invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16, num_pci_segments: MAX_NUM_PCI_SEGMENTS,
iommu_segments: Some(vec![1, 2, 3]), iommu_segments: Some(vec![1, 2, 3]),
..Default::default() ..Default::default()
}); });
@@ -3179,7 +3162,7 @@ mod tests {
let mut invalid_config = valid_config.clone(); let mut invalid_config = valid_config.clone();
invalid_config.memory.shared = true; invalid_config.memory.shared = true;
invalid_config.platform = Some(PlatformConfig { invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16, num_pci_segments: MAX_NUM_PCI_SEGMENTS,
iommu_segments: Some(vec![1, 2, 3]), iommu_segments: Some(vec![1, 2, 3]),
..Default::default() ..Default::default()
}); });
@@ -3205,7 +3188,7 @@ mod tests {
]); ]);
assert!(still_valid_config.validate().is_ok()); assert!(still_valid_config.validate().is_ok());
let mut invalid_config = valid_config; let mut invalid_config = valid_config.clone();
invalid_config.devices = Some(vec![ invalid_config.devices = Some(vec![
DeviceConfig { DeviceConfig {
path: "/device1".into(), path: "/device1".into(),
@@ -3217,5 +3200,16 @@ mod tests {
}, },
]); ]);
assert!(invalid_config.validate().is_err()); assert!(invalid_config.validate().is_err());
let mut still_valid_config = valid_config;
// SAFETY: Safe as the file was just opened
let fd1 = unsafe { libc::dup(File::open("/dev/null").unwrap().as_raw_fd()) };
// SAFETY: Safe as the file was just opened
let fd2 = unsafe { libc::dup(File::open("/dev/null").unwrap().as_raw_fd()) };
// SAFETY: safe as both FDs are valid
unsafe {
still_valid_config.add_preserved_fds(vec![fd1, fd2]);
}
let _still_valid_config = still_valid_config.clone();
} }
} }

View File

@@ -52,6 +52,8 @@ use hypervisor::arch::x86::MsrEntry;
use hypervisor::arch::x86::{SpecialRegisters, StandardRegisters}; use hypervisor::arch::x86::{SpecialRegisters, StandardRegisters};
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
use hypervisor::kvm::kvm_bindings; use hypervisor::kvm::kvm_bindings;
#[cfg(all(target_arch = "aarch64", feature = "kvm"))]
use hypervisor::kvm::kvm_ioctls::Cap;
#[cfg(feature = "tdx")] #[cfg(feature = "tdx")]
use hypervisor::kvm::{TdxExitDetails, TdxExitStatus}; use hypervisor::kvm::{TdxExitDetails, TdxExitStatus};
use hypervisor::{CpuState, HypervisorCpuError, HypervisorType, VmExit, VmOps}; use hypervisor::{CpuState, HypervisorCpuError, HypervisorType, VmExit, VmOps};
@@ -82,6 +84,7 @@ use vm_migration::{
}; };
use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::signal::{register_signal_handler, SIGRTMIN}; use vmm_sys_util::signal::{register_signal_handler, SIGRTMIN};
use zerocopy::AsBytes;
#[cfg(all(target_arch = "aarch64", feature = "guest_debug"))] #[cfg(all(target_arch = "aarch64", feature = "guest_debug"))]
/// Extract the specified bits of a 64-bit integer. /// Extract the specified bits of a 64-bit integer.
@@ -95,6 +98,13 @@ macro_rules! extract_bits_64 {
}; };
} }
#[cfg(all(target_arch = "aarch64", feature = "guest_debug"))]
macro_rules! extract_bits_64_without_offset {
($value: tt, $length: tt) => {
$value & (!0u64 >> (64 - $length))
};
}
pub const CPU_MANAGER_ACPI_SIZE: usize = 0xc; pub const CPU_MANAGER_ACPI_SIZE: usize = 0xc;
#[derive(Debug, Error)] #[derive(Debug, Error)]
@@ -165,12 +175,16 @@ pub enum Error {
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
#[error("Error setting up AMX: {0}")] #[error("Error setting up AMX: {0}")]
AmxEnable(#[source] anyhow::Error), AmxEnable(#[source] anyhow::Error),
#[error("Maximum number of vCPUs exceeds host limit")]
MaximumVcpusExceeded,
} }
pub type Result<T> = result::Result<T, Error>; pub type Result<T> = result::Result<T, Error>;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
#[allow(dead_code)] #[allow(dead_code)]
#[repr(packed)] #[repr(packed)]
#[derive(AsBytes)]
struct LocalApic { struct LocalApic {
pub r#type: u8, pub r#type: u8,
pub length: u8, pub length: u8,
@@ -181,7 +195,7 @@ struct LocalApic {
#[allow(dead_code)] #[allow(dead_code)]
#[repr(packed)] #[repr(packed)]
#[derive(Default)] #[derive(Default, AsBytes)]
struct Ioapic { struct Ioapic {
pub r#type: u8, pub r#type: u8,
pub length: u8, pub length: u8,
@@ -194,6 +208,7 @@ struct Ioapic {
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
#[allow(dead_code)] #[allow(dead_code)]
#[repr(packed)] #[repr(packed)]
#[derive(AsBytes)]
struct GicC { struct GicC {
pub r#type: u8, pub r#type: u8,
pub length: u8, pub length: u8,
@@ -218,6 +233,7 @@ struct GicC {
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
#[allow(dead_code)] #[allow(dead_code)]
#[repr(packed)] #[repr(packed)]
#[derive(AsBytes)]
struct GicD { struct GicD {
pub r#type: u8, pub r#type: u8,
pub length: u8, pub length: u8,
@@ -232,6 +248,7 @@ struct GicD {
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
#[allow(dead_code)] #[allow(dead_code)]
#[repr(packed)] #[repr(packed)]
#[derive(AsBytes)]
struct GicR { struct GicR {
pub r#type: u8, pub r#type: u8,
pub length: u8, pub length: u8,
@@ -243,6 +260,7 @@ struct GicR {
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
#[allow(dead_code)] #[allow(dead_code)]
#[repr(packed)] #[repr(packed)]
#[derive(AsBytes)]
struct GicIts { struct GicIts {
pub r#type: u8, pub r#type: u8,
pub length: u8, pub length: u8,
@@ -255,6 +273,7 @@ struct GicIts {
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
#[allow(dead_code)] #[allow(dead_code)]
#[repr(packed)] #[repr(packed)]
#[derive(AsBytes)]
struct ProcessorHierarchyNode { struct ProcessorHierarchyNode {
pub r#type: u8, pub r#type: u8,
pub length: u8, pub length: u8,
@@ -267,7 +286,7 @@ struct ProcessorHierarchyNode {
#[allow(dead_code)] #[allow(dead_code)]
#[repr(packed)] #[repr(packed)]
#[derive(Default)] #[derive(Default, AsBytes)]
struct InterruptSourceOverride { struct InterruptSourceOverride {
pub r#type: u8, pub r#type: u8,
pub length: u8, pub length: u8,
@@ -370,7 +389,14 @@ impl Vcpu {
.map_err(Error::VcpuArmPreferredTarget)?; .map_err(Error::VcpuArmPreferredTarget)?;
// We already checked that the capability is supported. // We already checked that the capability is supported.
kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PSCI_0_2; kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PSCI_0_2;
kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PMU_V3; if vm
.as_any()
.downcast_ref::<hypervisor::kvm::KvmVm>()
.unwrap()
.check_extension(Cap::ArmPmuV3)
{
kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PMU_V3;
}
// Non-boot cpus are powered off initially. // Non-boot cpus are powered off initially.
if self.id > 0 { if self.id > 0 {
kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_POWER_OFF; kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_POWER_OFF;
@@ -580,6 +606,10 @@ impl CpuManager {
#[cfg(feature = "tdx")] tdx_enabled: bool, #[cfg(feature = "tdx")] tdx_enabled: bool,
numa_nodes: &NumaNodes, numa_nodes: &NumaNodes,
) -> Result<Arc<Mutex<CpuManager>>> { ) -> Result<Arc<Mutex<CpuManager>>> {
if u32::from(config.max_vcpus) > hypervisor.get_max_vcpus() {
return Err(Error::MaximumVcpusExceeded);
}
let mut vcpu_states = Vec::with_capacity(usize::from(config.max_vcpus)); let mut vcpu_states = Vec::with_capacity(usize::from(config.max_vcpus));
vcpu_states.resize_with(usize::from(config.max_vcpus), VcpuState::default); vcpu_states.resize_with(usize::from(config.max_vcpus), VcpuState::default);
let hypervisor_type = hypervisor.hypervisor_type(); let hypervisor_type = hypervisor.hypervisor_type();
@@ -680,14 +710,23 @@ impl CpuManager {
.sgx_epc_region() .sgx_epc_region()
.as_ref() .as_ref()
.map(|sgx_epc_region| sgx_epc_region.epc_sections().values().cloned().collect()); .map(|sgx_epc_region| sgx_epc_region.epc_sections().values().cloned().collect());
let topology = self.config.topology.clone().map_or_else(
|| {
#[cfg(feature = "mshv")]
if matches!(hypervisor.hypervisor_type(), HypervisorType::Mshv) {
return Some((1, self.boot_vcpus(), 1));
}
None
},
|t| Some((t.threads_per_core, t.cores_per_die, t.dies_per_package)),
);
self.cpuid = { self.cpuid = {
let phys_bits = physical_bits(self.config.max_phys_bits); let phys_bits = physical_bits(hypervisor, self.config.max_phys_bits);
arch::generate_common_cpuid( arch::generate_common_cpuid(
hypervisor, hypervisor,
self.config topology,
.topology
.clone()
.map(|t| (t.threads_per_core, t.cores_per_die, t.dies_per_package)),
sgx_epc_sections, sgx_epc_sections,
phys_bits, phys_bits,
self.config.kvm_hyperv, self.config.kvm_hyperv,
@@ -1099,7 +1138,7 @@ impl CpuManager {
fn remove_vcpu(&mut self, cpu_id: u8) -> Result<()> { fn remove_vcpu(&mut self, cpu_id: u8) -> Result<()> {
info!("Removing vCPU: cpu_id = {}", cpu_id); info!("Removing vCPU: cpu_id = {}", cpu_id);
let mut state = &mut self.vcpu_states[usize::from(cpu_id)]; let state = &mut self.vcpu_states[usize::from(cpu_id)];
state.kill.store(true, Ordering::SeqCst); state.kill.store(true, Ordering::SeqCst);
state.signal_thread(); state.signal_thread();
state.join_thread()?; state.join_thread()?;
@@ -1251,7 +1290,7 @@ impl CpuManager {
let mut madt = Sdt::new(*b"APIC", 44, 5, *b"CLOUDH", *b"CHMADT ", 1); let mut madt = Sdt::new(*b"APIC", 44, 5, *b"CLOUDH", *b"CHMADT ", 1);
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
{ {
madt.write(36, arch::layout::APIC_START); madt.write(36, arch::layout::APIC_START.0);
for cpu in 0..self.config.max_vcpus { for cpu in 0..self.config.max_vcpus {
let lapic = LocalApic { let lapic = LocalApic {
@@ -1579,8 +1618,7 @@ impl CpuManager {
// PA or IPA size is determined // PA or IPA size is determined
let tcr_ips = extract_bits_64!(tcr_el1, 32, 3); let tcr_ips = extract_bits_64!(tcr_el1, 32, 3);
#[allow(clippy::identity_op)] let pa_range = extract_bits_64_without_offset!(id_aa64mmfr0_el1, 4);
let pa_range = extract_bits_64!(id_aa64mmfr0_el1, 0, 4);
// The IPA size in TCR_BL1 and PA Range in ID_AA64MMFR0_EL1 should match. // The IPA size in TCR_BL1 and PA Range in ID_AA64MMFR0_EL1 should match.
// To be safe, we use the minimum value if they are different. // To be safe, we use the minimum value if they are different.
let pa_range = std::cmp::min(tcr_ips, pa_range); let pa_range = std::cmp::min(tcr_ips, pa_range);
@@ -1613,8 +1651,7 @@ impl CpuManager {
let descaddrmask = descaddrmask & !indexmask_grainsize; let descaddrmask = descaddrmask & !indexmask_grainsize;
// Translation table base address // Translation table base address
#[allow(clippy::identity_op)] let mut descaddr: u64 = extract_bits_64_without_offset!(ttbr1_el1, 48);
let mut descaddr: u64 = extract_bits_64!(ttbr1_el1, 0, 48);
// In the case of FEAT_LPA and FEAT_LPA2, the initial translation table // In the case of FEAT_LPA and FEAT_LPA2, the initial translation table
// addresss bits [48:51] comes from TTBR1_EL1 bits [2:5]. // addresss bits [48:51] comes from TTBR1_EL1 bits [2:5].
if pa_size == 52 { if pa_size == 52 {
@@ -1955,6 +1992,7 @@ impl Aml for CpuManager {
true, true,
acpi_address.0, acpi_address.0,
acpi_address.0 + CPU_MANAGER_ACPI_SIZE as u64 - 1, acpi_address.0 + CPU_MANAGER_ACPI_SIZE as u64 - 1,
None,
)]), )]),
), ),
// OpRegion and Fields map MMIO range into individual field values // OpRegion and Fields map MMIO range into individual field values

View File

@@ -831,6 +831,9 @@ pub struct DeviceManager {
// pty foreground status, // pty foreground status,
console_resize_pipe: Option<Arc<File>>, console_resize_pipe: Option<Arc<File>>,
// To restore on exit.
original_termios_opt: Arc<Mutex<Option<termios>>>,
// Interrupt controller // Interrupt controller
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
interrupt_controller: Option<Arc<Mutex<ioapic::Ioapic>>>, interrupt_controller: Option<Arc<Mutex<ioapic::Ioapic>>>,
@@ -1115,6 +1118,7 @@ impl DeviceManager {
serial_manager: None, serial_manager: None,
console_pty: None, console_pty: None,
console_resize_pipe: None, console_resize_pipe: None,
original_termios_opt: Arc::new(Mutex::new(None)),
virtio_mem_devices: Vec::new(), virtio_mem_devices: Vec::new(),
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
gpio_device: None, gpio_device: None,
@@ -1162,6 +1166,7 @@ impl DeviceManager {
serial_pty: Option<PtyPair>, serial_pty: Option<PtyPair>,
console_pty: Option<PtyPair>, console_pty: Option<PtyPair>,
console_resize_pipe: Option<File>, console_resize_pipe: Option<File>,
original_termios_opt: Arc<Mutex<Option<termios>>>,
) -> DeviceManagerResult<()> { ) -> DeviceManagerResult<()> {
trace_scoped!("create_devices"); trace_scoped!("create_devices");
@@ -1217,6 +1222,8 @@ impl DeviceManager {
)?; )?;
} }
self.original_termios_opt = original_termios_opt;
self.console = self.add_console_device( self.console = self.add_console_device(
&legacy_interrupt_manager, &legacy_interrupt_manager,
&mut virtio_devices, &mut virtio_devices,
@@ -1836,7 +1843,7 @@ impl DeviceManager {
} }
fn modify_mode<F: FnOnce(&mut termios)>( fn modify_mode<F: FnOnce(&mut termios)>(
&self, &mut self,
fd: RawFd, fd: RawFd,
f: F, f: F,
) -> vmm_sys_util::errno::Result<()> { ) -> vmm_sys_util::errno::Result<()> {
@@ -1853,6 +1860,10 @@ impl DeviceManager {
if ret < 0 { if ret < 0 {
return vmm_sys_util::errno::errno_result(); return vmm_sys_util::errno::errno_result();
} }
let mut original_termios_opt = self.original_termios_opt.lock().unwrap();
if original_termios_opt.is_none() {
*original_termios_opt = Some(termios);
}
f(&mut termios); f(&mut termios);
// SAFETY: Safe because the syscall will only read the extent of termios and we check // SAFETY: Safe because the syscall will only read the extent of termios and we check
// the return result. // the return result.
@@ -1864,7 +1875,7 @@ impl DeviceManager {
Ok(()) Ok(())
} }
fn set_raw_mode(&self, f: &mut File) -> vmm_sys_util::errno::Result<()> { fn set_raw_mode(&mut self, f: &mut dyn AsRawFd) -> vmm_sys_util::errno::Result<()> {
// SAFETY: FFI call. Variable t is guaranteed to be a valid termios from modify_mode. // SAFETY: FFI call. Variable t is guaranteed to be a valid termios from modify_mode.
self.modify_mode(f.as_raw_fd(), |t| unsafe { cfmakeraw(t) }) self.modify_mode(f.as_raw_fd(), |t| unsafe { cfmakeraw(t) })
} }
@@ -1926,7 +1937,10 @@ impl DeviceManager {
return vmm_sys_util::errno::errno_result().map_err(DeviceManagerError::DupFd); return vmm_sys_util::errno::errno_result().map_err(DeviceManagerError::DupFd);
} }
// SAFETY: stdout is valid and owned solely by us. // SAFETY: stdout is valid and owned solely by us.
let stdout = unsafe { File::from_raw_fd(stdout) }; let mut stdout = unsafe { File::from_raw_fd(stdout) };
// Make sure stdout is in raw mode, if it's a terminal.
let _ = self.set_raw_mode(&mut stdout);
// SAFETY: FFI call. Trivially safe. // SAFETY: FFI call. Trivially safe.
if unsafe { libc::isatty(libc::STDOUT_FILENO) } == 1 { if unsafe { libc::isatty(libc::STDOUT_FILENO) } == 1 {
@@ -2025,7 +2039,11 @@ impl DeviceManager {
} }
None None
} }
ConsoleOutputMode::Tty => Some(Box::new(stdout())), ConsoleOutputMode::Tty => {
let mut out = stdout();
let _ = self.set_raw_mode(&mut out);
Some(Box::new(out))
}
ConsoleOutputMode::Off | ConsoleOutputMode::Null => None, ConsoleOutputMode::Off | ConsoleOutputMode::Null => None,
}; };
if serial_config.mode != ConsoleOutputMode::Off { if serial_config.mode != ConsoleOutputMode::Off {
@@ -2388,26 +2406,31 @@ impl DeviceManager {
.map_err(DeviceManagerError::CreateVirtioNet)?, .map_err(DeviceManagerError::CreateVirtioNet)?,
)) ))
} else if let Some(fds) = &net_cfg.fds { } else if let Some(fds) = &net_cfg.fds {
Arc::new(Mutex::new( let net = virtio_devices::Net::from_tap_fds(
virtio_devices::Net::from_tap_fds( id.clone(),
id.clone(), fds,
fds, Some(net_cfg.mac),
Some(net_cfg.mac), net_cfg.mtu,
net_cfg.mtu, self.force_iommu | net_cfg.iommu,
self.force_iommu | net_cfg.iommu, net_cfg.queue_size,
net_cfg.queue_size, self.seccomp_action.clone(),
self.seccomp_action.clone(), net_cfg.rate_limiter_config,
net_cfg.rate_limiter_config, self.exit_evt
self.exit_evt .try_clone()
.try_clone() .map_err(DeviceManagerError::EventFd)?,
.map_err(DeviceManagerError::EventFd)?, state,
state, net_cfg.offload_tso,
net_cfg.offload_tso, net_cfg.offload_ufo,
net_cfg.offload_ufo, net_cfg.offload_csum,
net_cfg.offload_csum, )
) .map_err(DeviceManagerError::CreateVirtioNet)?;
.map_err(DeviceManagerError::CreateVirtioNet)?,
)) // SAFETY: 'fds' are valid because TAP devices are created successfully
unsafe {
self.config.lock().unwrap().add_preserved_fds(fds.clone());
}
Arc::new(Mutex::new(net))
} else { } else {
Arc::new(Mutex::new( Arc::new(Mutex::new(
virtio_devices::Net::new( virtio_devices::Net::new(
@@ -4255,7 +4278,7 @@ impl Aml for DeviceManager {
let mut pci_scan_methods = Vec::new(); let mut pci_scan_methods = Vec::new();
for i in 0..self.pci_segments.len() { for i in 0..self.pci_segments.len() {
pci_scan_methods.push(aml::MethodCall::new( pci_scan_methods.push(aml::MethodCall::new(
format!("\\_SB_.PCI{i:X}.PCNT").as_str().into(), format!("\\_SB_.PC{i:02X}.PCNT").as_str().into(),
vec![], vec![],
)); ));
} }
@@ -4279,6 +4302,7 @@ impl Aml for DeviceManager {
true, true,
self.acpi_address.0, self.acpi_address.0,
self.acpi_address.0 + DEVICE_MANAGER_ACPI_SIZE as u64 - 1, self.acpi_address.0 + DEVICE_MANAGER_ACPI_SIZE as u64 - 1,
None,
)]), )]),
), ),
// OpRegion and Fields map MMIO range into individual field values // OpRegion and Fields map MMIO range into individual field values
@@ -4631,5 +4655,10 @@ impl Drop for DeviceManager {
for handle in self.virtio_devices.drain(..) { for handle in self.virtio_devices.drain(..) {
handle.virtio_device.lock().unwrap().shutdown(); handle.virtio_device.lock().unwrap().shutdown();
} }
if let Some(termios) = *self.original_termios_opt.lock().unwrap() {
// SAFETY: FFI call
let _ = unsafe { tcsetattr(stdout().lock().as_raw_fd(), TCSANOW, &termios) };
}
} }
} }

View File

@@ -109,7 +109,8 @@ pub struct BftIter<'a> {
impl<'a> BftIter<'a> { impl<'a> BftIter<'a> {
fn new(hash_map: &'a HashMap<String, DeviceNode>) -> Self { fn new(hash_map: &'a HashMap<String, DeviceNode>) -> Self {
let mut nodes = Vec::new(); let mut nodes = Vec::with_capacity(hash_map.len());
let mut i = 0;
for (_, node) in hash_map.iter() { for (_, node) in hash_map.iter() {
if node.parent.is_none() { if node.parent.is_none() {
@@ -117,26 +118,13 @@ impl<'a> BftIter<'a> {
} }
} }
let mut node_layer = nodes.as_slice(); while i < nodes.len() {
loop { for child_node_id in nodes[i].children.iter() {
let mut next_node_layer = Vec::new(); if let Some(child_node) = hash_map.get(child_node_id) {
nodes.push(child_node);
for node in node_layer.iter() {
for child_node_id in node.children.iter() {
if let Some(child_node) = hash_map.get(child_node_id) {
next_node_layer.push(child_node);
}
} }
} }
i += 1;
if next_node_layer.is_empty() {
break;
}
let pos = nodes.len();
nodes.extend(next_node_layer);
node_layer = &nodes[pos..];
} }
BftIter { nodes } BftIter { nodes }

View File

@@ -435,7 +435,6 @@ impl run_blocking::BlockingEventLoop for GdbEventLoop {
type Connection = Box<dyn ConnectionExt<Error = std::io::Error>>; type Connection = Box<dyn ConnectionExt<Error = std::io::Error>>;
type StopReason = MultiThreadStopReason<ArchUsize>; type StopReason = MultiThreadStopReason<ArchUsize>;
#[allow(clippy::type_complexity)]
fn wait_for_stop_reason( fn wait_for_stop_reason(
target: &mut Self::Target, target: &mut Self::Target,
conn: &mut Self::Connection, conn: &mut Self::Connection,

View File

@@ -25,7 +25,9 @@ use crate::migration::{recv_vm_config, recv_vm_state};
use crate::seccomp_filters::{get_seccomp_filter, Thread}; use crate::seccomp_filters::{get_seccomp_filter, Thread};
use crate::vm::{Error as VmError, Vm, VmState}; use crate::vm::{Error as VmError, Vm, VmState};
use anyhow::anyhow; use anyhow::anyhow;
use libc::{EFD_NONBLOCK, SIGINT, SIGTERM}; #[cfg(feature = "dbus_api")]
use api::dbus::{DBusApiOptions, DBusApiShutdownChannels};
use libc::{tcsetattr, termios, EFD_NONBLOCK, SIGINT, SIGTERM, TCSANOW};
use memory_manager::MemoryManagerSnapshotData; use memory_manager::MemoryManagerSnapshotData;
use pci::PciBdf; use pci::PciBdf;
use seccompiler::{apply_filter, SeccompAction}; use seccompiler::{apply_filter, SeccompAction};
@@ -35,7 +37,7 @@ use signal_hook::iterator::{Handle, Signals};
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
use std::io::{Read, Write}; use std::io::{stdout, Read, Write};
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::os::unix::net::UnixListener; use std::os::unix::net::UnixListener;
use std::os::unix::net::UnixStream; use std::os::unix::net::UnixStream;
@@ -53,7 +55,6 @@ use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable, Transport
use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::signal::unblock_signal; use vmm_sys_util::signal::unblock_signal;
use vmm_sys_util::sock_ctrl_msg::ScmSocket; use vmm_sys_util::sock_ctrl_msg::ScmSocket;
use vmm_sys_util::terminal::Terminal;
mod acpi; mod acpi;
pub mod api; pub mod api;
@@ -81,7 +82,6 @@ type GuestRegionMmap = vm_memory::GuestRegionMmap<AtomicBitmap>;
/// Errors associated with VMM management /// Errors associated with VMM management
#[derive(Debug, Error)] #[derive(Debug, Error)]
#[allow(clippy::large_enum_variant)]
pub enum Error { pub enum Error {
/// API request receive error /// API request receive error
#[error("Error receiving API request: {0}")] #[error("Error receiving API request: {0}")]
@@ -115,6 +115,16 @@ pub enum Error {
#[error("Error spawning HTTP thread: {0}")] #[error("Error spawning HTTP thread: {0}")]
HttpThreadSpawn(#[source] io::Error), HttpThreadSpawn(#[source] io::Error),
/// Cannot create D-Bus thread
#[cfg(feature = "dbus_api")]
#[error("Error spawning D-Bus thread: {0}")]
DBusThreadSpawn(#[source] io::Error),
/// Cannot start D-Bus session
#[cfg(feature = "dbus_api")]
#[error("Error starting D-Bus session: {0}")]
CreateDBusSession(#[source] zbus::Error),
/// Cannot handle the VM STDIN stream /// Cannot handle the VM STDIN stream
#[error("Error handling VM stdin: {0:?}")] #[error("Error handling VM stdin: {0:?}")]
Stdin(VmError), Stdin(VmError),
@@ -281,18 +291,20 @@ impl Serialize for PciDeviceInfo {
#[allow(unused_variables)] #[allow(unused_variables)]
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn start_vmm_thread( pub fn start_vmm_thread(
vmm_version: String, vmm_version: VmmVersionInfo,
http_path: &Option<String>, http_path: &Option<String>,
http_fd: Option<RawFd>, http_fd: Option<RawFd>,
#[cfg(feature = "dbus_api")] dbus_options: Option<DBusApiOptions>,
api_event: EventFd, api_event: EventFd,
api_sender: Sender<ApiRequest>, api_sender: Sender<ApiRequest>,
api_receiver: Receiver<ApiRequest>, api_receiver: Receiver<ApiRequest>,
#[cfg(feature = "guest_debug")] debug_path: Option<PathBuf>, #[cfg(feature = "guest_debug")] debug_path: Option<PathBuf>,
#[cfg(feature = "guest_debug")] debug_event: EventFd, #[cfg(feature = "guest_debug")] debug_event: EventFd,
#[cfg(feature = "guest_debug")] vm_debug_event: EventFd, #[cfg(feature = "guest_debug")] vm_debug_event: EventFd,
exit_event: EventFd,
seccomp_action: &SeccompAction, seccomp_action: &SeccompAction,
hypervisor: Arc<dyn hypervisor::Hypervisor>, hypervisor: Arc<dyn hypervisor::Hypervisor>,
) -> Result<thread::JoinHandle<Result<()>>> { ) -> Result<VmmThreadHandle> {
#[cfg(feature = "guest_debug")] #[cfg(feature = "guest_debug")]
let gdb_hw_breakpoints = hypervisor.get_guest_debug_hw_bps(); let gdb_hw_breakpoints = hypervisor.get_guest_debug_hw_bps();
#[cfg(feature = "guest_debug")] #[cfg(feature = "guest_debug")]
@@ -302,7 +314,7 @@ pub fn start_vmm_thread(
#[cfg(feature = "guest_debug")] #[cfg(feature = "guest_debug")]
let gdb_vm_debug_event = vm_debug_event.try_clone().map_err(Error::EventFdClone)?; let gdb_vm_debug_event = vm_debug_event.try_clone().map_err(Error::EventFdClone)?;
let http_api_event = api_event.try_clone().map_err(Error::EventFdClone)?; let api_event_clone = api_event.try_clone().map_err(Error::EventFdClone)?;
let hypervisor_type = hypervisor.hypervisor_type(); let hypervisor_type = hypervisor.hypervisor_type();
// Retrieve seccomp filter // Retrieve seccomp filter
@@ -310,9 +322,8 @@ pub fn start_vmm_thread(
.map_err(Error::CreateSeccompFilter)?; .map_err(Error::CreateSeccompFilter)?;
let vmm_seccomp_action = seccomp_action.clone(); let vmm_seccomp_action = seccomp_action.clone();
let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?;
let thread = { let thread = {
let exit_evt = exit_evt.try_clone().map_err(Error::EventFdClone)?; let exit_event = exit_event.try_clone().map_err(Error::EventFdClone)?;
thread::Builder::new() thread::Builder::new()
.name("vmm".to_string()) .name("vmm".to_string())
.spawn(move || { .spawn(move || {
@@ -322,7 +333,7 @@ pub fn start_vmm_thread(
} }
let mut vmm = Vmm::new( let mut vmm = Vmm::new(
vmm_version.to_string(), vmm_version,
api_event, api_event,
#[cfg(feature = "guest_debug")] #[cfg(feature = "guest_debug")]
debug_event, debug_event,
@@ -330,7 +341,7 @@ pub fn start_vmm_thread(
vm_debug_event, vm_debug_event,
vmm_seccomp_action, vmm_seccomp_action,
hypervisor, hypervisor,
exit_evt, exit_event,
)?; )?;
vmm.setup_signal_handler()?; vmm.setup_signal_handler()?;
@@ -344,23 +355,40 @@ pub fn start_vmm_thread(
.map_err(Error::VmmThreadSpawn)? .map_err(Error::VmmThreadSpawn)?
}; };
// The VMM thread is started, we can start serving HTTP requests // The VMM thread is started, we can start the dbus thread
// and start serving HTTP requests
#[cfg(feature = "dbus_api")]
let dbus_shutdown_chs = match dbus_options {
Some(opts) => {
let (_, chs) = api::start_dbus_thread(
opts,
api_event_clone.try_clone().map_err(Error::EventFdClone)?,
api_sender.clone(),
seccomp_action,
exit_event.try_clone().map_err(Error::EventFdClone)?,
hypervisor_type,
)?;
Some(chs)
}
None => None,
};
if let Some(http_path) = http_path { if let Some(http_path) = http_path {
api::start_http_path_thread( api::start_http_path_thread(
http_path, http_path,
http_api_event, api_event_clone,
api_sender, api_sender,
seccomp_action, seccomp_action,
exit_evt, exit_event,
hypervisor_type, hypervisor_type,
)?; )?;
} else if let Some(http_fd) = http_fd { } else if let Some(http_fd) = http_fd {
api::start_http_fd_thread( api::start_http_fd_thread(
http_fd, http_fd,
http_api_event, api_event_clone,
api_sender, api_sender,
seccomp_action, seccomp_action,
exit_evt, exit_event,
hypervisor_type, hypervisor_type,
)?; )?;
} }
@@ -379,7 +407,11 @@ pub fn start_vmm_thread(
.map_err(Error::GdbThreadSpawn)?; .map_err(Error::GdbThreadSpawn)?;
} }
Ok(thread) Ok(VmmThreadHandle {
thread_handle: thread,
#[cfg(feature = "dbus_api")]
dbus_shutdown_chs,
})
} }
#[derive(Clone, Deserialize, Serialize)] #[derive(Clone, Deserialize, Serialize)]
@@ -390,6 +422,27 @@ struct VmMigrationConfig {
memory_manager_data: MemoryManagerSnapshotData, memory_manager_data: MemoryManagerSnapshotData,
} }
#[derive(Debug, Clone)]
pub struct VmmVersionInfo {
pub build_version: String,
pub version: String,
}
impl VmmVersionInfo {
pub fn new(build_version: &str, version: &str) -> Self {
Self {
build_version: build_version.to_owned(),
version: version.to_owned(),
}
}
}
pub struct VmmThreadHandle {
pub thread_handle: thread::JoinHandle<Result<()>>,
#[cfg(feature = "dbus_api")]
pub dbus_shutdown_chs: Option<DBusApiShutdownChannels>,
}
pub struct Vmm { pub struct Vmm {
epoll: EpollContext, epoll: EpollContext,
exit_evt: EventFd, exit_evt: EventFd,
@@ -399,7 +452,7 @@ pub struct Vmm {
debug_evt: EventFd, debug_evt: EventFd,
#[cfg(feature = "guest_debug")] #[cfg(feature = "guest_debug")]
vm_debug_evt: EventFd, vm_debug_evt: EventFd,
version: String, version: VmmVersionInfo,
vm: Option<Vm>, vm: Option<Vm>,
vm_config: Option<Arc<Mutex<VmConfig>>>, vm_config: Option<Arc<Mutex<VmConfig>>>,
seccomp_action: SeccompAction, seccomp_action: SeccompAction,
@@ -407,12 +460,17 @@ pub struct Vmm {
activate_evt: EventFd, activate_evt: EventFd,
signals: Option<Handle>, signals: Option<Handle>,
threads: Vec<thread::JoinHandle<()>>, threads: Vec<thread::JoinHandle<()>>,
original_termios_opt: Arc<Mutex<Option<termios>>>,
} }
impl Vmm { impl Vmm {
pub const HANDLED_SIGNALS: [i32; 2] = [SIGTERM, SIGINT]; pub const HANDLED_SIGNALS: [i32; 2] = [SIGTERM, SIGINT];
fn signal_handler(mut signals: Signals, on_tty: bool, exit_evt: &EventFd) { fn signal_handler(
mut signals: Signals,
original_termios_opt: Arc<Mutex<Option<termios>>>,
exit_evt: &EventFd,
) {
for sig in &Self::HANDLED_SIGNALS { for sig in &Self::HANDLED_SIGNALS {
unblock_signal(*sig).unwrap(); unblock_signal(*sig).unwrap();
} }
@@ -422,12 +480,17 @@ impl Vmm {
SIGTERM | SIGINT => { SIGTERM | SIGINT => {
if exit_evt.write(1).is_err() { if exit_evt.write(1).is_err() {
// Resetting the terminal is usually done as the VMM exits // Resetting the terminal is usually done as the VMM exits
if on_tty { if let Ok(lock) = original_termios_opt.lock() {
io::stdin() if let Some(termios) = *lock {
.lock() // SAFETY: FFI call
.set_canon_mode() let _ = unsafe {
.expect("failed to restore terminal mode"); tcsetattr(stdout().lock().as_raw_fd(), TCSANOW, &termios)
};
}
} else {
warn!("Failed to lock original termios");
} }
std::process::exit(1); std::process::exit(1);
} }
} }
@@ -442,8 +505,7 @@ impl Vmm {
Ok(signals) => { Ok(signals) => {
self.signals = Some(signals.handle()); self.signals = Some(signals.handle());
let exit_evt = self.exit_evt.try_clone().map_err(Error::EventFdClone)?; let exit_evt = self.exit_evt.try_clone().map_err(Error::EventFdClone)?;
// SAFETY: trivially safe let original_termios_opt = Arc::clone(&self.original_termios_opt);
let on_tty = unsafe { libc::isatty(libc::STDIN_FILENO) } != 0;
let signal_handler_seccomp_filter = get_seccomp_filter( let signal_handler_seccomp_filter = get_seccomp_filter(
&self.seccomp_action, &self.seccomp_action,
@@ -465,7 +527,7 @@ impl Vmm {
} }
} }
std::panic::catch_unwind(AssertUnwindSafe(|| { std::panic::catch_unwind(AssertUnwindSafe(|| {
Vmm::signal_handler(signals, on_tty, &exit_evt); Vmm::signal_handler(signals, original_termios_opt, &exit_evt);
})) }))
.map_err(|_| { .map_err(|_| {
error!("vmm signal_handler thread panicked"); error!("vmm signal_handler thread panicked");
@@ -482,7 +544,7 @@ impl Vmm {
} }
fn new( fn new(
vmm_version: String, vmm_version: VmmVersionInfo,
api_evt: EventFd, api_evt: EventFd,
#[cfg(feature = "guest_debug")] debug_evt: EventFd, #[cfg(feature = "guest_debug")] debug_evt: EventFd,
#[cfg(feature = "guest_debug")] vm_debug_evt: EventFd, #[cfg(feature = "guest_debug")] vm_debug_evt: EventFd,
@@ -532,6 +594,7 @@ impl Vmm {
activate_evt, activate_evt,
signals: None, signals: None,
threads: vec![], threads: vec![],
original_termios_opt: Arc::new(Mutex::new(None)),
}) })
} }
@@ -582,6 +645,7 @@ impl Vmm {
None, None,
None, None,
None, None,
Arc::clone(&self.original_termios_opt),
None, None,
None, None,
None, None,
@@ -680,6 +744,7 @@ impl Vmm {
None, None,
None, None,
None, None,
Arc::clone(&self.original_termios_opt),
Some(snapshot), Some(snapshot),
Some(source_url), Some(source_url),
Some(restore_cfg.prefault), Some(restore_cfg.prefault),
@@ -760,6 +825,7 @@ impl Vmm {
serial_pty, serial_pty,
console_pty, console_pty,
console_resize_pipe, console_resize_pipe,
Arc::clone(&self.original_termios_opt),
None, None,
None, None,
None, None,
@@ -802,8 +868,15 @@ impl Vmm {
} }
fn vmm_ping(&self) -> VmmPingResponse { fn vmm_ping(&self) -> VmmPingResponse {
let VmmVersionInfo {
build_version,
version,
} = self.version.clone();
VmmPingResponse { VmmPingResponse {
version: self.version.clone(), build_version,
version,
pid: std::process::id() as i64,
} }
} }
@@ -1186,7 +1259,8 @@ impl Vmm {
)) ))
})?; })?;
let phys_bits = vm::physical_bits(config.lock().unwrap().cpus.max_phys_bits); let phys_bits =
vm::physical_bits(&self.hypervisor, config.lock().unwrap().cpus.max_phys_bits);
let memory_manager = MemoryManager::new( let memory_manager = MemoryManager::new(
vm, vm,
@@ -1262,6 +1336,7 @@ impl Vmm {
None, None,
None, None,
None, None,
Arc::clone(&self.original_termios_opt),
Some(snapshot), Some(snapshot),
) )
.map_err(|e| { .map_err(|e| {
@@ -1501,7 +1576,8 @@ impl Vmm {
let vm_config = vm.get_config(); let vm_config = vm.get_config();
#[cfg(all(feature = "kvm", target_arch = "x86_64"))] #[cfg(all(feature = "kvm", target_arch = "x86_64"))]
let common_cpuid = { let common_cpuid = {
let phys_bits = vm::physical_bits(vm_config.lock().unwrap().cpus.max_phys_bits); let phys_bits =
vm::physical_bits(&hypervisor, vm_config.lock().unwrap().cpus.max_phys_bits);
arch::generate_common_cpuid( arch::generate_common_cpuid(
&hypervisor, &hypervisor,
None, None,
@@ -1691,7 +1767,7 @@ impl Vmm {
let dest_cpuid = &{ let dest_cpuid = &{
let vm_config = &src_vm_config.lock().unwrap(); let vm_config = &src_vm_config.lock().unwrap();
let phys_bits = vm::physical_bits(vm_config.cpus.max_phys_bits); let phys_bits = vm::physical_bits(&self.hypervisor, vm_config.cpus.max_phys_bits);
arch::generate_common_cpuid( arch::generate_common_cpuid(
&self.hypervisor.clone(), &self.hypervisor.clone(),
None, None,
@@ -2057,7 +2133,7 @@ mod unit_tests {
fn create_dummy_vmm() -> Vmm { fn create_dummy_vmm() -> Vmm {
Vmm::new( Vmm::new(
"dummy".to_string(), VmmVersionInfo::new("dummy", "dummy"),
EventFd::new(EFD_NONBLOCK).unwrap(), EventFd::new(EFD_NONBLOCK).unwrap(),
#[cfg(feature = "guest_debug")] #[cfg(feature = "guest_debug")]
EventFd::new(EFD_NONBLOCK).unwrap(), EventFd::new(EFD_NONBLOCK).unwrap(),
@@ -2130,6 +2206,7 @@ mod unit_tests {
gdb: false, gdb: false,
platform: None, platform: None,
tpm: None, tpm: None,
preserved_fds: None,
})) }))
} }

View File

@@ -31,7 +31,7 @@ use std::convert::TryInto;
use std::ffi; use std::ffi;
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::io::{self, Read}; use std::io::{self, Read};
use std::ops::Deref; use std::ops::{BitAnd, Deref, Not, Sub};
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::path::PathBuf; use std::path::PathBuf;
use std::result; use std::result;
@@ -330,6 +330,12 @@ pub enum Error {
/// Using a directory as a backing file for memory is not supported /// Using a directory as a backing file for memory is not supported
DirectoryAsBackingFileForMemory, DirectoryAsBackingFileForMemory,
/// Failed to stat filesystem
GetFileSystemBlockSize(io::Error),
/// Memory size is misaligned with default page size or its hugepage size
MisalignedMemorySize,
} }
const ENABLE_FLAG: usize = 0; const ENABLE_FLAG: usize = 0;
@@ -353,6 +359,77 @@ fn mmio_address_space_size(phys_bits: u8) -> u64 {
(1 << phys_bits) - (1 << 16) (1 << phys_bits) - (1 << 16)
} }
// The `statfs` function can get information of hugetlbfs, and the hugepage size is in the
// `f_bsize` field.
//
// See: https://github.com/torvalds/linux/blob/v6.3/fs/hugetlbfs/inode.c#L1169
fn statfs_get_bsize(path: &str) -> Result<u64, Error> {
let path = std::ffi::CString::new(path).map_err(|_| Error::InvalidMemoryParameters)?;
let mut buf = std::mem::MaybeUninit::<libc::statfs>::uninit();
// SAFETY: FFI call with a valid path and buffer
let ret = unsafe { libc::statfs(path.as_ptr(), buf.as_mut_ptr()) };
if ret != 0 {
return Err(Error::GetFileSystemBlockSize(
std::io::Error::last_os_error(),
));
}
// SAFETY: `buf` is valid at this point
// Because this value is always positive, just convert it directly.
// Note that the `f_bsize` is `i64` in glibc and `u64` in musl, using `as u64` will be warned
// by `clippy` on musl target. To avoid the warning, there should be `as _` instead of
// `as u64`.
let bsize = unsafe { (*buf.as_ptr()).f_bsize } as _;
Ok(bsize)
}
fn memory_zone_get_align_size(zone: &MemoryZoneConfig) -> Result<u64, Error> {
// SAFETY: FFI call. Trivially safe.
let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as u64 };
// There is no backend file and the `hugepages` is disabled, just use system page size.
if zone.file.is_none() && !zone.hugepages {
return Ok(page_size);
}
// The `hugepages` is enabled and the `hugepage_size` is specified, just use it directly.
if zone.hugepages && zone.hugepage_size.is_some() {
return Ok(zone.hugepage_size.unwrap());
}
// There are two scenarios here:
// - `hugepages` is enabled but `hugepage_size` is not specified:
// Call `statfs` for `/dev/hugepages` for getting the default size of hugepage
// - The backing file is specified:
// Call `statfs` for the file and get its `f_bsize`. If the value is larger than the page
// size of normal page, just use the `f_bsize` because the file is in a hugetlbfs. If the
// value is less than or equal to the page size, just use the page size.
let path = zone.file.as_ref().map_or(Ok("/dev/hugepages"), |pathbuf| {
pathbuf.to_str().ok_or(Error::InvalidMemoryParameters)
})?;
let align_size = std::cmp::max(page_size, statfs_get_bsize(path)?);
Ok(align_size)
}
#[inline]
fn align_down<T>(val: T, align: T) -> T
where
T: BitAnd<Output = T> + Not<Output = T> + Sub<Output = T> + From<u8>,
{
val & !(align - 1u8.into())
}
#[inline]
fn is_aligned<T>(val: T, align: T) -> bool
where
T: BitAnd<Output = T> + Sub<Output = T> + From<u8> + PartialEq,
{
(val & (align - 1u8.into())) == 0u8.into()
}
impl BusDevice for MemoryManager { impl BusDevice for MemoryManager {
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) { fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
if self.selected_slot < self.hotplug_slots.len() { if self.selected_slot < self.hotplug_slots.len() {
@@ -442,6 +519,9 @@ impl MemoryManager {
/// - First one mapping entirely the first memory zone on 0-1G range /// - First one mapping entirely the first memory zone on 0-1G range
/// - Second one mapping partially the second memory zone on 1G-3G range /// - Second one mapping partially the second memory zone on 1G-3G range
/// - Third one mapping partially the second memory zone on 4G-6G range /// - Third one mapping partially the second memory zone on 4G-6G range
/// Also, all memory regions are page-size aligned (e.g. their sizes must
/// be multiple of page-size), which may leave an additional hole in the
/// address space when hugepage is used.
fn create_memory_regions_from_zones( fn create_memory_regions_from_zones(
ram_regions: &[(GuestAddress, usize)], ram_regions: &[(GuestAddress, usize)],
zones: &[MemoryZoneConfig], zones: &[MemoryZoneConfig],
@@ -451,9 +531,14 @@ impl MemoryManager {
let mut zones = zones.to_owned(); let mut zones = zones.to_owned();
let mut mem_regions = Vec::new(); let mut mem_regions = Vec::new();
let mut zone = zones.remove(0); let mut zone = zones.remove(0);
let mut zone_offset = 0; let mut zone_align_size = memory_zone_get_align_size(&zone)?;
let mut zone_offset = 0u64;
let mut memory_zones = HashMap::new(); let mut memory_zones = HashMap::new();
if !is_aligned(zone.size, zone_align_size) {
return Err(Error::MisalignedMemorySize);
}
// Add zone id to the list of memory zones. // Add zone id to the list of memory zones.
memory_zones.insert(zone.id.clone(), MemoryZone::default()); memory_zones.insert(zone.id.clone(), MemoryZone::default());
@@ -465,16 +550,20 @@ impl MemoryManager {
let mut ram_region_consumed = false; let mut ram_region_consumed = false;
let mut pull_next_zone = false; let mut pull_next_zone = false;
let ram_region_sub_size = ram_region.1 - ram_region_offset; let ram_region_available_size =
let zone_sub_size = zone.size as usize - zone_offset; align_down(ram_region.1 as u64 - ram_region_offset, zone_align_size);
if ram_region_available_size == 0 {
break;
}
let zone_sub_size = zone.size - zone_offset;
let file_offset = zone_offset as u64; let file_offset = zone_offset;
let region_start = ram_region let region_start = ram_region
.0 .0
.checked_add(ram_region_offset as u64) .checked_add(ram_region_offset)
.ok_or(Error::GuestAddressOverFlow)?; .ok_or(Error::GuestAddressOverFlow)?;
let region_size = if zone_sub_size <= ram_region_sub_size { let region_size = if zone_sub_size <= ram_region_available_size {
if zone_sub_size == ram_region_sub_size { if zone_sub_size == ram_region_available_size {
ram_region_consumed = true; ram_region_consumed = true;
} }
@@ -483,21 +572,24 @@ impl MemoryManager {
zone_sub_size zone_sub_size
} else { } else {
zone_offset += ram_region_sub_size; zone_offset += ram_region_available_size;
ram_region_consumed = true; ram_region_consumed = true;
ram_region_sub_size ram_region_available_size
}; };
info!(
"create ram region for zone {}, region_start: {:#x}, region_size: {:#x}",
zone.id,
region_start.raw_value(),
region_size
);
let region = MemoryManager::create_ram_region( let region = MemoryManager::create_ram_region(
&zone.file, &zone.file,
file_offset, file_offset,
region_start, region_start,
region_size, region_size as usize,
match prefault { prefault.unwrap_or(zone.prefault),
Some(pf) => pf,
None => zone.prefault,
},
zone.shared, zone.shared,
zone.hugepages, zone.hugepages,
zone.hugepage_size, zone.hugepage_size,
@@ -522,6 +614,10 @@ impl MemoryManager {
break; break;
} }
zone = zones.remove(0); zone = zones.remove(0);
zone_align_size = memory_zone_get_align_size(&zone)?;
if !is_aligned(zone.size, zone_align_size) {
return Err(Error::MisalignedMemorySize);
}
// Check if zone id already exist. In case it does, throw // Check if zone id already exist. In case it does, throw
// an error as we need unique identifiers. Otherwise, add // an error as we need unique identifiers. Otherwise, add
@@ -573,10 +669,7 @@ impl MemoryManager {
guest_ram_mapping.file_offset, guest_ram_mapping.file_offset,
GuestAddress(guest_ram_mapping.gpa), GuestAddress(guest_ram_mapping.gpa),
guest_ram_mapping.size as usize, guest_ram_mapping.size as usize,
match prefault { prefault.unwrap_or(zone_config.prefault),
Some(pf) => pf,
None => zone_config.prefault,
},
zone_config.shared, zone_config.shared,
zone_config.hugepages, zone_config.hugepages,
zone_config.hugepage_size, zone_config.hugepage_size,
@@ -939,7 +1032,7 @@ impl MemoryManager {
) )
} else { } else {
// Init guest memory // Init guest memory
let arch_mem_regions = arch::arch_memory_regions(ram_size); let arch_mem_regions = arch::arch_memory_regions();
let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions
.iter() .iter()
@@ -997,10 +1090,7 @@ impl MemoryManager {
0, 0,
start_addr, start_addr,
hotplug_size as usize, hotplug_size as usize,
match prefault { prefault.unwrap_or(zone.prefault),
Some(pf) => pf,
None => zone.prefault,
},
zone.shared, zone.shared,
zone.hugepages, zone.hugepages,
zone.hugepage_size, zone.hugepage_size,
@@ -1501,7 +1591,7 @@ impl MemoryManager {
.ok_or(Error::MemoryRangeAllocation)?; .ok_or(Error::MemoryRangeAllocation)?;
// Update the slot so that it can be queried via the I/O port // Update the slot so that it can be queried via the I/O port
let mut slot = &mut self.hotplug_slots[self.next_hotplug_slot]; let slot = &mut self.hotplug_slots[self.next_hotplug_slot];
slot.active = true; slot.active = true;
slot.inserting = true; slot.inserting = true;
slot.base = region.start_addr().0; slot.base = region.start_addr().0;
@@ -2244,6 +2334,7 @@ impl Aml for MemoryMethods {
true, true,
0x0000_0000_0000_0000u64, 0x0000_0000_0000_0000u64,
0xFFFF_FFFF_FFFF_FFFEu64, 0xFFFF_FFFF_FFFF_FFFEu64,
None,
)]), )]),
), ),
&aml::CreateQWordField::new( &aml::CreateQWordField::new(
@@ -2326,6 +2417,7 @@ impl Aml for MemoryManager {
true, true,
acpi_address.0, acpi_address.0,
acpi_address.0 + MEMORY_MANAGER_ACPI_SIZE as u64 - 1, acpi_address.0 + MEMORY_MANAGER_ACPI_SIZE as u64 - 1,
None,
)]), )]),
), ),
// OpRegion and Fields map MMIO range into individual field values // OpRegion and Fields map MMIO range into individual field values
@@ -2421,6 +2513,7 @@ impl Aml for MemoryManager {
true, true,
min, min,
max, max,
None,
)]), )]),
), ),
&aml::Method::new("_STA".into(), 0, false, vec![&aml::Return::new(&0xfu8)]), &aml::Method::new("_STA".into(), 0, false, vec![&aml::Return::new(&0xfu8)]),

View File

@@ -353,17 +353,19 @@ impl Aml for PciSegment {
layout::MEM_32BIT_DEVICES_START.0 as u32, layout::MEM_32BIT_DEVICES_START.0 as u32,
(layout::MEM_32BIT_DEVICES_START.0 + layout::MEM_32BIT_DEVICES_SIZE - 1) (layout::MEM_32BIT_DEVICES_START.0 + layout::MEM_32BIT_DEVICES_SIZE - 1)
as u32, as u32,
None,
), ),
&aml::AddressSpace::new_memory( &aml::AddressSpace::new_memory(
aml::AddressSpaceCachable::NotCacheable, aml::AddressSpaceCachable::NotCacheable,
true, true,
self.start_of_device_area, self.start_of_device_area,
self.end_of_device_area, self.end_of_device_area,
None,
), ),
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
&aml::AddressSpace::new_io(0u16, 0x0cf7u16), &aml::AddressSpace::new_io(0u16, 0x0cf7u16, None),
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
&aml::AddressSpace::new_io(0x0d00u16, 0xffffu16), &aml::AddressSpace::new_io(0x0d00u16, 0xffffu16, None),
]), ]),
) )
} else { } else {
@@ -381,6 +383,7 @@ impl Aml for PciSegment {
true, true,
self.start_of_device_area, self.start_of_device_area,
self.end_of_device_area, self.end_of_device_area,
None,
), ),
]), ]),
) )
@@ -418,7 +421,7 @@ impl Aml for PciSegment {
pci_dsdt_inner_data.push(&prt); pci_dsdt_inner_data.push(&prt);
aml::Device::new( aml::Device::new(
format!("_SB_.PCI{:X}", self.id).as_str().into(), format!("_SB_.PC{:02X}", self.id).as_str().into(),
pci_dsdt_inner_data, pci_dsdt_inner_data,
) )
.to_aml_bytes(sink) .to_aml_bytes(sink)

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