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