From d7c8c88baacc52eddd47b47c6ceec235e61e0324 Mon Sep 17 00:00:00 2001 From: Alvise Rigo Date: Wed, 8 Nov 2023 13:38:14 +0000 Subject: [PATCH 01/10] Update kvm-ioctls, kvm-bindings, vmm-sys-util, event-manager The following crates have been updated to a newer version kvm-ioctls 0.13.0 kvm-bindings 0.6 vmm-sys-util 0.11.2 event-manager 0.3.0 Signed-off-by: Alvise Rigo Reviewed-by: Timos Ampelikiotis --- Cargo.lock | 40 +++++++++++++++++++------------ src/devices/Cargo.toml | 8 +++---- src/devices/src/virtio/net/tap.rs | 2 +- src/vm-vcpu-ref/Cargo.toml | 6 ++--- src/vm-vcpu/Cargo.toml | 6 ++--- src/vm-vcpu/src/vcpu/mod.rs | 8 +++---- src/vm-vcpu/src/vcpu/regs.rs | 8 +++++-- src/vmm/Cargo.toml | 8 +++---- 8 files changed, 50 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 80689e68..2bb81cc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,17 +89,17 @@ dependencies = [ "vm-device", "vm-memory", "vm-superio", - "vmm-sys-util", + "vmm-sys-util 0.11.2", ] [[package]] name = "event-manager" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "377fa591135fbe23396a18e2655a6d5481bf7c5823cdfa3cc81b01a229cbe640" +checksum = "2bbb5f730c95a458654dee0afb13f1ebb4fc3d7b772789d5f30713ec68fed75d" dependencies = [ "libc", - "vmm-sys-util", + "vmm-sys-util 0.11.2", ] [[package]] @@ -129,22 +129,22 @@ dependencies = [ [[package]] name = "kvm-bindings" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78c049190826fff959994b7c1d8a2930d0a348f1b8f3aa4f9bb34cd5d7f2952" +checksum = "efe70e65a5b092161d17f5005b66e5eefe7a94a70c332e755036fc4af78c4e79" dependencies = [ - "vmm-sys-util", + "vmm-sys-util 0.11.2", ] [[package]] name = "kvm-ioctls" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97422ba48d7ffb66fd4d18130f72ab66f9bbbf791fb7a87b9291cdcfec437593" +checksum = "b8f8dc9c1896e5f144ec5d07169bc29f39a047686d29585a91f30489abfaeb6b" dependencies = [ "kvm-bindings", "libc", - "vmm-sys-util", + "vmm-sys-util 0.11.2", ] [[package]] @@ -265,7 +265,7 @@ dependencies = [ "virtio-device", "virtio-queue", "vm-memory", - "vmm-sys-util", + "vmm-sys-util 0.8.0", ] [[package]] @@ -285,7 +285,7 @@ source = "git+https://github.com/rust-vmm/vm-virtio.git#d8ef45f57b46baa99e80e555 dependencies = [ "log", "vm-memory", - "vmm-sys-util", + "vmm-sys-util 0.8.0", ] [[package]] @@ -339,7 +339,7 @@ dependencies = [ "vm-device", "vm-memory", "vm-vcpu-ref", - "vmm-sys-util", + "vmm-sys-util 0.11.2", ] [[package]] @@ -351,7 +351,7 @@ dependencies = [ "libc", "thiserror", "vm-memory", - "vmm-sys-util", + "vmm-sys-util 0.11.2", ] [[package]] @@ -372,7 +372,7 @@ dependencies = [ "vm-superio", "vm-vcpu", "vm-vcpu-ref", - "vmm-sys-util", + "vmm-sys-util 0.11.2", ] [[package]] @@ -393,6 +393,16 @@ dependencies = [ "libc", ] +[[package]] +name = "vmm-sys-util" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48b7b084231214f7427041e4220d77dfe726897a6d41fddee450696e66ff2a29" +dependencies = [ + "bitflags", + "libc", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/src/devices/Cargo.toml b/src/devices/Cargo.toml index eb7701ee..d8b141ed 100644 --- a/src/devices/Cargo.toml +++ b/src/devices/Cargo.toml @@ -6,14 +6,14 @@ edition = "2018" license = "Apache-2.0 OR BSD-3-Clause" [dependencies] -event-manager = { version = "0.2.1", features = ["remote_endpoint"] } -kvm-ioctls = "0.11.0" +event-manager = { version = "0.3.0", features = ["remote_endpoint"] } +kvm-ioctls = "0.13.0" libc = "0.2.76" linux-loader = "0.4.0" log = "0.4.6" vm-memory = "0.7.0" vm-superio = "0.5.0" -vmm-sys-util = "0.8.0" +vmm-sys-util = "0.11.2" vm-device = "0.1.0" virtio-blk = { git = "https://github.com/rust-vmm/vm-virtio.git", features = ["backend-stdio"] } @@ -24,4 +24,4 @@ utils = { path = "../utils" } [dev-dependencies] vm-memory = { version = "0.7.0", features = ["backend-mmap"] } -kvm-bindings = "0.5.0" +kvm-bindings = "0.6.0" diff --git a/src/devices/src/virtio/net/tap.rs b/src/devices/src/virtio/net/tap.rs index 0db62d98..236f836a 100644 --- a/src/devices/src/virtio/net/tap.rs +++ b/src/devices/src/virtio/net/tap.rs @@ -14,7 +14,7 @@ use std::os::raw::{c_char, c_int, c_uint, c_ulong}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val}; -use vmm_sys_util::{ioctl_expr, ioctl_ioc_nr, ioctl_iow_nr}; +use vmm_sys_util::{ioctl_ioc_nr, ioctl_iow_nr}; use super::bindings::ifreq; diff --git a/src/vm-vcpu-ref/Cargo.toml b/src/vm-vcpu-ref/Cargo.toml index 2080f4af..073c5beb 100644 --- a/src/vm-vcpu-ref/Cargo.toml +++ b/src/vm-vcpu-ref/Cargo.toml @@ -11,11 +11,11 @@ keywords = ["virt", "kvm", "vm"] [dependencies] thiserror = "1.0.30" -kvm-ioctls = { version = "0.11.0" } -kvm-bindings = { version = "0.5.0", features = ["fam-wrappers"] } +kvm-ioctls = "0.13.0" +kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } vm-memory = "0.7.0" libc = "0.2.76" [dev-dependencies] vm-memory = { version = "0.7.0", features = ["backend-mmap"] } -vmm-sys-util = "0.8.0" +vmm-sys-util = "0.11.2" diff --git a/src/vm-vcpu/Cargo.toml b/src/vm-vcpu/Cargo.toml index a3f878fb..dc16d354 100644 --- a/src/vm-vcpu/Cargo.toml +++ b/src/vm-vcpu/Cargo.toml @@ -9,10 +9,10 @@ edition = "2018" [dependencies] thiserror = "1.0.30" libc = "0.2.76" -kvm-bindings = { version = "0.5.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.11.0" +kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +kvm-ioctls = "0.13.0" vm-memory = "0.7.0" -vmm-sys-util = ">=0.8.0" +vmm-sys-util = "0.11.2" vm-device = "0.1.0" utils = { path = "../utils" } diff --git a/src/vm-vcpu/src/vcpu/mod.rs b/src/vm-vcpu/src/vcpu/mod.rs index 26c15ebc..7341067b 100644 --- a/src/vm-vcpu/src/vcpu/mod.rs +++ b/src/vm-vcpu/src/vcpu/mod.rs @@ -412,7 +412,7 @@ impl KvmVcpu { fn set_state(&mut self, state: VcpuState) -> Result<()> { for reg in state.regs { self.vcpu_fd - .set_one_reg(reg.id, reg.addr) + .set_one_reg(reg.id, reg.addr as u128) .map_err(Error::VcpuSetReg)?; } @@ -458,7 +458,7 @@ impl KvmVcpu { data = (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | PSR_MODE_EL1h).into(); reg_id = arm64_core_reg!(pstate); self.vcpu_fd - .set_one_reg(reg_id, data) + .set_one_reg(reg_id, data as u128) .map_err(Error::VcpuSetReg)?; // Other cpus are powered off initially @@ -470,7 +470,7 @@ impl KvmVcpu { // hack -- can't get this to do offsetof(regs[0]) but luckily it's at offset 0 reg_id = arm64_core_reg!(regs); self.vcpu_fd - .set_one_reg(reg_id, data) + .set_one_reg(reg_id, data as u128) .map_err(Error::VcpuSetReg)?; } @@ -682,7 +682,7 @@ impl KvmVcpu { let data = ip.0; let reg_id = arm64_core_reg!(pc); self.vcpu_fd - .set_one_reg(reg_id, data) + .set_one_reg(reg_id, data as u128) .map_err(Error::VcpuSetReg)?; } } diff --git a/src/vm-vcpu/src/vcpu/regs.rs b/src/vm-vcpu/src/vcpu/regs.rs index 226d70d2..ffbbd111 100644 --- a/src/vm-vcpu/src/vcpu/regs.rs +++ b/src/vm-vcpu/src/vcpu/regs.rs @@ -1,5 +1,6 @@ use kvm_bindings::*; use kvm_ioctls::VcpuFd; +use std::convert::TryInto; use super::Error; @@ -60,7 +61,10 @@ pub fn get_regs_and_mpidr(vcpu_fd: &VcpuFd) -> Result<(Vec, u64), E let mut regs = Vec::with_capacity(reg_id_list.as_slice().len()); for &id in reg_id_list.as_slice() { let addr = vcpu_fd.get_one_reg(id).map_err(Error::VcpuGetReg)?; - regs.push(kvm_one_reg { id, addr }); + regs.push(kvm_one_reg { + id, + addr: addr.try_into().unwrap(), + }); if id == MPIDR_EL1 { mpidr = Some(addr); @@ -72,5 +76,5 @@ pub fn get_regs_and_mpidr(vcpu_fd: &VcpuFd) -> Result<(Vec, u64), E } // unwrap() is safe because of the is_none() check above - Ok((regs, mpidr.unwrap())) + Ok((regs, mpidr.unwrap().try_into().unwrap())) } diff --git a/src/vmm/Cargo.toml b/src/vmm/Cargo.toml index a38c174e..7365b22a 100644 --- a/src/vmm/Cargo.toml +++ b/src/vmm/Cargo.toml @@ -5,15 +5,15 @@ authors = ["rust-vmm AWS maintainers "] edition = "2018" [dependencies] -event-manager = "0.2.1" -kvm-bindings = { version = "0.5.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.11.0" +event-manager = "0.3.0" +kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +kvm-ioctls = "0.13.0" libc = "0.2.91" linux-loader = { version = "0.4.0", features = ["bzimage", "elf"] } vm-allocator = "0.1.0" vm-memory = { version = "0.7.0", features = ["backend-mmap"] } vm-superio = "0.5.0" -vmm-sys-util = "0.8.0" +vmm-sys-util = "0.11.2" vm-device = "0.1.0" devices = { path = "../devices" } From 9cfe242cea2de357065162a1b085c86c4ca228c2 Mon Sep 17 00:00:00 2001 From: Alvise Rigo Date: Wed, 8 Nov 2023 14:56:20 +0000 Subject: [PATCH 02/10] Update to vm-memory 0.13.1 The new version of vm-memory requires to bump the log crate to version 0.4.20 and vm-virtio to 0.10.0. This has various consequences to the rest of the code. In particular: The vmm crate had to be reworked to add a reference to the guest memory in various places as it is not part of the Queue struct anymore. For this reason, it is preferable to wrap the array of Arc with an Arc reference to optimize the various clone() operation that will be added. This has the advantage of reducing the duplication of memory and makes sure the various parts of the code to see the same array of elements (which would not be the case if we clone the array of Arc<> iteself). While at it, the reference to the guest memory was added to some key virtio functions. devices::virtio::{self, block, net} had all to be updated to also accept the Arc reference to the guest memory. Signed-off-by: Alvise Rigo Reviewed-by: Timos Ampelikiotis --- Cargo.lock | 92 +++++++++---------- src/arch/Cargo.toml | 2 +- src/devices/Cargo.toml | 6 +- src/devices/src/virtio/block/device.rs | 42 +++++---- .../src/virtio/block/inorder_handler.rs | 34 ++++--- src/devices/src/virtio/block/queue_handler.rs | 12 ++- src/devices/src/virtio/mod.rs | 32 ++++--- src/devices/src/virtio/net/device.rs | 43 +++++---- src/devices/src/virtio/net/queue_handler.rs | 17 ++-- src/devices/src/virtio/net/simple_handler.rs | 74 +++++++++------ src/vm-vcpu-ref/Cargo.toml | 4 +- src/vm-vcpu/Cargo.toml | 4 +- src/vmm/Cargo.toml | 2 +- src/vmm/src/lib.rs | 38 ++++---- 14 files changed, 224 insertions(+), 178 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2bb81cc4..375f93d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,12 +42,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "clap" version = "3.2.17" @@ -89,7 +83,7 @@ dependencies = [ "vm-device", "vm-memory", "vm-superio", - "vmm-sys-util 0.11.2", + "vmm-sys-util", ] [[package]] @@ -99,7 +93,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bbb5f730c95a458654dee0afb13f1ebb4fc3d7b772789d5f30713ec68fed75d" dependencies = [ "libc", - "vmm-sys-util 0.11.2", + "vmm-sys-util", ] [[package]] @@ -133,7 +127,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efe70e65a5b092161d17f5005b66e5eefe7a94a70c332e755036fc4af78c4e79" dependencies = [ - "vmm-sys-util 0.11.2", + "vmm-sys-util", ] [[package]] @@ -144,7 +138,7 @@ checksum = "b8f8dc9c1896e5f144ec5d07169bc29f39a047686d29585a91f30489abfaeb6b" dependencies = [ "kvm-bindings", "libc", - "vmm-sys-util 0.11.2", + "vmm-sys-util", ] [[package]] @@ -163,12 +157,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.6" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" -dependencies = [ - "cfg-if", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "os_str_bytes" @@ -178,18 +169,18 @@ checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] name = "proc-macro2" -version = "1.0.34" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.10" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -202,13 +193,13 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.82" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -228,18 +219,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", @@ -247,45 +238,53 @@ dependencies = [ ] [[package]] -name = "unicode-xid" -version = "0.2.2" +name = "unicode-ident" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "utils" version = "0.1.0" +[[package]] +name = "virtio-bindings" +version = "0.2.2" +source = "git+https://github.com/rust-vmm/vm-virtio.git#3bbf0db2c24fe756ac3593f259d048dafc1fd3c5" + [[package]] name = "virtio-blk" version = "0.1.0" -source = "git+https://github.com/rust-vmm/vm-virtio.git#d8ef45f57b46baa99e80e555deffd3fa1ab9affc" +source = "git+https://github.com/rust-vmm/vm-virtio.git#3bbf0db2c24fe756ac3593f259d048dafc1fd3c5" dependencies = [ "log", + "virtio-bindings", "virtio-device", "virtio-queue", "vm-memory", - "vmm-sys-util 0.8.0", + "vmm-sys-util", ] [[package]] name = "virtio-device" version = "0.1.0" -source = "git+https://github.com/rust-vmm/vm-virtio.git#d8ef45f57b46baa99e80e555deffd3fa1ab9affc" +source = "git+https://github.com/rust-vmm/vm-virtio.git#3bbf0db2c24fe756ac3593f259d048dafc1fd3c5" dependencies = [ "log", + "virtio-bindings", "virtio-queue", "vm-memory", ] [[package]] name = "virtio-queue" -version = "0.2.0" -source = "git+https://github.com/rust-vmm/vm-virtio.git#d8ef45f57b46baa99e80e555deffd3fa1ab9affc" +version = "0.10.0" +source = "git+https://github.com/rust-vmm/vm-virtio.git#3bbf0db2c24fe756ac3593f259d048dafc1fd3c5" dependencies = [ "log", + "virtio-bindings", "vm-memory", - "vmm-sys-util 0.8.0", + "vmm-sys-util", ] [[package]] @@ -312,11 +311,12 @@ checksum = "f43fb5a6bd1a7d423ad72802801036719b7546cf847a103f8fe4575f5b0d45a6" [[package]] name = "vm-memory" -version = "0.7.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339d4349c126fdcd87e034631d7274370cf19eb0e87b33166bcd956589fc72c5" +checksum = "5376c9ee5ebe2103a310d8241936cfb93c946734b0479a4fa5bdf7a64abbacd8" dependencies = [ "libc", + "thiserror", "winapi", ] @@ -339,7 +339,7 @@ dependencies = [ "vm-device", "vm-memory", "vm-vcpu-ref", - "vmm-sys-util 0.11.2", + "vmm-sys-util", ] [[package]] @@ -351,7 +351,7 @@ dependencies = [ "libc", "thiserror", "vm-memory", - "vmm-sys-util 0.11.2", + "vmm-sys-util", ] [[package]] @@ -372,7 +372,7 @@ dependencies = [ "vm-superio", "vm-vcpu", "vm-vcpu-ref", - "vmm-sys-util 0.11.2", + "vmm-sys-util", ] [[package]] @@ -383,16 +383,6 @@ dependencies = [ "vmm", ] -[[package]] -name = "vmm-sys-util" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cf11afbc4ebc0d5c7a7748a77d19e2042677fc15faa2f4ccccb27c18a60605" -dependencies = [ - "bitflags", - "libc", -] - [[package]] name = "vmm-sys-util" version = "0.11.2" diff --git a/src/arch/Cargo.toml b/src/arch/Cargo.toml index f3a2eb9b..437893c5 100644 --- a/src/arch/Cargo.toml +++ b/src/arch/Cargo.toml @@ -8,4 +8,4 @@ edition = "2018" [dependencies] vm-fdt = "0.2.0" -vm-memory = "0.7.0" +vm-memory = "0.13.1" diff --git a/src/devices/Cargo.toml b/src/devices/Cargo.toml index d8b141ed..cc838137 100644 --- a/src/devices/Cargo.toml +++ b/src/devices/Cargo.toml @@ -10,8 +10,8 @@ event-manager = { version = "0.3.0", features = ["remote_endpoint"] } kvm-ioctls = "0.13.0" libc = "0.2.76" linux-loader = "0.4.0" -log = "0.4.6" -vm-memory = "0.7.0" +log = "*" +vm-memory = "0.13.1" vm-superio = "0.5.0" vmm-sys-util = "0.11.2" vm-device = "0.1.0" @@ -23,5 +23,5 @@ virtio-queue = { git = "https://github.com/rust-vmm/vm-virtio.git"} utils = { path = "../utils" } [dev-dependencies] -vm-memory = { version = "0.7.0", features = ["backend-mmap"] } +vm-memory = { version = "0.13.1", features = ["backend-mmap"] } kvm-bindings = "0.6.0" diff --git a/src/devices/src/virtio/block/device.rs b/src/devices/src/virtio/block/device.rs index a885a47e..0852b447 100644 --- a/src/devices/src/virtio/block/device.rs +++ b/src/devices/src/virtio/block/device.rs @@ -9,11 +9,11 @@ use std::sync::{Arc, Mutex}; use virtio_blk::stdio_executor::StdIoBackend; use virtio_device::{VirtioConfig, VirtioDeviceActions, VirtioDeviceType, VirtioMmioDevice}; -use virtio_queue::Queue; +use virtio_queue::{Queue, QueueT}; use vm_device::bus::MmioAddress; use vm_device::device_manager::MmioManager; use vm_device::{DeviceMmio, MutDeviceMmio}; -use vm_memory::GuestAddressSpace; +use vm_memory::{GuestAddressSpace, GuestMemoryMmap}; use crate::virtio::block::{BLOCK_DEVICE_ID, VIRTIO_BLK_F_RO}; use crate::virtio::{CommonConfig, Env, SingleFdSignalQueue, QUEUE_MAX_SIZE}; @@ -25,31 +25,34 @@ use super::{build_config_space, BlockArgs, Error, Result}; // This Block device can only use the MMIO transport for now, but we plan to reuse large parts of // the functionality when we implement virtio PCI as well, for example by having a base generic // type, and then separate concrete instantiations for `MmioConfig` and `PciConfig`. -pub struct Block { +pub struct Block { cfg: CommonConfig, file_path: PathBuf, read_only: bool, // We'll prob need to remember this for state save/restore unless we pass the info from // the outside. _root_device: bool, + mem: Arc, } -impl Block -where - M: GuestAddressSpace + Clone + Send + 'static, -{ +impl Block { // Helper method that only creates a `Block` object. - fn create_block(env: &mut Env, args: &BlockArgs) -> Result { + fn create_block( + mem: Arc, + env: &mut Env, + args: &BlockArgs, + ) -> Result { let device_features = args.device_features(); // A block device has a single queue. - let queues = vec![Queue::new(env.mem.clone(), QUEUE_MAX_SIZE)]; + let queues = vec![Queue::new(QUEUE_MAX_SIZE).unwrap()]; let config_space = build_config_space(&args.file_path)?; let virtio_cfg = VirtioConfig::new(device_features, queues, config_space); let common_cfg = CommonConfig::new(virtio_cfg, env).map_err(Error::Virtio)?; Ok(Block { + mem, cfg: common_cfg, file_path: args.file_path.clone(), read_only: args.read_only, @@ -59,14 +62,18 @@ where // Create `Block` object, register it on the MMIO bus, and add any extra required info to // the kernel cmdline from the environment. - pub fn new(env: &mut Env, args: &BlockArgs) -> Result>> + pub fn new( + mem: Arc, + env: &mut Env, + args: &BlockArgs, + ) -> Result>> where // We're using this (more convoluted) bound so we can pass both references and smart // pointers such as mutex guards here. B: DerefMut, B::Target: MmioManager>, { - let block = Arc::new(Mutex::new(Self::create_block(env, args)?)); + let block = Arc::new(Mutex::new(Self::create_block(mem, env, args)?)); // Register the device on the MMIO bus. env.register_mmio_device(block.clone()) @@ -79,14 +86,14 @@ where } } -impl Borrow> for Block { - fn borrow(&self) -> &VirtioConfig { +impl Borrow> for Block { + fn borrow(&self) -> &VirtioConfig { &self.cfg.virtio } } -impl BorrowMut> for Block { - fn borrow_mut(&mut self) -> &mut VirtioConfig { +impl BorrowMut> for Block { + fn borrow_mut(&mut self) -> &mut VirtioConfig { &mut self.cfg.virtio } } @@ -131,6 +138,7 @@ impl VirtioDeviceActions for Bloc }; let handler = Arc::new(Mutex::new(QueueHandler { + mem: self.mem.clone(), inner, ioeventfd: ioevents.remove(0), })); @@ -144,7 +152,7 @@ impl VirtioDeviceActions for Bloc } } -impl VirtioMmioDevice for Block {} +impl VirtioMmioDevice for Block {} impl MutDeviceMmio for Block { fn mmio_read(&mut self, _base: MmioAddress, offset: u64, data: &mut [u8]) { @@ -177,7 +185,7 @@ mod tests { advertise_flush: true, }; - let block_mutex = Block::new(&mut env, &args).unwrap(); + let block_mutex = Block::new(env.mem.clone(), &mut env, &args).unwrap(); let block = block_mutex.lock().unwrap(); assert_eq!(block.device_type(), BLOCK_DEVICE_ID); diff --git a/src/devices/src/virtio/block/inorder_handler.rs b/src/devices/src/virtio/block/inorder_handler.rs index eb8f04a6..376fc221 100644 --- a/src/devices/src/virtio/block/inorder_handler.rs +++ b/src/devices/src/virtio/block/inorder_handler.rs @@ -7,8 +7,10 @@ use std::result; use log::warn; use virtio_blk::request::Request; use virtio_blk::stdio_executor::{self, StdIoBackend}; -use virtio_queue::{DescriptorChain, Queue}; -use vm_memory::{self, GuestAddressSpace}; +use virtio_queue::{DescriptorChain, Queue, QueueOwnedT, QueueT}; +use vm_memory::{self, GuestAddressSpace, GuestMemoryMmap}; + +use std::sync::Arc; use crate::virtio::SignalUsedQueue; @@ -42,18 +44,20 @@ impl From for Error { // object), but the aim is to have a way of working with generic backends and turn this into // a more flexible building block. The name comes from processing and returning descriptor // chains back to the device in the same order they are received. -pub struct InOrderQueueHandler { +pub struct InOrderQueueHandler { pub driver_notify: S, - pub queue: Queue, + pub queue: Queue, pub disk: StdIoBackend, } -impl InOrderQueueHandler +impl InOrderQueueHandler where - M: GuestAddressSpace, S: SignalUsedQueue, { - fn process_chain(&mut self, mut chain: DescriptorChain) -> result::Result<(), Error> { + fn process_chain( + &mut self, + mut chain: DescriptorChain, + ) -> result::Result<(), Error> { let used_len = match Request::parse(&mut chain) { Ok(request) => self.disk.process_request(chain.memory(), &request)?, Err(e) => { @@ -62,26 +66,28 @@ where } }; - self.queue.add_used(chain.head_index(), used_len)?; + self.queue + .add_used(chain.memory(), chain.head_index(), used_len)?; - if self.queue.needs_notification()? { + if self.queue.needs_notification(chain.memory())? { self.driver_notify.signal_used_queue(0); } Ok(()) } - pub fn process_queue(&mut self) -> result::Result<(), Error> { + pub fn process_queue(&mut self, mem: Arc) -> result::Result<(), Error> { // To see why this is done in a loop, please look at the `Queue::enable_notification` // comments in `virtio_queue`. + loop { - self.queue.disable_notification()?; + self.queue.disable_notification(mem.as_ref())?; - while let Some(chain) = self.queue.iter()?.next() { - self.process_chain(chain)?; + while let Some(chain) = self.queue.iter(mem.as_ref())?.next() { + self.process_chain::<&GuestMemoryMmap>(chain)?; } - if !self.queue.enable_notification()? { + if !self.queue.enable_notification(mem.as_ref())? { break; } } diff --git a/src/devices/src/virtio/block/queue_handler.rs b/src/devices/src/virtio/block/queue_handler.rs index 13689227..efab7d2d 100644 --- a/src/devices/src/virtio/block/queue_handler.rs +++ b/src/devices/src/virtio/block/queue_handler.rs @@ -3,7 +3,8 @@ use event_manager::{EventOps, Events, MutEventSubscriber}; use log::error; -use vm_memory::GuestAddressSpace; +use std::sync::Arc; +use vm_memory::GuestMemoryMmap; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -16,12 +17,13 @@ const IOEVENT_DATA: u32 = 0; // signalling implementation based on `EventFd`s, and then also implements `MutEventSubscriber` // to interact with the event manager. `ioeventfd` is the `EventFd` connected to queue // notifications coming from the driver. -pub(crate) struct QueueHandler { - pub inner: InOrderQueueHandler, +pub(crate) struct QueueHandler { + pub mem: Arc, + pub inner: InOrderQueueHandler, pub ioeventfd: EventFd, } -impl MutEventSubscriber for QueueHandler { +impl MutEventSubscriber for QueueHandler { fn process(&mut self, events: Events, ops: &mut EventOps) { let mut error = true; @@ -33,7 +35,7 @@ impl MutEventSubscriber for QueueHandler { error!("unexpected events data {}", events.data()); } else if self.ioeventfd.read().is_err() { error!("ioeventfd read error") - } else if let Err(e) = self.inner.process_queue() { + } else if let Err(e) = self.inner.process_queue(self.mem.clone()) { error!("error processing block queue {:?}", e); } else { error = false; diff --git a/src/devices/src/virtio/mod.rs b/src/devices/src/virtio/mod.rs index d3cfc435..6d8e939f 100644 --- a/src/devices/src/virtio/mod.rs +++ b/src/devices/src/virtio/mod.rs @@ -8,7 +8,7 @@ pub mod net; use std::convert::TryFrom; use std::io; -use std::ops::DerefMut; +use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicU8, Ordering}; use std::sync::{Arc, Mutex}; @@ -19,6 +19,7 @@ use event_manager::{ use kvm_ioctls::{IoEventAddress, VmFd}; use linux_loader::cmdline::Cmdline; use virtio_device::VirtioConfig; +use virtio_queue::{Queue, QueueT}; use vm_device::bus::{self, MmioAddress, MmioRange}; use vm_device::device_manager::MmioManager; use vm_device::DeviceMmio; @@ -154,16 +155,17 @@ where } // Holds configuration objects which are common to all current devices. -pub struct CommonConfig { - pub virtio: VirtioConfig, +pub struct CommonConfig { + pub mem: M, + pub virtio: VirtioConfig, pub mmio: MmioConfig, pub endpoint: RemoteEndpoint, pub vm_fd: Arc, pub irqfd: Arc, } -impl CommonConfig { - pub fn new(virtio_cfg: VirtioConfig, env: &Env) -> Result { +impl CommonConfig { + pub fn new(virtio_cfg: VirtioConfig, env: &Env) -> Result { let irqfd = Arc::new(EventFd::new(EFD_NONBLOCK).map_err(Error::EventFd)?); env.vm_fd @@ -171,6 +173,7 @@ impl CommonConfig { .map_err(Error::RegisterIrqfd)?; Ok(CommonConfig { + mem: env.mem.clone(), virtio: virtio_cfg, mmio: env.mmio_cfg, endpoint: env.event_mgr.remote_endpoint(), @@ -183,9 +186,15 @@ impl CommonConfig { // a `Vec` that contains `EventFd`s registered as ioeventfds, which are used to convey queue // notifications coming from the driver. pub fn prepare_activate(&self) -> Result> { - if !self.virtio.queues_valid() { - return Err(Error::QueuesNotValid); - } + let m = self.mem.memory(); + let guest_mem = <::T>::deref(&m); + + self.virtio + .queues + .iter() + .all(|q| ::is_valid(q, guest_mem)) + .then_some(()) + .ok_or(Error::QueuesNotValid)?; if self.virtio.device_activated { return Err(Error::AlreadyActivated); @@ -284,7 +293,6 @@ pub(crate) mod tests { kvm_create_device, kvm_device_attr, kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, KVM_DEV_ARM_VGIC_CTRL_INIT, KVM_DEV_ARM_VGIC_GRP_CTRL, }; - use virtio_queue::Queue; pub type MockMem = Arc; // Can be used in other modules to test functionality that requires a `CommonArgs` struct as @@ -397,7 +405,7 @@ pub(crate) mod tests { let env = mock.env(); let device_features = 0; - let queues = vec![Queue::new(env.mem.clone(), 256)]; + let queues = vec![Queue::new(256).unwrap()]; let config_space = Vec::new(); let virtio_cfg = VirtioConfig::new(device_features, queues, config_space); @@ -407,8 +415,8 @@ pub(crate) mod tests { assert!(matches!(cfg.prepare_activate(), Err(Error::QueuesNotValid))); // Let's pretend the queue has been configured such that the `is_valid` check passes. - cfg.virtio.queues[0].state.ready = true; - cfg.virtio.queues[0].state.size = 256; + cfg.virtio.queues[0].set_ready(true); + cfg.virtio.queues[0].set_size(256); // This will fail because the "driver" didn't acknowledge `VIRTIO_F_VERSION_1`. assert!(matches!(cfg.prepare_activate(), Err(Error::BadFeatures(0)))); diff --git a/src/devices/src/virtio/net/device.rs b/src/devices/src/virtio/net/device.rs index ea78bc12..5d001748 100644 --- a/src/devices/src/virtio/net/device.rs +++ b/src/devices/src/virtio/net/device.rs @@ -6,11 +6,11 @@ use std::ops::DerefMut; use std::sync::{Arc, Mutex}; use virtio_device::{VirtioConfig, VirtioDeviceActions, VirtioDeviceType, VirtioMmioDevice}; -use virtio_queue::Queue; +use virtio_queue::{Queue, QueueT}; use vm_device::bus::MmioAddress; use vm_device::device_manager::MmioManager; use vm_device::{DeviceMmio, MutDeviceMmio}; -use vm_memory::GuestAddressSpace; +use vm_memory::{GuestAddressSpace, GuestMemoryMmap}; use crate::virtio::features::{VIRTIO_F_IN_ORDER, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1}; use crate::virtio::net::features::*; @@ -22,16 +22,24 @@ use super::queue_handler::QueueHandler; use super::simple_handler::SimpleHandler; use super::tap::Tap; -pub struct Net { +pub struct Net +where + M: GuestAddressSpace + Clone + Send + Sync + 'static, +{ + mem: Arc, cfg: CommonConfig, tap_name: String, } impl Net where - M: GuestAddressSpace + Clone + Send + 'static, + M: GuestAddressSpace + Clone + Send + Sync + 'static, { - pub fn new(env: &mut Env, args: &NetArgs) -> Result>> + pub fn new( + mem: Arc, + env: &mut Env, + args: &NetArgs, + ) -> Result>> where // We're using this (more convoluted) bound so we can pass both references and smart // pointers such as mutex guards here. @@ -52,8 +60,8 @@ where // An rx/tx queue pair. let queues = vec![ - Queue::new(env.mem.clone(), QUEUE_MAX_SIZE), - Queue::new(env.mem.clone(), QUEUE_MAX_SIZE), + Queue::new(QUEUE_MAX_SIZE).unwrap(), + Queue::new(QUEUE_MAX_SIZE).unwrap(), ]; // TODO: We'll need a minimal config space to support setting an explicit MAC addr @@ -64,6 +72,7 @@ where let common_cfg = CommonConfig::new(virtio_cfg, env).map_err(Error::Virtio)?; let net = Arc::new(Mutex::new(Net { + mem, cfg: common_cfg, tap_name: args.tap_name.clone(), })); @@ -75,25 +84,27 @@ where } } -impl VirtioDeviceType for Net { +impl VirtioDeviceType for Net { fn device_type(&self) -> u32 { NET_DEVICE_ID } } -impl Borrow> for Net { - fn borrow(&self) -> &VirtioConfig { +impl Borrow> for Net { + fn borrow(&self) -> &VirtioConfig { &self.cfg.virtio } } -impl BorrowMut> for Net { - fn borrow_mut(&mut self) -> &mut VirtioConfig { +impl BorrowMut> + for Net +{ + fn borrow_mut(&mut self) -> &mut VirtioConfig { &mut self.cfg.virtio } } -impl VirtioDeviceActions for Net { +impl VirtioDeviceActions for Net { type E = Error; fn activate(&mut self) -> Result<()> { @@ -123,7 +134,7 @@ impl VirtioDeviceActions for Net< let rxq = self.cfg.virtio.queues.remove(0); let txq = self.cfg.virtio.queues.remove(0); - let inner = SimpleHandler::new(driver_notify, rxq, txq, tap); + let inner = SimpleHandler::new(self.mem.clone(), driver_notify, rxq, txq, tap); let handler = Arc::new(Mutex::new(QueueHandler { inner, @@ -140,9 +151,9 @@ impl VirtioDeviceActions for Net< } } -impl VirtioMmioDevice for Net {} +impl VirtioMmioDevice for Net {} -impl MutDeviceMmio for Net { +impl MutDeviceMmio for Net { fn mmio_read(&mut self, _base: MmioAddress, offset: u64, data: &mut [u8]) { self.read(offset, data); } diff --git a/src/devices/src/virtio/net/queue_handler.rs b/src/devices/src/virtio/net/queue_handler.rs index 4759c2bf..90193dd8 100644 --- a/src/devices/src/virtio/net/queue_handler.rs +++ b/src/devices/src/virtio/net/queue_handler.rs @@ -1,9 +1,10 @@ // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +use std::os::fd::AsRawFd; + use event_manager::{EventOps, Events, MutEventSubscriber}; use log::error; -use vm_memory::GuestAddressSpace; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -15,13 +16,13 @@ const TAPFD_DATA: u32 = 0; const RX_IOEVENT_DATA: u32 = 1; const TX_IOEVENT_DATA: u32 = 2; -pub struct QueueHandler { - pub inner: SimpleHandler, +pub struct QueueHandler { + pub inner: SimpleHandler, pub rx_ioevent: EventFd, pub tx_ioevent: EventFd, } -impl QueueHandler { +impl QueueHandler { // Helper method that receives an error message to be logged and the `ops` handle // which is used to unregister all events. fn handle_error>(&self, s: S, ops: &mut EventOps) { @@ -30,12 +31,12 @@ impl QueueHandler { .expect("Failed to remove rx ioevent"); ops.remove(Events::empty(&self.tx_ioevent)) .expect("Failed to remove tx ioevent"); - ops.remove(Events::empty(&self.inner.tap)) + ops.remove(Events::empty(&self.inner.tap.borrow().as_raw_fd())) .expect("Failed to remove tap event"); } } -impl MutEventSubscriber for QueueHandler { +impl MutEventSubscriber for QueueHandler { fn process(&mut self, events: Events, ops: &mut EventOps) { // TODO: We can also consider panicking on the errors that cannot be generated // or influenced. @@ -71,8 +72,10 @@ impl MutEventSubscriber for QueueHandler { } fn init(&mut self, ops: &mut EventOps) { + let raw_fd = &mut self.inner.tap.borrow_mut().as_raw_fd(); + ops.add(Events::with_data( - &self.inner.tap, + raw_fd, TAPFD_DATA, EventSet::IN | EventSet::EDGE_TRIGGERED, )) diff --git a/src/devices/src/virtio/net/simple_handler.rs b/src/devices/src/virtio/net/simple_handler.rs index e3df98ef..3972c88e 100644 --- a/src/devices/src/virtio/net/simple_handler.rs +++ b/src/devices/src/virtio/net/simple_handler.rs @@ -1,17 +1,19 @@ // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +use std::cell::RefCell; use std::cmp; use std::io::{self, Read, Write}; use std::result; use log::warn; -use virtio_queue::{DescriptorChain, Queue}; -use vm_memory::{Bytes, GuestAddressSpace}; +use virtio_queue::{DescriptorChain, Queue, QueueOwnedT, QueueT}; +use vm_memory::{Bytes, GuestMemoryMmap}; use crate::virtio::net::tap::Tap; use crate::virtio::net::{RXQ_INDEX, TXQ_INDEX}; use crate::virtio::SignalUsedQueue; +use std::sync::Arc; // According to the standard: "If the VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6 or // VIRTIO_NET_F_GUEST_UFO features are used, the maximum incoming packet will be to 65550 @@ -38,27 +40,35 @@ impl From for Error { // A simple handler implementation for a RX/TX queue pair, which does not make assumptions about // the way queue notification is implemented. The backend is not yet generic (we always assume a // `Tap` object), but we're looking at improving that going forward. -// TODO: Find a better name. -pub struct SimpleHandler { +pub struct SimpleHandler { + pub mem: Arc, pub driver_notify: S, - pub rxq: Queue, + pub rxq: Queue, pub rxbuf_current: usize, pub rxbuf: [u8; MAX_BUFFER_SIZE], - pub txq: Queue, - pub txbuf: [u8; MAX_BUFFER_SIZE], - pub tap: Tap, + pub txq: Queue, + pub txbuf: RefCell<[u8; MAX_BUFFER_SIZE]>, + pub tap: RefCell, } -impl SimpleHandler { - pub fn new(driver_notify: S, rxq: Queue, txq: Queue, tap: Tap) -> Self { +impl SimpleHandler { + pub fn new( + mem: Arc, + driver_notify: S, + rxq: Queue, + txq: Queue, + tap: Tap, + ) -> Self { SimpleHandler { + mem, driver_notify, rxq, rxbuf_current: 0, rxbuf: [0u8; MAX_BUFFER_SIZE], txq, - txbuf: [0u8; MAX_BUFFER_SIZE], - tap, + //inner: RefCell::new(SimpleHandlerInner { txbuf: [0u8; MAX_BUFFER_SIZE], tap }), + txbuf: RefCell::new([0u8; MAX_BUFFER_SIZE]), + tap: RefCell::new(tap), } } @@ -69,7 +79,7 @@ impl SimpleHandler { fn write_frame_to_guest(&mut self) -> result::Result { let num_bytes = self.rxbuf_current; - let mut chain = match self.rxq.iter()?.next() { + let mut chain = match self.rxq.iter(self.mem.as_ref())?.next() { Some(c) => c, _ => return Ok(false), }; @@ -98,7 +108,8 @@ impl SimpleHandler { warn!("rx frame too large"); } - self.rxq.add_used(chain.head_index(), count as u32)?; + self.rxq + .add_used(self.mem.as_ref(), chain.head_index(), count as u32)?; self.rxbuf_current = 0; @@ -108,7 +119,7 @@ impl SimpleHandler { pub fn process_tap(&mut self) -> result::Result<(), Error> { loop { if self.rxbuf_current == 0 { - match self.tap.read(&mut self.rxbuf) { + match self.tap.borrow_mut().read(&mut self.rxbuf) { Ok(n) => self.rxbuf_current = n, Err(_) => { // TODO: Do something (logs, metrics, etc.) in response to an error when @@ -119,12 +130,12 @@ impl SimpleHandler { } } - if !self.write_frame_to_guest()? && !self.rxq.enable_notification()? { + if !self.write_frame_to_guest()? && !self.rxq.enable_notification(self.mem.as_ref())? { break; } } - if self.rxq.needs_notification()? { + if self.rxq.needs_notification(self.mem.as_ref())? { self.driver_notify.signal_used_queue(RXQ_INDEX); } @@ -132,13 +143,13 @@ impl SimpleHandler { } fn send_frame_from_chain( - &mut self, - chain: &mut DescriptorChain, + &self, + chain: &mut DescriptorChain<&GuestMemoryMmap>, ) -> result::Result { let mut count = 0; while let Some(desc) = chain.next() { - let left = self.txbuf.len() - count; + let left = self.txbuf.borrow().len() - count; let len = desc.len() as usize; if len > left { @@ -148,39 +159,46 @@ impl SimpleHandler { chain .memory() - .read_slice(&mut self.txbuf[count..count + len], desc.addr()) + .read_slice( + &mut self.txbuf.borrow_mut()[count..count + len], + desc.addr(), + ) .map_err(Error::GuestMemory)?; count += len; } - self.tap.write(&self.txbuf[..count]).map_err(Error::Tap)?; + self.tap + .borrow_mut() + .write(&self.txbuf.borrow()[..count]) + .map_err(Error::Tap)?; Ok(count as u32) } pub fn process_txq(&mut self) -> result::Result<(), Error> { loop { - self.txq.disable_notification()?; + self.txq.disable_notification(self.mem.as_ref())?; - while let Some(mut chain) = self.txq.iter()?.next() { + while let Some(mut chain) = self.txq.iter(self.mem.as_ref())?.next() { self.send_frame_from_chain(&mut chain)?; - self.txq.add_used(chain.head_index(), 0)?; + self.txq + .add_used(self.mem.as_ref(), chain.head_index(), 0)?; - if self.txq.needs_notification()? { + if self.txq.needs_notification(self.mem.as_ref())? { self.driver_notify.signal_used_queue(TXQ_INDEX); } } - if !self.txq.enable_notification()? { + if !self.txq.enable_notification(self.mem.as_ref())? { return Ok(()); } } } pub fn process_rxq(&mut self) -> result::Result<(), Error> { - self.rxq.disable_notification()?; + self.rxq.disable_notification(self.mem.as_ref())?; self.process_tap() } } diff --git a/src/vm-vcpu-ref/Cargo.toml b/src/vm-vcpu-ref/Cargo.toml index 073c5beb..848554a6 100644 --- a/src/vm-vcpu-ref/Cargo.toml +++ b/src/vm-vcpu-ref/Cargo.toml @@ -13,9 +13,9 @@ keywords = ["virt", "kvm", "vm"] thiserror = "1.0.30" kvm-ioctls = "0.13.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -vm-memory = "0.7.0" +vm-memory = "0.13.1" libc = "0.2.76" [dev-dependencies] -vm-memory = { version = "0.7.0", features = ["backend-mmap"] } +vm-memory = { version = "0.13.1", features = ["backend-mmap"] } vmm-sys-util = "0.11.2" diff --git a/src/vm-vcpu/Cargo.toml b/src/vm-vcpu/Cargo.toml index dc16d354..f7e5e6f1 100644 --- a/src/vm-vcpu/Cargo.toml +++ b/src/vm-vcpu/Cargo.toml @@ -11,7 +11,7 @@ thiserror = "1.0.30" libc = "0.2.76" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } kvm-ioctls = "0.13.0" -vm-memory = "0.7.0" +vm-memory = "0.13.1" vmm-sys-util = "0.11.2" vm-device = "0.1.0" @@ -20,4 +20,4 @@ vm-vcpu-ref = { path = "../vm-vcpu-ref" } arch = { path = "../arch" } [dev-dependencies] -vm-memory = { version = "0.7.0", features = ["backend-mmap"] } +vm-memory = { version = "0.13.1", features = ["backend-mmap"] } diff --git a/src/vmm/Cargo.toml b/src/vmm/Cargo.toml index 7365b22a..5324414d 100644 --- a/src/vmm/Cargo.toml +++ b/src/vmm/Cargo.toml @@ -11,7 +11,7 @@ kvm-ioctls = "0.13.0" libc = "0.2.91" linux-loader = { version = "0.4.0", features = ["bzimage", "elf"] } vm-allocator = "0.1.0" -vm-memory = { version = "0.7.0", features = ["backend-mmap"] } +vm-memory = { version = "0.13.1", features = ["backend-mmap"] } vm-superio = "0.5.0" vmm-sys-util = "0.11.2" vm-device = "0.1.0" diff --git a/src/vmm/src/lib.rs b/src/vmm/src/lib.rs index 92c2cfe6..36942237 100644 --- a/src/vmm/src/lib.rs +++ b/src/vmm/src/lib.rs @@ -207,7 +207,7 @@ type Net = net::Net>; pub struct Vmm { vm: KvmVm, kernel_cfg: KernelConfig, - guest_memory: GuestMemoryMmap, + guest_memory: Arc, address_allocator: AddressAllocator, irq_allocator: IrqAllocator, // The `device_mgr` is an Arc so that it can be shared between @@ -319,7 +319,7 @@ impl TryFrom for Vmm { let mut vmm = Vmm { vm, - guest_memory, + guest_memory: Arc::new(guest_memory), address_allocator, irq_allocator, device_mgr, @@ -428,14 +428,14 @@ impl Vmm { // Load the kernel into guest memory. let kernel_load = match Elf::load( - &self.guest_memory, + self.guest_memory.as_ref(), None, &mut kernel_image, Some(GuestAddress(self.kernel_cfg.load_addr)), ) { Ok(result) => result, Err(loader::Error::Elf(elf::Error::InvalidElfMagicNumber)) => BzImage::load( - &self.guest_memory, + self.guest_memory.as_ref(), None, &mut kernel_image, Some(GuestAddress(self.kernel_cfg.load_addr)), @@ -467,7 +467,7 @@ impl Vmm { .map_err(Error::Cmdline)?; load_cmdline( - &self.guest_memory, + self.guest_memory.as_ref(), GuestAddress(CMDLINE_START), // Safe because we know the command line string doesn't contain any 0 bytes. &cmdline, @@ -488,7 +488,7 @@ impl Vmm { fn load_kernel(&mut self) -> Result { let mut kernel_image = File::open(&self.kernel_cfg.path).map_err(Error::IO)?; linux_loader::loader::pe::PE::load( - &self.guest_memory, + self.guest_memory.as_ref(), Some(GuestAddress(self.kernel_cfg.load_addr)), &mut kernel_image, None, @@ -596,7 +596,6 @@ impl Vmm { // can do it after figuring out how to better separate concerns and make the VMM agnostic of // the actual device types. fn add_block_device(&mut self, cfg: &BlockConfig) -> Result<()> { - let mem = Arc::new(self.guest_memory.clone()); let range = self.address_allocator.allocate( 0x1000, DEFAULT_ADDRESSS_ALIGNEMNT, @@ -612,7 +611,7 @@ impl Vmm { let mut guard = self.device_mgr.lock().unwrap(); let mut env = Env { - mem, + mem: self.guest_memory.clone(), vm_fd: self.vm.vm_fd(), event_mgr: &mut self.event_mgr, mmio_mgr: guard.deref_mut(), @@ -628,7 +627,7 @@ impl Vmm { }; // We can also hold this somewhere if we need to keep the handle for later. - let block = Block::new(&mut env, &args).map_err(Error::Block)?; + let block = Block::new(self.guest_memory.clone(), &mut env, &args).map_err(Error::Block)?; #[cfg(target_arch = "aarch64")] self.fdt_builder .add_virtio_device(range.start(), range.len(), irq); @@ -638,7 +637,6 @@ impl Vmm { } fn add_net_device(&mut self, cfg: &NetConfig) -> Result<()> { - let mem = Arc::new(self.guest_memory.clone()); let range = self.address_allocator.allocate( 0x1000, DEFAULT_ADDRESSS_ALIGNEMNT, @@ -654,7 +652,7 @@ impl Vmm { let mut guard = self.device_mgr.lock().unwrap(); let mut env = Env { - mem, + mem: self.guest_memory.clone(), vm_fd: self.vm.vm_fd(), event_mgr: &mut self.event_mgr, mmio_mgr: guard.deref_mut(), @@ -667,7 +665,7 @@ impl Vmm { }; // We can also hold this somewhere if we need to keep the handle for later. - let net = Net::new(&mut env, &args).map_err(Error::Net)?; + let net = Net::new(self.guest_memory.clone(), &mut env, &args).map_err(Error::Net)?; self.net_devices.push(net); #[cfg(target_arch = "aarch64")] self.fdt_builder @@ -731,7 +729,7 @@ impl Vmm { .with_mem_size(mem_size) .create_fdt() .map_err(Error::SetupFdt)?; - fdt.write_to_mem(&self.guest_memory, fdt_offset) + fdt.write_to_mem(self.guest_memory.as_ref(), fdt_offset) .map_err(Error::SetupFdt)?; Ok(()) } @@ -754,7 +752,7 @@ mod tests { use std::path::PathBuf; #[cfg(target_arch = "x86_64")] use vm_memory::{ - bytes::{ByteValued, Bytes}, + bytes::ByteValued, Address, GuestAddress, GuestMemory, }; @@ -849,7 +847,7 @@ mod tests { let irq_allocator = IrqAllocator::new(SERIAL_IRQ, vm.max_irq()).unwrap(); Vmm { vm, - guest_memory, + guest_memory: Arc::new(guest_memory), address_allocator, irq_allocator, device_mgr, @@ -868,11 +866,13 @@ mod tests { // Return the address where an ELF file should be loaded, as specified in its header. #[cfg(target_arch = "x86_64")] fn elf_load_addr(elf_path: &Path) -> GuestAddress { + use vm_memory::ReadVolatile; + let mut elf_file = File::open(elf_path).unwrap(); let mut ehdr = Elf64_Ehdr::default(); - ehdr.as_bytes() - .read_from(0, &mut elf_file, std::mem::size_of::()) - .unwrap(); + + elf_file.read_volatile(&mut ehdr.as_bytes()).unwrap(); + GuestAddress(ehdr.e_entry) } @@ -1179,7 +1179,7 @@ mod tests { .with_rtc(0x40001000, 0x1000) .create_fdt() .unwrap(); - assert!(fdt.write_to_mem(&vmm.guest_memory, fdt_offset).is_err()); + assert!(fdt.write_to_mem(vmm.guest_memory.as_ref(), fdt_offset).is_err()); } } #[test] From a3772d91fc7d574ad66f2317152d611d36820073 Mon Sep 17 00:00:00 2001 From: Alvise Rigo Date: Fri, 27 Oct 2023 16:25:09 +0200 Subject: [PATCH 03/10] MPtable: update against vm-memory 0.13.1 Signed-off-by: Alvise Rigo Reviewed-by: Timos Ampelikiotis --- src/vm-vcpu-ref/src/x86_64/mptable.rs | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/vm-vcpu-ref/src/x86_64/mptable.rs b/src/vm-vcpu-ref/src/x86_64/mptable.rs index d8ec2c06..5fc2cb85 100644 --- a/src/vm-vcpu-ref/src/x86_64/mptable.rs +++ b/src/vm-vcpu-ref/src/x86_64/mptable.rs @@ -5,7 +5,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. -use std::io; use std::mem; use std::result; use std::slice; @@ -190,7 +189,8 @@ impl MpTable { let mut checksum: u8 = 0; let max_ioapic_id = self.cpu_num + 1; - mem.read_from(base_mp, &mut io::repeat(0), mp_size) + let zero_slice: Vec = (0..mp_size).map(|_| 0u8).collect(); + mem.write_slice(zero_slice.as_slice(), base_mp) .map_err(|_| Error::Clear)?; { @@ -405,23 +405,14 @@ mod tests { let mpc_offset = GuestAddress(u64::from(mpf_intel.0.physptr)); let mpc_table: MpcTable = mem.read_obj(mpc_offset).unwrap(); - struct Sum(u8); - impl io::Write for Sum { - fn write(&mut self, buf: &[u8]) -> io::Result { - for v in buf.iter() { - self.0 = self.0.wrapping_add(*v); - } - Ok(buf.len()) - } - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - } - - let mut sum = Sum(0); - mem.write_to(mpc_offset, &mut sum, mpc_table.0.length as usize) + let mut mpc_table: Vec = (0..mpc_table.0.length).map(|_| 0u8).collect(); + mem.read_slice(mpc_table.as_mut_slice(), mpc_offset) .unwrap(); - assert_eq!(sum.0, 0); + let mut csum: u8 = 0; + mpc_table + .iter() + .for_each(|byte| csum = csum.wrapping_add(*byte)); + assert_eq!(csum, 0); } #[test] From cfd22dd3a91d6e15b3e81bb9952ae4324439f00b Mon Sep 17 00:00:00 2001 From: Alvise Rigo Date: Mon, 23 Oct 2023 15:34:40 +0000 Subject: [PATCH 04/10] Update linux-loader to 0.10.0 Update to align to the new Cmdline API and kernel image loader. While at it, update the load_kernel() tests as well. Signed-off-by: Alvise Rigo Reviewed-by: Timos Ampelikiotis --- Cargo.lock | 4 +- Cargo.toml | 5 ++- src/api/Cargo.toml | 2 +- src/api/src/lib.rs | 2 +- src/devices/Cargo.toml | 2 +- src/devices/src/virtio/block/device.rs | 2 +- src/devices/src/virtio/mod.rs | 10 +++-- src/vmm/Cargo.toml | 2 +- src/vmm/src/config/mod.rs | 6 +-- src/vmm/src/lib.rs | 55 +++++++++++++++++++------- 10 files changed, 61 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 375f93d1..a9d2e21e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,8 +149,8 @@ checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" [[package]] name = "linux-loader" -version = "0.4.0" -source = "git+https://github.com/rust-vmm/linux-loader.git?rev=9a9f071#9a9f071219a8c74dcd703aec40ba0249e3a5993b" +version = "0.10.0" +source = "git+https://github.com/vitamin-v-software/linux-loader.git?rev=76aefef#76aefefc41c9a55217768bc0c89a55178db65b47" dependencies = [ "vm-memory", ] diff --git a/Cargo.toml b/Cargo.toml index aa0039fb..d3f94aca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,5 +21,6 @@ lto = true panic = "abort" [patch.crates-io] -# TODO: Using this patch until a version > 4.0 gets published. -linux-loader = { git = "https://github.com/rust-vmm/linux-loader.git", rev = "9a9f071" } +# TODO: Update with https://github.com/rust-vmm/linux-loader.git hash of commit +# "add as_string() to the Cmdline crate" +linux-loader = { git = "https://github.com/vitamin-v-software/linux-loader.git", rev = "76aefef" } diff --git a/src/api/Cargo.toml b/src/api/Cargo.toml index 7bc3dc38..7c08e547 100644 --- a/src/api/Cargo.toml +++ b/src/api/Cargo.toml @@ -10,4 +10,4 @@ clap = "3.2.17" vmm = { path = "../vmm" } [dev-dependencies] -linux-loader = "0.4.0" +linux-loader = "0.10.0" diff --git a/src/api/src/lib.rs b/src/api/src/lib.rs index 9d23c992..c9671da0 100644 --- a/src/api/src/lib.rs +++ b/src/api/src/lib.rs @@ -211,7 +211,7 @@ mod tests { ]) .is_err()); - let mut foo_cmdline = Cmdline::new(4096); + let mut foo_cmdline = Cmdline::new(4096).unwrap(); foo_cmdline.insert_str("\"foo=bar bar=foo\"").unwrap(); // OK. diff --git a/src/devices/Cargo.toml b/src/devices/Cargo.toml index cc838137..0338787d 100644 --- a/src/devices/Cargo.toml +++ b/src/devices/Cargo.toml @@ -9,7 +9,7 @@ license = "Apache-2.0 OR BSD-3-Clause" event-manager = { version = "0.3.0", features = ["remote_endpoint"] } kvm-ioctls = "0.13.0" libc = "0.2.76" -linux-loader = "0.4.0" +linux-loader = "0.10.0" log = "*" vm-memory = "0.13.1" vm-superio = "0.5.0" diff --git a/src/devices/src/virtio/block/device.rs b/src/devices/src/virtio/block/device.rs index 0852b447..b972ff74 100644 --- a/src/devices/src/virtio/block/device.rs +++ b/src/devices/src/virtio/block/device.rs @@ -191,7 +191,7 @@ mod tests { assert_eq!(block.device_type(), BLOCK_DEVICE_ID); assert_eq!( - mock.kernel_cmdline.as_str(), + mock.kernel_cmdline.as_string().unwrap(), format!( "virtio_mmio.device=4K@0x{:x}:{} root=/dev/vda ro", mock.mmio_cfg.range.base().0, diff --git a/src/devices/src/virtio/mod.rs b/src/devices/src/virtio/mod.rs index 6d8e939f..63b9e287 100644 --- a/src/devices/src/virtio/mod.rs +++ b/src/devices/src/virtio/mod.rs @@ -332,7 +332,7 @@ pub(crate) mod tests { mmio_mgr: IoManager::new(), mmio_cfg, // `4096` seems large enough for testing. - kernel_cmdline: Cmdline::new(4096), + kernel_cmdline: Cmdline::new(4096).unwrap(), } } pub fn env(&mut self) -> Env { @@ -387,7 +387,7 @@ pub(crate) mod tests { assert_eq!(bus_range.size(), range.size()); assert_eq!( - mock.kernel_cmdline.as_str(), + mock.kernel_cmdline.as_string().unwrap(), format!( "virtio_mmio.device=4K@0x{:x}:{}", range.base().0, @@ -396,7 +396,11 @@ pub(crate) mod tests { ); mock.env().insert_cmdline_str("ending_string").unwrap(); - assert!(mock.kernel_cmdline.as_str().ends_with("ending_string")); + assert!(mock + .kernel_cmdline + .as_string() + .unwrap() + .ends_with("ending_string")); } #[test] diff --git a/src/vmm/Cargo.toml b/src/vmm/Cargo.toml index 5324414d..415eadc7 100644 --- a/src/vmm/Cargo.toml +++ b/src/vmm/Cargo.toml @@ -9,7 +9,7 @@ event-manager = "0.3.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } kvm-ioctls = "0.13.0" libc = "0.2.91" -linux-loader = { version = "0.4.0", features = ["bzimage", "elf"] } +linux-loader = { version = "0.10.0", features = ["bzimage", "elf"] } vm-allocator = "0.1.0" vm-memory = { version = "0.13.1", features = ["backend-mmap"] } vm-superio = "0.5.0" diff --git a/src/vmm/src/config/mod.rs b/src/vmm/src/config/mod.rs index 275fca06..394df8c9 100644 --- a/src/vmm/src/config/mod.rs +++ b/src/vmm/src/config/mod.rs @@ -147,7 +147,7 @@ pub struct KernelConfig { impl KernelConfig { /// Return the default kernel command line used by the Vmm. pub fn default_cmdline() -> Cmdline { - let mut cmdline = Cmdline::new(KERNEL_CMDLINE_CAPACITY); + let mut cmdline = Cmdline::new(KERNEL_CMDLINE_CAPACITY).unwrap(); // It's ok to use `unwrap` because the initial capacity of `cmdline` should be // sufficient to accommodate the default kernel cmdline. @@ -181,7 +181,7 @@ impl TryFrom<&str> for KernelConfig { .map_err(ConversionError::new_kernel)? .unwrap_or_else(|| DEFAULT_KERNEL_CMDLINE.to_string()); - let mut cmdline = Cmdline::new(KERNEL_CMDLINE_CAPACITY); + let mut cmdline = Cmdline::new(KERNEL_CMDLINE_CAPACITY).unwrap(); cmdline .insert_str(cmdline_str) .map_err(|_| ConversionError::new_kernel("Kernel cmdline capacity error"))?; @@ -282,7 +282,7 @@ mod tests { // Check that additional commas in the kernel string do not cause a panic. let kernel_str = r#"path=/foo/bar,cmdline="foo=bar",kernel_load_addr=42,"#; - let mut foo_cmdline = Cmdline::new(128); + let mut foo_cmdline = Cmdline::new(128).unwrap(); foo_cmdline.insert_str("\"foo=bar\"").unwrap(); let expected_kernel_config = KernelConfig { diff --git a/src/vmm/src/lib.rs b/src/vmm/src/lib.rs index 36942237..dc5a5618 100644 --- a/src/vmm/src/lib.rs +++ b/src/vmm/src/lib.rs @@ -458,12 +458,13 @@ impl Vmm { // Add the kernel command line to the boot parameters. bootparams.hdr.cmd_line_ptr = CMDLINE_START as u32; - bootparams.hdr.cmdline_size = self.kernel_cfg.cmdline.as_str().len() as u32 + 1; + bootparams.hdr.cmdline_size = + String::try_from(&self.kernel_cfg.cmdline).unwrap().len() as u32 + 1; // Load the kernel command line into guest memory. - let mut cmdline = Cmdline::new(4096); + let mut cmdline = Cmdline::new(4096).unwrap(); cmdline - .insert_str(self.kernel_cfg.cmdline.as_str()) + .insert_str(String::try_from(&self.kernel_cfg.cmdline).unwrap()) .map_err(Error::Cmdline)?; load_cmdline( @@ -724,7 +725,7 @@ impl Vmm { let cmdline = &self.kernel_cfg.cmdline; let fdt = self .fdt_builder - .with_cmdline(cmdline.as_str().to_string()) + .with_cmdline(String::try_from(&cmdline).unwrap()) .with_num_vcpus(self.num_vcpus.try_into().unwrap()) .with_mem_size(mem_size) .create_fdt() @@ -750,6 +751,7 @@ mod tests { #[cfg(target_arch = "x86_64")] use std::path::Path; use std::path::PathBuf; + use std::fs::write; #[cfg(target_arch = "x86_64")] use vm_memory::{ bytes::ByteValued, @@ -956,13 +958,30 @@ mod tests { matches!(vmm.load_kernel().unwrap_err(), Error::IO(e) if e.kind() == ErrorKind::NotFound) ); - // Test case: kernel image is invalid. + // Test case: kernel image is invalid. This test has two flavors. In the first + // we try to load an empty file, in the second a file which has all zeros. let mut vmm_config = default_vmm_config(); let temp_file = TempFile::new().unwrap(); vmm_config.kernel_config.path = PathBuf::from(temp_file.as_path()); let mut vmm = mock_vmm(vmm_config); let err = vmm.load_kernel().unwrap_err(); + #[cfg(target_arch = "x86_64")] + assert!(matches!( + err, + Error::KernelLoad(loader::Error::Elf(loader::elf::Error::ReadElfHeader)) + )); + #[cfg(target_arch = "aarch64")] + assert!(matches!( + err, + Error::KernelLoad(loader::Error::Pe(loader::pe::Error::ReadImageHeader)) + )); + + let temp_file_path = PathBuf::from(temp_file.as_path()); + let buffer: Vec = vec![0_u8; 1024]; + write(temp_file_path, buffer).unwrap(); + let err = vmm.load_kernel().unwrap_err(); + #[cfg(target_arch = "x86_64")] assert!(matches!( err, @@ -970,6 +989,7 @@ mod tests { loader::bzimage::Error::InvalidBzImage )) )); + #[cfg(target_arch = "aarch64")] assert!(matches!( err, @@ -1011,15 +1031,18 @@ mod tests { let mut vmm_config = default_vmm_config(); vmm_config.kernel_config.path = default_elf_path(); let mut vmm = mock_vmm(vmm_config); - assert_eq!(vmm.kernel_cfg.cmdline.as_str(), DEFAULT_KERNEL_CMDLINE); + assert_eq!( + String::try_from(&vmm.kernel_cfg.cmdline).unwrap(), + DEFAULT_KERNEL_CMDLINE + ); vmm.add_serial_console().unwrap(); #[cfg(target_arch = "x86_64")] - assert!(vmm.kernel_cfg.cmdline.as_str().contains("console=ttyS0")); + assert!(String::try_from(&vmm.kernel_cfg.cmdline) + .unwrap() + .contains("console=ttyS0")); #[cfg(target_arch = "aarch64")] - assert!(vmm - .kernel_cfg - .cmdline - .as_str() + assert!(String::try_from(&vmm.kernel_cfg.cmdline) + .unwrap() .contains("earlycon=uart,mmio")); } @@ -1118,7 +1141,9 @@ mod tests { assert_eq!(vmm.block_devices.len(), 1); #[cfg(target_arch = "aarch64")] assert_eq!(vmm.fdt_builder.virtio_device_len(), 1); - assert!(vmm.kernel_cfg.cmdline.as_str().contains("virtio")); + assert!(String::try_from(&vmm.kernel_cfg.cmdline) + .unwrap() + .contains("virtio")); let invalid_block_config = BlockConfig { // Let's create the tempfile directly here so that it gets out of scope immediately @@ -1151,7 +1176,9 @@ mod tests { assert_eq!(vmm.net_devices.len(), 1); #[cfg(target_arch = "aarch64")] assert_eq!(vmm.fdt_builder.virtio_device_len(), 1); - assert!(vmm.kernel_cfg.cmdline.as_str().contains("virtio")); + assert!(String::try_from(&vmm.kernel_cfg.cmdline) + .unwrap() + .contains("virtio")); } } @@ -1172,7 +1199,7 @@ mod tests { let cmdline = &vmm.kernel_cfg.cmdline; let fdt = vmm .fdt_builder - .with_cmdline(cmdline.as_str().to_string()) + .with_cmdline(String::try_from(&cmdline).unwrap()) .with_num_vcpus(vmm.num_vcpus.try_into().unwrap()) .with_mem_size(mem_size) .with_serial_console(0x40000000, 0x1000) From a24e199c5bba10d8b7a672aa171a93b5fe85291f Mon Sep 17 00:00:00 2001 From: Alvise Rigo Date: Mon, 8 Jan 2024 16:23:07 +0100 Subject: [PATCH 05/10] cargo fmt: Fix formatting Signed-off-by: Alvise Rigo --- src/vmm/src/lib.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/vmm/src/lib.rs b/src/vmm/src/lib.rs index dc5a5618..85a56deb 100644 --- a/src/vmm/src/lib.rs +++ b/src/vmm/src/lib.rs @@ -747,16 +747,13 @@ mod tests { use linux_loader::elf::Elf64_Ehdr; #[cfg(target_arch = "x86_64")] use linux_loader::loader::{self, bootparam::setup_header, elf::PvhBootCapability}; + use std::fs::write; use std::io::ErrorKind; #[cfg(target_arch = "x86_64")] use std::path::Path; use std::path::PathBuf; - use std::fs::write; #[cfg(target_arch = "x86_64")] - use vm_memory::{ - bytes::ByteValued, - Address, GuestAddress, GuestMemory, - }; + use vm_memory::{bytes::ByteValued, Address, GuestAddress, GuestMemory}; use vmm_sys_util::{tempdir::TempDir, tempfile::TempFile}; @@ -1206,7 +1203,9 @@ mod tests { .with_rtc(0x40001000, 0x1000) .create_fdt() .unwrap(); - assert!(fdt.write_to_mem(vmm.guest_memory.as_ref(), fdt_offset).is_err()); + assert!(fdt + .write_to_mem(vmm.guest_memory.as_ref(), fdt_offset) + .is_err()); } } #[test] From 858ae3d902b25993cc8cff9a42737f15b1015594 Mon Sep 17 00:00:00 2001 From: Samuele Paone Date: Mon, 3 Feb 2025 17:14:48 +0100 Subject: [PATCH 06/10] versions: update dependencies This commit updates the following: - vm-memory - vmm-sys-util - kvm-ioctls - kvm-bindings - linux-loader - virtio-queue - virtio-blk - virtio-device - vm-superio - clap Signed-off-by: Samuele Paone --- Cargo.lock | 467 ++++++++++++++---- Cargo.toml | 2 +- src/api/Cargo.toml | 4 +- src/api/src/lib.rs | 31 +- src/arch/Cargo.toml | 7 +- src/devices/Cargo.toml | 23 +- src/devices/src/virtio/block/device.rs | 6 +- .../src/virtio/block/inorder_handler.rs | 6 +- src/devices/src/virtio/mod.rs | 14 +- src/devices/src/virtio/net/simple_handler.rs | 12 +- src/devices/src/virtio/net/tap.rs | 20 +- src/vm-vcpu-ref/Cargo.toml | 12 +- src/vm-vcpu-ref/src/aarch64/interrupts.rs | 5 +- src/vm-vcpu-ref/src/aarch64/regs/icc.rs | 14 +- src/vm-vcpu-ref/src/aarch64/regs/mod.rs | 7 +- src/vm-vcpu-ref/src/x86_64/cpuid.rs | 2 +- src/vm-vcpu-ref/src/x86_64/mptable.rs | 2 +- src/vm-vcpu/Cargo.toml | 10 +- src/vm-vcpu/src/vcpu/mod.rs | 56 ++- src/vm-vcpu/src/vcpu/regs.rs | 12 +- src/vm-vcpu/src/vm.rs | 8 +- src/vmm/Cargo.toml | 14 +- src/vmm/src/config/builder.rs | 72 ++- src/vmm/src/config/mod.rs | 125 ++--- src/vmm/src/lib.rs | 82 ++- 25 files changed, 672 insertions(+), 341 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a9d2e21e..7cd826cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,65 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys", +] [[package]] name = "api" @@ -15,56 +74,103 @@ dependencies = [ name = "arch" version = "0.1.0" dependencies = [ + "kvm-bindings", + "kvm-ioctls", + "log", "vm-fdt", "vm-memory", ] [[package]] -name = "atty" -version = "0.2.14" +name = "bindgen" +version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "hermit-abi", - "libc", - "winapi", + "bitflags 2.8.0", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", ] [[package]] -name = "autocfg" -version = "1.1.0" +name = "bitflags" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "1.2.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] [[package]] name = "clap" -version = "3.2.17" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29e724a68d9319343bb3328c9cc2dfde263f4b3142ee1059a9980580171c954b" +checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" dependencies = [ - "atty", - "bitflags", + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" +dependencies = [ + "anstream", + "anstyle", "clap_lex", - "indexmap", "strsim", - "termcolor", - "textwrap", ] [[package]] name = "clap_lex" -version = "0.2.4" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "devices" @@ -76,6 +182,7 @@ dependencies = [ "libc", "linux-loader", "log", + "thiserror", "utils", "virtio-blk", "virtio-device", @@ -86,56 +193,57 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "event-manager" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbb5f730c95a458654dee0afb13f1ebb4fc3d7b772789d5f30713ec68fed75d" +checksum = "90b16fe5161a1160c9c7cece9f7504f2412ef5e2c0643d1e322eccf37692a42b" dependencies = [ "libc", "vmm-sys-util", ] [[package]] -name = "hashbrown" -version = "0.12.3" +name = "glob" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] -name = "hermit-abi" -version = "0.1.17" +name = "is_terminal_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" -dependencies = [ - "libc", -] +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] -name = "indexmap" -version = "1.9.1" +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ - "autocfg", - "hashbrown", + "either", ] [[package]] name = "kvm-bindings" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efe70e65a5b092161d17f5005b66e5eefe7a94a70c332e755036fc4af78c4e79" +version = "0.11.0" +source = "git+https://github.com/rust-vmm/kvm.git#fac89f0a7b2f726577e859e5c869d5064a220460" dependencies = [ "vmm-sys-util", ] [[package]] name = "kvm-ioctls" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f8dc9c1896e5f144ec5d07169bc29f39a047686d29585a91f30489abfaeb6b" +version = "0.20.0" +source = "git+https://github.com/rust-vmm/kvm.git#fac89f0a7b2f726577e859e5c869d5064a220460" dependencies = [ + "bitflags 2.8.0", "kvm-bindings", "libc", "vmm-sys-util", @@ -143,94 +251,163 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.91" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libloading" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets", +] [[package]] name = "linux-loader" -version = "0.10.0" -source = "git+https://github.com/vitamin-v-software/linux-loader.git?rev=76aefef#76aefefc41c9a55217768bc0c89a55178db65b47" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870c3814345f050991f99869417779f6062542bcf4ed81db7a1b926ad1306638" dependencies = [ "vm-memory", ] [[package]] name = "log" -version = "0.4.20" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] -name = "os_str_bytes" -version = "6.3.0" +name = "memchr" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] -name = "proc-macro2" -version = "1.0.69" +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ - "unicode-ident", + "memchr", + "minimal-lexical", ] [[package]] -name = "quote" -version = "1.0.33" +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "prettyplease" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", + "syn", ] [[package]] -name = "strsim" -version = "0.10.0" +name = "proc-macro2" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] [[package]] -name = "syn" -version = "2.0.39" +name = "quote" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", - "quote", - "unicode-ident", ] [[package]] -name = "termcolor" -version = "1.1.3" +name = "regex" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ - "winapi-util", + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", ] [[package]] -name = "textwrap" -version = "0.15.0" +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", @@ -239,9 +416,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" + +[[package]] +name = "utf8parse" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "utils" @@ -249,13 +432,16 @@ version = "0.1.0" [[package]] name = "virtio-bindings" -version = "0.2.2" -source = "git+https://github.com/rust-vmm/vm-virtio.git#3bbf0db2c24fe756ac3593f259d048dafc1fd3c5" +version = "0.2.4" +source = "git+https://github.com/rust-vmm/vm-virtio.git#2c21f2536b0ab84589f011f3c3c395803bf9c6f1" +dependencies = [ + "bindgen", +] [[package]] name = "virtio-blk" version = "0.1.0" -source = "git+https://github.com/rust-vmm/vm-virtio.git#3bbf0db2c24fe756ac3593f259d048dafc1fd3c5" +source = "git+https://github.com/rust-vmm/vm-virtio.git#2c21f2536b0ab84589f011f3c3c395803bf9c6f1" dependencies = [ "log", "virtio-bindings", @@ -268,7 +454,7 @@ dependencies = [ [[package]] name = "virtio-device" version = "0.1.0" -source = "git+https://github.com/rust-vmm/vm-virtio.git#3bbf0db2c24fe756ac3593f259d048dafc1fd3c5" +source = "git+https://github.com/rust-vmm/vm-virtio.git#2c21f2536b0ab84589f011f3c3c395803bf9c6f1" dependencies = [ "log", "virtio-bindings", @@ -278,8 +464,8 @@ dependencies = [ [[package]] name = "virtio-queue" -version = "0.10.0" -source = "git+https://github.com/rust-vmm/vm-virtio.git#3bbf0db2c24fe756ac3593f259d048dafc1fd3c5" +version = "0.14.0" +source = "git+https://github.com/rust-vmm/vm-virtio.git#2c21f2536b0ab84589f011f3c3c395803bf9c6f1" dependencies = [ "log", "virtio-bindings", @@ -289,9 +475,9 @@ dependencies = [ [[package]] name = "vm-allocator" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "565b6886b7dd1b3bf34ec9243d90a97db4f2a83c2416caa52fcc95fd255d45e4" +checksum = "5e4ce718bd4e8d74b1747363e27f715a6b1bd6971597cb21425dadbf4e712241" dependencies = [ "libc", "thiserror", @@ -305,15 +491,15 @@ checksum = "599adbdaddea4947ca23c085d2b8e47f3499ccda35438424526f3853748a8eb6" [[package]] name = "vm-fdt" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43fb5a6bd1a7d423ad72802801036719b7546cf847a103f8fe4575f5b0d45a6" +checksum = "7e21282841a059bb62627ce8441c491f09603622cd5a21c43bfedc85a2952f23" [[package]] name = "vm-memory" -version = "0.13.1" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5376c9ee5ebe2103a310d8241936cfb93c946734b0479a4fa5bdf7a64abbacd8" +checksum = "f1720e7240cdc739f935456eb77f370d7e9b2a3909204da1e2b47bef1137a013" dependencies = [ "libc", "thiserror", @@ -322,9 +508,9 @@ dependencies = [ [[package]] name = "vm-superio" -version = "0.5.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b5231d334edbc03b22704caa1a022e4c07491d6df736593f26094df8b04a51" +checksum = "3428ee25acbfc75ed14600f2043876e0889cbd57c39dd441191417377cdceda0" [[package]] name = "vm-vcpu" @@ -349,8 +535,10 @@ dependencies = [ "kvm-bindings", "kvm-ioctls", "libc", + "log", "thiserror", "vm-memory", + "vm-superio", "vmm-sys-util", ] @@ -380,16 +568,17 @@ name = "vmm-reference" version = "0.1.0" dependencies = [ "api", + "linux-loader", "vmm", ] [[package]] name = "vmm-sys-util" -version = "0.11.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48b7b084231214f7427041e4220d77dfe726897a6d41fddee450696e66ff2a29" +checksum = "1d1435039746e20da4f8d507a72ee1b916f7b4b05af7a91c093d2c6561934ede" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", ] @@ -410,16 +599,80 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] -name = "winapi-util" -version = "0.1.5" +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "winapi", + "windows-targets", ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "windows-targets" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +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" diff --git a/Cargo.toml b/Cargo.toml index d3f94aca..08b63e84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ license = "Apache-2.0 OR BSD-3-Clause" [dependencies] vmm = { path = "src/vmm" } api = { path = "src/api" } +linux-loader = "0.13.0" [workspace] members = ["src/vm-vcpu-ref"] @@ -23,4 +24,3 @@ panic = "abort" [patch.crates-io] # TODO: Update with https://github.com/rust-vmm/linux-loader.git hash of commit # "add as_string() to the Cmdline crate" -linux-loader = { git = "https://github.com/vitamin-v-software/linux-loader.git", rev = "76aefef" } diff --git a/src/api/Cargo.toml b/src/api/Cargo.toml index 7c08e547..5f1a9161 100644 --- a/src/api/Cargo.toml +++ b/src/api/Cargo.toml @@ -5,9 +5,9 @@ authors = ["rust-vmm AWS maintainers "] edition = "2018" [dependencies] -clap = "3.2.17" +clap = "4.5.28" vmm = { path = "../vmm" } [dev-dependencies] -linux-loader = "0.10.0" +linux-loader = "0.13.0" diff --git a/src/api/src/lib.rs b/src/api/src/lib.rs index c9671da0..715ebe94 100644 --- a/src/api/src/lib.rs +++ b/src/api/src/lib.rs @@ -5,7 +5,7 @@ #![deny(missing_docs)] use std::result; -use clap::{App, Arg}; +use clap::{Arg, Command}; use vmm::VMMConfig; /// Command line parser. @@ -18,37 +18,32 @@ impl Cli { /// /// * `cmdline_args` - command line arguments passed to the application. pub fn launch(cmdline_args: Vec<&str>) -> result::Result { - let mut app = App::new(cmdline_args[0].to_string()) + let mut app = Command::new("vmm-reference") .arg( - Arg::with_name("memory") + Arg::new("memory") .long("memory") - .takes_value(true) .help("Guest memory configuration.\n\tFormat: \"size_mib=\""), ) .arg( - Arg::with_name("vcpu") + Arg::new("vcpu") .long("vcpu") - .takes_value(true) .help("vCPU configuration.\n\tFormat: \"num=\""), ) .arg( - Arg::with_name("kernel") + Arg::new("kernel") .long("kernel") .required(true) - .takes_value(true) .help("Kernel configuration.\n\tFormat: \"path=[,cmdline=,kernel_load_addr=]\""), ) .arg( - Arg::with_name("net") + Arg::new("net") .long("net") - .takes_value(true) .help("Network device configuration. \n\tFormat: \"tap=\"") ) .arg( - Arg::with_name("block") + Arg::new("block") .long("block") .required(false) - .takes_value(true) .help("Block device configuration. \n\tFormat: \"path=\"") ); @@ -58,17 +53,17 @@ impl Cli { let _ = app.write_long_help(&mut help_msg_buf); let help_msg = String::from_utf8_lossy(&help_msg_buf); - let matches = app.get_matches_from_safe(cmdline_args).map_err(|e| { + let matches = app.try_get_matches_from(cmdline_args).map_err(|e| { eprintln!("{}", help_msg); format!("Invalid command line arguments: {}", e) })?; VMMConfig::builder() - .memory_config(matches.value_of("memory")) - .kernel_config(matches.value_of("kernel")) - .vcpu_config(matches.value_of("vcpu")) - .net_config(matches.value_of("net")) - .block_config(matches.value_of("block")) + .memory_config(matches.get_one::("memory")) + .kernel_config(matches.get_one::("kernel")) + .vcpu_config(matches.get_one::("vcpu")) + .net_config(matches.get_one::("net")) + .block_config(matches.get_one::("block")) .build() .map_err(|e| format!("{:?}", e)) } diff --git a/src/arch/Cargo.toml b/src/arch/Cargo.toml index 437893c5..a9c51bf9 100644 --- a/src/arch/Cargo.toml +++ b/src/arch/Cargo.toml @@ -7,5 +7,8 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -vm-fdt = "0.2.0" -vm-memory = "0.13.1" +log = "0.4.21" +vm-fdt = "0.3.0" +vm-memory = "0.16.0" +kvm-ioctls = { git = "https://github.com/rust-vmm/kvm.git"} +kvm-bindings = { git = "https://github.com/rust-vmm/kvm.git", features = ["fam-wrappers"] } diff --git a/src/devices/Cargo.toml b/src/devices/Cargo.toml index 0338787d..a243ae8c 100644 --- a/src/devices/Cargo.toml +++ b/src/devices/Cargo.toml @@ -6,22 +6,23 @@ edition = "2018" license = "Apache-2.0 OR BSD-3-Clause" [dependencies] -event-manager = { version = "0.3.0", features = ["remote_endpoint"] } -kvm-ioctls = "0.13.0" +event-manager = { version = "0.4.0", features = ["remote_endpoint"] } +kvm-ioctls = { git = "https://github.com/rust-vmm/kvm.git", version = "0.20.0"} libc = "0.2.76" -linux-loader = "0.10.0" +linux-loader = "0.13.0" log = "*" -vm-memory = "0.13.1" -vm-superio = "0.5.0" -vmm-sys-util = "0.11.2" +vm-memory = "0.16.0" +vm-superio = "0.8.0" +vmm-sys-util = "0.12.1" vm-device = "0.1.0" -virtio-blk = { git = "https://github.com/rust-vmm/vm-virtio.git", features = ["backend-stdio"] } -virtio-device = { git = "https://github.com/rust-vmm/vm-virtio.git"} -virtio-queue = { git = "https://github.com/rust-vmm/vm-virtio.git"} +thiserror = "1.0" +virtio-blk = { git = "https://github.com/rust-vmm/vm-virtio.git", version = "0.1.0", features = ["backend-stdio"] } +virtio-device = { git = "https://github.com/rust-vmm/vm-virtio.git", version = "0.1.0"} +virtio-queue = { git = "https://github.com/rust-vmm/vm-virtio.git", version = "0.14.0"} utils = { path = "../utils" } [dev-dependencies] -vm-memory = { version = "0.13.1", features = ["backend-mmap"] } -kvm-bindings = "0.6.0" +vm-memory = { version = "0.16.0", features = ["backend-mmap"] } +kvm-bindings = { git = "https://github.com/rust-vmm/kvm.git", version = "0.11.0"} diff --git a/src/devices/src/virtio/block/device.rs b/src/devices/src/virtio/block/device.rs index b972ff74..4824cb00 100644 --- a/src/devices/src/virtio/block/device.rs +++ b/src/devices/src/virtio/block/device.rs @@ -191,7 +191,11 @@ mod tests { assert_eq!(block.device_type(), BLOCK_DEVICE_ID); assert_eq!( - mock.kernel_cmdline.as_string().unwrap(), + mock.kernel_cmdline + .as_cstring() + .unwrap() + .into_string() + .unwrap(), format!( "virtio_mmio.device=4K@0x{:x}:{} root=/dev/vda ro", mock.mmio_cfg.range.base().0, diff --git a/src/devices/src/virtio/block/inorder_handler.rs b/src/devices/src/virtio/block/inorder_handler.rs index 376fc221..3f364ff3 100644 --- a/src/devices/src/virtio/block/inorder_handler.rs +++ b/src/devices/src/virtio/block/inorder_handler.rs @@ -13,11 +13,15 @@ use vm_memory::{self, GuestAddressSpace, GuestMemoryMmap}; use std::sync::Arc; use crate::virtio::SignalUsedQueue; +use thiserror::Error; -#[derive(Debug)] +#[derive(Debug, Error)] pub enum Error { + #[error("Guest memory error: {0}")] GuestMemory(vm_memory::GuestMemoryError), + #[error("Queue error: {0}")] Queue(virtio_queue::Error), + #[error("Process request error")] ProcessRequest(stdio_executor::ProcessReqError), } diff --git a/src/devices/src/virtio/mod.rs b/src/devices/src/virtio/mod.rs index 63b9e287..a0b24822 100644 --- a/src/devices/src/virtio/mod.rs +++ b/src/devices/src/virtio/mod.rs @@ -118,7 +118,7 @@ pub struct Env<'a, M, B> { pub kernel_cmdline: &'a mut Cmdline, } -impl<'a, M, B> Env<'a, M, B> +impl Env<'_, M, B> where // We're using this (more convoluted) bound so we can pass both references and smart // pointers such as mutex guards here. @@ -350,7 +350,7 @@ pub(crate) mod tests { // the IRQ fds and thus test the virtio functionality in arm as well. fn create_gic(vm_fd: &VmFd) { let mut create_device_attr = kvm_create_device { - type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3 as u32, + type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, fd: 0, flags: 0, }; @@ -387,7 +387,11 @@ pub(crate) mod tests { assert_eq!(bus_range.size(), range.size()); assert_eq!( - mock.kernel_cmdline.as_string().unwrap(), + mock.kernel_cmdline + .as_cstring() + .unwrap() + .into_string() + .unwrap(), format!( "virtio_mmio.device=4K@0x{:x}:{}", range.base().0, @@ -398,7 +402,9 @@ pub(crate) mod tests { mock.env().insert_cmdline_str("ending_string").unwrap(); assert!(mock .kernel_cmdline - .as_string() + .as_cstring() + .unwrap() + .into_string() .unwrap() .ends_with("ending_string")); } diff --git a/src/devices/src/virtio/net/simple_handler.rs b/src/devices/src/virtio/net/simple_handler.rs index 3972c88e..d7ed9b60 100644 --- a/src/devices/src/virtio/net/simple_handler.rs +++ b/src/devices/src/virtio/net/simple_handler.rs @@ -7,6 +7,7 @@ use std::io::{self, Read, Write}; use std::result; use log::warn; +use thiserror::Error; use virtio_queue::{DescriptorChain, Queue, QueueOwnedT, QueueT}; use vm_memory::{Bytes, GuestMemoryMmap}; @@ -24,16 +25,19 @@ use std::sync::Arc; // We assume the TX frame will not exceed this size either. const MAX_BUFFER_SIZE: usize = 65562; -#[derive(Debug)] +#[derive(Debug, Error)] pub enum Error { + #[error("Guest memory error: {0}")] GuestMemory(vm_memory::GuestMemoryError), - Queue(virtio_queue::Error), + #[error("Queue error")] + Queue, + #[error("Tap device error: {0}")] Tap(io::Error), } impl From for Error { - fn from(e: virtio_queue::Error) -> Self { - Error::Queue(e) + fn from(_e: virtio_queue::Error) -> Self { + Error::Queue } } diff --git a/src/devices/src/virtio/net/tap.rs b/src/devices/src/virtio/net/tap.rs index 236f836a..a760a789 100644 --- a/src/devices/src/virtio/net/tap.rs +++ b/src/devices/src/virtio/net/tap.rs @@ -18,6 +18,7 @@ use vmm_sys_util::{ioctl_ioc_nr, ioctl_iow_nr}; use super::bindings::ifreq; +use std::ffi::CString; // As defined in the Linux UAPI: // https://elixir.bootlin.com/linux/v4.17/source/include/uapi/linux/if.h#L33 const IFACE_NAME_MAX_LEN: usize = 16; @@ -85,7 +86,7 @@ impl IfReqBuilder { } pub fn if_name(mut self, if_name: &[u8; IFACE_NAME_MAX_LEN]) -> Self { - // Since we don't call as_mut on the same union field more than once, this block is safe. + // SAFETY: Since we don't call as_mut on the same union field more than once, this block is safe. let ifrn_name = unsafe { self.0.ifr_ifrn.ifrn_name.as_mut() }; ifrn_name.copy_from_slice(if_name.as_ref()); @@ -93,7 +94,7 @@ impl IfReqBuilder { } pub(crate) fn flags(mut self, flags: i16) -> Self { - // Since we don't call as_mut on the same union field more than once, this block is safe. + // SAFETY: Since we don't call as_mut on the same union field more than once, this block is safe. let ifru_flags = unsafe { self.0.ifr_ifru.ifru_flags.as_mut() }; *ifru_flags = flags; @@ -101,7 +102,7 @@ impl IfReqBuilder { } pub(crate) fn execute(mut self, socket: &F, ioctl: u64) -> Result { - // ioctl is safe. Called with a valid socket fd, and we check the return. + // SAFETY: ioctl is safe. Called with a valid socket fd, and we check the return. let ret = unsafe { ioctl_with_mut_ref(socket, ioctl, &mut self.0) }; if ret < 0 { return Err(Error::IoctlError(IoError::last_os_error())); @@ -119,18 +120,21 @@ impl Tap { pub fn open_named(if_name: &str) -> Result { let terminated_if_name = build_terminated_if_name(if_name)?; + // SAFETY: Open calls are safe because we give a constant null-terminated + // string and verify the result. let fd = unsafe { // Open calls are safe because we give a constant null-terminated // string and verify the result. + let tun_str = CString::new("/dev/net/tun").unwrap(); libc::open( - b"/dev/net/tun\0".as_ptr() as *const c_char, + tun_str.as_ptr() as *const c_char, libc::O_RDWR | libc::O_NONBLOCK | libc::O_CLOEXEC, ) }; if fd < 0 { return Err(Error::OpenTun(IoError::last_os_error())); } - // We just checked that the fd is valid. + // SAFETY: We just checked that the fd is valid. let tuntap = unsafe { File::from_raw_fd(fd) }; let ifreq = IfReqBuilder::new() @@ -138,9 +142,9 @@ impl Tap { .flags((IFF_TAP | IFF_NO_PI | IFF_VNET_HDR) as i16) .execute(&tuntap, TUNSETIFF())?; - // Safe since only the name is accessed, and it's cloned out. Ok(Tap { tap_file: tuntap, + // SAFETY: since only the name is accessed, and it's cloned out. if_name: unsafe { *ifreq.ifr_ifrn.ifrn_name.as_ref() }, }) } @@ -156,7 +160,7 @@ impl Tap { /// Set the offload flags for the tap interface. pub fn set_offload(&self, flags: c_uint) -> Result<()> { - // ioctl is safe. Called with a valid tap fd, and we check the return. + // SAFETY: ioctl is safe. Called with a valid tap fd, and we check the return. let ret = unsafe { ioctl_with_val(&self.tap_file, TUNSETOFFLOAD(), c_ulong::from(flags)) }; if ret < 0 { return Err(Error::IoctlError(IoError::last_os_error())); @@ -167,7 +171,7 @@ impl Tap { /// Set the size of the vnet hdr. pub fn set_vnet_hdr_size(&self, size: c_int) -> Result<()> { - // ioctl is safe. Called with a valid tap fd, and we check the return. + // SAFETY: ioctl is safe. Called with a valid tap fd, and we check the return. let ret = unsafe { ioctl_with_ref(&self.tap_file, TUNSETVNETHDRSZ(), &size) }; if ret < 0 { return Err(Error::IoctlError(IoError::last_os_error())); diff --git a/src/vm-vcpu-ref/Cargo.toml b/src/vm-vcpu-ref/Cargo.toml index 848554a6..92379994 100644 --- a/src/vm-vcpu-ref/Cargo.toml +++ b/src/vm-vcpu-ref/Cargo.toml @@ -11,11 +11,13 @@ keywords = ["virt", "kvm", "vm"] [dependencies] thiserror = "1.0.30" -kvm-ioctls = "0.13.0" -kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -vm-memory = "0.13.1" +kvm-ioctls = { git = "https://github.com/rust-vmm/kvm.git", version = "0.20.0"} +kvm-bindings = { git = "https://github.com/rust-vmm/kvm.git", version = "0.11.0", features = ["fam-wrappers"] } +vm-memory = "0.16.0" libc = "0.2.76" +vm-superio = "0.8.0" +log = "0.4.21" [dev-dependencies] -vm-memory = { version = "0.13.1", features = ["backend-mmap"] } -vmm-sys-util = "0.11.2" +vm-memory = { version = "0.16.0", features = ["backend-mmap"] } +vmm-sys-util = "0.12.1" diff --git a/src/vm-vcpu-ref/src/aarch64/interrupts.rs b/src/vm-vcpu-ref/src/aarch64/interrupts.rs index d5e77a0e..0e4da236 100644 --- a/src/vm-vcpu-ref/src/aarch64/interrupts.rs +++ b/src/vm-vcpu-ref/src/aarch64/interrupts.rs @@ -392,7 +392,10 @@ mod tests { addr: &mut data as *const u32 as u64, ..Default::default() }; - gic.device_fd().get_device_attr(&mut nr_irqs_attr).unwrap(); + //SAFETY: we prepere the structure + unsafe { + gic.device_fd().get_device_attr(&mut nr_irqs_attr).unwrap(); + } assert_eq!(data, config.num_irqs); } diff --git a/src/vm-vcpu-ref/src/aarch64/regs/icc.rs b/src/vm-vcpu-ref/src/aarch64/regs/icc.rs index 837fe312..94ccc466 100644 --- a/src/vm-vcpu-ref/src/aarch64/regs/icc.rs +++ b/src/vm-vcpu-ref/src/aarch64/regs/icc.rs @@ -63,16 +63,12 @@ pub struct GicSysRegsState { impl SimpleReg { const fn gic_sys_reg(op0: u64, op1: u64, crn: u64, crm: u64, op2: u64) -> SimpleReg { - let offset = (((op0 as u64) << KVM_REG_ARM64_SYSREG_OP0_SHIFT) + let offset = ((op0 << KVM_REG_ARM64_SYSREG_OP0_SHIFT) & KVM_REG_ARM64_SYSREG_OP0_MASK as u64) - | (((op1 as u64) << KVM_REG_ARM64_SYSREG_OP1_SHIFT) - & KVM_REG_ARM64_SYSREG_OP1_MASK as u64) - | (((crn as u64) << KVM_REG_ARM64_SYSREG_CRN_SHIFT) - & KVM_REG_ARM64_SYSREG_CRN_MASK as u64) - | (((crm as u64) << KVM_REG_ARM64_SYSREG_CRM_SHIFT) - & KVM_REG_ARM64_SYSREG_CRM_MASK as u64) - | (((op2 as u64) << KVM_REG_ARM64_SYSREG_OP2_SHIFT) - & KVM_REG_ARM64_SYSREG_OP2_MASK as u64); + | ((op1 << KVM_REG_ARM64_SYSREG_OP1_SHIFT) & KVM_REG_ARM64_SYSREG_OP1_MASK as u64) + | ((crn << KVM_REG_ARM64_SYSREG_CRN_SHIFT) & KVM_REG_ARM64_SYSREG_CRN_MASK as u64) + | ((crm << KVM_REG_ARM64_SYSREG_CRM_SHIFT) & KVM_REG_ARM64_SYSREG_CRM_MASK as u64) + | ((op2 << KVM_REG_ARM64_SYSREG_OP2_SHIFT) & KVM_REG_ARM64_SYSREG_OP2_MASK as u64); SimpleReg { offset, size: 8 } } diff --git a/src/vm-vcpu-ref/src/aarch64/regs/mod.rs b/src/vm-vcpu-ref/src/aarch64/regs/mod.rs index 81d92108..65f47a1e 100644 --- a/src/vm-vcpu-ref/src/aarch64/regs/mod.rs +++ b/src/vm-vcpu-ref/src/aarch64/regs/mod.rs @@ -142,9 +142,10 @@ where let mut data = Vec::with_capacity(reg.iter::().count()); for offset in reg.iter::() { let mut val = RegChunk::default(); - fd.get_device_attr(&mut kvm_device_attr( - group, offset, &mut val, mpidr, mpidr_mask, - ))?; + let mut dev_attr = kvm_device_attr(group, offset, &mut val, mpidr, mpidr_mask); + unsafe { + fd.get_device_attr(&mut dev_attr)?; + } data.push(val); } diff --git a/src/vm-vcpu-ref/src/x86_64/cpuid.rs b/src/vm-vcpu-ref/src/x86_64/cpuid.rs index 5a615697..951c193f 100644 --- a/src/vm-vcpu-ref/src/x86_64/cpuid.rs +++ b/src/vm-vcpu-ref/src/x86_64/cpuid.rs @@ -48,7 +48,7 @@ pub fn filter_cpuid(kvm: &Kvm, vcpu_id: u8, cpu_count: u8, cpuid: &mut CpuId) { if kvm.check_extension(TscDeadlineTimer) { entry.ecx |= 1 << ECX_TSC_DEADLINE_TIMER_SHIFT; } - entry.ebx = ((vcpu_id as u32) << EBX_CPUID_SHIFT) as u32 + entry.ebx = ((vcpu_id as u32) << EBX_CPUID_SHIFT) | (EBX_CLFLUSH_CACHELINE << EBX_CLFLUSH_SIZE_SHIFT); if cpu_count > 1 { entry.ebx |= (cpu_count as u32) << EBX_CPU_COUNT_SHIFT; diff --git a/src/vm-vcpu-ref/src/x86_64/mptable.rs b/src/vm-vcpu-ref/src/x86_64/mptable.rs index 5fc2cb85..98bcccad 100644 --- a/src/vm-vcpu-ref/src/x86_64/mptable.rs +++ b/src/vm-vcpu-ref/src/x86_64/mptable.rs @@ -95,7 +95,7 @@ const MPC_SIGNATURE: [c_char; 4] = char_array!(c_char; 'P', 'C', 'M', 'P'); const MPC_SPEC: i8 = 4; const MPC_OEM: [c_char; 8] = char_array!(c_char; 'r', 'u', 's', 't', '-', 'v', 'm', 'm'); const MPC_PRODUCT_ID: [c_char; 12] = ['0' as c_char; 12]; -const BUS_TYPE_ISA: [u8; 6] = char_array!(u8; 'I', 'S', 'A', ' ', ' ', ' '); +const BUS_TYPE_ISA: [u8; 6] = char_array!(u8; b'I', b'S', b'A', b' ', b' ', b' '); const IO_APIC_DEFAULT_PHYS_BASE: u32 = 0xfec0_0000; // source: linux/arch/x86/include/asm/apicdef.h const APIC_DEFAULT_PHYS_BASE: u32 = 0xfee0_0000; // source: linux/arch/x86/include/asm/apicdef.h const APIC_VERSION: u8 = 0x14; diff --git a/src/vm-vcpu/Cargo.toml b/src/vm-vcpu/Cargo.toml index f7e5e6f1..32040516 100644 --- a/src/vm-vcpu/Cargo.toml +++ b/src/vm-vcpu/Cargo.toml @@ -9,10 +9,10 @@ edition = "2018" [dependencies] thiserror = "1.0.30" libc = "0.2.76" -kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.13.0" -vm-memory = "0.13.1" -vmm-sys-util = "0.11.2" +kvm-bindings = { git = "https://github.com/rust-vmm/kvm.git", version = "0.11.0", features = ["fam-wrappers"] } +kvm-ioctls = { git = "https://github.com/rust-vmm/kvm.git", version = "0.20.0"} +vm-memory = "0.16.0" +vmm-sys-util = "0.12.1" vm-device = "0.1.0" utils = { path = "../utils" } @@ -20,4 +20,4 @@ vm-vcpu-ref = { path = "../vm-vcpu-ref" } arch = { path = "../arch" } [dev-dependencies] -vm-memory = { version = "0.13.1", features = ["backend-mmap"] } +vm-memory = { version = "0.16.0", features = ["backend-mmap"] } diff --git a/src/vm-vcpu/src/vcpu/mod.rs b/src/vm-vcpu/src/vcpu/mod.rs index 7341067b..8aa9cad1 100644 --- a/src/vm-vcpu/src/vcpu/mod.rs +++ b/src/vm-vcpu/src/vcpu/mod.rs @@ -268,7 +268,6 @@ impl VcpuConfigList { /// Structure holding the kvm state for an x86_64 VCPU. #[cfg(target_arch = "x86_64")] -#[derive(Clone)] pub struct VcpuState { pub cpuid: CpuId, pub msrs: Msrs, @@ -283,6 +282,24 @@ pub struct VcpuState { pub config: VcpuConfig, } +#[cfg(target_arch = "x86_64")] +impl Clone for VcpuState { + fn clone(&self) -> Self { + VcpuState { + cpuid: self.cpuid.clone(), + msrs: self.msrs.clone(), + debug_regs: self.debug_regs, + lapic: self.lapic, + mp_state: self.mp_state, + regs: self.regs, + sregs: self.sregs, + vcpu_events: self.vcpu_events, + xcrs: self.xcrs, + xsave: Default::default(), + config: self.config.clone(), + } + } +} #[cfg(target_arch = "aarch64")] #[derive(Clone)] pub struct VcpuState { @@ -323,7 +340,7 @@ pub struct KvmVcpu { } impl KvmVcpu { - thread_local!(static TLS_VCPU_PTR: RefCell> = RefCell::new(None)); + thread_local!(static TLS_VCPU_PTR: RefCell> = const { RefCell::new(None) }); /// Create a new vCPU. // This is needed so we can initialize the vcpu the same way on x86_64 and aarch64, but @@ -412,7 +429,7 @@ impl KvmVcpu { fn set_state(&mut self, state: VcpuState) -> Result<()> { for reg in state.regs { self.vcpu_fd - .set_one_reg(reg.id, reg.addr as u128) + .set_one_reg(reg.id, &u128::to_le_bytes(reg.addr.into())) .map_err(Error::VcpuSetReg)?; } @@ -440,7 +457,6 @@ impl KvmVcpu { run_barrier, run_state, }; - #[cfg(target_arch = "aarch64")] vcpu.init(vm_fd)?; @@ -458,7 +474,7 @@ impl KvmVcpu { data = (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | PSR_MODE_EL1h).into(); reg_id = arm64_core_reg!(pstate); self.vcpu_fd - .set_one_reg(reg_id, data as u128) + .set_one_reg(reg_id, &(data as u128).to_le_bytes()) .map_err(Error::VcpuSetReg)?; // Other cpus are powered off initially @@ -466,11 +482,11 @@ impl KvmVcpu { /* X0 -- fdt address */ let mut fdt_offset: u64 = guest_mem.iter().map(|region| region.len()).sum(); fdt_offset = fdt_offset - AARCH64_FDT_MAX_SIZE - 0x10000; - data = (AARCH64_PHYS_MEM_START + fdt_offset) as u64; + data = AARCH64_PHYS_MEM_START + fdt_offset; // hack -- can't get this to do offsetof(regs[0]) but luckily it's at offset 0 reg_id = arm64_core_reg!(regs); self.vcpu_fd - .set_one_reg(reg_id, data as u128) + .set_one_reg(reg_id, &(data as u128).to_le_bytes()) .map_err(Error::VcpuSetReg)?; } @@ -554,11 +570,11 @@ impl KvmVcpu { // Write segments to guest memory. gdt_table.write_to_mem(guest_memory).map_err(Error::Gdt)?; - sregs.gdt.base = BOOT_GDT_OFFSET as u64; + sregs.gdt.base = BOOT_GDT_OFFSET; sregs.gdt.limit = std::mem::size_of_val(&gdt_table) as u16 - 1; write_idt_value(0, guest_memory).map_err(Error::Gdt)?; - sregs.idt.base = BOOT_IDT_OFFSET as u64; + sregs.idt.base = BOOT_IDT_OFFSET; sregs.idt.limit = std::mem::size_of::() as u16 - 1; sregs.cs = code_seg; @@ -642,7 +658,7 @@ impl KvmVcpu { fn init_tls(&mut self) -> Result<()> { Self::TLS_VCPU_PTR.with(|vcpu| { if vcpu.borrow().is_none() { - *vcpu.borrow_mut() = Some(self as *const KvmVcpu); + *vcpu.borrow_mut() = Some(self as *mut KvmVcpu); Ok(()) } else { Err(Error::TlsInitialized) @@ -653,13 +669,13 @@ impl KvmVcpu { fn set_local_immediate_exit(value: u8) { Self::TLS_VCPU_PTR.with(|v| { - if let Some(vcpu) = *v.borrow() { - // The block below modifies a mmaped memory region (`kvm_run` struct) which is valid + if let Some(vcpu) = *v.borrow_mut() { + // SAFETY: The block below modifies a mmaped memory region (`kvm_run` struct) which is valid // as long as the `VMM` is still in scope. This function is called in response to // SIGRTMIN(), while the vCPU threads are still active. Their termination are // strictly bound to the lifespan of the `VMM` and it precedes the `VMM` dropping. unsafe { - let vcpu_ref = &*vcpu; + let vcpu_ref = &mut *vcpu; vcpu_ref.vcpu_fd.set_kvm_immediate_exit(value); }; } @@ -670,8 +686,8 @@ impl KvmVcpu { /// /// # Arguments /// - /// * `instruction_pointer`: Represents the start address of the vcpu. This can be None - /// when the IP is specified using the platform dependent registers. + /// * `instruction_pointer`: Represents the start address of the vcpu. + /// This can be None when the IP is specified using the platform dependent registers. #[allow(clippy::if_same_then_else)] pub fn run(&mut self, instruction_pointer: Option) -> Result<()> { if let Some(ip) = instruction_pointer { @@ -682,7 +698,7 @@ impl KvmVcpu { let data = ip.0; let reg_id = arm64_core_reg!(pc); self.vcpu_fd - .set_one_reg(reg_id, data as u128) + .set_one_reg(reg_id, &(data as u128).to_le_bytes()) .map_err(Error::VcpuSetReg)?; } } @@ -783,6 +799,14 @@ impl KvmVcpu { eprintln!("Failed to set canon mode. Stdin will not echo."); } self.run_state.set_and_notify(VmRunState::Exiting); + if type_ == KVM_SYSTEM_EVENT_SHUTDOWN { + println!("KVM session ended correctly"); + } else { + println!( + "Exit reason: {:#?}", + VcpuExit::SystemEvent(type_, flags) + ); + } break; } _ => { diff --git a/src/vm-vcpu/src/vcpu/regs.rs b/src/vm-vcpu/src/vcpu/regs.rs index ffbbd111..6777e875 100644 --- a/src/vm-vcpu/src/vcpu/regs.rs +++ b/src/vm-vcpu/src/vcpu/regs.rs @@ -60,10 +60,14 @@ pub fn get_regs_and_mpidr(vcpu_fd: &VcpuFd) -> Result<(Vec, u64), E let mut mpidr = None; let mut regs = Vec::with_capacity(reg_id_list.as_slice().len()); for &id in reg_id_list.as_slice() { - let addr = vcpu_fd.get_one_reg(id).map_err(Error::VcpuGetReg)?; + let mut addr = [0_u8; 16]; + vcpu_fd + .get_one_reg(id, &mut addr) + .map_err(Error::VcpuGetReg)?; + let new_addr = &addr[0..8]; regs.push(kvm_one_reg { id, - addr: addr.try_into().unwrap(), + addr: u64::from_le_bytes(new_addr.try_into().unwrap()), }); if id == MPIDR_EL1 { @@ -76,5 +80,7 @@ pub fn get_regs_and_mpidr(vcpu_fd: &VcpuFd) -> Result<(Vec, u64), E } // unwrap() is safe because of the is_none() check above - Ok((regs, mpidr.unwrap().try_into().unwrap())) + let new_mpidr = &mpidr.unwrap()[0..8]; + // unwrap safe because we take the first 8 bits + Ok((regs, u64::from_le_bytes(new_mpidr.try_into().unwrap()))) } diff --git a/src/vm-vcpu/src/vm.rs b/src/vm-vcpu/src/vm.rs index 05514fe6..a67df99d 100644 --- a/src/vm-vcpu/src/vm.rs +++ b/src/vm-vcpu/src/vm.rs @@ -349,13 +349,13 @@ impl KvmVm { let memory_region = kvm_userspace_memory_region { slot: index as u32, guest_phys_addr: region.start_addr().raw_value(), - memory_size: region.len() as u64, + memory_size: region.len(), // It's safe to unwrap because the guest address is valid. userspace_addr: guest_memory.get_host_address(region.start_addr()).unwrap() as u64, flags: 0, }; - // Safe because: + // SAFETY: because: // * userspace_addr is a valid address for a memory region, obtained by calling // get_host_address() on a valid region's start address; // * the memory regions do not overlap - there's either a single region spanning @@ -478,7 +478,7 @@ impl KvmVm { /// # Arguments /// /// * `vcpu_run_addr`: address in guest memory where the vcpu run starts. This can be None - /// when the IP is specified using the platform dependent registers. + /// when the IP is specified using the platform dependent registers. pub fn run(&mut self, vcpu_run_addr: Option) -> Result<()> { if self.vcpus.len() != self.config.num_vcpus as usize { return Err(Error::RunVcpus(io::Error::from(ErrorKind::InvalidInput))); @@ -659,7 +659,7 @@ mod tests { #[test] #[cfg(target_arch = "x86_64")] fn test_failed_setup_mptable() { - let num_vcpus = (MAX_SUPPORTED_CPUS + 1) as u8; + let num_vcpus = MAX_SUPPORTED_CPUS + 1; let kvm = Kvm::new().unwrap(); let guest_memory = default_memory(); let res = default_vm(&kvm, &guest_memory, num_vcpus); diff --git a/src/vmm/Cargo.toml b/src/vmm/Cargo.toml index 415eadc7..8bb383bf 100644 --- a/src/vmm/Cargo.toml +++ b/src/vmm/Cargo.toml @@ -5,15 +5,15 @@ authors = ["rust-vmm AWS maintainers "] edition = "2018" [dependencies] -event-manager = "0.3.0" -kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.13.0" +event-manager = "0.4.0" +kvm-ioctls = { git = "https://github.com/rust-vmm/kvm.git"} +kvm-bindings = { git = "https://github.com/rust-vmm/kvm.git", features = ["fam-wrappers"] } libc = "0.2.91" -linux-loader = { version = "0.10.0", features = ["bzimage", "elf"] } +linux-loader = { version = "0.13.0", features = ["bzimage", "elf"] } vm-allocator = "0.1.0" -vm-memory = { version = "0.13.1", features = ["backend-mmap"] } -vm-superio = "0.5.0" -vmm-sys-util = "0.11.2" +vm-memory = { version = "0.16.0", features = ["backend-mmap"] } +vm-superio = "0.8.0" +vmm-sys-util = "0.12.1" vm-device = "0.1.0" devices = { path = "../devices" } diff --git a/src/vmm/src/config/builder.rs b/src/vmm/src/config/builder.rs index 30fb0a25..2f8e23df 100644 --- a/src/vmm/src/config/builder.rs +++ b/src/vmm/src/config/builder.rs @@ -40,29 +40,25 @@ impl Builder { /// # use vmm::VMMConfig; /// /// let vmmconfig = VMMConfig::builder() - /// .memory_config(Some("size_mib=1024")) - /// .vcpu_config(Some("num=1")) - /// .kernel_config(Some("path=/path/to/bzImage")) - /// .net_config(Some("tap=tap0")) - /// .block_config(Some("path=/dev/loop0")) + /// .memory_config(Some(&String::from("size_mib=1024"))) + /// .vcpu_config(Some(&String::from("num=1"))) + /// .kernel_config(Some(&String::from("path=/path/to/bzImage"))) + /// .net_config(Some(&String::from("tap=tap0"))) + /// .block_config(Some(&String::from("path=/dev/loop0"))) /// .build(); /// /// assert!(vmmconfig.is_ok()); /// ``` pub fn build(&self) -> Result { // Check if there are any errors - match &self.inner { - Ok(vc) => { - // Empty kernel image path. - if vc.kernel_config.path.to_str().unwrap().is_empty() { - return Err(ConversionError::ParseKernel( - "Kernel Image Path is Empty.".to_string(), - )); - } + if let Ok(vc) = &self.inner { + // Empty kernel image path. + if vc.kernel_config.path.to_str().unwrap().is_empty() { + return Err(ConversionError::ParseKernel( + "Kernel Image Path is Empty.".to_string(), + )); } - Err(_) => {} } - self.inner.clone() } @@ -195,8 +191,8 @@ mod tests { #[test] fn test_builder_memory_config_success() { let vmm_config = Builder::default() - .memory_config(Some("size_mib=1024")) - .kernel_config(Some("path=bzImage")) + .memory_config(Some(&String::from("size_mib=1024"))) + .kernel_config(Some(&String::from("path=bzImage"))) .build(); assert!(vmm_config.is_ok()); assert_eq!( @@ -208,8 +204,8 @@ mod tests { #[test] fn test_builder_memory_config_none_default() { let vmm_config = Builder::default() - .memory_config(None as Option<&str>) - .kernel_config(Some("path=bzImage")) + .memory_config(None as Option<&String>) + .kernel_config(Some(&String::from("path=bzImage"))) .build(); assert!(vmm_config.is_ok()); assert_eq!( @@ -221,8 +217,8 @@ mod tests { #[test] fn test_builder_vcpu_config_success() { let vmm_config = Builder::default() - .vcpu_config(Some("num=2")) - .kernel_config(Some("path=bzImage")) + .vcpu_config(Some(&String::from("num=2"))) + .kernel_config(Some(&String::from("path=bzImage"))) .build(); assert!(vmm_config.is_ok()); assert_eq!(vmm_config.unwrap().vcpu_config, VcpuConfig { num: 2 }); @@ -231,8 +227,8 @@ mod tests { #[test] fn test_builder_vcpu_config_none_default() { let vmm_config = Builder::default() - .memory_config(None as Option<&str>) - .kernel_config(Some("path=bzImage")) + .memory_config(None as Option<&String>) + .kernel_config(Some(&String::from("path=bzImage"))) .build(); assert!(vmm_config.is_ok()); assert_eq!(vmm_config.unwrap().vcpu_config, VcpuConfig { num: 1 }); @@ -241,7 +237,7 @@ mod tests { #[test] fn test_builder_kernel_config_success_default() { let vmm_config = Builder::default() - .kernel_config(Some("path=bzImage")) + .kernel_config(Some(&String::from("path=bzImage"))) .build(); assert!(vmm_config.is_ok()); assert_eq!( @@ -257,7 +253,7 @@ mod tests { #[test] fn test_builder_kernel_config_none_error() { let vmm_config = Builder::default() - .kernel_config(None as Option<&str>) + .kernel_config(None as Option<&String>) .build(); assert!(vmm_config.is_err()); @@ -266,8 +262,8 @@ mod tests { #[test] fn test_builder_net_config_none_default() { let vmm_config = Builder::default() - .net_config(None as Option<&str>) - .kernel_config(Some("path=bzImage")) + .net_config(None as Option<&String>) + .kernel_config(Some(&String::from("path=bzImage"))) .build(); assert!(vmm_config.is_ok()); assert!(vmm_config.unwrap().net_config.is_none()); @@ -276,8 +272,8 @@ mod tests { #[test] fn test_builder_net_config_success() { let vmm_config = Builder::default() - .net_config(Some("tap=tap0")) - .kernel_config(Some("path=bzImage")) + .net_config(Some(&String::from("tap=tap0"))) + .kernel_config(Some(&String::from("path=bzImage"))) .build(); assert!(vmm_config.is_ok()); assert_eq!( @@ -291,8 +287,8 @@ mod tests { #[test] fn test_builder_block_config_none_default() { let vmm_config = Builder::default() - .block_config(None as Option<&str>) - .kernel_config(Some("path=bzImage")) + .block_config(None as Option<&String>) + .kernel_config(Some(&String::from("path=bzImage"))) .build(); assert!(vmm_config.is_ok()); assert!(vmm_config.unwrap().block_config.is_none()); @@ -301,8 +297,8 @@ mod tests { #[test] fn test_builder_block_config_success() { let vmm_config = Builder::default() - .block_config(Some("path=/dev/loop0")) - .kernel_config(Some("path=bzImage")) + .block_config(Some(&String::from("path=/dev/loop0"))) + .kernel_config(Some(&String::from("path=bzImage"))) .build(); assert!(vmm_config.is_ok()); assert_eq!( @@ -316,11 +312,11 @@ mod tests { #[test] fn test_builder_vmm_config_success() { let vmm_config = Builder::default() - .memory_config(Some("size_mib=1024")) - .vcpu_config(Some("num=2")) - .net_config(Some("tap=tap0")) - .kernel_config(Some("path=bzImage")) - .block_config(Some("path=/dev/loop0")) + .memory_config(Some(&String::from("size_mib=1024"))) + .vcpu_config(Some(&String::from("num=2"))) + .net_config(Some(&String::from("tap=tap0"))) + .kernel_config(Some(&String::from("path=bzImage"))) + .block_config(Some(&String::from("path=/dev/loop0"))) .build(); assert!(vmm_config.is_ok()); assert_eq!( diff --git a/src/vmm/src/config/mod.rs b/src/vmm/src/config/mod.rs index 394df8c9..119b5b55 100644 --- a/src/vmm/src/config/mod.rs +++ b/src/vmm/src/config/mod.rs @@ -85,10 +85,10 @@ impl Default for MemoryConfig { } } -impl TryFrom<&str> for MemoryConfig { +impl TryFrom<&String> for MemoryConfig { type Error = ConversionError; - fn try_from(mem_cfg_str: &str) -> result::Result { + fn try_from(mem_cfg_str: &String) -> result::Result { // Supported options: `size=` let mut arg_parser = CfgArgParser::new(mem_cfg_str); @@ -116,10 +116,10 @@ impl Default for VcpuConfig { } } -impl TryFrom<&str> for VcpuConfig { +impl TryFrom<&String> for VcpuConfig { type Error = ConversionError; - fn try_from(vcpu_cfg_str: &str) -> result::Result { + fn try_from(vcpu_cfg_str: &String) -> result::Result { // Supported options: `num=` let mut arg_parser = CfgArgParser::new(vcpu_cfg_str); let num = arg_parser @@ -167,10 +167,10 @@ impl Default for KernelConfig { } } -impl TryFrom<&str> for KernelConfig { +impl TryFrom<&String> for KernelConfig { type Error = ConversionError; - fn try_from(kernel_cfg_str: &str) -> result::Result { + fn try_from(kernel_cfg_str: &String) -> result::Result { // Supported options: // `cmdline=<"string">,path=/path/to/kernel,kernel_load_addr=` // Required: path @@ -213,10 +213,10 @@ pub struct NetConfig { pub tap_name: String, } -impl TryFrom<&str> for NetConfig { +impl TryFrom<&String> for NetConfig { type Error = ConversionError; - fn try_from(net_config_str: &str) -> Result { + fn try_from(net_config_str: &String) -> Result { // Supported options: `tap=String` let mut arg_parser = CfgArgParser::new(net_config_str); @@ -239,10 +239,10 @@ pub struct BlockConfig { pub path: PathBuf, } -impl TryFrom<&str> for BlockConfig { +impl TryFrom<&String> for BlockConfig { type Error = ConversionError; - fn try_from(block_cfg_str: &str) -> Result { + fn try_from(block_cfg_str: &String) -> Result { // Supported options: `path=PathBuf` let mut arg_parser = CfgArgParser::new(block_cfg_str); @@ -280,8 +280,7 @@ mod tests { #[test] fn test_kernel_config() { // Check that additional commas in the kernel string do not cause a panic. - let kernel_str = r#"path=/foo/bar,cmdline="foo=bar",kernel_load_addr=42,"#; - + let kernel_str = String::from(r#"path=/foo/bar,cmdline="foo=bar",kernel_load_addr=42,"#); let mut foo_cmdline = Cmdline::new(128).unwrap(); foo_cmdline.insert_str("\"foo=bar\"").unwrap(); @@ -290,131 +289,115 @@ mod tests { load_addr: 42, path: PathBuf::from("/foo/bar"), }; - assert_eq!( - KernelConfig::try_from(kernel_str).unwrap(), - expected_kernel_config - ); + assert_eq!(&KernelConfig::try_from(&kernel_str).unwrap(), &expected_kernel_config); // Check that an empty path returns a conversion error. - let kernel_str = r#"path=,cmdline="foo=bar",kernel_load_addr=42,"#; + let kernel_str = String::from(r#"path=,cmdline="foo=bar",kernel_load_addr=42,"#); assert_eq!( - KernelConfig::try_from(kernel_str).unwrap_err(), - ConversionError::ParseKernel("Missing required argument: path".to_string()) + &KernelConfig::try_from(&kernel_str).unwrap_err(), + &ConversionError::ParseKernel("Missing required argument: path".to_string()) ); - assert!(KernelConfig::try_from("path=/something,not=valid").is_err()); - assert!(KernelConfig::try_from("path=/something,kernel_load_addr=invalid").is_err()); + assert!(KernelConfig::try_from(&String::from("path=/something,not=valid")).is_err()); + assert!(KernelConfig::try_from(&String::from("path=/something,kernel_load_addr=invalid")).is_err()); } #[test] fn test_vcpu_config() { // Invalid vCPU numbers: 0, 256 (exceeds the u8 limit). - let vcpu_str = "num=0"; + let vcpu_str = String::from("num=0"); assert_eq!( - VcpuConfig::try_from(vcpu_str).unwrap_err(), - ConversionError::ParseVcpus( - "Param \'num\', parsing failed: number would be zero for non-zero type".to_string() + &VcpuConfig::try_from(&vcpu_str).unwrap_err(), + &ConversionError::ParseVcpus( + "Param 'num', parsing failed: number would be zero for non-zero type".to_string() ) ); - let vcpu_str = "num=256"; + let vcpu_str = String::from("num=256"); assert_eq!( - VcpuConfig::try_from(vcpu_str).unwrap_err(), - ConversionError::ParseVcpus( + &VcpuConfig::try_from(&vcpu_str).unwrap_err(), + &ConversionError::ParseVcpus( "Param 'num', parsing failed: number too large to fit in target type".to_string() ) ); // Missing vCPU number in config string, use default - let vcpu_str = "num="; - assert!(VcpuConfig::try_from(vcpu_str).is_ok()); + assert!(VcpuConfig::try_from(&String::from("num=")).is_ok()); // vCPU number parsing error - let vcpu_str = "num=abc"; - assert!(VcpuConfig::try_from(vcpu_str).is_err()); + assert!(VcpuConfig::try_from(&String::from("num=abc")).is_err()); // Extra argument - let vcpu_str = "num=1,foo=bar"; - assert!(VcpuConfig::try_from(vcpu_str).is_err()); + assert!(VcpuConfig::try_from(&String::from("num=1,foo=bar")).is_err()); } #[test] fn test_net_config() { - let net_str = "tap=vmtap"; - let net_cfg = NetConfig::try_from(net_str).unwrap(); + let net_str = String::from("tap=vmtap"); + let net_cfg = NetConfig::try_from(&net_str).unwrap(); let expected_cfg = NetConfig { tap_name: "vmtap".to_string(), }; - assert_eq!(net_cfg, expected_cfg); + assert_eq!(&net_cfg, &expected_cfg); - // Test case: empty string error. - assert!(NetConfig::try_from("").is_err()); + // Test case: empty string error + assert!(NetConfig::try_from(&String::from("")).is_err()); - // Test case: empty tap name error. - let net_str = "tap="; - assert!(NetConfig::try_from(net_str).is_err()); + // Test case: empty tap name error + assert!(NetConfig::try_from(&String::from("tap=")).is_err()); // Test case: invalid string. - let net_str = "blah=blah"; - assert!(NetConfig::try_from(net_str).is_err()); + assert!(NetConfig::try_from(&String::from("blah=blah")).is_err()); // Test case: unused parameters - let net_str = "tap=something,blah=blah"; - assert!(NetConfig::try_from(net_str).is_err()); + assert!(NetConfig::try_from(&String::from("tap=something,blah=blah")).is_err()); } #[test] fn test_block_config() { - let block_str = "path=/foo/bar"; - let block_cfg = BlockConfig::try_from(block_str).unwrap(); + let block_str = String::from("path=/foo/bar"); + let block_cfg = BlockConfig::try_from(&block_str).unwrap(); let expected_cfg = BlockConfig { path: PathBuf::from("/foo/bar"), }; - assert_eq!(block_cfg, expected_cfg); + assert_eq!(&block_cfg, &expected_cfg); - // Test case: empty string error. - assert!(BlockConfig::try_from("").is_err()); + // Test case: empty string error + assert!(BlockConfig::try_from(&String::from("")).is_err()); - // Test case: empty tap name error. - let block_str = "path="; - assert!(BlockConfig::try_from(block_str).is_err()); + // Test case: empty tap name error + assert!(BlockConfig::try_from(&String::from("path=")).is_err()); // Test case: invalid string. - let block_str = "blah=blah"; - assert!(BlockConfig::try_from(block_str).is_err()); + assert!(BlockConfig::try_from(&String::from("blah=blah")).is_err()); // Test case: unused parameters - let block_str = "path=/foo/bar,blah=blah"; - assert!(BlockConfig::try_from(block_str).is_err()); + assert!(BlockConfig::try_from(&String::from("path=/foo/bar,blah=blah")).is_err()); } #[test] fn test_memory_config() { let default = MemoryConfig { size_mib: 256 }; - let size_str = "size_mib=42"; - let memory_cfg = MemoryConfig::try_from(size_str).unwrap(); + let size_str = String::from("size_mib=42"); + let memory_cfg = MemoryConfig::try_from(&size_str).unwrap(); let expected_cfg = MemoryConfig { size_mib: 42 }; - assert_eq!(memory_cfg, expected_cfg); + assert_eq!(&memory_cfg, &expected_cfg); // Test case: empty string should use default - assert_eq!(MemoryConfig::try_from("").unwrap(), default); + assert_eq!(&MemoryConfig::try_from(&String::from("" )).unwrap(), &default); // Test case: empty size_mib, use default - let memory_str = "size_mib="; - assert!(MemoryConfig::try_from(memory_str).is_ok()); + assert!(MemoryConfig::try_from(&String::from("size_mib=")).is_ok()); // Test case: size_mib invalid input - let memory_str = "size_mib=ciao"; - assert!(MemoryConfig::try_from(memory_str).is_err()); + assert!(MemoryConfig::try_from(&String::from("size_mib=ciao")).is_err()); // Test case: invalid string. - let memory_str = "blah=blah"; assert_eq!( - MemoryConfig::try_from(memory_str).unwrap_err(), - ConversionError::ParseMemory("Unknown arguments found: \'blah\'".to_string()) + &MemoryConfig::try_from(&String::from("blah=blah")).unwrap_err(), + &ConversionError::ParseMemory("Unknown arguments found: 'blah'".to_string()) ); // Test case: unused parameters - let memory_str = "size_mib=12,blah=blah"; - assert!(MemoryConfig::try_from(memory_str).is_err()); + assert!(MemoryConfig::try_from(&String::from("size_mib=12,blah=blah")).is_err()); } } diff --git a/src/vmm/src/lib.rs b/src/vmm/src/lib.rs index 85a56deb..07203d76 100644 --- a/src/vmm/src/lib.rs +++ b/src/vmm/src/lib.rs @@ -458,13 +458,28 @@ impl Vmm { // Add the kernel command line to the boot parameters. bootparams.hdr.cmd_line_ptr = CMDLINE_START as u32; - bootparams.hdr.cmdline_size = - String::try_from(&self.kernel_cfg.cmdline).unwrap().len() as u32 + 1; + bootparams.hdr.cmdline_size = String::from( + self.kernel_cfg + .cmdline + .as_cstring() + .unwrap() + .to_str() + .unwrap(), + ) + .len() as u32 + + 1; // Load the kernel command line into guest memory. let mut cmdline = Cmdline::new(4096).unwrap(); cmdline - .insert_str(String::try_from(&self.kernel_cfg.cmdline).unwrap()) + .insert_str(String::from( + self.kernel_cfg + .cmdline + .as_cstring() + .unwrap() + .to_str() + .unwrap(), + )) .map_err(Error::Cmdline)?; load_cmdline( @@ -517,7 +532,7 @@ impl Vmm { #[cfg(target_arch = "aarch64")] self.kernel_cfg .cmdline - .insert_str(&format!("earlycon=uart,mmio,0x{:08x}", AARCH64_MMIO_BASE)) + .insert_str(format!("earlycon=uart,mmio,0x{:08x}", AARCH64_MMIO_BASE)) .map_err(Error::Cmdline)?; // Put it on the bus. @@ -703,7 +718,7 @@ impl Vmm { } fn check_kvm_capabilities(kvm: &Kvm) -> Result<()> { - let capabilities = vec![Irqchip, Ioeventfd, Irqfd, UserMemory]; + let capabilities = [Irqchip, Ioeventfd, Irqfd, UserMemory]; // Check that all desired capabilities are supported. if let Some(c) = capabilities @@ -725,7 +740,9 @@ impl Vmm { let cmdline = &self.kernel_cfg.cmdline; let fdt = self .fdt_builder - .with_cmdline(String::try_from(&cmdline).unwrap()) + .with_cmdline(String::from( + cmdline.as_cstring().unwrap().to_str().unwrap(), + )) .with_num_vcpus(self.num_vcpus.try_into().unwrap()) .with_mem_size(mem_size) .create_fdt() @@ -1029,18 +1046,35 @@ mod tests { vmm_config.kernel_config.path = default_elf_path(); let mut vmm = mock_vmm(vmm_config); assert_eq!( - String::try_from(&vmm.kernel_cfg.cmdline).unwrap(), + vmm.kernel_cfg + .cmdline + .as_cstring() + .unwrap() + .to_str() + .unwrap(), DEFAULT_KERNEL_CMDLINE ); vmm.add_serial_console().unwrap(); #[cfg(target_arch = "x86_64")] - assert!(String::try_from(&vmm.kernel_cfg.cmdline) - .unwrap() - .contains("console=ttyS0")); + assert!(String::from( + vmm.kernel_cfg + .cmdline + .as_cstring() + .unwrap() + .to_str() + .unwrap() + ) + .contains("console=ttyS0")); #[cfg(target_arch = "aarch64")] - assert!(String::try_from(&vmm.kernel_cfg.cmdline) - .unwrap() - .contains("earlycon=uart,mmio")); + assert!(String::from( + vmm.kernel_cfg + .cmdline + .as_cstring() + .unwrap() + .to_str() + .unwrap(), + ) + .contains("earlycon=uart,mmio")); } #[test] @@ -1138,8 +1172,13 @@ mod tests { assert_eq!(vmm.block_devices.len(), 1); #[cfg(target_arch = "aarch64")] assert_eq!(vmm.fdt_builder.virtio_device_len(), 1); - assert!(String::try_from(&vmm.kernel_cfg.cmdline) - .unwrap() + assert!(vmm + .kernel_cfg + .cmdline + .as_cstring() + .expect("Could not open cmdline") + .to_str() + .expect("CString is not valid UTF-8") .contains("virtio")); let invalid_block_config = BlockConfig { @@ -1173,8 +1212,13 @@ mod tests { assert_eq!(vmm.net_devices.len(), 1); #[cfg(target_arch = "aarch64")] assert_eq!(vmm.fdt_builder.virtio_device_len(), 1); - assert!(String::try_from(&vmm.kernel_cfg.cmdline) - .unwrap() + assert!(vmm + .kernel_cfg + .cmdline + .as_cstring() + .expect("Could not open cmdline") + .to_str() + .expect("CString is not valid UTF-8") .contains("virtio")); } } @@ -1196,7 +1240,9 @@ mod tests { let cmdline = &vmm.kernel_cfg.cmdline; let fdt = vmm .fdt_builder - .with_cmdline(String::try_from(&cmdline).unwrap()) + .with_cmdline(String::from( + cmdline.as_cstring().unwrap().to_str().unwrap(), + )) .with_num_vcpus(vmm.num_vcpus.try_into().unwrap()) .with_mem_size(mem_size) .with_serial_console(0x40000000, 0x1000) From 75ba4d067f9006350eb2b1383f0a796c93ecaca3 Mon Sep 17 00:00:00 2001 From: Samuele Paone Date: Mon, 3 Feb 2025 17:16:44 +0100 Subject: [PATCH 07/10] fdt: Add --dump-dtb option This commit adds the --dump-dtb, which enables the user to make a dump of the dtb being used Signed-off-by: Samuele Paone --- src/api/src/lib.rs | 9 +++++++++ src/arch/src/lib.rs | 12 ++++++++++++ src/vmm/src/config/builder.rs | 21 +++++++++++++++++++-- src/vmm/src/config/mod.rs | 28 ++++++++++++++++++++++++++++ src/vmm/src/lib.rs | 10 ++++++++++ src/vmm/tests/integration_tests.rs | 1 + 6 files changed, 79 insertions(+), 2 deletions(-) diff --git a/src/api/src/lib.rs b/src/api/src/lib.rs index 715ebe94..bf80dd70 100644 --- a/src/api/src/lib.rs +++ b/src/api/src/lib.rs @@ -45,6 +45,12 @@ impl Cli { .long("block") .required(false) .help("Block device configuration. \n\tFormat: \"path=\"") + ) + .arg( + Arg::new("dump-dtb") + .long("dump-dtb") + .required(false) + .help("If set, dump the DTB to a file") ); // Save the usage beforehand as a string, because `get_matches` consumes the `App`. @@ -64,6 +70,7 @@ impl Cli { .vcpu_config(matches.get_one::("vcpu")) .net_config(matches.get_one::("net")) .block_config(matches.get_one::("block")) + .dump_dtb_config(matches.get_one::("dump-dtb")) .build() .map_err(|e| format!("{:?}", e)) } @@ -231,6 +238,7 @@ mod tests { vcpu_config: VcpuConfig { num: 1 }, block_config: None, net_config: None, + dump_dtb: None, } ); @@ -247,6 +255,7 @@ mod tests { vcpu_config: VcpuConfig { num: 1 }, block_config: None, net_config: None, + dump_dtb: None, } ); } diff --git a/src/arch/src/lib.rs b/src/arch/src/lib.rs index 5362b2e8..d3c82eb6 100644 --- a/src/arch/src/lib.rs +++ b/src/arch/src/lib.rs @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +use std::{fs::File, io::Write}; pub use vm_fdt::{Error as FdtError, FdtWriter}; use vm_memory::{guest_memory::Error as GuestMemoryError, Bytes, GuestAddress, GuestMemory}; // This is an arbitrary number to specify the node for the GIC. @@ -174,6 +175,17 @@ impl Fdt { guest_mem.write_slice(self.fdt_blob.as_slice(), fdt_address)?; Ok(()) } + + pub fn write_to_file(&self, path: &str) -> std::io::Result<()> { + let mut file = File::options() + .read(false) + .create(true) + .write(true) + .append(false) + .open(path)?; + file.write_all(&self.fdt_blob)?; + Ok(()) + } } fn create_chosen_node(fdt: &mut FdtWriter, cmdline: &str) -> Result<()> { diff --git a/src/vmm/src/config/builder.rs b/src/vmm/src/config/builder.rs index 2f8e23df..f578d8e0 100644 --- a/src/vmm/src/config/builder.rs +++ b/src/vmm/src/config/builder.rs @@ -5,7 +5,8 @@ use std::convert::TryFrom; use super::{ - BlockConfig, ConversionError, KernelConfig, MemoryConfig, NetConfig, VMMConfig, VcpuConfig, + BlockConfig, ConversionError, DumpDTBConfig, KernelConfig, MemoryConfig, NetConfig, VMMConfig, + VcpuConfig, }; /// Builder structure for VMMConfig @@ -164,6 +165,21 @@ impl Builder { } } + /// Configure Builder with dump DTB Configuration for the VMM. + pub fn dump_dtb_config(self, dump_dtb: Option) -> Self + where + DumpDTBConfig: TryFrom, + >::Error: Into, + { + match dump_dtb { + Some(dd) => self.and_then(|mut config| { + config.dump_dtb = Some(TryFrom::try_from(dd).map_err(Into::into)?); + Ok(config) + }), + None => self, + } + } + fn and_then(self, func: F) -> Self where F: FnOnce(VMMConfig) -> Result, @@ -334,7 +350,8 @@ mod tests { }), block_config: Some(BlockConfig { path: PathBuf::from("/dev/loop0") - }) + }), + dump_dtb: None, } ); } diff --git a/src/vmm/src/config/mod.rs b/src/vmm/src/config/mod.rs index 119b5b55..4ae1bb41 100644 --- a/src/vmm/src/config/mod.rs +++ b/src/vmm/src/config/mod.rs @@ -258,6 +258,32 @@ impl TryFrom<&String> for BlockConfig { } } +/// Dump DTB configuration +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DumpDTBConfig { + /// Path where to save dtb used. + pub path: PathBuf, +} + +impl TryFrom<&String> for DumpDTBConfig { + type Error = ConversionError; + + fn try_from(dump_dtb_cfg_str: &String) -> Result { + // Supported options: `path=PathBuf` + let mut arg_parser = CfgArgParser::new(dump_dtb_cfg_str); + + let path = arg_parser + .value_of("path") + .map_err(ConversionError::new_block)? + .ok_or_else(|| ConversionError::new_block("Missing required argument: path"))?; + + arg_parser + .all_consumed() + .map_err(ConversionError::new_block)?; + Ok(DumpDTBConfig { path }) + } +} + /// VMM configuration. #[derive(Clone, Debug, Default, PartialEq)] pub struct VMMConfig { @@ -271,6 +297,8 @@ pub struct VMMConfig { pub net_config: Option, /// Block device configuration. pub block_config: Option, + /// Dump DTB configuration + pub dump_dtb: Option, } #[cfg(test)] diff --git a/src/vmm/src/lib.rs b/src/vmm/src/lib.rs index 07203d76..22f19c9b 100644 --- a/src/vmm/src/lib.rs +++ b/src/vmm/src/lib.rs @@ -227,6 +227,7 @@ pub struct Vmm { num_vcpus: u64, #[cfg(target_arch = "aarch64")] fdt_builder: FdtBuilder, + config: VMMConfig, } // The `VmmExitHandler` is used as the mechanism for exiting from the event manager loop. @@ -285,6 +286,7 @@ impl TryFrom for Vmm { type Error = Error; fn try_from(config: VMMConfig) -> Result { + let config_copy = config.clone(); let kvm = Kvm::new().map_err(Error::KvmIoctl)?; // Check that the KVM on the host is supported. @@ -332,6 +334,7 @@ impl TryFrom for Vmm { num_vcpus: config.vcpu_config.num as u64, #[cfg(target_arch = "aarch64")] fdt_builder, + config: config_copy, }; vmm.add_serial_console()?; #[cfg(target_arch = "x86_64")] @@ -747,6 +750,12 @@ impl Vmm { .with_mem_size(mem_size) .create_fdt() .map_err(Error::SetupFdt)?; + + if let Some(dump_dtb_path) = &self.config.dump_dtb { + fdt.write_to_file(dump_dtb_path.path.to_str().unwrap()) + .expect("Failed to dump dtb"); + } + fdt.write_to_mem(self.guest_memory.as_ref(), fdt_offset) .map_err(Error::SetupFdt)?; Ok(()) @@ -828,6 +837,7 @@ mod tests { vcpu_config: VcpuConfig { num: NUM_VCPUS }, block_config: None, net_config: None, + dump_dtb: None, } } diff --git a/src/vmm/tests/integration_tests.rs b/src/vmm/tests/integration_tests.rs index a6bdf2db..2772ff78 100644 --- a/src/vmm/tests/integration_tests.rs +++ b/src/vmm/tests/integration_tests.rs @@ -29,6 +29,7 @@ fn run_vmm(kernel_path: PathBuf) { vcpu_config: default_vcpu_config(), block_config: None, net_config: None, + dump_dtb: None, }; let mut vmm = Vmm::try_from(vmm_config).unwrap(); From 8ff6b57ea71ccd7b5b411262f72fea243e83c651 Mon Sep 17 00:00:00 2001 From: Samuele Paone Date: Mon, 3 Feb 2025 17:17:53 +0100 Subject: [PATCH 08/10] fdt: Add --dtb option This commit adds the --dtb option which enables the user to upload his own dtb Signed-off-by: Samuele Paone --- src/api/src/lib.rs | 9 ++++++ src/arch/src/lib.rs | 48 ++++++++++++++++++++++++++++-- src/vmm/src/config/builder.rs | 20 +++++++++++-- src/vmm/src/config/mod.rs | 28 +++++++++++++++++ src/vmm/src/lib.rs | 42 +++++++++++++++++--------- src/vmm/tests/integration_tests.rs | 1 + 6 files changed, 130 insertions(+), 18 deletions(-) diff --git a/src/api/src/lib.rs b/src/api/src/lib.rs index bf80dd70..b73a366a 100644 --- a/src/api/src/lib.rs +++ b/src/api/src/lib.rs @@ -51,6 +51,12 @@ impl Cli { .long("dump-dtb") .required(false) .help("If set, dump the DTB to a file") + ) + .arg( + Arg::new("dtb") + .long("dtb") + .required(false) + .help("If set, takes as DTB the one at the specified path") ); // Save the usage beforehand as a string, because `get_matches` consumes the `App`. @@ -71,6 +77,7 @@ impl Cli { .net_config(matches.get_one::("net")) .block_config(matches.get_one::("block")) .dump_dtb_config(matches.get_one::("dump-dtb")) + .dtb_config(matches.get_one::("dtb")) .build() .map_err(|e| format!("{:?}", e)) } @@ -239,6 +246,7 @@ mod tests { block_config: None, net_config: None, dump_dtb: None, + dtb: None, } ); @@ -256,6 +264,7 @@ mod tests { block_config: None, net_config: None, dump_dtb: None, + dtb: None, } ); } diff --git a/src/arch/src/lib.rs b/src/arch/src/lib.rs index d3c82eb6..70476ed8 100644 --- a/src/arch/src/lib.rs +++ b/src/arch/src/lib.rs @@ -2,7 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use std::{fs::File, io::Write}; +use log::{debug, trace}; +use std::path::PathBuf; +use std::{ + fs::File, + io::{self, Read, Write}, +}; pub use vm_fdt::{Error as FdtError, FdtWriter}; use vm_memory::{guest_memory::Error as GuestMemoryError, Bytes, GuestAddress, GuestMemory}; // This is an arbitrary number to specify the node for the GIC. @@ -47,6 +52,7 @@ pub enum Error { Fdt(FdtError), Memory(GuestMemoryError), MissingRequiredConfig(String), + FdtCustomDtb(io::Error), } impl From for Error { @@ -61,6 +67,12 @@ impl From for Error { } } +impl From for Error { + fn from(inner: io::Error) -> Self { + Error::FdtCustomDtb(inner) + } +} + pub type Result = std::result::Result; /// It contains info about the virtio device for fdt. @@ -78,6 +90,7 @@ pub struct FdtBuilder { serial_console: Option<(u64, u64)>, rtc: Option<(u64, u64)>, virtio_devices: Vec, + prebuilt: Option, } impl FdtBuilder { @@ -85,6 +98,29 @@ impl FdtBuilder { FdtBuilder::default() } + pub fn with_prebuilt_fdt(&mut self, prebuilt_fdt_path: PathBuf) -> Result<&mut Self> { + let mut fdt_file = File::options() + .read(true) + .create(false) + .write(false) + .append(false) + .open(prebuilt_fdt_path) + .map_err(|e| Error::FdtCustomDtb(e))?; + + let mut prebuilt_fdt = Fdt::default(); + fdt_file.read_to_end(&mut prebuilt_fdt.fdt_blob).map_err(|e| Error::FdtCustomDtb(e))?; + self.prebuilt = Some(prebuilt_fdt); + Ok(self) + } + + pub fn has_prebuilt_fdt(&self) -> bool { + self.prebuilt.is_some() + } + + pub fn get_prebuilt_fdt(&self) -> Option { + self.prebuilt.clone() + } + pub fn with_cmdline(&mut self, cmdline: String) -> &mut Self { self.cmdline = Some(cmdline); self @@ -119,7 +155,14 @@ impl FdtBuilder { self.virtio_devices.len() } - pub fn create_fdt(&self) -> Result { + pub fn create_fdt(&mut self) -> Result { + if let Some(fdt) = &mut self.prebuilt { + let ret: Fdt = fdt.clone(); + fdt.fdt_blob.clear(); + debug!("FdtBuilder: Returning prebuilt FDT"); + return Ok(ret); + } + let mut fdt = FdtWriter::new()?; // The whole thing is put into one giant node with s @@ -165,6 +208,7 @@ impl FdtBuilder { } } +#[derive(Default, Clone)] pub struct Fdt { fdt_blob: Vec, } diff --git a/src/vmm/src/config/builder.rs b/src/vmm/src/config/builder.rs index f578d8e0..18a2311e 100644 --- a/src/vmm/src/config/builder.rs +++ b/src/vmm/src/config/builder.rs @@ -5,8 +5,8 @@ use std::convert::TryFrom; use super::{ - BlockConfig, ConversionError, DumpDTBConfig, KernelConfig, MemoryConfig, NetConfig, VMMConfig, - VcpuConfig, + BlockConfig, ConversionError, DTBConfig, DumpDTBConfig, KernelConfig, MemoryConfig, NetConfig, + VMMConfig, VcpuConfig, }; /// Builder structure for VMMConfig @@ -180,6 +180,21 @@ impl Builder { } } + /// Configure Builder with DTB Configuration for the VMM. + pub fn dtb_config(self, dtb: Option) -> Self + where + DTBConfig: TryFrom, + >::Error: Into, + { + match dtb { + Some(d) => self.and_then(|mut config| { + config.dtb = Some(TryFrom::try_from(d).map_err(Into::into)?); + Ok(config) + }), + None => self, + } + } + fn and_then(self, func: F) -> Self where F: FnOnce(VMMConfig) -> Result, @@ -352,6 +367,7 @@ mod tests { path: PathBuf::from("/dev/loop0") }), dump_dtb: None, + dtb: None, } ); } diff --git a/src/vmm/src/config/mod.rs b/src/vmm/src/config/mod.rs index 4ae1bb41..c4248f95 100644 --- a/src/vmm/src/config/mod.rs +++ b/src/vmm/src/config/mod.rs @@ -284,6 +284,32 @@ impl TryFrom<&String> for DumpDTBConfig { } } +/// DTB configuration +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DTBConfig { + /// Path to the FDT. + pub path: PathBuf, +} + +impl TryFrom<&String> for DTBConfig { + type Error = ConversionError; + + fn try_from(dtb_cfg_str: &String) -> Result { + // Supported options: `path=PathBuf` + let mut arg_parser = CfgArgParser::new(dtb_cfg_str); + + let path = arg_parser + .value_of("path") + .map_err(ConversionError::new_block)? + .ok_or_else(|| ConversionError::new_block("Missing required argument: path"))?; + + arg_parser + .all_consumed() + .map_err(ConversionError::new_block)?; + Ok(DTBConfig { path }) + } +} + /// VMM configuration. #[derive(Clone, Debug, Default, PartialEq)] pub struct VMMConfig { @@ -299,6 +325,8 @@ pub struct VMMConfig { pub block_config: Option, /// Dump DTB configuration pub dump_dtb: Option, + /// DTB configuration + pub dtb: Option, } #[cfg(test)] diff --git a/src/vmm/src/lib.rs b/src/vmm/src/lib.rs index 22f19c9b..653d5788 100644 --- a/src/vmm/src/lib.rs +++ b/src/vmm/src/lib.rs @@ -317,6 +317,13 @@ impl TryFrom for Vmm { #[cfg(target_arch = "aarch64")] let fdt_builder = FdtBuilder::new(); + #[cfg(target_arch = "aarch64")] + if let Some(prebuilt_dtb) = &config.dtb { + fdt_builder + .with_prebuilt_fdt(prebuilt_dtb.path.clone()) + .map_err(Error::SetupFdt)?; + } + let irq_allocator = IrqAllocator::new(SERIAL_IRQ, vm.max_irq())?; let mut vmm = Vmm { @@ -740,22 +747,28 @@ impl Vmm { fn setup_fdt(&mut self) -> Result<()> { let mem_size: u64 = self.guest_memory.iter().map(|region| region.len()).sum(); let fdt_offset = mem_size - AARCH64_FDT_MAX_SIZE - 0x10000; - let cmdline = &self.kernel_cfg.cmdline; - let fdt = self - .fdt_builder - .with_cmdline(String::from( - cmdline.as_cstring().unwrap().to_str().unwrap(), - )) - .with_num_vcpus(self.num_vcpus.try_into().unwrap()) - .with_mem_size(mem_size) - .create_fdt() - .map_err(Error::SetupFdt)?; - if let Some(dump_dtb_path) = &self.config.dump_dtb { - fdt.write_to_file(dump_dtb_path.path.to_str().unwrap()) - .expect("Failed to dump dtb"); - } + let fdt = if !self.fdt_builder.has_prebuilt_fdt() { + let cmdline = &self.kernel_cfg.cmdline; + #[cfg(target_arch = "aarch64")] + let fdt = self + .fdt_builder + .with_cmdline(String::from( + cmdline.as_cstring().unwrap().to_str().unwrap(), + )) + .with_num_vcpus(self.num_vcpus.try_into().unwrap()) + .with_mem_size(mem_size) + .create_fdt() + .map_err(Error::SetupFdt)?; + if let Some(dump_dtb_path) = &self.config.dump_dtb { + fdt.write_to_file(dump_dtb_path.path.to_str().unwrap()) + .expect("Failed to dump dtb"); + } + fdt + } else { + self.fdt_builder.get_prebuilt_fdt().unwrap() + }; fdt.write_to_mem(self.guest_memory.as_ref(), fdt_offset) .map_err(Error::SetupFdt)?; Ok(()) @@ -838,6 +851,7 @@ mod tests { block_config: None, net_config: None, dump_dtb: None, + dtb: None, } } diff --git a/src/vmm/tests/integration_tests.rs b/src/vmm/tests/integration_tests.rs index 2772ff78..8aeacfad 100644 --- a/src/vmm/tests/integration_tests.rs +++ b/src/vmm/tests/integration_tests.rs @@ -30,6 +30,7 @@ fn run_vmm(kernel_path: PathBuf) { block_config: None, net_config: None, dump_dtb: None, + dtb: None, }; let mut vmm = Vmm::try_from(vmm_config).unwrap(); From faa6d1ace08129253218d2ef28e7259dcef7b4fd Mon Sep 17 00:00:00 2001 From: Samuele Paone Date: Mon, 3 Feb 2025 17:19:31 +0100 Subject: [PATCH 09/10] riscv: fdt: dts autogeneration This commit implments the device tree autogeneration for the RISC-V architecture Signed-off-by: Samuele Paone --- src/arch/src/lib.rs | 1154 ++++++++++++++++++++++++++++--------------- 1 file changed, 763 insertions(+), 391 deletions(-) diff --git a/src/arch/src/lib.rs b/src/arch/src/lib.rs index 70476ed8..198352a1 100644 --- a/src/arch/src/lib.rs +++ b/src/arch/src/lib.rs @@ -2,457 +2,829 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use log::{debug, trace}; -use std::path::PathBuf; -use std::{ - fs::File, - io::{self, Read, Write}, -}; -pub use vm_fdt::{Error as FdtError, FdtWriter}; -use vm_memory::{guest_memory::Error as GuestMemoryError, Bytes, GuestAddress, GuestMemory}; // This is an arbitrary number to specify the node for the GIC. // If we had a more complex interrupt architecture, then we'd need an enum for // these. -const PHANDLE_GIC: u32 = 1; - -pub const AARCH64_FDT_MAX_SIZE: u64 = 0x200000; - -// This indicates the start of DRAM inside the physical address space. -pub const AARCH64_PHYS_MEM_START: u64 = 0x80000000; - -// This is the base address of MMIO devices. -pub const AARCH64_MMIO_BASE: u64 = 1 << 30; - -const AARCH64_AXI_BASE: u64 = 0x40000000; - -// These constants indicate the address space used by the ARM vGIC. -const AARCH64_GIC_DIST_SIZE: u64 = 0x10000; -const AARCH64_GIC_CPUI_SIZE: u64 = 0x20000; - -// These constants indicate the placement of the GIC registers in the physical -// address space. -pub const AARCH64_GIC_DIST_BASE: u64 = AARCH64_AXI_BASE - AARCH64_GIC_DIST_SIZE; -pub const AARCH64_GIC_CPUI_BASE: u64 = AARCH64_GIC_DIST_BASE - AARCH64_GIC_CPUI_SIZE; -pub const AARCH64_GIC_REDIST_SIZE: u64 = 0x20000; - -// These are specified by the Linux GIC bindings -const GIC_FDT_IRQ_NUM_CELLS: u32 = 3; -const GIC_FDT_IRQ_TYPE_SPI: u32 = 0; -const GIC_FDT_IRQ_TYPE_PPI: u32 = 1; -const GIC_FDT_IRQ_PPI_CPU_SHIFT: u32 = 8; -const GIC_FDT_IRQ_PPI_CPU_MASK: u32 = 0xff << GIC_FDT_IRQ_PPI_CPU_SHIFT; -const IRQ_TYPE_EDGE_RISING: u32 = 0x00000001; -const IRQ_TYPE_LEVEL_HIGH: u32 = 0x00000004; -const IRQ_TYPE_LEVEL_LOW: u32 = 0x00000008; -// PMU PPI interrupt, same as qemu -const AARCH64_PMU_IRQ: u32 = 7; - -#[derive(Debug)] -pub enum Error { - Fdt(FdtError), - Memory(GuestMemoryError), - MissingRequiredConfig(String), - FdtCustomDtb(io::Error), -} +#[cfg(target_arch = "aarch64")] +pub mod aarch64_consts { + // These constants indicate the placement of the GIC registers in the physical + // address space. + pub const AARCH64_GIC_DIST_BASE: u64 = AARCH64_AXI_BASE - AARCH64_GIC_DIST_SIZE; -impl From for Error { - fn from(inner: FdtError) -> Self { - Error::Fdt(inner) - } -} + pub const AARCH64_GIC_CPUI_BASE: u64 = AARCH64_GIC_DIST_BASE - AARCH64_GIC_CPUI_SIZE; -impl From for Error { - fn from(inner: GuestMemoryError) -> Self { - Error::Memory(inner) - } -} + pub const AARCH64_GIC_REDIST_SIZE: u64 = 0x20000; -impl From for Error { - fn from(inner: io::Error) -> Self { - Error::FdtCustomDtb(inner) - } -} + pub const AARCH64_FDT_MAX_SIZE: u64 = 0x200000; + + // This indicates the start of DRAM inside the physical address space. + pub const AARCH64_PHYS_MEM_START: u64 = 0x80000000; + + // This is the base address of MMIO devices. + pub const AARCH64_MMIO_BASE: u64 = 1 << 30; + + pub const AARCH64_AXI_BASE: u64 = 0x40000000; + + // These constants indicate the address space used by the ARM vGIC. + pub const AARCH64_GIC_DIST_SIZE: u64 = 0x10000; + + pub const AARCH64_GIC_CPUI_SIZE: u64 = 0x20000; + + // These are specified by the Linux GIC bindings + pub const GIC_FDT_IRQ_NUM_CELLS: u32 = 3; + + pub const GIC_FDT_IRQ_TYPE_SPI: u32 = 0; + + pub const GIC_FDT_IRQ_TYPE_PPI: u32 = 1; -pub type Result = std::result::Result; + pub const GIC_FDT_IRQ_PPI_CPU_SHIFT: u32 = 8; -/// It contains info about the virtio device for fdt. -struct DeviceInfo { - addr: u64, - size: u64, - irq: u32, + pub const GIC_FDT_IRQ_PPI_CPU_MASK: u32 = 0xff << GIC_FDT_IRQ_PPI_CPU_SHIFT; + + // PMU PPI interrupt, same as qemu + pub const AARCH64_PMU_IRQ: u32 = 7; + + pub const IRQ_TYPE_EDGE_RISING: u32 = 0x00000001; + + pub const IRQ_TYPE_LEVEL_HIGH: u32 = 0x00000004; + + pub const IRQ_TYPE_LEVEL_LOW: u32 = 0x00000008; + + pub const PHANDLE_GIC: u32 = 1; } -#[derive(Default)] -pub struct FdtBuilder { - cmdline: Option, - mem_size: Option, - num_vcpus: Option, - serial_console: Option<(u64, u64)>, - rtc: Option<(u64, u64)>, - virtio_devices: Vec, - prebuilt: Option, +// RISCV +#[cfg(target_arch = "riscv64")] +pub mod riscv64_consts { + pub const RISCV_PLIC: u64 = 0x0c00_0000; + + pub const RISCV_PLIC_SIZE: u64 = 0x600_000; + + pub const RISCV_IMSIC: u64 = 0x28000000; + + pub const RISCV_IMSIC_SIZE: u64 = 0x1000; + + pub const PHANDLE_CPU_INTC_BASE: u32 = 1; + + pub const PHANDLE_APLIC: u32 = 0xd; + + pub const PHANDLE_IMSIC: u32 = 0x9; + + pub const MAX_DEVICES: u32 = 1024; + + // This indicates the start of DRAM inside the physical address space. + pub const RISCV64_PHYS_MEM_START: u64 = 0x80000000; + + pub const RISCV64_FDT_MAX_SIZE: u64 = 0x200000; + + pub const RISCV64_MMIO_BASE: u64 = 1 << 30; + + pub const FDT_APLIC_INT_CELLS: u32 = 2; + + pub const FDT_APLIC_ADDR_CELLS: u32 = 0; + + pub const VIRT_IRQCHIP_NUM_SOURCES: u32 = 96; + + pub const IRQ_S_EXT: u32 = 0x9; + + pub const VIRT_IRQCHIP_NUM_MSIS: u32 = 255; + + pub const FDT_IMSIC_INT_CELLS: u32 = 0; + + pub const IRQ_TYPE_EDGE_RISING: u32 = 0x00000001; + + pub const IRQ_TYPE_LEVEL_HIGH: u32 = 0x00000004; + + pub const IRQ_TYPE_LEVEL_LOW: u32 = 0x00000008; } -impl FdtBuilder { - pub fn new() -> Self { - FdtBuilder::default() +#[cfg(target_arch = "riscv64")] +mod riscv_regs; + +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] +pub mod fdt { + #[cfg(target_arch = "riscv64")] + use kvm_bindings::*; + #[cfg(target_arch = "riscv64")] + use kvm_ioctls::VcpuFd; + use log::debug; + #[cfg(target_arch = "riscv64")] + use std::mem::offset_of; + use std::path::PathBuf; + use std::{ + fs::File, + io::{self, Read, Write}, + }; + pub use vm_fdt::{Error as FdtError, FdtWriter}; + use vm_memory::{guest_memory::Error as GuestMemoryError, Bytes, GuestAddress, GuestMemory}; + + #[cfg(target_arch = "aarch64")] + use crate::aarch64_consts::*; + + #[cfg(target_arch = "riscv64")] + use crate::riscv64_consts::*; + #[cfg(target_arch = "riscv64")] + use crate::*; + #[cfg(target_arch = "riscv64")] + const ISA_INFO_ARR: [(&str, u32); 9] = [ + /* sorted alphabetically */ + ("ssaia", KVM_RISCV_ISA_EXT_ID_KVM_RISCV_ISA_EXT_SSAIA), + ("sstc", KVM_RISCV_ISA_EXT_ID_KVM_RISCV_ISA_EXT_SSTC), + ("svinval", KVM_RISCV_ISA_EXT_ID_KVM_RISCV_ISA_EXT_SVINVAL), + ("svnapot", KVM_RISCV_ISA_EXT_ID_KVM_RISCV_ISA_EXT_SVNAPOT), + ("svpbmt", KVM_RISCV_ISA_EXT_ID_KVM_RISCV_ISA_EXT_SVPBMT), + ("zbb", KVM_RISCV_ISA_EXT_ID_KVM_RISCV_ISA_EXT_ZBB), + ("zicbom", KVM_RISCV_ISA_EXT_ID_KVM_RISCV_ISA_EXT_ZICBOM), + ("zicboz", KVM_RISCV_ISA_EXT_ID_KVM_RISCV_ISA_EXT_ZICBOZ), + ( + "zihintpause", + KVM_RISCV_ISA_EXT_ID_KVM_RISCV_ISA_EXT_ZIHINTPAUSE, + ), + ]; + #[derive(Debug)] + pub enum Error { + Fdt(FdtError), + Memory(GuestMemoryError), + MissingRequiredConfig(String), + FdtCustomDtb(io::Error), } - pub fn with_prebuilt_fdt(&mut self, prebuilt_fdt_path: PathBuf) -> Result<&mut Self> { - let mut fdt_file = File::options() - .read(true) - .create(false) - .write(false) - .append(false) - .open(prebuilt_fdt_path) - .map_err(|e| Error::FdtCustomDtb(e))?; - - let mut prebuilt_fdt = Fdt::default(); - fdt_file.read_to_end(&mut prebuilt_fdt.fdt_blob).map_err(|e| Error::FdtCustomDtb(e))?; - self.prebuilt = Some(prebuilt_fdt); - Ok(self) + impl From for Error { + fn from(inner: FdtError) -> Self { + Error::Fdt(inner) + } } - pub fn has_prebuilt_fdt(&self) -> bool { - self.prebuilt.is_some() + impl From for Error { + fn from(inner: GuestMemoryError) -> Self { + Error::Memory(inner) + } } - pub fn get_prebuilt_fdt(&self) -> Option { - self.prebuilt.clone() + impl From for Error { + fn from(inner: io::Error) -> Self { + Error::FdtCustomDtb(inner) + } } - pub fn with_cmdline(&mut self, cmdline: String) -> &mut Self { - self.cmdline = Some(cmdline); - self - } + pub type Result = std::result::Result; - pub fn with_mem_size(&mut self, mem_size: u64) -> &mut Self { - self.mem_size = Some(mem_size); - self + /// It contains info about the virtio device for fdt. + struct DeviceInfo { + addr: u64, + size: u64, + irq: u32, } - pub fn with_num_vcpus(&mut self, num_vcpus: u32) -> &mut Self { - self.num_vcpus = Some(num_vcpus); - self + #[derive(Default)] + pub struct FdtBuilder { + cmdline: Option, + mem_size: Option, + num_vcpus: Option, + serial_console: Option<(u64, u64)>, + rtc: Option<(u64, u64)>, + virtio_devices: Vec, + prebuilt: Option, } - pub fn with_serial_console(&mut self, addr: u64, size: u64) -> &mut Self { - self.serial_console = Some((addr, size)); - self - } + impl FdtBuilder { + pub fn new() -> Self { + FdtBuilder::default() + } - pub fn with_rtc(&mut self, addr: u64, size: u64) -> &mut Self { - self.rtc = Some((addr, size)); - self - } + pub fn with_prebuilt_fdt(&mut self, prebuilt_fdt_path: PathBuf) -> Result<&mut Self> { + let mut fdt_file = File::options() + .read(true) + .create(false) + .write(false) + .append(false) + .open(prebuilt_fdt_path) + .map_err(Error::FdtCustomDtb)?; + + let mut prebuilt_fdt = Fdt::default(); + fdt_file + .read_to_end(&mut prebuilt_fdt.fdt_blob) + .map_err(Error::FdtCustomDtb)?; + self.prebuilt = Some(prebuilt_fdt); + Ok(self) + } + + pub fn has_prebuilt_fdt(&self) -> bool { + self.prebuilt.is_some() + } + + pub fn get_prebuilt_fdt(&self) -> Option { + self.prebuilt.clone() + } + + pub fn with_cmdline(&mut self, cmdline: String) -> &mut Self { + self.cmdline = Some(cmdline); + self + } + + pub fn with_mem_size(&mut self, mem_size: u64) -> &mut Self { + self.mem_size = Some(mem_size); + self + } + + pub fn with_num_vcpus(&mut self, num_vcpus: u32) -> &mut Self { + self.num_vcpus = Some(num_vcpus); + self + } + + pub fn with_serial_console(&mut self, addr: u64, size: u64) -> &mut Self { + self.serial_console = Some((addr, size)); + self + } - pub fn add_virtio_device(&mut self, addr: u64, size: u64, irq: u32) -> &mut Self { - self.virtio_devices.push(DeviceInfo { addr, size, irq }); - self + pub fn with_rtc(&mut self, addr: u64, size: u64) -> &mut Self { + self.rtc = Some((addr, size)); + self + } + + pub fn add_virtio_device(&mut self, addr: u64, size: u64, irq: u32) -> &mut Self { + self.virtio_devices.push(DeviceInfo { addr, size, irq }); + self + } + + pub fn virtio_device_len(&self) -> usize { + self.virtio_devices.len() + } + + #[allow(clippy::needless_borrow)] + pub fn create_fdt( + &mut self, + #[cfg(target_arch = "riscv64")] vcpufd: &VcpuFd, + ) -> Result { + if let Some(fdt) = &mut self.prebuilt { + let ret: Fdt = fdt.clone(); + fdt.fdt_blob.clear(); + debug!("FdtBuilder: Returning prebuilt FDT"); + return Ok(ret); + } + + let mut fdt = FdtWriter::new()?; + + // The whole thing is put into one giant node with s + // ome top level properties + let root_node = fdt.begin_node("")?; + #[cfg(target_arch = "aarch64")] + fdt.property_u32("interrupt-parent", PHANDLE_GIC)?; + fdt.property_u32("#address-cells", 0x2)?; + fdt.property_u32("#size-cells", 0x2)?; + fdt.property_string("compatible", "riscv-virtio")?; + fdt.property_string("model", "linux,dummy-virt")?; + + let cmdline = self + .cmdline + .as_ref() + .ok_or_else(|| Error::MissingRequiredConfig("cmdline".to_owned()))?; + create_chosen_node(&mut fdt, cmdline)?; + + let mem_size = self + .mem_size + .ok_or_else(|| Error::MissingRequiredConfig("memory".to_owned()))?; + create_memory_node(&mut fdt, mem_size)?; + + let num_vcpus = self + .num_vcpus + .ok_or_else(|| Error::MissingRequiredConfig("vcpu".to_owned()))?; + + #[cfg(target_arch = "aarch64")] + { + create_cpu_nodes(&mut fdt, num_vcpus)?; + create_gic_node(&mut fdt, true, num_vcpus as u64)?; + } + #[cfg(target_arch = "riscv64")] + { + create_cpu_nodes(&mut fdt, num_vcpus, &vcpufd)?; + create_aplic_node(&mut fdt)?; + create_imsic_node(&mut fdt, num_vcpus)?; + } + + if let Some(serial_console) = self.serial_console { + create_serial_node(&mut fdt, serial_console.0, serial_console.1)?; + } + if let Some(rtc) = self.rtc { + create_rtc_node(&mut fdt, rtc.0, rtc.1)?; + } + + #[cfg(target_arch = "aarch64")] + { + create_timer_node(&mut fdt, num_vcpus)?; + create_psci_node(&mut fdt)?; + create_pmu_node(&mut fdt, num_vcpus)?; + } + + for info in &self.virtio_devices { + create_virtio_node(&mut fdt, info.addr, info.size, info.irq)?; + } + + fdt.end_node(root_node)?; + + Ok(Fdt { + fdt_blob: fdt.finish()?, + }) + } } - pub fn virtio_device_len(&self) -> usize { - self.virtio_devices.len() + #[derive(Default, Clone)] + pub struct Fdt { + fdt_blob: Vec, } - pub fn create_fdt(&mut self) -> Result { - if let Some(fdt) = &mut self.prebuilt { - let ret: Fdt = fdt.clone(); - fdt.fdt_blob.clear(); - debug!("FdtBuilder: Returning prebuilt FDT"); - return Ok(ret); - } - - let mut fdt = FdtWriter::new()?; - - // The whole thing is put into one giant node with s - // ome top level properties - let root_node = fdt.begin_node("")?; - fdt.property_u32("interrupt-parent", PHANDLE_GIC)?; - fdt.property_string("compatible", "linux,dummy-virt")?; - fdt.property_u32("#address-cells", 0x2)?; - fdt.property_u32("#size-cells", 0x2)?; - - let cmdline = self - .cmdline - .as_ref() - .ok_or_else(|| Error::MissingRequiredConfig("cmdline".to_owned()))?; - create_chosen_node(&mut fdt, cmdline)?; - - let mem_size = self - .mem_size - .ok_or_else(|| Error::MissingRequiredConfig("memory".to_owned()))?; - create_memory_node(&mut fdt, mem_size)?; - let num_vcpus = self - .num_vcpus - .ok_or_else(|| Error::MissingRequiredConfig("vcpu".to_owned()))?; - create_cpu_nodes(&mut fdt, num_vcpus)?; - create_gic_node(&mut fdt, true, num_vcpus as u64)?; - if let Some(serial_console) = self.serial_console { - create_serial_node(&mut fdt, serial_console.0, serial_console.1)?; - } - if let Some(rtc) = self.rtc { - create_rtc_node(&mut fdt, rtc.0, rtc.1)?; - } - create_timer_node(&mut fdt, num_vcpus)?; - create_psci_node(&mut fdt)?; - create_pmu_node(&mut fdt, num_vcpus)?; - for info in &self.virtio_devices { - create_virtio_node(&mut fdt, info.addr, info.size, info.irq)?; - } - fdt.end_node(root_node)?; - - Ok(Fdt { - fdt_blob: fdt.finish()?, - }) + impl Fdt { + pub fn write_to_mem( + &self, + guest_mem: &T, + fdt_load_offset: u64, + ) -> Result<()> { + #[cfg(target_arch = "aarch64")] + let fdt_address = GuestAddress(AARCH64_PHYS_MEM_START + fdt_load_offset); + #[cfg(target_arch = "riscv64")] + let fdt_address = GuestAddress(RISCV64_PHYS_MEM_START + fdt_load_offset); + guest_mem.write_slice(self.fdt_blob.as_slice(), fdt_address)?; + Ok(()) + } + + pub fn write_to_file(&self, path: &str) -> std::io::Result<()> { + let mut file = File::options() + .read(false) + .create(true) + .truncate(true) + .write(true) + .append(false) + .open(path)?; + file.write_all(&self.fdt_blob)?; + Ok(()) + } } -} -#[derive(Default, Clone)] -pub struct Fdt { - fdt_blob: Vec, -} + fn create_chosen_node(fdt: &mut FdtWriter, cmdline: &str) -> Result<()> { + let chosen_node = fdt.begin_node("chosen")?; + fdt.property_string("bootargs", cmdline)?; + fdt.end_node(chosen_node)?; -impl Fdt { - pub fn write_to_mem(&self, guest_mem: &T, fdt_load_offset: u64) -> Result<()> { - let fdt_address = GuestAddress(AARCH64_PHYS_MEM_START + fdt_load_offset); - guest_mem.write_slice(self.fdt_blob.as_slice(), fdt_address)?; Ok(()) } - pub fn write_to_file(&self, path: &str) -> std::io::Result<()> { - let mut file = File::options() - .read(false) - .create(true) - .write(true) - .append(false) - .open(path)?; - file.write_all(&self.fdt_blob)?; + fn create_memory_node(fdt: &mut FdtWriter, mem_size: u64) -> Result<()> { + #[cfg(target_arch = "aarch64")] + { + let mem_reg_prop = [AARCH64_PHYS_MEM_START, mem_size]; + let memory_str = format!("memory@{:x}", AARCH64_PHYS_MEM_START); + let memory_node = fdt.begin_node(&memory_str)?; + fdt.property_string("device_type", "memory")?; + fdt.property_array_u64("reg", &mem_reg_prop)?; + fdt.end_node(memory_node)?; + } + #[cfg(target_arch = "riscv64")] + { + let mem_reg_prop = [RISCV64_PHYS_MEM_START, mem_size]; + let memory_str = format!("memory@{:x}", RISCV64_PHYS_MEM_START); + let memory_node = fdt.begin_node(&memory_str)?; + fdt.property_string("device_type", "memory")?; + fdt.property_array_u64("reg", &mem_reg_prop)?; + fdt.end_node(memory_node)?; + } Ok(()) } -} -fn create_chosen_node(fdt: &mut FdtWriter, cmdline: &str) -> Result<()> { - let chosen_node = fdt.begin_node("chosen")?; - fdt.property_string("bootargs", cmdline)?; - fdt.end_node(chosen_node)?; + #[cfg(target_arch = "aarch64")] + fn create_cpu_nodes(fdt: &mut FdtWriter, num_cpus: u32) -> Result<()> { + let cpus_node = fdt.begin_node("cpus")?; + fdt.property_u32("#address-cells", 0x1)?; + fdt.property_u32("#size-cells", 0x0)?; + + for cpu_id in 0..num_cpus { + let cpu_name = format!("cpu@{:x}", cpu_id); + let cpu_node = fdt.begin_node(&cpu_name)?; + fdt.property_string("device_type", "cpu")?; + fdt.property_u32("reg", cpu_id)?; + fdt.property_string("compatible", "arm,arm-v8")?; + fdt.property_string("enable-method", "psci")?; + fdt.end_node(cpu_node)?; + } + fdt.end_node(cpus_node)?; + Ok(()) + } - Ok(()) -} + #[cfg(target_arch = "riscv64")] + fn create_cpu_nodes(fdt: &mut FdtWriter, num_cpus: u32, vcpu_fd: &VcpuFd) -> Result<()> { + let isa_id: u64 = riscv_config_reg!(isa); + let mut data = [0_u8; 8]; + vcpu_fd.get_one_reg(isa_id, &mut data).expect("Error"); + let isa = u64::from_le_bytes(data); + let valid_isa_order = "IEMAFDQCLBJTPVNSUHKORWXYZG"; + let arr_sz = ISA_INFO_ARR.len(); + let cpus_node = fdt.begin_node("cpus")?; + fdt.property_u32("#address-cells", 0x1)?; + fdt.property_u32("#size-cells", 0x0)?; + let timer_id: u64 = riscv_timer_reg!(frequency); + let mut data = [0_u8; 8]; + vcpu_fd.get_one_reg(timer_id, &mut data).expect("Error"); + let frequency = u64::from_le_bytes(data); + fdt.property_u32("timebase-frequency", frequency as u32)?; + + for cpu_id in 0..num_cpus { + let mut cpu_isa = String::from("rv64"); + let mut cbom_blksz = 0; + let mut cboz_blksz = 0; + for c in valid_isa_order.chars() { + let index = c as u32 - 'A' as u32; + if (isa & (1 << index)) != 0 { + let ret = 'a' as u32 + index; + cpu_isa += &std::char::from_u32(ret).unwrap().to_string(); + } + } + + for info in ISA_INFO_ARR.iter().take(arr_sz) { + let reg_id = riscv_isa_ext_reg!(info.1 as u64); + let mut data = [0_u8; 8]; + if vcpu_fd.get_one_reg(reg_id, &mut data).is_err() { + continue; + } + let isa_ext_out = u64::from_le_bytes(data); + if isa_ext_out == 0 { + continue; + } + + if info.1 == KVM_RISCV_ISA_EXT_ID_KVM_RISCV_ISA_EXT_ZICBOM && cbom_blksz == 0 { + vcpu_fd.get_one_reg(reg_id, &mut data).expect("Error"); + cbom_blksz = u64::from_le_bytes(data); + } + + if info.1 == KVM_RISCV_ISA_EXT_ID_KVM_RISCV_ISA_EXT_ZICBOZ && cboz_blksz == 0 { + vcpu_fd.get_one_reg(reg_id, &mut data).expect("Error"); + cboz_blksz = u64::from_le_bytes(data); + } + cpu_isa += "_"; + cpu_isa += info.0; + } + + let reg_id = riscv_config_reg!(satp_mode); + let mut data = [0_u8; 8]; + vcpu_fd.get_one_reg(reg_id, &mut data).expect("Error"); + let satp_mode = u64::from_le_bytes(data); + + let cpu_name = format!("cpu@{:x}", cpu_id); + let cpu_node = fdt.begin_node(&cpu_name)?; + fdt.property_string("device_type", "cpu")?; + fdt.property_string("compatible", "riscv")?; + match satp_mode { + 10 => fdt.property_string("mmu-type", "riscv,sv57")?, + 9 => fdt.property_string("mmu-type", "riscv,sv48")?, + 8 => fdt.property_string("mmu-type", "riscv,sv39")?, + _ => fdt.property_string("mmu-type", "riscv,none")?, + } + fdt.property_string("riscv,isa", &cpu_isa)?; + if cbom_blksz != 0 { + fdt.property_u32("riscv,cbom-block-size", cbom_blksz as u32)?; + } + + if cbom_blksz != 0 { + fdt.property_u32("riscv,cboz-block-size", cboz_blksz as u32)?; + } + + fdt.property_u32("reg", cpu_id)?; + fdt.property_string("status", "okay")?; + let intc_node = fdt.begin_node("interrupt-controller")?; + fdt.property_u32("#interrupt-cells", 1)?; + fdt.property_string("compatible", "riscv,cpu-intc")?; + fdt.property_null("interrupt-controller")?; + fdt.property_phandle(PHANDLE_CPU_INTC_BASE + cpu_id)?; + fdt.end_node(intc_node)?; + fdt.end_node(cpu_node)?; + } + fdt.end_node(cpus_node)?; + Ok(()) + } -fn create_memory_node(fdt: &mut FdtWriter, mem_size: u64) -> Result<()> { - let mem_reg_prop = [AARCH64_PHYS_MEM_START, mem_size]; + #[cfg(target_arch = "aarch64")] + fn create_gic_node(fdt: &mut FdtWriter, is_gicv3: bool, num_cpus: u64) -> Result<()> { + let mut gic_reg_prop = [AARCH64_GIC_DIST_BASE, AARCH64_GIC_DIST_SIZE, 0, 0]; + + let intc_node = fdt.begin_node("intc")?; + if is_gicv3 { + fdt.property_string("compatible", "arm,gic-v3")?; + gic_reg_prop[2] = AARCH64_GIC_DIST_BASE - (AARCH64_GIC_REDIST_SIZE * num_cpus); + gic_reg_prop[3] = AARCH64_GIC_REDIST_SIZE * num_cpus; + } else { + fdt.property_string("compatible", "arm,cortex-a15-gic")?; + gic_reg_prop[2] = AARCH64_GIC_CPUI_BASE; + gic_reg_prop[3] = AARCH64_GIC_CPUI_SIZE; + } + fdt.property_u32("#interrupt-cells", GIC_FDT_IRQ_NUM_CELLS)?; + fdt.property_null("interrupt-controller")?; + fdt.property_array_u64("reg", &gic_reg_prop)?; + fdt.property_phandle(PHANDLE_GIC)?; + fdt.property_u32("#address-cells", 2)?; + fdt.property_u32("#size-cells", 2)?; + fdt.end_node(intc_node)?; - let memory_node = fdt.begin_node("memory")?; - fdt.property_string("device_type", "memory")?; - fdt.property_array_u64("reg", &mem_reg_prop)?; - fdt.end_node(memory_node)?; - Ok(()) -} + Ok(()) + } + + #[cfg(target_arch = "riscv64")] + fn create_aplic_node(fdt: &mut FdtWriter) -> Result<()> { + let intc_node = fdt.begin_node(&format!("aplic@{:x}", RISCV_PLIC))?; + fdt.property_phandle(PHANDLE_APLIC)?; + fdt.property_u32("riscv,num-sources", VIRT_IRQCHIP_NUM_SOURCES)?; + fdt.property_u32("msi-parent", PHANDLE_IMSIC)?; + fdt.property_u32("riscv,ndev", MAX_DEVICES - 1)?; + let plic_reg_prop = [RISCV_PLIC, RISCV_PLIC_SIZE]; + fdt.property_array_u64("reg", &plic_reg_prop)?; + fdt.property_null("interrupt-controller")?; + fdt.property_string("compatible", "riscv,aplic")?; + fdt.property_u32("#address-cells", FDT_APLIC_ADDR_CELLS)?; + fdt.property_u32("#interrupt-cells", FDT_APLIC_INT_CELLS)?; + fdt.end_node(intc_node)?; -fn create_cpu_nodes(fdt: &mut FdtWriter, num_cpus: u32) -> Result<()> { - let cpus_node = fdt.begin_node("cpus")?; - fdt.property_u32("#address-cells", 0x1)?; - fdt.property_u32("#size-cells", 0x0)?; - - for cpu_id in 0..num_cpus { - let cpu_name = format!("cpu@{:x}", cpu_id); - let cpu_node = fdt.begin_node(&cpu_name)?; - fdt.property_string("device_type", "cpu")?; - fdt.property_string("compatible", "arm,arm-v8")?; - fdt.property_string("enable-method", "psci")?; - fdt.property_u32("reg", cpu_id)?; - fdt.end_node(cpu_node)?; + Ok(()) } - fdt.end_node(cpus_node)?; - Ok(()) -} -fn create_gic_node(fdt: &mut FdtWriter, is_gicv3: bool, num_cpus: u64) -> Result<()> { - let mut gic_reg_prop = [AARCH64_GIC_DIST_BASE, AARCH64_GIC_DIST_SIZE, 0, 0]; - - let intc_node = fdt.begin_node("intc")?; - if is_gicv3 { - fdt.property_string("compatible", "arm,gic-v3")?; - gic_reg_prop[2] = AARCH64_GIC_DIST_BASE - (AARCH64_GIC_REDIST_SIZE * num_cpus); - gic_reg_prop[3] = AARCH64_GIC_REDIST_SIZE * num_cpus; - } else { - fdt.property_string("compatible", "arm,cortex-a15-gic")?; - gic_reg_prop[2] = AARCH64_GIC_CPUI_BASE; - gic_reg_prop[3] = AARCH64_GIC_CPUI_SIZE; + #[cfg(target_arch = "riscv64")] + fn create_imsic_node(fdt: &mut FdtWriter, num_vcpus: u32) -> Result<()> { + let imsic_cells: Vec<_> = (0..num_vcpus) + .flat_map(|cpu| vec![PHANDLE_CPU_INTC_BASE + cpu, IRQ_S_EXT]) + .collect(); + + let imsic_regs = [RISCV_IMSIC, 0x8000]; + + let intc_node = fdt.begin_node(&format!("imsic@{:x}", RISCV_IMSIC))?; + fdt.property_array_u64("reg", &imsic_regs)?; + fdt.property_array_u32("interrupts-extended", &imsic_cells)?; + fdt.property_u32("#interrupt-cells", FDT_IMSIC_INT_CELLS)?; + fdt.property_null("msi-controller")?; + fdt.property_null("interrupt-controller")?; + fdt.property_string("compatible", "riscv,imsics")?; + fdt.property_phandle(PHANDLE_IMSIC)?; + fdt.property_u32("riscv,num-ids", VIRT_IRQCHIP_NUM_MSIS)?; + fdt.end_node(intc_node)?; + Ok(()) } - fdt.property_u32("#interrupt-cells", GIC_FDT_IRQ_NUM_CELLS)?; - fdt.property_null("interrupt-controller")?; - fdt.property_array_u64("reg", &gic_reg_prop)?; - fdt.property_phandle(PHANDLE_GIC)?; - fdt.property_u32("#address-cells", 2)?; - fdt.property_u32("#size-cells", 2)?; - fdt.end_node(intc_node)?; - - Ok(()) -} -fn create_psci_node(fdt: &mut FdtWriter) -> Result<()> { - let compatible = "arm,psci-0.2"; - let psci_node = fdt.begin_node("psci")?; - fdt.property_string("compatible", compatible)?; - // Two methods available: hvc and smc. - // As per documentation, PSCI calls between a guest and hypervisor may use the HVC conduit instead of SMC. - // So, since we are using kvm, we need to use hvc. - fdt.property_string("method", "hvc")?; - fdt.end_node(psci_node)?; - - Ok(()) -} + #[cfg(target_arch = "aarch64")] + fn create_psci_node(fdt: &mut FdtWriter) -> Result<()> { + let compatible = "arm,psci-0.2"; + let psci_node = fdt.begin_node("psci")?; + fdt.property_string("compatible", compatible)?; + // Two methods available: hvc and smc. + // As per documentation, PSCI calls between a guest and hypervisor may use the HVC conduit instead of SMC. + // So, since we are using kvm, we need to use hvc. + fdt.property_string("method", "hvc")?; + fdt.end_node(psci_node)?; -fn create_serial_node(fdt: &mut FdtWriter, addr: u64, size: u64) -> Result<()> { - let serial_node = fdt.begin_node(&format!("uart@{:x}", addr))?; - fdt.property_string("compatible", "ns16550a")?; - let serial_reg_prop = [addr, size]; - fdt.property_array_u64("reg", &serial_reg_prop)?; - - // fdt.property_u32("clock-frequency", AARCH64_SERIAL_SPEED)?; - const CLK_PHANDLE: u32 = 24; - fdt.property_u32("clocks", CLK_PHANDLE)?; - fdt.property_string("clock-names", "apb_pclk")?; - let irq = [GIC_FDT_IRQ_TYPE_SPI, 4, IRQ_TYPE_EDGE_RISING]; - fdt.property_array_u32("interrupts", &irq)?; - fdt.end_node(serial_node)?; - Ok(()) -} + Ok(()) + } -fn create_timer_node(fdt: &mut FdtWriter, num_cpus: u32) -> Result<()> { - // These are fixed interrupt numbers for the timer device. - let irqs = [13, 14, 11, 10]; - let compatible = "arm,armv8-timer"; - let cpu_mask: u32 = - (((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK; - - let mut timer_reg_cells = Vec::new(); - for &irq in &irqs { - timer_reg_cells.push(GIC_FDT_IRQ_TYPE_PPI); - timer_reg_cells.push(irq); - timer_reg_cells.push(cpu_mask | IRQ_TYPE_LEVEL_LOW); + fn create_serial_node(fdt: &mut FdtWriter, addr: u64, size: u64) -> Result<()> { + #[cfg(target_arch = "aarch64")] + { + let serial_node = fdt.begin_node(&format!("uart@{:x}", addr))?; + fdt.property_string("compatible", "ns16550a")?; + let serial_reg_prop = [addr, size]; + fdt.property_array_u64("reg", &serial_reg_prop)?; + + const CLK_PHANDLE: u32 = 24; + fdt.property_u32("clocks", CLK_PHANDLE)?; + fdt.property_string("clock-names", "apb_pclk")?; + let irq = [GIC_FDT_IRQ_TYPE_SPI, 4, IRQ_TYPE_EDGE_RISING]; + fdt.property_array_u32("interrupts", &irq)?; + fdt.end_node(serial_node)?; + } + #[cfg(target_arch = "riscv64")] + { + let serial_node = fdt.begin_node(&format!("serial@{:x}", addr))?; + fdt.property_array_u32("interrupts", &[4, IRQ_TYPE_LEVEL_HIGH])?; + fdt.property_u32("interrupt-parent", PHANDLE_APLIC)?; + let strs = vec!["".into(), "8@".into()]; + + fdt.property_string_list("clock-frequency", strs)?; + let serial_reg_prop = [addr, size]; + fdt.property_array_u64("reg", &serial_reg_prop)?; + fdt.property_string("compatible", "ns16550a")?; + fdt.end_node(serial_node)?; + } + Ok(()) } - let timer_node = fdt.begin_node("timer")?; - fdt.property_string("compatible", compatible)?; - fdt.property_array_u32("interrupts", &timer_reg_cells)?; - fdt.property_null("always-on")?; - fdt.end_node(timer_node)?; + #[cfg(target_arch = "aarch64")] + fn create_timer_node(fdt: &mut FdtWriter, num_cpus: u32) -> Result<()> { + // These are fixed interrupt numbers for the timer device. + let irqs = [13, 14, 11, 10]; + let compatible = "arm,armv8-timer"; + let cpu_mask: u32 = + (((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK; + + let mut timer_reg_cells = Vec::new(); + for &irq in &irqs { + timer_reg_cells.push(GIC_FDT_IRQ_TYPE_PPI); + timer_reg_cells.push(irq); + timer_reg_cells.push(cpu_mask | IRQ_TYPE_LEVEL_LOW); + } - Ok(()) -} + let timer_node = fdt.begin_node("timer")?; + fdt.property_string("compatible", compatible)?; + fdt.property_array_u32("interrupts", &timer_reg_cells)?; + fdt.property_null("always-on")?; + fdt.end_node(timer_node)?; -fn create_pmu_node(fdt: &mut FdtWriter, num_cpus: u32) -> Result<()> { - let compatible = "arm,armv8-pmuv3"; - let cpu_mask: u32 = - (((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK; - let irq = [ - GIC_FDT_IRQ_TYPE_PPI, - AARCH64_PMU_IRQ, - cpu_mask | IRQ_TYPE_LEVEL_HIGH, - ]; + Ok(()) + } - let pmu_node = fdt.begin_node("pmu")?; - fdt.property_string("compatible", compatible)?; - fdt.property_array_u32("interrupts", &irq)?; - fdt.end_node(pmu_node)?; - Ok(()) -} + #[cfg(target_arch = "aarch64")] + fn create_pmu_node(fdt: &mut FdtWriter, num_cpus: u32) -> Result<()> { + let compatible = "arm,armv8-pmuv3"; + let cpu_mask: u32 = + (((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK; + let irq = [ + GIC_FDT_IRQ_TYPE_PPI, + AARCH64_PMU_IRQ, + cpu_mask | IRQ_TYPE_LEVEL_HIGH, + ]; + + let pmu_node = fdt.begin_node("pmu")?; + fdt.property_string("compatible", compatible)?; + fdt.property_array_u32("interrupts", &irq)?; + fdt.end_node(pmu_node)?; + Ok(()) + } -fn create_rtc_node(fdt: &mut FdtWriter, rtc_addr: u64, size: u64) -> Result<()> { - // the kernel driver for pl030 really really wants a clock node - // associated with an AMBA device or it will fail to probe, so we - // need to make up a clock node to associate with the pl030 rtc - // node and an associated handle with a unique phandle value. - const CLK_PHANDLE: u32 = 24; - let clock_node = fdt.begin_node("apb-pclk")?; - fdt.property_u32("#clock-cells", 0)?; - fdt.property_string("compatible", "fixed-clock")?; - fdt.property_u32("clock-frequency", 24_000_000)?; - fdt.property_string("clock-output-names", "clk24mhz")?; - fdt.property_phandle(CLK_PHANDLE)?; - fdt.end_node(clock_node)?; - - let rtc_name = format!("rtc@{:x}", rtc_addr); - let reg = [rtc_addr, size]; - let irq = [GIC_FDT_IRQ_TYPE_SPI, 33, IRQ_TYPE_LEVEL_HIGH]; - - let rtc_node = fdt.begin_node(&rtc_name)?; - fdt.property_string_list( - "compatible", - vec![String::from("arm,pl031"), String::from("arm,primecell")], - )?; - // const PL030_AMBA_ID: u32 = 0x00041030; - // fdt.property_string("arm,pl031", PL030_AMBA_ID)?; - fdt.property_array_u64("reg", ®)?; - fdt.property_array_u32("interrupts", &irq)?; - fdt.property_u32("clocks", CLK_PHANDLE)?; - fdt.property_string("clock-names", "apb_pclk")?; - fdt.end_node(rtc_node)?; - Ok(()) -} + fn create_rtc_node(fdt: &mut FdtWriter, rtc_addr: u64, size: u64) -> Result<()> { + // the kernel driver for pl030 really really wants a clock node + // associated with an AMBA device or it will fail to probe, so we + // need to make up a clock node to associate with the pl030 rtc + // node and an associated handle with a unique phandle value. + #[cfg(target_arch = "aarch64")] + { + const CLK_PHANDLE: u32 = 24; + let clock_node = fdt.begin_node("apb-pclk")?; + fdt.property_u32("#clock-cells", 0)?; + fdt.property_string("compatible", "fixed-clock")?; + fdt.property_u32("clock-frequency", 24_000_000)?; + fdt.property_string("clock-output-names", "clk24mhz")?; + fdt.property_phandle(CLK_PHANDLE)?; + fdt.end_node(clock_node)?; + + let rtc_name = format!("rtc@{:x}", rtc_addr); + let reg = [rtc_addr, size]; + let irq = [GIC_FDT_IRQ_TYPE_SPI, 33, IRQ_TYPE_LEVEL_HIGH]; + + let rtc_node = fdt.begin_node(&rtc_name)?; + fdt.property_string_list( + "compatible", + vec![String::from("arm,pl031"), String::from("arm,primecell")], + )?; + // const PL030_AMBA_ID: u32 = 0x00041030; + // fdt.property_string("arm,pl031", PL030_AMBA_ID)?; + fdt.property_array_u64("reg", ®)?; + fdt.property_array_u32("interrupts", &irq)?; + fdt.property_u32("clocks", CLK_PHANDLE)?; + fdt.property_string("clock-names", "apb_pclk")?; + fdt.end_node(rtc_node)?; + } + #[cfg(target_arch = "riscv64")] + { + const CLK_PHANDLE: u32 = 24; + let clock_node = fdt.begin_node("apb-pclk")?; + fdt.property_u32("#clock-cells", 0)?; + fdt.property_string("compatible", "fixed-clock")?; + fdt.property_u32("clock-frequency", 24_000_000)?; + fdt.property_string("clock-output-names", "clk24mhz")?; + fdt.property_phandle(CLK_PHANDLE)?; + fdt.end_node(clock_node)?; + + let rtc_name = format!("rtc@{:x}", rtc_addr); + let reg = [rtc_addr, size]; + + let rtc_node = fdt.begin_node(&rtc_name)?; + fdt.property_string("compatible", "apb_pclk")?; + fdt.property_array_u64("reg", ®)?; + fdt.property_array_u32("interrupts", &[PHANDLE_CPU_INTC_BASE, IRQ_TYPE_LEVEL_HIGH])?; + fdt.property_u32("interrupt-parent", PHANDLE_APLIC)?; + fdt.property_u32("clocks", CLK_PHANDLE)?; + fdt.end_node(rtc_node)?; + } + Ok(()) + } -fn create_virtio_node(fdt: &mut FdtWriter, addr: u64, size: u64, irq: u32) -> Result<()> { - let virtio_mmio = fdt.begin_node(&format!("virtio_mmio@{:x}", addr))?; - fdt.property_string("compatible", "virtio,mmio")?; - fdt.property_array_u64("reg", &[addr, size])?; - fdt.property_array_u32( - "interrupts", - &[GIC_FDT_IRQ_TYPE_SPI, irq, IRQ_TYPE_EDGE_RISING], - )?; - fdt.property_array_u32("interrupt-parent", &[PHANDLE_GIC])?; - fdt.end_node(virtio_mmio)?; - Ok(()) -} + fn create_virtio_node(fdt: &mut FdtWriter, addr: u64, size: u64, irq: u32) -> Result<()> { + let virtio_mmio = fdt.begin_node(&format!("virtio_mmio@{:x}", addr))?; + fdt.property_string("compatible", "virtio,mmio")?; + fdt.property_array_u64("reg", &[addr, size])?; + #[cfg(target_arch = "aarch64")] + { + fdt.property_array_u32( + "interrupts", + &[GIC_FDT_IRQ_TYPE_SPI, irq, IRQ_TYPE_EDGE_RISING], + )?; + fdt.property_array_u32("interrupt-parent", &[PHANDLE_GIC])?; + } -#[cfg(test)] -mod tests { - use crate::FdtBuilder; + #[cfg(target_arch = "riscv64")] + { + fdt.property_array_u32("interrupts", &[irq, IRQ_TYPE_LEVEL_HIGH])?; + fdt.property_array_u32("interrupt-parent", &[PHANDLE_APLIC])?; + } - #[test] - fn test_adding_virtio() { - let mut fdt = FdtBuilder::new(); - let fdt = fdt.add_virtio_device(0x1000, 1000, 5); - assert_eq!(fdt.virtio_devices.len(), 1); - assert_eq!(fdt.virtio_device_len(), 1); + fdt.end_node(virtio_mmio)?; + Ok(()) } - #[test] - fn test_create_fdt() { - let fdt_ok = FdtBuilder::new() - .with_cmdline(String::from("reboot=t panic=1 pci=off")) - .with_num_vcpus(8) - .with_mem_size(4096) - .with_serial_console(0x40000000, 0x1000) - .with_rtc(0x40001000, 0x1000) - .add_virtio_device(0x1000, 1000, 5) - .create_fdt(); - assert!(fdt_ok.is_ok()); - - let fdt_no_cmdline = FdtBuilder::new() - .with_num_vcpus(8) - .with_mem_size(4096) - .with_serial_console(0x40000000, 0x1000) - .with_rtc(0x40001000, 0x1000) - .create_fdt(); - assert!(fdt_no_cmdline.is_err()); - - let fdt_no_num_vcpus = FdtBuilder::new() - .with_cmdline(String::from("reboot=t panic=1 pci=off")) - .with_mem_size(4096) - .with_serial_console(0x40000000, 0x1000) - .with_rtc(0x40001000, 0x1000) - .create_fdt(); - assert!(fdt_no_num_vcpus.is_err()); - - let fdt_no_mem_size = FdtBuilder::new() - .with_cmdline(String::from("reboot=t panic=1 pci=off")) - .with_num_vcpus(8) - .with_serial_console(0x40000000, 0x1000) - .with_rtc(0x40001000, 0x1000) - .create_fdt(); - assert!(fdt_no_mem_size.is_err()); + #[cfg(test)] + mod tests { + use crate::fdt::FdtBuilder; + #[cfg(target_arch = "riscv64")] + use kvm_ioctls::Kvm; + + #[test] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + fn test_adding_virtio() { + let mut fdt = FdtBuilder::new(); + let fdt = fdt.add_virtio_device(0x1000, 1000, 5); + assert_eq!(fdt.virtio_devices.len(), 1); + assert_eq!(fdt.virtio_device_len(), 1); + } + + #[test] + #[cfg(target_arch = "aarch64")] + fn test_create_fdt() { + let fdt_ok = FdtBuilder::new() + .with_cmdline(String::from("reboot=t panic=1 pci=off")) + .with_num_vcpus(8) + .with_mem_size(4096) + .with_serial_console(0x40000000, 0x1000) + .with_rtc(0x40001000, 0x1000) + .add_virtio_device(0x1000, 1000, 5) + .create_fdt(); + assert!(fdt_ok.is_ok()); + + let fdt_no_cmdline = FdtBuilder::new() + .with_num_vcpus(8) + .with_mem_size(4096) + .with_serial_console(0x40000000, 0x1000) + .with_rtc(0x40001000, 0x1000) + .create_fdt(); + assert!(fdt_no_cmdline.is_err()); + + let fdt_no_num_vcpus = FdtBuilder::new() + .with_cmdline(String::from("reboot=t panic=1 pci=off")) + .with_mem_size(4096) + .with_serial_console(0x40000000, 0x1000) + .with_rtc(0x40001000, 0x1000) + .create_fdt(); + assert!(fdt_no_num_vcpus.is_err()); + + let fdt_no_mem_size = FdtBuilder::new() + .with_cmdline(String::from("reboot=t panic=1 pci=off")) + .with_num_vcpus(8) + .with_serial_console(0x40000000, 0x1000) + .with_rtc(0x40001000, 0x1000) + .create_fdt(); + assert!(fdt_no_mem_size.is_err()); + } + + #[test] + #[cfg(target_arch = "riscv64")] + fn test_create_fdt() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + + let fdt_ok = FdtBuilder::new() + .with_cmdline(String::from("reboot=t panic=1 pci=off")) + .with_num_vcpus(8) + .with_mem_size(4096) + .with_serial_console(0x40000000, 0x1000) + .with_rtc(0x40001000, 0x1000) + .add_virtio_device(0x1000, 1000, 5) + .create_fdt(&vcpu); + assert!(fdt_ok.is_ok()); + + let fdt_no_cmdline = FdtBuilder::new() + .with_num_vcpus(8) + .with_mem_size(4096) + .with_serial_console(0x40000000, 0x1000) + .with_rtc(0x40001000, 0x1000) + .create_fdt(&vcpu); + assert!(fdt_no_cmdline.is_err()); + + let fdt_no_num_vcpus = FdtBuilder::new() + .with_cmdline(String::from("reboot=t panic=1 pci=off")) + .with_mem_size(4096) + .with_serial_console(0x40000000, 0x1000) + .with_rtc(0x40001000, 0x1000) + .create_fdt(&vcpu); + assert!(fdt_no_num_vcpus.is_err()); + + let fdt_no_mem_size = FdtBuilder::new() + .with_cmdline(String::from("reboot=t panic=1 pci=off")) + .with_num_vcpus(8) + .with_serial_console(0x40000000, 0x1000) + .with_rtc(0x40001000, 0x1000) + .create_fdt(&vcpu); + assert!(fdt_no_mem_size.is_err()); + } } } From 347564686db1fc971edbcbec806a4db86cf9fbad Mon Sep 17 00:00:00 2001 From: Samuele Paone Date: Mon, 3 Feb 2025 17:20:37 +0100 Subject: [PATCH 10/10] riscv: vmm-reference first support to RISC-V This commit implements a first support to the RISC-V architecture. Major changes are related to interrupts handling: - use of in-kernel IRQCHIP (APLIC) - interrupt injection from userspace through kvm ioctl (KVM_SET_IRQ_LINE) Signed-off-by: Samuele Paone --- src/arch/src/riscv_regs.rs | 74 ++++++++ src/devices/src/intc/mod.rs | 30 +++ src/devices/src/legacy/mod.rs | 4 +- src/devices/src/legacy/serial.rs | 12 +- src/devices/src/lib.rs | 1 + src/devices/src/virtio/block/device.rs | 33 +++- src/devices/src/virtio/mod.rs | 31 ++- src/devices/src/virtio/net/device.rs | 12 +- src/vm-vcpu-ref/src/lib.rs | 5 +- src/vm-vcpu-ref/src/riscv/interrupts.rs | 243 ++++++++++++++++++++++++ src/vm-vcpu-ref/src/riscv/mod.rs | 3 + src/vm-vcpu/src/vcpu/mod.rs | 97 +++++++++- src/vm-vcpu/src/vm.rs | 125 +++++++++--- src/vmm/src/lib.rs | 175 ++++++++++++----- src/vmm/tests/integration_tests.rs | 2 +- 15 files changed, 746 insertions(+), 101 deletions(-) create mode 100644 src/arch/src/riscv_regs.rs create mode 100644 src/devices/src/intc/mod.rs create mode 100644 src/vm-vcpu-ref/src/riscv/interrupts.rs create mode 100644 src/vm-vcpu-ref/src/riscv/mod.rs diff --git a/src/arch/src/riscv_regs.rs b/src/arch/src/riscv_regs.rs new file mode 100644 index 00000000..5aef40ab --- /dev/null +++ b/src/arch/src/riscv_regs.rs @@ -0,0 +1,74 @@ +#[cfg(target_arch = "riscv64")] +#[macro_export] +macro_rules! kvm_reg_riscv_core_reg { + ($name:ident) => { + offset_of!(kvm_bindings::user_regs_struct, $name) / std::mem::size_of::() + }; +} +#[macro_export] +macro_rules! riscv_core_reg { + ($name:ident) => { + kvm_reg_id!( + KVM_REG_RISCV_CORE as u64, + 0, + kvm_reg_riscv_core_reg!($name) as u64, + KVM_REG_SIZE_U64 + ) + }; +} + +// Registers configuration macros +#[macro_export] +macro_rules! kvm_reg_riscv_config_reg { + ($name:ident) => { + offset_of!(kvm_bindings::kvm_riscv_config, $name) / std::mem::size_of::() + }; +} +#[macro_export] +macro_rules! riscv_config_reg { + ($name:ident) => { + kvm_reg_id!( + KVM_REG_RISCV_CONFIG as u64, + 0, + kvm_reg_riscv_config_reg!($name) as u64, + KVM_REG_SIZE_U64 + ) + }; +} + +// Timer-related macros +#[macro_export] +macro_rules! kvm_reg_riscv_timer_reg { + ($name:ident) => { + offset_of!(kvm_riscv_timer, $name) / std::mem::size_of::() + }; +} +#[macro_export] +macro_rules! riscv_timer_reg { + ($name:ident) => { + kvm_reg_id!( + KVM_REG_RISCV_TIMER as u64, + 0, + kvm_reg_riscv_timer_reg!($name) as u64, + KVM_REG_SIZE_U64 + ) + }; +} + +#[macro_export] +macro_rules! riscv_isa_ext_reg { + ($id:expr) => { + kvm_reg_id!(KVM_REG_RISCV_ISA_EXT as u64, 0, $id, KVM_REG_SIZE_U64) + }; +} + +#[macro_export] +macro_rules! kvm_reg_id { + ($stype:expr, $subtype:expr, $idx:expr, $size:expr) => { + (KVM_REG_RISCV as u64) + | ($stype as u64) + | ($subtype as u64) + | ($idx as u64) + | ($size as u64) + }; +} diff --git a/src/devices/src/intc/mod.rs b/src/devices/src/intc/mod.rs new file mode 100644 index 00000000..8cd4d5f1 --- /dev/null +++ b/src/devices/src/intc/mod.rs @@ -0,0 +1,30 @@ +use kvm_ioctls::VmFd; +use std::io; +use std::sync::Arc; +use vm_superio::Trigger; + +#[derive(Clone)] +pub struct APlicTrigger { + pub gsi: u32, + pub vm: Arc, +} + +impl APlicTrigger { + pub fn try_clone(&self) -> io::Result { + Ok(APlicTrigger { + gsi: self.gsi, + vm: self.vm.clone(), + }) + } +} + +impl Trigger for APlicTrigger { + type E = io::Error; + + fn trigger(&self) -> io::Result<()> { + self.vm.set_irq_line(self.gsi, true)?; + self.vm.set_irq_line(self.gsi, false)?; + + Ok(()) + } +} diff --git a/src/devices/src/legacy/mod.rs b/src/devices/src/legacy/mod.rs index d6ea0610..a364eb48 100644 --- a/src/devices/src/legacy/mod.rs +++ b/src/devices/src/legacy/mod.rs @@ -2,12 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause #[cfg(target_arch = "x86_64")] mod i8042; -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] mod rtc; mod serial; #[cfg(target_arch = "x86_64")] pub use i8042::I8042Wrapper; -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] pub use rtc::RtcWrapper; pub use serial::Error as SerialError; pub use serial::SerialWrapper; diff --git a/src/devices/src/legacy/serial.rs b/src/devices/src/legacy/serial.rs index a0f19f58..e49c0691 100644 --- a/src/devices/src/legacy/serial.rs +++ b/src/devices/src/legacy/serial.rs @@ -5,7 +5,7 @@ use std::convert::TryInto; use std::io::{self, stdin, Read, Write}; use event_manager::{EventOps, Events, MutEventSubscriber}; -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] use vm_device::{bus::MmioAddress, MutDeviceMmio}; #[cfg(target_arch = "x86_64")] use vm_device::{ @@ -106,7 +106,7 @@ impl, W: Write> MutDevicePio for SerialWrapper, W: Write> MutDeviceMmio for SerialWrapper { fn mmio_read(&mut self, _base: MmioAddress, offset: u64, data: &mut [u8]) { // TODO: this function can't return an Err, so we'll mark error conditions @@ -150,13 +150,13 @@ mod tests { // Check that passing invalid data does not result in a crash. #[cfg(target_arch = "x86_64")] serial_console.pio_read(PioAddress(0), valid_iir_offset, invalid_data); - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] serial_console.mmio_read(MmioAddress(0), valid_iir_offset, invalid_data); // The same scenario happens for writes. #[cfg(target_arch = "x86_64")] serial_console.pio_write(PioAddress(0), valid_iir_offset, invalid_data); - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] serial_console.mmio_write(MmioAddress(0), valid_iir_offset, invalid_data); } @@ -182,7 +182,7 @@ mod tests { let invalid_offset = PioAddressOffset::MAX; serial_console.pio_write(PioAddress(0), invalid_offset, &data); } - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] { let invalid_offset = u64::MAX; serial_console.mmio_write(MmioAddress(0), invalid_offset, &data); @@ -203,7 +203,7 @@ mod tests { serial_console.pio_write(PioAddress(0), offset, &write_data); serial_console.pio_read(PioAddress(0), offset, read_data.as_mut()); } - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] { serial_console.mmio_write(MmioAddress(0), offset, &write_data); serial_console.mmio_read(MmioAddress(0), offset, read_data.as_mut()); diff --git a/src/devices/src/lib.rs b/src/devices/src/lib.rs index 52789e1e..86303b0d 100644 --- a/src/devices/src/lib.rs +++ b/src/devices/src/lib.rs @@ -5,5 +5,6 @@ // we are striving to turn as much of the local code as possible into reusable building blocks // going forward. +pub mod intc; pub mod legacy; pub mod virtio; diff --git a/src/devices/src/virtio/block/device.rs b/src/devices/src/virtio/block/device.rs index 4824cb00..84588f12 100644 --- a/src/devices/src/virtio/block/device.rs +++ b/src/devices/src/virtio/block/device.rs @@ -16,12 +16,14 @@ use vm_device::{DeviceMmio, MutDeviceMmio}; use vm_memory::{GuestAddressSpace, GuestMemoryMmap}; use crate::virtio::block::{BLOCK_DEVICE_ID, VIRTIO_BLK_F_RO}; -use crate::virtio::{CommonConfig, Env, SingleFdSignalQueue, QUEUE_MAX_SIZE}; +use crate::virtio::{CommonConfig, Env, IrqTrigger, SingleFdSignalQueue, QUEUE_MAX_SIZE}; use super::inorder_handler::InOrderQueueHandler; use super::queue_handler::QueueHandler; use super::{build_config_space, BlockArgs, Error, Result}; +use crate::intc::APlicTrigger; + // This Block device can only use the MMIO transport for now, but we plan to reuse large parts of // the functionality when we implement virtio PCI as well, for example by having a base generic // type, and then separate concrete instantiations for `MmioConfig` and `PciConfig`. @@ -33,6 +35,7 @@ pub struct Block { // the outside. _root_device: bool, mem: Arc, + aplic_trigger: Option, } impl Block { @@ -41,6 +44,7 @@ impl Block { mem: Arc, env: &mut Env, args: &BlockArgs, + aplic_trigger: Option, ) -> Result { let device_features = args.device_features(); @@ -57,6 +61,7 @@ impl Block { file_path: args.file_path.clone(), read_only: args.read_only, _root_device: args.root_device, + aplic_trigger, }) } @@ -66,6 +71,7 @@ impl Block { mem: Arc, env: &mut Env, args: &BlockArgs, + aplic_trigger: Option, ) -> Result>> where // We're using this (more convoluted) bound so we can pass both references and smart @@ -73,7 +79,12 @@ impl Block { B: DerefMut, B::Target: MmioManager>, { - let block = Arc::new(Mutex::new(Self::create_block(mem, env, args)?)); + let block = Arc::new(Mutex::new(Self::create_block( + mem, + env, + args, + aplic_trigger, + )?)); // Register the device on the MMIO bus. env.register_mmio_device(block.clone()) @@ -123,9 +134,14 @@ impl VirtioDeviceActions for Bloc // TODO: Create the backend earlier (as part of `Block::new`)? let disk = StdIoBackend::new(file, features).map_err(Error::Backend)?; + let irq_trigger = if cfg!(target_arch = "riscv64") { + IrqTrigger::APlicTrigger(Some(self.aplic_trigger.clone().unwrap())) + } else { + IrqTrigger::IrqFd(self.cfg.irqfd.clone()) + }; let driver_notify = SingleFdSignalQueue { - irqfd: self.cfg.irqfd.clone(), + irq_trigger, interrupt_status: self.cfg.virtio.interrupt_status.clone(), }; @@ -185,7 +201,16 @@ mod tests { advertise_flush: true, }; - let block_mutex = Block::new(env.mem.clone(), &mut env, &args).unwrap(); + let aplic_trigger = if cfg!(target_arch = "riscv64") { + Some(APlicTrigger { + gsi: 5, + vm: env.vm_fd.clone(), + }) + } else { + None + }; + + let block_mutex = Block::new(env.mem.clone(), &mut env, &args, aplic_trigger).unwrap(); let block = block_mutex.lock().unwrap(); assert_eq!(block.device_type(), BLOCK_DEVICE_ID); diff --git a/src/devices/src/virtio/mod.rs b/src/devices/src/virtio/mod.rs index a0b24822..76501abf 100644 --- a/src/devices/src/virtio/mod.rs +++ b/src/devices/src/virtio/mod.rs @@ -12,6 +12,7 @@ use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicU8, Ordering}; use std::sync::{Arc, Mutex}; +use crate::intc::APlicTrigger; use event_manager::{ Error as EvmgrError, EventManager, MutEventSubscriber, RemoteEndpoint, Result as EvmgrResult, SubscriberId, @@ -24,6 +25,7 @@ use vm_device::bus::{self, MmioAddress, MmioRange}; use vm_device::device_manager::MmioManager; use vm_device::DeviceMmio; use vm_memory::{GuestAddress, GuestAddressSpace}; +use vm_superio::Trigger; use vmm_sys_util::errno; use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK}; @@ -66,6 +68,28 @@ pub enum Error { RegisterIrqfd(errno::Error), } +// Enum that abstracts the way in which IRQs are triggered +pub enum IrqTrigger { + IrqFd(Arc), + APlicTrigger(Option), +} + +impl IrqTrigger { + fn trigger(&self) { + match self { + IrqTrigger::IrqFd(irqfd) => { + irqfd + .write(1) + .expect("Failed write to eventfd when signaling queue"); + } + IrqTrigger::APlicTrigger(aplic_trigger) => aplic_trigger + .clone() + .unwrap() + .trigger() + .expect("Failed write to aplic trigger when signaling queue"), + } + } +} type Result = std::result::Result; pub type Subscriber = Arc>; @@ -168,6 +192,7 @@ impl CommonConfig { pub fn new(virtio_cfg: VirtioConfig, env: &Env) -> Result { let irqfd = Arc::new(EventFd::new(EFD_NONBLOCK).map_err(Error::EventFd)?); + #[cfg(not(target_arch = "riscv64"))] env.vm_fd .register_irqfd(&irqfd, env.mmio_cfg.gsi) .map_err(Error::RegisterIrqfd)?; @@ -264,7 +289,7 @@ pub trait SignalUsedQueue { /// Uses a single irqfd as the basis of signalling any queue (useful for the MMIO transport, /// where a single interrupt is shared for everything). pub struct SingleFdSignalQueue { - pub irqfd: Arc, + pub irq_trigger: IrqTrigger, pub interrupt_status: Arc, } @@ -272,9 +297,7 @@ impl SignalUsedQueue for SingleFdSignalQueue { fn signal_used_queue(&self, _index: u16) { self.interrupt_status .fetch_or(VIRTIO_MMIO_INT_VRING, Ordering::SeqCst); - self.irqfd - .write(1) - .expect("Failed write to eventfd when signalling queue"); + self.irq_trigger.trigger(); } } diff --git a/src/devices/src/virtio/net/device.rs b/src/devices/src/virtio/net/device.rs index 5d001748..7fbe7358 100644 --- a/src/devices/src/virtio/net/device.rs +++ b/src/devices/src/virtio/net/device.rs @@ -12,9 +12,11 @@ use vm_device::device_manager::MmioManager; use vm_device::{DeviceMmio, MutDeviceMmio}; use vm_memory::{GuestAddressSpace, GuestMemoryMmap}; +use crate::intc::APlicTrigger; use crate::virtio::features::{VIRTIO_F_IN_ORDER, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1}; use crate::virtio::net::features::*; use crate::virtio::net::{Error, NetArgs, Result, NET_DEVICE_ID, VIRTIO_NET_HDR_SIZE}; +use crate::virtio::IrqTrigger; use crate::virtio::{CommonConfig, Env, SingleFdSignalQueue, QUEUE_MAX_SIZE}; use super::bindings; @@ -29,6 +31,7 @@ where mem: Arc, cfg: CommonConfig, tap_name: String, + aplic_trigger: Option, } impl Net @@ -39,6 +42,7 @@ where mem: Arc, env: &mut Env, args: &NetArgs, + aplic_trigger: Option, ) -> Result>> where // We're using this (more convoluted) bound so we can pass both references and smart @@ -75,6 +79,7 @@ where mem, cfg: common_cfg, tap_name: args.tap_name.clone(), + aplic_trigger, })); env.register_mmio_device(net.clone()) @@ -124,9 +129,14 @@ impl VirtioDeviceActions f // should define this somewhere. tap.set_vnet_hdr_size(VIRTIO_NET_HDR_SIZE as i32) .map_err(Error::Tap)?; + let irq_trigger = if cfg!(target_arch = "riscv64") { + IrqTrigger::APlicTrigger(Some(self.aplic_trigger.clone().unwrap())) + } else { + IrqTrigger::IrqFd(self.cfg.irqfd.clone()) + }; let driver_notify = SingleFdSignalQueue { - irqfd: self.cfg.irqfd.clone(), + irq_trigger, interrupt_status: self.cfg.virtio.interrupt_status.clone(), }; diff --git a/src/vm-vcpu-ref/src/lib.rs b/src/vm-vcpu-ref/src/lib.rs index 30967515..b419943a 100644 --- a/src/vm-vcpu-ref/src/lib.rs +++ b/src/vm-vcpu-ref/src/lib.rs @@ -3,7 +3,7 @@ #![deny(missing_docs)] //! The `vm-vcpu-ref` crate provides abstractions for setting up the `VM` and `vCPUs` for booting. //! The high level interface exported by this crate is uniform on both supported platforms -//! (x86_64 and aarch64). Differences only arise in configuration parameters as there are +//! (x86_64, aarch64, riscv64). Differences only arise in configuration parameters as there are //! features only supported on one platform (i.e. CPUID on x86_64), and in the saved/restored //! state as both platforms define registers and VM/vCPU specific features differently. @@ -12,3 +12,6 @@ pub mod x86_64; /// Helpers for setting up the `VM` for running on aarch64. pub mod aarch64; + +/// Helpers for setting up the `VM` for running on riscv64. +pub mod riscv; diff --git a/src/vm-vcpu-ref/src/riscv/interrupts.rs b/src/vm-vcpu-ref/src/riscv/interrupts.rs new file mode 100644 index 00000000..103527bd --- /dev/null +++ b/src/vm-vcpu-ref/src/riscv/interrupts.rs @@ -0,0 +1,243 @@ +use kvm_ioctls::{DeviceFd, VmFd}; +//use kvm_bindings::{kvm_create_device, kvm_device_type_KVM_DEV_TYPE_RISCV_AIA, KVM_DEV_RISCV_AIA_GRP_ADDR, KVM_DEV_RISCV_AIA_ADDR_APLIC, KVM_DEV_RISCV_AIA_CONFIG_GUEST_BITS, KVM_DEV_RISCV_AIA_CONFIG_SRCS, KVM_DEV_RISCV_AIA_CTRL_INIT, KVM_DEV_RISCV_AIA_GRP_CTRL, KVM_DEV_RISCV_AIA_CONFIG_GROUP_SHIFT, KVM_DEV_RISCV_AIA_CONFIG_GROUP_BITS, KVM_DEV_RISCV_AIA_CONFIG_IDS, KVM_DEV_RISCV_AIA_CONFIG_MODE, KVM_DEV_RISCV_AIA_GRP_CONFIG, KVM_DEV_RISCV_AIA_CONFIG_HART_BITS}; +use kvm_bindings::*; +use std::convert::TryInto; + +const IMSIC_MMIO_GROUP_MIN_SHIFT: u64 = 24; +const AIA_IRQ_NUM_SRCS: u64 = 96; +const AIA_MSI_NUM: u64 = 255; +const BITS_PER_LONG: usize = std::mem::size_of::() * 8; +const IMSIC_MMIO_PAGE_SZ: u64 = 1u64 << 12; + +/// Errors associated with operations related to the GIC. +#[derive(Debug, PartialEq, thiserror::Error)] +pub enum Error { + /// Error calling into KVM ioctl. + #[error("Error calling into KVM ioctl: {0}")] + Kvm(kvm_ioctls::Error), + /// Error creating the APLIC device. + #[error("Error creating the APLIC device: {0}")] + CreateDevice(kvm_ioctls::Error), + /// Error setting an attribute for the APLIC device. + #[error("Error setting an attribute ({0}) for the APLIC device: {1}")] + SetAttr(&'static str, kvm_ioctls::Error), + /// Inconsisted vCPU count between APLIC and vCPU states. + #[error("Inconsisted vCPU count between the APLIC and vCPU state")] + InconsistentVcpuCount, +} + +impl From for Error { + fn from(inner: kvm_ioctls::Error) -> Self { + Error::Kvm(inner) + } +} + +/// Specialized result type for operations on the APLIC. +pub type Result = std::result::Result; + +/// High level wrapper for creating and managing the APLIC device. +#[derive(Debug)] +pub struct APlic { + device_fd: DeviceFd, + num_cpus: u8, +} + +#[derive(Debug, Clone, Default)] +/// Struct to config the APlic +pub struct APlicConfig { + /// Number of CPUs that this APLIC supports. This is not used when configuring + /// a APLIC. + pub num_cpus: u8, +} + +impl APlic { + /// Create a new Plic device + pub fn new(config: APlicConfig, vm_fd: &VmFd) -> Result { + let device_fd = APlic::create_device(vm_fd)?; + let mut aplic = APlic { + num_cpus: config.num_cpus, + device_fd, + }; + aplic.configure_device()?; + Ok(aplic) + } + + // Helper function that sets the required attributes for the device. + fn configure_device(&mut self) -> Result<()> { + let hart_count = self.num_cpus as u64; + let socket_count = 1; + let mut max_hart_per_socket = 0; + let imsic_base: u64 = 0x28000000; + let aplic_base: u64 = 0xc000000; + + let mut attr = kvm_bindings::kvm_device_attr::default(); + let mut default_aia_mode: u32 = 0; + attr.group = KVM_DEV_RISCV_AIA_GRP_CONFIG; + attr.attr = KVM_DEV_RISCV_AIA_CONFIG_MODE as u64; + attr.addr = &mut default_aia_mode as *const u32 as u64; + // SAFETY: This is safe because we set properly kvm_device_attr struct + assert!(unsafe { self.device_fd.get_device_attr(&mut attr).is_ok() }); + + let attr = kvm_bindings::kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_CONFIG, + attr: KVM_DEV_RISCV_AIA_CONFIG_SRCS as u64, + addr: &AIA_IRQ_NUM_SRCS as *const u64 as u64, + flags: 0, + }; + self.device_fd + .set_device_attr(&attr) + .map_err(|e| Error::SetAttr("Could not config srcs", e))?; + + let attr = kvm_bindings::kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_CONFIG, + attr: KVM_DEV_RISCV_AIA_CONFIG_IDS as u64, + addr: &AIA_MSI_NUM as *const u64 as u64, + flags: 0, + }; + self.device_fd + .set_device_attr(&attr) + .map_err(|e| Error::SetAttr("Could not config ids", e))?; + + if socket_count > 1 { + let socket_bits: u64 = find_last_bit(&[socket_count], BITS_PER_LONG) + 1; + let attr = kvm_bindings::kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_CONFIG, + attr: KVM_DEV_RISCV_AIA_CONFIG_GROUP_BITS as u64, + addr: &socket_bits as *const u64 as u64, + flags: 0, + }; + self.device_fd + .set_device_attr(&attr) + .map_err(|e| Error::SetAttr("Could not set group bits", e))?; + + let attr = kvm_bindings::kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_CONFIG, + attr: KVM_DEV_RISCV_AIA_CONFIG_GROUP_SHIFT as u64, + addr: &IMSIC_MMIO_GROUP_MIN_SHIFT as *const u64 as u64, + flags: 0, + }; + self.device_fd + .set_device_attr(&attr) + .map_err(|e| Error::SetAttr("Could not set group shift", e))?; + } + + let mut guest_bits: u64 = 0; + let guest_num = 0; + + if guest_num != 0 { + let last_bit = find_last_bit(&[guest_bits], BITS_PER_LONG) + 1; + guest_bits = last_bit; + } + + let attr = kvm_bindings::kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_CONFIG, + attr: KVM_DEV_RISCV_AIA_CONFIG_GUEST_BITS as u64, + addr: &guest_bits as *const u64 as u64, + flags: 0, + }; + self.device_fd + .set_device_attr(&attr) + .map_err(|e| Error::SetAttr("Could not set guest bits", e))?; + + let attr = kvm_bindings::kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_ADDR, + attr: KVM_DEV_RISCV_AIA_ADDR_APLIC as u64, + addr: &aplic_base as *const u64 as u64, + flags: 0, + }; + self.device_fd + .set_device_attr(&attr) + .map_err(|e| Error::SetAttr("Could not set APLIC base address", e))?; + + let imsic_hart_num_guests: u64 = 1u64 << guest_bits; + let imsic_hart_size: u64 = imsic_hart_num_guests * IMSIC_MMIO_PAGE_SZ; + let group_shift = IMSIC_MMIO_GROUP_MIN_SHIFT; + for socket in 0..socket_count { + let socket_imsic_base = imsic_base + socket * (1u64 << group_shift); + + if max_hart_per_socket < hart_count { + max_hart_per_socket = hart_count; + } + + for i in 0..hart_count { + let imsic_addr: u64 = socket_imsic_base + i * imsic_hart_size; + let attr = kvm_bindings::kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_ADDR, + attr: 1 + i, + addr: &imsic_addr as *const u64 as u64, + flags: 0, + }; + self.device_fd + .set_device_attr(&attr) + .map_err(|e| Error::SetAttr("Could not set IMSIC addr", e))?; + } + } + + let hart_bits = if max_hart_per_socket > 1 { + max_hart_per_socket -= 1; + find_last_bit(&[max_hart_per_socket], BITS_PER_LONG) + 1 + } else { + 0 + }; + + let attr = kvm_bindings::kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_CONFIG, + attr: u64::from(KVM_DEV_RISCV_AIA_CONFIG_HART_BITS), + addr: &hart_bits as *const u64 as u64, + flags: 0, + }; + self.device_fd + .set_device_attr(&attr) + .map_err(|e| Error::SetAttr("Could not set hart bits", e))?; + + let attr = kvm_bindings::kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_CTRL, + attr: u64::from(KVM_DEV_RISCV_AIA_CTRL_INIT), + addr: 0x0, + flags: 0, + }; + self.device_fd + .set_device_attr(&attr) + .map_err(|e| Error::SetAttr("Could not initialize AIA", e))?; + + Ok(()) + } + + // Create the device FD corresponding to the APLIC version specified as parameter. + fn create_device(vm_fd: &VmFd) -> Result { + let mut create_device_attr = kvm_create_device { + type_: kvm_device_type_KVM_DEV_TYPE_RISCV_AIA, + fd: 0, + flags: 0, + }; + vm_fd + .create_device(&mut create_device_attr) + .map_err(Error::CreateDevice) + } +} + +fn find_last_bit(addr: &[u64], size: usize) -> u64 { + let mut words = size / BITS_PER_LONG; + + if size & (BITS_PER_LONG - 1) != 0 { + let remaining_bits = size & (BITS_PER_LONG - 1); + let tmp = addr[words] & (!0usize >> (BITS_PER_LONG - remaining_bits)) as u64; + if tmp != 0 { + return (words * BITS_PER_LONG + BITS_PER_LONG - 1 - tmp.leading_zeros() as usize) + .try_into() + .unwrap(); + } + } + + while words > 0 { + words -= 1; + let tmp = addr[words]; + if tmp != 0 { + return (words * BITS_PER_LONG + BITS_PER_LONG - 1 - tmp.leading_zeros() as usize) + .try_into() + .unwrap(); + } + } + + size as u64 +} diff --git a/src/vm-vcpu-ref/src/riscv/mod.rs b/src/vm-vcpu-ref/src/riscv/mod.rs new file mode 100644 index 00000000..d40a1d68 --- /dev/null +++ b/src/vm-vcpu-ref/src/riscv/mod.rs @@ -0,0 +1,3 @@ +#![cfg(target_arch = "riscv64")] +/// Helpers for setting up the interrupt controller. +pub mod interrupts; diff --git a/src/vm-vcpu/src/vcpu/mod.rs b/src/vm-vcpu/src/vcpu/mod.rs index 8aa9cad1..5259681d 100644 --- a/src/vm-vcpu/src/vcpu/mod.rs +++ b/src/vm-vcpu/src/vcpu/mod.rs @@ -6,6 +6,8 @@ use libc::siginfo_t; use std::cell::RefCell; use std::ffi::c_void; use std::io::{self, stdin}; +#[cfg(target_arch = "riscv64")] +use std::mem::offset_of; use std::os::raw::c_int; use std::result; use std::sync::{Arc, Barrier, Condvar, Mutex}; @@ -20,10 +22,16 @@ use kvm_bindings::{ kvm_mp_state, kvm_one_reg, kvm_vcpu_init, KVM_REG_ARM64, KVM_REG_ARM_CORE, KVM_REG_SIZE_U64, KVM_SYSTEM_EVENT_CRASH, KVM_SYSTEM_EVENT_RESET, KVM_SYSTEM_EVENT_SHUTDOWN, }; +#[cfg(target_arch = "riscv64")] +use kvm_bindings::{ + kvm_mp_state, KVM_REG_RISCV, KVM_REG_RISCV_CORE, KVM_REG_SIZE_U64, KVM_SYSTEM_EVENT_CRASH, + KVM_SYSTEM_EVENT_RESET, KVM_SYSTEM_EVENT_SHUTDOWN, +}; + use kvm_ioctls::{Kvm, VcpuExit, VcpuFd, VmFd}; use vm_device::bus::{MmioAddress, PioAddress}; use vm_device::device_manager::{IoManager, MmioManager, PioManager}; -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] use vm_memory::GuestMemoryRegion; #[cfg(target_arch = "x86_64")] use vm_memory::{Address, Bytes}; @@ -51,7 +59,11 @@ use regs::*; use crate::vm::VmRunState; #[cfg(target_arch = "aarch64")] -use arch::{AARCH64_FDT_MAX_SIZE, AARCH64_PHYS_MEM_START}; +use arch::aarch64_consts::{AARCH64_FDT_MAX_SIZE, AARCH64_PHYS_MEM_START}; +#[cfg(target_arch = "riscv64")] +use arch::riscv64_consts::*; +#[cfg(target_arch = "riscv64")] +use arch::{kvm_reg_id, kvm_reg_riscv_core_reg, riscv_core_reg}; /// Initial stack for the boot CPU. #[cfg(target_arch = "x86_64")] @@ -256,7 +268,7 @@ impl VcpuConfigList { id: index, msrs: supported_msrs.clone(), }; - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] let vcpu_config = VcpuConfig { id: index }; configs.push(vcpu_config); @@ -311,6 +323,13 @@ pub struct VcpuState { pub config: VcpuConfig, } +#[cfg(target_arch = "riscv64")] +#[derive(Clone)] +pub struct VcpuState { + pub mp_state: kvm_mp_state, + pub config: VcpuConfig, +} + /// Represents the current run state of the VCPUs. #[derive(Default)] pub struct VcpuRunState { @@ -356,8 +375,8 @@ impl KvmVcpu { ) -> Result { #[cfg(target_arch = "x86_64")] let vcpu; - #[cfg(target_arch = "aarch64")] - let mut vcpu; + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + let mut vcpu; //TODO check if there are missing configurations since we do just create_vcpu vcpu = KvmVcpu { vcpu_fd: vm_fd @@ -384,6 +403,9 @@ impl KvmVcpu { vcpu.configure_regs(memory)?; } + #[cfg(target_arch = "riscv64")] + vcpu.configure_regs(memory)?; + Ok(vcpu) } @@ -440,6 +462,15 @@ impl KvmVcpu { Ok(()) } + #[cfg(target_arch = "riscv64")] + fn set_state(&mut self, state: VcpuState) -> Result<()> { + self.vcpu_fd + .set_mp_state(state.mp_state) + .map_err(Error::VcpuSetMpState)?; + + Ok(()) + } + /// Create a vCPU from a previously saved state. pub fn from_state( vm_fd: &VmFd, @@ -493,6 +524,28 @@ impl KvmVcpu { Ok(()) } + #[cfg(target_arch = "riscv64")] + fn configure_regs(&mut self, guest_mem: &M) -> Result<()> { + let mut data: u64; + if self.config.id == 0 { + let mut fdt_offset: u64 = guest_mem.iter().map(|region| region.len()).sum(); + fdt_offset = fdt_offset - RISCV64_FDT_MAX_SIZE - 0x10000; + + data = RISCV64_PHYS_MEM_START + fdt_offset; + let dt_reg = riscv_core_reg!(a1); + self.vcpu_fd + .set_one_reg(dt_reg, &(data as u128).to_le_bytes()) + .map_err(Error::VcpuSetReg)?; + + data = 0; + let hartid_reg = riscv_core_reg!(a0); + self.vcpu_fd + .set_one_reg(hartid_reg, &(data as u128).to_le_bytes()) + .map_err(Error::VcpuSetReg)?; + } + Ok(()) + } + #[cfg(target_arch = "aarch64")] fn init(&mut self, vm_fd: &VmFd) -> Result<()> { let mut kvi: kvm_vcpu_init = kvm_vcpu_init::default(); @@ -701,6 +754,15 @@ impl KvmVcpu { .set_one_reg(reg_id, &(data as u128).to_le_bytes()) .map_err(Error::VcpuSetReg)?; } + #[cfg(target_arch = "riscv64")] + if self.config.id == 0 { + let data = ip.0; + let reg_id: u64 = riscv_core_reg!(pc); + /* Set program counter */ + self.vcpu_fd + .set_one_reg(reg_id, &(data as u128).to_le_bytes()) + .map_err(Error::VcpuSetReg)?; + } } self.init_tls()?; @@ -775,7 +837,10 @@ impl KvmVcpu { .mmio_read(MmioAddress(addr), data) .is_err() { - debug!("Failed to read from mmio addr={} data={:#?}", addr, data); + debug!( + "Failed to read from mmio addr={:#016x?} data={:#?}", + addr, data + ); } } VcpuExit::MmioWrite(addr, data) => { @@ -786,10 +851,13 @@ impl KvmVcpu { .mmio_write(MmioAddress(addr), data) .is_err() { - debug!("Failed to write to mmio"); + debug!( + "Failed to write to mmio addr={:#016x?} data={:#?}", + addr, data + ); } } - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] VcpuExit::SystemEvent(type_, flags) => match type_ { KVM_SYSTEM_EVENT_SHUTDOWN | KVM_SYSTEM_EVENT_RESET @@ -930,6 +998,19 @@ impl KvmVcpu { config: self.config.clone(), }) } + + #[cfg(target_arch = "riscv64")] + pub fn save_state(&mut self) -> Result { + let mp_state = self.vcpu_fd.get_mp_state().map_err(Error::VcpuGetMpState)?; + Ok(VcpuState { + mp_state, + config: self.config.clone(), + }) + } + + pub fn get_vcpu_fd(&self) -> &VcpuFd { + &self.vcpu_fd + } } impl Drop for KvmVcpu { diff --git a/src/vm-vcpu/src/vm.rs b/src/vm-vcpu/src/vm.rs index a67df99d..bdd70c4f 100644 --- a/src/vm-vcpu/src/vm.rs +++ b/src/vm-vcpu/src/vm.rs @@ -1,13 +1,7 @@ // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright 2017 The Chromium OS Authors. All rights reserved. // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause - -#[cfg(target_arch = "x86_64")] -use std::convert::TryInto; -use std::io::{self, ErrorKind}; -use std::sync::{Arc, Barrier, Mutex}; -use std::thread::{self, JoinHandle}; - +use crate::vcpu::VcpuState; use kvm_bindings::kvm_userspace_memory_region; #[cfg(target_arch = "x86_64")] use kvm_bindings::{ @@ -16,21 +10,30 @@ use kvm_bindings::{ }; use kvm_ioctls::{Kvm, VmFd}; +#[cfg(target_arch = "x86_64")] +use std::convert::TryInto; +use std::io::{self, ErrorKind}; +use std::sync::{Arc, Barrier, Mutex}; +use std::thread::{self, JoinHandle}; use vm_device::device_manager::IoManager; use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryRegion}; use vmm_sys_util::errno::Error as Errno; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::signal::{Killable, SIGRTMIN}; -use crate::vcpu::{self, KvmVcpu, VcpuConfigList, VcpuRunState, VcpuState}; +use crate::vcpu::{self, KvmVcpu, VcpuConfigList, VcpuRunState}; #[cfg(target_arch = "aarch64")] use vm_vcpu_ref::aarch64::interrupts::{self, Gic, GicConfig, GicState}; +#[cfg(target_arch = "riscv64")] +use vm_vcpu_ref::riscv::interrupts::{self, APlic, APlicConfig}; #[cfg(target_arch = "x86_64")] use vm_vcpu_ref::x86_64::mptable::{self, MpTable}; #[cfg(target_arch = "aarch64")] pub const MAX_IRQ: u32 = interrupts::MIN_NR_IRQS; +#[cfg(target_arch = "riscv64")] +pub const MAX_IRQ: u32 = 64; #[cfg(target_arch = "x86_64")] pub const MAX_IRQ: u32 = mptable::IRQ_MAX as u32; @@ -73,6 +76,13 @@ pub struct VmState { pub gic_state: GicState, } +#[cfg(target_arch = "riscv64")] +#[derive(Clone)] +pub struct VmState { + pub config: VmConfig, + pub vcpus_state: Vec, +} + /// A KVM specific implementation of a Virtual Machine. /// /// Provides abstractions for working with a VM. Once a generic Vm trait will be available, @@ -89,8 +99,8 @@ pub struct KvmVm { vcpu_barrier: Arc, vcpu_run_state: Arc, - #[cfg(target_arch = "aarch64")] - gic: Option, + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + irqchip: Option, } #[derive(Debug, thiserror::Error)] @@ -112,7 +122,7 @@ pub enum Error { #[cfg(target_arch = "x86_64")] SetupInterruptController(kvm_ioctls::Error), #[error("Failed to setup the interrupt controller: {0}")] - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] SetupInterruptController(interrupts::Error), /// Failed to create the vcpu. #[error("Failed to create the vcpu: {0}")] @@ -170,13 +180,36 @@ impl From for Error { } } -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] impl From for Error { fn from(inner: interrupts::Error) -> Self { Error::SetupInterruptController(inner) } } +pub enum IrqChip { + #[cfg(target_arch = "aarch64")] + Gic(Gic), + #[cfg(target_arch = "riscv64")] + APlic(APlic), +} + +impl IrqChip { + #[allow(dead_code)] + #[cfg(target_arch = "aarch64")] + fn get_gic(&self) -> &Gic { + match self { + IrqChip::Gic(gic) => gic, + } + } + #[allow(dead_code)] + #[cfg(target_arch = "riscv64")] + fn get_aplic(&self) -> &APlic { + match self { + IrqChip::APlic(aplic) => aplic, + } + } +} /// Dedicated [`Result`](https://doc.rust-lang.org/std/result/) type. pub type Result = std::result::Result; @@ -222,8 +255,8 @@ impl KvmVm { exit_handler, vcpu_run_state, - #[cfg(target_arch = "aarch64")] - gic: None, + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + irqchip: None, }; vm.configure_memory_regions(guest_memory, kvm)?; @@ -283,7 +316,9 @@ impl KvmVm { #[cfg(target_arch = "aarch64")] fn set_state(&mut self, state: VmState) -> Result<()> { let mpidrs = state.vcpus_state.iter().map(|state| state.mpidr).collect(); - self.gic().restore_state(&state.gic_state, mpidrs)?; + self.irqchip() + .get_gic() + .restore_state(&state.gic_state, mpidrs)?; Ok(()) } @@ -318,6 +353,11 @@ impl KvmVm { vm.setup_irq_controller()?; vm.set_state(state)?; } + #[cfg(target_arch = "riscv64")] + { + vm.create_vcpus_from_state::(bus, vcpus_state)?; + vm.setup_irq_controller()?; + } Ok(vm) } @@ -326,11 +366,16 @@ impl KvmVm { self.fd.clone() } - #[cfg(target_arch = "aarch64")] - fn gic(&self) -> &Gic { - // This method panics if the `gic` field, which - // is Option is not properly initialized. - self.gic.as_ref().expect("GIC is not set") + pub fn get_vcpus(&self) -> &Vec { + &self.vcpus + } + + #[allow(dead_code)] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + fn irqchip(&self) -> &IrqChip { + // This method panics if the `irqchip` field, which + // is Option is not properly initialized. + self.irqchip.as_ref().expect("Irqchip is not set") } /// Returns the max irq number independent of arch. @@ -405,7 +450,19 @@ impl KvmVm { }, &self.vm_fd(), )?; - self.gic = Some(gic); + self.irqchip = Some(IrqChip::Gic(gic)); + Ok(()) + } + + #[cfg(target_arch = "riscv64")] + pub fn setup_irq_controller(&mut self) -> Result<()> { + let aplic = APlic::new( + APlicConfig { + num_cpus: self.config.num_vcpus, + }, + &self.vm_fd(), + )?; + self.irqchip = Some(IrqChip::APlic(aplic)); Ok(()) } @@ -434,7 +491,7 @@ impl KvmVm { }) .collect::>>() .map_err(Error::CreateVcpu)?; - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] self.setup_irq_controller()?; Ok(()) @@ -530,7 +587,7 @@ impl KvmVm { .map_err(Error::SaveVcpuState)?; let mpidrs = vcpus_state.iter().map(|state| state.mpidr).collect(); - let gic_state = self.gic().save_state(mpidrs)?; + let gic_state = self.irqchip().get_gic().save_state(mpidrs)?; Ok(VmState { config: self.config.clone(), @@ -539,6 +596,20 @@ impl KvmVm { }) } + #[cfg(target_arch = "riscv64")] + pub fn save_state(&mut self) -> Result { + let vcpus_state = self + .vcpus + .iter_mut() + .map(|vcpu| vcpu.save_state()) + .collect::>>() + .map_err(Error::SaveVcpuState)?; + Ok(VmState { + config: self.config.clone(), + vcpus_state, + }) + } + /// Retrieve the state of a `paused` VM. /// /// Returns an error when the VM is not paused. @@ -710,10 +781,12 @@ mod tests { fd: Arc::new(kvm.create_vm().unwrap()), exit_handler: WrappedExitHandler::default(), vcpu_run_state: Arc::new(VcpuRunState::default()), - #[cfg(target_arch = "aarch64")] - gic: None, + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + irqchip: None, }; + #[cfg(target_arch = "riscv64")] + vm.fd.create_vcpu(0).unwrap(); // Setting up the irq_controller twice should return an error. vm.setup_irq_controller().unwrap(); let res = vm.setup_irq_controller(); @@ -786,7 +859,7 @@ mod tests { assert!(KvmVm::from_state(&kvm, vm_state, &guest_memory, exit_handler, io_manager).is_ok()); } - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] #[test] fn test_vm_save_state() { let num_vcpus = 4; diff --git a/src/vmm/src/lib.rs b/src/vmm/src/lib.rs index 653d5788..d4cd376e 100644 --- a/src/vmm/src/lib.rs +++ b/src/vmm/src/lib.rs @@ -4,7 +4,7 @@ #![deny(missing_docs)] use std::convert::TryFrom; -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] use std::convert::TryInto; use std::fs::File; use std::io::{self, stdin, stdout}; @@ -39,16 +39,16 @@ use vm_device::bus::{MmioAddress, MmioRange}; #[cfg(target_arch = "x86_64")] use vm_device::bus::{PioAddress, PioRange}; use vm_device::device_manager::IoManager; -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] use vm_device::device_manager::MmioManager; #[cfg(target_arch = "x86_64")] use vm_device::device_manager::PioManager; -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] use vm_memory::GuestMemoryRegion; use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap}; #[cfg(target_arch = "x86_64")] use vm_superio::I8042Device; -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] use vm_superio::Rtc; use vm_superio::Serial; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd, terminal::Terminal}; @@ -60,16 +60,22 @@ use devices::virtio::block::{self, BlockArgs}; use devices::virtio::net::{self, NetArgs}; use devices::virtio::{Env, MmioConfig}; +use devices::intc::APlicTrigger; #[cfg(target_arch = "x86_64")] use devices::legacy::I8042Wrapper; use devices::legacy::{EventFdTrigger, SerialWrapper}; use vm_vcpu::vm::{self, ExitHandler, KvmVm, VmConfig}; -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] +use arch::fdt::FdtBuilder; +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] use devices::legacy::RtcWrapper; #[cfg(target_arch = "aarch64")] -use arch::{FdtBuilder, AARCH64_FDT_MAX_SIZE, AARCH64_MMIO_BASE, AARCH64_PHYS_MEM_START}; +use arch::aarch64_consts::{AARCH64_FDT_MAX_SIZE, AARCH64_MMIO_BASE, AARCH64_PHYS_MEM_START}; + +#[cfg(target_arch = "riscv64")] +use arch::riscv64_consts::{RISCV64_FDT_MAX_SIZE, RISCV64_MMIO_BASE, RISCV64_PHYS_MEM_START}; use vm_allocator::{AddressAllocator, AllocPolicy, RangeInclusive}; @@ -102,15 +108,21 @@ pub const DEFAULT_HIGH_RAM_START: u64 = 0x0010_0000; /// Default address for loading the kernel. #[cfg(target_arch = "x86_64")] pub const DEFAULT_KERNEL_LOAD_ADDR: u64 = DEFAULT_HIGH_RAM_START; -#[cfg(target_arch = "aarch64")] /// Default address for loading the kernel. +#[cfg(target_arch = "aarch64")] pub const DEFAULT_KERNEL_LOAD_ADDR: u64 = AARCH64_PHYS_MEM_START; +/// Default address for loading the kernel. +#[cfg(target_arch = "riscv64")] +pub const DEFAULT_KERNEL_LOAD_ADDR: u64 = RISCV64_PHYS_MEM_START; /// Default kernel command line. #[cfg(target_arch = "x86_64")] pub const DEFAULT_KERNEL_CMDLINE: &str = "panic=1 pci=off"; +/// Default kernel command line. #[cfg(target_arch = "aarch64")] +pub const DEFAULT_KERNEL_CMDLINE: &str = "reboot=t panic=1 pci=off"; /// Default kernel command line. +#[cfg(target_arch = "riscv64")] pub const DEFAULT_KERNEL_CMDLINE: &str = "reboot=t panic=1 pci=off"; /// Default address allocator alignment. It needs to be a power of 2. pub const DEFAULT_ADDRESSS_ALIGNEMNT: u64 = 4; @@ -172,9 +184,9 @@ pub enum Error { #[cfg(target_arch = "x86_64")] /// Cannot retrieve the supported MSRs. GetSupportedMsrs(vm_vcpu_ref::x86_64::msrs::Error), - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] /// Cannot setup the FDT for booting. - SetupFdt(arch::Error), + SetupFdt(arch::fdt::Error), /// IrqAllocator error IrqAllocator(irq_allocator::Error), } @@ -223,10 +235,11 @@ pub struct Vmm { // TODO: fetch the vcpu number from the `vm` object. // TODO-continued: this is needed to make the arm POC work as we need to create the FDT // TODO-continued: after the other resources are created. - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] num_vcpus: u64, - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] fdt_builder: FdtBuilder, + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] config: VMMConfig, } @@ -286,6 +299,7 @@ impl TryFrom for Vmm { type Error = Error; fn try_from(config: VMMConfig) -> Result { + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] let config_copy = config.clone(); let kvm = Kvm::new().map_err(Error::KvmIoctl)?; @@ -314,10 +328,10 @@ impl TryFrom for Vmm { let mut event_manager = EventManager::>>::new() .map_err(Error::EventManager)?; event_manager.add_subscriber(wrapped_exit_handler.0.clone()); - #[cfg(target_arch = "aarch64")] - let fdt_builder = FdtBuilder::new(); + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + let mut fdt_builder = FdtBuilder::new(); - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] if let Some(prebuilt_dtb) = &config.dtb { fdt_builder .with_prebuilt_fdt(prebuilt_dtb.path.clone()) @@ -337,16 +351,17 @@ impl TryFrom for Vmm { exit_handler: wrapped_exit_handler, block_devices: Vec::new(), net_devices: Vec::new(), - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] num_vcpus: config.vcpu_config.num as u64, - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] fdt_builder, + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] config: config_copy, }; vmm.add_serial_console()?; #[cfg(target_arch = "x86_64")] vmm.add_i8042_device()?; - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] vmm.add_rtc_device()?; // Adding the virtio devices. We'll come up with a cleaner abstraction for `Env`. @@ -368,9 +383,13 @@ impl Vmm { let load_result = self.load_kernel()?; #[cfg(target_arch = "x86_64")] let kernel_load_addr = self.compute_kernel_load_addr(&load_result)?; - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + /* + The RISC-V kernel expects to be placed at a PMD boundary (2MB aligned for rv64 + and 4MB aligned for rv32). + */ let kernel_load_addr = load_result.kernel_load; - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] self.setup_fdt()?; if stdin().lock().set_raw_mode().is_err() { @@ -417,7 +436,9 @@ impl Vmm { } #[cfg(target_arch = "aarch64")] - vec![(GuestAddress(AARCH64_PHYS_MEM_START), mem_size)] + return vec![(GuestAddress(AARCH64_PHYS_MEM_START), mem_size)]; + #[cfg(target_arch = "riscv64")] + return vec![(GuestAddress(RISCV64_PHYS_MEM_START), mem_size)]; } fn create_address_allocator(memory_config: &MemoryConfig) -> Result { @@ -426,6 +447,8 @@ impl Vmm { let start_addr = MMIO_GAP_START; #[cfg(target_arch = "aarch64")] let start_addr = AARCH64_MMIO_BASE; + #[cfg(target_arch = "riscv64")] + let start_addr = RISCV64_MMIO_BASE; let address_allocator = AddressAllocator::new(start_addr, mem_size)?; Ok(address_allocator) } @@ -510,7 +533,7 @@ impl Vmm { Ok(kernel_load) } - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] fn load_kernel(&mut self) -> Result { let mut kernel_image = File::open(&self.kernel_cfg.path).map_err(Error::IO)?; linux_loader::loader::pe::PE::load( @@ -526,10 +549,21 @@ impl Vmm { fn add_serial_console(&mut self) -> Result<()> { // Create the serial console. let interrupt_evt = EventFdTrigger::new(libc::EFD_NONBLOCK).map_err(Error::IO)?; + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] let serial = Arc::new(Mutex::new(SerialWrapper(Serial::new( interrupt_evt.try_clone().map_err(Error::IO)?, stdout(), )))); + #[cfg(target_arch = "riscv64")] + let serial = Arc::new(Mutex::new(SerialWrapper(Serial::new( + APlicTrigger { + gsi: SERIAL_IRQ, + vm: self.vm.vm_fd(), + } + .try_clone() + .map_err(Error::IO)?, + stdout(), + )))); // Register its interrupt fd with KVM. self.vm.register_irqfd(&interrupt_evt, SERIAL_IRQ)?; @@ -545,6 +579,12 @@ impl Vmm { .insert_str(format!("earlycon=uart,mmio,0x{:08x}", AARCH64_MMIO_BASE)) .map_err(Error::Cmdline)?; + #[cfg(target_arch = "riscv64")] + self.kernel_cfg + .cmdline + .insert_str(format!("earlycon=uart,mmio,0x{:08x}", RISCV64_MMIO_BASE)) + .map_err(Error::Cmdline)?; + // Put it on the bus. // Safe to use unwrap() because the device manager is instantiated in new(), there's no // default implementation, and the field is private inside the VMM struct. @@ -558,13 +598,20 @@ impl Vmm { .unwrap(); } + #[cfg(target_arch = "riscv64")] + let range = self.address_allocator.allocate( + 0x1000, + DEFAULT_ADDRESSS_ALIGNEMNT, + AllocPolicy::ExactMatch(RISCV64_MMIO_BASE), + )?; #[cfg(target_arch = "aarch64")] + let range = self.address_allocator.allocate( + 0x1000, + DEFAULT_ADDRESSS_ALIGNEMNT, + AllocPolicy::ExactMatch(AARCH64_MMIO_BASE), + )?; + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] { - let range = self.address_allocator.allocate( - 0x1000, - DEFAULT_ADDRESSS_ALIGNEMNT, - AllocPolicy::ExactMatch(AARCH64_MMIO_BASE), - )?; self.fdt_builder .with_serial_console(range.start(), range.len()); let range = mmio_from_range(&range); @@ -599,7 +646,7 @@ impl Vmm { Ok(()) } - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] fn add_rtc_device(&mut self) -> Result<()> { let rtc = Arc::new(Mutex::new(RtcWrapper(Rtc::new()))); let range = self.address_allocator.allocate( @@ -652,9 +699,19 @@ impl Vmm { advertise_flush: true, }; + let aplic_trigger = if cfg!(target_arch = "riscv64") { + Some(APlicTrigger { + gsi: irq, + vm: self.vm.vm_fd(), + }) + } else { + None + }; + let block = Block::new(self.guest_memory.clone(), &mut env, &args, aplic_trigger) + .map_err(Error::Block)?; + // We can also hold this somewhere if we need to keep the handle for later. - let block = Block::new(self.guest_memory.clone(), &mut env, &args).map_err(Error::Block)?; - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] self.fdt_builder .add_virtio_device(range.start(), range.len(), irq); self.block_devices.push(block); @@ -690,12 +747,22 @@ impl Vmm { tap_name: cfg.tap_name.clone(), }; + let aplic_trigger = if cfg!(target_arch = "riscv64") { + Some(APlicTrigger { + gsi: irq, + vm: self.vm.vm_fd(), + }) + } else { + None + }; + let net = Net::new(self.guest_memory.clone(), &mut env, &args, aplic_trigger) + .map_err(Error::Net)?; + // We can also hold this somewhere if we need to keep the handle for later. - let net = Net::new(self.guest_memory.clone(), &mut env, &args).map_err(Error::Net)?; - self.net_devices.push(net); - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] self.fdt_builder .add_virtio_device(range.start(), range.len(), irq); + self.net_devices.push(net); Ok(()) } @@ -741,12 +808,15 @@ impl Vmm { } } - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] // TODO: move this where it makes sense from a config point of view as we add all // needed stuff in FDT. fn setup_fdt(&mut self) -> Result<()> { let mem_size: u64 = self.guest_memory.iter().map(|region| region.len()).sum(); + #[cfg(target_arch = "aarch64")] let fdt_offset = mem_size - AARCH64_FDT_MAX_SIZE - 0x10000; + #[cfg(target_arch = "riscv64")] + let fdt_offset = mem_size - RISCV64_FDT_MAX_SIZE - 0x10000; let fdt = if !self.fdt_builder.has_prebuilt_fdt() { let cmdline = &self.kernel_cfg.cmdline; @@ -761,6 +831,16 @@ impl Vmm { .create_fdt() .map_err(Error::SetupFdt)?; + #[cfg(target_arch = "riscv64")] + let fdt = self + .fdt_builder + .with_cmdline(String::from( + cmdline.as_cstring().unwrap().to_str().unwrap(), + )) + .with_num_vcpus(self.num_vcpus.try_into().unwrap()) + .with_mem_size(mem_size) + .create_fdt(self.vm.get_vcpus().first().unwrap().get_vcpu_fd()) + .map_err(Error::SetupFdt)?; if let Some(dump_dtb_path) = &self.config.dump_dtb { fdt.write_to_file(dump_dtb_path.path.to_str().unwrap()) .expect("Failed to dump dtb"); @@ -837,7 +917,7 @@ mod tests { fn default_vmm_config() -> VMMConfig { VMMConfig { kernel_config: KernelConfig { - #[cfg(target_arch = "x86_64")] + #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))] path: default_elf_path(), #[cfg(target_arch = "aarch64")] path: default_pe_path(), @@ -882,7 +962,7 @@ mod tests { device_mgr.clone(), ) .unwrap(); - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] let fdt_builder = FdtBuilder::new(); let irq_allocator = IrqAllocator::new(SERIAL_IRQ, vm.max_irq()).unwrap(); Vmm { @@ -896,10 +976,12 @@ mod tests { exit_handler, block_devices: Vec::new(), net_devices: Vec::new(), - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] num_vcpus: vmm_config.vcpu_config.num as u64, - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] fdt_builder, + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + config: VMMConfig::default(), } } @@ -985,7 +1067,6 @@ mod tests { ); assert!(kernel_load_result.setup_header.is_some()); } - #[test] fn test_load_kernel_errors() { // Test case: kernel file does not exist. @@ -1009,7 +1090,7 @@ mod tests { err, Error::KernelLoad(loader::Error::Elf(loader::elf::Error::ReadElfHeader)) )); - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] assert!(matches!( err, Error::KernelLoad(loader::Error::Pe(loader::pe::Error::ReadImageHeader)) @@ -1028,7 +1109,7 @@ mod tests { )) )); - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] assert!(matches!( err, Error::KernelLoad(loader::Error::Pe( @@ -1048,22 +1129,20 @@ mod tests { err, Error::KernelLoad(loader::Error::Elf(loader::elf::Error::ReadElfHeader)) )); - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] assert!(matches!( err, Error::KernelLoad(loader::Error::Pe(loader::pe::Error::ReadImageHeader)) )); } - #[test] - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] fn test_load_kernel() { // Test case: Loading the default & valid image is ok. let vmm_config = default_vmm_config(); let mut vmm = mock_vmm(vmm_config); assert!(vmm.load_kernel().is_ok()); } - #[test] fn test_cmdline_updates() { let mut vmm_config = default_vmm_config(); @@ -1089,7 +1168,7 @@ mod tests { .unwrap() ) .contains("console=ttyS0")); - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] assert!(String::from( vmm.kernel_cfg .cmdline @@ -1100,7 +1179,6 @@ mod tests { ) .contains("earlycon=uart,mmio")); } - #[test] #[cfg(target_arch = "x86_64")] fn test_create_guest_memory() { @@ -1178,7 +1256,6 @@ mod tests { vmm_config.vcpu_config = VcpuConfig { num: 254 }; Vmm::try_from(vmm_config).unwrap(); } - #[test] // FIXME: We cannot run this on aarch64 because we do not have an image that just runs and // FIXME-continued: halts afterwards. Once we have this, we need to update `default_vmm_config` @@ -1188,6 +1265,7 @@ mod tests { let mut vmm = mock_vmm(vmm_config); let tempfile = TempFile::new().unwrap(); + let block_config = BlockConfig { path: tempfile.as_path().to_path_buf(), }; @@ -1246,7 +1324,6 @@ mod tests { .contains("virtio")); } } - #[test] #[cfg(target_arch = "aarch64")] fn test_setup_fdt() { @@ -1285,6 +1362,8 @@ mod tests { }; #[cfg(target_arch = "x86_64")] let start_addr = MMIO_GAP_START; + #[cfg(target_arch = "riscv64")] + let start_addr = RISCV64_MMIO_BASE; #[cfg(target_arch = "aarch64")] let start_addr = AARCH64_MMIO_BASE; let mut address_alloc = Vmm::create_address_allocator(&memory_config).unwrap(); diff --git a/src/vmm/tests/integration_tests.rs b/src/vmm/tests/integration_tests.rs index 8aeacfad..633a31a3 100644 --- a/src/vmm/tests/integration_tests.rs +++ b/src/vmm/tests/integration_tests.rs @@ -67,7 +67,7 @@ fn test_dummy_vmm_bzimage() { } #[test] -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] fn test_dummy_vmm_pe() { let tags = r#" {