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