From d1b2a3fca9ad51687f330c6f0469361b84ba1e32 Mon Sep 17 00:00:00 2001 From: Michael Zhao Date: Tue, 5 Apr 2022 10:52:22 +0800 Subject: [PATCH] aarch64: Add a memory-simulated flash for UEFI EDK2 execution requires a flash device at address 0. The new added device is not a fully functional flash. It doesn't implement any spec of a flash device. Instead, a piece of memory is used to simulate the flash simply. Signed-off-by: Michael Zhao --- arch/src/aarch64/layout.rs | 6 ++--- vmm/src/device_manager.rs | 50 ++++++++++++++++++++++++++++++++++++++ vmm/src/vm.rs | 3 +++ 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/arch/src/aarch64/layout.rs b/arch/src/aarch64/layout.rs index 9fea0b852..6a6907d1a 100644 --- a/arch/src/aarch64/layout.rs +++ b/arch/src/aarch64/layout.rs @@ -42,11 +42,11 @@ // | Legacy devices space | // | | // 144 M +---------------------------------------------------------------| +// | | // | Reserved (now GIC is here) | -// 64 M +---------------------------------------------------------------+ -// | | -// | UEFI space | // | | +// 4 M +---------------------------------------------------------------+ +// | UEFI flash | // 0GB +---------------------------------------------------------------+ // // diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 087ce3024..c339aa3d8 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -25,6 +25,8 @@ use crate::pci_segment::PciSegment; use crate::seccomp_filters::{get_seccomp_filter, Thread}; use crate::serial_manager::{Error as SerialManagerError, SerialManager}; use crate::sigwinch_listener::start_sigwinch_listener; +#[cfg(target_arch = "aarch64")] +use crate::GuestMemoryMmap; use crate::GuestRegionMmap; use crate::PciDeviceInfo; use crate::{device_node, DEVICE_MANAGER_SNAPSHOT_ID}; @@ -96,6 +98,8 @@ use vm_device::interrupt::{ }; use vm_device::{Bus, BusDevice, Resource}; use vm_memory::guest_memory::FileOffset; +#[cfg(target_arch = "aarch64")] +use vm_memory::GuestMemoryAtomic; use vm_memory::GuestMemoryRegion; use vm_memory::{Address, GuestAddress, GuestUsize, MmapRegion}; #[cfg(target_arch = "x86_64")] @@ -473,6 +477,9 @@ pub enum DeviceManagerError { /// Cannot hotplug device behind vIOMMU InvalidIommuHotplug, + + /// Failed to create UEFI flash + CreateUefiFlash(hypervisor::vm::HypervisorVmError), } pub type DeviceManagerResult = result::Result; @@ -924,6 +931,10 @@ pub struct DeviceManager { // GPIO device for AArch64 gpio_device: Option>>, + #[cfg(target_arch = "aarch64")] + // Flash device for UEFI on AArch64 + uefi_flash: Option>, + // Flag to force setting the iommu on virtio devices force_iommu: bool, @@ -1067,6 +1078,8 @@ impl DeviceManager { virtio_mem_devices: Vec::new(), #[cfg(target_arch = "aarch64")] gpio_device: None, + #[cfg(target_arch = "aarch64")] + uefi_flash: None, force_iommu, restoring, io_uring_supported: None, @@ -1602,6 +1615,38 @@ impl DeviceManager { .unwrap() .insert(id.clone(), device_node!(id, gpio_device)); + // On AArch64, the UEFI binary requires a flash device at address 0. + // 4 MiB memory is mapped to simulate the flash. + let uefi_mem_slot = self.memory_manager.lock().unwrap().allocate_memory_slot(); + let uefi_region = GuestRegionMmap::new( + MmapRegion::new(arch::layout::UEFI_SIZE as usize).unwrap(), + arch::layout::UEFI_START, + ) + .unwrap(); + let uefi_mem_region = self + .memory_manager + .lock() + .unwrap() + .vm + .make_user_memory_region( + uefi_mem_slot, + uefi_region.start_addr().raw_value(), + uefi_region.len() as u64, + uefi_region.as_ptr() as u64, + false, + false, + ); + self.memory_manager + .lock() + .unwrap() + .vm + .create_user_memory_region(uefi_mem_region) + .map_err(DeviceManagerError::CreateUefiFlash)?; + + let uefi_flash = + GuestMemoryAtomic::new(GuestMemoryMmap::from_regions(vec![uefi_region]).unwrap()); + self.uefi_flash = Some(uefi_flash); + Ok(()) } @@ -4141,6 +4186,11 @@ impl DeviceManager { pub fn iommu_attached_devices(&self) -> &Option<(PciBdf, Vec)> { &self.iommu_attached_devices } + + #[cfg(target_arch = "aarch64")] + pub fn uefi_flash(&self) -> GuestMemoryAtomic { + self.uefi_flash.as_ref().unwrap().clone() + } } fn numa_node_id_from_memory_zone_id(numa_nodes: &NumaNodes, memory_zone_id: &str) -> Option { diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index ae3879220..2c356e571 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -1001,8 +1001,11 @@ impl Vm { // If failed, retry to load it as UEFI binary. // As the UEFI binary is formatless, it must be the last option to try. Err(linux_loader::loader::Error::Pe(InvalidImageMagicNumber)) => { + let uefi_flash = self.device_manager.lock().as_ref().unwrap().uefi_flash(); + let mem = uefi_flash.memory(); arch::aarch64::uefi::load_uefi(mem.deref(), arch::layout::UEFI_START, &mut kernel) .map_err(Error::UefiLoad)?; + // The entry point offset in UEFI image is always 0. return Ok(EntryPoint { entry_addr: arch::layout::UEFI_START,