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