From b8b5dccfd8a8e68e53ebd4a0452a0fc0709042cd Mon Sep 17 00:00:00 2001 From: Jianyong Wu Date: Mon, 19 Apr 2021 15:50:46 +0800 Subject: [PATCH] aarch64: Enable UEFI image loading Implemented an architecture specific function for loading UEFI binary. Changed the logic of loading kernel image: 1. First try to load the image as kernel in PE format; 2. If failed, try again to load it as formatless UEFI binary. Signed-off-by: Jianyong Wu --- arch/src/aarch64/fdt.rs | 13 ++++++++++-- arch/src/aarch64/layout.rs | 7 +++++-- arch/src/aarch64/mod.rs | 7 +++++++ arch/src/aarch64/uefi.rs | 43 ++++++++++++++++++++++++++++++++++++++ arch/src/lib.rs | 4 ++-- vmm/src/vm.rs | 21 +++++++++++++++++++ 6 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 arch/src/aarch64/uefi.rs diff --git a/arch/src/aarch64/fdt.rs b/arch/src/aarch64/fdt.rs index fe3c017f3..a0027e227 100644 --- a/arch/src/aarch64/fdt.rs +++ b/arch/src/aarch64/fdt.rs @@ -17,8 +17,8 @@ use super::super::InitramfsConfig; use super::get_fdt_addr; use super::gic::GicDevice; use super::layout::{ - IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, PCI_MMCONFIG_SIZE, - PCI_MMCONFIG_START, + IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_PCI_IO_SIZE, MEM_PCI_IO_START, + PCI_HIGH_BASE, PCI_MMCONFIG_SIZE, PCI_MMCONFIG_START, }; use vm_fdt::{FdtWriter, FdtWriterResult}; use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestMemoryError}; @@ -413,7 +413,16 @@ fn create_pci_nodes( // Add node for PCIe controller. // See Documentation/devicetree/bindings/pci/host-generic-pci.txt in the kernel // and https://elinux.org/Device_Tree_Usage. + let pci_device_base = pci_device_base + PCI_HIGH_BASE; let ranges = [ + // io addresses + 0x1000000, + 0_u32, + 0_u32, + (MEM_PCI_IO_START.0 >> 32) as u32, + MEM_PCI_IO_START.0 as u32, + (MEM_PCI_IO_SIZE >> 32) as u32, + MEM_PCI_IO_SIZE as u32, // mmio addresses 0x2000000, // (ss = 10: 32-bit memory space) (MEM_32BIT_DEVICES_START.0 >> 32) as u32, // PCI address diff --git a/arch/src/aarch64/layout.rs b/arch/src/aarch64/layout.rs index a79778e81..da029cbda 100644 --- a/arch/src/aarch64/layout.rs +++ b/arch/src/aarch64/layout.rs @@ -54,12 +54,12 @@ pub const UEFI_SIZE: u64 = 0x0400_0000; /// Below this address will reside the GIC, above this address will reside the MMIO devices. pub const MAPPED_IO_START: u64 = 0x0900_0000; -/// Space 0x0900_0000 ~ 0x1000_0000 is reserved for legacy devices. +/// Space 0x0900_0000 ~ 0x0905_0000 is reserved for legacy devices. pub const LEGACY_SERIAL_MAPPED_IO_START: u64 = 0x0900_0000; pub const LEGACY_RTC_MAPPED_IO_START: u64 = 0x0901_0000; pub const LEGACY_GPIO_MAPPED_IO_START: u64 = 0x0902_0000; -/// Space 0x0905_0000 ~ 0x906_0000 is reserved for pcie io address +/// Space 0x0905_0000 ~ 0x0906_0000 is reserved for pcie io address pub const MEM_PCI_IO_START: GuestAddress = GuestAddress(0x0905_0000); pub const MEM_PCI_IO_SIZE: u64 = 0x10000; @@ -91,6 +91,9 @@ pub const RSDP_POINTER: GuestAddress = GuestAddress(ACPI_START); /// Kernel start after FDT and ACPI pub const KERNEL_START: u64 = ACPI_START + ACPI_MAX_SIZE as u64; +/// Pci high memory base +pub const PCI_HIGH_BASE: u64 = 0x80_0000_0000_u64; + // As per virt/kvm/arm/vgic/vgic-kvm-device.c we need // the number of interrupts our GIC will support to be: // * bigger than 32 diff --git a/arch/src/aarch64/mod.rs b/arch/src/aarch64/mod.rs index cbe8e872d..08f4be21f 100644 --- a/arch/src/aarch64/mod.rs +++ b/arch/src/aarch64/mod.rs @@ -10,6 +10,8 @@ pub mod gic; pub mod layout; /// Logic for configuring aarch64 registers. pub mod regs; +/// Module for loading UEFI binary. +pub mod uefi; pub use self::fdt::DeviceInfoForFdt; use crate::DeviceType; @@ -182,6 +184,11 @@ pub fn get_kernel_start() -> u64 { layout::KERNEL_START } +///Return guest memory address where the uefi should be loaded. +pub fn get_uefi_start() -> u64 { + layout::UEFI_START +} + // Auxiliary function to get the address where the device tree blob is loaded. fn get_fdt_addr() -> u64 { layout::FDT_START diff --git a/arch/src/aarch64/uefi.rs b/arch/src/aarch64/uefi.rs new file mode 100644 index 000000000..b394b2835 --- /dev/null +++ b/arch/src/aarch64/uefi.rs @@ -0,0 +1,43 @@ +// Copyright 2020 Arm Limited (or its affiliates). All rights reserved. + +use std::io::{Read, Seek, SeekFrom}; +use std::result; +use vm_memory::{Bytes, GuestAddress, GuestMemory}; + +/// Errors thrown while loading UEFI binary +#[derive(Debug)] +pub enum Error { + /// Unable to seek to UEFI image start. + SeekUefiStart, + /// Unable to seek to UEFI image end. + SeekUefiEnd, + /// UEFI image too big. + UefiTooBig, + /// Unable to read UEFI image + ReadUefiImage, +} +type Result = result::Result; + +pub fn load_uefi( + guest_mem: &M, + guest_addr: GuestAddress, + uefi_image: &mut F, +) -> Result<()> +where + F: Read + Seek, +{ + let uefi_size = uefi_image + .seek(SeekFrom::End(0)) + .map_err(|_| Error::SeekUefiEnd)? as usize; + + // edk2 image on virtual platform is smaller than 3M + if uefi_size > 0x300000 { + return Err(Error::UefiTooBig); + } + uefi_image + .seek(SeekFrom::Start(0)) + .map_err(|_| Error::SeekUefiStart)?; + guest_mem + .read_exact_from(guest_addr, uefi_image, uefi_size) + .map_err(|_| Error::ReadUefiImage) +} diff --git a/arch/src/lib.rs b/arch/src/lib.rs index 74ce3748c..51378113b 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -74,8 +74,8 @@ pub mod aarch64; #[cfg(target_arch = "aarch64")] pub use aarch64::{ arch_memory_regions, configure_system, configure_vcpu, fdt::DeviceInfoForFdt, - get_host_cpu_phys_bits, get_kernel_start, initramfs_load_addr, layout, - layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE, EntryPoint, + get_host_cpu_phys_bits, get_kernel_start, get_uefi_start, initramfs_load_addr, layout, + layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE, uefi, EntryPoint, }; #[cfg(target_arch = "x86_64")] diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 062b27d2e..f623e83f1 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -39,6 +39,8 @@ use hypervisor::vm::{HypervisorVmError, VmmOps}; use linux_loader::cmdline::Cmdline; #[cfg(target_arch = "x86_64")] use linux_loader::loader::elf::PvhBootCapability::PvhEntryPresent; +#[cfg(target_arch = "aarch64")] +use linux_loader::loader::pe::Error::InvalidImageMagicNumber; use linux_loader::loader::KernelLoader; use seccomp::{SeccompAction, SeccompFilter}; use signal_hook::{ @@ -91,6 +93,10 @@ pub enum Error { /// Cannot load the kernel in memory KernelLoad(linux_loader::loader::Error), + #[cfg(target_arch = "aarch64")] + /// Cannot load the UEFI binary in memory + UefiLoad(arch::aarch64::uefi::Error), + /// Cannot load the initramfs in memory InitramfsLoad, @@ -883,6 +889,21 @@ impl Vm { None, ) { Ok(entry_addr) => entry_addr, + // Try to load the binary as kernel PE file at first. + // 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)) => { + arch::aarch64::uefi::load_uefi( + mem.deref(), + GuestAddress(arch::get_uefi_start()), + &mut kernel, + ) + .map_err(Error::UefiLoad)?; + // The entry point offset in UEFI image is always 0. + return Ok(EntryPoint { + entry_addr: GuestAddress(arch::get_uefi_start()), + }); + } Err(e) => { return Err(Error::KernelLoad(e)); }