mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-08 19:33:07 +00:00
aarch64: provide initial vcpu regs via vcpu_init
The code from configure_vcpu_early() is split into two parts: 1. vcpu_features() returns the `VcpuFeature`s required for vcpu.init() 2. vcpu_init() returns the general register state This makes use of the new generic vcpu_init code path and moves the general purpose register initialization into configure_vcpu() rather than the ARM-specific confgiure_vcpu_early() to be consistent with other architectures. Only the vcpu.init() call needs to happen before irqchip is finalized on ARM, not other register setup. BUG=b:237095693 TEST=tools/presubmit --all TEST=Boot Crostini on trogdor Change-Id: Ib3eab946ba9f1e407f339c2119d36d280655066f Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3786979 Reviewed-by: Junichi Uekawa <uekawa@chromium.org> Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
parent
5097b46a62
commit
e8fe4b2400
2 changed files with 117 additions and 46 deletions
|
@ -360,30 +360,39 @@ impl arch::LinuxArch for AArch64 {
|
|||
}
|
||||
};
|
||||
|
||||
let memory_end = GuestAddress(AARCH64_PHYS_MEM_START + components.memory_size);
|
||||
let fdt_offset = fdt_address(memory_end, has_bios);
|
||||
|
||||
let mut use_pmu = vm
|
||||
.get_hypervisor()
|
||||
.check_capability(HypervisorCap::ArmPmuV3);
|
||||
let vcpu_count = components.vcpu_count;
|
||||
let mut has_pvtime = true;
|
||||
let mut vcpus = Vec::with_capacity(vcpu_count);
|
||||
let mut vcpu_init = Vec::with_capacity(vcpu_count);
|
||||
for vcpu_id in 0..vcpu_count {
|
||||
let vcpu: Vcpu = *vm
|
||||
.create_vcpu(vcpu_id)
|
||||
.map_err(Error::CreateVcpu)?
|
||||
.downcast::<Vcpu>()
|
||||
.map_err(|_| Error::DowncastVcpu)?;
|
||||
Self::configure_vcpu_early(
|
||||
vm.get_memory(),
|
||||
&vcpu,
|
||||
let per_vcpu_init = Self::vcpu_init(
|
||||
vcpu_id,
|
||||
use_pmu,
|
||||
has_bios,
|
||||
fdt_offset,
|
||||
image_size,
|
||||
components.hv_cfg.protection_type,
|
||||
)?;
|
||||
);
|
||||
has_pvtime &= vcpu.has_pvtime_support();
|
||||
vcpus.push(vcpu);
|
||||
vcpu_ids.push(vcpu_id);
|
||||
vcpu_init.push(per_vcpu_init);
|
||||
}
|
||||
|
||||
// Initialize Vcpus after all Vcpu objects have been created.
|
||||
for (vcpu_id, vcpu) in vcpus.iter().enumerate() {
|
||||
vcpu.init(&Self::vcpu_features(vcpu_id, use_pmu))
|
||||
.map_err(Error::VcpuInit)?;
|
||||
}
|
||||
|
||||
irq_chip.finalize().map_err(Error::FinalizeIrqChip)?;
|
||||
|
@ -576,7 +585,6 @@ impl arch::LinuxArch for AArch64 {
|
|||
timeout_sec: VMWDT_DEFAULT_TIMEOUT_SEC,
|
||||
};
|
||||
|
||||
let memory_end = GuestAddress(AARCH64_PHYS_MEM_START + components.memory_size);
|
||||
fdt::create_fdt(
|
||||
AARCH64_FDT_MAX_SIZE as usize,
|
||||
&mem,
|
||||
|
@ -586,7 +594,7 @@ impl arch::LinuxArch for AArch64 {
|
|||
vcpu_count as u32,
|
||||
components.cpu_clusters,
|
||||
components.cpu_capacity,
|
||||
fdt_address(memory_end, has_bios),
|
||||
fdt_offset,
|
||||
cmdline.as_str(),
|
||||
initrd,
|
||||
components.android_fstab,
|
||||
|
@ -599,8 +607,6 @@ impl arch::LinuxArch for AArch64 {
|
|||
)
|
||||
.map_err(Error::CreateFdt)?;
|
||||
|
||||
let vcpu_init = vec![VcpuInitAArch64::default(); vcpu_count];
|
||||
|
||||
Ok(RunnableLinuxVm {
|
||||
vm,
|
||||
vcpu_count,
|
||||
|
@ -631,14 +637,16 @@ impl arch::LinuxArch for AArch64 {
|
|||
_vm: &V,
|
||||
_hypervisor: &dyn Hypervisor,
|
||||
_irq_chip: &mut dyn IrqChipAArch64,
|
||||
_vcpu: &mut dyn VcpuAArch64,
|
||||
_vcpu_init: VcpuInitAArch64,
|
||||
vcpu: &mut dyn VcpuAArch64,
|
||||
vcpu_init: VcpuInitAArch64,
|
||||
_vcpu_id: usize,
|
||||
_num_cpus: usize,
|
||||
_has_bios: bool,
|
||||
_cpu_config: Option<CpuConfigAArch64>,
|
||||
) -> std::result::Result<(), Self::Error> {
|
||||
// AArch64 doesn't configure vcpus on the vcpu thread, so nothing to do here.
|
||||
for (reg, value) in vcpu_init.regs.iter() {
|
||||
vcpu.set_one_reg(*reg, *value).map_err(Error::SetReg)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -808,42 +816,42 @@ impl AArch64 {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets up `vcpu`.
|
||||
///
|
||||
/// AArch64 needs vcpus set up before its kernel IRQ chip is created, so `configure_vcpu_early`
|
||||
/// is called from `build_vm` on the main thread. `LinuxArch::configure_vcpu`, which is used
|
||||
/// by X86_64 to do setup later from the vcpu thread, is a no-op on AArch64 since vcpus were
|
||||
/// already configured here.
|
||||
/// Get ARM-specific features for vcpu with index `vcpu_id`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `guest_mem` - The guest memory object.
|
||||
/// * `vcpu` - The vcpu to configure.
|
||||
/// * `vcpu_id` - The VM's index for `vcpu`.
|
||||
/// * `use_pmu` - Should `vcpu` be configured to use the Performance Monitor Unit.
|
||||
fn configure_vcpu_early(
|
||||
guest_mem: &GuestMemory,
|
||||
vcpu: &dyn VcpuAArch64,
|
||||
vcpu_id: usize,
|
||||
use_pmu: bool,
|
||||
has_bios: bool,
|
||||
image_size: usize,
|
||||
protection_type: ProtectionType,
|
||||
) -> Result<()> {
|
||||
fn vcpu_features(vcpu_id: usize, use_pmu: bool) -> Vec<VcpuFeature> {
|
||||
let mut features = vec![VcpuFeature::PsciV0_2];
|
||||
if use_pmu {
|
||||
features.push(VcpuFeature::PmuV3);
|
||||
}
|
||||
// Non-boot cpus are powered off initially
|
||||
if vcpu_id != 0 {
|
||||
features.push(VcpuFeature::PowerOff)
|
||||
features.push(VcpuFeature::PowerOff);
|
||||
}
|
||||
vcpu.init(&features).map_err(Error::VcpuInit)?;
|
||||
|
||||
features
|
||||
}
|
||||
|
||||
/// Get initial register state for vcpu with index `vcpu_id`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `vcpu_id` - The VM's index for `vcpu`.
|
||||
fn vcpu_init(
|
||||
vcpu_id: usize,
|
||||
has_bios: bool,
|
||||
fdt_address: GuestAddress,
|
||||
image_size: usize,
|
||||
protection_type: ProtectionType,
|
||||
) -> VcpuInitAArch64 {
|
||||
let mut regs: BTreeMap<VcpuRegAArch64, u64> = Default::default();
|
||||
|
||||
// All interrupts masked
|
||||
let pstate = PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | PSR_MODE_EL1H;
|
||||
vcpu.set_one_reg(VcpuRegAArch64::Pstate, pstate)
|
||||
.map_err(Error::SetReg)?;
|
||||
regs.insert(VcpuRegAArch64::Pstate, pstate);
|
||||
|
||||
// Other cpus are powered off initially
|
||||
if vcpu_id == 0 {
|
||||
|
@ -863,28 +871,22 @@ impl AArch64 {
|
|||
|
||||
/* PC -- entry point */
|
||||
if let Some(entry) = entry_addr {
|
||||
vcpu.set_one_reg(VcpuRegAArch64::Pc, entry)
|
||||
.map_err(Error::SetReg)?;
|
||||
regs.insert(VcpuRegAArch64::Pc, entry);
|
||||
}
|
||||
|
||||
/* X0 -- fdt address */
|
||||
let memory_end = guest_mem.end_addr();
|
||||
let fdt_addr = fdt_address(memory_end, has_bios);
|
||||
vcpu.set_one_reg(VcpuRegAArch64::X(0), fdt_addr.offset())
|
||||
.map_err(Error::SetReg)?;
|
||||
regs.insert(VcpuRegAArch64::X(0), fdt_address.offset());
|
||||
|
||||
if protection_type.runs_firmware() {
|
||||
/* X1 -- payload entry point */
|
||||
vcpu.set_one_reg(VcpuRegAArch64::X(1), image_addr.offset())
|
||||
.map_err(Error::SetReg)?;
|
||||
regs.insert(VcpuRegAArch64::X(1), image_addr.offset());
|
||||
|
||||
/* X2 -- image size */
|
||||
vcpu.set_one_reg(VcpuRegAArch64::X(2), image_size as u64)
|
||||
.map_err(Error::SetReg)?;
|
||||
regs.insert(VcpuRegAArch64::X(2), image_size as u64);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
VcpuInitAArch64 { regs }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -912,3 +914,66 @@ impl MsrHandlers {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn vcpu_init_unprotected_kernel() {
|
||||
let has_bios = false;
|
||||
let fdt_address = GuestAddress(0x1234);
|
||||
let image_size = 0x1000;
|
||||
let prot = ProtectionType::Unprotected;
|
||||
|
||||
let vcpu_init = AArch64::vcpu_init(0, has_bios, fdt_address, image_size, prot);
|
||||
|
||||
// PC: kernel image entry point
|
||||
assert_eq!(vcpu_init.regs.get(&VcpuRegAArch64::Pc), Some(&0x8080_0000));
|
||||
|
||||
// X0: fdt_offset
|
||||
assert_eq!(vcpu_init.regs.get(&VcpuRegAArch64::X(0)), Some(&0x1234));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vcpu_init_unprotected_bios() {
|
||||
let has_bios = true;
|
||||
let fdt_address = GuestAddress(0x1234);
|
||||
let image_size = 0x1000;
|
||||
let prot = ProtectionType::Unprotected;
|
||||
|
||||
let vcpu_init = AArch64::vcpu_init(0, has_bios, fdt_address, image_size, prot);
|
||||
|
||||
// PC: bios image entry point
|
||||
assert_eq!(vcpu_init.regs.get(&VcpuRegAArch64::Pc), Some(&0x8020_0000));
|
||||
|
||||
// X0: fdt_offset
|
||||
assert_eq!(vcpu_init.regs.get(&VcpuRegAArch64::X(0)), Some(&0x1234));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vcpu_init_protected_kernel() {
|
||||
let has_bios = false;
|
||||
let fdt_address = GuestAddress(0x1234);
|
||||
let image_size = 0x1000;
|
||||
let prot = ProtectionType::Protected;
|
||||
|
||||
let vcpu_init = AArch64::vcpu_init(0, has_bios, fdt_address, image_size, prot);
|
||||
|
||||
// The hypervisor provides the initial value of PC, so PC should not be present in the
|
||||
// vcpu_init register map.
|
||||
assert_eq!(vcpu_init.regs.get(&VcpuRegAArch64::Pc), None);
|
||||
|
||||
// X0: fdt_offset
|
||||
assert_eq!(vcpu_init.regs.get(&VcpuRegAArch64::X(0)), Some(&0x1234));
|
||||
|
||||
// X1: kernel image entry point
|
||||
assert_eq!(
|
||||
vcpu_init.regs.get(&VcpuRegAArch64::X(1)),
|
||||
Some(&0x8080_0000)
|
||||
);
|
||||
|
||||
// X2: image size
|
||||
assert_eq!(vcpu_init.regs.get(&VcpuRegAArch64::X(2)), Some(&0x1000));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use base::Error;
|
||||
|
@ -49,6 +50,7 @@ impl TryFrom<u32> for PsciVersion {
|
|||
pub const PSCI_0_2: PsciVersion = PsciVersion { major: 0, minor: 2 };
|
||||
pub const PSCI_1_0: PsciVersion = PsciVersion { major: 1, minor: 0 };
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum VcpuRegAArch64 {
|
||||
X(u8),
|
||||
Sp,
|
||||
|
@ -125,9 +127,13 @@ pub trait VcpuAArch64: Vcpu {
|
|||
|
||||
impl_downcast!(VcpuAArch64);
|
||||
|
||||
/// Initial state for AArch64 VCPUs.
|
||||
/// Initial register state for AArch64 VCPUs.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct VcpuInitAArch64 {}
|
||||
pub struct VcpuInitAArch64 {
|
||||
/// Initial register state as a map of register name to value pairs. Registers that do not have
|
||||
/// a value specified in this map will retain the original value provided by the hypervisor.
|
||||
pub regs: BTreeMap<VcpuRegAArch64, u64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CpuConfigAArch64 {}
|
||||
|
|
Loading…
Reference in a new issue