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