Compare commits

..

139 Commits
v40.0 ... v41.0

Author SHA1 Message Date
Rob Bradford
ea3e2ff625 build: Release v41.0
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2024-08-15 16:50:09 +00:00
ihciah
3320015793 virtio-devices: net: reduce vec allocations for iovec conversion
Signed-off-by: ihciah <ihciah@gmail.com>
2024-08-15 11:56:46 +00:00
dependabot[bot]
9113544e21 build: Bump anstyle-wincon from 3.0.3 to 3.0.4
Bumps [anstyle-wincon](https://github.com/rust-cli/anstyle) from 3.0.3 to 3.0.4.
- [Commits](https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v3.0.3...anstyle-wincon-v3.0.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-15 07:02:09 +00:00
dependabot[bot]
8c526891ac build: Bump cc from 1.1.10 to 1.1.11 in /fuzz
Bumps [cc](https://github.com/rust-lang/cc-rs) from 1.1.10 to 1.1.11.
- [Release notes](https://github.com/rust-lang/cc-rs/releases)
- [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.10...cc-v1.1.11)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-15 00:33:32 +00:00
Wei Liu
78a30012fb pci: validate index before accessing MSI-X arrays
The index is derived from the access offset, so it is controlled by the
guest. Check it before accessing internal data structures.

Since Rust enforces strict bound check even in release builds, the VMM
process will crash if the guest misbehaves. There is no security issue
since the guest can only DoS itself.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-08-14 08:01:25 +00:00
dependabot[bot]
c5c751c478 build: Bump cc from 1.1.7 to 1.1.10 in /fuzz
Bumps [cc](https://github.com/rust-lang/cc-rs) from 1.1.7 to 1.1.10.
- [Release notes](https://github.com/rust-lang/cc-rs/releases)
- [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.7...cc-v1.1.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-14 00:49:14 +00:00
dependabot[bot]
f92cce888b build: Bump signal-hook-registry from 1.4.1 to 1.4.2
Bumps [signal-hook-registry](https://github.com/vorner/signal-hook) from 1.4.1 to 1.4.2.
- [Changelog](https://github.com/vorner/signal-hook/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vorner/signal-hook/compare/registry-v1.4.1...registry-v1.4.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-13 23:49:52 +00:00
Nuno Das Neves
00b4d97826 vmm: Replace hardcoded mshv IOCTL numbers in seccomp
Use the IOCTL numbers directly from mshv-ioctls instead of hardcoding
them in the seccomp filters.

Remove seccomp rules for unused ioctls:
MSHV_GET_VERSION_INFO,
MSHV_ASSERT_INTERRUPT.

Signed-off-by: Nuno Das Neves <nudasnev@microsoft.com>
2024-08-13 18:52:46 +00:00
Jinank Jain
3fe7d6d904 hypervisor: mshv: Disable previous GHCB page before setting new one
CVM guests can configure GHCB page multiple times during it's
lifetime depending on it's requirement. For example a Linux CVM guest
configures a different GHCB page during compressed kernel boot and sets
up a new one after decompressing the kernel. As a cleanup step, VMM
should unset the previous GHCB page before registering a new one for
a particular vcpu thread.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2024-08-13 16:59:31 +00:00
Alyssa Ross
02f146fef8 hypervisor: kvm: aarch64: fix get_device_attr() UB
DeviceFd::get_device_attr should be marked as unsafe, because it
allows writing to an arbitrary address.  I have opened a kvm-ioctls
PR[1] to fix this.  The hypervisor crate was using the function
unsafely by passing it addresses of immutable variables.  I noticed
this because an optimisation change[2] in Rust 1.80.0 caused the
kvm::aarch64::gic::tests::test_get_set_icc_regs test to start failing
when built in release mode.

To fix this, I've broken up the _access functions into _set and _get
variants, with the _get variant using a pointer to a mutable variable.
This has the side effect of making these functions a bit nicer to use,
because the caller now has no need to use references at all, for
either getting or setting.

[1]: https://github.com/rust-vmm/kvm-ioctls/pull/273
[2]: d2d24e395a

Signed-off-by: Alyssa Ross <hi@alyssa.is>
2024-08-13 16:16:11 +00:00
dependabot[bot]
36cdd67b9c build: Bump bitflags from 2.5.0 to 2.6.0
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.5.0 to 2.6.0.
- [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.5.0...2.6.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-13 00:21:45 +00:00
dependabot[bot]
92b12ce2f4 build: Bump anstream from 0.6.14 to 0.6.15 in /fuzz
Bumps [anstream](https://github.com/rust-cli/anstyle) from 0.6.14 to 0.6.15.
- [Commits](https://github.com/rust-cli/anstyle/compare/anstream-v0.6.14...anstream-v0.6.15)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-12 23:51:09 +00:00
Alyssa Ross
cbb588c380 virtio-devices: allow vsock to call fcntl in debug
This fixes the vsock::device::tests::test_virtio_device test with Rust
1.80.0 or later, where this check was introduced.

Signed-off-by: Alyssa Ross <hi@alyssa.is>
2024-08-12 20:13:13 +00:00
Wei Liu
1c7997c5c3 hypervisor: x86: emulate MOVSQ
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-08-10 23:49:09 +00:00
dependabot[bot]
fee769bed4 build: Bump libc from 0.2.153 to 0.2.155
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.153 to 0.2.155.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.153...0.2.155)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-10 00:43:15 +00:00
dependabot[bot]
6b2c2c938a build: Bump indexmap from 2.2.6 to 2.3.0 in /fuzz
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.2.6 to 2.3.0.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/master/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.2.6...2.3.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-10 00:20:55 +00:00
Muminul Islam
1e967697c2 vmm: pass AccessPlatform implementation for SEV-SNP guest
Passing AccessPlatform trait to virtio-device for requesting
restricting page access during IO for SEV-SNP guest.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2024-08-09 17:40:13 +00:00
Muminul Islam
959ea7115f vmm: implement AccessPlatform for SEV-SNP proxy struct
Implement AccessPlatform for SEV-SNP guest to access
restricted page using IO. VMM calls MSHV api to get access
of the pages, MSHV requests guest to release the access.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2024-08-09 17:40:13 +00:00
Muminul Islam
398e9338a7 vmm: add a proxy struct for SEV-SNP guest
Add a structure to hold the reference of the Vm trait
from Hypervisor crate to access of restricted page
from SEV-SNP guest.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2024-08-09 17:40:13 +00:00
Muminul Islam
5c4b5c0e40 hypervisor: mshv: add definition to get access pages
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2024-08-09 17:40:13 +00:00
dependabot[bot]
f9bd2aaf9f build: Bump errno from 0.3.8 to 0.3.9
Bumps [errno](https://github.com/lambda-fairy/rust-errno) from 0.3.8 to 0.3.9.
- [Changelog](https://github.com/lambda-fairy/rust-errno/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lambda-fairy/rust-errno/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-09 07:38:12 +00:00
dependabot[bot]
f126288159 build: Bump anstyle-wincon from 3.0.3 to 3.0.4 in /fuzz
Bumps [anstyle-wincon](https://github.com/rust-cli/anstyle) from 3.0.3 to 3.0.4.
- [Commits](https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v3.0.3...anstyle-wincon-v3.0.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-09 00:05:18 +00:00
dependabot[bot]
9f9a4e657f build: Bump async-channel from 2.2.0 to 2.3.0
Bumps [async-channel](https://github.com/smol-rs/async-channel) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/smol-rs/async-channel/releases)
- [Changelog](https://github.com/smol-rs/async-channel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/async-channel/compare/v2.2.0...v2.3.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-08 00:44:32 +00:00
dependabot[bot]
3097d78c6e build: Bump jobserver from 0.1.31 to 0.1.32 in /fuzz
Bumps [jobserver](https://github.com/rust-lang/jobserver-rs) from 0.1.31 to 0.1.32.
- [Commits](https://github.com/rust-lang/jobserver-rs/compare/0.1.31...0.1.32)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-08 00:34:17 +00:00
dependabot[bot]
7c2a252c02 build: Bump toml_datetime from 0.6.6 to 0.6.8 in /fuzz
Bumps [toml_datetime](https://github.com/toml-rs/toml) from 0.6.6 to 0.6.8.
- [Commits](https://github.com/toml-rs/toml/compare/toml_datetime-v0.6.6...toml_datetime-v0.6.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-07 00:06:19 +00:00
dependabot[bot]
a87b25a962 build: Bump bumpalo from 3.15.4 to 3.16.0
Bumps [bumpalo](https://github.com/fitzgen/bumpalo) from 3.15.4 to 3.16.0.
- [Changelog](https://github.com/fitzgen/bumpalo/blob/main/CHANGELOG.md)
- [Commits](https://github.com/fitzgen/bumpalo/compare/3.15.4...3.16.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-06 23:55:41 +00:00
Wei Liu
24ed063dbe github: add build and clippy tests for pvmemcontrol
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-08-06 18:39:30 +00:00
dependabot[bot]
fbac81843f build: Bump clap from 4.5.9 to 4.5.13 in /fuzz
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.9 to 4.5.13.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.9...v4.5.13)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-06 08:08:06 +00:00
dependabot[bot]
610a2234dc build: Bump ppv-lite86 from 0.2.17 to 0.2.20
Bumps [ppv-lite86](https://github.com/cryptocorrosion/cryptocorrosion) from 0.2.17 to 0.2.20.
- [Commits](https://github.com/cryptocorrosion/cryptocorrosion/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 23:49:43 +00:00
Yuanchu Xie
5f18ac3bc0 devices: Add pvmemcontrol device
Pvmemcontrol provides a way for the guest to control its physical memory
properties, and enables optimizations and security features. For
example, the guest can provide information to the host where parts of a
hugepage may be unbacked, or sensitive data may not be swapped out, etc.

Pvmemcontrol allows guests to manipulate its gPTE entries in the SLAT,
and also some other properties of the memory map the back's host memory.
This is achieved by using the KVM_CAP_SYNC_MMU capability. When this
capability is available, the changes in the backing of the memory region
on the host are automatically reflected into the guest. For example, an
mmap() or madvise() that affects the region will be made visible
immediately.

There are two components of the implementation: the guest Linux driver
and Virtual Machine Monitor (VMM) device. A guest-allocated shared
buffer is negotiated per-cpu through a few PCI MMIO registers, the VMM
device assigns a unique command for each per-cpu buffer. The guest
writes its pvmemcontrol request in the per-cpu buffer, then writes the
corresponding command into the command register, calling into the VMM
device to perform the pvmemcontrol request.

The synchronous per-cpu shared buffer approach avoids the kick and busy
waiting that the guest would have to do with virtio virtqueue transport.

The Cloud Hypervisor component can be enabled with --pvmemcontrol.

Co-developed-by: Stanko Novakovic <stanko@google.com>
Co-developed-by: Pasha Tatashin <tatashin@google.com>
Signed-off-by: Yuanchu Xie <yuanchu@google.com>
2024-08-05 22:41:56 +00:00
Yuanchu Xie
4bf2d4f7dd pci: Remove BusDevice requirement from PciDevice
The BusDevice requirement is not needed, only Send is required.

Signed-off-by: Yuanchu Xie <yuanchu@google.com>
2024-08-05 22:41:56 +00:00
Yuanchu Xie
954f3dd057 vm-device: generalize BusDevice to use a shared reference
BusDevice trait functions currently holds a mutable reference to self,
and exclusive access is guaranteed by taking a Mutex when dispatched by
the Bus object. However, this prevents individual devices from serving
accesses that do not require an mutable reference or is better served
with different synchronization primitives. We switch Bus to dispatch via
BusDeviceSync, which holds a shared reference, and delegate locking to
the BusDeviceSync trait implementation for Mutex<BusDevice>.

Other changes are made to make use of the dyn BusDeviceSync
trait object.

Signed-off-by: Yuanchu Xie <yuanchu@google.com>
2024-08-05 22:41:56 +00:00
Praveen K Paladugu
026e2c6aa8 docs: Minor doc update
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-08-05 17:46:30 +00:00
Praveen K Paladugu
be9f57dcf9 vmm: Add landlock elements to openapi spec
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-08-05 17:46:30 +00:00
Praveen K Paladugu
bd180bc3eb main: rename landlock_config to landlock_rules
To keep the naming consistent, rename all uses of landlock_config
to landlock_rules.

Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-08-05 17:46:30 +00:00
Praveen K Paladugu
b9f086bcb3 tests: drop landlock parameter while starting dest
After moving landlock config to VMConfig, there is no need to start
destination VM with landlock cmdline options in
test_live_migration_with_landlock test.

Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-08-05 17:46:30 +00:00
Praveen K Paladugu
d2f0e8aebb Revert "vmm: make landlock configs VMM-level config"
This reverts commit 94929889ac.
This revert moves landlock config back to VMConfig.

Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-08-05 17:46:30 +00:00
dependabot[bot]
513973873c build: Bump igvm from 0.3.1 to 0.3.3
Bumps [igvm](https://github.com/microsoft/igvm) from 0.3.1 to 0.3.3.
- [Release notes](https://github.com/microsoft/igvm/releases)
- [Commits](https://github.com/microsoft/igvm/compare/igvm-v0.3.1...igvm-v0.3.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-02 23:56:38 +00:00
dependabot[bot]
4c52fa344e build: Bump anstyle-query from 1.1.0 to 1.1.1 in /fuzz
Bumps [anstyle-query](https://github.com/rust-cli/anstyle) from 1.1.0 to 1.1.1.
- [Commits](https://github.com/rust-cli/anstyle/compare/anstyle-query-v1.1.0...anstyle-query-v1.1.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-02 23:48:15 +00:00
Songqian Li
e18d32bac2 performance-metrics: add restore latency performance metric
This patch calculates the recovery latency from the start of
recovery program to the end of restore.

Signed-off-by: Songqian Li <sionli@tencent.com>
2024-08-02 08:29:03 +00:00
dependabot[bot]
59185050d5 build: Bump colorchoice from 1.0.1 to 1.0.2 in /fuzz
Bumps [colorchoice](https://github.com/rust-cli/anstyle) from 1.0.1 to 1.0.2.
- [Commits](https://github.com/rust-cli/anstyle/compare/colorchoice-v1.0.1...colorchoice-v1.0.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-02 00:44:49 +00:00
dependabot[bot]
dc72ef42dc build: Bump serde_with from 3.7.0 to 3.9.0
Bumps [serde_with](https://github.com/jonasbb/serde_with) from 3.7.0 to 3.9.0.
- [Release notes](https://github.com/jonasbb/serde_with/releases)
- [Commits](https://github.com/jonasbb/serde_with/compare/v3.7.0...v3.9.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-02 00:35:54 +00:00
dependabot[bot]
68bb32a3e7 build: Bump cc from 1.0.104 to 1.1.7 in /fuzz
Bumps [cc](https://github.com/rust-lang/cc-rs) from 1.0.104 to 1.1.7.
- [Release notes](https://github.com/rust-lang/cc-rs/releases)
- [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.104...cc-v1.1.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-01 12:27:25 +00:00
dependabot[bot]
0f89387475 build: Bump zerocopy from 0.7.32 to 0.7.35
Bumps [zerocopy](https://github.com/google/zerocopy) from 0.7.32 to 0.7.35.
- [Release notes](https://github.com/google/zerocopy/releases)
- [Changelog](https://github.com/google/zerocopy/blob/main/CHANGELOG.md)
- [Commits](https://github.com/google/zerocopy/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-01 00:34:37 +00:00
Wei Liu
f5b2eb5c76 virtio-devices: vsock: drop a useless line
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-31 16:36:10 +00:00
Wei Liu
82ac114b8a virtio-devices: vsock: handle short read in muxer
Use read_exact to make sure we really get the minimum number of bytes.

Fixes: #6621
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-31 16:36:10 +00:00
dependabot[bot]
2ffd5df018 build: Bump clap_lex from 0.7.1 to 0.7.2 in /fuzz
Bumps [clap_lex](https://github.com/clap-rs/clap) from 0.7.1 to 0.7.2.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_lex-v0.7.1...clap_lex-v0.7.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-31 07:30:47 +00:00
dependabot[bot]
006a12d911 build: Bump colorchoice from 1.0.0 to 1.0.2
Bumps [colorchoice](https://github.com/rust-cli/anstyle) from 1.0.0 to 1.0.2.
- [Commits](https://github.com/rust-cli/anstyle/compare/colorchoice-v1.0.0...colorchoice-v1.0.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-31 00:22:37 +00:00
dependabot[bot]
bb55976454 build: Bump num_enum from 0.7.2 to 0.7.3
Bumps [num_enum](https://github.com/illicitonion/num_enum) from 0.7.2 to 0.7.3.
- [Commits](https://github.com/illicitonion/num_enum/compare/0.7.2...0.7.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-30 08:24:29 +00:00
dependabot[bot]
a15e041138 build: Bump anstyle from 1.0.7 to 1.0.8 in /fuzz
Bumps [anstyle](https://github.com/rust-cli/anstyle) from 1.0.7 to 1.0.8.
- [Commits](https://github.com/rust-cli/anstyle/compare/v1.0.7...v1.0.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-30 00:44:06 +00:00
dependabot[bot]
0fa96402b8 build: Bump paste from 1.0.14 to 1.0.15
Bumps [paste](https://github.com/dtolnay/paste) from 1.0.14 to 1.0.15.
- [Release notes](https://github.com/dtolnay/paste/releases)
- [Commits](https://github.com/dtolnay/paste/compare/1.0.14...1.0.15)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-29 16:25:22 +00:00
dependabot[bot]
bc0ef1d8ba build: Bump is_terminal_polyfill from 1.70.0 to 1.70.1 in /fuzz
Bumps [is_terminal_polyfill](https://github.com/polyfill-rs/is_terminal_polyfill) from 1.70.0 to 1.70.1.
- [Changelog](https://github.com/polyfill-rs/is_terminal_polyfill/blob/main-v1.70/CHANGELOG.md)
- [Commits](https://github.com/polyfill-rs/is_terminal_polyfill/compare/v1.70.0...v1.70.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-27 00:41:27 +00:00
dependabot[bot]
86153225cf build: Bump darling from 0.20.8 to 0.20.10 in /fuzz
Bumps [darling](https://github.com/TedDriggs/darling) from 0.20.8 to 0.20.10.
- [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.8...v0.20.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-26 08:31:02 +00:00
dependabot[bot]
ddc3f194aa build: Bump anyhow from 1.0.81 to 1.0.86
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.81 to 1.0.86.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.81...1.0.86)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-26 00:25:08 +00:00
dependabot[bot]
c6cc3d3ba9 build: Bump utf8parse from 0.2.1 to 0.2.2 in /fuzz
Bumps [utf8parse](https://github.com/alacritty/vte) from 0.2.1 to 0.2.2.
- [Release notes](https://github.com/alacritty/vte/releases)
- [Changelog](https://github.com/alacritty/vte/blob/master/CHANGELOG.md)
- [Commits](https://github.com/alacritty/vte/compare/utf8parse_v0.2.1...utf8parse_v0.2.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-25 12:19:27 +00:00
dependabot[bot]
0fbb3e2c2c build: Bump serde_json from 1.0.115 to 1.0.120
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.115 to 1.0.120.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.115...v1.0.120)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-25 00:28:26 +00:00
Jinank Jain
330e1aac36 hypervisor: mshv: Clear SW_EXIT_INFO1 in case of no error
There were some scenarios where we are not clearing SW_EXIT_INFO1 to
indicate that there were no error while handling the GHCB exit.
Recently, new Linux guests got stricter with checking the value of
SW_EXIT_INFO1 after coming back from VMGEXIT and started crashing. Fix
this behavior by clearing out SW_EXIT_INFO1 in case of no error.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2024-07-24 01:31:10 +00:00
dependabot[bot]
5fddf76a3b build: Bump remain from 0.2.13 to 0.2.14
Bumps [remain](https://github.com/dtolnay/remain) from 0.2.13 to 0.2.14.
- [Release notes](https://github.com/dtolnay/remain/releases)
- [Commits](https://github.com/dtolnay/remain/compare/0.2.13...0.2.14)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-24 00:28:08 +00:00
dependabot[bot]
b50dfb3538 build: Bump syn from 2.0.70 to 2.0.72 in /fuzz
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.70 to 2.0.72.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.70...2.0.72)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-24 00:19:04 +00:00
dependabot[bot]
502995746a build: Bump anstream from 0.6.13 to 0.6.14
Bumps [anstream](https://github.com/rust-cli/anstyle) from 0.6.13 to 0.6.14.
- [Commits](https://github.com/rust-cli/anstyle/compare/anstream-v0.6.13...anstream-v0.6.14)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-23 21:07:26 +00:00
Wei Liu
8aa2d7ab2a devices: use byte strings directly
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-23 21:07:17 +00:00
Wei Liu
bd8c28d341 vmm: use inspect_err instead of map_err
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-23 21:07:17 +00:00
Wei Liu
422906a0c7 performance-metrics: use inspect_err instead of map_err
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-23 21:07:17 +00:00
Wei Liu
b7512263be virtio-devices: iommu: use inspect_err instead of map_err
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-23 21:07:17 +00:00
Wei Liu
7e749c0390 block: use inspect_err instead of map_err
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-23 21:07:17 +00:00
Wei Liu
61a5bae25c option_parser: use an array directly to simplify code
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-23 21:07:17 +00:00
wuxinyue
a2438700e4 virtio-devices: support event idx for virtio-blk
Support event idx feature for virtio-blk device.
This feature could improve disk IO performance by suppressing
notifications from guest to host and interrupts from host to
guest, which has been already supported in virtio-net and
vhost-user devices.

To achieve this, virtqueue's event-idx-related API is
leveraged for avail_event field update and needs_notification
check.

Fixes: #6580
Signed-off-by: wuxinyue <wuxinyue.wxy@antgroup.com>
2024-07-23 14:16:34 +00:00
dependabot[bot]
bb4af57219 build: Bump windows-targets from 0.52.5 to 0.52.6 in /fuzz
Bumps [windows-targets](https://github.com/microsoft/windows-rs) from 0.52.5 to 0.52.6.
- [Release notes](https://github.com/microsoft/windows-rs/releases)
- [Commits](https://github.com/microsoft/windows-rs/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-23 00:21:58 +00:00
Wei Liu
824e83ab0d hypervisor: x86: emulate STOS
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-22 15:07:59 +00:00
dependabot[bot]
29675cfe68 build: Bump piper from 0.2.1 to 0.2.3
Bumps [piper](https://github.com/smol-rs/piper) from 0.2.1 to 0.2.3.
- [Release notes](https://github.com/smol-rs/piper/releases)
- [Changelog](https://github.com/smol-rs/piper/blob/main/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/piper/compare/v0.2.1...v0.2.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-20 00:48:21 +00:00
dependabot[bot]
43632f255b build: Bump clap from 4.5.7 to 4.5.9 in /fuzz
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.7 to 4.5.9.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.7...v4.5.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-19 23:51:34 +00:00
dependabot[bot]
a1941ad10b build: Bump regex from 1.10.4 to 1.10.5
Bumps [regex](https://github.com/rust-lang/regex) from 1.10.4 to 1.10.5.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.10.4...1.10.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-19 16:50:49 +00:00
dependabot[bot]
70db454b90 build: Bump windows_i686_msvc from 0.52.5 to 0.52.6 in /fuzz
Bumps [windows_i686_msvc](https://github.com/microsoft/windows-rs) from 0.52.5 to 0.52.6.
- [Release notes](https://github.com/microsoft/windows-rs/releases)
- [Commits](https://github.com/microsoft/windows-rs/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-19 02:49:59 +00:00
dependabot[bot]
7b3ffd89a5 build: Bump windows_i686_gnu from 0.52.5 to 0.52.6 in /fuzz
Bumps [windows_i686_gnu](https://github.com/microsoft/windows-rs) from 0.52.5 to 0.52.6.
- [Release notes](https://github.com/microsoft/windows-rs/releases)
- [Commits](https://github.com/microsoft/windows-rs/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-18 00:03:20 +00:00
Jinank Jain
c1f18fa634 arch: x86_64: Don't expose TSC deadline timer for MSHV guests
HV APIC(i.e., synthetic APIC controller exposed by Microsoft Hypervisor)
does not support one-shot operation using a TSC deadline value. Due to
which we see the following backtrace inside the guest when running with
hypervisor-fw/OVMF:

[    0.560765] unchecked MSR access error: WRMSR to 0x832 (tried to
write 0x00000000000400ec) at rIP: 0xffffffff8f473594
(native_write_msr+0x4/0x30)
[    0.560765] Call Trace:
[    0.560765]  ? native_apic_msr_write+0x2b/0x30
[    0.560765]  __setup_APIC_LVTT+0xbc/0xe0
[    0.560765]  lapic_timer_set_oneshot+0x27/0x30
[    0.560765]  clockevents_switch_state+0xaf/0xf0
[    0.560765]  tick_setup_periodic+0x47/0x90
[    0.560765]  tick_setup_device.isra.0+0x7c/0x110
[    0.560765]  tick_check_new_device+0xce/0xf0
[    0.560765]  clockevents_register_device+0x82/0x170
[    0.560765]  clockevents_config_and_register+0x2f/0x40
[    0.560765]  setup_APIC_timer+0xe1/0xf0
[    0.560765]  setup_boot_APIC_clock+0x5f/0x66
[    0.560765]  native_smp_prepare_cpus+0x1d6/0x286
[    0.560765]  kernel_init_freeable+0xcf/0x255
[    0.560765]  ? rest_init+0xb0/0xb0
[    0.560765]  kernel_init+0xe/0x110
[    0.560765]  ret_from_fork+0x22/0x40

Also, if this feature is exposed guest would not finish booting and get
stuck right before unpacking the root filesystem.

Fixes: 06e8d1c40 ("hypervisor: mshv: fix topology for Intel HW on MSHV")
Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2024-07-17 17:10:08 +00:00
Bo Chen
efb92d409f docs: performance_metrics: Add example section with sample numbers
Signed-off-by: Bo Chen <chen.bo@intel.com>
2024-07-17 15:25:03 +00:00
Bo Chen
e4aa3617c7 docs: performance_metrics: Wrap line at 80 characters
Signed-off-by: Bo Chen <chen.bo@intel.com>
2024-07-17 15:25:03 +00:00
Songqian Li
d7e8cd8258 docs: improve performance metrics documentation
Add a performance metrics list and the definition of output format.

Signed-off-by: Songqian Li <sionli@tencent.com>
2024-07-17 15:25:03 +00:00
dependabot[bot]
8803e4a2e7 build: Bump thiserror from 1.0.61 to 1.0.62
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.61 to 1.0.62.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.61...1.0.62)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-17 08:00:35 +00:00
dependabot[bot]
d2a01f7cec build: Bump uuid from 1.8.0 to 1.10.0 in /fuzz
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.8.0 to 1.10.0.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.8.0...1.10.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-17 00:12:50 +00:00
dependabot[bot]
dec4a82058 build: Bump log from 0.4.21 to 0.4.22
Bumps [log](https://github.com/rust-lang/log) from 0.4.21 to 0.4.22.
- [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.21...0.4.22)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-16 06:20:32 +00:00
dependabot[bot]
5fc143205c build: Bump windows_aarch64_msvc from 0.52.5 to 0.52.6 in /fuzz
Bumps [windows_aarch64_msvc](https://github.com/microsoft/windows-rs) from 0.52.5 to 0.52.6.
- [Release notes](https://github.com/microsoft/windows-rs/releases)
- [Commits](https://github.com/microsoft/windows-rs/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-16 00:29:11 +00:00
Jinank Jain
b73d94f969 hypervisor: mshv: Refactor x86 emulator into different module
This is a preparatory work to make space for ARM64 emulator.
There is no functional change in this commit, just moving code from one
file to another.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2024-07-15 23:05:11 +00:00
Jinank Jain
ead4f767ff hypervisor: mshv: Restrict dependency to x86
iced_x86 is only available on x86 architecture.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2024-07-15 23:05:11 +00:00
Changyuan Lyu
bc6acb842f block: fix status value size
As per VirtIO spec 1.2 section 5.2.6, the `status` field is a byte, not
u32. cloud-hypervisor writes an `u32` to guest memory, which
accidentally zeros out the following 3 bytes, and may corrupt guest OS
internal state.

Signed-off-by: Changyuan Lyu <changyuanl@google.com>
2024-07-14 19:23:06 +00:00
dependabot[bot]
d89f1f4f21 build: Bump serde_with from 3.8.1 to 3.8.3 in /fuzz
Bumps [serde_with](https://github.com/jonasbb/serde_with) from 3.8.1 to 3.8.3.
- [Release notes](https://github.com/jonasbb/serde_with/releases)
- [Commits](https://github.com/jonasbb/serde_with/compare/v3.8.1...v3.8.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-13 00:23:33 +00:00
dependabot[bot]
dc90744ec3 build: Bump darling from 0.20.9 to 0.20.10
Bumps [darling](https://github.com/TedDriggs/darling) from 0.20.9 to 0.20.10.
- [Release notes](https://github.com/TedDriggs/darling/releases)
- [Changelog](https://github.com/TedDriggs/darling/blob/master/CHANGELOG.md)
- [Commits](https://github.com/TedDriggs/darling/commits/v0.20.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-12 23:46:59 +00:00
Wei Liu
18340d9761 hypervisor: mshv: fine-grained control over translation flags
The assertion that only code emulation requires GVA to GPA translation
is wrong.

Allow the caller of `translate` to pass in permission flags directly.

Provide a new method `read_memory_flags` so that we can add the EXECUTE
permission flag where necessary.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-12 16:12:33 +00:00
Wei Liu
cfaa192eb4 hypervisor: emulator: drop the unused gva_to_gpa hook
That function is too limiting. It doesn't consider page permissions. It
is not used, so just drop it.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-12 16:12:33 +00:00
dependabot[bot]
c67f799717 build: Bump windows_x86_64_msvc from 0.52.5 to 0.52.6 in /fuzz
Bumps [windows_x86_64_msvc](https://github.com/microsoft/windows-rs) from 0.52.5 to 0.52.6.
- [Release notes](https://github.com/microsoft/windows-rs/releases)
- [Commits](https://github.com/microsoft/windows-rs/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-12 00:02:26 +00:00
dependabot[bot]
b47efc3bc2 build: Bump enumflags2 from 0.7.9 to 0.7.10
Bumps [enumflags2](https://github.com/meithecatte/enumflags2) from 0.7.9 to 0.7.10.
- [Release notes](https://github.com/meithecatte/enumflags2/releases)
- [Commits](https://github.com/meithecatte/enumflags2/compare/v0.7.9...v0.7.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-11 23:55:37 +00:00
dependabot[bot]
498f35a1ab build: Bump zerocopy from 0.7.34 to 0.7.35 in /fuzz
Bumps [zerocopy](https://github.com/google/zerocopy) from 0.7.34 to 0.7.35.
- [Release notes](https://github.com/google/zerocopy/releases)
- [Changelog](https://github.com/google/zerocopy/blob/main/CHANGELOG.md)
- [Commits](https://github.com/google/zerocopy/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-11 00:52:33 +00:00
dependabot[bot]
2d61bc36ed build: Bump anstyle from 1.0.6 to 1.0.7
Bumps [anstyle](https://github.com/rust-cli/anstyle) from 1.0.6 to 1.0.7.
- [Commits](https://github.com/rust-cli/anstyle/compare/v1.0.6...v1.0.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-10 23:53:26 +00:00
dependabot[bot]
46447962b8 build: Bump windows_i686_gnullvm from 0.52.5 to 0.52.6 in /fuzz
Bumps [windows_i686_gnullvm](https://github.com/microsoft/windows-rs) from 0.52.5 to 0.52.6.
- [Release notes](https://github.com/microsoft/windows-rs/releases)
- [Commits](https://github.com/microsoft/windows-rs/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-10 00:21:49 +00:00
dependabot[bot]
9a6bc025a7 build: Bump backtrace from 0.3.71 to 0.3.73
Bumps [backtrace](https://github.com/rust-lang/backtrace-rs) from 0.3.71 to 0.3.73.
- [Release notes](https://github.com/rust-lang/backtrace-rs/releases)
- [Commits](https://github.com/rust-lang/backtrace-rs/compare/0.3.71...0.3.73)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-10 00:08:56 +00:00
Rob Bradford
de1abe0e30 vmm: Use Response::ok_or_abandon() in migration logic
The use of this method removes duplicated code yet provides clarity on
the logic.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2024-07-09 19:53:26 +00:00
Rob Bradford
e97cee99ef vm-migration: Introduce Response::ok_or_abandon()
This method will return the existing Response if the status is
successful (Status::Ok) otherwise issue a command to abandon the
migration and return the desired error.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2024-07-09 19:53:26 +00:00
Wei Liu
3103526153 build: update pnet crates
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-09 19:52:59 +00:00
dependabot[bot]
08ff89ba6e build: Bump zvariant from 4.1.1 to 4.1.2
Bumps [zvariant](https://github.com/dbus2/zbus) from 4.1.1 to 4.1.2.
- [Release notes](https://github.com/dbus2/zbus/releases)
- [Commits](https://github.com/dbus2/zbus/compare/zvariant-4.1.1...zvariant-4.1.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-09 00:34:10 +00:00
dependabot[bot]
514f36fb5a build: Bump syn from 2.0.68 to 2.0.70 in /fuzz
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.68 to 2.0.70.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.68...2.0.70)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-09 00:00:14 +00:00
dependabot[bot]
fe3506976e build: Bump cc from 1.0.101 to 1.0.104 in /fuzz
Bumps [cc](https://github.com/rust-lang/cc-rs) from 1.0.101 to 1.0.104.
- [Release notes](https://github.com/rust-lang/cc-rs/releases)
- [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.101...cc-v1.0.104)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-06 04:54:03 +00:00
Wei Liu
94929889ac vmm: make landlock configs VMM-level config
This requires stashing the config values in `struct Vmm`. The configs
should be validated before before creating the VMM thread. Refactor the
code and update documentation where necessary.

The place where the rules are applied remain the same.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-06 04:42:58 +00:00
Praveen K Paladugu
8452edfcc7 tests: Test live migration with Landlock
Add a test case to check Live Migration with Landlock support.

Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-07-06 04:42:58 +00:00
Praveen K Paladugu
466cc5e043 tests: Add disk_hotplug test with Landlock
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-07-06 04:42:58 +00:00
Praveen K Paladugu
034c674c4c tests: Add a basic Landlock test
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-07-06 04:42:58 +00:00
Praveen K Paladugu
b785e00317 docs: Add doc for Landlock feature
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-07-06 04:42:58 +00:00
Praveen K Paladugu
7f6731cd05 vmm: Limit the visibility of Landlock objects
Limit the visibility of objects and methods introduced by Landlock
implementation.

Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-07-06 04:42:58 +00:00
Praveen K Paladugu
457fd9ef96 vmm: enable landlock during live migration
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-07-06 04:42:58 +00:00
Praveen K Paladugu
eea45a2c78 vmm: Enable Landlock in restore path
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-07-06 04:42:58 +00:00
Praveen K Paladugu
11c17ca319 main: Enable landlock on main thread
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-07-06 04:42:58 +00:00
Praveen K Paladugu
249e362c70 vmm: Enable Landlock on vmm thread
Add file/dir paths from landlock-rules arguments to ruleset. Invoke
apply_landlock on VmConfig to apply config specific rules to ruleset.

Once done, any threads spawned by vmm thread will be automatically
sandboxed with the ruleset in vmm thread.

Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-07-06 04:42:58 +00:00
Praveen K Paladugu
b3e5738b40 vmm: Introduce ApplyLandlock trait
Introduce ApplyLandlock trait and add implementations to VmConfig
elements with PathBufs. This trait adds config specific rules to
landlock ruleset.

Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-07-06 04:42:58 +00:00
Praveen K Paladugu
1dd53c3d24 vmm: Enable Landlock on http-server thread
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-07-06 04:42:58 +00:00
Praveen K Paladugu
130c988380 vmm: Enable Landlock on signal-handler thread
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-07-06 04:42:58 +00:00
Praveen K Paladugu
8c76a3e4b5 vmm: Enable Landlock on event-monitor thread
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-07-06 04:42:58 +00:00
Praveen K Paladugu
af5a9677c8 vmm: Introduce Landlock module
This module introduces methods to apply Landlock LSM to cloud-hypervisor
threads.

Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-07-06 04:42:58 +00:00
Praveen K Paladugu
1d89f98edf vmm: Introduce landlock-rules cmdline param
Users can use this parameter to pass extra paths that 'vmm' and its
child threads can use at runtime. Hotplug is the primary usecase for
this parameter.

In order to hotplug devices that use local files: disks, memory zones,
pmem devices etc, users can use this option to pass the path/s that will
be used during hotplug while starting cloud-hypervisor. Doing this will
allow landlock to add required rules to grant access to these paths when
cloud-hypervisor process starts.

Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-06 04:42:58 +00:00
Praveen K Paladugu
287dbd4fc9 vmm: Introduce landlock cmdline parameter
Users can use this cmdline option to enable/disable Landlock based
sandboxing while running cloud-hypervisor.

Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-07-06 04:42:58 +00:00
Praveen K Paladugu
c50ea2c708 vmm: Add seccomp rules to allow landlock syscalls
landlock syscalls are required by event_monitor, signal_handler,
http-server and vmm threads. Rest of the threads are spawned by the vmm
thread and they automatically inherit the ruleset from the vmm thread.

Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
2024-07-06 04:42:58 +00:00
Wei Liu
14b45e4d2e hypervisor: mshv: handle GPA intercept
We will start receiving GPA intercepts. For our use cases they are
handled the same way as UNMAPPED GPA intercepts.

Put in some logging to distinguish the two cases.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-04 18:45:01 +00:00
Wei Liu
519476e842 hypervisor: mshv: relax the requirement for instruction emulation
Previously we required the hypervisor to give us a valid instruction
stream. That worked well enough because we never hit any edge conditions
(such as when the instruction stream crosses page boundary).

Now that MSHV can deal with partial or empty instruction stream, we can
remove that requirement.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-04 18:45:01 +00:00
Wei Liu
5fec858130 hypervisor: mshv: implement fetching instructions in emulator
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-04 18:45:01 +00:00
Wei Liu
67f22b6aa4 hypervisor: mshv: fix GVA translation flags
Original we checked for R and W, but that code path never got executed.

It is now understood that we can only get here when we execute code. Fix
the permission flags.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-04 18:45:01 +00:00
Wei Liu
1eb4133034 hypervisor: x86: emulator: set IP properly for newly fetched stream
The default value of IP is zero. If the decoder's state not set
properly, then the guest state is going to be wrong.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-04 18:45:01 +00:00
dependabot[bot]
9f3bea3e3f build: Bump anstyle-parse from 0.2.3 to 0.2.4 in /fuzz
Bumps [anstyle-parse](https://github.com/rust-cli/anstyle) from 0.2.3 to 0.2.4.
- [Commits](https://github.com/rust-cli/anstyle/compare/anstyle-parse-v0.2.3...anstyle-parse-v0.2.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-03 21:54:50 +00:00
Alyssa Ross
e7c7a304e8 virtio-devices: fix UB getting tty size
TIOCGWINSZ modifies its argument, so it needs to mutably borrow it.
Unfortunately, ioctl()'s signature is not able to enforce this, and
the write happens in the kernel, so I don't think anything like miri,
valgrind, UBSan, etc. would have been able to catch this.

The UB passing an immutable reference caused resulted, for me, in
get_win_size() returning (0, 0) since LLVM commit
9a09c737a052 ("[BasicAA] Make isNotCapturedBeforeOrAt() check for
calls more precise (#69931)").

I've had a look through the other ioctl() calls in Cloud Hypervisor,
and I don't think any others have the same problem.

Signed-off-by: Alyssa Ross <hi@alyssa.is>
2024-07-03 21:26:04 +00:00
Bo Chen
b5cce0d371 build: Enable vfio and sgx worker
This reverts commit 7d84654a79.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2024-07-03 00:34:28 +00:00
Wei Liu
7c608f6380 hypervisor: x86: emulator: accept empty instruction stream input
The emulator should fetch from memory just fine.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-02 14:59:50 +00:00
Wei Liu
3ad8d24943 hypervisor: x86: emulator: fix a variable
Comparing RAX with RIP makes no logical sense other than RIP happens to
be the correct value. Use `target_rax` instead.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-02 14:59:50 +00:00
Wei Liu
56c6c02724 hypervisor: x86: emulator: test executing only one instruction
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-02 14:59:50 +00:00
Wei Liu
19b0ea842b hypervisor: x86: emulator: add the second instruction to test comment
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2024-07-02 14:59:50 +00:00
dependabot[bot]
16bd88b4b5 build: Bump cc from 1.0.99 to 1.0.101 in /fuzz
Bumps [cc](https://github.com/rust-lang/cc-rs) from 1.0.99 to 1.0.101.
- [Release notes](https://github.com/rust-lang/cc-rs/releases)
- [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/cc-rs/compare/1.0.99...cc-v1.0.101)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-28 00:28:40 +00:00
dependabot[bot]
09136c50b5 build: Bump syn from 2.0.66 to 2.0.68 in /fuzz
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.66 to 2.0.68.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.66...2.0.68)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-27 17:44:00 +00:00
Nuno Das Neves
380b7d398c build: Bump openssl-src to 3.3.1
Signed-off-by: Nuno Das Neves <nudasnev@microsoft.com>
2024-06-25 18:55:52 +00:00
Nuno Das Neves
764959c9a8 build: Bump libz-sys to 1.1.18
Signed-off-by: Nuno Das Neves <nudasnev@microsoft.com>
2024-06-25 18:55:52 +00:00
Bo Chen
7d84654a79 build: Temporarily disable vfio and sgx worker
The corresponding bare-metal runner needs to be offline for
maintenance.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2024-06-25 17:40:20 +00:00
Julian Stecklina
246cb94dc5 build: Fix license string to comply with SPDX list
cargo-cyclonedx rejects the current license key in Cargo.toml, because
it doesn't comply with the SPDX standard:

  Package cloud-hypervisor has an invalid license expression (LICENSE-APACHE & LICENSE-BSD-3-Clause), using as named license: Invalid Lax SPDX expression: unknown term

Fix by using the names from the SPDX list:
https://spdx.github.io/license-list-data/

Signed-off-by: Julian Stecklina <julian.stecklina@cyberus-technology.de>
2024-06-25 09:14:19 -07:00
dependabot[bot]
165c2c476f build: Bump proc-macro2 from 1.0.83 to 1.0.86 in /fuzz
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.83 to 1.0.86.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.83...1.0.86)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-22 09:04:44 +00:00
dependabot[bot]
5e8f380698 build: Bump anyhow from 1.0.82 to 1.0.86 in /fuzz
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.82 to 1.0.86.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.82...1.0.86)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-21 16:45:25 +00:00
77 changed files with 4018 additions and 970 deletions

View File

@@ -49,6 +49,9 @@ jobs:
- name: Build (default features + guest_debug)
run: cargo rustc --locked --bin cloud-hypervisor --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks
- name: Build (default features + pvmemcontrol)
run: cargo rustc --locked --bin cloud-hypervisor --features "pvmemcontrol" -- -D warnings -D clippy::undocumented_unsafe_blocks
- name: Build (mshv)
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks

View File

@@ -80,6 +80,13 @@ jobs:
command: clippy
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks
- name: Clippy (default features + pvmemcontrol)
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "pvmemcontrol" -- -D warnings -D clippy::undocumented_unsafe_blocks
- name: Clippy (default features + tracing)
uses: actions-rs/cargo@v1
with:

254
Cargo.lock generated
View File

@@ -12,9 +12,9 @@ dependencies = [
[[package]]
name = "addr2line"
version = "0.21.0"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
dependencies = [
"gimli",
]
@@ -36,23 +36,24 @@ dependencies = [
[[package]]
name = "anstream"
version = "0.6.13"
version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.6"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
[[package]]
name = "anstyle-parse"
@@ -74,9 +75,9 @@ dependencies = [
[[package]]
name = "anstyle-wincon"
version = "3.0.3"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
dependencies = [
"anstyle",
"windows-sys 0.52.0",
@@ -84,9 +85,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.81"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "api_client"
@@ -136,9 +137,9 @@ dependencies = [
[[package]]
name = "async-channel"
version = "2.2.0"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3"
checksum = "9f2776ead772134d55b62dd45e59a79e21612d85d0af729b8b7d3967d601a62a"
dependencies = [
"concurrent-queue",
"event-listener 5.3.0",
@@ -281,9 +282,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "backtrace"
version = "0.3.71"
version = "0.3.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
dependencies = [
"addr2line",
"cc",
@@ -296,9 +297,9 @@ dependencies = [
[[package]]
name = "bitfield-struct"
version = "0.5.6"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26b8cea8bb6a81b75a84603b9e096f05fa86db057904ef29be1deee900532bd"
checksum = "6c2ce686adbebce0ee484a502c440b4657739adbad65eadf06d64f5816ee9765"
dependencies = [
"proc-macro2",
"quote",
@@ -313,9 +314,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "block"
@@ -362,9 +363,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.15.4"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "byteorder"
@@ -420,7 +421,7 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "cloud-hypervisor"
version = "40.0.0"
version = "41.0.0"
dependencies = [
"anyhow",
"api_client",
@@ -451,9 +452,9 @@ dependencies = [
[[package]]
name = "colorchoice"
version = "1.0.0"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]]
name = "concurrent-queue"
@@ -509,9 +510,9 @@ dependencies = [
[[package]]
name = "darling"
version = "0.20.9"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1"
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
dependencies = [
"darling_core",
"darling_macro",
@@ -519,9 +520,9 @@ dependencies = [
[[package]]
name = "darling_core"
version = "0.20.9"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120"
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
dependencies = [
"fnv",
"ident_case",
@@ -533,9 +534,9 @@ dependencies = [
[[package]]
name = "darling_macro"
version = "0.20.9"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
dependencies = [
"darling_core",
"quote",
@@ -566,12 +567,13 @@ dependencies = [
"acpi_tables",
"anyhow",
"arch",
"bitflags 2.5.0",
"bitflags 2.6.0",
"byteorder",
"event_monitor",
"hypervisor",
"libc",
"log",
"num_enum",
"pci",
"serde",
"thiserror",
@@ -638,9 +640,9 @@ checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
[[package]]
name = "enumflags2"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d"
checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d"
dependencies = [
"enumflags2_derive",
"serde",
@@ -648,9 +650,9 @@ dependencies = [
[[package]]
name = "enumflags2_derive"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4"
checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
dependencies = [
"proc-macro2",
"quote",
@@ -686,7 +688,7 @@ version = "4.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"libc",
]
@@ -698,9 +700,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.8"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys 0.52.0",
@@ -897,7 +899,7 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6341b3480afbb34eaefc7f92713bc92f2d83e338aaa1c44192f9c2956f4a4903"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"cfg-if",
"log",
"managed",
@@ -940,9 +942,9 @@ dependencies = [
[[package]]
name = "gimli"
version = "0.28.1"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
[[package]]
name = "glob"
@@ -1015,9 +1017,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "igvm"
version = "0.3.1"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bc8970c7e36437c52af3b3ef1acaa5e334c2a95cd8ee9639d574830f48af17e"
checksum = "7984b10433b50e06a06bd50c69bca4888a5d7de8975f64ea4c2a7687eb99b09d"
dependencies = [
"bitfield-struct",
"crc32fast",
@@ -1032,9 +1034,9 @@ dependencies = [
[[package]]
name = "igvm_defs"
version = "0.3.1"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c4942827cef415726296f6d62411afdb13c1b1924125f3222988f04bef33ad2"
checksum = "b64ec5588c475372ae830475d3ee9a7bd255407dcb9f03faf6d493556eb6105a"
dependencies = [
"bitfield-struct",
"open-enum",
@@ -1080,6 +1082,12 @@ dependencies = [
"serde",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
[[package]]
name = "itoa"
version = "1.0.11"
@@ -1112,12 +1120,23 @@ version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bedae2ca4a531bebe311abaf9691f5cc14eaa21475243caa2e39c43bb872947d"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"kvm-bindings",
"libc",
"vmm-sys-util",
]
[[package]]
name = "landlock"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dafb8a4afee64f167eb2b52d32f0eea002e41a7a6450e68c799c8ec3a81a634c"
dependencies = [
"enumflags2",
"libc",
"thiserror",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -1126,9 +1145,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.153"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "libredox"
@@ -1136,7 +1155,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"libc",
]
@@ -1156,9 +1175,9 @@ dependencies = [
[[package]]
name = "libz-sys"
version = "1.1.16"
version = "1.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9"
checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e"
dependencies = [
"cc",
"libc",
@@ -1193,9 +1212,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.21"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "managed"
@@ -1311,7 +1330,7 @@ version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"cfg-if",
"cfg_aliases",
"libc",
@@ -1335,19 +1354,20 @@ dependencies = [
[[package]]
name = "num_enum"
version = "0.7.2"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.7.2"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.66",
@@ -1355,9 +1375,9 @@ dependencies = [
[[package]]
name = "object"
version = "0.32.2"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce"
dependencies = [
"memchr",
]
@@ -1370,29 +1390,29 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "open-enum"
version = "0.4.1"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba485b94b3e73fa752d98cfcab74647a4a537269682cc1ee5256aa020432506d"
checksum = "2eb2508143a400b3361812094d987dd5adc81f0f5294a46491be648d6c94cab5"
dependencies = [
"open-enum-derive",
]
[[package]]
name = "open-enum-derive"
version = "0.4.1"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed1c261430059cab8b2b51eec42a3c15750439ec6c013cd8fe41d4a450de776"
checksum = "8d1296fab5231654a5aec8bf9e87ba4e3938c502fc4c3c0425a00084c78944be"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"syn 2.0.66",
]
[[package]]
name = "openssl-src"
version = "300.2.3+3.2.1"
version = "300.3.1+3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843"
checksum = "7259953d42a81bf137fbbd73bd30a8e1914d6dce43c2b90ed575783a22608b91"
dependencies = [
"cc",
]
@@ -1486,9 +1506,9 @@ dependencies = [
[[package]]
name = "paste"
version = "1.0.14"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pci"
@@ -1538,9 +1558,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "piper"
version = "0.2.1"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4"
checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391"
dependencies = [
"atomic-waker",
"fastrand",
@@ -1555,9 +1575,9 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "pnet"
version = "0.34.0"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "130c5b738eeda2dc5796fe2671e49027e6935e817ab51b930a36ec9e6a206a64"
checksum = "682396b533413cc2e009fbb48aadf93619a149d3e57defba19ff50ce0201bd0d"
dependencies = [
"ipnetwork",
"pnet_base",
@@ -1569,18 +1589,18 @@ dependencies = [
[[package]]
name = "pnet_base"
version = "0.34.0"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe4cf6fb3ab38b68d01ab2aea03ed3d1132b4868fa4e06285f29f16da01c5f4c"
checksum = "ffc190d4067df16af3aba49b3b74c469e611cad6314676eaf1157f31aa0fb2f7"
dependencies = [
"no-std-net",
]
[[package]]
name = "pnet_datalink"
version = "0.34.0"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad5854abf0067ebbd3967f7d45ebc8976ff577ff0c7bd101c4973ae3c70f98fe"
checksum = "e79e70ec0be163102a332e1d2d5586d362ad76b01cec86f830241f2b6452a7b7"
dependencies = [
"ipnetwork",
"libc",
@@ -1591,9 +1611,9 @@ dependencies = [
[[package]]
name = "pnet_macros"
version = "0.34.0"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "688b17499eee04a0408aca0aa5cba5fc86401d7216de8a63fdf7a4c227871804"
checksum = "13325ac86ee1a80a480b0bc8e3d30c25d133616112bb16e86f712dcf8a71c863"
dependencies = [
"proc-macro2",
"quote",
@@ -1603,18 +1623,18 @@ dependencies = [
[[package]]
name = "pnet_macros_support"
version = "0.34.0"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eea925b72f4bd37f8eab0f221bbe4c78b63498350c983ffa9dd4bcde7e030f56"
checksum = "eed67a952585d509dd0003049b1fc56b982ac665c8299b124b90ea2bdb3134ab"
dependencies = [
"pnet_base",
]
[[package]]
name = "pnet_packet"
version = "0.34.0"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9a005825396b7fe7a38a8e288dbc342d5034dac80c15212436424fef8ea90ba"
checksum = "4c96ebadfab635fcc23036ba30a7d33a80c39e8461b8bd7dc7bb186acb96560f"
dependencies = [
"glob",
"pnet_base",
@@ -1624,9 +1644,9 @@ dependencies = [
[[package]]
name = "pnet_sys"
version = "0.34.0"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "417c0becd1b573f6d544f73671070b039051e5ad819cc64aa96377b536128d00"
checksum = "7d4643d3d4db6b08741050c2f3afa9a892c4244c085a72fcda93c9c2c9a00f4b"
dependencies = [
"libc",
"winapi",
@@ -1634,9 +1654,9 @@ dependencies = [
[[package]]
name = "pnet_transport"
version = "0.34.0"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2637e14d7de974ee2f74393afccbc8704f3e54e6eb31488715e72481d1662cc3"
checksum = "5f604d98bc2a6591cf719b58d3203fd882bdd6bf1db696c4ac97978e9f4776bf"
dependencies = [
"libc",
"pnet_base",
@@ -1661,9 +1681,12 @@ dependencies = [
[[package]]
name = "ppv-lite86"
version = "0.2.17"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro-crate"
@@ -1770,9 +1793,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.10.4"
version = "1.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
dependencies = [
"aho-corasick",
"memchr",
@@ -1799,9 +1822,9 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "remain"
version = "0.2.13"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad9f2390298a947ee0aa6073d440e221c0726188cfbcdf9604addb6ee393eb4a"
checksum = "46aef80f842736de545ada6ec65b81ee91504efd6853f4b96de7414c42ae7443"
dependencies = [
"proc-macro2",
"quote",
@@ -1826,7 +1849,7 @@ version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"errno",
"libc",
"linux-raw-sys",
@@ -1876,9 +1899,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.115"
version = "1.0.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
dependencies = [
"itoa",
"ryu",
@@ -1898,9 +1921,9 @@ dependencies = [
[[package]]
name = "serde_with"
version = "3.7.0"
version = "3.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a"
checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857"
dependencies = [
"serde",
"serde_derive",
@@ -1909,9 +1932,9 @@ dependencies = [
[[package]]
name = "serde_with_macros"
version = "3.7.0"
version = "3.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655"
checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350"
dependencies = [
"darling",
"proc-macro2",
@@ -1946,9 +1969,9 @@ dependencies = [
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
dependencies = [
"libc",
]
@@ -2062,18 +2085,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.61"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.61"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
dependencies = [
"proc-macro2",
"quote",
@@ -2257,7 +2280,7 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6be08d1166d41a78861ad50212ab3f9eca0729c349ac3a7a8f557c62406b87cc"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"libc",
"vm-memory",
"vmm-sys-util",
@@ -2433,7 +2456,7 @@ dependencies = [
"anyhow",
"arc-swap",
"arch",
"bitflags 2.5.0",
"bitflags 2.6.0",
"block",
"blocking",
"cfg-if",
@@ -2450,6 +2473,7 @@ dependencies = [
"hypervisor",
"igvm",
"igvm_defs",
"landlock",
"libc",
"linux-loader",
"log",
@@ -2803,9 +2827,9 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.7.32"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"byteorder",
"zerocopy-derive",
@@ -2813,9 +2837,9 @@ dependencies = [
[[package]]
name = "zerocopy-derive"
version = "0.7.32"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
@@ -2824,9 +2848,9 @@ dependencies = [
[[package]]
name = "zvariant"
version = "4.1.1"
version = "4.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aa6d31a02fbfb602bfde791de7fedeb9c2c18115b3d00f3a36e489f46ffbbc7"
checksum = "1724a2b330760dc7d2a8402d841119dc869ef120b139d29862d6980e9c75bfc9"
dependencies = [
"endi",
"enumflags2",
@@ -2837,9 +2861,9 @@ dependencies = [
[[package]]
name = "zvariant_derive"
version = "4.1.1"
version = "4.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "642bf1b6b6d527988b3e8193d20969d53700a36eac734d21ae6639db168701c8"
checksum = "55025a7a518ad14518fb243559c058a2e5b848b015e31f1d90414f36e3317859"
dependencies = [
"proc-macro-crate",
"proc-macro2",

View File

@@ -5,9 +5,9 @@ default-run = "cloud-hypervisor"
description = "Open source Virtual Machine Monitor (VMM) that runs on top of KVM & MSHV"
edition = "2021"
homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
license = "LICENSE-APACHE & LICENSE-BSD-3-Clause"
license = "Apache-2.0 AND BSD-3-Clause"
name = "cloud-hypervisor"
version = "40.0.0"
version = "41.0.0"
# Minimum buildable version:
# Keep in sync with version in .github/workflows/build.yaml
# Policy on MSRV (see #4318):
@@ -29,20 +29,20 @@ inherits = "release"
strip = false
[dependencies]
anyhow = "1.0.81"
anyhow = "1.0.86"
api_client = { path = "api_client" }
clap = { version = "4.5.4", features = ["string"] }
dhat = { version = "0.3.3", optional = true }
epoll = "4.3.3"
event_monitor = { path = "event_monitor" }
hypervisor = { path = "hypervisor" }
libc = "0.2.153"
log = { version = "0.4.21", features = ["std"] }
libc = "0.2.155"
log = { version = "0.4.22", features = ["std"] }
option_parser = { path = "option_parser" }
seccompiler = "0.4.0"
serde_json = "1.0.115"
serde_json = "1.0.120"
signal-hook = "0.3.17"
thiserror = "1.0.60"
thiserror = "1.0.62"
tpm = { path = "tpm" }
tracer = { path = "tracer" }
vm-memory = "0.14.1"
@@ -54,7 +54,7 @@ zbus = { version = "4.1.2", optional = true }
dirs = "5.0.1"
net_util = { path = "net_util" }
once_cell = "1.19.0"
serde_json = "1.0.115"
serde_json = "1.0.120"
test_infra = { path = "test_infra" }
wait-timeout = "0.2.0"
@@ -69,6 +69,7 @@ igvm = ["mshv", "vmm/igvm"]
io_uring = ["vmm/io_uring"]
kvm = ["vmm/kvm"]
mshv = ["vmm/mshv"]
pvmemcontrol = ["vmm/pvmemcontrol"]
sev_snp = ["igvm", "mshv", "vmm/sev_snp"]
tdx = ["vmm/tdx"]
tracing = ["tracer/tracing", "vmm/tracing"]

View File

@@ -5,5 +5,5 @@ name = "api_client"
version = "0.1.0"
[dependencies]
thiserror = "1.0.61"
thiserror = "1.0.62"
vmm-sys-util = "0.12.1"

View File

@@ -6,18 +6,19 @@ version = "0.1.0"
[features]
default = []
kvm = []
sev_snp = []
tdx = []
[dependencies]
anyhow = "1.0.81"
anyhow = "1.0.86"
byteorder = "1.5.0"
hypervisor = { path = "../hypervisor" }
libc = "0.2.153"
libc = "0.2.155"
linux-loader = { version = "0.11.0", features = ["bzimage", "elf", "pe"] }
log = "0.4.21"
log = "0.4.22"
serde = { version = "1.0.197", features = ["derive", "rc"] }
thiserror = "1.0.60"
thiserror = "1.0.62"
uuid = "1.8.0"
vm-memory = { version = "0.14.1", features = [
"backend-bitmap",

View File

@@ -34,6 +34,7 @@ use std::arch::x86_64;
pub mod tdx;
// CPUID feature bits
#[cfg(feature = "kvm")]
const TSC_DEADLINE_TIMER_ECX_BIT: u8 = 24; // tsc deadline timer ecx bit.
const HYPERVISOR_ECX_BIT: u8 = 31; // Hypervisor ecx bit.
const MTRR_EDX_BIT: u8 = 12; // Hypervisor ecx bit.
@@ -619,17 +620,8 @@ pub fn generate_common_cpuid(
"Generating guest CPUID for with physical address size: {}",
config.phys_bits
);
let cpuid_patches = vec![
// Patch tsc deadline timer bit
CpuidPatch {
function: 1,
index: 0,
flags_bit: None,
eax_bit: None,
ebx_bit: None,
ecx_bit: Some(TSC_DEADLINE_TIMER_ECX_BIT),
edx_bit: None,
},
#[allow(unused_mut)]
let mut cpuid_patches = vec![
// Patch hypervisor bit
CpuidPatch {
function: 1,
@@ -652,6 +644,23 @@ pub fn generate_common_cpuid(
},
];
#[cfg(feature = "kvm")]
if matches!(
hypervisor.hypervisor_type(),
hypervisor::HypervisorType::Kvm
) {
// Patch tsc deadline timer bit
cpuid_patches.push(CpuidPatch {
function: 1,
index: 0,
flags_bit: None,
eax_bit: None,
ebx_bit: None,
ecx_bit: Some(TSC_DEADLINE_TIMER_ECX_BIT),
edx_bit: None,
});
}
// Supported CPUID
let mut cpuid = hypervisor
.get_supported_cpuid()

View File

@@ -12,12 +12,12 @@ io_uring = ["dep:io-uring"]
byteorder = "1.5.0"
crc-any = "2.4.4"
io-uring = { version = "0.6.3", optional = true }
libc = "0.2.153"
log = "0.4.21"
remain = "0.2.13"
libc = "0.2.155"
log = "0.4.22"
remain = "0.2.14"
serde = { version = "1.0.197", features = ["derive"] }
smallvec = "1.13.2"
thiserror = "1.0.60"
thiserror = "1.0.62"
uuid = { version = "1.8.0", features = ["v4"] }
virtio-bindings = { version = "0.2.2", features = ["virtio-v5_0_0"] }
virtio-queue = "0.12.0"

View File

@@ -164,8 +164,8 @@ pub enum ExecuteError {
}
impl ExecuteError {
pub fn status(&self) -> u32 {
match *self {
pub fn status(&self) -> u8 {
let status = match *self {
ExecuteError::BadRequest(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::Flush(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::Read(_) => VIRTIO_BLK_S_IOERR,
@@ -180,7 +180,8 @@ impl ExecuteError {
ExecuteError::AsyncWrite(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::AsyncFlush(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::TemporaryBufferAllocation(_) => VIRTIO_BLK_S_IOERR,
}
};
status as u8
}
}
@@ -247,9 +248,8 @@ impl Request {
let hdr_desc = desc_chain
.next()
.ok_or(Error::DescriptorChainTooShort)
.map_err(|e| {
.inspect_err(|_| {
error!("Missing head descriptor");
e
})?;
// The head contains the request type which MUST be readable.
@@ -275,9 +275,8 @@ impl Request {
let mut desc = desc_chain
.next()
.ok_or(Error::DescriptorChainTooShort)
.map_err(|e| {
.inspect_err(|_| {
error!("Only head descriptor present: request = {:?}", req);
e
})?;
if !desc.has_next() {
@@ -308,9 +307,8 @@ impl Request {
desc = desc_chain
.next()
.ok_or(Error::DescriptorChainTooShort)
.map_err(|e| {
.inspect_err(|_| {
error!("DescriptorChain corrupted: request = {:?}", req);
e
})?;
}
status_desc = desc;

View File

@@ -6,21 +6,26 @@ version = "0.1.0"
[dependencies]
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
anyhow = "1.0.81"
anyhow = "1.0.86"
arch = { path = "../arch" }
bitflags = "2.5.0"
bitflags = "2.6.0"
byteorder = "1.5.0"
event_monitor = { path = "../event_monitor" }
hypervisor = { path = "../hypervisor" }
libc = "0.2.153"
log = "0.4.21"
libc = "0.2.155"
log = "0.4.22"
num_enum = "0.7.2"
pci = { path = "../pci" }
serde = { version = "1.0.197", features = ["derive"] }
thiserror = "1.0.60"
thiserror = "1.0.62"
tpm = { path = "../tpm" }
vm-allocator = { path = "../vm-allocator" }
vm-device = { path = "../vm-device" }
vm-memory = "0.14.1"
vm-memory = { version = "0.14.1", features = [
"backend-atomic",
"backend-bitmap",
"backend-mmap",
] }
vm-migration = { path = "../vm-migration" }
vmm-sys-util = "0.12.1"
@@ -29,3 +34,4 @@ arch = { path = "../arch" }
[features]
default = []
pvmemcontrol = []

View File

@@ -411,14 +411,11 @@ mod tests {
None,
);
serial.write(0, DATA as u64, &[b'x', b'y']);
serial.write(0, DATA as u64, &[b'a']);
serial.write(0, DATA as u64, &[b'b']);
serial.write(0, DATA as u64, &[b'c']);
assert_eq!(
serial_out.buf.lock().unwrap().as_slice(),
&[b'a', b'b', b'c']
);
serial.write(0, DATA as u64, b"xy");
serial.write(0, DATA as u64, b"a");
serial.write(0, DATA as u64, b"b");
serial.write(0, DATA as u64, b"c");
assert_eq!(serial_out.buf.lock().unwrap().as_slice(), b"abc");
}
#[test]
@@ -436,7 +433,7 @@ mod tests {
// counter doesn't change (for 0 it blocks)
assert!(intr_evt.write(1).is_ok());
serial.write(0, IER as u64, &[IER_RECV_BIT]);
serial.queue_input_bytes(&[b'a', b'b', b'c']).unwrap();
serial.queue_input_bytes(b"abc").unwrap();
assert_eq!(intr_evt.read().unwrap(), 2);
@@ -473,7 +470,7 @@ mod tests {
// counter doesn't change (for 0 it blocks)
assert!(intr_evt.write(1).is_ok());
serial.write(0, IER as u64, &[IER_THR_BIT]);
serial.write(0, DATA as u64, &[b'a']);
serial.write(0, DATA as u64, b"a");
assert_eq!(intr_evt.read().unwrap(), 2);
let mut data = [0u8];
@@ -515,9 +512,9 @@ mod tests {
);
serial.write(0, MCR as u64, &[MCR_LOOP_BIT]);
serial.write(0, DATA as u64, &[b'a']);
serial.write(0, DATA as u64, &[b'b']);
serial.write(0, DATA as u64, &[b'c']);
serial.write(0, DATA as u64, b"a");
serial.write(0, DATA as u64, b"b");
serial.write(0, DATA as u64, b"c");
let mut data = [0u8];
serial.read(0, MSR as u64, &mut data[..]);

View File

@@ -524,14 +524,11 @@ mod tests {
None,
);
pl011.write(0, UARTDR, &[b'x', b'y']);
pl011.write(0, UARTDR, &[b'a']);
pl011.write(0, UARTDR, &[b'b']);
pl011.write(0, UARTDR, &[b'c']);
assert_eq!(
pl011_out.buf.lock().unwrap().as_slice(),
&[b'x', b'a', b'b', b'c']
);
pl011.write(0, UARTDR, b"xy");
pl011.write(0, UARTDR, b"a");
pl011.write(0, UARTDR, b"b");
pl011.write(0, UARTDR, b"c");
assert_eq!(pl011_out.buf.lock().unwrap().as_slice(), b"xabc");
}
#[test]
@@ -549,7 +546,7 @@ mod tests {
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd
// counter doesn't change (for 0 it blocks)
assert!(intr_evt.write(1).is_ok());
pl011.queue_input_bytes(&[b'a', b'b', b'c']).unwrap();
pl011.queue_input_bytes(b"abc").unwrap();
assert_eq!(intr_evt.read().unwrap(), 2);

View File

@@ -23,6 +23,8 @@ pub mod interrupt_controller;
#[cfg(target_arch = "x86_64")]
pub mod ioapic;
pub mod legacy;
#[cfg(feature = "pvmemcontrol")]
pub mod pvmemcontrol;
pub mod pvpanic;
pub mod tpm;

819
devices/src/pvmemcontrol.rs Normal file
View File

@@ -0,0 +1,819 @@
// Copyright © 2024 Google LLC
//
// SPDX-License-Identifier: Apache-2.0
//
use num_enum::TryFromPrimitive;
use pci::{
BarReprogrammingParams, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType,
PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciSubclass,
};
use std::{
collections::HashMap,
ffi::CString,
io, result,
sync::{Arc, Barrier, Mutex, RwLock},
};
use thiserror::Error;
use vm_allocator::{page_size::get_page_size, AddressAllocator, SystemAllocator};
use vm_device::{BusDeviceSync, Resource};
use vm_memory::{
bitmap::AtomicBitmap, Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemory,
GuestMemoryAtomic, GuestMemoryError, GuestMemoryMmap, Le32, Le64,
};
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
const PVMEMCONTROL_VENDOR_ID: u16 = 0x1ae0;
const PVMEMCONTROL_DEVICE_ID: u16 = 0x0087;
const PVMEMCONTROL_SUBSYSTEM_VENDOR_ID: u16 = 0x1ae0;
const PVMEMCONTROL_SUBSYSTEM_ID: u16 = 0x011F;
const MAJOR_VERSION: u64 = 1;
const MINOR_VERSION: u64 = 0;
#[derive(Error, Debug)]
pub enum Error {
// device errors
#[error("Guest gave us bad memory addresses: {0}")]
GuestMemory(#[source] GuestMemoryError),
#[error("Guest sent us invalid request")]
InvalidRequest,
#[error("Guest sent us invalid command: {0}")]
InvalidCommand(u32),
#[error("Guest sent us invalid connection: {0}")]
InvalidConnection(u32),
// pvmemcontrol errors
#[error("Request contains invalid arguments: {0}")]
InvalidArgument(u64),
#[error("Unknown function code: {0}")]
UnknownFunctionCode(u64),
#[error("Libc call fail: {0}")]
LibcFail(#[source] std::io::Error),
}
#[derive(Copy, Clone)]
enum PvmemcontrolSubclass {
Other = 0x80,
}
impl PciSubclass for PvmemcontrolSubclass {
fn get_register_value(&self) -> u8 {
*self as u8
}
}
/// commands have 0 as the most significant byte
#[repr(u32)]
#[derive(PartialEq, Eq, Copy, Clone, TryFromPrimitive)]
enum PvmemcontrolTransportCommand {
Reset = 0x060f_e6d2,
Register = 0x0e35_9539,
Ready = 0x0ca8_d227,
Disconnect = 0x030f_5da0,
Ack = 0x03cf_5196,
Error = 0x01fb_a249,
}
#[repr(C)]
#[derive(Copy, Clone)]
struct PvmemcontrolTransportRegister {
buf_phys_addr: Le64,
}
#[repr(C)]
#[derive(Copy, Clone)]
struct PvmemcontrolTransportRegisterResponse {
command: Le32,
_padding: u32,
}
#[repr(C)]
#[derive(Copy, Clone)]
union PvmemcontrolTransportUnion {
register: PvmemcontrolTransportRegister,
register_response: PvmemcontrolTransportRegisterResponse,
unit: (),
}
#[repr(C)]
#[derive(Copy, Clone)]
struct PvmemcontrolTransport {
payload: PvmemcontrolTransportUnion,
command: PvmemcontrolTransportCommand,
}
const PVMEMCONTROL_DEVICE_MMIO_SIZE: u64 = std::mem::size_of::<PvmemcontrolTransport>() as u64;
const PVMEMCONTROL_DEVICE_MMIO_ALIGN: u64 = std::mem::align_of::<PvmemcontrolTransport>() as u64;
impl PvmemcontrolTransport {
fn ack() -> Self {
PvmemcontrolTransport {
payload: PvmemcontrolTransportUnion { unit: () },
command: PvmemcontrolTransportCommand::Ack,
}
}
fn error() -> Self {
PvmemcontrolTransport {
payload: PvmemcontrolTransportUnion { unit: () },
command: PvmemcontrolTransportCommand::Error,
}
}
fn register_response(command: u32) -> Self {
PvmemcontrolTransport {
payload: PvmemcontrolTransportUnion {
register_response: PvmemcontrolTransportRegisterResponse {
command: command.into(),
_padding: 0,
},
},
command: PvmemcontrolTransportCommand::Ack,
}
}
unsafe fn as_register(self) -> PvmemcontrolTransportRegister {
self.payload.register
}
}
// SAFETY: Contains no references and does not have compiler-inserted padding
unsafe impl ByteValued for PvmemcontrolTransportUnion {}
// SAFETY: Contains no references and does not have compiler-inserted padding
unsafe impl ByteValued for PvmemcontrolTransport {}
#[repr(u64)]
#[derive(Copy, Clone, TryFromPrimitive, Debug)]
enum FunctionCode {
Info = 0,
Dontneed = 1,
Remove = 2,
Free = 3,
Pageout = 4,
Dontdump = 5,
SetVMAAnonName = 6,
Mlock = 7,
Munlock = 8,
MprotectNone = 9,
MprotectR = 10,
MprotectW = 11,
MprotectRW = 12,
Mergeable = 13,
Unmergeable = 14,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Default)]
struct PvmemcontrolReq {
func_code: Le64,
addr: Le64,
length: Le64,
arg: Le64,
}
// SAFETY: it only has data and has no implicit padding.
unsafe impl ByteValued for PvmemcontrolReq {}
#[repr(C)]
#[derive(Copy, Clone, Default)]
struct PvmemcontrolResp {
ret_errno: Le32,
ret_code: Le32,
ret_value: Le64,
arg0: Le64,
arg1: Le64,
}
impl std::fmt::Debug for PvmemcontrolResp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let PvmemcontrolResp {
ret_errno,
ret_code,
..
} = self;
write!(
f,
"PvmemcontrolResp {{ ret_errno: {}, ret_code: {}, .. }}",
ret_errno.to_native(),
ret_code.to_native()
)
}
}
// SAFETY: it only has data and has no implicit padding.
unsafe impl ByteValued for PvmemcontrolResp {}
/// The guest connections start at 0x8000_0000, which has a leading 1 in
/// the most significant byte, this ensures it does not conflict with
/// any of the transport commands
#[derive(Hash, Clone, Copy, PartialEq, Eq, Debug)]
pub struct GuestConnection {
command: u32,
}
impl Default for GuestConnection {
fn default() -> Self {
GuestConnection::new(0x8000_0000)
}
}
impl GuestConnection {
fn new(command: u32) -> Self {
Self { command }
}
fn next(&self) -> Self {
let GuestConnection { command } = *self;
if command == u32::MAX {
GuestConnection::default()
} else {
GuestConnection::new(command + 1)
}
}
}
impl TryFrom<u32> for GuestConnection {
type Error = Error;
fn try_from(value: u32) -> Result<Self, Self::Error> {
if (value & 0x8000_0000) != 0 {
Ok(GuestConnection::new(value))
} else {
Err(Error::InvalidConnection(value))
}
}
}
struct PercpuInitState {
port_buf_map: HashMap<GuestConnection, GuestAddress>,
next_conn: GuestConnection,
}
impl PercpuInitState {
fn new() -> Self {
PercpuInitState {
port_buf_map: HashMap::new(),
next_conn: GuestConnection::default(),
}
}
}
enum PvmemcontrolState {
PercpuInit(PercpuInitState),
Ready(HashMap<GuestConnection, GuestAddress>),
Broken,
}
pub struct PvmemcontrolDevice {
transport: PvmemcontrolTransport,
state: PvmemcontrolState,
}
impl PvmemcontrolDevice {
fn new(transport: PvmemcontrolTransport, state: PvmemcontrolState) -> Self {
PvmemcontrolDevice { transport, state }
}
}
impl PvmemcontrolDevice {
fn register_percpu_buf(
guest_memory: &GuestMemoryAtomic<GuestMemoryMmap<AtomicBitmap>>,
mut state: PercpuInitState,
PvmemcontrolTransportRegister { buf_phys_addr }: PvmemcontrolTransportRegister,
) -> Self {
// access to this address is checked
let buf_phys_addr = GuestAddress(buf_phys_addr.into());
if !guest_memory.memory().check_range(
buf_phys_addr,
std::mem::size_of::<PvmemcontrolResp>().max(std::mem::size_of::<PvmemcontrolReq>()),
) {
warn!("guest sent invalid phys addr {:#x}", buf_phys_addr.0);
return PvmemcontrolDevice::new(
PvmemcontrolTransport::error(),
PvmemcontrolState::Broken,
);
}
let conn = {
// find an available port+byte combination, and fail if full
let mut next_conn = state.next_conn;
while state.port_buf_map.contains_key(&next_conn) {
next_conn = next_conn.next();
if next_conn == state.next_conn {
warn!("connections exhausted");
return PvmemcontrolDevice::new(
PvmemcontrolTransport::error(),
PvmemcontrolState::Broken,
);
}
}
next_conn
};
state.next_conn = conn.next();
state.port_buf_map.insert(conn, buf_phys_addr);
// inform guest of the connection
let response = PvmemcontrolTransport::register_response(conn.command);
PvmemcontrolDevice::new(response, PvmemcontrolState::PercpuInit(state))
}
fn reset() -> Self {
PvmemcontrolDevice::new(
PvmemcontrolTransport::ack(),
PvmemcontrolState::PercpuInit(PercpuInitState::new()),
)
}
fn error() -> Self {
PvmemcontrolDevice::new(PvmemcontrolTransport::error(), PvmemcontrolState::Broken)
}
fn ready(PercpuInitState { port_buf_map, .. }: PercpuInitState) -> Self {
PvmemcontrolDevice::new(
PvmemcontrolTransport::ack(),
PvmemcontrolState::Ready(port_buf_map),
)
}
fn run_command(
&mut self,
guest_memory: &GuestMemoryAtomic<GuestMemoryMmap<AtomicBitmap>>,
command: PvmemcontrolTransportCommand,
) {
let state = std::mem::replace(&mut self.state, PvmemcontrolState::Broken);
*self = match command {
PvmemcontrolTransportCommand::Reset => Self::reset(),
PvmemcontrolTransportCommand::Register => {
if let PvmemcontrolState::PercpuInit(state) = state {
// SAFETY: By device protocol. If driver is wrong the device
// can enter a Broken state, but the behavior is still sound.
Self::register_percpu_buf(guest_memory, state, unsafe {
self.transport.as_register()
})
} else {
debug!("received register without reset");
Self::error()
}
}
PvmemcontrolTransportCommand::Ready => {
if let PvmemcontrolState::PercpuInit(state) = state {
Self::ready(state)
} else {
debug!("received ready without reset");
Self::error()
}
}
PvmemcontrolTransportCommand::Disconnect => Self::error(),
PvmemcontrolTransportCommand::Ack => {
debug!("received ack as command");
Self::error()
}
PvmemcontrolTransportCommand::Error => {
debug!("received error as command");
Self::error()
}
}
}
/// read from the transport
fn read_transport(&self, offset: u64, data: &mut [u8]) {
self.transport
.as_slice()
.iter()
.skip(offset as usize)
.zip(data.iter_mut())
.for_each(|(src, dest)| *dest = *src)
}
/// can only write to transport payload
/// command is a special register that needs separate dispatching
fn write_transport(&mut self, offset: u64, data: &[u8]) {
self.transport
.payload
.as_mut_slice()
.iter_mut()
.skip(offset as usize)
.zip(data.iter())
.for_each(|(dest, src)| *dest = *src)
}
fn find_connection(&self, conn: GuestConnection) -> Option<GuestAddress> {
match &self.state {
PvmemcontrolState::Ready(map) => map.get(&conn).copied(),
_ => None,
}
}
}
pub struct PvmemcontrolBusDevice {
mem: GuestMemoryAtomic<GuestMemoryMmap<AtomicBitmap>>,
dev: RwLock<PvmemcontrolDevice>,
}
pub struct PvmemcontrolPciDevice {
id: String,
configuration: PciConfiguration,
bar_regions: Vec<PciBarConfiguration>,
}
impl PvmemcontrolBusDevice {
/// f is called with the host address of `range_base` and only when
/// [`range_base`, `range_base` + `range_len`) is present in the guest
fn operate_on_memory_range<F>(&self, addr: u64, length: u64, f: F) -> result::Result<(), Error>
where
F: FnOnce(*mut libc::c_void, libc::size_t) -> libc::c_int,
{
let memory = self.mem.memory();
let range_base = GuestAddress(addr);
let range_len = usize::try_from(length).map_err(|_| Error::InvalidRequest)?;
// assume guest memory is not interleaved with vmm memory on the host.
if !memory.check_range(range_base, range_len) {
return Err(Error::GuestMemory(GuestMemoryError::InvalidGuestAddress(
range_base,
)));
}
let hva = memory
.get_host_address(range_base)
.map_err(Error::GuestMemory)?;
let res = f(hva as *mut libc::c_void, range_len as libc::size_t);
if res != 0 {
return Err(Error::LibcFail(io::Error::last_os_error()));
}
Ok(())
}
fn madvise(&self, addr: u64, length: u64, advice: libc::c_int) -> result::Result<(), Error> {
// SAFETY: [`base`, `base` + `len`) is guest memory
self.operate_on_memory_range(addr, length, |base, len| unsafe {
libc::madvise(base, len, advice)
})
}
fn mlock(&self, addr: u64, length: u64, on_default: bool) -> result::Result<(), Error> {
// SAFETY: [`base`, `base` + `len`) is guest memory
self.operate_on_memory_range(addr, length, |base, len| unsafe {
libc::mlock2(base, len, if on_default { libc::MLOCK_ONFAULT } else { 0 })
})
}
fn munlock(&self, addr: u64, length: u64) -> result::Result<(), Error> {
// SAFETY: [`base`, `base` + `len`) is guest memory
self.operate_on_memory_range(addr, length, |base, len| unsafe {
libc::munlock(base, len)
})
}
fn mprotect(
&self,
addr: u64,
length: u64,
protection: libc::c_int,
) -> result::Result<(), Error> {
// SAFETY: [`base`, `base` + `len`) is guest memory
self.operate_on_memory_range(addr, length, |base, len| unsafe {
libc::mprotect(base, len, protection)
})
}
fn set_vma_anon_name(&self, addr: u64, length: u64, name: u64) -> result::Result<(), Error> {
let name = (name != 0).then(|| CString::new(format!("pvmemcontrol-{}", name)).unwrap());
let name_ptr = if let Some(name) = &name {
name.as_ptr()
} else {
std::ptr::null()
};
debug!("addr {:X} length {} name {:?}", addr, length, name);
// SAFETY: [`base`, `base` + `len`) is guest memory
self.operate_on_memory_range(addr, length, |base, len| unsafe {
libc::prctl(
libc::PR_SET_VMA,
libc::PR_SET_VMA_ANON_NAME,
base,
len,
name_ptr,
)
})
}
fn process_request(
&self,
func_code: FunctionCode,
addr: u64,
length: u64,
arg: u64,
) -> Result<PvmemcontrolResp, Error> {
let result = match func_code {
FunctionCode::Info => {
return Ok(PvmemcontrolResp {
ret_errno: 0.into(),
ret_code: 0.into(),
ret_value: get_page_size().into(),
arg0: MAJOR_VERSION.into(),
arg1: MINOR_VERSION.into(),
})
}
FunctionCode::Dontneed => self.madvise(addr, length, libc::MADV_DONTNEED),
FunctionCode::Remove => self.madvise(addr, length, libc::MADV_REMOVE),
FunctionCode::Free => self.madvise(addr, length, libc::MADV_FREE),
FunctionCode::Pageout => self.madvise(addr, length, libc::MADV_PAGEOUT),
FunctionCode::Dontdump => self.madvise(addr, length, libc::MADV_DONTDUMP),
FunctionCode::SetVMAAnonName => self.set_vma_anon_name(addr, length, arg),
FunctionCode::Mlock => self.mlock(addr, length, false),
FunctionCode::Munlock => self.munlock(addr, length),
FunctionCode::MprotectNone => self.mprotect(addr, length, libc::PROT_NONE),
FunctionCode::MprotectR => self.mprotect(addr, length, libc::PROT_READ),
FunctionCode::MprotectW => self.mprotect(addr, length, libc::PROT_WRITE),
FunctionCode::MprotectRW => {
self.mprotect(addr, length, libc::PROT_READ | libc::PROT_WRITE)
}
FunctionCode::Mergeable => self.madvise(addr, length, libc::MADV_MERGEABLE),
FunctionCode::Unmergeable => self.madvise(addr, length, libc::MADV_UNMERGEABLE),
};
result.map(|_| PvmemcontrolResp::default())
}
fn handle_request(
&self,
PvmemcontrolReq {
func_code,
addr,
length,
arg,
}: PvmemcontrolReq,
) -> Result<PvmemcontrolResp, Error> {
let (func_code, addr, length, arg) = (
func_code.to_native(),
addr.to_native(),
length.to_native(),
arg.to_native(),
);
let resp_or_err = FunctionCode::try_from(func_code)
.map_err(|_| Error::UnknownFunctionCode(func_code))
.and_then(|func_code| self.process_request(func_code, addr, length, arg));
let resp = match resp_or_err {
Ok(resp) => resp,
Err(e) => match e {
Error::InvalidArgument(arg) => PvmemcontrolResp {
ret_errno: (libc::EINVAL as u32).into(),
ret_code: (arg as u32).into(),
..Default::default()
},
Error::LibcFail(err) => PvmemcontrolResp {
ret_errno: (err.raw_os_error().unwrap_or(libc::EFAULT) as u32).into(),
ret_code: 0u32.into(),
..Default::default()
},
Error::UnknownFunctionCode(func_code) => PvmemcontrolResp {
ret_errno: (libc::EOPNOTSUPP as u32).into(),
ret_code: (func_code as u32).into(),
..Default::default()
},
Error::GuestMemory(err) => {
warn!("{}", err);
PvmemcontrolResp {
ret_errno: (libc::EINVAL as u32).into(),
ret_code: (func_code as u32).into(),
..Default::default()
}
}
// device error, stop responding
other => return Err(other),
},
};
Ok(resp)
}
fn handle_pvmemcontrol_request(&self, guest_addr: GuestAddress) {
let request: PvmemcontrolReq = if let Ok(x) = self.mem.memory().read_obj(guest_addr) {
x
} else {
warn!("cannot read from guest address {:#x}", guest_addr.0);
return;
};
let response: PvmemcontrolResp = match self.handle_request(request) {
Ok(x) => x,
Err(e) => {
warn!("cannot process request {:?} with error {}", request, e);
return;
}
};
if self.mem.memory().write_obj(response, guest_addr).is_err() {
warn!("cannot write to guest address {:#x}", guest_addr.0);
}
}
fn handle_guest_write(&self, offset: u64, data: &[u8]) {
if offset as usize != std::mem::offset_of!(PvmemcontrolTransport, command) {
if data.len() != 4 && data.len() != 8 {
warn!("guest write is not 4 or 8 bytes long");
return;
}
self.dev.write().unwrap().write_transport(offset, data);
return;
}
let data = if data.len() == 4 {
let mut d = [0u8; 4];
d.iter_mut()
.zip(data.iter())
.for_each(|(d, data)| *d = *data);
d
} else {
warn!("guest write with non u32 at command register");
return;
};
let data_cmd = u32::from_le_bytes(data);
let command = PvmemcontrolTransportCommand::try_from(data_cmd);
match command {
Ok(command) => self.dev.write().unwrap().run_command(&self.mem, command),
Err(_) => {
GuestConnection::try_from(data_cmd)
.and_then(|conn| {
self.dev
.read()
.unwrap()
.find_connection(conn)
.ok_or(Error::InvalidConnection(conn.command))
})
.map(|gpa| self.handle_pvmemcontrol_request(gpa))
.unwrap_or_else(|err| warn!("{:?}", err));
}
}
}
fn handle_guest_read(&self, offset: u64, data: &mut [u8]) {
self.dev.read().unwrap().read_transport(offset, data)
}
}
impl PvmemcontrolDevice {
pub fn make_device(
id: String,
mem: GuestMemoryAtomic<GuestMemoryMmap<AtomicBitmap>>,
) -> (PvmemcontrolPciDevice, PvmemcontrolBusDevice) {
let dev = RwLock::new(PvmemcontrolDevice::error());
let mut configuration = PciConfiguration::new(
PVMEMCONTROL_VENDOR_ID,
PVMEMCONTROL_DEVICE_ID,
0x1,
PciClassCode::BaseSystemPeripheral,
&PvmemcontrolSubclass::Other,
None,
PciHeaderType::Device,
PVMEMCONTROL_SUBSYSTEM_VENDOR_ID,
PVMEMCONTROL_SUBSYSTEM_ID,
None,
None,
);
let command: [u8; 2] = [0x03, 0x01]; // memory, io, SERR#
configuration.write_config_register(1, 0, &command);
(
PvmemcontrolPciDevice {
id,
configuration,
bar_regions: Vec::new(),
},
PvmemcontrolBusDevice { mem, dev },
)
}
}
impl PciDevice for PvmemcontrolPciDevice {
fn write_config_register(
&mut self,
reg_idx: usize,
offset: u64,
data: &[u8],
) -> Option<Arc<Barrier>> {
self.configuration
.write_config_register(reg_idx, offset, data);
None
}
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
self.configuration.read_config_register(reg_idx)
}
fn as_any(&mut self) -> &mut dyn std::any::Any {
self
}
fn id(&self) -> Option<String> {
Some(self.id.clone())
}
fn detect_bar_reprogramming(
&mut self,
reg_idx: usize,
data: &[u8],
) -> Option<BarReprogrammingParams> {
self.configuration.detect_bar_reprogramming(reg_idx, data)
}
fn allocate_bars(
&mut self,
_allocator: &Arc<Mutex<SystemAllocator>>,
mmio32_allocator: &mut AddressAllocator,
_mmio64_allocator: &mut AddressAllocator,
resources: Option<Vec<Resource>>,
) -> Result<Vec<PciBarConfiguration>, PciDeviceError> {
let mut bars = Vec::new();
let region_type = PciBarRegionType::Memory32BitRegion;
let bar_id = 0;
let region_size = PVMEMCONTROL_DEVICE_MMIO_SIZE;
let restoring = resources.is_some();
let bar_addr = mmio32_allocator
.allocate(None, region_size, Some(PVMEMCONTROL_DEVICE_MMIO_ALIGN))
.ok_or(PciDeviceError::IoAllocationFailed(region_size))?;
let bar = PciBarConfiguration::default()
.set_index(bar_id as usize)
.set_address(bar_addr.raw_value())
.set_size(region_size)
.set_region_type(region_type)
.set_prefetchable(PciBarPrefetchable::NotPrefetchable);
if !restoring {
self.configuration
.add_pci_bar(&bar)
.map_err(|e| PciDeviceError::IoRegistrationFailed(bar_addr.raw_value(), e))?;
}
bars.push(bar);
self.bar_regions.clone_from(&bars);
Ok(bars)
}
fn free_bars(
&mut self,
_allocator: &mut SystemAllocator,
mmio32_allocator: &mut AddressAllocator,
_mmio64_allocator: &mut AddressAllocator,
) -> Result<(), PciDeviceError> {
for bar in self.bar_regions.drain(..) {
mmio32_allocator.free(GuestAddress(bar.addr()), bar.size())
}
Ok(())
}
fn move_bar(&mut self, old_base: u64, new_base: u64) -> result::Result<(), io::Error> {
for bar in self.bar_regions.iter_mut() {
if bar.addr() == old_base {
*bar = bar.set_address(new_base);
}
}
Ok(())
}
}
impl Pausable for PvmemcontrolPciDevice {
fn pause(&mut self) -> std::result::Result<(), MigratableError> {
Ok(())
}
fn resume(&mut self) -> std::result::Result<(), MigratableError> {
Ok(())
}
}
impl Snapshottable for PvmemcontrolPciDevice {
fn id(&self) -> String {
self.id.clone()
}
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
let mut snapshot = Snapshot::new_from_state(&())?;
// Snapshot PciConfiguration
snapshot.add_snapshot(self.configuration.id(), self.configuration.snapshot()?);
Ok(snapshot)
}
}
impl Transportable for PvmemcontrolPciDevice {}
impl Migratable for PvmemcontrolPciDevice {}
impl BusDeviceSync for PvmemcontrolBusDevice {
fn read(&self, _base: u64, offset: u64, data: &mut [u8]) {
self.handle_guest_read(offset, data)
}
fn write(&self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
self.handle_guest_write(offset, data);
None
}
}

105
docs/landlock.md Normal file
View File

@@ -0,0 +1,105 @@
# Sandboxing using Landlock
Landlock is a lightweight mechanism to allow unprivileged applications to
sandbox themselves.
During initial stages of running, applications can define the set of resources
(mostly files) they need to access during their lifetime. All such rules are
used to create a ruleset. Once the ruleset is applied, the process cannot access
any resources outside of the ruleset during its lifetime, even if it were
compromised.
Under the scope of `read` and `write` access, Landlock currently allows some
additional accesses (eg: for now, access to extended file attributes is always
allowed). Eventually, Landlock will only allow accesses similar to Unix
permissions.
## Host Setup
Landlock should be enabled in Host kernel to use it with cloud-hypervisor.
Please following [Kernel-Support](https://docs.kernel.org/userspace-api/landlock.html#kernel-support) link to enable Landlock on Host kernel.
Landlock support can be checked with following command:
```
$ sudo dmesg | grep -w landlock
[ 0.000000] landlock: Up and running.
```
Linux kernel confirms Landlock support with above message in dmesg.
## Enable Landlock
At the time of enabling Landlock, Cloud-Hypervisor process needs the complete
list of files it accesses over its lifetime. So, Landlock is enabled `vm_create`
stage of guest boot.
### Command Line
Append `--landlock` to Cloud-Hypervisor's command line to enable Landlock
support.
If you expect guest to access additional paths after it boots
(ex: during hotplug), those paths can be passed using `--landlock-rules` command
line parameter.
### API
Landlock can also be enabled during `vm.create` request by passing a config like below:
```
{
...
"landlock_enable": true,
"landlock_rules": [
{
"path": "/tmp/disk1",
"access": "rw"
},
{
"path": "/tmp/disk2",
"access": "rw"
}
]
...
}
```
## Usage Examples
To enable Landlock:
```
./cloud-hypervisor \
--kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
--cmdline "console=hvc0 root=/dev/vda1 rw" \
--cpus boot=4 \
--memory size=1024M \
--net "tap=,mac=,ip=,mask=" \
--landlock
```
Hotplugging any new file-backed resources to above guest will result in
**Permission Denied** error.
To enable Landlock with hotplug support:
```
./cloud-hypervisor \
--api-socket /tmpXXXX/ch.socket \
--kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
--cmdline "console=hvc0 root=/dev/vda1 rw" \
--cpus boot=4 \
--memory size=1024M \
--net "tap=,mac=,ip=,mask=" \
--landlock \
--landlock-rules path="/path/to/hotplug1",access="rw" path="/path/to/hotplug2",access="rw"
./ch-remote --api-socket /tmpXXXX/ch.socket \
add-disk "path=/path/to/hotplug/blk.raw"
```
`--landlock-rules` accepts file or directory paths among its options.
# References
* https://landlock.io/

View File

@@ -8,6 +8,8 @@ e.g. `dev_cli.sh`. The only prerequisite is [Docker installation](https://docs.d
Please note that upon its first invocation, this script will pull a
fairly large container image.
## Run the Performance Tests
To generate metrics data for all available performance tests (including
boot time, block I/O throughput, and network throughput & latency) and
output the result into a json file:
@@ -27,3 +29,92 @@ To generate metrics data for selected performance tests, e.g. boot time only:
```
$ ./scripts/dev_cli.sh tests --metrics -- -- --report-file /tmp/metrics.json --test-filter boot_time
```
To set custom timeout or test iterations for all performance tests:
```
$ ./scripts/dev_cli.sh tests --metrics -- -- --timeout 5 --iterations 10
```
## Performance Tests Details
The following table lists the supported performance tests with default
timeout and number of iterations. The `timeout` defines the maximum
execution time of each test for each iteration. The `iteration` defines
how many times a test needs to be executed to generate the final metrics
data.
| **Type** | **Metric** | **Timeout(s)** | **Iterations** |
|------------|--------------------------------------------|----------------|----------------|
| Boot Time | boot_time_ms | 2 | 10 |
| | boot_time_pmem_ms | 2 | 10 |
| | boot_time_16_vcpus_ms | 2 | 10 |
| | boot_time_16_vcpus_pmem_ms | 2 | 10 |
| Virtio Net | virtio_net_latency_us | 10 | 5 |
| | virtio_net_throughput_single_queue_rx_gbps | 10 | 5 |
| | virtio_net_throughput_single_queue_tx_gbps | 10 | 5 |
| | virtio_net_throughput_multi_queue_rx_gbps | 10 | 5 |
| | virtio_net_throughput_multi_queue_tx_gbps | 10 | 5 |
| Block | block_read_MiBps | 10 | 5 |
| | block_write_MiBps | 10 | 5 |
| | block_random_read_MiBps | 10 | 5 |
| | block_random_write_MiBps | 10 | 5 |
| | block_multi_queue_read_MiBps | 10 | 5 |
| | block_multi_queue_write_MiBps | 10 | 5 |
| | block_multi_queue_random_read_MiBps | 10 | 5 |
| | block_multi_queue_random_write_MiBps | 10 | 5 |
| | block_read_IOPS | 10 | 5 |
| | block_write_IOPS | 10 | 5 |
| | block_random_read_IOPS | 10 | 5 |
| | block_random_write_IOPS | 10 | 5 |
| | block_multi_queue_read_IOPS | 10 | 5 |
| | block_multi_queue_write_IOPS | 10 | 5 |
| | block_multi_queue_random_read_IOPS | 10 | 5 |
| | block_multi_queue_random_write_IOPS | 10 | 5 |
| Other | restore_latency_time_ms | 2 | 10 |
## Output Format
Performance-metrics output the result into a json file if `report-file`
param is set. The fields included in JSON include:
| Field Name | Content |
|--------------------|------------------------------------------|
| git_human_readable | Recent tag information of git repository |
| git_revision | Commit id of HEAD |
| git_commit_date | Commit date of HEAD |
| date | Date for executing the program |
| results | A list of metrics |
## Example
Here is an example of generating metrics data for the boot time using
`pmem`:
```bash
$ ./scripts/dev_cli.sh tests --metrics -- -- --test-filter boot_time_pmem_ms
```
Here is a sample output:
```json
{
"git_human_readable": "v40.0",
"git_revision": "e9b263975786abbf895469b93dfc00f21ce39a88",
"git_commit_date": "Fri Jun 21 08:40:44 2024 +0000",
"date": "Tue Jul 16 16:35:29 UTC 2024",
"results": [
{
"name": "boot_time_pmem_ms",
"mean": 105.9461,
"std_dev": 7.140993312558129,
"max": 120.01499999999999,
"min": 92.37600000000002
}
]
}
```
Note that the metrics data above is for illustration purpose only and
does not represent the actual performance of Cloud Hypervisor on your
system.

View File

@@ -6,7 +6,7 @@ version = "0.1.0"
[dependencies]
flume = "0.11.0"
libc = "0.2.153"
libc = "0.2.155"
once_cell = "1.19.0"
serde = { version = "1.0.197", features = ["derive", "rc"] }
serde_json = "1.0.115"
serde_json = "1.0.120"

303
fuzz/Cargo.lock generated
View File

@@ -12,9 +12,9 @@ dependencies = [
[[package]]
name = "anstream"
version = "0.6.14"
version = "0.6.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -27,33 +27,33 @@ dependencies = [
[[package]]
name = "anstyle"
version = "1.0.7"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
[[package]]
name = "anstyle-parse"
version = "0.2.3"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.3"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
dependencies = [
"anstyle",
"windows-sys",
@@ -61,9 +61,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.82"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "arbitrary"
@@ -111,9 +111,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "block"
@@ -149,13 +149,13 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
version = "1.0.99"
version = "1.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
checksum = "5fb8dd288a69fc53a1996d7ecfbf4a20d59065bff137ce7e56bbd620de191189"
dependencies = [
"jobserver",
"libc",
"once_cell",
"shlex",
]
[[package]]
@@ -166,30 +166,30 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.5.7"
version = "4.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f"
checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.7"
version = "4.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f"
checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim 0.11.1",
"strsim",
]
[[package]]
name = "clap_lex"
version = "0.7.1"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]]
name = "cloud-hypervisor-fuzz"
@@ -217,9 +217,9 @@ dependencies = [
[[package]]
name = "colorchoice"
version = "1.0.1"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]]
name = "crc-any"
@@ -232,9 +232,9 @@ dependencies = [
[[package]]
name = "darling"
version = "0.20.8"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391"
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
dependencies = [
"darling_core",
"darling_macro",
@@ -242,23 +242,23 @@ dependencies = [
[[package]]
name = "darling_core"
version = "0.20.8"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f"
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim 0.10.0",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.20.8"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
dependencies = [
"darling_core",
"quote",
@@ -278,12 +278,13 @@ dependencies = [
"acpi_tables",
"anyhow",
"arch",
"bitflags 2.5.0",
"bitflags 2.6.0",
"byteorder",
"event_monitor",
"hypervisor",
"libc",
"log",
"num_enum",
"pci",
"serde",
"thiserror",
@@ -295,16 +296,42 @@ dependencies = [
"vmm-sys-util",
]
[[package]]
name = "enumflags2"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d"
dependencies = [
"enumflags2_derive",
]
[[package]]
name = "enumflags2_derive"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "epoll"
version = "4.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"libc",
]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "event_monitor"
version = "0.1.0"
@@ -358,7 +385,7 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6341b3480afbb34eaefc7f92713bc92f2d83e338aaa1c44192f9c2956f4a4903"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"cfg-if",
"log",
"managed",
@@ -389,6 +416,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "hypervisor"
version = "0.1.0"
@@ -414,10 +447,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.0"
name = "indexmap"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itoa"
@@ -427,9 +470,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "jobserver"
version = "0.1.31"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
dependencies = [
"libc",
]
@@ -460,12 +503,23 @@ version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bedae2ca4a531bebe311abaf9691f5cc14eaa21475243caa2e39c43bb872947d"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"kvm-bindings",
"libc",
"vmm-sys-util",
]
[[package]]
name = "landlock"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dafb8a4afee64f167eb2b52d32f0eea002e41a7a6450e68c799c8ec3a81a634c"
dependencies = [
"enumflags2",
"libc",
"thiserror",
]
[[package]]
name = "libc"
version = "0.2.155"
@@ -504,9 +558,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.21"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "managed"
@@ -514,6 +568,12 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
[[package]]
name = "memchr"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[package]]
name = "micro_http"
version = "0.1.0"
@@ -567,6 +627,27 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num_enum"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "once_cell"
version = "1.19.0"
@@ -605,10 +686,19 @@ dependencies = [
]
[[package]]
name = "proc-macro2"
version = "1.0.83"
name = "proc-macro-crate"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284"
dependencies = [
"toml_edit",
]
[[package]]
name = "proc-macro2"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
@@ -687,9 +777,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.117"
version = "1.0.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
dependencies = [
"itoa",
"ryu",
@@ -698,9 +788,9 @@ dependencies = [
[[package]]
name = "serde_with"
version = "3.8.1"
version = "3.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20"
checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857"
dependencies = [
"serde",
"serde_derive",
@@ -709,9 +799,9 @@ dependencies = [
[[package]]
name = "serde_with_macros"
version = "3.8.1"
version = "3.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2"
checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350"
dependencies = [
"darling",
"proc-macro2",
@@ -723,6 +813,12 @@ dependencies = [
name = "serial_buffer"
version = "0.1.0"
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook"
version = "0.3.17"
@@ -757,12 +853,6 @@ dependencies = [
"lock_api",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strsim"
version = "0.11.1"
@@ -771,9 +861,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.66"
version = "2.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
dependencies = [
"proc-macro2",
"quote",
@@ -782,24 +872,41 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.61"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.61"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml_datetime"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
[[package]]
name = "toml_edit"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
dependencies = [
"indexmap",
"toml_datetime",
"winnow",
]
[[package]]
name = "tpm"
version = "0.1.0"
@@ -832,15 +939,15 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "utf8parse"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.8.0"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
dependencies = [
"getrandom",
]
@@ -892,7 +999,7 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6be08d1166d41a78861ad50212ab3f9eca0729c349ac3a7a8f557c62406b87cc"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"libc",
"vm-memory",
"vmm-sys-util",
@@ -1016,7 +1123,7 @@ dependencies = [
"anyhow",
"arc-swap",
"arch",
"bitflags 2.5.0",
"bitflags 2.6.0",
"block",
"cfg-if",
"clap",
@@ -1027,6 +1134,7 @@ dependencies = [
"gdbstub",
"gdbstub_arch",
"hypervisor",
"landlock",
"libc",
"linux-loader",
"log",
@@ -1162,9 +1270,9 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
@@ -1178,57 +1286,66 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
dependencies = [
"memchr",
]
[[package]]
name = "zerocopy"
version = "0.7.34"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"byteorder",
"zerocopy-derive",
@@ -1236,9 +1353,9 @@ dependencies = [
[[package]]
name = "zerocopy-derive"
version = "0.7.34"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -10,6 +10,7 @@ cargo-fuzz = true
[features]
igvm = []
pvmemcontrol = []
[dependencies]
block = { path = "../block" }

View File

@@ -180,6 +180,8 @@ impl RequestHandler for StubApiRequestHandler {
vdpa: None,
vsock: None,
pvpanic: false,
#[cfg(feature = "pvmemcontrol")]
pvmemcontrol: None,
iommu: false,
#[cfg(target_arch = "x86_64")]
sgx_epc: None,
@@ -190,6 +192,8 @@ impl RequestHandler for StubApiRequestHandler {
platform: None,
tpm: None,
preserved_fds: None,
landlock_enable: false,
landlock_rules: None,
})),
state: VmState::Running,
memory_actual_size: 0,

View File

@@ -12,24 +12,24 @@ sev_snp = ["igvm", "igvm_defs"]
tdx = []
[dependencies]
anyhow = "1.0.81"
anyhow = "1.0.86"
byteorder = "1.5.0"
igvm = { version = "0.3.1", optional = true }
igvm = { version = "0.3.3", optional = true }
igvm_defs = { version = "0.3.1", optional = true }
kvm-bindings = { version = "0.8.1", optional = true, features = ["serde"] }
kvm-ioctls = { version = "0.17.0", optional = true }
libc = "0.2.153"
log = "0.4.21"
libc = "0.2.155"
log = "0.4.22"
mshv-bindings = { git = "https://github.com/rust-vmm/mshv", tag = "v0.2.0", features = [
"fam-wrappers",
"with-serde",
], optional = true }
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", tag = "v0.2.0", optional = true }
serde = { version = "1.0.197", features = ["derive", "rc"] }
serde_with = { version = "3.7.0", default-features = false, features = [
serde_with = { version = "3.9.0", default-features = false, features = [
"macros",
] }
thiserror = "1.0.60"
thiserror = "1.0.62"
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
vm-memory = { version = "0.14.1", features = [
"backend-atomic",

View File

@@ -137,14 +137,6 @@ pub trait PlatformEmulator {
///
fn set_cpu_state(&self, cpu_id: usize, state: Self::CpuState) -> Result<(), PlatformError>;
/// Translate a guest virtual address into a physical one
///
/// # Arguments
///
/// * `gva` - Guest virtual address to translate.
///
fn gva_to_gpa(&self, gva: u64) -> Result<u64, PlatformError>;
/// Fetch instruction bytes from memory.
///
/// # Arguments

View File

@@ -13,6 +13,7 @@ pub mod cmp;
pub mod mov;
pub mod movs;
pub mod or;
pub mod stos;
fn get_op<T: CpuStateManager>(
insn: &Instruction,

View File

@@ -40,7 +40,7 @@ macro_rules! movs {
let len = std::mem::size_of::<$bound>();
while count > 0 {
let mut memory: [u8; 4] = [0; 4];
let mut memory: [u8; 8] = [0; 8];
let src = state
.linearize(Register::DS, rsi, false)
@@ -83,6 +83,11 @@ macro_rules! movs {
};
}
pub struct Movsq_m64_m64;
impl<T: CpuStateManager> InstructionHandler<T> for Movsq_m64_m64 {
movs!(u64);
}
pub struct Movsd_m32_m32;
impl<T: CpuStateManager> InstructionHandler<T> for Movsd_m32_m32 {
movs!(u32);
@@ -103,6 +108,40 @@ mod tests {
use super::*;
use crate::arch::x86::emulator::mock_vmm::*;
#[test]
fn test_rep_movsq_m64_m64() {
let ip: u64 = 0x1000;
let memory: [u8; 32] = [
0x78, 0x56, 0x34, 0x12, // 0x12345678
0xdd, 0xcc, 0xbb, 0xaa, // 0xaabbccdd
0xa5, 0x5a, 0xa5, 0x5a, // 0x5aa55aa5
0xcd, 0xcd, 0xcd, 0xcd, // 0xcdcdcdcd
0x00, 0x00, 0x00, 0x00, // 0x00000000
0x00, 0x00, 0x00, 0x00, // 0x00000000
0x00, 0x00, 0x00, 0x00, // 0x00000000
0x00, 0x00, 0x00, 0x00, // 0x00000000
];
let insn = [0xf3, 0x48, 0xa5]; // rep movsq
let regs = vec![
(Register::ECX, 2),
(Register::ESI, 0),
(Register::EDI, 0x10),
];
let mut data = [0u8; 8];
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
vmm.read_memory(0x10, &mut data).unwrap();
assert_eq!(0xaabbccdd12345678, <u64>::from_le_bytes(data));
vmm.read_memory(0x18, &mut data).unwrap();
assert_eq!(0xcdcdcdcd5aa55aa5, <u64>::from_le_bytes(data));
// The rest should be default value 0 from MockVmm
vmm.read_memory(0x20, &mut data).unwrap();
assert_eq!(0x0, <u64>::from_le_bytes(data));
}
#[test]
fn test_rep_movsd_m32_m32() {
let ip: u64 = 0x1000;

View File

@@ -0,0 +1,236 @@
//
// Copyright © 2024 Microsoft
//
// SPDX-License-Identifier: Apache-2.0
//
#![allow(non_camel_case_types)]
//
// STOS - Store String
//
use crate::arch::x86::emulator::instructions::*;
use crate::arch::x86::regs::DF;
macro_rules! stos {
($bound:ty) => {
fn emulate(
&self,
insn: &Instruction,
state: &mut T,
platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<(), EmulationError<Exception>> {
let mut count: u64 = if insn.has_rep_prefix() {
state
.read_reg(Register::ECX)
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?
} else {
1
};
let rax = state
.read_reg(Register::RAX)
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
let mut rdi = state
.read_reg(Register::RDI)
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
let df = (state.flags() & DF) != 0;
let len = std::mem::size_of::<$bound>();
let rax_bytes = rax.to_le_bytes();
while count > 0 {
let dst = state
.linearize(Register::ES, rdi, true)
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
platform
.write_memory(dst, &rax_bytes[0..len])
.map_err(EmulationError::PlatformEmulationError)?;
if df {
rdi = rdi.wrapping_sub(len as u64);
} else {
rdi = rdi.wrapping_add(len as u64);
}
count -= 1;
}
if insn.has_rep_prefix() {
state
.write_reg(Register::ECX, 0)
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
}
Ok(())
}
};
}
pub struct Stosq_m64_RAX;
impl<T: CpuStateManager> InstructionHandler<T> for Stosq_m64_RAX {
stos!(u64);
}
pub struct Stosd_m32_EAX;
impl<T: CpuStateManager> InstructionHandler<T> for Stosd_m32_EAX {
stos!(u32);
}
pub struct Stosw_m16_AX;
impl<T: CpuStateManager> InstructionHandler<T> for Stosw_m16_AX {
stos!(u16);
}
pub struct Stosb_m8_AL;
impl<T: CpuStateManager> InstructionHandler<T> for Stosb_m8_AL {
stos!(u8);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::arch::x86::emulator::mock_vmm::*;
#[test]
fn test_rep_stosb() {
let ip: u64 = 0x1000;
let memory: [u8; 12] = [
0x78, 0x56, 0x34, 0x12, // 0x12345678
0xdd, 0xcc, 0xbb, 0xaa, // 0xaabbccdd
0xa5, 0x5a, 0xa5, 0x5a, // 0x5aa55aa5
];
let insn = [0xf3, 0xaa]; // rep stosb
let regs = vec![
(Register::ECX, 3),
(Register::EDI, 0x0),
(Register::RAX, 0x123456ff),
];
let mut data = [0u8; 4];
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
vmm.read_memory(0, &mut data).unwrap();
assert_eq!(0x12ffffff, <u32>::from_le_bytes(data));
vmm.read_memory(4, &mut data).unwrap();
assert_eq!(0xaabbccdd, <u32>::from_le_bytes(data));
vmm.read_memory(8, &mut data).unwrap();
assert_eq!(0x5aa55aa5, <u32>::from_le_bytes(data));
}
#[test]
fn test_stosw() {
let ip: u64 = 0x1000;
let memory: [u8; 4] = [
0x78, 0x56, 0x34, 0x12, // 0x12345678
];
let insn = [0x66, 0xab]; // stosw
let regs = vec![(Register::EDI, 0x1), (Register::AX, 0xaabb)];
let mut data = [0u8; 4];
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
vmm.read_memory(0x0, &mut data).unwrap();
assert_eq!(0x12aabb78, <u32>::from_le_bytes(data));
// The rest should be default value 0 from MockVmm
vmm.read_memory(0x4, &mut data).unwrap();
assert_eq!(0x0, <u32>::from_le_bytes(data));
vmm.read_memory(0x8 + 8, &mut data).unwrap();
assert_eq!(0x0, <u32>::from_le_bytes(data));
}
#[test]
fn test_rep_stosw() {
let ip: u64 = 0x1000;
let memory: [u8; 8] = [
0x78, 0x56, 0x34, 0x12, // 0x12345678
0x00, 0x00, 0x00, 0x00, // 0x00000000
];
let insn = [0x66, 0xf3, 0xab]; // rep stosw
let regs = vec![
(Register::ECX, 2),
(Register::EDI, 0x2),
(Register::AX, 0xaabb),
];
let mut data = [0u8; 4];
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
vmm.read_memory(0x0, &mut data).unwrap();
assert_eq!(0xaabb5678, <u32>::from_le_bytes(data));
vmm.read_memory(0x4, &mut data).unwrap();
assert_eq!(0x0000aabb, <u32>::from_le_bytes(data));
vmm.read_memory(0x8, &mut data).unwrap();
assert_eq!(0x0, <u32>::from_le_bytes(data));
}
#[test]
fn test_rep_stosd() {
let ip: u64 = 0x1000;
let memory: [u8; 12] = [
0x78, 0x56, 0x34, 0x12, // 0x12345678
0x00, 0x00, 0x00, 0x00, // 0x00000000
0x00, 0x00, 0x00, 0x00, // 0x00000000
];
let insn = [0xf3, 0xab]; // rep stosd
let regs = vec![
(Register::ECX, 2),
(Register::EDI, 0x8),
(Register::EAX, 0xaabbccdd),
];
let mut data = [0u8; 4];
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
// Go backwards this time
let mut state = vmm.cpu_state(0).unwrap();
state.set_flags(state.flags() | DF);
vmm.set_cpu_state(0, state).unwrap();
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
vmm.read_memory(0x0, &mut data).unwrap();
assert_eq!(0x12345678, <u32>::from_le_bytes(data));
vmm.read_memory(0x4, &mut data).unwrap();
assert_eq!(0xaabbccdd, <u32>::from_le_bytes(data));
vmm.read_memory(0x8, &mut data).unwrap();
assert_eq!(0xaabbccdd, <u32>::from_le_bytes(data));
vmm.read_memory(0xc, &mut data).unwrap();
assert_eq!(0x0, <u32>::from_le_bytes(data));
}
#[test]
fn test_rep_stosq() {
let ip: u64 = 0x1000;
let memory: [u8; 8] = [
0x78, 0x56, 0x34, 0x12, // 0x12345678
0x00, 0x00, 0x00, 0x00, // 0x00000000
];
let insn = [0xf3, 0x48, 0xab]; // rep stosq
let regs = vec![
(Register::ECX, 2),
(Register::RDI, 0x0),
(Register::RAX, 0x11223344aabbccdd),
];
let mut data = [0u8; 8];
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
vmm.read_memory(0x0, &mut data).unwrap();
assert_eq!(0x11223344aabbccdd, <u64>::from_le_bytes(data));
vmm.read_memory(0x8, &mut data).unwrap();
assert_eq!(0x11223344aabbccdd, <u64>::from_le_bytes(data));
vmm.read_memory(0x10, &mut data).unwrap();
assert_eq!(0x0, <u64>::from_le_bytes(data));
}
}

View File

@@ -532,11 +532,17 @@ impl<'a, T: CpuStateManager> Emulator<'a, T> {
(mov, Mov_moffs64_RAX),
(mov, Mov_RAX_moffs64),
// MOVS
(movs, Movsq_m64_m64),
(movs, Movsd_m32_m32),
(movs, Movsw_m16_m16),
(movs, Movsb_m8_m8),
// OR
(or, Or_rm8_r8)
(or, Or_rm8_r8),
// STOS
(stos, Stosb_m8_AL),
(stos, Stosw_m16_AX),
(stos, Stosd_m32_EAX),
(stos, Stosq_m64_RAX)
);
handler
@@ -561,7 +567,7 @@ impl<'a, T: CpuStateManager> Emulator<'a, T> {
decoder.set_ip(state.ip());
while decoder.can_decode() && !stop_emulation {
while !stop_emulation {
decoder.decode_out(&mut insn);
if decoder.last_error() == DecoderError::NoMoreBytes {
@@ -588,6 +594,7 @@ impl<'a, T: CpuStateManager> Emulator<'a, T> {
// Once we have the new stream, we must create a new decoder
// and emulate one last instruction from the last decoded IP.
decoder = Decoder::new(64, &fetched_insn_stream, DecoderOptions::NONE);
decoder.set_ip(last_decoded_ip);
decoder.decode_out(&mut insn);
if decoder.last_error() != DecoderError::None {
return Err(EmulationError::InstructionFetchingError(anyhow!(
@@ -595,8 +602,6 @@ impl<'a, T: CpuStateManager> Emulator<'a, T> {
insn_format!(insn)
)));
}
stop_emulation = true;
}
// Emulate the decoded instruction
@@ -751,10 +756,6 @@ mod mock_vmm {
Ok(())
}
fn gva_to_gpa(&self, gva: u64) -> Result<u64, PlatformError> {
Ok(gva)
}
fn fetch(&self, ip: u64, instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
let rip = self
.state
@@ -771,12 +772,77 @@ mod tests {
use super::*;
use crate::arch::x86::emulator::mock_vmm::*;
#[test]
// Emulate executing an empty stream. Instructions should be fetched from
// memory.
//
// mov rax, 0x1000
// mov rbx, qword ptr [rax+10h]
fn test_empty_instruction_stream() {
let target_rax: u64 = 0x1000;
let target_rbx: u64 = 0x1234567812345678;
let ip: u64 = 0x1000;
let cpu_id = 0;
let memory = [
// Code at IP
0x48, 0xc7, 0xc0, 0x00, 0x10, 0x00, 0x00, // mov rax, 0x1000
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
// Padding
0x00, 0x00, 0x00, 0x00, 0x00, // Padding is all zeroes
// Data at IP + 0x10 (0x1234567812345678 in LE)
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12,
];
let mut vmm = MockVmm::new(ip, vec![], Some((ip, &memory)));
assert!(vmm.emulate_insn(cpu_id, &[], Some(2)).is_ok());
let rax: u64 = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RAX)
.unwrap();
assert_eq!(rax, target_rax);
let rbx: u64 = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RBX)
.unwrap();
assert_eq!(rbx, target_rbx);
}
#[test]
// Emulate executing an empty stream. Instructions should be fetched from
// memory. The emulation should abort.
//
// mov rax, 0x1000
// mov rbx, qword ptr [rax+10h]
// ... garbage ...
fn test_empty_instruction_stream_bad() {
let ip: u64 = 0x1000;
let cpu_id = 0;
let memory = [
// Code at IP
0x48, 0xc7, 0xc0, 0x00, 0x10, 0x00, 0x00, // mov rax, 0x1000
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
// Padding
0xff, 0xff, 0xff, 0xff, 0xff, // Garbage
// Data at IP + 0x10 (0x1234567812345678 in LE)
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12,
];
let mut vmm = MockVmm::new(ip, vec![], Some((ip, &memory)));
assert!(vmm.emulate_insn(cpu_id, &[], None).is_err());
}
#[test]
// Emulate truncated instruction stream, which should cause a fetch.
//
// mov rax, 0x1000
// mov rbx, qword ptr [rax+10h]
// Test with a first instruction truncated.
fn test_fetch_first_instruction() {
let target_rax: u64 = 0x1000;
let ip: u64 = 0x1000;
let cpu_id = 0;
let memory = [
@@ -801,7 +867,7 @@ mod tests {
.unwrap()
.read_reg(Register::RAX)
.unwrap();
assert_eq!(rax, ip);
assert_eq!(rax, target_rax);
}
#[test]
@@ -839,6 +905,52 @@ mod tests {
assert_eq!(rbx, target_rax);
}
#[test]
// Emulate only one instruction.
//
// mov rax, 0x1000
// mov rbx, qword ptr [rax+10h]
// The emulation should stop after the first instruction.
fn test_emulate_one_instruction() {
let target_rax: u64 = 0x1000;
let ip: u64 = 0x1000;
let cpu_id = 0;
let memory = [
// Code at IP
0x48, 0xc7, 0xc0, 0x00, 0x10, 0x00, 0x00, // mov rax, 0x1000
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
// Padding
0x00, 0x00, 0x00, 0x00, 0x00, // Padding is all zeroes
// Data at IP + 0x10 (0x1234567812345678 in LE)
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12,
];
let insn = [
0x48, 0xc7, 0xc0, 0x00, 0x10, 0x00, 0x00, // mov rax, 0x1000
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
];
let mut vmm = MockVmm::new(ip, vec![], Some((ip, &memory)));
assert!(vmm.emulate_insn(cpu_id, &insn, Some(1)).is_ok());
let new_ip: u64 = vmm.cpu_state(cpu_id).unwrap().ip();
assert_eq!(new_ip, ip + 0x7 /* length of mov rax,0x1000 */);
let rax: u64 = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RAX)
.unwrap();
assert_eq!(rax, target_rax);
// The second instruction is not executed so RBX should be zero.
let rbx: u64 = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RBX)
.unwrap();
assert_eq!(rbx, 0);
}
#[test]
// Emulate truncated instruction stream, which should cause a fetch.
//

View File

@@ -77,46 +77,61 @@ static VGIC_DIST_REGS: &[DistReg] = &[
VGIC_DIST_REG!(GICD_IPRIORITYR, 8, 0),
];
fn dist_attr_access(gic: &DeviceFd, offset: u32, val: &u32, set: bool) -> Result<()> {
fn dist_attr_set(gic: &DeviceFd, offset: u32, val: u32) -> Result<()> {
let gic_dist_attr = kvm_device_attr {
group: KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
attr: offset as u64,
addr: &val as *const u32 as u64,
flags: 0,
};
gic.set_device_attr(&gic_dist_attr).map_err(|e| {
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
})?;
Ok(())
}
fn dist_attr_get(gic: &DeviceFd, offset: u32) -> Result<u32> {
let mut val = 0;
let mut gic_dist_attr = kvm_device_attr {
group: KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
attr: offset as u64,
addr: val as *const u32 as u64,
addr: &mut val as *mut u32 as u64,
flags: 0,
};
if set {
gic.set_device_attr(&gic_dist_attr).map_err(|e| {
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
})?;
} else {
gic.get_device_attr(&mut gic_dist_attr).map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;
}
Ok(())
// get_device_attr should be marked as unsafe, and will be in future.
// SAFETY: gic_dist_attr.addr is safe to write to.
gic.get_device_attr(&mut gic_dist_attr).map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;
Ok(val)
}
/// Get the distributor control register.
pub fn read_ctlr(gic: &DeviceFd) -> Result<u32> {
let val: u32 = 0;
dist_attr_access(gic, GICD_CTLR, &val, false)?;
Ok(val)
dist_attr_get(gic, GICD_CTLR)
}
/// Set the distributor control register.
pub fn write_ctlr(gic: &DeviceFd, val: u32) -> Result<()> {
dist_attr_access(gic, GICD_CTLR, &val, true)
dist_attr_set(gic, GICD_CTLR, val)
}
fn get_interrupts_num(gic: &DeviceFd) -> Result<u32> {
let num_irq = 0;
let mut num_irq = 0;
let mut nr_irqs_attr = kvm_device_attr {
group: KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
attr: 0,
addr: &num_irq as *const u32 as u64,
addr: &mut num_irq as *mut u32 as u64,
flags: 0,
};
// get_device_attr should be marked as unsafe, and will be in future.
// SAFETY: nr_irqs_attr.addr is safe to write to.
gic.get_device_attr(&mut nr_irqs_attr).map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;
@@ -158,8 +173,7 @@ pub fn set_dist_regs(gic: &DeviceFd, state: &[u32]) -> Result<()> {
let end = compute_reg_len(gic, dreg, base)?;
while base < end {
let val = state[idx];
dist_attr_access(gic, base, &val, true)?;
dist_attr_set(gic, base, state[idx])?;
idx += 1;
base += REG_SIZE as u32;
}
@@ -175,9 +189,7 @@ pub fn get_dist_regs(gic: &DeviceFd) -> Result<Vec<u32>> {
let end = compute_reg_len(gic, dreg, base)?;
while base < end {
let val: u32 = 0;
dist_attr_access(gic, base, &val, false)?;
state.push(val);
state.push(dist_attr_get(gic, base)?);
base += REG_SIZE as u32;
}
}

View File

@@ -79,23 +79,38 @@ static VGIC_ICC_REGS: &[u64] = &[
SYS_ICC_AP1R3_EL1,
];
fn icc_attr_access(gic: &DeviceFd, offset: u64, typer: u64, val: &u32, set: bool) -> Result<()> {
fn icc_attr_set(gic: &DeviceFd, offset: u64, typer: u64, val: u32) -> Result<()> {
let gic_icc_attr = kvm_device_attr {
group: KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
attr: ((typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | offset), // this needs the mpidr
addr: &val as *const u32 as u64,
flags: 0,
};
gic.set_device_attr(&gic_icc_attr).map_err(|e| {
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
})?;
Ok(())
}
fn icc_attr_get(gic: &DeviceFd, offset: u64, typer: u64) -> Result<u32> {
let mut val = 0;
let mut gic_icc_attr = kvm_device_attr {
group: KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
attr: ((typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | offset), // this needs the mpidr
addr: val as *const u32 as u64,
addr: &mut val as *mut u32 as u64,
flags: 0,
};
if set {
gic.set_device_attr(&gic_icc_attr).map_err(|e| {
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
})?;
} else {
gic.get_device_attr(&mut gic_icc_attr).map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;
}
Ok(())
// get_device_attr should be marked as unsafe, and will be in future.
// SAFETY: gic_icc_attr.addr is safe to write to.
gic.get_device_attr(&mut gic_icc_attr).map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;
Ok(val)
}
/// Get ICC registers.
@@ -107,10 +122,9 @@ pub fn get_icc_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
for ix in gicr_typer {
let i = *ix;
for icc_offset in VGIC_ICC_REGS {
let val = 0;
if *icc_offset == SYS_ICC_CTLR_EL1 {
// calculate priority bits by reading the ctrl_el1 register.
icc_attr_access(gic, *icc_offset, i, &val, false)?;
let val = icc_attr_get(gic, *icc_offset, i)?;
// The priority bits are found in the ICC_CTLR_EL1 register (bits from 10:8).
// See page 194 from https://static.docs.arm.com/ihi0069/c/IHI0069C_gic_
// architecture_specification.pdf.
@@ -130,8 +144,7 @@ pub fn get_icc_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
// 7 bits of priority.
else if *icc_offset == SYS_ICC_AP0R1_EL1 || *icc_offset == SYS_ICC_AP1R1_EL1 {
if num_priority_bits >= 6 {
icc_attr_access(gic, *icc_offset, i, &val, false)?;
state.push(val);
state.push(icc_attr_get(gic, *icc_offset, i)?);
}
} else if *icc_offset == SYS_ICC_AP0R2_EL1
|| *icc_offset == SYS_ICC_AP0R3_EL1
@@ -139,12 +152,10 @@ pub fn get_icc_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
|| *icc_offset == SYS_ICC_AP1R3_EL1
{
if num_priority_bits == 7 {
icc_attr_access(gic, *icc_offset, i, &val, false)?;
state.push(val);
state.push(icc_attr_get(gic, *icc_offset, i)?);
}
} else {
icc_attr_access(gic, *icc_offset, i, &val, false)?;
state.push(val);
state.push(icc_attr_get(gic, *icc_offset, i)?);
}
}
}
@@ -165,7 +176,7 @@ pub fn set_icc_regs(gic: &DeviceFd, gicr_typer: &[u64], state: &[u32]) -> Result
}
if *icc_offset == SYS_ICC_AP0R1_EL1 || *icc_offset == SYS_ICC_AP1R1_EL1 {
if num_priority_bits >= 6 {
icc_attr_access(gic, *icc_offset, i, &state[idx], true)?;
icc_attr_set(gic, *icc_offset, i, state[idx])?;
idx += 1;
}
continue;
@@ -176,12 +187,12 @@ pub fn set_icc_regs(gic: &DeviceFd, gicr_typer: &[u64], state: &[u32]) -> Result
|| *icc_offset == SYS_ICC_AP1R3_EL1
{
if num_priority_bits == 7 {
icc_attr_access(gic, *icc_offset, i, &state[idx], true)?;
icc_attr_set(gic, *icc_offset, i, state[idx])?;
idx += 1;
}
continue;
}
icc_attr_access(gic, *icc_offset, i, &state[idx], true)?;
icc_attr_set(gic, *icc_offset, i, state[idx])?;
idx += 1;
}
}

View File

@@ -24,34 +24,38 @@ const GITS_CWRITER: u32 = 0x0088;
const GITS_CREADR: u32 = 0x0090;
const GITS_BASER: u32 = 0x0100;
/// Access an ITS device attribute.
///
/// This is a helper function to get/set the ITS device attribute depending
/// the bool parameter `set` provided.
pub fn gicv3_its_attr_access(
its_device: &DeviceFd,
group: u32,
attr: u32,
val: &u64,
set: bool,
) -> Result<()> {
fn gicv3_its_attr_set(its_device: &DeviceFd, group: u32, attr: u32, val: u64) -> Result<()> {
let gicv3_its_attr = kvm_bindings::kvm_device_attr {
group,
attr: attr as u64,
addr: &val as *const u64 as u64,
flags: 0,
};
its_device
.set_device_attr(&gicv3_its_attr)
.map_err(|e| Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into())))
}
fn gicv3_its_attr_get(its_device: &DeviceFd, group: u32, attr: u32) -> Result<u64> {
let mut val = 0;
let mut gicv3_its_attr = kvm_bindings::kvm_device_attr {
group,
attr: attr as u64,
addr: val as *const u64 as u64,
addr: &mut val as *mut u64 as u64,
flags: 0,
};
if set {
its_device.set_device_attr(&gicv3_its_attr).map_err(|e| {
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
})
} else {
its_device
.get_device_attr(&mut gicv3_its_attr)
.map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})
}
// get_device_attr should be marked as unsafe, and will be in future.
// SAFETY: gicv3_its_attr.addr is safe to write to.
its_device
.get_device_attr(&mut gicv3_its_attr)
.map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;
Ok(val)
}
/// Function that saves/restores ITS tables into guest RAM.
@@ -324,60 +328,43 @@ impl Vgic for KvmGicV3Its {
let icc_state = get_icc_regs(&self.device, &gicr_typers)?;
let its_baser_state: [u64; 8] = [0; 8];
let mut its_baser_state: [u64; 8] = [0; 8];
for i in 0..8 {
gicv3_its_attr_access(
its_baser_state[i as usize] = gicv3_its_attr_get(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_BASER + i * 8,
&its_baser_state[i as usize],
false,
)?;
}
let its_ctlr_state: u64 = 0;
gicv3_its_attr_access(
let its_ctlr_state = gicv3_its_attr_get(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CTLR,
&its_ctlr_state,
false,
)?;
let its_cbaser_state: u64 = 0;
gicv3_its_attr_access(
let its_cbaser_state = gicv3_its_attr_get(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CBASER,
&its_cbaser_state,
false,
)?;
let its_creadr_state: u64 = 0;
gicv3_its_attr_access(
let its_creadr_state = gicv3_its_attr_get(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CREADR,
&its_creadr_state,
false,
)?;
let its_cwriter_state: u64 = 0;
gicv3_its_attr_access(
let its_cwriter_state = gicv3_its_attr_get(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CWRITER,
&its_cwriter_state,
false,
)?;
let its_iidr_state: u64 = 0;
gicv3_its_attr_access(
let its_iidr_state = gicv3_its_attr_get(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_IIDR,
&its_iidr_state,
false,
)?;
Ok(Gicv3ItsState {
@@ -407,57 +394,51 @@ impl Vgic for KvmGicV3Its {
set_icc_regs(&self.device, &gicr_typers, &state.icc)?;
//Restore GICv3ITS registers
gicv3_its_attr_access(
gicv3_its_attr_set(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_IIDR,
&state.its_iidr,
true,
state.its_iidr,
)?;
gicv3_its_attr_access(
gicv3_its_attr_set(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CBASER,
&state.its_cbaser,
true,
state.its_cbaser,
)?;
gicv3_its_attr_access(
gicv3_its_attr_set(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CREADR,
&state.its_creadr,
true,
state.its_creadr,
)?;
gicv3_its_attr_access(
gicv3_its_attr_set(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CWRITER,
&state.its_cwriter,
true,
state.its_cwriter,
)?;
for i in 0..8 {
gicv3_its_attr_access(
gicv3_its_attr_set(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_BASER + i * 8,
&state.its_baser[i as usize],
true,
state.its_baser[i as usize],
)?;
}
// Restore ITS tables
gicv3_its_tables_access(self.its_device.as_ref().unwrap(), false)?;
gicv3_its_attr_access(
gicv3_its_attr_set(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CTLR,
&state.its_ctlr,
true,
state.its_ctlr,
)
}

View File

@@ -96,23 +96,38 @@ static VGIC_SGI_REGS: &[RdistReg] = &[
VGIC_RDIST_REG!(GICR_IPRIORITYR0, 32),
];
fn redist_attr_access(gic: &DeviceFd, offset: u32, typer: u64, val: &u32, set: bool) -> Result<()> {
fn redist_attr_set(gic: &DeviceFd, offset: u32, typer: u64, val: u32) -> Result<()> {
let gic_redist_attr = kvm_device_attr {
group: KVM_DEV_ARM_VGIC_GRP_REDIST_REGS,
attr: (typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | (offset as u64), // this needs the mpidr
addr: &val as *const u32 as u64,
flags: 0,
};
gic.set_device_attr(&gic_redist_attr).map_err(|e| {
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
})?;
Ok(())
}
fn redist_attr_get(gic: &DeviceFd, offset: u32, typer: u64) -> Result<u32> {
let mut val = 0;
let mut gic_redist_attr = kvm_device_attr {
group: KVM_DEV_ARM_VGIC_GRP_REDIST_REGS,
attr: (typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | (offset as u64), // this needs the mpidr
addr: val as *const u32 as u64,
addr: &mut val as *mut u32 as u64,
flags: 0,
};
if set {
gic.set_device_attr(&gic_redist_attr).map_err(|e| {
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
})?;
} else {
gic.get_device_attr(&mut gic_redist_attr).map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;
}
Ok(())
// get_device_attr should be marked as unsafe, and will be in future.
// SAFETY: gic_redist_attr.addr is safe to write to.
gic.get_device_attr(&mut gic_redist_attr).map_err(|e| {
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
})?;
Ok(val)
}
fn access_redists_aux(
@@ -129,14 +144,11 @@ fn access_redists_aux(
let end = base + rdreg.length as u32;
while base < end {
let mut val = 0;
if set {
val = state[*idx];
redist_attr_access(gic, base, *i, &val, true)?;
redist_attr_set(gic, base, *i, state[*idx])?;
*idx += 1;
} else {
redist_attr_access(gic, base, *i, &val, false)?;
state.push(val);
state.push(redist_attr_get(gic, base, *i)?);
}
base += REG_SIZE as u32;
}

View File

@@ -3,13 +3,12 @@
// Copyright © 2020, Microsoft Corporation
//
use crate::arch::emulator::{PlatformEmulator, PlatformError};
use crate::arch::emulator::PlatformEmulator;
#[cfg(target_arch = "x86_64")]
use crate::arch::x86::emulator::{Emulator, EmulatorCpuState};
use crate::arch::x86::emulator::Emulator;
use crate::cpu;
use crate::cpu::Vcpu;
use crate::hypervisor;
use crate::mshv::emulator::MshvEmulatorContext;
use crate::vec_with_array_field;
use crate::vm::{self, InterruptSourceConfig, VmOps};
use crate::HypervisorType;
@@ -20,18 +19,15 @@ use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use vfio_ioctls::VfioDeviceFd;
use vm::DataMatch;
#[cfg(feature = "sev_snp")]
mod snp_constants;
// x86_64 dependencies
#[cfg(target_arch = "x86_64")]
pub mod x86_64;
#[cfg(feature = "sev_snp")]
use snp_constants::*;
#[cfg(target_arch = "x86_64")]
use crate::arch::x86::{CpuIdEntry, FpuState, MsrEntry};
#[cfg(target_arch = "x86_64")]
use crate::ClockData;
use crate::{
CpuState, IoEventAddress, IrqRoutingEntry, MpState, UserMemoryRegion,
USER_MEMORY_REGION_ADJUSTABLE, USER_MEMORY_REGION_EXECUTE, USER_MEMORY_REGION_READ,
@@ -39,18 +35,16 @@ use crate::{
};
#[cfg(feature = "sev_snp")]
use igvm_defs::IGVM_VHS_SNP_ID_BLOCK;
use vmm_sys_util::eventfd::EventFd;
#[cfg(target_arch = "x86_64")]
pub use x86_64::VcpuMshvState;
#[cfg(target_arch = "x86_64")]
pub use x86_64::*;
#[cfg(feature = "sev_snp")]
use snp_constants::*;
#[cfg(target_arch = "x86_64")]
use std::fs::File;
use std::os::unix::io::AsRawFd;
use vmm_sys_util::eventfd::EventFd;
#[cfg(target_arch = "x86_64")]
use crate::arch::x86::{CpuIdEntry, FpuState, MsrEntry};
pub use x86_64::*;
#[cfg(target_arch = "x86_64")]
pub use x86_64::{emulator, VcpuMshvState};
const DIRTY_BITMAP_CLEAR_DIRTY: u64 = 0x4;
const DIRTY_BITMAP_SET_DIRTY: u64 = 0x8;
@@ -60,7 +54,7 @@ const DIRTY_BITMAP_SET_DIRTY: u64 = 0x8;
///
pub use {
mshv_bindings::mshv_create_device as CreateDevice,
mshv_bindings::mshv_device_attr as DeviceAttr, mshv_ioctls::DeviceFd,
mshv_bindings::mshv_device_attr as DeviceAttr, mshv_ioctls, mshv_ioctls::DeviceFd,
};
pub const PAGE_SHIFT: usize = 12;
@@ -637,14 +631,18 @@ impl cpu::Vcpu for MshvVcpu {
Ok(cpu::VmExit::Ignore)
}
#[cfg(target_arch = "x86_64")]
hv_message_type_HVMSG_UNMAPPED_GPA => {
msg_type @ (hv_message_type_HVMSG_UNMAPPED_GPA
| hv_message_type_HVMSG_GPA_INTERCEPT) => {
let info = x.to_memory_info().unwrap();
let insn_len = info.instruction_byte_count as usize;
assert!(insn_len > 0 && insn_len <= 16);
let gva = info.guest_virtual_address;
let gpa = info.guest_physical_address;
debug!("Exit ({:?}) GVA {:x} GPA {:x}", msg_type, gva, gpa);
let mut context = MshvEmulatorContext {
vcpu: self,
map: (info.guest_virtual_address, info.guest_physical_address),
map: (gva, gpa),
};
// Create a new emulator.
@@ -652,7 +650,10 @@ impl cpu::Vcpu for MshvVcpu {
// Emulate the trapped instruction, and only the first one.
let new_state = emul
.emulate_first_insn(self.vp_index as usize, &info.instruction_bytes)
.emulate_first_insn(
self.vp_index as usize,
&info.instruction_bytes[..insn_len],
)
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
// Set CPU state back.
@@ -798,6 +799,10 @@ impl cpu::Vcpu for MshvVcpu {
}
GHCB_INFO_REGISTER_REQUEST => {
let mut ghcb_gpa = hv_x64_register_sev_ghcb::default();
// Disable the previously used GHCB page.
self.disable_prev_ghcb_page()?;
// SAFETY: Accessing a union element from bindgen generated bindings.
unsafe {
ghcb_gpa.__bindgen_anon_1.set_enabled(1);
@@ -825,6 +830,7 @@ impl cpu::Vcpu for MshvVcpu {
resp_ghcb_msr.__bindgen_anon_2.set_gpa_page_number(
ghcb_msr.__bindgen_anon_2.gpa_page_number(),
);
debug!("GHCB GPA is {:x}", ghcb_gpa.as_uint64);
}
// SAFETY: Accessing a union element from bindgen generated bindings.
let reg_name_value = unsafe {
@@ -925,15 +931,7 @@ impl cpu::Vcpu for MshvVcpu {
)?;
// Clear the SW_EXIT_INFO1 register to indicate no error
let mut swei1_rw_gpa_arg =
mshv_bindings::mshv_read_write_gpa {
base_gpa: ghcb_gpa + GHCB_SW_EXITINFO1_OFFSET,
byte_count: std::mem::size_of::<u64>() as u32,
..Default::default()
};
self.fd.gpa_write(&mut swei1_rw_gpa_arg).map_err(
|e| cpu::HypervisorCpuError::GpaWrite(e.into()),
)?;
self.clear_swexit_info1(ghcb_gpa)?;
}
SVM_NAE_HV_DOORBELL_PAGE_QUERY => {
let mut reg_assocs = [ hv_register_assoc {
@@ -955,6 +953,9 @@ impl cpu::Vcpu for MshvVcpu {
self.fd.gpa_write(&mut swei2_rw_gpa_arg).map_err(
|e| cpu::HypervisorCpuError::GpaWrite(e.into()),
)?;
// Clear the SW_EXIT_INFO1 register to indicate no error
self.clear_swexit_info1(ghcb_gpa)?;
}
SVM_NAE_HV_DOORBELL_PAGE_CLEAR => {
let mut swei2_rw_gpa_arg =
@@ -1055,14 +1056,7 @@ impl cpu::Vcpu for MshvVcpu {
}
// Clear the SW_EXIT_INFO1 register to indicate no error
let mut swei1_rw_gpa_arg = mshv_bindings::mshv_read_write_gpa {
base_gpa: ghcb_gpa + GHCB_SW_EXITINFO1_OFFSET,
byte_count: std::mem::size_of::<u64>() as u32,
..Default::default()
};
self.fd
.gpa_write(&mut swei1_rw_gpa_arg)
.map_err(|e| cpu::HypervisorCpuError::GpaWrite(e.into()))?;
self.clear_swexit_info1(ghcb_gpa)?;
}
SVM_EXITCODE_MMIO_READ => {
let src_gpa =
@@ -1091,6 +1085,9 @@ impl cpu::Vcpu for MshvVcpu {
self.fd
.gpa_write(&mut arg)
.map_err(|e| cpu::HypervisorCpuError::GpaWrite(e.into()))?;
// Clear the SW_EXIT_INFO1 register to indicate no error
self.clear_swexit_info1(ghcb_gpa)?;
}
SVM_EXITCODE_MMIO_WRITE => {
let dst_gpa =
@@ -1119,6 +1116,9 @@ impl cpu::Vcpu for MshvVcpu {
cpu::HypervisorCpuError::RunVcpu(e.into())
})?;
}
// Clear the SW_EXIT_INFO1 register to indicate no error
self.clear_swexit_info1(ghcb_gpa)?;
}
SVM_EXITCODE_SNP_GUEST_REQUEST => {
let req_gpa =
@@ -1164,15 +1164,8 @@ impl cpu::Vcpu for MshvVcpu {
.sev_snp_ap_create(&mshv_ap_create_req)
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
let mut swei1_rw_gpa_arg = mshv_bindings::mshv_read_write_gpa {
base_gpa: ghcb_gpa + GHCB_SW_EXITINFO1_OFFSET,
byte_count: std::mem::size_of::<u64>() as u32,
..Default::default()
};
self.fd
.gpa_write(&mut swei1_rw_gpa_arg)
.map_err(|e| cpu::HypervisorCpuError::GpaWrite(e.into()))?;
// Clear the SW_EXIT_INFO1 register to indicate no error
self.clear_swexit_info1(ghcb_gpa)?;
}
_ => panic!(
"GHCB_INFO_NORMAL: Unhandled exit code: {:0x}",
@@ -1468,6 +1461,42 @@ impl cpu::Vcpu for MshvVcpu {
}
impl MshvVcpu {
///
/// Deactivate previously used GHCB page.
///
#[cfg(feature = "sev_snp")]
fn disable_prev_ghcb_page(&self) -> cpu::Result<()> {
let mut reg_assocs = [hv_register_assoc {
name: hv_register_name_HV_X64_REGISTER_SEV_GHCB_GPA,
..Default::default()
}];
self.fd.get_reg(&mut reg_assocs).unwrap();
// SAFETY: Accessing a union element from bindgen generated bindings.
let prev_ghcb_gpa = unsafe { reg_assocs[0].value.reg64 };
debug!("Prev GHCB GPA is {:x}", prev_ghcb_gpa);
let mut ghcb_gpa = hv_x64_register_sev_ghcb::default();
// SAFETY: Accessing a union element from bindgen generated bindings.
unsafe {
ghcb_gpa.__bindgen_anon_1.set_enabled(0);
ghcb_gpa.__bindgen_anon_1.set_page_number(prev_ghcb_gpa);
}
// SAFETY: Accessing a union element from bindgen generated bindings.
let reg_name_value = unsafe {
[(
hv_register_name_HV_X64_REGISTER_SEV_GHCB_GPA,
ghcb_gpa.as_uint64,
)]
};
set_registers_64!(self.fd, reg_name_value)
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
Ok(())
}
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call that returns the vcpu's current "xcrs".
@@ -1509,133 +1538,26 @@ impl MshvVcpu {
.set_vcpu_events(events)
.map_err(|e| cpu::HypervisorCpuError::SetVcpuEvents(e.into()))
}
}
#[cfg(target_arch = "x86_64")]
struct MshvEmulatorContext<'a> {
vcpu: &'a MshvVcpu,
map: (u64, u64), // Initial GVA to GPA mapping provided by the hypervisor
}
///
/// Clear SW_EXIT_INFO1 register for SEV-SNP guests.
///
#[cfg(feature = "sev_snp")]
fn clear_swexit_info1(
&self,
ghcb_gpa: u64,
) -> std::result::Result<cpu::VmExit, cpu::HypervisorCpuError> {
// Clear the SW_EXIT_INFO1 register to indicate no error
let mut swei1_rw_gpa_arg = mshv_bindings::mshv_read_write_gpa {
base_gpa: ghcb_gpa + GHCB_SW_EXITINFO1_OFFSET,
byte_count: std::mem::size_of::<u64>() as u32,
..Default::default()
};
self.fd
.gpa_write(&mut swei1_rw_gpa_arg)
.map_err(|e| cpu::HypervisorCpuError::GpaWrite(e.into()))?;
#[cfg(target_arch = "x86_64")]
impl<'a> MshvEmulatorContext<'a> {
// Do the actual gva -> gpa translation
#[allow(non_upper_case_globals)]
fn translate(&self, gva: u64) -> Result<u64, PlatformError> {
if self.map.0 == gva {
return Ok(self.map.1);
}
// TODO: More fine-grained control for the flags
let flags = HV_TRANSLATE_GVA_VALIDATE_READ | HV_TRANSLATE_GVA_VALIDATE_WRITE;
let (gpa, result_code) = self
.vcpu
.translate_gva(gva, flags.into())
.map_err(|e| PlatformError::TranslateVirtualAddress(anyhow!(e)))?;
match result_code {
hv_translate_gva_result_code_HV_TRANSLATE_GVA_SUCCESS => Ok(gpa),
_ => Err(PlatformError::TranslateVirtualAddress(anyhow!(result_code))),
}
}
}
#[cfg(target_arch = "x86_64")]
/// Platform emulation for Hyper-V
impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
type CpuState = EmulatorCpuState;
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
let gpa = self.translate(gva)?;
debug!(
"mshv emulator: memory read {} bytes from [{:#x} -> {:#x}]",
data.len(),
gva,
gpa
);
if let Some(vm_ops) = &self.vcpu.vm_ops {
if vm_ops.guest_mem_read(gpa, data).is_err() {
vm_ops
.mmio_read(gpa, data)
.map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
}
}
Ok(())
}
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
let gpa = self.translate(gva)?;
debug!(
"mshv emulator: memory write {} bytes at [{:#x} -> {:#x}]",
data.len(),
gva,
gpa
);
if let Some(vm_ops) = &self.vcpu.vm_ops {
if vm_ops.guest_mem_write(gpa, data).is_err() {
vm_ops
.mmio_write(gpa, data)
.map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
}
}
Ok(())
}
fn cpu_state(&self, cpu_id: usize) -> Result<Self::CpuState, PlatformError> {
if cpu_id != self.vcpu.vp_index as usize {
return Err(PlatformError::GetCpuStateFailure(anyhow!(
"CPU id mismatch {:?} {:?}",
cpu_id,
self.vcpu.vp_index
)));
}
let regs = self
.vcpu
.get_regs()
.map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
let sregs = self
.vcpu
.get_sregs()
.map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
debug!("mshv emulator: Getting new CPU state");
debug!("mshv emulator: {:#x?}", regs);
Ok(EmulatorCpuState { regs, sregs })
}
fn set_cpu_state(&self, cpu_id: usize, state: Self::CpuState) -> Result<(), PlatformError> {
if cpu_id != self.vcpu.vp_index as usize {
return Err(PlatformError::SetCpuStateFailure(anyhow!(
"CPU id mismatch {:?} {:?}",
cpu_id,
self.vcpu.vp_index
)));
}
debug!("mshv emulator: Setting new CPU state");
debug!("mshv emulator: {:#x?}", state.regs);
self.vcpu
.set_regs(&state.regs)
.map_err(|e| PlatformError::SetCpuStateFailure(e.into()))?;
self.vcpu
.set_sregs(&state.sregs)
.map_err(|e| PlatformError::SetCpuStateFailure(e.into()))
}
fn gva_to_gpa(&self, gva: u64) -> Result<u64, PlatformError> {
self.translate(gva)
}
fn fetch(&self, _ip: u64, _instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
Err(PlatformError::MemoryReadFailure(anyhow!("unimplemented")))
Ok(cpu::VmExit::Ignore)
}
}
@@ -2130,4 +2052,38 @@ impl vm::Vm for MshvVm {
))
})
}
#[cfg(feature = "sev_snp")]
fn gain_page_access(&self, gpa: u64, size: u32) -> vm::Result<()> {
if !self.sev_snp_enabled {
return Ok(());
}
let start_gpfn: u64 = gpa >> PAGE_SHIFT;
let end_gpfn: u64 = (gpa + size as u64 - 1) >> PAGE_SHIFT;
let gpas: Vec<u64> = (start_gpfn..=end_gpfn).map(|x| x << PAGE_SHIFT).collect();
if !gpas.is_empty() {
let mut gpa_list = vec_with_array_field::<mshv_modify_gpa_host_access, u64>(gpas.len());
gpa_list[0].gpa_list_size = gpas.len() as u64;
gpa_list[0].host_access = HV_MAP_GPA_READABLE | HV_MAP_GPA_WRITABLE;
gpa_list[0].acquire = 1;
gpa_list[0].flags = 0;
// SAFETY: gpa_list initialized with gpas.len() and now it is being turned into
// gpas_slice with gpas.len() again. It is guaranteed to be large enough to hold
// everything from gpas.
unsafe {
let gpas_slice: &mut [u64] = gpa_list[0].gpa_list.as_mut_slice(gpas.len());
gpas_slice.copy_from_slice(gpas.as_slice());
}
self.fd
.modify_gpa_host_access(&gpa_list[0])
.map_err(|e| vm::HypervisorVmError::ModifyGpaHostAccess(e.into()))?;
}
Ok(())
}
}

View File

@@ -0,0 +1,145 @@
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
// Copyright © 2024, Microsoft Corporation
//
use crate::arch::emulator::{PlatformEmulator, PlatformError};
use crate::arch::x86::emulator::{CpuStateManager, EmulatorCpuState};
use crate::cpu::Vcpu;
use crate::mshv::MshvVcpu;
use iced_x86::Register;
use mshv_bindings::*;
pub struct MshvEmulatorContext<'a> {
pub vcpu: &'a MshvVcpu,
pub map: (u64, u64), // Initial GVA to GPA mapping provided by the hypervisor
}
impl<'a> MshvEmulatorContext<'a> {
// Do the actual gva -> gpa translation
#[allow(non_upper_case_globals)]
fn translate(&self, gva: u64, flags: u32) -> Result<u64, PlatformError> {
if self.map.0 == gva {
return Ok(self.map.1);
}
let (gpa, result_code) = self
.vcpu
.translate_gva(gva, flags.into())
.map_err(|e| PlatformError::TranslateVirtualAddress(anyhow!(e)))?;
match result_code {
hv_translate_gva_result_code_HV_TRANSLATE_GVA_SUCCESS => Ok(gpa),
_ => Err(PlatformError::TranslateVirtualAddress(anyhow!(result_code))),
}
}
fn read_memory_flags(
&self,
gva: u64,
data: &mut [u8],
flags: u32,
) -> Result<(), PlatformError> {
let gpa = self.translate(gva, flags)?;
debug!(
"mshv emulator: memory read {} bytes from [{:#x} -> {:#x}]",
data.len(),
gva,
gpa
);
if let Some(vm_ops) = &self.vcpu.vm_ops {
if vm_ops.guest_mem_read(gpa, data).is_err() {
vm_ops
.mmio_read(gpa, data)
.map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
}
}
Ok(())
}
}
/// Platform emulation for Hyper-V
impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
type CpuState = EmulatorCpuState;
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
self.read_memory_flags(gva, data, HV_TRANSLATE_GVA_VALIDATE_READ)
}
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
let gpa = self.translate(gva, HV_TRANSLATE_GVA_VALIDATE_WRITE)?;
debug!(
"mshv emulator: memory write {} bytes at [{:#x} -> {:#x}]",
data.len(),
gva,
gpa
);
if let Some(vm_ops) = &self.vcpu.vm_ops {
if vm_ops.guest_mem_write(gpa, data).is_err() {
vm_ops
.mmio_write(gpa, data)
.map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
}
}
Ok(())
}
fn cpu_state(&self, cpu_id: usize) -> Result<Self::CpuState, PlatformError> {
if cpu_id != self.vcpu.vp_index as usize {
return Err(PlatformError::GetCpuStateFailure(anyhow!(
"CPU id mismatch {:?} {:?}",
cpu_id,
self.vcpu.vp_index
)));
}
let regs = self
.vcpu
.get_regs()
.map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
let sregs = self
.vcpu
.get_sregs()
.map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
debug!("mshv emulator: Getting new CPU state");
debug!("mshv emulator: {:#x?}", regs);
Ok(EmulatorCpuState { regs, sregs })
}
fn set_cpu_state(&self, cpu_id: usize, state: Self::CpuState) -> Result<(), PlatformError> {
if cpu_id != self.vcpu.vp_index as usize {
return Err(PlatformError::SetCpuStateFailure(anyhow!(
"CPU id mismatch {:?} {:?}",
cpu_id,
self.vcpu.vp_index
)));
}
debug!("mshv emulator: Setting new CPU state");
debug!("mshv emulator: {:#x?}", state.regs);
self.vcpu
.set_regs(&state.regs)
.map_err(|e| PlatformError::SetCpuStateFailure(e.into()))?;
self.vcpu
.set_sregs(&state.sregs)
.map_err(|e| PlatformError::SetCpuStateFailure(e.into()))
}
fn fetch(&self, ip: u64, instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
let rip =
self.cpu_state(self.vcpu.vp_index as usize)?
.linearize(Register::CS, ip, false)?;
self.read_memory_flags(
rip,
instruction_bytes,
HV_TRANSLATE_GVA_VALIDATE_READ | HV_TRANSLATE_GVA_VALIDATE_EXECUTE,
)
}
}

View File

@@ -14,6 +14,8 @@ use crate::arch::x86::{
use serde::{Deserialize, Serialize};
use std::fmt;
pub mod emulator;
///
/// Export generically-named wrappers of mshv_bindings for Unix-based platforms
///

View File

@@ -235,6 +235,12 @@ pub enum HypervisorVmError {
///
#[error("Failed to set VM property: {0}")]
SetVmProperty(#[source] anyhow::Error),
///
/// Modify GPA host access error
///
#[cfg(feature = "sev_snp")]
#[error("Failed to modify GPA host access: {0}")]
ModifyGpaHostAccess(#[source] anyhow::Error),
}
///
/// Result type for returning from a function
@@ -404,6 +410,11 @@ pub trait Vm: Send + Sync + Any {
fn resume(&self) -> Result<()> {
Ok(())
}
#[cfg(feature = "sev_snp")]
fn gain_page_access(&self, _gpa: u64, _size: u32) -> Result<()> {
Ok(())
}
}
pub trait VmOps: Send + Sync {

View File

@@ -7,12 +7,12 @@ version = "0.1.0"
[dependencies]
epoll = "4.3.3"
getrandom = "0.2.14"
libc = "0.2.153"
log = "0.4.21"
libc = "0.2.155"
log = "0.4.22"
net_gen = { path = "../net_gen" }
rate_limiter = { path = "../rate_limiter" }
serde = { version = "1.0.197", features = ["derive"] }
thiserror = "1.0.60"
thiserror = "1.0.62"
virtio-bindings = "0.2.2"
virtio-queue = "0.12.0"
vm-memory = { version = "0.14.1", features = [
@@ -25,6 +25,6 @@ vmm-sys-util = "0.12.1"
[dev-dependencies]
once_cell = "1.19.0"
pnet = "0.34.0"
pnet_datalink = "0.34.0"
serde_json = "1.0.115"
pnet = "0.35.0"
pnet_datalink = "0.35.0"
serde_json = "1.0.120"

View File

@@ -19,6 +19,7 @@ use vm_virtio::{AccessPlatform, Translatable};
pub struct TxVirtio {
pub counter_bytes: Wrapping<u64>,
pub counter_frames: Wrapping<u64>,
iovecs: IovecBuffer,
}
impl Default for TxVirtio {
@@ -32,6 +33,7 @@ impl TxVirtio {
TxVirtio {
counter_bytes: Wrapping(0),
counter_frames: Wrapping(0),
iovecs: IovecBuffer::new(),
}
}
@@ -54,7 +56,7 @@ impl TxVirtio {
let mut next_desc = desc_chain.next();
let mut iovecs = Vec::new();
let mut iovecs = self.iovecs.borrow();
while let Some(desc) = next_desc {
let desc_addr = desc
.addr()
@@ -145,6 +147,7 @@ impl TxVirtio {
pub struct RxVirtio {
pub counter_bytes: Wrapping<u64>,
pub counter_frames: Wrapping<u64>,
iovecs: IovecBuffer,
}
impl Default for RxVirtio {
@@ -158,6 +161,7 @@ impl RxVirtio {
RxVirtio {
counter_bytes: Wrapping(0),
counter_frames: Wrapping(0),
iovecs: IovecBuffer::new(),
}
}
@@ -193,7 +197,7 @@ impl RxVirtio {
.ok_or(NetQueuePairError::DescriptorInvalidHeader)?;
let mut next_desc = Some(desc);
let mut iovecs = Vec::new();
let mut iovecs = self.iovecs.borrow();
while let Some(desc) = next_desc {
let desc_addr = desc
.addr()
@@ -288,6 +292,53 @@ impl RxVirtio {
}
}
#[derive(Default, Clone)]
struct IovecBuffer(Vec<libc::iovec>);
// SAFETY: Implementing Send for IovecBuffer is safe as the pointer inside is iovec.
// The iovecs are usually constructed from virtio descriptors, which are safe to send across
// threads.
unsafe impl Send for IovecBuffer {}
// SAFETY: Implementing Sync for IovecBuffer is safe as the pointer inside is iovec.
// The iovecs are usually constructed from virtio descriptors, which are safe to access from
// multiple threads.
unsafe impl Sync for IovecBuffer {}
impl IovecBuffer {
fn new() -> Self {
// Here we use 4 as the default capacity because it is enough for most cases.
const DEFAULT_CAPACITY: usize = 4;
IovecBuffer(Vec::with_capacity(DEFAULT_CAPACITY))
}
fn borrow(&mut self) -> IovecBufferBorrowed<'_> {
IovecBufferBorrowed(&mut self.0)
}
}
struct IovecBufferBorrowed<'a>(&'a mut Vec<libc::iovec>);
impl<'a> std::ops::Deref for IovecBufferBorrowed<'a> {
type Target = Vec<libc::iovec>;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl<'a> std::ops::DerefMut for IovecBufferBorrowed<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0
}
}
impl Drop for IovecBufferBorrowed<'_> {
fn drop(&mut self) {
// Clear the buffer to make sure old values are not used after
self.0.clear();
}
}
#[derive(Default, Clone)]
pub struct NetCounters {
pub tx_bytes: Arc<AtomicU64>,

View File

@@ -23,6 +23,7 @@ pub enum OptionParserError {
UnknownOption(String),
InvalidSyntax(String),
Conversion(String, String),
InvalidValue(String),
}
impl fmt::Display for OptionParserError {
@@ -33,6 +34,7 @@ impl fmt::Display for OptionParserError {
OptionParserError::Conversion(field, value) => {
write!(f, "unable to convert {value} for {field}")
}
OptionParserError::InvalidValue(s) => write!(f, "invalid value: {s}"),
}
}
}
@@ -203,7 +205,7 @@ impl FromStr for ByteSized {
0
};
let s = s.trim_end_matches(|c| c == 'K' || c == 'M' || c == 'G');
let s = s.trim_end_matches(['K', 'M', 'G']);
s.parse::<u64>()
.map_err(|_| ByteSizedParseError::InvalidValue(s.to_owned()))?
<< shift

View File

@@ -10,13 +10,13 @@ kvm = ["vfio-ioctls/kvm"]
mshv = ["vfio-ioctls/mshv"]
[dependencies]
anyhow = "1.0.81"
anyhow = "1.0.86"
byteorder = "1.5.0"
hypervisor = { path = "../hypervisor" }
libc = "0.2.153"
log = "0.4.21"
libc = "0.2.155"
log = "0.4.22"
serde = { version = "1.0.197", features = ["derive"] }
thiserror = "1.0.60"
thiserror = "1.0.62"
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main", features = [
"fam-wrappers",
] }

View File

@@ -14,7 +14,7 @@ use std::any::Any;
use std::collections::HashMap;
use std::ops::DerefMut;
use std::sync::{Arc, Barrier, Mutex};
use vm_device::{Bus, BusDevice};
use vm_device::{Bus, BusDevice, BusDeviceSync};
const VENDOR_ID_INTEL: u16 = 0x8086;
const DEVICE_ID_INTEL_VIRT_PCIE_HOST: u16 = 0x0d57;
@@ -122,7 +122,7 @@ impl PciBus {
pub fn register_mapping(
&self,
dev: Arc<Mutex<dyn BusDevice>>,
dev: Arc<dyn BusDeviceSync>,
#[cfg(target_arch = "x86_64")] io_bus: &Bus,
mmio_bus: &Bus,
bars: Vec<PciBarConfiguration>,

View File

@@ -11,7 +11,7 @@ use std::fmt::{self, Display};
use std::sync::{Arc, Barrier, Mutex};
use std::{io, result};
use vm_allocator::{AddressAllocator, SystemAllocator};
use vm_device::{BusDevice, Resource};
use vm_device::Resource;
#[derive(Debug)]
pub enum Error {
@@ -54,7 +54,7 @@ pub struct BarReprogrammingParams {
pub region_type: PciBarRegionType,
}
pub trait PciDevice: BusDevice {
pub trait PciDevice: Send {
/// Allocates the needed PCI BARs space using the `allocate` function which takes a size and
/// returns an address. Returns a Vec of (GuestAddress, GuestUsize) tuples.
fn allocate_bars(

View File

@@ -213,6 +213,12 @@ impl MsixConfig {
let index: usize = (offset / MSIX_TABLE_ENTRIES_MODULO) as usize;
let modulo_offset = offset % MSIX_TABLE_ENTRIES_MODULO;
if index >= self.table_entries.len() {
debug!("Invalid MSI-X table entry index {index}");
data.copy_from_slice(&[0xff; 8][..data.len()]);
return;
}
match data.len() {
4 => {
let value = match modulo_offset {
@@ -260,6 +266,11 @@ impl MsixConfig {
let index: usize = (offset / MSIX_TABLE_ENTRIES_MODULO) as usize;
let modulo_offset = offset % MSIX_TABLE_ENTRIES_MODULO;
if index >= self.table_entries.len() {
debug!("Invalid MSI-X table entry index {index}");
return;
}
// Store the value of the entry before modification
let old_entry = self.table_entries[index].clone();
@@ -351,6 +362,12 @@ impl MsixConfig {
let index: usize = (offset / MSIX_PBA_ENTRIES_MODULO) as usize;
let modulo_offset = offset % MSIX_PBA_ENTRIES_MODULO;
if index >= self.pba_entries.len() {
debug!("Invalid MSI-X PBA entry index {index}");
data.copy_from_slice(&[0xff; 8][..data.len()]);
return;
}
match data.len() {
4 => {
let value: u32 = match modulo_offset {

View File

@@ -9,7 +9,7 @@ version = "0.1.0"
clap = { version = "4.5.4", features = ["wrap_help"] }
dirs = "5.0.1"
serde = { version = "1.0.197", features = ["derive", "rc"] }
serde_json = "1.0.115"
serde_json = "1.0.120"
test_infra = { path = "../test_infra" }
thiserror = "1.0.60"
thiserror = "1.0.62"
wait-timeout = "0.2.0"

View File

@@ -274,7 +274,7 @@ mod adjuster {
}
}
const TEST_LIST: [PerformanceTest; 29] = [
const TEST_LIST: [PerformanceTest; 30] = [
PerformanceTest {
name: "boot_time_ms",
func_ptr: performance_boot_time,
@@ -306,6 +306,16 @@ const TEST_LIST: [PerformanceTest; 29] = [
},
unit_adjuster: adjuster::s_to_ms,
},
PerformanceTest {
name: "restore_latency_time_ms",
func_ptr: performance_restore_latency,
control: PerformanceTestControl {
test_timeout: 2,
test_iterations: 10,
..PerformanceTestControl::default()
},
unit_adjuster: adjuster::identity,
},
PerformanceTest {
name: "boot_time_16_vcpus_pmem_ms",
func_ptr: performance_boot_time_pmem,

View File

@@ -23,6 +23,7 @@ pub const FOCAL_IMAGE_NAME: &str = "focal-server-cloudimg-arm64-custom-20210929-
enum Error {
BootTimeParse,
Infra(InfraError),
RestoreTimeParse,
}
impl From<InfraError> for Error {
@@ -76,6 +77,26 @@ fn direct_kernel_boot_path() -> PathBuf {
kernel_path
}
fn remote_command(api_socket: &str, command: &str, arg: Option<&str>) -> bool {
let mut cmd = std::process::Command::new(clh_command("ch-remote"));
cmd.args([&format!("--api-socket={}", api_socket), 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: {:?}\nstderr: {}",
&cmd,
String::from_utf8_lossy(&output.stderr)
);
false
}
}
pub fn performance_net_throughput(control: &PerformanceTestControl) -> f64 {
let test_timeout = control.test_timeout;
let (rx, bandwidth) = control.net_control.unwrap();
@@ -255,7 +276,7 @@ fn measure_boot_time(cmd: &mut GuestCommand, test_timeout: u32) -> Result<f64, E
let _ = child.kill();
let output = child.wait_with_output().unwrap();
parse_boot_time_output(&output.stderr).map_err(|e| {
parse_boot_time_output(&output.stderr).inspect_err(|_| {
eprintln!(
"\n\n==== Start child stdout ====\n\n{}\n\n==== End child stdout ====",
String::from_utf8_lossy(&output.stdout)
@@ -264,8 +285,6 @@ fn measure_boot_time(cmd: &mut GuestCommand, test_timeout: u32) -> Result<f64, E
"\n\n==== Start child stderr ====\n\n{}\n\n==== End child stderr ====",
String::from_utf8_lossy(&output.stderr)
);
e
})
}
@@ -406,6 +425,120 @@ pub fn performance_block_io(control: &PerformanceTestControl) -> f64 {
}
}
// Parse the event_monitor file based on the format that each event
// is followed by a double newline
fn parse_event_file(event_file: &str) -> Vec<serde_json::Value> {
let content = fs::read(event_file).unwrap();
let mut ret = Vec::new();
for entry in String::from_utf8_lossy(&content)
.trim()
.split("\n\n")
.collect::<Vec<&str>>()
{
ret.push(serde_json::from_str(entry).unwrap());
}
ret
}
fn parse_restore_time_output(events: &[serde_json::Value]) -> Result<f64, Error> {
for entry in events.iter() {
if entry["event"].as_str().unwrap() == "restored" {
let duration = entry["timestamp"]["secs"].as_u64().unwrap() as f64 * 1_000f64
+ entry["timestamp"]["nanos"].as_u64().unwrap() as f64 / 1_000_000f64;
return Ok(duration);
}
}
Err(Error::RestoreTimeParse)
}
fn measure_restore_time(
cmd: &mut GuestCommand,
event_file: &str,
test_timeout: u32,
) -> Result<f64, Error> {
let mut child = cmd
.capture_output()
.verbosity(VerbosityLevel::Warn)
.set_print_cmd(false)
.spawn()
.unwrap();
thread::sleep(Duration::new((test_timeout / 2) as u64, 0));
let _ = child.kill();
let output = child.wait_with_output().unwrap();
let json_events = parse_event_file(event_file);
parse_restore_time_output(&json_events).inspect_err(|_| {
eprintln!(
"\n\n==== Start child stdout ====\n\n{}\n\n==== End child stdout ====\
\n\n==== Start child stderr ====\n\n{}\n\n==== End child stderr ====",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
)
})
}
pub fn performance_restore_latency(control: &PerformanceTestControl) -> f64 {
let r = std::panic::catch_unwind(|| {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = performance_test_new_guest(Box::new(focal));
let api_socket_source = String::from(
guest
.tmp_dir
.as_path()
.join("cloud-hypervisor.sock")
.to_str()
.unwrap(),
);
let mut child = GuestCommand::new(&guest)
.args(["--api-socket", &api_socket_source])
.args([
"--cpus",
&format!("boot={}", control.num_boot_vcpus.unwrap_or(1)),
])
.args(["--memory", "size=256M"])
.args(["--kernel", direct_kernel_boot_path().to_str().unwrap()])
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
.args(["--console", "off"])
.default_disks()
.set_print_cmd(false)
.spawn()
.unwrap();
thread::sleep(Duration::new((control.test_timeout / 2) as u64, 0));
let snapshot_dir = String::from(guest.tmp_dir.as_path().join("snapshot").to_str().unwrap());
std::fs::create_dir(&snapshot_dir).unwrap();
assert!(remote_command(&api_socket_source, "pause", None));
assert!(remote_command(
&api_socket_source,
"snapshot",
Some(format!("file://{}", snapshot_dir).as_str()),
));
let _ = child.kill();
let event_path = String::from(guest.tmp_dir.as_path().join("event.json").to_str().unwrap());
let mut cmd = GuestCommand::new(&guest);
let c = cmd
.args([
"--restore",
format!("source_url=file://{}", snapshot_dir).as_str(),
])
.args(["--event-monitor", format!("path={}", event_path).as_str()]);
measure_restore_time(c, event_path.as_str(), control.test_timeout).unwrap()
});
match r {
Ok(r) => r,
Err(_) => {
panic!("test failed!");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -496,7 +629,42 @@ cloud-hypervisor: 613.57361ms: <vcpu0> INFO:vmm/src/vm.rs:392 -- [Debug I/O port
assert_eq!(parse_boot_time_output(output.as_bytes()).unwrap(), 0.4525);
}
#[test]
fn test_parse_restore_time_output() {
let output = r#"
{
"timestamp": {
"secs": 0,
"nanos": 4664404
},
"source": "virtio-device",
"event": "activated",
"properties": {
"id": "__rng"
}
}
{
"timestamp": {
"secs": 0,
"nanos": 5505133
},
"source": "vm",
"event": "restored",
"properties": null
}
"#;
let mut ret = Vec::new();
for entry in String::from(output)
.trim()
.split("\n\n")
.collect::<Vec<&str>>()
{
ret.push(serde_json::from_str(entry).unwrap());
}
assert_eq!(parse_restore_time_output(&ret).unwrap(), 5.505133_f64);
}
#[test]
fn test_parse_fio_output() {
let output = r#"

View File

@@ -5,7 +5,7 @@ version = "0.1.0"
[dependencies]
epoll = "4.3.3"
libc = "0.2.153"
log = "0.4.21"
thiserror = "1.0.60"
libc = "0.2.155"
log = "0.4.22"
thiserror = "1.0.62"
vmm-sys-util = "0.12.1"

View File

@@ -1,14 +1,20 @@
- [v40.0](#v400)
- [Support for Restoring File Descriptor Backed Network Devices](#support-for-restoring-file-descriptor-backed-network-devices)
- [v41.0](#v410)
- [Experimental "Pvmemcontrol" Support](#experimental-pvmemcontrol-support)
- [Sandboxing With Landlock Support](#sandboxing-with-landlock-support)
- [Notable Performance Improvements](#notable-performance-improvements)
- [Notable Bug Fixes](#notable-bug-fixes)
- [Contributors](#contributors)
- [v40.0](#v400)
- [Support for Restoring File Descriptor Backed Network Devices](#support-for-restoring-file-descriptor-backed-network-devices)
- [Notable Bug Fixes](#notable-bug-fixes-1)
- [Contributors](#contributors-1)
- [v39.0](#v390)
- [Variable Sizing of PCI Apertures for Segments](#variable-sizing-of-pci-apertures-for-segments)
- [Direct Booting with bzImages](#direct-booting-with-bzimages)
- [Support for NVIDIA GPUDirect P2P Support](#support-for-nvidia-gpudirect-p2p-support)
- [Guest NMI Injection Support](#guest-nmi-injection-support)
- [Notable Bug Fixes](#notable-bug-fixes-1)
- [Contributors](#contributors-1)
- [Notable Bug Fixes](#notable-bug-fixes-2)
- [Contributors](#contributors-2)
- [v38.0](#v380)
- [Group Rate Limiter on Block Devices](#group-rate-limiter-on-block-devices)
- [CPU Pinning Support for Block Device Worker Thread](#cpu-pinning-support-for-block-device-worker-thread)
@@ -16,16 +22,16 @@
- [New 'debug-console' Device](#new-debug-console-device)
- [Improved VFIO Device Support](#improved-vfio-device-support)
- [Extended CPU Affinity Support](#extended-cpu-affinity-support)
- [Notable Bug Fixes](#notable-bug-fixes-2)
- [Contributors](#contributors-2)
- [Notable Bug Fixes](#notable-bug-fixes-3)
- [Contributors](#contributors-3)
- [v37.0](#v370)
- [Long Term Support (LTS) Release](#long-term-support-lts-release)
- [Multiple PCI segments Support for 32-bit VFIO devices](#multiple-pci-segments-support-for-32-bit-vfio-devices)
- [Configurable Named TAP Devices](#configurable-named-tap-devices)
- [TTY Output from Both Serial Device and Virtio Console](#tty-output-from-both-serial-device-and-virtio-console)
- [Faster VM Restoration from Snapshots](#faster-vm-restoration-from-snapshots)
- [Notable Bug Fixes](#notable-bug-fixes-3)
- [Contributors](#contributors-3)
- [Notable Bug Fixes](#notable-bug-fixes-4)
- [Contributors](#contributors-4)
- [v36.0](#v360)
- [Command Line Changes](#command-line-changes)
- [Enabled Features Reported via API Endpoint and CLI](#enabled-features-reported-via-api-endpoint-and-cli)
@@ -34,31 +40,31 @@
- [Unix Socket Backend for Serial Port](#unix-socket-backend-for-serial-port)
- [AIO Backend for Block Devices](#aio-backend-for-block-devices)
- [Documentation Improvements](#documentation-improvements)
- [Notable Bug Fixes](#notable-bug-fixes-4)
- [Contributors](#contributors-4)
- [Notable Bug Fixes](#notable-bug-fixes-5)
- [Contributors](#contributors-5)
- [v35.0](#v350)
- [`virtio-vsock` Support for Linux Guest Kernel v6.3+](#virtio-vsock-support-for-linux-guest-kernel-v63)
- [User Specified Serial Number for `virtio-block`](#user-specified-serial-number-for-virtio-block)
- [vCPU TSC Frequency Included in Migration State](#vcpu-tsc-frequency-included-in-migration-state)
- [Notable Bug Fixes](#notable-bug-fixes-5)
- [Contributors](#contributors-5)
- [Notable Bug Fixes](#notable-bug-fixes-6)
- [Contributors](#contributors-6)
- [v34.0](#v340)
- [Paravirtualised Panic Device Support](#paravirtualised-panic-device-support)
- [Improvements to VM Core Dump](#improvements-to-vm-core-dump)
- [QCOW2 Support for Backing Files](#qcow2-support-for-backing-files)
- [Minimum Host Kernel Bump](#minimum-host-kernel-bump)
- [Notable Bug Fixes](#notable-bug-fixes-6)
- [Contributors](#contributors-6)
- [Notable Bug Fixes](#notable-bug-fixes-7)
- [Contributors](#contributors-7)
- [v33.0](#v330)
- [D-Bus based API](#d-bus-based-api)
- [Expose Host CPU Cache Details for AArch64](#expose-host-cpu-cache-details-for-aarch64)
- [Notable Bug Fixes](#notable-bug-fixes-7)
- [Contributors](#contributors-7)
- [Notable Bug Fixes](#notable-bug-fixes-8)
- [Contributors](#contributors-8)
- [v32.0](#v320)
- [Increased PCI Segment Limit](#increased-pci-segment-limit)
- [API Changes](#api-changes)
- [Notable Bug Fixes](#notable-bug-fixes-8)
- [Contributors](#contributors-8)
- [Notable Bug Fixes](#notable-bug-fixes-9)
- [Contributors](#contributors-9)
- [v31.1](#v311)
- [v31.0](#v310)
- [Update to Latest `acpi_tables`](#update-to-latest-acpi_tables)
@@ -66,15 +72,15 @@
- [Improvements on Console `SIGWINCH` Handler](#improvements-on-console-sigwinch-handler)
- [Remove Directory Support from `MemoryZoneConfig::file`](#remove-directory-support-from-memoryzoneconfigfile)
- [Documentation Improvements](#documentation-improvements-1)
- [Notable Bug Fixes](#notable-bug-fixes-9)
- [Contributors](#contributors-9)
- [Notable Bug Fixes](#notable-bug-fixes-10)
- [Contributors](#contributors-10)
- [v30.0](#v300)
- [Command Line Changes for Reduced Binary Size](#command-line-changes-for-reduced-binary-size)
- [Basic vfio-user Server Support](#basic-vfio-user-server-support)
- [Heap Profiling Support](#heap-profiling-support)
- [Documentation Improvements](#documentation-improvements-2)
- [Notable Bug Fixes](#notable-bug-fixes-10)
- [Contributors](#contributors-10)
- [Notable Bug Fixes](#notable-bug-fixes-11)
- [Contributors](#contributors-11)
- [v28.2](#v282)
- [v29.0](#v290)
- [Release Binary Supports Both MSHV and KVM](#release-binary-supports-both-mshv-and-kvm)
@@ -84,10 +90,10 @@
- [`AArch64` Documentation Integration](#aarch64-documentation-integration)
- [`virtio-block` Counters Enhancement](#virtio-block-counters-enhancement)
- [TCP Offload Control](#tcp-offload-control)
- [Notable Bug Fixes](#notable-bug-fixes-11)
- [Notable Bug Fixes](#notable-bug-fixes-12)
- [Removals](#removals)
- [Deprecations](#deprecations)
- [Contributors](#contributors-11)
- [Contributors](#contributors-12)
- [v28.1](#v281)
- [v28.0](#v280)
- [Community Engagement (Reminder)](#community-engagement-reminder)
@@ -95,9 +101,9 @@
- [Virtualised TPM Support](#virtualised-tpm-support)
- [Transparent Huge Page Support](#transparent-huge-page-support)
- [README Quick Start Improved](#readme-quick-start-improved)
- [Notable Bug Fixes](#notable-bug-fixes-12)
- [Notable Bug Fixes](#notable-bug-fixes-13)
- [Removals](#removals-1)
- [Contributors](#contributors-12)
- [Contributors](#contributors-13)
- [v27.0](#v270)
- [Community Engagement](#community-engagement)
- [Prebuilt Packages](#prebuilt-packages)
@@ -106,41 +112,41 @@
- [Simplified Build Feature Flags](#simplified-build-feature-flags)
- [Asynchronous Kernel Loading](#asynchronous-kernel-loading)
- [GDB Support for AArch64](#gdb-support-for-aarch64)
- [Notable Bug Fixes](#notable-bug-fixes-13)
- [Notable Bug Fixes](#notable-bug-fixes-14)
- [Deprecations](#deprecations-1)
- [Contributors](#contributors-13)
- [Contributors](#contributors-14)
- [v26.0](#v260)
- [SMBIOS Improvements via `--platform`](#smbios-improvements-via---platform)
- [Unified Binary MSHV and KVM Support](#unified-binary-mshv-and-kvm-support)
- [Notable Bug Fixes](#notable-bug-fixes-14)
- [Notable Bug Fixes](#notable-bug-fixes-15)
- [Deprecations](#deprecations-2)
- [Removals](#removals-2)
- [Contributors](#contributors-14)
- [Contributors](#contributors-15)
- [v25.0](#v250)
- [`ch-remote` Improvements](#ch-remote-improvements-1)
- [VM "Coredump" Support](#vm-coredump-support)
- [Notable Bug Fixes](#notable-bug-fixes-15)
- [Notable Bug Fixes](#notable-bug-fixes-16)
- [Removals](#removals-3)
- [Contributors](#contributors-15)
- [Contributors](#contributors-16)
- [v24.0](#v240)
- [Bypass Mode for `virtio-iommu`](#bypass-mode-for-virtio-iommu)
- [Ensure Identifiers Uniqueness](#ensure-identifiers-uniqueness)
- [Sparse Mmap support](#sparse-mmap-support)
- [Expose Platform Serial Number](#expose-platform-serial-number)
- [Notable Bug Fixes](#notable-bug-fixes-16)
- [Notable Bug Fixes](#notable-bug-fixes-17)
- [Notable Improvements](#notable-improvements)
- [Deprecations](#deprecations-3)
- [New on the Website](#new-on-the-website)
- [Contributors](#contributors-16)
- [Contributors](#contributors-17)
- [v23.1](#v231)
- [v23.0](#v230)
- [vDPA Support](#vdpa-support)
- [Updated OS Support list](#updated-os-support-list)
- [`AArch64` Memory Map Improvements](#aarch64-memory-map-improvements)
- [`AMX` Support](#amx-support)
- [Notable Bug Fixes](#notable-bug-fixes-17)
- [Notable Bug Fixes](#notable-bug-fixes-18)
- [Deprecations](#deprecations-4)
- [Contributors](#contributors-17)
- [Contributors](#contributors-18)
- [v22.1](#v221)
- [v22.0](#v220)
- [GDB Debug Stub Support](#gdb-debug-stub-support)
@@ -151,13 +157,13 @@
- [PMU Support for AArch64](#pmu-support-for-aarch64)
- [Documentation Under CC-BY-4.0 License](#documentation-under-cc-by-40-license)
- [Deprecation of "Classic" `virtiofsd`](#deprecation-of-classic-virtiofsd)
- [Notable Bug Fixes](#notable-bug-fixes-18)
- [Contributors](#contributors-18)
- [Notable Bug Fixes](#notable-bug-fixes-19)
- [Contributors](#contributors-19)
- [v21.0](#v210)
- [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)
- [Notable Bug fixes](#notable-bug-fixes-19)
- [Contributors](#contributors-19)
- [Notable Bug fixes](#notable-bug-fixes-20)
- [Contributors](#contributors-20)
- [v20.2](#v202)
- [v20.1](#v201)
- [v20.0](#v200)
@@ -166,8 +172,8 @@
- [Improved VFIO support](#improved-vfio-support)
- [Safer code](#safer-code)
- [Extended documentation](#extended-documentation)
- [Notable bug fixes](#notable-bug-fixes-20)
- [Contributors](#contributors-20)
- [Notable bug fixes](#notable-bug-fixes-21)
- [Contributors](#contributors-21)
- [v19.0](#v190)
- [Improved PTY handling for serial and `virtio-console`](#improved-pty-handling-for-serial-and-virtio-console)
- [PCI boot time optimisations](#pci-boot-time-optimisations)
@@ -175,8 +181,8 @@
- [Live migration enhancements](#live-migration-enhancements)
- [`virtio-mem` support with `vfio-user`](#virtio-mem-support-with-vfio-user)
- [AArch64 for `virtio-iommu`](#aarch64-for-virtio-iommu)
- [Notable bug fixes](#notable-bug-fixes-21)
- [Contributors](#contributors-21)
- [Notable bug fixes](#notable-bug-fixes-22)
- [Contributors](#contributors-22)
- [v18.0](#v180)
- [Experimental User Device (`vfio-user`) support](#experimental-user-device-vfio-user-support)
- [Migration support for `vhost-user` devices](#migration-support-for-vhost-user-devices)
@@ -186,23 +192,23 @@
- [Live migration on MSHV hypervisor](#live-migration-on-mshv-hypervisor)
- [AArch64 CPU topology support](#aarch64-cpu-topology-support)
- [Power button support on AArch64](#power-button-support-on-aarch64)
- [Notable bug fixes](#notable-bug-fixes-22)
- [Contributors](#contributors-22)
- [Notable bug fixes](#notable-bug-fixes-23)
- [Contributors](#contributors-23)
- [v17.0](#v170)
- [ARM64 NUMA support using ACPI](#arm64-numa-support-using-acpi)
- [`Seccomp` support for MSHV backend](#seccomp-support-for-mshv-backend)
- [Hotplug of `macvtap` devices](#hotplug-of-macvtap-devices)
- [Improved SGX support](#improved-sgx-support)
- [Inflight tracking for `vhost-user` devices](#inflight-tracking-for-vhost-user-devices)
- [Notable bug fixes](#notable-bug-fixes-23)
- [Contributors](#contributors-23)
- [Notable bug fixes](#notable-bug-fixes-24)
- [Contributors](#contributors-24)
- [v16.0](#v160)
- [Improved live migration support](#improved-live-migration-support)
- [Improved `vhost-user` support](#improved-vhost-user-support)
- [ARM64 ACPI and UEFI support](#arm64-acpi-and-uefi-support)
- [Notable bug fixes](#notable-bug-fixes-24)
- [Notable bug fixes](#notable-bug-fixes-25)
- [Removed functionality](#removed-functionality)
- [Contributors](#contributors-24)
- [Contributors](#contributors-25)
- [v15.0](#v150)
- [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees)
- [Network device rate limiting](#network-device-rate-limiting)
@@ -210,7 +216,7 @@
- [`--api-socket` supports file descriptor parameter](#--api-socket-supports-file-descriptor-parameter)
- [Bug fixes](#bug-fixes)
- [Deprecations](#deprecations-5)
- [Contributors](#contributors-25)
- [Contributors](#contributors-26)
- [v0.14.1](#v0141)
- [v0.14.0](#v0140)
- [Structured event monitoring](#structured-event-monitoring)
@@ -220,7 +226,7 @@
- [PTY control for serial and `virtio-console`](#pty-control-for-serial-and-virtio-console)
- [Block device rate limiting](#block-device-rate-limiting)
- [Deprecations](#deprecations-6)
- [Contributors](#contributors-26)
- [Contributors](#contributors-27)
- [v0.13.0](#v0130)
- [Wider VFIO device support](#wider-vfio-device-support)
- [Improved huge page support](#improved-huge-page-support)
@@ -228,13 +234,13 @@
- [VHD disk image support](#vhd-disk-image-support)
- [Improved Virtio device threading](#improved-virtio-device-threading)
- [Clean shutdown support via synthetic power button](#clean-shutdown-support-via-synthetic-power-button)
- [Contributors](#contributors-27)
- [Contributors](#contributors-28)
- [v0.12.0](#v0120)
- [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)
- [Migration of `vhost-user-fs` backend](#migration-of-vhost-user-fs-backend)
- [Enhanced "info" API](#enhanced-info-api)
- [Contributors](#contributors-28)
- [Contributors](#contributors-29)
- [v0.11.0](#v0110)
- [`io_uring` support by default for `virtio-block`](#io_uring-support-by-default-for-virtio-block)
- [Windows Guest Support](#windows-guest-support)
@@ -246,15 +252,15 @@
- [Default Log Level Changed](#default-log-level-changed)
- [New `--balloon` Parameter Added](#new---balloon-parameter-added)
- [Experimental `virtio-watchdog` Support](#experimental-virtio-watchdog-support)
- [Notable Bug Fixes](#notable-bug-fixes-25)
- [Contributors](#contributors-29)
- [Notable Bug Fixes](#notable-bug-fixes-26)
- [Contributors](#contributors-30)
- [v0.10.0](#v0100)
- [`virtio-block` Support for Multiple Descriptors](#virtio-block-support-for-multiple-descriptors)
- [Memory Zones](#memory-zones)
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements)
- [Preliminary KVM HyperV Emulation Control](#preliminary-kvm-hyperv-emulation-control)
- [Notable Bug Fixes](#notable-bug-fixes-26)
- [Contributors](#contributors-30)
- [Notable Bug Fixes](#notable-bug-fixes-27)
- [Contributors](#contributors-31)
- [v0.9.0](#v090)
- [`io_uring` Based Block Device Support](#io_uring-based-block-device-support)
- [Block and Network Device Statistics](#block-and-network-device-statistics)
@@ -267,17 +273,17 @@
- [Enhancements to ARM64 Support](#enhancements-to-arm64-support)
- [Intel SGX Support](#intel-sgx-support)
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements-1)
- [Notable Bug Fixes](#notable-bug-fixes-27)
- [Contributors](#contributors-31)
- [Notable Bug Fixes](#notable-bug-fixes-28)
- [Contributors](#contributors-32)
- [v0.8.0](#v080)
- [Experimental Snapshot and Restore Support](#experimental-snapshot-and-restore-support)
- [Experimental ARM64 Support](#experimental-arm64-support)
- [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)
- [`vhost_user_fs` Improvements](#vhost_user_fs-improvements)
- [Notable Bug Fixes](#notable-bug-fixes-28)
- [Notable Bug Fixes](#notable-bug-fixes-29)
- [Command Line and API Changes](#command-line-and-api-changes)
- [Contributors](#contributors-32)
- [Contributors](#contributors-33)
- [v0.7.0](#v070)
- [Block, Network, Persistent Memory (PMEM), VirtioFS and Vsock hotplug](#block-network-persistent-memory-pmem-virtiofs-and-vsock-hotplug)
- [Alternative `libc` Support](#alternative-libc-support)
@@ -287,14 +293,14 @@
- [`Seccomp` Sandboxing](#seccomp-sandboxing)
- [Updated Distribution Support](#updated-distribution-support)
- [Command Line and API Changes](#command-line-and-api-changes-1)
- [Contributors](#contributors-33)
- [Contributors](#contributors-34)
- [v0.6.0](#v060)
- [Directly Assigned Devices Hotplug](#directly-assigned-devices-hotplug)
- [Shared Filesystem Improvements](#shared-filesystem-improvements)
- [Block and Networking IO Self Offloading](#block-and-networking-io-self-offloading)
- [Command Line Interface](#command-line-interface)
- [PVH Boot](#pvh-boot)
- [Contributors](#contributors-34)
- [Contributors](#contributors-35)
- [v0.5.1](#v051)
- [v0.5.0](#v050)
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
@@ -302,7 +308,7 @@
- [New Interrupt Management Framework](#new-interrupt-management-framework)
- [Development Tools](#development-tools)
- [Kata Containers Integration](#kata-containers-integration)
- [Contributors](#contributors-35)
- [Contributors](#contributors-36)
- [v0.4.0](#v040)
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
@@ -311,7 +317,7 @@
- [Userspace IOAPIC by default](#userspace-ioapic-by-default)
- [PCI BAR reprogramming](#pci-bar-reprogramming)
- [New `cloud-hypervisor` organization](#new-cloud-hypervisor-organization)
- [Contributors](#contributors-36)
- [Contributors](#contributors-37)
- [v0.3.0](#v030)
- [Block device offloading](#block-device-offloading)
- [Network device backend](#network-device-backend)
@@ -338,6 +344,65 @@
- [Unit testing](#unit-testing)
- [Integration tests parallelization](#integration-tests-parallelization)
# v41.0
This release has been tracked in our [roadmap
project](https://github.com/orgs/cloud-hypervisor/projects/6) as iteration
v41.0. The following user visible changes have been made:
### Experimental "Pvmemcontrol" Support
VMM support has been added for this experimental functionality (requires
currently out of tree Linux kernel patches) to allow guests to control its
physical memory properties to allow optimisations and security features.
(#6318, #6467)
### Sandboxing With Landlock Support
Support for restricting the VMM process using the Linux kernel "Landlock" API
has been added - this can be used to restrict the files (and the read/write
permissions) that the VMM process can access. This adds another layer of
security alongside the existing sycall filters (`seccomp`) - this can be
enabled with `--landlock` and [fully documentated](docs/landlock.md). (#5170)
### Notable Performance Improvements
* Reduced heap allocations in `virtio-net` via the use of a cache of `Iovec`
structures (#6636)
* Notification suppression ("`EVENT_IDX`") support has been added to
`virtio-block` giving a 60% improvement in single queue block throughput and
IOPs performance (#6580)
* Correct size used for `status` field in `virtio-block` state (#6586)
### Notable Bug Fixes
* Avoid panic on out-of-bounds PCI MSI-X access (#6657)
* Fix undefined behaviour on AArch64 leading to wrong optimisation on KVM API
access (#6647)
* Rust v1.80.0 added use of `fcntl` syscall on debug assertions so this is now
included in the virtio-device seccomp filters for tests that use this (#6648)
* Short reads are now handled correctly in the `virtio-vsock` device (#6621)
* Fix undefined behaviour on TTY ioctl leading to wrong optimisation (#6568)
### Contributors
Many thanks to everyone who has contributed to our release:
* Alyssa Ross <hi@alyssa.is>
* Bo Chen <chen.bo@intel.com>
* Changyuan Lyu <changyuanl@google.com>
* Jinank Jain <jinankjain@microsoft.com>
* Julian Stecklina <julian.stecklina@cyberus-technology.de>
* Muminul Islam <muislam@microsoft.com>
* Nuno Das Neves <nudasnev@microsoft.com>
* Praveen K Paladugu <prapal@linux.microsoft.com>
* Rob Bradford <rbradford@rivosinc.com>
* Songqian Li <sionli@tencent.com>
* Wei Liu <liuwe@microsoft.com>
* Yuanchu Xie <yuanchu@google.com>
* ihciah <ihciah@gmail.com>
* wuxinyue <wuxinyue.wxy@antgroup.com>
# v40.0
This release has been tracked in our [roadmap

View File

@@ -23,6 +23,7 @@ use vmm::api::dbus::{dbus_api_graceful_shutdown, DBusApiOptions};
use vmm::api::http::http_api_graceful_shutdown;
use vmm::api::ApiAction;
use vmm::config;
use vmm::landlock::{Landlock, LandlockError};
use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::signal::block_signal;
@@ -85,6 +86,10 @@ enum Error {
LoggerSetup(log::SetLoggerError),
#[error("Failed to gracefully shutdown http api: {0}")]
HttpApiShutdown(#[source] vmm::Error),
#[error("Failed to create Landlock object: {0}")]
CreateLandlock(#[source] LandlockError),
#[error("Failed to apply Landlock: {0}")]
ApplyLandlock(#[source] LandlockError),
}
#[derive(Error, Debug)]
@@ -271,6 +276,24 @@ fn create_app(default_vcpus: String, default_memory: String, default_rng: String
.num_args(1..)
.group("vm-config"),
)
.arg(
Arg::new("landlock")
.long("landlock")
.num_args(0)
.help(
"enable/disable Landlock.",
)
.action(ArgAction::SetTrue)
.default_value("false")
.group("vm-config"),
)
.arg(
Arg::new("landlock-rules")
.long("landlock-rules")
.help(config::LandlockConfig::SYNTAX)
.num_args(1..)
.group("vm-config"),
)
.arg(
Arg::new("net")
.long("net")
@@ -499,6 +522,16 @@ fn create_app(default_vcpus: String, default_memory: String, default_rng: String
.num_args(1)
.group("vm-config"),
);
#[cfg(feature = "pvmemcontrol")]
let app = app.arg(
Arg::new("pvmemcontrol")
.long("pvmemcontrol")
.help("Pvmemcontrol device")
.num_args(0)
.action(ArgAction::SetTrue)
.group("vm-config"),
);
app.arg(
Arg::new("version")
.short('V')
@@ -634,6 +667,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
let vm_debug_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateDebugEventFd)?;
let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateExitEventFd)?;
let landlock_enable = cmd_arguments.get_flag("landlock");
#[allow(unused_mut)]
let mut event_monitor = cmd_arguments
@@ -703,6 +737,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
vmm::start_event_monitor_thread(
monitor,
&seccomp_action,
landlock_enable,
hypervisor.hypervisor_type(),
exit_evt.try_clone().unwrap(),
)
@@ -729,6 +764,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
exit_evt.try_clone().unwrap(),
&seccomp_action,
hypervisor,
landlock_enable,
)
.map_err(Error::StartVmmThread)?;
@@ -776,6 +812,13 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
}
}
if landlock_enable {
Landlock::new()
.map_err(Error::CreateLandlock)?
.restrict_self()
.map_err(Error::ApplyLandlock)?;
}
vmm_thread_handle
.thread_handle
.join()
@@ -1021,6 +1064,8 @@ mod unit_tests {
vdpa: None,
vsock: None,
pvpanic: false,
#[cfg(feature = "pvmemcontrol")]
pvmemcontrol: None,
iommu: false,
#[cfg(target_arch = "x86_64")]
sgx_epc: None,
@@ -1032,6 +1077,8 @@ mod unit_tests {
platform: None,
tpm: None,
preserved_fds: None,
landlock_enable: false,
landlock_rules: None,
};
assert_eq!(expected_vm_config, result_vm_config);

View File

@@ -7,10 +7,10 @@ version = "0.1.0"
[dependencies]
dirs = "5.0.1"
epoll = "4.3.3"
libc = "0.2.153"
libc = "0.2.155"
once_cell = "1.19.0"
serde = { version = "1.0.197", features = ["derive", "rc"] }
serde_json = "1.0.115"
serde_json = "1.0.120"
ssh2 = { version = "0.9.4", features = ["vendored-openssl"] }
vmm-sys-util = "0.12.1"
wait-timeout = "0.2.0"

View File

@@ -5149,7 +5149,11 @@ mod common_parallel {
}
#[test]
fn test_disk_hotplug() {
#[cfg(target_arch = "x86_64")]
// This test runs a guest with Landlock enabled and hotplugs a new disk. As
// the path for the hotplug disk is not pre-added to Landlock rules, this
// the test will result in a failure.
fn test_landlock() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal));
@@ -5166,6 +5170,7 @@ mod common_parallel {
.args(["--memory", "size=512M"])
.args(["--kernel", kernel_path.to_str().unwrap()])
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
.args(["--landlock"])
.default_disks()
.default_net()
.capture_output()
@@ -5190,6 +5195,70 @@ mod common_parallel {
let mut blk_file_path = dirs::home_dir().unwrap();
blk_file_path.push("workloads");
blk_file_path.push("blk.img");
// As the path to the hotplug disk is not pre-added, this remote
// command will fail.
assert!(!remote_command(
&api_socket,
"add-disk",
Some(format!("path={},id=test0", blk_file_path.to_str().unwrap()).as_str()),
));
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
}
fn _test_disk_hotplug(landlock_enabled: bool) {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal));
#[cfg(target_arch = "x86_64")]
let kernel_path = direct_kernel_boot_path();
#[cfg(target_arch = "aarch64")]
let kernel_path = edk2_path();
let api_socket = temp_api_path(&guest.tmp_dir);
let mut blk_file_path = dirs::home_dir().unwrap();
blk_file_path.push("workloads");
blk_file_path.push("blk.img");
let mut cmd = GuestCommand::new(&guest);
if landlock_enabled {
cmd.args(["--landlock"]).args([
"--landlock-rules",
format!("path={:?},access=rw", blk_file_path).as_str(),
]);
}
cmd.args(["--api-socket", &api_socket])
.args(["--cpus", "boot=1"])
.args(["--memory", "size=512M"])
.args(["--kernel", kernel_path.to_str().unwrap()])
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
.default_disks()
.default_net()
.capture_output();
let mut child = cmd.spawn().unwrap();
let r = std::panic::catch_unwind(|| {
guest.wait_vm_boot(None).unwrap();
// Check /dev/vdc is not there
assert_eq!(
guest
.ssh_command("lsblk | grep -c vdc.*16M || true")
.unwrap()
.trim()
.parse::<u32>()
.unwrap_or(1),
0
);
// Now let's add the extra disk.
let (cmd_success, cmd_output) = remote_command_w_output(
&api_socket,
"add-disk",
@@ -5306,6 +5375,17 @@ mod common_parallel {
handle_child_output(r, &output);
}
#[test]
fn test_disk_hotplug() {
_test_disk_hotplug(false)
}
#[test]
#[cfg(target_arch = "x86_64")]
fn test_disk_hotplug_with_landlock() {
_test_disk_hotplug(true)
}
fn create_loop_device(backing_file_path: &str, block_size: u32, num_retries: usize) -> String {
const LOOP_CONFIGURE: u64 = 0x4c0a;
const LOOP_CTL_GET_FREE: u64 = 0x4c82;
@@ -10080,6 +10160,137 @@ mod live_migration {
handle_child_output(Ok(()), &ovs_output);
}
// This test exercises the local live-migration between two Cloud Hypervisor VMs on the
// same host with Landlock enabled on both VMs. The test validates the following:
// 1. The source VM is up and functional
// 2. Ensure Landlock is enabled on source VM by hotplugging a disk. As the path for this
// disk is not known to the source VM this step will fail.
// 3. The 'send-migration' and 'receive-migration' command finished successfully;
// 4. The source VM terminated gracefully after live migration;
// 5. The destination VM is functional after live migration;
// 6. Ensure Landlock is enabled on destination VM by hotplugging a disk. As the path for
// this disk is not known to the destination VM this step will fail.
fn _test_live_migration_with_landlock() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal));
let kernel_path = direct_kernel_boot_path();
let net_id = "net123";
let net_params = format!(
"id={},tap=,mac={},ip={},mask=255.255.255.0",
net_id, guest.network.guest_mac, guest.network.host_ip
);
let boot_vcpus = 2;
let max_vcpus = 4;
let mut blk_file_path = dirs::home_dir().unwrap();
blk_file_path.push("workloads");
blk_file_path.push("blk.img");
let src_api_socket = temp_api_path(&guest.tmp_dir);
let mut src_child = GuestCommand::new(&guest)
.args([
"--cpus",
format!("boot={boot_vcpus},max={max_vcpus}").as_str(),
])
.args(["--memory", "size=4G,shared=on"])
.args(["--kernel", kernel_path.to_str().unwrap()])
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
.default_disks()
.args(["--api-socket", &src_api_socket])
.args(["--landlock"])
.args(["--net", net_params.as_str()])
.args([
"--landlock-rules",
format!("path={:?},access=rw", guest.tmp_dir.as_path()).as_str(),
])
.capture_output()
.spawn()
.unwrap();
// Start the destination VM
let mut dest_api_socket = temp_api_path(&guest.tmp_dir);
dest_api_socket.push_str(".dest");
let mut dest_child = GuestCommand::new(&guest)
.args(["--api-socket", &dest_api_socket])
.capture_output()
.spawn()
.unwrap();
let r = std::panic::catch_unwind(|| {
guest.wait_vm_boot(None).unwrap();
// Make sure the source VM is functaionl
// Check the number of vCPUs
assert_eq!(guest.get_cpu_count().unwrap_or_default(), boot_vcpus);
// Check the guest RAM
assert!(guest.get_total_memory().unwrap_or_default() > 3_840_000);
// Check Landlock is enabled by hot-plugging a disk.
assert!(!remote_command(
&src_api_socket,
"add-disk",
Some(format!("path={},id=test0", blk_file_path.to_str().unwrap()).as_str()),
));
// Start the live-migration
let migration_socket = String::from(
guest
.tmp_dir
.as_path()
.join("live-migration.sock")
.to_str()
.unwrap(),
);
assert!(
start_live_migration(&migration_socket, &src_api_socket, &dest_api_socket, true),
"Unsuccessful command: 'send-migration' or 'receive-migration'."
);
});
// Check and report any errors occurred during the live-migration
if r.is_err() {
print_and_panic(
src_child,
dest_child,
None,
"Error occurred during live-migration",
);
}
// Check the source vm has been terminated successful (give it '3s' to settle)
thread::sleep(std::time::Duration::new(3, 0));
if !src_child.try_wait().unwrap().map_or(false, |s| s.success()) {
print_and_panic(
src_child,
dest_child,
None,
"source VM was not terminated successfully.",
);
};
// Post live-migration check to make sure the destination VM is funcational
let r = std::panic::catch_unwind(|| {
// Perform same checks to validate VM has been properly migrated
assert_eq!(guest.get_cpu_count().unwrap_or_default(), boot_vcpus);
assert!(guest.get_total_memory().unwrap_or_default() > 3_840_000);
});
// Check Landlock is enabled on destination VM by hot-plugging a disk.
assert!(!remote_command(
&dest_api_socket,
"add-disk",
Some(format!("path={},id=test0", blk_file_path.to_str().unwrap()).as_str()),
));
// Clean-up the destination VM and make sure it terminated correctly
let _ = dest_child.kill();
let dest_output = dest_child.wait_with_output().unwrap();
handle_child_output(r, &dest_output);
}
mod live_migration_parallel {
use super::*;
#[test]
@@ -10121,6 +10332,11 @@ mod live_migration {
fn test_live_upgrade_watchdog_local() {
_test_live_migration_watchdog(true, true)
}
#[test]
#[cfg(target_arch = "x86_64")]
fn test_live_migration_with_landlock() {
_test_live_migration_with_landlock()
}
}
mod live_migration_sequential {

View File

@@ -5,11 +5,11 @@ name = "tracer"
version = "0.1.0"
[dependencies]
libc = "0.2.153"
log = "0.4.21"
libc = "0.2.155"
log = "0.4.22"
once_cell = "1.19.0"
serde = { version = "1.0.197", features = ["derive", "rc"] }
serde_json = "1.0.115"
serde_json = "1.0.120"
[features]
tracing = []

View File

@@ -10,8 +10,8 @@ block = { path = "../block" }
clap = { version = "4.5.4", features = ["cargo", "wrap_help"] }
env_logger = "0.11.3"
epoll = "4.3.3"
libc = "0.2.153"
log = "0.4.21"
libc = "0.2.155"
log = "0.4.22"
option_parser = { path = "../option_parser" }
vhost = { version = "0.11.0", features = ["vhost-user-backend"] }
vhost-user-backend = "0.15.0"

View File

@@ -148,7 +148,7 @@ impl VhostUserBlkThread {
) {
Ok(l) => {
len = l;
VIRTIO_BLK_S_OK
VIRTIO_BLK_S_OK as u8
}
Err(e) => {
len = 1;

View File

@@ -9,8 +9,8 @@ version = "0.1.0"
clap = { version = "4.5.4", features = ["cargo", "wrap_help"] }
env_logger = "0.11.3"
epoll = "4.3.3"
libc = "0.2.153"
log = "0.4.21"
libc = "0.2.155"
log = "0.4.22"
net_util = { path = "../net_util" }
option_parser = { path = "../option_parser" }
vhost = { version = "0.11.0", features = ["vhost-user-backend"] }

View File

@@ -9,26 +9,26 @@ default = []
sev_snp = []
[dependencies]
anyhow = "1.0.81"
anyhow = "1.0.86"
arc-swap = "1.7.1"
block = { path = "../block" }
byteorder = "1.5.0"
epoll = "4.3.3"
event_monitor = { path = "../event_monitor" }
libc = "0.2.153"
log = "0.4.21"
libc = "0.2.155"
log = "0.4.22"
net_gen = { path = "../net_gen" }
net_util = { path = "../net_util" }
pci = { path = "../pci" }
rate_limiter = { path = "../rate_limiter" }
seccompiler = "0.4.0"
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.115"
serde_with = { version = "3.7.0", default-features = false, features = [
serde_json = "1.0.120"
serde_with = { version = "3.9.0", default-features = false, features = [
"macros",
] }
serial_buffer = { path = "../serial_buffer" }
thiserror = "1.0.60"
thiserror = "1.0.62"
vhost = { version = "0.11.0", features = [
"vhost-kern",
"vhost-user-backend",

View File

@@ -40,6 +40,7 @@ use std::sync::{Arc, Barrier};
use thiserror::Error;
use virtio_bindings::virtio_blk::*;
use virtio_bindings::virtio_config::*;
use virtio_bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
use virtio_queue::{Queue, QueueOwnedT, QueueT};
use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryError};
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
@@ -79,6 +80,8 @@ pub enum Error {
QueueIterator(virtio_queue::Error),
#[error("Failed to update request status: {0}")]
RequestStatus(GuestMemoryError),
#[error("Failed to enable notification: {0}")]
QueueEnableNotification(virtio_queue::Error),
}
pub type Result<T> = result::Result<T, Error>;
@@ -137,11 +140,9 @@ struct BlockEpollHandler {
}
impl BlockEpollHandler {
fn process_queue_submit(&mut self) -> Result<bool> {
fn process_queue_submit(&mut self) -> Result<()> {
let queue = &mut self.queue;
let mut used_descs = false;
while let Some(mut desc_chain) = queue.pop_descriptor_chain(self.mem.memory()) {
let mut request = Request::parse(&mut desc_chain, self.access_platform.as_ref())
.map_err(Error::RequestParsing)?;
@@ -163,7 +164,9 @@ impl BlockEpollHandler {
queue
.add_used(desc_chain.memory(), desc_chain.head_index(), 0)
.map_err(Error::QueueAddUsed)?;
used_descs = true;
queue
.enable_notification(self.mem.memory().deref())
.map_err(Error::QueueEnableNotification)?;
continue;
}
@@ -215,7 +218,7 @@ impl BlockEpollHandler {
} else {
desc_chain
.memory()
.write_obj(VIRTIO_BLK_S_OK, request.status_addr)
.write_obj(VIRTIO_BLK_S_OK as u8, request.status_addr)
.map_err(Error::RequestStatus)?;
// If no asynchronous operation has been submitted, we can
@@ -223,23 +226,34 @@ impl BlockEpollHandler {
queue
.add_used(desc_chain.memory(), desc_chain.head_index(), 0)
.map_err(Error::QueueAddUsed)?;
used_descs = true;
queue
.enable_notification(self.mem.memory().deref())
.map_err(Error::QueueEnableNotification)?;
}
}
Ok(used_descs)
Ok(())
}
fn process_queue_submit_and_signal(&mut self) -> result::Result<(), EpollHelperError> {
let needs_notification = self.process_queue_submit().map_err(|e| {
self.process_queue_submit().map_err(|e| {
EpollHelperError::HandleEvent(anyhow!("Failed to process queue (submit): {:?}", e))
})?;
if needs_notification {
if self
.queue
.needs_notification(self.mem.memory().deref())
.map_err(|e| {
EpollHelperError::HandleEvent(anyhow!(
"Failed to check needs_notification: {:?}",
e
))
})?
{
self.signal_used_queue().map_err(|e| {
EpollHelperError::HandleEvent(anyhow!("Failed to signal used queue: {:?}", e))
})?
};
})?;
}
Ok(())
}
@@ -265,8 +279,7 @@ impl BlockEpollHandler {
Err(Error::MissingEntryRequestList)
}
fn process_queue_complete(&mut self) -> Result<bool> {
let mut used_descs = false;
fn process_queue_complete(&mut self) -> Result<()> {
let mem = self.mem.memory();
let mut read_bytes = Wrapping(0);
let mut write_bytes = Wrapping(0);
@@ -361,7 +374,7 @@ impl BlockEpollHandler {
.write_latency_avg
.store(write_avg, Ordering::Relaxed);
(VIRTIO_BLK_S_OK, result as u32)
(VIRTIO_BLK_S_OK as u8, result as u32)
} else {
error!(
"Request failed: {:x?} {:?}",
@@ -379,7 +392,9 @@ impl BlockEpollHandler {
queue
.add_used(mem.deref(), desc_index, len)
.map_err(Error::QueueAddUsed)?;
used_descs = true;
queue
.enable_notification(mem.deref())
.map_err(Error::QueueEnableNotification)?;
}
self.counters
@@ -396,7 +411,7 @@ impl BlockEpollHandler {
.read_ops
.fetch_add(read_ops.0, Ordering::AcqRel);
Ok(used_descs)
Ok(())
}
fn signal_used_queue(&self) -> result::Result<(), DeviceError> {
@@ -487,20 +502,19 @@ impl EpollHelperHandler for BlockEpollHandler {
EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
})?;
let needs_notification = self.process_queue_complete().map_err(|e| {
self.process_queue_complete().map_err(|e| {
EpollHelperError::HandleEvent(anyhow!(
"Failed to process queue (complete): {:?}",
e
))
})?;
if needs_notification {
self.signal_used_queue().map_err(|e| {
EpollHelperError::HandleEvent(anyhow!(
"Failed to signal used queue: {:?}",
e
))
})?;
let rate_limit_reached =
self.rate_limiter.as_ref().map_or(false, |r| r.is_blocked());
// Process the queue only when the rate limit is not reached
if !rate_limit_reached {
self.process_queue_submit_and_signal()?
}
}
RATE_LIMITER_EVENT => {
@@ -606,7 +620,8 @@ impl Block {
| (1u64 << VIRTIO_BLK_F_FLUSH)
| (1u64 << VIRTIO_BLK_F_CONFIG_WCE)
| (1u64 << VIRTIO_BLK_F_BLK_SIZE)
| (1u64 << VIRTIO_BLK_F_TOPOLOGY);
| (1u64 << VIRTIO_BLK_F_TOPOLOGY)
| (1u64 << VIRTIO_RING_F_EVENT_IDX);
if iommu {
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
@@ -779,8 +794,12 @@ impl VirtioDevice for Block {
self.update_writeback();
let mut epoll_threads = Vec::new();
let event_idx = self.common.feature_acked(VIRTIO_RING_F_EVENT_IDX.into());
for i in 0..queues.len() {
let (_, queue, queue_evt) = queues.remove(0);
let (_, mut queue, queue_evt) = queues.remove(0);
queue.set_event_idx(event_idx);
let queue_size = queue.size();
let (kill_evt, pause_evt) = self.common.dup_eventfds();
let queue_idx = i as u16;

View File

@@ -608,11 +608,11 @@ fn get_win_size(tty: &dyn AsRawFd) -> (u16, u16) {
xpixel: u16,
ypixel: u16,
}
let ws: WindowSize = WindowSize::default();
let mut ws: WindowSize = WindowSize::default();
// SAFETY: FFI call with correct arguments
unsafe {
libc::ioctl(tty.as_raw_fd(), TIOCGWINSZ, &ws);
libc::ioctl(tty.as_raw_fd(), TIOCGWINSZ, &mut ws);
}
(ws.cols, ws.rows)

View File

@@ -352,9 +352,8 @@ impl Request {
let desc = desc_chain
.next()
.ok_or(Error::DescriptorChainTooShort)
.map_err(|e| {
.inspect_err(|_| {
error!("Missing head descriptor");
e
})?;
// The descriptor contains the request type which MUST be readable.

View File

@@ -209,6 +209,10 @@ fn virtio_vsock_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
(libc::SYS_ioctl, create_vsock_ioctl_seccomp_rule()),
(libc::SYS_recvfrom, vec![]),
(libc::SYS_socket, vec![]),
// If debug_assertions is enabled, closing a file first checks
// whether the FD is valid with fcntl.
#[cfg(debug_assertions)]
(libc::SYS_fcntl, vec![]),
]
}

View File

@@ -495,11 +495,10 @@ impl VsockMuxer {
const MIN_COMMAND_LEN: usize = 10;
// Bring in the minimum number of bytes that we should be able to read.
if command.len < MIN_COMMAND_LEN {
command.len += stream
.read(&mut command.buf[command.len..MIN_COMMAND_LEN])
.map_err(Error::UnixRead)?;
}
stream
.read_exact(&mut command.buf[command.len..MIN_COMMAND_LEN])
.map_err(Error::UnixRead)?;
command.len = MIN_COMMAND_LEN;
// Now, finish reading the destination port number, by bringing in one byte at a time,
// until we reach an EOL terminator (or our buffer space runs out). Yeah, not
@@ -510,7 +509,6 @@ impl VsockMuxer {
.map_err(Error::UnixRead)?;
}
let _ = command;
let command = partial_command_map.remove(&stream.as_raw_fd()).unwrap();
let mut word_iter = std::str::from_utf8(&command.buf[..command.len])

View File

@@ -5,7 +5,7 @@ name = "vm-allocator"
version = "0.1.0"
[dependencies]
libc = "0.2.153"
libc = "0.2.155"
vm-memory = "0.14.1"
[target.'cfg(target_arch = "aarch64")'.dependencies]

View File

@@ -10,10 +10,10 @@ kvm = ["vfio-ioctls/kvm"]
mshv = ["vfio-ioctls/mshv"]
[dependencies]
anyhow = "1.0.81"
anyhow = "1.0.86"
hypervisor = { path = "../hypervisor" }
serde = { version = "1.0.197", features = ["derive", "rc"] }
thiserror = "1.0.60"
thiserror = "1.0.62"
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
vm-memory = { version = "0.14.1", features = ["backend-mmap"] }
vmm-sys-util = "0.12.1"

View File

@@ -26,6 +26,31 @@ pub trait BusDevice: Send {
}
}
#[allow(unused_variables)]
pub trait BusDeviceSync: Send + Sync {
/// Reads at `offset` from this device
fn read(&self, base: u64, offset: u64, data: &mut [u8]) {}
/// Writes at `offset` into this device
fn write(&self, base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
None
}
}
impl<B: BusDevice> BusDeviceSync for Mutex<B> {
/// Reads at `offset` from this device
fn read(&self, base: u64, offset: u64, data: &mut [u8]) {
self.lock()
.expect("Failed to acquire device lock")
.read(base, offset, data)
}
/// Writes at `offset` into this device
fn write(&self, base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
self.lock()
.expect("Failed to acquire device lock")
.write(base, offset, data)
}
}
#[derive(Debug)]
pub enum Error {
/// The insertion failed because the new device overlapped with an old device.
@@ -95,7 +120,7 @@ impl PartialOrd for BusRange {
/// only restriction is that no two devices can overlap in this address space.
#[derive(Default)]
pub struct Bus {
devices: RwLock<BTreeMap<BusRange, Weak<Mutex<dyn BusDevice>>>>,
devices: RwLock<BTreeMap<BusRange, Weak<dyn BusDeviceSync>>>,
}
impl Bus {
@@ -106,7 +131,7 @@ impl Bus {
}
}
fn first_before(&self, addr: u64) -> Option<(BusRange, Arc<Mutex<dyn BusDevice>>)> {
fn first_before(&self, addr: u64) -> Option<(BusRange, Arc<dyn BusDeviceSync>)> {
let devices = self.devices.read().unwrap();
let (range, dev) = devices
.range(..=BusRange { base: addr, len: 1 })
@@ -115,7 +140,7 @@ impl Bus {
}
#[allow(clippy::type_complexity)]
pub fn resolve(&self, addr: u64) -> Option<(u64, u64, Arc<Mutex<dyn BusDevice>>)> {
fn resolve(&self, addr: u64) -> Option<(u64, u64, Arc<dyn BusDeviceSync>)> {
if let Some((range, dev)) = self.first_before(addr) {
let offset = addr - range.base;
if offset < range.len {
@@ -125,8 +150,7 @@ impl Bus {
None
}
/// Puts the given device at the given address space.
pub fn insert(&self, device: Arc<Mutex<dyn BusDevice>>, base: u64, len: u64) -> Result<()> {
pub fn insert(&self, device: Arc<dyn BusDeviceSync>, base: u64, len: u64) -> Result<()> {
if len == 0 {
return Err(Error::ZeroSizedRange);
}
@@ -171,7 +195,7 @@ impl Bus {
}
/// Removes all entries referencing the given device.
pub fn remove_by_device(&self, device: &Arc<Mutex<dyn BusDevice>>) -> Result<()> {
pub fn remove_by_device(&self, device: &Arc<dyn BusDeviceSync>) -> Result<()> {
let mut device_list = self.devices.write().unwrap();
let mut remove_key_list = Vec::new();
@@ -216,9 +240,7 @@ impl Bus {
pub fn read(&self, addr: u64, data: &mut [u8]) -> Result<()> {
if let Some((base, offset, dev)) = self.resolve(addr) {
// OK to unwrap as lock() failing is a serious error condition and should panic.
dev.lock()
.expect("Failed to acquire device lock")
.read(base, offset, data);
dev.read(base, offset, data);
Ok(())
} else {
Err(Error::MissingAddressRange)
@@ -231,10 +253,7 @@ impl Bus {
pub fn write(&self, addr: u64, data: &[u8]) -> Result<Option<Arc<Barrier>>> {
if let Some((base, offset, dev)) = self.resolve(addr) {
// OK to unwrap as lock() failing is a serious error condition and should panic.
Ok(dev
.lock()
.expect("Failed to acquire device lock")
.write(base, offset, data))
Ok(dev.write(base, offset, data))
} else {
Err(Error::MissingAddressRange)
}
@@ -246,17 +265,17 @@ mod tests {
use super::*;
struct DummyDevice;
impl BusDevice for DummyDevice {}
impl BusDeviceSync for DummyDevice {}
struct ConstantDevice;
impl BusDevice for ConstantDevice {
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
impl BusDeviceSync for ConstantDevice {
fn read(&self, _base: u64, offset: u64, data: &mut [u8]) {
for (i, v) in data.iter_mut().enumerate() {
*v = (offset as u8) + (i as u8);
}
}
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
fn write(&self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
for (i, v) in data.iter().enumerate() {
assert_eq!(*v, (offset as u8) + (i as u8))
}
@@ -268,7 +287,7 @@ mod tests {
#[test]
fn bus_insert() {
let bus = Bus::new();
let dummy = Arc::new(Mutex::new(DummyDevice));
let dummy = Arc::new(DummyDevice);
assert!(bus.insert(dummy.clone(), 0x10, 0).is_err());
assert!(bus.insert(dummy.clone(), 0x10, 0x10).is_ok());
@@ -290,7 +309,7 @@ mod tests {
#[allow(clippy::redundant_clone)]
fn bus_read_write() {
let bus = Bus::new();
let dummy = Arc::new(Mutex::new(DummyDevice));
let dummy = Arc::new(DummyDevice);
assert!(bus.insert(dummy.clone(), 0x10, 0x10).is_ok());
assert!(bus.read(0x10, &mut [0, 0, 0, 0]).is_ok());
assert!(bus.write(0x10, &[0, 0, 0, 0]).is_ok());
@@ -308,7 +327,7 @@ mod tests {
#[allow(clippy::redundant_clone)]
fn bus_read_write_values() {
let bus = Bus::new();
let dummy = Arc::new(Mutex::new(ConstantDevice));
let dummy = Arc::new(ConstantDevice);
assert!(bus.insert(dummy.clone(), 0x10, 0x10).is_ok());
let mut values = [0, 1, 2, 3];
@@ -334,7 +353,7 @@ mod tests {
let bus = Bus::new();
let mut data = [1, 2, 3, 4];
let device = Arc::new(Mutex::new(DummyDevice));
let device = Arc::new(DummyDevice);
assert!(bus.insert(device.clone(), 0x10, 0x10).is_ok());
assert!(bus.write(0x10, &data).is_ok());
assert!(bus.read(0x10, &mut data).is_ok());

View File

@@ -9,7 +9,7 @@ mod bus;
pub mod dma_mapping;
pub mod interrupt;
pub use self::bus::{Bus, BusDevice, Error as BusError};
pub use self::bus::{Bus, BusDevice, BusDeviceSync, Error as BusError};
/// Type of Message Signalled Interrupt
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]

View File

@@ -5,10 +5,10 @@ name = "vm-migration"
version = "0.1.0"
[dependencies]
anyhow = "1.0.81"
anyhow = "1.0.86"
serde = { version = "1.0.197", features = ["derive", "rc"] }
serde_json = "1.0.115"
thiserror = "1.0.60"
serde_json = "1.0.120"
thiserror = "1.0.62"
vm-memory = { version = "0.14.1", features = [
"backend-atomic",
"backend-mmap",

View File

@@ -190,6 +190,22 @@ impl Response {
Ok(response)
}
pub fn ok_or_abandon<T>(
self,
fd: &mut T,
error: MigratableError,
) -> Result<Response, MigratableError>
where
T: Read + Write,
{
if self.status != Status::Ok {
Request::abandon().write_to(fd)?;
Response::read_from(fd)?;
return Err(error);
}
Ok(self)
}
pub fn write_to(&self, fd: &mut dyn Write) -> Result<(), MigratableError> {
fd.write_all(Self::as_slice(self))
.map_err(MigratableError::MigrateSocket)

View File

@@ -8,7 +8,7 @@ version = "0.1.0"
default = []
[dependencies]
log = "0.4.21"
log = "0.4.22"
virtio-queue = "0.12.0"
vm-memory = { version = "0.14.1", features = [
"backend-atomic",

View File

@@ -7,22 +7,29 @@ version = "0.1.0"
[features]
dbus_api = ["blocking", "futures", "zbus"]
default = []
dhat-heap = ["dhat"] # For heap profiling
dhat-heap = ["dhat"] # For heap profiling
guest_debug = ["gdbstub", "gdbstub_arch", "kvm"]
igvm = ["dep:igvm", "hex", "igvm_defs", "mshv-bindings", "range_map_vec"]
io_uring = ["block/io_uring"]
kvm = ["hypervisor/kvm", "pci/kvm", "vfio-ioctls/kvm", "vm-device/kvm"]
kvm = [
"arch/kvm",
"hypervisor/kvm",
"pci/kvm",
"vfio-ioctls/kvm",
"vm-device/kvm",
]
mshv = ["hypervisor/mshv", "pci/mshv", "vfio-ioctls/mshv", "vm-device/mshv"]
pvmemcontrol = ["devices/pvmemcontrol"]
sev_snp = ["arch/sev_snp", "hypervisor/sev_snp", "virtio-devices/sev_snp"]
tdx = ["arch/tdx", "hypervisor/tdx"]
tracing = ["tracer/tracing"]
[dependencies]
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
anyhow = "1.0.81"
anyhow = "1.0.86"
arc-swap = "1.7.1"
arch = { path = "../arch" }
bitflags = "2.5.0"
bitflags = "2.6.0"
block = { path = "../block" }
blocking = { version = "1.6.1", optional = true }
cfg-if = "1.0.0"
@@ -37,11 +44,12 @@ gdbstub = { version = "0.7.1", optional = true }
gdbstub_arch = { version = "0.3.0", optional = true }
hex = { version = "0.4.3", optional = true }
hypervisor = { path = "../hypervisor" }
igvm = { version = "0.3.1", optional = true }
igvm = { version = "0.3.3", optional = true }
igvm_defs = { version = "0.3.1", optional = true }
libc = "0.2.153"
landlock = "0.4.0"
libc = "0.2.155"
linux-loader = { version = "0.11.0", features = ["bzimage", "elf", "pe"] }
log = "0.4.21"
log = "0.4.22"
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
mshv-bindings = { git = "https://github.com/rust-vmm/mshv", tag = "v0.2.0", features = [
"fam-wrappers",
@@ -55,10 +63,10 @@ range_map_vec = { version = "0.2.0", optional = true }
rate_limiter = { path = "../rate_limiter" }
seccompiler = "0.4.0"
serde = { version = "1.0.197", features = ["derive", "rc"] }
serde_json = "1.0.115"
serde_json = "1.0.120"
serial_buffer = { path = "../serial_buffer" }
signal-hook = "0.3.17"
thiserror = "1.0.60"
thiserror = "1.0.62"
tracer = { path = "../tracer" }
uuid = "1.8.0"
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
@@ -76,4 +84,4 @@ vm-migration = { path = "../vm-migration" }
vm-virtio = { path = "../vm-virtio" }
vmm-sys-util = { version = "0.12.1", features = ["with-serde"] }
zbus = { version = "4.1.2", optional = true }
zerocopy = { version = "0.7.32", features = ["alloc", "derive"] }
zerocopy = { version = "0.7.35", features = ["alloc", "derive"] }

View File

@@ -12,6 +12,7 @@ use crate::api::{
VmReceiveMigration, VmRemoveDevice, VmResize, VmResizeZone, VmRestore, VmResume,
VmSendMigration, VmShutdown, VmSnapshot,
};
use crate::landlock::Landlock;
use crate::seccomp_filters::{get_seccomp_filter, Thread};
use crate::{Error as VmmError, Result};
use core::fmt;
@@ -303,6 +304,7 @@ fn start_http_thread(
seccomp_action: &SeccompAction,
exit_evt: EventFd,
hypervisor_type: HypervisorType,
landlock_enable: bool,
) -> Result<HttpApiHandle> {
// Retrieve seccomp filter for API thread
let api_seccomp_filter = get_seccomp_filter(seccomp_action, Thread::HttpApi, hypervisor_type)
@@ -329,6 +331,18 @@ fn start_http_thread(
})?;
}
if landlock_enable {
Landlock::new()
.map_err(VmmError::CreateLandlock)?
.restrict_self()
.map_err(VmmError::ApplyLandlock)
.map_err(|e| {
error!("Error applying landlock to http-server thread: {:?}", e);
exit_evt.write(1).ok();
e
})?;
}
std::panic::catch_unwind(AssertUnwindSafe(move || {
server.start_server().unwrap();
loop {
@@ -375,6 +389,7 @@ pub fn start_http_path_thread(
seccomp_action: &SeccompAction,
exit_evt: EventFd,
hypervisor_type: HypervisorType,
landlock_enable: bool,
) -> Result<HttpApiHandle> {
let socket_path = PathBuf::from(path);
let socket_fd = UnixListener::bind(socket_path).map_err(VmmError::CreateApiServerSocket)?;
@@ -389,6 +404,7 @@ pub fn start_http_path_thread(
seccomp_action,
exit_evt,
hypervisor_type,
landlock_enable,
)
}
@@ -399,6 +415,7 @@ pub fn start_http_fd_thread(
seccomp_action: &SeccompAction,
exit_evt: EventFd,
hypervisor_type: HypervisorType,
landlock_enable: bool,
) -> Result<HttpApiHandle> {
// SAFETY: Valid FD
let server = unsafe { HttpServer::new_from_fd(fd) }.map_err(VmmError::CreateApiServer)?;
@@ -409,6 +426,7 @@ pub fn start_http_fd_thread(
seccomp_action,
exit_evt,
hypervisor_type,
landlock_enable,
)
}

View File

@@ -626,6 +626,13 @@ components:
$ref: "#/components/schemas/PlatformConfig"
tpm:
$ref: "#/components/schemas/TpmConfig"
landlock_enable:
type: boolean
default: false
landlock_rules:
type: array
items:
$ref: "#/components/schemas/LandlockConfig"
description: Virtual machine configuration
CpuAffinity:
@@ -1261,3 +1268,14 @@ components:
properties:
socket:
type: string
LandlockConfig:
required:
- path
- access
type: object
properties:
path:
type: string
access:
type: string

View File

@@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
//
use crate::landlock::LandlockAccess;
pub use crate::vm_config::*;
use clap::ArgMatches;
use option_parser::{
@@ -110,6 +111,10 @@ pub enum Error {
ParseTpm(OptionParserError),
/// Missing path for TPM device
ParseTpmPathMissing,
/// Error parsing Landlock rules
ParseLandlockRules(OptionParserError),
/// Missing fields in Landlock rules
ParseLandlockMissingFields,
}
#[derive(Debug, PartialEq, Eq, Error)]
@@ -205,6 +210,10 @@ pub enum ValidationError {
RestoreMissingRequiredNetId(String),
/// Number of FDs passed during Restore are incorrect to the NetConfig
RestoreNetFdCountMismatch(String, usize, usize),
/// Path provided in landlock-rules doesn't exist
LandlockPathDoesNotExist(PathBuf),
/// Access provided in landlock-rules in invalid
InvalidLandlockAccess(String),
}
type ValidationResult<T> = std::result::Result<T, ValidationError>;
@@ -356,6 +365,16 @@ impl fmt::Display for ValidationError {
"Number of Net FDs passed for '{s}' during Restore: {u1}. Expected: {u2}"
)
}
LandlockPathDoesNotExist(s) => {
write!(
f,
"Path {:?} provided in landlock-rules does not exist",
s.as_path()
)
}
InvalidLandlockAccess(s) => {
write!(f, "{s}")
}
}
}
}
@@ -421,6 +440,11 @@ impl fmt::Display for Error {
ParseVdpaPathMissing => write!(f, "Error parsing --vdpa: path missing"),
ParseTpm(o) => write!(f, "Error parsing --tpm: {o}"),
ParseTpmPathMissing => write!(f, "Error parsing --tpm: path missing"),
ParseLandlockRules(o) => write!(f, "Error parsing --landlock-rules: {o}"),
ParseLandlockMissingFields => write!(
f,
"Error parsing --landlock-rules: path/access field missing"
),
}
}
}
@@ -458,6 +482,8 @@ pub struct VmParams<'a> {
pub user_devices: Option<Vec<&'a str>>,
pub vdpa: Option<Vec<&'a str>>,
pub vsock: Option<&'a str>,
#[cfg(feature = "pvmemcontrol")]
pub pvmemcontrol: bool,
pub pvpanic: bool,
#[cfg(target_arch = "x86_64")]
pub sgx_epc: Option<Vec<&'a str>>,
@@ -472,6 +498,8 @@ pub struct VmParams<'a> {
pub igvm: Option<&'a str>,
#[cfg(feature = "sev_snp")]
pub host_data: Option<&'a str>,
pub landlock_enable: bool,
pub landlock_rules: Option<Vec<&'a str>>,
}
impl<'a> VmParams<'a> {
@@ -517,6 +545,8 @@ impl<'a> VmParams<'a> {
.get_many::<String>("vdpa")
.map(|x| x.map(|y| y as &str).collect());
let vsock: Option<&str> = args.get_one::<String>("vsock").map(|x| x as &str);
#[cfg(feature = "pvmemcontrol")]
let pvmemcontrol = args.get_flag("pvmemcontrol");
let pvpanic = args.get_flag("pvpanic");
#[cfg(target_arch = "x86_64")]
let sgx_epc: Option<Vec<&str>> = args
@@ -537,6 +567,11 @@ impl<'a> VmParams<'a> {
let igvm = args.get_one::<String>("igvm").map(|x| x as &str);
#[cfg(feature = "sev_snp")]
let host_data = args.get_one::<String>("host-data").map(|x| x as &str);
let landlock_enable = args.get_flag("landlock");
let landlock_rules: Option<Vec<&str>> = args
.get_many::<String>("landlock-rules")
.map(|x| x.map(|y| y as &str).collect());
VmParams {
cpus,
memory,
@@ -560,6 +595,8 @@ impl<'a> VmParams<'a> {
user_devices,
vdpa,
vsock,
#[cfg(feature = "pvmemcontrol")]
pvmemcontrol,
pvpanic,
#[cfg(target_arch = "x86_64")]
sgx_epc,
@@ -574,6 +611,8 @@ impl<'a> VmParams<'a> {
igvm,
#[cfg(feature = "sev_snp")]
host_data,
landlock_enable,
landlock_rules,
}
}
}
@@ -2301,6 +2340,45 @@ impl TpmConfig {
}
}
impl LandlockConfig {
pub const SYNTAX: &'static str = "Landlock parameters \
\"path=<path/to/{file/dir}>,access=[rw]\"";
pub fn parse(landlock_rule: &str) -> Result<Self> {
let mut parser = OptionParser::new();
parser.add("path").add("access");
parser
.parse(landlock_rule)
.map_err(Error::ParseLandlockRules)?;
let path = parser
.get("path")
.map(PathBuf::from)
.ok_or(Error::ParseLandlockMissingFields)?;
let access = parser
.get("access")
.ok_or(Error::ParseLandlockMissingFields)?;
if access.chars().count() > 2 {
return Err(Error::ParseLandlockRules(OptionParserError::InvalidValue(
access.to_string(),
)));
}
Ok(LandlockConfig { path, access })
}
pub fn validate(&self) -> ValidationResult<()> {
if !self.path.exists() {
return Err(ValidationError::LandlockPathDoesNotExist(self.path.clone()));
}
LandlockAccess::try_from(self.access.as_str())
.map_err(|e| ValidationError::InvalidLandlockAccess(e.to_string()))?;
Ok(())
}
}
impl VmConfig {
fn validate_identifier(
id_list: &mut BTreeSet<String>,
@@ -2653,6 +2731,12 @@ impl VmConfig {
.map(|p| p.iommu_segments.is_some())
.unwrap_or_default();
if let Some(landlock_rules) = &self.landlock_rules {
for landlock_rule in landlock_rules {
landlock_rule.validate()?;
}
}
Ok(id_list)
}
@@ -2694,6 +2778,11 @@ impl VmConfig {
balloon = Some(BalloonConfig::parse(balloon_params)?);
}
#[cfg(feature = "pvmemcontrol")]
let pvmemcontrol: Option<PvmemcontrolConfig> = vm_params
.pvmemcontrol
.then_some(PvmemcontrolConfig::default());
let mut fs: Option<Vec<FsConfig>> = None;
if let Some(fs_list) = &vm_params.fs {
let mut fs_config_list = Vec::new();
@@ -2823,6 +2912,16 @@ impl VmConfig {
#[cfg(feature = "guest_debug")]
let gdb = vm_params.gdb;
let mut landlock_rules: Option<Vec<LandlockConfig>> = None;
if let Some(ll_rules) = vm_params.landlock_rules {
landlock_rules = Some(
ll_rules
.iter()
.map(|rule| LandlockConfig::parse(rule))
.collect::<Result<Vec<LandlockConfig>>>()?,
);
}
let mut config = VmConfig {
cpus: CpusConfig::parse(vm_params.cpus)?,
memory: MemoryConfig::parse(vm_params.memory, vm_params.memory_zones)?,
@@ -2842,6 +2941,8 @@ impl VmConfig {
user_devices,
vdpa,
vsock,
#[cfg(feature = "pvmemcontrol")]
pvmemcontrol,
pvpanic: vm_params.pvpanic,
iommu: false, // updated in VmConfig::validate()
#[cfg(target_arch = "x86_64")]
@@ -2854,6 +2955,8 @@ impl VmConfig {
platform,
tpm,
preserved_fds: None,
landlock_enable: vm_params.landlock_enable,
landlock_rules,
};
config.validate().map_err(Error::Validation)?;
Ok(config)
@@ -2959,6 +3062,8 @@ impl Clone for VmConfig {
net: self.net.clone(),
rng: self.rng.clone(),
balloon: self.balloon.clone(),
#[cfg(feature = "pvmemcontrol")]
pvmemcontrol: self.pvmemcontrol.clone(),
fs: self.fs.clone(),
pmem: self.pmem.clone(),
serial: self.serial.clone(),
@@ -2980,6 +3085,7 @@ impl Clone for VmConfig {
.as_ref()
// SAFETY: FFI call with valid FDs
.map(|fds| fds.iter().map(|fd| unsafe { libc::dup(*fd) }).collect()),
landlock_rules: self.landlock_rules.clone(),
..*self
}
}
@@ -3747,6 +3853,8 @@ mod tests {
user_devices: None,
vdpa: None,
vsock: None,
#[cfg(feature = "pvmemcontrol")]
pvmemcontrol: None,
pvpanic: false,
iommu: false,
#[cfg(target_arch = "x86_64")]
@@ -3778,6 +3886,8 @@ mod tests {
..net_fixture()
},
]),
landlock_enable: false,
landlock_rules: None,
};
let valid_config = RestoreConfig {
@@ -3954,6 +4064,8 @@ mod tests {
user_devices: None,
vdpa: None,
vsock: None,
#[cfg(feature = "pvmemcontrol")]
pvmemcontrol: None,
pvpanic: false,
iommu: false,
#[cfg(target_arch = "x86_64")]
@@ -3966,6 +4078,8 @@ mod tests {
platform: None,
tpm: None,
preserved_fds: None,
landlock_enable: false,
landlock_rules: None,
};
assert!(valid_config.validate().is_ok());
@@ -4523,4 +4637,20 @@ mod tests {
}
let _still_valid_config = still_valid_config.clone();
}
#[test]
fn test_landlock_parsing() -> Result<()> {
// should not be empty
assert!(LandlockConfig::parse("").is_err());
// access should not be empty
assert!(LandlockConfig::parse("path=/dir/path1").is_err());
assert!(LandlockConfig::parse("path=/dir/path1,access=rwr").is_err());
assert_eq!(
LandlockConfig::parse("path=/dir/path1,access=rw")?,
LandlockConfig {
path: PathBuf::from("/dir/path1"),
access: "rw".to_string(),
}
);
Ok(())
}
}

View File

@@ -49,6 +49,8 @@ use devices::gic;
use devices::ioapic;
#[cfg(target_arch = "aarch64")]
use devices::legacy::Pl011;
#[cfg(feature = "pvmemcontrol")]
use devices::pvmemcontrol::{PvmemcontrolBusDevice, PvmemcontrolPciDevice};
use devices::{
interrupt_controller, interrupt_controller::InterruptController, AcpiNotificationFlags,
};
@@ -89,7 +91,7 @@ use vm_device::dma_mapping::ExternalDmaMapping;
use vm_device::interrupt::{
InterruptIndex, InterruptManager, LegacyIrqGroupConfig, MsiIrqGroupConfig,
};
use vm_device::{Bus, BusDevice, Resource};
use vm_device::{Bus, BusDevice, BusDeviceSync, Resource};
use vm_memory::guest_memory::FileOffset;
use vm_memory::GuestMemoryRegion;
use vm_memory::{Address, GuestAddress, GuestUsize, MmapRegion};
@@ -118,6 +120,8 @@ const DEBUGCON_DEVICE_NAME: &str = "__debug_console";
const GPIO_DEVICE_NAME: &str = "__gpio";
const RNG_DEVICE_NAME: &str = "__rng";
const IOMMU_DEVICE_NAME: &str = "__iommu";
#[cfg(feature = "pvmemcontrol")]
const PVMEMCONTROL_DEVICE_NAME: &str = "__pvmemcontrol";
const BALLOON_DEVICE_NAME: &str = "__balloon";
const CONSOLE_DEVICE_NAME: &str = "__console";
const PVPANIC_DEVICE_NAME: &str = "__pvpanic";
@@ -195,6 +199,10 @@ pub enum DeviceManagerError {
/// Cannot create virtio-balloon device
CreateVirtioBalloon(io::Error),
/// Cannot create pvmemcontrol device
#[cfg(feature = "pvmemcontrol")]
CreatePvmemcontrol(io::Error),
/// Cannot create virtio-watchdog device
CreateVirtioWatchdog(io::Error),
@@ -773,6 +781,39 @@ pub struct AcpiPlatformAddresses {
pub sleep_status_reg_address: Option<GenericAddress>,
}
#[cfg(all(feature = "mshv", feature = "sev_snp"))]
struct SevSnpPageAccessProxy {
vm: Arc<dyn hypervisor::Vm>,
}
#[cfg(all(feature = "mshv", feature = "sev_snp"))]
impl std::fmt::Debug for SevSnpPageAccessProxy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SNP Page access proxy")
}
}
#[cfg(all(feature = "mshv", feature = "sev_snp"))]
impl SevSnpPageAccessProxy {
fn new(vm: Arc<dyn hypervisor::Vm>) -> SevSnpPageAccessProxy {
SevSnpPageAccessProxy { vm }
}
}
#[cfg(all(feature = "mshv", feature = "sev_snp"))]
impl AccessPlatform for SevSnpPageAccessProxy {
fn translate_gpa(&self, base: u64, _size: u64) -> std::result::Result<u64, std::io::Error> {
Ok(base)
}
fn translate_gva(&self, base: u64, size: u64) -> std::result::Result<u64, std::io::Error> {
self.vm
.gain_page_access(base, size as u32)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
Ok(base)
}
}
pub struct DeviceManager {
// Manage address space related to devices
address_manager: Arc<AddressManager>,
@@ -818,7 +859,7 @@ pub struct DeviceManager {
// Let the DeviceManager keep strong references to the BusDevice devices.
// This allows the IO and MMIO buses to be provided with Weak references,
// which prevents cyclic dependencies.
bus_devices: Vec<Arc<Mutex<dyn BusDevice>>>,
bus_devices: Vec<Arc<dyn BusDeviceSync>>,
// Counter to keep track of the consumed device IDs.
device_id_cnt: Wrapping<usize>,
@@ -886,6 +927,12 @@ pub struct DeviceManager {
// GPIO device for AArch64
gpio_device: Option<Arc<Mutex<devices::legacy::Gpio>>>,
#[cfg(feature = "pvmemcontrol")]
pvmemcontrol_devices: Option<(
Arc<PvmemcontrolBusDevice>,
Arc<Mutex<PvmemcontrolPciDevice>>,
)>,
// pvpanic device
pvpanic_device: Option<Arc<Mutex<devices::PvPanicDevice>>>,
@@ -1165,6 +1212,8 @@ impl DeviceManager {
virtio_mem_devices: Vec::new(),
#[cfg(target_arch = "aarch64")]
gpio_device: None,
#[cfg(feature = "pvmemcontrol")]
pvmemcontrol_devices: None,
pvpanic_device: None,
force_iommu,
io_uring_supported: None,
@@ -1183,7 +1232,7 @@ impl DeviceManager {
address_manager
.mmio_bus
.insert(
Arc::clone(&device_manager) as Arc<Mutex<dyn BusDevice>>,
Arc::clone(&device_manager) as Arc<dyn BusDeviceSync>,
acpi_address.0,
DEVICE_MANAGER_ACPI_SIZE as u64,
)
@@ -1226,7 +1275,7 @@ impl DeviceManager {
self.address_manager
.mmio_bus
.insert(
Arc::clone(&self.memory_manager) as Arc<Mutex<dyn BusDevice>>,
Arc::clone(&self.memory_manager) as Arc<dyn BusDeviceSync>,
acpi_address.0,
MEMORY_MANAGER_ACPI_SIZE as u64,
)
@@ -1268,7 +1317,7 @@ impl DeviceManager {
if let Some(tpm) = self.config.clone().lock().unwrap().tpm.as_ref() {
let tpm_dev = self.add_tpm_device(tpm.socket.clone())?;
self.bus_devices
.push(Arc::clone(&tpm_dev) as Arc<Mutex<dyn BusDevice>>)
.push(Arc::clone(&tpm_dev) as Arc<dyn BusDeviceSync>)
}
self.legacy_interrupt_manager = Some(legacy_interrupt_manager);
@@ -1278,6 +1327,17 @@ impl DeviceManager {
self.virtio_devices = virtio_devices;
// Add pvmemcontrol if required
#[cfg(feature = "pvmemcontrol")]
{
if self.config.lock().unwrap().pvmemcontrol.is_some() {
let (pvmemcontrol_bus_device, pvmemcontrol_pci_device) =
self.make_pvmemcontrol_device()?;
self.pvmemcontrol_devices =
Some((pvmemcontrol_bus_device, pvmemcontrol_pci_device));
}
}
if self.config.clone().lock().unwrap().pvpanic {
self.pvpanic_device = self.add_pvpanic_device()?;
}
@@ -1400,11 +1460,11 @@ impl DeviceManager {
#[cfg(target_arch = "x86_64")]
if let Some(pci_config_io) = segment.pci_config_io.as_ref() {
self.bus_devices
.push(Arc::clone(pci_config_io) as Arc<Mutex<dyn BusDevice>>);
.push(Arc::clone(pci_config_io) as Arc<dyn BusDeviceSync>);
}
self.bus_devices
.push(Arc::clone(&segment.pci_config_mmio) as Arc<Mutex<dyn BusDevice>>);
.push(Arc::clone(&segment.pci_config_mmio) as Arc<dyn BusDeviceSync>);
}
Ok(())
@@ -1489,7 +1549,7 @@ impl DeviceManager {
.map_err(DeviceManagerError::BusError)?;
self.bus_devices
.push(Arc::clone(&interrupt_controller) as Arc<Mutex<dyn BusDevice>>);
.push(Arc::clone(&interrupt_controller) as Arc<dyn BusDeviceSync>);
// Fill the device tree with a new node. In case of restore, we
// know there is nothing to do, so we can simply override the
@@ -1521,7 +1581,7 @@ impl DeviceManager {
)));
self.bus_devices
.push(Arc::clone(&shutdown_device) as Arc<Mutex<dyn BusDevice>>);
.push(Arc::clone(&shutdown_device) as Arc<dyn BusDeviceSync>);
#[cfg(target_arch = "x86_64")]
{
@@ -1584,12 +1644,12 @@ impl DeviceManager {
)
.map_err(DeviceManagerError::BusError)?;
self.bus_devices
.push(Arc::clone(&ged_device) as Arc<Mutex<dyn BusDevice>>);
.push(Arc::clone(&ged_device) as Arc<dyn BusDeviceSync>);
let pm_timer_device = Arc::new(Mutex::new(devices::AcpiPmTimerDevice::new()));
self.bus_devices
.push(Arc::clone(&pm_timer_device) as Arc<Mutex<dyn BusDevice>>);
.push(Arc::clone(&pm_timer_device) as Arc<dyn BusDeviceSync>);
#[cfg(target_arch = "x86_64")]
{
@@ -1629,7 +1689,7 @@ impl DeviceManager {
)));
self.bus_devices
.push(Arc::clone(&i8042) as Arc<Mutex<dyn BusDevice>>);
.push(Arc::clone(&i8042) as Arc<dyn BusDeviceSync>);
self.address_manager
.io_bus
@@ -1657,7 +1717,7 @@ impl DeviceManager {
)));
self.bus_devices
.push(Arc::clone(&cmos) as Arc<Mutex<dyn BusDevice>>);
.push(Arc::clone(&cmos) as Arc<dyn BusDeviceSync>);
self.address_manager
.io_bus
@@ -1667,7 +1727,7 @@ impl DeviceManager {
let fwdebug = Arc::new(Mutex::new(devices::legacy::FwDebugDevice::new()));
self.bus_devices
.push(Arc::clone(&fwdebug) as Arc<Mutex<dyn BusDevice>>);
.push(Arc::clone(&fwdebug) as Arc<dyn BusDeviceSync>);
self.address_manager
.io_bus
@@ -1678,7 +1738,7 @@ impl DeviceManager {
// 0x80 debug port
let debug_port = Arc::new(Mutex::new(devices::legacy::DebugPort::new(self.timestamp)));
self.bus_devices
.push(Arc::clone(&debug_port) as Arc<Mutex<dyn BusDevice>>);
.push(Arc::clone(&debug_port) as Arc<dyn BusDeviceSync>);
self.address_manager
.io_bus
.insert(debug_port, 0x80, 0x1)
@@ -1710,7 +1770,7 @@ impl DeviceManager {
let rtc_device = Arc::new(Mutex::new(devices::legacy::Rtc::new(interrupt_group)));
self.bus_devices
.push(Arc::clone(&rtc_device) as Arc<Mutex<dyn BusDevice>>);
.push(Arc::clone(&rtc_device) as Arc<dyn BusDeviceSync>);
let addr = arch::layout::LEGACY_RTC_MAPPED_IO_START;
@@ -1752,7 +1812,7 @@ impl DeviceManager {
)));
self.bus_devices
.push(Arc::clone(&gpio_device) as Arc<Mutex<dyn BusDevice>>);
.push(Arc::clone(&gpio_device) as Arc<dyn BusDeviceSync>);
let addr = arch::layout::LEGACY_GPIO_MAPPED_IO_START;
@@ -1802,7 +1862,7 @@ impl DeviceManager {
.unwrap_or(debug_console::DEFAULT_PORT);
self.bus_devices
.push(Arc::clone(&debug_console) as Arc<Mutex<dyn BusDevice>>);
.push(Arc::clone(&debug_console) as Arc<dyn BusDeviceSync>);
self.address_manager
.allocator
@@ -1853,7 +1913,7 @@ impl DeviceManager {
)));
self.bus_devices
.push(Arc::clone(&serial) as Arc<Mutex<dyn BusDevice>>);
.push(Arc::clone(&serial) as Arc<dyn BusDeviceSync>);
self.address_manager
.allocator
@@ -1910,7 +1970,7 @@ impl DeviceManager {
)));
self.bus_devices
.push(Arc::clone(&serial) as Arc<Mutex<dyn BusDevice>>);
.push(Arc::clone(&serial) as Arc<dyn BusDeviceSync>);
let addr = arch::layout::LEGACY_SERIAL_MAPPED_IO_START;
@@ -3048,6 +3108,48 @@ impl DeviceManager {
Ok(devices)
}
#[cfg(feature = "pvmemcontrol")]
fn make_pvmemcontrol_device(
&mut self,
) -> DeviceManagerResult<(
Arc<PvmemcontrolBusDevice>,
Arc<Mutex<PvmemcontrolPciDevice>>,
)> {
let id = String::from(PVMEMCONTROL_DEVICE_NAME);
let pci_segment_id = 0x0_u16;
let (pci_segment_id, pci_device_bdf, resources) =
self.pci_resources(&id, pci_segment_id)?;
info!("Creating pvmemcontrol device: id = {}", id);
let (pvmemcontrol_pci_device, pvmemcontrol_bus_device) =
devices::pvmemcontrol::PvmemcontrolDevice::make_device(
id.clone(),
self.memory_manager.lock().unwrap().guest_memory(),
);
let pvmemcontrol_pci_device = Arc::new(Mutex::new(pvmemcontrol_pci_device));
let pvmemcontrol_bus_device = Arc::new(pvmemcontrol_bus_device);
let new_resources = self.add_pci_device(
pvmemcontrol_bus_device.clone(),
pvmemcontrol_pci_device.clone(),
pci_segment_id,
pci_device_bdf,
resources,
)?;
let mut node = device_node!(id, pvmemcontrol_pci_device);
node.resources = new_resources;
node.pci_bdf = Some(pci_device_bdf);
node.pci_device_handle = None;
self.device_tree.lock().unwrap().insert(id, node);
Ok((pvmemcontrol_bus_device, pvmemcontrol_pci_device))
}
fn make_virtio_balloon_devices(&mut self) -> DeviceManagerResult<Vec<MetaVirtioDevice>> {
let mut devices = Vec::new();
@@ -3412,7 +3514,7 @@ impl DeviceManager {
fn add_pci_device(
&mut self,
bus_device: Arc<Mutex<dyn BusDevice>>,
bus_device: Arc<dyn BusDeviceSync>,
pci_device: Arc<Mutex<dyn PciDevice>>,
segment_id: u16,
bdf: PciBdf,
@@ -3639,15 +3741,22 @@ impl DeviceManager {
// Create the AccessPlatform trait from the implementation IommuMapping.
// This will provide address translation for any virtio device sitting
// behind a vIOMMU.
let access_platform: Option<Arc<dyn AccessPlatform>> = if let Some(mapping) = iommu_mapping
{
Some(Arc::new(AccessPlatformMapping::new(
let mut access_platform: Option<Arc<dyn AccessPlatform>> = None;
if let Some(mapping) = iommu_mapping {
access_platform = Some(Arc::new(AccessPlatformMapping::new(
pci_device_bdf.into(),
mapping.clone(),
)))
} else {
None
};
)));
}
// If SEV-SNP is enabled create the AccessPlatform from SevSnpPageAccessProxy
#[cfg(feature = "sev_snp")]
if self.config.lock().unwrap().is_sev_snp_enabled() {
access_platform = Some(Arc::new(SevSnpPageAccessProxy::new(
self.address_manager.vm.clone(),
)));
}
let memory = self.memory_manager.lock().unwrap().guest_memory();
@@ -4076,7 +4185,7 @@ impl DeviceManager {
(
Arc::clone(&vfio_pci_device) as Arc<Mutex<dyn PciDevice>>,
Arc::clone(&vfio_pci_device) as Arc<Mutex<dyn BusDevice>>,
Arc::clone(&vfio_pci_device) as Arc<dyn BusDeviceSync>,
None as Option<Arc<Mutex<dyn virtio_devices::VirtioDevice>>>,
false,
)
@@ -4108,7 +4217,7 @@ impl DeviceManager {
(
Arc::clone(&virtio_pci_device) as Arc<Mutex<dyn PciDevice>>,
Arc::clone(&virtio_pci_device) as Arc<Mutex<dyn BusDevice>>,
Arc::clone(&virtio_pci_device) as Arc<dyn BusDeviceSync>,
Some(dev.virtio_device()),
dev.dma_handler().is_some() && !iommu_attached,
)
@@ -4124,7 +4233,7 @@ impl DeviceManager {
(
Arc::clone(&vfio_user_pci_device) as Arc<Mutex<dyn PciDevice>>,
Arc::clone(&vfio_user_pci_device) as Arc<Mutex<dyn BusDevice>>,
Arc::clone(&vfio_user_pci_device) as Arc<dyn BusDeviceSync>,
None as Option<Arc<Mutex<dyn virtio_devices::VirtioDevice>>>,
true,
)

153
vmm/src/landlock.rs Normal file
View File

@@ -0,0 +1,153 @@
// Copyright © 2024 Microsoft Corporation
//
// SPDX-License-Identifier: Apache-2.0
#[cfg(test)]
use landlock::make_bitflags;
use landlock::{
path_beneath_rules, Access, AccessFs, BitFlags, Ruleset, RulesetAttr, RulesetCreated,
RulesetCreatedAttr, RulesetError, ABI,
};
use std::convert::TryFrom;
use std::io::Error as IoError;
use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum LandlockError {
/// All RulesetErrors from Landlock library are wrapped in this error
#[error("Error creating/adding/restricting ruleset: {0}")]
ManageRuleset(#[source] RulesetError),
/// Error opening path
#[error("Error opening path: {0}")]
OpenPath(#[source] IoError),
/// Invalid Landlock access
#[error("Invalid Landlock access: {0}")]
InvalidLandlockAccess(String),
/// Invalid Path
#[error("Invalid path")]
InvalidPath,
}
// https://docs.rs/landlock/latest/landlock/enum.ABI.html for more info on ABI
static ABI: ABI = ABI::V3;
pub(crate) struct LandlockAccess {
access: BitFlags<AccessFs>,
}
impl TryFrom<&str> for LandlockAccess {
type Error = LandlockError;
fn try_from(s: &str) -> Result<LandlockAccess, LandlockError> {
if s.is_empty() {
return Err(LandlockError::InvalidLandlockAccess(
"Access cannot be empty".to_string(),
));
}
let mut access = BitFlags::<AccessFs>::empty();
for c in s.chars() {
match c {
'r' => access |= AccessFs::from_read(ABI),
'w' => access |= AccessFs::from_write(ABI),
_ => {
return Err(LandlockError::InvalidLandlockAccess(
format!("Invalid access: {c}").to_string(),
))
}
};
}
Ok(LandlockAccess { access })
}
}
pub struct Landlock {
ruleset: RulesetCreated,
}
impl Landlock {
pub fn new() -> Result<Landlock, LandlockError> {
let file_access = AccessFs::from_all(ABI);
let def_ruleset = Ruleset::default()
.handle_access(file_access)
.map_err(LandlockError::ManageRuleset)?;
// By default, rulesets are created in `BestEffort` mode. This lets Landlock
// to enable all the supported rules and silently ignore the unsupported ones.
let ruleset = def_ruleset.create().map_err(LandlockError::ManageRuleset)?;
Ok(Landlock { ruleset })
}
pub(crate) fn add_rule(
&mut self,
path: PathBuf,
access: BitFlags<AccessFs>,
) -> Result<(), LandlockError> {
// path_beneath_rules in landlock crate handles file and directory access rules.
// Incoming path/s are passed to path_beneath_rules, so that we don't
// have to worry about the type of the path.
let paths = vec![path.clone()];
let path_beneath_rules = path_beneath_rules(paths, access);
self.ruleset
.as_mut()
.add_rules(path_beneath_rules)
.map_err(LandlockError::ManageRuleset)?;
Ok(())
}
pub(crate) fn add_rule_with_access(
&mut self,
path: PathBuf,
access: &str,
) -> Result<(), LandlockError> {
self.add_rule(path, LandlockAccess::try_from(access)?.access)?;
Ok(())
}
pub fn restrict_self(self) -> Result<(), LandlockError> {
self.ruleset
.restrict_self()
.map_err(LandlockError::ManageRuleset)?;
Ok(())
}
}
#[test]
fn test_try_from_access() {
// These access rights could change in future versions of Landlock. Listing
// them here explicitly to raise their visibility during code reviews.
let read_access = make_bitflags!(AccessFs::{
Execute
| ReadFile
| ReadDir
});
let write_access = make_bitflags!(AccessFs::{
WriteFile
| RemoveDir
| RemoveFile
| MakeChar
| MakeDir
| MakeReg
| MakeSock
| MakeFifo
| MakeBlock
| MakeSym
| Refer
| Truncate
});
let landlock_access = LandlockAccess::try_from("rw").unwrap();
assert!(landlock_access.access == read_access | write_access);
let landlock_access = LandlockAccess::try_from("r").unwrap();
assert!(landlock_access.access == read_access);
let landlock_access = LandlockAccess::try_from("w").unwrap();
assert!(landlock_access.access == write_access);
assert!(LandlockAccess::try_from("").is_err());
}

View File

@@ -18,6 +18,7 @@ use crate::config::{
};
#[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
use crate::coredump::GuestDebuggable;
use crate::landlock::Landlock;
use crate::memory_manager::MemoryManager;
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
use crate::migration::get_vm_snapshot;
@@ -29,6 +30,7 @@ use anyhow::anyhow;
use api::dbus::{DBusApiOptions, DBusApiShutdownChannels};
use api::http::HttpApiHandle;
use console_devices::{pre_create_console_devices, ConsoleInfo};
use landlock::LandlockError;
use libc::{tcsetattr, termios, EFD_NONBLOCK, SIGINT, SIGTERM, TCSANOW};
use memory_manager::MemoryManagerSnapshotData;
use pci::PciBdf;
@@ -75,6 +77,7 @@ mod gdb;
#[cfg(feature = "igvm")]
mod igvm;
pub mod interrupt;
pub mod landlock;
pub mod memory_manager;
pub mod migration;
mod pci_segment;
@@ -195,6 +198,14 @@ pub enum Error {
#[error("Failed to join on threads: {0:?}")]
ThreadCleanup(std::boxed::Box<dyn std::any::Any + std::marker::Send>),
/// Cannot create Landlock object
#[error("Error creating landlock object: {0}")]
CreateLandlock(LandlockError),
/// Cannot apply landlock based sandboxing
#[error("Error applying landlock: {0}")]
ApplyLandlock(LandlockError),
}
pub type Result<T> = result::Result<T, Error>;
@@ -327,6 +338,7 @@ pub fn feature_list() -> Vec<String> {
pub fn start_event_monitor_thread(
mut monitor: event_monitor::Monitor,
seccomp_action: &SeccompAction,
landlock_enable: bool,
hypervisor_type: hypervisor::HypervisorType,
exit_event: EventFd,
) -> Result<thread::JoinHandle<Result<()>>> {
@@ -347,6 +359,17 @@ pub fn start_event_monitor_thread(
e
})?;
}
if landlock_enable {
Landlock::new()
.map_err(Error::CreateLandlock)?
.restrict_self()
.map_err(Error::ApplyLandlock)
.map_err(|e| {
error!("Error applying landlock to event monitor thread: {:?}", e);
exit_event.write(1).ok();
e
})?;
}
std::panic::catch_unwind(AssertUnwindSafe(move || {
while let Ok(event) = monitor.rx.recv() {
@@ -389,6 +412,7 @@ pub fn start_vmm_thread(
exit_event: EventFd,
seccomp_action: &SeccompAction,
hypervisor: Arc<dyn hypervisor::Hypervisor>,
landlock_enable: bool,
) -> Result<VmmThreadHandle> {
#[cfg(feature = "guest_debug")]
let gdb_hw_breakpoints = hypervisor.get_guest_debug_hw_bps();
@@ -429,7 +453,7 @@ pub fn start_vmm_thread(
exit_event,
)?;
vmm.setup_signal_handler()?;
vmm.setup_signal_handler(landlock_enable)?;
vmm.control_loop(
Rc::new(api_receiver),
@@ -466,6 +490,7 @@ pub fn start_vmm_thread(
seccomp_action,
exit_event,
hypervisor_type,
landlock_enable,
)?)
} else if let Some(http_fd) = http_fd {
Some(api::start_http_fd_thread(
@@ -475,6 +500,7 @@ pub fn start_vmm_thread(
seccomp_action,
exit_event,
hypervisor_type,
landlock_enable,
)?)
} else {
None
@@ -590,7 +616,7 @@ impl Vmm {
}
}
fn setup_signal_handler(&mut self) -> Result<()> {
fn setup_signal_handler(&mut self, landlock_enable: bool) -> Result<()> {
let signals = Signals::new(Self::HANDLED_SIGNALS);
match signals {
Ok(signals) => {
@@ -617,6 +643,21 @@ impl Vmm {
return;
}
}
if landlock_enable{
match Landlock::new() {
Ok(landlock) => {
let _ = landlock.restrict_self().map_err(Error::ApplyLandlock).map_err(|e| {
error!("Error applying Landlock to signal handler thread: {:?}", e);
exit_evt.write(1).ok();
});
}
Err(e) => {
error!("Error creating Landlock object: {:?}", e);
exit_evt.write(1).ok();
}
};
}
std::panic::catch_unwind(AssertUnwindSafe(|| {
Vmm::signal_handler(signals, original_termios_opt, &exit_evt);
}))
@@ -634,6 +675,7 @@ impl Vmm {
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn new(
vmm_version: VmmVersionInfo,
api_evt: EventFd,
@@ -724,6 +766,19 @@ impl Vmm {
MigratableError::MigrateReceive(anyhow!("Error creating console devices: {:?}", e))
})?);
if self
.vm_config
.as_ref()
.unwrap()
.lock()
.unwrap()
.landlock_enable
{
apply_landlock(self.vm_config.as_ref().unwrap().clone()).map_err(|e| {
MigratableError::MigrateReceive(anyhow!("Error applying landlock: {:?}", e))
})?;
}
let vm = Vm::create_hypervisor_vm(
&self.hypervisor,
#[cfg(feature = "tdx")]
@@ -848,9 +903,8 @@ impl Vmm {
// And then read the memory itself
memory_manager
.receive_memory_regions(&table, socket)
.map_err(|e| {
.inspect_err(|_| {
Response::error().write_to(socket).ok();
e
})?;
Response::ok().write_to(socket)?;
Ok(())
@@ -884,15 +938,10 @@ impl Vmm {
table.write_to(socket)?;
// And then the memory itself
vm.send_memory_regions(&table, socket)?;
let res = Response::read_from(socket)?;
if res.status() != Status::Ok {
warn!("Error during dirty memory migration");
Request::abandon().write_to(socket)?;
Response::read_from(socket).ok();
return Err(MigratableError::MigrateSend(anyhow!(
"Error during dirty memory migration"
)));
}
Response::read_from(socket)?.ok_or_abandon(
socket,
MigratableError::MigrateSend(anyhow!("Error during dirty memory migration")),
)?;
Ok(true)
}
@@ -911,15 +960,10 @@ impl Vmm {
// Start the migration
Request::start().write_to(&mut socket)?;
let res = Response::read_from(&mut socket)?;
if res.status() != Status::Ok {
warn!("Error starting migration");
Request::abandon().write_to(&mut socket)?;
Response::read_from(&mut socket).ok();
return Err(MigratableError::MigrateSend(anyhow!(
"Error starting migration"
)));
}
Response::read_from(&mut socket)?.ok_or_abandon(
&mut socket,
MigratableError::MigrateSend(anyhow!("Error starting migration")),
)?;
// Send config
let vm_config = vm.get_config();
@@ -966,15 +1010,10 @@ impl Vmm {
socket
.write_all(&config_data)
.map_err(MigratableError::MigrateSocket)?;
let res = Response::read_from(&mut socket)?;
if res.status() != Status::Ok {
warn!("Error during config migration");
Request::abandon().write_to(&mut socket)?;
Response::read_from(&mut socket).ok();
return Err(MigratableError::MigrateSend(anyhow!(
"Error during config migration"
)));
}
Response::read_from(&mut socket)?.ok_or_abandon(
&mut socket,
MigratableError::MigrateSend(anyhow!("Error during config migration")),
)?;
// Let every Migratable object know about the migration being started.
vm.start_migration()?;
@@ -994,15 +1033,10 @@ impl Vmm {
table.write_to(&mut socket)?;
// And then the memory itself
vm.send_memory_regions(&table, &mut socket)?;
let res = Response::read_from(&mut socket)?;
if res.status() != Status::Ok {
warn!("Error during memory migration");
Request::abandon().write_to(&mut socket)?;
Response::read_from(&mut socket).ok();
return Err(MigratableError::MigrateSend(anyhow!(
"Error during memory migration"
)));
}
Response::read_from(&mut socket)?.ok_or_abandon(
&mut socket,
MigratableError::MigrateSend(anyhow!("Error during dirty memory migration")),
)?;
// Try at most 5 passes of dirty memory sending
const MAX_DIRTY_MIGRATIONS: usize = 5;
@@ -1029,27 +1063,17 @@ impl Vmm {
socket
.write_all(&snapshot_data)
.map_err(MigratableError::MigrateSocket)?;
let res = Response::read_from(&mut socket)?;
if res.status() != Status::Ok {
warn!("Error during state migration");
Request::abandon().write_to(&mut socket)?;
Response::read_from(&mut socket).ok();
return Err(MigratableError::MigrateSend(anyhow!(
"Error during state migration"
)));
}
Response::read_from(&mut socket)?.ok_or_abandon(
&mut socket,
MigratableError::MigrateSend(anyhow!("Error during state migration")),
)?;
// Complete the migration
Request::complete().write_to(&mut socket)?;
let res = Response::read_from(&mut socket)?;
if res.status() != Status::Ok {
warn!("Error completing migration");
Request::abandon().write_to(&mut socket)?;
Response::read_from(&mut socket).ok();
return Err(MigratableError::MigrateSend(anyhow!(
"Error completing migration"
)));
}
Response::read_from(&mut socket)?.ok_or_abandon(
&mut socket,
MigratableError::MigrateSend(anyhow!("Error completing migration")),
)?;
info!("Migration complete");
// Let every Migratable object know about the migration being complete
@@ -1209,6 +1233,11 @@ impl Vmm {
}
}
fn apply_landlock(vm_config: Arc<Mutex<VmConfig>>) -> result::Result<(), LandlockError> {
vm_config.lock().unwrap().apply_landlock()?;
Ok(())
}
impl RequestHandler for Vmm {
fn vm_create(&mut self, config: Arc<Mutex<VmConfig>>) -> result::Result<(), VmError> {
// We only store the passed VM config.
@@ -1217,6 +1246,18 @@ impl RequestHandler for Vmm {
self.vm_config = Some(config);
self.console_info =
Some(pre_create_console_devices(self).map_err(VmError::CreateConsoleDevices)?);
if self
.vm_config
.as_ref()
.unwrap()
.lock()
.unwrap()
.landlock_enable
{
apply_landlock(self.vm_config.as_ref().unwrap().clone())
.map_err(VmError::ApplyLandlock)?;
}
Ok(())
} else {
Err(VmError::VmAlreadyCreated)
@@ -1400,6 +1441,18 @@ impl RequestHandler for Vmm {
)?;
self.vm = Some(vm);
if self
.vm_config
.as_ref()
.unwrap()
.lock()
.unwrap()
.landlock_enable
{
apply_landlock(self.vm_config.as_ref().unwrap().clone())
.map_err(VmError::ApplyLandlock)?;
}
// Now we can restore the rest of the VM.
if let Some(ref mut vm) = self.vm {
vm.restore()
@@ -2177,6 +2230,8 @@ mod unit_tests {
user_devices: None,
vdpa: None,
vsock: None,
#[cfg(feature = "pvmemcontrol")]
pvmemcontrol: None,
pvpanic: false,
iommu: false,
#[cfg(target_arch = "x86_64")]
@@ -2189,6 +2244,8 @@ mod unit_tests {
platform: None,
tpm: None,
preserved_fds: None,
landlock_enable: false,
landlock_rules: None,
}))
}

View File

@@ -18,7 +18,7 @@ use pci::{PciConfigIo, PCI_CONFIG_IO_PORT, PCI_CONFIG_IO_PORT_SIZE};
use std::sync::{Arc, Mutex};
use uuid::Uuid;
use vm_allocator::AddressAllocator;
use vm_device::BusDevice;
use vm_device::BusDeviceSync;
pub(crate) struct PciSegment {
pub(crate) id: u16,
@@ -70,7 +70,7 @@ impl PciSegment {
address_manager
.mmio_bus
.insert(
Arc::clone(&pci_config_mmio) as Arc<Mutex<dyn BusDevice>>,
Arc::clone(&pci_config_mmio) as Arc<dyn BusDeviceSync>,
mmio_config_address,
layout::PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
)

View File

@@ -153,106 +153,79 @@ use kvm::*;
// MSHV IOCTL code. This is unstable until the kernel code has been declared stable.
#[cfg(feature = "mshv")]
mod mshv {
pub const MSHV_GET_API_VERSION: u64 = 0xb800;
pub const MSHV_CREATE_VM: u64 = 0x4028_b801;
pub const MSHV_MAP_GUEST_MEMORY: u64 = 0x4020_b802;
pub const MSHV_UNMAP_GUEST_MEMORY: u64 = 0x4020_b803;
pub const MSHV_CREATE_VP: u64 = 0x4004_b804;
pub const MSHV_IRQFD: u64 = 0x4010_b80e;
pub const MSHV_IOEVENTFD: u64 = 0x4020_b80f;
pub const MSHV_SET_MSI_ROUTING: u64 = 0x4008_b811;
pub const MSHV_GET_VP_REGISTERS: u64 = 0xc010_b805;
pub const MSHV_SET_VP_REGISTERS: u64 = 0x4010_b806;
pub const MSHV_RUN_VP: u64 = 0x8100_b807;
pub const MSHV_GET_VP_STATE: u64 = 0xc010_b80a;
pub const MSHV_SET_VP_STATE: u64 = 0xc010_b80b;
pub const MSHV_SET_PARTITION_PROPERTY: u64 = 0x4010_b80c;
pub const MSHV_GET_PARTITION_PROPERTY: u64 = 0xc010_b80d;
pub const MSHV_GET_GPA_ACCESS_STATES: u64 = 0xc01c_b812;
pub const MSHV_VP_TRANSLATE_GVA: u64 = 0xc020_b80e;
pub const MSHV_CREATE_PARTITION: u64 = 0x4030_b801;
pub const MSHV_CREATE_DEVICE: u64 = 0xc00c_b813;
pub const MSHV_SET_DEVICE_ATTR: u64 = 0x4018_b814;
pub const MSHV_VP_REGISTER_INTERCEPT_RESULT: u64 = 0x4030_b817;
pub const MSHV_GET_VP_CPUID_VALUES: u64 = 0xc028_b81b;
pub const MSHV_MODIFY_GPA_HOST_ACCESS: u64 = 0x4018_b828;
pub const MSHV_IMPORT_ISOLATED_PAGES: u64 = 0x4010_b829;
pub const MSHV_COMPLETE_ISOLATED_IMPORT: u64 = 0x4d06_b830;
pub const MSHV_READ_GPA: u64 = 0xc020_b832;
pub const MSHV_WRITE_GPA: u64 = 0x4020_b833;
pub const MSHV_SEV_SNP_AP_CREATE: u64 = 0x4010_b834;
pub const MSHV_ISSUE_PSP_GUEST_REQUEST: u64 = 0x4010_b831;
pub const MSHV_ASSERT_INTERRUPT: u64 = 0x4018_b809;
pub const MSHV_ROOT_HVCALL: u64 = 0xc020_b835;
}
#[cfg(feature = "mshv")]
use mshv::*;
use hypervisor::mshv::mshv_ioctls::*;
#[cfg(feature = "mshv")]
fn create_vmm_ioctl_seccomp_rule_common_mshv() -> Result<Vec<SeccompRule>, BackendError> {
Ok(or![
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_GET_API_VERSION,)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_CREATE_VM)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_MAP_GUEST_MEMORY)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_UNMAP_GUEST_MEMORY)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_CREATE_VP)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_IRQFD)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_IOEVENTFD)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_SET_MSI_ROUTING)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_GET_VP_REGISTERS)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_SET_VP_REGISTERS)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_RUN_VP)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_ASSERT_INTERRUPT)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_GET_VP_STATE)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_SET_VP_STATE)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_CREATE_PARTITION())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_MAP_GUEST_MEMORY())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_UNMAP_GUEST_MEMORY())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_CREATE_VP())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_IRQFD())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_IOEVENTFD())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_SET_MSI_ROUTING())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_GET_VP_REGISTERS())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_SET_VP_REGISTERS())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_RUN_VP())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_GET_VP_STATE())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_SET_VP_STATE())?],
and![Cond::new(
1,
ArgLen::Dword,
Eq,
MSHV_SET_PARTITION_PROPERTY
MSHV_SET_PARTITION_PROPERTY()
)?],
and![Cond::new(
1,
ArgLen::Dword,
Eq,
MSHV_GET_PARTITION_PROPERTY
MSHV_GET_PARTITION_PROPERTY()
)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_GET_GPA_ACCESS_STATES)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_VP_TRANSLATE_GVA)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_CREATE_PARTITION)?],
and![Cond::new(
1,
ArgLen::Dword,
Eq,
MSHV_VP_REGISTER_INTERCEPT_RESULT
MSHV_GET_GPA_ACCESS_STATES()
)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_CREATE_DEVICE)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_SET_DEVICE_ATTR)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_GET_VP_CPUID_VALUES)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_VP_TRANSLATE_GVA())?],
and![Cond::new(
1,
ArgLen::Dword,
Eq,
MSHV_MODIFY_GPA_HOST_ACCESS
MSHV_VP_REGISTER_INTERCEPT_RESULT()
)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_IMPORT_ISOLATED_PAGES)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_CREATE_DEVICE())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_SET_DEVICE_ATTR())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_GET_VP_CPUID_VALUES())?],
and![Cond::new(
1,
ArgLen::Dword,
Eq,
MSHV_COMPLETE_ISOLATED_IMPORT
MSHV_MODIFY_GPA_HOST_ACCESS()
)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_READ_GPA)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_WRITE_GPA)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_SEV_SNP_AP_CREATE)?],
and![Cond::new(
1,
ArgLen::Dword,
Eq,
MSHV_ISSUE_PSP_GUEST_REQUEST
MSHV_IMPORT_ISOLATED_PAGES()
)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_ROOT_HVCALL)?],
and![Cond::new(
1,
ArgLen::Dword,
Eq,
MSHV_COMPLETE_ISOLATED_IMPORT()
)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_READ_GPA())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_WRITE_GPA())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_SEV_SNP_AP_CREATE())?],
and![Cond::new(
1,
ArgLen::Dword,
Eq,
MSHV_ISSUE_PSP_GUEST_REQUEST()
)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_ROOT_HVCALL())?],
])
}
@@ -502,9 +475,12 @@ fn signal_handler_thread_rules() -> Result<Vec<(i64, Vec<SeccompRule>)>, Backend
(libc::SYS_exit_group, vec![]),
(libc::SYS_futex, vec![]),
(libc::SYS_ioctl, create_signal_handler_ioctl_seccomp_rule()?),
(libc::SYS_landlock_create_ruleset, vec![]),
(libc::SYS_landlock_restrict_self, vec![]),
(libc::SYS_madvise, vec![]),
(libc::SYS_mmap, vec![]),
(libc::SYS_munmap, vec![]),
(libc::SYS_prctl, vec![]),
(libc::SYS_recvfrom, vec![]),
(libc::SYS_rt_sigprocmask, vec![]),
(libc::SYS_rt_sigreturn, vec![]),
@@ -607,6 +583,9 @@ fn vmm_thread_rules(
(libc::SYS_io_uring_setup, vec![]),
(libc::SYS_io_uring_register, vec![]),
(libc::SYS_kill, vec![]),
(libc::SYS_landlock_create_ruleset, vec![]),
(libc::SYS_landlock_add_rule, vec![]),
(libc::SYS_landlock_restrict_self, vec![]),
(libc::SYS_listen, vec![]),
(libc::SYS_lseek, vec![]),
(libc::SYS_madvise, vec![]),
@@ -708,33 +687,32 @@ fn create_vcpu_ioctl_seccomp_rule_kvm() -> Result<Vec<SeccompRule>, BackendError
#[cfg(feature = "mshv")]
fn create_vcpu_ioctl_seccomp_rule_mshv() -> Result<Vec<SeccompRule>, BackendError> {
Ok(or![
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_SET_MSI_ROUTING)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_IOEVENTFD)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_IRQFD)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_RUN_VP)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_GET_VP_REGISTERS)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_SET_VP_REGISTERS)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_MAP_GUEST_MEMORY)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_UNMAP_GUEST_MEMORY)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_ASSERT_INTERRUPT)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_VP_TRANSLATE_GVA)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_GET_VP_CPUID_VALUES)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_SET_MSI_ROUTING())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_IOEVENTFD())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_IRQFD())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_RUN_VP())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_GET_VP_REGISTERS())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_SET_VP_REGISTERS())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_MAP_GUEST_MEMORY())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_UNMAP_GUEST_MEMORY())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_VP_TRANSLATE_GVA())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_GET_VP_CPUID_VALUES())?],
and![Cond::new(
1,
ArgLen::Dword,
Eq,
MSHV_MODIFY_GPA_HOST_ACCESS
MSHV_MODIFY_GPA_HOST_ACCESS()
)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_READ_GPA)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_WRITE_GPA)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_SEV_SNP_AP_CREATE)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_READ_GPA())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_WRITE_GPA())?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_SEV_SNP_AP_CREATE())?],
and![Cond::new(
1,
ArgLen::Dword,
Eq,
MSHV_ISSUE_PSP_GUEST_REQUEST
MSHV_ISSUE_PSP_GUEST_REQUEST()
)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_ROOT_HVCALL)?],
and![Cond::new(1, ArgLen::Dword, Eq, MSHV_ROOT_HVCALL())?],
])
}
@@ -845,10 +823,13 @@ fn http_api_thread_rules() -> Result<Vec<(i64, Vec<SeccompRule>)>, BackendError>
(libc::SYS_futex, vec![]),
(libc::SYS_getrandom, vec![]),
(libc::SYS_ioctl, create_api_ioctl_seccomp_rule()?),
(libc::SYS_landlock_create_ruleset, vec![]),
(libc::SYS_landlock_restrict_self, vec![]),
(libc::SYS_madvise, vec![]),
(libc::SYS_mmap, vec![]),
(libc::SYS_mprotect, vec![]),
(libc::SYS_munmap, vec![]),
(libc::SYS_prctl, vec![]),
(libc::SYS_recvfrom, vec![]),
(libc::SYS_recvmsg, vec![]),
(libc::SYS_sched_yield, vec![]),
@@ -898,9 +879,13 @@ fn dbus_api_thread_rules() -> Result<Vec<(i64, Vec<SeccompRule>)>, BackendError>
fn event_monitor_thread_rules() -> Result<Vec<(i64, Vec<SeccompRule>)>, BackendError> {
Ok(vec![
(libc::SYS_brk, vec![]),
(libc::SYS_close, vec![]),
(libc::SYS_futex, vec![]),
(libc::SYS_landlock_create_ruleset, vec![]),
(libc::SYS_landlock_restrict_self, vec![]),
(libc::SYS_mmap, vec![]),
(libc::SYS_munmap, vec![]),
(libc::SYS_prctl, vec![]),
(libc::SYS_sched_yield, vec![]),
(libc::SYS_write, vec![]),
])

View File

@@ -28,6 +28,7 @@ use crate::device_tree::DeviceTree;
use crate::gdb::{Debuggable, DebuggableError, GdbRequestPayload, GdbResponsePayload};
#[cfg(feature = "igvm")]
use crate::igvm::igvm_loader;
use crate::landlock::LandlockError;
use crate::memory_manager::{
Error as MemoryManagerError, MemoryManager, MemoryManagerSnapshotData,
};
@@ -92,7 +93,7 @@ use vm_memory::{Address, ByteValued, GuestMemoryRegion, ReadVolatile};
use vm_memory::{
Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic, WriteVolatile,
};
use vm_migration::protocol::{Request, Response, Status};
use vm_migration::protocol::{Request, Response};
use vm_migration::{
protocol::MemoryRangeTable, snapshot_from_id, Migratable, MigratableError, Pausable, Snapshot,
Snapshottable, Transportable,
@@ -122,6 +123,9 @@ pub enum Error {
#[error("Cannot load the kernel command line in memory: {0}")]
LoadCmdLine(#[source] linux_loader::loader::Error),
#[error("Failed to apply landlock config during vm_create: {0}")]
ApplyLandlock(#[source] LandlockError),
#[error("Cannot modify the kernel command line: {0}")]
CmdLineInsertStr(#[source] linux_loader::cmdline::Error),
@@ -2237,15 +2241,10 @@ impl Vm {
MigratableError::MigrateSend(anyhow!("Error sending memory fd: {}", e))
})?;
let res = Response::read_from(socket)?;
if res.status() != Status::Ok {
warn!("Error during memory fd migration");
Request::abandon().write_to(socket)?;
Response::read_from(socket).ok();
return Err(MigratableError::MigrateSend(anyhow!(
"Error during memory fd migration"
)));
}
Response::read_from(socket)?.ok_or_abandon(
socket,
MigratableError::MigrateSend(anyhow!("Error during memory fd migration")),
)?;
}
Ok(())

View File

@@ -2,11 +2,20 @@
//
// SPDX-License-Identifier: Apache-2.0
//
use crate::{landlock::LandlockError, Landlock};
use net_util::MacAddr;
use serde::{Deserialize, Serialize};
use std::{net::Ipv4Addr, path::PathBuf};
use std::{fs, net::Ipv4Addr, path::PathBuf, result};
use virtio_devices::RateLimiterConfig;
pub type LandlockResult<T> = result::Result<T, LandlockError>;
/// Trait to apply Landlock on VmConfig elements
pub(crate) trait ApplyLandlock {
/// Apply Landlock rules to file paths
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>;
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct CpuAffinity {
pub vcpu: u8,
@@ -132,6 +141,15 @@ pub struct MemoryZoneConfig {
pub prefault: bool,
}
impl ApplyLandlock for MemoryZoneConfig {
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
if let Some(file) = &self.file {
landlock.add_rule_with_access(file.to_path_buf(), "rw")?;
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
pub enum HotplugMethod {
#[default]
@@ -245,6 +263,15 @@ pub struct DiskConfig {
pub queue_affinity: Option<Vec<VirtQueueAffinity>>,
}
impl ApplyLandlock for DiskConfig {
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
if let Some(path) = &self.path {
landlock.add_rule_with_access(path.to_path_buf(), "rw")?;
}
Ok(())
}
}
pub const DEFAULT_DISK_NUM_QUEUES: usize = 1;
pub fn default_diskconfig_num_queues() -> usize {
@@ -378,6 +405,14 @@ impl Default for RngConfig {
}
}
impl ApplyLandlock for RngConfig {
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
// Rng Path only need read access
landlock.add_rule_with_access(self.src.to_path_buf(), "r")?;
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct BalloonConfig {
pub size: u64,
@@ -389,6 +424,10 @@ pub struct BalloonConfig {
pub free_page_reporting: bool,
}
#[cfg(feature = "pvmemcontrol")]
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
pub struct PvmemcontrolConfig {}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct FsConfig {
pub tag: String,
@@ -411,6 +450,13 @@ pub fn default_fsconfig_queue_size() -> u16 {
1024
}
impl ApplyLandlock for FsConfig {
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct PmemConfig {
pub file: PathBuf,
@@ -426,6 +472,13 @@ pub struct PmemConfig {
pub pci_segment: u16,
}
impl ApplyLandlock for PmemConfig {
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
landlock.add_rule_with_access(self.file.to_path_buf(), "rw")?;
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub enum ConsoleOutputMode {
Off,
@@ -450,6 +503,18 @@ pub fn default_consoleconfig_file() -> Option<PathBuf> {
None
}
impl ApplyLandlock for ConsoleConfig {
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
if let Some(file) = &self.file {
landlock.add_rule_with_access(file.to_path_buf(), "rw")?;
}
if let Some(socket) = &self.socket {
landlock.add_rule_with_access(socket.to_path_buf(), "rw")?;
}
Ok(())
}
}
#[cfg(target_arch = "x86_64")]
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct DebugConsoleConfig {
@@ -470,6 +535,15 @@ impl Default for DebugConsoleConfig {
}
}
}
#[cfg(target_arch = "x86_64")]
impl ApplyLandlock for DebugConsoleConfig {
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
if let Some(file) = &self.file {
landlock.add_rule_with_access(file.to_path_buf(), "rw")?;
}
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct DeviceConfig {
@@ -484,6 +558,22 @@ pub struct DeviceConfig {
pub x_nv_gpudirect_clique: Option<u8>,
}
impl ApplyLandlock for DeviceConfig {
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
let device_path = fs::read_link(self.path.as_path()).map_err(LandlockError::OpenPath)?;
let iommu_group = device_path.file_name();
let iommu_group_str = iommu_group
.ok_or(LandlockError::InvalidPath)?
.to_str()
.ok_or(LandlockError::InvalidPath)?;
let vfio_group_path = "/dev/vfio/".to_owned() + iommu_group_str;
landlock.add_rule_with_access(vfio_group_path.into(), "rw")?;
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct UserDeviceConfig {
pub socket: PathBuf,
@@ -493,6 +583,13 @@ pub struct UserDeviceConfig {
pub pci_segment: u16,
}
impl ApplyLandlock for UserDeviceConfig {
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct VdpaConfig {
pub path: PathBuf,
@@ -510,6 +607,13 @@ pub fn default_vdpaconfig_num_queues() -> usize {
1
}
impl ApplyLandlock for VdpaConfig {
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
landlock.add_rule_with_access(self.path.to_path_buf(), "rw")?;
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct VsockConfig {
pub cid: u32,
@@ -522,6 +626,13 @@ pub struct VsockConfig {
pub pci_segment: u16,
}
impl ApplyLandlock for VsockConfig {
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
Ok(())
}
}
#[cfg(target_arch = "x86_64")]
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct SgxEpcConfig {
@@ -575,6 +686,30 @@ pub struct PayloadConfig {
pub host_data: Option<String>,
}
impl ApplyLandlock for PayloadConfig {
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
// Payload only needs read access
if let Some(firmware) = &self.firmware {
landlock.add_rule_with_access(firmware.to_path_buf(), "r")?;
}
if let Some(kernel) = &self.kernel {
landlock.add_rule_with_access(kernel.to_path_buf(), "r")?;
}
if let Some(initramfs) = &self.initramfs {
landlock.add_rule_with_access(initramfs.to_path_buf(), "r")?;
}
#[cfg(feature = "igvm")]
if let Some(igvm) = &self.igvm {
landlock.add_rule_with_access(igvm.to_path_buf(), "r")?;
}
Ok(())
}
}
pub fn default_serial() -> ConsoleConfig {
ConsoleConfig {
file: None,
@@ -598,6 +733,26 @@ pub struct TpmConfig {
pub socket: PathBuf,
}
impl ApplyLandlock for TpmConfig {
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct LandlockConfig {
pub path: PathBuf,
pub access: String,
}
impl ApplyLandlock for LandlockConfig {
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
landlock.add_rule_with_access(self.path.to_path_buf(), self.access.clone().as_str())?;
Ok(())
}
}
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct VmConfig {
#[serde(default)]
@@ -624,6 +779,9 @@ pub struct VmConfig {
pub user_devices: Option<Vec<UserDeviceConfig>>,
pub vdpa: Option<Vec<VdpaConfig>>,
pub vsock: Option<VsockConfig>,
#[cfg(feature = "pvmemcontrol")]
#[serde(default)]
pub pvmemcontrol: Option<PvmemcontrolConfig>,
#[serde(default)]
pub pvpanic: bool,
#[serde(default)]
@@ -645,4 +803,94 @@ pub struct VmConfig {
// valid, and will be closed when the holding VmConfig instance is destroyed.
#[serde(skip)]
pub preserved_fds: Option<Vec<i32>>,
#[serde(default)]
pub landlock_enable: bool,
pub landlock_rules: Option<Vec<LandlockConfig>>,
}
impl VmConfig {
pub(crate) fn apply_landlock(&self) -> LandlockResult<()> {
let mut landlock = Landlock::new()?;
if let Some(mem_zones) = &self.memory.zones {
for zone in mem_zones.iter() {
zone.apply_landlock(&mut landlock)?;
}
}
let disks = &self.disks;
if let Some(disks) = disks {
for disk in disks.iter() {
disk.apply_landlock(&mut landlock)?;
}
}
self.rng.apply_landlock(&mut landlock)?;
if let Some(fs_configs) = &self.fs {
for fs_config in fs_configs.iter() {
fs_config.apply_landlock(&mut landlock)?;
}
}
if let Some(pmem_configs) = &self.pmem {
for pmem_config in pmem_configs.iter() {
pmem_config.apply_landlock(&mut landlock)?;
}
}
self.console.apply_landlock(&mut landlock)?;
self.serial.apply_landlock(&mut landlock)?;
#[cfg(target_arch = "x86_64")]
{
self.debug_console.apply_landlock(&mut landlock)?;
}
if let Some(devices) = &self.devices {
landlock.add_rule_with_access("/dev/vfio/vfio".into(), "rw")?;
for device in devices.iter() {
device.apply_landlock(&mut landlock)?;
}
}
if let Some(user_devices) = &self.user_devices {
for user_devices in user_devices.iter() {
user_devices.apply_landlock(&mut landlock)?;
}
}
if let Some(vdpa_configs) = &self.vdpa {
for vdpa_config in vdpa_configs.iter() {
vdpa_config.apply_landlock(&mut landlock)?;
}
}
if let Some(vsock_config) = &self.vsock {
vsock_config.apply_landlock(&mut landlock)?;
}
if let Some(payload) = &self.payload {
payload.apply_landlock(&mut landlock)?;
}
if let Some(tpm_config) = &self.tpm {
tpm_config.apply_landlock(&mut landlock)?;
}
if self.net.is_some() {
landlock.add_rule_with_access("/dev/net/tun".into(), "rw")?;
}
if let Some(landlock_rules) = &self.landlock_rules {
for landlock_rule in landlock_rules.iter() {
landlock_rule.apply_landlock(&mut landlock)?;
}
}
landlock.restrict_self()?;
Ok(())
}
}