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