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