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