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