From 41d0f60d4c8a5b234e7336c78e39866db5d96701 Mon Sep 17 00:00:00 2001 From: German Maglione Date: Wed, 22 May 2024 11:06:15 +0200 Subject: [PATCH] Add SMBIOS OEM Strings support (aarch64 only) Add a new API `krun_set_smbios_oem_strings()` to set SMBIOS OEM Strings (type 11), and all the internal machinery to provide them to the firmware. The EDK2 firmware expect the SMBIOS tables on address 0x4000_F000. Currently, this is only supported on aarch64 architectures. Signed-off-by: German Maglione --- Cargo.lock | 8 ++++++++ include/libkrun.h | 12 ++++++++++++ src/arch/Cargo.toml | 1 + src/arch/src/aarch64/layout.rs | 3 +++ src/arch/src/aarch64/mod.rs | 14 ++++++++++++++ src/libkrun/src/lib.rs | 32 ++++++++++++++++++++++++++++++++ src/vmm/src/builder.rs | 8 ++++++-- src/vmm/src/lib.rs | 9 ++++++++- src/vmm/src/resources.rs | 3 +++ 9 files changed, 87 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c6a1ae81..9c74b9ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,6 +74,7 @@ dependencies = [ "kvm-bindings", "kvm-ioctls", "libc", + "smbios", "utils", "vm-fdt", "vm-memory", @@ -1244,6 +1245,13 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "smbios" +version = "0.1.0" +dependencies = [ + "vm-memory", +] + [[package]] name = "socket2" version = "0.5.5" diff --git a/include/libkrun.h b/include/libkrun.h index 15ce54a6..15090769 100644 --- a/include/libkrun.h +++ b/include/libkrun.h @@ -243,6 +243,18 @@ int32_t krun_set_snd_device(uint32_t ctx_id, bool enable); */ int32_t krun_set_rlimits(uint32_t ctx_id, const char *const rlimits[]); +/** + * Sets the SMBIOS OEM Strings. + * + * Arguments: + * "ctx_id" - the configuration context ID. + * "oem_strings" - an array of string pointers. Must be terminated with an additional NULL pointer. + * + * Returns: + * Zero on success or a negative error number on failure. + */ +int32_t krun_set_smbios_oem_strings(uint32_t ctx_id, const char *const oem_strings[]); + /** * Sets the working directory for the executable to be run inside the microVM. * diff --git a/src/arch/Cargo.toml b/src/arch/Cargo.toml index 6de790f2..44202edf 100644 --- a/src/arch/Cargo.toml +++ b/src/arch/Cargo.toml @@ -14,6 +14,7 @@ libc = ">=0.2.39" vm-memory = { version = ">=0.13", features = ["backend-mmap"] } arch_gen = { path = "../arch_gen" } +smbios = { path = "../smbios" } utils = { path = "../utils" } [target.'cfg(target_os = "linux")'.dependencies] diff --git a/src/arch/src/aarch64/layout.rs b/src/arch/src/aarch64/layout.rs index f8b8d3aa..8c0bd112 100644 --- a/src/arch/src/aarch64/layout.rs +++ b/src/arch/src/aarch64/layout.rs @@ -89,3 +89,6 @@ pub const GTIMER_PHYS: u32 = 12; pub const MAPPED_IO_START: u64 = 1 << 30; // 1 GB #[cfg(feature = "efi")] pub const MAPPED_IO_START: u64 = 0x0a00_0000; + +#[cfg(feature = "efi")] +pub const SMBIOS_START: u64 = 0x4000_F000; diff --git a/src/arch/src/aarch64/mod.rs b/src/arch/src/aarch64/mod.rs index cb4d05c5..9450b94d 100644 --- a/src/arch/src/aarch64/mod.rs +++ b/src/arch/src/aarch64/mod.rs @@ -24,6 +24,9 @@ use self::gic::GICDevice; use crate::ArchMemoryInfo; use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryMmap}; +#[cfg(feature = "efi")] +use smbios; + /// Errors thrown while configuring aarch64 system. #[derive(Debug)] pub enum Error { @@ -31,6 +34,10 @@ pub enum Error { SetupFDT(fdt::Error), /// Failed to compute the initrd address. InitrdAddress, + + #[cfg(feature = "efi")] + /// SMBIOS Error + Smbios(smbios::Error), } /// The start of the memory area reserved for MMIO devices. @@ -80,6 +87,7 @@ pub fn arch_memory_regions(size: usize) -> (ArchMemoryInfo, Vec<(GuestAddress, u /// * `device_info` - A hashmap containing the attached devices for building FDT device nodes. /// * `gic_device` - The GIC device. /// * `initrd` - Information about an optional initrd. +#[allow(clippy::too_many_arguments)] pub fn configure_system( guest_mem: &GuestMemoryMmap, arch_memory_info: &ArchMemoryInfo, @@ -88,6 +96,7 @@ pub fn configure_system( device_info: &HashMap<(DeviceType, String), T>, gic_device: &Box, initrd: &Option, + _smbios_oem_strings: &Option>, ) -> super::Result<()> { fdt::create_fdt( guest_mem, @@ -99,6 +108,11 @@ pub fn configure_system( initrd, ) .map_err(Error::SetupFDT)?; + + #[cfg(feature = "efi")] + smbios::setup_smbios(guest_mem, layout::SMBIOS_START, _smbios_oem_strings) + .map_err(Error::Smbios)?; + Ok(()) } diff --git a/src/libkrun/src/lib.rs b/src/libkrun/src/lib.rs index dcf17adb..6cd2b156 100644 --- a/src/libkrun/src/lib.rs +++ b/src/libkrun/src/lib.rs @@ -891,6 +891,38 @@ pub unsafe extern "C" fn krun_set_console_output(ctx_id: u32, c_filepath: *const } } +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +pub unsafe extern "C" fn krun_set_smbios_oem_strings( + ctx_id: u32, + oem_strings: *const *const c_char, +) -> i32 { + if oem_strings.is_null() { + return -libc::EINVAL; + } + + let cstr_ptr_slice = slice::from_raw_parts(oem_strings, MAX_ARGS); + + let mut oem_strings = Vec::new(); + + for cstr_ptr in cstr_ptr_slice.iter().take_while(|p| !p.is_null()) { + let Ok(s) = CStr::from_ptr(*cstr_ptr).to_str() else { + return -libc::EINVAL; + }; + oem_strings.push(s.to_string()); + } + + match CTX_MAP.lock().unwrap().entry(ctx_id) { + Entry::Occupied(mut ctx_cfg) => { + ctx_cfg.get_mut().vmr.smbios_oem_strings = + (!oem_strings.is_empty()).then_some(oem_strings) + } + Entry::Vacant(_) => return -libc::ENOENT, + } + + KRUN_SUCCESS +} + #[cfg(feature = "net")] fn create_virtio_net(ctx_cfg: &mut ContextConfig, backend: VirtioNetBackend) { let mac = if let Some(mac) = ctx_cfg.mac { diff --git a/src/vmm/src/builder.rs b/src/vmm/src/builder.rs index f393a771..13d61dfb 100644 --- a/src/vmm/src/builder.rs +++ b/src/vmm/src/builder.rs @@ -638,8 +638,12 @@ pub fn build_microvm( #[cfg(not(feature = "tee"))] let initrd_config = None; - vmm.configure_system(vcpus.as_slice(), &initrd_config) - .map_err(StartMicrovmError::Internal)?; + vmm.configure_system( + vcpus.as_slice(), + &initrd_config, + &vm_resources.smbios_oem_strings, + ) + .map_err(StartMicrovmError::Internal)?; #[cfg(feature = "tee")] { diff --git a/src/vmm/src/lib.rs b/src/vmm/src/lib.rs index d59af4b9..376d5d7a 100644 --- a/src/vmm/src/lib.rs +++ b/src/vmm/src/lib.rs @@ -261,7 +261,12 @@ impl Vmm { } /// Configures the system for boot. - pub fn configure_system(&self, vcpus: &[Vcpu], initrd: &Option) -> Result<()> { + pub fn configure_system( + &self, + vcpus: &[Vcpu], + initrd: &Option, + _smbios_oem_strings: &Option>, + ) -> Result<()> { #[cfg(target_arch = "x86_64")] { let cmdline_len = if cfg!(feature = "tee") { @@ -292,6 +297,7 @@ impl Vmm { self.mmio_device_manager.get_device_info(), self.vm.get_irqchip(), initrd, + _smbios_oem_strings, ) .map_err(Error::ConfigureSystem)?; } @@ -307,6 +313,7 @@ impl Vmm { self.mmio_device_manager.get_device_info(), self.vm.get_irqchip(), initrd, + _smbios_oem_strings, ) .map_err(Error::ConfigureSystem)?; } diff --git a/src/vmm/src/resources.rs b/src/vmm/src/resources.rs index edfe632c..f8bd6571 100644 --- a/src/vmm/src/resources.rs +++ b/src/vmm/src/resources.rs @@ -115,6 +115,8 @@ pub struct VmResources { pub snd_device: bool, /// File to send console output. pub console_output: Option, + /// SMBIOS OEM Strings + pub smbios_oem_strings: Option>, } impl VmResources { @@ -327,6 +329,7 @@ mod tests { #[cfg(feature = "snd")] enable_snd: False, console_output: None, + smbios_oem_strings: None, } }