1 //! PCI transport for VirtIO. 2 use super::volatile::{ 3 volread, volwrite, ReadOnly, Volatile, VolatileReadable, VolatileWritable, WriteOnly, 4 }; 5 use crate::driver::pci::pci::{ 6 capabilities_offset, pci_bar_init, CapabilityIterator, DeviceFunction, PciDeviceBar, PciError, 7 PCI_CAP_ID_VNDR, 8 }; 9 use crate::include::bindings::bindings::pci_read_config; 10 use core::{ 11 fmt::{self, Display, Formatter}, 12 mem::{align_of, size_of}, 13 ptr::{self, addr_of_mut, NonNull}, 14 }; 15 use virtio_drivers::{ 16 transport::{DeviceStatus, DeviceType, Transport}, 17 Error, Hal, PhysAddr, VirtAddr, 18 }; 19 20 /// The PCI vendor ID for VirtIO devices. 21 /// PCI Virtio设备的vendor ID 22 const VIRTIO_VENDOR_ID: u16 = 0x1af4; 23 24 /// The offset to add to a VirtIO device ID to get the corresponding PCI device ID. 25 /// PCI Virtio设备的DEVICE_ID 的offset 26 const PCI_DEVICE_ID_OFFSET: u16 = 0x1040; 27 /// PCI Virtio 设备的DEVICE_ID及其对应的设备类型 28 const TRANSITIONAL_NETWORK: u16 = 0x1000; 29 const TRANSITIONAL_BLOCK: u16 = 0x1001; 30 const TRANSITIONAL_MEMORY_BALLOONING: u16 = 0x1002; 31 const TRANSITIONAL_CONSOLE: u16 = 0x1003; 32 const TRANSITIONAL_SCSI_HOST: u16 = 0x1004; 33 const TRANSITIONAL_ENTROPY_SOURCE: u16 = 0x1005; 34 const TRANSITIONAL_9P_TRANSPORT: u16 = 0x1009; 35 36 /// The offset of the bar field within `virtio_pci_cap`. 37 const CAP_BAR_OFFSET: u8 = 4; 38 /// The offset of the offset field with `virtio_pci_cap`. 39 const CAP_BAR_OFFSET_OFFSET: u8 = 8; 40 /// The offset of the `length` field within `virtio_pci_cap`. 41 const CAP_LENGTH_OFFSET: u8 = 12; 42 /// The offset of the`notify_off_multiplier` field within `virtio_pci_notify_cap`. 43 const CAP_NOTIFY_OFF_MULTIPLIER_OFFSET: u8 = 16; 44 45 /// Common configuration. 46 const VIRTIO_PCI_CAP_COMMON_CFG: u8 = 1; 47 /// Notifications. 48 const VIRTIO_PCI_CAP_NOTIFY_CFG: u8 = 2; 49 /// ISR Status. 50 const VIRTIO_PCI_CAP_ISR_CFG: u8 = 3; 51 /// Device specific configuration. 52 const VIRTIO_PCI_CAP_DEVICE_CFG: u8 = 4; 53 54 ///@brief device id 转换为设备类型 55 ///@param pci_device_id,device_id 56 ///@return DeviceType 对应的设备类型 57 fn device_type(pci_device_id: u16) -> DeviceType { 58 match pci_device_id { 59 TRANSITIONAL_NETWORK => DeviceType::Network, 60 TRANSITIONAL_BLOCK => DeviceType::Block, 61 TRANSITIONAL_MEMORY_BALLOONING => DeviceType::MemoryBalloon, 62 TRANSITIONAL_CONSOLE => DeviceType::Console, 63 TRANSITIONAL_SCSI_HOST => DeviceType::ScsiHost, 64 TRANSITIONAL_ENTROPY_SOURCE => DeviceType::EntropySource, 65 TRANSITIONAL_9P_TRANSPORT => DeviceType::_9P, 66 id if id >= PCI_DEVICE_ID_OFFSET => DeviceType::from(id - PCI_DEVICE_ID_OFFSET), 67 _ => DeviceType::Invalid, 68 } 69 } 70 71 /// PCI transport for VirtIO. 72 /// 73 /// Ref: 4.1 Virtio Over PCI Bus 74 #[derive(Debug)] 75 pub struct PciTransport { 76 device_type: DeviceType, 77 /// The bus, device and function identifier for the VirtIO device. 78 device_function: DeviceFunction, 79 /// The common configuration structure within some BAR. 80 common_cfg: NonNull<CommonCfg>, 81 /// The start of the queue notification region within some BAR. 82 notify_region: NonNull<[WriteOnly<u16>]>, 83 notify_off_multiplier: u32, 84 /// The ISR status register within some BAR. 85 isr_status: NonNull<Volatile<u8>>, 86 /// The VirtIO device-specific configuration within some BAR. 87 config_space: Option<NonNull<[u32]>>, 88 } 89 90 impl PciTransport { 91 /// Construct a new PCI VirtIO device driver for the given device function on the given PCI 92 /// root controller. 93 /// 94 /// The PCI device must already have had its BARs allocated. 95 pub fn new<H: Hal>(device_function: DeviceFunction) -> Result<Self, VirtioPciError> { 96 let device_vendor = unsafe { 97 let bar_temp = pci_read_config( 98 device_function.bus, 99 device_function.device, 100 device_function.function, 101 0, 102 ); 103 bar_temp 104 }; 105 let device_id = (device_vendor >> 16) as u16; 106 let vendor_id = device_vendor as u16; 107 if vendor_id != VIRTIO_VENDOR_ID { 108 return Err(VirtioPciError::InvalidVendorId(vendor_id)); 109 } 110 let device_type = device_type(device_id); 111 // Find the PCI capabilities we need. 112 let mut common_cfg = None; 113 let mut notify_cfg = None; 114 let mut notify_off_multiplier = 0; 115 let mut isr_cfg = None; 116 let mut device_cfg = None; 117 //device_capability为迭代器,遍历其相当于遍历所有的cap空间 118 let device_capability = CapabilityIterator { 119 device_function: device_function, 120 next_capability_offset: capabilities_offset(device_function), 121 }; 122 let device_bar = pci_bar_init(device_function)?; 123 124 for capability in device_capability { 125 if capability.id != PCI_CAP_ID_VNDR { 126 continue; 127 } 128 let cap_len = capability.private_header as u8; 129 let cfg_type = (capability.private_header >> 8) as u8; 130 if cap_len < 16 { 131 continue; 132 } 133 let struct_info = VirtioCapabilityInfo { 134 bar: unsafe { 135 let temp = pci_read_config( 136 device_function.bus, 137 device_function.device, 138 device_function.function, 139 capability.offset + CAP_BAR_OFFSET, 140 ); 141 temp as u8 142 }, 143 offset: unsafe { 144 let temp = pci_read_config( 145 device_function.bus, 146 device_function.device, 147 device_function.function, 148 capability.offset + CAP_BAR_OFFSET_OFFSET, 149 ); 150 temp 151 }, 152 length: unsafe { 153 let temp = pci_read_config( 154 device_function.bus, 155 device_function.device, 156 device_function.function, 157 capability.offset + CAP_LENGTH_OFFSET, 158 ); 159 temp 160 }, 161 }; 162 163 match cfg_type { 164 VIRTIO_PCI_CAP_COMMON_CFG if common_cfg.is_none() => { 165 common_cfg = Some(struct_info); 166 } 167 VIRTIO_PCI_CAP_NOTIFY_CFG if cap_len >= 20 && notify_cfg.is_none() => { 168 notify_cfg = Some(struct_info); 169 notify_off_multiplier = unsafe { 170 let temp = pci_read_config( 171 device_function.bus, 172 device_function.device, 173 device_function.function, 174 capability.offset + CAP_NOTIFY_OFF_MULTIPLIER_OFFSET, 175 ); 176 temp 177 }; 178 } 179 VIRTIO_PCI_CAP_ISR_CFG if isr_cfg.is_none() => { 180 isr_cfg = Some(struct_info); 181 } 182 VIRTIO_PCI_CAP_DEVICE_CFG if device_cfg.is_none() => { 183 device_cfg = Some(struct_info); 184 } 185 _ => {} 186 } 187 } 188 189 let common_cfg = get_bar_region::<_>( 190 &device_bar, 191 &common_cfg.ok_or(VirtioPciError::MissingCommonConfig)?, 192 )?; 193 194 let notify_cfg = notify_cfg.ok_or(VirtioPciError::MissingNotifyConfig)?; 195 if notify_off_multiplier % 2 != 0 { 196 return Err(VirtioPciError::InvalidNotifyOffMultiplier( 197 notify_off_multiplier, 198 )); 199 } 200 let notify_region = get_bar_region_slice::<_>(&device_bar, ¬ify_cfg)?; 201 let isr_status = get_bar_region::<_>( 202 &device_bar, 203 &isr_cfg.ok_or(VirtioPciError::MissingIsrConfig)?, 204 )?; 205 let config_space = if let Some(device_cfg) = device_cfg { 206 Some(get_bar_region_slice::<_>(&device_bar, &device_cfg)?) 207 } else { 208 None 209 }; 210 Ok(Self { 211 device_type, 212 device_function, 213 common_cfg, 214 notify_region, 215 notify_off_multiplier, 216 isr_status, 217 config_space, 218 }) 219 } 220 } 221 222 impl Transport for PciTransport { 223 fn device_type(&self) -> DeviceType { 224 self.device_type 225 } 226 227 fn read_device_features(&mut self) -> u64 { 228 // Safe because the common config pointer is valid and we checked in get_bar_region that it 229 // was aligned. 230 unsafe { 231 volwrite!(self.common_cfg, device_feature_select, 0); 232 let mut device_features_bits = volread!(self.common_cfg, device_feature) as u64; 233 volwrite!(self.common_cfg, device_feature_select, 1); 234 device_features_bits |= (volread!(self.common_cfg, device_feature) as u64) << 32; 235 device_features_bits 236 } 237 } 238 239 fn write_driver_features(&mut self, driver_features: u64) { 240 // Safe because the common config pointer is valid and we checked in get_bar_region that it 241 // was aligned. 242 unsafe { 243 volwrite!(self.common_cfg, driver_feature_select, 0); 244 volwrite!(self.common_cfg, driver_feature, driver_features as u32); 245 volwrite!(self.common_cfg, driver_feature_select, 1); 246 volwrite!( 247 self.common_cfg, 248 driver_feature, 249 (driver_features >> 32) as u32 250 ); 251 } 252 } 253 254 fn max_queue_size(&self) -> u32 { 255 // Safe because the common config pointer is valid and we checked in get_bar_region that it 256 // was aligned. 257 unsafe { volread!(self.common_cfg, queue_size) }.into() 258 } 259 260 fn notify(&mut self, queue: u16) { 261 // Safe because the common config and notify region pointers are valid and we checked in 262 // get_bar_region that they were aligned. 263 unsafe { 264 volwrite!(self.common_cfg, queue_select, queue); 265 // TODO: Consider caching this somewhere (per queue). 266 let queue_notify_off = volread!(self.common_cfg, queue_notify_off); 267 268 let offset_bytes = usize::from(queue_notify_off) * self.notify_off_multiplier as usize; 269 let index = offset_bytes / size_of::<u16>(); 270 addr_of_mut!((*self.notify_region.as_ptr())[index]).vwrite(queue); 271 } 272 } 273 274 fn set_status(&mut self, status: DeviceStatus) { 275 // Safe because the common config pointer is valid and we checked in get_bar_region that it 276 // was aligned. 277 unsafe { 278 volwrite!(self.common_cfg, device_status, status.bits() as u8); 279 } 280 } 281 282 fn set_guest_page_size(&mut self, _guest_page_size: u32) { 283 // No-op, the PCI transport doesn't care. 284 } 285 286 fn queue_set( 287 &mut self, 288 queue: u16, 289 size: u32, 290 descriptors: PhysAddr, 291 driver_area: PhysAddr, 292 device_area: PhysAddr, 293 ) { 294 // Safe because the common config pointer is valid and we checked in get_bar_region that it 295 // was aligned. 296 unsafe { 297 volwrite!(self.common_cfg, queue_select, queue); 298 volwrite!(self.common_cfg, queue_size, size as u16); 299 volwrite!(self.common_cfg, queue_desc, descriptors as u64); 300 volwrite!(self.common_cfg, queue_driver, driver_area as u64); 301 volwrite!(self.common_cfg, queue_device, device_area as u64); 302 volwrite!(self.common_cfg, queue_enable, 1); 303 } 304 } 305 306 fn queue_unset(&mut self, queue: u16) { 307 // Safe because the common config pointer is valid and we checked in get_bar_region that it 308 // was aligned. 309 unsafe { 310 volwrite!(self.common_cfg, queue_enable, 0); 311 volwrite!(self.common_cfg, queue_select, queue); 312 volwrite!(self.common_cfg, queue_size, 0); 313 volwrite!(self.common_cfg, queue_desc, 0); 314 volwrite!(self.common_cfg, queue_driver, 0); 315 volwrite!(self.common_cfg, queue_device, 0); 316 } 317 } 318 319 fn queue_used(&mut self, queue: u16) -> bool { 320 // Safe because the common config pointer is valid and we checked in get_bar_region that it 321 // was aligned. 322 unsafe { 323 volwrite!(self.common_cfg, queue_select, queue); 324 volread!(self.common_cfg, queue_enable) == 1 325 } 326 } 327 328 fn ack_interrupt(&mut self) -> bool { 329 // Safe because the common config pointer is valid and we checked in get_bar_region that it 330 // was aligned. 331 // Reading the ISR status resets it to 0 and causes the device to de-assert the interrupt. 332 let isr_status = unsafe { self.isr_status.as_ptr().vread() }; 333 // TODO: Distinguish between queue interrupt and device configuration interrupt. 334 isr_status & 0x3 != 0 335 } 336 337 fn config_space<T>(&self) -> Result<NonNull<T>, Error> { 338 if let Some(config_space) = self.config_space { 339 if size_of::<T>() > config_space.len() * size_of::<u32>() { 340 Err(Error::ConfigSpaceTooSmall) 341 } else if align_of::<T>() > 4 { 342 // Panic as this should only happen if the driver is written incorrectly. 343 panic!( 344 "Driver expected config space alignment of {} bytes, but VirtIO only guarantees 4 byte alignment.", 345 align_of::<T>() 346 ); 347 } else { 348 // TODO: Use NonNull::as_non_null_ptr once it is stable. 349 let config_space_ptr = NonNull::new(config_space.as_ptr() as *mut u32).unwrap(); 350 Ok(config_space_ptr.cast()) 351 } 352 } else { 353 Err(Error::ConfigSpaceMissing) 354 } 355 } 356 } 357 358 /// `virtio_pci_common_cfg`, see 4.1.4.3 "Common configuration structure layout". 359 /// 360 #[repr(C)] 361 struct CommonCfg { 362 device_feature_select: Volatile<u32>, 363 device_feature: ReadOnly<u32>, 364 driver_feature_select: Volatile<u32>, 365 driver_feature: Volatile<u32>, 366 msix_config: Volatile<u16>, 367 num_queues: ReadOnly<u16>, 368 device_status: Volatile<u8>, 369 config_generation: ReadOnly<u8>, 370 queue_select: Volatile<u16>, 371 queue_size: Volatile<u16>, 372 queue_msix_vector: Volatile<u16>, 373 queue_enable: Volatile<u16>, 374 queue_notify_off: Volatile<u16>, 375 queue_desc: Volatile<u64>, 376 queue_driver: Volatile<u64>, 377 queue_device: Volatile<u64>, 378 } 379 380 /// Information about a VirtIO structure within some BAR, as provided by a `virtio_pci_cap`. 381 ///cfg空间在哪个bar的多少偏移处,长度多少 382 #[derive(Clone, Debug, Eq, PartialEq)] 383 struct VirtioCapabilityInfo { 384 /// The bar in which the structure can be found. 385 bar: u8, 386 /// The offset within the bar. 387 offset: u32, 388 /// The length in bytes of the structure within the bar. 389 length: u32, 390 } 391 392 /// An error encountered initialising a VirtIO PCI transport. 393 /// VirtIO PCI transport 初始化时的错误 394 #[derive(Clone, Debug, Eq, PartialEq)] 395 pub enum VirtioPciError { 396 /// PCI device vender ID was not the VirtIO vendor ID. 397 InvalidVendorId(u16), 398 /// No valid `VIRTIO_PCI_CAP_COMMON_CFG` capability was found. 399 MissingCommonConfig, 400 /// No valid `VIRTIO_PCI_CAP_NOTIFY_CFG` capability was found. 401 MissingNotifyConfig, 402 /// `VIRTIO_PCI_CAP_NOTIFY_CFG` capability has a `notify_off_multiplier` that is not a multiple 403 /// of 2. 404 InvalidNotifyOffMultiplier(u32), 405 /// No valid `VIRTIO_PCI_CAP_ISR_CFG` capability was found. 406 MissingIsrConfig, 407 /// An IO BAR was provided rather than a memory BAR. 408 UnexpectedBarType, 409 /// A BAR which we need was not allocated an address. 410 BarNotAllocated(u8), 411 /// The offset for some capability was greater than the length of the BAR. 412 BarOffsetOutOfRange, 413 /// The virtual address was not aligned as expected. 414 Misaligned { 415 /// The virtual address in question. 416 vaddr: VirtAddr, 417 /// The expected alignment in bytes. 418 alignment: usize, 419 }, 420 ///获取虚拟地址失败 421 BarGetVaddrFailed, 422 /// A generic PCI error, 423 Pci(PciError), 424 } 425 426 impl Display for VirtioPciError { 427 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 428 match self { 429 Self::InvalidVendorId(vendor_id) => write!( 430 f, 431 "PCI device vender ID {:#06x} was not the VirtIO vendor ID {:#06x}.", 432 vendor_id, VIRTIO_VENDOR_ID 433 ), 434 Self::MissingCommonConfig => write!( 435 f, 436 "No valid `VIRTIO_PCI_CAP_COMMON_CFG` capability was found." 437 ), 438 Self::MissingNotifyConfig => write!( 439 f, 440 "No valid `VIRTIO_PCI_CAP_NOTIFY_CFG` capability was found." 441 ), 442 Self::InvalidNotifyOffMultiplier(notify_off_multiplier) => { 443 write!( 444 f, 445 "`VIRTIO_PCI_CAP_NOTIFY_CFG` capability has a `notify_off_multiplier` that is not a multiple of 2: {}", 446 notify_off_multiplier 447 ) 448 } 449 Self::MissingIsrConfig => { 450 write!(f, "No valid `VIRTIO_PCI_CAP_ISR_CFG` capability was found.") 451 } 452 Self::UnexpectedBarType => write!(f, "Unexpected BAR (expected memory BAR)."), 453 Self::BarNotAllocated(bar_index) => write!(f, "Bar {} not allocated.", bar_index), 454 Self::BarOffsetOutOfRange => write!(f, "Capability offset greater than BAR length."), 455 Self::Misaligned { vaddr, alignment } => write!( 456 f, 457 "Virtual address {:#018x} was not aligned to a {} byte boundary as expected.", 458 vaddr, alignment 459 ), 460 Self::BarGetVaddrFailed => write!(f, "Get bar virtaddress failed"), 461 Self::Pci(pci_error) => pci_error.fmt(f), 462 } 463 } 464 } 465 ///PCI error到VirtioPciError的转换,层层上报 466 impl From<PciError> for VirtioPciError { 467 fn from(error: PciError) -> Self { 468 Self::Pci(error) 469 } 470 } 471 472 ///@brief 获取虚拟地址并将其转化为对应类型的指针 473 ///@param device_bar 存储bar信息的结构体 struct_info 存储cfg空间的位置信息 474 ///@return Result<NonNull<T>, VirtioPciError> 成功则返回对应类型的指针,失败则返回Error 475 fn get_bar_region<T>( 476 device_bar: &PciDeviceBar, 477 struct_info: &VirtioCapabilityInfo, 478 ) -> Result<NonNull<T>, VirtioPciError> { 479 let bar_info = device_bar.get_bar(struct_info.bar)?; 480 let (bar_address, bar_size) = bar_info 481 .memory_address_size() 482 .ok_or(VirtioPciError::UnexpectedBarType)?; 483 if bar_address == 0 { 484 return Err(VirtioPciError::BarNotAllocated(struct_info.bar)); 485 } 486 if struct_info.offset + struct_info.length > bar_size 487 || size_of::<T>() > struct_info.length as usize 488 { 489 return Err(VirtioPciError::BarOffsetOutOfRange); 490 } 491 let vaddr = (bar_info 492 .virtual_address() 493 .ok_or(VirtioPciError::BarGetVaddrFailed)?) as usize 494 + struct_info.offset as usize; 495 if vaddr % align_of::<T>() != 0 { 496 return Err(VirtioPciError::Misaligned { 497 vaddr, 498 alignment: align_of::<T>(), 499 }); 500 } 501 Ok(NonNull::new((vaddr) as _).unwrap()) 502 } 503 504 ///@brief 获取虚拟地址并将其转化为对应类型的指针 505 ///@param device_bar 存储bar信息的结构体 struct_info 存储cfg空间的位置信息 506 ///@return Result<NonNull<[T]>, VirtioPciError> 成功则返回对应类型的指针切片,失败则返回Error 507 fn get_bar_region_slice<T>( 508 device_bar: &PciDeviceBar, 509 struct_info: &VirtioCapabilityInfo, 510 ) -> Result<NonNull<[T]>, VirtioPciError> { 511 let ptr = get_bar_region::<T>(device_bar, struct_info)?; 512 let raw_slice = 513 ptr::slice_from_raw_parts_mut(ptr.as_ptr(), struct_info.length as usize / size_of::<T>()); 514 Ok(NonNull::new(raw_slice).unwrap()) 515 } 516