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