diff --git a/rust/helpers.c b/rust/helpers.c index ead3a64f700c73ef73fd7a58a49b109e392f8b5c..cfb54a0207d8fbc7d966f20cb8ff79b48eedae0a 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -620,6 +621,18 @@ unsigned int rust_helper_NF_QUEUE_NR(unsigned int n) } EXPORT_SYMBOL_GPL(rust_helper_NF_QUEUE_NR); +void rust_helper_pci_set_drvdata(struct pci_dev *pdev, void *data) +{ + pci_set_drvdata(pdev, data); +} +EXPORT_SYMBOL_GPL(rust_helper_pci_set_drvdata); + +void *rust_helper_pci_get_drvdata(struct pci_dev *pdev) +{ + return pci_get_drvdata(pdev); +} +EXPORT_SYMBOL_GPL(rust_helper_pci_get_drvdata); + /* * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type * as the Rust `usize` type, so we can use it in contexts where Rust diff --git a/rust/kernel/bindings_helper.h b/rust/kernel/bindings_helper.h index 73100fa139ebb5b527139aa68bd1baabea640a13..3bab72c8eced7720a06c3cc5f294b4e93a3adf26 100644 --- a/rust/kernel/bindings_helper.h +++ b/rust/kernel/bindings_helper.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 3e01c30de6704a98d98dba2e4b8286f3c7360f80..6c11382a03f7f09e0a155c3be577266d4c00af2c 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -60,6 +60,8 @@ pub mod mm; #[cfg(CONFIG_NET)] pub mod net; pub mod pages; +#[cfg(CONFIG_PCI)] +pub mod pci; pub mod power; pub mod revocable; pub mod security; diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs new file mode 100644 index 0000000000000000000000000000000000000000..1dab72181eb1319d4b10eef1c8324c2d291af0cc --- /dev/null +++ b/rust/kernel/pci.rs @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! PCI devices and drivers. +//! +//! C header: [`include/linux/pci.h`](../../../../include/linux/pci.h) + +use crate::{ + bindings, c_types, device, driver, + error::{from_kernel_result, Result}, + str::CStr, + to_result, + types::PointerWrapper, + ThisModule, +}; + +/// An adapter for the registration of PCI drivers. +pub struct Adapter(T); + +impl driver::DriverOps for Adapter { + type RegType = bindings::pci_driver; + + unsafe fn register( + reg: *mut bindings::pci_driver, + name: &'static CStr, + module: &'static ThisModule, + ) -> Result { + let pdrv: &mut bindings::pci_driver = unsafe { &mut *reg }; + + pdrv.name = name.as_char_ptr(); + pdrv.probe = Some(Self::probe_callback); + pdrv.remove = Some(Self::remove_callback); + pdrv.id_table = T::PCI_ID_TABLE.as_ptr(); + to_result(|| unsafe { bindings::__pci_register_driver(reg, module.0, name.as_char_ptr()) }) + } + + unsafe fn unregister(reg: *mut bindings::pci_driver) { + unsafe { bindings::pci_unregister_driver(reg) } + } +} + +impl Adapter { + extern "C" fn probe_callback( + pdev: *mut bindings::pci_dev, + _id: *const bindings::pci_device_id, + ) -> c_types::c_int { + from_kernel_result! { + let mut dev = unsafe { Device::from_ptr(pdev) }; + let data = T::probe(&mut dev)?; + unsafe { bindings::pci_set_drvdata(pdev, data.into_pointer() as _) }; + Ok(0) + } + } + + extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) { + let ptr = unsafe { bindings::pci_get_drvdata(pdev) }; + let data = unsafe { T::Data::from_pointer(ptr) }; + T::remove(&data); + ::device_remove(&data); + } +} + +/// A PCI driver +pub trait Driver { + /// Data stored on device by driver. + /// + /// Corresponds to the data set or retrieved via the kernel's + /// `pci_{set,get}_drvdata()` functions. + /// + /// Require that `Data` implements `PointerWrapper`. We guarantee to + /// never move the underlying wrapped data structure. This allows + type Data: PointerWrapper + Send + Sync + driver::DeviceRemoval = (); + + /// The table of device ids supported by the driver. + const PCI_ID_TABLE: &'static [bindings::pci_device_id]; + + /// PCI driver probe. + /// + /// Called when a new platform device is added or discovered. + /// Implementers should attempt to initialize the device here. + fn probe(dev: &mut Device) -> Result; + + /// PCI driver remove. + /// + /// Called when a platform device is removed. + /// Implementers should prepare the device for complete removal here. + fn remove(_data: &Self::Data); +} + +/// A PCI device. +/// +/// # Invariants +/// +/// The field `ptr` is non-null and valid for the lifetime of the object. +pub struct Device { + ptr: *mut bindings::pci_dev, +} + +impl Device { + unsafe fn from_ptr(ptr: *mut bindings::pci_dev) -> Self { + Self { ptr } + } +} + +unsafe impl device::RawDevice for Device { + fn raw_device(&self) -> *mut bindings::device { + // SAFETY: By the type invariants, we know that `self.ptr` is non-null and valid. + unsafe { &mut (*self.ptr).dev } + } +}