pci.rs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. //! PCI transport for VirtIO.
  2. pub mod bus;
  3. use self::bus::{DeviceFunction, DeviceFunctionInfo, PciError, PciRoot, PCI_CAP_ID_VNDR};
  4. use super::{DeviceStatus, DeviceType, Transport};
  5. use crate::{
  6. hal::{Hal, PhysAddr, VirtAddr},
  7. volatile::{
  8. volread, volwrite, ReadOnly, Volatile, VolatileReadable, VolatileWritable, WriteOnly,
  9. },
  10. };
  11. use core::{
  12. fmt::{self, Display, Formatter},
  13. mem::{align_of, size_of},
  14. ptr::NonNull,
  15. };
  16. /// The PCI vendor ID for VirtIO devices.
  17. const VIRTIO_VENDOR_ID: u16 = 0x1af4;
  18. /// The offset to add to a VirtIO device ID to get the corresponding PCI device ID.
  19. const PCI_DEVICE_ID_OFFSET: u16 = 0x1040;
  20. const TRANSITIONAL_NETWORK: u16 = 0x1000;
  21. const TRANSITIONAL_BLOCK: u16 = 0x1001;
  22. const TRANSITIONAL_MEMORY_BALLOONING: u16 = 0x1002;
  23. const TRANSITIONAL_CONSOLE: u16 = 0x1003;
  24. const TRANSITIONAL_SCSI_HOST: u16 = 0x1004;
  25. const TRANSITIONAL_ENTROPY_SOURCE: u16 = 0x1005;
  26. const TRANSITIONAL_9P_TRANSPORT: u16 = 0x1009;
  27. /// The offset of the bar field within `virtio_pci_cap`.
  28. const CAP_BAR_OFFSET: u8 = 4;
  29. /// The offset of the offset field with `virtio_pci_cap`.
  30. const CAP_BAR_OFFSET_OFFSET: u8 = 8;
  31. /// The offset of the `length` field within `virtio_pci_cap`.
  32. const CAP_LENGTH_OFFSET: u8 = 12;
  33. /// The offset of the`notify_off_multiplier` field within `virtio_pci_notify_cap`.
  34. const CAP_NOTIFY_OFF_MULTIPLIER_OFFSET: u8 = 16;
  35. /// Common configuration.
  36. const VIRTIO_PCI_CAP_COMMON_CFG: u8 = 1;
  37. /// Notifications.
  38. const VIRTIO_PCI_CAP_NOTIFY_CFG: u8 = 2;
  39. /// ISR Status.
  40. const VIRTIO_PCI_CAP_ISR_CFG: u8 = 3;
  41. /// Device specific configuration.
  42. const VIRTIO_PCI_CAP_DEVICE_CFG: u8 = 4;
  43. fn device_type(pci_device_id: u16) -> DeviceType {
  44. match pci_device_id {
  45. TRANSITIONAL_NETWORK => DeviceType::Network,
  46. TRANSITIONAL_BLOCK => DeviceType::Block,
  47. TRANSITIONAL_MEMORY_BALLOONING => DeviceType::MemoryBalloon,
  48. TRANSITIONAL_CONSOLE => DeviceType::Console,
  49. TRANSITIONAL_SCSI_HOST => DeviceType::ScsiHost,
  50. TRANSITIONAL_ENTROPY_SOURCE => DeviceType::EntropySource,
  51. TRANSITIONAL_9P_TRANSPORT => DeviceType::_9P,
  52. id if id >= PCI_DEVICE_ID_OFFSET => DeviceType::from(id - PCI_DEVICE_ID_OFFSET),
  53. _ => DeviceType::Invalid,
  54. }
  55. }
  56. /// Returns the type of VirtIO device to which the given PCI vendor and device ID corresponds, or
  57. /// `None` if it is not a recognised VirtIO device.
  58. pub fn virtio_device_type(device_function_info: &DeviceFunctionInfo) -> Option<DeviceType> {
  59. if device_function_info.vendor_id == VIRTIO_VENDOR_ID {
  60. let device_type = device_type(device_function_info.device_id);
  61. if device_type != DeviceType::Invalid {
  62. return Some(device_type);
  63. }
  64. }
  65. None
  66. }
  67. /// PCI transport for VirtIO.
  68. ///
  69. /// Ref: 4.1 Virtio Over PCI Bus
  70. #[derive(Debug)]
  71. pub struct PciTransport {
  72. device_type: DeviceType,
  73. /// The bus, device and function identifier for the VirtIO device.
  74. device_function: DeviceFunction,
  75. /// The common configuration structure within some BAR.
  76. common_cfg: NonNull<CommonCfg>,
  77. // TODO: Use a raw slice, once they are supported by our MSRV.
  78. /// The start of the queue notification region within some BAR.
  79. notify_region: NonNull<WriteOnly<u16>>,
  80. /// The size of the queue notification region in bytes.
  81. notify_region_size: usize,
  82. notify_off_multiplier: u32,
  83. /// The ISR status register within some BAR.
  84. isr_status: NonNull<Volatile<u8>>,
  85. /// The VirtIO device-specific configuration within some BAR.
  86. config_space: Option<NonNull<u64>>,
  87. /// The size of the VirtIO device-specific configuration region in bytes.
  88. config_space_size: usize,
  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>(
  96. root: &mut PciRoot,
  97. device_function: DeviceFunction,
  98. ) -> Result<Self, VirtioPciError> {
  99. let device_vendor = root.config_read_word(device_function, 0);
  100. let device_id = (device_vendor >> 16) as u16;
  101. let vendor_id = device_vendor as u16;
  102. if vendor_id != VIRTIO_VENDOR_ID {
  103. return Err(VirtioPciError::InvalidVendorId(vendor_id));
  104. }
  105. let device_type = device_type(device_id);
  106. // Find the PCI capabilities we need.
  107. let mut common_cfg = None;
  108. let mut notify_cfg = None;
  109. let mut notify_off_multiplier = 0;
  110. let mut isr_cfg = None;
  111. let mut device_cfg = None;
  112. for capability in root.capabilities(device_function) {
  113. if capability.id != PCI_CAP_ID_VNDR {
  114. continue;
  115. }
  116. let cap_len = capability.private_header as u8;
  117. let cfg_type = (capability.private_header >> 8) as u8;
  118. if cap_len < 16 {
  119. continue;
  120. }
  121. let struct_info = VirtioCapabilityInfo {
  122. bar: root.config_read_word(device_function, capability.offset + CAP_BAR_OFFSET)
  123. as u8,
  124. offset: root
  125. .config_read_word(device_function, capability.offset + CAP_BAR_OFFSET_OFFSET),
  126. length: root
  127. .config_read_word(device_function, capability.offset + CAP_LENGTH_OFFSET),
  128. };
  129. match cfg_type {
  130. VIRTIO_PCI_CAP_COMMON_CFG if common_cfg.is_none() => {
  131. common_cfg = Some(struct_info);
  132. }
  133. VIRTIO_PCI_CAP_NOTIFY_CFG if cap_len >= 20 && notify_cfg.is_none() => {
  134. notify_cfg = Some(struct_info);
  135. notify_off_multiplier = root.config_read_word(
  136. device_function,
  137. capability.offset + CAP_NOTIFY_OFF_MULTIPLIER_OFFSET,
  138. );
  139. }
  140. VIRTIO_PCI_CAP_ISR_CFG if isr_cfg.is_none() => {
  141. isr_cfg = Some(struct_info);
  142. }
  143. VIRTIO_PCI_CAP_DEVICE_CFG if device_cfg.is_none() => {
  144. device_cfg = Some(struct_info);
  145. }
  146. _ => {}
  147. }
  148. }
  149. let common_cfg = get_bar_region::<H, _>(
  150. root,
  151. device_function,
  152. &common_cfg.ok_or(VirtioPciError::MissingCommonConfig)?,
  153. )?;
  154. let notify_cfg = notify_cfg.ok_or(VirtioPciError::MissingNotifyConfig)?;
  155. if notify_off_multiplier % 2 != 0 {
  156. return Err(VirtioPciError::InvalidNotifyOffMultiplier(
  157. notify_off_multiplier,
  158. ));
  159. }
  160. let notify_region = get_bar_region::<H, _>(root, device_function, &notify_cfg)?;
  161. let isr_status = get_bar_region::<H, _>(
  162. root,
  163. device_function,
  164. &isr_cfg.ok_or(VirtioPciError::MissingIsrConfig)?,
  165. )?;
  166. let config_space;
  167. let config_space_size;
  168. if let Some(device_cfg) = device_cfg {
  169. config_space = Some(get_bar_region::<H, _>(root, device_function, &device_cfg)?);
  170. config_space_size = device_cfg.length as usize;
  171. } else {
  172. config_space = None;
  173. config_space_size = 0;
  174. }
  175. Ok(Self {
  176. device_type,
  177. device_function,
  178. common_cfg,
  179. notify_region,
  180. notify_region_size: notify_cfg.length as usize,
  181. notify_off_multiplier,
  182. isr_status,
  183. config_space,
  184. config_space_size,
  185. })
  186. }
  187. }
  188. impl Transport for PciTransport {
  189. fn device_type(&self) -> DeviceType {
  190. self.device_type
  191. }
  192. fn read_device_features(&mut self) -> u64 {
  193. // Safe because the common config pointer is valid and we checked in get_bar_region that it
  194. // was aligned.
  195. unsafe {
  196. volwrite!(self.common_cfg, device_feature_select, 0);
  197. let mut device_features_bits = volread!(self.common_cfg, device_feature) as u64;
  198. volwrite!(self.common_cfg, device_feature_select, 1);
  199. device_features_bits |= (volread!(self.common_cfg, device_feature) as u64) << 32;
  200. device_features_bits
  201. }
  202. }
  203. fn write_driver_features(&mut self, driver_features: u64) {
  204. // Safe because the common config pointer is valid and we checked in get_bar_region that it
  205. // was aligned.
  206. unsafe {
  207. volwrite!(self.common_cfg, driver_feature_select, 0);
  208. volwrite!(self.common_cfg, driver_feature, driver_features as u32);
  209. volwrite!(self.common_cfg, driver_feature_select, 1);
  210. volwrite!(
  211. self.common_cfg,
  212. driver_feature,
  213. (driver_features >> 32) as u32
  214. );
  215. }
  216. }
  217. fn max_queue_size(&self) -> u32 {
  218. // Safe because the common config pointer is valid and we checked in get_bar_region that it
  219. // was aligned.
  220. unsafe { volread!(self.common_cfg, queue_size) }.into()
  221. }
  222. fn notify(&mut self, queue: u16) {
  223. // Safe because the common config and notify region pointers are valid and we checked in
  224. // get_bar_region that they were aligned.
  225. unsafe {
  226. volwrite!(self.common_cfg, queue_select, queue);
  227. // TODO: Consider caching this somewhere (per queue).
  228. let queue_notify_off = volread!(self.common_cfg, queue_notify_off);
  229. let offset_bytes = usize::from(queue_notify_off) * self.notify_off_multiplier as usize;
  230. assert!(offset_bytes + size_of::<u16>() <= self.notify_region_size);
  231. self.notify_region
  232. .as_ptr()
  233. .add(offset_bytes / size_of::<u16>())
  234. .vwrite(queue);
  235. }
  236. }
  237. fn set_status(&mut self, status: DeviceStatus) {
  238. // Safe because the common config pointer is valid and we checked in get_bar_region that it
  239. // was aligned.
  240. unsafe {
  241. volwrite!(self.common_cfg, device_status, status.bits() as u8);
  242. }
  243. }
  244. fn set_guest_page_size(&mut self, _guest_page_size: u32) {
  245. // No-op, the PCI transport doesn't care.
  246. }
  247. fn queue_set(
  248. &mut self,
  249. queue: u16,
  250. size: u32,
  251. descriptors: PhysAddr,
  252. driver_area: PhysAddr,
  253. device_area: PhysAddr,
  254. ) {
  255. // Safe because the common config pointer is valid and we checked in get_bar_region that it
  256. // was aligned.
  257. unsafe {
  258. volwrite!(self.common_cfg, queue_select, queue);
  259. volwrite!(self.common_cfg, queue_size, size as u16);
  260. volwrite!(self.common_cfg, queue_desc, descriptors as u64);
  261. volwrite!(self.common_cfg, queue_driver, driver_area as u64);
  262. volwrite!(self.common_cfg, queue_device, device_area as u64);
  263. volwrite!(self.common_cfg, queue_enable, 1);
  264. }
  265. }
  266. fn queue_used(&mut self, queue: u16) -> bool {
  267. // Safe because the common config pointer is valid and we checked in get_bar_region that it
  268. // was aligned.
  269. unsafe {
  270. volwrite!(self.common_cfg, queue_select, queue);
  271. volread!(self.common_cfg, queue_enable) == 1
  272. }
  273. }
  274. fn ack_interrupt(&mut self) -> bool {
  275. // Safe because the common config pointer is valid and we checked in get_bar_region that it
  276. // was aligned.
  277. // Reading the ISR status resets it to 0 and causes the device to de-assert the interrupt.
  278. let isr_status = unsafe { self.isr_status.as_ptr().vread() };
  279. // TODO: Distinguish between queue interrupt and device configuration interrupt.
  280. isr_status & 0x3 != 0
  281. }
  282. fn config_space(&self) -> NonNull<u64> {
  283. // TODO: Check config_space_size
  284. self.config_space
  285. .expect("No VIRTIO_PCI_CAP_DEVICE_CFG capability.")
  286. }
  287. }
  288. /// `virtio_pci_common_cfg`, see 4.1.4.3 "Common configuration structure layout".
  289. #[repr(C)]
  290. struct CommonCfg {
  291. device_feature_select: Volatile<u32>,
  292. device_feature: ReadOnly<u32>,
  293. driver_feature_select: Volatile<u32>,
  294. driver_feature: Volatile<u32>,
  295. msix_config: Volatile<u16>,
  296. num_queues: ReadOnly<u16>,
  297. device_status: Volatile<u8>,
  298. config_generation: ReadOnly<u8>,
  299. queue_select: Volatile<u16>,
  300. queue_size: Volatile<u16>,
  301. queue_msix_vector: Volatile<u16>,
  302. queue_enable: Volatile<u16>,
  303. queue_notify_off: Volatile<u16>,
  304. queue_desc: Volatile<u64>,
  305. queue_driver: Volatile<u64>,
  306. queue_device: Volatile<u64>,
  307. }
  308. /// Information about a VirtIO structure within some BAR, as provided by a `virtio_pci_cap`.
  309. #[derive(Clone, Debug, Eq, PartialEq)]
  310. struct VirtioCapabilityInfo {
  311. /// The bar in which the structure can be found.
  312. bar: u8,
  313. /// The offset within the bar.
  314. offset: u32,
  315. /// The length in bytes of the structure within the bar.
  316. length: u32,
  317. }
  318. fn get_bar_region<H: Hal, T>(
  319. root: &mut PciRoot,
  320. device_function: DeviceFunction,
  321. struct_info: &VirtioCapabilityInfo,
  322. ) -> Result<NonNull<T>, VirtioPciError> {
  323. let bar_info = root.bar_info(device_function, struct_info.bar)?;
  324. let (bar_address, bar_size) = bar_info
  325. .memory_address_size()
  326. .ok_or(VirtioPciError::UnexpectedIoBar)?;
  327. if bar_address == 0 {
  328. return Err(VirtioPciError::BarNotAllocated(struct_info.bar));
  329. }
  330. if struct_info.offset + struct_info.length > bar_size
  331. || size_of::<T>() > struct_info.length as usize
  332. {
  333. return Err(VirtioPciError::BarOffsetOutOfRange);
  334. }
  335. let paddr = bar_address as PhysAddr + struct_info.offset as PhysAddr;
  336. let vaddr = H::phys_to_virt(paddr);
  337. if vaddr % align_of::<T>() != 0 {
  338. return Err(VirtioPciError::Misaligned {
  339. vaddr,
  340. alignment: align_of::<T>(),
  341. });
  342. }
  343. Ok(NonNull::new(vaddr as _).unwrap())
  344. }
  345. /// An error encountered initialising a VirtIO PCI transport.
  346. #[derive(Clone, Debug, Eq, PartialEq)]
  347. pub enum VirtioPciError {
  348. /// PCI device vender ID was not the VirtIO vendor ID.
  349. InvalidVendorId(u16),
  350. /// No valid `VIRTIO_PCI_CAP_COMMON_CFG` capability was found.
  351. MissingCommonConfig,
  352. /// No valid `VIRTIO_PCI_CAP_NOTIFY_CFG` capability was found.
  353. MissingNotifyConfig,
  354. /// `VIRTIO_PCI_CAP_NOTIFY_CFG` capability has a `notify_off_multiplier` that is not a multiple
  355. /// of 2.
  356. InvalidNotifyOffMultiplier(u32),
  357. /// No valid `VIRTIO_PCI_CAP_ISR_CFG` capability was found.
  358. MissingIsrConfig,
  359. /// An IO BAR was provided rather than a memory BAR.
  360. UnexpectedIoBar,
  361. /// A BAR which we need was not allocated an address.
  362. BarNotAllocated(u8),
  363. /// The offset for some capability was greater than the length of the BAR.
  364. BarOffsetOutOfRange,
  365. /// The virtual address was not aligned as expected.
  366. Misaligned {
  367. /// The virtual address in question.
  368. vaddr: VirtAddr,
  369. /// The expected alignment in bytes.
  370. alignment: usize,
  371. },
  372. /// A generic PCI error,
  373. Pci(PciError),
  374. }
  375. impl Display for VirtioPciError {
  376. fn fmt(&self, f: &mut Formatter) -> fmt::Result {
  377. match self {
  378. Self::InvalidVendorId(vendor_id) => write!(
  379. f,
  380. "PCI device vender ID {:#06x} was not the VirtIO vendor ID {:#06x}.",
  381. vendor_id, VIRTIO_VENDOR_ID
  382. ),
  383. Self::MissingCommonConfig => write!(
  384. f,
  385. "No valid `VIRTIO_PCI_CAP_COMMON_CFG` capability was found."
  386. ),
  387. Self::MissingNotifyConfig => write!(
  388. f,
  389. "No valid `VIRTIO_PCI_CAP_NOTIFY_CFG` capability was found."
  390. ),
  391. Self::InvalidNotifyOffMultiplier(notify_off_multiplier) => {
  392. write!(
  393. f,
  394. "`VIRTIO_PCI_CAP_NOTIFY_CFG` capability has a `notify_off_multiplier` that is not a multiple of 2: {}",
  395. notify_off_multiplier
  396. )
  397. }
  398. Self::MissingIsrConfig => {
  399. write!(f, "No valid `VIRTIO_PCI_CAP_ISR_CFG` capability was found.")
  400. }
  401. Self::UnexpectedIoBar => write!(f, "Unexpected IO BAR (expected memory BAR)."),
  402. Self::BarNotAllocated(bar_index) => write!(f, "Bar {} not allocated.", bar_index),
  403. Self::BarOffsetOutOfRange => write!(f, "Capability offset greater than BAR length."),
  404. Self::Misaligned { vaddr, alignment } => write!(
  405. f,
  406. "Virtual address {:#018x} was not aligned to a {} byte boundary as expected.",
  407. vaddr, alignment
  408. ),
  409. Self::Pci(pci_error) => pci_error.fmt(f),
  410. }
  411. }
  412. }
  413. impl From<PciError> for VirtioPciError {
  414. fn from(error: PciError) -> Self {
  415. Self::Pci(error)
  416. }
  417. }
  418. #[cfg(test)]
  419. mod tests {
  420. use super::*;
  421. #[test]
  422. fn transitional_device_ids() {
  423. assert_eq!(device_type(0x1000), DeviceType::Network);
  424. assert_eq!(device_type(0x1002), DeviceType::MemoryBalloon);
  425. assert_eq!(device_type(0x1009), DeviceType::_9P);
  426. }
  427. #[test]
  428. fn offset_device_ids() {
  429. assert_eq!(device_type(0x1045), DeviceType::MemoryBalloon);
  430. assert_eq!(device_type(0x1049), DeviceType::_9P);
  431. assert_eq!(device_type(0x1058), DeviceType::Memory);
  432. assert_eq!(device_type(0x1040), DeviceType::Invalid);
  433. assert_eq!(device_type(0x1059), DeviceType::Invalid);
  434. }
  435. #[test]
  436. fn virtio_device_type_valid() {
  437. assert_eq!(
  438. virtio_device_type(&DeviceFunctionInfo {
  439. vendor_id: VIRTIO_VENDOR_ID,
  440. device_id: TRANSITIONAL_BLOCK,
  441. class: 0,
  442. subclass: 0,
  443. prog_if: 0,
  444. revision: 0,
  445. header_type: bus::HeaderType::Standard,
  446. }),
  447. Some(DeviceType::Block)
  448. );
  449. }
  450. #[test]
  451. fn virtio_device_type_invalid() {
  452. // Non-VirtIO vendor ID.
  453. assert_eq!(
  454. virtio_device_type(&DeviceFunctionInfo {
  455. vendor_id: 0x1234,
  456. device_id: TRANSITIONAL_BLOCK,
  457. class: 0,
  458. subclass: 0,
  459. prog_if: 0,
  460. revision: 0,
  461. header_type: bus::HeaderType::Standard,
  462. }),
  463. None
  464. );
  465. // Invalid device ID.
  466. assert_eq!(
  467. virtio_device_type(&DeviceFunctionInfo {
  468. vendor_id: VIRTIO_VENDOR_ID,
  469. device_id: 0x1040,
  470. class: 0,
  471. subclass: 0,
  472. prog_if: 0,
  473. revision: 0,
  474. header_type: bus::HeaderType::Standard,
  475. }),
  476. None
  477. );
  478. }
  479. }