• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&param).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