2020-06-03 05:01:56 +00:00
|
|
|
// Copyright 2020 Arm Limited (or its affiliates). All rights reserved.
|
2019-02-25 21:53:01 +00:00
|
|
|
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2020-06-03 05:01:56 +00:00
|
|
|
/// Module for the flattened device tree.
|
|
|
|
pub mod fdt;
|
2020-04-20 02:03:00 +00:00
|
|
|
/// Module for the global interrupt controller configuration.
|
|
|
|
pub mod gic;
|
|
|
|
/// Layout for this aarch64 system.
|
2019-02-25 21:53:01 +00:00
|
|
|
pub mod layout;
|
2020-06-03 04:50:09 +00:00
|
|
|
/// Logic for configuring aarch64 registers.
|
|
|
|
pub mod regs;
|
2019-02-25 21:53:01 +00:00
|
|
|
|
2021-03-25 17:01:21 +00:00
|
|
|
pub use self::fdt::DeviceInfoForFdt;
|
2020-06-09 07:24:23 +00:00
|
|
|
use crate::DeviceType;
|
2021-06-02 19:08:04 +00:00
|
|
|
use crate::GuestMemoryMmap;
|
2020-05-12 09:49:12 +00:00
|
|
|
use crate::RegionType;
|
2021-05-11 14:19:04 +00:00
|
|
|
use gic::GicDevice;
|
2020-06-03 05:01:56 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::ffi::CStr;
|
2020-06-03 04:50:09 +00:00
|
|
|
use std::fmt::Debug;
|
2020-06-02 02:29:54 +00:00
|
|
|
use std::sync::Arc;
|
2020-06-03 04:50:09 +00:00
|
|
|
use vm_memory::{
|
2021-06-02 19:08:04 +00:00
|
|
|
Address, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic, GuestUsize,
|
2020-06-03 04:50:09 +00:00
|
|
|
};
|
2020-05-28 07:27:22 +00:00
|
|
|
|
2020-06-03 05:01:56 +00:00
|
|
|
/// Errors thrown while configuring aarch64 system.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum Error {
|
2020-06-09 10:28:02 +00:00
|
|
|
/// Failed to create a FDT.
|
2021-05-07 02:34:37 +00:00
|
|
|
SetupFdt,
|
|
|
|
|
|
|
|
/// Failed to write FDT to memory.
|
|
|
|
WriteFdtToMemory(fdt::Error),
|
2020-06-09 07:24:23 +00:00
|
|
|
|
2020-06-09 10:28:02 +00:00
|
|
|
/// Failed to create a GIC.
|
2021-03-25 17:01:21 +00:00
|
|
|
SetupGic(gic::Error),
|
2020-06-09 10:28:02 +00:00
|
|
|
|
2020-07-14 10:11:03 +00:00
|
|
|
/// Failed to compute the initramfs address.
|
|
|
|
InitramfsAddress,
|
2020-06-09 07:24:23 +00:00
|
|
|
|
|
|
|
/// Error configuring the general purpose registers
|
2021-03-25 17:01:21 +00:00
|
|
|
RegsConfiguration(regs::Error),
|
2020-06-09 07:24:23 +00:00
|
|
|
|
2020-08-28 09:06:54 +00:00
|
|
|
/// Error configuring the MPIDR register
|
2021-03-25 17:01:21 +00:00
|
|
|
VcpuRegMpidr(hypervisor::HypervisorCpuError),
|
2020-06-03 05:01:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Error> for super::Error {
|
|
|
|
fn from(e: Error) -> super::Error {
|
|
|
|
super::Error::AArch64Setup(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-09 07:24:23 +00:00
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
/// Specifies the entry point address where the guest must start
|
|
|
|
/// executing code.
|
|
|
|
pub struct EntryPoint {
|
|
|
|
/// Address in guest memory where the guest must start execution
|
|
|
|
pub entry_addr: GuestAddress,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Configure the specified VCPU, and return its MPIDR.
|
|
|
|
pub fn configure_vcpu(
|
2020-06-02 02:29:54 +00:00
|
|
|
fd: &Arc<dyn hypervisor::Vcpu>,
|
2020-06-09 07:24:23 +00:00
|
|
|
id: u8,
|
|
|
|
kernel_entry_point: Option<EntryPoint>,
|
|
|
|
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
|
|
|
|
) -> super::Result<u64> {
|
|
|
|
if let Some(kernel_entry_point) = kernel_entry_point {
|
|
|
|
regs::setup_regs(
|
|
|
|
fd,
|
|
|
|
id,
|
|
|
|
kernel_entry_point.entry_addr.raw_value(),
|
|
|
|
&vm_memory.memory(),
|
|
|
|
)
|
2021-03-25 17:01:21 +00:00
|
|
|
.map_err(Error::RegsConfiguration)?;
|
2020-06-09 07:24:23 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 17:01:21 +00:00
|
|
|
let mpidr = fd.read_mpidr().map_err(Error::VcpuRegMpidr)?;
|
2020-06-09 07:24:23 +00:00
|
|
|
Ok(mpidr)
|
|
|
|
}
|
2020-06-03 05:01:56 +00:00
|
|
|
|
|
|
|
pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> {
|
2021-02-02 12:27:14 +00:00
|
|
|
// Normally UEFI should be loaded to a flash area at the beginning of memory.
|
|
|
|
// But now flash memory type is not supported.
|
|
|
|
// As a workaround, we take 64 MiB memory from the main RAM for UEFI.
|
|
|
|
// As a result, the RAM that the guest can see is less than what has been
|
|
|
|
// assigned in command line, when ACPI and UEFI is enabled.
|
|
|
|
let ram_deduction = if cfg!(feature = "acpi") {
|
|
|
|
layout::UEFI_SIZE
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
2021-03-25 17:01:21 +00:00
|
|
|
vec![
|
2021-02-02 12:27:14 +00:00
|
|
|
// 0 ~ 64 MiB: Reserved for UEFI space
|
|
|
|
#[cfg(feature = "acpi")]
|
|
|
|
(GuestAddress(0), layout::UEFI_SIZE as usize, RegionType::Ram),
|
|
|
|
#[cfg(not(feature = "acpi"))]
|
2021-03-25 17:01:21 +00:00
|
|
|
(
|
|
|
|
GuestAddress(0),
|
2021-02-02 12:27:14 +00:00
|
|
|
layout::UEFI_SIZE as usize,
|
|
|
|
RegionType::Reserved,
|
|
|
|
),
|
|
|
|
// 64 MiB ~ 256 MiB: Gic and legacy devices
|
|
|
|
(
|
|
|
|
GuestAddress(layout::UEFI_SIZE),
|
|
|
|
(layout::MEM_32BIT_DEVICES_START.0 - layout::UEFI_SIZE) as usize,
|
2021-03-25 17:01:21 +00:00
|
|
|
RegionType::Reserved,
|
|
|
|
),
|
2021-02-02 12:27:14 +00:00
|
|
|
// 256 MiB ~ 768 MiB: MMIO space
|
2021-03-25 17:01:21 +00:00
|
|
|
(
|
|
|
|
layout::MEM_32BIT_DEVICES_START,
|
|
|
|
layout::MEM_32BIT_DEVICES_SIZE as usize,
|
|
|
|
RegionType::SubRegion,
|
|
|
|
),
|
2021-02-02 12:27:14 +00:00
|
|
|
// 768 MiB ~ 1 GiB: reserved. The leading 256M for PCIe MMCONFIG space
|
2021-03-25 17:01:21 +00:00
|
|
|
(
|
|
|
|
layout::PCI_MMCONFIG_START,
|
2021-02-02 12:27:14 +00:00
|
|
|
layout::PCI_MMCONFIG_SIZE as usize,
|
2021-03-25 17:01:21 +00:00
|
|
|
RegionType::Reserved,
|
|
|
|
),
|
2021-02-02 12:27:14 +00:00
|
|
|
// 1 GiB ~ : Ram
|
2021-03-25 17:01:21 +00:00
|
|
|
(
|
|
|
|
GuestAddress(layout::RAM_64BIT_START),
|
2021-02-02 12:27:14 +00:00
|
|
|
(size - ram_deduction) as usize,
|
2021-03-25 17:01:21 +00:00
|
|
|
RegionType::Ram,
|
|
|
|
),
|
|
|
|
]
|
2020-06-03 05:01:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Configures the system and should be called once per vm before starting vcpu threads.
|
2021-03-25 17:01:21 +00:00
|
|
|
pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
|
2020-06-03 05:01:56 +00:00
|
|
|
guest_mem: &GuestMemoryMmap,
|
|
|
|
cmdline_cstring: &CStr,
|
|
|
|
vcpu_mpidr: Vec<u64>,
|
2020-08-23 07:44:57 +00:00
|
|
|
device_info: &HashMap<(DeviceType, String), T, S>,
|
2020-06-09 03:27:09 +00:00
|
|
|
initrd: &Option<super::InitramfsConfig>,
|
2020-10-21 09:52:21 +00:00
|
|
|
pci_space_address: &(u64, u64),
|
2021-05-31 14:30:24 +00:00
|
|
|
gic_device: &dyn GicDevice,
|
|
|
|
) -> super::Result<()> {
|
2021-05-07 02:34:37 +00:00
|
|
|
let fdt_final = fdt::create_fdt(
|
2020-06-03 05:01:56 +00:00
|
|
|
guest_mem,
|
|
|
|
cmdline_cstring,
|
|
|
|
vcpu_mpidr,
|
|
|
|
device_info,
|
2021-05-31 14:30:24 +00:00
|
|
|
gic_device,
|
2020-06-03 05:01:56 +00:00
|
|
|
initrd,
|
2020-06-03 08:30:33 +00:00
|
|
|
pci_space_address,
|
2020-06-03 05:01:56 +00:00
|
|
|
)
|
2021-05-07 02:34:37 +00:00
|
|
|
.map_err(|_| Error::SetupFdt)?;
|
|
|
|
|
|
|
|
fdt::write_fdt_to_memory(fdt_final, guest_mem).map_err(Error::WriteFdtToMemory)?;
|
2020-06-03 05:01:56 +00:00
|
|
|
|
2021-05-31 14:30:24 +00:00
|
|
|
Ok(())
|
2019-02-25 21:53:01 +00:00
|
|
|
}
|
|
|
|
|
2020-07-14 10:11:03 +00:00
|
|
|
/// Returns the memory address where the initramfs could be loaded.
|
|
|
|
pub fn initramfs_load_addr(
|
|
|
|
guest_mem: &GuestMemoryMmap,
|
|
|
|
initramfs_size: usize,
|
|
|
|
) -> super::Result<u64> {
|
|
|
|
let round_to_pagesize = |size| (size + (super::PAGE_SIZE - 1)) & !(super::PAGE_SIZE - 1);
|
2021-02-02 12:27:14 +00:00
|
|
|
match guest_mem
|
|
|
|
.last_addr()
|
|
|
|
.checked_sub(round_to_pagesize(initramfs_size) as u64 - 1)
|
2020-07-14 10:11:03 +00:00
|
|
|
{
|
|
|
|
Some(offset) => {
|
|
|
|
if guest_mem.address_in_range(offset) {
|
|
|
|
Ok(offset.raw_value())
|
|
|
|
} else {
|
|
|
|
Err(super::Error::AArch64Setup(Error::InitramfsAddress))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => Err(super::Error::AArch64Setup(Error::InitramfsAddress)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-03 05:01:56 +00:00
|
|
|
/// Returns the memory address where the kernel could be loaded.
|
|
|
|
pub fn get_kernel_start() -> u64 {
|
2021-02-02 12:27:14 +00:00
|
|
|
layout::KERNEL_START
|
2019-02-25 21:53:01 +00:00
|
|
|
}
|
2020-05-12 09:49:12 +00:00
|
|
|
|
2020-06-03 04:50:09 +00:00
|
|
|
// Auxiliary function to get the address where the device tree blob is loaded.
|
2021-02-02 12:27:14 +00:00
|
|
|
fn get_fdt_addr() -> u64 {
|
|
|
|
layout::FDT_START
|
2020-06-03 04:50:09 +00:00
|
|
|
}
|
|
|
|
|
2020-05-12 09:49:12 +00:00
|
|
|
pub fn get_host_cpu_phys_bits() -> u8 {
|
|
|
|
// The value returned here is used to determine the physical address space size
|
|
|
|
// for a VM (IPA size).
|
2020-09-22 11:31:42 +00:00
|
|
|
// In recent kernel versions, the maximum IPA size supported by the host can be
|
2020-05-12 09:49:12 +00:00
|
|
|
// known by querying cap KVM_CAP_ARM_VM_IPA_SIZE. And the IPA size for a
|
|
|
|
// guest can be configured smaller.
|
2020-09-22 11:31:42 +00:00
|
|
|
// But in Cloud-Hypervisor we simply use the maximum value for the VM.
|
2020-05-12 09:49:12 +00:00
|
|
|
// Reference https://lwn.net/Articles/766767/.
|
|
|
|
//
|
|
|
|
// The correct way to query KVM_CAP_ARM_VM_IPA_SIZE is via rust-vmm/kvm-ioctls,
|
|
|
|
// which wraps all IOCTL's and provides easy interface to user hypervisors.
|
|
|
|
// For now the cap hasn't been supported. A separate patch will be submitted to
|
|
|
|
// rust-vmm to add it.
|
|
|
|
// So a hardcoded value is used here as a temporary solution.
|
|
|
|
// It will be replace once rust-vmm/kvm-ioctls is ready.
|
|
|
|
//
|
|
|
|
40
|
|
|
|
}
|
|
|
|
|
2020-06-03 05:01:56 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_arch_memory_regions_dram() {
|
|
|
|
let regions = arch_memory_regions((1usize << 32) as u64); //4GB
|
2021-02-02 12:27:14 +00:00
|
|
|
assert_eq!(5, regions.len());
|
|
|
|
assert_eq!(GuestAddress(layout::RAM_64BIT_START), regions[4].0);
|
|
|
|
assert_eq!(1usize << 32, regions[4].1);
|
|
|
|
assert_eq!(RegionType::Ram, regions[4].2);
|
2020-06-03 05:01:56 +00:00
|
|
|
}
|
|
|
|
}
|