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