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")]
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")]
48 RegsConfiguration(#[source] hypervisor::HypervisorCpuError),
49
50 /// Error configuring the MPIDR register
51 #[error("Error configuring the MPIDR register")]
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.
configure_vcpu( vcpu: &Arc<dyn hypervisor::Vcpu>, id: u8, boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>, ) -> super::Result<u64>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
arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)>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)]
configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>( guest_mem: &GuestMemoryMmap, cmdline: &str, vcpu_mpidr: Vec<u64>, vcpu_topology: Option<(u8, u8, u8)>, device_info: &HashMap<(DeviceType, String), T, S>, initrd: &Option<super::InitramfsConfig>, pci_space_info: &[PciSpaceInfo], virtio_iommu_bdf: Option<u32>, gic_device: &Arc<Mutex<dyn Vgic>>, numa_nodes: &NumaNodes, pmu_supported: bool, ) -> super::Result<()>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.
initramfs_load_addr( guest_mem: &GuestMemoryMmap, initramfs_size: usize, ) -> super::Result<u64>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
get_host_cpu_phys_bits(hypervisor: &Arc<dyn hypervisor::Hypervisor>) -> u8183 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]
test_arch_memory_regions_dram()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