xref: /cloud-hypervisor/arch/src/aarch64/mod.rs (revision fee769bed4c58a07b67e25a7339cfd397f701f3a)
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