aarch64: Change memory layout for UEFI & ACPI

Before this change, the FDT was loaded at the end of RAM. The address of
FDT was not fixed.
While UEFI (edk2 now) requires fixed address to find FDT and RSDP.
Now the FDT is moved to the beginning of RAM, which is a fixed address.
RSDP is wrote to 2 MiB after FDT, also a fixed address.
Kernel comes 2 MiB after RSDP.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
This commit is contained in:
Michael Zhao 2021-02-02 20:27:14 +08:00 committed by Sebastien Boeuf
parent b282ff44d4
commit e4bb6409ae
5 changed files with 51 additions and 74 deletions

View File

@ -114,7 +114,7 @@ pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHash
pub fn write_fdt_to_memory(fdt_final: Vec<u8>, guest_mem: &GuestMemoryMmap) -> Result<()> {
// Write FDT to memory.
let fdt_address = GuestAddress(get_fdt_addr(&guest_mem));
let fdt_address = GuestAddress(get_fdt_addr());
guest_mem
.write_slice(fdt_final.as_slice(), fdt_address)
.map_err(Error::WriteFdtToMemory)?;

View File

@ -36,7 +36,7 @@
// | |
// 144 M +---------------------------------------------------------------|
// | Reserved (now GIC is here) |
// 128 M +---------------------------------------------------------------+
// 64 M +---------------------------------------------------------------+
// | |
// | UEFI space |
// | |
@ -46,10 +46,10 @@
use vm_memory::GuestAddress;
/// 0x0 ~ 0x800_0000 is reserved to uefi
/// 0x0 ~ 0x400_0000 is reserved to uefi
pub const UEFI_START: u64 = 0x0;
pub const MEM_UEFI_START: GuestAddress = GuestAddress(0);
pub const UEFI_SIZE: u64 = 0x0800_0000;
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;
@ -59,13 +59,10 @@ 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 0x0902_0000 ~ 0x903_0000 is reserved for pcie io address
pub const MEM_PCI_IO_START: GuestAddress = GuestAddress(0x0902_0000);
/// Space 0x0905_0000 ~ 0x906_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;
/// Legacy space will be allocated at once whiling setting up legacy devices.
pub const LEGACY_DEVICES_MAPPED_IO_SIZE: u64 = 0x0700_0000;
/// Starting from 0x1000_0000 (256MiB) to 0x3000_0000 (768MiB) is used for PCIE MMIO
pub const MEM_32BIT_DEVICES_START: GuestAddress = GuestAddress(0x1000_0000);
pub const MEM_32BIT_DEVICES_SIZE: u64 = 0x2000_0000;
@ -81,13 +78,19 @@ pub const RAM_64BIT_START: u64 = 0x4000_0000;
/// As per `arch/arm64/include/uapi/asm/setup.h`.
pub const CMDLINE_MAX_SIZE: usize = 2048;
/// FDT is at the beginning of RAM.
/// Maximum size of the device tree blob as specified in https://www.kernel.org/doc/Documentation/arm64/booting.txt.
pub const FDT_START: u64 = RAM_64BIT_START;
pub const FDT_MAX_SIZE: usize = 0x20_0000;
/// Put ACPI table above dtb
pub const ACPI_START: u64 = RAM_64BIT_START + FDT_MAX_SIZE as u64;
pub const ACPI_MAX_SIZE: usize = 0x20_0000;
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;
// 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

View File

@ -82,28 +82,49 @@ pub fn configure_vcpu(
}
pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> {
// Normally UEFI should be loaded to a flash area at the beginning of memory.
// But now flash memory type is not supported.
// As a workaround, we take 64 MiB memory from the main RAM for UEFI.
// As a result, the RAM that the guest can see is less than what has been
// assigned in command line, when ACPI and UEFI is enabled.
let ram_deduction = if cfg!(feature = "acpi") {
layout::UEFI_SIZE
} else {
0
};
vec![
// 0 ~ 256 MiB: Reserved
// 0 ~ 64 MiB: Reserved for UEFI space
#[cfg(feature = "acpi")]
(GuestAddress(0), layout::UEFI_SIZE as usize, RegionType::Ram),
#[cfg(not(feature = "acpi"))]
(
GuestAddress(0),
layout::MEM_32BIT_DEVICES_START.0 as usize,
layout::UEFI_SIZE as usize,
RegionType::Reserved,
),
// 256 MiB ~ 1 G: MMIO space
// 64 MiB ~ 256 MiB: Gic and legacy devices
(
GuestAddress(layout::UEFI_SIZE),
(layout::MEM_32BIT_DEVICES_START.0 - layout::UEFI_SIZE) as usize,
RegionType::Reserved,
),
// 256 MiB ~ 768 MiB: MMIO space
(
layout::MEM_32BIT_DEVICES_START,
layout::MEM_32BIT_DEVICES_SIZE as usize,
RegionType::SubRegion,
),
// 1G ~ 2G: reserved. The leading 256M for PCIe MMCONFIG space
// 768 MiB ~ 1 GiB: reserved. The leading 256M for PCIe MMCONFIG space
(
layout::PCI_MMCONFIG_START,
(layout::RAM_64BIT_START - layout::PCI_MMCONFIG_START.0) as usize,
layout::PCI_MMCONFIG_SIZE as usize,
RegionType::Reserved,
),
// 1 GiB ~ : Ram
(
GuestAddress(layout::RAM_64BIT_START),
size as usize,
(size - ram_deduction) as usize,
RegionType::Ram,
),
]
@ -150,8 +171,9 @@ pub fn initramfs_load_addr(
initramfs_size: usize,
) -> super::Result<u64> {
let round_to_pagesize = |size| (size + (super::PAGE_SIZE - 1)) & !(super::PAGE_SIZE - 1);
match GuestAddress(get_fdt_addr(&guest_mem))
.checked_sub(round_to_pagesize(initramfs_size) as u64)
match guest_mem
.last_addr()
.checked_sub(round_to_pagesize(initramfs_size) as u64 - 1)
{
Some(offset) => {
if guest_mem.address_in_range(offset) {
@ -166,22 +188,12 @@ pub fn initramfs_load_addr(
/// Returns the memory address where the kernel could be loaded.
pub fn get_kernel_start() -> u64 {
layout::RAM_64BIT_START
layout::KERNEL_START
}
// Auxiliary function to get the address where the device tree blob is loaded.
fn get_fdt_addr(mem: &GuestMemoryMmap) -> u64 {
// If the memory allocated is smaller than the size allocated for the FDT,
// we return the start of the DRAM so that
// we allow the code to try and load the FDT.
if let Some(addr) = mem.last_addr().checked_sub(layout::FDT_MAX_SIZE as u64 - 1) {
if mem.address_in_range(addr) {
return addr.raw_value();
}
}
layout::RAM_64BIT_START
fn get_fdt_addr() -> u64 {
layout::FDT_START
}
pub fn get_host_cpu_phys_bits() -> u8 {
@ -210,38 +222,9 @@ mod tests {
#[test]
fn test_arch_memory_regions_dram() {
let regions = arch_memory_regions((1usize << 32) as u64); //4GB
assert_eq!(4, regions.len());
assert_eq!(GuestAddress(layout::RAM_64BIT_START), regions[3].0);
assert_eq!(1usize << 32, regions[3].1);
assert_eq!(RegionType::Ram, regions[3].2);
}
#[test]
fn test_get_fdt_addr() {
let mut regions = Vec::new();
regions.push((
GuestAddress(layout::RAM_64BIT_START),
(layout::FDT_MAX_SIZE - 0x1000) as usize,
));
let mem = GuestMemoryMmap::from_ranges(&regions).expect("Cannot initialize memory");
assert_eq!(get_fdt_addr(&mem), layout::RAM_64BIT_START);
regions.clear();
regions.push((
GuestAddress(layout::RAM_64BIT_START),
(layout::FDT_MAX_SIZE) as usize,
));
let mem = GuestMemoryMmap::from_ranges(&regions).expect("Cannot initialize memory");
assert_eq!(get_fdt_addr(&mem), layout::RAM_64BIT_START);
regions.clear();
regions.push((
GuestAddress(layout::RAM_64BIT_START),
(layout::FDT_MAX_SIZE + 0x1000) as usize,
));
let mem = GuestMemoryMmap::from_ranges(&regions).expect("Cannot initialize memory");
assert_eq!(get_fdt_addr(&mem), 0x1000 + layout::RAM_64BIT_START);
regions.clear();
assert_eq!(5, regions.len());
assert_eq!(GuestAddress(layout::RAM_64BIT_START), regions[4].0);
assert_eq!(1usize << 32, regions[4].1);
assert_eq!(RegionType::Ram, regions[4].2);
}
}

View File

@ -47,7 +47,7 @@ pub fn setup_regs(
vcpu: &Arc<dyn hypervisor::Vcpu>,
cpu_id: u8,
boot_ip: u64,
mem: &GuestMemoryMmap,
_mem: &GuestMemoryMmap,
) -> Result<()> {
let kreg_off = offset__of!(kvm_regs, regs);
@ -73,7 +73,7 @@ pub fn setup_regs(
let regs0 = offset__of!(user_pt_regs, regs) + kreg_off;
vcpu.set_reg(
arm64_core_reg_id!(KVM_REG_SIZE_U64, regs0),
get_fdt_addr(mem) as u64,
get_fdt_addr() as u64,
)
.map_err(Error::SetCoreRegister)?;
}

View File

@ -112,16 +112,7 @@ pub fn create_acpi_tables(
memory_manager: &Arc<Mutex<MemoryManager>>,
numa_nodes: &NumaNodes,
) -> GuestAddress {
#[cfg(target_arch = "x86_64")]
// RSDP is at the EBDA
let rsdp_offset = arch::layout::RSDP_POINTER;
#[cfg(target_arch = "aarch64")]
// TODO: For aarch64 place the ACPI tables in the last MiB of guest RAM
let rsdp_offset = {
use vm_memory::GuestMemory;
guest_mem.last_addr().checked_sub(1 << 20).unwrap()
};
let mut tables: Vec<u64> = Vec::new();
// DSDT