1 // Copyright 2020 Arm Limited (or its affiliates). All rights reserved. 2 // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 // SPDX-License-Identifier: Apache-2.0 4 5 /// Module for the flattened device tree. 6 pub mod fdt; 7 /// Layout for this aarch64 system. 8 pub mod layout; 9 /// Module for loading UEFI binary. 10 pub mod uefi; 11 12 use std::collections::HashMap; 13 use std::fmt::Debug; 14 use std::sync::{Arc, Mutex}; 15 16 use hypervisor::arch::aarch64::gic::Vgic; 17 use hypervisor::arch::aarch64::regs::MPIDR_EL1; 18 use log::{log_enabled, Level}; 19 use thiserror::Error; 20 use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic}; 21 22 pub use self::fdt::DeviceInfoForFdt; 23 use crate::{DeviceType, GuestMemoryMmap, NumaNodes, PciSpaceInfo, RegionType}; 24 25 pub const _NSIG: i32 = 65; 26 27 /// Errors thrown while configuring aarch64 system. 28 #[derive(Debug, Error)] 29 pub enum Error { 30 /// Failed to create a FDT. 31 #[error("Failed to create a FDT")] 32 SetupFdt, 33 34 /// Failed to write FDT to memory. 35 #[error("Failed to write FDT to memory: {0}")] 36 WriteFdtToMemory(#[source] fdt::Error), 37 38 /// Failed to create a GIC. 39 #[error("Failed to create a GIC")] 40 SetupGic, 41 42 /// Failed to compute the initramfs address. 43 #[error("Failed to compute the initramfs address")] 44 InitramfsAddress, 45 46 /// Error configuring the general purpose registers 47 #[error("Error configuring the general purpose registers: {0}")] 48 RegsConfiguration(#[source] hypervisor::HypervisorCpuError), 49 50 /// Error configuring the MPIDR register 51 #[error("Error configuring the MPIDR register: {0}")] 52 VcpuRegMpidr(#[source] hypervisor::HypervisorCpuError), 53 54 /// Error initializing PMU for vcpu 55 #[error("Error initializing PMU for vcpu")] 56 VcpuInitPmu, 57 } 58 59 #[derive(Debug, Copy, Clone)] 60 /// Specifies the entry point address where the guest must start 61 /// executing code. 62 pub struct EntryPoint { 63 /// Address in guest memory where the guest must start execution 64 pub entry_addr: GuestAddress, 65 } 66 67 /// Configure the specified VCPU, and return its MPIDR. 68 pub fn configure_vcpu( 69 vcpu: &Arc<dyn hypervisor::Vcpu>, 70 id: u8, 71 boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>, 72 ) -> super::Result<u64> { 73 if let Some((kernel_entry_point, _guest_memory)) = boot_setup { 74 vcpu.setup_regs( 75 id, 76 kernel_entry_point.entry_addr.raw_value(), 77 super::layout::FDT_START.raw_value(), 78 ) 79 .map_err(Error::RegsConfiguration)?; 80 } 81 82 let mpidr = vcpu.get_sys_reg(MPIDR_EL1).map_err(Error::VcpuRegMpidr)?; 83 Ok(mpidr) 84 } 85 86 pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> { 87 vec![ 88 // 0 MiB ~ 256 MiB: UEFI, GIC and legacy devices 89 ( 90 GuestAddress(0), 91 layout::MEM_32BIT_DEVICES_START.0 as usize, 92 RegionType::Reserved, 93 ), 94 // 256 MiB ~ 768 MiB: MMIO space 95 ( 96 layout::MEM_32BIT_DEVICES_START, 97 layout::MEM_32BIT_DEVICES_SIZE as usize, 98 RegionType::SubRegion, 99 ), 100 // 768 MiB ~ 1 GiB: reserved. The leading 256M for PCIe MMCONFIG space 101 ( 102 layout::PCI_MMCONFIG_START, 103 layout::PCI_MMCONFIG_SIZE as usize, 104 RegionType::Reserved, 105 ), 106 // 1GiB ~ 4032 MiB: RAM before the gap 107 ( 108 layout::RAM_START, 109 layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START) as usize, 110 RegionType::Ram, 111 ), 112 // 4GiB ~ inf: RAM after the gap 113 (layout::RAM_64BIT_START, usize::MAX, RegionType::Ram), 114 // Add the 32-bit reserved memory hole as a reserved region 115 ( 116 layout::MEM_32BIT_RESERVED_START, 117 layout::MEM_32BIT_RESERVED_SIZE as usize, 118 RegionType::Reserved, 119 ), 120 ] 121 } 122 123 /// Configures the system and should be called once per vm before starting vcpu threads. 124 #[allow(clippy::too_many_arguments)] 125 pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>( 126 guest_mem: &GuestMemoryMmap, 127 cmdline: &str, 128 vcpu_mpidr: Vec<u64>, 129 vcpu_topology: Option<(u8, u8, u8)>, 130 device_info: &HashMap<(DeviceType, String), T, S>, 131 initrd: &Option<super::InitramfsConfig>, 132 pci_space_info: &[PciSpaceInfo], 133 virtio_iommu_bdf: Option<u32>, 134 gic_device: &Arc<Mutex<dyn Vgic>>, 135 numa_nodes: &NumaNodes, 136 pmu_supported: bool, 137 ) -> super::Result<()> { 138 let fdt_final = fdt::create_fdt( 139 guest_mem, 140 cmdline, 141 vcpu_mpidr, 142 vcpu_topology, 143 device_info, 144 gic_device, 145 initrd, 146 pci_space_info, 147 numa_nodes, 148 virtio_iommu_bdf, 149 pmu_supported, 150 ) 151 .map_err(|_| Error::SetupFdt)?; 152 153 if log_enabled!(Level::Debug) { 154 fdt::print_fdt(&fdt_final); 155 } 156 157 fdt::write_fdt_to_memory(fdt_final, guest_mem).map_err(Error::WriteFdtToMemory)?; 158 159 Ok(()) 160 } 161 162 /// Returns the memory address where the initramfs could be loaded. 163 pub fn initramfs_load_addr( 164 guest_mem: &GuestMemoryMmap, 165 initramfs_size: usize, 166 ) -> super::Result<u64> { 167 let round_to_pagesize = |size| (size + (super::PAGE_SIZE - 1)) & !(super::PAGE_SIZE - 1); 168 match guest_mem 169 .last_addr() 170 .checked_sub(round_to_pagesize(initramfs_size) as u64 - 1) 171 { 172 Some(offset) => { 173 if guest_mem.address_in_range(offset) { 174 Ok(offset.raw_value()) 175 } else { 176 Err(super::Error::PlatformSpecific(Error::InitramfsAddress)) 177 } 178 } 179 None => Err(super::Error::PlatformSpecific(Error::InitramfsAddress)), 180 } 181 } 182 183 pub fn get_host_cpu_phys_bits(hypervisor: &Arc<dyn hypervisor::Hypervisor>) -> u8 { 184 let host_cpu_phys_bits = hypervisor.get_host_ipa_limit().try_into().unwrap(); 185 if host_cpu_phys_bits == 0 { 186 // Host kernel does not support `get_host_ipa_limit`, 187 // we return the default value 40 here. 188 40 189 } else { 190 host_cpu_phys_bits 191 } 192 } 193 194 #[cfg(test)] 195 mod tests { 196 use super::*; 197 198 #[test] 199 fn test_arch_memory_regions_dram() { 200 let regions = arch_memory_regions(); 201 assert_eq!(6, regions.len()); 202 assert_eq!(layout::RAM_START, regions[3].0); 203 assert_eq!(RegionType::Ram, regions[3].2); 204 assert_eq!(RegionType::Reserved, regions[5].2); 205 assert_eq!(RegionType::Ram, regions[4].2); 206 } 207 } 208