Compare commits

...

185 Commits
v44.0 ... v46.0

Author SHA1 Message Date
Bo Chen
c9a39cf5b5 build: Release v46.0
Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-05-23 18:29:42 +00:00
dependabot[bot]
f6326df68b build: Bump cc from 1.0.99 to 1.2.23
Bumps [cc](https://github.com/rust-lang/cc-rs) from 1.0.99 to 1.2.23.
- [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.2.23)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-22 23:53:39 +00:00
Bo Chen
7571e93a69 vmm: Deprecate SGX support
This commit adds the warning to deprecate the SGX support with the
intention to remove the support from code base in two release cycles.

See: #6960

Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-05-22 17:41:32 +00:00
Philipp Schuster
ab6e1bd2d8 misc: ch-remote: streamline #[source] and Error impl
This streamlines the Error implementation in the Cloud Hypervisor code
base to match the remaining parts so that everything follows the agreed
conventions. These are leftovers missed in the previous commits.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-22 15:13:27 +00:00
Philipp Schuster
517ea00bd9 misc: vmm/api: streamline #[source] and Error impl
This streamlines the Error implementation in the Cloud Hypervisor code
base to match the remaining parts so that everything follows the agreed
conventions. These are leftovers missed in the previous commits.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-22 15:13:27 +00:00
Philipp Schuster
ea6d5a04fa misc: net_util: streamline #[source] and Error impl
This streamlines the Error implementation in the Cloud Hypervisor code
base to match the remaining parts so that everything follows the agreed
conventions. These are leftovers missed in the previous commits.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-22 12:17:13 +00:00
Philipp Schuster
799336459d misc: performance-metrics: streamline #[source] and Error impl
This streamlines the Error implementation in the Cloud Hypervisor code
base to match the remaining parts so that everything follows the agreed
conventions. These are leftovers missed in the previous commits.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-22 12:17:13 +00:00
Philipp Schuster
f934e142ba misc: rate_limiter: streamline #[source] and Error impl
This streamlines the Error implementation in the Cloud Hypervisor code
base to match the remaining parts so that everything follows the agreed
conventions. These are leftovers missed in the previous commits.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-22 12:17:13 +00:00
Philipp Schuster
0cde3df44a misc: test_infra: streamline #[source] and Error impl
This streamlines the Error implementation in the Cloud Hypervisor code
base to match the remaining parts so that everything follows the agreed
conventions. These are leftovers missed in the previous commits.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-22 12:17:13 +00:00
Philipp Schuster
7585e16f9d misc: option_parser: streamline #[source] and Error impl
This streamlines the Error implementation in the Cloud Hypervisor code
base to match the remaining parts so that everything follows the agreed
conventions. These are leftovers missed in the previous commits.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-22 12:17:13 +00:00
Philipp Schuster
ab575a54b9 misc: devices: streamline #[source] and Error impl
This streamlines the Error implementation in the Cloud Hypervisor code
base to match the remaining parts so that everything follows the agreed
conventions. These are leftovers missed in the previous commits.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-22 12:17:13 +00:00
Philipp Schuster
d2ca7b0e87 misc: arch: streamline #[source] and Error impl
This streamlines the Error implementation in the Cloud Hypervisor code
base to match the remaining parts so that everything follows the agreed
conventions. These are leftovers missed in the previous commits.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-22 12:17:13 +00:00
Philipp Schuster
dd9bce31e8 misc: block: streamline #[source] and Error impl
This streamlines the Error implementation in the Cloud Hypervisor code
base to match the remaining parts so that everything follows the agreed
conventions. These are leftovers missed in the previous commits.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-22 12:17:13 +00:00
dependabot[bot]
a6370b74d9 build: Bump signal-hook from 0.3.17 to 0.3.18
Bumps [signal-hook](https://github.com/vorner/signal-hook) from 0.3.17 to 0.3.18.
- [Changelog](https://github.com/vorner/signal-hook/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vorner/signal-hook/compare/v0.3.17...v0.3.18)

---
updated-dependencies:
- dependency-name: signal-hook
  dependency-version: 0.3.18
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-22 00:34:40 +00:00
Thomas Prescher
d172a5dddb arch: fix extended topology enumeration leaf
When booting a Linux guest in SMP configuration,
the following kernel warning can be observed:

[Firmware Bug]: CPUID leaf 0xb subleaf 1 APIC ID mismatch 1 != 0

The reason is that we announce the presence of the extended topology
leaf, but fail to announce the x2apic ID in EDX.

Signed-off-by: Thomas Prescher <thomas.prescher@cyberus-technology.de>
On-behalf-of: SAP thomas.prescher@sap.com
2025-05-21 23:30:13 +00:00
Thomas Prescher
d46517559b vmm: don't allow resizing to 0 vCPUs
No sane guest OS will allow hotplugging all cpus.
However, the REST API currently allows specifying
`ch-remote resize --cpus 0`.

On Linux, we can then observe the following error in the kernel log:

processor cpu0: Offline failed.

Subsequent resize commands via ch-remote will then fail with
VcpuPendingRemovedVcpu because the removal of cpu0 was never
successful.

Fix this by disallowing resizing to zero vCPUs.

Signed-off-by: Thomas Prescher <thomas.prescher@cyberus-technology.de>
On-behalf-of: SAP thomas.prescher@sap.com
2025-05-21 15:28:22 +00:00
Paolo Bonzini
0463f4f156 vmm: use MmapRegion::bitmap() directly
For use in QEMU, I would like GuestMemoryRegion to return a BitmapSlice
instead of a &Bitmap.  This adds some flexibility that QEMU needs in
order to support a single global dirty bitmap that is sliced by the
various GuestMemoryRegions.

However, this removes access to the methods of AtomicBitmap, and in
particular reset() and get_and_reset().  Fortunately, cloud-hypervisor
always uses GuestMemoryMmap, and therefore `region` is known to be a
&GuestRegionMmap.  Dereferencing it returns the MmapRegion to which the
bitmap is attached, thus calling MmapRegion::bitmap(); this has the
same effect as `<GuestRegionMmap as GuestRegion>::bitmap()`, and works
both with or without https://github.com/rust-vmm/vm-memory/pull/324.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2025-05-21 13:00:07 +00:00
Philipp Schuster
fff62d9302 misc: vmm: streamline #[source] and Error
This streamlines the code base to follow best practices for
error handling in Rust: Each error struct implements
std::error::Error (most due via thiserror::Error derive macro)
and sets its source accordingly.

This allows future work that nicely prints the error chains,
for example.

So far, the convention is that each error prints its
sub error as part of its Display::fmt() impl.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-21 09:09:30 +00:00
Philipp Schuster
5db92f79bf misc: arch: streamline #[source] and Error
This streamlines the code base to follow best practices for
error handling in Rust: Each error struct implements
std::error::Error (most due via thiserror::Error derive macro)
and sets its source accordingly.

This allows future work that nicely prints the error chains,
for example.

So far, the convention is that each error prints its
sub error as part of its Display::fmt() impl.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-21 09:09:30 +00:00
Philipp Schuster
06f3049d24 misc: arch/x86_64: streamline #[source] and Error
This streamlines the code base to follow best practices for
error handling in Rust: Each error struct implements
std::error::Error (most due via thiserror::Error derive macro)
and sets its source accordingly.

This allows future work that nicely prints the error chains,
for example.

So far, the convention is that each error prints its
sub error as part of its Display::fmt() impl.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-21 09:09:30 +00:00
Philipp Schuster
28e0a95450 misc: virtio-devices: streamline #[source] and Error
This streamlines the code base to follow best practices for
error handling in Rust: Each error struct implements
std::error::Error (most due via thiserror::Error derive macro)
and sets its source accordingly.

This allows future work that nicely prints the error chains,
for example.

So far, the convention is that each error prints its
sub error as part of its Display::fmt() impl.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-21 09:09:30 +00:00
Philipp Schuster
a615c809eb misc: vsock: streamline #[source] and Error
This streamlines the code base to follow best practices for
error handling in Rust: Each error struct implements
std::error::Error (most due via thiserror::Error derive macro)
and sets its source accordingly.

This allows future work that nicely prints the error chains,
for example.

So far, the convention is that each error prints its
sub error as part of its Display::fmt() impl.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-21 09:09:30 +00:00
Philipp Schuster
a3dcaedf7e misc: vhost_user_net: streamline #[source] and Error
This streamlines the code base to follow best practices for
error handling in Rust: Each error struct implements
std::error::Error (most due via thiserror::Error derive macro)
and sets its source accordingly.

This allows future work that nicely prints the error chains,
for example.

So far, the convention is that each error prints its
sub error as part of its Display::fmt() impl.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-21 09:09:30 +00:00
Philipp Schuster
eb0b14f70e misc: vhost_user_block: streamline #[source] and Error
This streamlines the code base to follow best practices for
error handling in Rust: Each error struct implements
std::error::Error (most due via thiserror::Error derive macro)
and sets its source accordingly.

This allows future work that nicely prints the error chains,
for example.

So far, the convention is that each error prints its
sub error as part of its Display::fmt() impl.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-21 09:09:30 +00:00
Philipp Schuster
80e66657cc misc: pci: streamline #[source] and Error
This streamlines the code base to follow best practices for
error handling in Rust: Each error struct implements
std::error::Error (most due via thiserror::Error derive macro)
and sets its source accordingly.

This allows future work that nicely prints the error chains,
for example.

So far, the convention is that each error prints its
sub error as part of its Display::fmt() impl.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-21 09:09:30 +00:00
Philipp Schuster
fd5cfb75f3 misc: net_util: streamline #[source] and Error
This streamlines the code base to follow best practices for
error handling in Rust: Each error struct implements
std::error::Error (most due via thiserror::Error derive macro)
and sets its source accordingly.

This allows future work that nicely prints the error chains,
for example.

So far, the convention is that each error prints its
sub error as part of its Display::fmt() impl.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-21 09:09:30 +00:00
Philipp Schuster
8696bc6604 misc: hypervisor: streamline #[source] and Error
This streamlines the code base to follow best practices for
error handling in Rust: Each error struct implements
std::error::Error (most due via thiserror::Error derive macro)
and sets its source accordingly.

This allows future work that nicely prints the error chains,
for example.

So far, the convention is that each error prints its
sub error as part of its Display::fmt() impl.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-21 09:09:30 +00:00
Philipp Schuster
93b599e59e misc: devices: streamline #[source] and Error
This streamlines the code base to follow best practices for
error handling in Rust: Each error struct implements
std::error::Error (most due via thiserror::Error derive macro)
and sets its source accordingly.

This allows future work that nicely prints the error chains,
for example.

So far, the convention is that each error prints its
sub error as part of its Display::fmt() impl.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-21 09:09:30 +00:00
Philipp Schuster
01761c2596 misc: block: streamline #[source] and Error
This streamlines the code base to follow best practices for
error handling in Rust: Each error struct implements
std::error::Error (most due via thiserror::Error derive macro)
and sets its source accordingly.

This allows future work that nicely prints the error chains,
for example.

So far, the convention is that each error prints its
sub error as part of its Display::fmt() impl.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-21 09:09:30 +00:00
Philipp Schuster
a212343908 misc: arch/riscv64: streamline #[source] and Error
This streamlines the code base to follow best practices for
error handling in Rust: Each error struct implements
std::error::Error (most due via thiserror::Error derive macro)
and sets its source accordingly.

This allows future work that nicely prints the error chains,
for example.

So far, the convention is that each error prints its
sub error as part of its Display::fmt() impl.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-21 09:09:30 +00:00
Philipp Schuster
d1a406143d misc: arch/aarch64: streamline #[source] and Error
This streamlines the code base to follow best practices for
error handling in Rust: Each error struct implements
std::error::Error (most due via thiserror::Error derive macro)
and sets its source accordingly.

This allows future work that nicely prints the error chains,
for example.

So far, the convention is that each error prints its
sub error as part of its Display::fmt() impl.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-21 09:09:30 +00:00
Philipp Schuster
38380198e1 misc: api_client: streamline #[source] and Error
This streamlines the code base to follow best practices for
error handling in Rust: Each error struct implements
std::error::Error (most due via thiserror::Error derive macro)
and sets its source accordingly.

This allows future work that nicely prints the error chains,
for example.

So far, the convention is that each error prints its
sub error as part of its Display::fmt() impl.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-21 09:09:30 +00:00
Philipp Schuster
0e40a50407 misc: option_parser: streamline #[source] and Error
This streamlines the code base to follow best practices for
error handling in Rust: Each error struct implements
std::error::Error (most due via thiserror::Error derive macro)
and sets its source accordingly.

This allows future work that nicely prints the error chains,
for example.

So far, the convention is that each error prints its
sub error as part of its Display::fmt() impl.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-21 09:09:30 +00:00
dependabot[bot]
262984f8fc build: Bump miniz_oxide from 0.8.0 to 0.8.8
Bumps [miniz_oxide](https://github.com/Frommi/miniz_oxide) from 0.8.0 to 0.8.8.
- [Changelog](https://github.com/Frommi/miniz_oxide/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Frommi/miniz_oxide/compare/0.8.0...0.8.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-21 00:11:41 +00:00
Gregory Anders
dce82a34d0 net_util: add support for IPv6 addresses on tap interfaces
Allow tap interfaces to be configured with an IPv6 address. The change
is fairly straightforward: we need to update the API types and CLI
parsing to accept either an IPv6 or IPv4 and then match on the IP
address type when the tap device is configured.

For IPv6 addresses, the netmask (prefix) must be provided at the same
time as the address itself (in the SIOCSIFADDR ioctl). They cannot be
configured separately. So we remove the separate "set_netmask" function
and convert "set_ip_addr" to also accept a netmask. For IPv4 addresses,
the IP address and netmask were already always set together, so this
should have no functional impact for users of IPv4 addresses.

Signed-off-by: Gregory Anders <ganders@cloudflare.com>
2025-05-20 16:41:04 +00:00
dependabot[bot]
1454d39b28 build: Bump igvm_defs from d062818 to 01daa63
Bumps [igvm_defs](https://github.com/microsoft/igvm) from `d062818` to `01daa63`.
- [Release notes](https://github.com/microsoft/igvm/releases)
- [Commits](d062818ffb...01daa631a5)

---
updated-dependencies:
- dependency-name: igvm_defs
  dependency-version: 01daa631a596459cb4de58505881007dd13d4410
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-20 01:28:42 +00:00
dependabot[bot]
2588fc9e42 build: Bump vfio-bindings from 21d06ce to 3d158a1
Bumps [vfio-bindings](https://github.com/rust-vmm/vfio) from `21d06ce` to `3d158a1`.
- [Release notes](https://github.com/rust-vmm/vfio/releases)
- [Commits](21d06ceb91...3d158a1446)

---
updated-dependencies:
- dependency-name: vfio-bindings
  dependency-version: 3d158a14460cac7ca3c99c2effa0a46880935cb0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-19 23:56:55 +00:00
Philipp Schuster
67793ca375 vmm: improve disk locking error message
This adds guidance on how to resolve the issue.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-19 15:13:01 +01:00
Philipp Schuster
96deca9dc9 vmm: streamline display format for DeviceManagerError
This format is also used elsewhere.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>

On-behalf-of: SAP <philipp.schuster@sap.com>
2025-05-16 11:42:01 +00:00
Philipp Schuster
78b0f68b21 vmm: Error for MemoryManagerError
Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>

On-behalf-of: SAP <philipp.schuster@sap.com>
2025-05-16 11:42:01 +00:00
Philipp Schuster
a007b750ff vmm: Error for PciDeviceError and PciRootError
Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>

On-behalf-of: SAP <philipp.schuster@sap.com>
2025-05-16 11:42:01 +00:00
Philipp Schuster
b2993fb2fa vmm: Error for vsock::unix::Error
Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-16 11:42:01 +00:00
Philipp Schuster
8d11bf7979 vmm: Error for DeviceManagerError
The DeviceManagerError type is among the types missing the Error trait
so far. To streamline the code and to simplify usage on higher levels
of this error type, this type now implements Display and Error.

As not all variant values are Error yet, `#[source]` is not everywhere
where it could be. This is done in the next commits.

The high level goal is: Enable future work to improve the error output
of cloud hypervisor.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-16 11:42:01 +00:00
Philipp Schuster
d4718a9bc8 tests: fix parallel rw disk access
The new locking behavior uncovered that unfortunate test situation:
Many tests running in parallel access the same disk image with
rw permissions. Luckily, none of the tests actually writes to
the disk. Therefore, we can set it to readonly=true. In case this
changes, the test needs to be moved to the sequential test module.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-16 08:07:32 +00:00
Philipp Schuster
05968f5c2c block: introduce advisory locks for disk image files
# What

This commit introduces file-based advisory locking for the files backing
up the block devices by using the fcntl() syscall with OFD locks. The
per-open-file-descriptor (OFD) locks are more robust than traditional
POSIX locks (F_SETLK) as they are not tied to process IDs and avoid
common issues in multithreaded or multi-fd scenarios [1]. Therefore,
we don't use `std::fs::File::try_lock()`, which is backed by F_SETLKW.

The locking mechanism is aware of the `readonly` property and allows
`n` readers or `1` writer (exclusive mode).

As the locks are advisory, multiple cloud-hypervisor processes can
prevent themselves from writing to the same file. However, this is not
a system-wide file-system level locking mechanism preventing to open()
a file.

The introduced new locking mechanism does not cover vhost-user devices.

# Why

To prevent misconfiguration and improve safety, it is good practice to
protect disk image files with a locking mechanism. Experience and common
best practices suggest that advisory locks are preferable over mandatory
locks due to better compatibility and fewer pitfalls (in fs space).

The introduced functionality is aligned with the approach taken by
QEMU [0], and is also recommended in [1].

# Implementation Details

We need to ensure that not only normal operation keeps working but also
state save/resume and live-migration. Especially for live migration,
it is crucial that the sender VMM releases the locks when the VM stops
so the receiver VMM can acquire them right after that.

Therefore, the locking and releasing happen directly on the block
device struct. The device manager knows all block devices and can
forward requests to these types.

Last but not least, this commit uses on explicit lock acquiring
but implicit lock releasing (FD close). It only explicitly releases
the locks where this integrates more smoothly into the existing
code.

# Testing

I tested
- normal operation
- state save/resume,
- device hot plugging,
- and live-migration
with read/shared and write/exclusive locks.

One can use the `fcntl-tool` to test if locks are actually acquired
or released [2].

# Links

[0] 825b96dbce/util/osdep.c (L266)
[1] https://apenwarr.ca/log/20101213
[2] https://crates.io/crates/fcntl-tool

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-16 08:07:32 +00:00
Philipp Schuster
71a36e0c69 block: add fcntl module for locking
This is a prerequisite for the next steps.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-16 08:07:32 +00:00
Philipp Schuster
2da5e10689 block: bind FD lifetime of DiskFile
As we can't use BorrowedFd, we should at least create a similar
safe alternative.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-16 08:07:32 +00:00
Philipp Schuster
a647d7863c block: enable to get a raw FD of each block device's DiskFile
This is a prerequisite for the next steps.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-16 08:07:32 +00:00
Philipp Schuster
a23d4b7cf2 block: fixing typo, increase clarity
Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-16 08:07:32 +00:00
Philipp Schuster
f3209e4f78 devices: silence IRQ debug!() spam
This simplifies debugging of running VMs.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-16 08:07:32 +00:00
Bo Chen
596d6453c5 vmm: Allow 'VFIO_IOMMU_MAP_DMA' ioctl from the vcpu worker thread
Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-05-15 20:11:48 +00:00
Bo Chen
1307d31ede pci: vfio: Report more information with failed vfio_dma_unmap
Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-05-15 20:11:48 +00:00
Bo Chen
2f21827430 pci: vfio: Update IOMMU mappings of MMIO regions with BAR reprogram
To support PCIe P2P between VFIO devices, we populate IOMMU mappings for
the non-emulated MMIO regions of all VFIO devices via
`VFIO_IOMMU_MAP_DMA` (f0c1f8d), but the patch did not properly update
the IOMMU mappings with BAR reprogramming.

Fixes: #7027

Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-05-15 20:11:48 +00:00
Bo Chen
8da7c13e26 pci: Handle pending BAR reprogramming for VFIO devices properly
The way how we handle PCI configuration space for vfio and vfio-user
devices are different from the rest of PCI devices. Besides accesses to
BAR registers (trapped to access the shadowing PCI config space we
maintained), accesses to other registers (including the COMMAND
register) are handled directly by the underline vfio or vfio-user
device.

This patch adds the proper handling of pending BAR reprogramming for
vfio and vfio-user devices.

Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-05-15 17:35:44 +00:00
Bo Chen
aaf86ef209 pci: Reprogram device BAR when its MSE bit is set
The Memory Space Enable (MSE) bit from the COMMAND register in the
PCI configuration space controls whether a PCI device responds to memory
space accesses, e.g. read and write cycles to the device MMIO regions
defined by its BARs. The MSE bit is used by the device drivers to ensure
the correctness of BAR reprogramming. A common workflow is, the driver
first clears the MSE bit, then writes new values to the BAR registers,
and finally set the MSE bit to finish the BAR reprogramming.

This patch changes how we handle BAR reprogramming for all PCI
devices (e.g. virtio-pci, vfio, vfio-user, etc.), so that we follow the
same convention, e.g. moving PCI BARs when its MSE bit is set.

Note that some device drivers (such as edk2) only clear and set MSE once
while reprogramming multiple BARs of a single device. To support such
behavior, this patch adds support for multiple pending BAR reprogramming.

See: https://github.com/cloud-hypervisor/cloud-hypervisor/issues/7027#issuecomment-2853642959

Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-05-15 17:35:44 +00:00
Bo Chen
59f98a2edc pci: configuration: Log BAR reprogramming correctly
Use the right bar index and bar address maintained internally by the
VMM when logging BAR reprogramming.

Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-05-15 15:04:01 +00:00
Bo Chen
cb52cf91df pci: Keep detect_bar_reprogramming internal to PciConfiguration
A BAR reprogramming of a PCI device will only happen when the (guest)
kernel write to its PCI config space, e.g. the detection of bar
reprogramming (`detect_bar_repgraomming()`) can be embedded to the PCI
config space write (`write_config_register()`). It simplifies APIs
exposed by the `struct PciConfiguration` and `trait PciDevice`. It also
prepares for easier handling of pending bar reprogramming when the MSE
bit of the COMMAND register is not enabled at the time of changing BAR
registers.

See: https://github.com/cloud-hypervisor/cloud-hypervisor/issues/7027#issuecomment-2853642959

Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-05-15 15:04:01 +00:00
Muminul Islam
5814193722 build: Bump mshv crates from 0.5.0 to 0.5.1
This release has critical bug fixes IOCTL changes.
No API changes.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2025-05-15 06:39:22 +00:00
Bo Chen
10ee003d66 misc: Fix beta clippy issues
Fixing the following clippy issue using `cargo clippy --fix`:

error: variables can be used directly in the `format!` string
  --> build.rs:25:27
   |
25 |         version.push_str(&format!("-{}", extra_version));
   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args

Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-05-14 03:44:12 +00:00
Jinank Jain
8f402687ce hypervisor: mshv: Add missing implementation
Currently a lot of functions are stubbed out with unimplemented feature
tag. Add the missing implementation to successfully boot ARM64 guests on
MSHV.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-05-09 16:06:12 +00:00
Jinank Jain
f72f16ee50 arch: Fix incorrect FDT generation
Num SPI and Base SPI nodes should be added before the end node to be
included in the device tree generation.

Fixes: eac44e6 (arch: Extend FDT for GICv2M device for ARM64 on MSHV)
Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-05-09 16:06:12 +00:00
Jinank Jain
034aa514d7 vmm: Unify address space allocation
It seems like address allocation has been spread into different files
and different location for x86 vs ARM. This makes it hard to follow the
code. Thus, unify it a single location which satisfies all the
requirement.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-05-09 16:06:12 +00:00
Jinank Jain
4e48f429eb vmm: Unify loading of payload for IGVM and non-IGVM
It just simplifies code and improves the code read-ability without much
affecting the boot performance.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-05-09 16:06:12 +00:00
Jinank Jain
50b0493371 vmm: Move vm init after interrupt controller init
This will satisfy the requirement of MSHV i.e., setting the GICD base
address before initializing the VM.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-05-09 16:06:12 +00:00
Jinank Jain
df418a2153 vmm: Split device creation from interrupt controller creation
For MSHV guests, we would need interrupt controller to be initialized
before the VM gets initialized. This is because we are registering the
base address of GIC distributor with MSHV as part of interrupt
controller initialization workflow. And MSHV mandates that this property
is set before we initialize the VM.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-05-09 16:06:12 +00:00
Jinank Jain
a072b9a356 hypervisor: Set additional partition property for MSHV guest
For ARM64 guests we need to set three important partition property:

1) PPI interrupt ID for timer interrupt
2) PPI interrupt ID for PMU interrupts.
3) Hiding LPI support from the guest because MSHV does emulate ITS for
   the guest.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-05-09 16:06:12 +00:00
Jinank Jain
691fe0ca68 hypervisor: arch: Move PMU IRQ definition from arch to hypervisor crate
Since this would be used in other places inside the hypervisor and
hypervisor crate cannot take a dependency on arch crate, as that creates
cyclic dependency.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-05-09 16:06:12 +00:00
Jinank Jain
aaa3a114dd arch: hypervisor: Define PPI constants for ARM arch timer
Currently PPI interrupt ID are hardcoded as numbers, it would be ideal
to define them as constants and could be reused in other parts of the
hypervisor crate.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-05-09 16:06:12 +00:00
Philipp Schuster
77e042237d ci: improve gitlint (max line length in body with exceptions)
Follow-up of 5aa1540c5d but way more
mature. We now use custom gitlint rules written in Python to better
handle the max line length, with respect to a few valid exceptions.
Recognizing code blocks or compiler output, as discussed, is not
trivial and hard to get right for all corner-cases. Therefore, this
commit is a pragmatic way forward. The CI job should be kept optional.

Allowed exceptions for the 72 line length limit are now:

1. links in the following three common patterns:
https://example.com/very-long-links/very-long-links/very-long-links/very-long-links/very-long-links/very-long-links/very-long-links
[0] https://example.com/very-long-links/very-long-links/very-long-links/very-long-links/very-long-links/very-long-links/very-long-links
[0]: https://example.com/very-long-links/very-long-links/very-long-links/very-long-links/very-long-links/very-long-links/very-long-links

2. code blocks (anything between the three backticks)

```
let x = "very_long_very_long_very_long_very_long_very_long_very_long_very_long_very_long_very_long_very_long_"
```

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-09 14:50:23 +01:00
Jinank Jain
f16d45e86e build: Bump mshv crates from 0.4.0 to 0.5.0
Along with also bump the vfio-bindings crates to use the latest
mshv-bindings.

There is a breaking change in the new mshv crate which requires an
additional step to initialize vm after creating it.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-05-05 17:42:30 +00:00
Jinank Jain
3eb6b69dd2 hypervisor: Extend interrupt handling for legacy IRQ
On x86 MSHV guests only used to support MSI based interrupts via IOAPIC
but ARM64 guests uses legacy interrupt for its functioning. Thus, extend
the logic to create routing entry to support legacy interrupts for ARM64
guests on MSHV.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-05-05 10:44:16 +00:00
Jinank Jain
fa2b5ca12b vmm: hypervisor: Add a new interface to setup GICR for vcpus
For MSHV arm64 guest, there is an in-hypervisor GICv2M emulation and for
that to work, it needs to be enlightened with the base address of GIC
redistributor exposed to guest via FDT.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-05-05 09:57:21 +00:00
Jinank Jain
eac44e6af0 arch: Extend FDT for GICv2M device for ARM64 on MSHV
GICv2M requires two additional properties to be exposed via FDT:
1) Base SPI number and 2) Total number of SPIs. SPIs in general starts
from 32 and goes upto 1019. But currently we are limiting the range to
96 as that should be good enough for any normal Linux guest to function.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-05-05 09:05:02 +00:00
abm-77
40055595ff docs: Update riscv docs for developing in QEMU VM
Since there are not a wide availability of RISC-V boards available at
the moment, it is easiest to develop with a QEMU virtual machine. I had
a hard time setting one up, but with the assistance of Ruoqing, I was
able to get one running. These are the steps I took to do so.

Signed-off-by: abm-77 <andrewmiller77@protonmail.com>
2025-05-03 11:11:05 +00:00
Bingxin Li
149c08981b README: Update ubuntu support status
Ubuntu 24.04 LTS (Noble Numbat) is tested against cloud-hypervisor v45.0
release and it is working, document status in README.md.

Signed-off-by: Bingxin Li <bl497@cam.ac.uk>
2025-05-02 07:58:04 +00:00
Jinank Jain
f1f6814774 hypervisor: Implement support for fetching sys regs on MSHV
ARM64 system register constants are not 1:1 mapped to MSHV definition of
those registers so we need a small helper function to translate that
mapping before retrieving those system registers.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-30 06:11:26 +00:00
Jinank Jain
58f71b0c44 hypervisor: arch: Move common regs from arch to hypervisor crate
There are other potential users of these registers definitions in the
hypervisor crate. And hypervisor crate cannot use definitions from arch
crate because it creates cyclic dependency.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-30 06:11:26 +00:00
Fabiano Fidêncio
1968805ba2 api_client: Add TooManyRequests status code
In order to be on-pair with what's we're using from micro-http, let's
also add the proper status code here as well (as it will be used by
`ch-remote`).

Signed-off-by: Fabiano Fidêncio <fidencio@northflank.com>
2025-04-28 16:24:10 +00:00
Fabiano Fidêncio
d0225fe68f vmm: api: Be more specific on "Still pending remove vcpu" errors
Although the CPU manager gives us a quite descriptive error, on the
application side (the part calling Cloud Hypervisor) we have absolutely
no way to distinguish such error from any other error that may happen
when resizing a VM.

With this in mind, let's be more specific and return a TooManyRequests
(429) error, allowing the caller to have a chance to decide whether they
want to retry the operation or not.

https://datatracker.ietf.org/doc/html/rfc6585#section-4

Signed-off-by: Fabiano Fidêncio <fidencio@northflank.com>
2025-04-28 16:24:10 +00:00
Fabiano Fidêncio
87007a288f build: Bump micro-http crate
As the coming patches in this series will take advantage of a status
code that was recently added there.

Signed-off-by: Fabiano Fidêncio <fidencio@northflank.com>
2025-04-28 16:24:10 +00:00
Muminul Islam
f67484a714 hypervisor: mshv: advance_rip_rax after port handle
Call function to advance RIP and RAX after handling the
port.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2025-04-26 21:18:22 +00:00
Muminul Islam
3c63779302 hypervisor: mshv: function to advance RIP and RAX
A separate function to advance RIP and RAX based on
register page.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2025-04-26 21:18:22 +00:00
Muminul Islam
1c22c4a57b misc: docs: Fix broken link of dpdk.org
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2025-04-24 23:23:15 +00:00
Muminul Islam
4af98f4cb2 hypervisor: mshv: get_msr_list return vector instead fam-wrapper
New MSHV version updates the get_msr_list output as
vector instead of fam-wrapper. It avoids unnecessary
conversions.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2025-04-24 23:23:15 +00:00
Muminul Islam
c58c686f3e hypervisor: mshv: fix clippy warnings for latest mshv crates
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2025-04-24 23:23:15 +00:00
Muminul Islam
9b13d63f28 build: update mshv crates to the latest release
Latest mshv crates contains some IOCTL changes that
enhances VM creation and configures the features in correct
way. Also adds some features that improves register access.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2025-04-24 23:23:15 +00:00
Muminul Islam
b5aeb1f63c hypervisor: mshv: use mapped register page for port handling
MSHV allows VMM to map the VP register page into root.
This feature helps VMM to faster process most of the frequent
used registers. This patch uses the VP register page for port
handling in CPU run method.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2025-04-24 20:37:19 +00:00
Jinank Jain
af2ce3e0cc hypervisor: Basic implementation of setup_regs for MSHV ARM64 guests
As part of this configure the program counter, pstate and X0 registers.
Program counter will point to the start address of the kernel/firmware
in the guest memory. X0 will point to start of the FDT.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-23 07:42:29 +00:00
Jinank Jain
7fd1b9a284 hypervisor: Configure VGIC for MSHV guests
As part of this configuration, two things are being done:

1. Setting up the base address of GIC Distributor
2. Setting up the base address of GIC Interrupt Translator

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-23 07:42:29 +00:00
Jinank Jain
e69acd1dc3 hypervisor: Refactor common PSTATE register definition
Initial PSTATE value would be same for both KVM and MSHV. Thus, move it
to common register definition pool.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-23 07:42:29 +00:00
Julian Stecklina
de764456ce pci: reduce visibility of VfioCommon internals
There are a lot of internal functions that are not and probably should
not be called from other places.

Signed-off-by: Julian Stecklina <julian.stecklina@cyberus-technology.de>
2025-04-18 18:25:37 +00:00
Julian Stecklina
0095556847 pci: gracefully handle devices that return 0xff as a capability pointer
If a device returns 0xff as a capability pointer bad things happen.
The code before the previous commits would crash in debug builds due
to integer overflow. With the two lowest bits masked out, it sends the
code into an endless loop.

Be more robust by at least handling the case where the capability
appears to point to itself.

Signed-off-by: Julian Stecklina <julian.stecklina@cyberus-technology.de>
2025-04-18 18:25:37 +00:00
Julian Stecklina
a0065452d8 pci: mask out lower 2 bits in capability list pointers
The PCI standard mandates that the lower bits of the capability
pointer are masked out before using the pointer. See PCI Local Bus
Specification 3.0 Chapter 6.7 "Capabilities List".

Signed-off-by: Julian Stecklina <julian.stecklina@cyberus-technology.de>
2025-04-18 18:25:37 +00:00
Julian Stecklina
56ca26e72c pci: only parse capabilities if the device claims to have some
Currently, the code tries to follow the PCI capabilities list in
offset 0x34 in the config space regardless of whether the status
registers says this is valid. Fix by adding the appropriate check.

Signed-off-by: Julian Stecklina <julian.stecklina@cyberus-technology.de>
2025-04-18 18:25:37 +00:00
Julian Stecklina
21b9806cad ci: Exclude osdev.org from link check
OSDev has cranked up its bot protection. The following link works for
me locally after clicking the "I'm a human" button. I guess the CI
fails this check...

Without this exception the CI fails the link check stage:

* [403] [https://wiki.osdev.org/IOAPIC](https://wiki.osdev.org/IOAPIC) | Network error: Forbidden

Signed-off-by: Julian Stecklina <julian.stecklina@cyberus-technology.de>
2025-04-18 18:25:37 +00:00
Jinank Jain
d374101f38 hypervisor: Use instruction emulator to handle unmapped gpa
Use the context from Unmapped Gpa exit from the hypervisor to initialize
the MshvEmulatorContext and later call the emulator to decode the
instruction.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-17 13:11:23 +00:00
Jinank Jain
461e31e6d8 hypervisor: Instruction emulator for ARM64 guest on MSHV
Currently it would be using the syndrome register for instruction
decoding which is what KVM has been using in-kernel to decode
instructions for ARM64 guests. In future, it could be extended with an
actual instruction emulator if required. But most Linux guests works
well with the instruction decoder using syndrome register.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-17 13:11:23 +00:00
Jinank Jain
d22e7e2638 hypervisor: Add definition for parsing EsrEl2 register
This helps in implementing an instruction decoder for MSVH which does
not support in-kernel instruction decoding like KVM.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-17 13:11:23 +00:00
Jinank Jain
960d702255 hypervisor: Enable MSHV compilation on ARM64
Along with it also enable clippy tests on MSHV aarch64 builds.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-16 03:36:12 +00:00
Jinank Jain
1105243aca vmm: Guard KVM specific unit test with feature guard
Some tests are specifically designed for KVM hypervisor platform. Thus,
guard them using appropriate feature flags.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-16 03:36:12 +00:00
Jinank Jain
317f8002d7 hypervisor: Silence compiler warning for unused variables
There are a bunch of unused variables as of now on the MSHV side and
compiler warns about them. Thus, mark them as unused for the time being.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-16 03:36:12 +00:00
dependabot[bot]
278b57ba49 build: Bump equivalent from 1.0.1 to 1.0.2
Bumps [equivalent](https://github.com/indexmap-rs/equivalent) from 1.0.1 to 1.0.2.
- [Commits](https://github.com/indexmap-rs/equivalent/compare/v1.0.1...v1.0.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-15 23:57:05 +00:00
Jinank Jain
d7f87425cd build: Bump mshv crates from 0.3.3 to 0.3.5
Latest mshv crates contains some binding changes required for supporting
ARM64 guests on MSHV.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-15 07:23:44 +00:00
dependabot[bot]
573868c035 build: Bump bitflags from 2.6.0 to 2.9.0
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.6.0 to 2.9.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.6.0...2.9.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-15 00:35:22 +00:00
Jinank Jain
2798286278 hypervisor: Add GICv2M support for MSHV ARM64 guest
MSHV does not emulate a GICv3-ITS for guests to support MSI interrupts,
instead it exposes a GICv2m device. Currently adding a skeleton code
which would be modified later on with complete implementation.

With this we can start compiling cloud-hypervisor for MSHV on ARM64.
This will make sure that we don't regress in future in terms of basic
compilation test.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-14 16:33:10 +00:00
Ruoqing He
bcc314eb8b build: Manually bump igvm crates to d062818
`zerocopy` is bumped to 0.8.x after 0.3.4 of igvm crates, bump to rev
d062818 to capture `zerocopy` upgrade, but we should bump to 0.3.5
later.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-04-13 08:06:11 +00:00
Ruoqing He
af28569611 build: Bump zerocopy and acpi_tables
Manually bump zerocopy to 0.8.24 since our dependabot could not perform
the upgrade properly.

Manually bump acpi_tabls as well since it's depending on zerocopy.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-04-13 08:06:11 +00:00
Rob Bradford
1a5dcc5e70 build: Clarify that MSRV bump is an OR of potential reasons
Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-04-13 07:39:00 +00:00
Rob Bradford
29b089296d tests: Move test_virtio_pmem_persist_writes to sequential group
This test has been generating a flaky OOM situation when run in the
parallel group.

Signed-off-by: Rob Bradford <rbradford@rivosinc.com>
2025-04-13 07:38:42 +00:00
Ruoqing He
6e4bf84383 hypervisor: Fix clippy empty_line_after_doc_comments
Fix clippy warning empty_line_after_doc_comments reported by rustc
1.83.0 (90b35a623 2024-11-26).

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-04-12 18:31:02 +01:00
Ruoqing He
226ecf47bb build: Bump MSRV to 1.83.0
The dependency `bitfield-struct` 0.10.x of `igvm` 0.3.5 requires MSRV
1.83.0, bump to catch up.

Update image to 20250412-0 because MSRV in Dockerfile is updated.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-04-12 18:31:02 +01:00
Bo Chen
0e3733e938 vmm: openapi: Remove path as required for DiskConfig
This aligns with our CLI syntax. The correctness of `DiskConfig` will be
ensured via `VmConfig::validate()`, e.g. `path` and `socket` are
mutually exclusive.

Fixes: #7016

Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-04-09 16:03:12 +00:00
Jinank Jain
f811e36443 hypervisor: Add support for get/set regs for ARM guest on MSHV
Enable getting and setting registers for ARM64 guests on MSHV.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-03 16:46:23 +00:00
Jinank Jain
a64ba04e78 pci: Fix clippy warning while comparing raw pointers
Use the builtin function instead of using `==` operator.

Warning from the beta compiler:

error: use `std::ptr::eq` when comparing raw pointers
--> pci/src/vfio.rs:1616:24

if host_addr == libc::MAP_FAILED {
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    help: try: `std::ptr::eq(host_addr, libc::MAP_FAILED)`

 = help: for further information visit
 = https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
 = note: `-D clippy::ptr-eq` implied by `-D warnings`
 = help: to override `-D warnings` add `#[allow(clippy::ptr_eq)]`

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-03 13:11:49 +00:00
Jinank Jain
b686a5bb24 vm-allocator: Fix clippy warning for implicit saturating sub
Use the builtin function to improve the readability of the code.

Warning from beta compiler:

error: manual arithmetic check found
--> vm-allocator/src/address.rs:151:30
|
|let adjust = if alignment > 1 { alignment - 1 } else { 0 };
|             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|             help: replace it with: `alignment.saturating_sub(1)`
|
= help: for further information visit
https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
= note: `-D clippy::implicit-saturating-sub` implied by `-D warnings`
= help: to override `-D warnings` add`#[allow(clippy::implicit_saturating_sub)]`

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-03 13:11:49 +00:00
Jinank Jain
ea4693a091 misc: Fix clippy error from beta compiler
Rust has a new way of constructing other error and clippy complains if
we are still using the older way to construct error message. Thus,
migrate to the new approach suggested by the clippy.

Warning from beta compiler:

error: this can be `std::io::Error::other(_)`
--> block/src/vhdx/mod.rs:142:17
 |
 | /                 std::io::Error::new(
 | |                     std::io::ErrorKind::Other,
 | |                     format!("Failed to update VHDx header: {e}"),
 | |                 )
 | |_________________^
 |
 = help: for further information visit
https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error
help: use `std::io::Error::other`

                 std::io::Error::other(
                     format!("Failed to update VHDx header: {e}"),

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-03 13:11:49 +00:00
Jinank Jain
3698b8e74c build: Centralize serde_json crate to workspace
`serde_json` crate is referenced by multiple components, centralize it
to workspace to better manage this crate.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-02 06:20:54 +00:00
Jinank Jain
6bb33601d0 hypervisor: Avoid leaking KVM GIC state into common GIC state
KVM supports GICv3-ITS emulation and the current GicState is modelled
around the KVM implementation. We should refactor this to accomodate
other hypervisor requirements. For example, MSHV only support GICv2M
emulation for guests for delivering MSI interrupts instead of GICv3-ITS.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

so has been skipped for now.

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

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

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

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

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

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

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

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

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

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

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

And quote from @rbradford:

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

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

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

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

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

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

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

Removed deprecated package `python3-distutils`.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

The urls explictly excluded in config file are manually checked.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-02-04 19:33:10 +00:00
165 changed files with 5328 additions and 3187 deletions

View File

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

View File

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

View File

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

View File

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

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

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

View File

@@ -24,7 +24,7 @@ jobs:
fetch-depth: 0
- name: Install Rust toolchain
run: /opt/scripts/exec-in-qemu.sh rustup default 1.77.0
run: /opt/scripts/exec-in-qemu.sh rustup default 1.83.0
- name: Build ${{ matrix.module }} Module (kvm)
run: /opt/scripts/exec-in-qemu.sh cargo rustc --locked -p ${{ matrix.module }} --no-default-features --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states

View File

@@ -56,6 +56,20 @@ jobs:
command: clippy
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
- name: Clippy (mshv)
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 --no-default-features --tests --examples --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
- name: Clippy (mshv + kvm)
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 --no-default-features --tests --examples --features "mshv,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
- name: Clippy (default features)
uses: actions-rs/cargo@v1
with:

View File

@@ -45,7 +45,7 @@ jobs:
target: ${{ matrix.platform.target }}
args: ${{ matrix.platform.args }}
strip: true
toolchain: "1.77.0"
toolchain: "1.83.0"
- name: Copy Release Binaries
if: github.event_name == 'create' && github.event.ref_type == 'tag'
shell: bash

View File

@@ -1,6 +1,7 @@
[general]
extra-path=scripts/gitlint/rules.py
extra-path=scripts/gitlint/rules
regex-style-search=true
ignore=body-max-line-length
[ignore-by-author-name]
regex=dependabot
@@ -10,6 +11,3 @@ ignore=all
[title-max-length]
line-length=72
# default 80
[body-max-line-length]
line-length=72

21
.lychee.toml Normal file
View File

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

View File

@@ -15,6 +15,7 @@ EXTINT = "EXTINT"
INOUT = "INOUT"
liness = "liness"
outout = "outout"
TRANSLATER = "TRANSLATER"
[default.extend-identifiers]
fo = "fo"

View File

@@ -13,7 +13,7 @@ License](https://opensource.org/licenses/Apache-2.0).
## Coding Style
We follow the [Rust Style](https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/guide.md)
We follow the [Rust Style](https://github.com/rust-lang/rust/tree/HEAD/src/doc/style-guide/src)
convention and enforce it through the Continuous Integration (CI) process calling into `rustfmt`
for each submitted Pull Request (PR).
@@ -36,7 +36,7 @@ commit you make.
## Certificate of Origin
In order to get a clear contribution chain of trust we use the [signed-off-by language](https://01.org/community/signed-process)
In order to get a clear contribution chain of trust we use the [signed-off-by language](https://web.archive.org/web/20230406041855/https://01.org/community/signed-process)
used by the Linux kernel project.
## Patch format
@@ -79,12 +79,12 @@ you want to merge your changes to `cloud-hypervisor`:
1. Fork the [cloud-hypervisor](https://github.com/cloud-hypervisor/cloud-hypervisor) project
into your github organization.
2. Within your fork, create a branch for your contribution.
3. [Create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)
1. Within your fork, create a branch for your contribution.
1. [Create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)
against the main branch of the Cloud Hypervisor repository.
4. To update your pull request amend existing commits whenever applicable and
1. To update your pull request amend existing commits whenever applicable and
then push the new changes to your pull request branch.
5. Once the pull request is approved it can be integrated.
1. Once the pull request is approved it can be integrated.
## Issue tracking

231
Cargo.lock generated
View File

@@ -1,13 +1,13 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "acpi_tables"
version = "0.1.0"
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#e268627630839bd22f1c13e7e81ec70c7e9b73d6"
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#e08a3f0b0a59b98859dbf59f5aa7fd4d2eb4018a"
dependencies = [
"zerocopy 0.7.35",
"zerocopy 0.8.24",
]
[[package]]
@@ -66,11 +66,11 @@ dependencies = [
[[package]]
name = "anstyle-query"
version = "1.1.1"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -137,12 +137,11 @@ dependencies = [
[[package]]
name = "async-channel"
version = "2.3.0"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f2776ead772134d55b62dd45e59a79e21612d85d0af729b8b7d3967d601a62a"
checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a"
dependencies = [
"concurrent-queue",
"event-listener",
"event-listener-strategy",
"futures-core",
"pin-project-lite",
@@ -258,9 +257,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
[[package]]
name = "async-trait"
version = "0.1.85"
version = "0.1.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d"
dependencies = [
"proc-macro2",
"quote",
@@ -296,9 +295,9 @@ dependencies = [
[[package]]
name = "bitfield-struct"
version = "0.7.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c2ce686adbebce0ee484a502c440b4657739adbad65eadf06d64f5816ee9765"
checksum = "2be5a46ba01b60005ae2c51a36a29cfe134bcacae2dd5cedcd4615fbaad1494b"
dependencies = [
"proc-macro2",
"quote",
@@ -313,9 +312,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.6.0"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
[[package]]
name = "block"
@@ -374,9 +373,12 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
version = "1.0.99"
version = "1.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766"
dependencies = [
"shlex",
]
[[package]]
name = "cfg-if"
@@ -420,7 +422,7 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
[[package]]
name = "cloud-hypervisor"
version = "44.0.0"
version = "46.0.0"
dependencies = [
"anyhow",
"api_client",
@@ -565,7 +567,7 @@ dependencies = [
"acpi_tables",
"anyhow",
"arch",
"bitflags 2.6.0",
"bitflags 2.9.0",
"byteorder",
"event_monitor",
"hypervisor",
@@ -611,23 +613,23 @@ dependencies = [
[[package]]
name = "dirs"
version = "5.0.1"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.4.1"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -686,15 +688,15 @@ version = "4.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.0",
"libc",
]
[[package]]
name = "equivalent"
version = "1.0.1"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
@@ -876,7 +878,7 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6341b3480afbb34eaefc7f92713bc92f2d83e338aaa1c44192f9c2956f4a4903"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.0",
"cfg-if",
"log",
"managed",
@@ -913,10 +915,22 @@ dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi",
"wasi 0.11.0+wasi-snapshot-preview1",
"wasm-bindgen",
]
[[package]]
name = "getrandom"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
dependencies = [
"cfg-if",
"libc",
"wasi 0.13.3+wasi-0.2.2",
"windows-targets 0.52.6",
]
[[package]]
name = "gimli"
version = "0.31.1"
@@ -931,9 +945,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "hashbrown"
version = "0.14.5"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "hermit-abi"
@@ -959,6 +973,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"arc-swap",
"bitfield-struct",
"byteorder",
"cfg-if",
"concat-idents",
@@ -972,12 +987,15 @@ dependencies = [
"log",
"mshv-bindings",
"mshv-ioctls",
"open-enum",
"serde",
"serde_json",
"serde_with",
"thiserror 2.0.6",
"vfio-ioctls",
"vm-memory",
"vmm-sys-util",
"zerocopy 0.8.24",
]
[[package]]
@@ -997,9 +1015,8 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "igvm"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7984b10433b50e06a06bd50c69bca4888a5d7de8975f64ea4c2a7687eb99b09d"
version = "0.3.4"
source = "git+https://github.com/microsoft/igvm?branch=main#01daa631a596459cb4de58505881007dd13d4410"
dependencies = [
"bitfield-struct",
"crc32fast",
@@ -1007,28 +1024,28 @@ dependencies = [
"igvm_defs",
"open-enum",
"range_map_vec",
"thiserror 1.0.62",
"static_assertions",
"thiserror 2.0.6",
"tracing",
"zerocopy 0.7.35",
"zerocopy 0.8.24",
]
[[package]]
name = "igvm_defs"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b64ec5588c475372ae830475d3ee9a7bd255407dcb9f03faf6d493556eb6105a"
version = "0.3.4"
source = "git+https://github.com/microsoft/igvm?branch=main#01daa631a596459cb4de58505881007dd13d4410"
dependencies = [
"bitfield-struct",
"open-enum",
"static_assertions",
"zerocopy 0.7.35",
"zerocopy 0.8.24",
]
[[package]]
name = "indexmap"
version = "2.2.6"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
dependencies = [
"equivalent",
"hashbrown",
@@ -1100,7 +1117,7 @@ version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e013ae7fcd2c6a8f384104d16afe7ea02969301ea2bb2a56e44b011ebc907cab"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.0",
"kvm-bindings",
"libc",
"vmm-sys-util",
@@ -1135,7 +1152,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.0",
"libc",
]
@@ -1220,7 +1237,7 @@ dependencies = [
[[package]]
name = "micro_http"
version = "0.1.0"
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#ef96f623c46e221ebf9b6037567f97ec57683afd"
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#4f621532e81ee2ad096a9c9592fdacc40d19de48"
dependencies = [
"libc",
"vmm-sys-util",
@@ -1228,9 +1245,9 @@ dependencies = [
[[package]]
name = "miniz_oxide"
version = "0.8.0"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler2",
]
@@ -1243,23 +1260,23 @@ checksum = "9bec4598fddb13cc7b528819e697852653252b760f1228b7642679bf2ff2cd07"
[[package]]
name = "mshv-bindings"
version = "0.3.2"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0cb5031f3243a7459b7c13d960d25420980874eebda816db24ce6077e21d43"
checksum = "909de5fd4a5a3347a6c62872f6816e6279efd8615a753f10a3bc4daaef8a72ef"
dependencies = [
"libc",
"num_enum",
"serde",
"serde_derive",
"vmm-sys-util",
"zerocopy 0.8.8",
"zerocopy 0.8.24",
]
[[package]]
name = "mshv-ioctls"
version = "0.3.2"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89abe853221fa6f14ad4066affb9abda241a03d65622887d5794e1422d0bd75a"
checksum = "8c7d94972588d562bd349b916de6a43f2ee268e6e9c91cfb5b30549ed4ea2751"
dependencies = [
"libc",
"mshv-bindings",
@@ -1273,7 +1290,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
dependencies = [
"getrandom",
"getrandom 0.2.15",
]
[[package]]
@@ -1288,7 +1305,7 @@ name = "net_util"
version = "0.1.0"
dependencies = [
"epoll",
"getrandom",
"getrandom 0.3.1",
"libc",
"log",
"net_gen",
@@ -1312,7 +1329,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.0",
"cfg-if",
"cfg_aliases",
"libc",
@@ -1421,6 +1438,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "option_parser"
version = "0.1.0"
dependencies = [
"thiserror 2.0.6",
]
[[package]]
name = "ordered-stream"
@@ -1672,18 +1692,18 @@ dependencies = [
[[package]]
name = "proc-macro-crate"
version = "3.1.0"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284"
checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
dependencies = [
"toml_edit",
]
[[package]]
name = "proc-macro2"
version = "1.0.92"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [
"unicode-ident",
]
@@ -1724,7 +1744,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
"getrandom 0.2.15",
]
[[package]]
@@ -1764,13 +1784,13 @@ dependencies = [
[[package]]
name = "redox_users"
version = "0.4.6"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
dependencies = [
"getrandom",
"getrandom 0.2.15",
"libredox",
"thiserror 1.0.62",
"thiserror 2.0.6",
]
[[package]]
@@ -1831,7 +1851,7 @@ version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.0",
"errno",
"libc",
"linux-raw-sys",
@@ -1840,9 +1860,9 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.18"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "scopeguard"
@@ -1852,9 +1872,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "seccompiler"
version = "0.4.0"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "345a3e4dddf721a478089d4697b83c6c0a8f5bf16086f6c13397e4534eb6e2e5"
checksum = "a4ae55de56877481d112a559bbc12667635fdaf5e005712fd4e2b2fa50ffc884"
dependencies = [
"libc",
]
@@ -1940,10 +1960,16 @@ dependencies = [
]
[[package]]
name = "signal-hook"
version = "0.3.17"
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
dependencies = [
"libc",
"signal-hook-registry",
@@ -2051,6 +2077,7 @@ dependencies = [
"serde",
"serde_json",
"ssh2",
"thiserror 2.0.6",
"vmm-sys-util",
"wait-timeout",
]
@@ -2103,15 +2130,15 @@ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
[[package]]
name = "toml_datetime"
version = "0.6.5"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
[[package]]
name = "toml_edit"
version = "0.21.1"
version = "0.22.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
dependencies = [
"indexmap",
"toml_datetime",
@@ -2192,9 +2219,9 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.12"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "utf8parse"
@@ -2208,7 +2235,7 @@ version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
dependencies = [
"getrandom",
"getrandom 0.2.15",
"rand",
"uuid-macro-internal",
]
@@ -2239,15 +2266,15 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "vfio-bindings"
version = "0.4.0"
source = "git+https://github.com/rust-vmm/vfio?branch=main#bdbb1cd486484faa23db48c82d51484e2ee43692"
source = "git+https://github.com/rust-vmm/vfio?branch=main#3d158a14460cac7ca3c99c2effa0a46880935cb0"
dependencies = [
"vmm-sys-util",
]
[[package]]
name = "vfio-ioctls"
version = "0.2.0"
source = "git+https://github.com/rust-vmm/vfio?branch=main#bdbb1cd486484faa23db48c82d51484e2ee43692"
version = "0.4.0"
source = "git+https://github.com/rust-vmm/vfio?branch=main#3d158a14460cac7ca3c99c2effa0a46880935cb0"
dependencies = [
"byteorder",
"kvm-bindings",
@@ -2284,7 +2311,7 @@ name = "vhost"
version = "0.12.1"
source = "git+https://github.com/rust-vmm/vhost?rev=d983ae0#d983ae07f78663b7d24059667376992460b571a2"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.0",
"libc",
"uuid",
"vm-memory",
@@ -2316,6 +2343,7 @@ dependencies = [
"libc",
"log",
"option_parser",
"thiserror 2.0.6",
"vhost",
"vhost-user-backend",
"virtio-bindings",
@@ -2335,6 +2363,7 @@ dependencies = [
"log",
"net_util",
"option_parser",
"thiserror 2.0.6",
"vhost",
"vhost-user-backend",
"virtio-bindings",
@@ -2461,7 +2490,7 @@ dependencies = [
"anyhow",
"arc-swap",
"arch",
"bitflags 2.6.0",
"bitflags 2.9.0",
"block",
"blocking",
"cfg-if",
@@ -2509,7 +2538,7 @@ dependencies = [
"vm-virtio",
"vmm-sys-util",
"zbus",
"zerocopy 0.7.35",
"zerocopy 0.8.24",
]
[[package]]
@@ -2539,6 +2568,15 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.13.3+wasi-0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
@@ -2765,13 +2803,22 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.5.40"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603"
dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [
"bitflags 2.9.0",
]
[[package]]
name = "xdg-home"
version = "1.3.0"
@@ -2856,11 +2903,11 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.8"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a4e33e6dce36f2adba29746927f8e848ba70989fdb61c772773bbdda8b5d6a7"
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
dependencies = [
"zerocopy-derive 0.8.8",
"zerocopy-derive 0.8.24",
]
[[package]]
@@ -2876,9 +2923,9 @@ dependencies = [
[[package]]
name = "zerocopy-derive"
version = "0.8.8"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cd137b4cc21bde6ecce3bbbb3350130872cda0be2c6888874279ea76e17d4c1"
checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -7,15 +7,15 @@ edition = "2021"
homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
license = "Apache-2.0 AND BSD-3-Clause"
name = "cloud-hypervisor"
version = "44.0.0"
version = "46.0.0"
# Minimum buildable version:
# Keep in sync with version in .github/workflows/build.yaml
# Policy on MSRV (see #4318):
# Can only be bumped by:
# Can only be bumped if satisfying any of the following:
# a.) A dependency requires it,
# b.) If we want to use a new feature and that MSRV is at least 6 months old,
# c.) There is a security issue that is addressed by the toolchain update.
rust-version = "1.77.0"
rust-version = "1.83.0"
[profile.release]
codegen-units = 1
@@ -40,8 +40,8 @@ libc = "0.2.167"
log = { version = "0.4.22", features = ["std"] }
option_parser = { path = "option_parser" }
seccompiler = { workspace = true }
serde_json = "1.0.120"
signal-hook = "0.3.17"
serde_json = { workspace = true }
signal-hook = "0.3.18"
thiserror = "2.0.6"
tpm = { path = "tpm" }
tracer = { path = "tracer" }
@@ -51,10 +51,10 @@ vmm-sys-util = { workspace = true }
zbus = { version = "4.4.0", optional = true }
[dev-dependencies]
dirs = "5.0.1"
dirs = "6.0.0"
net_util = { path = "net_util" }
once_cell = "1.20.2"
serde_json = "1.0.120"
serde_json = { workspace = true }
test_infra = { path = "test_infra" }
wait-timeout = "0.2.0"
@@ -102,13 +102,14 @@ members = [
]
[workspace.dependencies]
# rust-vmm crates
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
kvm-bindings = "0.10.0"
kvm-ioctls = "0.19.1"
linux-loader = "0.13.0"
mshv-bindings = "0.3.2"
mshv-ioctls = "0.3.2"
seccompiler = "0.4.0"
mshv-bindings = "0.5.1"
mshv-ioctls = "0.5.1"
seccompiler = "0.5.0"
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main" }
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
vfio_user = { git = "https://github.com/rust-vmm/vfio-user", branch = "main" }
@@ -119,3 +120,11 @@ virtio-queue = "0.14.0"
vm-fdt = { git = "https://github.com/rust-vmm/vm-fdt", branch = "main" }
vm-memory = "0.16.1"
vmm-sys-util = "0.12.1"
# igvm crates
# TODO: bump to 0.3.5 release
igvm = { git = "https://github.com/microsoft/igvm", branch = "main" }
igvm_defs = { git = "https://github.com/microsoft/igvm", branch = "main" }
# serde crates
serde_json = "1.0.120"

View File

@@ -59,9 +59,13 @@ based on the [Rust VMM](https://github.com/rust-vmm) crates.
### Architectures
Cloud Hypervisor supports the `x86-64` and `AArch64` architectures. There are
minor differences in functionality between the two architectures
(see [#1125](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/1125)).
Cloud Hypervisor supports the `x86-64`, `AArch64` and `riscv64`
architectures, with functionality varying across these platforms. The
functionality differences between `x86-64` and `AArch64` are documented
in [#1125](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/1125).
The `riscv64` architecture support is experimental and offers limited
functionality. For more details and instructions, please refer to [riscv
documentation](docs/riscv.md).
### Guest OS
@@ -297,8 +301,9 @@ Further details can be found in the [release documentation](docs/releases.md).
As of 2023-01-03, the following cloud images are supported:
- [Ubuntu Focal](https://cloud-images.ubuntu.com/focal/current/) (focal-server-cloudimg-{amd64,arm64}.img)
- [Ubuntu Jammy](https://cloud-images.ubuntu.com/jammy/current/) (jammy-server-cloudimg-{amd64,arm64}.img )
- [Fedora 36](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/) ([Fedora-Cloud-Base-36-1.5.x86_64.raw.xz](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/x86_64/images/) / [Fedora-Cloud-Base-36-1.5.aarch64.raw.xz](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/aarch64/images/))
- [Ubuntu Jammy](https://cloud-images.ubuntu.com/jammy/current/) (jammy-server-cloudimg-{amd64,arm64}.img)
- [Ubuntu Noble](https://cloud-images.ubuntu.com/noble/current/) (noble-server-cloudimg-{amd64,arm64}.img)
- [Fedora 36](https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/36/Cloud/) ([Fedora-Cloud-Base-36-1.5.x86_64.raw.xz](https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/36/Cloud/x86_64/images/) / [Fedora-Cloud-Base-36-1.5.aarch64.raw.xz](https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/36/Cloud/aarch64/images/))
Direct kernel boot to userspace should work with a rootfs from most
distributions although you may need to enable exotic filesystem types in the

View File

@@ -12,15 +12,15 @@ use vmm_sys_util::sock_ctrl_msg::ScmSocket;
#[derive(Debug, Error)]
pub enum Error {
#[error("Error writing to or reading from HTTP socket: {0}")]
Socket(std::io::Error),
Socket(#[source] std::io::Error),
#[error("Error sending file descriptors: {0}")]
SocketSendFds(vmm_sys_util::errno::Error),
SocketSendFds(#[source] vmm_sys_util::errno::Error),
#[error("Error parsing HTTP status code: {0}")]
StatusCodeParsing(std::num::ParseIntError),
StatusCodeParsing(#[source] std::num::ParseIntError),
#[error("HTTP output is missing protocol statement")]
MissingProtocol,
#[error("Error parsing HTTP Content-Length field: {0}")]
ContentLengthParsing(std::num::ParseIntError),
ContentLengthParsing(#[source] std::num::ParseIntError),
#[error("Server responded with an error: {0:?}: {1:?}")]
ServerResponse(StatusCode, Option<String>),
}
@@ -32,6 +32,7 @@ pub enum StatusCode {
NoContent,
BadRequest,
NotFound,
TooManyRequests,
InternalServerError,
NotImplemented,
Unknown,
@@ -45,6 +46,7 @@ impl StatusCode {
204 => StatusCode::NoContent,
400 => StatusCode::BadRequest,
404 => StatusCode::NotFound,
429 => StatusCode::TooManyRequests,
500 => StatusCode::InternalServerError,
501 => StatusCode::NotImplemented,
_ => StatusCode::Unknown,

View File

@@ -15,14 +15,18 @@ use std::{cmp, fs, result, str};
use byteorder::{BigEndian, ByteOrder};
use hypervisor::arch::aarch64::gic::Vgic;
use hypervisor::arch::aarch64::regs::{
AARCH64_ARCH_TIMER_HYP_IRQ, AARCH64_ARCH_TIMER_PHYS_NONSECURE_IRQ,
AARCH64_ARCH_TIMER_PHYS_SECURE_IRQ, AARCH64_ARCH_TIMER_VIRT_IRQ, AARCH64_PMU_IRQ,
};
use thiserror::Error;
use vm_fdt::{FdtWriter, FdtWriterResult};
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryRegion};
use super::super::{DeviceType, GuestMemoryMmap, InitramfsConfig};
use super::layout::{
IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_PCI_IO_SIZE, MEM_PCI_IO_START,
PCI_HIGH_BASE, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
GIC_V2M_COMPATIBLE, IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_PCI_IO_SIZE,
MEM_PCI_IO_START, PCI_HIGH_BASE, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT, SPI_BASE, SPI_NUM,
};
use crate::{NumaNodes, PciSpaceInfo};
@@ -59,9 +63,6 @@ const GIC_FDT_IRQ_TYPE_PPI: u32 = 1;
const IRQ_TYPE_EDGE_RISING: u32 = 1;
const IRQ_TYPE_LEVEL_HI: u32 = 4;
// PMU PPI interrupt number
pub const AARCH64_PMU_IRQ: u32 = 7;
// Keys and Buttons
// System Power Down
const KEY_POWER: u32 = 116;
@@ -81,7 +82,7 @@ pub trait DeviceInfoForFdt {
pub enum Error {
/// Failure in writing FDT in memory.
#[error("Failure in writing FDT in memory: {0}")]
WriteFdtToMemory(GuestMemoryError),
WriteFdtToMemory(#[source] GuestMemoryError),
}
type Result<T> = result::Result<T, Error>;
@@ -585,15 +586,14 @@ fn create_memory_node(
&& (first_region_end <= &mem_32bit_reserved_start))
{
panic!(
"Unexpected first memory region layout: (start: 0x{:08x}, end: 0x{:08x}).
ram_start: 0x{:08x}, mem_32bit_reserved_start: 0x{:08x}",
first_region_start, first_region_end, ram_start, mem_32bit_reserved_start
"Unexpected first memory region layout: (start: 0x{first_region_start:08x}, end: 0x{first_region_end:08x}).
ram_start: 0x{ram_start:08x}, mem_32bit_reserved_start: 0x{mem_32bit_reserved_start:08x}"
);
}
let mem_size = first_region_end - ram_start;
let mem_reg_prop = [ram_start, mem_size];
let memory_node_name = format!("memory@{:x}", ram_start);
let memory_node_name = format!("memory@{ram_start:x}");
let memory_node = fdt.begin_node(&memory_node_name)?;
fdt.property_string("device_type", "memory")?;
fdt.property_array_u64("reg", &mem_reg_prop)?;
@@ -606,14 +606,13 @@ fn create_memory_node(
if second_region_start != &ram_64bit_start {
panic!(
"Unexpected second memory region layout: start: 0x{:08x}, ram_64bit_start: 0x{:08x}",
second_region_start, ram_64bit_start
"Unexpected second memory region layout: start: 0x{second_region_start:08x}, ram_64bit_start: 0x{ram_64bit_start:08x}"
);
}
let mem_size = second_region_end - ram_64bit_start;
let mem_reg_prop = [ram_64bit_start, mem_size];
let memory_node_name = format!("memory@{:x}", ram_64bit_start);
let memory_node_name = format!("memory@{ram_64bit_start:x}");
let memory_node = fdt.begin_node(&memory_node_name)?;
fdt.property_string("device_type", "memory")?;
fdt.property_array_u64("reg", &mem_reg_prop)?;
@@ -670,11 +669,19 @@ fn create_gic_node(fdt: &mut FdtWriter, gic_device: &Arc<Mutex<dyn Vgic>>) -> Fd
if gic_device.lock().unwrap().msi_compatible() {
let msic_node = fdt.begin_node("msic")?;
fdt.property_string("compatible", gic_device.lock().unwrap().msi_compatibility())?;
let msi_compatibility = gic_device.lock().unwrap().msi_compatibility().to_string();
fdt.property_string("compatible", msi_compatibility.as_str())?;
fdt.property_null("msi-controller")?;
fdt.property_u32("phandle", MSI_PHANDLE)?;
let msi_reg_prop = gic_device.lock().unwrap().msi_properties();
fdt.property_array_u64("reg", &msi_reg_prop)?;
if msi_compatibility == GIC_V2M_COMPATIBLE {
fdt.property_u32("arm,msi-base-spi", SPI_BASE)?;
fdt.property_u32("arm,msi-num-spis", SPI_NUM)?;
}
fdt.end_node(msic_node)?;
}
@@ -701,9 +708,14 @@ fn create_clock_node(fdt: &mut FdtWriter) -> FdtWriterResult<()> {
fn create_timer_node(fdt: &mut FdtWriter) -> FdtWriterResult<()> {
// See
// https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/interrupt-controller/arch_timer.txt
// https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/timer/arm%2Carch_timer.yaml
// These are fixed interrupt numbers for the timer device.
let irqs = [13, 14, 11, 10];
let irqs = [
AARCH64_ARCH_TIMER_PHYS_SECURE_IRQ,
AARCH64_ARCH_TIMER_PHYS_NONSECURE_IRQ,
AARCH64_ARCH_TIMER_VIRT_IRQ,
AARCH64_ARCH_TIMER_HYP_IRQ,
];
let compatible = "arm,armv8-timer";
let mut timer_reg_cells: Vec<u32> = Vec::new();
@@ -1109,10 +1121,7 @@ fn print_node(node: fdt_parser::node::FdtNode<'_, '_>, n_spaces: usize) {
// - At first, try to convert it to CStr and print,
// - If failed, print it as u32 array.
let value_result = match CStr::from_bytes_with_nul(value) {
Ok(value_cstr) => match value_cstr.to_str() {
Ok(value_str) => Some(value_str),
Err(_e) => None,
},
Ok(value_cstr) => value_cstr.to_str().ok(),
Err(_e) => None,
};

View File

@@ -138,3 +138,12 @@ pub const IRQ_BASE: u32 = 32;
/// Number of supported interrupts
pub const IRQ_NUM: u32 = 256;
/// Base SPI interrupt number
pub const SPI_BASE: u32 = 32;
/// Total number of SPIs
pub const SPI_NUM: u32 = 64;
/// GICv2M compatible string
pub const GIC_V2M_COMPATIBLE: &str = "arm,gic-v2m-frame";

View File

@@ -6,8 +6,6 @@
pub mod fdt;
/// Layout for this aarch64 system.
pub mod layout;
/// Module for system registers definition
pub mod regs;
/// Module for loading UEFI binary.
pub mod uefi;
@@ -16,6 +14,7 @@ use std::fmt::Debug;
use std::sync::{Arc, Mutex};
use hypervisor::arch::aarch64::gic::Vgic;
use hypervisor::arch::aarch64::regs::MPIDR_EL1;
use log::{log_enabled, Level};
use thiserror::Error;
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic};
@@ -34,7 +33,7 @@ pub enum Error {
/// Failed to write FDT to memory.
#[error("Failed to write FDT to memory: {0}")]
WriteFdtToMemory(fdt::Error),
WriteFdtToMemory(#[source] fdt::Error),
/// Failed to create a GIC.
#[error("Failed to create a GIC")]
@@ -46,11 +45,11 @@ pub enum Error {
/// Error configuring the general purpose registers
#[error("Error configuring the general purpose registers: {0}")]
RegsConfiguration(hypervisor::HypervisorCpuError),
RegsConfiguration(#[source] hypervisor::HypervisorCpuError),
/// Error configuring the MPIDR register
#[error("Error configuring the MPIDR register: {0}")]
VcpuRegMpidr(hypervisor::HypervisorCpuError),
VcpuRegMpidr(#[source] hypervisor::HypervisorCpuError),
/// Error initializing PMU for vcpu
#[error("Error initializing PMU for vcpu")]
@@ -86,9 +85,7 @@ pub fn configure_vcpu(
.map_err(Error::RegsConfiguration)?;
}
let mpidr = vcpu
.get_sys_reg(regs::MPIDR_EL1)
.map_err(Error::VcpuRegMpidr)?;
let mpidr = vcpu.get_sys_reg(MPIDR_EL1).map_err(Error::VcpuRegMpidr)?;
Ok(mpidr)
}

View File

@@ -1,43 +0,0 @@
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
// SPDX-License-Identifier: Apache-2.0
// AArch64 system register encoding:
// See https://developer.arm.com/documentation/ddi0487 (chapter D12)
//
// 31 22 21 20 19 18 16 15 12 11 8 7 5 4 0
// +----------+---+-----+-----+-----+-----+-----+----+
// |1101010100| L | op0 | op1 | CRn | CRm | op2 | Rt |
// +----------+---+-----+-----+-----+-----+-----+----+
//
// Notes:
// - L and Rt are reserved as implementation defined fields, ignored.
const SYSREG_HEAD: u32 = 0b1101010100u32 << 22;
const SYSREG_OP0_SHIFT: u32 = 19;
const SYSREG_OP0_MASK: u32 = 0b11u32 << 19;
const SYSREG_OP1_SHIFT: u32 = 16;
const SYSREG_OP1_MASK: u32 = 0b111u32 << 16;
const SYSREG_CRN_SHIFT: u32 = 12;
const SYSREG_CRN_MASK: u32 = 0b1111u32 << 12;
const SYSREG_CRM_SHIFT: u32 = 8;
const SYSREG_CRM_MASK: u32 = 0b1111u32 << 8;
const SYSREG_OP2_SHIFT: u32 = 5;
const SYSREG_OP2_MASK: u32 = 0b111u32 << 5;
/// Define the ID of system registers
#[macro_export]
macro_rules! arm64_sys_reg {
($name: tt, $op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => {
pub const $name: u32 = SYSREG_HEAD
| ((($op0 as u32) << SYSREG_OP0_SHIFT) & SYSREG_OP0_MASK as u32)
| ((($op1 as u32) << SYSREG_OP1_SHIFT) & SYSREG_OP1_MASK as u32)
| ((($crn as u32) << SYSREG_CRN_SHIFT) & SYSREG_CRN_MASK as u32)
| ((($crm as u32) << SYSREG_CRM_SHIFT) & SYSREG_CRM_MASK as u32)
| ((($op2 as u32) << SYSREG_OP2_SHIFT) & SYSREG_OP2_MASK as u32);
};
}
arm64_sys_reg!(MPIDR_EL1, 3, 0, 0, 0, 5);
arm64_sys_reg!(ID_AA64MMFR0_EL1, 3, 0, 0, 7, 0);
arm64_sys_reg!(TTBR1_EL1, 3, 0, 2, 0, 1);
arm64_sys_reg!(TCR_EL1, 3, 0, 2, 0, 2);

View File

@@ -28,14 +28,14 @@ type GuestRegionMmap = vm_memory::GuestRegionMmap<vm_memory::bitmap::AtomicBitma
#[derive(Debug, Error)]
pub enum Error {
#[cfg(target_arch = "x86_64")]
#[error("Platform specific error (x86_64): {0:?}")]
PlatformSpecific(x86_64::Error),
#[error("Platform specific error (x86_64): {0}")]
PlatformSpecific(#[source] x86_64::Error),
#[cfg(target_arch = "aarch64")]
#[error("Platform specific error (aarch64): {0:?}")]
PlatformSpecific(aarch64::Error),
PlatformSpecific(#[source] aarch64::Error),
#[cfg(target_arch = "riscv64")]
#[error("Platform specific error (riscv64): {0:?}")]
PlatformSpecific(riscv64::Error),
PlatformSpecific(#[source] riscv64::Error),
#[error("The memory map table extends past the end of guest memory")]
MemmapTablePastRamEnd,
#[error("Error writing memory map table to guest memory")]

View File

@@ -55,7 +55,7 @@ pub trait DeviceInfoForFdt {
pub enum Error {
/// Failure in writing FDT in memory.
#[error("Failure in writing FDT in memory: {0}")]
WriteFdtToMemory(GuestMemoryError),
WriteFdtToMemory(#[source] GuestMemoryError),
}
type Result<T> = result::Result<T, Error>;

View File

@@ -31,7 +31,7 @@ pub enum Error {
/// Failed to write FDT to memory.
#[error("Failed to write FDT to memory: {0}")]
WriteFdtToMemory(fdt::Error),
WriteFdtToMemory(#[source] fdt::Error),
/// Failed to create a AIA.
#[error("Failed to create a AIA")]
@@ -43,7 +43,7 @@ pub enum Error {
/// Error configuring the general purpose registers
#[error("Error configuring the general purpose registers: {0}")]
RegsConfiguration(hypervisor::HypervisorCpuError),
RegsConfiguration(#[source] hypervisor::HypervisorCpuError),
}
impl From<Error> for super::Error {

View File

@@ -133,35 +133,35 @@ pub struct CpuidConfig {
pub enum Error {
/// Error writing MP table to memory.
#[error("Error writing MP table to memory: {0}")]
MpTableSetup(mptable::Error),
MpTableSetup(#[source] mptable::Error),
/// Error configuring the general purpose registers
#[error("Error configuring the general purpose registers: {0}")]
RegsConfiguration(regs::Error),
RegsConfiguration(#[source] regs::Error),
/// Error configuring the special registers
#[error("Error configuring the special registers: {0}")]
SregsConfiguration(regs::Error),
SregsConfiguration(#[source] regs::Error),
/// Error configuring the floating point related registers
#[error("Error configuring the floating point related registers: {0}")]
FpuConfiguration(regs::Error),
FpuConfiguration(#[source] regs::Error),
/// Error configuring the MSR registers
#[error("Error configuring the MSR registers: {0}")]
MsrsConfiguration(regs::Error),
MsrsConfiguration(#[source] regs::Error),
/// Failed to set supported CPUs.
#[error("Failed to set supported CPUs: {0}")]
SetSupportedCpusFailed(anyhow::Error),
SetSupportedCpusFailed(#[source] anyhow::Error),
/// Cannot set the local interruption due to bad configuration.
#[error("Cannot set the local interruption due to bad configuration: {0}")]
LocalIntConfiguration(anyhow::Error),
LocalIntConfiguration(#[source] anyhow::Error),
/// Error setting up SMBIOS table
#[error("Error setting up SMBIOS table: {0}")]
SmbiosSetup(smbios::Error),
SmbiosSetup(#[source] smbios::Error),
/// Could not find any SGX EPC section
#[error("Could not find any SGX EPC section")]
@@ -177,15 +177,15 @@ pub enum Error {
/// Error getting supported CPUID through the hypervisor (kvm/mshv) API
#[error("Error getting supported CPUID through the hypervisor API: {0}")]
CpuidGetSupported(HypervisorError),
CpuidGetSupported(#[source] HypervisorError),
/// Error populating CPUID with KVM HyperV emulation details
#[error("Error populating CPUID with KVM HyperV emulation details: {0}")]
CpuidKvmHyperV(vmm_sys_util::fam::Error),
CpuidKvmHyperV(#[source] vmm_sys_util::fam::Error),
/// Error populating CPUID with CPU identification
#[error("Error populating CPUID with CPU identification: {0}")]
CpuidIdentification(vmm_sys_util::fam::Error),
CpuidIdentification(#[source] vmm_sys_util::fam::Error),
/// Error checking CPUID compatibility
#[error("Error checking CPUID compatibility")]
@@ -193,16 +193,16 @@ pub enum Error {
// Error writing EBDA address
#[error("Error writing EBDA address: {0}")]
EbdaSetup(vm_memory::GuestMemoryError),
EbdaSetup(#[source] vm_memory::GuestMemoryError),
// Error getting CPU TSC frequency
#[error("Error getting CPU TSC frequency: {0}")]
GetTscFrequency(HypervisorCpuError),
GetTscFrequency(#[source] HypervisorCpuError),
/// Error retrieving TDX capabilities through the hypervisor (kvm/mshv) API
#[cfg(feature = "tdx")]
#[error("Error retrieving TDX capabilities through the hypervisor API: {0}")]
TdxCapabilities(HypervisorError),
TdxCapabilities(#[source] HypervisorError),
/// Failed to configure E820 map for bzImage
#[error("Failed to configure E820 map for bzImage")]
@@ -747,12 +747,12 @@ pub fn generate_common_cpuid(
// These features are not supported by TDX
#[cfg(feature = "tdx")]
if config.tdx {
entry.eax &= !(1 << KVM_FEATURE_CLOCKSOURCE_BIT
| 1 << KVM_FEATURE_CLOCKSOURCE2_BIT
| 1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT
| 1 << KVM_FEATURE_ASYNC_PF_BIT
| 1 << KVM_FEATURE_ASYNC_PF_VMEXIT_BIT
| 1 << KVM_FEATURE_STEAL_TIME_BIT)
entry.eax &= !((1 << KVM_FEATURE_CLOCKSOURCE_BIT)
| (1 << KVM_FEATURE_CLOCKSOURCE2_BIT)
| (1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT)
| (1 << KVM_FEATURE_ASYNC_PF_BIT)
| (1 << KVM_FEATURE_ASYNC_PF_VMEXIT_BIT)
| (1 << KVM_FEATURE_STEAL_TIME_BIT))
}
}
_ => {}
@@ -1411,6 +1411,7 @@ fn update_cpuid_topology(
u32::from(dies_per_package * cores_per_die * threads_per_core),
);
CpuidPatch::set_cpuid_reg(cpuid, 0xb, Some(1), CpuidReg::ECX, 2 << 8);
CpuidPatch::set_cpuid_reg(cpuid, 0xb, Some(1), CpuidReg::EDX, x2apic_id);
// CPU Topology leaf 0x1f
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(0), CpuidReg::EAX, thread_width);

View File

@@ -60,31 +60,31 @@ pub enum Error {
AddressOverflow,
/// Failure while zeroing out the memory for the MP table.
#[error("Failure while zeroing out the memory for the MP table: {0}")]
Clear(GuestMemoryError),
Clear(#[source] GuestMemoryError),
/// Number of CPUs exceeds the maximum supported CPUs
#[error("Number of CPUs exceeds the maximum supported CPUs")]
TooManyCpus,
/// Failure to write the MP floating pointer.
#[error("Failure to write the MP floating pointer: {0}")]
WriteMpfIntel(GuestMemoryError),
WriteMpfIntel(#[source] GuestMemoryError),
/// Failure to write MP CPU entry.
#[error("Failure to write MP CPU entry: {0}")]
WriteMpcCpu(GuestMemoryError),
WriteMpcCpu(#[source] GuestMemoryError),
/// Failure to write MP ioapic entry.
#[error("Failure to write MP ioapic entry: {0}")]
WriteMpcIoapic(GuestMemoryError),
WriteMpcIoapic(#[source] GuestMemoryError),
/// Failure to write MP bus entry.
#[error("Failure to write MP bus entry: {0}")]
WriteMpcBus(GuestMemoryError),
WriteMpcBus(#[source] GuestMemoryError),
/// Failure to write MP interrupt source entry.
#[error("Failure to write MP interrupt source entry: {0}")]
WriteMpcIntsrc(GuestMemoryError),
WriteMpcIntsrc(#[source] GuestMemoryError),
/// Failure to write MP local interrupt source entry.
#[error("Failure to write MP local interrupt source entry: {0}")]
WriteMpcLintsrc(GuestMemoryError),
WriteMpcLintsrc(#[source] GuestMemoryError),
/// Failure to write MP table header.
#[error("Failure to write MP table header: {0}")]
WriteMpcTable(GuestMemoryError),
WriteMpcTable(#[source] GuestMemoryError),
}
pub type Result<T> = result::Result<T, Error>;

View File

@@ -24,40 +24,40 @@ use crate::{EntryPoint, GuestMemoryMmap};
pub enum Error {
/// Failed to get SREGs for this CPU.
#[error("Failed to get SREGs for this CPU: {0}")]
GetStatusRegisters(hypervisor::HypervisorCpuError),
GetStatusRegisters(#[source] hypervisor::HypervisorCpuError),
/// Failed to set base registers for this CPU.
#[error("Failed to set base registers for this CPU: {0}")]
SetBaseRegisters(hypervisor::HypervisorCpuError),
SetBaseRegisters(#[source] hypervisor::HypervisorCpuError),
/// Failed to configure the FPU.
#[error("Failed to configure the FPU: {0}")]
SetFpuRegisters(hypervisor::HypervisorCpuError),
SetFpuRegisters(#[source] hypervisor::HypervisorCpuError),
/// Setting up MSRs failed.
#[error("Setting up MSRs failed: {0}")]
SetModelSpecificRegisters(hypervisor::HypervisorCpuError),
SetModelSpecificRegisters(#[source] hypervisor::HypervisorCpuError),
/// Failed to set SREGs for this CPU.
#[error("Failed to set SREGs for this CPU: {0}")]
SetStatusRegisters(hypervisor::HypervisorCpuError),
SetStatusRegisters(#[source] hypervisor::HypervisorCpuError),
/// Checking the GDT address failed.
#[error("Checking the GDT address failed")]
CheckGdtAddr,
/// Writing the GDT to RAM failed.
#[error("Writing the GDT to RAM failed: {0}")]
WriteGdt(GuestMemoryError),
WriteGdt(#[source] GuestMemoryError),
/// Writing the IDT to RAM failed.
#[error("Writing the IDT to RAM failed: {0}")]
WriteIdt(GuestMemoryError),
WriteIdt(#[source] GuestMemoryError),
/// Writing PDPTE to RAM failed.
#[error("Writing PDPTE to RAM failed: {0}")]
WritePdpteAddress(GuestMemoryError),
WritePdpteAddress(#[source] GuestMemoryError),
/// Writing PDE to RAM failed.
#[error("Writing PDE to RAM failed: {0}")]
WritePdeAddress(GuestMemoryError),
WritePdeAddress(#[source] GuestMemoryError),
/// Writing PML4 to RAM failed.
#[error("Writing PML4 to RAM failed: {0}")]
WritePml4Address(GuestMemoryError),
WritePml4Address(#[source] GuestMemoryError),
/// Writing PML5 to RAM failed.
#[error("Writing PML5 to RAM failed: {0}")]
WritePml5Address(GuestMemoryError),
WritePml5Address(#[source] GuestMemoryError),
}
pub type Result<T> = result::Result<T, Error>;

View File

@@ -34,7 +34,7 @@ pub enum Error {
WriteData,
/// Failure to parse uuid, uuid format may be error
#[error("Failure to parse uuid: {0}")]
ParseUuid(uuid::Error),
ParseUuid(#[source] uuid::Error),
}
pub type Result<T> = result::Result<T, Error>;

View File

@@ -299,7 +299,7 @@ pub struct TdHob {
}
fn align_hob(v: u64) -> u64 {
(v + 7) / 8 * 8
v.div_ceil(8) * 8
}
impl TdHob {

View File

@@ -2,6 +2,9 @@
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use std::marker::PhantomData;
use std::os::fd::{AsRawFd, OwnedFd, RawFd};
use thiserror::Error;
use vmm_sys_util::eventfd::EventFd;
@@ -19,12 +22,50 @@ pub enum DiskFileError {
pub type DiskFileResult<T> = std::result::Result<T, DiskFileError>;
/// A wrapper for [`RawFd`] capturing the lifetime of a corresponding [`DiskFile`].
///
/// This fulfills the same role as [`BorrowedFd`] but is tailored to the limitations
/// by some implementations of [`DiskFile`], which wrap the effective [`File`]
/// in an `Arc<Mutex<T>>`, making the use of [`BorrowedFd`] impossible.
///
/// [`BorrowedFd`]: std::os::fd::BorrowedFd
#[derive(Copy, Clone, Debug)]
pub struct BorrowedDiskFd<'fd> {
raw_fd: RawFd,
_lifetime: PhantomData<&'fd OwnedFd>,
}
impl BorrowedDiskFd<'_> {
pub(super) fn new(raw_fd: RawFd) -> Self {
Self {
raw_fd,
_lifetime: PhantomData,
}
}
}
impl AsRawFd for BorrowedDiskFd<'_> {
fn as_raw_fd(&self) -> RawFd {
self.raw_fd
}
}
/// Abstraction over the effective [`File`] backing up a block device,
/// with support for synchronous and asynchronous I/O.
///
/// This allows abstracting over raw image formats as well as structured
/// image formats.
pub trait DiskFile: Send {
fn size(&mut self) -> DiskFileResult<u64>;
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>>;
fn topology(&mut self) -> DiskTopology {
DiskTopology::default()
}
/// Returns the file descriptor of the underlying disk image file.
///
/// The file descriptor is supposed to be used for `fcntl()` calls but no
/// other operation.
fn fd(&mut self) -> BorrowedDiskFd;
}
#[derive(Error, Debug)]

173
block/src/fcntl.rs Normal file
View File

@@ -0,0 +1,173 @@
// Copyright © 2025 Cyberus Technology GmbH
//
// SPDX-License-Identifier: Apache-2.0
//
//! Helpers for advisory file locking.
//!
//! Under the hood, the implementation uses OFD locks for the entire file,
//! as described in [[0]]. The advantage over `F_SETLKW` (currently used by
//! Rust std: `File::try_lock()`) is that only the very last `close()` on a
//! file descriptor releases the lock. This prevents mistakes and unexpected
//! behavior.
//!
//! [0]: <https://apenwarr.ca/log/20101213>.
use std::fmt::Debug;
use std::io;
use std::os::fd::{AsRawFd, RawFd};
use thiserror::Error;
/// Errors that can happen when working with file locks.
#[derive(Error, Debug)]
pub enum LockError {
/// The file is already locked.
///
/// A call to [`get_lock_state`] can help to identify the reason.
#[error("The file is already locked")]
AlreadyLocked,
/// IO error.
#[error("The lock state could not be checked or set: {0}")]
Io(#[source] io::Error),
}
/// Commands for use with [`fcntl`].
#[allow(non_camel_case_types)]
enum FcntlArg<'a> {
/// Set an OFD lock from the given lock description.
F_OFD_SETLK(&'a libc::flock),
/// Get the first OFD lock for the given lock description.
F_OFD_GETLK(&'a mut libc::flock),
}
/// Wrapper for [`libc::fcntl`] that properly sets the function arguments.
fn fcntl(fd: RawFd, arg: FcntlArg) -> libc::c_int {
// SAFETY: We use a valid FD.
unsafe {
match arg {
FcntlArg::F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock),
FcntlArg::F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock),
}
}
}
/// Describes the type of lock you want to set.
#[derive(Clone, Copy, Debug)]
pub enum LockType {
/// Clear a lock.
Unlock,
/// Set a write lock (exclusive).
Write,
/// Set a read lock (shared).
Read,
}
impl LockType {
pub const fn to_libc_val(self) -> libc::c_int {
match self {
Self::Unlock => libc::F_UNLCK as libc::c_int,
Self::Write => libc::F_WRLCK as libc::c_int,
Self::Read => libc::F_RDLCK as libc::c_int,
}
}
}
/// Describes the current state of a lock.
#[derive(Debug)]
pub enum LockState {
/// No lock set.
Unlocked,
/// Locked for reading (non-exclusive).
SharedRead,
/// Locked for writing (exclusive mode).
ExclusiveWrite,
}
impl LockState {
fn new(value: libc::c_int) -> Self {
const F_UNLCK: libc::c_int = libc::F_UNLCK as libc::c_int;
const F_WRLCK: libc::c_int = libc::F_WRLCK as libc::c_int;
const F_RDLCK: libc::c_int = libc::F_RDLCK as libc::c_int;
match value {
F_UNLCK => Self::Unlocked,
F_WRLCK => Self::ExclusiveWrite,
F_RDLCK => Self::SharedRead,
// This is so unlikely that we want to avoid the complexity of
// coping with this error case. Can only fail if either Linux
// is broken or memory is messed up.
other => panic!("Unexpected lock state: {other}"),
}
}
}
/// Returns a [`struct@libc::flock`] structure for the whole file.
const fn get_flock(lock_type: LockType) -> libc::flock {
libc::flock {
l_type: lock_type.to_libc_val() as libc::c_short,
l_whence: libc::SEEK_SET as libc::c_short,
l_start: 0,
l_len: 0, /* EOF */
l_pid: 0, /* filled by callee */
}
}
/// Tries to acquire a lock using [`fcntl`] with respect to the given
/// parameters.
///
/// Please note that `fcntl()` OFD locks are **advisory locks**, which do not
/// prevent to `open()` a file if a lock is already placed.
///
/// # Parameters
/// - `file`: The file to acquire a lock for [`LockType`]. The file's state will
/// be logically mutated, but not technically.
/// - `lock_type`: The [`LockType`]
pub fn try_acquire_lock<Fd: AsRawFd>(file: Fd, lock_type: LockType) -> Result<(), LockError> {
let flock = get_flock(lock_type);
let res = fcntl(file.as_raw_fd(), FcntlArg::F_OFD_SETLK(&flock));
match res {
0 => Ok(()),
-1 => {
let io_error = io::Error::last_os_error();
let errno = io_error.raw_os_error().unwrap();
match errno {
// See man page for error code:
// <https://man7.org/linux/man-pages/man2/fcntl.2.html>
libc::EAGAIN | libc::EACCES => Err(LockError::AlreadyLocked),
_ => Err(LockError::Io(io_error)),
}
}
val => panic!("Unexpected return value from fcntl(): {val}"),
}
}
/// Clears a lock.
///
/// # Parameters
/// - `file`: The file to clear all locks for [`LockType`].
pub fn clear_lock<Fd: AsRawFd>(file: Fd) -> Result<(), LockError> {
try_acquire_lock(file, LockType::Unlock)
}
/// Returns the current lock state using [`fcntl`] with respect to the given
/// parameters.
///
/// # Parameters
/// - `file`: The file for which to get the lock state.
pub fn get_lock_state<Fd: AsRawFd>(file: Fd) -> Result<LockState, LockError> {
let mut flock = get_flock(LockType::Write);
let res = fcntl(file.as_raw_fd(), FcntlArg::F_OFD_GETLK(&mut flock));
match res {
0 => {
let state = flock.l_type as libc::c_int;
let state = LockState::new(state);
Ok(state)
}
-1 => {
let io_error = io::Error::last_os_error();
Err(LockError::Io(io_error))
}
val => panic!("Unexpected return value from fcntl(): {val}"),
}
}

View File

@@ -8,7 +8,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
use vmm_sys_util::eventfd::EventFd;
use crate::async_io::{
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
};
use crate::fixed_vhd::FixedVhd;
use crate::raw_async::RawFileAsync;
@@ -33,6 +33,10 @@ impl DiskFile for FixedVhdDiskAsync {
.map_err(DiskFileError::NewAsyncIo)?,
) as Box<dyn AsyncIo>)
}
fn fd(&mut self) -> BorrowedDiskFd {
BorrowedDiskFd::new(self.0.as_raw_fd())
}
}
pub struct FixedVhdAsync {

View File

@@ -8,7 +8,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
use vmm_sys_util::eventfd::EventFd;
use crate::async_io::{
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
};
use crate::fixed_vhd::FixedVhd;
use crate::raw_sync::RawFileSync;
@@ -33,6 +33,10 @@ impl DiskFile for FixedVhdDiskSync {
.map_err(DiskFileError::NewAsyncIo)?,
) as Box<dyn AsyncIo>)
}
fn fd(&mut self) -> BorrowedDiskFd {
BorrowedDiskFd::new(self.0.as_raw_fd())
}
}
pub struct FixedVhdSync {

View File

@@ -12,6 +12,7 @@
extern crate log;
pub mod async_io;
pub mod fcntl;
pub mod fixed_vhd;
#[cfg(feature = "io_uring")]
/// Enabled with the `"io_uring"` feature
@@ -67,9 +68,9 @@ pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
#[derive(Error, Debug)]
pub enum Error {
#[error("Guest gave us bad memory addresses")]
GuestMemory(GuestMemoryError),
GuestMemory(#[source] GuestMemoryError),
#[error("Guest gave us offsets that would have overflowed a usize")]
CheckedOffset(GuestAddress, usize),
CheckedOffset(GuestAddress, usize /* sector offset */),
#[error("Guest gave us a write only descriptor that protocol says to read from")]
UnexpectedWriteOnlyDescriptor,
#[error("Guest gave us a read only descriptor that protocol says to write to")]
@@ -79,21 +80,21 @@ pub enum Error {
#[error("Guest gave us a descriptor that was too short to use")]
DescriptorLengthTooSmall,
#[error("Failed to detect image type: {0}")]
DetectImageType(std::io::Error),
DetectImageType(#[source] std::io::Error),
#[error("Failure in fixed vhd: {0}")]
FixedVhdError(std::io::Error),
FixedVhdError(#[source] std::io::Error),
#[error("Getting a block's metadata fails for any reason")]
GetFileMetadata,
#[error("The requested operation would cause a seek beyond disk end")]
InvalidOffset,
#[error("Failure in qcow: {0}")]
QcowError(qcow::Error),
QcowError(#[source] qcow::Error),
#[error("Failure in raw file: {0}")]
RawFileError(std::io::Error),
RawFileError(#[source] std::io::Error),
#[error("The requested operation does not support multiple descriptors")]
TooManyDescriptors,
#[error("Failure in vhdx: {0}")]
VhdxError(VhdxError),
VhdxError(#[source] VhdxError),
}
fn build_device_id(disk_path: &Path) -> result::Result<String, Error> {
@@ -131,33 +132,33 @@ pub fn build_serial(disk_path: &Path) -> Vec<u8> {
#[derive(Error, Debug)]
pub enum ExecuteError {
#[error("Bad request: {0}")]
BadRequest(Error),
BadRequest(#[source] Error),
#[error("Failed to flush: {0}")]
Flush(io::Error),
Flush(#[source] io::Error),
#[error("Failed to read: {0}")]
Read(GuestMemoryError),
Read(#[source] GuestMemoryError),
#[error("Failed to read_exact: {0}")]
ReadExact(io::Error),
ReadExact(#[source] io::Error),
#[error("Failed to seek: {0}")]
Seek(io::Error),
Seek(#[source] io::Error),
#[error("Failed to write: {0}")]
Write(GuestMemoryError),
Write(#[source] GuestMemoryError),
#[error("Failed to write_all: {0}")]
WriteAll(io::Error),
WriteAll(#[source] io::Error),
#[error("Unsupported request: {0}")]
Unsupported(u32),
#[error("Failed to submit io uring: {0}")]
SubmitIoUring(io::Error),
SubmitIoUring(#[source] io::Error),
#[error("Failed to get guest address: {0}")]
GetHostAddress(GuestMemoryError),
GetHostAddress(#[source] GuestMemoryError),
#[error("Failed to async read: {0}")]
AsyncRead(AsyncIoError),
AsyncRead(#[source] AsyncIoError),
#[error("Failed to async write: {0}")]
AsyncWrite(AsyncIoError),
AsyncWrite(#[source] AsyncIoError),
#[error("failed to async flush: {0}")]
AsyncFlush(AsyncIoError),
AsyncFlush(#[source] AsyncIoError),
#[error("Failed allocating a temporary buffer: {0}")]
TemporaryBufferAllocation(io::Error),
TemporaryBufferAllocation(#[source] io::Error),
}
impl ExecuteError {

View File

@@ -13,6 +13,7 @@ use std::cmp::{max, min};
use std::fs::OpenOptions;
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::mem::size_of;
use std::os::fd::{AsRawFd, RawFd};
use std::str;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
@@ -36,23 +37,23 @@ const MAX_NESTING_DEPTH: u32 = 10;
#[derive(Debug, Error)]
pub enum Error {
#[error("Backing file io error: {0}")]
BackingFileIo(io::Error),
BackingFileIo(#[source] io::Error),
#[error("Backing file open error: {0}")]
BackingFileOpen(Box<Error>),
BackingFileOpen(#[source] Box<Error>),
#[error("Backing file name is too long: {0} bytes over")]
BackingFileTooLong(usize),
#[error("Compressed blocks not supported")]
CompressedBlocksNotSupported,
#[error("Failed to evict cache: {0}")]
EvictingCache(io::Error),
EvictingCache(#[source] io::Error),
#[error("File larger than max of {MAX_QCOW_FILE_SIZE}: {0}")]
FileTooBig(u64),
#[error("Failed to get file size: {0}")]
GettingFileSize(io::Error),
GettingFileSize(#[source] io::Error),
#[error("Failed to get refcount: {0}")]
GettingRefcount(refcount::Error),
GettingRefcount(#[source] refcount::Error),
#[error("Failed to parse filename: {0}")]
InvalidBackingFileName(str::Utf8Error),
InvalidBackingFileName(#[source] str::Utf8Error),
#[error("Invalid cluster index")]
InvalidClusterIndex,
#[error("Invalid cluster size")]
@@ -80,29 +81,29 @@ pub enum Error {
#[error("Not enough space for refcounts")]
NotEnoughSpaceForRefcounts,
#[error("Failed to open file {0}")]
OpeningFile(io::Error),
OpeningFile(#[source] io::Error),
#[error("Failed to read data: {0}")]
ReadingData(io::Error),
ReadingData(#[source] io::Error),
#[error("Failed to read header: {0}")]
ReadingHeader(io::Error),
ReadingHeader(#[source] io::Error),
#[error("Failed to read pointers: {0}")]
ReadingPointers(io::Error),
ReadingPointers(#[source] io::Error),
#[error("Failed to read ref count block: {0}")]
ReadingRefCountBlock(refcount::Error),
ReadingRefCountBlock(#[source] refcount::Error),
#[error("Failed to read ref counts: {0}")]
ReadingRefCounts(io::Error),
ReadingRefCounts(#[source] io::Error),
#[error("Failed to rebuild ref counts: {0}")]
RebuildingRefCounts(io::Error),
RebuildingRefCounts(#[source] io::Error),
#[error("Refcount table offset past file end")]
RefcountTableOffEnd,
#[error("Too many clusters specified for refcount")]
RefcountTableTooLarge,
#[error("Failed to seek file: {0}")]
SeekingFile(io::Error),
SeekingFile(#[source] io::Error),
#[error("Failed to set file size: {0}")]
SettingFileSize(io::Error),
SettingFileSize(#[source] io::Error),
#[error("Failed to set refcount refcount: {0}")]
SettingRefcountRefcount(io::Error),
SettingRefcountRefcount(#[source] io::Error),
#[error("Size too small for number of clusters")]
SizeTooSmallForNumberOfClusters,
#[error("L1 entry table too large: {0}")]
@@ -114,9 +115,9 @@ pub enum Error {
#[error("Unsupported version: {0}")]
UnsupportedVersion(u32),
#[error("Failed to write data: {0}")]
WritingData(io::Error),
WritingData(#[source] io::Error),
#[error("Failed to write header: {0}")]
WritingHeader(io::Error),
WritingHeader(#[source] io::Error),
}
pub type Result<T> = std::result::Result<T, Error>;
@@ -378,7 +379,7 @@ impl QcowHeader {
}
if let Some(backing_file_path) = self.backing_file_path.as_ref() {
write!(file, "{}", backing_file_path).map_err(Error::WritingHeader)?;
write!(file, "{backing_file_path}").map_err(Error::WritingHeader)?;
}
// Set the file length by seeking and writing a zero to the last byte. This avoids needing
@@ -502,7 +503,7 @@ impl QcowFile {
if refcount_bits != 16 {
return Err(Error::UnsupportedRefcountOrder);
}
let refcount_bytes = (refcount_bits + 7) / 8;
let refcount_bytes = refcount_bits.div_ceil(8);
// Need at least one refcount cluster
if header.refcount_table_clusters == 0 {
@@ -1516,6 +1517,12 @@ impl QcowFile {
}
}
impl AsRawFd for QcowFile {
fn as_raw_fd(&self) -> RawFd {
self.raw_file.as_raw_fd()
}
}
impl Drop for QcowFile {
fn drop(&mut self) {
let _ = self.sync_caches();
@@ -1625,8 +1632,7 @@ impl FileSync for QcowFile {
impl FileSetLen for QcowFile {
fn set_len(&self, _len: u64) -> std::io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
Err(std::io::Error::other(
"set_len() not supported for QcowFile",
))
}

View File

@@ -6,6 +6,7 @@
use std::io::{self, BufWriter, Seek, SeekFrom, Write};
use std::mem::size_of;
use std::os::fd::{AsRawFd, RawFd};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use vmm_sys_util::write_zeroes::WriteZeroes;
@@ -159,3 +160,9 @@ impl Clone for QcowRawFile {
}
}
}
impl AsRawFd for QcowRawFile {
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}

View File

@@ -369,3 +369,9 @@ impl Clone for RawFile {
}
}
}
impl AsRawFd for RawFile {
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}

View File

@@ -16,7 +16,7 @@ use crate::qcow::vec_cache::{CacheMap, Cacheable, VecCache};
pub enum Error {
/// `EvictingCache` - Error writing a refblock from the cache to disk.
#[error("Failed to write a refblock from the cache to disk: {0}")]
EvictingRefCounts(io::Error),
EvictingRefCounts(#[source] io::Error),
/// `InvalidIndex` - Address requested isn't within the range of the disk.
#[error("Address requested is not within the range of the disk")]
InvalidIndex,
@@ -28,7 +28,7 @@ pub enum Error {
NeedNewCluster,
/// `ReadingRefCounts` - Error reading the file into the refcount cache.
#[error("Failed to read the file into the refcount cache: {0}")]
ReadingRefCounts(io::Error),
ReadingRefCounts(#[source] io::Error),
}
pub type Result<T> = std::result::Result<T, Error>;

View File

@@ -5,11 +5,14 @@
use std::collections::VecDeque;
use std::fs::File;
use std::io::{Seek, SeekFrom};
use std::os::fd::AsRawFd;
use std::sync::{Arc, Mutex, MutexGuard};
use vmm_sys_util::eventfd::EventFd;
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult};
use crate::async_io::{
AsyncIo, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
};
use crate::qcow::{QcowFile, RawFile, Result as QcowResult};
use crate::AsyncAdaptor;
@@ -35,6 +38,11 @@ impl DiskFile for QcowDiskSync {
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
Ok(Box::new(QcowSync::new(self.qcow_file.clone())) as Box<dyn AsyncIo>)
}
fn fd(&mut self) -> BorrowedDiskFd {
let lock = self.qcow_file.lock().unwrap();
BorrowedDiskFd::new(lock.as_raw_fd())
}
}
pub struct QcowSync {

View File

@@ -10,7 +10,7 @@ use io_uring::{opcode, types, IoUring};
use vmm_sys_util::eventfd::EventFd;
use crate::async_io::{
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
};
use crate::DiskTopology;
@@ -46,6 +46,10 @@ impl DiskFile for RawFileDisk {
DiskTopology::default()
}
}
fn fd(&mut self) -> BorrowedDiskFd {
BorrowedDiskFd::new(self.file.as_raw_fd())
}
}
pub struct RawFileAsync {

View File

@@ -13,7 +13,7 @@ use vmm_sys_util::aio;
use vmm_sys_util::eventfd::EventFd;
use crate::async_io::{
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
};
use crate::DiskTopology;
@@ -49,6 +49,10 @@ impl DiskFile for RawFileDiskAio {
DiskTopology::default()
}
}
fn fd(&mut self) -> BorrowedDiskFd {
BorrowedDiskFd::new(self.file.as_raw_fd())
}
}
pub struct RawFileAsyncAio {

View File

@@ -10,7 +10,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
use vmm_sys_util::eventfd::EventFd;
use crate::async_io::{
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
};
use crate::DiskTopology;
@@ -43,6 +43,10 @@ impl DiskFile for RawFileDiskSync {
DiskTopology::default()
}
}
fn fd(&mut self) -> BorrowedDiskFd {
BorrowedDiskFd::new(self.file.as_raw_fd())
}
}
pub struct RawFileSync {

View File

@@ -5,6 +5,7 @@
use std::collections::btree_map::BTreeMap;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom, Write};
use std::os::fd::{AsRawFd, RawFd};
use byteorder::{BigEndian, ByteOrder};
use remain::sorted;
@@ -25,19 +26,19 @@ mod vhdx_metadata;
#[sorted]
#[derive(Error, Debug)]
pub enum VhdxError {
#[error("Not a VHDx file {0}")]
#[error("Not a VHDx file: {0}")]
NotVhdx(#[source] VhdxHeaderError),
#[error("Failed to parse VHDx header {0}")]
#[error("Failed to parse VHDx header: {0}")]
ParseVhdxHeader(#[source] VhdxHeaderError),
#[error("Failed to parse VHDx metadata {0}")]
#[error("Failed to parse VHDx metadata: {0}")]
ParseVhdxMetadata(#[source] VhdxMetadataError),
#[error("Failed to parse VHDx region entries {0}")]
#[error("Failed to parse VHDx region entries: {0}")]
ParseVhdxRegionEntry(#[source] VhdxHeaderError),
#[error("Failed reading metadata {0}")]
#[error("Failed reading metadata: {0}")]
ReadBatEntry(#[source] VhdxBatError),
#[error("Failed reading sector from disk {0}")]
#[error("Failed reading sector from disk: {0}")]
ReadFailed(#[source] VhdxIoError),
#[error("Failed writing to sector on disk {0}")]
#[error("Failed writing to sector on disk: {0}")]
WriteFailed(#[source] VhdxIoError),
}
@@ -111,12 +112,9 @@ impl Read for Vhdx {
sector_count,
)
.map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"Failed reading {sector_count} sectors from VHDx at index {sector_index}: {e}"
),
)
std::io::Error::other(format!(
"Failed reading {sector_count} sectors from VHDx at index {sector_index}: {e}"
))
})?;
self.current_offset = self.current_offset.checked_add(result as u64).unwrap();
@@ -138,12 +136,9 @@ impl Write for Vhdx {
if self.first_write {
self.first_write = false;
self.vhdx_header.update(&mut self.file).map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!("Failed to update VHDx header: {e}"),
)
})?;
self.vhdx_header
.update(&mut self.file)
.map_err(|e| std::io::Error::other(format!("Failed to update VHDx header: {e}")))?;
}
let result = vhdx_io::write(
@@ -156,12 +151,9 @@ impl Write for Vhdx {
sector_count,
)
.map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"Failed writing {sector_count} sectors on VHDx at index {sector_index}: {e}"
),
)
std::io::Error::other(format!(
"Failed writing {sector_count} sectors on VHDx at index {sector_index}: {e}"
))
})?;
self.current_offset = self.current_offset.checked_add(result as u64).unwrap();
@@ -231,6 +223,12 @@ impl Clone for Vhdx {
}
}
impl AsRawFd for Vhdx {
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}
pub(crate) fn uuid_from_guid(buf: &[u8]) -> Uuid {
// The first 3 fields of UUID are stored in Big Endian format, and
// the last 8 bytes are stored as byte array. Therefore, we read the

View File

@@ -33,9 +33,9 @@ pub enum VhdxBatError {
InvalidBatEntry,
#[error("Invalid BAT entry count")]
InvalidEntryCount,
#[error("Failed to read BAT entry {0}")]
#[error("Failed to read BAT entry: {0}")]
ReadBat(#[source] io::Error),
#[error("Failed to write BAT entry {0}")]
#[error("Failed to write BAT entry: {0}")]
WriteBat(#[source] io::Error),
}

View File

@@ -4,11 +4,14 @@
use std::collections::VecDeque;
use std::fs::File;
use std::os::fd::AsRawFd;
use std::sync::{Arc, Mutex, MutexGuard};
use vmm_sys_util::eventfd::EventFd;
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult};
use crate::async_io::{
AsyncIo, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
};
use crate::vhdx::{Result as VhdxResult, Vhdx};
use crate::AsyncAdaptor;
@@ -35,6 +38,11 @@ impl DiskFile for VhdxDiskSync {
as Box<dyn AsyncIo>,
)
}
fn fd(&mut self) -> BorrowedDiskFd {
let lock = self.vhdx_file.lock().unwrap();
BorrowedDiskFd::new(lock.as_raw_fd())
}
}
pub struct VhdxSync {

View File

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

View File

@@ -8,7 +8,7 @@ version = "0.1.0"
acpi_tables = { workspace = true }
anyhow = "1.0.94"
arch = { path = "../arch" }
bitflags = "2.6.0"
bitflags = "2.9.0"
byteorder = "1.5.0"
event_monitor = { path = "../event_monitor" }
hypervisor = { path = "../hypervisor" }

View File

@@ -9,8 +9,8 @@ use std::sync::{Arc, Mutex};
use anyhow::anyhow;
use arch::layout;
use hypervisor::arch::aarch64::gic::{Vgic, VgicConfig};
use hypervisor::{CpuState, GicState};
use hypervisor::arch::aarch64::gic::{GicState, Vgic, VgicConfig};
use hypervisor::CpuState;
use vm_device::interrupt::{
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
LegacyIrqSourceConfig, MsiIrqGroupConfig,

View File

@@ -18,38 +18,38 @@ pub enum Error {
InvalidDeliveryMode,
/// Failed creating the interrupt source group.
#[error("Failed creating the interrupt source group: {0}")]
CreateInterruptSourceGroup(io::Error),
CreateInterruptSourceGroup(#[source] io::Error),
/// Failed triggering the interrupt.
#[error("Failed triggering the interrupt: {0}")]
TriggerInterrupt(io::Error),
TriggerInterrupt(#[source] io::Error),
/// Failed masking the interrupt.
#[error("Failed masking the interrupt: {0}")]
MaskInterrupt(io::Error),
MaskInterrupt(#[source] io::Error),
/// Failed unmasking the interrupt.
#[error("Failed unmasking the interrupt: {0}")]
UnmaskInterrupt(io::Error),
UnmaskInterrupt(#[source] io::Error),
/// Failed updating the interrupt.
#[error("Failed updating the interrupt: {0}")]
UpdateInterrupt(io::Error),
UpdateInterrupt(#[source] io::Error),
/// Failed enabling the interrupt.
#[error("Failed enabling the interrupt: {0}")]
EnableInterrupt(io::Error),
EnableInterrupt(#[source] io::Error),
#[cfg(target_arch = "aarch64")]
/// Failed creating GIC device.
#[error("Failed creating GIC device: {0}")]
CreateGic(hypervisor::HypervisorVmError),
CreateGic(#[source] hypervisor::HypervisorVmError),
#[cfg(target_arch = "aarch64")]
/// Failed restoring GIC device.
#[error("Failed restoring GIC device: {0}")]
RestoreGic(hypervisor::arch::aarch64::gic::Error),
RestoreGic(#[source] hypervisor::arch::aarch64::gic::Error),
#[cfg(target_arch = "riscv64")]
/// Failed creating AIA device.
#[error("Failed creating AIA device: {0}")]
CreateAia(hypervisor::HypervisorVmError),
CreateAia(#[source] hypervisor::HypervisorVmError),
#[cfg(target_arch = "riscv64")]
/// Failed restoring AIA device.
#[error("Failed restoring AIA device: {0}")]
RestoreAia(hypervisor::arch::riscv64::aia::Error),
RestoreAia(#[source] hypervisor::arch::riscv64::aia::Error),
}
type Result<T> = result::Result<T, Error>;

View File

@@ -418,7 +418,7 @@ impl InterruptController for Ioapic {
self.interrupt_source_group
.trigger(irq as InterruptIndex)
.map_err(Error::TriggerInterrupt)?;
debug!("Interrupt {irq} successfully delivered");
trace!("Interrupt {irq} successfully delivered");
// If trigger mode is level sensitive, set the Remote IRR bit.
// It will be cleared when the EOI is received.

View File

@@ -43,11 +43,11 @@ const N_GPIOS: u32 = 8;
pub enum Error {
#[error("Bad Write Offset: {0}")]
BadWriteOffset(u64),
#[error("GPIO interrupt disabled by guest driver.")]
#[error("GPIO interrupt disabled by guest driver")]
GpioInterruptDisabled,
#[error("Could not trigger GPIO interrupt: {0}.")]
GpioInterruptFailure(io::Error),
#[error("Invalid GPIO Input key triggered: {0}.")]
#[error("Could not trigger GPIO interrupt: {0}")]
GpioInterruptFailure(#[source] io::Error),
#[error("Invalid GPIO Input key triggered: {0}")]
GpioTriggerKeyFailure(u32),
}

View File

@@ -46,7 +46,7 @@ pub enum Error {
#[error("Bad Write Offset: {0}")]
BadWriteOffset(u64),
#[error("Failed to trigger interrupt: {0}")]
InterruptFailure(io::Error),
InterruptFailure(#[source] io::Error),
}
type Result<T> = result::Result<T, Error>;

View File

@@ -51,14 +51,14 @@ const AMBA_ID_HIGH: u64 = 0x401;
pub enum Error {
#[error("pl011_write: Bad Write Offset: {0}")]
BadWriteOffset(u64),
#[error("pl011: DMA not implemented.")]
#[error("pl011: DMA not implemented")]
DmaNotImplemented,
#[error("Failed to trigger interrupt: {0}")]
InterruptFailure(io::Error),
InterruptFailure(#[source] io::Error),
#[error("Failed to write: {0}")]
WriteAllFailure(io::Error),
WriteAllFailure(#[source] io::Error),
#[error("Failed to flush: {0}")]
FlushFailure(io::Error),
FlushFailure(#[source] io::Error),
}
type Result<T> = result::Result<T, Error>;

View File

@@ -484,7 +484,7 @@ impl PvmemcontrolBusDevice {
}
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 = (name != 0).then(|| CString::new(format!("pvmemcontrol-{name}")).unwrap());
let name_ptr = if let Some(name) = &name {
name.as_ptr()
} else {
@@ -698,10 +698,12 @@ impl PciDevice for PvmemcontrolPciDevice {
reg_idx: usize,
offset: u64,
data: &[u8],
) -> Option<Arc<Barrier>> {
self.configuration
.write_config_register(reg_idx, offset, data);
None
) -> (Vec<BarReprogrammingParams>, Option<Arc<Barrier>>) {
(
self.configuration
.write_config_register(reg_idx, offset, data),
None,
)
}
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
@@ -716,14 +718,6 @@ impl PciDevice for PvmemcontrolPciDevice {
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>>,

View File

@@ -88,7 +88,11 @@ impl PvPanicDevice {
);
let command: [u8; 2] = [0x03, 0x01];
configuration.write_config_register(1, 0, &command);
let bar_reprogram = configuration.write_config_register(1, 0, &command);
assert!(
bar_reprogram.is_empty(),
"No bar reprogrammig is expected from writing to the COMMAND register"
);
let state: Option<PvPanicDeviceState> = snapshot
.as_ref()
@@ -156,24 +160,18 @@ impl PciDevice for PvPanicDevice {
reg_idx: usize,
offset: u64,
data: &[u8],
) -> Option<Arc<Barrier>> {
self.configuration
.write_config_register(reg_idx, offset, data);
None
) -> (Vec<BarReprogrammingParams>, 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_reg(reg_idx)
}
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>>,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

168
docs/riscv.md Normal file
View File

@@ -0,0 +1,168 @@
# How to build and test Cloud Hypervisor on riscv64
This document introduces how to build and test Cloud Hypervisor on `riscv64`.
All instructions here are tested with Ubuntu 24.04.2 as the host OS.
## Hardware requirements
- riscv64 servers (recommended) or development boards equipped with the AIA
(Advance Interrupt Architecture) interrupt controller.
## Getting started
We create a folder to build and run Cloud Hypervisor at `$HOME/cloud-hypervisor`
```console
export CLOUDH=$HOME/cloud-hypervisor
mkdir $CLOUDH
```
## Prerequisites
You need to install some prerequisite packages to build and test Cloud Hypervisor.
### Tools
```console
# Install rust tool chain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Install the tools used for building guest kernel, EDK2 and converting guest disk
sudo apt-get update
sudo apt-get install git build-essential m4 bison flex uuid-dev qemu-utils
```
### Building Cloud Hypervisor
```console
pushd $CLOUDH
git clone https://github.com/cloud-hypervisor/cloud-hypervisor.git
cd cloud-hypervisor
cargo build
popd
```
### Disk image
Download the Ubuntu cloud image and convert the image type.
```console
pushd $CLOUDH
wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-riscv64.img
qemu-img convert -p -f qcow2 -O raw jammy-server-cloudimg-riscv64.img jammy-server-cloudimg-riscv64.raw
popd
```
## Direct-kernel booting
### Building kernel
```console
pushd $CLOUDH
git clone --depth 1 "https://github.com/cloud-hypervisor/linux.git" -b ch-6.12.8
cd linux
make ch_defconfig
make -j `nproc`
popd
```
### Booting the guest VM
```console
pushd $CLOUDH
sudo $CLOUDH/cloud-hypervisor/target/debug/cloud-hypervisor \
--kernel $CLOUDH/linux/arch/riscv64/boot/Image \
--disk path=jammy-server-cloudimg-riscv64.raw \
--cmdline "console=hvc0 root=/dev/vda rw" \
--cpus boot=1 \
--memory size=1024M \
--seccomp false \
--log-file boot.log -vv
popd
```
## Virtualized Development Setup
Since there are few RISC-V development boards on the market and not
many details about the AIA interrupt controller featured in product listings,
QEMU is a popular and viable choice for creating a RISC-V development environment.
Below are the steps used to create a QEMU virtual machine that can be used for
cloud-hypervisor RISC-V development:
### Install Dependencies
```console
sudo apt update
sudo apt install opensbi qemu-system-misc u-boot-qemu
```
### Download and Build QEMU (>=v9.2.0)
Older versions of QEMU may not have support for the AIA
interrupt controller.
```console
wget https://download.qemu.org/qemu-10.0.0.tar.xz
tar xvJf qemu-10.0.0.tar.xz
cd qemu-10.0.0
./configure --target-list=riscv64-softmmu
make -j $(nproc)
sudo make install
```
### Download Ubuntu Server Image
At the time of writing, the best results have been seen with
the Ubuntu 24.10 (Oracular) server image. Ex:
```console
wget https://cdimage.ubuntu.com/releases/oracular/release/ubuntu-24.10-preinstalled-server-riscv64.img.xz
xz -dk ubuntu-24.10-preinstalled-server-riscv64.img.xz
```
### (Optional) Resize Disk
If you would like a larger disk, you can resize it now.
```console
qemu-img resize -f raw <ubuntu-image> +5G
```
### Boot VM
Note the inclusion of the AIA interrupt controller in the
invocation.
```console
qemu-system-riscv64 \
-machine virt,aia=aplic-imsic \
-nographic -m 1G -smp 8 \
-kernel /usr/lib/u-boot/qemu-riscv64_smode/uboot.elf \
-device virtio-rng-pci \
-device virtio-net-device,netdev=eth0 -netdev user,id=eth0 \
-drive file=<ubuntu-image>,format=raw,if=virtio
```
### Install KVM Kernel Module Within VM
KVM is not enabled within the VM by default, so we must enable
it manually.
```console
sudo modprobe kvm
```
From this point, you can continue with the above steps from the beginning.
### Sources
https://risc-v-getting-started-guide.readthedocs.io/en/latest/linux-qemu.html
https://canonical-ubuntu-boards.readthedocs-hosted.com/en/latest/how-to/qemu-riscv/#using-the-live-server-image
https://www.qemu.org/docs/master/specs/riscv-aia.html
## Known limitations
- Direct kernel boot only
- `64-bit Linux` guest OS only
- For more details, see
[here](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/6978).

View File

@@ -9,4 +9,4 @@ flume = "0.11.1"
libc = "0.2.167"
once_cell = "1.20.2"
serde = { version = "1.0.208", features = ["derive", "rc"] }
serde_json = "1.0.120"
serde_json = { workspace = true }

141
fuzz/Cargo.lock generated
View File

@@ -5,9 +5,9 @@ version = 4
[[package]]
name = "acpi_tables"
version = "0.1.0"
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#849d5950196f66dd10f2b2606d8fe8c7cb39ec24"
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#e08a3f0b0a59b98859dbf59f5aa7fd4d2eb4018a"
dependencies = [
"zerocopy 0.7.35",
"zerocopy 0.8.24",
]
[[package]]
@@ -103,6 +103,17 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "bitfield-struct"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2be5a46ba01b60005ae2c51a36a29cfe134bcacae2dd5cedcd4615fbaad1494b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -111,9 +122,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.6.0"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
[[package]]
name = "block"
@@ -291,7 +302,7 @@ dependencies = [
"acpi_tables",
"anyhow",
"arch",
"bitflags 2.6.0",
"bitflags 2.9.0",
"byteorder",
"event_monitor",
"hypervisor",
@@ -335,7 +346,7 @@ version = "4.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.0",
"libc",
]
@@ -398,7 +409,7 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbcc892208d6998fb57e7c3e05883def66f8130924bba066beb0cfe71566a9f6"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.0",
"cfg-if",
"log",
"managed",
@@ -425,10 +436,22 @@ dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi",
"wasi 0.11.0+wasi-snapshot-preview1",
"wasm-bindgen",
]
[[package]]
name = "getrandom"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
dependencies = [
"cfg-if",
"libc",
"wasi 0.13.3+wasi-0.2.2",
"windows-targets",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
@@ -441,6 +464,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"arc-swap",
"bitfield-struct",
"byteorder",
"cfg-if",
"concat-idents",
@@ -450,12 +474,15 @@ dependencies = [
"libc",
"log",
"mshv-bindings",
"open-enum",
"serde",
"serde_json",
"serde_with",
"thiserror 2.0.9",
"vfio-ioctls",
"vm-memory",
"vmm-sys-util",
"zerocopy 0.8.24",
]
[[package]]
@@ -530,7 +557,7 @@ version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e013ae7fcd2c6a8f384104d16afe7ea02969301ea2bb2a56e44b011ebc907cab"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.0",
"kvm-bindings",
"libc",
"vmm-sys-util",
@@ -610,7 +637,7 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "micro_http"
version = "0.1.0"
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#8182cd5523b63ceb52ad9d0e7eb6fb95683e6d1b"
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#4f621532e81ee2ad096a9c9592fdacc40d19de48"
dependencies = [
"libc",
"vmm-sys-util",
@@ -618,16 +645,16 @@ dependencies = [
[[package]]
name = "mshv-bindings"
version = "0.3.2"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0cb5031f3243a7459b7c13d960d25420980874eebda816db24ce6077e21d43"
checksum = "577073a0abbf515d17bfe96ca2ce49c44a68454d4179e95ce1244e858a9ebd4e"
dependencies = [
"libc",
"num_enum",
"serde",
"serde_derive",
"vmm-sys-util",
"zerocopy 0.8.14",
"zerocopy 0.8.24",
]
[[package]]
@@ -636,7 +663,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
dependencies = [
"getrandom",
"getrandom 0.2.15",
]
[[package]]
@@ -651,7 +678,7 @@ name = "net_util"
version = "0.1.0"
dependencies = [
"epoll",
"getrandom",
"getrandom 0.3.1",
"libc",
"log",
"net_gen",
@@ -701,6 +728,26 @@ version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "open-enum"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eb2508143a400b3361812094d987dd5adc81f0f5294a46491be648d6c94cab5"
dependencies = [
"open-enum-derive",
]
[[package]]
name = "open-enum-derive"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d1296fab5231654a5aec8bf9e87ba4e3938c502fc4c3c0425a00084c78944be"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "option_parser"
version = "0.1.0"
@@ -770,20 +817,20 @@ dependencies = [
[[package]]
name = "rand"
version = "0.8.5"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"zerocopy 0.8.24",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
@@ -791,11 +838,11 @@ dependencies = [
[[package]]
name = "rand_core"
version = "0.6.4"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom",
"getrandom 0.3.1",
]
[[package]]
@@ -834,9 +881,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "seccompiler"
version = "0.4.0"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "345a3e4dddf721a478089d4697b83c6c0a8f5bf16086f6c13397e4534eb6e2e5"
checksum = "a4ae55de56877481d112a559bbc12667635fdaf5e005712fd4e2b2fa50ffc884"
dependencies = [
"libc",
]
@@ -1052,20 +1099,20 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.10.0"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
dependencies = [
"getrandom",
"getrandom 0.3.1",
"rand",
"uuid-macro-internal",
]
[[package]]
name = "uuid-macro-internal"
version = "1.10.0"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee1cd046f83ea2c4e920d6ee9f7c3537ef928d75dce5d84a87c2c5d6b3999a3a"
checksum = "9521621447c21497fac206ffe6e9f642f977c4f82eeba9201055f64884d9cb01"
dependencies = [
"proc-macro2",
"quote",
@@ -1118,7 +1165,7 @@ name = "vhost"
version = "0.12.1"
source = "git+https://github.com/rust-vmm/vhost?rev=d983ae0#d983ae07f78663b7d24059667376992460b571a2"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.0",
"libc",
"uuid",
"vm-memory",
@@ -1243,7 +1290,7 @@ dependencies = [
"anyhow",
"arc-swap",
"arch",
"bitflags 2.6.0",
"bitflags 2.9.0",
"block",
"cfg-if",
"clap",
@@ -1282,7 +1329,7 @@ dependencies = [
"vm-migration",
"vm-virtio",
"vmm-sys-util",
"zerocopy 0.7.35",
"zerocopy 0.8.24",
]
[[package]]
@@ -1303,6 +1350,15 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.13.3+wasi-0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.93"
@@ -1462,6 +1518,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [
"bitflags 2.9.0",
]
[[package]]
name = "zerocopy"
version = "0.7.35"
@@ -1474,11 +1539,11 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.14"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468"
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
dependencies = [
"zerocopy-derive 0.8.14",
"zerocopy-derive 0.8.24",
]
[[package]]
@@ -1494,9 +1559,9 @@ dependencies = [
[[package]]
name = "zerocopy-derive"
version = "0.8.14"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1"
checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -24,10 +24,10 @@ libc = "0.2.155"
libfuzzer-sys = "0.4.7"
linux-loader = { version = "0.13.0", features = ["bzimage", "elf", "pe"] }
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
mshv-bindings = "0.3.2"
mshv-bindings = "0.5.0"
net_util = { path = "../net_util" }
once_cell = "1.19.0"
seccompiler = "0.4.0"
seccompiler = "0.5.0"
virtio-devices = { path = "../virtio-devices" }
virtio-queue = "0.14.0"
vm-device = { path = "../vm-device" }

View File

@@ -15,11 +15,12 @@ tdx = []
[dependencies]
anyhow = "1.0.94"
arc-swap = "1.7.1"
bitfield-struct = "0.10.1"
byteorder = "1.5.0"
cfg-if = "1.0.0"
concat-idents = "1.1.5"
igvm = { version = "0.3.3", optional = true }
igvm_defs = { version = "0.3.1", optional = true }
igvm = { workspace = true, optional = true }
igvm_defs = { workspace = true, optional = true }
kvm-bindings = { workspace = true, optional = true, features = ["serde"] }
kvm-ioctls = { workspace = true, optional = true }
libc = "0.2.167"
@@ -29,7 +30,9 @@ mshv-bindings = { workspace = true, features = [
"with-serde",
], optional = true }
mshv-ioctls = { workspace = true, optional = true }
open-enum = "0.5.2"
serde = { version = "1.0.208", features = ["derive", "rc"] }
serde_json = { workspace = true }
serde_with = { version = "3.9.0", default-features = false, features = [
"macros",
] }
@@ -41,6 +44,7 @@ vm-memory = { workspace = true, features = [
"backend-mmap",
] }
vmm-sys-util = { workspace = true, features = ["with-serde"] }
zerocopy = { version = "0.8.24", features = ["derive"] }
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
default-features = false

View File

@@ -5,22 +5,25 @@
use std::any::Any;
use std::result;
use serde::de::Error as SerdeError;
use serde::{Deserialize, Serialize};
use serde_json;
use thiserror::Error;
use crate::{CpuState, GicState, HypervisorDeviceError, HypervisorVmError};
use crate::{CpuState, HypervisorDeviceError, HypervisorVmError};
/// Errors thrown while setting up the VGIC.
#[derive(Debug, Error)]
pub enum Error {
/// Error while calling KVM ioctl for setting up the global interrupt controller.
#[error("Failed creating GIC device: {0}")]
CreateGic(HypervisorVmError),
CreateGic(#[source] HypervisorVmError),
/// Error while setting device attributes for the GIC.
#[error("Failed setting device attributes for the GIC: {0}")]
SetDeviceAttribute(HypervisorDeviceError),
SetDeviceAttribute(#[source] HypervisorDeviceError),
/// Error while getting device attributes for the GIC.
#[error("Failed getting device attributes for the GIC: {0}")]
GetDeviceAttribute(HypervisorDeviceError),
GetDeviceAttribute(#[source] HypervisorDeviceError),
}
pub type Result<T> = result::Result<T, Error>;
@@ -36,6 +39,56 @@ pub struct VgicConfig {
pub nr_irqs: u32,
}
#[derive(Clone, Serialize)]
pub enum GicState {
#[cfg(feature = "kvm")]
Kvm(crate::kvm::aarch64::gic::Gicv3ItsState),
#[cfg(feature = "mshv")]
MshvGicV2M(crate::mshv::aarch64::gic::MshvGicV2MState),
}
impl<'de> Deserialize<'de> for GicState {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
// GicStateDefaultDeserialize is a helper enum that mirrors GicState but also derives the Deserialize trait.
// This enables backward-compatible deserialization of GicState, facilitating live-upgrade scenarios.
#[derive(Deserialize)]
pub enum GicStateDefaultDeserialize {
#[cfg(feature = "kvm")]
Kvm(crate::kvm::aarch64::gic::Gicv3ItsState),
#[cfg(feature = "mshv")]
MshvGicV2M(crate::mshv::aarch64::gic::MshvGicV2MState),
}
const {
assert!(
std::mem::size_of::<GicStateDefaultDeserialize>()
== std::mem::size_of::<GicState>()
)
};
let value: serde_json::Value = Deserialize::deserialize(deserializer)?;
#[cfg(feature = "kvm")]
if let Ok(gicv3_its_state) =
crate::kvm::aarch64::gic::Gicv3ItsState::deserialize(value.clone())
{
return Ok(GicState::Kvm(gicv3_its_state));
}
if let Ok(gic_state_de) = GicStateDefaultDeserialize::deserialize(value.clone()) {
return match gic_state_de {
#[cfg(feature = "kvm")]
GicStateDefaultDeserialize::Kvm(state) => Ok(GicState::Kvm(state)),
#[cfg(feature = "mshv")]
GicStateDefaultDeserialize::MshvGicV2M(state) => Ok(GicState::MshvGicV2M(state)),
};
}
Err(SerdeError::custom("Failed to deserialize GicState"))
}
}
/// Hypervisor agnostic interface for a virtualized GIC
pub trait Vgic: Send + Sync {
/// Returns the fdt compatibility property of the device

View File

@@ -3,3 +3,4 @@
// SPDX-License-Identifier: Apache-2.0
pub mod gic;
pub mod regs;

View File

@@ -0,0 +1,228 @@
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
// Copyright © 2025, Microsoft Corporation
//
use bitfield_struct::bitfield;
use open_enum::open_enum;
use zerocopy::{FromBytes, IntoBytes};
/// ESR_EL2, exception syndrome register.
#[bitfield(u64)]
#[derive(IntoBytes, FromBytes)]
pub struct EsrEl2 {
#[bits(25)]
pub iss: u32,
pub il: bool,
#[bits(6)]
pub ec: u8,
#[bits(5)]
pub iss2: u8,
#[bits(27)]
_rsvd: u32,
}
#[open_enum]
#[derive(Debug)]
#[repr(u8)]
pub enum FaultStatusCode {
ADDRESS_SIZE_FAULT_LEVEL0 = 0b000000,
ADDRESS_SIZE_FAULT_LEVEL1 = 0b000001,
ADDRESS_SIZE_FAULT_LEVEL2 = 0b000010,
ADDRESS_SIZE_FAULT_LEVEL3 = 0b000011,
TRANSLATION_FAULT_LEVEL0 = 0b000100,
TRANSLATION_FAULT_LEVEL1 = 0b000101,
TRANSLATION_FAULT_LEVEL2 = 0b000110,
TRANSLATION_FAULT_LEVEL3 = 0b000111,
ACCESS_FLAG_FAULT_LEVEL0 = 0b001000,
ACCESS_FLAG_FAULT_LEVEL1 = 0b001001,
ACCESS_FLAG_FAULT_LEVEL2 = 0b001010,
ACCESS_FLAG_FAULT_LEVEL3 = 0b001011,
PERMISSION_FAULT_LEVEL0 = 0b001100,
PERMISSION_FAULT_LEVEL1 = 0b001101,
PERMISSION_FAULT_LEVEL2 = 0b001110,
PERMISSION_FAULT_LEVEL3 = 0b001111,
SYNCHRONOUS_EXTERNAL_ABORT = 0b010000,
SYNC_TAG_CHECK_FAULT = 0b010001,
SEA_TTW_LEVEL_NEG1 = 0b010011,
SEA_TTW_LEVEL0 = 0b010100,
SEA_TTW_LEVEL1 = 0b010101,
SEA_TTW_LEVEL2 = 0b010110,
SEA_TTW_LEVEL3 = 0b010111,
ECC_PARITY = 0b011000,
ECC_PARITY_TTW_LEVEL_NEG1 = 0b011011,
ECC_PARITY_TTW_LEVEL0 = 0b011100,
ECC_PARITY_TTW_LEVEL1 = 0b011101,
ECC_PARITY_TTW_LEVEL2 = 0b011110,
ECC_PARITY_TTW_LEVEL3 = 0b011111,
/// Valid only for data fault.
ALIGNMENT_FAULT = 0b100001,
/// Valid only for instruction fault.
GRANULE_PROTECTION_FAULT_LEVEL_NEG = 0b100011,
/// Valid only for instruction fault.
GRANULE_PROTECTION_FAULT_LEVEL0 = 0b100100,
/// Valid only for instruction fault.
GRANULE_PROTECTION_FAULT_LEVEL1 = 0b100101,
/// Valid only for instruction fault.
GRANULE_PROTECTION_FAULT_LEVEL2 = 0b100110,
/// Valid only for instruction fault.
GRANULE_PROTECTION_FAULT_LEVEL3 = 0b100111,
ADDRESS_SIZE_FAULT_LEVEL_NEG1 = 0b101001,
TRANSLATION_FAULT_LEVEL_NEG1 = 0b101011,
TLB_CONFLICT_ABORT = 0b110000,
UNSUPPORTED_HW_UPDATE_FAULT = 0b110001,
}
/// Support for embedding within IssDataAbort/IssInstructionAbort
impl FaultStatusCode {
const fn from_bits(bits: u32) -> Self {
FaultStatusCode((bits & 0x3f) as u8)
}
const fn into_bits(self) -> u32 {
self.0 as u32
}
}
#[bitfield(u32)]
pub struct IssDataAbort {
#[bits(6)]
pub dfsc: FaultStatusCode,
// Write operation (write not read)
pub wnr: bool,
pub s1ptw: bool,
pub cm: bool,
pub ea: bool,
/// FAR not valid
pub fnv: bool,
#[bits(2)]
pub set: u8,
pub vncr: bool,
/// Acquire/release
pub ar: bool,
/// (ISV==1) 64-bit, (ISV==0) FAR is approximate
pub sf: bool,
#[bits(5)]
/// Register index.
pub srt: u8,
/// Sign extended.
pub sse: bool,
#[bits(2)]
/// access width log2
pub sas: u8,
/// Valid ESREL2 iss field.
pub isv: bool,
#[bits(7)]
_unused: u8,
}
#[open_enum]
#[repr(u8)]
pub enum ExceptionClass {
UNKNOWN = 0b000000,
WFI = 0b000001,
MCR_MRC_COPROC_15 = 0b000011,
MCRR_MRRC_COPROC_15 = 0b000100,
MCR_MRC_COPROC_14 = 0b000101,
LDC_STC = 0b000110,
FP_OR_SIMD = 0b000111,
VMRS = 0b001000,
POINTER_AUTH_HCR_OR_SCR = 0b001001,
LS64 = 0b001010,
MRRC_COPROC_14 = 0b001100,
BRANCH_TARGET = 0b001101,
ILLEGAL_STATE = 0b001110,
SVC32 = 0b010001,
HVC32 = 0b010010,
SMC32 = 0b010011,
SVC = 0b010101,
HVC = 0b010110,
SMC = 0b010111,
SYSTEM = 0b011000,
SVE = 0b011001,
ERET = 0b011010,
TSTART = 0b011011,
POINTER_AUTH = 0b011100,
SME = 0b011101,
INSTRUCTION_ABORT_LOWER = 0b100000,
INSTRUCTION_ABORT = 0b100001,
PC_ALIGNMENT = 0b100010,
DATA_ABORT_LOWER = 0b100100,
DATA_ABORT = 0b100101,
SP_ALIGNMENT_FAULT = 0b100110,
MEMORY_OP = 0b100111,
FP_EXCEPTION_32 = 0b101000,
FP_EXCEPTION_64 = 0b101100,
SERROR = 0b101111,
BREAKPOINT_LOWER = 0b110000,
BREAKPOINT = 0b110001,
STEP_LOWER = 0b110010,
STEP = 0b110011,
WATCHPOINT_LOWER = 0b110100,
WATCHPOINT = 0b110101,
BRK32 = 0b111000,
VECTOR_CATCH_32 = 0b111010,
BRK = 0b111100,
}
#[allow(non_upper_case_globals)]
// PSR (Processor State Register) bits.
// Taken from arch/arm64/include/uapi/asm/ptrace.h.
const PSR_MODE_EL1h: u64 = 0x0000_0005;
const PSR_F_BIT: u64 = 0x0000_0040;
const PSR_I_BIT: u64 = 0x0000_0080;
const PSR_A_BIT: u64 = 0x0000_0100;
const PSR_D_BIT: u64 = 0x0000_0200;
// Taken from arch/arm64/kvm/inject_fault.c.
pub const PSTATE_FAULT_BITS_64: u64 = PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT;
// AArch64 system register encoding:
// See https://developer.arm.com/documentation/ddi0487 (chapter D12)
//
// 31 22 21 20 19 18 16 15 12 11 8 7 5 4 0
// +----------+---+-----+-----+-----+-----+-----+----+
// |1101010100| L | op0 | op1 | CRn | CRm | op2 | Rt |
// +----------+---+-----+-----+-----+-----+-----+----+
//
// Notes:
// - L and Rt are reserved as implementation defined fields, ignored.
const SYSREG_HEAD: u32 = 0b1101010100u32 << 22;
const SYSREG_OP0_SHIFT: u32 = 19;
const SYSREG_OP0_MASK: u32 = 0b11u32 << 19;
const SYSREG_OP1_SHIFT: u32 = 16;
const SYSREG_OP1_MASK: u32 = 0b111u32 << 16;
const SYSREG_CRN_SHIFT: u32 = 12;
const SYSREG_CRN_MASK: u32 = 0b1111u32 << 12;
const SYSREG_CRM_SHIFT: u32 = 8;
const SYSREG_CRM_MASK: u32 = 0b1111u32 << 8;
const SYSREG_OP2_SHIFT: u32 = 5;
const SYSREG_OP2_MASK: u32 = 0b111u32 << 5;
/// Define the ID of system registers
#[macro_export]
macro_rules! arm64_sys_reg {
($name: tt, $op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => {
pub const $name: u32 = SYSREG_HEAD
| ((($op0 as u32) << SYSREG_OP0_SHIFT) & SYSREG_OP0_MASK as u32)
| ((($op1 as u32) << SYSREG_OP1_SHIFT) & SYSREG_OP1_MASK as u32)
| ((($crn as u32) << SYSREG_CRN_SHIFT) & SYSREG_CRN_MASK as u32)
| ((($crm as u32) << SYSREG_CRM_SHIFT) & SYSREG_CRM_MASK as u32)
| ((($op2 as u32) << SYSREG_OP2_SHIFT) & SYSREG_OP2_MASK as u32);
};
}
arm64_sys_reg!(MPIDR_EL1, 3, 0, 0, 0, 5);
arm64_sys_reg!(ID_AA64MMFR0_EL1, 3, 0, 0, 7, 0);
arm64_sys_reg!(TTBR1_EL1, 3, 0, 2, 0, 1);
arm64_sys_reg!(TCR_EL1, 3, 0, 2, 0, 2);
pub const AARCH64_ARCH_TIMER_PHYS_SECURE_IRQ: u32 = 13;
pub const AARCH64_ARCH_TIMER_PHYS_NONSECURE_IRQ: u32 = 14;
pub const AARCH64_ARCH_TIMER_VIRT_IRQ: u32 = 11;
pub const AARCH64_ARCH_TIMER_HYP_IRQ: u32 = 10;
// PMU PPI interrupt number
pub const AARCH64_PMU_IRQ: u32 = 7;
pub const AARCH64_MIN_PPI_IRQ: u32 = 16;

View File

@@ -82,13 +82,13 @@ pub enum EmulationError<T: Debug> {
WrongNumberOperands(#[source] anyhow::Error),
#[error("Instruction Exception: {0}")]
InstructionException(Exception<T>),
InstructionException(#[source] Exception<T>),
#[error("Instruction fetching error: {0}")]
InstructionFetchingError(#[source] anyhow::Error),
#[error("Platform emulation error: {0}")]
PlatformEmulationError(PlatformError),
PlatformEmulationError(#[source] PlatformError),
#[error(transparent)]
EmulationError(#[from] anyhow::Error),

View File

@@ -14,13 +14,13 @@ use crate::{AiaState, HypervisorDeviceError, HypervisorVmError};
pub enum Error {
/// Error while calling KVM ioctl for setting up the global interrupt controller.
#[error("Failed creating AIA device: {0}")]
CreateAia(HypervisorVmError),
CreateAia(#[source] HypervisorVmError),
/// Error while setting device attributes for the AIA.
#[error("Failed setting device attributes for the AIA: {0}")]
SetDeviceAttribute(HypervisorDeviceError),
SetDeviceAttribute(#[source] HypervisorDeviceError),
/// Error while getting device attributes for the AIA.
#[error("Failed getting device attributes for the AIA: {0}")]
GetDeviceAttribute(HypervisorDeviceError),
GetDeviceAttribute(#[source] HypervisorDeviceError),
}
pub type Result<T> = result::Result<T, Error>;

View File

@@ -10,18 +10,21 @@
//
//
#[cfg(target_arch = "aarch64")]
use std::sync::Arc;
use thiserror::Error;
#[cfg(not(target_arch = "riscv64"))]
use vm_memory::GuestAddress;
#[cfg(target_arch = "aarch64")]
use crate::aarch64::{RegList, VcpuInit};
#[cfg(target_arch = "x86_64")]
use crate::arch::x86::{CpuIdEntry, FpuState, LapicState, MsrEntry, SpecialRegisters};
#[cfg(feature = "tdx")]
use crate::kvm::{TdxExitDetails, TdxExitStatus};
#[cfg(target_arch = "riscv64")]
use crate::riscv64::RegList;
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
use crate::RegList;
#[cfg(target_arch = "aarch64")]
use crate::VcpuInit;
use crate::{CpuState, MpState, StandardRegisters};
#[cfg(target_arch = "x86_64")]
@@ -320,7 +323,13 @@ pub enum HypervisorCpuError {
#[cfg(feature = "sev_snp")]
#[error("Failed to set sev control register: {0}")]
SetSevControlRegister(#[source] anyhow::Error),
///
/// Unsupported SysReg registers
///
#[cfg(target_arch = "aarch64")]
#[error("Unsupported SysReg registers: {0}")]
UnsupportedSysReg(u32),
///
/// Error injecting NMI
///
#[error("Failed to inject NMI")]
@@ -449,7 +458,26 @@ pub trait Vcpu: Send + Sync {
#[cfg(target_arch = "aarch64")]
fn vcpu_finalize(&self, feature: i32) -> Result<()>;
///
/// Gets the features that have been finalized for a given CPU.
///
#[cfg(target_arch = "aarch64")]
fn vcpu_get_finalized_features(&self) -> i32;
///
/// Sets processor features for a given CPU.
///
#[cfg(target_arch = "aarch64")]
fn vcpu_set_processor_features(
&self,
vm: &Arc<dyn crate::Vm>,
kvi: &mut VcpuInit,
id: u8,
) -> Result<()>;
///
/// Returns VcpuInit with default value set
///
#[cfg(target_arch = "aarch64")]
fn create_vcpu_init(&self) -> VcpuInit;
///
/// Gets a list of the guest registers that are supported for the
/// KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
@@ -562,7 +590,13 @@ pub trait Vcpu: Send + Sync {
fn set_sev_control_register(&self, _reg: u64) -> Result<()> {
unimplemented!()
}
///
/// Sets the value of GIC redistributor address
///
#[cfg(target_arch = "aarch64")]
fn set_gic_redistributor_addr(&self, _gicr_base_addr: u64) -> Result<()> {
Ok(())
}
#[cfg(target_arch = "x86_64")]
///
/// Trigger NMI interrupt

View File

@@ -67,22 +67,22 @@ pub enum HypervisorError {
///
/// Checking extensions failed
///
#[error("Checking extensions:{0}")]
#[error("Checking extensions: {0}")]
CheckExtensions(#[source] anyhow::Error),
///
/// Failed to retrieve TDX capabilities
///
#[error("Failed to retrieve TDX capabilities:{0}")]
#[error("Failed to retrieve TDX capabilities: {0}")]
TdxCapabilities(#[source] anyhow::Error),
///
/// Failed to set partition property
///
#[error("Failed to set partition property:{0}")]
#[error("Failed to set partition property: {0}")]
SetPartitionProperty(#[source] anyhow::Error),
///
/// Running on an unsupported CPU
///
#[error("Unsupported CPU:{0}")]
#[error("Unsupported CPU: {0}")]
UnsupportedCpu(#[source] anyhow::Error),
///
/// Launching a VM with unsupported VM Type

View File

@@ -14,7 +14,7 @@ use kvm_ioctls::DeviceFd;
use redist_regs::{construct_gicr_typers, get_redist_regs, set_redist_regs};
use serde::{Deserialize, Serialize};
use crate::arch::aarch64::gic::{Error, Result, Vgic, VgicConfig};
use crate::arch::aarch64::gic::{Error, GicState, Result, Vgic, VgicConfig};
use crate::device::HypervisorDeviceError;
use crate::kvm::KvmVm;
use crate::{CpuState, Vm};
@@ -125,6 +125,23 @@ pub struct Gicv3ItsState {
its_baser: [u64; 8],
}
impl From<GicState> for Gicv3ItsState {
fn from(state: GicState) -> Self {
match state {
GicState::Kvm(state) => state,
/* Needed in case other hypervisors are enabled */
#[allow(unreachable_patterns)]
_ => panic!("GicState is not valid"),
}
}
}
impl From<Gicv3ItsState> for GicState {
fn from(state: Gicv3ItsState) -> Self {
GicState::Kvm(state)
}
}
impl KvmGicV3Its {
/// Device trees specific constants
pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
@@ -316,7 +333,7 @@ impl Vgic for KvmGicV3Its {
}
/// Save the state of GICv3ITS.
fn state(&self) -> Result<Gicv3ItsState> {
fn state(&self) -> Result<GicState> {
let gicr_typers = self.gicr_typers.clone();
let gicd_ctlr = read_ctlr(&self.device)?;
@@ -366,7 +383,7 @@ impl Vgic for KvmGicV3Its {
GITS_IIDR,
)?;
Ok(Gicv3ItsState {
let gic_state: GicState = Gicv3ItsState {
dist: dist_state,
rdist: rdist_state,
icc: icc_state,
@@ -377,48 +394,53 @@ impl Vgic for KvmGicV3Its {
its_cwriter: its_cwriter_state,
its_creadr: its_creadr_state,
its_baser: its_baser_state,
})
}
.into();
Ok(gic_state)
}
/// Restore the state of GICv3ITS.
fn set_state(&mut self, state: &Gicv3ItsState) -> Result<()> {
fn set_state(&mut self, state: &GicState) -> Result<()> {
let kvm_state: Gicv3ItsState = state.clone().into();
let gicr_typers = self.gicr_typers.clone();
write_ctlr(&self.device, state.gicd_ctlr)?;
write_ctlr(&self.device, kvm_state.gicd_ctlr)?;
set_dist_regs(&self.device, &state.dist)?;
set_dist_regs(&self.device, &kvm_state.dist)?;
set_redist_regs(&self.device, &gicr_typers, &state.rdist)?;
set_redist_regs(&self.device, &gicr_typers, &kvm_state.rdist)?;
set_icc_regs(&self.device, &gicr_typers, &state.icc)?;
set_icc_regs(&self.device, &gicr_typers, &kvm_state.icc)?;
//Restore GICv3ITS registers
gicv3_its_attr_set(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_IIDR,
state.its_iidr,
kvm_state.its_iidr,
)?;
gicv3_its_attr_set(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CBASER,
state.its_cbaser,
kvm_state.its_cbaser,
)?;
gicv3_its_attr_set(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CREADR,
state.its_creadr,
kvm_state.its_creadr,
)?;
gicv3_its_attr_set(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CWRITER,
state.its_cwriter,
kvm_state.its_cwriter,
)?;
for i in 0..8 {
@@ -426,7 +448,7 @@ impl Vgic for KvmGicV3Its {
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],
kvm_state.its_baser[i as usize],
)?;
}
@@ -437,7 +459,7 @@ impl Vgic for KvmGicV3Its {
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CTLR,
state.its_ctlr,
kvm_state.its_ctlr,
)
}

View File

@@ -7,11 +7,11 @@ use kvm_ioctls::DeviceFd;
use crate::arch::aarch64::gic::{Error, Result};
use crate::device::HypervisorDeviceError;
use crate::kvm::kvm_bindings::{
kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, KVM_REG_ARM64, KVM_REG_ARM64_SYSREG,
KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK,
KVM_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_SIZE_U64,
kvm_device_attr, kvm_one_reg, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, KVM_REG_ARM64,
KVM_REG_ARM64_SYSREG, KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT,
KVM_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_SIZE_U64,
};
use crate::kvm::{Register, VcpuKvmState};
use crate::kvm::VcpuKvmState;
use crate::CpuState;
// Relevant redistributor registers that we want to save/restore.
@@ -213,7 +213,7 @@ pub fn construct_gicr_typers(vcpu_states: &[CpuState]) -> Vec<u64> {
let state: VcpuKvmState = state.clone().into();
let last = (index == vcpu_states.len() - 1) as u64;
// state.sys_regs is a big collection of system registers, including MIPDR_EL1
let mpidr: Vec<Register> = state
let mpidr: Vec<kvm_one_reg> = state
.sys_regs
.into_iter()
.filter(|reg| reg.id == KVM_ARM64_SYSREG_MPIDR_EL1)

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -31,7 +31,6 @@ pub struct AiaImsicsState {}
impl KvmAiaImsics {
/// Device trees specific constants
fn version() -> u32 {
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_RISCV_AIA
}

View File

@@ -8,7 +8,6 @@ use kvm_bindings::{
kvm_mp_state, kvm_one_reg, kvm_riscv_core, KVM_REG_RISCV_CORE, KVM_REG_RISCV_TYPE_MASK,
KVM_REG_SIZE_MASK, KVM_REG_SIZE_U64,
};
pub use kvm_bindings::{kvm_one_reg as Register, RegList};
pub use kvm_ioctls::{Cap, Kvm};
use serde::{Deserialize, Serialize};

View File

@@ -36,7 +36,7 @@ pub mod arch;
pub mod kvm;
/// Microsoft Hypervisor implementation module
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
#[cfg(feature = "mshv")]
pub mod mshv;
/// Hypervisor related module
@@ -59,7 +59,7 @@ pub use cpu::CpuVendor;
pub use cpu::{HypervisorCpuError, Vcpu, VmExit};
pub use device::HypervisorDeviceError;
#[cfg(all(feature = "kvm", target_arch = "aarch64"))]
pub use kvm::{aarch64, GicState};
pub use kvm::aarch64;
#[cfg(all(feature = "kvm", target_arch = "riscv64"))]
pub use kvm::{riscv64, AiaState};
pub use vm::{
@@ -148,7 +148,7 @@ pub const USER_MEMORY_REGION_ADJUSTABLE: u32 = 1 << 4;
pub enum MpState {
#[cfg(feature = "kvm")]
Kvm(kvm_bindings::kvm_mp_state),
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
#[cfg(feature = "mshv")]
Mshv, /* MSHV does not support MpState yet */
}
@@ -163,7 +163,7 @@ pub enum IoEventAddress {
pub enum CpuState {
#[cfg(feature = "kvm")]
Kvm(kvm::VcpuKvmState),
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
#[cfg(feature = "mshv")]
Mshv(mshv::VcpuMshvState),
}
@@ -196,16 +196,35 @@ pub enum IrqRoutingEntry {
Mshv(mshv_bindings::mshv_user_irq_entry),
}
#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum VcpuInit {
#[cfg(all(feature = "kvm", target_arch = "aarch64"))]
Kvm(kvm_bindings::kvm_vcpu_init),
#[cfg(all(feature = "mshv", target_arch = "aarch64"))]
Mshv(mshv_bindings::MshvVcpuInit),
}
#[derive(Debug, Clone, PartialEq)]
pub enum RegList {
#[cfg(all(feature = "kvm", any(target_arch = "aarch64", target_arch = "riscv64")))]
Kvm(kvm_bindings::RegList),
#[cfg(all(feature = "mshv", target_arch = "aarch64"))]
Mshv(mshv_bindings::MshvRegList),
}
pub enum Register {
#[cfg(feature = "kvm")]
Kvm(kvm_bindings::kvm_one_reg),
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum StandardRegisters {
#[cfg(all(feature = "kvm", not(target_arch = "riscv64")))]
Kvm(kvm_bindings::kvm_regs),
#[cfg(all(feature = "kvm", target_arch = "riscv64"))]
Kvm(kvm_bindings::kvm_riscv_core),
#[cfg(all(
any(feature = "mshv", feature = "mshv_emulator"),
target_arch = "x86_64"
))]
#[cfg(any(feature = "mshv", feature = "mshv_emulator"))]
Mshv(mshv_bindings::StandardRegisters),
}
@@ -292,6 +311,8 @@ macro_rules! set_aarch64_reg {
match self {
#[cfg(feature = "kvm")]
StandardRegisters::Kvm(s) => s.regs.$reg_name = val,
#[cfg(feature = "mshv")]
StandardRegisters::Mshv(s) => s.$reg_name = val,
}
}
}
@@ -308,6 +329,8 @@ macro_rules! get_aarch64_reg {
match self {
#[cfg(feature = "kvm")]
StandardRegisters::Kvm(s) => s.regs.$reg_name,
#[cfg(feature = "mshv")]
StandardRegisters::Mshv(s) => s.$reg_name,
}
}
}

View File

@@ -0,0 +1,113 @@
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
// Copyright © 2025, Microsoft Corporation
//
use crate::arch::aarch64::regs::{EsrEl2, ExceptionClass, IssDataAbort};
use crate::arch::emulator::PlatformError;
use crate::cpu::Vcpu;
use crate::mshv::MshvVcpu;
pub struct MshvEmulatorContext<'a> {
pub vcpu: &'a MshvVcpu,
pub map: (u64, u64), // Initial GVA to GPA mapping provided by the hypervisor
pub syndrome: u64,
pub instruction_bytes: [u8; 4],
pub instruction_byte_count: u8,
pub interruption_pending: bool,
pub pc: u64,
}
pub struct Emulator<'a> {
pub context: MshvEmulatorContext<'a>,
}
impl<'a> Emulator<'a> {
/// Create a new emulator instance.
pub fn new(context: MshvEmulatorContext<'a>) -> Self {
Emulator { context }
}
/// Decode & emulate the instruction using the syndrome register.
pub fn emulate_with_syndrome(&mut self) -> Result<bool, PlatformError> {
let esr_el2 = EsrEl2::from(self.context.syndrome);
if !matches!(
ExceptionClass(esr_el2.ec()),
ExceptionClass::DATA_ABORT | ExceptionClass::DATA_ABORT_LOWER
) {
return Ok(false);
}
let iss = IssDataAbort::from(esr_el2.iss());
if !iss.isv() {
return Ok(false);
}
let len = 1 << iss.sas();
let sign_extend = iss.sse();
let reg_index = iss.srt();
let mut regs = self
.context
.vcpu
.get_regs()
.map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
let mut gprs = regs.get_regs();
if iss.wnr() {
let data: [u8; 8] = match reg_index {
0..=30 => gprs[reg_index as usize],
31 => 0u64,
_ => unreachable!(),
}
.to_ne_bytes();
if let Some(vm_ops) = &self.context.vcpu.vm_ops {
vm_ops
.mmio_write(self.context.map.1, &data[0..len])
.map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
}
} else {
let mut data = [0_u8; 8];
if let Some(vm_ops) = &self.context.vcpu.vm_ops {
vm_ops
.mmio_read(self.context.map.1, &mut data[0..len])
.map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
}
let mut data = u64::from_ne_bytes(data);
if sign_extend {
let shift = 64 - len * 8;
data = ((data as i64) << shift >> shift) as u64;
if !iss.sf() {
data &= 0xffffffff;
}
}
gprs[reg_index as usize] = data;
}
let pc = regs.get_pc();
regs.set_pc(if esr_el2.il() { pc + 4 } else { pc + 2 });
regs.set_regs(gprs);
self.context
.vcpu
.set_regs(&regs)
.map_err(|e| PlatformError::SetCpuStateFailure(e.into()))?;
Ok(true)
}
/// Emulate the instruction.
pub fn emulate(&mut self) -> Result<(), PlatformError> {
match self.emulate_with_syndrome() {
Ok(true) => Ok(()),
Ok(false) => Err(PlatformError::InvalidState(anyhow!(
"Failed to decode instruction using syndrome register"
))),
Err(e) => Err(e),
}
// TODO: Add support for instruction decoding in case of failure from
// decode_with_syndrome. This will require aarch64 instruction emulator
// implementation like x86_64.
}
}

View File

@@ -0,0 +1,126 @@
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
// Copyright © 2025, Microsoft Corporation
//
use std::any::Any;
use serde::{Deserialize, Serialize};
use crate::arch::aarch64::gic::{GicState, Result, Vgic, VgicConfig};
use crate::{CpuState, Vm};
pub struct MshvGicV2M {
/// GIC distributor address
pub dist_addr: u64,
/// GIC distributor size
pub dist_size: u64,
/// GIC re-distributors address
pub redists_addr: u64,
/// GIC re-distributors size
pub redists_size: u64,
/// GITS translator address
pub gits_addr: u64,
/// GITS translator size
pub gits_size: u64,
/// Number of CPUs handled by the device
pub vcpu_count: u64,
}
pub const BASE_SPI_IRQ: u32 = 32;
#[derive(Clone, Default, Serialize, Deserialize)]
pub struct MshvGicV2MState {}
impl From<GicState> for MshvGicV2MState {
fn from(state: GicState) -> Self {
match state {
GicState::MshvGicV2M(state) => state,
/* Needed in case other hypervisors are enabled */
#[allow(unreachable_patterns)]
_ => panic!("GicState is not valid"),
}
}
}
impl From<MshvGicV2MState> for GicState {
fn from(state: MshvGicV2MState) -> Self {
GicState::MshvGicV2M(state)
}
}
impl MshvGicV2M {
/// Create a new GICv2m device
pub fn new(_vm: &dyn Vm, config: VgicConfig) -> Result<MshvGicV2M> {
let gic_device = MshvGicV2M {
dist_addr: config.dist_addr,
dist_size: config.dist_size,
redists_addr: config.redists_addr,
redists_size: config.redists_size,
gits_addr: config.msi_addr,
gits_size: config.msi_size,
vcpu_count: config.vcpu_count,
};
Ok(gic_device)
}
}
impl Vgic for MshvGicV2M {
fn fdt_compatibility(&self) -> &str {
"arm,gic-v3"
}
fn msi_compatible(&self) -> bool {
true
}
fn msi_compatibility(&self) -> &str {
"arm,gic-v2m-frame"
}
fn fdt_maint_irq(&self) -> u32 {
0
}
fn vcpu_count(&self) -> u64 {
self.vcpu_count
}
fn msi_properties(&self) -> [u64; 2] {
[self.gits_addr, self.gits_size]
}
fn device_properties(&self) -> [u64; 4] {
[
self.dist_addr,
self.dist_size,
self.redists_addr,
self.redists_size,
]
}
fn set_gicr_typers(&mut self, _vcpu_states: &[CpuState]) {
unimplemented!()
}
fn state(&self) -> Result<GicState> {
unimplemented!()
}
fn as_any_concrete_mut(&mut self) -> &mut dyn Any {
self
}
fn set_state(&mut self, _state: &GicState) -> Result<()> {
unimplemented!()
}
fn save_data_tables(&self) -> Result<()> {
unimplemented!()
}
}

View File

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

View File

@@ -12,15 +12,24 @@ use std::sync::{Arc, RwLock};
#[cfg(feature = "sev_snp")]
use arc_swap::ArcSwap;
use mshv_bindings::*;
use mshv_ioctls::{set_registers_64, InterruptRequest, Mshv, NoDatamatch, VcpuFd, VmFd, VmType};
#[cfg(target_arch = "x86_64")]
use mshv_ioctls::InterruptRequest;
use mshv_ioctls::{set_registers_64, Mshv, NoDatamatch, VcpuFd, VmFd, VmType};
use vfio_ioctls::VfioDeviceFd;
use vm::DataMatch;
#[cfg(feature = "sev_snp")]
use vm_memory::bitmap::AtomicBitmap;
#[cfg(target_arch = "aarch64")]
use crate::arch::aarch64::regs::{
AARCH64_ARCH_TIMER_VIRT_IRQ, AARCH64_MIN_PPI_IRQ, AARCH64_PMU_IRQ,
};
#[cfg(target_arch = "x86_64")]
use crate::arch::emulator::PlatformEmulator;
#[cfg(target_arch = "x86_64")]
use crate::arch::x86::emulator::Emulator;
#[cfg(target_arch = "aarch64")]
use crate::mshv::aarch64::emulator;
use crate::mshv::emulator::MshvEmulatorContext;
use crate::vm::{self, InterruptSourceConfig, VmOps};
use crate::{cpu, hypervisor, vec_with_array_field, HypervisorType};
@@ -29,10 +38,19 @@ mod snp_constants;
// x86_64 dependencies
#[cfg(target_arch = "x86_64")]
pub mod x86_64;
// aarch64 dependencies
#[cfg(target_arch = "aarch64")]
pub mod aarch64;
#[cfg(target_arch = "x86_64")]
use std::fs::File;
use std::os::unix::io::AsRawFd;
#[cfg(target_arch = "aarch64")]
use std::sync::Mutex;
#[cfg(target_arch = "aarch64")]
use aarch64::gic::{MshvGicV2M, BASE_SPI_IRQ};
#[cfg(target_arch = "aarch64")]
pub use aarch64::VcpuMshvState;
#[cfg(feature = "sev_snp")]
use igvm_defs::IGVM_VHS_SNP_ID_BLOCK;
#[cfg(feature = "sev_snp")]
@@ -50,6 +68,10 @@ pub use {
mshv_bindings::mshv_device_attr as DeviceAttr, mshv_ioctls, mshv_ioctls::DeviceFd,
};
#[cfg(target_arch = "aarch64")]
use crate::arch::aarch64::gic::{Vgic, VgicConfig};
#[cfg(target_arch = "aarch64")]
use crate::arch::aarch64::regs;
#[cfg(target_arch = "x86_64")]
use crate::arch::x86::{CpuIdEntry, FpuState, MsrEntry};
#[cfg(target_arch = "x86_64")]
@@ -191,6 +213,44 @@ impl From<IrqRoutingEntry> for mshv_user_irq_entry {
}
}
#[cfg(target_arch = "aarch64")]
impl From<mshv_bindings::MshvRegList> for crate::RegList {
fn from(s: mshv_bindings::MshvRegList) -> Self {
crate::RegList::Mshv(s)
}
}
#[cfg(target_arch = "aarch64")]
impl From<crate::RegList> for mshv_bindings::MshvRegList {
fn from(e: crate::RegList) -> Self {
match e {
crate::RegList::Mshv(e) => e,
/* Needed in case other hypervisors are enabled */
#[allow(unreachable_patterns)]
_ => panic!("RegList is not valid"),
}
}
}
#[cfg(target_arch = "aarch64")]
impl From<mshv_bindings::MshvVcpuInit> for crate::VcpuInit {
fn from(s: mshv_bindings::MshvVcpuInit) -> Self {
crate::VcpuInit::Mshv(s)
}
}
#[cfg(target_arch = "aarch64")]
impl From<crate::VcpuInit> for mshv_bindings::MshvVcpuInit {
fn from(e: crate::VcpuInit) -> Self {
match e {
crate::VcpuInit::Mshv(e) => e,
/* Needed in case other hypervisors are enabled */
#[allow(unreachable_patterns)]
_ => panic!("VcpuInit is not valid"),
}
}
}
struct MshvDirtyLogSlot {
guest_pfn: u64,
memory_size: u64,
@@ -206,7 +266,7 @@ impl MshvHypervisor {
///
/// Retrieve the list of MSRs supported by MSHV.
///
fn get_msr_list(&self) -> hypervisor::Result<MsrList> {
fn get_msr_list(&self) -> hypervisor::Result<Vec<u32>> {
self.mshv
.get_msr_index_list()
.map_err(|e| hypervisor::HypervisorError::GetMsrList(e.into()))
@@ -239,67 +299,18 @@ impl MshvHypervisor {
break;
}
// Set additional partition property for SEV-SNP partition.
#[cfg(target_arch = "x86_64")]
if mshv_vm_type == VmType::Snp {
let snp_policy = snp::get_default_snp_guest_policy();
let vmgexit_offloads = snp::get_default_vmgexit_offload_features();
// SAFETY: access union fields
unsafe {
debug!(
"Setting the partition isolation policy as: 0x{:x}",
snp_policy.as_uint64
);
fd.set_partition_property(
hv_partition_property_code_HV_PARTITION_PROPERTY_ISOLATION_POLICY,
snp_policy.as_uint64,
)
.map_err(|e| hypervisor::HypervisorError::SetPartitionProperty(e.into()))?;
debug!(
"Setting the partition property to enable VMGEXIT offloads as : 0x{:x}",
vmgexit_offloads.as_uint64
);
fd.set_partition_property(
hv_partition_property_code_HV_PARTITION_PROPERTY_SEV_VMGEXIT_OFFLOADS,
vmgexit_offloads.as_uint64,
)
.map_err(|e| hypervisor::HypervisorError::SetPartitionProperty(e.into()))?;
}
}
// Default Microsoft Hypervisor behavior for unimplemented MSR is to
// send a fault to the guest if it tries to access it. It is possible
// to override this behavior with a more suitable option i.e., ignore
// writes from the guest and return zero in attempt to read unimplemented
// MSR.
#[cfg(target_arch = "x86_64")]
fd.set_partition_property(
hv_partition_property_code_HV_PARTITION_PROPERTY_UNIMPLEMENTED_MSR_ACTION,
hv_unimplemented_msr_action_HV_UNIMPLEMENTED_MSR_ACTION_IGNORE_WRITE_READ_ZERO as u64,
)
.map_err(|e| hypervisor::HypervisorError::SetPartitionProperty(e.into()))?;
// Always create a frozen partition
fd.set_partition_property(
hv_partition_property_code_HV_PARTITION_PROPERTY_TIME_FREEZE,
1u64,
)
.map_err(|e| hypervisor::HypervisorError::SetPartitionProperty(e.into()))?;
let vm_fd = Arc::new(fd);
#[cfg(target_arch = "x86_64")]
{
let msr_list = self.get_msr_list()?;
let num_msrs = msr_list.as_fam_struct_ref().nmsrs as usize;
let mut msrs: Vec<MsrEntry> = vec![
MsrEntry {
..Default::default()
};
num_msrs
msr_list.len()
];
let indices = msr_list.as_slice();
for (pos, index) in indices.iter().enumerate() {
for (pos, index) in msr_list.iter().enumerate() {
msrs[pos].index = *index;
}
@@ -443,6 +454,23 @@ impl hypervisor::Hypervisor for MshvHypervisor {
fn get_guest_debug_hw_bps(&self) -> usize {
0
}
#[cfg(target_arch = "aarch64")]
///
/// Retrieve AArch64 host maximum IPA size supported by MSHV.
///
fn get_host_ipa_limit(&self) -> i32 {
let host_ipa = self.mshv.get_host_partition_property(
hv_partition_property_code_HV_PARTITION_PROPERTY_PHYSICAL_ADDRESS_WIDTH,
);
match host_ipa {
Ok(ipa) => ipa.try_into().unwrap(),
Err(e) => {
panic!("Failed to get host IPA limit: {e:?}");
}
}
}
}
#[cfg(feature = "sev_snp")]
@@ -460,6 +488,7 @@ unsafe impl Send for Ghcb {}
unsafe impl Sync for Ghcb {}
/// Vcpu struct for Microsoft Hypervisor
#[allow(dead_code)]
pub struct MshvVcpu {
fd: VcpuFd,
vp_index: u8,
@@ -491,11 +520,9 @@ impl cpu::Vcpu for MshvVcpu {
///
/// Returns StandardRegisters with default value set
///
#[cfg(target_arch = "x86_64")]
fn create_standard_regs(&self) -> crate::StandardRegisters {
mshv_bindings::StandardRegisters::default().into()
}
#[cfg(target_arch = "x86_64")]
///
/// Returns the vCPU general purpose registers.
///
@@ -507,7 +534,6 @@ impl cpu::Vcpu for MshvVcpu {
.into())
}
#[cfg(target_arch = "x86_64")]
///
/// Sets the vCPU general purpose registers.
///
@@ -648,18 +674,7 @@ impl cpu::Vcpu for MshvVcpu {
*/
match port {
0x402 | 0x510 | 0x511 | 0x514 => {
let insn_len = info.header.instruction_length() as u64;
/* Advance RIP and update RAX */
let arr_reg_name_value = [
(
hv_register_name_HV_X64_REGISTER_RIP,
info.header.rip + insn_len,
),
(hv_register_name_HV_X64_REGISTER_RAX, ret_rax),
];
set_registers_64!(self.fd, arr_reg_name_value)
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
self.advance_rip_update_rax(&info, ret_rax)?;
return Ok(cpu::VmExit::Ignore);
}
_ => {}
@@ -697,18 +712,39 @@ impl cpu::Vcpu for MshvVcpu {
ret_rax = eax as u64;
}
let insn_len = info.header.instruction_length() as u64;
self.advance_rip_update_rax(&info, ret_rax)?;
Ok(cpu::VmExit::Ignore)
}
#[cfg(target_arch = "aarch64")]
hv_message_type_HVMSG_UNMAPPED_GPA => {
let info = x.to_memory_info().unwrap();
let gva = info.guest_virtual_address;
let gpa = info.guest_physical_address;
debug!("Unmapped GPA exit: GVA {:x} GPA {:x}", gva, gpa);
let context = MshvEmulatorContext {
vcpu: self,
map: (gva, gpa),
syndrome: info.syndrome,
instruction_bytes: info.instruction_bytes,
instruction_byte_count: info.instruction_byte_count,
// SAFETY: Accessing a union element from bindgen generated bindings.
interruption_pending: unsafe {
info.header
.execution_state
.__bindgen_anon_1
.interruption_pending()
!= 0
},
pc: info.header.pc,
};
let mut emulator = emulator::Emulator::new(context);
emulator
.emulate()
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
/* Advance RIP and update RAX */
let arr_reg_name_value = [
(
hv_register_name_HV_X64_REGISTER_RIP,
info.header.rip + insn_len,
),
(hv_register_name_HV_X64_REGISTER_RAX, ret_rax),
];
set_registers_64!(self.fd, arr_reg_name_value)
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
Ok(cpu::VmExit::Ignore)
}
#[cfg(target_arch = "x86_64")]
@@ -1029,8 +1065,7 @@ impl cpu::Vcpu for MshvVcpu {
}
_ => {
panic!(
"SVM_EXITCODE_HV_DOORBELL_PAGE: Unhandled exit code: {:0x}",
exit_info1
"SVM_EXITCODE_HV_DOORBELL_PAGE: Unhandled exit code: {exit_info1:0x}"
);
}
}
@@ -1190,13 +1225,12 @@ impl cpu::Vcpu for MshvVcpu {
// Clear the SW_EXIT_INFO1 register to indicate no error
self.clear_swexit_info1()?;
}
_ => panic!(
"GHCB_INFO_NORMAL: Unhandled exit code: {:0x}",
exit_code
),
_ => {
panic!("GHCB_INFO_NORMAL: Unhandled exit code: {exit_code:0x}")
}
}
}
_ => panic!("Unsupported VMGEXIT operation: {:0x}", ghcb_op),
_ => panic!("Unsupported VMGEXIT operation: {ghcb_op:0x}"),
}
Ok(cpu::VmExit::Ignore)
@@ -1218,43 +1252,86 @@ impl cpu::Vcpu for MshvVcpu {
}
#[cfg(target_arch = "aarch64")]
fn init_pmu(&self, irq: u32) -> cpu::Result<()> {
unimplemented!()
fn init_pmu(&self, _irq: u32) -> cpu::Result<()> {
Ok(())
}
#[cfg(target_arch = "aarch64")]
fn has_pmu_support(&self) -> bool {
unimplemented!()
true
}
#[cfg(target_arch = "aarch64")]
fn setup_regs(&self, cpu_id: u8, boot_ip: u64, fdt_start: u64) -> cpu::Result<()> {
unimplemented!()
let arr_reg_name_value = [(
hv_register_name_HV_ARM64_REGISTER_PSTATE,
regs::PSTATE_FAULT_BITS_64,
)];
set_registers_64!(self.fd, arr_reg_name_value)
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
if cpu_id == 0 {
let arr_reg_name_value = [
(hv_register_name_HV_ARM64_REGISTER_PC, boot_ip),
(hv_register_name_HV_ARM64_REGISTER_X0, fdt_start),
];
set_registers_64!(self.fd, arr_reg_name_value)
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
}
Ok(())
}
#[cfg(target_arch = "aarch64")]
fn get_sys_reg(&self, sys_reg: u32) -> cpu::Result<u64> {
let mshv_reg = self.sys_reg_to_mshv_reg(sys_reg)?;
let mut reg_assocs = [hv_register_assoc {
name: mshv_reg,
..Default::default()
}];
self.fd
.get_reg(&mut reg_assocs)
.map_err(|e| cpu::HypervisorCpuError::GetRegister(e.into()))?;
// SAFETY: Accessing a union element from bindgen generated definition.
let res = unsafe { reg_assocs[0].value.reg64 };
Ok(res)
}
#[cfg(target_arch = "aarch64")]
fn get_reg_list(&self, _reg_list: &mut crate::RegList) -> cpu::Result<()> {
unimplemented!()
}
#[cfg(target_arch = "aarch64")]
fn get_reg_list(&self, reg_list: &mut RegList) -> cpu::Result<()> {
unimplemented!()
fn vcpu_init(&self, _kvi: &crate::VcpuInit) -> cpu::Result<()> {
Ok(())
}
#[cfg(target_arch = "aarch64")]
fn vcpu_init(&self, kvi: &VcpuInit) -> cpu::Result<()> {
unimplemented!()
fn vcpu_finalize(&self, _feature: i32) -> cpu::Result<()> {
Ok(())
}
#[cfg(target_arch = "aarch64")]
fn set_regs(&self, regs: &StandardRegisters) -> cpu::Result<()> {
unimplemented!()
fn vcpu_get_finalized_features(&self) -> i32 {
0
}
#[cfg(target_arch = "aarch64")]
fn get_regs(&self) -> cpu::Result<StandardRegisters> {
unimplemented!()
fn vcpu_set_processor_features(
&self,
_vm: &Arc<dyn crate::Vm>,
_kvi: &mut crate::VcpuInit,
_id: u8,
) -> cpu::Result<()> {
Ok(())
}
#[cfg(target_arch = "aarch64")]
fn create_vcpu_init(&self) -> crate::VcpuInit {
MshvVcpuInit {}.into()
}
#[cfg(target_arch = "x86_64")]
@@ -1364,7 +1441,7 @@ impl cpu::Vcpu for MshvVcpu {
///
/// Set CPU state for aarch64 guest.
///
fn set_state(&self, state: &CpuState) -> cpu::Result<()> {
fn set_state(&self, _state: &CpuState) -> cpu::Result<()> {
unimplemented!()
}
@@ -1481,6 +1558,24 @@ impl cpu::Vcpu for MshvVcpu {
.request_virtual_interrupt(&cfg)
.map_err(|e| cpu::HypervisorCpuError::Nmi(e.into()))
}
///
/// Set the GICR base address for the vcpu.
///
#[cfg(target_arch = "aarch64")]
fn set_gic_redistributor_addr(&self, gicr_base_addr: u64) -> cpu::Result<()> {
debug!(
"Setting GICR base address to: {:#x}, for vp_index: {:?}",
gicr_base_addr, self.vp_index
);
let arr_reg_name_value = [(
hv_register_name_HV_ARM64_REGISTER_GICR_BASE_GPA,
gicr_base_addr,
)];
set_registers_64!(self.fd, arr_reg_name_value)
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
Ok(())
}
}
impl MshvVcpu {
@@ -1598,6 +1693,50 @@ impl MshvVcpu {
Ok(())
}
#[cfg(target_arch = "x86_64")]
fn advance_rip_update_rax(
&self,
info: &hv_x64_io_port_intercept_message,
ret_rax: u64,
) -> cpu::Result<()> {
let insn_len = info.header.instruction_length() as u64;
/*
* Advance RIP and update RAX
* First, try to update the registers using VP register page
* which is mapped into user space for faster access.
* If the register page is not available, fall back to regular
* IOCTL to update the registers.
*/
if let Some(reg_page) = self.fd.get_vp_reg_page() {
let vp_reg_page = reg_page.0;
set_gp_regs_field_ptr!(vp_reg_page, rax, ret_rax);
// SAFETY: access union fields
unsafe {
(*vp_reg_page).__bindgen_anon_1.__bindgen_anon_1.rip = info.header.rip + insn_len;
(*vp_reg_page).dirty |= 1 << HV_X64_REGISTER_CLASS_IP;
}
} else {
let arr_reg_name_value = [
(
hv_register_name_HV_X64_REGISTER_RIP,
info.header.rip + insn_len,
),
(hv_register_name_HV_X64_REGISTER_RAX, ret_rax),
];
set_registers_64!(self.fd, arr_reg_name_value)
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
}
Ok(())
}
#[cfg(target_arch = "aarch64")]
fn sys_reg_to_mshv_reg(&self, sys_regs: u32) -> cpu::Result<u32> {
match sys_regs {
regs::MPIDR_EL1 => Ok(hv_register_name_HV_ARM64_REGISTER_MPIDR_EL1),
_ => Err(cpu::HypervisorCpuError::UnsupportedSysReg(sys_regs)),
}
}
}
/// Wrapper over Mshv VM ioctls.
@@ -1720,7 +1859,7 @@ impl vm::Vm for MshvVm {
MSHV_VP_MMAP_OFFSET_GHCB as i64 * libc::sysconf(libc::_SC_PAGE_SIZE),
)
};
if addr == libc::MAP_FAILED {
if std::ptr::eq(addr, libc::MAP_FAILED) {
// No point of continuing, without this mmap VMGEXIT will fail anyway
// Return error
return Err(vm::HypervisorVmError::MmapToRoot);
@@ -1868,7 +2007,7 @@ impl vm::Vm for MshvVm {
fn create_passthrough_device(&self) -> vm::Result<VfioDeviceFd> {
let mut vfio_dev = mshv_create_device {
type_: mshv_device_type_MSHV_DEV_TYPE_VFIO,
type_: MSHV_DEV_TYPE_VFIO,
fd: 0,
flags: 0,
};
@@ -1889,9 +2028,20 @@ impl vm::Vm for MshvVm {
data: cfg.data,
}
.into(),
#[cfg(target_arch = "x86_64")]
_ => {
unreachable!()
}
#[cfg(target_arch = "aarch64")]
InterruptSourceConfig::LegacyIrq(cfg) => mshv_user_irq_entry {
gsi,
// In order to get IRQ line we need to add `BASE_SPI_IRQ` to the pin number
// as `BASE_SPI_IRQ` is the base SPI interrupt number exposed via FDT to the
// guest.
data: cfg.pin + BASE_SPI_IRQ,
..Default::default()
}
.into(),
}
}
@@ -2091,12 +2241,36 @@ impl vm::Vm for MshvVm {
#[cfg(target_arch = "aarch64")]
fn create_vgic(&self, config: VgicConfig) -> vm::Result<Arc<Mutex<dyn Vgic>>> {
unimplemented!()
let gic_device = MshvGicV2M::new(self, config)
.map_err(|e| vm::HypervisorVmError::CreateVgic(anyhow!("Vgic error {:?}", e)))?;
// Register GICD address with the hypervisor
self.fd
.set_partition_property(
hv_partition_property_code_HV_PARTITION_PROPERTY_GICD_BASE_ADDRESS,
gic_device.dist_addr,
)
.map_err(|e| {
vm::HypervisorVmError::CreateVgic(anyhow!("Failed to set GICD address: {}", e))
})?;
// Register GITS address with the hypervisor
self.fd
.set_partition_property(
// spellchecker:disable-line
hv_partition_property_code_HV_PARTITION_PROPERTY_GITS_TRANSLATER_BASE_ADDRESS,
gic_device.gits_addr,
)
.map_err(|e| {
vm::HypervisorVmError::CreateVgic(anyhow!("Failed to set GITS address: {}", e))
})?;
Ok(Arc::new(Mutex::new(gic_device)))
}
#[cfg(target_arch = "aarch64")]
fn get_preferred_target(&self, kvi: &mut VcpuInit) -> vm::Result<()> {
unimplemented!()
fn get_preferred_target(&self, _kvi: &mut crate::VcpuInit) -> vm::Result<()> {
Ok(())
}
/// Pause the VM
@@ -2196,4 +2370,102 @@ impl vm::Vm for MshvVm {
Ok(())
}
fn init(&self) -> vm::Result<()> {
#[cfg(target_arch = "aarch64")]
{
self.fd
.set_partition_property(
hv_partition_property_code_HV_PARTITION_PROPERTY_GIC_LPI_INT_ID_BITS,
0,
)
.map_err(|e| {
vm::HypervisorVmError::InitializeVm(anyhow!(
"Failed to set GIC LPI support: {}",
e
))
})?;
self.fd
.set_partition_property(
hv_partition_property_code_HV_PARTITION_PROPERTY_GIC_PPI_OVERFLOW_INTERRUPT_FROM_CNTV,
(AARCH64_ARCH_TIMER_VIRT_IRQ + AARCH64_MIN_PPI_IRQ) as u64,
)
.map_err(|e| {
vm::HypervisorVmError::InitializeVm(anyhow!(
"Failed to set arch timer interrupt ID: {}",
e
))
})?;
self.fd
.set_partition_property(
hv_partition_property_code_HV_PARTITION_PROPERTY_GIC_PPI_PERFORMANCE_MONITORS_INTERRUPT,
(AARCH64_PMU_IRQ + AARCH64_MIN_PPI_IRQ) as u64,
)
.map_err(|e| {
vm::HypervisorVmError::InitializeVm(anyhow!(
"Failed to set PMU interrupt ID: {}",
e
))
})?;
}
self.fd
.initialize()
.map_err(|e| vm::HypervisorVmError::InitializeVm(e.into()))?;
// Set additional partition property for SEV-SNP partition.
#[cfg(feature = "sev_snp")]
if self.sev_snp_enabled {
let snp_policy = snp::get_default_snp_guest_policy();
let vmgexit_offloads = snp::get_default_vmgexit_offload_features();
// SAFETY: access union fields
unsafe {
debug!(
"Setting the partition isolation policy as: 0x{:x}",
snp_policy.as_uint64
);
self.fd
.set_partition_property(
hv_partition_property_code_HV_PARTITION_PROPERTY_ISOLATION_POLICY,
snp_policy.as_uint64,
)
.map_err(|e| vm::HypervisorVmError::InitializeVm(e.into()))?;
debug!(
"Setting the partition property to enable VMGEXIT offloads as : 0x{:x}",
vmgexit_offloads.as_uint64
);
self.fd
.set_partition_property(
hv_partition_property_code_HV_PARTITION_PROPERTY_SEV_VMGEXIT_OFFLOADS,
vmgexit_offloads.as_uint64,
)
.map_err(|e| vm::HypervisorVmError::InitializeVm(e.into()))?;
}
}
// Default Microsoft Hypervisor behavior for unimplemented MSR is to
// send a fault to the guest if it tries to access it. It is possible
// to override this behavior with a more suitable option i.e., ignore
// writes from the guest and return zero in attempt to read unimplemented
// MSR.
#[cfg(target_arch = "x86_64")]
self.fd
.set_partition_property(
hv_partition_property_code_HV_PARTITION_PROPERTY_UNIMPLEMENTED_MSR_ACTION,
hv_unimplemented_msr_action_HV_UNIMPLEMENTED_MSR_ACTION_IGNORE_WRITE_READ_ZERO
as u64,
)
.map_err(|e| vm::HypervisorVmError::InitializeVm(e.into()))?;
// Always create a frozen partition
self.fd
.set_partition_property(
hv_partition_property_code_HV_PARTITION_PROPERTY_TIME_FREEZE,
1u64,
)
.map_err(|e| vm::HypervisorVmError::InitializeVm(e.into()))?;
Ok(())
}
}

View File

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

View File

@@ -22,8 +22,6 @@ use igvm_defs::IGVM_VHS_SNP_ID_BLOCK;
use thiserror::Error;
use vmm_sys_util::eventfd::EventFd;
#[cfg(target_arch = "aarch64")]
use crate::aarch64::VcpuInit;
#[cfg(target_arch = "aarch64")]
use crate::arch::aarch64::gic::{Vgic, VgicConfig};
#[cfg(target_arch = "riscv64")]
@@ -257,6 +255,11 @@ pub enum HypervisorVmError {
#[cfg(feature = "sev_snp")]
#[error("Failed to mmap:")]
MmapToRoot,
///
/// Failed to initialize VM
///
#[error("Failed to initialize VM: {0}")]
InitializeVm(#[source] anyhow::Error),
}
///
/// Result type for returning from a function
@@ -351,7 +354,7 @@ pub trait Vm: Send + Sync + Any {
fn remove_user_memory_region(&self, user_memory_region: UserMemoryRegion) -> Result<()>;
/// Returns the preferred CPU target type which can be emulated by KVM on underlying host.
#[cfg(target_arch = "aarch64")]
fn get_preferred_target(&self, kvi: &mut VcpuInit) -> Result<()>;
fn get_preferred_target(&self, kvi: &mut crate::VcpuInit) -> Result<()>;
/// Enable split Irq capability
#[cfg(target_arch = "x86_64")]
fn enable_split_irq(&self) -> Result<()>;
@@ -419,7 +422,10 @@ pub trait Vm: Send + Sync + Any {
) -> Result<()> {
unimplemented!()
}
/// Initialize the VM
fn init(&self) -> Result<()> {
Ok(())
}
/// Pause the VM
fn pause(&self) -> Result<()> {
Ok(())

41
net_gen/src/ipv6.rs Normal file
View File

@@ -0,0 +1,41 @@
// Copyright © 2025 Cloud Hypervisor Authors
//
// SPDX-License-Identifier: Apache-2.0
// bindgen /usr/include/linux/ipv6.h --no-layout-tests --constified-enum '*' --allowlist-type 'sockaddr_in6|in6_ifreq'
/* automatically generated by rust-bindgen 0.71.1 */
pub type __u8 = ::std::os::raw::c_uchar;
pub type __u16 = ::std::os::raw::c_ushort;
pub type __u32 = ::std::os::raw::c_uint;
pub type __be16 = __u16;
pub type __be32 = __u32;
#[repr(C)]
#[derive(Copy, Clone)]
pub struct in6_addr {
pub in6_u: in6_addr__bindgen_ty_1,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub union in6_addr__bindgen_ty_1 {
pub u6_addr8: [__u8; 16usize],
pub u6_addr16: [__be16; 8usize],
pub u6_addr32: [__be32; 4usize],
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct sockaddr_in6 {
pub sin6_family: ::std::os::raw::c_ushort,
pub sin6_port: __be16,
pub sin6_flowinfo: __be32,
pub sin6_addr: in6_addr,
pub sin6_scope_id: __u32,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct in6_ifreq {
pub ifr6_addr: in6_addr,
pub ifr6_prefixlen: __u32,
pub ifr6_ifindex: ::std::os::raw::c_int,
}

View File

@@ -26,6 +26,9 @@ pub mod if_tun;
// --constified-enum '*' --with-derive-default
// Name is "inn" to avoid conflicting with "in" keyword.
pub mod inn;
// generated with bindgen /usr/include/linux/ipv6.h --no-layout-tests --constified-enum '*'
// --allowlist-type 'sockaddr_in6|in6_ifreq'
pub mod ipv6;
// generated with bindgen /usr/include/linux/sockios.h --no-unstable-rust
// --constified-enum '*' --with-derive-default
pub mod sockios;
@@ -35,6 +38,7 @@ pub use if_tun::{
};
pub use iff::{ifreq, net_device_flags_IFF_UP, setsockopt, sockaddr, AF_INET};
pub use inn::sockaddr_in;
pub use ipv6::{in6_ifreq, sockaddr_in6};
pub const TUNTAP: ::std::os::raw::c_uint = 84;

View File

@@ -6,7 +6,7 @@ version = "0.1.0"
[dependencies]
epoll = "4.3.3"
getrandom = "0.2.15"
getrandom = "0.3.1"
libc = "0.2.167"
log = "0.4.22"
net_gen = { path = "../net_gen" }
@@ -27,4 +27,4 @@ vmm-sys-util = { workspace = true }
once_cell = "1.20.2"
pnet = "0.35.0"
pnet_datalink = "0.35.0"
serde_json = "1.0.120"
serde_json = { workspace = true }

View File

@@ -5,6 +5,7 @@
use std::sync::Arc;
use libc::c_uint;
use thiserror::Error;
use virtio_bindings::virtio_net::{
VIRTIO_NET_CTRL_GUEST_OFFLOADS, VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, VIRTIO_NET_CTRL_MQ,
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN,
@@ -18,22 +19,29 @@ use vm_virtio::{AccessPlatform, Translatable};
use crate::{GuestMemoryMmap, Tap};
#[derive(Debug)]
#[derive(Error, Debug)]
pub enum Error {
/// Read queue failed.
GuestMemory(GuestMemoryError),
#[error("Read queue failed")]
GuestMemory(#[source] GuestMemoryError),
/// No control header descriptor
#[error("No control header descriptor")]
NoControlHeaderDescriptor,
/// Missing the data descriptor in the chain.
#[error("Missing the data descriptor in the chain")]
NoDataDescriptor,
/// No status descriptor
#[error("No status descriptor")]
NoStatusDescriptor,
/// Failed adding used index
QueueAddUsed(virtio_queue::Error),
#[error("Failed adding used index")]
QueueAddUsed(#[source] virtio_queue::Error),
/// Failed creating an iterator over the queue
QueueIterator(virtio_queue::Error),
#[error("Failed creating an iterator over the queue")]
QueueIterator(#[source] virtio_queue::Error),
/// Failed enabling notification for the queue
QueueEnableNotification(virtio_queue::Error),
#[error("Failed enabling notification for the queue")]
QueueEnableNotification(#[source] virtio_queue::Error),
}
type Result<T> = std::result::Result<T, Error>;

View File

@@ -15,6 +15,7 @@ mod queue_pair;
mod tap;
use std::io::Error as IoError;
use std::net::IpAddr;
use std::os::raw::c_uint;
use std::os::unix::io::{FromRawFd, RawFd};
use std::{io, mem, net};
@@ -40,7 +41,7 @@ pub use tap::{Error as TapError, Tap};
#[derive(Error, Debug)]
pub enum Error {
#[error("Failed to create a socket: {0}")]
CreateSocket(IoError),
CreateSocket(#[source] IoError),
}
pub type Result<T> = std::result::Result<T, Error>;
@@ -76,9 +77,14 @@ fn create_sockaddr(ip_addr: net::Ipv4Addr) -> net_gen::sockaddr {
unsafe { mem::transmute(addr_in) }
}
fn create_inet_socket() -> Result<net::UdpSocket> {
fn create_inet_socket(addr: IpAddr) -> Result<net::UdpSocket> {
let domain = match addr {
IpAddr::V4(_) => libc::AF_INET,
IpAddr::V6(_) => libc::AF_INET6,
};
// SAFETY: we check the return value.
let sock = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
let sock = unsafe { libc::socket(domain, libc::SOCK_DGRAM, 0) };
if sock < 0 {
return Err(Error::CreateSocket(IoError::last_os_error()));
}

View File

@@ -25,10 +25,10 @@ impl MacAddr {
{
let v: Vec<&str> = s.as_ref().split(':').collect();
let mut bytes = [0u8; MAC_ADDR_LEN];
let common_err = Err(io::Error::new(
io::ErrorKind::Other,
format!("parsing of {} into a MAC address failed", s.as_ref()),
));
let common_err = Err(io::Error::other(format!(
"parsing of {} into a MAC address failed",
s.as_ref()
)));
if v.len() != MAC_ADDR_LEN {
return common_err;
@@ -39,10 +39,11 @@ impl MacAddr {
return common_err;
}
bytes[i] = u8::from_str_radix(v[i], 16).map_err(|e| {
io::Error::new(
io::ErrorKind::Other,
format!("parsing of {} into a MAC address failed: {}", s.as_ref(), e),
)
io::Error::other(format!(
"parsing of {} into a MAC address failed: {}",
s.as_ref(),
e
))
})?;
}
@@ -64,10 +65,11 @@ impl MacAddr {
#[inline]
pub fn from_bytes(src: &[u8]) -> Result<MacAddr, io::Error> {
if src.len() != MAC_ADDR_LEN {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("invalid length of slice: {} vs {}", src.len(), MAC_ADDR_LEN),
));
return Err(io::Error::other(format!(
"invalid length of slice: {} vs {}",
src.len(),
MAC_ADDR_LEN
)));
}
Ok(MacAddr::from_bytes_unchecked(src))
}
@@ -80,7 +82,7 @@ impl MacAddr {
pub fn local_random() -> MacAddr {
// Generate a fully random MAC
let mut random_bytes = [0u8; MAC_ADDR_LEN];
if let Err(e) = getrandom::getrandom(&mut random_bytes) {
if let Err(e) = getrandom::fill(&mut random_bytes) {
error!(
"Error populating MAC address with random data: {}",
e.to_string()

View File

@@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use std::net::Ipv4Addr;
use std::net::IpAddr;
use std::path::Path;
use std::{fs, io};
@@ -13,29 +13,27 @@ use super::{vnet_hdr_len, MacAddr, Tap, TapError};
#[derive(Error, Debug)]
pub enum Error {
#[error("Failed to convert an hexadecimal string into an integer: {0}")]
ConvertHexStringToInt(std::num::ParseIntError),
ConvertHexStringToInt(#[source] std::num::ParseIntError),
#[error("Error related to the multiqueue support (no support TAP side)")]
MultiQueueNoTapSupport,
#[error("Error related to the multiqueue support (no support device side)")]
MultiQueueNoDeviceSupport,
#[error("Failed to read the TAP flags from sysfs: {0}")]
ReadSysfsTunFlags(io::Error),
ReadSysfsTunFlags(#[source] io::Error),
#[error("Open tap device failed: {0}")]
TapOpen(TapError),
#[error("Setting tap IP failed: {0}")]
TapSetIp(TapError),
#[error("Setting tap netmask failed: {0}")]
TapSetNetmask(TapError),
TapOpen(#[source] TapError),
#[error("Setting tap IP and/or netmask failed: {0}")]
TapSetIpNetmask(#[source] TapError),
#[error("Setting MAC address failed: {0}")]
TapSetMac(TapError),
TapSetMac(#[source] TapError),
#[error("Getting MAC address failed: {0}")]
TapGetMac(TapError),
TapGetMac(#[source] TapError),
#[error("Setting vnet header size failed: {0}")]
TapSetVnetHdrSize(TapError),
TapSetVnetHdrSize(#[source] TapError),
#[error("Setting MTU failed: {0}")]
TapSetMtu(TapError),
TapSetMtu(#[source] TapError),
#[error("Enabling tap interface failed: {0}")]
TapEnable(TapError),
TapEnable(#[source] TapError),
}
type Result<T> = std::result::Result<T, Error>;
@@ -64,8 +62,8 @@ fn check_mq_support(if_name: &Option<&str>, queue_pairs: usize) -> Result<()> {
/// netmask.
pub fn open_tap(
if_name: Option<&str>,
ip_addr: Option<Ipv4Addr>,
netmask: Option<Ipv4Addr>,
ip_addr: Option<IpAddr>,
netmask: Option<IpAddr>,
host_mac: &mut Option<MacAddr>,
mtu: Option<u16>,
num_rx_q: usize,
@@ -94,10 +92,8 @@ pub fn open_tap(
// Don't overwrite ip configuration of existing interfaces:
if !tap_existed {
if let Some(ip) = ip_addr {
tap.set_ip_addr(ip).map_err(Error::TapSetIp)?;
}
if let Some(mask) = netmask {
tap.set_netmask(mask).map_err(Error::TapSetNetmask)?;
tap.set_ip_addr(ip, netmask)
.map_err(Error::TapSetIpNetmask)?;
}
} else {
warn!(

View File

@@ -354,27 +354,27 @@ pub enum NetQueuePairError {
#[error("No memory configured")]
NoMemoryConfigured,
#[error("Error registering listener: {0}")]
RegisterListener(io::Error),
RegisterListener(#[source] io::Error),
#[error("Error unregistering listener: {0}")]
UnregisterListener(io::Error),
UnregisterListener(#[source] io::Error),
#[error("Error writing to the TAP device: {0}")]
WriteTap(io::Error),
WriteTap(#[source] io::Error),
#[error("Error reading from the TAP device: {0}")]
ReadTap(io::Error),
ReadTap(#[source] io::Error),
#[error("Error related to guest memory: {0}")]
GuestMemory(vm_memory::GuestMemoryError),
GuestMemory(#[source] vm_memory::GuestMemoryError),
#[error("Returned an error while iterating through the queue: {0}")]
QueueIteratorFailed(virtio_queue::Error),
QueueIteratorFailed(#[source] virtio_queue::Error),
#[error("Descriptor chain is too short")]
DescriptorChainTooShort,
#[error("Descriptor chain does not contain valid descriptors")]
DescriptorChainInvalid,
#[error("Failed to determine if queue needed notification: {0}")]
QueueNeedsNotification(virtio_queue::Error),
QueueNeedsNotification(#[source] virtio_queue::Error),
#[error("Failed to enable notification on the queue: {0}")]
QueueEnableNotification(virtio_queue::Error),
QueueEnableNotification(#[source] virtio_queue::Error),
#[error("Failed to add used index to the queue: {0}")]
QueueAddUsed(virtio_queue::Error),
QueueAddUsed(#[source] virtio_queue::Error),
#[error("Descriptor with invalid virtio-net header")]
DescriptorInvalidHeader,
#[error("Invalid virtio-net header")]

View File

@@ -7,7 +7,7 @@
use std::fs::File;
use std::io::{Error as IoError, Read, Result as IoResult, Write};
use std::net;
use std::net::{IpAddr, Ipv6Addr};
use std::os::raw::*;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
@@ -23,21 +23,23 @@ use crate::mac::MAC_ADDR_LEN;
#[derive(Error, Debug)]
pub enum Error {
#[error("Couldn't open /dev/net/tun: {0}")]
OpenTun(IoError),
OpenTun(#[source] IoError),
#[error("Unable to configure tap interface: {0}")]
ConfigureTap(IoError),
ConfigureTap(#[source] IoError),
#[error("Unable to retrieve features: {0}")]
GetFeatures(IoError),
GetFeatures(#[source] IoError),
#[error("Missing multiqueue support in the kernel")]
MultiQueueKernelSupport,
#[error("ioctl ({0}) failed: {1}")]
IoctlError(c_ulong, IoError),
IoctlError(c_ulong, #[source] IoError),
#[error("Failed to create a socket: {0}")]
NetUtil(NetUtilError),
NetUtil(#[source] NetUtilError),
#[error("Invalid interface name")]
InvalidIfname,
#[error("Error parsing MAC data: {0}")]
MacParsing(IoError),
MacParsing(#[source] IoError),
#[error("Invalid netmask")]
InvalidNetmask,
}
pub type Result<T> = ::std::result::Result<T, Error>;
@@ -88,6 +90,37 @@ fn build_terminated_if_name(if_name: &str) -> Result<Vec<u8>> {
Ok(terminated_if_name)
}
fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8> {
let mask = mask.segments();
let mut iter = mask.iter();
let mut prefix = 0;
for &segment in &mut iter {
if segment == 0xffff {
prefix += 16;
} else if segment == 0 {
break;
} else {
let prefix_bits = segment.leading_ones() as u8;
if segment << prefix_bits != 0 {
return Err(Error::InvalidNetmask);
}
prefix += prefix_bits;
break;
}
}
// Check that remaining bits are all unset
for &segment in iter {
if segment != 0 {
return Err(Error::InvalidNetmask);
}
}
Ok(prefix)
}
impl Tap {
unsafe fn ioctl_with_mut_ref<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: &mut T) -> Result<()> {
let ret = ioctl_with_mut_ref(fd, req, arg);
@@ -235,16 +268,78 @@ impl Tap {
}
/// Set the host-side IP address for the tap interface.
pub fn set_ip_addr(&self, ip_addr: net::Ipv4Addr) -> Result<()> {
let sock = create_inet_socket().map_err(Error::NetUtil)?;
let addr = create_sockaddr(ip_addr);
pub fn set_ip_addr(&self, ip_addr: IpAddr, netmask: Option<IpAddr>) -> Result<()> {
let sock = create_inet_socket(ip_addr).map_err(Error::NetUtil)?;
let mut ifreq = self.get_ifreq();
ifreq.ifr_ifru.ifru_addr = addr;
match ip_addr {
IpAddr::V4(addr) => {
let addr = create_sockaddr(addr);
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFADDR as c_ulong, &ifreq) }
ifreq.ifr_ifru.ifru_addr = addr;
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
unsafe {
Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFADDR as c_ulong, &ifreq)?;
}
if let Some(IpAddr::V4(mask)) = netmask {
ifreq.ifr_ifru.ifru_netmask = create_sockaddr(mask);
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
unsafe {
Self::ioctl_with_ref(
&sock,
net_gen::sockios::SIOCSIFNETMASK as c_ulong,
&ifreq,
)?;
}
};
Ok(())
}
IpAddr::V6(addr) => {
let ifindex = {
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
unsafe {
Self::ioctl_with_ref(
&sock,
net_gen::sockios::SIOCGIFINDEX as c_ulong,
&ifreq,
)?;
}
// SAFETY: ifru_ivalue contains the ifindex and is set by the previous ioctl
unsafe {
match ifreq.ifr_ifru.ifru_ivalue {
0 => return Err(Error::InvalidIfname),
i => i,
}
}
};
let prefixlen = match netmask {
Some(IpAddr::V6(netmask)) => ipv6_mask_to_prefix(netmask)?,
Some(IpAddr::V4(_)) => return Err(Error::InvalidNetmask),
None => 0,
};
let ifreq = net_gen::in6_ifreq {
// SAFETY: addr can be safely transmuted to in6_addr
ifr6_addr: unsafe {
std::mem::transmute::<[u8; 16], net_gen::ipv6::in6_addr>(addr.octets())
},
ifr6_prefixlen: prefixlen as u32,
ifr6_ifindex: ifindex,
};
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
unsafe {
Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFADDR as c_ulong, &ifreq)
}
}
}
}
/// Set mac addr for tap interface.
@@ -294,19 +389,6 @@ impl Tap {
Ok(addr)
}
/// Set the netmask for the subnet that the tap interface will exist on.
pub fn set_netmask(&self, netmask: net::Ipv4Addr) -> Result<()> {
let sock = create_inet_socket().map_err(Error::NetUtil)?;
let addr = create_sockaddr(netmask);
let mut ifreq = self.get_ifreq();
ifreq.ifr_ifru.ifru_addr = addr;
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFNETMASK as c_ulong, &ifreq) }
}
#[cfg(not(fuzzing))]
pub fn mtu(&self) -> Result<i32> {
let sock = create_unix_socket().map_err(Error::NetUtil)?;
@@ -602,11 +684,22 @@ mod tests {
let tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
let tap = Tap::new(1).unwrap();
let ip_addr: net::Ipv4Addr = (*tap_ip_guard).parse().unwrap();
let netmask: net::Ipv4Addr = SUBNET_MASK.parse().unwrap();
let ip_addr = IpAddr::V4((*tap_ip_guard).parse().unwrap());
let netmask = IpAddr::V4(SUBNET_MASK.parse().unwrap());
tap.set_ip_addr(ip_addr).unwrap();
tap.set_netmask(netmask).unwrap();
tap.set_ip_addr(ip_addr, Some(netmask)).unwrap();
}
#[test]
fn test_tap_configure_ipv6() {
let tap_ip6_lock: Mutex<&'static str> = Mutex::new("2001:db8:85a3::8a2e:370:7334");
let tap_ip6_guard = tap_ip6_lock.lock().unwrap();
let tap = Tap::new(1).unwrap();
let ip_addr = IpAddr::V6((*tap_ip6_guard).parse().unwrap());
let netmask = IpAddr::V6("ffff:ffff::".parse().unwrap());
tap.set_ip_addr(ip_addr, Some(netmask)).unwrap();
}
#[test]
@@ -640,8 +733,9 @@ mod tests {
let tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
let mut tap = Tap::new(1).unwrap();
tap.set_ip_addr((*tap_ip_guard).parse().unwrap()).unwrap();
tap.set_netmask(SUBNET_MASK.parse().unwrap()).unwrap();
let ip_addr = IpAddr::V4((*tap_ip_guard).parse().unwrap());
let netmask = IpAddr::V4(SUBNET_MASK.parse().unwrap());
tap.set_ip_addr(ip_addr, Some(netmask)).unwrap();
tap.enable().unwrap();
// Send a packet to the interface. We expect to be able to receive it on the associated fd.
@@ -698,8 +792,9 @@ mod tests {
let tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
let mut tap = Tap::new(1).unwrap();
tap.set_ip_addr((*tap_ip_guard).parse().unwrap()).unwrap();
tap.set_netmask(SUBNET_MASK.parse().unwrap()).unwrap();
let ip_addr = IpAddr::V4((*tap_ip_guard).parse().unwrap());
let netmask = IpAddr::V4(SUBNET_MASK.parse().unwrap());
tap.set_ip_addr(ip_addr, Some(netmask)).unwrap();
tap.enable().unwrap();
let (mac, _, mut rx) = pnet_get_mac_tx_rx(tap_name_to_string(&tap));

View File

@@ -3,3 +3,6 @@ authors = ["The Cloud Hypervisor Authors"]
edition = "2021"
name = "option_parser"
version = "0.1.0"
[dependencies]
thiserror = "2.0.6"

View File

@@ -4,10 +4,11 @@
//
use std::collections::HashMap;
use std::fmt;
use std::num::ParseIntError;
use std::str::FromStr;
use thiserror::Error;
#[derive(Default)]
pub struct OptionParser {
options: HashMap<String, OptionParserValue>,
@@ -18,26 +19,17 @@ struct OptionParserValue {
requires_value: bool,
}
#[derive(Debug)]
#[derive(Error, Debug)]
pub enum OptionParserError {
#[error("unknown option: {0}")]
UnknownOption(String),
#[error("unknown option: {0}")]
InvalidSyntax(String),
Conversion(String, String),
#[error("unable to convert {1} for {0}")]
Conversion(String /* field */, String /* value */),
#[error("invalid value: {0}")]
InvalidValue(String),
}
impl fmt::Display for OptionParserError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
OptionParserError::UnknownOption(s) => write!(f, "unknown option: {s}"),
OptionParserError::InvalidSyntax(s) => write!(f, "invalid syntax:{s}"),
OptionParserError::Conversion(field, value) => {
write!(f, "unable to convert {value} for {field}")
}
OptionParserError::InvalidValue(s) => write!(f, "invalid value: {s}"),
}
}
}
type OptionParserResult<T> = std::result::Result<T, OptionParserError>;
fn split_commas(s: &str) -> OptionParserResult<Vec<String>> {
@@ -163,7 +155,9 @@ impl OptionParser {
pub struct Toggle(pub bool);
#[derive(Error, Debug)]
pub enum ToggleParseError {
#[error("invalid value: {0}")]
InvalidValue(String),
}
@@ -184,8 +178,9 @@ impl FromStr for Toggle {
pub struct ByteSized(pub u64);
#[derive(Debug)]
#[derive(Error, Debug)]
pub enum ByteSizedParseError {
#[error("invalid value: {0}")]
InvalidValue(String),
}
@@ -215,7 +210,9 @@ impl FromStr for ByteSized {
pub struct IntegerList(pub Vec<u64>);
#[derive(Error, Debug)]
pub enum IntegerListParseError {
#[error("invalid value: {0}")]
InvalidValue(String),
}
@@ -305,11 +302,16 @@ impl TupleValue for Vec<usize> {
pub struct Tuple<S, T>(pub Vec<(S, T)>);
#[derive(Error, Debug)]
pub enum TupleError {
#[error("invalid value: {0}")]
InvalidValue(String),
SplitOutsideBrackets(OptionParserError),
InvalidIntegerList(IntegerListParseError),
InvalidInteger(ParseIntError),
#[error("split outside brackets: {0}")]
SplitOutsideBrackets(#[source] OptionParserError),
#[error("invalid integer list: {0}")]
InvalidIntegerList(#[source] IntegerListParseError),
#[error("invalid integer: {0}")]
InvalidInteger(#[source] ParseIntError),
}
impl<S: FromStr, T: TupleValue> FromStr for Tuple<S, T> {
@@ -347,7 +349,9 @@ impl<S: FromStr, T: TupleValue> FromStr for Tuple<S, T> {
#[derive(Default)]
pub struct StringList(pub Vec<String>);
#[derive(Error, Debug)]
pub enum StringListParseError {
#[error("invalid value: {0}")]
InvalidValue(String),
}

View File

@@ -10,12 +10,13 @@ use std::ops::DerefMut;
use std::sync::{Arc, Barrier, Mutex};
use byteorder::{ByteOrder, LittleEndian};
use thiserror::Error;
use vm_device::{Bus, BusDevice, BusDeviceSync};
use crate::configuration::{
PciBarRegionType, PciBridgeSubclass, PciClassCode, PciConfiguration, PciHeaderType,
};
use crate::device::{DeviceRelocation, Error as PciDeviceError, PciDevice};
use crate::device::{BarReprogrammingParams, DeviceRelocation, Error as PciDeviceError, PciDevice};
use crate::PciBarConfiguration;
const VENDOR_ID_INTEL: u16 = 0x8086;
@@ -23,21 +24,28 @@ const DEVICE_ID_INTEL_VIRT_PCIE_HOST: u16 = 0x0d57;
const NUM_DEVICE_IDS: usize = 32;
/// Errors for device manager.
#[derive(Debug)]
#[derive(Error, Debug)]
pub enum PciRootError {
/// Could not allocate device address space for the device.
AllocateDeviceAddrs(PciDeviceError),
#[error("Could not allocate device address space for the device: {0}")]
AllocateDeviceAddrs(#[source] PciDeviceError),
/// Could not allocate an IRQ number.
#[error("Could not allocate an IRQ number")]
AllocateIrq,
/// Could not add a device to the port io bus.
PioInsert(vm_device::BusError),
#[error("Could not add a device to the port io bus: {0}")]
PioInsert(#[source] vm_device::BusError),
/// Could not add a device to the mmio bus.
MmioInsert(vm_device::BusError),
#[error("Could not add a device to the mmio bus: {0}")]
MmioInsert(#[source] vm_device::BusError),
/// Could not find an available device slot on the PCI bus.
#[error("Could not find an available device slot on the PCI bus")]
NoPciDeviceSlotAvailable,
/// Invalid PCI device identifier provided.
#[error("Invalid PCI device identifier provided")]
InvalidPciDeviceSlot(usize),
/// Valid PCI device identifier but already used.
#[error("Valid PCI device identifier but already used")]
AlreadyInUsePciDeviceSlot(usize),
}
pub type Result<T> = std::result::Result<T, PciRootError>;
@@ -81,9 +89,11 @@ impl PciDevice for PciRoot {
reg_idx: usize,
offset: u64,
data: &[u8],
) -> Option<Arc<Barrier>> {
self.config.write_config_register(reg_idx, offset, data);
None
) -> (Vec<BarReprogrammingParams>, Option<Arc<Barrier>>) {
(
self.config.write_config_register(reg_idx, offset, data),
None,
)
}
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
@@ -256,9 +266,11 @@ impl PciConfigIo {
if let Some(d) = pci_bus.devices.get(&(device as u32)) {
let mut device = d.lock().unwrap();
// Find out if one of the device's BAR is being reprogrammed, and
// reprogram it if needed.
if let Some(params) = device.detect_bar_reprogramming(register, data) {
// Update the register value
let (bar_reprogram, ret) = device.write_config_register(register, offset, data);
// Move the device's BAR if needed
for params in &bar_reprogram {
if let Err(e) = pci_bus.device_reloc.move_bar(
params.old_base,
params.new_base,
@@ -273,8 +285,7 @@ impl PciConfigIo {
}
}
// Update the register value
device.write_config_register(register, offset, data)
ret
} else {
None
}
@@ -380,9 +391,11 @@ impl PciConfigMmio {
if let Some(d) = pci_bus.devices.get(&(device as u32)) {
let mut device = d.lock().unwrap();
// Find out if one of the device's BAR is being reprogrammed, and
// reprogram it if needed.
if let Some(params) = device.detect_bar_reprogramming(register, data) {
// Update the register value
let (bar_reprogram, _) = device.write_config_register(register, offset, data);
// Move the device's BAR if needed
for params in &bar_reprogram {
if let Err(e) = pci_bus.device_reloc.move_bar(
params.old_base,
params.new_base,
@@ -396,9 +409,6 @@ impl PciConfigMmio {
);
}
}
// Update the register value
device.write_config_register(register, offset, data);
}
}
}

View File

@@ -4,11 +4,11 @@
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use std::fmt::{self, Display};
use std::sync::{Arc, Mutex};
use byteorder::{ByteOrder, LittleEndian};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use vm_device::PciBarType;
use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable};
@@ -18,6 +18,8 @@ use crate::{MsixConfig, PciInterruptPin};
// The number of 32bit registers in the config space, 4096 bytes.
const NUM_CONFIGURATION_REGISTERS: usize = 1024;
pub(crate) const COMMAND_REG: usize = 1;
pub(crate) const COMMAND_REG_MEMORY_SPACE_MASK: u32 = 0x0000_0002;
const STATUS_REG: usize = 1;
const STATUS_REG_CAPABILITIES_USED_MASK: u32 = 0x0010_0000;
const BAR0_REG: usize = 4;
@@ -436,6 +438,7 @@ pub struct PciConfiguration {
last_capability: Option<(usize, usize)>,
msix_cap_reg_idx: Option<usize>,
msix_config: Option<Arc<Mutex<MsixConfig>>>,
pending_bar_reprogram: Vec<BarReprogrammingParams>,
}
/// See pci_regs.h in kernel
@@ -490,60 +493,45 @@ pub struct PciBarConfiguration {
prefetchable: PciBarPrefetchable,
}
#[derive(Debug)]
#[derive(Error, Debug)]
pub enum Error {
#[error("address {0} size {1} too big")]
BarAddressInvalid(u64, u64),
#[error("bar {0} already used")]
BarInUse(usize),
#[error("64bit bar {0} already used (requires two regs)")]
BarInUse64(usize),
#[error("bar {0} invalid, max {max}", max = NUM_BAR_REGS - 1)]
BarInvalid(usize),
#[error("64bitbar {0} invalid, requires two regs, max {max}", max = NUM_BAR_REGS - 1)]
BarInvalid64(usize),
#[error("bar address {0} not a power of two")]
BarSizeInvalid(u64),
#[error("empty capabilities are invalid")]
CapabilityEmpty,
#[error("Invalid capability length {0}")]
CapabilityLengthInvalid(usize),
#[error("capability of size {0} doesn't fit")]
CapabilitySpaceFull(usize),
#[error("failed to decode 32 bits BAR size")]
Decode32BarSize,
#[error("failed to decode 64 bits BAR size")]
Decode64BarSize,
#[error("failed to encode 32 bits BAR size")]
Encode32BarSize,
#[error("failed to encode 64 bits BAR size")]
Encode64BarSize,
#[error("address {0} size {1} too big")]
RomBarAddressInvalid(u64, u64),
#[error("rom bar {0} already used")]
RomBarInUse(usize),
#[error("rom bar {0} invalid, max {max}", max = NUM_BAR_REGS - 1)]
RomBarInvalid(usize),
#[error("rom bar address {0} not a power of two")]
RomBarSizeInvalid(u64),
}
pub type Result<T> = std::result::Result<T, Error>;
impl std::error::Error for Error {}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match self {
BarAddressInvalid(a, s) => write!(f, "address {a} size {s} too big"),
BarInUse(b) => write!(f, "bar {b} already used"),
BarInUse64(b) => write!(f, "64bit bar {b} already used(requires two regs)"),
BarInvalid(b) => write!(f, "bar {} invalid, max {}", b, NUM_BAR_REGS - 1),
BarInvalid64(b) => write!(
f,
"64bitbar {} invalid, requires two regs, max {}",
b,
NUM_BAR_REGS - 1
),
BarSizeInvalid(s) => write!(f, "bar address {s} not a power of two"),
CapabilityEmpty => write!(f, "empty capabilities are invalid"),
CapabilityLengthInvalid(l) => write!(f, "Invalid capability length {l}"),
CapabilitySpaceFull(s) => write!(f, "capability of size {s} doesn't fit"),
Decode32BarSize => write!(f, "failed to decode 32 bits BAR size"),
Decode64BarSize => write!(f, "failed to decode 64 bits BAR size"),
Encode32BarSize => write!(f, "failed to encode 32 bits BAR size"),
Encode64BarSize => write!(f, "failed to encode 64 bits BAR size"),
RomBarAddressInvalid(a, s) => write!(f, "address {a} size {s} too big"),
RomBarInUse(b) => write!(f, "rom bar {b} already used"),
RomBarInvalid(b) => write!(f, "rom bar {} invalid, max {}", b, NUM_BAR_REGS - 1),
RomBarSizeInvalid(s) => write!(f, "rom bar address {s} not a power of two"),
}
}
}
impl PciConfiguration {
#[allow(clippy::too_many_arguments)]
pub fn new(
@@ -630,6 +618,7 @@ impl PciConfiguration {
last_capability,
msix_cap_reg_idx,
msix_config,
pending_bar_reprogram: Vec::new(),
}
}
@@ -907,9 +896,14 @@ impl PciConfiguration {
(next + 3) & !3
}
pub fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
pub fn write_config_register(
&mut self,
reg_idx: usize,
offset: u64,
data: &[u8],
) -> Vec<BarReprogrammingParams> {
if offset as usize + data.len() > 4 {
return;
return Vec::new();
}
// Handle potential write to MSI-X message control register
@@ -938,13 +932,37 @@ impl PciConfiguration {
4 => self.write_reg(reg_idx, LittleEndian::read_u32(data)),
_ => (),
}
if let Some(param) = self.detect_bar_reprogramming(reg_idx, data) {
self.pending_bar_reprogram.push(param);
}
if !self.pending_bar_reprogram.is_empty() {
// Return bar reprogramming only if the MSE bit is enabled;
if self.read_config_register(COMMAND_REG) & COMMAND_REG_MEMORY_SPACE_MASK
== COMMAND_REG_MEMORY_SPACE_MASK
{
info!(
"BAR reprogramming parameter is returned: {:x?}",
self.pending_bar_reprogram
);
return self.pending_bar_reprogram.drain(..).collect();
} else {
info!(
"MSE bit is disabled. No BAR reprogramming parameter is returned: {:x?}",
self.pending_bar_reprogram
);
}
}
Vec::new()
}
pub fn read_config_register(&self, reg_idx: usize) -> u32 {
self.read_reg(reg_idx)
}
pub fn detect_bar_reprogramming(
fn detect_bar_reprogramming(
&mut self,
reg_idx: usize,
data: &[u8],
@@ -981,7 +999,7 @@ impl PciConfiguration {
info!(
"Detected BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}",
reg_idx, self.registers[reg_idx], value
bar_idx, self.bars[bar_idx].addr, value
);
let old_base = u64::from(self.bars[bar_idx].addr & mask);
let new_base = u64::from(value & mask);
@@ -1007,7 +1025,7 @@ impl PciConfiguration {
{
info!(
"Detected BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}",
reg_idx, self.registers[reg_idx], value
bar_idx, self.bars[bar_idx].addr, value
);
let old_base = (u64::from(self.bars[bar_idx].addr & mask) << 32)
| u64::from(self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1]);
@@ -1036,8 +1054,8 @@ impl PciConfiguration {
}
info!(
"Detected ROM BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}",
reg_idx, self.registers[reg_idx], value
"Detected ROM BAR reprogramming: (Expansion ROM BAR) 0x{:x}->0x{:x}",
self.rom_bar_addr, value
);
let old_base = u64::from(self.rom_bar_addr & mask);
let new_base = u64::from(value & mask);
@@ -1060,6 +1078,14 @@ impl PciConfiguration {
None
}
pub(crate) fn pending_bar_reprogram(&self) -> Vec<BarReprogrammingParams> {
self.pending_bar_reprogram.clone()
}
pub(crate) fn clear_pending_bar_reprogram(&mut self) {
self.pending_bar_reprogram = Vec::new();
}
}
impl Pausable for PciConfiguration {}

View File

@@ -5,50 +5,37 @@
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use std::any::Any;
use std::fmt::{self, Display};
use std::sync::{Arc, Barrier, Mutex};
use std::{io, result};
use thiserror::Error;
use vm_allocator::{AddressAllocator, SystemAllocator};
use vm_device::Resource;
use crate::configuration::{self, PciBarRegionType};
use crate::PciBarConfiguration;
#[derive(Debug)]
#[derive(Error, Debug)]
pub enum Error {
/// Setup of the device capabilities failed.
CapabilitiesSetup(configuration::Error),
#[error("Setup of the device capabilities failed: {0}")]
CapabilitiesSetup(#[source] configuration::Error),
/// Allocating space for an IO BAR failed.
#[error("Allocating space for an IO BAR failed")]
IoAllocationFailed(u64),
/// Registering an IO BAR failed.
IoRegistrationFailed(u64, configuration::Error),
#[error("Registering an IO BAR failed: {0}")]
IoRegistrationFailed(u64, #[source] configuration::Error),
/// Expected resource not found.
#[error("Expected resource not found")]
MissingResource,
/// Invalid resource.
#[error("Invalid resource: {0:?}")]
InvalidResource(Resource),
}
pub type Result<T> = std::result::Result<T, Error>;
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match self {
CapabilitiesSetup(e) => write!(f, "failed to add capability {e}"),
IoAllocationFailed(size) => {
write!(f, "failed to allocate space for an IO BAR, size={size}")
}
IoRegistrationFailed(addr, e) => {
write!(f, "failed to register an IO BAR, addr={addr} err={e}")
}
MissingResource => write!(f, "failed to find expected resource"),
InvalidResource(r) => write!(f, "invalid resource {r:?}"),
}
}
}
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Debug)]
pub struct BarReprogrammingParams {
pub old_base: u64,
pub new_base: u64,
@@ -87,18 +74,10 @@ pub trait PciDevice: Send {
reg_idx: usize,
offset: u64,
data: &[u8],
) -> Option<Arc<Barrier>>;
) -> (Vec<BarReprogrammingParams>, Option<Arc<Barrier>>);
/// Gets a register from the configuration space.
/// * `reg_idx` - The index of the config register to read.
fn read_config_register(&mut self, reg_idx: usize) -> u32;
/// Detects if a BAR is being reprogrammed.
fn detect_bar_reprogramming(
&mut self,
_reg_idx: usize,
_data: &[u8],
) -> Option<BarReprogrammingParams> {
None
}
/// Reads from a BAR region mapped into the device.
/// * `addr` - The guest address inside the BAR.
/// * `data` - Filled with the data from `addr`.

View File

@@ -40,9 +40,9 @@ pub fn msi_num_enabled_vectors(msg_ctl: u16) -> usize {
#[derive(Error, Debug)]
pub enum Error {
#[error("Failed enabling the interrupt route: {0}")]
EnableInterruptRoute(io::Error),
EnableInterruptRoute(#[source] io::Error),
#[error("Failed updating the interrupt route: {0}")]
UpdateInterruptRoute(io::Error),
UpdateInterruptRoute(#[source] io::Error),
}
pub const MSI_CONFIG_ID: &str = "msi_config";

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