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