1 // Copyright 2018 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 //! ARM 64-bit architecture support.
6
7 #![cfg(any(target_arch = "arm", target_arch = "aarch64"))]
8
9 use std::collections::BTreeMap;
10 use std::fs::File;
11 use std::io;
12 use std::path::PathBuf;
13 use std::sync::mpsc;
14 use std::sync::Arc;
15
16 use arch::get_serial_cmdline;
17 use arch::CpuSet;
18 use arch::DtbOverlay;
19 use arch::GetSerialCmdlineError;
20 use arch::RunnableLinuxVm;
21 use arch::VcpuAffinity;
22 use arch::VmComponents;
23 use arch::VmImage;
24 use base::Event;
25 use base::MemoryMappingBuilder;
26 use base::SendTube;
27 use devices::serial_device::SerialHardware;
28 use devices::serial_device::SerialParameters;
29 use devices::vmwdt::VMWDT_DEFAULT_CLOCK_HZ;
30 use devices::vmwdt::VMWDT_DEFAULT_TIMEOUT_SEC;
31 use devices::Bus;
32 use devices::BusDeviceObj;
33 use devices::BusError;
34 use devices::BusType;
35 use devices::IrqChip;
36 use devices::IrqChipAArch64;
37 use devices::IrqEventSource;
38 use devices::PciAddress;
39 use devices::PciConfigMmio;
40 use devices::PciDevice;
41 use devices::PciRootCommand;
42 use devices::Serial;
43 #[cfg(any(target_os = "android", target_os = "linux"))]
44 use devices::VirtCpufreq;
45 #[cfg(feature = "gdb")]
46 use gdbstub::arch::Arch;
47 #[cfg(feature = "gdb")]
48 use gdbstub_arch::aarch64::AArch64 as GdbArch;
49 use hypervisor::CpuConfigAArch64;
50 use hypervisor::DeviceKind;
51 use hypervisor::Hypervisor;
52 use hypervisor::HypervisorCap;
53 use hypervisor::MemCacheType;
54 use hypervisor::ProtectionType;
55 use hypervisor::VcpuAArch64;
56 use hypervisor::VcpuFeature;
57 use hypervisor::VcpuInitAArch64;
58 use hypervisor::VcpuRegAArch64;
59 use hypervisor::Vm;
60 use hypervisor::VmAArch64;
61 #[cfg(windows)]
62 use jail::FakeMinijailStub as Minijail;
63 use kernel_loader::LoadedKernel;
64 #[cfg(any(target_os = "android", target_os = "linux"))]
65 use minijail::Minijail;
66 use remain::sorted;
67 use resources::AddressRange;
68 use resources::SystemAllocator;
69 use resources::SystemAllocatorConfig;
70 #[cfg(any(target_os = "android", target_os = "linux"))]
71 use sync::Condvar;
72 use sync::Mutex;
73 use thiserror::Error;
74 use vm_control::BatControl;
75 use vm_control::BatteryType;
76 use vm_memory::GuestAddress;
77 use vm_memory::GuestMemory;
78 use vm_memory::GuestMemoryError;
79 use vm_memory::MemoryRegionOptions;
80 use vm_memory::MemoryRegionPurpose;
81
82 mod fdt;
83
84 // We place the kernel at the very beginning of physical memory.
85 const AARCH64_KERNEL_OFFSET: u64 = 0;
86 const AARCH64_FDT_MAX_SIZE: u64 = 0x200000;
87 const AARCH64_INITRD_ALIGN: u64 = 0x1000000;
88
89 // These constants indicate the address space used by the ARM vGIC.
90 const AARCH64_GIC_DIST_SIZE: u64 = 0x10000;
91 const AARCH64_GIC_CPUI_SIZE: u64 = 0x20000;
92
93 // This indicates the start of DRAM inside the physical address space.
94 const AARCH64_PHYS_MEM_START: u64 = 0x80000000;
95 const AARCH64_AXI_BASE: u64 = 0x40000000;
96 const AARCH64_PLATFORM_MMIO_SIZE: u64 = 0x800000;
97
98 // FDT is placed at the front of RAM when booting in BIOS mode.
99 const AARCH64_FDT_OFFSET_IN_BIOS_MODE: u64 = 0x0;
100 // Therefore, the BIOS is placed after the FDT in memory.
101 const AARCH64_BIOS_OFFSET: u64 = AARCH64_FDT_MAX_SIZE;
102 const AARCH64_BIOS_MAX_LEN: u64 = 1 << 20;
103
104 const AARCH64_PROTECTED_VM_FW_MAX_SIZE: u64 = 0x400000;
105 const AARCH64_PROTECTED_VM_FW_START: u64 =
106 AARCH64_PHYS_MEM_START - AARCH64_PROTECTED_VM_FW_MAX_SIZE;
107
108 const AARCH64_PVTIME_IPA_MAX_SIZE: u64 = 0x10000;
109 const AARCH64_PVTIME_IPA_START: u64 = AARCH64_MMIO_BASE - AARCH64_PVTIME_IPA_MAX_SIZE;
110 const AARCH64_PVTIME_SIZE: u64 = 64;
111
112 // These constants indicate the placement of the GIC registers in the physical
113 // address space.
114 const AARCH64_GIC_DIST_BASE: u64 = AARCH64_AXI_BASE - AARCH64_GIC_DIST_SIZE;
115 const AARCH64_GIC_CPUI_BASE: u64 = AARCH64_GIC_DIST_BASE - AARCH64_GIC_CPUI_SIZE;
116 const AARCH64_GIC_REDIST_SIZE: u64 = 0x20000;
117
118 // PSR (Processor State Register) bits
119 const PSR_MODE_EL1H: u64 = 0x00000005;
120 const PSR_F_BIT: u64 = 0x00000040;
121 const PSR_I_BIT: u64 = 0x00000080;
122 const PSR_A_BIT: u64 = 0x00000100;
123 const PSR_D_BIT: u64 = 0x00000200;
124
125 enum PayloadType {
126 Bios {
127 entry: GuestAddress,
128 image_size: u64,
129 },
130 Kernel(LoadedKernel),
131 }
132
133 impl PayloadType {
entry(&self) -> GuestAddress134 fn entry(&self) -> GuestAddress {
135 match self {
136 Self::Bios {
137 entry,
138 image_size: _,
139 } => *entry,
140 Self::Kernel(k) => k.entry,
141 }
142 }
143
size(&self) -> u64144 fn size(&self) -> u64 {
145 match self {
146 Self::Bios {
147 entry: _,
148 image_size,
149 } => *image_size,
150 Self::Kernel(k) => k.size,
151 }
152 }
153 }
154
get_kernel_addr() -> GuestAddress155 fn get_kernel_addr() -> GuestAddress {
156 GuestAddress(AARCH64_PHYS_MEM_START + AARCH64_KERNEL_OFFSET)
157 }
158
get_bios_addr() -> GuestAddress159 fn get_bios_addr() -> GuestAddress {
160 GuestAddress(AARCH64_PHYS_MEM_START + AARCH64_BIOS_OFFSET)
161 }
162
163 // When static swiotlb allocation is required, returns the address it should be allocated at.
164 // Otherwise, returns None.
get_swiotlb_addr( memory_size: u64, hypervisor: &(impl Hypervisor + ?Sized), ) -> Option<GuestAddress>165 fn get_swiotlb_addr(
166 memory_size: u64,
167 hypervisor: &(impl Hypervisor + ?Sized),
168 ) -> Option<GuestAddress> {
169 if hypervisor.check_capability(HypervisorCap::StaticSwiotlbAllocationRequired) {
170 Some(GuestAddress(AARCH64_PHYS_MEM_START + memory_size))
171 } else {
172 None
173 }
174 }
175
176 // This was the speed kvmtool used, not sure if it matters.
177 const AARCH64_SERIAL_SPEED: u32 = 1843200;
178 // The serial device gets the first interrupt line
179 // Which gets mapped to the first SPI interrupt (physical 32).
180 const AARCH64_SERIAL_1_3_IRQ: u32 = 0;
181 const AARCH64_SERIAL_2_4_IRQ: u32 = 2;
182
183 // Place the RTC device at page 2
184 const AARCH64_RTC_ADDR: u64 = 0x2000;
185 // The RTC device gets one 4k page
186 const AARCH64_RTC_SIZE: u64 = 0x1000;
187 // The RTC device gets the second interrupt line
188 const AARCH64_RTC_IRQ: u32 = 1;
189
190 // The Goldfish battery device gets the 3rd interrupt line
191 const AARCH64_BAT_IRQ: u32 = 3;
192
193 // Place the virtual watchdog device at page 3
194 const AARCH64_VMWDT_ADDR: u64 = 0x3000;
195 // The virtual watchdog device gets one 4k page
196 const AARCH64_VMWDT_SIZE: u64 = 0x1000;
197
198 // PCI MMIO configuration region base address.
199 const AARCH64_PCI_CFG_BASE: u64 = 0x10000;
200 // PCI MMIO configuration region size.
201 const AARCH64_PCI_CFG_SIZE: u64 = 0x1000000;
202 // This is the base address of MMIO devices.
203 const AARCH64_MMIO_BASE: u64 = 0x2000000;
204 // Size of the whole MMIO region.
205 const AARCH64_MMIO_SIZE: u64 = 0x2000000;
206 // Virtio devices start at SPI interrupt number 4
207 const AARCH64_IRQ_BASE: u32 = 4;
208
209 // Virtual CPU Frequency Device.
210 const AARCH64_VIRTFREQ_BASE: u64 = 0x1040000;
211 const AARCH64_VIRTFREQ_SIZE: u64 = 0x8;
212 const AARCH64_VIRTFREQ_MAXSIZE: u64 = 0x10000;
213
214 // PMU PPI interrupt, same as qemu
215 const AARCH64_PMU_IRQ: u32 = 7;
216
217 #[sorted]
218 #[derive(Error, Debug)]
219 pub enum Error {
220 #[error("failed to allocate IRQ number")]
221 AllocateIrq,
222 #[error("bios could not be loaded: {0}")]
223 BiosLoadFailure(arch::LoadImageError),
224 #[error("failed to build arm pvtime memory: {0}")]
225 BuildPvtimeError(base::MmapError),
226 #[error("unable to clone an Event: {0}")]
227 CloneEvent(base::Error),
228 #[error("failed to clone IRQ chip: {0}")]
229 CloneIrqChip(base::Error),
230 #[error("the given kernel command line was invalid: {0}")]
231 Cmdline(kernel_cmdline::Error),
232 #[error("failed to configure CPU Frequencies: {0}")]
233 CpuFrequencies(base::Error),
234 #[error("failed to configure CPU topology: {0}")]
235 CpuTopology(base::Error),
236 #[error("unable to create battery devices: {0}")]
237 CreateBatDevices(arch::DeviceRegistrationError),
238 #[error("unable to make an Event: {0}")]
239 CreateEvent(base::Error),
240 #[error("FDT could not be created: {0}")]
241 CreateFdt(cros_fdt::Error),
242 #[error("failed to create GIC: {0}")]
243 CreateGICFailure(base::Error),
244 #[error("failed to create a PCI root hub: {0}")]
245 CreatePciRoot(arch::DeviceRegistrationError),
246 #[error("failed to create platform bus: {0}")]
247 CreatePlatformBus(arch::DeviceRegistrationError),
248 #[error("unable to create serial devices: {0}")]
249 CreateSerialDevices(arch::DeviceRegistrationError),
250 #[error("failed to create socket: {0}")]
251 CreateSocket(io::Error),
252 #[error("failed to create VCPU: {0}")]
253 CreateVcpu(base::Error),
254 #[error("custom pVM firmware could not be loaded: {0}")]
255 CustomPvmFwLoadFailure(arch::LoadImageError),
256 #[error("vm created wrong kind of vcpu")]
257 DowncastVcpu,
258 #[error("failed to enable singlestep execution: {0}")]
259 EnableSinglestep(base::Error),
260 #[error("failed to finalize IRQ chip: {0}")]
261 FinalizeIrqChip(base::Error),
262 #[error("failed to get HW breakpoint count: {0}")]
263 GetMaxHwBreakPoint(base::Error),
264 #[error("failed to get PSCI version: {0}")]
265 GetPsciVersion(base::Error),
266 #[error("failed to get serial cmdline: {0}")]
267 GetSerialCmdline(GetSerialCmdlineError),
268 #[error("failed to initialize arm pvtime: {0}")]
269 InitPvtimeError(base::Error),
270 #[error("initrd could not be loaded: {0}")]
271 InitrdLoadFailure(arch::LoadImageError),
272 #[error("failed to initialize virtual machine {0}")]
273 InitVmError(base::Error),
274 #[error("kernel could not be loaded: {0}")]
275 KernelLoadFailure(kernel_loader::Error),
276 #[error("error loading Kernel from Elf image: {0}")]
277 LoadElfKernel(kernel_loader::Error),
278 #[error("failed to map arm pvtime memory: {0}")]
279 MapPvtimeError(base::Error),
280 #[error("pVM firmware could not be loaded: {0}")]
281 PvmFwLoadFailure(base::Error),
282 #[error("ramoops address is different from high_mmio_base: {0} vs {1}")]
283 RamoopsAddress(u64, u64),
284 #[error("error reading guest memory: {0}")]
285 ReadGuestMemory(vm_memory::GuestMemoryError),
286 #[error("error reading CPU register: {0}")]
287 ReadReg(base::Error),
288 #[error("error reading CPU registers: {0}")]
289 ReadRegs(base::Error),
290 #[error("failed to register irq fd: {0}")]
291 RegisterIrqfd(base::Error),
292 #[error("error registering PCI bus: {0}")]
293 RegisterPci(BusError),
294 #[error("error registering virtual cpufreq device: {0}")]
295 RegisterVirtCpufreq(BusError),
296 #[error("error registering virtual socket device: {0}")]
297 RegisterVsock(arch::DeviceRegistrationError),
298 #[error("failed to set device attr: {0}")]
299 SetDeviceAttr(base::Error),
300 #[error("failed to set a hardware breakpoint: {0}")]
301 SetHwBreakpoint(base::Error),
302 #[error("failed to set register: {0}")]
303 SetReg(base::Error),
304 #[error("failed to set up guest memory: {0}")]
305 SetupGuestMemory(GuestMemoryError),
306 #[error("this function isn't supported")]
307 Unsupported,
308 #[error("failed to initialize VCPU: {0}")]
309 VcpuInit(base::Error),
310 #[error("error writing guest memory: {0}")]
311 WriteGuestMemory(GuestMemoryError),
312 #[error("error writing CPU register: {0}")]
313 WriteReg(base::Error),
314 #[error("error writing CPU registers: {0}")]
315 WriteRegs(base::Error),
316 }
317
318 pub type Result<T> = std::result::Result<T, Error>;
319
320 /// Returns the address in guest memory at which the FDT should be located.
fdt_address(memory_end: GuestAddress, has_bios: bool) -> GuestAddress321 fn fdt_address(memory_end: GuestAddress, has_bios: bool) -> GuestAddress {
322 // TODO(rammuthiah) make kernel and BIOS startup use FDT from the same location. ARCVM startup
323 // currently expects the kernel at 0x80080000 and the FDT at the end of RAM for unknown reasons.
324 // Root cause and figure out how to fold these code paths together.
325 if has_bios {
326 GuestAddress(AARCH64_PHYS_MEM_START + AARCH64_FDT_OFFSET_IN_BIOS_MODE)
327 } else {
328 // Put fdt up near the top of memory
329 // TODO(sonnyrao): will have to handle this differently if there's
330 // > 4GB memory
331 memory_end
332 .checked_sub(AARCH64_FDT_MAX_SIZE)
333 .expect("Not enough memory for FDT")
334 .checked_sub(0x10000)
335 .expect("Not enough memory for FDT")
336 }
337 }
338
load_kernel( guest_mem: &GuestMemory, kernel_start: GuestAddress, mut kernel_image: &mut File, ) -> Result<LoadedKernel>339 fn load_kernel(
340 guest_mem: &GuestMemory,
341 kernel_start: GuestAddress,
342 mut kernel_image: &mut File,
343 ) -> Result<LoadedKernel> {
344 if let Ok(elf_kernel) = kernel_loader::load_elf(
345 guest_mem,
346 kernel_start,
347 &mut kernel_image,
348 AARCH64_PHYS_MEM_START,
349 ) {
350 return Ok(elf_kernel);
351 }
352
353 if let Ok(lz4_kernel) =
354 kernel_loader::load_arm64_kernel_lz4(guest_mem, kernel_start, &mut kernel_image)
355 {
356 return Ok(lz4_kernel);
357 }
358
359 kernel_loader::load_arm64_kernel(guest_mem, kernel_start, kernel_image)
360 .map_err(Error::KernelLoadFailure)
361 }
362
363 pub struct AArch64;
364
get_block_size() -> u64365 fn get_block_size() -> u64 {
366 let page_size = base::pagesize();
367 // Each PTE entry being 8 bytes long, we can fit in one page (page_size / 8)
368 // entries.
369 let ptes_per_page = page_size / 8;
370 let block_size = page_size * ptes_per_page;
371
372 block_size as u64
373 }
374
get_vcpu_mpidr_aff<Vcpu: VcpuAArch64>(vcpus: &[Vcpu], index: usize) -> Option<u64>375 fn get_vcpu_mpidr_aff<Vcpu: VcpuAArch64>(vcpus: &[Vcpu], index: usize) -> Option<u64> {
376 const MPIDR_AFF_MASK: u64 = 0xff_00ff_ffff;
377
378 Some(vcpus.get(index)?.get_mpidr().ok()? & MPIDR_AFF_MASK)
379 }
380
381 impl arch::LinuxArch for AArch64 {
382 type Error = Error;
383
384 /// Returns a Vec of the valid memory addresses.
385 /// These should be used to configure the GuestMemory structure for the platform.
guest_memory_layout( components: &VmComponents, hypervisor: &impl Hypervisor, ) -> std::result::Result<Vec<(GuestAddress, u64, MemoryRegionOptions)>, Self::Error>386 fn guest_memory_layout(
387 components: &VmComponents,
388 hypervisor: &impl Hypervisor,
389 ) -> std::result::Result<Vec<(GuestAddress, u64, MemoryRegionOptions)>, Self::Error> {
390 let mut memory_regions = vec![(
391 GuestAddress(AARCH64_PHYS_MEM_START),
392 components.memory_size,
393 MemoryRegionOptions::new().align(get_block_size()),
394 )];
395
396 // Allocate memory for the pVM firmware.
397 if components.hv_cfg.protection_type.runs_firmware() {
398 memory_regions.push((
399 GuestAddress(AARCH64_PROTECTED_VM_FW_START),
400 AARCH64_PROTECTED_VM_FW_MAX_SIZE,
401 MemoryRegionOptions::new().purpose(MemoryRegionPurpose::ProtectedFirmwareRegion),
402 ));
403 }
404
405 if let Some(size) = components.swiotlb {
406 if let Some(addr) = get_swiotlb_addr(components.memory_size, hypervisor) {
407 memory_regions.push((
408 addr,
409 size,
410 MemoryRegionOptions::new().purpose(MemoryRegionPurpose::StaticSwiotlbRegion),
411 ));
412 }
413 }
414
415 Ok(memory_regions)
416 }
417
get_system_allocator_config<V: Vm>(vm: &V) -> SystemAllocatorConfig418 fn get_system_allocator_config<V: Vm>(vm: &V) -> SystemAllocatorConfig {
419 Self::get_resource_allocator_config(
420 vm.get_memory().end_addr(),
421 vm.get_guest_phys_addr_bits(),
422 )
423 }
424
build_vm<V, Vcpu>( mut components: VmComponents, _vm_evt_wrtube: &SendTube, system_allocator: &mut SystemAllocator, serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>, serial_jail: Option<Minijail>, (bat_type, bat_jail): (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, vcpu_ids: &mut Vec<usize>, dump_device_tree_blob: Option<PathBuf>, _debugcon_jail: Option<Minijail>, #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>, #[cfg(any(target_os = "android", target_os = "linux"))] _guest_suspended_cvar: Option< Arc<(Mutex<bool>, Condvar)>, >, device_tree_overlays: Vec<DtbOverlay>, ) -> std::result::Result<RunnableLinuxVm<V, Vcpu>, Self::Error> where V: VmAArch64, Vcpu: VcpuAArch64,425 fn build_vm<V, Vcpu>(
426 mut components: VmComponents,
427 _vm_evt_wrtube: &SendTube,
428 system_allocator: &mut SystemAllocator,
429 serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
430 serial_jail: Option<Minijail>,
431 (bat_type, bat_jail): (Option<BatteryType>, Option<Minijail>),
432 mut vm: V,
433 ramoops_region: Option<arch::pstore::RamoopsRegion>,
434 devs: Vec<(Box<dyn BusDeviceObj>, Option<Minijail>)>,
435 irq_chip: &mut dyn IrqChipAArch64,
436 vcpu_ids: &mut Vec<usize>,
437 dump_device_tree_blob: Option<PathBuf>,
438 _debugcon_jail: Option<Minijail>,
439 #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>,
440 #[cfg(any(target_os = "android", target_os = "linux"))] _guest_suspended_cvar: Option<
441 Arc<(Mutex<bool>, Condvar)>,
442 >,
443 device_tree_overlays: Vec<DtbOverlay>,
444 ) -> std::result::Result<RunnableLinuxVm<V, Vcpu>, Self::Error>
445 where
446 V: VmAArch64,
447 Vcpu: VcpuAArch64,
448 {
449 let has_bios = matches!(components.vm_image, VmImage::Bios(_));
450 let mem = vm.get_memory().clone();
451
452 // separate out image loading from other setup to get a specific error for
453 // image loading
454 let mut initrd = None;
455 let payload = match components.vm_image {
456 VmImage::Bios(ref mut bios) => {
457 let image_size =
458 arch::load_image(&mem, bios, get_bios_addr(), AARCH64_BIOS_MAX_LEN)
459 .map_err(Error::BiosLoadFailure)?;
460 PayloadType::Bios {
461 entry: get_bios_addr(),
462 image_size: image_size as u64,
463 }
464 }
465 VmImage::Kernel(ref mut kernel_image) => {
466 let loaded_kernel = load_kernel(&mem, get_kernel_addr(), kernel_image)?;
467 let kernel_end = loaded_kernel.address_range.end;
468 initrd = match components.initrd_image {
469 Some(initrd_file) => {
470 let mut initrd_file = initrd_file;
471 let initrd_addr =
472 (kernel_end + (AARCH64_INITRD_ALIGN - 1)) & !(AARCH64_INITRD_ALIGN - 1);
473 let initrd_max_size =
474 components.memory_size - (initrd_addr - AARCH64_PHYS_MEM_START);
475 let initrd_addr = GuestAddress(initrd_addr);
476 let initrd_size =
477 arch::load_image(&mem, &mut initrd_file, initrd_addr, initrd_max_size)
478 .map_err(Error::InitrdLoadFailure)?;
479 Some((initrd_addr, initrd_size))
480 }
481 None => None,
482 };
483 PayloadType::Kernel(loaded_kernel)
484 }
485 };
486
487 let memory_end = GuestAddress(AARCH64_PHYS_MEM_START + components.memory_size);
488 let fdt_offset = fdt_address(memory_end, has_bios);
489
490 let mut use_pmu = vm
491 .get_hypervisor()
492 .check_capability(HypervisorCap::ArmPmuV3);
493 let vcpu_count = components.vcpu_count;
494 let mut has_pvtime = true;
495 let mut vcpus = Vec::with_capacity(vcpu_count);
496 let mut vcpu_init = Vec::with_capacity(vcpu_count);
497 for vcpu_id in 0..vcpu_count {
498 let vcpu: Vcpu = *vm
499 .create_vcpu(vcpu_id)
500 .map_err(Error::CreateVcpu)?
501 .downcast::<Vcpu>()
502 .map_err(|_| Error::DowncastVcpu)?;
503 let per_vcpu_init = if vm
504 .get_hypervisor()
505 .check_capability(HypervisorCap::HypervisorInitializedBootContext)
506 {
507 // No registers are initialized: VcpuInitAArch64.regs is an empty BTreeMap
508 Default::default()
509 } else {
510 Self::vcpu_init(
511 vcpu_id,
512 &payload,
513 fdt_offset,
514 components.hv_cfg.protection_type,
515 components.boot_cpu,
516 )
517 };
518 has_pvtime &= vcpu.has_pvtime_support();
519 vcpus.push(vcpu);
520 vcpu_ids.push(vcpu_id);
521 vcpu_init.push(per_vcpu_init);
522 }
523
524 // Initialize Vcpus after all Vcpu objects have been created.
525 for (vcpu_id, vcpu) in vcpus.iter().enumerate() {
526 vcpu.init(&Self::vcpu_features(vcpu_id, use_pmu, components.boot_cpu))
527 .map_err(Error::VcpuInit)?;
528 }
529
530 irq_chip.finalize().map_err(Error::FinalizeIrqChip)?;
531
532 if has_pvtime {
533 let pvtime_mem = MemoryMappingBuilder::new(AARCH64_PVTIME_IPA_MAX_SIZE as usize)
534 .build()
535 .map_err(Error::BuildPvtimeError)?;
536 vm.add_memory_region(
537 GuestAddress(AARCH64_PVTIME_IPA_START),
538 Box::new(pvtime_mem),
539 false,
540 false,
541 MemCacheType::CacheCoherent,
542 )
543 .map_err(Error::MapPvtimeError)?;
544 }
545
546 if components.hv_cfg.protection_type.loads_firmware() {
547 arch::load_image(
548 &mem,
549 &mut components
550 .pvm_fw
551 .expect("pvmfw must be available if ProtectionType loads it"),
552 GuestAddress(AARCH64_PROTECTED_VM_FW_START),
553 AARCH64_PROTECTED_VM_FW_MAX_SIZE,
554 )
555 .map_err(Error::CustomPvmFwLoadFailure)?;
556 } else if components.hv_cfg.protection_type.runs_firmware() {
557 // Tell the hypervisor to load the pVM firmware.
558 vm.load_protected_vm_firmware(
559 GuestAddress(AARCH64_PROTECTED_VM_FW_START),
560 AARCH64_PROTECTED_VM_FW_MAX_SIZE,
561 )
562 .map_err(Error::PvmFwLoadFailure)?;
563 }
564
565 for (vcpu_id, vcpu) in vcpus.iter().enumerate() {
566 use_pmu &= vcpu.init_pmu(AARCH64_PMU_IRQ as u64 + 16).is_ok();
567 if has_pvtime {
568 vcpu.init_pvtime(AARCH64_PVTIME_IPA_START + (vcpu_id as u64 * AARCH64_PVTIME_SIZE))
569 .map_err(Error::InitPvtimeError)?;
570 }
571 }
572
573 let mmio_bus = Arc::new(devices::Bus::new(BusType::Mmio));
574
575 // ARM doesn't really use the io bus like x86, so just create an empty bus.
576 let io_bus = Arc::new(devices::Bus::new(BusType::Io));
577
578 // Event used by PMDevice to notify crosvm that
579 // guest OS is trying to suspend.
580 let suspend_evt = Event::new().map_err(Error::CreateEvent)?;
581
582 let (pci_devices, others): (Vec<_>, Vec<_>) = devs
583 .into_iter()
584 .partition(|(dev, _)| dev.as_pci_device().is_some());
585
586 let pci_devices = pci_devices
587 .into_iter()
588 .map(|(dev, jail_orig)| (dev.into_pci_device().unwrap(), jail_orig))
589 .collect();
590 let (pci, pci_irqs, mut pid_debug_label_map, _amls, _gpe_scope_amls) =
591 arch::generate_pci_root(
592 pci_devices,
593 irq_chip.as_irq_chip_mut(),
594 mmio_bus.clone(),
595 GuestAddress(AARCH64_PCI_CFG_BASE),
596 8,
597 io_bus.clone(),
598 system_allocator,
599 &mut vm,
600 (devices::AARCH64_GIC_NR_SPIS - AARCH64_IRQ_BASE) as usize,
601 None,
602 #[cfg(feature = "swap")]
603 swap_controller,
604 )
605 .map_err(Error::CreatePciRoot)?;
606
607 let pci_root = Arc::new(Mutex::new(pci));
608 let pci_bus = Arc::new(Mutex::new(PciConfigMmio::new(pci_root.clone(), 8)));
609 let (platform_devices, _others): (Vec<_>, Vec<_>) = others
610 .into_iter()
611 .partition(|(dev, _)| dev.as_platform_device().is_some());
612
613 let platform_devices = platform_devices
614 .into_iter()
615 .map(|(dev, jail_orig)| (*(dev.into_platform_device().unwrap()), jail_orig))
616 .collect();
617 let (platform_devices, mut platform_pid_debug_label_map, dev_resources) =
618 arch::sys::linux::generate_platform_bus(
619 platform_devices,
620 irq_chip.as_irq_chip_mut(),
621 &mmio_bus,
622 system_allocator,
623 &mut vm,
624 #[cfg(feature = "swap")]
625 swap_controller,
626 components.hv_cfg.protection_type,
627 )
628 .map_err(Error::CreatePlatformBus)?;
629 pid_debug_label_map.append(&mut platform_pid_debug_label_map);
630
631 Self::add_arch_devs(
632 irq_chip.as_irq_chip_mut(),
633 &mmio_bus,
634 vcpu_count,
635 _vm_evt_wrtube,
636 )?;
637
638 let com_evt_1_3 = devices::IrqEdgeEvent::new().map_err(Error::CreateEvent)?;
639 let com_evt_2_4 = devices::IrqEdgeEvent::new().map_err(Error::CreateEvent)?;
640 let serial_devices = arch::add_serial_devices(
641 components.hv_cfg.protection_type,
642 &mmio_bus,
643 (AARCH64_SERIAL_1_3_IRQ, com_evt_1_3.get_trigger()),
644 (AARCH64_SERIAL_2_4_IRQ, com_evt_2_4.get_trigger()),
645 serial_parameters,
646 serial_jail,
647 #[cfg(feature = "swap")]
648 swap_controller,
649 )
650 .map_err(Error::CreateSerialDevices)?;
651
652 let source = IrqEventSource {
653 device_id: Serial::device_id(),
654 queue_id: 0,
655 device_name: Serial::debug_label(),
656 };
657 irq_chip
658 .register_edge_irq_event(AARCH64_SERIAL_1_3_IRQ, &com_evt_1_3, source.clone())
659 .map_err(Error::RegisterIrqfd)?;
660 irq_chip
661 .register_edge_irq_event(AARCH64_SERIAL_2_4_IRQ, &com_evt_2_4, source)
662 .map_err(Error::RegisterIrqfd)?;
663
664 mmio_bus
665 .insert(pci_bus, AARCH64_PCI_CFG_BASE, AARCH64_PCI_CFG_SIZE)
666 .map_err(Error::RegisterPci)?;
667
668 #[cfg(any(target_os = "android", target_os = "linux"))]
669 if !components.cpu_frequencies.is_empty() {
670 // TODO: Revisit and optimization after benchmarking
671 let socket = components
672 .virt_cpufreq_socket
673 .map(|s| Arc::new(Mutex::new(s)));
674 for vcpu in 0..vcpu_count {
675 let vcpu_affinity = match components.vcpu_affinity.clone() {
676 Some(VcpuAffinity::Global(v)) => v,
677 Some(VcpuAffinity::PerVcpu(mut m)) => m.remove(&vcpu).unwrap_or_default(),
678 None => panic!("vcpu_affinity needs to be set for VirtCpufreq"),
679 };
680
681 let virt_cpufreq = Arc::new(Mutex::new(VirtCpufreq::new(
682 vcpu_affinity[0].try_into().unwrap(),
683 socket.clone(),
684 )));
685
686 if vcpu as u64 * AARCH64_VIRTFREQ_SIZE + AARCH64_VIRTFREQ_SIZE
687 > AARCH64_VIRTFREQ_MAXSIZE
688 {
689 panic!("Exceeded maximum number of virt cpufreq devices");
690 }
691
692 mmio_bus
693 .insert(
694 virt_cpufreq,
695 AARCH64_VIRTFREQ_BASE + (vcpu as u64 * AARCH64_VIRTFREQ_SIZE),
696 AARCH64_VIRTFREQ_SIZE,
697 )
698 .map_err(Error::RegisterVirtCpufreq)?;
699 }
700 }
701
702 let mut cmdline = Self::get_base_linux_cmdline();
703 get_serial_cmdline(&mut cmdline, serial_parameters, "mmio", &serial_devices)
704 .map_err(Error::GetSerialCmdline)?;
705 for param in components.extra_kernel_params {
706 cmdline.insert_str(¶m).map_err(Error::Cmdline)?;
707 }
708
709 if let Some(ramoops_region) = ramoops_region {
710 arch::pstore::add_ramoops_kernel_cmdline(&mut cmdline, &ramoops_region)
711 .map_err(Error::Cmdline)?;
712 }
713
714 let psci_version = vcpus[0].get_psci_version().map_err(Error::GetPsciVersion)?;
715
716 let pci_cfg = fdt::PciConfigRegion {
717 base: AARCH64_PCI_CFG_BASE,
718 size: AARCH64_PCI_CFG_SIZE,
719 };
720
721 let pci_ranges: Vec<fdt::PciRange> = system_allocator
722 .mmio_pools()
723 .iter()
724 .map(|range| fdt::PciRange {
725 space: fdt::PciAddressSpace::Memory64,
726 bus_address: range.start,
727 cpu_physical_address: range.start,
728 size: range.len().unwrap(),
729 prefetchable: false,
730 })
731 .collect();
732
733 let (bat_control, bat_mmio_base_and_irq) = match bat_type {
734 Some(BatteryType::Goldfish) => {
735 let bat_irq = AARCH64_BAT_IRQ;
736
737 // a dummy AML buffer. Aarch64 crosvm doesn't use ACPI.
738 let mut amls = Vec::new();
739 let (control_tube, mmio_base) = arch::sys::linux::add_goldfish_battery(
740 &mut amls,
741 bat_jail,
742 &mmio_bus,
743 irq_chip.as_irq_chip_mut(),
744 bat_irq,
745 system_allocator,
746 #[cfg(feature = "swap")]
747 swap_controller,
748 )
749 .map_err(Error::CreateBatDevices)?;
750 (
751 Some(BatControl {
752 type_: BatteryType::Goldfish,
753 control_tube,
754 }),
755 Some((mmio_base, bat_irq)),
756 )
757 }
758 None => (None, None),
759 };
760
761 let vmwdt_cfg = fdt::VmWdtConfig {
762 base: AARCH64_VMWDT_ADDR,
763 size: AARCH64_VMWDT_SIZE,
764 clock_hz: VMWDT_DEFAULT_CLOCK_HZ,
765 timeout_sec: VMWDT_DEFAULT_TIMEOUT_SEC,
766 };
767
768 fdt::create_fdt(
769 AARCH64_FDT_MAX_SIZE as usize,
770 &mem,
771 pci_irqs,
772 pci_cfg,
773 &pci_ranges,
774 dev_resources,
775 vcpu_count as u32,
776 &|n| get_vcpu_mpidr_aff(&vcpus, n),
777 components.cpu_clusters,
778 components.cpu_capacity,
779 components.cpu_frequencies,
780 fdt_offset,
781 cmdline.as_str(),
782 (payload.entry(), payload.size() as usize),
783 initrd,
784 components.android_fstab,
785 irq_chip.get_vgic_version() == DeviceKind::ArmVgicV3,
786 use_pmu,
787 psci_version,
788 components.swiotlb.map(|size| {
789 (
790 get_swiotlb_addr(components.memory_size, vm.get_hypervisor()),
791 size,
792 )
793 }),
794 bat_mmio_base_and_irq,
795 vmwdt_cfg,
796 dump_device_tree_blob,
797 &|writer, phandles| vm.create_fdt(writer, phandles),
798 components.dynamic_power_coefficient,
799 device_tree_overlays,
800 &serial_devices,
801 )
802 .map_err(Error::CreateFdt)?;
803
804 vm.init_arch(
805 payload.entry(),
806 fdt_offset,
807 AARCH64_FDT_MAX_SIZE.try_into().unwrap(),
808 )
809 .map_err(Error::InitVmError)?;
810
811 Ok(RunnableLinuxVm {
812 vm,
813 vcpu_count,
814 vcpus: Some(vcpus),
815 vcpu_init,
816 vcpu_affinity: components.vcpu_affinity,
817 no_smt: components.no_smt,
818 irq_chip: irq_chip.try_box_clone().map_err(Error::CloneIrqChip)?,
819 io_bus,
820 mmio_bus,
821 pid_debug_label_map,
822 suspend_evt,
823 rt_cpus: components.rt_cpus,
824 delay_rt: components.delay_rt,
825 bat_control,
826 #[cfg(feature = "gdb")]
827 gdb: components.gdb,
828 pm: None,
829 resume_notify_devices: Vec::new(),
830 root_config: pci_root,
831 platform_devices,
832 hotplug_bus: BTreeMap::new(),
833 devices_thread: None,
834 vm_request_tube: None,
835 })
836 }
837
configure_vcpu<V: Vm>( _vm: &V, _hypervisor: &dyn Hypervisor, _irq_chip: &mut dyn IrqChipAArch64, vcpu: &mut dyn VcpuAArch64, vcpu_init: VcpuInitAArch64, _vcpu_id: usize, _num_cpus: usize, _cpu_config: Option<CpuConfigAArch64>, ) -> std::result::Result<(), Self::Error>838 fn configure_vcpu<V: Vm>(
839 _vm: &V,
840 _hypervisor: &dyn Hypervisor,
841 _irq_chip: &mut dyn IrqChipAArch64,
842 vcpu: &mut dyn VcpuAArch64,
843 vcpu_init: VcpuInitAArch64,
844 _vcpu_id: usize,
845 _num_cpus: usize,
846 _cpu_config: Option<CpuConfigAArch64>,
847 ) -> std::result::Result<(), Self::Error> {
848 for (reg, value) in vcpu_init.regs.iter() {
849 vcpu.set_one_reg(*reg, *value).map_err(Error::SetReg)?;
850 }
851 Ok(())
852 }
853
register_pci_device<V: VmAArch64, Vcpu: VcpuAArch64>( _linux: &mut RunnableLinuxVm<V, Vcpu>, _device: Box<dyn PciDevice>, _minijail: Option<Minijail>, _resources: &mut SystemAllocator, _tube: &mpsc::Sender<PciRootCommand>, #[cfg(feature = "swap")] _swap_controller: &mut Option<swap::SwapController>, ) -> std::result::Result<PciAddress, Self::Error>854 fn register_pci_device<V: VmAArch64, Vcpu: VcpuAArch64>(
855 _linux: &mut RunnableLinuxVm<V, Vcpu>,
856 _device: Box<dyn PciDevice>,
857 _minijail: Option<Minijail>,
858 _resources: &mut SystemAllocator,
859 _tube: &mpsc::Sender<PciRootCommand>,
860 #[cfg(feature = "swap")] _swap_controller: &mut Option<swap::SwapController>,
861 ) -> std::result::Result<PciAddress, Self::Error> {
862 // hotplug function isn't verified on AArch64, so set it unsupported here.
863 Err(Error::Unsupported)
864 }
865
get_host_cpu_frequencies_khz() -> std::result::Result<BTreeMap<usize, Vec<u32>>, Self::Error>866 fn get_host_cpu_frequencies_khz() -> std::result::Result<BTreeMap<usize, Vec<u32>>, Self::Error>
867 {
868 Ok(
869 Self::collect_for_each_cpu(base::logical_core_frequencies_khz)
870 .map_err(Error::CpuFrequencies)?
871 .into_iter()
872 .enumerate()
873 .collect(),
874 )
875 }
876
877 // Returns a (cpu_id -> value) map of the DMIPS/MHz capacities of logical cores
878 // in the host system.
get_host_cpu_capacity() -> std::result::Result<BTreeMap<usize, u32>, Self::Error>879 fn get_host_cpu_capacity() -> std::result::Result<BTreeMap<usize, u32>, Self::Error> {
880 Ok(Self::collect_for_each_cpu(base::logical_core_capacity)
881 .map_err(Error::CpuTopology)?
882 .into_iter()
883 .enumerate()
884 .collect())
885 }
886
887 // Creates CPU cluster mask for each CPU in the host system.
get_host_cpu_clusters() -> std::result::Result<Vec<CpuSet>, Self::Error>888 fn get_host_cpu_clusters() -> std::result::Result<Vec<CpuSet>, Self::Error> {
889 let cluster_ids = Self::collect_for_each_cpu(base::logical_core_cluster_id)
890 .map_err(Error::CpuTopology)?;
891 let mut unique_clusters: Vec<CpuSet> = cluster_ids
892 .iter()
893 .map(|&vcpu_cluster_id| {
894 cluster_ids
895 .iter()
896 .enumerate()
897 .filter(|(_, &cpu_cluster_id)| vcpu_cluster_id == cpu_cluster_id)
898 .map(|(cpu_id, _)| cpu_id)
899 .collect()
900 })
901 .collect();
902 unique_clusters.sort_unstable();
903 unique_clusters.dedup();
904 Ok(unique_clusters)
905 }
906 }
907
908 #[cfg(feature = "gdb")]
909 impl<T: VcpuAArch64> arch::GdbOps<T> for AArch64 {
910 type Error = Error;
911
read_memory( _vcpu: &T, guest_mem: &GuestMemory, vaddr: GuestAddress, len: usize, ) -> Result<Vec<u8>>912 fn read_memory(
913 _vcpu: &T,
914 guest_mem: &GuestMemory,
915 vaddr: GuestAddress,
916 len: usize,
917 ) -> Result<Vec<u8>> {
918 let mut buf = vec![0; len];
919
920 guest_mem
921 .read_exact_at_addr(&mut buf, vaddr)
922 .map_err(Error::ReadGuestMemory)?;
923
924 Ok(buf)
925 }
926
write_memory( _vcpu: &T, guest_mem: &GuestMemory, vaddr: GuestAddress, buf: &[u8], ) -> Result<()>927 fn write_memory(
928 _vcpu: &T,
929 guest_mem: &GuestMemory,
930 vaddr: GuestAddress,
931 buf: &[u8],
932 ) -> Result<()> {
933 guest_mem
934 .write_all_at_addr(buf, vaddr)
935 .map_err(Error::WriteGuestMemory)
936 }
937
read_registers(vcpu: &T) -> Result<<GdbArch as Arch>::Registers>938 fn read_registers(vcpu: &T) -> Result<<GdbArch as Arch>::Registers> {
939 let mut regs: <GdbArch as Arch>::Registers = Default::default();
940
941 vcpu.get_gdb_registers(&mut regs).map_err(Error::ReadRegs)?;
942
943 Ok(regs)
944 }
945
write_registers(vcpu: &T, regs: &<GdbArch as Arch>::Registers) -> Result<()>946 fn write_registers(vcpu: &T, regs: &<GdbArch as Arch>::Registers) -> Result<()> {
947 vcpu.set_gdb_registers(regs).map_err(Error::WriteRegs)
948 }
949
read_register(vcpu: &T, reg_id: <GdbArch as Arch>::RegId) -> Result<Vec<u8>>950 fn read_register(vcpu: &T, reg_id: <GdbArch as Arch>::RegId) -> Result<Vec<u8>> {
951 let mut reg = vec![0; std::mem::size_of::<u128>()];
952 let size = vcpu
953 .get_gdb_register(reg_id, reg.as_mut_slice())
954 .map_err(Error::ReadReg)?;
955 reg.truncate(size);
956 Ok(reg)
957 }
958
write_register(vcpu: &T, reg_id: <GdbArch as Arch>::RegId, data: &[u8]) -> Result<()>959 fn write_register(vcpu: &T, reg_id: <GdbArch as Arch>::RegId, data: &[u8]) -> Result<()> {
960 vcpu.set_gdb_register(reg_id, data).map_err(Error::WriteReg)
961 }
962
enable_singlestep(vcpu: &T) -> Result<()>963 fn enable_singlestep(vcpu: &T) -> Result<()> {
964 const SINGLE_STEP: bool = true;
965 vcpu.set_guest_debug(&[], SINGLE_STEP)
966 .map_err(Error::EnableSinglestep)
967 }
968
get_max_hw_breakpoints(vcpu: &T) -> Result<usize>969 fn get_max_hw_breakpoints(vcpu: &T) -> Result<usize> {
970 vcpu.get_max_hw_bps().map_err(Error::GetMaxHwBreakPoint)
971 }
972
set_hw_breakpoints(vcpu: &T, breakpoints: &[GuestAddress]) -> Result<()>973 fn set_hw_breakpoints(vcpu: &T, breakpoints: &[GuestAddress]) -> Result<()> {
974 const SINGLE_STEP: bool = false;
975 vcpu.set_guest_debug(breakpoints, SINGLE_STEP)
976 .map_err(Error::SetHwBreakpoint)
977 }
978 }
979
980 impl AArch64 {
981 /// This returns a base part of the kernel command for this architecture
get_base_linux_cmdline() -> kernel_cmdline::Cmdline982 fn get_base_linux_cmdline() -> kernel_cmdline::Cmdline {
983 let mut cmdline = kernel_cmdline::Cmdline::new(base::pagesize());
984 cmdline.insert_str("panic=-1").unwrap();
985 cmdline
986 }
987
988 /// Returns a system resource allocator configuration.
989 ///
990 /// # Arguments
991 ///
992 /// * `memory_end` - The first address beyond the end of guest memory.
993 /// * `guest_phys_addr_bits` - Size of guest physical addresses (IPA) in bits.
get_resource_allocator_config( memory_end: GuestAddress, guest_phys_addr_bits: u8, ) -> SystemAllocatorConfig994 fn get_resource_allocator_config(
995 memory_end: GuestAddress,
996 guest_phys_addr_bits: u8,
997 ) -> SystemAllocatorConfig {
998 let guest_phys_end = 1u64 << guest_phys_addr_bits;
999 // The platform MMIO region is immediately past the end of RAM.
1000 let plat_mmio_base = memory_end.offset();
1001 let plat_mmio_size = AARCH64_PLATFORM_MMIO_SIZE;
1002 // The high MMIO region is the rest of the address space after the platform MMIO region.
1003 let high_mmio_base = plat_mmio_base + plat_mmio_size;
1004 let high_mmio_size = guest_phys_end
1005 .checked_sub(high_mmio_base)
1006 .unwrap_or_else(|| {
1007 panic!(
1008 "guest_phys_end {:#x} < high_mmio_base {:#x}",
1009 guest_phys_end, high_mmio_base,
1010 );
1011 });
1012 SystemAllocatorConfig {
1013 io: None,
1014 low_mmio: AddressRange::from_start_and_size(AARCH64_MMIO_BASE, AARCH64_MMIO_SIZE)
1015 .expect("invalid mmio region"),
1016 high_mmio: AddressRange::from_start_and_size(high_mmio_base, high_mmio_size)
1017 .expect("invalid high mmio region"),
1018 platform_mmio: Some(
1019 AddressRange::from_start_and_size(plat_mmio_base, plat_mmio_size)
1020 .expect("invalid platform mmio region"),
1021 ),
1022 first_irq: AARCH64_IRQ_BASE,
1023 }
1024 }
1025
1026 /// This adds any early platform devices for this architecture.
1027 ///
1028 /// # Arguments
1029 ///
1030 /// * `irq_chip` - The IRQ chip to add irqs to.
1031 /// * `bus` - The bus to add devices to.
1032 /// * `vcpu_count` - The number of virtual CPUs for this guest VM
1033 /// * `vm_evt_wrtube` - The notification channel
add_arch_devs( irq_chip: &mut dyn IrqChip, bus: &Bus, vcpu_count: usize, vm_evt_wrtube: &SendTube, ) -> Result<()>1034 fn add_arch_devs(
1035 irq_chip: &mut dyn IrqChip,
1036 bus: &Bus,
1037 vcpu_count: usize,
1038 vm_evt_wrtube: &SendTube,
1039 ) -> Result<()> {
1040 let rtc_evt = devices::IrqEdgeEvent::new().map_err(Error::CreateEvent)?;
1041 let rtc = devices::pl030::Pl030::new(rtc_evt.try_clone().map_err(Error::CloneEvent)?);
1042 irq_chip
1043 .register_edge_irq_event(AARCH64_RTC_IRQ, &rtc_evt, IrqEventSource::from_device(&rtc))
1044 .map_err(Error::RegisterIrqfd)?;
1045
1046 bus.insert(
1047 Arc::new(Mutex::new(rtc)),
1048 AARCH64_RTC_ADDR,
1049 AARCH64_RTC_SIZE,
1050 )
1051 .expect("failed to add rtc device");
1052
1053 let vm_wdt = Arc::new(Mutex::new(
1054 devices::vmwdt::Vmwdt::new(vcpu_count, vm_evt_wrtube.try_clone().unwrap()).unwrap(),
1055 ));
1056 bus.insert(vm_wdt, AARCH64_VMWDT_ADDR, AARCH64_VMWDT_SIZE)
1057 .expect("failed to add vmwdt device");
1058
1059 Ok(())
1060 }
1061
1062 /// Get ARM-specific features for vcpu with index `vcpu_id`.
1063 ///
1064 /// # Arguments
1065 ///
1066 /// * `vcpu_id` - The VM's index for `vcpu`.
1067 /// * `use_pmu` - Should `vcpu` be configured to use the Performance Monitor Unit.
vcpu_features(vcpu_id: usize, use_pmu: bool, boot_cpu: usize) -> Vec<VcpuFeature>1068 fn vcpu_features(vcpu_id: usize, use_pmu: bool, boot_cpu: usize) -> Vec<VcpuFeature> {
1069 let mut features = vec![VcpuFeature::PsciV0_2];
1070 if use_pmu {
1071 features.push(VcpuFeature::PmuV3);
1072 }
1073 // Non-boot cpus are powered off initially
1074 if vcpu_id != boot_cpu {
1075 features.push(VcpuFeature::PowerOff);
1076 }
1077
1078 features
1079 }
1080
1081 /// Get initial register state for vcpu with index `vcpu_id`.
1082 ///
1083 /// # Arguments
1084 ///
1085 /// * `vcpu_id` - The VM's index for `vcpu`.
vcpu_init( vcpu_id: usize, payload: &PayloadType, fdt_address: GuestAddress, protection_type: ProtectionType, boot_cpu: usize, ) -> VcpuInitAArch641086 fn vcpu_init(
1087 vcpu_id: usize,
1088 payload: &PayloadType,
1089 fdt_address: GuestAddress,
1090 protection_type: ProtectionType,
1091 boot_cpu: usize,
1092 ) -> VcpuInitAArch64 {
1093 let mut regs: BTreeMap<VcpuRegAArch64, u64> = Default::default();
1094
1095 // All interrupts masked
1096 let pstate = PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | PSR_MODE_EL1H;
1097 regs.insert(VcpuRegAArch64::Pstate, pstate);
1098
1099 // Other cpus are powered off initially
1100 if vcpu_id == boot_cpu {
1101 let entry_addr = if protection_type.loads_firmware() {
1102 Some(AARCH64_PROTECTED_VM_FW_START)
1103 } else if protection_type.runs_firmware() {
1104 None // Initial PC value is set by the hypervisor
1105 } else {
1106 Some(payload.entry().offset())
1107 };
1108
1109 /* PC -- entry point */
1110 if let Some(entry) = entry_addr {
1111 regs.insert(VcpuRegAArch64::Pc, entry);
1112 }
1113
1114 /* X0 -- fdt address */
1115 regs.insert(VcpuRegAArch64::X(0), fdt_address.offset());
1116
1117 if protection_type.runs_firmware() {
1118 /* X1 -- payload entry point */
1119 regs.insert(VcpuRegAArch64::X(1), payload.entry().offset());
1120
1121 /* X2 -- image size */
1122 regs.insert(VcpuRegAArch64::X(2), payload.size());
1123 }
1124 }
1125
1126 VcpuInitAArch64 { regs }
1127 }
1128
collect_for_each_cpu<F, T>(func: F) -> std::result::Result<Vec<T>, base::Error> where F: Fn(usize) -> std::result::Result<T, base::Error>,1129 fn collect_for_each_cpu<F, T>(func: F) -> std::result::Result<Vec<T>, base::Error>
1130 where
1131 F: Fn(usize) -> std::result::Result<T, base::Error>,
1132 {
1133 (0..base::number_of_logical_cores()?).map(func).collect()
1134 }
1135 }
1136
1137 #[cfg(test)]
1138 mod tests {
1139 use super::*;
1140
1141 #[test]
vcpu_init_unprotected_kernel()1142 fn vcpu_init_unprotected_kernel() {
1143 let payload = PayloadType::Kernel(LoadedKernel {
1144 address_range: AddressRange::from_start_and_size(0x8080_0000, 0x1000).unwrap(),
1145 size: 0x1000,
1146 entry: GuestAddress(0x8080_0000),
1147 });
1148 let fdt_address = GuestAddress(0x1234);
1149 let prot = ProtectionType::Unprotected;
1150
1151 let vcpu_init = AArch64::vcpu_init(0, &payload, fdt_address, prot, 0);
1152
1153 // PC: kernel image entry point
1154 assert_eq!(vcpu_init.regs.get(&VcpuRegAArch64::Pc), Some(&0x8080_0000));
1155
1156 // X0: fdt_offset
1157 assert_eq!(vcpu_init.regs.get(&VcpuRegAArch64::X(0)), Some(&0x1234));
1158 }
1159
1160 #[test]
vcpu_init_unprotected_bios()1161 fn vcpu_init_unprotected_bios() {
1162 let payload = PayloadType::Bios {
1163 entry: GuestAddress(0x8020_0000),
1164 image_size: 0x1000,
1165 };
1166 let fdt_address = GuestAddress(0x1234);
1167 let prot = ProtectionType::Unprotected;
1168
1169 let vcpu_init = AArch64::vcpu_init(0, &payload, fdt_address, prot, 0);
1170
1171 // PC: bios image entry point
1172 assert_eq!(vcpu_init.regs.get(&VcpuRegAArch64::Pc), Some(&0x8020_0000));
1173
1174 // X0: fdt_offset
1175 assert_eq!(vcpu_init.regs.get(&VcpuRegAArch64::X(0)), Some(&0x1234));
1176 }
1177
1178 #[test]
vcpu_init_protected_kernel()1179 fn vcpu_init_protected_kernel() {
1180 let payload = PayloadType::Kernel(LoadedKernel {
1181 address_range: AddressRange::from_start_and_size(0x8080_0000, 0x1000).unwrap(),
1182 size: 0x1000,
1183 entry: GuestAddress(0x8080_0000),
1184 });
1185 let fdt_address = GuestAddress(0x1234);
1186 let prot = ProtectionType::Protected;
1187
1188 let vcpu_init = AArch64::vcpu_init(0, &payload, fdt_address, prot, 0);
1189
1190 // The hypervisor provides the initial value of PC, so PC should not be present in the
1191 // vcpu_init register map.
1192 assert_eq!(vcpu_init.regs.get(&VcpuRegAArch64::Pc), None);
1193
1194 // X0: fdt_offset
1195 assert_eq!(vcpu_init.regs.get(&VcpuRegAArch64::X(0)), Some(&0x1234));
1196
1197 // X1: kernel image entry point
1198 assert_eq!(
1199 vcpu_init.regs.get(&VcpuRegAArch64::X(1)),
1200 Some(&0x8080_0000)
1201 );
1202
1203 // X2: image size
1204 assert_eq!(vcpu_init.regs.get(&VcpuRegAArch64::X(2)), Some(&0x1000));
1205 }
1206 }
1207