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 use std::collections::BTreeMap;
6 use std::error::Error as StdError;
7 use std::fmt::{self, Display};
8 use std::io::{self};
9 use std::sync::Arc;
10
11 use arch::{
12 get_serial_cmdline, GetSerialCmdlineError, RunnableLinuxVm, SerialHardware, SerialParameters,
13 VmComponents, VmImage,
14 };
15 use base::Event;
16 use devices::{Bus, BusError, IrqChip, IrqChipAArch64, PciConfigMmio, PciDevice, ProtectionType};
17 use hypervisor::{DeviceKind, Hypervisor, HypervisorCap, VcpuAArch64, VcpuFeature, VmAArch64};
18 use minijail::Minijail;
19 use remain::sorted;
20 use resources::SystemAllocator;
21 use sync::Mutex;
22 use vm_control::BatteryType;
23 use vm_memory::{GuestAddress, GuestMemory, GuestMemoryError};
24
25 mod fdt;
26
27 // We place the kernel at offset 8MB
28 const AARCH64_KERNEL_OFFSET: u64 = 0x80000;
29 const AARCH64_FDT_MAX_SIZE: u64 = 0x200000;
30 const AARCH64_INITRD_ALIGN: u64 = 0x1000000;
31
32 // These constants indicate the address space used by the ARM vGIC.
33 const AARCH64_GIC_DIST_SIZE: u64 = 0x10000;
34 const AARCH64_GIC_CPUI_SIZE: u64 = 0x20000;
35
36 // This indicates the start of DRAM inside the physical address space.
37 const AARCH64_PHYS_MEM_START: u64 = 0x80000000;
38 const AARCH64_AXI_BASE: u64 = 0x40000000;
39
40 // FDT is placed at the front of RAM when booting in BIOS mode.
41 const AARCH64_FDT_OFFSET_IN_BIOS_MODE: u64 = 0x0;
42 // Therefore, the BIOS is placed after the FDT in memory.
43 const AARCH64_BIOS_OFFSET: u64 = AARCH64_FDT_MAX_SIZE;
44 const AARCH64_BIOS_MAX_LEN: u64 = 1 << 20;
45
46 const AARCH64_PROTECTED_VM_FW_MAX_SIZE: u64 = 0x200000;
47 const AARCH64_PROTECTED_VM_FW_START: u64 =
48 AARCH64_PHYS_MEM_START - AARCH64_PROTECTED_VM_FW_MAX_SIZE;
49
50 // These constants indicate the placement of the GIC registers in the physical
51 // address space.
52 const AARCH64_GIC_DIST_BASE: u64 = AARCH64_AXI_BASE - AARCH64_GIC_DIST_SIZE;
53 const AARCH64_GIC_CPUI_BASE: u64 = AARCH64_GIC_DIST_BASE - AARCH64_GIC_CPUI_SIZE;
54 const AARCH64_GIC_REDIST_SIZE: u64 = 0x20000;
55
56 // PSR (Processor State Register) bits
57 const PSR_MODE_EL1H: u64 = 0x00000005;
58 const PSR_F_BIT: u64 = 0x00000040;
59 const PSR_I_BIT: u64 = 0x00000080;
60 const PSR_A_BIT: u64 = 0x00000100;
61 const PSR_D_BIT: u64 = 0x00000200;
62
63 macro_rules! offset__of {
64 ($str:ty, $($field:ident).+ $([$idx:expr])*) => {
65 unsafe { &(*(0 as *const $str))$(.$field)* $([$idx])* as *const _ as usize }
66 }
67 }
68
69 const KVM_REG_ARM64: u64 = 0x6000000000000000;
70 const KVM_REG_SIZE_U64: u64 = 0x0030000000000000;
71 const KVM_REG_ARM_COPROC_SHIFT: u64 = 16;
72 const KVM_REG_ARM_CORE: u64 = 0x0010 << KVM_REG_ARM_COPROC_SHIFT;
73
74 macro_rules! arm64_core_reg {
75 ($reg: tt) => {
76 KVM_REG_ARM64
77 | KVM_REG_SIZE_U64
78 | KVM_REG_ARM_CORE
79 | ((offset__of!(kvm_sys::user_pt_regs, $reg) / 4) as u64)
80 };
81 }
82
get_kernel_addr() -> GuestAddress83 fn get_kernel_addr() -> GuestAddress {
84 GuestAddress(AARCH64_PHYS_MEM_START + AARCH64_KERNEL_OFFSET)
85 }
86
get_bios_addr() -> GuestAddress87 fn get_bios_addr() -> GuestAddress {
88 GuestAddress(AARCH64_PHYS_MEM_START + AARCH64_BIOS_OFFSET)
89 }
90
91 // Serial device requires 8 bytes of registers;
92 const AARCH64_SERIAL_SIZE: u64 = 0x8;
93 // This was the speed kvmtool used, not sure if it matters.
94 const AARCH64_SERIAL_SPEED: u32 = 1843200;
95 // The serial device gets the first interrupt line
96 // Which gets mapped to the first SPI interrupt (physical 32).
97 const AARCH64_SERIAL_1_3_IRQ: u32 = 0;
98 const AARCH64_SERIAL_2_4_IRQ: u32 = 2;
99
100 // Place the RTC device at page 2
101 const AARCH64_RTC_ADDR: u64 = 0x2000;
102 // The RTC device gets one 4k page
103 const AARCH64_RTC_SIZE: u64 = 0x1000;
104 // The RTC device gets the second interrupt line
105 const AARCH64_RTC_IRQ: u32 = 1;
106
107 // PCI MMIO configuration region base address.
108 const AARCH64_PCI_CFG_BASE: u64 = 0x10000;
109 // PCI MMIO configuration region size.
110 const AARCH64_PCI_CFG_SIZE: u64 = 0x1000000;
111 // This is the base address of MMIO devices.
112 const AARCH64_MMIO_BASE: u64 = 0x1010000;
113 // Size of the whole MMIO region.
114 const AARCH64_MMIO_SIZE: u64 = 0x100000;
115 // Virtio devices start at SPI interrupt number 3
116 const AARCH64_IRQ_BASE: u32 = 3;
117
118 // PMU PPI interrupt, same as qemu
119 const AARCH64_PMU_IRQ: u32 = 7;
120
121 #[sorted]
122 #[derive(Debug)]
123 pub enum Error {
124 BiosLoadFailure(arch::LoadImageError),
125 CloneEvent(base::Error),
126 Cmdline(kernel_cmdline::Error),
127 CreateDevices(Box<dyn StdError>),
128 CreateEvent(base::Error),
129 CreateFdt(arch::fdt::Error),
130 CreateGICFailure(base::Error),
131 CreateIrqChip(Box<dyn StdError>),
132 CreatePciRoot(arch::DeviceRegistrationError),
133 CreateSerialDevices(arch::DeviceRegistrationError),
134 CreateSocket(io::Error),
135 CreateVcpu(base::Error),
136 CreateVm(Box<dyn StdError>),
137 DowncastVcpu,
138 GetPsciVersion(base::Error),
139 GetSerialCmdline(GetSerialCmdlineError),
140 InitrdLoadFailure(arch::LoadImageError),
141 KernelLoadFailure(arch::LoadImageError),
142 ProtectVm(base::Error),
143 RegisterIrqfd(base::Error),
144 RegisterPci(BusError),
145 RegisterVsock(arch::DeviceRegistrationError),
146 SetDeviceAttr(base::Error),
147 SetReg(base::Error),
148 SetupGuestMemory(GuestMemoryError),
149 VcpuInit(base::Error),
150 }
151
152 impl Display for Error {
153 #[remain::check]
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result154 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
155 use self::Error::*;
156
157 #[sorted]
158 match self {
159 BiosLoadFailure(e) => write!(f, "bios could not be loaded: {}", e),
160 CloneEvent(e) => write!(f, "unable to clone an Event: {}", e),
161 Cmdline(e) => write!(f, "the given kernel command line was invalid: {}", e),
162 CreateDevices(e) => write!(f, "error creating devices: {}", e),
163 CreateEvent(e) => write!(f, "unable to make an Event: {}", e),
164 CreateFdt(e) => write!(f, "FDT could not be created: {}", e),
165 CreateGICFailure(e) => write!(f, "failed to create GIC: {}", e),
166 CreateIrqChip(e) => write!(f, "failed to create IRQ chip: {}", e),
167 CreatePciRoot(e) => write!(f, "failed to create a PCI root hub: {}", e),
168 CreateSerialDevices(e) => write!(f, "unable to create serial devices: {}", e),
169 CreateSocket(e) => write!(f, "failed to create socket: {}", e),
170 CreateVcpu(e) => write!(f, "failed to create VCPU: {}", e),
171 CreateVm(e) => write!(f, "failed to create vm: {}", e),
172 DowncastVcpu => write!(f, "vm created wrong kind of vcpu"),
173 GetPsciVersion(e) => write!(f, "failed to get PSCI version: {}", e),
174 GetSerialCmdline(e) => write!(f, "failed to get serial cmdline: {}", e),
175 InitrdLoadFailure(e) => write!(f, "initrd could not be loaded: {}", e),
176 KernelLoadFailure(e) => write!(f, "kernel could not be loaded: {}", e),
177 ProtectVm(e) => write!(f, "failed to protect vm: {}", e),
178 RegisterIrqfd(e) => write!(f, "failed to register irq fd: {}", e),
179 RegisterPci(e) => write!(f, "error registering PCI bus: {}", e),
180 RegisterVsock(e) => write!(f, "error registering virtual socket device: {}", e),
181 SetDeviceAttr(e) => write!(f, "failed to set device attr: {}", e),
182 SetReg(e) => write!(f, "failed to set register: {}", e),
183 SetupGuestMemory(e) => write!(f, "failed to set up guest memory: {}", e),
184 VcpuInit(e) => write!(f, "failed to initialize VCPU: {}", e),
185 }
186 }
187 }
188
189 pub type Result<T> = std::result::Result<T, Error>;
190
191 impl std::error::Error for Error {}
192
193 /// Returns a Vec of the valid memory addresses.
194 /// These should be used to configure the GuestMemory structure for the platfrom.
arch_memory_regions(size: u64) -> Vec<(GuestAddress, u64)>195 pub fn arch_memory_regions(size: u64) -> Vec<(GuestAddress, u64)> {
196 vec![(GuestAddress(AARCH64_PHYS_MEM_START), size)]
197 }
198
fdt_offset(mem_size: u64, has_bios: bool) -> u64199 fn fdt_offset(mem_size: u64, has_bios: bool) -> u64 {
200 // TODO(rammuthiah) make kernel and BIOS startup use FDT from the same location. ARCVM startup
201 // currently expects the kernel at 0x80080000 and the FDT at the end of RAM for unknown reasons.
202 // Root cause and figure out how to fold these code paths together.
203 if has_bios {
204 AARCH64_FDT_OFFSET_IN_BIOS_MODE
205 } else {
206 // Put fdt up near the top of memory
207 // TODO(sonnyrao): will have to handle this differently if there's
208 // > 4GB memory
209 mem_size - AARCH64_FDT_MAX_SIZE - 0x10000
210 }
211 }
212
213 pub struct AArch64;
214
215 impl arch::LinuxArch for AArch64 {
216 type Error = Error;
217
guest_memory_layout( components: &VmComponents, ) -> std::result::Result<Vec<(GuestAddress, u64)>, Self::Error>218 fn guest_memory_layout(
219 components: &VmComponents,
220 ) -> std::result::Result<Vec<(GuestAddress, u64)>, Self::Error> {
221 Ok(arch_memory_regions(components.memory_size))
222 }
223
build_vm<V, Vcpu, I, FD, FI, E1, E2>( mut components: VmComponents, serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>, serial_jail: Option<Minijail>, _battery: (&Option<BatteryType>, Option<Minijail>), mut vm: V, create_devices: FD, create_irq_chip: FI, ) -> std::result::Result<RunnableLinuxVm<V, Vcpu, I>, Self::Error> where V: VmAArch64, Vcpu: VcpuAArch64, I: IrqChipAArch64, FD: FnOnce( &GuestMemory, &mut V, &mut SystemAllocator, &Event, ) -> std::result::Result<Vec<(Box<dyn PciDevice>, Option<Minijail>)>, E1>, FI: FnOnce(&V, usize) -> std::result::Result<I, E2>, E1: StdError + 'static, E2: StdError + 'static,224 fn build_vm<V, Vcpu, I, FD, FI, E1, E2>(
225 mut components: VmComponents,
226 serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
227 serial_jail: Option<Minijail>,
228 _battery: (&Option<BatteryType>, Option<Minijail>),
229 mut vm: V,
230 create_devices: FD,
231 create_irq_chip: FI,
232 ) -> std::result::Result<RunnableLinuxVm<V, Vcpu, I>, Self::Error>
233 where
234 V: VmAArch64,
235 Vcpu: VcpuAArch64,
236 I: IrqChipAArch64,
237 FD: FnOnce(
238 &GuestMemory,
239 &mut V,
240 &mut SystemAllocator,
241 &Event,
242 ) -> std::result::Result<Vec<(Box<dyn PciDevice>, Option<Minijail>)>, E1>,
243 FI: FnOnce(&V, /* vcpu_count: */ usize) -> std::result::Result<I, E2>,
244 E1: StdError + 'static,
245 E2: StdError + 'static,
246 {
247 let has_bios = match components.vm_image {
248 VmImage::Bios(_) => true,
249 _ => false,
250 };
251
252 let mem = vm.get_memory().clone();
253 let mut resources = Self::get_resource_allocator(components.memory_size);
254
255 if components.protected_vm == ProtectionType::Protected {
256 vm.enable_protected_vm(
257 GuestAddress(AARCH64_PROTECTED_VM_FW_START),
258 AARCH64_PROTECTED_VM_FW_MAX_SIZE,
259 )
260 .map_err(Error::ProtectVm)?;
261 }
262
263 let mut use_pmu = vm
264 .get_hypervisor()
265 .check_capability(&HypervisorCap::ArmPmuV3);
266 let vcpu_count = components.vcpu_count;
267 let mut vcpus = Vec::with_capacity(vcpu_count);
268 for vcpu_id in 0..vcpu_count {
269 let vcpu: Vcpu = *vm
270 .create_vcpu(vcpu_id)
271 .map_err(Error::CreateVcpu)?
272 .downcast::<Vcpu>()
273 .map_err(|_| Error::DowncastVcpu)?;
274 Self::configure_vcpu_early(vm.get_memory(), &vcpu, vcpu_id, use_pmu, has_bios)?;
275 vcpus.push(vcpu);
276 }
277
278 let mut irq_chip =
279 create_irq_chip(&vm, vcpu_count).map_err(|e| Error::CreateIrqChip(Box::new(e)))?;
280
281 for vcpu in &vcpus {
282 use_pmu &= vcpu.init_pmu(AARCH64_PMU_IRQ as u64 + 16).is_ok();
283 }
284
285 let mut mmio_bus = devices::Bus::new();
286
287 let exit_evt = Event::new().map_err(Error::CreateEvent)?;
288
289 // Event used by PMDevice to notify crosvm that
290 // guest OS is trying to suspend.
291 let suspend_evt = Event::new().map_err(Error::CreateEvent)?;
292
293 let pci_devices = create_devices(&mem, &mut vm, &mut resources, &exit_evt)
294 .map_err(|e| Error::CreateDevices(Box::new(e)))?;
295 let (pci, pci_irqs, pid_debug_label_map) = arch::generate_pci_root(
296 pci_devices,
297 &mut irq_chip,
298 &mut mmio_bus,
299 &mut resources,
300 &mut vm,
301 (devices::AARCH64_GIC_NR_IRQS - AARCH64_IRQ_BASE) as usize,
302 )
303 .map_err(Error::CreatePciRoot)?;
304 let pci_bus = Arc::new(Mutex::new(PciConfigMmio::new(pci)));
305
306 // ARM doesn't really use the io bus like x86, so just create an empty bus.
307 let io_bus = devices::Bus::new();
308
309 Self::add_arch_devs(&mut irq_chip, &mut mmio_bus)?;
310
311 let com_evt_1_3 = Event::new().map_err(Error::CreateEvent)?;
312 let com_evt_2_4 = Event::new().map_err(Error::CreateEvent)?;
313 arch::add_serial_devices(
314 components.protected_vm,
315 &mut mmio_bus,
316 &com_evt_1_3,
317 &com_evt_2_4,
318 serial_parameters,
319 serial_jail,
320 )
321 .map_err(Error::CreateSerialDevices)?;
322
323 irq_chip
324 .register_irq_event(AARCH64_SERIAL_1_3_IRQ, &com_evt_1_3, None)
325 .map_err(Error::RegisterIrqfd)?;
326 irq_chip
327 .register_irq_event(AARCH64_SERIAL_2_4_IRQ, &com_evt_2_4, None)
328 .map_err(Error::RegisterIrqfd)?;
329
330 mmio_bus
331 .insert(pci_bus.clone(), AARCH64_PCI_CFG_BASE, AARCH64_PCI_CFG_SIZE)
332 .map_err(Error::RegisterPci)?;
333
334 let mut cmdline = Self::get_base_linux_cmdline();
335 get_serial_cmdline(&mut cmdline, serial_parameters, "mmio")
336 .map_err(Error::GetSerialCmdline)?;
337 for param in components.extra_kernel_params {
338 cmdline.insert_str(¶m).map_err(Error::Cmdline)?;
339 }
340
341 let psci_version = vcpus[0].get_psci_version().map_err(Error::GetPsciVersion)?;
342 let (pci_device_base, pci_device_size) =
343 Self::get_high_mmio_base_size(components.memory_size);
344 let mut initrd = None;
345
346 // separate out image loading from other setup to get a specific error for
347 // image loading
348 match components.vm_image {
349 VmImage::Bios(ref mut bios) => {
350 arch::load_image(&mem, bios, get_bios_addr(), AARCH64_BIOS_MAX_LEN)
351 .map_err(Error::BiosLoadFailure)?;
352 }
353 VmImage::Kernel(ref mut kernel_image) => {
354 let kernel_size =
355 arch::load_image(&mem, kernel_image, get_kernel_addr(), u64::max_value())
356 .map_err(Error::KernelLoadFailure)?;
357 let kernel_end = get_kernel_addr().offset() + kernel_size as u64;
358 initrd = match components.initrd_image {
359 Some(initrd_file) => {
360 let mut initrd_file = initrd_file;
361 let initrd_addr =
362 (kernel_end + (AARCH64_INITRD_ALIGN - 1)) & !(AARCH64_INITRD_ALIGN - 1);
363 let initrd_max_size =
364 components.memory_size - (initrd_addr - AARCH64_PHYS_MEM_START);
365 let initrd_addr = GuestAddress(initrd_addr);
366 let initrd_size =
367 arch::load_image(&mem, &mut initrd_file, initrd_addr, initrd_max_size)
368 .map_err(Error::InitrdLoadFailure)?;
369 Some((initrd_addr, initrd_size))
370 }
371 None => None,
372 };
373 }
374 }
375
376 fdt::create_fdt(
377 AARCH64_FDT_MAX_SIZE as usize,
378 &mem,
379 pci_irqs,
380 vcpu_count as u32,
381 fdt_offset(components.memory_size, has_bios),
382 pci_device_base,
383 pci_device_size,
384 cmdline.as_str(),
385 initrd,
386 components.android_fstab,
387 irq_chip.get_vgic_version() == DeviceKind::ArmVgicV3,
388 use_pmu,
389 psci_version,
390 )
391 .map_err(Error::CreateFdt)?;
392
393 Ok(RunnableLinuxVm {
394 vm,
395 resources,
396 exit_evt,
397 vcpu_count,
398 vcpus: Some(vcpus),
399 vcpu_affinity: components.vcpu_affinity,
400 no_smt: components.no_smt,
401 irq_chip,
402 has_bios,
403 io_bus,
404 mmio_bus,
405 pid_debug_label_map,
406 suspend_evt,
407 rt_cpus: components.rt_cpus,
408 bat_control: None,
409 })
410 }
411
configure_vcpu( _guest_mem: &GuestMemory, _hypervisor: &dyn Hypervisor, _irq_chip: &mut dyn IrqChipAArch64, _vcpu: &mut dyn VcpuAArch64, _vcpu_id: usize, _num_cpus: usize, _has_bios: bool, _no_smt: bool, ) -> std::result::Result<(), Self::Error>412 fn configure_vcpu(
413 _guest_mem: &GuestMemory,
414 _hypervisor: &dyn Hypervisor,
415 _irq_chip: &mut dyn IrqChipAArch64,
416 _vcpu: &mut dyn VcpuAArch64,
417 _vcpu_id: usize,
418 _num_cpus: usize,
419 _has_bios: bool,
420 _no_smt: bool,
421 ) -> std::result::Result<(), Self::Error> {
422 // AArch64 doesn't configure vcpus on the vcpu thread, so nothing to do here.
423 Ok(())
424 }
425 }
426
427 impl AArch64 {
get_high_mmio_base_size(mem_size: u64) -> (u64, u64)428 fn get_high_mmio_base_size(mem_size: u64) -> (u64, u64) {
429 let base = AARCH64_PHYS_MEM_START + mem_size;
430 let size = u64::max_value() - base;
431 (base, size)
432 }
433
434 /// This returns a base part of the kernel command for this architecture
get_base_linux_cmdline() -> kernel_cmdline::Cmdline435 fn get_base_linux_cmdline() -> kernel_cmdline::Cmdline {
436 let mut cmdline = kernel_cmdline::Cmdline::new(base::pagesize());
437 cmdline.insert_str("panic=-1").unwrap();
438 cmdline
439 }
440
441 /// Returns a system resource allocator.
get_resource_allocator(mem_size: u64) -> SystemAllocator442 fn get_resource_allocator(mem_size: u64) -> SystemAllocator {
443 let (high_mmio_base, high_mmio_size) = Self::get_high_mmio_base_size(mem_size);
444 SystemAllocator::builder()
445 .add_high_mmio_addresses(high_mmio_base, high_mmio_size)
446 .add_low_mmio_addresses(AARCH64_MMIO_BASE, AARCH64_MMIO_SIZE)
447 .create_allocator(AARCH64_IRQ_BASE)
448 .unwrap()
449 }
450
451 /// This adds any early platform devices for this architecture.
452 ///
453 /// # Arguments
454 ///
455 /// * `irq_chip` - The IRQ chip to add irqs to.
456 /// * `bus` - The bus to add devices to.
add_arch_devs(irq_chip: &mut dyn IrqChip, bus: &mut Bus) -> Result<()>457 fn add_arch_devs(irq_chip: &mut dyn IrqChip, bus: &mut Bus) -> Result<()> {
458 let rtc_evt = Event::new().map_err(Error::CreateEvent)?;
459 irq_chip
460 .register_irq_event(AARCH64_RTC_IRQ, &rtc_evt, None)
461 .map_err(Error::RegisterIrqfd)?;
462
463 let rtc = Arc::new(Mutex::new(devices::pl030::Pl030::new(rtc_evt)));
464 bus.insert(rtc, AARCH64_RTC_ADDR, AARCH64_RTC_SIZE)
465 .expect("failed to add rtc device");
466
467 Ok(())
468 }
469
470 /// Sets up `vcpu`.
471 ///
472 /// AArch64 needs vcpus set up before its kernel IRQ chip is created, so `configure_vcpu_early`
473 /// is called from `build_vm` on the main thread. `LinuxArch::configure_vcpu`, which is used
474 /// by X86_64 to do setup later from the vcpu thread, is a no-op on AArch64 since vcpus were
475 /// already configured here.
476 ///
477 /// # Arguments
478 ///
479 /// * `guest_mem` - The guest memory object.
480 /// * `vcpu` - The vcpu to configure.
481 /// * `vcpu_id` - The VM's index for `vcpu`.
482 /// * `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, ) -> Result<()>483 fn configure_vcpu_early(
484 guest_mem: &GuestMemory,
485 vcpu: &dyn VcpuAArch64,
486 vcpu_id: usize,
487 use_pmu: bool,
488 has_bios: bool,
489 ) -> Result<()> {
490 let mut features = vec![VcpuFeature::PsciV0_2];
491 if use_pmu {
492 features.push(VcpuFeature::PmuV3);
493 }
494 // Non-boot cpus are powered off initially
495 if vcpu_id != 0 {
496 features.push(VcpuFeature::PowerOff)
497 }
498 vcpu.init(&features).map_err(Error::VcpuInit)?;
499
500 // All interrupts masked
501 let pstate = PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | PSR_MODE_EL1H;
502 vcpu.set_one_reg(arm64_core_reg!(pstate), pstate)
503 .map_err(Error::SetReg)?;
504
505 // Other cpus are powered off initially
506 if vcpu_id == 0 {
507 let entry_addr = if has_bios {
508 AARCH64_PHYS_MEM_START + AARCH64_BIOS_OFFSET
509 } else {
510 AARCH64_PHYS_MEM_START + AARCH64_KERNEL_OFFSET
511 };
512 vcpu.set_one_reg(arm64_core_reg!(pc), entry_addr)
513 .map_err(Error::SetReg)?;
514
515 /* X0 -- fdt address */
516 let mem_size = guest_mem.memory_size();
517 let fdt_addr = (AARCH64_PHYS_MEM_START + fdt_offset(mem_size, has_bios)) as u64;
518 // hack -- can't get this to do offsetof(regs[0]) but luckily it's at offset 0
519 vcpu.set_one_reg(arm64_core_reg!(regs), fdt_addr)
520 .map_err(Error::SetReg)?;
521 }
522
523 Ok(())
524 }
525 }
526