From b0a2bdc588f6e0cd610539138e10e11048fcc11e Mon Sep 17 00:00:00 2001 From: Dorinda Bassey Date: Tue, 3 Feb 2026 00:49:54 +0100 Subject: [PATCH 01/14] libkrun/vmm: Add vhost-user infrastructure and API Implement vhost-user support for connecting to external virtio device backends running in separate processes. Add vhost-user feature flag, vhost dependency, and krun_add_vhost_user_device() generalized API for adding vhost-user devices. Signed-off-by: Dorinda Bassey --- Cargo.lock | 294 ++++++++++++++++++++++++++++++++++++--- Makefile | 3 + include/libkrun.h | 70 ++++++++++ src/devices/Cargo.toml | 2 + src/libkrun/Cargo.toml | 1 + src/libkrun/src/lib.rs | 88 ++++++++++++ src/vmm/Cargo.toml | 1 + src/vmm/src/resources.rs | 21 +++ 8 files changed, 460 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 74b8420d2..253d0f910 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -268,6 +268,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + [[package]] name = "clang-sys" version = "1.8.1" @@ -309,6 +320,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -425,6 +445,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foldhash" version = "0.2.0" @@ -449,8 +475,22 @@ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", "wasip2", + "wasip3", ] [[package]] @@ -459,6 +499,15 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + [[package]] name = "hashbrown" version = "0.16.1" @@ -467,7 +516,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.2.0", ] [[package]] @@ -476,6 +525,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "imago" version = "0.2.2" @@ -492,7 +547,7 @@ dependencies = [ "rustc_version", "tokio", "tracing", - "vm-memory", + "vm-memory 0.17.1", "windows-sys", ] @@ -503,7 +558,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.16.1", + "serde", + "serde_core", ] [[package]] @@ -563,7 +620,7 @@ version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom", + "getrandom 0.3.4", "libc", ] @@ -603,7 +660,7 @@ dependencies = [ "kvm-ioctls", "libc", "tdx", - "vm-memory", + "vm-memory 0.17.1", "vmm-sys-util 0.14.0", ] @@ -657,11 +714,12 @@ dependencies = [ "lru", "nix 0.30.1", "pipewire", - "rand", + "rand 0.9.2", "thiserror 2.0.18", + "vhost", "virtio-bindings", "vm-fdt", - "vm-memory", + "vm-memory 0.17.1", "zerocopy", ] @@ -703,7 +761,7 @@ name = "krun-kernel" version = "0.1.0-1.18.0" dependencies = [ "krun-utils", - "vm-memory", + "vm-memory 0.17.1", ] [[package]] @@ -735,7 +793,7 @@ dependencies = [ name = "krun-smbios" version = "0.1.0-1.18.0" dependencies = [ - "vm-memory", + "vm-memory 0.17.1", ] [[package]] @@ -782,7 +840,7 @@ dependencies = [ "serde", "serde_json", "tdx", - "vm-memory", + "vm-memory 0.17.1", "vmm-sys-util 0.14.0", "zstd", ] @@ -808,6 +866,12 @@ dependencies = [ "vmm-sys-util 0.14.0", ] +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" version = "0.2.183" @@ -835,8 +899,8 @@ dependencies = [ "log", "nitro-enclaves 0.5.0", "once_cell", - "rand", - "vm-memory", + "rand 0.9.2", + "vm-memory 0.17.1", ] [[package]] @@ -895,7 +959,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de72cb02c55ecffcf75fe78295926f872eb6eb0a58d629c58a8c324dc26380f6" dependencies = [ - "vm-memory", + "vm-memory 0.17.1", ] [[package]] @@ -916,7 +980,7 @@ version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" dependencies = [ - "hashbrown", + "hashbrown 0.16.1", ] [[package]] @@ -968,7 +1032,7 @@ dependencies = [ "bitflags 2.11.0", "libc", "nix 0.26.4", - "rand", + "rand 0.9.2", "vsock", ] @@ -1140,6 +1204,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -1164,6 +1238,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "rand" version = "0.9.2" @@ -1171,7 +1251,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", - "rand_core", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.1", ] [[package]] @@ -1181,7 +1272,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.5", ] [[package]] @@ -1190,9 +1281,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "getrandom", + "getrandom 0.3.4", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "redox_syscall" version = "0.7.3" @@ -1341,7 +1438,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] @@ -1618,6 +1715,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "unty" version = "0.0.4" @@ -1636,7 +1739,9 @@ version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" dependencies = [ + "getrandom 0.4.2", "js-sys", + "rand 0.10.1", "wasm-bindgen", ] @@ -1652,6 +1757,19 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vhost" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4dcad85a129d97d5d4b2f3c47a4affdeedd76bdcd02094bcb5d9b76cac2d05" +dependencies = [ + "bitflags 2.11.0", + "libc", + "uuid", + "vm-memory 0.16.2", + "vmm-sys-util 0.14.0", +] + [[package]] name = "virtio-bindings" version = "0.2.7" @@ -1670,6 +1788,17 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e21282841a059bb62627ce8441c491f09603622cd5a21c43bfedc85a2952f23" +[[package]] +name = "vm-memory" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd5e56d48353c5f54ef50bd158a0452fc82f5383da840f7b8efc31695dd3b9d" +dependencies = [ + "libc", + "thiserror 1.0.69", + "winapi", +] + [[package]] name = "vm-memory" version = "0.17.1" @@ -1720,6 +1849,15 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.114" @@ -1765,6 +1903,40 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1813,6 +1985,88 @@ name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "xattr" diff --git a/Makefile b/Makefile index d7f5a981b..17ec2f2d3 100644 --- a/Makefile +++ b/Makefile @@ -48,6 +48,9 @@ endif ifeq ($(INPUT),1) FEATURE_FLAGS += --features input endif +ifeq ($(VHOST_USER),1) + FEATURE_FLAGS += --features vhost-user +endif ifeq ($(AWS_NITRO),1) VARIANT = -awsnitro FEATURE_FLAGS := --features aws-nitro,net diff --git a/include/libkrun.h b/include/libkrun.h index b8f8008a5..2608dedc2 100644 --- a/include/libkrun.h +++ b/include/libkrun.h @@ -747,6 +747,76 @@ int krun_add_input_device_fd(uint32_t ctx_id, int input_fd); */ int32_t krun_set_snd_device(uint32_t ctx_id, bool enable); +/** + * Vhost-user device types. + * These correspond to virtio device type IDs for devices. + */ +#define KRUN_VIRTIO_DEVICE_RNG 4 +#define KRUN_VIRTIO_DEVICE_SND 25 +#define KRUN_VIRTIO_DEVICE_CAN 36 + +/** + * Vhost-user RNG device default queue configuration. + * Use these when you want explicit defaults instead of auto-detection. + */ +#define KRUN_VHOST_USER_RNG_NUM_QUEUES 1 +#define KRUN_VHOST_USER_RNG_QUEUE_SIZES ((uint16_t[]){256}) + +/** + * Add a vhost-user device to the VM. + * + * This function adds a vhost-user device by connecting to an external + * backend process (e.g., vhost-device-rng, vhost-device-snd). The backend + * must be running and listening on the specified socket before starting the VM. + * + * This API is designed for devices like RNG, sound, and CAN. + * + * Arguments: + * "ctx_id" - the configuration context ID. + * "device_type" - type of vhost-user device (e.g., KRUN_VHOST_USER_DEVICE_RNG). + * "socket_path" - path to the vhost-user Unix domain socket (e.g., "/tmp/vhost-rng.sock"). + * "name" - device name for logging/debugging (e.g., "vhost-rng", "vhost-snd"). + * NULL = auto-generate from device_type ("vhost-user-4", "vhost-user-25", etc.) + * "num_queues" - number of virtqueues. + * 0 = auto-detect from backend (requires backend MQ support). + * >0 = explicit queue count. + * Or use device-specific constants like KRUN_VHOST_USER_RNG_NUM_QUEUES. + * "queue_sizes" - array of queue sizes for each queue. + * NULL = use default size (256) for all queues. + * When num_queues=0 (auto-detect): array must be 0-terminated (sentinel). + * When num_queues>0 (explicit): array must have exactly num_queues elements. + * Use device-specific constants like KRUN_VHOST_USER_RNG_QUEUE_SIZES for defaults. + * + * Examples: + * // Auto-detect queue count, use default size (256) + * krun_add_vhost_user_device(ctx, KRUN_VHOST_USER_DEVICE_RNG, "/tmp/rng.sock", NULL, 0, NULL); + * + * // Auto-detect queue count, use custom size (512) for all queues + * uint16_t custom_size[] = {512, 0}; // 0 = sentinel terminator + * krun_add_vhost_user_device(ctx, KRUN_VHOST_USER_DEVICE_RNG, "/tmp/rng.sock", NULL, 0, custom_size); + * + * // Explicit defaults using #define constants + * krun_add_vhost_user_device(ctx, KRUN_VHOST_USER_DEVICE_RNG, "/tmp/rng.sock", "vhost-rng", + * KRUN_VHOST_USER_RNG_NUM_QUEUES, + * KRUN_VHOST_USER_RNG_QUEUE_SIZES); + * + * // Explicit queue count with custom sizes + * uint16_t sizes[] = {256, 512}; + * krun_add_vhost_user_device(ctx, KRUN_VHOST_USER_DEVICE_SND, "/tmp/snd.sock", "vhost-snd", 2, sizes); + * + * Returns: + * Zero on success or a negative error number on failure. + * -EINVAL - Invalid parameters (device_type, socket_path, etc.) + * -ENOENT - Context doesn't exist + * -ENOTSUP - vhost-user support not compiled in + */ +int32_t krun_add_vhost_user_device(uint32_t ctx_id, + uint32_t device_type, + const char *socket_path, + const char *name, + uint16_t num_queues, + const uint16_t *queue_sizes); + /** * Configures a map of rlimits to be set in the guest before starting the isolated binary. * diff --git a/src/devices/Cargo.toml b/src/devices/Cargo.toml index 4f5125d9a..edd1f4144 100644 --- a/src/devices/Cargo.toml +++ b/src/devices/Cargo.toml @@ -20,6 +20,7 @@ input = ["zerocopy", "krun_input"] virgl_resource_map2 = [] aws-nitro = [] test_utils = [] +vhost-user = ["vhost"] [dependencies] bitflags = "1.2.0" @@ -31,6 +32,7 @@ nix = { version = "0.30.1", features = ["ioctl", "net", "poll", "socket", "fs"] pw = { package = "pipewire", version = "0.9.2", optional = true } rand = "0.9.2" thiserror = { version = "2.0", optional = true } +vhost = { version = "0.14", optional = true, features = ["vhost-user-frontend"] } virtio-bindings = "0.2.0" vm-memory = { version = "0.17", features = ["backend-mmap"] } zerocopy = { version = "0.8.26", optional = true, features = ["derive"] } diff --git a/src/libkrun/Cargo.toml b/src/libkrun/Cargo.toml index dc9000916..24db7a9ff 100644 --- a/src/libkrun/Cargo.toml +++ b/src/libkrun/Cargo.toml @@ -19,6 +19,7 @@ snd = ["vmm/snd", "devices/snd"] input = ["krun_input", "vmm/input", "devices/input"] virgl_resource_map2 = ["devices/virgl_resource_map2"] aws-nitro = ["vmm/aws-nitro", "devices/aws-nitro", "dep:aws-nitro", "dep:nitro-enclaves"] +vhost-user = ["vmm/vhost-user", "devices/vhost-user"] [dependencies] crossbeam-channel = ">=0.5.15" diff --git a/src/libkrun/src/lib.rs b/src/libkrun/src/lib.rs index 8acf6d205..1d8b3fcb1 100644 --- a/src/libkrun/src/lib.rs +++ b/src/libkrun/src/lib.rs @@ -70,6 +70,9 @@ use krun_input::{InputConfigBackend, InputEventProviderBackend}; const KRUN_SUCCESS: i32 = 0; // Maximum number of arguments/environment variables we allow const MAX_ARGS: usize = 4096; +/// Maximum number of virtqueues allowed by virtio spec (16-bit queue index: 0-65535) +#[cfg(feature = "vhost-user")] +const VIRTIO_MAX_QUEUES: usize = 65536; // krunfw library name for each context #[cfg(all(target_os = "linux", not(feature = "tee")))] @@ -1798,6 +1801,91 @@ pub unsafe extern "C" fn krun_set_snd_device(ctx_id: u32, enable: bool) -> i32 { KRUN_SUCCESS } +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +#[cfg(feature = "vhost-user")] +pub unsafe extern "C" fn krun_add_vhost_user_device( + ctx_id: u32, + device_type: u32, + socket_path: *const c_char, + name: *const c_char, + num_queues: u16, + queue_sizes: *const u16, +) -> i32 { + use vmm::resources::VhostUserDeviceConfig; + + let socket_path_str = match CStr::from_ptr(socket_path).to_str() { + Ok(s) => s, + Err(_) => return -libc::EINVAL, + }; + + if socket_path_str.is_empty() { + return -libc::EINVAL; + } + + let name_opt = if name.is_null() { + None + } else { + match CStr::from_ptr(name).to_str() { + Ok(s) if !s.is_empty() => Some(s.to_string()), + _ => None, + } + }; + + let queue_sizes_vec = if queue_sizes.is_null() { + Vec::new() + } else if num_queues == 0 { + // Auto-detect mode: read queue_sizes until we hit 0 (sentinel) + let mut sizes = Vec::new(); + let mut i = 0; + loop { + let size = *queue_sizes.add(i); + if size == 0 { + break; + } + sizes.push(size); + i += 1; + + // Safety: prevent infinite loop if user forgets sentinel terminator + if i >= VIRTIO_MAX_QUEUES { + return -libc::EINVAL; + } + } + sizes + } else { + std::slice::from_raw_parts(queue_sizes, num_queues as usize).to_vec() + }; + + match CTX_MAP.lock().unwrap().entry(ctx_id) { + Entry::Occupied(mut ctx_cfg) => { + let cfg = ctx_cfg.get_mut(); + cfg.vmr.vhost_user_devices.push(VhostUserDeviceConfig { + device_type, + socket_path: socket_path_str.to_string(), + name: name_opt, + num_queues, + queue_sizes: queue_sizes_vec, + }); + KRUN_SUCCESS + } + Entry::Vacant(_) => -libc::ENOENT, + } +} + +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +#[cfg(not(feature = "vhost-user"))] +pub unsafe extern "C" fn krun_add_vhost_user_device( + _ctx_id: u32, + _device_type: u32, + _socket_path: *const c_char, + _name: *const c_char, + _num_queues: u16, + _queue_sizes: *const u16, +) -> i32 { + -libc::ENOTSUP +} + #[allow(unused_assignments)] #[no_mangle] pub extern "C" fn krun_get_shutdown_eventfd(ctx_id: u32) -> i32 { diff --git a/src/vmm/Cargo.toml b/src/vmm/Cargo.toml index 10e3d3674..2385bc343 100644 --- a/src/vmm/Cargo.toml +++ b/src/vmm/Cargo.toml @@ -18,6 +18,7 @@ gpu = ["devices/gpu", "krun_display"] snd = ["devices/snd"] input = ["devices/input", "krun_input"] aws-nitro = [] +vhost-user = ["devices/vhost-user"] [dependencies] crossbeam-channel = ">=0.5.15" diff --git a/src/vmm/src/resources.rs b/src/vmm/src/resources.rs index 2d3e5ef2b..62756ca1c 100644 --- a/src/vmm/src/resources.rs +++ b/src/vmm/src/resources.rs @@ -40,6 +40,22 @@ type Result = std::result::Result<(), E>; // Re-export TsiFlags from devices crate pub use devices::virtio::TsiFlags; +#[cfg(feature = "vhost-user")] +/// Configuration for a vhost-user device. +#[derive(Debug, Clone)] +pub struct VhostUserDeviceConfig { + /// Virtio device type ID (e.g., 4 for RNG, 25 for sound, 36 for CAN) + pub device_type: u32, + /// Path to the vhost-user Unix domain socket + pub socket_path: String, + /// Device name for logging/debugging (None = auto-generate from type) + pub name: Option, + /// Number of virtqueues (0 = use device default) + pub num_queues: u16, + /// Size of each queue (empty = use device defaults) + pub queue_sizes: Vec, +} + /// Errors encountered when configuring microVM resources. #[derive(Debug)] pub enum Error { @@ -173,6 +189,9 @@ pub struct VmResources { #[cfg(feature = "snd")] /// Enable the virtio-snd device. pub snd_device: bool, + #[cfg(feature = "vhost-user")] + /// Vhost-user device configurations + pub vhost_user_devices: Vec, /// File to send console output. pub console_output: Option, /// SMBIOS OEM Strings @@ -429,6 +448,8 @@ mod tests { input_backends: Vec::new(), #[cfg(feature = "snd")] snd_device: false, + #[cfg(feature = "vhost-user")] + vhost_user_devices: Vec::new(), console_output: None, smbios_oem_strings: None, nested_enabled: false, From 4f6f8e21f1b65dd5254114d502df629c631c2841 Mon Sep 17 00:00:00 2001 From: Dorinda Bassey Date: Tue, 3 Feb 2026 01:36:37 +0100 Subject: [PATCH 02/14] vmm: Implement file-backed memory using memfd for vhost-user Add memfd-backed memory region creation to enable memory sharing with vhost-user backends via FD passing. When vhost-user is enabled, all guest RAM regions are created with memfd backing instead of anonymous mmap. This lays the groundwork for vhost-user device support while maintaining backward compatibility such that the VM boots normally with standard memory when vhost-user is not configured. Signed-off-by: Dorinda Bassey --- Cargo.lock | 46 ++--- examples/Cargo.lock | 445 ++++++++++++++++++----------------------- src/devices/Cargo.toml | 5 +- src/vmm/src/builder.rs | 140 ++++++++++++- 4 files changed, 352 insertions(+), 284 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 253d0f910..c066dd4eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -547,7 +547,7 @@ dependencies = [ "rustc_version", "tokio", "tracing", - "vm-memory 0.17.1", + "vm-memory", "windows-sys", ] @@ -660,7 +660,7 @@ dependencies = [ "kvm-ioctls", "libc", "tdx", - "vm-memory 0.17.1", + "vm-memory", "vmm-sys-util 0.14.0", ] @@ -719,7 +719,8 @@ dependencies = [ "vhost", "virtio-bindings", "vm-fdt", - "vm-memory 0.17.1", + "vm-memory", + "vmm-sys-util 0.15.0", "zerocopy", ] @@ -761,7 +762,7 @@ name = "krun-kernel" version = "0.1.0-1.18.0" dependencies = [ "krun-utils", - "vm-memory 0.17.1", + "vm-memory", ] [[package]] @@ -793,7 +794,7 @@ dependencies = [ name = "krun-smbios" version = "0.1.0-1.18.0" dependencies = [ - "vm-memory 0.17.1", + "vm-memory", ] [[package]] @@ -840,7 +841,7 @@ dependencies = [ "serde", "serde_json", "tdx", - "vm-memory 0.17.1", + "vm-memory", "vmm-sys-util 0.14.0", "zstd", ] @@ -900,7 +901,7 @@ dependencies = [ "nitro-enclaves 0.5.0", "once_cell", "rand 0.9.2", - "vm-memory 0.17.1", + "vm-memory", ] [[package]] @@ -959,7 +960,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de72cb02c55ecffcf75fe78295926f872eb6eb0a58d629c58a8c324dc26380f6" dependencies = [ - "vm-memory 0.17.1", + "vm-memory", ] [[package]] @@ -1759,15 +1760,15 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vhost" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4dcad85a129d97d5d4b2f3c47a4affdeedd76bdcd02094bcb5d9b76cac2d05" +checksum = "c76d90ce3c6b37d610a5304c9a445cfff580cf8b4b9fd02fb256aaf68552c28a" dependencies = [ "bitflags 2.11.0", "libc", "uuid", - "vm-memory 0.16.2", - "vmm-sys-util 0.14.0", + "vm-memory", + "vmm-sys-util 0.15.0", ] [[package]] @@ -1788,17 +1789,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e21282841a059bb62627ce8441c491f09603622cd5a21c43bfedc85a2952f23" -[[package]] -name = "vm-memory" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd5e56d48353c5f54ef50bd158a0452fc82f5383da840f7b8efc31695dd3b9d" -dependencies = [ - "libc", - "thiserror 1.0.69", - "winapi", -] - [[package]] name = "vm-memory" version = "0.17.1" @@ -1830,6 +1820,16 @@ dependencies = [ "libc", ] +[[package]] +name = "vmm-sys-util" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "506c62fdf617a5176827c2f9afbcf1be155b03a9b4bf9617a60dbc07e3a1642f" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "vsock" version = "0.5.3" diff --git a/examples/Cargo.lock b/examples/Cargo.lock index 48f7d878f..c4c63539f 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -4,18 +4,18 @@ version = 4 [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] [[package]] name = "anstream" -version = "0.6.19" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -28,44 +28,44 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.59.0", + "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "atty" @@ -90,7 +90,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.1", "cexpr", "clang-sys", "itertools", @@ -110,17 +110,17 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "cairo-rs" -version = "0.21.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6466a563dea2e99f59f6ffbb749fd0bdf75764f5e6e93976b5e7bd73c4c9efb" +checksum = "b01fe135c0bd16afe262b6dea349bd5ea30e6de50708cec639aae7c5c14cc7e4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.1", "cairo-sys-rs", "glib", "libc", @@ -128,9 +128,9 @@ dependencies = [ [[package]] name = "cairo-sys-rs" -version = "0.21.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab7e9f13c802625aad1ad2b4ae3989f4ce9339ff388f335a6f109f9338705e2" +checksum = "06c28280c6b12055b5e39e4554271ae4e6630b27c0da9148c4cf6485fc6d245c" dependencies = [ "glib-sys", "libc", @@ -148,9 +148,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.20.1" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d0390889d58f934f01cd49736275b4c2da15bcfc328c78ff2349907e6cabf22" +checksum = "3c6b04e07d8080154ed4ac03546d9a2b303cc2fe1901ba0b35b301516e289368" dependencies = [ "smallvec", "target-lexicon", @@ -158,9 +158,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -180,18 +180,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.41" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.41" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstream", "anstyle", @@ -201,9 +201,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.41" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck", "proc-macro2", @@ -213,15 +213,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.5" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "crossbeam-channel" @@ -275,24 +275,24 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -301,15 +301,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", @@ -318,29 +318,28 @@ dependencies = [ [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", "futures-macro", "futures-task", "pin-project-lite", - "pin-utils", "slab", ] [[package]] name = "gdk-pixbuf" -version = "0.21.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688dc7eaf551dbac1f5b11d000d089c3db29feb25562455f47c1a2080cc60bda" +checksum = "debb0d39e3cdd84626edfd54d6e4a6ba2da9a0ef2e796e691c4e9f8646fda00c" dependencies = [ "gdk-pixbuf-sys", "gio", @@ -350,9 +349,9 @@ dependencies = [ [[package]] name = "gdk-pixbuf-sys" -version = "0.21.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5af1823d3d1cb72616873ba0a593bd440eb92da700fdfb047505a21ee3ec3e10" +checksum = "bd95ad50b9a3d2551e25dd4f6892aff0b772fe5372d84514e9d0583af60a0ce7" dependencies = [ "gio-sys", "glib-sys", @@ -363,9 +362,9 @@ dependencies = [ [[package]] name = "gdk4" -version = "0.10.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a67b064d2f35e649232455c7724f56f977555d2608c43300eabc530eaa4e359" +checksum = "756564212bbe4a4ce05d88ffbd2582581ac6003832d0d32822d0825cca84bfbf" dependencies = [ "cairo-rs", "gdk-pixbuf", @@ -378,9 +377,9 @@ dependencies = [ [[package]] name = "gdk4-sys" -version = "0.10.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2edbda0d879eb85317bdb49a3da591ed70a804a10776e358ef416be38c6db2c5" +checksum = "a6d4e5b3ccf591826a4adcc83f5f57b4e59d1925cb4bf620b0d645f79498b034" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -395,9 +394,9 @@ dependencies = [ [[package]] name = "gio" -version = "0.21.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "273d64c833fbbf7cd86c4cdced893c5d3f2f5d6aeb30fd0c30d172456ce8be2e" +checksum = "c5ff48bf600c68b476e61dc6b7c762f2f4eb91deef66583ba8bb815c30b5811a" dependencies = [ "futures-channel", "futures-core", @@ -412,24 +411,24 @@ dependencies = [ [[package]] name = "gio-sys" -version = "0.21.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8130f5810a839d74afc3a929c34a700bf194972bb034f2ecfe639682dd13cc" +checksum = "0071fe88dba8e40086c8ff9bbb62622999f49628344b1d1bf490a48a29d80f22" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] name = "glib" -version = "0.21.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "690e8bcf8a819b5911d6ae79879226191d01253a4f602748072603defd5b9553" +checksum = "16de123c2e6c90ce3b573b7330de19be649080ec612033d397d72da265f1bd8b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.1", "futures-channel", "futures-core", "futures-executor", @@ -446,9 +445,9 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.21.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e772291ebea14c28eb11bb75741f62f4a4894f25e60ce80100797b6b010ef0f9" +checksum = "cf59b675301228a696fe01c3073974643365080a76cc3ed5bc2cbc466ad87f17" dependencies = [ "heck", "proc-macro-crate", @@ -459,9 +458,9 @@ dependencies = [ [[package]] name = "glib-sys" -version = "0.21.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2be4c74454fb4a6bd3328320737d0fa3d6939e2d570f5d846da00cb222f6a0" +checksum = "2d95e1a3a19ae464a7286e14af9a90683c64d70c02532d88d87ce95056af3e6c" dependencies = [ "libc", "system-deps", @@ -469,15 +468,15 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "gobject-sys" -version = "0.21.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab318a786f9abd49d388013b9161fa0ef8218ea6118ee7111c95e62186f7d31f" +checksum = "2dca35da0d19a18f4575f3cb99fe1c9e029a2941af5662f326f738a21edaf294" dependencies = [ "glib-sys", "libc", @@ -486,9 +485,9 @@ dependencies = [ [[package]] name = "graphene-rs" -version = "0.21.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0487f78e8a772ec89020458fbabadd1332bc1e3236ca1c63ef1d61afd4e5f2cc" +checksum = "2730030ac9db663fd8bfe1e7093742c1cafb92db9c315c9417c29032341fe2f9" dependencies = [ "glib", "graphene-sys", @@ -497,9 +496,9 @@ dependencies = [ [[package]] name = "graphene-sys" -version = "0.21.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "270cefb6b270fcb2ef9708c3a35c0e25c2e831dac28d75c4f87e5ad3540c9543" +checksum = "915e32091ea9ad241e4b044af62b7351c2d68aeb24f489a0d7f37a0fc484fd93" dependencies = [ "glib-sys", "libc", @@ -509,9 +508,9 @@ dependencies = [ [[package]] name = "gsk4" -version = "0.10.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5dbe33ceed6fc20def67c03d36e532f5a4a569ae437ae015a7146094f31e10c" +checksum = "e755de9d8c5896c5beaa028b89e1969d067f1b9bf1511384ede971f5983aa153" dependencies = [ "cairo-rs", "gdk4", @@ -524,9 +523,9 @@ dependencies = [ [[package]] name = "gsk4-sys" -version = "0.10.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d76011d55dd19fde16ffdedee08877ae6ec942818cfa7bc08a91259bc0b9fc9" +checksum = "7ce91472391146f482065f1041876d8f869057b195b95399414caa163d72f4f7" dependencies = [ "cairo-sys-rs", "gdk4-sys", @@ -540,9 +539,9 @@ dependencies = [ [[package]] name = "gtk4" -version = "0.10.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938d68ad43080ad5ee710c30d467c1bc022ee5947856f593855691d726305b3e" +checksum = "acb21d53cfc6f7bfaf43549731c43b67ca47d87348d81c8cfc4dcdd44828e1a4" dependencies = [ "cairo-rs", "field-offset", @@ -561,9 +560,9 @@ dependencies = [ [[package]] name = "gtk4-macros" -version = "0.10.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0912d2068695633002b92c5966edc108b2e4f54b58c509d1eeddd4cbceb7315c" +checksum = "3ccfb5a14a3d941244815d5f8101fa12d4577b59cc47245778d8d907b0003e42" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -573,9 +572,9 @@ dependencies = [ [[package]] name = "gtk4-sys" -version = "0.10.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a923bdcf00e46723801162de24432cbce38a6810e0178a2d0b6dd4ecc26a1c74" +checksum = "842577fe5a1ee15d166cd3afe804ce0cab6173bc789ca32e21308834f20088dd" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -621,9 +620,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" [[package]] name = "heck" @@ -642,15 +641,15 @@ dependencies = [ [[package]] name = "humantime" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "indexmap" -version = "2.10.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown", @@ -658,9 +657,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -676,7 +675,7 @@ name = "krun-display" version = "0.1.0" dependencies = [ "bindgen", - "bitflags 2.9.1", + "bitflags 2.11.1", "log", "static_assertions", "thiserror", @@ -687,7 +686,7 @@ name = "krun-input" version = "0.1.0" dependencies = [ "bindgen", - "bitflags 2.9.1", + "bitflags 2.11.1", "libc", "log", "static_assertions", @@ -713,7 +712,7 @@ dependencies = [ "log", "nix", "vmm-sys-util", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -727,21 +726,21 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.174" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "log" -version = "0.4.27" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" -version = "2.7.5" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memoffset" @@ -764,7 +763,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.1", "cfg-if", "cfg_aliases", "libc", @@ -782,15 +781,15 @@ dependencies = [ [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "pango" -version = "0.21.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d4803f086c4f49163c31ac14db162112a22401c116435080e4be8678c507d61" +checksum = "52d1d85e2078077a065bb7fc072783d5bcd4e51b379f22d67107d0a16937eb69" dependencies = [ "gio", "glib", @@ -800,9 +799,9 @@ dependencies = [ [[package]] name = "pango-sys" -version = "0.21.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66872b3cfd328ad6d1a4f89ebd5357119bd4c592a4ddbb8f6bc2386f8ce7b898" +checksum = "b4f06627d36ed5ff303d2df65211fc2e52ba5b17bf18dd80ff3d9628d6e06cfd" dependencies = [ "glib-sys", "gobject-sys", @@ -812,54 +811,48 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -869,9 +862,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -880,15 +873,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rustc_version" @@ -901,24 +894,24 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] -name = "serde" -version = "1.0.219" +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -927,11 +920,11 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.9" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -942,9 +935,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "slab" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -966,9 +959,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.104" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -977,9 +970,9 @@ dependencies = [ [[package]] name = "system-deps" -version = "7.0.5" +version = "7.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4be53aa0cba896d2dc615bd42bbc130acdcffa239e0a2d965ea5b3b2a86ffdb" +checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7" dependencies = [ "cfg-expr", "heck", @@ -990,9 +983,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" +checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" [[package]] name = "termcolor" @@ -1005,18 +998,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -1025,43 +1018,60 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.23" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ - "serde", + "indexmap", + "serde_core", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.11" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ - "serde", + "serde_core", ] [[package]] name = "toml_edit" -version = "0.22.27" +version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ "indexmap", - "serde", - "serde_spanned", "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ "winnow", ] +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "utf8parse" @@ -1071,9 +1081,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "version-compare" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" [[package]] name = "vmm-sys-util" @@ -1103,11 +1113,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -1122,15 +1132,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -1140,75 +1141,11 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "winnow" -version = "0.7.12" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" dependencies = [ "memchr", ] diff --git a/src/devices/Cargo.toml b/src/devices/Cargo.toml index edd1f4144..ab6ecfe2a 100644 --- a/src/devices/Cargo.toml +++ b/src/devices/Cargo.toml @@ -20,7 +20,7 @@ input = ["zerocopy", "krun_input"] virgl_resource_map2 = [] aws-nitro = [] test_utils = [] -vhost-user = ["vhost"] +vhost-user = ["vhost", "vmm-sys-util"] [dependencies] bitflags = "1.2.0" @@ -32,7 +32,8 @@ nix = { version = "0.30.1", features = ["ioctl", "net", "poll", "socket", "fs"] pw = { package = "pipewire", version = "0.9.2", optional = true } rand = "0.9.2" thiserror = { version = "2.0", optional = true } -vhost = { version = "0.14", optional = true, features = ["vhost-user-frontend"] } +vhost = { version = "0.15", optional = true, features = ["vhost-user-frontend"] } +vmm-sys-util = { version = "0.15", optional = true } virtio-bindings = "0.2.0" vm-memory = { version = "0.17", features = ["backend-mmap"] } zerocopy = { version = "0.8.26", optional = true, features = ["derive"] } diff --git a/src/vmm/src/builder.rs b/src/vmm/src/builder.rs index 80609eb6b..5880f50c5 100644 --- a/src/vmm/src/builder.rs +++ b/src/vmm/src/builder.rs @@ -93,6 +93,8 @@ use vm_memory::mmap::MmapRegion; #[cfg(not(any(feature = "tee", feature = "aws-nitro")))] use vm_memory::Address; use vm_memory::Bytes; +#[cfg(all(feature = "vhost-user", target_os = "linux"))] +use vm_memory::FileOffset; #[cfg(not(feature = "aws-nitro"))] use vm_memory::GuestMemory; #[cfg(all(target_arch = "x86_64", not(feature = "tee")))] @@ -1330,9 +1332,76 @@ fn load_payload( return Err(StartMicrovmError::MissingKernelConfig); }; - let kernel_region = unsafe { - MmapRegion::build_raw(kernel_host_addr as *mut u8, kernel_size, 0, 0) - .map_err(StartMicrovmError::InvalidKernelBundle)? + #[cfg(all(feature = "vhost-user", target_os = "linux"))] + let use_vhost_user = !_vm_resources.vhost_user_devices.is_empty(); + #[cfg(not(all(feature = "vhost-user", target_os = "linux")))] + let use_vhost_user = false; + + let kernel_region = if use_vhost_user { + #[cfg(all(feature = "vhost-user", target_os = "linux"))] + { + debug!( + "Creating file-backed kernel region for vhost-user (size=0x{:x})", + kernel_size + ); + // SAFETY: memfd_create is called with a valid null-terminated C string and valid flags. + // File descriptor ownership is transferred to File::from_raw_fd below. + let memfd = unsafe { + let fd = libc::memfd_create(c"kernel".as_ptr(), libc::MFD_CLOEXEC); + if fd < 0 { + error!( + "Failed to create memfd for kernel: {:?}", + io::Error::last_os_error() + ); + return Err(StartMicrovmError::GuestMemoryMmap(format!( + "memfd_create failed: {:?}", + io::Error::last_os_error() + ))); + } + if libc::ftruncate(fd, kernel_size as i64) < 0 { + error!( + "Failed to ftruncate kernel memfd: {:?}", + io::Error::last_os_error() + ); + libc::close(fd); + return Err(StartMicrovmError::GuestMemoryMmap(format!( + "ftruncate failed: {:?}", + io::Error::last_os_error() + ))); + } + debug!("Created kernel memfd with fd={}", fd); + File::from_raw_fd(fd) + }; + + let file_offset = FileOffset::new(memfd, 0); + let region = MmapRegion::from_file(file_offset, kernel_size) + .map_err(StartMicrovmError::InvalidKernelBundle)?; + + // SAFETY: kernel_host_addr points to valid kernel data of size kernel_size, + // provided by the kernel bundle loader. + let kernel_data = unsafe { + std::slice::from_raw_parts(kernel_host_addr as *const u8, kernel_size) + }; + // SAFETY: Both source (kernel_data) and destination (region) are valid for + // kernel_size bytes. Regions don't overlap as dest is newly allocated memfd-backed + // memory and source is from kernel bundle. + unsafe { + let dest = region.as_ptr() as *mut u8; + std::ptr::copy_nonoverlapping(kernel_data.as_ptr(), dest, kernel_size); + } + debug!("Copied kernel data to file-backed region"); + + region + } + #[cfg(not(all(feature = "vhost-user", target_os = "linux")))] + unreachable!() + } else { + // SAFETY: kernel_host_addr points to valid kernel data of size kernel_size. + // The memory region is managed by the kernel bundle and remains valid. + unsafe { + MmapRegion::build_raw(kernel_host_addr as *mut u8, kernel_size, 0, 0) + .map_err(StartMicrovmError::InvalidKernelBundle)? + } }; Ok(( @@ -1498,10 +1567,71 @@ pub fn create_guest_memory( .map_err(StartMicrovmError::ShmCreate)?; } + // For vhost-user devices, we need file-backed memory so the backend can mmap it + #[cfg(all(feature = "vhost-user", target_os = "linux"))] + let use_vhost_user = !vm_resources.vhost_user_devices.is_empty(); + #[cfg(not(all(feature = "vhost-user", target_os = "linux")))] + let use_vhost_user = false; + + // Add SHM regions before creating guest memory arch_mem_regions.extend(shm_manager.regions()); - let guest_mem = GuestMemoryMmap::from_ranges(&arch_mem_regions) - .map_err(|e| StartMicrovmError::GuestMemoryMmap(format!("{e:?}")))?; + let guest_mem = if use_vhost_user { + #[cfg(all(feature = "vhost-user", target_os = "linux"))] + { + debug!( + "Creating file-backed memory for vhost-user (regions: {})", + arch_mem_regions.len() + ); + // Create file-backed memory regions using memfd + let regions_with_files: Vec<_> = arch_mem_regions + .iter() + .map(|(addr, size)| { + debug!( + "Creating memfd for region: addr=0x{:x}, size=0x{:x}", + addr.0, size + ); + // SAFETY: memfd_create is called with a valid null-terminated C string and valid flags. + // File descriptor ownership is transferred to File::from_raw_fd below. + let memfd = unsafe { + let fd = libc::memfd_create(c"guest_mem".as_ptr(), libc::MFD_CLOEXEC); + if fd < 0 { + error!("Failed to create memfd: {:?}", io::Error::last_os_error()); + return Err(io::Error::last_os_error()); + } + if libc::ftruncate(fd, *size as i64) < 0 { + error!( + "Failed to ftruncate memfd: {:?}", + io::Error::last_os_error() + ); + libc::close(fd); + return Err(io::Error::last_os_error()); + } + debug!("Created memfd with fd={}", fd); + File::from_raw_fd(fd) + }; + + let file_offset = FileOffset::new(memfd, 0); + Ok((*addr, *size, Some(file_offset))) + }) + .collect::, io::Error>>() + .map_err(|e| { + StartMicrovmError::GuestMemoryMmap(format!("memfd creation failed: {e:?}")) + })?; + + debug!( + "Created {} file-backed memory regions", + regions_with_files.len() + ); + GuestMemoryMmap::from_ranges_with_files(®ions_with_files) + .map_err(|e| StartMicrovmError::GuestMemoryMmap(format!("{e:?}")))? + } + #[cfg(not(all(feature = "vhost-user", target_os = "linux")))] + unreachable!() + } else { + GuestMemoryMmap::from_ranges(&arch_mem_regions) + .map_err(|e| StartMicrovmError::GuestMemoryMmap(format!("{e:?}")))? + }; let (guest_mem, entry_addr, initrd_config, cmdline) = load_payload(vm_resources, guest_mem, &arch_mem_info, payload)?; From a2742e292b37da79ce05053c7858202b8f51be82 Mon Sep 17 00:00:00 2001 From: Dorinda Bassey Date: Tue, 3 Feb 2026 12:58:48 +0100 Subject: [PATCH 03/14] devices/vhost: Add core VhostUserDevice implementation Implement generic vhost-user device wrapper with connection, feature negotiation, and Guest physical address(GPA) to Virtual address(VA) translation. Supports protocol feature negotiation (CONFIG, MQ). Backend interrupts (vring_call_event) are monitored by the EventManager and forwarded to the guest without spawning additional threads. Bridge vmm-sys-util version mismatch: vhost 0.15 requires vmm-sys-util 0.15 (for vm-memory 0.17 compatibility), while libkrun core uses 0.14 and TDX uses 0.12. Convert EventFd types via AsRawFd/FromRawFd when calling vhost functions to avoid type conflicts. This allows all 3 versions coexist in the dependency tree. Co-authored-by: Matej Hrica Signed-off-by: Dorinda Bassey --- src/devices/src/virtio/mod.rs | 4 + src/devices/src/virtio/vhost_user/device.rs | 558 ++++++++++++++++++++ src/devices/src/virtio/vhost_user/mod.rs | 11 + 3 files changed, 573 insertions(+) create mode 100644 src/devices/src/virtio/vhost_user/device.rs create mode 100644 src/devices/src/virtio/vhost_user/mod.rs diff --git a/src/devices/src/virtio/mod.rs b/src/devices/src/virtio/mod.rs index 384aef5ac..64f19c35b 100644 --- a/src/devices/src/virtio/mod.rs +++ b/src/devices/src/virtio/mod.rs @@ -36,6 +36,8 @@ mod queue; pub mod rng; #[cfg(feature = "snd")] pub mod snd; +#[cfg(feature = "vhost-user")] +pub mod vhost_user; pub mod vsock; #[cfg(not(feature = "tee"))] @@ -56,6 +58,8 @@ pub use self::queue::{Descriptor, DescriptorChain, Queue}; pub use self::rng::*; #[cfg(feature = "snd")] pub use self::snd::Snd; +#[cfg(feature = "vhost-user")] +pub use self::vhost_user::VhostUserDevice; pub use self::vsock::*; /// When the driver initializes the device, it lets the device know about the diff --git a/src/devices/src/virtio/vhost_user/device.rs b/src/devices/src/virtio/vhost_user/device.rs new file mode 100644 index 000000000..3d4e4e59f --- /dev/null +++ b/src/devices/src/virtio/vhost_user/device.rs @@ -0,0 +1,558 @@ +// Copyright 2026, Red Hat Inc. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Generic vhost-user device wrapper. +//! +//! This module provides a wrapper around the vhost crate's Frontend, +//! adapting it to work with libkrun's VirtioDevice trait. + +use std::io::{self, ErrorKind, Result as IoResult}; +use std::os::fd::{AsRawFd, FromRawFd}; +use std::os::unix::net::UnixStream; +use std::sync::{Arc, Mutex}; + +use log::{debug, error, warn}; +use polly::event_manager::{EventManager, Subscriber}; +use utils::epoll::{EpollEvent, EventSet}; +use utils::eventfd::{EventFd, EFD_NONBLOCK}; +use vmm_sys_util::eventfd::EventFd as VhostEventFd; +use vhost::vhost_user::{Frontend, VhostUserFrontend, VhostUserProtocolFeatures}; +use vhost::{VhostBackend, VhostUserMemoryRegionInfo, VringConfigData}; +use vm_memory::{Address, GuestMemory, GuestMemoryMmap, GuestMemoryRegion}; + +use crate::virtio::{ + ActivateError, ActivateResult, DeviceQueue, DeviceState, InterruptTransport, QueueConfig, + VirtioDevice, +}; + +/// VHOST_USER_F_PROTOCOL_FEATURES (bit 30) is a backend-only feature +/// that enables vhost-user protocol extensions. It's not a virtio feature. +const VHOST_USER_F_PROTOCOL_FEATURES: u64 = 1 << 30; + +/// Generic vhost-user device wrapper. +/// +/// This wraps a vhost-user backend connection and implements the VirtioDevice +/// trait, allowing it to be used like any other virtio device in libkrun. +pub struct VhostUserDevice { + /// Vhost-user frontend connection + frontend: Arc>, + + /// Device type (e.g., VIRTIO_ID_RNG = 4) + device_type: u32, + + /// Device name for logging + device_name: String, + + /// Queue configurations + queue_configs: Vec, + + /// Available features from the backend + avail_features: u64, + + /// Whether the backend supports protocol features + has_protocol_features: bool, + + /// Acknowledged features + acked_features: u64, + + /// Device state + device_state: DeviceState, + + /// Activation event (registered with EventManager) + activate_evt: EventFd, + + /// Vring call event (backend->VMM interrupt notification) + vring_call_event: Option, +} + +impl VhostUserDevice { + /// Create a new vhost-user device by connecting to a socket. + /// + /// # Arguments + /// + /// * `socket_path` - Path to the vhost-user Unix domain socket + /// * `device_type` - Virtio device type ID + /// * `device_name` - Human-readable device name for logging + /// * `num_queues` - Number of queues (0 = query backend via MQ protocol) + /// * `queue_sizes` - Size for each queue (empty = use default 256) + /// + /// # Returns + /// + /// A new VhostUserDevice or an error if connection fails. + pub fn new( + socket_path: impl AsRef, + device_type: u32, + device_name: String, + num_queues: u16, + queue_sizes: &[u16], + ) -> IoResult { + debug!( + "Connecting to vhost-user backend at {}", + socket_path.as_ref().display() + ); + + // Connect to the vhost-user backend + let stream = UnixStream::connect(socket_path)?; + // NOTE: `num_queues` could be 0 here, but this is actually fine + // because if `VhostUserProtocolFeatures::MQ` is supported the negotiated + // value will be used automatically by Frontend + let mut frontend = Frontend::from_stream(stream, num_queues as u64); + + // Get available features from backend + let avail_features = frontend + .get_features() + .map_err(|e| io::Error::new(ErrorKind::Other, e))?; + + debug!("{}: backend features: 0x{:x}", device_name, avail_features); + + // Strip the vhost specific bit to leave only standard virtio features + let has_protocol_features = avail_features & VHOST_USER_F_PROTOCOL_FEATURES != 0; + let avail_features = avail_features & !VHOST_USER_F_PROTOCOL_FEATURES; + + if has_protocol_features { + let protocol_features = frontend + .get_protocol_features() + .map_err(|e| io::Error::new(ErrorKind::Other, e))?; + + let mut our_protocol_features = VhostUserProtocolFeatures::empty(); + if protocol_features.contains(VhostUserProtocolFeatures::CONFIG) { + our_protocol_features |= VhostUserProtocolFeatures::CONFIG; + } + if protocol_features.contains(VhostUserProtocolFeatures::MQ) { + our_protocol_features |= VhostUserProtocolFeatures::MQ; + } + + frontend + .set_protocol_features(our_protocol_features) + .map_err(|e| io::Error::new(ErrorKind::Other, e))?; + } + + // Determine actual queue count - may require protocol feature negotiation + let actual_num_queues = if num_queues == 0 { + if has_protocol_features { + let backend_queue_num = frontend + .get_queue_num() + .map_err(|e| io::Error::new(ErrorKind::Other, e))?; + + debug!( + "{}: backend reports {} queues available", + device_name, backend_queue_num + ); + + backend_queue_num as usize + } else { + return Err(io::Error::new( + ErrorKind::InvalidInput, + "Backend doesn't support protocol features, must specify queue count", + )); + } + } else { + num_queues as usize + }; + + debug!( + "{}: using {} queues (requested: {}, sizes provided: {})", + device_name, + actual_num_queues, + num_queues, + queue_sizes.len() + ); + + let default_size = queue_sizes.last().copied().unwrap_or(256); + let queue_configs: Vec<_> = (0..actual_num_queues) + .map(|i| { + let size = queue_sizes.get(i).copied().unwrap_or(default_size); + QueueConfig::new(size) + }) + .collect(); + + Ok(Self { + frontend: Arc::new(Mutex::new(frontend)), + device_type, + device_name, + queue_configs, + avail_features, + has_protocol_features, + acked_features: 0, + device_state: DeviceState::Inactive, + activate_evt: EventFd::new(EFD_NONBLOCK)?, + vring_call_event: None, + }) + } + + /// Activate the vhost-user device by setting up memory and vrings. + fn activate_vhost_user( + &mut self, + mem: &GuestMemoryMmap, + queues: &[DeviceQueue], + ) -> IoResult<()> { + let mut frontend = self.frontend.lock().unwrap(); + + debug!("{}: activating vhost-user device", self.device_name); + + // Combine guest-acked features with backend-only features (QEMU approach) + let backend_feature_bits = if self.has_protocol_features { + self.acked_features | VHOST_USER_F_PROTOCOL_FEATURES + } else { + self.acked_features + }; + + frontend + .set_owner() + .map_err(|e| io::Error::new(ErrorKind::Other, e))?; + + // Only share memory regions that have file backing (memfd) + let regions: Vec = mem + .iter() + .filter_map(|region| { + if region.file_offset().is_some() { + Some(VhostUserMemoryRegionInfo::from_guest_region(region)) + } else { + None + } + }) + .collect::, _>>() + .map_err(|e| { + error!( + "{}: failed to convert memory regions: {:?}", + self.device_name, e + ); + io::Error::new(ErrorKind::Other, e) + })?; + + debug!( + "{}: sharing {} file-backed regions with backend", + self.device_name, + regions.len() + ); + + frontend.set_mem_table(®ions).map_err(|e| { + error!("{}: set_mem_table failed: {:?}", self.device_name, e); + io::Error::new(ErrorKind::Other, e) + })?; + + // If protocol features not negotiated, this triggers automatic ring enabling + frontend + .set_features(backend_feature_bits) + .map_err(|e| io::Error::new(ErrorKind::Other, e))?; + + let vring_call_event = EventFd::new(EFD_NONBLOCK)?; + + for (queue_index, device_queue) in queues.iter().enumerate() { + let queue = &device_queue.queue; + + frontend + .set_vring_num(queue_index, queue.actual_size()) + .map_err(|e| io::Error::new(ErrorKind::Other, e))?; + + // Set vring base + frontend + .set_vring_base(queue_index, 0) + .map_err(|e| io::Error::new(ErrorKind::Other, e))?; + + // Vring addresses in queue are GPAs, but vhost-user protocol expects VMM VAs + let desc_table_gpa = queue.desc_table.0; + let avail_ring_gpa = queue.avail_ring.0; + let used_ring_gpa = queue.used_ring.0; + + let desc_table_vmm = + mem.get_host_address(Address::new(desc_table_gpa)) + .map_err(|_| { + io::Error::new( + ErrorKind::InvalidInput, + format!("GPA 0x{:x} not found in any memory region", desc_table_gpa), + ) + })? as u64; + let avail_ring_vmm = + mem.get_host_address(Address::new(avail_ring_gpa)) + .map_err(|_| { + io::Error::new( + ErrorKind::InvalidInput, + format!("GPA 0x{:x} not found in any memory region", avail_ring_gpa), + ) + })? as u64; + let used_ring_vmm = mem + .get_host_address(Address::new(used_ring_gpa)) + .map_err(|_| { + io::Error::new( + ErrorKind::InvalidInput, + format!("GPA 0x{:x} not found in any memory region", used_ring_gpa), + ) + })? as u64; + + let vring_config = VringConfigData { + flags: 0, + queue_max_size: queue.get_max_size(), + queue_size: queue.actual_size(), + desc_table_addr: desc_table_vmm, + used_ring_addr: used_ring_vmm, + avail_ring_addr: avail_ring_vmm, + log_addr: None, + }; + + frontend + .set_vring_addr(queue_index, &vring_config) + .map_err(|e| { + error!("{}: set_vring_addr failed: {:?}", self.device_name, e); + io::Error::new(ErrorKind::Other, e) + })?; + + // Create vhost-compatible EventFd from the raw fd + // (bridges krun_utils::EventFd with vmm_sys_util::EventFd type mismatch) + let kick_fd = unsafe { VhostEventFd::from_raw_fd(device_queue.event.as_raw_fd()) }; + frontend + .set_vring_kick(queue_index, &kick_fd) + .map_err(|e| { + error!("{}: set_vring_kick failed: {:?}", self.device_name, e); + io::Error::new(ErrorKind::Other, e) + })?; + std::mem::forget(kick_fd); // Don't close the fd twice + + let call_fd = unsafe { VhostEventFd::from_raw_fd(vring_call_event.as_raw_fd()) }; + frontend + .set_vring_call(queue_index, &call_fd) + .map_err(|e| { + error!("{}: set_vring_call failed: {:?}", self.device_name, e); + io::Error::new(ErrorKind::Other, e) + })?; + std::mem::forget(call_fd); // Don't close the fd twice + + // Per QEMU vhost.c: when VHOST_USER_F_PROTOCOL_FEATURES is not negotiated, + // the rings start directly in the enabled state, and set_vring_enable will fail. + if self.has_protocol_features { + frontend + .set_vring_enable(queue_index, true) + .map_err(|e| io::Error::new(ErrorKind::Other, e))?; + } else { + debug!( + "{}: vring {} already enabled (protocol features not negotiated)", + self.device_name, queue_index + ); + } + } + + self.vring_call_event = Some(vring_call_event); + + debug!( + "{}: vhost-user device activated successfully", + self.device_name + ); + + Ok(()) + } +} + +impl VirtioDevice for VhostUserDevice { + fn device_type(&self) -> u32 { + self.device_type + } + + fn device_name(&self) -> &str { + &self.device_name + } + + fn queue_config(&self) -> &[QueueConfig] { + &self.queue_configs + } + + fn avail_features(&self) -> u64 { + self.avail_features + } + + fn acked_features(&self) -> u64 { + self.acked_features + } + + fn set_acked_features(&mut self, acked_features: u64) { + self.acked_features = acked_features; + } + + fn read_config(&self, offset: u64, data: &mut [u8]) { + // For now, configuration space reads are not supported + // This can be extended using VHOST_USER_GET_CONFIG + debug!( + "{}: config read at offset {} (not yet implemented)", + self.device_name, offset + ); + data.fill(0); + } + + fn write_config(&mut self, offset: u64, _data: &[u8]) { + // For now, configuration space writes are not supported + // This can be extended using VHOST_USER_SET_CONFIG + debug!( + "{}: config write at offset {} (not yet implemented)", + self.device_name, offset + ); + } + + fn activate( + &mut self, + mem: GuestMemoryMmap, + interrupt: InterruptTransport, + queues: Vec, + ) -> ActivateResult { + if let Err(e) = self.activate_vhost_user(&mem, &queues) { + error!( + "{}: failed to activate vhost-user device: {}", + self.device_name, e + ); + return Err(ActivateError::BadActivate); + } + + self.device_state = DeviceState::Activated(mem, interrupt); + + if let Err(e) = self.activate_evt.write(1) { + error!( + "{}: failed to write activate event: {}", + self.device_name, e + ); + return Err(ActivateError::BadActivate); + } + + Ok(()) + } + + fn is_activated(&self) -> bool { + matches!(self.device_state, DeviceState::Activated(_, _)) + } + + fn reset(&mut self) -> bool { + debug!("{}: resetting vhost-user device", self.device_name); + + // Disable all vrings + if let Ok(mut frontend) = self.frontend.lock() { + for queue_index in 0..self.queue_configs.len() { + if let Err(e) = frontend.set_vring_enable(queue_index, false) { + debug!( + "{}: failed to disable vring {} during reset: {}", + self.device_name, queue_index, e + ); + } + } + } + + self.vring_call_event = None; + self.device_state = DeviceState::Inactive; + true + } +} + +impl VhostUserDevice { + fn handle_vring_call_event(&mut self, event: &EpollEvent) { + debug!("{}: vring call event received", self.device_name); + + let event_set = event.event_set(); + if event_set != EventSet::IN { + warn!( + "{}: vring call unexpected event {event_set:?}", + self.device_name + ); + return; + } + + if let Some(ref vring_call_event) = self.vring_call_event { + if let Err(e) = vring_call_event.read() { + error!( + "{}: failed to read vring_call_event: {}", + self.device_name, e + ); + return; + } + } else { + error!("{}: vring_call_event is None", self.device_name); + return; + } + + if let DeviceState::Activated(_, ref interrupt) = self.device_state { + debug!( + "{}: interrupt received from backend, signaling guest", + self.device_name + ); + interrupt.signal_used_queue(); + } + } + + fn handle_activate_event(&mut self, event_manager: &mut EventManager) { + debug!("{}: activate event", self.device_name); + + if let Err(e) = self.activate_evt.read() { + error!( + "{}: failed to consume activate event: {}", + self.device_name, e + ); + } + + if let Some(ref vring_call_event) = self.vring_call_event { + let self_subscriber = event_manager + .subscriber(self.activate_evt.as_raw_fd()) + .unwrap(); + + event_manager + .register( + vring_call_event.as_raw_fd(), + EpollEvent::new(EventSet::IN, vring_call_event.as_raw_fd() as u64), + self_subscriber.clone(), + ) + .unwrap_or_else(|e| { + error!( + "{}: failed to register vring_call_event with event manager: {e:?}", + self.device_name + ); + }); + } else { + error!( + "{}: vring_call_event is None during activation", + self.device_name + ); + } + + // Unregister activate_evt as it's only needed once + event_manager + .unregister(self.activate_evt.as_raw_fd()) + .unwrap_or_else(|e| { + error!( + "{}: failed to unregister activate event: {e:?}", + self.device_name + ); + }); + } +} + +impl Subscriber for VhostUserDevice { + fn process(&mut self, event: &EpollEvent, event_manager: &mut EventManager) { + let source = event.fd(); + let activate_evt_fd = self.activate_evt.as_raw_fd(); + let vring_call_fd = self + .vring_call_event + .as_ref() + .map(|e| e.as_raw_fd()) + .unwrap_or(-1); + + if self.is_activated() { + match source { + _ if source == vring_call_fd => self.handle_vring_call_event(event), + _ if source == activate_evt_fd => self.handle_activate_event(event_manager), + _ => warn!( + "{}: unexpected event received: {source:?}", + self.device_name + ), + } + } else if source == activate_evt_fd { + // Allow activation event even before device is activated + self.handle_activate_event(event_manager); + } else { + warn!( + "{}: device not yet activated, spurious event received: {source:?}", + self.device_name + ); + } + } + + fn interest_list(&self) -> Vec { + vec![EpollEvent::new( + EventSet::IN, + self.activate_evt.as_raw_fd() as u64, + )] + } +} diff --git a/src/devices/src/virtio/vhost_user/mod.rs b/src/devices/src/virtio/vhost_user/mod.rs new file mode 100644 index 000000000..96e0c6883 --- /dev/null +++ b/src/devices/src/virtio/vhost_user/mod.rs @@ -0,0 +1,11 @@ +// Copyright 2026, Red Hat Inc. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Vhost-user device implementations for libkrun. +//! +//! This module provides vhost-user frontend support, allowing virtio devices +//! to run in separate processes for better isolation and flexibility. + +mod device; + +pub use device::VhostUserDevice; From f02fe752e25b57a1fbb60b9b05e618b6b8bcce2b Mon Sep 17 00:00:00 2001 From: Dorinda Bassey Date: Wed, 4 Feb 2026 09:58:24 +0100 Subject: [PATCH 04/14] vmm: Implement attaching vhost-user devices Add support for attaching vhost-user devices to the VM. Devices are registered with the EventManager as subscribers to integrate with the VMM's event loop for interrupt handling. The VMM now automatically suppresses the implicit RNG device when a vhost-user RNG is configured via krun_add_vhost_user_device(), allowing seamless switching between the standard virtio-rng and external vhost-user-rng backend for better isolation and flexibility. Signed-off-by: Dorinda Bassey --- src/vmm/src/builder.rs | 71 +++++++++++++++++++++++++- src/vmm/src/device_manager/kvm/mmio.rs | 5 ++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/vmm/src/builder.rs b/src/vmm/src/builder.rs index 5880f50c5..1aa9c5c48 100644 --- a/src/vmm/src/builder.rs +++ b/src/vmm/src/builder.rs @@ -51,6 +51,8 @@ use devices::virtio::{port_io, MmioTransport, PortDescription, VirtioDevice, Vso use kbs_types::Tee; use crate::device_manager; +#[cfg(all(feature = "vhost-user", target_os = "linux"))] +use crate::resources::VhostUserDeviceConfig; #[cfg(target_os = "linux")] use crate::signal_handler::register_sigint_handler; #[cfg(target_os = "linux")] @@ -207,6 +209,8 @@ pub enum StartMicrovmError { RegisterRngDevice(device_manager::mmio::Error), /// Cannot initialize a MMIO Snd device or add a device to the MMIO Bus. RegisterSndDevice(device_manager::mmio::Error), + /// Cannot initialize a vhost-user device or add a device to the MMIO Bus. + RegisterVhostUserDevice(device_manager::mmio::Error), /// Cannot initialize a MMIO Vsock Device or add a device to the MMIO Bus. RegisterVsockDevice(device_manager::mmio::Error), /// Cannot attest the VM in the Secure Virtualization context. @@ -459,6 +463,14 @@ impl Display for StartMicrovmError { "Cannot initialize a MMIO Snd Device or add a device to the MMIO Bus. {err_msg}" ) } + RegisterVhostUserDevice(ref err) => { + let mut err_msg = err.to_string(); + err_msg = err_msg.replace('\"', ""); + write!( + f, + "Cannot initialize a vhost-user device or add a device to the MMIO Bus. {err_msg}" + ) + } RegisterVsockDevice(ref err) => { let mut err_msg = format!("{err}"); err_msg = err_msg.replace('\"', ""); @@ -969,7 +981,29 @@ pub fn build_microvm( #[cfg(not(feature = "tee"))] attach_balloon_device(&mut vmm, event_manager, intc.clone())?; #[cfg(not(feature = "tee"))] - attach_rng_device(&mut vmm, event_manager, intc.clone())?; + { + #[cfg(all(feature = "vhost-user", target_os = "linux"))] + { + const VIRTIO_ID_RNG: u32 = 4; + for device_config in &vm_resources.vhost_user_devices { + attach_vhost_user_device(&mut vmm, event_manager, intc.clone(), device_config)?; + } + + let has_vhost_user_rng = vm_resources + .vhost_user_devices + .iter() + .any(|dev| dev.device_type == VIRTIO_ID_RNG); + + if !has_vhost_user_rng { + attach_rng_device(&mut vmm, event_manager, intc.clone())?; + } + } + + #[cfg(not(all(feature = "vhost-user", target_os = "linux")))] + { + attach_rng_device(&mut vmm, event_manager, intc.clone())?; + } + } let mut console_id = 0; if !vm_resources.disable_implicit_console { attach_console_devices( @@ -2379,6 +2413,41 @@ fn attach_rng_device( Ok(()) } +#[cfg(not(feature = "tee"))] +#[cfg(all(feature = "vhost-user", target_os = "linux"))] +fn attach_vhost_user_device( + vmm: &mut Vmm, + event_manager: &mut EventManager, + intc: IrqChip, + device_config: &VhostUserDeviceConfig, +) -> std::result::Result<(), StartMicrovmError> { + use self::StartMicrovmError::*; + + let device_name = device_config + .name + .clone() + .unwrap_or_else(|| format!("vhost-user-{}", device_config.device_type)); + + let device = Arc::new(Mutex::new( + devices::virtio::VhostUserDevice::new( + &device_config.socket_path, + device_config.device_type, + device_name.clone(), + device_config.num_queues, + &device_config.queue_sizes, + ) + .map_err(|e| RegisterVhostUserDevice(device_manager::mmio::Error::VhostUserDevice(e)))?, + )); + + event_manager + .add_subscriber(device.clone()) + .map_err(RegisterEvent)?; + + attach_mmio_device(vmm, device_name, intc.clone(), device).map_err(RegisterVhostUserDevice)?; + + Ok(()) +} + #[cfg(feature = "gpu")] #[allow(clippy::too_many_arguments)] fn attach_gpu_device( diff --git a/src/vmm/src/device_manager/kvm/mmio.rs b/src/vmm/src/device_manager/kvm/mmio.rs index e739afb42..f2e0a687b 100644 --- a/src/vmm/src/device_manager/kvm/mmio.rs +++ b/src/vmm/src/device_manager/kvm/mmio.rs @@ -41,6 +41,9 @@ pub enum Error { DeviceNotFound, /// Failed to update the mmio device. UpdateFailed, + /// Failed to create vhost-user device. + #[cfg(all(feature = "vhost-user", target_os = "linux"))] + VhostUserDevice(io::Error), } impl fmt::Display for Error { @@ -59,6 +62,8 @@ impl fmt::Display for Error { Error::RegisterIrqFd(ref e) => write!(f, "failed to register irqfd: {e}"), Error::DeviceNotFound => write!(f, "the device couldn't be found"), Error::UpdateFailed => write!(f, "failed to update the mmio device"), + #[cfg(all(feature = "vhost-user", target_os = "linux"))] + Error::VhostUserDevice(ref e) => write!(f, "failed to create vhost-user device: {e}"), } } } From b107ca81aa72a0dc78bc9e4dcd79cd9520e34805 Mon Sep 17 00:00:00 2001 From: Dorinda Bassey Date: Wed, 4 Feb 2026 10:18:12 +0100 Subject: [PATCH 05/14] examples: Add vhost-user RNG support to chroot_vm Adds --vhost-user-rng command line option to specify a vhost-user RNG backend socket path. When provided, the VM uses the external vhost-user RNG device instead of the built-in virtio-rng implementation. Example usage: ./examples/chroot_vm \ --vhost-user-rng=/tmp/vhost-rng.sock0 \ / /bin/sh -c "head -c 32 /dev/hwrng | xxd" Signed-off-by: Dorinda Bassey --- examples/chroot_vm.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/examples/chroot_vm.c b/examples/chroot_vm.c index 4c25dabd3..18b988104 100644 --- a/examples/chroot_vm.c +++ b/examples/chroot_vm.c @@ -38,7 +38,8 @@ static void print_help(char *const name) " --log=PATH Write libkrun log to file or named pipe at PATH\n" " --color-log=PATH Write libkrun log to file or named pipe at PATH, use color\n" " --net=NET_MODE Set network mode\n" - " --passt-socket=PATH Instead of starting passt, connect to passt socket at PATH" + " --passt-socket=PATH Instead of starting passt, connect to passt socket at PATH\n" + " --vhost-user-rng=PATH Use vhost-user RNG backend at socket PATH\n" "NET_MODE can be either TSI (default) or PASST\n" "\n" "NEWROOT: the root directory of the vm\n" @@ -48,12 +49,23 @@ static void print_help(char *const name) ); } +static bool check_krun_error(int err, const char *msg) +{ + if (err) { + errno = -err; + perror(msg); + return false; + } + return true; +} + static const struct option long_options[] = { { "help", no_argument, NULL, 'h' }, { "log", required_argument, NULL, 'L' }, { "color-log", required_argument, NULL, 'C' }, { "net_mode", required_argument, NULL, 'N' }, { "passt-socket", required_argument, NULL, 'P' }, + { "vhost-user-rng", required_argument, NULL, 'V' }, { NULL, 0, NULL, 0 } }; @@ -63,6 +75,7 @@ struct cmdline { uint32_t log_style; enum net_mode net_mode; char const *passt_socket_path; + char const *vhost_user_rng_socket; char const *new_root; char *const *guest_argv; }; @@ -89,6 +102,7 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline) .show_help = false, .net_mode = NET_MODE_TSI, .passt_socket_path = NULL, + .vhost_user_rng_socket = NULL, .new_root = NULL, .guest_argv = NULL, .log_target = KRUN_LOG_TARGET_DEFAULT, @@ -124,6 +138,9 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline) case 'P': cmdline->passt_socket_path = optarg; break; + case 'V': + cmdline->vhost_user_rng_socket = optarg; + break; case '?': return false; default: @@ -249,6 +266,19 @@ int main(int argc, char *const argv[]) return -1; } + // Configure vhost-user RNG if requested + if (cmdline.vhost_user_rng_socket != NULL) { + // Test sentinel-terminated array: auto-detect queue count, use custom size + uint16_t custom_sizes[] = {512, 0}; // 0 = sentinel terminator + + if (!check_krun_error(krun_add_vhost_user_device(ctx_id, KRUN_VIRTIO_DEVICE_RNG, + cmdline.vhost_user_rng_socket, NULL, 0, custom_sizes), + "Error adding vhost-user RNG device")) { + return -1; + } + printf("Using vhost-user RNG backend at %s (custom queue size: 512)\n", cmdline.vhost_user_rng_socket); + } + // Raise RLIMIT_NOFILE to the maximum allowed to create some room for virtio-fs getrlimit(RLIMIT_NOFILE, &rlim); rlim.rlim_cur = rlim.rlim_max; From c7c0b19b73e60639addc2fb793389a696e3e729b Mon Sep 17 00:00:00 2001 From: Dorinda Bassey Date: Thu, 26 Mar 2026 14:45:08 +0100 Subject: [PATCH 06/14] devices/vhost: Add config space support for vhost-user devices Implement read_config() for vhost-user devices using the VHOST_USER_GET_CONFIG protocol message. This enables vhost-user devices to expose their configuration space to the guest. This provides a general mechanism for any vhost-user device that needs to expose configuration to the guest (e.g., virtio-snd). Signed-off-by: Dorinda Bassey --- src/devices/src/virtio/vhost_user/device.rs | 47 +++++++++++++++------ 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/src/devices/src/virtio/vhost_user/device.rs b/src/devices/src/virtio/vhost_user/device.rs index 3d4e4e59f..331296952 100644 --- a/src/devices/src/virtio/vhost_user/device.rs +++ b/src/devices/src/virtio/vhost_user/device.rs @@ -15,10 +15,11 @@ use log::{debug, error, warn}; use polly::event_manager::{EventManager, Subscriber}; use utils::epoll::{EpollEvent, EventSet}; use utils::eventfd::{EventFd, EFD_NONBLOCK}; -use vmm_sys_util::eventfd::EventFd as VhostEventFd; +use vhost::vhost_user::message::VhostUserConfigFlags; use vhost::vhost_user::{Frontend, VhostUserFrontend, VhostUserProtocolFeatures}; use vhost::{VhostBackend, VhostUserMemoryRegionInfo, VringConfigData}; use vm_memory::{Address, GuestMemory, GuestMemoryMmap, GuestMemoryRegion}; +use vmm_sys_util::eventfd::EventFd as VhostEventFd; use crate::virtio::{ ActivateError, ActivateResult, DeviceQueue, DeviceState, InterruptTransport, QueueConfig, @@ -150,14 +151,6 @@ impl VhostUserDevice { num_queues as usize }; - debug!( - "{}: using {} queues (requested: {}, sizes provided: {})", - device_name, - actual_num_queues, - num_queues, - queue_sizes.len() - ); - let default_size = queue_sizes.last().copied().unwrap_or(256); let queue_configs: Vec<_> = (0..actual_num_queues) .map(|i| { @@ -368,10 +361,40 @@ impl VirtioDevice for VhostUserDevice { } fn read_config(&self, offset: u64, data: &mut [u8]) { - // For now, configuration space reads are not supported - // This can be extended using VHOST_USER_GET_CONFIG + // Fetch config from backend on every read (same as QEMU/crosvm) + // No caching to avoid invalidation issues + if self.has_protocol_features { + if let Ok(mut frontend) = self.frontend.lock() { + match frontend.get_config( + offset as u32, + data.len() as u32, + VhostUserConfigFlags::empty(), + data, + ) { + Ok((_, returned_buf)) => { + if data.len() <= returned_buf.len() { + data.copy_from_slice(&returned_buf[..data.len()]); + debug!( + "{}: read {} bytes from config at offset {}", + self.device_name, + data.len(), + offset + ); + return; + } + } + Err(e) => { + debug!( + "{}: failed to read config from backend: {:?}", + self.device_name, e + ); + } + } + } + } + debug!( - "{}: config read at offset {} (not yet implemented)", + "{}: config read at offset {} returning zeros (backend not available)", self.device_name, offset ); data.fill(0); From 9fa0ed7839b43718ed9ef069624b082c82727049 Mon Sep 17 00:00:00 2001 From: Dorinda Bassey Date: Thu, 26 Mar 2026 15:01:18 +0100 Subject: [PATCH 07/14] libkrun: Add vhost-user sound device constants and support Add constants and device-specific configuration for virtio-snd (vhost-user sound device). Signed-off-by: Dorinda Bassey --- include/libkrun.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/libkrun.h b/include/libkrun.h index 2608dedc2..4afa13967 100644 --- a/include/libkrun.h +++ b/include/libkrun.h @@ -762,6 +762,13 @@ int32_t krun_set_snd_device(uint32_t ctx_id, bool enable); #define KRUN_VHOST_USER_RNG_NUM_QUEUES 1 #define KRUN_VHOST_USER_RNG_QUEUE_SIZES ((uint16_t[]){256}) +/** + * Vhost-user sound device default queue configuration. + * Sound device uses 4 queues: control (idx 0), event (idx 1), TX/playback (idx 2), RX/capture (idx 3). + */ +#define KRUN_VHOST_USER_SND_NUM_QUEUES 4 +#define KRUN_VHOST_USER_SND_QUEUE_SIZES ((uint16_t[]){64, 64, 64, 64}) + /** * Add a vhost-user device to the VM. * From 841610b445e5a6f89c43087a2eb388c4dd05c29e Mon Sep 17 00:00:00 2001 From: Dorinda Bassey Date: Thu, 26 Mar 2026 15:03:32 +0100 Subject: [PATCH 08/14] examples: Add vhost-user sound device support to chroot_vm Add --vhost-user-snd option to chroot_vm example, allowing VMs to use external vhost-user sound backends for audio playback and capture. Usage: ./chroot_vm --vhost-user-snd=/path/to/sound.sock ... Note: In the guest, the ALSA default device may not work without additional configuration. Use explicit device specification: aplay -D hw:0,0 /path/to/audio.wav Or create /etc/asound.conf in the guest: defaults.pcm.card 0 defaults.pcm.device 0 pcm.!default { type hw card 0 device 0 } Tested with vhost-device-sound backend using PipeWire. Signed-off-by: Dorinda Bassey --- examples/chroot_vm.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/examples/chroot_vm.c b/examples/chroot_vm.c index 18b988104..f5d4d8682 100644 --- a/examples/chroot_vm.c +++ b/examples/chroot_vm.c @@ -40,6 +40,7 @@ static void print_help(char *const name) " --net=NET_MODE Set network mode\n" " --passt-socket=PATH Instead of starting passt, connect to passt socket at PATH\n" " --vhost-user-rng=PATH Use vhost-user RNG backend at socket PATH\n" + " --vhost-user-snd=PATH Use vhost-user sound backend at socket PATH\n" "NET_MODE can be either TSI (default) or PASST\n" "\n" "NEWROOT: the root directory of the vm\n" @@ -66,6 +67,7 @@ static const struct option long_options[] = { { "net_mode", required_argument, NULL, 'N' }, { "passt-socket", required_argument, NULL, 'P' }, { "vhost-user-rng", required_argument, NULL, 'V' }, + { "vhost-user-snd", required_argument, NULL, 'S' }, { NULL, 0, NULL, 0 } }; @@ -76,6 +78,7 @@ struct cmdline { enum net_mode net_mode; char const *passt_socket_path; char const *vhost_user_rng_socket; + char const *vhost_user_snd_socket; char const *new_root; char *const *guest_argv; }; @@ -103,6 +106,7 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline) .net_mode = NET_MODE_TSI, .passt_socket_path = NULL, .vhost_user_rng_socket = NULL, + .vhost_user_snd_socket = NULL, .new_root = NULL, .guest_argv = NULL, .log_target = KRUN_LOG_TARGET_DEFAULT, @@ -141,6 +145,9 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline) case 'V': cmdline->vhost_user_rng_socket = optarg; break; + case 'S': + cmdline->vhost_user_snd_socket = optarg; + break; case '?': return false; default: @@ -279,6 +286,18 @@ int main(int argc, char *const argv[]) printf("Using vhost-user RNG backend at %s (custom queue size: 512)\n", cmdline.vhost_user_rng_socket); } + // Configure vhost-user sound if requested + if (cmdline.vhost_user_snd_socket != NULL) { + if (!check_krun_error(krun_add_vhost_user_device(ctx_id, KRUN_VIRTIO_DEVICE_SND, + cmdline.vhost_user_snd_socket, NULL, + KRUN_VHOST_USER_SND_NUM_QUEUES, + KRUN_VHOST_USER_SND_QUEUE_SIZES), + "Error adding vhost-user sound device")) { + return -1; + } + printf("Using vhost-user sound backend at %s\n", cmdline.vhost_user_snd_socket); + } + // Raise RLIMIT_NOFILE to the maximum allowed to create some room for virtio-fs getrlimit(RLIMIT_NOFILE, &rlim); rlim.rlim_cur = rlim.rlim_max; From d3bad244086624b779b29cc68633273e246c188d Mon Sep 17 00:00:00 2001 From: Dorinda Bassey Date: Tue, 31 Mar 2026 17:24:46 +0200 Subject: [PATCH 09/14] libkrun: Add API constants and example for vhost-user vsock Add public API constants for vhost-user vsock devices and example usage in chroot_vm. The underlying support already exists via the generic VhostUserDevice wrapper. Example integration: - Added --vhost-user-vsock option to chroot_vm - Calls krun_disable_implicit_vsock() to avoid conflict with built-in device - Skips TSI port mapping when vhost-user-vsock is active Signed-off-by: Dorinda Bassey --- examples/chroot_vm.c | 28 +++++++++++++++++++++++++++- include/libkrun.h | 8 ++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/examples/chroot_vm.c b/examples/chroot_vm.c index f5d4d8682..4b0393e1a 100644 --- a/examples/chroot_vm.c +++ b/examples/chroot_vm.c @@ -41,6 +41,7 @@ static void print_help(char *const name) " --passt-socket=PATH Instead of starting passt, connect to passt socket at PATH\n" " --vhost-user-rng=PATH Use vhost-user RNG backend at socket PATH\n" " --vhost-user-snd=PATH Use vhost-user sound backend at socket PATH\n" + " --vhost-user-vsock=PATH Use vhost-user vsock backend at socket PATH\n" "NET_MODE can be either TSI (default) or PASST\n" "\n" "NEWROOT: the root directory of the vm\n" @@ -68,6 +69,7 @@ static const struct option long_options[] = { { "passt-socket", required_argument, NULL, 'P' }, { "vhost-user-rng", required_argument, NULL, 'V' }, { "vhost-user-snd", required_argument, NULL, 'S' }, + { "vhost-user-vsock", required_argument, NULL, 'K' }, { NULL, 0, NULL, 0 } }; @@ -79,6 +81,7 @@ struct cmdline { char const *passt_socket_path; char const *vhost_user_rng_socket; char const *vhost_user_snd_socket; + char const *vhost_user_vsock_socket; char const *new_root; char *const *guest_argv; }; @@ -107,6 +110,7 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline) .passt_socket_path = NULL, .vhost_user_rng_socket = NULL, .vhost_user_snd_socket = NULL, + .vhost_user_vsock_socket = NULL, .new_root = NULL, .guest_argv = NULL, .log_target = KRUN_LOG_TARGET_DEFAULT, @@ -148,6 +152,9 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline) case 'S': cmdline->vhost_user_snd_socket = optarg; break; + case 'K': + cmdline->vhost_user_vsock_socket = optarg; + break; case '?': return false; default: @@ -298,6 +305,24 @@ int main(int argc, char *const argv[]) printf("Using vhost-user sound backend at %s\n", cmdline.vhost_user_snd_socket); } + // Configure vhost-user vsock if requested + if (cmdline.vhost_user_vsock_socket != NULL) { + // Disable the implicit vsock device to avoid conflict + if (!check_krun_error(krun_disable_implicit_vsock(ctx_id), + "Error disabling implicit vsock")) { + return -1; + } + + if (!check_krun_error(krun_add_vhost_user_device(ctx_id, KRUN_VIRTIO_DEVICE_VSOCK, + cmdline.vhost_user_vsock_socket, NULL, + KRUN_VHOST_USER_VSOCK_NUM_QUEUES, + KRUN_VHOST_USER_VSOCK_QUEUE_SIZES), + "Error adding vhost-user vsock device")) { + return -1; + } + printf("Using vhost-user vsock backend at %s\n", cmdline.vhost_user_vsock_socket); + } + // Raise RLIMIT_NOFILE to the maximum allowed to create some room for virtio-fs getrlimit(RLIMIT_NOFILE, &rlim); rlim.rlim_cur = rlim.rlim_max; @@ -318,7 +343,8 @@ int main(int argc, char *const argv[]) } // Map port 18000 in the host to 8000 in the guest (if networking uses TSI) - if (cmdline.net_mode == NET_MODE_TSI) { + // Skip port mapping when using vhost-user-vsock (TSI requires built-in vsock) + if (cmdline.net_mode == NET_MODE_TSI && cmdline.vhost_user_vsock_socket == NULL) { if (err = krun_set_port_map(ctx_id, &port_map[0])) { errno = -err; perror("Error configuring port map"); diff --git a/include/libkrun.h b/include/libkrun.h index 4afa13967..c91601134 100644 --- a/include/libkrun.h +++ b/include/libkrun.h @@ -752,6 +752,7 @@ int32_t krun_set_snd_device(uint32_t ctx_id, bool enable); * These correspond to virtio device type IDs for devices. */ #define KRUN_VIRTIO_DEVICE_RNG 4 +#define KRUN_VIRTIO_DEVICE_VSOCK 19 #define KRUN_VIRTIO_DEVICE_SND 25 #define KRUN_VIRTIO_DEVICE_CAN 36 @@ -769,6 +770,13 @@ int32_t krun_set_snd_device(uint32_t ctx_id, bool enable); #define KRUN_VHOST_USER_SND_NUM_QUEUES 4 #define KRUN_VHOST_USER_SND_QUEUE_SIZES ((uint16_t[]){64, 64, 64, 64}) +/** + * Vhost-user vsock device default queue configuration. + * Vsock device uses 3 queues: RX (idx 0), TX (idx 1), event (idx 2). + */ +#define KRUN_VHOST_USER_VSOCK_NUM_QUEUES 3 +#define KRUN_VHOST_USER_VSOCK_QUEUE_SIZES ((uint16_t[]){128, 128, 128}) + /** * Add a vhost-user device to the VM. * From 3a8fd4aca5dbd953dd8b9a10d9d5794a8d2f3587 Mon Sep 17 00:00:00 2001 From: Dorinda Bassey Date: Thu, 2 Apr 2026 17:08:19 +0200 Subject: [PATCH 10/14] libkrun: Add API constants and example for vhost-user CAN Add public API constants for vhost-user CAN devices and example usage in chroot_vm. The underlying support already exists via the generic VhostUserDevice wrapper. Example integration: - Added --vhost-user-can option to chroot_vm - Uses 3 queues (TX, RX, control) with 64-entry queue sizes - Follows same pattern as other vhost-user device integrations Signed-off-by: Dorinda Bassey --- examples/chroot_vm.c | 19 +++++++++++++++++++ include/libkrun.h | 7 +++++++ 2 files changed, 26 insertions(+) diff --git a/examples/chroot_vm.c b/examples/chroot_vm.c index 4b0393e1a..1f8468ca0 100644 --- a/examples/chroot_vm.c +++ b/examples/chroot_vm.c @@ -42,6 +42,7 @@ static void print_help(char *const name) " --vhost-user-rng=PATH Use vhost-user RNG backend at socket PATH\n" " --vhost-user-snd=PATH Use vhost-user sound backend at socket PATH\n" " --vhost-user-vsock=PATH Use vhost-user vsock backend at socket PATH\n" + " --vhost-user-can=PATH Use vhost-user CAN backend at socket PATH\n" "NET_MODE can be either TSI (default) or PASST\n" "\n" "NEWROOT: the root directory of the vm\n" @@ -70,6 +71,7 @@ static const struct option long_options[] = { { "vhost-user-rng", required_argument, NULL, 'V' }, { "vhost-user-snd", required_argument, NULL, 'S' }, { "vhost-user-vsock", required_argument, NULL, 'K' }, + { "vhost-user-can", required_argument, NULL, 'A' }, { NULL, 0, NULL, 0 } }; @@ -82,6 +84,7 @@ struct cmdline { char const *vhost_user_rng_socket; char const *vhost_user_snd_socket; char const *vhost_user_vsock_socket; + char const *vhost_user_can_socket; char const *new_root; char *const *guest_argv; }; @@ -111,6 +114,7 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline) .vhost_user_rng_socket = NULL, .vhost_user_snd_socket = NULL, .vhost_user_vsock_socket = NULL, + .vhost_user_can_socket = NULL, .new_root = NULL, .guest_argv = NULL, .log_target = KRUN_LOG_TARGET_DEFAULT, @@ -155,6 +159,9 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline) case 'K': cmdline->vhost_user_vsock_socket = optarg; break; + case 'A': + cmdline->vhost_user_can_socket = optarg; + break; case '?': return false; default: @@ -323,6 +330,18 @@ int main(int argc, char *const argv[]) printf("Using vhost-user vsock backend at %s\n", cmdline.vhost_user_vsock_socket); } + // Configure vhost-user CAN if requested + if (cmdline.vhost_user_can_socket != NULL) { + if (!check_krun_error(krun_add_vhost_user_device(ctx_id, KRUN_VIRTIO_DEVICE_CAN, + cmdline.vhost_user_can_socket, NULL, + KRUN_VHOST_USER_CAN_NUM_QUEUES, + KRUN_VHOST_USER_CAN_QUEUE_SIZES), + "Error adding vhost-user CAN device")) { + return -1; + } + printf("Using vhost-user CAN backend at %s\n", cmdline.vhost_user_can_socket); + } + // Raise RLIMIT_NOFILE to the maximum allowed to create some room for virtio-fs getrlimit(RLIMIT_NOFILE, &rlim); rlim.rlim_cur = rlim.rlim_max; diff --git a/include/libkrun.h b/include/libkrun.h index c91601134..77d661f70 100644 --- a/include/libkrun.h +++ b/include/libkrun.h @@ -777,6 +777,13 @@ int32_t krun_set_snd_device(uint32_t ctx_id, bool enable); #define KRUN_VHOST_USER_VSOCK_NUM_QUEUES 3 #define KRUN_VHOST_USER_VSOCK_QUEUE_SIZES ((uint16_t[]){128, 128, 128}) +/** + * Vhost-user CAN device default queue configuration. + * CAN device uses 3 queues: TX (idx 0), RX (idx 1), control (idx 2). + */ +#define KRUN_VHOST_USER_CAN_NUM_QUEUES 3 +#define KRUN_VHOST_USER_CAN_QUEUE_SIZES ((uint16_t[]){64, 64, 64}) + /** * Add a vhost-user device to the VM. * From 2512a06561def4e6fd3fa9f5abd2b702e3783f7f Mon Sep 17 00:00:00 2001 From: Dorinda Bassey Date: Mon, 13 Apr 2026 14:17:22 +0200 Subject: [PATCH 11/14] libkrun: Add API constants and example for vhost-user console Add public API constants for vhost-user console devices and example usage in chroot_vm. The underlying support already exists via the generic VhostUserDevice wrapper. Console devices require 4 queues for multiport support (receiveq, transmitq, control receiveq, control transmitq). Example integration: - Added --vhost-user-console option to chroot_vm - Available as /dev/hvc1 in the guest (implicit console uses hvc0) - Can be used with vhost-device-console for remote console access Example workflow: Terminal 1: Start vhost-device-console backend vhost-device-console --socket-path=/tmp/console.sock \ --socket-count=1 --tcp-port=12346 --backend=network Terminal 2: Connect to console (before starting VM) nc localhost 12346 Terminal 3: Start VM with vhost-user console chroot_vm --vhost-user-console=/tmp/console.sock0 / /bin/sh In guest: Test with echo "hello" > /dev/hvc1 Signed-off-by: Dorinda Bassey --- examples/chroot_vm.c | 20 ++++++++++++++++++++ include/libkrun.h | 9 +++++++++ 2 files changed, 29 insertions(+) diff --git a/examples/chroot_vm.c b/examples/chroot_vm.c index 1f8468ca0..862358535 100644 --- a/examples/chroot_vm.c +++ b/examples/chroot_vm.c @@ -43,6 +43,7 @@ static void print_help(char *const name) " --vhost-user-snd=PATH Use vhost-user sound backend at socket PATH\n" " --vhost-user-vsock=PATH Use vhost-user vsock backend at socket PATH\n" " --vhost-user-can=PATH Use vhost-user CAN backend at socket PATH\n" + " --vhost-user-console=PATH Use vhost-user console backend at socket PATH\n" "NET_MODE can be either TSI (default) or PASST\n" "\n" "NEWROOT: the root directory of the vm\n" @@ -72,6 +73,7 @@ static const struct option long_options[] = { { "vhost-user-snd", required_argument, NULL, 'S' }, { "vhost-user-vsock", required_argument, NULL, 'K' }, { "vhost-user-can", required_argument, NULL, 'A' }, + { "vhost-user-console", required_argument, NULL, 'O' }, { NULL, 0, NULL, 0 } }; @@ -85,6 +87,7 @@ struct cmdline { char const *vhost_user_snd_socket; char const *vhost_user_vsock_socket; char const *vhost_user_can_socket; + char const *vhost_user_console_socket; char const *new_root; char *const *guest_argv; }; @@ -115,6 +118,7 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline) .vhost_user_snd_socket = NULL, .vhost_user_vsock_socket = NULL, .vhost_user_can_socket = NULL, + .vhost_user_console_socket = NULL, .new_root = NULL, .guest_argv = NULL, .log_target = KRUN_LOG_TARGET_DEFAULT, @@ -162,6 +166,9 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline) case 'A': cmdline->vhost_user_can_socket = optarg; break; + case 'O': + cmdline->vhost_user_console_socket = optarg; + break; case '?': return false; default: @@ -342,6 +349,19 @@ int main(int argc, char *const argv[]) printf("Using vhost-user CAN backend at %s\n", cmdline.vhost_user_can_socket); } + // Configure vhost-user console if requested + if (cmdline.vhost_user_console_socket != NULL) { + if (!check_krun_error(krun_add_vhost_user_device(ctx_id, KRUN_VIRTIO_DEVICE_CONSOLE, + cmdline.vhost_user_console_socket, NULL, + KRUN_VHOST_USER_CONSOLE_NUM_QUEUES, + KRUN_VHOST_USER_CONSOLE_QUEUE_SIZES), + "Error adding vhost-user console device")) { + return -1; + } + printf("Using vhost-user console backend at %s (available as /dev/hvc1 in guest)\n", cmdline.vhost_user_console_socket); + printf("Test with: echo 'hello' > /dev/hvc1\n"); + } + // Raise RLIMIT_NOFILE to the maximum allowed to create some room for virtio-fs getrlimit(RLIMIT_NOFILE, &rlim); rlim.rlim_cur = rlim.rlim_max; diff --git a/include/libkrun.h b/include/libkrun.h index 77d661f70..9703c6c29 100644 --- a/include/libkrun.h +++ b/include/libkrun.h @@ -751,11 +751,20 @@ int32_t krun_set_snd_device(uint32_t ctx_id, bool enable); * Vhost-user device types. * These correspond to virtio device type IDs for devices. */ +#define KRUN_VIRTIO_DEVICE_CONSOLE 3 #define KRUN_VIRTIO_DEVICE_RNG 4 #define KRUN_VIRTIO_DEVICE_VSOCK 19 #define KRUN_VIRTIO_DEVICE_SND 25 #define KRUN_VIRTIO_DEVICE_CAN 36 +/** + * Vhost-user console device default queue configuration. + * Console device uses 4 queues for multiport support: + * receiveq (idx 0), transmitq (idx 1), control receiveq (idx 2), control transmitq (idx 3). + */ +#define KRUN_VHOST_USER_CONSOLE_NUM_QUEUES 4 +#define KRUN_VHOST_USER_CONSOLE_QUEUE_SIZES ((uint16_t[]){128, 128, 64, 64}) + /** * Vhost-user RNG device default queue configuration. * Use these when you want explicit defaults instead of auto-detection. From b3e8e8e4134ebccdda3fbb314d8caa3b05214aa8 Mon Sep 17 00:00:00 2001 From: Dorinda Bassey Date: Fri, 17 Apr 2026 03:20:56 +0200 Subject: [PATCH 12/14] devices/vhost: Implement write_config for dynamic config Implement write_config in VhostUserDevice to support virtio devices that use dynamic configuration space, such as virtio-input. Some virtio devices like virtio-input use a request-response pattern for device discovery. Without write_config support, the backend never receives the virtio_input config values, causing device initialization to fail. This implementation uses VHOST_USER_SET_CONFIG to forward writes to the backend. Signed-off-by: Dorinda Bassey --- src/devices/src/virtio/vhost_user/device.rs | 34 ++++++++++++++++----- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/devices/src/virtio/vhost_user/device.rs b/src/devices/src/virtio/vhost_user/device.rs index 331296952..973efce43 100644 --- a/src/devices/src/virtio/vhost_user/device.rs +++ b/src/devices/src/virtio/vhost_user/device.rs @@ -400,13 +400,33 @@ impl VirtioDevice for VhostUserDevice { data.fill(0); } - fn write_config(&mut self, offset: u64, _data: &[u8]) { - // For now, configuration space writes are not supported - // This can be extended using VHOST_USER_SET_CONFIG - debug!( - "{}: config write at offset {} (not yet implemented)", - self.device_name, offset - ); + fn write_config(&mut self, offset: u64, data: &[u8]) { + if !self.has_protocol_features { + debug!( + "{}: config write at offset {} skipped (no protocol features)", + self.device_name, offset + ); + return; + } + + if let Ok(mut frontend) = self.frontend.lock() { + match frontend.set_config(offset as u32, VhostUserConfigFlags::empty(), data) { + Ok(_) => { + debug!( + "{}: wrote {} bytes to config at offset {}", + self.device_name, + data.len(), + offset + ); + } + Err(e) => { + warn!( + "{}: failed to write config at offset {}: {:?}", + self.device_name, offset, e + ); + } + } + } } fn activate( From 762d18894c7a72c5a5c09da923a21ff8d6edb8b7 Mon Sep 17 00:00:00 2001 From: Dorinda Bassey Date: Fri, 17 Apr 2026 03:30:06 +0200 Subject: [PATCH 13/14] libkrun: Add API constants and example for vhost-user input Add public API constants for vhost-user input devices and example usage in chroot_vm. The underlying support already exists via the generic VhostUserDevice wrapper. Console devices require 2 queues (event, status). Example integration: - Added --vhost-user-console option to chroot_vm Example usage: On host, start vhost-device-input backend vhost-device-input --socket-path /tmp/input.sock \ --event-list /dev/input/event3 In libkrun ./chroot_vm --vhost-user-input=/tmp/input.sock0 / /bin/sh The guest will see the input device as /dev/input/event0 and can read input events from the host device. Signed-off-by: Dorinda Bassey --- examples/chroot_vm.c | 19 +++++++++++++++++++ include/libkrun.h | 8 ++++++++ 2 files changed, 27 insertions(+) diff --git a/examples/chroot_vm.c b/examples/chroot_vm.c index 862358535..d202fb59b 100644 --- a/examples/chroot_vm.c +++ b/examples/chroot_vm.c @@ -40,6 +40,7 @@ static void print_help(char *const name) " --net=NET_MODE Set network mode\n" " --passt-socket=PATH Instead of starting passt, connect to passt socket at PATH\n" " --vhost-user-rng=PATH Use vhost-user RNG backend at socket PATH\n" + " --vhost-user-input=PATH Use vhost-user input backend at socket PATH\n" " --vhost-user-snd=PATH Use vhost-user sound backend at socket PATH\n" " --vhost-user-vsock=PATH Use vhost-user vsock backend at socket PATH\n" " --vhost-user-can=PATH Use vhost-user CAN backend at socket PATH\n" @@ -70,6 +71,7 @@ static const struct option long_options[] = { { "net_mode", required_argument, NULL, 'N' }, { "passt-socket", required_argument, NULL, 'P' }, { "vhost-user-rng", required_argument, NULL, 'V' }, + { "vhost-user-input", required_argument, NULL, 'I' }, { "vhost-user-snd", required_argument, NULL, 'S' }, { "vhost-user-vsock", required_argument, NULL, 'K' }, { "vhost-user-can", required_argument, NULL, 'A' }, @@ -84,6 +86,7 @@ struct cmdline { enum net_mode net_mode; char const *passt_socket_path; char const *vhost_user_rng_socket; + char const *vhost_user_input_socket; char const *vhost_user_snd_socket; char const *vhost_user_vsock_socket; char const *vhost_user_can_socket; @@ -115,6 +118,7 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline) .net_mode = NET_MODE_TSI, .passt_socket_path = NULL, .vhost_user_rng_socket = NULL, + .vhost_user_input_socket = NULL, .vhost_user_snd_socket = NULL, .vhost_user_vsock_socket = NULL, .vhost_user_can_socket = NULL, @@ -157,6 +161,9 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline) case 'V': cmdline->vhost_user_rng_socket = optarg; break; + case 'I': + cmdline->vhost_user_input_socket = optarg; + break; case 'S': cmdline->vhost_user_snd_socket = optarg; break; @@ -307,6 +314,18 @@ int main(int argc, char *const argv[]) printf("Using vhost-user RNG backend at %s (custom queue size: 512)\n", cmdline.vhost_user_rng_socket); } + // Configure vhost-user input if requested + if (cmdline.vhost_user_input_socket != NULL) { + if (!check_krun_error(krun_add_vhost_user_device(ctx_id, KRUN_VIRTIO_DEVICE_INPUT, + cmdline.vhost_user_input_socket, NULL, + KRUN_VHOST_USER_INPUT_NUM_QUEUES, + KRUN_VHOST_USER_INPUT_QUEUE_SIZES), + "Error adding vhost-user input device")) { + return -1; + } + printf("Using vhost-user input backend at %s\n", cmdline.vhost_user_input_socket); + } + // Configure vhost-user sound if requested if (cmdline.vhost_user_snd_socket != NULL) { if (!check_krun_error(krun_add_vhost_user_device(ctx_id, KRUN_VIRTIO_DEVICE_SND, diff --git a/include/libkrun.h b/include/libkrun.h index 9703c6c29..3055ef117 100644 --- a/include/libkrun.h +++ b/include/libkrun.h @@ -753,6 +753,7 @@ int32_t krun_set_snd_device(uint32_t ctx_id, bool enable); */ #define KRUN_VIRTIO_DEVICE_CONSOLE 3 #define KRUN_VIRTIO_DEVICE_RNG 4 +#define KRUN_VIRTIO_DEVICE_INPUT 18 #define KRUN_VIRTIO_DEVICE_VSOCK 19 #define KRUN_VIRTIO_DEVICE_SND 25 #define KRUN_VIRTIO_DEVICE_CAN 36 @@ -772,6 +773,13 @@ int32_t krun_set_snd_device(uint32_t ctx_id, bool enable); #define KRUN_VHOST_USER_RNG_NUM_QUEUES 1 #define KRUN_VHOST_USER_RNG_QUEUE_SIZES ((uint16_t[]){256}) +/** + * Vhost-user input device default queue configuration. + * Input device uses 2 queues: eventq (idx 0), statusq (idx 1). + */ +#define KRUN_VHOST_USER_INPUT_NUM_QUEUES 2 +#define KRUN_VHOST_USER_INPUT_QUEUE_SIZES ((uint16_t[]){1024, 1024}) + /** * Vhost-user sound device default queue configuration. * Sound device uses 4 queues: control (idx 0), event (idx 1), TX/playback (idx 2), RX/capture (idx 3). From ada66372d61dd709eda2003d941c796951e7c368 Mon Sep 17 00:00:00 2001 From: Dorinda Bassey Date: Wed, 29 Apr 2026 14:43:27 +0200 Subject: [PATCH 14/14] libkrun: Add API constants and example for vhost-user rtc Add public API constants for vhost-user rtc devices and example usage in chroot_vm. The underlying support already exists via the generic VhostUserDevice wrapper. RTC devices require 2 queues (requestq, alarmq). Example integration: - Added --vhost-user-rtc option to chroot_vm Example usage: On host, start vhost-device-rtc backend vhost-device-rtc --socket-path /tmp/rtc.sock Run libkrun with RTC device ./chroot_vm --vhost-user-rtc=/tmp/rtc.sock / /bin/sh The RTC device provides: - Accurate time synchronization without NTP - Multiple clock types (UTC, TAI, monotonic) - PTP clocks for precision time protocol - Alarm support for wake-from-suspend Signed-off-by: Dorinda Bassey --- examples/chroot_vm.c | 19 +++++++++++++++++++ include/libkrun.h | 8 ++++++++ 2 files changed, 27 insertions(+) diff --git a/examples/chroot_vm.c b/examples/chroot_vm.c index d202fb59b..b0ab0a05e 100644 --- a/examples/chroot_vm.c +++ b/examples/chroot_vm.c @@ -40,6 +40,7 @@ static void print_help(char *const name) " --net=NET_MODE Set network mode\n" " --passt-socket=PATH Instead of starting passt, connect to passt socket at PATH\n" " --vhost-user-rng=PATH Use vhost-user RNG backend at socket PATH\n" + " --vhost-user-rtc=PATH Use vhost-user RTC backend at socket PATH\n" " --vhost-user-input=PATH Use vhost-user input backend at socket PATH\n" " --vhost-user-snd=PATH Use vhost-user sound backend at socket PATH\n" " --vhost-user-vsock=PATH Use vhost-user vsock backend at socket PATH\n" @@ -71,6 +72,7 @@ static const struct option long_options[] = { { "net_mode", required_argument, NULL, 'N' }, { "passt-socket", required_argument, NULL, 'P' }, { "vhost-user-rng", required_argument, NULL, 'V' }, + { "vhost-user-rtc", required_argument, NULL, 'R' }, { "vhost-user-input", required_argument, NULL, 'I' }, { "vhost-user-snd", required_argument, NULL, 'S' }, { "vhost-user-vsock", required_argument, NULL, 'K' }, @@ -86,6 +88,7 @@ struct cmdline { enum net_mode net_mode; char const *passt_socket_path; char const *vhost_user_rng_socket; + char const *vhost_user_rtc_socket; char const *vhost_user_input_socket; char const *vhost_user_snd_socket; char const *vhost_user_vsock_socket; @@ -118,6 +121,7 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline) .net_mode = NET_MODE_TSI, .passt_socket_path = NULL, .vhost_user_rng_socket = NULL, + .vhost_user_rtc_socket = NULL, .vhost_user_input_socket = NULL, .vhost_user_snd_socket = NULL, .vhost_user_vsock_socket = NULL, @@ -161,6 +165,9 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline) case 'V': cmdline->vhost_user_rng_socket = optarg; break; + case 'R': + cmdline->vhost_user_rtc_socket = optarg; + break; case 'I': cmdline->vhost_user_input_socket = optarg; break; @@ -314,6 +321,18 @@ int main(int argc, char *const argv[]) printf("Using vhost-user RNG backend at %s (custom queue size: 512)\n", cmdline.vhost_user_rng_socket); } + // Configure vhost-user RTC if requested + if (cmdline.vhost_user_rtc_socket != NULL) { + if (!check_krun_error(krun_add_vhost_user_device(ctx_id, KRUN_VIRTIO_DEVICE_RTC, + cmdline.vhost_user_rtc_socket, NULL, + KRUN_VHOST_USER_RTC_NUM_QUEUES, + KRUN_VHOST_USER_RTC_QUEUE_SIZES), + "Error adding vhost-user RTC device")) { + return -1; + } + printf("Using vhost-user RTC backend at %s (available as /dev/ptp* and /dev/rtc* in guest)\n", cmdline.vhost_user_rtc_socket); + } + // Configure vhost-user input if requested if (cmdline.vhost_user_input_socket != NULL) { if (!check_krun_error(krun_add_vhost_user_device(ctx_id, KRUN_VIRTIO_DEVICE_INPUT, diff --git a/include/libkrun.h b/include/libkrun.h index 3055ef117..87d5e1fa1 100644 --- a/include/libkrun.h +++ b/include/libkrun.h @@ -753,6 +753,7 @@ int32_t krun_set_snd_device(uint32_t ctx_id, bool enable); */ #define KRUN_VIRTIO_DEVICE_CONSOLE 3 #define KRUN_VIRTIO_DEVICE_RNG 4 +#define KRUN_VIRTIO_DEVICE_RTC 17 #define KRUN_VIRTIO_DEVICE_INPUT 18 #define KRUN_VIRTIO_DEVICE_VSOCK 19 #define KRUN_VIRTIO_DEVICE_SND 25 @@ -773,6 +774,13 @@ int32_t krun_set_snd_device(uint32_t ctx_id, bool enable); #define KRUN_VHOST_USER_RNG_NUM_QUEUES 1 #define KRUN_VHOST_USER_RNG_QUEUE_SIZES ((uint16_t[]){256}) +/** + * Vhost-user RTC device default queue configuration. + * RTC device uses 2 queues: requestq (idx 0), alarmq (idx 1). + */ +#define KRUN_VHOST_USER_RTC_NUM_QUEUES 2 +#define KRUN_VHOST_USER_RTC_QUEUE_SIZES ((uint16_t[]){1024, 1024}) + /** * Vhost-user input device default queue configuration. * Input device uses 2 queues: eventq (idx 0), statusq (idx 1).