diff --git a/arch/src/lib.rs b/arch/src/lib.rs index 6cbe44573..88eb1af9b 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -1,3 +1,4 @@ +// Copyright © 2024 Institute of Software, CAS. All rights reserved. // Copyright 2020 Arm Limited (or its affiliates). All rights reserved. // Copyright © 2020, Oracle and/or its affiliates. // @@ -5,7 +6,7 @@ // SPDX-License-Identifier: Apache-2.0 //! Implements platform specific functionality. -//! Supported platforms: x86_64, aarch64. +//! Supported platforms: x86_64, aarch64, riscv64. #[macro_use] extern crate log; @@ -32,6 +33,9 @@ pub enum Error { #[cfg(target_arch = "aarch64")] #[error("Platform specific error (aarch64): {0:?}")] PlatformSpecific(aarch64::Error), + #[cfg(target_arch = "riscv64")] + #[error("Platform specific error (riscv64): {0:?}")] + PlatformSpecific(riscv64::Error), #[error("The memory map table extends past the end of guest memory")] MemmapTablePastRamEnd, #[error("Error writing memory map table to guest memory")] @@ -85,6 +89,17 @@ pub use aarch64::{ layout::IRQ_BASE, uefi, EntryPoint, _NSIG, }; +/// Module for riscv64 related functionality. +#[cfg(target_arch = "riscv64")] +pub mod riscv64; + +#[cfg(target_arch = "riscv64")] +pub use riscv64::{ + arch_memory_regions, configure_system, configure_vcpu, fdt::DeviceInfoForFdt, + get_host_cpu_phys_bits, initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE, + layout::IRQ_BASE, EntryPoint, _NSIG, +}; + #[cfg(target_arch = "x86_64")] pub mod x86_64; @@ -132,7 +147,7 @@ pub enum DeviceType { /// Device Type: Virtio. Virtio(u32), /// Device Type: Serial. - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] Serial, /// Device Type: RTC. #[cfg(target_arch = "aarch64")] @@ -153,7 +168,7 @@ impl fmt::Display for DeviceType { /// Structure to describe MMIO device information #[derive(Clone, Debug)] -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] pub struct MmioDeviceInfo { pub addr: u64, pub len: u64, @@ -162,7 +177,7 @@ pub struct MmioDeviceInfo { /// Structure to describe PCI space information #[derive(Clone, Debug)] -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] pub struct PciSpaceInfo { pub pci_segment_id: u16, pub mmio_config_address: u64, @@ -170,7 +185,7 @@ pub struct PciSpaceInfo { pub pci_device_space_size: u64, } -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] impl DeviceInfoForFdt for MmioDeviceInfo { fn addr(&self) -> u64 { self.addr diff --git a/arch/src/riscv64/mod.rs b/arch/src/riscv64/mod.rs new file mode 100644 index 000000000..4230dc344 --- /dev/null +++ b/arch/src/riscv64/mod.rs @@ -0,0 +1,173 @@ +// Copyright © 2024 Institute of Software, CAS. All rights reserved. +// Copyright 2020 Arm Limited (or its affiliates). All rights reserved. +// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/// Module for the flattened device tree. +pub mod fdt; +/// Layout for this riscv64 system. +pub mod layout; + +use std::collections::HashMap; +use std::fmt::Debug; +use std::sync::{Arc, Mutex}; + +use hypervisor::arch::riscv64::aia::Vaia; +use log::{log_enabled, Level}; +use thiserror::Error; +use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic}; + +pub use self::fdt::DeviceInfoForFdt; +use crate::{DeviceType, GuestMemoryMmap, PciSpaceInfo, RegionType}; + +pub const _NSIG: i32 = 65; + +/// Errors thrown while configuring riscv64 system. +#[derive(Debug, Error)] +pub enum Error { + /// Failed to create a FDT. + #[error("Failed to create a FDT")] + SetupFdt, + + /// Failed to write FDT to memory. + #[error("Failed to write FDT to memory: {0}")] + WriteFdtToMemory(fdt::Error), + + /// Failed to create a AIA. + #[error("Failed to create a AIA")] + SetupAia, + + /// Failed to compute the initramfs address. + #[error("Failed to compute the initramfs address")] + InitramfsAddress, + + /// Error configuring the general purpose registers + #[error("Error configuring the general purpose registers: {0}")] + RegsConfiguration(hypervisor::HypervisorCpuError), +} + +impl From for super::Error { + fn from(e: Error) -> super::Error { + super::Error::PlatformSpecific(e) + } +} + +#[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( + vcpu: &Arc, + id: u8, + boot_setup: Option<(EntryPoint, &GuestMemoryAtomic)>, +) -> super::Result<()> { + if let Some((kernel_entry_point, _guest_memory)) = boot_setup { + vcpu.setup_regs( + id, + kernel_entry_point.entry_addr.raw_value(), + layout::FDT_START.raw_value(), + ) + .map_err(Error::RegsConfiguration)?; + } + + Ok(()) +} + +pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> { + vec![ + // 0 MiB ~ 256 MiB: AIA and legacy devices + ( + GuestAddress(0), + layout::MEM_32BIT_DEVICES_START.0 as usize, + RegionType::Reserved, + ), + // 256 MiB ~ 768 MiB: MMIO space + ( + layout::MEM_32BIT_DEVICES_START, + layout::MEM_32BIT_DEVICES_SIZE as usize, + RegionType::SubRegion, + ), + // 768 MiB ~ 1 GiB: reserved. The leading 256M for PCIe MMCONFIG space + ( + layout::PCI_MMCONFIG_START, + layout::PCI_MMCONFIG_SIZE as usize, + RegionType::Reserved, + ), + // 1GiB ~ inf: RAM + (layout::RAM_START, usize::MAX, RegionType::Ram), + ] +} + +/// Configures the system and should be called once per vm before starting vcpu threads. +#[allow(clippy::too_many_arguments)] +pub fn configure_system( + guest_mem: &GuestMemoryMmap, + cmdline: &str, + num_vcpu: u32, + device_info: &HashMap<(DeviceType, String), T, S>, + initrd: &Option, + pci_space_info: &[PciSpaceInfo], + aia_device: &Arc>, +) -> super::Result<()> { + let fdt_final = fdt::create_fdt( + guest_mem, + cmdline, + num_vcpu, + device_info, + aia_device, + initrd, + pci_space_info, + ) + .map_err(|_| Error::SetupFdt)?; + + if log_enabled!(Level::Debug) { + fdt::print_fdt(&fdt_final); + } + + fdt::write_fdt_to_memory(fdt_final, guest_mem).map_err(Error::WriteFdtToMemory)?; + + Ok(()) +} + +/// Returns the memory address where the initramfs could be loaded. +pub fn initramfs_load_addr( + guest_mem: &GuestMemoryMmap, + initramfs_size: usize, +) -> super::Result { + let round_to_pagesize = |size| (size + (super::PAGE_SIZE - 1)) & !(super::PAGE_SIZE - 1); + match guest_mem + .last_addr() + .checked_sub(round_to_pagesize(initramfs_size) as u64 - 1) + { + Some(offset) => { + if guest_mem.address_in_range(offset) { + Ok(offset.raw_value()) + } else { + Err(super::Error::PlatformSpecific(Error::InitramfsAddress)) + } + } + None => Err(super::Error::PlatformSpecific(Error::InitramfsAddress)), + } +} + +pub fn get_host_cpu_phys_bits(_hypervisor: &Arc) -> u8 { + 40 +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_arch_memory_regions_dram() { + let regions = arch_memory_regions(); + assert_eq!(4, regions.len()); + assert_eq!(layout::RAM_START, regions[3].0); + assert_eq!(RegionType::Ram, regions[3].2); + } +}