1 // SPDX-License-Identifier: GPL-2.0 2 3 //! Rust DMA api test (based on QEMU's `pci-testdev`). 4 //! 5 //! To make this driver probe, QEMU must be run with `-device pci-testdev`. 6 7 use kernel::{ 8 device::Core, 9 dma::{ 10 Coherent, 11 DataDirection, 12 Device, 13 DmaMask, // 14 }, 15 io::{ 16 io_project, 17 io_read, 18 Io, // 19 }, 20 page, 21 pci, 22 prelude::*, 23 scatterlist::{ 24 Owned, 25 SGTable, // 26 }, 27 sync::aref::ARef, // 28 }; 29 30 #[pin_data(PinnedDrop)] 31 struct DmaSampleDriver { 32 pdev: ARef<pci::Device>, 33 ca: Coherent<[MyStruct]>, 34 #[pin] 35 sgt: SGTable<Owned<VVec<u8>>>, 36 } 37 38 const TEST_VALUES: [(u32, u32); 5] = [ 39 (0xa, 0xb), 40 (0xc, 0xd), 41 (0xe, 0xf), 42 (0xab, 0xba), 43 (0xcd, 0xef), 44 ]; 45 46 #[derive(FromBytes, IntoBytes)] 47 struct MyStruct { 48 h: u32, 49 b: u32, 50 } 51 52 impl MyStruct { 53 fn new(h: u32, b: u32) -> Self { 54 Self { h, b } 55 } 56 } 57 // SAFETY: All bit patterns are acceptable values for `MyStruct`. 58 unsafe impl kernel::transmute::AsBytes for MyStruct {} 59 // SAFETY: Instances of `MyStruct` have no uninitialized portions. 60 unsafe impl kernel::transmute::FromBytes for MyStruct {} 61 62 kernel::pci_device_table!( 63 PCI_TABLE, 64 <DmaSampleDriver as pci::Driver>::IdInfo, 65 [(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())] 66 ); 67 68 impl pci::Driver for DmaSampleDriver { 69 type IdInfo = (); 70 type Data<'bound> = Self; 71 const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE; 72 73 fn probe<'bound>( 74 pdev: &'bound pci::Device<Core<'_>>, 75 _info: Option<&'bound Self::IdInfo>, 76 ) -> impl PinInit<Self, Error> + 'bound { 77 pin_init::pin_init_scope(move || { 78 dev_info!(pdev, "Probe DMA test driver.\n"); 79 80 let mask = DmaMask::new::<64>(); 81 82 // SAFETY: There are no concurrent calls to DMA allocation and mapping primitives. 83 unsafe { pdev.dma_set_mask_and_coherent(mask)? }; 84 85 let ca: Coherent<[MyStruct]> = 86 Coherent::zeroed_slice(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?; 87 88 for (i, value) in TEST_VALUES.into_iter().enumerate() { 89 io_project!(ca, [panic: i]).copy_write(MyStruct::new(value.0, value.1)); 90 } 91 92 let size = 4 * page::PAGE_SIZE; 93 let pages = VVec::with_capacity(size, GFP_KERNEL)?; 94 95 let sgt = SGTable::new(pdev.as_ref(), pages, DataDirection::ToDevice, GFP_KERNEL); 96 97 Ok(try_pin_init!(Self { 98 pdev: pdev.into(), 99 ca, 100 sgt <- sgt, 101 })) 102 }) 103 } 104 } 105 106 impl DmaSampleDriver { 107 fn check_dma(&self) { 108 for (i, value) in TEST_VALUES.into_iter().enumerate() { 109 let val0 = io_read!(self.ca, [panic: i].h); 110 let val1 = io_read!(self.ca, [panic: i].b); 111 112 assert_eq!(val0, value.0); 113 assert_eq!(val1, value.1); 114 } 115 } 116 } 117 118 #[pinned_drop] 119 impl PinnedDrop for DmaSampleDriver { 120 fn drop(self: Pin<&mut Self>) { 121 dev_info!(self.pdev, "Unload DMA test driver.\n"); 122 123 self.check_dma(); 124 125 for (i, entry) in self.sgt.iter().enumerate() { 126 dev_info!( 127 self.pdev, 128 "Entry[{}]: DMA address: {:#x}", 129 i, 130 entry.dma_address(), 131 ); 132 } 133 } 134 } 135 136 kernel::module_pci_driver! { 137 type: DmaSampleDriver, 138 name: "rust_dma", 139 authors: ["Abdiel Janulgue"], 140 description: "Rust DMA test", 141 license: "GPL v2", 142 } 143