|
5 | 5 |
|
6 | 6 | use std::fs::{File, OpenOptions}; |
7 | 7 | use std::os::unix::io::{AsRawFd, RawFd}; |
| 8 | +use std::sync::Arc; |
8 | 9 |
|
9 | 10 | use iommufd_bindings::iommufd::*; |
10 | 11 | use vmm_sys_util::errno::Error as SysError; |
@@ -68,6 +69,313 @@ impl IommuFd { |
68 | 69 | } |
69 | 70 | } |
70 | 71 |
|
| 72 | +#[derive(Debug, Copy, Clone)] |
| 73 | +pub enum IommufdInvalidateData { |
| 74 | + Smmuv3(iommu_viommu_arm_smmuv3_invalidate), |
| 75 | + Vtd(iommu_hwpt_vtd_s1_invalidate), |
| 76 | +} |
| 77 | + |
| 78 | +#[derive(Clone)] |
| 79 | +pub struct IommufdVIommu { |
| 80 | + pub iommufd: Arc<IommuFd>, |
| 81 | + pub viommu_id: u32, |
| 82 | + pub dev_id: u32, |
| 83 | + pub s2_hwpt_id: u32, |
| 84 | + pub bypass_hwpt_id: u32, |
| 85 | + pub abort_hwpt_id: u32, |
| 86 | +} |
| 87 | + |
| 88 | +impl IommufdVIommu { |
| 89 | + /// Create a new vIOMMU instance |
| 90 | + /// # Arguments |
| 91 | + /// * `iommufd` - The iommufd instance to use |
| 92 | + /// * `ioas_id` - The IOAS ID to associate with the vIOMMU |
| 93 | + /// * `dev_id` - The device ID of the VFIO device |
| 94 | + /// * `s1_hwpt_data_type` - The s1 hwpt data type |
| 95 | + pub fn new( |
| 96 | + iommufd: Arc<IommuFd>, |
| 97 | + ioas_id: u32, |
| 98 | + dev_id: u32, |
| 99 | + s1_hwpt_data_type: iommu_hwpt_data_type, |
| 100 | + ) -> Result<Self> { |
| 101 | + if s1_hwpt_data_type != iommu_hwpt_data_type_IOMMU_HWPT_DATA_ARM_SMMUV3 { |
| 102 | + return Err(IommufdError::UnsupportedS1HwptDataType(s1_hwpt_data_type)); |
| 103 | + } |
| 104 | + |
| 105 | + // Refer to “5.2 Stream Table Entry” in SMMUv3 HW Specification |
| 106 | + const SMMU_STE_VALID: u64 = 1 << 0; |
| 107 | + const SMMU_STE_CFG_BYPASS: u64 = 1 << 3; |
| 108 | + |
| 109 | + // Allocate s2_hwpt who will be shared for all devices behind this vIOMMU instance |
| 110 | + let mut s2_iommufd_hwpt_alloc = iommu_hwpt_alloc { |
| 111 | + size: std::mem::size_of::<iommu_hwpt_alloc>() as u32, |
| 112 | + flags: iommufd_hwpt_alloc_flags_IOMMU_HWPT_ALLOC_NEST_PARENT, |
| 113 | + dev_id, |
| 114 | + pt_id: ioas_id, |
| 115 | + data_type: iommu_hwpt_data_type_IOMMU_HWPT_DATA_NONE, |
| 116 | + ..Default::default() |
| 117 | + }; |
| 118 | + iommufd.alloc_iommu_hwpt(&mut s2_iommufd_hwpt_alloc)?; |
| 119 | + let s2_hwpt_id = s2_iommufd_hwpt_alloc.out_hwpt_id; |
| 120 | + |
| 121 | + // Allocate vIOMMU |
| 122 | + let mut viommu_alloc = iommu_viommu_alloc { |
| 123 | + size: std::mem::size_of::<iommu_viommu_alloc>() as u32, |
| 124 | + type_: iommu_viommu_type_IOMMU_VIOMMU_TYPE_ARM_SMMUV3, |
| 125 | + hwpt_id: s2_hwpt_id, |
| 126 | + dev_id, |
| 127 | + ..Default::default() |
| 128 | + }; |
| 129 | + iommufd.alloc_iommu_viommu(&mut viommu_alloc)?; |
| 130 | + let viommu_id = viommu_alloc.out_viommu_id; |
| 131 | + |
| 132 | + // ALlocate bypass s1_hwpt which will be used when the virtual IOMMU |
| 133 | + // is not initilized by the guest |
| 134 | + let bypass_s1_hwpt_data = iommu_hwpt_arm_smmuv3 { |
| 135 | + ste: [SMMU_STE_CFG_BYPASS | SMMU_STE_VALID, 0x0], |
| 136 | + }; |
| 137 | + let mut bypass_iommufd_hwpt_alloc = iommu_hwpt_alloc { |
| 138 | + size: std::mem::size_of::<iommu_hwpt_alloc>() as u32, |
| 139 | + dev_id, |
| 140 | + pt_id: s2_hwpt_id, |
| 141 | + data_type: s1_hwpt_data_type, |
| 142 | + data_len: std::mem::size_of::<iommu_hwpt_arm_smmuv3>() as u32, |
| 143 | + data_uptr: &bypass_s1_hwpt_data as *const iommu_hwpt_arm_smmuv3 as u64, |
| 144 | + ..Default::default() |
| 145 | + }; |
| 146 | + iommufd.alloc_iommu_hwpt(&mut bypass_iommufd_hwpt_alloc)?; |
| 147 | + let bypass_hwpt_id = bypass_iommufd_hwpt_alloc.out_hwpt_id; |
| 148 | + |
| 149 | + // Allocate abort s1_hwpt which will be used when the virtual IOMMU |
| 150 | + // is configured in such mode |
| 151 | + let abort_s1_hwpt_data = iommu_hwpt_arm_smmuv3 { |
| 152 | + ste: [SMMU_STE_VALID, 0x0], |
| 153 | + }; |
| 154 | + let mut abort_iommufd_hwpt_alloc = iommu_hwpt_alloc { |
| 155 | + size: std::mem::size_of::<iommu_hwpt_alloc>() as u32, |
| 156 | + dev_id, |
| 157 | + pt_id: s2_hwpt_id, |
| 158 | + data_type: s1_hwpt_data_type, |
| 159 | + data_len: std::mem::size_of::<iommu_hwpt_arm_smmuv3>() as u32, |
| 160 | + data_uptr: &abort_s1_hwpt_data as *const iommu_hwpt_arm_smmuv3 as u64, |
| 161 | + ..Default::default() |
| 162 | + }; |
| 163 | + iommufd.alloc_iommu_hwpt(&mut abort_iommufd_hwpt_alloc)?; |
| 164 | + let abort_hwpt_id = abort_iommufd_hwpt_alloc.out_hwpt_id; |
| 165 | + |
| 166 | + Ok(IommufdVIommu { |
| 167 | + iommufd, |
| 168 | + viommu_id, |
| 169 | + dev_id, |
| 170 | + s2_hwpt_id, |
| 171 | + bypass_hwpt_id, |
| 172 | + abort_hwpt_id, |
| 173 | + }) |
| 174 | + } |
| 175 | + |
| 176 | + /// Invalidate a hwpt entry |
| 177 | + /// # Arguments |
| 178 | + /// * `cmd` - The invalidate data |
| 179 | + /// Returns: |
| 180 | + /// * `Ok(true)` if the entry is invalidated |
| 181 | + /// * `Ok(false)` if the entry is not invalidated |
| 182 | + pub fn invalidate_hwpt(&self, cmd: &mut IommufdInvalidateData) -> Result<bool> { |
| 183 | + match cmd { |
| 184 | + IommufdInvalidateData::Smmuv3(data) => { |
| 185 | + let mut hw_invalidate = iommu_hwpt_invalidate { |
| 186 | + size: std::mem::size_of::<iommu_hwpt_invalidate>() as u32, |
| 187 | + hwpt_id: self.viommu_id, |
| 188 | + data_type: |
| 189 | + iommu_hwpt_invalidate_data_type_IOMMU_VIOMMU_INVALIDATE_DATA_ARM_SMMUV3, |
| 190 | + entry_len: std::mem::size_of::<iommu_viommu_arm_smmuv3_invalidate>() as u32, |
| 191 | + entry_num: 1, |
| 192 | + data_uptr: data as *mut iommu_viommu_arm_smmuv3_invalidate as u64, |
| 193 | + ..Default::default() |
| 194 | + }; |
| 195 | + self.iommufd.invalidate_hwpt(&mut hw_invalidate)?; |
| 196 | + |
| 197 | + if hw_invalidate.entry_num == 1 { |
| 198 | + Ok(true) |
| 199 | + } else { |
| 200 | + Ok(false) |
| 201 | + } |
| 202 | + } |
| 203 | + IommufdInvalidateData::Vtd(_) => { |
| 204 | + unimplemented!() |
| 205 | + } |
| 206 | + } |
| 207 | + } |
| 208 | +} |
| 209 | + |
| 210 | +impl Drop for IommufdVIommu { |
| 211 | + fn drop(&mut self) { |
| 212 | + self.iommufd |
| 213 | + .destroy_iommufd(self.viommu_id) |
| 214 | + .inspect_err(|e| { |
| 215 | + eprintln!("Failed to destroy vIOMMU id {}: {}", self.viommu_id, e); |
| 216 | + }) |
| 217 | + .unwrap(); |
| 218 | + |
| 219 | + self.iommufd |
| 220 | + .destroy_iommufd(self.s2_hwpt_id) |
| 221 | + .inspect_err(|e| { |
| 222 | + eprintln!("Failed to destroy s2_hwpt id {}: {}", self.s2_hwpt_id, e); |
| 223 | + }) |
| 224 | + .unwrap(); |
| 225 | + |
| 226 | + self.iommufd |
| 227 | + .destroy_iommufd(self.bypass_hwpt_id) |
| 228 | + .inspect_err(|e| { |
| 229 | + eprintln!( |
| 230 | + "Failed to destroy bypass_hwpt id {}: {}", |
| 231 | + self.bypass_hwpt_id, e |
| 232 | + ); |
| 233 | + }) |
| 234 | + .unwrap(); |
| 235 | + |
| 236 | + self.iommufd |
| 237 | + .destroy_iommufd(self.abort_hwpt_id) |
| 238 | + .inspect_err(|e| { |
| 239 | + eprintln!( |
| 240 | + "Failed to destroy abort_hwpt id {}: {}", |
| 241 | + self.abort_hwpt_id, e |
| 242 | + ); |
| 243 | + }) |
| 244 | + .unwrap(); |
| 245 | + } |
| 246 | +} |
| 247 | + |
| 248 | +#[derive(Debug, Copy, Clone)] |
| 249 | +pub enum IommufdHwInfoData { |
| 250 | + Smmuv3(iommu_hw_info_arm_smmuv3), |
| 251 | + Vtd(iommu_hw_info_vtd), |
| 252 | +} |
| 253 | + |
| 254 | +#[derive(Debug, Copy, Clone)] |
| 255 | +pub enum IommufdHwptData { |
| 256 | + Smmuv3(iommu_hwpt_arm_smmuv3), |
| 257 | + Vtd(iommu_hwpt_vtd_s1), |
| 258 | +} |
| 259 | + |
| 260 | +#[derive(Clone)] |
| 261 | +pub struct IommufdVDevice { |
| 262 | + pub viommu: Arc<IommufdVIommu>, |
| 263 | + pub dev_id: u32, |
| 264 | + pub virt_id: u64, |
| 265 | + pub vdevice_id: u32, |
| 266 | + pub s1_hwpt_id: Option<u32>, |
| 267 | +} |
| 268 | + |
| 269 | +impl IommufdVDevice { |
| 270 | + /// Create a new vDevice instance |
| 271 | + /// # Arguments |
| 272 | + /// * `viommu` - The vIOMMU instance the vDevice is associated with |
| 273 | + /// * `dev_id` - The device ID of the vDevice |
| 274 | + /// * `virt_id` - The virtual Stream ID of the vDevice |
| 275 | + pub fn new(viommu: Arc<IommufdVIommu>, dev_id: u32, virt_id: u64) -> Result<Self> { |
| 276 | + let mut vdevice_alloc = iommu_vdevice_alloc { |
| 277 | + size: std::mem::size_of::<iommu_vdevice_alloc>() as u32, |
| 278 | + viommu_id: viommu.viommu_id, |
| 279 | + dev_id, |
| 280 | + virt_id, |
| 281 | + ..Default::default() |
| 282 | + }; |
| 283 | + viommu.iommufd.alloc_iommu_vdevice(&mut vdevice_alloc)?; |
| 284 | + |
| 285 | + Ok(IommufdVDevice { |
| 286 | + viommu, |
| 287 | + dev_id, |
| 288 | + virt_id, |
| 289 | + vdevice_id: vdevice_alloc.out_vdevice_id, |
| 290 | + s1_hwpt_id: None, |
| 291 | + }) |
| 292 | + } |
| 293 | + |
| 294 | + /// Allocate s1 hwpt for the vDevice |
| 295 | + pub fn allocate_s1_hwpt(&mut self, hwpt_data: &IommufdHwptData) -> Result<u32> { |
| 296 | + if self.s1_hwpt_id.is_some() { |
| 297 | + return Err(IommufdError::S1HwptAlreadyAllocated(self.vdevice_id)); |
| 298 | + } |
| 299 | + |
| 300 | + match hwpt_data { |
| 301 | + IommufdHwptData::Smmuv3(data) => { |
| 302 | + let mut s1_iommufd_hwpt_alloc = iommu_hwpt_alloc { |
| 303 | + size: std::mem::size_of::<iommu_hwpt_alloc>() as u32, |
| 304 | + dev_id: self.dev_id, |
| 305 | + pt_id: self.viommu.viommu_id, |
| 306 | + data_type: iommu_hwpt_data_type_IOMMU_HWPT_DATA_ARM_SMMUV3, |
| 307 | + data_len: std::mem::size_of::<iommu_hwpt_arm_smmuv3>() as u32, |
| 308 | + data_uptr: data as *const iommu_hwpt_arm_smmuv3 as u64, |
| 309 | + ..Default::default() |
| 310 | + }; |
| 311 | + self.viommu |
| 312 | + .iommufd |
| 313 | + .alloc_iommu_hwpt(&mut s1_iommufd_hwpt_alloc)?; |
| 314 | + |
| 315 | + let s1_hwpt_id = s1_iommufd_hwpt_alloc.out_hwpt_id; |
| 316 | + self.s1_hwpt_id = Some(s1_hwpt_id); |
| 317 | + |
| 318 | + Ok(s1_hwpt_id) |
| 319 | + } |
| 320 | + IommufdHwptData::Vtd(_) => unimplemented!(), |
| 321 | + } |
| 322 | + } |
| 323 | + |
| 324 | + /// Destroy s1 hwpt for the vDevice |
| 325 | + pub fn destroy_s1_hwpt(&mut self) -> Result<()> { |
| 326 | + if let Some(s1_hwpt_id) = self.s1_hwpt_id { |
| 327 | + self.viommu.iommufd.destroy_iommufd(s1_hwpt_id)?; |
| 328 | + self.s1_hwpt_id = None; |
| 329 | + } |
| 330 | + Ok(()) |
| 331 | + } |
| 332 | + |
| 333 | + /// Get device hardware information |
| 334 | + pub fn get_device_hw_info( |
| 335 | + &self, |
| 336 | + hw_info_data: &mut IommufdHwInfoData, |
| 337 | + ) -> Result<iommu_hw_info> { |
| 338 | + let mut hw_info = match hw_info_data { |
| 339 | + IommufdHwInfoData::Smmuv3(data) => iommu_hw_info { |
| 340 | + size: std::mem::size_of::<iommu_hw_info>() as u32, |
| 341 | + dev_id: self.dev_id, |
| 342 | + data_len: std::mem::size_of::<iommu_hw_info_arm_smmuv3>() as u32, |
| 343 | + data_uptr: data as *mut _ as u64, |
| 344 | + ..Default::default() |
| 345 | + }, |
| 346 | + IommufdHwInfoData::Vtd(_) => { |
| 347 | + unimplemented!() |
| 348 | + } |
| 349 | + }; |
| 350 | + |
| 351 | + self.viommu.iommufd.get_hw_info(&mut hw_info)?; |
| 352 | + |
| 353 | + Ok(hw_info) |
| 354 | + } |
| 355 | +} |
| 356 | + |
| 357 | +impl Drop for IommufdVDevice { |
| 358 | + fn drop(&mut self) { |
| 359 | + self.viommu |
| 360 | + .iommufd |
| 361 | + .destroy_iommufd(self.vdevice_id) |
| 362 | + .inspect_err(|e| { |
| 363 | + eprintln!("Failed to destroy vDevice id {}: {}", self.vdevice_id, e); |
| 364 | + }) |
| 365 | + .unwrap(); |
| 366 | + |
| 367 | + if let Some(s1_hwpt_id) = self.s1_hwpt_id { |
| 368 | + self.viommu |
| 369 | + .iommufd |
| 370 | + .destroy_iommufd(s1_hwpt_id) |
| 371 | + .inspect_err(|e| { |
| 372 | + eprintln!("Failed to destroy s1_hwpt id {}: {}", s1_hwpt_id, e); |
| 373 | + }) |
| 374 | + .unwrap(); |
| 375 | + } |
| 376 | + } |
| 377 | +} |
| 378 | + |
71 | 379 | impl AsRawFd for IommuFd { |
72 | 380 | fn as_raw_fd(&self) -> RawFd { |
73 | 381 | self.iommufd.as_raw_fd() |
@@ -112,6 +420,8 @@ ioctl_io_nr!( |
112 | 420 | IOMMUFD_CMD_VDEVICE_ALLOC |
113 | 421 | ); |
114 | 422 |
|
| 423 | +// IOMMU_HW_QUEUE_ALLOC |
| 424 | + |
115 | 425 | // Safety: |
116 | 426 | // - absolutely trust the underlying kernel |
117 | 427 | // - absolutely trust data returned by the underlying kernel |
|
0 commit comments