Compare commits

..

231 Commits
v45.0 ... v47.0

Author SHA1 Message Date
Bo Chen
62001b65e9 build: Release v47.0
Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-07-22 21:06:54 +00:00
Wei Liu
4be2ca4c10 vhost_user_net: Use Mutex::get_mut() where possible
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-07-17 19:20:59 +00:00
Wei Liu
5716af09a5 vhost_user_block: Use Mutex::get_mut() where possible
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-07-17 19:20:59 +00:00
Wei Liu
4ea40b4bea rate_limiter: Use Mutex::get_mut() in update_buckets
There is no need to lock. That function already holds a mutable
reference to self.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-07-17 19:20:59 +00:00
Jean-Philippe Brucker
4528e2f1ea devices: rtc_pl031: Disable broken interrupt
The PL031 RTC provides two features: a real-time counter and an alarm
interrupt. To use the alarm, the driver normally writes a time value
into the match register RTCMR, and when the counter reaches that value
the device triggers the interrupt.

At the moment the implementation ignores programming of the alarm, as
the feature seems rarely used in VMs. However the interrupt is still
triggered arbitrarily when the guest writes to registers, and the line
is never cleared. This really confuses the Linux driver, which loops in
the interrupt handler until Linux realizes that no one is dealing with
the interrupt (200000 unanswered calls) and disables the handler.

One way to fix this would be implementing the alarm function properly,
which isn't too difficult but requires adding some async timer logic
which probably won't ever get used. In addition the device's interrupt
is level-triggered and we don't support level interrupts at the moment,
though we could probably get away with changing this interrupt to edge.

The simplest fix, though, is to just disable the interrupt logic
entirely, so that the alarm function still doesn't work but the guest
doesn't see spurious interrupts.

