xref: /cloud-hypervisor/arch/src/riscv64/mod.rs (revision 190a11f2124b0b60a2d44e85b7c9988373acfb6d)
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