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