1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #![cfg(any(target_arch = "arm", target_arch = "aarch64"))]
6
7 use std::collections::BTreeMap;
8 use std::io;
9 use std::sync::Arc;
10
11 use arch::{get_serial_cmdline, GetSerialCmdlineError, RunnableLinuxVm, VmComponents, VmImage};
12 use base::{Event, MemoryMappingBuilder};
13 use devices::serial_device::{SerialHardware, SerialParameters};
14 use devices::{
15 Bus, BusDeviceObj, BusError, IrqChip, IrqChipAArch64, PciAddress, PciConfigMmio, PciDevice,
16 };
17 use hypervisor::{
18 arm64_core_reg, DeviceKind, Hypervisor, HypervisorCap, ProtectionType, VcpuAArch64,
19 VcpuFeature, Vm, VmAArch64,
20 };
21 use minijail::Minijail;
22 use remain::sorted;
23 use resources::{range_inclusive_len, MemRegion, SystemAllocator, SystemAllocatorConfig};
24 use sync::Mutex;
25 use thiserror::Error;
26 use vm_control::BatteryType;
27 use vm_memory::{GuestAddress, GuestMemory, GuestMemoryError};
28
29 mod fdt;
30
31 // We place the kernel at offset 8MB
32 const AARCH64_KERNEL_OFFSET: u64 = 0x800000;
33 const AARCH64_FDT_MAX_SIZE: u64 = 0x200000;
34 const AARCH64_INITRD_ALIGN: u64 = 0x1000000;
35
36 // These constants indicate the address space used by the ARM vGIC.
37 const AARCH64_GIC_DIST_SIZE: u64 = 0x10000;
38 const AARCH64_GIC_CPUI_SIZE: u64 = 0x20000;
39
40 // This indicates the start of DRAM inside the physical address space.
41 const AARCH64_PHYS_MEM_START: u64 = 0x80000000;
42 const AARCH64_AXI_BASE: u64 = 0x40000000;
43 const AARCH64_PLATFORM_MMIO_SIZE: u64 = 0x800000;
44
45 // FDT is placed at the front of RAM when booting in BIOS mode.
46 const AARCH64_FDT_OFFSET_IN_BIOS_MODE: u64 = 0x0;
47 // Therefore, the BIOS is placed after the FDT in memory.
48 const AARCH64_BIOS_OFFSET: u64 = AARCH64_FDT_MAX_SIZE;
49 const AARCH64_BIOS_MAX_LEN: u64 = 1 << 20;
50
51 const AARCH64_PROTECTED_VM_FW_MAX_SIZE: u64 = 0x200000;
52 const AARCH64_PROTECTED_VM_FW_START: u64 =
53 AARCH64_PHYS_MEM_START - AARCH64_PROTECTED_VM_FW_MAX_SIZE;
54
55 const AARCH64_PVTIME_IPA_MAX_SIZE: u64 = 0x10000;
56 const AARCH64_PVTIME_IPA_START: u64 = AARCH64_PROTECTED_VM_FW_START - AARCH64_PVTIME_IPA_MAX_SIZE;
57 const AARCH64_PVTIME_SIZE: u64 = 64;
58
59 // These constants indicate the placement of the GIC registers in the physical
60 // address space.
61 const AARCH64_GIC_DIST_BASE: u64 = AARCH64_AXI_BASE - AARCH64_GIC_DIST_SIZE;
62 const AARCH64_GIC_CPUI_BASE: u64 = AARCH64_GIC_DIST_BASE - AARCH64_GIC_CPUI_SIZE;
63 const AARCH64_GIC_REDIST_SIZE: u64 = 0x20000;
64
65 // PSR (Processor State Register) bits
66 const PSR_MODE_EL1H: u64 = 0x00000005;
67 const PSR_F_BIT: u64 = 0x00000040;
68 const PSR_I_BIT: u64 = 0x00000080;
69 const PSR_A_BIT: u64 = 0x00000100;
70 const PSR_D_BIT: u64 = 0x00000200;
71
get_kernel_addr() -> GuestAddress72 fn get_kernel_addr() -> GuestAddress {
73 GuestAddress(AARCH64_PHYS_MEM_START + AARCH64_KERNEL_OFFSET)
74 }
75
get_bios_addr() -> GuestAddress76 fn get_bios_addr() -> GuestAddress {
77 GuestAddress(AARCH64_PHYS_MEM_START + AARCH64_BIOS_OFFSET)
78 }
79
80 // Serial device requires 8 bytes of registers;
81 const AARCH64_SERIAL_SIZE: u64 = 0x8;
82 // This was the speed kvmtool used, not sure if it matters.
83 const AARCH64_SERIAL_SPEED: u32 = 1843200;
84 // The serial device gets the first interrupt line
85 // Which gets mapped to the first SPI interrupt (physical 32).
86 const AARCH64_SERIAL_1_3_IRQ: u32 = 0;
87 const AARCH64_SERIAL_2_4_IRQ: u32 = 2;
88
89 // Place the RTC device at page 2
90 const AARCH64_RTC_ADDR: u64 = 0x2000;
91 // The RTC device gets one 4k page
92 const AARCH64_RTC_SIZE: u64 = 0x1000;
93 // The RTC device gets the second interrupt line
94 const AARCH64_RTC_IRQ: u32 = 1;
95
96 // PCI MMIO configuration region base address.
97 const AARCH64_PCI_CFG_BASE: u64 = 0x10000;
98 // PCI MMIO configuration region size.
99 const AARCH64_PCI_CFG_SIZE: u64 = 0x1000000;
100 // This is the base address of MMIO devices.
101 const AARCH64_MMIO_BASE: u64 = 0x2000000;
102 // Size of the whole MMIO region.
103 const AARCH64_MMIO_SIZE: u64 = 0x2000000;
104 // Virtio devices start at SPI interrupt number 3
105 const AARCH64_IRQ_BASE: u32 = 3;
106
107 // PMU PPI interrupt, same as qemu
108 const AARCH64_PMU_IRQ: u32 = 7;
109
110 #[sorted]
111 #[derive(Error, Debug)]
112 pub enum Error {
113 #[error("bios could not be loaded: {0}")]
114 BiosLoadFailure(arch::LoadImageError),
115 #[error("failed to build arm pvtime memory: {0}")]
116 BuildPvtimeError(base::MmapError),
117 #[error("unable to clone an Event: {0}")]
118 CloneEvent(base::Error),
119 #[error("failed to clone IRQ chip: {0}")]
120 CloneIrqChip(base::Error),
121 #[error("the given kernel command line was invalid: {0}")]
122 Cmdline(kernel_cmdline::Error),
123 #[error("unable to make an Event: {0}")]
124 CreateEvent(base::Error),
125 #[error("FDT could not be created: {0}")]
126 CreateFdt(arch::fdt::Error),
127 #[error("failed to create GIC: {0}")]
128 CreateGICFailure(base::Error),
129 #[error("failed to create a PCI root hub: {0}")]
130 CreatePciRoot(arch::DeviceRegistrationError),
131 #[error("failed to create platform bus: {0}")]
132 CreatePlatformBus(arch::DeviceRegistrationError),
133 #[error("unable to create serial devices: {0}")]
134 CreateSerialDevices(arch::DeviceRegistrationError),
135 #[error("failed to create socket: {0}")]
136 CreateSocket(io::Error),
137 #[error("failed to create VCPU: {0}")]
138 CreateVcpu(base::Error),
139 #[error("vm created wrong kind of vcpu")]
140 DowncastVcpu,
141 #[error("failed to finalize IRQ chip: {0}")]
142 FinalizeIrqChip(base::Error),
143 #[error("failed to get PSCI version: {0}")]
144 GetPsciVersion(base::Error),
145 #[error("failed to get serial cmdline: {0}")]
146 GetSerialCmdline(GetSerialCmdlineError),
147 #[error("failed to initialize arm pvtime: {0}")]
148 InitPvtimeError(base::Error),
149 #[error("initrd could not be loaded: {0}")]
150 InitrdLoadFailure(arch::LoadImageError),
151 #[error("kernel could not be loaded: {0}")]
152 KernelLoadFailure(arch::LoadImageError),
153 #[error("failed to map arm pvtime memory: {0}")]
154 MapPvtimeError(base::Error),
155 #[error("failed to protect vm: {0}")]
156 ProtectVm(base::Error),
157 #[error("ramoops address is different from high_mmio_base: {0} vs {1}")]
158 RamoopsAddress(u64, u64),
159 #[error("failed to register irq fd: {0}")]
160 RegisterIrqfd(base::Error),
161 #[error("error registering PCI bus: {0}")]
162 RegisterPci(BusError),
163 #[error("error registering virtual socket device: {0}")]
164 RegisterVsock(arch::DeviceRegistrationError),
165 #[error("failed to set device attr: {0}")]
166 SetDeviceAttr(base::Error),
167 #[error("failed to set register: {0}")]
168 SetReg(base::Error),
169 #[error("failed to set up guest memory: {0}")]
170 SetupGuestMemory(GuestMemoryError),
171 #[error("this function isn't supported")]
172 Unsupported,
173 #[error("failed to initialize VCPU: {0}")]
174 VcpuInit(base::Error),
175 }
176
177 pub type Result<T> = std::result::Result<T, Error>;
178
179 /// Returns a Vec of the valid memory addresses.
180 /// These should be used to configure the GuestMemory structure for the platfrom.
arch_memory_regions(size: u64) -> Vec<(GuestAddress, u64)>181 pub fn arch_memory_regions(size: u64) -> Vec<(GuestAddress, u64)> {
182 vec![(GuestAddress(AARCH64_PHYS_MEM_START), size)]
183 }
184
fdt_offset(mem_size: u64, has_bios: bool) -> u64185 fn fdt_offset(mem_size: u64, has_bios: bool) -> u64 {
186 // TODO(rammuthiah) make kernel and BIOS startup use FDT from the same location. ARCVM startup
187 // currently expects the kernel at 0x80080000 and the FDT at the end of RAM for unknown reasons.
188 // Root cause and figure out how to fold these code paths together.
189 if has_bios {
190 AARCH64_FDT_OFFSET_IN_BIOS_MODE
191 } else {
192 // Put fdt up near the top of memory
193 // TODO(sonnyrao): will have to handle this differently if there's
194 // > 4GB memory
195 mem_size - AARCH64_FDT_MAX_SIZE - 0x10000
196 }
197 }
198
199 pub struct AArch64;
200
201 impl arch::LinuxArch for AArch64 {
202 type Error = Error;
203
guest_memory_layout( components: &VmComponents, ) -> std::result::Result<Vec<(GuestAddress, u64)>, Self::Error>204 fn guest_memory_layout(
205 components: &VmComponents,
206 ) -> std::result::Result<Vec<(GuestAddress, u64)>, Self::Error> {
207 Ok(arch_memory_regions(components.memory_size))
208 }
209
get_system_allocator_config<V: Vm>(vm: &V) -> SystemAllocatorConfig210 fn get_system_allocator_config<V: Vm>(vm: &V) -> SystemAllocatorConfig {
211 Self::get_resource_allocator_config(
212 vm.get_memory().memory_size(),
213 vm.get_guest_phys_addr_bits(),
214 )
215 }
216
build_vm<V, Vcpu>( mut components: VmComponents, _exit_evt: &Event, _reset_evt: &Event, system_allocator: &mut SystemAllocator, serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>, serial_jail: Option<Minijail>, _battery: (&Option<BatteryType>, Option<Minijail>), mut vm: V, ramoops_region: Option<arch::pstore::RamoopsRegion>, devs: Vec<(Box<dyn BusDeviceObj>, Option<Minijail>)>, irq_chip: &mut dyn IrqChipAArch64, kvm_vcpu_ids: &mut Vec<usize>, ) -> std::result::Result<RunnableLinuxVm<V, Vcpu>, Self::Error> where V: VmAArch64, Vcpu: VcpuAArch64,217 fn build_vm<V, Vcpu>(
218 mut components: VmComponents,
219 _exit_evt: &Event,
220 _reset_evt: &Event,
221 system_allocator: &mut SystemAllocator,
222 serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
223 serial_jail: Option<Minijail>,
224 _battery: (&Option<BatteryType>, Option<Minijail>),
225 mut vm: V,
226 ramoops_region: Option<arch::pstore::RamoopsRegion>,
227 devs: Vec<(Box<dyn BusDeviceObj>, Option<Minijail>)>,
228 irq_chip: &mut dyn IrqChipAArch64,
229 kvm_vcpu_ids: &mut Vec<usize>,
230 ) -> std::result::Result<RunnableLinuxVm<V, Vcpu>, Self::Error>
231 where
232 V: VmAArch64,
233 Vcpu: VcpuAArch64,
234 {
235 let has_bios = match components.vm_image {
236 VmImage::Bios(_) => true,
237 _ => false,
238 };
239
240 let mem = vm.get_memory().clone();
241
242 // separate out image loading from other setup to get a specific error for
243 // image loading
244 let mut initrd = None;
245 let image_size = match components.vm_image {
246 VmImage::Bios(ref mut bios) => {
247 arch::load_image(&mem, bios, get_bios_addr(), AARCH64_BIOS_MAX_LEN)
248 .map_err(Error::BiosLoadFailure)?
249 }
250 VmImage::Kernel(ref mut kernel_image) => {
251 let kernel_size =
252 arch::load_image(&mem, kernel_image, get_kernel_addr(), u64::max_value())
253 .map_err(Error::KernelLoadFailure)?;
254 let kernel_end = get_kernel_addr().offset() + kernel_size as u64;
255 initrd = match components.initrd_image {
256 Some(initrd_file) => {
257 let mut initrd_file = initrd_file;
258 let initrd_addr =
259 (kernel_end + (AARCH64_INITRD_ALIGN - 1)) & !(AARCH64_INITRD_ALIGN - 1);
260 let initrd_max_size =
261 components.memory_size - (initrd_addr - AARCH64_PHYS_MEM_START);
262 let initrd_addr = GuestAddress(initrd_addr);
263 let initrd_size =
264 arch::load_image(&mem, &mut initrd_file, initrd_addr, initrd_max_size)
265 .map_err(Error::InitrdLoadFailure)?;
266 Some((initrd_addr, initrd_size))
267 }
268 None => None,
269 };
270 kernel_size
271 }
272 };
273
274 let mut use_pmu = vm
275 .get_hypervisor()
276 .check_capability(HypervisorCap::ArmPmuV3);
277 let vcpu_count = components.vcpu_count;
278 let mut has_pvtime = true;
279 let mut vcpus = Vec::with_capacity(vcpu_count);
280 for vcpu_id in 0..vcpu_count {
281 let vcpu: Vcpu = *vm
282 .create_vcpu(vcpu_id)
283 .map_err(Error::CreateVcpu)?
284 .downcast::<Vcpu>()
285 .map_err(|_| Error::DowncastVcpu)?;
286 Self::configure_vcpu_early(
287 vm.get_memory(),
288 &vcpu,
289 vcpu_id,
290 use_pmu,
291 has_bios,
292 image_size,
293 components.protected_vm,
294 )?;
295 has_pvtime &= vcpu.has_pvtime_support();
296 vcpus.push(vcpu);
297 kvm_vcpu_ids.push(vcpu_id);
298 }
299
300 irq_chip.finalize().map_err(Error::FinalizeIrqChip)?;
301
302 if has_pvtime {
303 let pvtime_mem = MemoryMappingBuilder::new(AARCH64_PVTIME_IPA_MAX_SIZE as usize)
304 .build()
305 .map_err(Error::BuildPvtimeError)?;
306 vm.add_memory_region(
307 GuestAddress(AARCH64_PVTIME_IPA_START),
308 Box::new(pvtime_mem),
309 false,
310 false,
311 )
312 .map_err(Error::MapPvtimeError)?;
313 }
314
315 if components.protected_vm == ProtectionType::Protected {
316 vm.load_protected_vm_firmware(
317 GuestAddress(AARCH64_PROTECTED_VM_FW_START),
318 AARCH64_PROTECTED_VM_FW_MAX_SIZE,
319 )
320 .map_err(Error::ProtectVm)?;
321 }
322
323 for (vcpu_id, vcpu) in vcpus.iter().enumerate() {
324 use_pmu &= vcpu.init_pmu(AARCH64_PMU_IRQ as u64 + 16).is_ok();
325 if has_pvtime {
326 vcpu.init_pvtime(AARCH64_PVTIME_IPA_START + (vcpu_id as u64 * AARCH64_PVTIME_SIZE))
327 .map_err(Error::InitPvtimeError)?;
328 }
329 }
330
331 let mmio_bus = Arc::new(devices::Bus::new());
332
333 // ARM doesn't really use the io bus like x86, so just create an empty bus.
334 let io_bus = Arc::new(devices::Bus::new());
335
336 // Event used by PMDevice to notify crosvm that
337 // guest OS is trying to suspend.
338 let suspend_evt = Event::new().map_err(Error::CreateEvent)?;
339
340 let (pci_devices, others): (Vec<_>, Vec<_>) = devs
341 .into_iter()
342 .partition(|(dev, _)| dev.as_pci_device().is_some());
343
344 let pci_devices = pci_devices
345 .into_iter()
346 .map(|(dev, jail_orig)| (dev.into_pci_device().unwrap(), jail_orig))
347 .collect();
348 let (pci, pci_irqs, mut pid_debug_label_map) = arch::generate_pci_root(
349 pci_devices,
350 irq_chip.as_irq_chip_mut(),
351 mmio_bus.clone(),
352 io_bus.clone(),
353 system_allocator,
354 &mut vm,
355 (devices::AARCH64_GIC_NR_SPIS - AARCH64_IRQ_BASE) as usize,
356 )
357 .map_err(Error::CreatePciRoot)?;
358
359 let pci_root = Arc::new(Mutex::new(pci));
360 let pci_bus = Arc::new(Mutex::new(PciConfigMmio::new(pci_root.clone(), 8)));
361 let (platform_devices, _others): (Vec<_>, Vec<_>) = others
362 .into_iter()
363 .partition(|(dev, _)| dev.as_platform_device().is_some());
364
365 let platform_devices = platform_devices
366 .into_iter()
367 .map(|(dev, jail_orig)| (*(dev.into_platform_device().unwrap()), jail_orig))
368 .collect();
369 let mut platform_pid_debug_label_map = arch::generate_platform_bus(
370 platform_devices,
371 irq_chip.as_irq_chip_mut(),
372 &mmio_bus,
373 system_allocator,
374 )
375 .map_err(Error::CreatePlatformBus)?;
376 pid_debug_label_map.append(&mut platform_pid_debug_label_map);
377
378 Self::add_arch_devs(irq_chip.as_irq_chip_mut(), &mmio_bus)?;
379
380 let com_evt_1_3 = devices::IrqEdgeEvent::new().map_err(Error::CreateEvent)?;
381 let com_evt_2_4 = devices::IrqEdgeEvent::new().map_err(Error::CreateEvent)?;
382 arch::add_serial_devices(
383 components.protected_vm,
384 &mmio_bus,
385 &com_evt_1_3.get_trigger(),
386 &com_evt_2_4.get_trigger(),
387 serial_parameters,
388 serial_jail,
389 )
390 .map_err(Error::CreateSerialDevices)?;
391
392 irq_chip
393 .register_edge_irq_event(AARCH64_SERIAL_1_3_IRQ, &com_evt_1_3)
394 .map_err(Error::RegisterIrqfd)?;
395 irq_chip
396 .register_edge_irq_event(AARCH64_SERIAL_2_4_IRQ, &com_evt_2_4)
397 .map_err(Error::RegisterIrqfd)?;
398
399 mmio_bus
400 .insert(pci_bus.clone(), AARCH64_PCI_CFG_BASE, AARCH64_PCI_CFG_SIZE)
401 .map_err(Error::RegisterPci)?;
402
403 let mut cmdline = Self::get_base_linux_cmdline();
404 get_serial_cmdline(&mut cmdline, serial_parameters, "mmio")
405 .map_err(Error::GetSerialCmdline)?;
406 for param in components.extra_kernel_params {
407 cmdline.insert_str(¶m).map_err(Error::Cmdline)?;
408 }
409
410 if let Some(ramoops_region) = ramoops_region {
411 arch::pstore::add_ramoops_kernel_cmdline(&mut cmdline, &ramoops_region)
412 .map_err(Error::Cmdline)?;
413 }
414
415 let psci_version = vcpus[0].get_psci_version().map_err(Error::GetPsciVersion)?;
416
417 let pci_cfg = fdt::PciConfigRegion {
418 base: AARCH64_PCI_CFG_BASE,
419 size: AARCH64_PCI_CFG_SIZE,
420 };
421
422 let pci_ranges: Vec<fdt::PciRange> = system_allocator
423 .mmio_pools()
424 .iter()
425 .map(|range| fdt::PciRange {
426 space: fdt::PciAddressSpace::Memory64,
427 bus_address: *range.start(),
428 cpu_physical_address: *range.start(),
429 size: range_inclusive_len(range).unwrap(),
430 prefetchable: false,
431 })
432 .collect();
433
434 fdt::create_fdt(
435 AARCH64_FDT_MAX_SIZE as usize,
436 &mem,
437 pci_irqs,
438 pci_cfg,
439 &pci_ranges,
440 vcpu_count as u32,
441 components.cpu_clusters,
442 components.cpu_capacity,
443 fdt_offset(components.memory_size, has_bios),
444 cmdline.as_str(),
445 initrd,
446 components.android_fstab,
447 irq_chip.get_vgic_version() == DeviceKind::ArmVgicV3,
448 use_pmu,
449 psci_version,
450 components.swiotlb,
451 )
452 .map_err(Error::CreateFdt)?;
453
454 Ok(RunnableLinuxVm {
455 vm,
456 vcpu_count,
457 vcpus: Some(vcpus),
458 vcpu_affinity: components.vcpu_affinity,
459 no_smt: components.no_smt,
460 irq_chip: irq_chip.try_box_clone().map_err(Error::CloneIrqChip)?,
461 has_bios,
462 io_bus,
463 mmio_bus,
464 pid_debug_label_map,
465 suspend_evt,
466 rt_cpus: components.rt_cpus,
467 delay_rt: components.delay_rt,
468 bat_control: None,
469 pm: None,
470 resume_notify_devices: Vec::new(),
471 root_config: pci_root,
472 hotplug_bus: Vec::new(),
473 })
474 }
475
configure_vcpu<V: Vm>( _vm: &V, _hypervisor: &dyn Hypervisor, _irq_chip: &mut dyn IrqChipAArch64, _vcpu: &mut dyn VcpuAArch64, _vcpu_id: usize, _num_cpus: usize, _has_bios: bool, _no_smt: bool, _host_cpu_topology: bool, ) -> std::result::Result<(), Self::Error>476 fn configure_vcpu<V: Vm>(
477 _vm: &V,
478 _hypervisor: &dyn Hypervisor,
479 _irq_chip: &mut dyn IrqChipAArch64,
480 _vcpu: &mut dyn VcpuAArch64,
481 _vcpu_id: usize,
482 _num_cpus: usize,
483 _has_bios: bool,
484 _no_smt: bool,
485 _host_cpu_topology: bool,
486 ) -> std::result::Result<(), Self::Error> {
487 // AArch64 doesn't configure vcpus on the vcpu thread, so nothing to do here.
488 Ok(())
489 }
490
register_pci_device<V: VmAArch64, Vcpu: VcpuAArch64>( _linux: &mut RunnableLinuxVm<V, Vcpu>, _device: Box<dyn PciDevice>, _minijail: Option<Minijail>, _resources: &mut SystemAllocator, ) -> std::result::Result<PciAddress, Self::Error>491 fn register_pci_device<V: VmAArch64, Vcpu: VcpuAArch64>(
492 _linux: &mut RunnableLinuxVm<V, Vcpu>,
493 _device: Box<dyn PciDevice>,
494 _minijail: Option<Minijail>,
495 _resources: &mut SystemAllocator,
496 ) -> std::result::Result<PciAddress, Self::Error> {
497 // hotplug function isn't verified on AArch64, so set it unsupported here.
498 Err(Error::Unsupported)
499 }
500 }
501
502 impl AArch64 {
503 /// This returns a base part of the kernel command for this architecture
get_base_linux_cmdline() -> kernel_cmdline::Cmdline504 fn get_base_linux_cmdline() -> kernel_cmdline::Cmdline {
505 let mut cmdline = kernel_cmdline::Cmdline::new(base::pagesize());
506 cmdline.insert_str("panic=-1").unwrap();
507 cmdline
508 }
509
510 /// Returns a system resource allocator configuration.
511 ///
512 /// # Arguments
513 ///
514 /// * `mem_size` - Size of guest memory (RAM) in bytes.
515 /// * `guest_phys_addr_bits` - Size of guest physical addresses (IPA) in bits.
get_resource_allocator_config( mem_size: u64, guest_phys_addr_bits: u8, ) -> SystemAllocatorConfig516 fn get_resource_allocator_config(
517 mem_size: u64,
518 guest_phys_addr_bits: u8,
519 ) -> SystemAllocatorConfig {
520 let guest_phys_end = 1u64 << guest_phys_addr_bits;
521 // The platform MMIO region is immediately past the end of RAM.
522 let plat_mmio_base = AARCH64_PHYS_MEM_START + mem_size;
523 let plat_mmio_size = AARCH64_PLATFORM_MMIO_SIZE;
524 // The high MMIO region is the rest of the address space after the platform MMIO region.
525 let high_mmio_base = plat_mmio_base + plat_mmio_size;
526 let high_mmio_size = guest_phys_end
527 .checked_sub(high_mmio_base)
528 .unwrap_or_else(|| {
529 panic!(
530 "guest_phys_end {:#x} < high_mmio_base {:#x}",
531 guest_phys_end, high_mmio_base,
532 );
533 });
534 SystemAllocatorConfig {
535 io: None,
536 low_mmio: MemRegion {
537 base: AARCH64_MMIO_BASE,
538 size: AARCH64_MMIO_SIZE,
539 },
540 high_mmio: MemRegion {
541 base: high_mmio_base,
542 size: high_mmio_size,
543 },
544 platform_mmio: Some(MemRegion {
545 base: plat_mmio_base,
546 size: plat_mmio_size,
547 }),
548 first_irq: AARCH64_IRQ_BASE,
549 }
550 }
551
552 /// This adds any early platform devices for this architecture.
553 ///
554 /// # Arguments
555 ///
556 /// * `irq_chip` - The IRQ chip to add irqs to.
557 /// * `bus` - The bus to add devices to.
add_arch_devs(irq_chip: &mut dyn IrqChip, bus: &Bus) -> Result<()>558 fn add_arch_devs(irq_chip: &mut dyn IrqChip, bus: &Bus) -> Result<()> {
559 let rtc_evt = devices::IrqEdgeEvent::new().map_err(Error::CreateEvent)?;
560 irq_chip
561 .register_edge_irq_event(AARCH64_RTC_IRQ, &rtc_evt)
562 .map_err(Error::RegisterIrqfd)?;
563
564 let rtc = Arc::new(Mutex::new(devices::pl030::Pl030::new(rtc_evt)));
565 bus.insert(rtc, AARCH64_RTC_ADDR, AARCH64_RTC_SIZE)
566 .expect("failed to add rtc device");
567
568 Ok(())
569 }
570
571 /// Sets up `vcpu`.
572 ///
573 /// AArch64 needs vcpus set up before its kernel IRQ chip is created, so `configure_vcpu_early`
574 /// is called from `build_vm` on the main thread. `LinuxArch::configure_vcpu`, which is used
575 /// by X86_64 to do setup later from the vcpu thread, is a no-op on AArch64 since vcpus were
576 /// already configured here.
577 ///
578 /// # Arguments
579 ///
580 /// * `guest_mem` - The guest memory object.
581 /// * `vcpu` - The vcpu to configure.
582 /// * `vcpu_id` - The VM's index for `vcpu`.
583 /// * `use_pmu` - Should `vcpu` be configured to use the Performance Monitor Unit.
configure_vcpu_early( guest_mem: &GuestMemory, vcpu: &dyn VcpuAArch64, vcpu_id: usize, use_pmu: bool, has_bios: bool, image_size: usize, protected_vm: ProtectionType, ) -> Result<()>584 fn configure_vcpu_early(
585 guest_mem: &GuestMemory,
586 vcpu: &dyn VcpuAArch64,
587 vcpu_id: usize,
588 use_pmu: bool,
589 has_bios: bool,
590 image_size: usize,
591 protected_vm: ProtectionType,
592 ) -> Result<()> {
593 let mut features = vec![VcpuFeature::PsciV0_2];
594 if use_pmu {
595 features.push(VcpuFeature::PmuV3);
596 }
597 // Non-boot cpus are powered off initially
598 if vcpu_id != 0 {
599 features.push(VcpuFeature::PowerOff)
600 }
601 vcpu.init(&features).map_err(Error::VcpuInit)?;
602
603 // All interrupts masked
604 let pstate = PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | PSR_MODE_EL1H;
605 vcpu.set_one_reg(arm64_core_reg!(pstate), pstate)
606 .map_err(Error::SetReg)?;
607
608 // Other cpus are powered off initially
609 if vcpu_id == 0 {
610 let entry_addr = if has_bios {
611 get_bios_addr()
612 } else {
613 get_kernel_addr()
614 };
615 let entry_addr_reg_id = if protected_vm == ProtectionType::Protected {
616 arm64_core_reg!(regs, 1)
617 } else {
618 arm64_core_reg!(pc)
619 };
620 vcpu.set_one_reg(entry_addr_reg_id, entry_addr.offset())
621 .map_err(Error::SetReg)?;
622
623 /* X0 -- fdt address */
624 let mem_size = guest_mem.memory_size();
625 let fdt_addr = (AARCH64_PHYS_MEM_START + fdt_offset(mem_size, has_bios)) as u64;
626 vcpu.set_one_reg(arm64_core_reg!(regs, 0), fdt_addr)
627 .map_err(Error::SetReg)?;
628
629 /* X2 -- image size */
630 if protected_vm == ProtectionType::Protected {
631 vcpu.set_one_reg(arm64_core_reg!(regs, 2), image_size as u64)
632 .map_err(Error::SetReg)?;
633 }
634 }
635
636 Ok(())
637 }
638 }
639