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: {0}")] 34 WriteFdtToMemory(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: {0}")] 46 RegsConfiguration(hypervisor::HypervisorCpuError), 47 } 48 49 impl From<Error> for super::Error { 50 fn from(e: Error) -> super::Error { 51 super::Error::PlatformSpecific(e) 52 } 53 } 54 55 #[derive(Debug, Copy, Clone)] 56 /// Specifies the entry point address where the guest must start 57 /// executing code. 58 pub struct EntryPoint { 59 /// Address in guest memory where the guest must start execution 60 pub entry_addr: GuestAddress, 61 } 62 63 /// Configure the specified VCPU, and return its MPIDR. 64 pub fn configure_vcpu( 65 vcpu: &Arc<dyn hypervisor::Vcpu>, 66 id: u8, 67 boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>, 68 ) -> super::Result<()> { 69 if let Some((kernel_entry_point, _guest_memory)) = boot_setup { 70 vcpu.setup_regs( 71 id, 72 kernel_entry_point.entry_addr.raw_value(), 73 layout::FDT_START.raw_value(), 74 ) 75 .map_err(Error::RegsConfiguration)?; 76 } 77 78 Ok(()) 79 } 80 81 pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> { 82 vec![ 83 // 0 MiB ~ 256 MiB: AIA and legacy devices 84 ( 85 GuestAddress(0), 86 layout::MEM_32BIT_DEVICES_START.0 as usize, 87 RegionType::Reserved, 88 ), 89 // 256 MiB ~ 768 MiB: MMIO space 90 ( 91 layout::MEM_32BIT_DEVICES_START, 92 layout::MEM_32BIT_DEVICES_SIZE as usize, 93 RegionType::SubRegion, 94 ), 95 // 768 MiB ~ 1 GiB: reserved. The leading 256M for PCIe MMCONFIG space 96 ( 97 layout::PCI_MMCONFIG_START, 98 layout::PCI_MMCONFIG_SIZE as usize, 99 RegionType::Reserved, 100 ), 101 // 1GiB ~ inf: RAM 102 (layout::RAM_START, usize::MAX, RegionType::Ram), 103 ] 104 } 105 106 /// Configures the system and should be called once per vm before starting vcpu threads. 107 #[allow(clippy::too_many_arguments)] 108 pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>( 109 guest_mem: &GuestMemoryMmap, 110 cmdline: &str, 111 num_vcpu: u32, 112 device_info: &HashMap<(DeviceType, String), T, S>, 113 initrd: &Option<super::InitramfsConfig>, 114 pci_space_info: &[PciSpaceInfo], 115 aia_device: &Arc<Mutex<dyn Vaia>>, 116 ) -> super::Result<()> { 117 let fdt_final = fdt::create_fdt( 118 guest_mem, 119 cmdline, 120 num_vcpu, 121 device_info, 122 aia_device, 123 initrd, 124 pci_space_info, 125 ) 126 .map_err(|_| Error::SetupFdt)?; 127 128 if log_enabled!(Level::Debug) { 129 fdt::print_fdt(&fdt_final); 130 } 131 132 fdt::write_fdt_to_memory(fdt_final, guest_mem).map_err(Error::WriteFdtToMemory)?; 133 134 Ok(()) 135 } 136 137 /// Returns the memory address where the initramfs could be loaded. 138 pub fn initramfs_load_addr( 139 guest_mem: &GuestMemoryMmap, 140 initramfs_size: usize, 141 ) -> super::Result<u64> { 142 let round_to_pagesize = |size| (size + (super::PAGE_SIZE - 1)) & !(super::PAGE_SIZE - 1); 143 match guest_mem 144 .last_addr() 145 .checked_sub(round_to_pagesize(initramfs_size) as u64 - 1) 146 { 147 Some(offset) => { 148 if guest_mem.address_in_range(offset) { 149 Ok(offset.raw_value()) 150 } else { 151 Err(super::Error::PlatformSpecific(Error::InitramfsAddress)) 152 } 153 } 154 None => Err(super::Error::PlatformSpecific(Error::InitramfsAddress)), 155 } 156 } 157 158 pub fn get_host_cpu_phys_bits(_hypervisor: &Arc<dyn hypervisor::Hypervisor>) -> u8 { 159 40 160 } 161 162 #[cfg(test)] 163 mod tests { 164 use super::*; 165 166 #[test] 167 fn test_arch_memory_regions_dram() { 168 let regions = arch_memory_regions(); 169 assert_eq!(4, regions.len()); 170 assert_eq!(layout::RAM_START, regions[3].0); 171 assert_eq!(RegionType::Ram, regions[3].2); 172 } 173 } 174