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