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