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