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