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