142 lines · 3.4 KB · Rust
RawDownload
// SPDX-License-Identifier: GPL-2.0 //! Rust DMA api test (based on QEMU's `pci-testdev`).//!//! To make this driver probe, QEMU must be run with `-device pci-testdev`. use kernel::{    device::Core,    dma::{        Coherent,        DataDirection,        Device,        DmaMask, //    },    io::{        io_project,        io_read,        Io, //    },    page,    pci,    prelude::*,    scatterlist::{        Owned,        SGTable, //    },    sync::aref::ARef, //}; #[pin_data(PinnedDrop)]struct DmaSampleDriver {    pdev: ARef<pci::Device>,    ca: Coherent<[MyStruct]>,    #[pin]    sgt: SGTable<Owned<VVec<u8>>>,} const TEST_VALUES: [(u32, u32); 5] = [    (0xa, 0xb),    (0xc, 0xd),    (0xe, 0xf),    (0xab, 0xba),    (0xcd, 0xef),]; #[derive(FromBytes, IntoBytes)]struct MyStruct {    h: u32,    b: u32,} impl MyStruct {    fn new(h: u32, b: u32) -> Self {        Self { h, b }    }}// SAFETY: All bit patterns are acceptable values for `MyStruct`.unsafe impl kernel::transmute::AsBytes for MyStruct {}// SAFETY: Instances of `MyStruct` have no uninitialized portions.unsafe impl kernel::transmute::FromBytes for MyStruct {} kernel::pci_device_table!(    PCI_TABLE,    <DmaSampleDriver as pci::Driver>::IdInfo,    [(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())]); impl pci::Driver for DmaSampleDriver {    type IdInfo = ();    type Data<'bound> = Self;    const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;     fn probe<'bound>(        pdev: &'bound pci::Device<Core<'_>>,        _info: Option<&'bound Self::IdInfo>,    ) -> impl PinInit<Self, Error> + 'bound {        pin_init::pin_init_scope(move || {            dev_info!(pdev, "Probe DMA test driver.\n");             let mask = DmaMask::new::<64>();             // SAFETY: There are no concurrent calls to DMA allocation and mapping primitives.            unsafe { pdev.dma_set_mask_and_coherent(mask)? };             let ca: Coherent<[MyStruct]> =                Coherent::zeroed_slice(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?;             for (i, value) in TEST_VALUES.into_iter().enumerate() {                io_project!(ca, [panic: i]).copy_write(MyStruct::new(value.0, value.1));            }             let size = 4 * page::PAGE_SIZE;            let pages = VVec::with_capacity(size, GFP_KERNEL)?;             let sgt = SGTable::new(pdev.as_ref(), pages, DataDirection::ToDevice, GFP_KERNEL);             Ok(try_pin_init!(Self {                pdev: pdev.into(),                ca,                sgt <- sgt,            }))        })    }} impl DmaSampleDriver {    fn check_dma(&self) {        for (i, value) in TEST_VALUES.into_iter().enumerate() {            let val0 = io_read!(self.ca, [panic: i].h);            let val1 = io_read!(self.ca, [panic: i].b);             assert_eq!(val0, value.0);            assert_eq!(val1, value.1);        }    }} #[pinned_drop]impl PinnedDrop for DmaSampleDriver {    fn drop(self: Pin<&mut Self>) {        dev_info!(self.pdev, "Unload DMA test driver.\n");         self.check_dma();         for (i, entry) in self.sgt.iter().enumerate() {            dev_info!(                self.pdev,                "Entry[{}]: DMA address: {:#x}",                i,                entry.dma_address(),            );        }    }} kernel::module_pci_driver! {    type: DmaSampleDriver,    name: "rust_dma",    authors: ["Abdiel Janulgue"],    description: "Rust DMA test",    license: "GPL v2",}