1 // Copyright © 2024 Institute of Software, CAS. All rights reserved. 2 // Copyright 2020 Arm Limited (or its affiliates). All rights reserved. 3 // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 // SPDX-License-Identifier: Apache-2.0 5 6 /// Module for the flattened device tree. 7 pub mod fdt; 8 /// Layout for this riscv64 system. 9 pub mod layout; 10 11 use std::collections::HashMap; 12 use std::fmt::Debug; 13 use std::sync::{Arc, Mutex}; 14 15 use hypervisor::arch::riscv64::aia::Vaia; 16 use log::{log_enabled, Level}; 17 use thiserror::Error; 18 use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic}; 19 20 pub use self::fdt::DeviceInfoForFdt; 21 use crate::{DeviceType, GuestMemoryMmap, PciSpaceInfo, RegionType}; 22 23 pub const _NSIG: i32 = 65; 24 25 /// Errors thrown while configuring riscv64 system. 26 #[derive(Debug, Error)] 27 pub enum Error { 28 /// Failed to create a FDT. 29 #[error("Failed to create a FDT")] 30 SetupFdt, 31 32 /// Failed to write FDT to memory. 33 #[error("Failed to write FDT to memory")] 34 WriteFdtToMemory(#[source] fdt::Error), 35 36 /// Failed to create a AIA. 37 #[error("Failed to create a AIA")] 38 SetupAia, 39 40 /// Failed to compute the initramfs address. 41 #[error("Failed to compute the initramfs address")] 42 InitramfsAddress, 43 44 /// Error configuring the general purpose registers 45 #[error("Error configuring the general purpose registers")] 46 RegsConfiguration(#[source] hypervisor::HypervisorCpuError), 47 } 48 49 #[derive(Debug, Copy, Clone)] 50 /// Specifies the entry point address where the guest must start 51 /// executing code. 52 pub struct EntryPoint { 53 /// Address in guest memory where the guest must start execution 54 pub entry_addr: GuestAddress, 55 } 56 57 /// Configure the specified VCPU, and return its MPIDR. 58 pub fn configure_vcpu( 59 vcpu: &Arc<dyn hypervisor::Vcpu>, 60 id: u8, 61 boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>, 62 ) -> super::Result<()> { 63 if let Some((kernel_entry_point, _guest_memory)) = boot_setup { 64 vcpu.setup_regs( 65 id, 66 kernel_entry_point.entry_addr.raw_value(), 67 layout::FDT_START.raw_value(), 68 ) 69 .map_err(Error::RegsConfiguration)?; 70 } 71 72 Ok(()) 73 } 74 75 pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> { 76 vec![ 77 // 0 MiB ~ 256 MiB: AIA and legacy devices 78 ( 79 GuestAddress(0), 80 layout::MEM_32BIT_DEVICES_START.0 as usize, 81 RegionType::Reserved, 82 ), 83 // 256 MiB ~ 768 MiB: MMIO space 84 ( 85 layout::MEM_32BIT_DEVICES_START, 86 layout::MEM_32BIT_DEVICES_SIZE as usize, 87 RegionType::SubRegion, 88 ), 89 // 768 MiB ~ 1 GiB: reserved. The leading 256M for PCIe MMCONFIG space 90 ( 91 layout::PCI_MMCONFIG_START, 92 layout::PCI_MMCONFIG_SIZE as usize, 93 RegionType::Reserved, 94 ), 95 // 1GiB ~ inf: RAM 96 (layout::RAM_START, usize::MAX, RegionType::Ram), 97 ] 98 } 99 100 /// Configures the system and should be called once per vm before starting vcpu threads. 101 #[allow(clippy::too_many_arguments)] 102 pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>( 103 guest_mem: &GuestMemoryMmap, 104 cmdline: &str, 105 num_vcpu: u32, 106 device_info: &HashMap<(DeviceType, String), T, S>, 107 initrd: &Option<super::InitramfsConfig>, 108 pci_space_info: &[PciSpaceInfo], 109 aia_device: &Arc<Mutex<dyn Vaia>>, 110 ) -> super::Result<()> { 111 let fdt_final = fdt::create_fdt( 112 guest_mem, 113 cmdline, 114 num_vcpu, 115 device_info, 116 aia_device, 117 initrd, 118 pci_space_info, 119 ) 120 .map_err(|_| Error::SetupFdt)?; 121 122 if log_enabled!(Level::Debug) { 123 fdt::print_fdt(&fdt_final); 124 } 125 126 fdt::write_fdt_to_memory(fdt_final, guest_mem).map_err(Error::WriteFdtToMemory)?; 127 128 Ok(()) 129 } 130 131 /// Returns the memory address where the initramfs could be loaded. 132 pub fn initramfs_load_addr( 133 guest_mem: &GuestMemoryMmap, 134 initramfs_size: usize, 135 ) -> super::Result<u64> { 136 let round_to_pagesize = |size| (size + (super::PAGE_SIZE - 1)) & !(super::PAGE_SIZE - 1); 137 match guest_mem 138 .last_addr() 139 .checked_sub(round_to_pagesize(initramfs_size) as u64 - 1) 140 { 141 Some(offset) => { 142 if guest_mem.address_in_range(offset) { 143 Ok(offset.raw_value()) 144 } else { 145 Err(super::Error::PlatformSpecific(Error::InitramfsAddress)) 146 } 147 } 148 None => Err(super::Error::PlatformSpecific(Error::InitramfsAddress)), 149 } 150 } 151 152 pub fn get_host_cpu_phys_bits(_hypervisor: &Arc<dyn hypervisor::Hypervisor>) -> u8 { 153 40 154 } 155 156 #[cfg(test)] 157 mod tests { 158 use super::*; 159 160 #[test] 161 fn test_arch_memory_regions_dram() { 162 let regions = arch_memory_regions(); 163 assert_eq!(4, regions.len()); 164 assert_eq!(layout::RAM_START, regions[3].0); 165 assert_eq!(RegionType::Ram, regions[3].2); 166 } 167 } 168