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