xref: /cloud-hypervisor/hypervisor/src/kvm/riscv64/aia.rs (revision 30cf1eed5e63499f3101ed320fc384b59c60fc6b)
1 // Copyright © 2024 Institute of Software, CAS. All rights reserved.
2 //
3 // SPDX-License-Identifier: Apache-2.0
4 
5 use std::any::Any;
6 
7 use kvm_ioctls::DeviceFd;
8 use serde::{Deserialize, Serialize};
9 
10 use crate::arch::riscv64::aia::{Error, Result, Vaia, VaiaConfig};
11 use crate::device::HypervisorDeviceError;
12 use crate::kvm::KvmVm;
13 use crate::Vm;
14 
15 pub struct KvmAiaImsics {
16     /// The KVM device for the Aia
17     device: DeviceFd,
18 
19     /// AIA APLIC address
20     aplic_addr: u64,
21 
22     /// AIA APLIC size
23     aplic_size: u64,
24 
25     /// AIA IMSIC address
26     imsic_addr: u64,
27 
28     /// AIA IMSIC size
29     imsic_size: u64,
30 
31     /// Number of CPUs handled by the device
32     vcpu_count: u64,
33 }
34 
35 #[derive(Clone, Default, Serialize, Deserialize)]
36 pub struct AiaImsicsState {}
37 
38 impl KvmAiaImsics {
39     /// Device trees specific constants
40 
41     fn version() -> u32 {
42         kvm_bindings::kvm_device_type_KVM_DEV_TYPE_RISCV_AIA
43     }
44 
45     /// Setup the device-specific attributes
46     fn init_device_attributes(&mut self, nr_irqs: u32) -> Result<()> {
47         // AIA part attributes
48         // Getting the working mode of RISC-V AIA, defaults to EMUL, passible
49         // variants are EMUL, HW_ACCL, AUTO
50         let mut aia_mode = kvm_bindings::KVM_DEV_RISCV_AIA_MODE_EMUL;
51         Self::get_device_attribute(
52             &self.device,
53             kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CONFIG,
54             u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CONFIG_MODE),
55             &mut aia_mode as *mut u32 as u64,
56             0,
57         )?;
58 
59         // Report AIA MODE
60 
61         // Setting up the number of wired interrupt sources
62         Self::set_device_attribute(
63             &self.device,
64             kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CONFIG,
65             u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CONFIG_SRCS),
66             &nr_irqs as *const u32 as u64,
67             0,
68         )?;
69 
70         // Getting the number of ids
71         let mut aia_nr_ids: u32 = 0;
72         Self::get_device_attribute(
73             &self.device,
74             kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CONFIG,
75             u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CONFIG_IDS),
76             &mut aia_nr_ids as *mut u32 as u64,
77             0,
78         )?;
79 
80         // Report NR_IDS
81 
82         // Setting up hart_bits
83         let max_hart_index = self.vcpu_count - 1;
84         let hart_bits = std::cmp::max(64 - max_hart_index.leading_zeros(), 1);
85         Self::set_device_attribute(
86             &self.device,
87             kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CONFIG,
88             u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CONFIG_HART_BITS),
89             &hart_bits as *const u32 as u64,
90             0,
91         )?;
92 
93         // Designate addresses of APLIC and IMSICS
94 
95         // Setting up RISC-V APLIC
96         Self::set_device_attribute(
97             &self.device,
98             kvm_bindings::KVM_DEV_RISCV_AIA_GRP_ADDR,
99             u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_ADDR_APLIC),
100             &self.aplic_addr as *const u64 as u64,
101             0,
102         )?;
103 
104         // Helpers to calculate address and attribute of IMSIC of each vCPU
105         let riscv_imsic_addr_of =
106             |cpu_index: u64| -> u64 { self.imsic_addr + cpu_index * self.imsic_size };
107         let riscv_imsic_attr_of = |cpu_index: u64| -> u64 { cpu_index };
108 
109         // Setting up RISC-V IMSICs
110         for cpu_index in 0..self.vcpu_count {
111             let cpu_imsic_addr = riscv_imsic_addr_of(cpu_index);
112             Self::set_device_attribute(
113                 &self.device,
114                 kvm_bindings::KVM_DEV_RISCV_AIA_GRP_ADDR,
115                 riscv_imsic_attr_of(cpu_index),
116                 &cpu_imsic_addr as *const u64 as u64,
117                 0,
118             )?;
119         }
120 
121         // Finalizing the AIA device
122         Self::set_device_attribute(
123             &self.device,
124             kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CTRL,
125             u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CTRL_INIT),
126             0,
127             0,
128         )
129     }
130 
131     /// Create a KVM Vaia device
132     fn create_device(vm: &KvmVm) -> Result<DeviceFd> {
133         let mut aia_device = kvm_bindings::kvm_create_device {
134             type_: Self::version(),
135             fd: 0,
136             flags: 0,
137         };
138 
139         let device_fd = vm
140             .create_device(&mut aia_device)
141             .map_err(Error::CreateAia)?;
142 
143         // We know for sure this is a KVM fd
144         Ok(device_fd.to_kvm().unwrap())
145     }
146 
147     /// Get an AIA device attribute
148     fn get_device_attribute(
149         device: &DeviceFd,
150         group: u32,
151         attr: u64,
152         addr: u64,
153         flags: u32,
154     ) -> Result<()> {
155         let mut attr = kvm_bindings::kvm_device_attr {
156             flags,
157             group,
158             attr,
159             addr,
160         };
161         // SAFETY: attr.addr is safe to write to.
162         unsafe {
163             device.get_device_attr(&mut attr).map_err(|e| {
164                 Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
165             })
166         }
167     }
168 
169     /// Set an AIA device attribute
170     fn set_device_attribute(
171         device: &DeviceFd,
172         group: u32,
173         attr: u64,
174         addr: u64,
175         flags: u32,
176     ) -> Result<()> {
177         let attr = kvm_bindings::kvm_device_attr {
178             flags,
179             group,
180             attr,
181             addr,
182         };
183         device.set_device_attr(&attr).map_err(|e| {
184             Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
185         })
186     }
187 
188     /// Method to initialize the AIA device
189     pub fn new(vm: &dyn Vm, config: VaiaConfig) -> Result<KvmAiaImsics> {
190         // This is inside KVM module
191         let vm = vm.as_any().downcast_ref::<KvmVm>().expect("Wrong VM type?");
192 
193         let vaia = Self::create_device(vm)?;
194 
195         let mut aia_device = KvmAiaImsics {
196             device: vaia,
197             vcpu_count: config.vcpu_count,
198             aplic_addr: config.aplic_addr,
199             aplic_size: config.aplic_size,
200             imsic_addr: config.imsic_addr,
201             imsic_size: config.imsic_size,
202         };
203 
204         aia_device.init_device_attributes(config.nr_irqs)?;
205 
206         Ok(aia_device)
207     }
208 }
209 
210 impl Vaia for KvmAiaImsics {
211     fn aplic_compatibility(&self) -> &str {
212         "riscv,aplic"
213     }
214 
215     fn aplic_properties(&self) -> [u64; 4] {
216         [0, self.aplic_addr, 0, self.aplic_size]
217     }
218 
219     fn imsic_compatibility(&self) -> &str {
220         "riscv,imsics"
221     }
222 
223     fn imsic_properties(&self) -> [u64; 4] {
224         [0, self.imsic_addr, 0, self.imsic_size * self.vcpu_count]
225     }
226 
227     fn vcpu_count(&self) -> u64 {
228         self.vcpu_count
229     }
230 
231     fn msi_compatible(&self) -> bool {
232         true
233     }
234 
235     fn as_any_concrete_mut(&mut self) -> &mut dyn Any {
236         self
237     }
238 
239     /// Save the state of AIA.
240     fn state(&self) -> Result<AiaImsicsState> {
241         unimplemented!()
242     }
243 
244     /// Restore the state of AIA_IMSICs.
245     fn set_state(&mut self, _state: &AiaImsicsState) -> Result<()> {
246         unimplemented!()
247     }
248 }
249 
250 #[cfg(test)]
251 mod tests {
252     use crate::arch::riscv64::aia::VaiaConfig;
253     use crate::kvm::KvmAiaImsics;
254 
255     fn create_test_vaia_config() -> VaiaConfig {
256         VaiaConfig {
257             vcpu_count: 1,
258             aplic_addr: 0xd000000,
259             aplic_size: 0x4000,
260             imsic_addr: 0x2800000,
261             imsic_size: 0x1000,
262             nr_irqs: 256,
263         }
264     }
265 
266     #[test]
267     fn test_create_aia() {
268         let hv = crate::new().unwrap();
269         let vm = hv.create_vm().unwrap();
270 
271         assert!(KvmAiaImsics::new(&*vm, create_test_vaia_config()).is_ok());
272     }
273 }
274