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 <michael.zhao@arm.com>
This commit is contained in:
Michael Zhao 2022-04-05 10:52:22 +08:00 committed by Rob Bradford
parent 9dd107bb03
commit d1b2a3fca9
3 changed files with 56 additions and 3 deletions

View File

@ -42,11 +42,11 @@
// | Legacy devices space |
// | |
// 144 M +---------------------------------------------------------------|
// | |
// | Reserved (now GIC is here) |
// 64 M +---------------------------------------------------------------+
// | |
// | UEFI space |
// | |
// 4 M +---------------------------------------------------------------+
// | UEFI flash |
// 0GB +---------------------------------------------------------------+
//
//

View File

@ -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<T> = result::Result<T, DeviceManagerError>;
@ -924,6 +931,10 @@ pub struct DeviceManager {
// GPIO device for AArch64
gpio_device: Option<Arc<Mutex<devices::legacy::Gpio>>>,
#[cfg(target_arch = "aarch64")]
// Flash device for UEFI on AArch64
uefi_flash: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
// 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<PciBdf>)> {
&self.iommu_attached_devices
}
#[cfg(target_arch = "aarch64")]
pub fn uefi_flash(&self) -> GuestMemoryAtomic<GuestMemoryMmap> {
self.uefi_flash.as_ref().unwrap().clone()
}
}
fn numa_node_id_from_memory_zone_id(numa_nodes: &NumaNodes, memory_zone_id: &str) -> Option<u32> {

View File

@ -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,