Add a default() implementation to satisfy clippy's new_without_default
check, since Rtc::new() doesn't take a parameter after this change.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
2025-07-17 17:21:05 +00:00
Bo Chen
987ad11c90 main: Report errors with 'error!()'
This was missed from #7183, likely because `eprint!` is used instead of
`eprintln!`.

Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-07-17 16:18:56 +00:00
Wei Liu
cea708deb9 performance-metrics: Fix the names of the kernels
In 2b05753716, the names of the reference kernels are changed.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-07-16 20:20:32 +00:00
Songqian Li
e32fa593e5 build: clean up unused dependencies
Signed-off-by: Songqian Li <sionli@tencent.com>
2025-07-15 07:16:36 +00:00
Alyssa Ross
ec8fceb4a6 virtio-devices: stop corrupting vsock commands
The read_exact() call was introduced in 82ac114b8 ("virtio-devices:
vsock: handle short read in muxer") to solve a crash when a connection
disconnected without sending any data, but it introduced a problem of
its own: because the socket is non-blocking, read_exact() may read
some data, then return ErrorKind::WouldBlock.  In that case, the data
it read will be discarded.  So for example if it read "CONNECT ",
and then nothing else was available to read yet, "CONNECT " would be
discarded, and so the next time this function was called, when epoll
triggered again for the socket, only the following data would end up
in command.buf, causing an error due to just a port number being an
invalid command.

Contrary to that commit message, this code was actually designed to
handle short reads just fine — in the case of a short read, it stores
the data it has read in command, and returns
Error::UnixRead(ErrorKind::WouldBlock), which is ignored by the
caller, and the function gets called again when there is more data to
read, building up command potentially over the course of several
reads.  The only thing it didn't handle correctly, as far as I can
tell, was a 0-byte read, which happens when a client disconnects from
the socket without writing anything.  All that's needed to fix this is
to avoid an invalid subtraction in that case, so this change reverts
82ac114b8, fixing the issue with partial commands being discarded, and
instead handles the 0-byte read by using slice::get, and treating an
empty command as an incomplete command, which of course it is.

Fixes: 82ac114b8 ("virtio-devices: vsock: handle short read in muxer")
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2025-07-14 18:07:07 +00:00
Alyssa Ross
01aed9733c build: add missing dependency features
This makes it possible to run cargo test just for the virtio-devices
crate (as long as either KVM or MSHV is specified).

Signed-off-by: Alyssa Ross <hi@alyssa.is>
2025-07-14 18:06:54 +00:00
Nuno Das Neves
a5cd1b4fbe build: Bump mshv-ioctls and mshv-bindings to v0.5.2
Also update the version in the fuzz crate.

Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
2025-07-12 01:17:26 +00:00
Muminul Islam
b268e88ba3 virtio-devices: remove unnecessary parentheses
Cargo fuzz build report an warning:

warning: unnecessary parentheses around closure body
--> virtio-devices/src/iommu.rs:578:41
|
578 |.retain(|&x, _| (x < req.virt_start || x > req.virt_end));
|                                         ^
|
= note: `#[warn(unused_parens)]` on by default
help: remove these parentheses
|
578 -.retain(|&x, _| (x < req.virt_start || x > req.virt_end));
578 +.retain(|&x, _| x < req.virt_start || x > req.virt_end);
|

warning: `virtio-devices` (lib) generated 1 warning
(run `cargo fix --lib -p virtio-devices` to apply 1 suggestion)

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2025-07-11 22:02:15 +00:00
dependabot[bot]
0659eaeba1 build: Bump async-signal from 0.2.10 to 0.2.11
Bumps [async-signal](https://github.com/smol-rs/async-signal) from 0.2.10 to 0.2.11.
- [Release notes](https://github.com/smol-rs/async-signal/releases)
- [Changelog](https://github.com/smol-rs/async-signal/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/async-signal/compare/v0.2.10...v0.2.11)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-11 00:38:49 +00:00
Bo Chen
96528f84f9 build: Bump gdbstub from 0.7.1 to 0.7.6
Bumps [gdbstub](https://github.com/daniel5151/gdbstub) from 0.7.1 to 0.7.6.
- [Release notes](https://github.com/daniel5151/gdbstub/releases)
- [Changelog](https://github.com/daniel5151/gdbstub/blob/master/CHANGELOG.md)
- [Commits](https://github.com/daniel5151/gdbstub/compare/0.7.1...0.7.6)

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

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-07-10 19:46:28 +00:00
Ruoqing He
6da5c32fd9 hypervisor: aarch64: Use offset_of for nested fields
`std::mem::offset_of` could be used for calculating nested fields, use
this feature to shorten aarch64 reg offset calculation.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-07-10 16:39:39 +00:00
Ruoqing He
07cc1f6545 hypervisor: aarch64: Remove manually implemented offset_of
Manually implemented `offset_of` in `arch/aarch64/mod.rs` is not used
now, remove it.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-07-10 16:39:39 +00:00
Ruoqing He
008f259aff hypervisor: aarch64: Use offset_of from std::mem
`std::mem::offset_of` is stabilized since Rust 1.77, let's use
implementation provided by std instead of manual implementation.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-07-10 16:39:39 +00:00
Ruoqing He
aa6fefa80f hypervisor: riscv64: Remove manually implemented offset_of
Manually implemented `_offset_of` and `offset_of` in
`arch/riscv64/mod.rs` are not used now, remove them.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-07-10 16:39:39 +00:00
Ruoqing He
87e74719ec hypervisor: riscv64: Use offset_of from std::mem
`std::mem::offset_of` supports calculating offset of nested structures,
let's use implementation provided by std instead of manual
implementation.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
2025-07-10 16:39:39 +00:00
Maximilian Güntner
072f06ff4c misc: vhost_user_net: replace eprintln with log::error
Other lines are already logged using `log::error!` and
`env_logger` is initialized before calling
`start_net_backend` in `main()`.

Signed-off-by: Maximilian Güntner <code@mguentner.de>
2025-07-10 16:36:54 +00:00
Maximilian Güntner
50b33db718 vmm: replace eprintln with log::error
Unify log formatting and printing as `eprintln!` and `log::error!`
would be used alongside each other.
When using e.g. `env_logger` lines printed with `eprintln!` would
lack formatting / colors.
Currently only relevant in `ch-remote` + `cli_print_error_chain`.

Note that the replaced messages now also end up in the logfile of
`cloud-hypervisor` when configured and not any longer in stderr.

Signed-off-by: Maximilian Güntner <code@mguentner.de>
2025-07-10 16:36:54 +00:00
Maximilian Güntner
19dc733267 ch-remote: add env_logger, log messages to stderr
Until now all messages generated using `log::level!`
(e.g., `warn!`) have not been printed as `ch-remote` did not
register a logger.
Furthermore, replace all `eprintln!` with `error!`
to align formatting for consistency.

Signed-off-by: Maximilian Güntner <code@mguentner.de>
2025-07-10 16:36:54 +00:00
Maximilian Güntner
6ba949d741 build: consolidate env_logger to workspace, update to 0.11.8
Signed-off-by: Maximilian Güntner <code@mguentner.de>
2025-07-10 16:36:54 +00:00
Philipp Schuster
9d4408ba76 vmm: add directory path to error message
Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-07-10 16:24:50 +00:00
Maximilian Güntner
f9c134471a vmm: warn about deprecation of default IP address + mask
Issue: #7083

Signed-off-by: Maximilian Güntner <code@mguentner.de>
2025-07-08 19:05:45 +00:00
ninollei
3d5b4d0b0c vmm: acpi: Use correct table name in error message
Fix a copy-paste error using the wrong table name in the assertion

Signed-off-by: ninollei <ninollx@hotmail.com>
2025-07-08 09:02:40 +00:00
dependabot[bot]
ea32b67098 build: Bump proc-macro-crate from 3.2.0 to 3.3.0
Bumps [proc-macro-crate](https://github.com/bkchr/proc-macro-crate) from 3.2.0 to 3.3.0.
- [Release notes](https://github.com/bkchr/proc-macro-crate/releases)
- [Commits](https://github.com/bkchr/proc-macro-crate/compare/v3.2.0...v3.3.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-07 17:23:51 +00:00
Jinank Jain
190d90196f build: Bump vfio and all the dependent crates to latest version
Recently vfio crates have moved to crates.io, thus we should start
consuming the crate from crates.io instead git url.

This results in better versioning instead of tracking some git commit
sha.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-07-07 03:05:38 +00:00
Jinank Jain
fe422a45af build: Move away from actions-rs/cross
Since action-rs/cross is deprecrated, thus move to
houseabsolute/actions-rust-cross.

We should pin the cross-version to the latest version to fix the build
issues with virtio-bindings crate.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-07-07 03:05:38 +00:00
Wei Liu
da5fae3814 docs: Fix the chown command in macvtap-bridge.md
When invoking the script chown shows a warning.

    chown: warning: '.' should be ':': ‘1000.1000’

From `info coreutils 'chown invocation'`.

   Some older scripts may still use ‘.’ in place of the ‘:’ separator.
POSIX 1003.1-2001 (*note Standards conformance::) does not require
support for that, but for backward compatibility GNU ‘chown’ supports
‘.’ so long as no ambiguity results, although it issues a warning and
support may be removed in future versions.  New scripts should avoid the
use of ‘.’ because it is not portable, and because it has undesirable
results if the entire OWNER‘.’GROUP happens to identify a user whose
name contains ‘.’.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-07-01 15:12:03 +00:00
dependabot[bot]
8a78043e2f build: Bump crate-ci/typos from 1.33.1 to 1.34.0
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.33.1 to 1.34.0.
- [Release notes](https://github.com/crate-ci/typos/releases)
- [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crate-ci/typos/compare/v1.33.1...v1.34.0)

---
updated-dependencies:
- dependency-name: crate-ci/typos
  dependency-version: 1.34.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-01 02:50:46 +00:00
Wei Liu
fdeb778210 block: Add back UUID crate's v4 feature
That feature was dropped when consolidating the UUID dependency because
somehow building the whole project worked. The CI system was happy.

However, building the block crate alone is broken. The vhdx code uses
Uuid::new_v4, which requires `v4` to be enabled.

Add the feature back.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-06-30 10:54:54 +00:00
dependabot[bot]
13c222a879 build: Bump proc-macro2 from 1.0.93 to 1.0.95
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.93 to 1.0.95.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.93...1.0.95)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-28 00:18:30 +00:00
dependabot[bot]
b27d9ccfab build: Bump rustix from 0.38.34 to 0.38.44
Bumps [rustix](https://github.com/bytecodealliance/rustix) from 0.38.34 to 0.38.44.
- [Release notes](https://github.com/bytecodealliance/rustix/releases)
- [Changelog](https://github.com/bytecodealliance/rustix/blob/main/CHANGES.md)
- [Commits](https://github.com/bytecodealliance/rustix/compare/v0.38.34...v0.38.44)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-26 23:56:47 +00:00
Philipp Schuster
d580ed55c6 seccomp: add SYS_getcwd (79) to support proper Rust backtraces
When a proper Rust backtrace is printed, the Rust std wants to use the
SYS_getcwd(79) system call to prettify some paths while printing. In
Cloud Hypervisor, this is at least relevant for printing panics or if
a `anyhow::Error` value is printed using `{e:?}` (but not `{e:#?}`).

The syscall cause can be found in `impl fmt::Display for Backtrace {}`
in `library/std/src/backtrace.rs`.

Without this addition, the seccomp violation of the SYS_getcwd (79)
hinders the proper error message including a full backtrace from showing
up. This annoying behaviour already delayed many debugging efforts. With
this fix, things just work. The new syscall itself should be pretty
harmless for normal operation.

```
thread 'vmm' panicked at virtio-devices/src/rng.rs:224:9:
Yikes, things went horribly wrong!

==== Possible seccomp violation ====
Try running with `strace -ff` to identify the cause and open an issue: https://github.com/cloud-hypervisor/cloud-hypervisor/issues/new
[1]    287683 invalid system call (core dumped)  RUST_BACKTRACE=full cargo run --bin cloud-hypervisor -- --api-socket  --kerne
```

```
thread 'vmm' panicked at virtio-devices/src/rng.rs:224:9:
Yikes, things went horribly wrong!
stack backtrace:
   0:     0x557d91286b62 - std::backtrace_rs::backtrace::libunwind::trace::hc20b48b31ee52608
                               at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/../../backtrace/src/backtrace/libunwind.rs:117:9
   1:     0x557d91286b62 - std::backtrace_rs::backtrace::trace_unsynchronized::h5d207cd20f193d88
                               at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/../../backtrace/src/backtrace/mod.rs:66:14

...

  67:                0x0 - <unknown>
Error: Cloud Hypervisor exited with the following error:
  Failed to join on VMM thread: Any { .. }

Debug Info: ThreadJoin(Any { .. })
```

- add any panic, for example into the create or drop function of a
  device
- add --seccomp=true|log to analyze the situation

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-26 20:50:57 +00:00
dependabot[bot]
2cb8c41adc build: Bump remain from 0.2.14 to 0.2.15
Bumps [remain](https://github.com/dtolnay/remain) from 0.2.14 to 0.2.15.
- [Release notes](https://github.com/dtolnay/remain/releases)
- [Commits](https://github.com/dtolnay/remain/compare/0.2.14...0.2.15)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-26 00:37:52 +00:00
Philipp Schuster
48b67ed03b net_util: code readability improvements
Small cleanup to improve code readability.
Specifically, refactoring a huge loop body into
a function call.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-25 16:22:16 +00:00
Philipp Schuster
e0f0065cbd net_util: improve Error types
Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-25 16:22:16 +00:00
dependabot[bot]
1866a85a3d build: Bump zerocopy from 0.8.24 to 0.8.26
Bumps [zerocopy](https://github.com/google/zerocopy) from 0.8.24 to 0.8.26.
- [Release notes](https://github.com/google/zerocopy/releases)
- [Changelog](https://github.com/google/zerocopy/blob/main/CHANGELOG.md)
- [Commits](https://github.com/google/zerocopy/compare/v0.8.24...v0.8.26)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-25 10:07:20 +00:00
Hengqi Chen
8338fa642f net_util: Drop duplicated virtio_features_to_tap_offload
The virtio_features_to_tap_offload() defined in ctrl_queue.rs
is duplicated. Remove it and use the one defined in lib.rs
instead.

Signed-off-by: Hengqi Chen <hengqi.chen@gmail.com>
2025-06-24 17:43:22 +00:00
Wei Liu
a5287c6f67 build: Consolidate UUID crate to workspace
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-06-24 16:50:35 +00:00
Philipp Schuster
fe07617f5d misc: add __pycache__ to gitignore
Running `gitlint` locally produces a __pycache__ directory in
`scripts/gitlint/rules/`. It makes sense to exclude this directory.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-24 11:22:00 +00:00
dependabot[bot]
1820c22ba4 build: Bump cc from 1.2.23 to 1.2.27
Bumps [cc](https://github.com/rust-lang/cc-rs) from 1.2.23 to 1.2.27.
- [Release notes](https://github.com/rust-lang/cc-rs/releases)
- [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.23...cc-v1.2.27)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-24 04:25:02 +00:00
dependabot[bot]
f828e16d62 build: Bump syn from 2.0.87 to 2.0.104
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.87 to 2.0.104.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.87...2.0.104)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-24 02:37:24 +00:00
Demi Marie Obenour
24998c1672 vmm: do not treat libc::MAP_FAILED as a pointer
It will likely be safely rejected by the kernel, but it's still wrong.

Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
2025-06-23 09:06:32 +00:00
Wei Liu
821f7994fc build: Bump UUID crate to 1.17.0
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2025-06-21 14:27:19 +00:00
Philipp Schuster
4182ef91e0 misc: remove once_cell; superseded by std::*
We now have types in the Rust standard library.
Dropping the dependency.

I found this by using the `clippy::pedantic` group.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-21 14:25:20 +00:00
Demi Marie Obenour
100c6d8142 docs: SGX virt changes are upstream
Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
2025-06-20 10:21:04 +00:00
Demi Marie Obenour
269976c7b3 block: Remove unnecessary pointer indirection
No functional change intended.

Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
2025-06-20 10:21:04 +00:00
Demi Marie Obenour
8769b78bf3 arch: x86_64: Require that types to be checksummed are ByteValued
Checksumming a type that has padding would use the padding in the
checksum, which is definitely wrong and is often unsound.

Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
2025-06-20 10:21:04 +00:00
Philipp Schuster
a991de9955 vmm: streamline Display impl of Error types
Streamlines the code base by using thiserror's #[error] attribute
consistently for implementing `Display`.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-20 10:19:35 +00:00
dependabot[bot]
47c9ddfa7e build: Bump hashbrown from 0.15.2 to 0.15.4
Bumps [hashbrown](https://github.com/rust-lang/hashbrown) from 0.15.2 to 0.15.4.
- [Release notes](https://github.com/rust-lang/hashbrown/releases)
- [Changelog](https://github.com/rust-lang/hashbrown/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/hashbrown/commits/v0.15.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-17 01:49:45 +00:00
Bo Chen
2b05753716 ci: Update reference kernel to 'v6.12.8-20250613'
This bump also includes another release 'ch-release-v6.12.8-20250422'
that changed the naming convention of the released kernel binaries
[1]. As a result, few changes are made to our integration tests and test
scripts.

[1] https://github.com/cloud-hypervisor/linux/releases/tag/ch-release-v6.12.8-20250422

Signed-off-by: Bo Chen <bchen@crusoe.ai>
2025-06-16 17:59:22 +00:00
Yi Wang
80f4bfac00 block: keep lifetime consistent from input to output
Cargo fuzz build report an error:
error: lifetime flowing from input to output with different syntax can be confusing
   --> /home/runner/work/cloud-hypervisor/cloud-hypervisor/block/src/lib.rs:747:13
    |
747 |     fn file(&mut self) -> MutexGuard<F>;
    |             ^^^^^^^^^     ------------- the lifetime gets resolved as `'_`
    |             |
    |             this lifetime flows to the output

error: lifetime flowing from input to output with different syntax can be confusing
  --> /home/runner/work/cloud-hypervisor/cloud-hypervisor/block/src/async_io.rs:68:11
   |
68 |     fn fd(&mut self) -> BorrowedDiskFd;
   |           ^^^^^^^^^     -------------- the lifetime gets resolved as `'_`
   |           |
   |           this lifetime flows to the output

Signed-off-by: Yi Wang <foxywang@tencent.com>
2025-06-16 06:25:57 +00:00
dependabot[bot]
b26b09ec0c build: Bump crate-ci/typos from 1.32.0 to 1.33.1
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.32.0 to 1.33.1.
- [Release notes](https://github.com/crate-ci/typos/releases)
- [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crate-ci/typos/compare/v1.32.0...v1.33.1)

---
updated-dependencies:
- dependency-name: crate-ci/typos
  dependency-version: 1.33.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-14 00:42:13 +00:00
dependabot[bot]
39a6664e62 build: Bump event-listener from 5.3.1 to 5.4.0
Bumps [event-listener](https://github.com/smol-rs/event-listener) from 5.3.1 to 5.4.0.
- [Release notes](https://github.com/smol-rs/event-listener/releases)
- [Changelog](https://github.com/smol-rs/event-listener/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/event-listener/compare/v5.3.1...v5.4.0)

---
updated-dependencies:
- dependency-name: event-listener
  dependency-version: 5.4.0
  dependency-type: indirect
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-13 23:58:27 +00:00
Philipp Schuster
190a11f212 ch-remote: also pretty-print remote server errors
Remote server errors are transferred as raw HTTP body. This way,
we lose the nested structured error information.

This is an attempt to retrieve the errors from the HTTP response
and to align the output with the normal error output.

For example, this produces the following chain of errors. Note
that everything after level 0 was retrieved from the HTTP server
response:

```
Error: ch-remote exited with the following chain of errors:
  0: http client error
  1: Server responded with InternalServerError
  2: Error from API
  3: The disk could not be added to the VM
  4: Failed to validate config
  5: Identifier disk1 is not unique

Debug Info: HttpApiClient(ServerResponse(InternalServerError, Some("Error from API<br>The disk could not be added to the VM<br>Failed to validate config<br>Identifier disk1 is not unique")))
```

In case the JSON can't be parsed properly, ch-remote will print:

```
Error: ch-remote exited with the following chain of errors:
  0: http client error
  X: Can't get remote's error messages from JSON response: EOF while parsing a value at line 1 column 0: body=''

Debug Info: HttpApiClient(ServerResponse(InternalServerError, Some("")))
```

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
6ea132708c vmm: use Error trait directly with Note for compiler bug
While working on this, I found a subtle but severe compiler bug [0].
To fight the bug with explicitness rather than implicitness (to
prevent weird stuff in the future), this change is beneficial.

The bug is at least in Rust stable 1.34..1.87.

[0]: https://github.com/rust-lang/rust/issues/141673

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
060c9de07f vmm: introduce nice error messages on exit (CHV and ch-remote)
With the foundations of each error type implementing std::error::Error,
we can now nicely walk the `.source()` chain and print an error trace.

This commit introduces improved user-facing error printing when:
- Cloud Hypervisor fails with an error
- ch-remote fails (client error)
- ch-remote fails (remote error)

The additional context is a clear improvement in UX for both users and
developers. In the following example, the new behaviour is shown for
a direct invocation of Cloud Hypervisor leading to a failure. This
looks similar for ch-remote.

```
Old Style
`target/release/cloud-hypervisor --api-socket /tmp/chv2.sock --kernel /etc/bootitems/linux/kernel_minimal/stable.bzImage --cmdline console=ttyS0 --serial tty --console off --disk path=img.raw --initramfs /etc/bootitems/linux/initrd_minimal/default`

Error booting VM: VmBoot(LockingError(BlockError(LockDiskImage(AlreadyLocked)))
```

```
`target/release/cloud-hypervisor --api-socket /tmp/chv2.sock --kernel /etc/bootitems/linux/kernel_minimal/stable.bzImage --cmdline console=ttyS0 --serial tty --console off --disk path=img.raw --initramfs
/etc/bootitems/linux/initrd_minimal/default`

Error: Cloud Hypervisor exited with the following chain of errors:
  0: Error booting VM
  1: The VM could not boot
  2: Error locking disk images: Another instance likely holds a lock
  3: Cannot lock images of all block devices
  4: Failed to get Write lock for disk image: ./img.raw
  5: The file is already locked

Debug Info: VmBoot(VmBoot(LockingError(DiskLockError(LockDiskImage { error: AlreadyLocked, lock_type: Write, path: "./raw_disk.bin" })))
```

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
1433763d40 misc: virtio-devices: manual adjustment of special case
Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
30ee2c129d misc: vm-migration: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
9bf15ed280 misc: vhost_user_net: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
ed63b352d1 misc: vhost_user_block: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
8e2973fe7c misc: virtio-devices: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
3541cebbf1 misc: tpm: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
1ede418dcc misc: test_infra: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
1b03e59152 misc: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
8f56de713b misc: rate_limiter: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
2100d8c30f misc: performance-metrics: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
a3692144f0 misc: pci: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
192b19c060 misc: option_parser: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
67896333e3 misc: net_util: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
72c8178335 misc: hypervisor: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
1b91aa8ef3 misc: devices: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
5711f31995 misc: ch-remote: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
aebbd1aecd misc: block: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
06a868cb85 misc: arch: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
3f3489e38e misc: api_client: streamline error Display::fmt()
The changes were mostly automatically applied using the Python
script mentioned in the first commit of this series.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
d7edd9d51f misc: vmm: streamline error Display::fmt()
The changes were mostly automatically applied using the following
Python script:

```python
import os, re

for root, _, files in os.walk("."):
    for f in files:
        if not f.endswith(".rs"):
            continue
        p = os.path.join(root, f)
        with open(p, "r", encoding="utf-8") as file:
            lines = file.readlines()
        changed = False
        for i in range(len(lines) - 1):
            if re.search(r'#\[error\(".*: \{0[^}]*\}"\)\]', lines[i]) and "#[source]" in lines[i + 1].strip():
                lines[i] = re.sub(r': \{0[^}]*\}"\)\]', '")]', lines[i])
                changed = True
        if changed:
            with open(p, "w", encoding="utf-8") as file:
                file.writelines(lines)
            print("Fixed:", p)
```

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com

# Conflicts:
#	vmm/src/api/http/mod.rs
2025-06-13 19:55:54 +00:00
Philipp Schuster
4987d63b6a vmm: fix typo
Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:55:54 +00:00
Philipp Schuster
53e9c94e68 tests: cleanup test_util module
We can remove the `tests` module as the entire file is only
available when running tests.

Follow-up of #7130 / 1f13165fae.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-13 19:02:39 +00:00
dependabot[bot]
9d8bda20e5 build: Bump object from 0.36.5 to 0.36.7
Bumps [object](https://github.com/gimli-rs/object) from 0.36.5 to 0.36.7.
- [Changelog](https://github.com/gimli-rs/object/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gimli-rs/object/compare/0.36.5...0.36.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-13 00:25:40 +00:00
dependabot[bot]
63d7f562e5 build: Bump houseabsolute/actions-rust-cross from 0 to 1
Bumps [houseabsolute/actions-rust-cross](https://github.com/houseabsolute/actions-rust-cross) from 0 to 1.
- [Release notes](https://github.com/houseabsolute/actions-rust-cross/releases)
- [Changelog](https://github.com/houseabsolute/actions-rust-cross/blob/v1/Changes.md)
- [Commits](https://github.com/houseabsolute/actions-rust-cross/compare/v0...v1)

---
updated-dependencies:
- dependency-name: houseabsolute/actions-rust-cross
  dependency-version: '1'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-13 00:09:49 +00:00
Philipp Schuster
9bd9c0cb71 misc: replace manual From<T> for *Error with #[from]
This is a small simplification we can use since we use `thiserror`
anyway. Note that `#[from]` implies `#[source]` [0].

[0]: https://docs.rs/thiserror/2.0.12/thiserror/index.html

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-12 15:55:54 +00:00
Philipp Schuster
d594107c0d ch-remote: sort all commands and args alphabetically
Having them sorted alphabetically makes more sense since there are
already many, and the list is growing. This improves the UX for users
and developers.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-12 13:53:55 +00:00
Philipp Schuster
1f13165fae tests: prepare common test infrastructure for CLI args
Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-12 13:53:55 +00:00
Philipp Schuster
12493db144 ch-remote: move Args and Commands creation to function
This enables to sort them alphabetically in a next step, similar
to #6988 / c37c639f3f.

[0] https://github.com/cloud-hypervisor/cloud-hypervisor/pull/6988

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-12 13:53:55 +00:00
Jinrong Liang
f151fdb16f vmm: config: Add tests for check block serial length
Signed-off-by: Jinrong Liang <cloudliang@tencent.com>
2025-06-12 13:51:52 +00:00
Jinrong Liang
bb6ca56fb0 vmm: config: Add DiskConfig check for device serial length
Signed-off-by: Jinrong Liang <cloudliang@tencent.com>
2025-06-12 13:51:52 +00:00
dependabot[bot]
a336533389 build: Bump actions/setup-python from 1 to 5
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 1 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v1...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-12 00:17:53 +00:00
dependabot[bot]
c4b6ed1077 build: Bump getrandom from 0.3.1 to 0.3.3
Bumps [getrandom](https://github.com/rust-random/getrandom) from 0.3.1 to 0.3.3.
- [Changelog](https://github.com/rust-random/getrandom/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/getrandom/compare/v0.3.1...v0.3.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-12 00:01:56 +00:00
dependabot[bot]
3e8dda4e7d build: Bump softprops/action-gh-release from 1 to 2
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 1 to 2.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v1...v2)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: '2'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-11 00:57:58 +00:00
dependabot[bot]
e9edb2e51c build: Bump adler2 from 2.0.0 to 2.0.1
Bumps [adler2](https://github.com/oyvindln/adler2) from 2.0.0 to 2.0.1.
- [Changelog](https://github.com/oyvindln/adler2/blob/main/CHANGELOG.md)
- [Commits](https://github.com/oyvindln/adler2/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-10 23:48:15 +00:00
dependabot[bot]
0798effcc6 build: Bump async-io from 2.3.3 to 2.4.1
Bumps [async-io](https://github.com/smol-rs/async-io) from 2.3.3 to 2.4.1.
- [Release notes](https://github.com/smol-rs/async-io/releases)
- [Changelog](https://github.com/smol-rs/async-io/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/async-io/compare/v2.3.3...v2.4.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-10 17:42:39 +00:00
dependabot[bot]
6079791bca build: Bump fsfe/reuse-action from 3 to 5
Bumps [fsfe/reuse-action](https://github.com/fsfe/reuse-action) from 3 to 5.
- [Release notes](https://github.com/fsfe/reuse-action/releases)
- [Commits](https://github.com/fsfe/reuse-action/compare/v3...v5)

---
updated-dependencies:
- dependency-name: fsfe/reuse-action
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-10 17:26:50 +00:00
Philipp Schuster
ff2330defe ci: activate dependabot for github-actions
Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-10 15:52:12 +00:00
Philipp Schuster
12b72ba3c1 ci: bump typos
Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-06-10 15:52:12 +00:00
dependabot[bot]
92f9e20f57 build: Bump itoa from 1.0.11 to 1.0.15
Bumps [itoa](https://github.com/dtolnay/itoa) from 1.0.11 to 1.0.15.
- [Release notes](https://github.com/dtolnay/itoa/releases)
- [Commits](https://github.com/dtolnay/itoa/compare/1.0.11...1.0.15)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-10 00:38:46 +00:00
Gauthier Jolly
3d78662498 block: virtio-blk: report IO errors to the guest
Instead of exiting on IO errors, report the errors to the guest with
VIRTIO_BLK_S_IOERR. For example, the guest kernel will log something
similar to this if the nbd behind /dev/vdc is unexpectedly disconnected:

[  166.033957] I/O error, dev vdc, sector 264 op 0x1:(WRITE) flags 0x9800 phys_seg 1 prio class 2
[  166.035083] Aborting journal on device vdc-8.
[  166.037307] Buffer I/O error on dev vdc, logical block 9, lost sync page write
[  166.038471] JBD2: I/O error when updating journal superblock for vdc-8.
[...]
[  174.234470] EXT4-fs (vdc): I/O error while writing superblock

In case the rootfs is not located on the affected block device, this
will not crash the guest.

Fixes: #6995

Signed-off-by: Gauthier Jolly <contact@gjolly.fr>
2025-06-09 16:48:07 +00:00
Jinank Jain
2bc8d51a60 misc: Fix missing lifetime syntax clippy warning
This was caught by the nightly compiler during cargo fuzz build.

error: lifetime flowing from input to output with different syntax can be confusing
   --> /home/runner/work/cloud-hypervisor/cloud-hypervisor/hypervisor/src/arch/x86/emulator/mod.rs:493:26
    |
493 |     pub fn new(platform: &mut dyn PlatformEmulator<CpuState = T>) -> Emulator<T> {
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^     ----------- the lifetime gets resolved as `'_`
    |                          |
    |                          this lifetime flows to the output
    |
    = note: `-D mismatched-lifetime-syntaxes` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(mismatched_lifetime_syntaxes)]`
help: one option is to remove the lifetime for references and use the anonymous lifetime for paths
    |
493 |     pub fn new(platform: &mut dyn PlatformEmulator<CpuState = T>) -> Emulator<'_, T> {

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-06-09 11:19:11 +00:00
Jinank Jain
51002f2bae build: Bump zbus from 4.4.0 to 5.7.1
Along with this also fix some API incompatibilities issues.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-06-09 11:19:11 +00:00
Nuno Das Neves
59e11f1b0b hypervisor: mshv: fix advance_rip_update_rax() helper
The dirty bit for the GP registers must be set for the hypervisor to
update them.

Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
2025-06-05 20:23:30 +00:00
Jinank Jain
fc01e4cbec fuzz: Update Cargo.lock for fuzz build
It seems like multiple packages inside Cargo.lock are outdated.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-06-04 17:04:07 +00:00
Jinank Jain
3f8186f627 hypervisor: Fix issues with nightly compilers
cargo fuzz build complaints about some un-used function in the
instruction emultator. Silence the warning by allowing dead code
generation.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-06-04 17:04:07 +00:00
Jinank Jain
6f56ef9a36 misc: Move zerocopy to workspace dependencies
Since it is used by multiple components at this point, it is better to
move it to workspace level dependency.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-06-04 17:04:07 +00:00
Philipp Schuster
d6ed74b5b8 tests: fix vCPU test relying on specific Display formatting
Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-30 19:07:15 +00:00
dependabot[bot]
7fbe94a227 build: Bump quote from 1.0.36 to 1.0.40
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.36 to 1.0.40.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.36...1.0.40)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-30 00:37:50 +00:00
dependabot[bot]
4a1809f596 build: Bump glob from 0.3.1 to 0.3.2
Bumps [glob](https://github.com/rust-lang/glob) from 0.3.1 to 0.3.2.
- [Release notes](https://github.com/rust-lang/glob/releases)
- [Changelog](https://github.com/rust-lang/glob/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/glob/compare/0.3.1...v0.3.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-28 23:45:19 +00:00
Yi Wang
0a4169801b arch: x86_64: lower the cpuid log level
There are a little many cpuid logs now. When starting a vm with
64 vcpu, we can get more than four thousand INFO messages:

cat vm1.log |grep 'arch/src/x86_64/mod.rs:891' |wc -l
4352

Signed-off-by: Yi Wang <foxywang@tencent.com>
2025-05-28 17:32:22 +00:00
Philipp Schuster
20296e909a misc: streamline thiserror cargo dep
As almost every sub crate depends on thiserror, lets upgrade it to a
workspace dependency.

Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
2025-05-28 17:24:34 +00:00
dependabot[bot]
24a6a1805b build: Bump crc-any from 2.4.4 to 2.5.0
Bumps [crc-any](https://github.com/magiclen/crc-any) from 2.4.4 to 2.5.0.
- [Commits](https://github.com/magiclen/crc-any/compare/v2.4.4...v2.5.0)

---
updated-dependencies:
- dependency-name: crc-any
  dependency-version: 2.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-28 00:18:12 +00:00
dependabot[bot]
dd7318e839 build: Bump crossbeam-utils from 0.8.20 to 0.8.21
Bumps [crossbeam-utils](https://github.com/crossbeam-rs/crossbeam) from 0.8.20 to 0.8.21.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-utils-0.8.20...crossbeam-utils-0.8.21)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-27 00:01:50 +00:00
dependabot[bot]
37444c4bab build: Bump backtrace from 0.3.74 to 0.3.75
Bumps [backtrace](https://github.com/rust-lang/backtrace-rs) from 0.3.74 to 0.3.75.
- [Release notes](https://github.com/rust-lang/backtrace-rs/releases)
- [Commits](https://github.com/rust-lang/backtrace-rs/compare/0.3.74...0.3.75)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-24 00:30:20 +00:00
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
194 changed files with 5774 additions and 3725 deletions

View File

@@ -16,3 +16,8 @@ updates:
allow:
- dependency-type: direct
- dependency-type: indirect
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 1

View File

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

View File

@@ -8,7 +8,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.x
uses: actions/setup-python@v1
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Check DCO

View File

@@ -41,7 +41,7 @@ jobs:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# generate Docker tags based on the following events/attributes
tags: |
type=raw,value=20250307-2
type=raw,value=20250412-0
type=sha
- name: Build and push

View File

@@ -13,7 +13,7 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Set up Python 3.10
uses: actions/setup-python@v3
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install dependencies

View File

@@ -24,7 +24,7 @@ jobs:
fetch-depth: 0
- name: Install Rust toolchain
run: /opt/scripts/exec-in-qemu.sh rustup default 1.82.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

@@ -50,74 +50,97 @@ jobs:
git checkout ${{ github.sha }}
- name: Clippy (kvm)
uses: actions-rs/cargo@v1
uses: houseabsolute/actions-rust-cross@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 "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
- name: Clippy (default features)
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
- name: Clippy (default features + guest_debug)
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
- name: Clippy (default features + pvmemcontrol)
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "pvmemcontrol" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
- name: Clippy (default features + tracing)
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
command: clippy
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "tracing" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
args: --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)
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
uses: actions-rs/cargo@v1
uses: houseabsolute/actions-rust-cross@v1
with:
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
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
args: --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)
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
uses: actions-rs/cargo@v1
uses: houseabsolute/actions-rust-cross@v1
with:
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
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
args: --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: houseabsolute/actions-rust-cross@v1
with:
command: clippy
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
args: --locked --all --all-targets --tests --examples -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
- name: Clippy (default features + guest_debug)
uses: houseabsolute/actions-rust-cross@v1
with:
command: clippy
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
args: --locked --all --all-targets --tests --examples --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
- name: Clippy (default features + pvmemcontrol)
uses: houseabsolute/actions-rust-cross@v1
with:
command: clippy
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
args: --locked --all --all-targets --tests --examples --features "pvmemcontrol" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
- name: Clippy (default features + tracing)
uses: houseabsolute/actions-rust-cross@v1
with:
command: clippy
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
args: --locked --all --all-targets --tests --examples --features "tracing" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
- name: Clippy (sev_snp)
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
uses: actions-rs/cargo@v1
uses: houseabsolute/actions-rust-cross@v1
with:
command: clippy
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "sev_snp" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
args: --locked --all --all-targets --no-default-features --tests --examples --features "sev_snp" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
- name: Clippy (igvm)
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
uses: actions-rs/cargo@v1
uses: houseabsolute/actions-rust-cross@v1
with:
command: clippy
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "igvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
args: --locked --all --all-targets --no-default-features --tests --examples --features "igvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
- name: Clippy (kvm + tdx)
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
uses: actions-rs/cargo@v1
uses: houseabsolute/actions-rust-cross@v1
with:
command: clippy
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "tdx,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
args: --locked --all --all-targets --no-default-features --tests --examples --features "tdx,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
- name: Check build did not modify any files
run: test -z "$(git status --porcelain)"
@@ -129,4 +152,4 @@ jobs:
steps:
- uses: actions/checkout@v4
# Executes "typos ."
- uses: crate-ci/typos@v1.16.11
- uses: crate-ci/typos@v1.34.0

View File

@@ -39,13 +39,13 @@ jobs:
matrix.platform.target == 'x86_64-unknown-linux-gnu'
run: rsync -rv --exclude=.git . ../cloud-hypervisor-${{ github.event.ref }}
- name: Build ${{ matrix.platform.target }}
uses: houseabsolute/actions-rust-cross@v0
uses: houseabsolute/actions-rust-cross@v1
with:
command: build
target: ${{ matrix.platform.target }}
args: ${{ matrix.platform.args }}
strip: true
toolchain: "1.82.0"
toolchain: "1.83.0"
- name: Copy Release Binaries
if: github.event_name == 'create' && github.event.ref_type == 'tag'
shell: bash
@@ -86,7 +86,7 @@ jobs:
name: cloud-hypervisor-${{ github.event.ref }}.tar.xz
- name: Create GitHub Release
if: github.event_name == 'create' && github.event.ref_type == 'tag'
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
draft: true
files: |

View File

@@ -9,4 +9,4 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: REUSE Compliance Check
uses: fsfe/reuse-action@v3
uses: fsfe/reuse-action@v5

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@
/rpm/SOURCES
/.vscode
/vendor
__pycache__

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,11 +11,3 @@ ignore=all
[title-max-length]
line-length=72
# default 80
[body-max-line-length]
line-length=72
# Allow developers to add long links to useful resources
[ignore-by-body]
regex=^https?:\/\/
ignore=body-max-line-length

View File

@@ -11,6 +11,9 @@ exclude = [
# 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

View File

@@ -7,12 +7,15 @@ extend-exclude = [
]
[default.extend-words]
ba = "ba"
CLASSE = "CLASSE"
conectix = "conectix"
Dake = "Dake"
EXTINT = "EXTINT"
INOUT = "INOUT"
SME = "SME" # Secure Memory Encryption
THR = "THR" # Transmitter Holding Register
TRANSLATER = "TRANSLATER"
ba = "ba"
conectix = "conectix"
liness = "liness"
outout = "outout"

634
Cargo.lock generated

File diff suppressed because it is too large Load Diff

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 = "45.0.0"
version = "47.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.82.0"
rust-version = "1.83.0"
[profile.release]
codegen-units = 1
@@ -33,6 +33,7 @@ anyhow = "1.0.94"
api_client = { path = "api_client" }
clap = { version = "4.5.13", features = ["string"] }
dhat = { version = "0.3.3", optional = true }
env_logger = { workspace = true }
epoll = "4.3.3"
event_monitor = { path = "event_monitor" }
hypervisor = { path = "hypervisor" }
@@ -40,21 +41,20 @@ libc = "0.2.167"
log = { version = "0.4.22", features = ["std"] }
option_parser = { path = "option_parser" }
seccompiler = { workspace = true }
serde_json = "1.0.120"
signal-hook = "0.3.17"
thiserror = "2.0.6"
serde_json = { workspace = true }
signal-hook = "0.3.18"
thiserror = { workspace = true }
tpm = { path = "tpm" }
tracer = { path = "tracer" }
vm-memory = { workspace = true }
vmm = { path = "vmm" }
vmm-sys-util = { workspace = true }
zbus = { version = "4.4.0", optional = true }
zbus = { version = "5.7.1", optional = true }
[dev-dependencies]
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"
@@ -104,23 +104,33 @@ 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"
kvm-bindings = "0.12.0"
kvm-ioctls = "0.22.0"
linux-loader = "0.13.0"
mshv-bindings = "0.3.3"
mshv-ioctls = "0.3.3"
mshv-bindings = "0.5.2"
mshv-ioctls = "0.5.2"
seccompiler = "0.5.0"
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main" }
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
vfio_user = { git = "https://github.com/rust-vmm/vfio-user", branch = "main" }
vhost = { git = "https://github.com/rust-vmm/vhost", rev = "d983ae0" }
vhost-user-backend = { git = "https://github.com/rust-vmm/vhost", rev = "d983ae0" }
virtio-bindings = "0.2.4"
virtio-queue = "0.14.0"
vfio-bindings = { version = "0.5.0", default-features = false }
vfio-ioctls = { version = "0.5.0", default-features = false }
vfio_user = { version = "0.1.0", default-features = false }
vhost = { version = "0.14.0", default-features = false }
vhost-user-backend = { version = "0.20.0", default-features = false }
virtio-bindings = "0.2.6"
virtio-queue = "0.16.0"
vm-fdt = { git = "https://github.com/rust-vmm/vm-fdt", branch = "main" }
vm-memory = "0.16.1"
vmm-sys-util = "0.12.1"
vmm-sys-util = "0.14.0"
# igvm crates
igvm = "0.3.4"
igvm_defs = "0.3.1"
# 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"
# other crates
env_logger = "0.11.8"
thiserror = "2.0.12"
uuid = { version = "1.17.0" }
zerocopy = { version = "0.8.26", default-features = false }

View File

@@ -301,7 +301,8 @@ 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 )
- [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

View File

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

View File

@@ -11,18 +11,24 @@ 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),
#[error("Error sending file descriptors: {0}")]
SocketSendFds(vmm_sys_util::errno::Error),
#[error("Error parsing HTTP status code: {0}")]
StatusCodeParsing(std::num::ParseIntError),
#[error("Error writing to or reading from HTTP socket")]
Socket(#[source] std::io::Error),
#[error("Error sending file descriptors")]
SocketSendFds(#[source] vmm_sys_util::errno::Error),
#[error("Error parsing HTTP status code")]
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),
#[error("Server responded with an error: {0:?}: {1:?}")]
ServerResponse(StatusCode, Option<String>),
#[error("Error parsing HTTP Content-Length field")]
ContentLengthParsing(#[source] std::num::ParseIntError),
#[error("Server responded with error {0:?}: {1:?}")]
ServerResponse(
StatusCode,
// TODO: Move `api` module from `vmm` to dedicated crate and use a common type definition
Option<
String, /* Untyped: Currently Vec<String> of error messages from top to root cause */
>,
),
}
#[derive(Clone, Copy, Debug)]
@@ -32,6 +38,7 @@ pub enum StatusCode {
NoContent,
BadRequest,
NotFound,
TooManyRequests,
InternalServerError,
NotImplemented,
Unknown,
@@ -45,6 +52,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

@@ -18,10 +18,9 @@ libc = "0.2.167"
linux-loader = { workspace = true, features = ["bzimage", "elf", "pe"] }
log = "0.4.22"
serde = { version = "1.0.208", features = ["derive", "rc"] }
thiserror = "2.0.6"
uuid = "1.12.1"
thiserror = { workspace = true }
uuid = { workspace = true }
vm-memory = { workspace = true, features = ["backend-bitmap", "backend-mmap"] }
vm-migration = { path = "../vm-migration" }
vmm-sys-util = { workspace = true, features = ["with-serde"] }
[target.'cfg(any(target_arch = "aarch64", target_arch = "riscv64"))'.dependencies]

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;
@@ -80,8 +81,8 @@ pub trait DeviceInfoForFdt {
#[derive(Debug, Error)]
pub enum Error {
/// Failure in writing FDT in memory.
#[error("Failure in writing FDT in memory: {0}")]
WriteFdtToMemory(GuestMemoryError),
#[error("Failure in writing FDT in memory")]
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();

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};
@@ -33,8 +32,8 @@ pub enum Error {
SetupFdt,
/// Failed to write FDT to memory.
#[error("Failed to write FDT to memory: {0}")]
WriteFdtToMemory(fdt::Error),
#[error("Failed to write FDT to memory")]
WriteFdtToMemory(#[source] fdt::Error),
/// Failed to create a GIC.
#[error("Failed to create a GIC")]
@@ -45,24 +44,18 @@ pub enum Error {
InitramfsAddress,
/// Error configuring the general purpose registers
#[error("Error configuring the general purpose registers: {0}")]
RegsConfiguration(hypervisor::HypervisorCpuError),
#[error("Error configuring the general purpose registers")]
RegsConfiguration(#[source] hypervisor::HypervisorCpuError),
/// Error configuring the MPIDR register
#[error("Error configuring the MPIDR register: {0}")]
VcpuRegMpidr(hypervisor::HypervisorCpuError),
#[error("Error configuring the MPIDR register")]
VcpuRegMpidr(#[source] hypervisor::HypervisorCpuError),
/// Error initializing PMU for vcpu
#[error("Error initializing PMU for vcpu")]
VcpuInitPmu,
}
impl From<Error> for super::Error {
fn from(e: Error) -> super::Error {
super::Error::PlatformSpecific(e)
}
}
#[derive(Debug, Copy, Clone)]
/// Specifies the entry point address where the guest must start
/// executing code.
@@ -86,9 +79,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)")]
PlatformSpecific(#[from] x86_64::Error),
#[cfg(target_arch = "aarch64")]
#[error("Platform specific error (aarch64): {0:?}")]
PlatformSpecific(aarch64::Error),
#[error("Platform specific error (aarch64)")]
PlatformSpecific(#[from] aarch64::Error),
#[cfg(target_arch = "riscv64")]
#[error("Platform specific error (riscv64): {0:?}")]
PlatformSpecific(riscv64::Error),
#[error("Platform specific error (riscv64)")]
PlatformSpecific(#[from] riscv64::Error),
#[error("The memory map table extends past the end of guest memory")]
MemmapTablePastRamEnd,
#[error("Error writing memory map table to guest memory")]
@@ -46,7 +46,7 @@ pub enum Error {
StartInfoSetup,
#[error("Failed to compute initramfs address")]
InitramfsAddress,
#[error("Error writing module entry to guest memory: {0}")]
#[error("Error writing module entry to guest memory")]
ModlistSetup(#[source] vm_memory::GuestMemoryError),
#[error("RSDP extends past the end of guest memory")]
RsdpPastRamEnd,

View File

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

View File

@@ -30,8 +30,8 @@ pub enum Error {
SetupFdt,
/// Failed to write FDT to memory.
#[error("Failed to write FDT to memory: {0}")]
WriteFdtToMemory(fdt::Error),
#[error("Failed to write FDT to memory")]
WriteFdtToMemory(#[source] fdt::Error),
/// Failed to create a AIA.
#[error("Failed to create a AIA")]
@@ -42,14 +42,8 @@ pub enum Error {
InitramfsAddress,
/// Error configuring the general purpose registers
#[error("Error configuring the general purpose registers: {0}")]
RegsConfiguration(hypervisor::HypervisorCpuError),
}
impl From<Error> for super::Error {
fn from(e: Error) -> super::Error {
super::Error::PlatformSpecific(e)
}
#[error("Error configuring the general purpose registers")]
RegsConfiguration(#[source] hypervisor::HypervisorCpuError),
}
#[derive(Debug, Copy, Clone)]

View File

@@ -132,36 +132,36 @@ pub struct CpuidConfig {
#[derive(Debug, Error)]
pub enum Error {
/// Error writing MP table to memory.
#[error("Error writing MP table to memory: {0}")]
MpTableSetup(mptable::Error),
#[error("Error writing MP table to memory")]
MpTableSetup(#[source] mptable::Error),
/// Error configuring the general purpose registers
#[error("Error configuring the general purpose registers: {0}")]
RegsConfiguration(regs::Error),
#[error("Error configuring the general purpose registers")]
RegsConfiguration(#[source] regs::Error),
/// Error configuring the special registers
#[error("Error configuring the special registers: {0}")]
SregsConfiguration(regs::Error),
#[error("Error configuring the special registers")]
SregsConfiguration(#[source] regs::Error),
/// Error configuring the floating point related registers
#[error("Error configuring the floating point related registers: {0}")]
FpuConfiguration(regs::Error),
#[error("Error configuring the floating point related registers")]
FpuConfiguration(#[source] regs::Error),
/// Error configuring the MSR registers
#[error("Error configuring the MSR registers: {0}")]
MsrsConfiguration(regs::Error),
#[error("Error configuring the MSR registers")]
MsrsConfiguration(#[source] regs::Error),
/// Failed to set supported CPUs.
#[error("Failed to set supported CPUs: {0}")]
SetSupportedCpusFailed(anyhow::Error),
#[error("Failed to set supported CPUs")]
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),
#[error("Cannot set the local interruption due to bad configuration")]
LocalIntConfiguration(#[source] anyhow::Error),
/// Error setting up SMBIOS table
#[error("Error setting up SMBIOS table: {0}")]
SmbiosSetup(smbios::Error),
#[error("Error setting up SMBIOS table")]
SmbiosSetup(#[source] smbios::Error),
/// Could not find any SGX EPC section
#[error("Could not find any SGX EPC section")]
@@ -176,45 +176,39 @@ pub enum Error {
MissingSgxLaunchControlFeature,
/// Error getting supported CPUID through the hypervisor (kvm/mshv) API
#[error("Error getting supported CPUID through the hypervisor API: {0}")]
CpuidGetSupported(HypervisorError),
#[error("Error getting supported CPUID through the hypervisor API")]
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),
#[error("Error populating CPUID with KVM HyperV emulation details")]
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),
#[error("Error populating CPUID with CPU identification")]
CpuidIdentification(#[source] vmm_sys_util::fam::Error),
/// Error checking CPUID compatibility
#[error("Error checking CPUID compatibility")]
CpuidCheckCompatibility,
// Error writing EBDA address
#[error("Error writing EBDA address: {0}")]
EbdaSetup(vm_memory::GuestMemoryError),
#[error("Error writing EBDA address")]
EbdaSetup(#[source] vm_memory::GuestMemoryError),
// Error getting CPU TSC frequency
#[error("Error getting CPU TSC frequency: {0}")]
GetTscFrequency(HypervisorCpuError),
#[error("Error getting CPU TSC frequency")]
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),
#[error("Error retrieving TDX capabilities through the hypervisor API")]
TdxCapabilities(#[source] HypervisorError),
/// Failed to configure E820 map for bzImage
#[error("Failed to configure E820 map for bzImage")]
E820Configuration,
}
impl From<Error> for super::Error {
fn from(e: Error) -> super::Error {
super::Error::PlatformSpecific(e)
}
}
pub fn get_x2apic_id(cpu_id: u32, topology: Option<(u8, u8, u8)>) -> u32 {
if let Some(t) = topology {
let thread_mask_width = u8::BITS - (t.0 - 1).leading_zeros();
@@ -888,7 +882,7 @@ pub fn configure_vcpu(
}
for c in &cpuid {
info!("{}", c);
debug!("{}", c);
}
vcpu.set_cpuid2(&cpuid)
@@ -1411,6 +1405,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

@@ -3,6 +3,8 @@
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
#![allow(non_camel_case_types)]
use vm_memory::ByteValued;
pub const MP_PROCESSOR: ::std::os::raw::c_uint = 0;
pub const MP_BUS: ::std::os::raw::c_uint = 1;
@@ -29,6 +31,13 @@ pub struct mpf_intel {
pub feature5: ::std::os::raw::c_uchar,
}
const _: () = assert!(::core::mem::size_of::<mpf_intel>() == 16);
// SAFETY: all members of this struct are plain integers
// and the sum of their sizes is the size of the struct, so
// padding and reserved values are not possible as there
// would be nowhere for them to exist.
unsafe impl ByteValued for mpf_intel {}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct mpc_table {
@@ -45,6 +54,19 @@ pub struct mpc_table {
pub reserved: ::std::os::raw::c_uint,
}
const _: () = {
assert!(::core::mem::size_of::<mpc_table>() == 4 + 2 + 1 + 1 + 8 + 12 + 4 + 2 + 2 + 4 + 4);
assert!(::core::mem::size_of::<::std::os::raw::c_uint>() == 4);
assert!(::core::mem::size_of::<::std::os::raw::c_ushort>() == 2);
assert!(::core::mem::size_of::<::std::os::raw::c_uchar>() == 1);
};
// SAFETY: all members of this struct are plain integers
// and the sum of their sizes is the size of the struct, so
// padding and reserved values are not possible as there
// would be nowhere for them to exist.
unsafe impl ByteValued for mpc_table {}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct mpc_cpu {
@@ -57,6 +79,13 @@ pub struct mpc_cpu {
pub reserved: [::std::os::raw::c_uint; 2usize],
}
const _: () = assert!(::core::mem::size_of::<mpc_cpu>() == 20);
// SAFETY: all members of this struct are plain integers
// and the sum of their sizes is the size of the struct, so
// padding and reserved values are not possible as there
// would be nowhere for them to exist.
unsafe impl ByteValued for mpc_cpu {}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct mpc_bus {
@@ -65,6 +94,13 @@ pub struct mpc_bus {
pub bustype: [::std::os::raw::c_uchar; 6usize],
}
const _: () = assert!(::core::mem::size_of::<mpc_bus>() == 8);
// SAFETY: all members of this struct are plain integers
// and the sum of their sizes is the size of the struct, so
// padding and reserved values are not possible as there
// would be nowhere for them to exist.
unsafe impl ByteValued for mpc_bus {}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct mpc_ioapic {
@@ -75,6 +111,13 @@ pub struct mpc_ioapic {
pub apicaddr: ::std::os::raw::c_uint,
}
const _: () = assert!(::core::mem::size_of::<mpc_ioapic>() == 8);
// SAFETY: all members of this struct are plain integers
// and the sum of their sizes is the size of the struct, so
// padding and reserved values are not possible as there
// would be nowhere for them to exist.
unsafe impl ByteValued for mpc_ioapic {}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct mpc_intsrc {
@@ -87,6 +130,13 @@ pub struct mpc_intsrc {
pub dstirq: ::std::os::raw::c_uchar,
}
const _: () = assert!(::core::mem::size_of::<mpc_intsrc>() == 8);
// SAFETY: all members of this struct are plain integers
// and the sum of their sizes is the size of the struct, so
// padding and reserved values are not possible as there
// would be nowhere for them to exist.
unsafe impl ByteValued for mpc_intsrc {}
pub const MP_IRQ_SOURCE_TYPES_MP_INT: ::std::os::raw::c_uint = 0;
pub const MP_IRQ_SOURCE_TYPES_MP_NMI: ::std::os::raw::c_uint = 1;
pub const MP_IRQ_SOURCE_TYPES_MP_EXT_INT: ::std::os::raw::c_uint = 3;
@@ -103,6 +153,13 @@ pub struct mpc_lintsrc {
pub destapiclint: ::std::os::raw::c_uchar,
}
const _: () = assert!(::core::mem::size_of::<mpc_lintsrc>() == 8);
// SAFETY: all members of this struct are plain integers
// and the sum of their sizes is the size of the struct, so
// padding and reserved values are not possible as there
// would be nowhere for them to exist.
unsafe impl ByteValued for mpc_lintsrc {}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct mpc_oemtable {
@@ -112,3 +169,10 @@ pub struct mpc_oemtable {
pub checksum: ::std::os::raw::c_uchar,
pub mpc: [::std::os::raw::c_uchar; 8usize],
}
const _: () = assert!(::core::mem::size_of::<mpc_oemtable>() == 16);
// SAFETY: all members of this struct are plain integers
// and the sum of their sizes is the size of the struct, so
// padding and reserved values are not possible as there
// would be nowhere for them to exist.
unsafe impl ByteValued for mpc_oemtable {}

View File

@@ -59,32 +59,32 @@ pub enum Error {
#[error("The MP table has too little address space to be stored")]
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),
#[error("Failure while zeroing out the memory for the MP table")]
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),
#[error("Failure to write the MP floating pointer")]
WriteMpfIntel(#[source] GuestMemoryError),
/// Failure to write MP CPU entry.
#[error("Failure to write MP CPU entry: {0}")]
WriteMpcCpu(GuestMemoryError),
#[error("Failure to write MP CPU entry")]
WriteMpcCpu(#[source] GuestMemoryError),
/// Failure to write MP ioapic entry.
#[error("Failure to write MP ioapic entry: {0}")]
WriteMpcIoapic(GuestMemoryError),
#[error("Failure to write MP ioapic entry")]
WriteMpcIoapic(#[source] GuestMemoryError),
/// Failure to write MP bus entry.
#[error("Failure to write MP bus entry: {0}")]
WriteMpcBus(GuestMemoryError),
#[error("Failure to write MP bus entry")]
WriteMpcBus(#[source] GuestMemoryError),
/// Failure to write MP interrupt source entry.
#[error("Failure to write MP interrupt source entry: {0}")]
WriteMpcIntsrc(GuestMemoryError),
#[error("Failure to write MP interrupt source entry")]
WriteMpcIntsrc(#[source] GuestMemoryError),
/// Failure to write MP local interrupt source entry.
#[error("Failure to write MP local interrupt source entry: {0}")]
WriteMpcLintsrc(GuestMemoryError),
#[error("Failure to write MP local interrupt source entry")]
WriteMpcLintsrc(#[source] GuestMemoryError),
/// Failure to write MP table header.
#[error("Failure to write MP table header: {0}")]
WriteMpcTable(GuestMemoryError),
#[error("Failure to write MP table header")]
WriteMpcTable(#[source] GuestMemoryError),
}
pub type Result<T> = result::Result<T, Error>;
@@ -106,7 +106,7 @@ const CPU_STEPPING: u32 = 0x600;
const CPU_FEATURE_APIC: u32 = 0x200;
const CPU_FEATURE_FPU: u32 = 0x001;
fn compute_checksum<T: Copy>(v: &T) -> u8 {
fn compute_checksum<T: Copy + ByteValued>(v: &T) -> u8 {
// SAFETY: we are only reading the bytes within the size of the `T` reference `v`.
let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
let mut checksum: u8 = 0;

View File

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

View File

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

View File

@@ -13,11 +13,11 @@ use crate::GuestMemoryMmap;
#[derive(Error, Debug)]
pub enum TdvfError {
#[error("Failed read TDVF descriptor: {0}")]
#[error("Failed read TDVF descriptor")]
ReadDescriptor(#[source] std::io::Error),
#[error("Failed read TDVF descriptor offset: {0}")]
#[error("Failed read TDVF descriptor offset")]
ReadDescriptorOffset(#[source] std::io::Error),
#[error("Failed read GUID table: {0}")]
#[error("Failed read GUID table")]
ReadGuidTable(#[source] std::io::Error),
#[error("Invalid descriptor signature")]
InvalidDescriptorSignature,
@@ -25,9 +25,9 @@ pub enum TdvfError {
InvalidDescriptorSize,
#[error("Invalid descriptor version")]
InvalidDescriptorVersion,
#[error("Failed to write HOB details to guest memory: {0}")]
#[error("Failed to write HOB details to guest memory")]
GuestMemoryWriteHob(#[source] GuestMemoryError),
#[error("Failed to create Uuid: {0}")]
#[error("Failed to create Uuid")]
UuidCreation(#[source] uuid::Error),
}

View File

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

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;
@@ -10,33 +13,71 @@ use crate::DiskTopology;
#[derive(Error, Debug)]
pub enum DiskFileError {
/// Failed getting disk file size.
#[error("Failed getting disk file size: {0}")]
#[error("Failed getting disk file size")]
Size(#[source] std::io::Error),
/// Failed creating a new AsyncIo.
#[error("Failed creating a new AsyncIo: {0}")]
#[error("Failed creating a new AsyncIo")]
NewAsyncIo(#[source] std::io::Error),
}
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)]
pub enum AsyncIoError {
/// Failed vectored reading from file.
#[error("Failed vectored reading from file: {0}")]
#[error("Failed vectored reading from file")]
ReadVectored(#[source] std::io::Error),
/// Failed vectored writing to file.
#[error("Failed vectored writing to file: {0}")]
#[error("Failed vectored writing to file")]
WriteVectored(#[source] std::io::Error),
/// Failed synchronizing file.
#[error("Failed synchronizing file: {0}")]
#[error("Failed synchronizing file")]
Fsync(#[source] std::io::Error),
}

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")]
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
@@ -56,7 +57,7 @@ use vm_memory::{
};
use vm_virtio::{AccessPlatform, Translatable};
use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::{aio, ioctl_io_nr, ioctl_ioc_nr};
use vmm_sys_util::{aio, ioctl_io_nr};
use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult};
use crate::vhdx::VhdxError;
@@ -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")]
@@ -78,22 +79,22 @@ pub enum Error {
DescriptorChainTooShort,
#[error("Guest gave us a descriptor that was too short to use")]
DescriptorLengthTooSmall,
#[error("Failed to detect image type: {0}")]
DetectImageType(std::io::Error),
#[error("Failure in fixed vhd: {0}")]
FixedVhdError(std::io::Error),
#[error("Failed to detect image type")]
DetectImageType(#[source] std::io::Error),
#[error("Failure in fixed vhd")]
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),
#[error("Failure in raw file: {0}")]
RawFileError(std::io::Error),
#[error("Failure in qcow")]
QcowError(#[source] qcow::Error),
#[error("Failure in raw file")]
RawFileError(#[source] std::io::Error),
#[error("The requested operation does not support multiple descriptors")]
TooManyDescriptors,
#[error("Failure in vhdx: {0}")]
VhdxError(VhdxError),
#[error("Failure in vhdx")]
VhdxError(#[source] VhdxError),
}
fn build_device_id(disk_path: &Path) -> result::Result<String, Error> {
@@ -130,34 +131,34 @@ pub fn build_serial(disk_path: &Path) -> Vec<u8> {
#[derive(Error, Debug)]
pub enum ExecuteError {
#[error("Bad request: {0}")]
BadRequest(Error),
#[error("Failed to flush: {0}")]
Flush(io::Error),
#[error("Failed to read: {0}")]
Read(GuestMemoryError),
#[error("Failed to read_exact: {0}")]
ReadExact(io::Error),
#[error("Failed to seek: {0}")]
Seek(io::Error),
#[error("Failed to write: {0}")]
Write(GuestMemoryError),
#[error("Failed to write_all: {0}")]
WriteAll(io::Error),
#[error("Bad request")]
BadRequest(#[source] Error),
#[error("Failed to flush")]
Flush(#[source] io::Error),
#[error("Failed to read")]
Read(#[source] GuestMemoryError),
#[error("Failed to read_exact")]
ReadExact(#[source] io::Error),
#[error("Failed to seek")]
Seek(#[source] io::Error),
#[error("Failed to write")]
Write(#[source] GuestMemoryError),
#[error("Failed to write_all")]
WriteAll(#[source] io::Error),
#[error("Unsupported request: {0}")]
Unsupported(u32),
#[error("Failed to submit io uring: {0}")]
SubmitIoUring(io::Error),
#[error("Failed to get guest address: {0}")]
GetHostAddress(GuestMemoryError),
#[error("Failed to async read: {0}")]
AsyncRead(AsyncIoError),
#[error("Failed to async write: {0}")]
AsyncWrite(AsyncIoError),
#[error("failed to async flush: {0}")]
AsyncFlush(AsyncIoError),
#[error("Failed allocating a temporary buffer: {0}")]
TemporaryBufferAllocation(io::Error),
#[error("Failed to submit io uring")]
SubmitIoUring(#[source] io::Error),
#[error("Failed to get guest address")]
GetHostAddress(#[source] GuestMemoryError),
#[error("Failed to async read")]
AsyncRead(#[source] AsyncIoError),
#[error("Failed to async write")]
AsyncWrite(#[source] AsyncIoError),
#[error("failed to async flush")]
AsyncFlush(#[source] AsyncIoError),
#[error("Failed allocating a temporary buffer")]
TemporaryBufferAllocation(#[source] io::Error),
}
impl ExecuteError {
@@ -400,14 +401,20 @@ impl Request {
let mut iovecs: SmallVec<[libc::iovec; DEFAULT_DESCRIPTOR_VEC_SIZE]> =
SmallVec::with_capacity(self.data_descriptors.len());
for (data_addr, data_len) in &self.data_descriptors {
if *data_len == 0 {
for &(data_addr, data_len) in &self.data_descriptors {
let _: u32 = data_len; // compiler-checked documentation
const _: () = assert!(
core::mem::size_of::<u32>() <= core::mem::size_of::<usize>(),
"unsupported platform"
);
if data_len == 0 {
continue;
}
let mut top: u64 = u64::from(*data_len) / SECTOR_SIZE;
if u64::from(*data_len) % SECTOR_SIZE != 0 {
let mut top: u64 = u64::from(data_len) / SECTOR_SIZE;
if u64::from(data_len) % SECTOR_SIZE != 0 {
top += 1;
}
let data_len = data_len as usize;
top = top
.checked_add(sector)
.ok_or(ExecuteError::BadRequest(Error::InvalidOffset))?;
@@ -416,7 +423,7 @@ impl Request {
}
let origin_ptr = mem
.get_slice(*data_addr, *data_len as usize)
.get_slice(data_addr, data_len)
.map_err(ExecuteError::GetHostAddress)?
.ptr_guard();
@@ -425,8 +432,7 @@ impl Request {
// created with the correct alignment, and a copy from/to the
// origin buffer is performed, depending on the type of operation.
let iov_base = if (origin_ptr.as_ptr() as u64) % SECTOR_SIZE != 0 {
let layout =
Layout::from_size_align(*data_len as usize, SECTOR_SIZE as usize).unwrap();
let layout = Layout::from_size_align(data_len, SECTOR_SIZE as usize).unwrap();
// SAFETY: layout has non-zero size
let aligned_ptr = unsafe { alloc_zeroed(layout) };
if aligned_ptr.is_null() {
@@ -440,7 +446,7 @@ impl Request {
if request_type == RequestType::Out {
// SAFETY: destination buffer has been allocated with
// the proper size.
unsafe { std::ptr::copy(origin_ptr.as_ptr(), aligned_ptr, *data_len as usize) };
unsafe { std::ptr::copy(origin_ptr.as_ptr(), aligned_ptr, data_len) };
}
// Store both origin and aligned pointers for complete_async()
@@ -448,7 +454,7 @@ impl Request {
self.aligned_operations.push(AlignedOperation {
origin_ptr: origin_ptr.as_ptr() as u64,
aligned_ptr: aligned_ptr as u64,
size: *data_len as usize,
size: data_len,
layout,
});
@@ -459,7 +465,7 @@ impl Request {
let iovec = libc::iovec {
iov_base,
iov_len: *data_len as libc::size_t,
iov_len: data_len as libc::size_t,
};
iovecs.push(iovec);
}
@@ -743,7 +749,7 @@ where
Ok(())
}
fn file(&mut self) -> MutexGuard<F>;
fn file(&mut self) -> MutexGuard<'_, F>;
}
pub enum ImageType {

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};
@@ -35,24 +36,24 @@ const MAX_NESTING_DEPTH: u32 = 10;
#[sorted]
#[derive(Debug, Error)]
pub enum Error {
#[error("Backing file io error: {0}")]
BackingFileIo(io::Error),
#[error("Backing file open error: {0}")]
BackingFileOpen(Box<Error>),
#[error("Backing file io error")]
BackingFileIo(#[source] io::Error),
#[error("Backing file open 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),
#[error("Failed to evict cache")]
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),
#[error("Failed to get refcount: {0}")]
GettingRefcount(refcount::Error),
#[error("Failed to parse filename: {0}")]
InvalidBackingFileName(str::Utf8Error),
#[error("Failed to get file size")]
GettingFileSize(#[source] io::Error),
#[error("Failed to get refcount")]
GettingRefcount(#[source] refcount::Error),
#[error("Failed to parse filename")]
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),
#[error("Failed to read data: {0}")]
ReadingData(io::Error),
#[error("Failed to read header: {0}")]
ReadingHeader(io::Error),
#[error("Failed to read pointers: {0}")]
ReadingPointers(io::Error),
#[error("Failed to read ref count block: {0}")]
ReadingRefCountBlock(refcount::Error),
#[error("Failed to read ref counts: {0}")]
ReadingRefCounts(io::Error),
#[error("Failed to rebuild ref counts: {0}")]
RebuildingRefCounts(io::Error),
OpeningFile(#[source] io::Error),
#[error("Failed to read data")]
ReadingData(#[source] io::Error),
#[error("Failed to read header")]
ReadingHeader(#[source] io::Error),
#[error("Failed to read pointers")]
ReadingPointers(#[source] io::Error),
#[error("Failed to read ref count block")]
ReadingRefCountBlock(#[source] refcount::Error),
#[error("Failed to read ref counts")]
ReadingRefCounts(#[source] io::Error),
#[error("Failed to rebuild ref counts")]
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),
#[error("Failed to set file size: {0}")]
SettingFileSize(io::Error),
#[error("Failed to set refcount refcount: {0}")]
SettingRefcountRefcount(io::Error),
#[error("Failed to seek file")]
SeekingFile(#[source] io::Error),
#[error("Failed to set file size")]
SettingFileSize(#[source] io::Error),
#[error("Failed to set refcount refcount")]
SettingRefcountRefcount(#[source] io::Error),
#[error("Size too small for number of clusters")]
SizeTooSmallForNumberOfClusters,
#[error("L1 entry table too large: {0}")]
@@ -113,10 +114,10 @@ pub enum Error {
UnsupportedRefcountOrder,
#[error("Unsupported version: {0}")]
UnsupportedVersion(u32),
#[error("Failed to write data: {0}")]
WritingData(io::Error),
#[error("Failed to write header: {0}")]
WritingHeader(io::Error),
#[error("Failed to write data")]
WritingData(#[source] io::Error),
#[error("Failed to write header")]
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
@@ -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;
@@ -35,7 +36,7 @@ impl QcowRawFile {
}
/// Reads `count` 64 bit offsets and returns them as a vector.
/// `mask` optionally ands out some of the bits on the file.
/// `mask` optionally `&`s out some of the bits on the file.
pub fn read_pointer_table(
&mut self,
offset: u64,
@@ -54,7 +55,7 @@ impl QcowRawFile {
}
/// Reads a cluster's worth of 64 bit offsets and returns them as a vector.
/// `mask` optionally ands out some of the bits on the file.
/// `mask` optionally `&`s out some of the bits on the file.
pub fn read_pointer_cluster(&mut self, offset: u64, mask: Option<u64>) -> io::Result<Vec<u64>> {
let count = self.cluster_size / size_of::<u64>() as u64;
self.read_pointer_table(offset, count, mask)
@@ -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

@@ -15,8 +15,8 @@ use crate::qcow::vec_cache::{CacheMap, Cacheable, VecCache};
#[derive(Debug, Error)]
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),
#[error("Failed to write a refblock from the cache to disk")]
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,
@@ -27,8 +27,8 @@ pub enum Error {
#[error("New cluster needs to be allocated for refcounts")]
NeedNewCluster,
/// `ReadingRefCounts` - Error reading the file into the refcount cache.
#[error("Failed to read the file into the refcount cache: {0}")]
ReadingRefCounts(io::Error),
#[error("Failed to read the file into the refcount cache")]
ReadingRefCounts(#[source] io::Error),
}
pub type Result<T> = std::result::Result<T, Error>;

View File

@@ -111,7 +111,7 @@ impl<T: Cacheable> CacheMap<T> {
self.map.get_mut(&index)
}
pub fn iter_mut(&mut self) -> IterMut<usize, T> {
pub fn iter_mut(&mut self) -> IterMut<'_, usize, T> {
self.map.iter_mut()
}

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 {
@@ -55,7 +63,7 @@ impl QcowSync {
}
impl AsyncAdaptor<QcowFile> for Arc<Mutex<QcowFile>> {
fn file(&mut self) -> MutexGuard<QcowFile> {
fn file(&mut self) -> MutexGuard<'_, QcowFile> {
self.lock().unwrap()
}
}

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")]
NotVhdx(#[source] VhdxHeaderError),
#[error("Failed to parse VHDx header {0}")]
#[error("Failed to parse VHDx header")]
ParseVhdxHeader(#[source] VhdxHeaderError),
#[error("Failed to parse VHDx metadata {0}")]
#[error("Failed to parse VHDx metadata")]
ParseVhdxMetadata(#[source] VhdxMetadataError),
#[error("Failed to parse VHDx region entries {0}")]
#[error("Failed to parse VHDx region entries")]
ParseVhdxRegionEntry(#[source] VhdxHeaderError),
#[error("Failed reading metadata {0}")]
#[error("Failed reading metadata")]
ReadBatEntry(#[source] VhdxBatError),
#[error("Failed reading sector from disk {0}")]
#[error("Failed reading sector from disk")]
ReadFailed(#[source] VhdxIoError),
#[error("Failed writing to sector on disk {0}")]
#[error("Failed writing to sector on disk")]
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")]
ReadBat(#[source] io::Error),
#[error("Failed to write BAT entry {0}")]
#[error("Failed to write BAT entry")]
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 {
@@ -54,7 +62,7 @@ impl VhdxSync {
}
impl AsyncAdaptor<Vhdx> for Arc<Mutex<Vhdx>> {
fn file(&mut self) -> MutexGuard<Vhdx> {
fn file(&mut self) -> MutexGuard<'_, Vhdx> {
self.lock().unwrap()
}
}

View File

@@ -22,7 +22,7 @@ fn main() {
// 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));
version.push_str(&format!("-{extra_version}"));
}
// This println!() has a special behavior, as it will set the environment

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" }
@@ -17,7 +17,7 @@ log = "0.4.22"
num_enum = "0.7.2"
pci = { path = "../pci" }
serde = { version = "1.0.208", features = ["derive"] }
thiserror = "2.0.6"
thiserror = { workspace = true }
tpm = { path = "../tpm" }
vm-allocator = { path = "../vm-allocator" }
vm-device = { path = "../vm-device" }

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

@@ -17,39 +17,39 @@ pub enum Error {
#[error("Invalid delivery mode")]
InvalidDeliveryMode,
/// Failed creating the interrupt source group.
#[error("Failed creating the interrupt source group: {0}")]
CreateInterruptSourceGroup(io::Error),
#[error("Failed creating the interrupt source group")]
CreateInterruptSourceGroup(#[source] io::Error),
/// Failed triggering the interrupt.
#[error("Failed triggering the interrupt: {0}")]
TriggerInterrupt(io::Error),
#[error("Failed triggering the interrupt")]
TriggerInterrupt(#[source] io::Error),
/// Failed masking the interrupt.
#[error("Failed masking the interrupt: {0}")]
MaskInterrupt(io::Error),
#[error("Failed masking the interrupt")]
MaskInterrupt(#[source] io::Error),
/// Failed unmasking the interrupt.
#[error("Failed unmasking the interrupt: {0}")]
UnmaskInterrupt(io::Error),
#[error("Failed unmasking the interrupt")]
UnmaskInterrupt(#[source] io::Error),
/// Failed updating the interrupt.
#[error("Failed updating the interrupt: {0}")]
UpdateInterrupt(io::Error),
#[error("Failed updating the interrupt")]
UpdateInterrupt(#[source] io::Error),
/// Failed enabling the interrupt.
#[error("Failed enabling the interrupt: {0}")]
EnableInterrupt(io::Error),
#[error("Failed enabling the interrupt")]
EnableInterrupt(#[source] io::Error),
#[cfg(target_arch = "aarch64")]
/// Failed creating GIC device.
#[error("Failed creating GIC device: {0}")]
CreateGic(hypervisor::HypervisorVmError),
#[error("Failed creating GIC device")]
CreateGic(#[source] hypervisor::HypervisorVmError),
#[cfg(target_arch = "aarch64")]
/// Failed restoring GIC device.
#[error("Failed restoring GIC device: {0}")]
RestoreGic(hypervisor::arch::aarch64::gic::Error),
#[error("Failed restoring GIC device")]
RestoreGic(#[source] hypervisor::arch::aarch64::gic::Error),
#[cfg(target_arch = "riscv64")]
/// Failed creating AIA device.
#[error("Failed creating AIA device: {0}")]
CreateAia(hypervisor::HypervisorVmError),
#[error("Failed creating AIA device")]
CreateAia(#[source] hypervisor::HypervisorVmError),
#[cfg(target_arch = "riscv64")]
/// Failed restoring AIA device.
#[error("Failed restoring AIA device: {0}")]
RestoreAia(hypervisor::arch::riscv64::aia::Error),
#[error("Failed restoring AIA device")]
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")]
GpioInterruptFailure(#[source] io::Error),
#[error("Invalid GPIO Input key triggered: {0}")]
GpioTriggerKeyFailure(u32),
}

View File

@@ -4,16 +4,18 @@
//! ARM PL031 Real Time Clock
//!
//! This module implements a PL031 Real Time Clock (RTC) that provides to provides long time base counter.
//! This is achieved by generating an interrupt signal after counting for a programmed number of cycles of
//! a real-time clock input.
//! This module implements part of a PL031 Real Time Clock (RTC):
//! * provide a clock value via RTCDR
//! * no alarm is implemented through the match register
//! * no interrupt is generated
//! * RTC cannot be disabled via RTCCR
//! * no test registers
//!
use std::result;
use std::sync::{Arc, Barrier};
use std::time::Instant;
use std::{io, result};
use thiserror::Error;
use vm_device::interrupt::InterruptSourceGroup;
use vm_device::BusDevice;
use crate::{read_le_u32, write_le_u32};
@@ -45,8 +47,6 @@ pub const NANOS_PER_SECOND: u64 = 1_000_000_000;
pub enum Error {
#[error("Bad Write Offset: {0}")]
BadWriteOffset(u64),
#[error("Failed to trigger interrupt: {0}")]
InterruptFailure(io::Error),
}
type Result<T> = result::Result<T, Error>;
@@ -107,31 +107,20 @@ pub struct Rtc {
match_value: u32,
// Writes to this register load an update value into the RTC.
load: u32,
imsc: u32,
ris: u32,
interrupt: Arc<dyn InterruptSourceGroup>,
}
impl Rtc {
/// Constructs an AMBA PL031 RTC device.
pub fn new(interrupt: Arc<dyn InterruptSourceGroup>) -> Self {
pub fn new() -> Self {
Self {
// This is used only for duration measuring purposes.
previous_now: Instant::now(),
tick_offset: get_time(ClockType::Real) as i64,
match_value: 0,
load: 0,
imsc: 0,
ris: 0,
interrupt,
}
}
fn trigger_interrupt(&mut self) -> Result<()> {
self.interrupt.trigger(0).map_err(Error::InterruptFailure)?;
Ok(())
}
fn get_time(&self) -> u32 {
let ts = (self.tick_offset as i128)
+ (Instant::now().duration_since(self.previous_now).as_nanos() as i128);
@@ -155,16 +144,8 @@ impl Rtc {
// we want to terminate the execution of the process.
self.tick_offset = seconds_to_nanoseconds(i64::from(val)).unwrap();
}
RTCIMSC => {
self.imsc = val & 1;
self.trigger_interrupt()?;
}
RTCICR => {
// As per above mentioned doc, the interrupt is cleared by writing any data value to
// the Interrupt Clear Register.
self.ris = 0;
self.trigger_interrupt()?;
}
RTCIMSC => (),
RTCICR => (),
RTCCR => (), // ignore attempts to turn off the timer.
o => {
return Err(Error::BadWriteOffset(o));
@@ -174,6 +155,12 @@ impl Rtc {
}
}
impl Default for Rtc {
fn default() -> Self {
Self::new()
}
}
impl BusDevice for Rtc {
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
let mut read_ok = true;
@@ -189,10 +176,10 @@ impl BusDevice for Rtc {
self.match_value
}
RTCLR => self.load,
RTCCR => 1, // RTC is always enabled.
RTCIMSC => self.imsc,
RTCRIS => self.ris,
RTCMIS => self.ris & self.imsc,
RTCCR => 1, // RTC is always enabled.
RTCIMSC => 0, // Interrupt is always disabled.
RTCRIS => 0,
RTCMIS => 0,
_ => {
read_ok = false;
0
@@ -230,9 +217,6 @@ impl BusDevice for Rtc {
#[cfg(test)]
mod tests {
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
use vmm_sys_util::eventfd::EventFd;
use super::*;
use crate::{
read_be_u16, read_be_u32, read_le_i32, read_le_u16, read_le_u64, write_be_u16,
@@ -366,45 +350,9 @@ mod tests {
assert!(seconds_to_nanoseconds(9_223_372_037).is_none());
}
struct TestInterrupt {
event_fd: EventFd,
}
impl InterruptSourceGroup for TestInterrupt {
fn trigger(&self, _index: InterruptIndex) -> result::Result<(), std::io::Error> {
self.event_fd.write(1)
}
fn update(
&self,
_index: InterruptIndex,
_config: InterruptSourceConfig,
_masked: bool,
_set_gsi: bool,
) -> result::Result<(), std::io::Error> {
Ok(())
}
fn set_gsi(&self) -> result::Result<(), std::io::Error> {
Ok(())
}
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
Some(self.event_fd.try_clone().unwrap())
}
}
impl TestInterrupt {
fn new(event_fd: EventFd) -> Self {
TestInterrupt { event_fd }
}
}
#[test]
fn test_rtc_read_write_and_event() {
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
let mut rtc = Rtc::new(Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())));
let mut rtc = Rtc::new();
let mut data = [0; 4];
// Read and write to the MR register.
@@ -427,15 +375,13 @@ mod tests {
assert_eq!((v / NANOS_PER_SECOND) as u32, v_read);
// Read and write to IMSC register.
// Test with non zero value.
// Test with non zero value. Our device ignores the write.
let non_zero = 1;
write_le_u32(&mut data, non_zero);
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &data);
// The interrupt line should be on.
assert!(rtc.interrupt.notifier(0).unwrap().read().unwrap() == 1);
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
let v = read_le_u32(&data);
assert_eq!(non_zero & 1, v);
assert_eq!(0, v);
// Now test with 0.
write_le_u32(&mut data, 0);
@@ -447,8 +393,6 @@ mod tests {
// Read and write to the ICR register.
write_le_u32(&mut data, 1);
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCICR, &data);
// The interrupt line should be on.
assert!(rtc.interrupt.notifier(0).unwrap().read().unwrap() > 1);
let v_before = read_le_u32(&data);
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCICR, &mut data);

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),
#[error("Failed to write: {0}")]
WriteAllFailure(io::Error),
#[error("Failed to flush: {0}")]
FlushFailure(io::Error),
#[error("Failed to trigger interrupt")]
InterruptFailure(#[source] io::Error),
#[error("Failed to write")]
WriteAllFailure(#[source] io::Error),
#[error("Failed to flush")]
FlushFailure(#[source] io::Error),
}
type Result<T> = result::Result<T, Error>;

View File

@@ -36,7 +36,7 @@ const MINOR_VERSION: u64 = 0;
#[derive(Error, Debug)]
pub enum Error {
// device errors
#[error("Guest gave us bad memory addresses: {0}")]
#[error("Guest gave us bad memory addresses")]
GuestMemory(#[source] GuestMemoryError),
#[error("Guest sent us invalid request")]
InvalidRequest,
@@ -51,7 +51,7 @@ pub enum Error {
InvalidArgument(u64),
#[error("Unknown function code: {0}")]
UnknownFunctionCode(u64),
#[error("Libc call fail: {0}")]
#[error("Libc call fail")]
LibcFail(#[source] std::io::Error),
}
@@ -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

@@ -31,9 +31,9 @@ const PVPANIC_CRASH_LOADED: u8 = 1 << 1;
#[derive(Debug, Error)]
pub enum PvPanicError {
#[error("Failed creating PvPanicDevice: {0}")]
#[error("Failed creating PvPanicDevice")]
CreatePvPanicDevice(#[source] anyhow::Error),
#[error("Failed to retrieve PciConfigurationState: {0}")]
#[error("Failed to retrieve PciConfigurationState")]
RetrievePciConfigurationState(#[source] anyhow::Error),
}
@@ -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

@@ -18,9 +18,9 @@ use vm_device::BusDevice;
#[derive(Error, Debug)]
pub enum Error {
#[error("Emulator doesn't implement min required capabilities: {0}")]
#[error("Emulator doesn't implement min required capabilities")]
CheckCaps(#[source] anyhow::Error),
#[error("Failed to initialize tpm: {0}")]
#[error("Failed to initialize tpm")]
Init(#[source] anyhow::Error),
}
type Result<T> = anyhow::Result<T, Error>;

View File

@@ -4,14 +4,12 @@ Intel® Software Guard Extensions (Intel® SGX) is an Intel technology designed
to increase the security of application code and data. Cloud Hypervisor supports
SGX virtualization through KVM. Because SGX is built on hardware features that
cannot be emulated in software, virtualizing SGX requires support in KVM and in
the host kernel. The required Linux and KVM changes can be found in the
[KVM SGX Tree](https://github.com/intel/kvm-sgx).
the host kernel. The required Linux and KVM changes can be found in Linux 5.13+.
Utilizing SGX in the guest requires a kernel/OS with SGX support, e.g. a kernel
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.
[here](https://www.intel.com/content/www/us/en/developer/tools/software-guard-extensions/linux-overview.html).
Running Linux 5.13+ as the guest kernel allows nested virtualization of 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).

View File

@@ -18,7 +18,7 @@ tapindex=$(< /sys/class/net/macvtap0/ifindex)
tapdevice="/dev/tap$tapindex"
# Ensure that we can access this device
sudo chown "$UID.$UID" "$tapdevice"
sudo chown "$UID:$UID" "$tapdevice"
# Use --net fd=3 to point to fd 3 which the shell has opened to point to the /dev/tapN device
target/debug/cloud-hypervisor \

View File

@@ -80,6 +80,86 @@ sudo $CLOUDH/cloud-hypervisor/target/debug/cloud-hypervisor \
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

View File

@@ -7,6 +7,5 @@ version = "0.1.0"
[dependencies]
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 }

View File

@@ -8,13 +8,12 @@ use std::collections::HashMap;
use std::fs::File;
use std::io;
use std::os::unix::io::AsRawFd;
use std::sync::Arc;
use std::sync::{Arc, OnceLock};
use std::time::{Duration, Instant};
use once_cell::sync::OnceCell;
use serde::Serialize;
static MONITOR: OnceCell<MonitorHandle> = OnceCell::new();
static MONITOR: OnceLock<MonitorHandle> = OnceLock::new();
#[derive(Serialize)]
struct Event<'a> {

265
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.26",
]
[[package]]
@@ -89,7 +89,7 @@ dependencies = [
"linux-loader",
"log",
"serde",
"thiserror 2.0.9",
"thiserror 2.0.12",
"uuid",
"vm-fdt",
"vm-memory",
@@ -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"
@@ -126,7 +137,7 @@ dependencies = [
"remain",
"serde",
"smallvec",
"thiserror 2.0.9",
"thiserror 2.0.12",
"uuid",
"virtio-bindings",
"virtio-queue",
@@ -206,7 +217,6 @@ dependencies = [
"micro_http",
"mshv-bindings",
"net_util",
"once_cell",
"seccompiler",
"virtio-devices",
"virtio-queue",
@@ -291,7 +301,7 @@ dependencies = [
"acpi_tables",
"anyhow",
"arch",
"bitflags 2.6.0",
"bitflags 2.9.0",
"byteorder",
"event_monitor",
"hypervisor",
@@ -300,7 +310,7 @@ dependencies = [
"num_enum",
"pci",
"serde",
"thiserror 2.0.9",
"thiserror 2.0.12",
"tpm",
"vm-allocator",
"vm-device",
@@ -335,7 +345,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",
]
@@ -351,7 +361,6 @@ version = "0.1.0"
dependencies = [
"flume",
"libc",
"once_cell",
"serde",
"serde_json",
]
@@ -394,11 +403,11 @@ checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
[[package]]
name = "gdbstub"
version = "0.7.2"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbcc892208d6998fb57e7c3e05883def66f8130924bba066beb0cfe71566a9f6"
checksum = "71d66e32caf5dd59f561be0143e413e01d651bd8498eb9aa0be8c482c81c8d31"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.0",
"cfg-if",
"log",
"managed",
@@ -431,14 +440,14 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.3.1"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"libc",
"wasi 0.13.3+wasi-0.2.2",
"windows-targets",
"r-efi",
"wasi 0.14.2+wasi-0.2.4",
]
[[package]]
@@ -453,6 +462,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"arc-swap",
"bitfield-struct",
"byteorder",
"cfg-if",
"concat-idents",
@@ -462,12 +472,15 @@ dependencies = [
"libc",
"log",
"mshv-bindings",
"open-enum",
"serde",
"serde_json",
"serde_with",
"thiserror 2.0.9",
"thiserror 2.0.12",
"vfio-ioctls",
"vm-memory",
"vmm-sys-util",
"zerocopy 0.8.26",
]
[[package]]
@@ -518,31 +531,32 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.69"
version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "kvm-bindings"
version = "0.10.0"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4933174d0cc4b77b958578cd45784071cc5ae212c2d78fbd755aaaa6dfa71a"
checksum = "d4b153a59bb3ca930ff8148655b2ef68c34259a623ae08cf2fb9b570b2e45363"
dependencies = [
"serde",
"vmm-sys-util",
"zerocopy 0.7.35",
"zerocopy 0.8.26",
]
[[package]]
name = "kvm-ioctls"
version = "0.19.1"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e013ae7fcd2c6a8f384104d16afe7ea02969301ea2bb2a56e44b011ebc907cab"
checksum = "b702df98508cb63ad89dd9beb9f6409761b30edca10d48e57941d3f11513a006"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.0",
"kvm-bindings",
"libc",
"vmm-sys-util",
@@ -622,7 +636,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#bf5098916006912f8dd35aaa6daa5579c6c297b2"
dependencies = [
"libc",
"vmm-sys-util",
@@ -630,16 +644,16 @@ dependencies = [
[[package]]
name = "mshv-bindings"
version = "0.3.4"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9c369385758f81ca937414dc2147737c92032e4fb399669f287abf94d89252d"
checksum = "07f94f542c738f19317363222a7f415588c04cda964882479af41948ac3c3647"
dependencies = [
"libc",
"num_enum",
"serde",
"serde_derive",
"vmm-sys-util",
"zerocopy 0.8.14",
"zerocopy 0.8.26",
]
[[package]]
@@ -663,13 +677,13 @@ name = "net_util"
version = "0.1.0"
dependencies = [
"epoll",
"getrandom 0.3.1",
"getrandom 0.3.3",
"libc",
"log",
"net_gen",
"rate_limiter",
"serde",
"thiserror 2.0.9",
"thiserror 2.0.12",
"virtio-bindings",
"virtio-queue",
"vm-memory",
@@ -713,9 +727,32 @@ 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"
dependencies = [
"thiserror 2.0.12",
]
[[package]]
name = "paste"
@@ -733,7 +770,7 @@ dependencies = [
"libc",
"log",
"serde",
"thiserror 2.0.9",
"thiserror 2.0.12",
"vfio-bindings",
"vfio-ioctls",
"vfio_user",
@@ -780,6 +817,12 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "rand"
version = "0.9.0"
@@ -788,7 +831,7 @@ checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
dependencies = [
"rand_chacha",
"rand_core",
"zerocopy 0.8.14",
"zerocopy 0.8.26",
]
[[package]]
@@ -807,7 +850,7 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom 0.3.1",
"getrandom 0.3.3",
]
[[package]]
@@ -817,21 +860,27 @@ dependencies = [
"epoll",
"libc",
"log",
"thiserror 2.0.9",
"thiserror 2.0.12",
"vmm-sys-util",
]
[[package]]
name = "remain"
version = "0.2.14"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46aef80f842736de545ada6ec65b81ee91504efd6853f4b96de7414c42ae7443"
checksum = "d7ef12e84481ab4006cb942f8682bba28ece7270743e649442027c5db87df126"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "rustversion"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
[[package]]
name = "ryu"
version = "1.0.18"
@@ -920,9 +969,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook"
version = "0.3.17"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
dependencies = [
"libc",
"signal-hook-registry",
@@ -980,11 +1029,11 @@ dependencies = [
[[package]]
name = "thiserror"
version = "2.0.9"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl 2.0.9",
"thiserror-impl 2.0.12",
]
[[package]]
@@ -1000,9 +1049,9 @@ dependencies = [
[[package]]
name = "thiserror-impl"
version = "2.0.9"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
@@ -1035,7 +1084,7 @@ dependencies = [
"libc",
"log",
"net_gen",
"thiserror 2.0.9",
"thiserror 2.0.12",
"vmm-sys-util",
]
@@ -1045,7 +1094,6 @@ version = "0.1.0"
dependencies = [
"libc",
"log",
"once_cell",
"serde",
"serde_json",
]
@@ -1064,45 +1112,37 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.15.1"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
dependencies = [
"getrandom 0.3.1",
"getrandom 0.3.3",
"js-sys",
"rand",
"uuid-macro-internal",
]
[[package]]
name = "uuid-macro-internal"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9521621447c21497fac206ffe6e9f642f977c4f82eeba9201055f64884d9cb01"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen",
]
[[package]]
name = "vfio-bindings"
version = "0.4.0"
source = "git+https://github.com/rust-vmm/vfio?branch=main#b135b8305c2cc8ec333e0cf77a780445cc98dcee"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b565663f62e091ca47db9a674c8c95c9686a000e82970f391a3cacf6470ff060"
dependencies = [
"vmm-sys-util",
]
[[package]]
name = "vfio-ioctls"
version = "0.2.0"
source = "git+https://github.com/rust-vmm/vfio?branch=main#b135b8305c2cc8ec333e0cf77a780445cc98dcee"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61316b5e308faa8ed4a87c4130256f765e46de3442eb2e2e619840ef73456738"
dependencies = [
"byteorder",
"kvm-bindings",
"kvm-ioctls",
"libc",
"log",
"thiserror 1.0.64",
"thiserror 2.0.12",
"vfio-bindings",
"vm-memory",
"vmm-sys-util",
@@ -1111,15 +1151,16 @@ dependencies = [
[[package]]
name = "vfio_user"
version = "0.1.0"
source = "git+https://github.com/rust-vmm/vfio-user?branch=main#3febcdd3fa2531623865663ca1721e1962ed9979"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed81c5ed8224d468a322e923777ed0615cad433fe61177126098af995f89cecf"
dependencies = [
"bitflags 1.3.2",
"bitflags 2.9.0",
"libc",
"log",
"serde",
"serde_derive",
"serde_json",
"thiserror 1.0.64",
"thiserror 2.0.12",
"vfio-bindings",
"vm-memory",
"vmm-sys-util",
@@ -1127,10 +1168,11 @@ dependencies = [
[[package]]
name = "vhost"
version = "0.12.1"
source = "git+https://github.com/rust-vmm/vhost?rev=d983ae0#d983ae07f78663b7d24059667376992460b571a2"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a4dcad85a129d97d5d4b2f3c47a4affdeedd76bdcd02094bcb5d9b76cac2d05"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.0",
"libc",
"uuid",
"vm-memory",
@@ -1139,9 +1181,9 @@ dependencies = [
[[package]]
name = "virtio-bindings"
version = "0.2.4"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1711e61c00f8cb450bd15368152a1e37a12ef195008ddc7d0f4812f9e2b30a68"
checksum = "804f498a26d5a63be7bbb8bdcd3869c3f286c4c4a17108905276454da0caf8cb"
[[package]]
name = "virtio-devices"
@@ -1164,7 +1206,7 @@ dependencies = [
"serde_json",
"serde_with",
"serial_buffer",
"thiserror 2.0.9",
"thiserror 2.0.12",
"vhost",
"virtio-bindings",
"virtio-queue",
@@ -1178,9 +1220,9 @@ dependencies = [
[[package]]
name = "virtio-queue"
version = "0.14.0"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872e2f3fbd70a7e6f01689720cce3d5c2c5efe52b484dd07b674246ada0e9a8d"
checksum = "fb0479158f863e59323771a1f684d843962f76960b86fecfec2bfa9c8f0f9180"
dependencies = [
"log",
"virtio-bindings",
@@ -1204,7 +1246,7 @@ dependencies = [
"anyhow",
"hypervisor",
"serde",
"thiserror 2.0.9",
"thiserror 2.0.12",
"vfio-ioctls",
"vm-memory",
"vmm-sys-util",
@@ -1217,9 +1259,9 @@ source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#ef5bd734f5f66fb0772
[[package]]
name = "vm-memory"
version = "0.16.1"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1720e7240cdc739f935456eb77f370d7e9b2a3909204da1e2b47bef1137a013"
checksum = "1fd5e56d48353c5f54ef50bd158a0452fc82f5383da840f7b8efc31695dd3b9d"
dependencies = [
"arc-swap",
"libc",
@@ -1234,7 +1276,7 @@ dependencies = [
"anyhow",
"serde",
"serde_json",
"thiserror 2.0.9",
"thiserror 2.0.12",
"vm-memory",
]
@@ -1255,7 +1297,7 @@ dependencies = [
"anyhow",
"arc-swap",
"arch",
"bitflags 2.6.0",
"bitflags 2.9.0",
"block",
"cfg-if",
"clap",
@@ -1272,7 +1314,6 @@ dependencies = [
"log",
"micro_http",
"net_util",
"once_cell",
"option_parser",
"pci",
"rate_limiter",
@@ -1281,11 +1322,12 @@ dependencies = [
"serde_json",
"serial_buffer",
"signal-hook",
"thiserror 2.0.9",
"thiserror 2.0.12",
"tracer",
"uuid",
"vfio-ioctls",
"vfio_user",
"virtio-bindings",
"virtio-devices",
"virtio-queue",
"vm-allocator",
@@ -1294,14 +1336,14 @@ dependencies = [
"vm-migration",
"vm-virtio",
"vmm-sys-util",
"zerocopy 0.7.35",
"zerocopy 0.8.26",
]
[[package]]
name = "vmm-sys-util"
version = "0.12.1"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1435039746e20da4f8d507a72ee1b916f7b4b05af7a91c093d2c6561934ede"
checksum = "d21f366bf22bfba3e868349978766a965cbe628c323d58e026be80b8357ab789"
dependencies = [
"bitflags 1.3.2",
"libc",
@@ -1317,33 +1359,33 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.13.3+wasi-0.2.2"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.93"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.93"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
@@ -1352,9 +1394,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.93"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1362,9 +1404,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.93"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
@@ -1375,9 +1417,12 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.93"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
dependencies = [
"unicode-ident",
]
[[package]]
name = "winapi"
@@ -1485,11 +1530,11 @@ dependencies = [
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.0",
]
[[package]]
@@ -1504,11 +1549,11 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.14"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468"
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
dependencies = [
"zerocopy-derive 0.8.14",
"zerocopy-derive 0.8.26",
]
[[package]]
@@ -1524,9 +1569,9 @@ dependencies = [
[[package]]
name = "zerocopy-derive"
version = "0.8.14"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1"
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -24,18 +24,17 @@ 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.2"
net_util = { path = "../net_util" }
once_cell = "1.19.0"
seccompiler = "0.5.0"
virtio-devices = { path = "../virtio-devices" }
virtio-queue = "0.14.0"
virtio-queue = "0.16.0"
vm-device = { path = "../vm-device" }
vm-memory = "0.16.0"
vm-migration = { path = "../vm-migration" }
vm-virtio = { path = "../vm-virtio" }
vmm = { path = "../vmm", features = ["guest_debug"] }
vmm-sys-util = "0.12.1"
vmm-sys-util = "0.14.0"
# Prevent this from interfering with workspaces
[workspace]

View File

@@ -6,11 +6,11 @@
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
use std::sync::mpsc::{channel, Receiver};
use std::sync::LazyLock;
use std::thread;
use libfuzzer_sys::{fuzz_target, Corpus};
use micro_http::Request;
use once_cell::sync::Lazy;
use vm_migration::MigratableError;
use vmm::api::http::*;
use vmm::api::{
@@ -24,8 +24,8 @@ use vmm::{EpollContext, EpollDispatch};
use vmm_sys_util::eventfd::EventFd;
// Need to be ordered for test case reproducibility
static ROUTES: Lazy<Vec<&Box<dyn EndpointHandler + Sync + Send>>> =
Lazy::new(|| HTTP_ROUTES.routes.values().collect());
static ROUTES: LazyLock<Vec<&Box<dyn EndpointHandler + Sync + Send>>> =
LazyLock::new(|| HTTP_ROUTES.routes.values().collect());
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < 2 {

View File

@@ -15,6 +15,7 @@ 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"
@@ -29,11 +30,13 @@ 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",
] }
thiserror = "2.0.6"
thiserror = { workspace = true }
vfio-ioctls = { workspace = true, default-features = false }
vm-memory = { workspace = true, features = [
"backend-atomic",
@@ -41,6 +44,7 @@ vm-memory = { workspace = true, features = [
"backend-mmap",
] }
vmm-sys-util = { workspace = true, features = ["with-serde"] }
zerocopy = { workspace = true, features = ["derive"] }
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
default-features = false
@@ -58,4 +62,4 @@ optional = true
version = "1.21.0"
[dev-dependencies]
env_logger = "0.11.3"
env_logger = { workspace = true }

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),
#[error("Failed creating GIC device")]
CreateGic(#[source] HypervisorVmError),
/// Error while setting device attributes for the GIC.
#[error("Failed setting device attributes for the GIC: {0}")]
SetDeviceAttribute(HypervisorDeviceError),
#[error("Failed setting device attributes for the GIC")]
SetDeviceAttribute(#[source] HypervisorDeviceError),
/// Error while getting device attributes for the GIC.
#[error("Failed getting device attributes for the GIC: {0}")]
GetDeviceAttribute(HypervisorDeviceError),
#[error("Failed getting device attributes for the GIC")]
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

@@ -36,59 +36,59 @@ impl<T: Debug> Display for Exception<T> {
#[derive(Error, Debug)]
pub enum PlatformError {
#[error("Invalid address: {0}")]
#[error("Invalid address")]
InvalidAddress(#[source] anyhow::Error),
#[error("Invalid register: {0}")]
#[error("Invalid register")]
InvalidRegister(#[source] anyhow::Error),
#[error("Invalid state: {0}")]
#[error("Invalid state")]
InvalidState(#[source] anyhow::Error),
#[error("Memory read failure: {0}")]
#[error("Memory read failure")]
MemoryReadFailure(#[source] anyhow::Error),
#[error("Memory write failure: {0}")]
#[error("Memory write failure")]
MemoryWriteFailure(#[source] anyhow::Error),
#[error("Get CPU state failure: {0}")]
#[error("Get CPU state failure")]
GetCpuStateFailure(#[source] anyhow::Error),
#[error("Set CPU state failure: {0}")]
#[error("Set CPU state failure")]
SetCpuStateFailure(#[source] anyhow::Error),
#[error("Translate virtual address: {0}")]
#[error("Translate virtual address")]
TranslateVirtualAddress(#[source] anyhow::Error),
#[error("Unsupported CPU Mode: {0}")]
#[error("Unsupported CPU Mode")]
UnsupportedCpuMode(#[source] anyhow::Error),
#[error("Invalid instruction operand: {0}")]
#[error("Invalid instruction operand")]
InvalidOperand(#[source] anyhow::Error),
}
#[derive(Error, Debug)]
pub enum EmulationError<T: Debug> {
#[error("Unsupported instruction: {0}")]
#[error("Unsupported instruction")]
UnsupportedInstruction(#[source] anyhow::Error),
#[error("Unsupported memory size: {0}")]
#[error("Unsupported memory size")]
UnsupportedMemorySize(#[source] anyhow::Error),
#[error("Invalid operand: {0}")]
#[error("Invalid operand")]
InvalidOperand(#[source] anyhow::Error),
#[error("Wrong number of operands: {0}")]
#[error("Wrong number of operands")]
WrongNumberOperands(#[source] anyhow::Error),
#[error("Instruction Exception: {0}")]
InstructionException(Exception<T>),
#[error("Instruction Exception")]
InstructionException(#[source] Exception<T>),
#[error("Instruction fetching error: {0}")]
#[error("Instruction fetching error")]
InstructionFetchingError(#[source] anyhow::Error),
#[error("Platform emulation error: {0}")]
PlatformEmulationError(PlatformError),
#[error("Platform emulation error")]
PlatformEmulationError(#[source] PlatformError),
#[error(transparent)]
EmulationError(#[from] anyhow::Error),

View File

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

View File

@@ -4,7 +4,7 @@
// SPDX-License-Identifier: Apache-2.0
//
#![allow(non_camel_case_types, clippy::upper_case_acronyms)]
#![allow(non_camel_case_types, dead_code, clippy::upper_case_acronyms)]
//
// CMP-Compare Two Operands

View File

@@ -490,7 +490,7 @@ macro_rules! gen_handler_match {
}
impl<T: CpuStateManager> Emulator<'_, T> {
pub fn new(platform: &mut dyn PlatformEmulator<CpuState = T>) -> Emulator<T> {
pub fn new(platform: &mut dyn PlatformEmulator<CpuState = T>) -> Emulator<'_, T> {
Emulator { platform }
}

View File

@@ -43,171 +43,171 @@ pub enum HypervisorCpuError {
///
/// Setting standard registers error
///
#[error("Failed to set standard register: {0}")]
#[error("Failed to set standard register")]
SetStandardRegs(#[source] anyhow::Error),
///
/// Setting standard registers error
///
#[error("Failed to get standard registers: {0}")]
#[error("Failed to get standard registers")]
GetStandardRegs(#[source] anyhow::Error),
///
/// Setting special register error
///
#[error("Failed to set special registers: {0}")]
#[error("Failed to set special registers")]
SetSpecialRegs(#[source] anyhow::Error),
///
/// Getting standard register error
///
#[error("Failed to get special registers: {0}")]
#[error("Failed to get special registers")]
GetSpecialRegs(#[source] anyhow::Error),
///
/// Setting floating point registers error
///
#[error("Failed to set floating point registers: {0}")]
#[error("Failed to set floating point registers")]
SetFloatingPointRegs(#[source] anyhow::Error),
///
/// Getting floating point register error
///
#[error("Failed to get floating points registers: {0}")]
#[error("Failed to get floating points registers")]
GetFloatingPointRegs(#[source] anyhow::Error),
///
/// Setting Cpuid error
///
#[error("Failed to set Cpuid: {0}")]
#[error("Failed to set Cpuid")]
SetCpuid(#[source] anyhow::Error),
///
/// Getting Cpuid error
///
#[error("Failed to get Cpuid: {0}")]
#[error("Failed to get Cpuid")]
GetCpuid(#[source] anyhow::Error),
///
/// Setting lapic state error
///
#[error("Failed to set Lapic state: {0}")]
#[error("Failed to set Lapic state")]
SetLapicState(#[source] anyhow::Error),
///
/// Getting Lapic state error
///
#[error("Failed to get Lapic state: {0}")]
#[error("Failed to get Lapic state")]
GetlapicState(#[source] anyhow::Error),
///
/// Setting MSR entries error
///
#[error("Failed to set Msr entries: {0}")]
#[error("Failed to set Msr entries")]
SetMsrEntries(#[source] anyhow::Error),
///
/// Getting Msr entries error
///
#[error("Failed to get Msr entries: {0}")]
#[error("Failed to get Msr entries")]
GetMsrEntries(#[source] anyhow::Error),
///
/// Setting multi-processing state error
///
#[error("Failed to set MP state: {0}")]
#[error("Failed to set MP state")]
SetMpState(#[source] anyhow::Error),
///
/// Getting multi-processing state error
///
#[error("Failed to get MP state: {0}")]
#[error("Failed to get MP state")]
GetMpState(#[source] anyhow::Error),
///
/// Setting Saved Processor Extended States error
///
#[cfg(feature = "kvm")]
#[error("Failed to set Saved Processor Extended States: {0}")]
#[error("Failed to set Saved Processor Extended States")]
SetXsaveState(#[source] anyhow::Error),
///
/// Getting Saved Processor Extended States error
///
#[cfg(feature = "kvm")]
#[error("Failed to get Saved Processor Extended States: {0}")]
#[error("Failed to get Saved Processor Extended States")]
GetXsaveState(#[source] anyhow::Error),
///
/// Getting the VP state components error
///
#[cfg(feature = "mshv")]
#[error("Failed to get VP State Components: {0}")]
#[error("Failed to get VP State Components")]
GetAllVpStateComponents(#[source] anyhow::Error),
///
/// Setting the VP state components error
///
#[cfg(feature = "mshv")]
#[error("Failed to set VP State Components: {0}")]
#[error("Failed to set VP State Components")]
SetAllVpStateComponents(#[source] anyhow::Error),
///
/// Setting Extended Control Registers error
///
#[error("Failed to set Extended Control Registers: {0}")]
#[error("Failed to set Extended Control Registers")]
SetXcsr(#[source] anyhow::Error),
///
/// Getting Extended Control Registers error
///
#[error("Failed to get Extended Control Registers: {0}")]
#[error("Failed to get Extended Control Registers")]
GetXcsr(#[source] anyhow::Error),
///
/// Running Vcpu error
///
#[error("Failed to run vcpu: {0}")]
#[error("Failed to run vcpu")]
RunVcpu(#[source] anyhow::Error),
///
/// Getting Vcpu events error
///
#[error("Failed to get Vcpu events: {0}")]
#[error("Failed to get Vcpu events")]
GetVcpuEvents(#[source] anyhow::Error),
///
/// Setting Vcpu events error
///
#[error("Failed to set Vcpu events: {0}")]
#[error("Failed to set Vcpu events")]
SetVcpuEvents(#[source] anyhow::Error),
///
/// Vcpu Init error
///
#[error("Failed to init vcpu: {0}")]
#[error("Failed to init vcpu")]
VcpuInit(#[source] anyhow::Error),
///
/// Vcpu Finalize error
///
#[error("Failed to finalize vcpu: {0}")]
#[error("Failed to finalize vcpu")]
VcpuFinalize(#[source] anyhow::Error),
///
/// Setting one reg error
///
#[error("Failed to set one reg: {0}")]
#[error("Failed to set one reg")]
SetRegister(#[source] anyhow::Error),
///
/// Getting one reg error
///
#[error("Failed to get one reg: {0}")]
#[error("Failed to get one reg")]
GetRegister(#[source] anyhow::Error),
///
/// Getting guest clock paused error
///
#[error("Failed to notify guest its clock was paused: {0}")]
#[error("Failed to notify guest its clock was paused")]
NotifyGuestClockPaused(#[source] anyhow::Error),
///
/// Setting debug register error
///
#[error("Failed to set debug registers: {0}")]
#[error("Failed to set debug registers")]
SetDebugRegs(#[source] anyhow::Error),
///
/// Getting debug register error
///
#[error("Failed to get debug registers: {0}")]
#[error("Failed to get debug registers")]
GetDebugRegs(#[source] anyhow::Error),
///
/// Setting misc register error
///
#[error("Failed to set misc registers: {0}")]
#[error("Failed to set misc registers")]
SetMiscRegs(#[source] anyhow::Error),
///
/// Getting misc register error
///
#[error("Failed to get misc registers: {0}")]
#[error("Failed to get misc registers")]
GetMiscRegs(#[source] anyhow::Error),
///
/// Write to Guest Mem
///
#[error("Failed to write to Guest Mem at: {0}")]
#[error("Failed to write to Guest Mem at")]
GuestMemWrite(#[source] anyhow::Error),
/// Enabling HyperV SynIC error
///
@@ -216,68 +216,68 @@ pub enum HypervisorCpuError {
///
/// Getting AArch64 core register error
///
#[error("Failed to get aarch64 core register: {0}")]
#[error("Failed to get aarch64 core register")]
GetAarchCoreRegister(#[source] anyhow::Error),
///
/// Setting AArch64 core register error
///
#[error("Failed to set aarch64 core register: {0}")]
#[error("Failed to set aarch64 core register")]
SetAarchCoreRegister(#[source] anyhow::Error),
///
/// Getting RISC-V 64-bit core register error
///
#[error("Failed to get riscv64 core register: {0}")]
#[error("Failed to get riscv64 core register")]
GetRiscvCoreRegister(#[source] anyhow::Error),
///
/// Setting RISC-V 64-bit core register error
///
#[error("Failed to set riscv64 core register: {0}")]
#[error("Failed to set riscv64 core register")]
SetRiscvCoreRegister(#[source] anyhow::Error),
///
/// Getting registers list error
///
#[error("Failed to retrieve list of registers: {0}")]
#[error("Failed to retrieve list of registers")]
GetRegList(#[source] anyhow::Error),
///
/// Getting AArch64 system register error
///
#[error("Failed to get system register: {0}")]
#[error("Failed to get system register")]
GetSysRegister(#[source] anyhow::Error),
///
/// Setting AArch64 system register error
///
#[error("Failed to set system register: {0}")]
#[error("Failed to set system register")]
SetSysRegister(#[source] anyhow::Error),
///
/// Getting RISC-V 64-bit non-core register error
///
#[error("Failed to get non-core register: {0}")]
#[error("Failed to get non-core register")]
GetNonCoreRegister(#[source] anyhow::Error),
///
/// Setting RISC-V 64-bit non-core register error
///
#[error("Failed to set non-core register: {0}")]
#[error("Failed to set non-core register")]
SetNonCoreRegister(#[source] anyhow::Error),
///
/// GVA translation error
///
#[error("Failed to translate GVA: {0}")]
#[error("Failed to translate GVA")]
TranslateVirtualAddress(#[source] anyhow::Error),
///
/// Set cpu attribute error
///
#[error("Failed to set vcpu attribute: {0}")]
#[error("Failed to set vcpu attribute")]
SetVcpuAttribute(#[source] anyhow::Error),
///
/// Check if cpu has a certain attribute error
///
#[error("Failed to check if vcpu has attribute: {0}")]
#[error("Failed to check if vcpu has attribute")]
HasVcpuAttribute(#[source] anyhow::Error),
///
/// Failed to initialize TDX on CPU
///
#[cfg(feature = "tdx")]
#[error("Failed to initialize TDX: {0}")]
#[error("Failed to initialize TDX")]
InitializeTdx(#[source] std::io::Error),
///
/// Unknown TDX VM call
@@ -295,35 +295,41 @@ pub enum HypervisorCpuError {
///
/// Error getting TSC frequency
///
#[error("Failed to get TSC frequency: {0}")]
#[error("Failed to get TSC frequency")]
GetTscKhz(#[source] anyhow::Error),
///
/// Error setting TSC frequency
///
#[error("Failed to set TSC frequency: {0}")]
#[error("Failed to set TSC frequency")]
SetTscKhz(#[source] anyhow::Error),
///
/// Error reading value at given GPA
///
#[error("Failed to read from GPA: {0}")]
#[error("Failed to read from GPA")]
GpaRead(#[source] anyhow::Error),
///
/// Error writing value at given GPA
///
#[error("Failed to write to GPA: {0}")]
#[error("Failed to write to GPA")]
GpaWrite(#[source] anyhow::Error),
///
/// Error getting CPUID leaf
///
#[error("Failed to get CPUID entries: {0}")]
#[error("Failed to get CPUID entries")]
GetCpuidVales(#[source] anyhow::Error),
///
/// Setting SEV control register error
///
#[cfg(feature = "sev_snp")]
#[error("Failed to set sev control register: {0}")]
#[error("Failed to set sev control register")]
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")]
@@ -584,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

@@ -18,11 +18,11 @@ pub enum HypervisorDeviceError {
///
/// Set device attribute error
///
#[error("Failed to set device attribute: {0}")]
#[error("Failed to set device attribute")]
SetDeviceAttribute(#[source] anyhow::Error),
///
/// Get device attribute error
///
#[error("Failed to get device attribute: {0}")]
#[error("Failed to get device attribute")]
GetDeviceAttribute(#[source] anyhow::Error),
}

View File

@@ -27,37 +27,37 @@ pub enum HypervisorError {
///
/// Hypervisor availability check error
///
#[error("Failed to check availability of the hypervisor: {0}")]
#[error("Failed to check availability of the hypervisor")]
HypervisorAvailableCheck(#[source] anyhow::Error),
///
/// hypervisor creation error
///
#[error("Failed to create the hypervisor: {0}")]
#[error("Failed to create the hypervisor")]
HypervisorCreate(#[source] anyhow::Error),
///
/// Vm creation failure
///
#[error("Failed to create Vm: {0}")]
#[error("Failed to create Vm")]
VmCreate(#[source] anyhow::Error),
///
/// Vm setup failure
///
#[error("Failed to setup Vm: {0}")]
#[error("Failed to setup Vm")]
VmSetup(#[source] anyhow::Error),
///
/// API version error
///
#[error("Failed to get API Version: {0}")]
#[error("Failed to get API Version")]
GetApiVersion(#[source] anyhow::Error),
///
/// CpuId error
///
#[error("Failed to get cpuid: {0}")]
#[error("Failed to get cpuid")]
GetCpuId(#[source] anyhow::Error),
///
/// Failed to retrieve list of MSRs.
///
#[error("Failed to get the list of supported MSRs: {0}")]
#[error("Failed to get the list of supported MSRs")]
GetMsrList(#[source] anyhow::Error),
///
/// API version is not compatible
@@ -67,22 +67,22 @@ pub enum HypervisorError {
///
/// Checking extensions failed
///
#[error("Checking extensions:{0}")]
#[error("Checking extensions")]
CheckExtensions(#[source] anyhow::Error),
///
/// Failed to retrieve TDX capabilities
///
#[error("Failed to retrieve TDX capabilities:{0}")]
#[error("Failed to retrieve TDX capabilities")]
TdxCapabilities(#[source] anyhow::Error),
///
/// Failed to set partition property
///
#[error("Failed to set partition property:{0}")]
#[error("Failed to set partition property")]
SetPartitionProperty(#[source] anyhow::Error),
///
/// Running on an unsupported CPU
///
#[error("Unsupported CPU:{0}")]
#[error("Unsupported CPU")]
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

@@ -19,31 +19,6 @@ use serde::{Deserialize, Serialize};
use crate::kvm::{KvmError, KvmResult};
// This macro gets the offset of a structure (i.e `str`) member (i.e `field`) without having
// an instance of that structure.
#[macro_export]
macro_rules! offset_of {
($str:ty, $field:ident) => {{
let tmp: std::mem::MaybeUninit<$str> = std::mem::MaybeUninit::uninit();
let base = tmp.as_ptr();
// Avoid warnings when nesting `unsafe` blocks.
#[allow(unused_unsafe)]
// SAFETY: The pointer is valid and aligned, just not initialised. Using `addr_of` ensures
// that we don't actually read from `base` (which would be UB) nor create an intermediate
// reference.
let member = unsafe { core::ptr::addr_of!((*base).$field) } as *const u8;
// Avoid warnings when nesting `unsafe` blocks.
#[allow(unused_unsafe)]
// SAFETY: The two pointers are within the same allocated object `tmp`. All requirements
// from offset_from are upheld.
unsafe {
member.offset_from(base as *const u8) as usize
}
}};
}
// Following are macros that help with getting the ID of a aarch64 core register.
// The core register are represented by the user_pt_regs structure. Look for it in
// arch/arm64/include/uapi/asm/ptrace.h.

View File

@@ -14,6 +14,8 @@ use std::any::Any;
use std::collections::HashMap;
#[cfg(target_arch = "x86_64")]
use std::fs::File;
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
use std::mem::offset_of;
#[cfg(target_arch = "x86_64")]
use std::os::unix::io::AsRawFd;
#[cfg(feature = "tdx")]
@@ -29,13 +31,13 @@ use vmm_sys_util::eventfd::EventFd;
#[cfg(target_arch = "aarch64")]
use crate::aarch64::gic::KvmGicV3Its;
#[cfg(target_arch = "aarch64")]
pub use crate::aarch64::{
check_required_kvm_extensions, gic::Gicv3ItsState as GicState, is_system_register, VcpuKvmState,
};
pub use crate::aarch64::{check_required_kvm_extensions, is_system_register, VcpuKvmState};
#[cfg(target_arch = "aarch64")]
use crate::arch::aarch64::gic::{Vgic, VgicConfig};
#[cfg(target_arch = "riscv64")]
use crate::arch::riscv64::aia::{Vaia, VaiaConfig};
#[cfg(target_arch = "aarch64")]
use crate::arm64_core_reg_id;
#[cfg(target_arch = "riscv64")]
use crate::riscv64::aia::KvmAiaImsics;
#[cfg(target_arch = "riscv64")]
@@ -43,12 +45,10 @@ pub use crate::riscv64::{
aia::AiaImsicsState as AiaState, check_required_kvm_extensions, is_non_core_register,
VcpuKvmState,
};
use crate::vm::{self, InterruptSourceConfig, VmOps};
#[cfg(target_arch = "aarch64")]
use crate::{arm64_core_reg_id, offset_of};
use crate::{cpu, hypervisor, vec_with_array_field, HypervisorType};
#[cfg(target_arch = "riscv64")]
use crate::{offset_of, riscv64_reg_id};
use crate::riscv64_reg_id;
use crate::vm::{self, InterruptSourceConfig, VmOps};
use crate::{cpu, hypervisor, HypervisorType};
// x86_64 dependencies
#[cfg(target_arch = "x86_64")]
pub mod x86_64;
@@ -95,22 +95,24 @@ pub use kvm_bindings::{
};
#[cfg(target_arch = "aarch64")]
use kvm_bindings::{
kvm_regs, user_fpsimd_state, user_pt_regs, KVM_GUESTDBG_USE_HW, KVM_NR_SPSR, KVM_REG_ARM64,
KVM_REG_ARM64_SYSREG, KVM_REG_ARM64_SYSREG_CRM_MASK, KVM_REG_ARM64_SYSREG_CRN_MASK,
KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP1_MASK, KVM_REG_ARM64_SYSREG_OP2_MASK,
KVM_REG_ARM_CORE, KVM_REG_SIZE_U128, KVM_REG_SIZE_U32, KVM_REG_SIZE_U64,
kvm_regs, user_pt_regs, KVM_GUESTDBG_USE_HW, KVM_NR_SPSR, KVM_REG_ARM64, KVM_REG_ARM64_SYSREG,
KVM_REG_ARM64_SYSREG_CRM_MASK, KVM_REG_ARM64_SYSREG_CRN_MASK, KVM_REG_ARM64_SYSREG_OP0_MASK,
KVM_REG_ARM64_SYSREG_OP1_MASK, KVM_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM_CORE,
KVM_REG_SIZE_U128, KVM_REG_SIZE_U32, KVM_REG_SIZE_U64,
};
#[cfg(target_arch = "riscv64")]
use kvm_bindings::{kvm_riscv_core, user_regs_struct, KVM_REG_RISCV_CORE};
use kvm_bindings::{kvm_riscv_core, KVM_REG_RISCV_CORE};
#[cfg(feature = "tdx")]
use kvm_bindings::{kvm_run__bindgen_ty_1, KVMIO};
pub use kvm_ioctls::{Cap, Kvm, VcpuExit};
use thiserror::Error;
use vfio_ioctls::VfioDeviceFd;
#[cfg(feature = "tdx")]
use vmm_sys_util::{ioctl::ioctl_with_val, ioctl_ioc_nr, ioctl_iowr_nr};
use vmm_sys_util::{ioctl::ioctl_with_val, ioctl_iowr_nr};
pub use {kvm_bindings, kvm_ioctls};
#[cfg(target_arch = "aarch64")]
use crate::arch::aarch64::regs;
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
use crate::RegList;
@@ -119,8 +121,6 @@ const KVM_CAP_SGX_ATTRIBUTE: u32 = 196;
#[cfg(target_arch = "x86_64")]
use vmm_sys_util::ioctl_io_nr;
#[cfg(all(not(feature = "tdx"), target_arch = "x86_64"))]
use vmm_sys_util::ioctl_ioc_nr;
#[cfg(target_arch = "x86_64")]
ioctl_io_nr!(KVM_NMI, kvm_bindings::KVMIO, 0x9a);
@@ -693,10 +693,6 @@ impl vm::Vm for KvmVm {
/// entries, as per the `KVM_SET_GSI_ROUTING` ioctl.
///
fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> vm::Result<()> {
let mut irq_routing =
vec_with_array_field::<kvm_irq_routing, kvm_irq_routing_entry>(entries.len());
irq_routing[0].nr = entries.len() as u32;
irq_routing[0].flags = 0;
let entries: Vec<kvm_irq_routing_entry> = entries
.iter()
.map(|entry| match entry {
@@ -706,17 +702,11 @@ impl vm::Vm for KvmVm {
})
.collect();
// SAFETY: irq_routing initialized with entries.len() and now it is being turned into
// entries_slice with entries.len() again. It is guaranteed to be large enough to hold
// everything from entries.
unsafe {
let entries_slice: &mut [kvm_irq_routing_entry] =
irq_routing[0].entries.as_mut_slice(entries.len());
entries_slice.copy_from_slice(&entries);
}
let irq_routing =
kvm_bindings::fam_wrappers::KvmIrqRouting::from_entries(&entries).unwrap();
self.fd
.set_gsi_routing(&irq_routing[0])
.set_gsi_routing(&irq_routing)
.map_err(|e| vm::HypervisorVmError::SetGsiRouting(e.into()))
}
@@ -1450,7 +1440,7 @@ impl cpu::Vcpu for KvmVcpu {
// Now moving on to floating point registers which are stored in the user_fpsimd_state in the kernel:
// https://elixir.free-electrons.com/linux/v4.9.62/source/arch/arm64/include/uapi/asm/kvm.h#L53
let mut off = offset_of!(kvm_regs, fp_regs) + offset_of!(user_fpsimd_state, vregs);
let mut off = offset_of!(kvm_regs, fp_regs.vregs);
for i in 0..32 {
let mut bytes = [0_u8; 16];
self.fd
@@ -1463,7 +1453,7 @@ impl cpu::Vcpu for KvmVcpu {
}
// Floating-point Status Register
let off = offset_of!(kvm_regs, fp_regs) + offset_of!(user_fpsimd_state, fpsr);
let off = offset_of!(kvm_regs, fp_regs.fpsr);
let mut bytes = [0_u8; 4];
self.fd
.lock()
@@ -1473,7 +1463,7 @@ impl cpu::Vcpu for KvmVcpu {
state.fp_regs.fpsr = u32::from_le_bytes(bytes);
// Floating-point Control Register
let off = offset_of!(kvm_regs, fp_regs) + offset_of!(user_fpsimd_state, fpcr);
let off = offset_of!(kvm_regs, fp_regs.fpcr);
let mut bytes = [0_u8; 4];
self.fd
.lock()
@@ -1507,7 +1497,7 @@ impl cpu::Vcpu for KvmVcpu {
state.mode = u64::from_le_bytes(bytes);
};
($reg_name:ident) => {
let off = offset_of!(kvm_riscv_core, regs, user_regs_struct, $reg_name);
let off = offset_of!(kvm_riscv_core, regs.$reg_name);
let mut bytes = [0_u8; 8];
self.fd
.lock()
@@ -1654,7 +1644,7 @@ impl cpu::Vcpu for KvmVcpu {
off += std::mem::size_of::<u64>();
}
let mut off = offset_of!(kvm_regs, fp_regs) + offset_of!(user_fpsimd_state, vregs);
let mut off = offset_of!(kvm_regs, fp_regs.vregs);
for i in 0..32 {
self.fd
.lock()
@@ -1667,7 +1657,7 @@ impl cpu::Vcpu for KvmVcpu {
off += mem::size_of::<u128>();
}
let off = offset_of!(kvm_regs, fp_regs) + offset_of!(user_fpsimd_state, fpsr);
let off = offset_of!(kvm_regs, fp_regs.fpsr);
self.fd
.lock()
.unwrap()
@@ -1677,7 +1667,7 @@ impl cpu::Vcpu for KvmVcpu {
)
.map_err(|e| cpu::HypervisorCpuError::SetAarchCoreRegister(e.into()))?;
let off = offset_of!(kvm_regs, fp_regs) + offset_of!(user_fpsimd_state, fpcr);
let off = offset_of!(kvm_regs, fp_regs.fpcr);
self.fd
.lock()
.unwrap()
@@ -1715,7 +1705,7 @@ impl cpu::Vcpu for KvmVcpu {
.map_err(|e| cpu::HypervisorCpuError::SetRiscvCoreRegister(e.into()))?;
};
($reg_name:ident) => {
let off = offset_of!(kvm_riscv_core, regs, user_regs_struct, $reg_name);
let off = offset_of!(kvm_riscv_core, regs.$reg_name);
self.fd
.lock()
.unwrap()
@@ -2291,35 +2281,21 @@ impl cpu::Vcpu for KvmVcpu {
///
#[cfg(target_arch = "aarch64")]
fn setup_regs(&self, cpu_id: u8, boot_ip: u64, fdt_start: u64) -> cpu::Result<()> {
#[allow(non_upper_case_globals)]
// PSR (Processor State Register) bits.
// Taken from arch/arm64/include/uapi/asm/ptrace.h.
const PSR_MODE_EL1h: u64 = 0x0000_0005;
const PSR_F_BIT: u64 = 0x0000_0040;
const PSR_I_BIT: u64 = 0x0000_0080;
const PSR_A_BIT: u64 = 0x0000_0100;
const PSR_D_BIT: u64 = 0x0000_0200;
// Taken from arch/arm64/kvm/inject_fault.c.
const PSTATE_FAULT_BITS_64: u64 =
PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT;
let kreg_off = offset_of!(kvm_regs, regs);
// Get the register index of the PSTATE (Processor State) register.
let pstate = offset_of!(user_pt_regs, pstate) + kreg_off;
let pstate = offset_of!(kvm_regs, regs.pstate);
self.fd
.lock()
.unwrap()
.set_one_reg(
arm64_core_reg_id!(KVM_REG_SIZE_U64, pstate),
&PSTATE_FAULT_BITS_64.to_le_bytes(),
&regs::PSTATE_FAULT_BITS_64.to_le_bytes(),
)
.map_err(|e| cpu::HypervisorCpuError::SetAarchCoreRegister(e.into()))?;
// Other vCPUs are powered off initially awaiting PSCI wakeup.
if cpu_id == 0 {
// Setting the PC (Processor Counter) to the current program address (kernel address).
let pc = offset_of!(user_pt_regs, pc) + kreg_off;
let pc = offset_of!(kvm_regs, regs.pc);
self.fd
.lock()
.unwrap()
@@ -2333,7 +2309,7 @@ impl cpu::Vcpu for KvmVcpu {
// "The device tree blob (dtb) must be placed on an 8-byte boundary and must
// not exceed 2 megabytes in size." -> https://www.kernel.org/doc/Documentation/arm64/booting.txt.
// We are choosing to place it the end of DRAM. See `get_fdt_addr`.
let regs0 = offset_of!(user_pt_regs, regs) + kreg_off;
let regs0 = offset_of!(kvm_regs, regs.regs);
self.fd
.lock()
.unwrap()
@@ -2352,7 +2328,7 @@ impl cpu::Vcpu for KvmVcpu {
///
fn setup_regs(&self, cpu_id: u8, boot_ip: u64, fdt_start: u64) -> cpu::Result<()> {
// Setting the A0 () to the hartid of this CPU.
let a0 = offset_of!(kvm_riscv_core, regs, user_regs_struct, a0);
let a0 = offset_of!(kvm_riscv_core, regs.a0);
self.fd
.lock()
.unwrap()
@@ -2363,7 +2339,7 @@ impl cpu::Vcpu for KvmVcpu {
.map_err(|e| cpu::HypervisorCpuError::SetRiscvCoreRegister(e.into()))?;
// Setting the PC (Processor Counter) to the current program address (kernel address).
let pc = offset_of!(kvm_riscv_core, regs, user_regs_struct, pc);
let pc = offset_of!(kvm_riscv_core, regs.pc);
self.fd
.lock()
.unwrap()
@@ -2376,7 +2352,7 @@ impl cpu::Vcpu for KvmVcpu {
// Last mandatory thing to set -> the address pointing to the FDT (also called DTB).
// "The device tree blob (dtb) must be placed on an 8-byte boundary and must
// not exceed 64 kilobytes in size." -> https://www.kernel.org/doc/Documentation/arch/riscv/boot.txt.
let a1 = offset_of!(kvm_riscv_core, regs, user_regs_struct, a1);
let a1 = offset_of!(kvm_riscv_core, regs.a1);
self.fd
.lock()
.unwrap()
@@ -2968,11 +2944,15 @@ impl KvmVcpu {
///
fn set_xsave(&self, xsave: &XsaveState) -> cpu::Result<()> {
let xsave: kvm_bindings::kvm_xsave = (*xsave).clone().into();
self.fd
.lock()
.unwrap()
.set_xsave(&xsave)
.map_err(|e| cpu::HypervisorCpuError::SetXsaveState(e.into()))
// SAFETY: Here we trust the kernel not to read past the end of the kvm_xsave struct
// when calling the kvm-ioctl library function.
unsafe {
self.fd
.lock()
.unwrap()
.set_xsave(&xsave)
.map_err(|e| cpu::HypervisorCpuError::SetXsaveState(e.into()))
}
}
#[cfg(target_arch = "x86_64")]
@@ -3039,7 +3019,7 @@ mod tests {
let vcpu0 = vm.create_vcpu(0, None).unwrap();
let core_regs = StandardRegisters::from(kvm_riscv_core {
regs: user_regs_struct {
regs: kvm_bindings::user_regs_struct {
pc: 0x00,
ra: 0x01,
sp: 0x02,

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

@@ -13,41 +13,6 @@ use serde::{Deserialize, Serialize};
use crate::kvm::{KvmError, KvmResult};
// This macro gets the offset of a structure (i.e `str`) member (i.e `field`) without having
// an instance of that structure.
#[macro_export]
macro_rules! _offset_of {
($str:ty, $field:ident) => {{
let tmp: std::mem::MaybeUninit<$str> = std::mem::MaybeUninit::uninit();
let base = tmp.as_ptr();
// Avoid warnings when nesting `unsafe` blocks.
#[allow(unused_unsafe)]
// SAFETY: The pointer is valid and aligned, just not initialised. Using `addr_of` ensures
// that we don't actually read from `base` (which would be UB) nor create an intermediate
// reference.
let member = unsafe { core::ptr::addr_of!((*base).$field) } as *const u8;
// Avoid warnings when nesting `unsafe` blocks.
#[allow(unused_unsafe)]
// SAFETY: The two pointers are within the same allocated object `tmp`. All requirements
// from offset_from are upheld.
unsafe {
member.offset_from(base as *const u8) as usize
}
}};
}
#[macro_export]
macro_rules! offset_of {
($reg_struct:ty, $field:ident) => {
$crate::_offset_of!($reg_struct, $field)
};
($outer_reg_struct:ty, $outer_field:ident, $($inner_reg_struct:ty, $inner_field:ident), +) => {
$crate::_offset_of!($outer_reg_struct, $outer_field) + offset_of!($($inner_reg_struct, $inner_field), +)
};
}
// Following are macros that help with getting the ID of a riscv64 register, including config registers, core registers and timer registers.
// The register of core registers are wrapped in the `user_regs_struct` structure. See:
// https://elixir.bootlin.com/linux/v6.10/source/arch/riscv/include/uapi/asm/kvm.h#L62

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 */
}

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

@@ -2,6 +2,8 @@
//
// Copyright © 2025, Microsoft Corporation
//
pub mod emulator;
pub mod gic;
use std::fmt;
///

View File

@@ -13,18 +13,23 @@ use std::sync::{Arc, RwLock};
use arc_swap::ArcSwap;
use mshv_bindings::*;
#[cfg(target_arch = "x86_64")]
use mshv_ioctls::{set_registers_64, InterruptRequest};
use mshv_ioctls::{Mshv, NoDatamatch, VcpuFd, VmFd, VmType};
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 = "x86_64")]
#[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};
@@ -42,6 +47,8 @@ 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")]
@@ -63,6 +70,8 @@ pub use {
#[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")]
@@ -257,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()))
@@ -290,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;
}
@@ -501,13 +461,13 @@ impl hypervisor::Hypervisor for MshvHypervisor {
///
fn get_host_ipa_limit(&self) -> i32 {
let host_ipa = self.mshv.get_host_partition_property(
hv_partition_property_code_HV_PARTITION_PROPERTY_PHYSICAL_ADDRESS_WIDTH as u64,
hv_partition_property_code_HV_PARTITION_PROPERTY_PHYSICAL_ADDRESS_WIDTH,
);
match host_ipa {
Ok(ipa) => ipa,
Ok(ipa) => ipa.try_into().unwrap(),
Err(e) => {
panic!("Failed to get host IPA limit: {:?}", e);
panic!("Failed to get host IPA limit: {e:?}");
}
}
}
@@ -528,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,
@@ -559,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.
///
@@ -575,7 +534,6 @@ impl cpu::Vcpu for MshvVcpu {
.into())
}
#[cfg(target_arch = "x86_64")]
///
/// Sets the vCPU general purpose registers.
///
@@ -716,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);
}
_ => {}
@@ -765,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")]
@@ -1097,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}"
);
}
}
@@ -1258,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)
@@ -1286,23 +1252,51 @@ 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> {
unimplemented!()
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")]
@@ -1312,27 +1306,17 @@ impl cpu::Vcpu for MshvVcpu {
#[cfg(target_arch = "aarch64")]
fn vcpu_init(&self, _kvi: &crate::VcpuInit) -> cpu::Result<()> {
unimplemented!()
}
#[cfg(target_arch = "aarch64")]
fn set_regs(&self, _regs: &crate::StandardRegisters) -> cpu::Result<()> {
unimplemented!()
}
#[cfg(target_arch = "aarch64")]
fn get_regs(&self) -> cpu::Result<crate::StandardRegisters> {
unimplemented!()
Ok(())
}
#[cfg(target_arch = "aarch64")]
fn vcpu_finalize(&self, _feature: i32) -> cpu::Result<()> {
unimplemented!()
Ok(())
}
#[cfg(target_arch = "aarch64")]
fn vcpu_get_finalized_features(&self) -> i32 {
unimplemented!()
0
}
#[cfg(target_arch = "aarch64")]
@@ -1342,12 +1326,12 @@ impl cpu::Vcpu for MshvVcpu {
_kvi: &mut crate::VcpuInit,
_id: u8,
) -> cpu::Result<()> {
unimplemented!()
Ok(())
}
#[cfg(target_arch = "aarch64")]
fn create_vcpu_init(&self) -> crate::VcpuInit {
unimplemented!();
MshvVcpuInit {}.into()
}
#[cfg(target_arch = "x86_64")]
@@ -1457,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!()
}
@@ -1574,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 {
@@ -1691,6 +1693,51 @@ 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 raw pointer to reg page, 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;
(*vp_reg_page).dirty |= 1 << HV_X64_REGISTER_CLASS_GENERAL;
}
} 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.
@@ -1813,7 +1860,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);
@@ -1982,9 +2029,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(),
}
}
@@ -2184,12 +2242,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 crate::VcpuInit) -> vm::Result<()> {
unimplemented!()
Ok(())
}
/// Pause the VM
@@ -2289,4 +2371,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

@@ -58,196 +58,196 @@ pub enum HypervisorVmError {
///
/// Create Vcpu error
///
#[error("Failed to create Vcpu: {0}")]
#[error("Failed to create Vcpu")]
CreateVcpu(#[source] anyhow::Error),
///
/// Identity map address error
///
#[error("Failed to set identity map address: {0}")]
#[error("Failed to set identity map address")]
SetIdentityMapAddress(#[source] anyhow::Error),
///
/// TSS address error
///
#[error("Failed to set TSS address: {0}")]
#[error("Failed to set TSS address")]
SetTssAddress(#[source] anyhow::Error),
///
/// Create interrupt controller error
///
#[error("Failed to create interrupt controller: {0}")]
#[error("Failed to create interrupt controller")]
CreateIrq(#[source] anyhow::Error),
///
/// Register interrupt event error
///
#[error("Failed to register interrupt event: {0}")]
#[error("Failed to register interrupt event")]
RegisterIrqFd(#[source] anyhow::Error),
///
/// Un register interrupt event error
///
#[error("Failed to unregister interrupt event: {0}")]
#[error("Failed to unregister interrupt event")]
UnregisterIrqFd(#[source] anyhow::Error),
///
/// Register IO event error
///
#[error("Failed to register IO event: {0}")]
#[error("Failed to register IO event")]
RegisterIoEvent(#[source] anyhow::Error),
///
/// Unregister IO event error
///
#[error("Failed to unregister IO event: {0}")]
#[error("Failed to unregister IO event")]
UnregisterIoEvent(#[source] anyhow::Error),
///
/// Set GSI routing error
///
#[error("Failed to set GSI routing: {0}")]
#[error("Failed to set GSI routing")]
SetGsiRouting(#[source] anyhow::Error),
///
/// Create user memory error
///
#[error("Failed to create user memory: {0}")]
#[error("Failed to create user memory")]
CreateUserMemory(#[source] anyhow::Error),
///
/// Remove user memory region error
///
#[error("Failed to remove user memory: {0}")]
#[error("Failed to remove user memory")]
RemoveUserMemory(#[source] anyhow::Error),
///
/// Create device error
///
#[error("Failed to set GSI routing: {0}")]
#[error("Failed to set GSI routing")]
CreateDevice(#[source] anyhow::Error),
///
/// Get preferred target error
///
#[error("Failed to get preferred target: {0}")]
#[error("Failed to get preferred target")]
GetPreferredTarget(#[source] anyhow::Error),
///
/// Enable split Irq error
///
#[error("Failed to enable split Irq: {0}")]
#[error("Failed to enable split Irq")]
EnableSplitIrq(#[source] anyhow::Error),
///
/// Enable SGX attribute error
///
#[error("Failed to enable SGX attribute: {0}")]
#[error("Failed to enable SGX attribute")]
EnableSgxAttribute(#[source] anyhow::Error),
///
/// Get clock error
///
#[error("Failed to get clock: {0}")]
#[error("Failed to get clock")]
GetClock(#[source] anyhow::Error),
///
/// Set clock error
///
#[error("Failed to set clock: {0}")]
#[error("Failed to set clock")]
SetClock(#[source] anyhow::Error),
///
/// Create passthrough device
///
#[error("Failed to create passthrough device: {0}")]
#[error("Failed to create passthrough device")]
CreatePassthroughDevice(#[source] anyhow::Error),
/// Write to Guest memory
///
#[error("Failed to write to guest memory: {0}")]
#[error("Failed to write to guest memory")]
GuestMemWrite(#[source] anyhow::Error),
///
/// Read Guest memory
///
#[error("Failed to read guest memory: {0}")]
#[error("Failed to read guest memory")]
GuestMemRead(#[source] anyhow::Error),
///
/// Read from MMIO Bus
///
#[error("Failed to read from MMIO Bus: {0}")]
#[error("Failed to read from MMIO Bus")]
MmioBusRead(#[source] anyhow::Error),
///
/// Write to MMIO Bus
///
#[error("Failed to write to MMIO Bus: {0}")]
#[error("Failed to write to MMIO Bus")]
MmioBusWrite(#[source] anyhow::Error),
///
/// Read from IO Bus
///
#[error("Failed to read from IO Bus: {0}")]
#[error("Failed to read from IO Bus")]
IoBusRead(#[source] anyhow::Error),
///
/// Write to IO Bus
///
#[error("Failed to write to IO Bus: {0}")]
#[error("Failed to write to IO Bus")]
IoBusWrite(#[source] anyhow::Error),
///
/// Start dirty log error
///
#[error("Failed to get dirty log: {0}")]
#[error("Failed to get dirty log")]
StartDirtyLog(#[source] anyhow::Error),
///
/// Stop dirty log error
///
#[error("Failed to get dirty log: {0}")]
#[error("Failed to get dirty log")]
StopDirtyLog(#[source] anyhow::Error),
///
/// Get dirty log error
///
#[error("Failed to get dirty log: {0}")]
#[error("Failed to get dirty log")]
GetDirtyLog(#[source] anyhow::Error),
///
/// Assert virtual interrupt error
///
#[error("Failed to assert virtual Interrupt: {0}")]
#[error("Failed to assert virtual Interrupt")]
AssertVirtualInterrupt(#[source] anyhow::Error),
#[cfg(feature = "sev_snp")]
///
/// Error initializing SEV-SNP on the VM
///
#[error("Failed to initialize SEV-SNP: {0}")]
#[error("Failed to initialize SEV-SNP")]
InitializeSevSnp(#[source] std::io::Error),
#[cfg(feature = "tdx")]
///
/// Error initializing TDX on the VM
///
#[error("Failed to initialize TDX: {0}")]
#[error("Failed to initialize TDX")]
InitializeTdx(#[source] std::io::Error),
#[cfg(feature = "tdx")]
///
/// Error finalizing the TDX configuration on the VM
///
#[error("Failed to finalize TDX: {0}")]
#[error("Failed to finalize TDX")]
FinalizeTdx(#[source] std::io::Error),
#[cfg(feature = "tdx")]
///
/// Error initializing the TDX memory region
///
#[error("Failed to initialize memory region TDX: {0}")]
#[error("Failed to initialize memory region TDX")]
InitMemRegionTdx(#[source] std::io::Error),
///
/// Create Vgic error
///
#[error("Failed to create Vgic: {0}")]
#[error("Failed to create Vgic")]
CreateVgic(#[source] anyhow::Error),
///
/// Create Vaia error
///
#[error("Failed to create Vaia: {0}")]
#[error("Failed to create Vaia")]
CreateVaia(#[source] anyhow::Error),
///
/// Import isolated pages error
///
#[error("Failed to import isolated pages: {0}")]
#[error("Failed to import isolated pages")]
ImportIsolatedPages(#[source] anyhow::Error),
/// Failed to complete isolated import
///
#[error("Failed to complete isolated import: {0}")]
#[error("Failed to complete isolated import")]
CompleteIsolatedImport(#[source] anyhow::Error),
/// Failed to set VM property
///
#[error("Failed to set VM property: {0}")]
#[error("Failed to set VM property")]
SetVmProperty(#[source] anyhow::Error),
///
/// Modify GPA host access error
///
#[cfg(feature = "sev_snp")]
#[error("Failed to modify GPA host access: {0}")]
#[error("Failed to modify GPA host access")]
ModifyGpaHostAccess(#[source] anyhow::Error),
///
/// Failed to mmap
@@ -255,6 +255,11 @@ pub enum HypervisorVmError {
#[cfg(feature = "sev_snp")]
#[error("Failed to mmap:")]
MmapToRoot,
///
/// Failed to initialize VM
///
#[error("Failed to initialize VM")]
InitializeVm(#[source] anyhow::Error),
}
///
/// Result type for returning from a function
@@ -417,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,13 +6,13 @@ version = "0.1.0"
[dependencies]
epoll = "4.3.3"
getrandom = "0.3.1"
getrandom = "0.3.3"
libc = "0.2.167"
log = "0.4.22"
net_gen = { path = "../net_gen" }
rate_limiter = { path = "../rate_limiter" }
serde = { version = "1.0.208", features = ["derive"] }
thiserror = "2.0.6"
thiserror = { workspace = true }
virtio-bindings = { workspace = true }
virtio-queue = { workspace = true }
vm-memory = { workspace = true, features = [
@@ -24,7 +24,6 @@ vm-virtio = { path = "../vm-virtio" }
vmm-sys-util = { workspace = true }
[dev-dependencies]
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

@@ -4,36 +4,42 @@
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,
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_ERR, VIRTIO_NET_F_GUEST_CSUM,
VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6,
VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_OK,
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_ERR, VIRTIO_NET_OK,
};
use virtio_queue::{Queue, QueueT};
use vm_memory::{ByteValued, Bytes, GuestMemoryError};
use vm_virtio::{AccessPlatform, Translatable};
use super::virtio_features_to_tap_offload;
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>;
@@ -155,24 +161,3 @@ impl CtrlQueue {
Ok(())
}
}
pub fn virtio_features_to_tap_offload(features: u64) -> c_uint {
let mut tap_offloads: c_uint = 0;
if features & (1 << VIRTIO_NET_F_GUEST_CSUM) != 0 {
tap_offloads |= net_gen::TUN_F_CSUM;
}
if features & (1 << VIRTIO_NET_F_GUEST_TSO4) != 0 {
tap_offloads |= net_gen::TUN_F_TSO4;
}
if features & (1 << VIRTIO_NET_F_GUEST_TSO6) != 0 {
tap_offloads |= net_gen::TUN_F_TSO6;
}
if features & (1 << VIRTIO_NET_F_GUEST_ECN) != 0 {
tap_offloads |= net_gen::TUN_F_TSO_ECN;
}
if features & (1 << VIRTIO_NET_F_GUEST_UFO) != 0 {
tap_offloads |= net_gen::TUN_F_UFO;
}
tap_offloads
}

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};
@@ -39,8 +40,8 @@ pub use tap::{Error as TapError, Tap};
#[derive(Error, Debug)]
pub enum Error {
#[error("Failed to create a socket: {0}")]
CreateSocket(IoError),
#[error("Failed to create a socket")]
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))
}

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};
@@ -12,30 +12,28 @@ 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),
#[error("Failed to convert an hexadecimal string into an integer")]
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),
#[error("Open tap device failed: {0}")]
TapOpen(TapError),
#[error("Setting tap IP failed: {0}")]
TapSetIp(TapError),
#[error("Setting tap netmask failed: {0}")]
TapSetNetmask(TapError),
#[error("Setting MAC address failed: {0}")]
TapSetMac(TapError),
#[error("Getting MAC address failed: {0}")]
TapGetMac(TapError),
#[error("Setting vnet header size failed: {0}")]
TapSetVnetHdrSize(TapError),
#[error("Setting MTU failed: {0}")]
TapSetMtu(TapError),
#[error("Enabling tap interface failed: {0}")]
TapEnable(TapError),
#[error("Failed to read the TAP flags from sysfs")]
ReadSysfsTunFlags(#[source] io::Error),
#[error("Open tap device failed")]
TapOpen(#[source] TapError),
#[error("Setting tap IP and/or netmask failed")]
TapSetIpNetmask(#[source] TapError),
#[error("Setting MAC address failed")]
TapSetMac(#[source] TapError),
#[error("Getting MAC address failed")]
TapGetMac(#[source] TapError),
#[error("Setting vnet header size failed")]
TapSetVnetHdrSize(#[source] TapError),
#[error("Setting MTU failed")]
TapSetMtu(#[source] TapError),
#[error("Enabling tap interface failed")]
TapEnable(#[source] TapError),
}
type Result<T> = std::result::Result<T, Error>;
@@ -60,12 +58,60 @@ fn check_mq_support(if_name: &Option<&str>, queue_pairs: usize) -> Result<()> {
Ok(())
}
/// Opens a Tap device and configures it.
///
/// Afterward, further RX queues can be opened with a common config.
fn open_tap_rx_q_0(
if_name: Option<&str>,
ip_addr: Option<IpAddr>,
netmask: Option<IpAddr>,
host_mac: &mut Option<MacAddr>,
mtu: Option<u16>,
num_rx_q: usize,
flags: Option<i32>,
) -> Result<Tap> {
// Check if the given interface exists before we create it.
let tap_exists = if_name.is_some_and(|n| Path::new(&format!("/sys/class/net/{n}")).exists());
let tap = match if_name {
Some(name) => Tap::open_named(name, num_rx_q, flags).map_err(Error::TapOpen)?,
// Create a new Tap device in Linux, if none was specified.
None => Tap::new(num_rx_q).map_err(Error::TapOpen)?,
};
// Don't overwrite ip configuration of existing interfaces:
if !tap_exists {
if let Some(ip) = ip_addr {
tap.set_ip_addr(ip, netmask)
.map_err(Error::TapSetIpNetmask)?;
}
} else {
warn!(
"Tap {} already exists. IP configuration will not be overwritten.",
if_name.unwrap_or_default()
);
}
if let Some(mac) = host_mac {
tap.set_mac_addr(*mac).map_err(Error::TapSetMac)?
} else {
*host_mac = Some(tap.get_mac_addr().map_err(Error::TapGetMac)?)
}
if let Some(mtu) = mtu {
tap.set_mtu(mtu as i32).map_err(Error::TapSetMtu)?;
}
tap.enable().map_err(Error::TapEnable)?;
tap.set_vnet_hdr_size(vnet_hdr_len() as i32)
.map_err(Error::TapSetVnetHdrSize)?;
Ok(tap)
}
/// Create a new virtio network device with the given IP address and
/// 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,
@@ -73,9 +119,6 @@ pub fn open_tap(
) -> Result<Vec<Tap>> {
let mut taps: Vec<Tap> = Vec::new();
let mut ifname: String = String::new();
let vnet_hdr_size = vnet_hdr_len() as i32;
// Check if the given interface exists before we create it.
let tap_existed = if_name.is_some_and(|n| Path::new(&format!("/sys/class/net/{n}")).exists());
// In case the tap interface already exists, check if the number of
// queues is appropriate. The tap might not support multiqueue while
@@ -87,42 +130,16 @@ pub fn open_tap(
for i in 0..num_rx_q {
let tap: Tap;
if i == 0 {
tap = match if_name {
Some(name) => Tap::open_named(name, num_rx_q, flags).map_err(Error::TapOpen)?,
None => Tap::new(num_rx_q).map_err(Error::TapOpen)?,
};
// 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)?;
}
} else {
warn!(
"Tap {} already exists. IP configuration will not be overwritten.",
if_name.unwrap_or_default()
);
}
if let Some(mac) = host_mac {
tap.set_mac_addr(*mac).map_err(Error::TapSetMac)?
} else {
*host_mac = Some(tap.get_mac_addr().map_err(Error::TapGetMac)?)
}
if let Some(mtu) = mtu {
tap.set_mtu(mtu as i32).map_err(Error::TapSetMtu)?;
}
tap.enable().map_err(Error::TapEnable)?;
tap.set_vnet_hdr_size(vnet_hdr_size)
.map_err(Error::TapSetVnetHdrSize)?;
// Special handling is required for the first RX queue, such as
// configuring the device. Subsequent iterations will then use the
// same device.
tap = open_tap_rx_q_0(if_name, ip_addr, netmask, host_mac, mtu, num_rx_q, flags)?;
// Set the name of the tap device we open in subsequent iterations.
ifname = String::from_utf8(tap.get_if_name()).unwrap();
} else {
tap = Tap::open_named(ifname.as_str(), num_rx_q, flags).map_err(Error::TapOpen)?;
tap.set_vnet_hdr_size(vnet_hdr_size)
tap.set_vnet_hdr_size(vnet_hdr_len() as i32)
.map_err(Error::TapSetVnetHdrSize)?;
}
taps.push(tap);

View File

@@ -353,28 +353,28 @@ pub struct NetCounters {
pub enum NetQueuePairError {
#[error("No memory configured")]
NoMemoryConfigured,
#[error("Error registering listener: {0}")]
RegisterListener(io::Error),
#[error("Error unregistering listener: {0}")]
UnregisterListener(io::Error),
#[error("Error writing to the TAP device: {0}")]
WriteTap(io::Error),
#[error("Error reading from the TAP device: {0}")]
ReadTap(io::Error),
#[error("Error related to guest memory: {0}")]
GuestMemory(vm_memory::GuestMemoryError),
#[error("Returned an error while iterating through the queue: {0}")]
QueueIteratorFailed(virtio_queue::Error),
#[error("Error registering listener")]
RegisterListener(#[source] io::Error),
#[error("Error unregistering listener")]
UnregisterListener(#[source] io::Error),
#[error("Error writing to the TAP device")]
WriteTap(#[source] io::Error),
#[error("Error reading from the TAP device")]
ReadTap(#[source] io::Error),
#[error("Error related to guest memory")]
GuestMemory(#[source] vm_memory::GuestMemoryError),
#[error("Returned an error while iterating through the queue")]
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),
#[error("Failed to enable notification on the queue: {0}")]
QueueEnableNotification(virtio_queue::Error),
#[error("Failed to add used index to the queue: {0}")]
QueueAddUsed(virtio_queue::Error),
#[error("Failed to determine if queue needed notification")]
QueueNeedsNotification(#[source] virtio_queue::Error),
#[error("Failed to enable notification on the queue")]
QueueEnableNotification(#[source] virtio_queue::Error),
#[error("Failed to add used index to the queue")]
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};
@@ -20,24 +20,35 @@ use super::{
};
use crate::mac::MAC_ADDR_LEN;
/// Maximum length of a network interface name in Linux, excluding any NUL byte.
///
/// This corresponds to `IFNAMSIZ` in Linux [[0]].
///
/// [0]: https://elixir.bootlin.com/linux/v6.12/source/include/uapi/linux/if.h#L33
const MAX_INTERFACE_NAME_LEN: usize = 15;
#[derive(Error, Debug)]
pub enum Error {
#[error("Couldn't open /dev/net/tun: {0}")]
OpenTun(IoError),
#[error("Unable to configure tap interface: {0}")]
ConfigureTap(IoError),
#[error("Unable to retrieve features: {0}")]
GetFeatures(IoError),
#[error("Couldn't open /dev/net/tun")]
OpenTun(#[source] IoError),
#[error("Unable to configure tap interface")]
ConfigureTap(#[source] IoError),
#[error("Unable to retrieve features")]
GetFeatures(#[source] IoError),
#[error("Missing multiqueue support in the kernel")]
MultiQueueKernelSupport,
#[error("ioctl ({0}) failed: {1}")]
IoctlError(c_ulong, IoError),
#[error("Failed to create a socket: {0}")]
NetUtil(NetUtilError),
#[error("Invalid interface name")]
InvalidIfname,
#[error("Error parsing MAC data: {0}")]
MacParsing(IoError),
IoctlError(c_ulong, #[source] IoError),
#[error("Failed to create a socket")]
NetUtil(#[source] NetUtilError),
#[error("Interface name too long (max length is {MAX_INTERFACE_NAME_LEN}): {0}")]
IfnameTooLong(String),
#[error("Invalid interface name (does it exist?): {0}")]
InvalidIfname(String),
#[error("Error parsing MAC data")]
MacParsing(#[source] IoError),
#[error("Invalid netmask")]
InvalidNetmask,
}
pub type Result<T> = ::std::result::Result<T, Error>;
@@ -74,20 +85,49 @@ impl std::clone::Clone for Tap {
fn build_terminated_if_name(if_name: &str) -> Result<Vec<u8>> {
// Convert the string slice to bytes, and shadow the variable,
// since we no longer need the &str version.
let if_name = if_name.as_bytes();
let bytes = if_name.as_bytes();
// TODO: the 16usize limit of the if_name member from struct Tap is pretty arbitrary.
// We leave it as is for now, but this should be refactored at some point.
if if_name.len() > 15 {
return Err(Error::InvalidIfname);
if bytes.len() > MAX_INTERFACE_NAME_LEN {
return Err(Error::IfnameTooLong(if_name.to_string()));
}
let mut terminated_if_name = vec![b'\0'; if_name.len() + 1];
terminated_if_name[..if_name.len()].copy_from_slice(if_name);
let mut terminated_if_name = vec![b'\0'; bytes.len() + 1];
terminated_if_name[..bytes.len()].copy_from_slice(bytes);
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 +275,81 @@ 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 => {
let name = String::from_utf8_lossy(&self.if_name).to_string();
return Err(Error::InvalidIfname(name));
}
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 +399,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)?;
@@ -424,11 +516,10 @@ impl AsRawFd for Tap {
#[cfg(test)]
mod tests {
use std::net::Ipv4Addr;
use std::sync::{mpsc, Mutex};
use std::sync::{mpsc, LazyLock, Mutex};
use std::time::Duration;
use std::{str, thread};
use once_cell::sync::Lazy;
use pnet::packet::ethernet::{EtherTypes, EthernetPacket, MutableEthernetPacket};
use pnet::packet::ip::IpNextHeaderProtocols;
use pnet::packet::ipv4::{Ipv4Packet, MutableIpv4Packet};
@@ -443,13 +534,14 @@ mod tests {
static DATA_STRING: &str = "test for tap";
static SUBNET_MASK: &str = "255.255.255.0";
// We needed to have a mutex as a global variable, so we used once_cell for testing. The main
// We needed to have a mutex as a global variable, so we use a once cell for testing. The main
// potential problem, caused by tests being run in parallel by cargo, is creating different
// TAPs and trying to associate the same address, so we hide the IP address &str behind this
// mutex, more as a convention to remember to lock it at the very beginning of each function
// susceptible to this issue. Another variant is to use a different IP address per function,
// but we must remember to pick an unique one each time.
static TAP_IP_LOCK: Lazy<Mutex<&'static str>> = Lazy::new(|| Mutex::new("192.168.241.1"));
static TAP_IP_LOCK: LazyLock<Mutex<&'static str>> =
LazyLock::new(|| Mutex::new("192.168.241.1"));
// Describes the outcomes we are currently interested in when parsing a packet (we use
// an UDP packet for testing).
@@ -602,11 +694,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 +743,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 +802,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));

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