From: Danilo Krummrich <dakr@kernel.org>
To: gregkh@linuxfoundation.org, rafael@kernel.org,
bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com,
boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com,
benno.lossin@proton.me, tmgross@umich.edu,
a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com,
fujita.tomonori@gmail.com, lina@asahilina.net,
pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com,
robh@kernel.org, daniel.almeida@collabora.com,
saravanak@google.com, dirk.behme@de.bosch.com, j@jannau.net,
fabien.parent@linaro.org, chrisi.schrefl@gmail.com
Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-pci@vger.kernel.org, devicetree@vger.kernel.org,
Danilo Krummrich <dakr@kernel.org>
Subject: [PATCH v5 10/16] rust: pci: implement I/O mappable `pci::Bar`
Date: Tue, 10 Dec 2024 23:46:37 +0100 [thread overview]
Message-ID: <20241210224947.23804-11-dakr@kernel.org> (raw)
In-Reply-To: <20241210224947.23804-1-dakr@kernel.org>
Implement `pci::Bar`, `pci::Device::iomap_region` and
`pci::Device::iomap_region_sized` to allow for I/O mappings of PCI BARs.
To ensure that a `pci::Bar`, and hence the I/O memory mapping, can't
out-live the PCI device, the `pci::Bar` type is always embedded into a
`Devres` container, such that the `pci::Bar` is revoked once the device
is unbound and hence the I/O mapped memory is unmapped.
A `pci::Bar` can be requested with (`pci::Device::iomap_region_sized`) or
without (`pci::Device::iomap_region`) a const generic representing the
minimal requested size of the I/O mapped memory region. In case of the
latter only runtime checked I/O reads / writes are possible.
Co-developed-by: Philipp Stanner <pstanner@redhat.com>
Signed-off-by: Philipp Stanner <pstanner@redhat.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/pci.rs | 140 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 140 insertions(+)
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index 238eed06f388..a91f90585a1f 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -5,10 +5,14 @@
//! C header: [`include/linux/pci.h`](srctree/include/linux/pci.h)
use crate::{
+ alloc::flags::*,
bindings, container_of, device,
device_id::RawDeviceId,
+ devres::Devres,
driver,
error::{to_result, Result},
+ io::Io,
+ io::IoRaw,
str::CStr,
types::{ARef, ForeignOwnable, Opaque},
ThisModule,
@@ -241,9 +245,115 @@ pub trait Driver {
///
/// A PCI device is based on an always reference counted `device:Device` instance. Cloning a PCI
/// device, hence, also increments the base device' reference count.
+///
+/// # Invariants
+///
+/// `Device` hold a valid reference of `ARef<device::Device>` whose underlying `struct device` is a
+/// member of a `struct pci_dev`.
#[derive(Clone)]
pub struct Device(ARef<device::Device>);
+/// A PCI BAR to perform I/O-Operations on.
+///
+/// # Invariants
+///
+/// `Bar` always holds an `IoRaw` inststance that holds a valid pointer to the start of the I/O
+/// memory mapped PCI bar and its size.
+pub struct Bar<const SIZE: usize = 0> {
+ pdev: Device,
+ io: IoRaw<SIZE>,
+ num: i32,
+}
+
+impl<const SIZE: usize> Bar<SIZE> {
+ fn new(pdev: Device, num: u32, name: &CStr) -> Result<Self> {
+ let len = pdev.resource_len(num)?;
+ if len == 0 {
+ return Err(ENOMEM);
+ }
+
+ // Convert to `i32`, since that's what all the C bindings use.
+ let num = i32::try_from(num)?;
+
+ // SAFETY:
+ // `pdev` is valid by the invariants of `Device`.
+ // `num` is checked for validity by a previous call to `Device::resource_len`.
+ // `name` is always valid.
+ let ret = unsafe { bindings::pci_request_region(pdev.as_raw(), num, name.as_char_ptr()) };
+ if ret != 0 {
+ return Err(EBUSY);
+ }
+
+ // SAFETY:
+ // `pdev` is valid by the invariants of `Device`.
+ // `num` is checked for validity by a previous call to `Device::resource_len`.
+ // `name` is always valid.
+ let ioptr: usize = unsafe { bindings::pci_iomap(pdev.as_raw(), num, 0) } as usize;
+ if ioptr == 0 {
+ // SAFETY:
+ // `pdev` valid by the invariants of `Device`.
+ // `num` is checked for validity by a previous call to `Device::resource_len`.
+ unsafe { bindings::pci_release_region(pdev.as_raw(), num) };
+ return Err(ENOMEM);
+ }
+
+ let io = match IoRaw::new(ioptr, len as usize) {
+ Ok(io) => io,
+ Err(err) => {
+ // SAFETY:
+ // `pdev` is valid by the invariants of `Device`.
+ // `ioptr` is guaranteed to be the start of a valid I/O mapped memory region.
+ // `num` is checked for validity by a previous call to `Device::resource_len`.
+ unsafe { Self::do_release(&pdev, ioptr, num) };
+ return Err(err);
+ }
+ };
+
+ Ok(Bar { pdev, io, num })
+ }
+
+ /// # Safety
+ ///
+ /// `ioptr` must be a valid pointer to the memory mapped PCI bar number `num`.
+ unsafe fn do_release(pdev: &Device, ioptr: usize, num: i32) {
+ // SAFETY:
+ // `pdev` is valid by the invariants of `Device`.
+ // `ioptr` is valid by the safety requirements.
+ // `num` is valid by the safety requirements.
+ unsafe {
+ bindings::pci_iounmap(pdev.as_raw(), ioptr as _);
+ bindings::pci_release_region(pdev.as_raw(), num);
+ }
+ }
+
+ fn release(&self) {
+ // SAFETY: The safety requirements are guaranteed by the type invariant of `self.pdev`.
+ unsafe { Self::do_release(&self.pdev, self.io.addr(), self.num) };
+ }
+}
+
+impl Bar {
+ fn index_is_valid(index: u32) -> bool {
+ // A `struct pci_dev` owns an array of resources with at most `PCI_NUM_RESOURCES` entries.
+ index < bindings::PCI_NUM_RESOURCES
+ }
+}
+
+impl<const SIZE: usize> Drop for Bar<SIZE> {
+ fn drop(&mut self) {
+ self.release();
+ }
+}
+
+impl<const SIZE: usize> Deref for Bar<SIZE> {
+ type Target = Io<SIZE>;
+
+ fn deref(&self) -> &Self::Target {
+ // SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped.
+ unsafe { Io::from_raw(&self.io) }
+ }
+}
+
impl Device {
/// Create a PCI Device instance from an existing `device::Device`.
///
@@ -289,6 +399,36 @@ pub fn set_master(&self) {
// SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
unsafe { bindings::pci_set_master(self.as_raw()) };
}
+
+ /// Returns the size of the given PCI bar resource.
+ pub fn resource_len(&self, bar: u32) -> Result<bindings::resource_size_t> {
+ if !Bar::index_is_valid(bar) {
+ return Err(EINVAL);
+ }
+
+ // SAFETY:
+ // - `bar` is a valid bar number, as guaranteed by the above call to `Bar::index_is_valid`,
+ // - by its type invariant `self.as_raw` is always a valid pointer to a `struct pci_dev`.
+ Ok(unsafe { bindings::pci_resource_len(self.as_raw(), bar.try_into()?) })
+ }
+
+ /// Mapps an entire PCI-BAR after performing a region-request on it. I/O operation bound checks
+ /// can be performed on compile time for offsets (plus the requested type size) < SIZE.
+ pub fn iomap_region_sized<const SIZE: usize>(
+ &self,
+ bar: u32,
+ name: &CStr,
+ ) -> Result<Devres<Bar<SIZE>>> {
+ let bar = Bar::<SIZE>::new(self.clone(), bar, name)?;
+ let devres = Devres::new(self.as_ref(), bar, GFP_KERNEL)?;
+
+ Ok(devres)
+ }
+
+ /// Mapps an entire PCI-BAR after performing a region-request on it.
+ pub fn iomap_region(&self, bar: u32, name: &CStr) -> Result<Devres<Bar>> {
+ self.iomap_region_sized::<0>(bar, name)
+ }
}
impl AsRef<device::Device> for Device {
--
2.47.0
next prev parent reply other threads:[~2024-12-10 22:51 UTC|newest]
Thread overview: 47+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-12-10 22:46 [PATCH v5 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
2024-12-10 22:46 ` [PATCH v5 01/16] rust: pass module name to `Module::init` Danilo Krummrich
2024-12-11 10:45 ` Greg KH
2024-12-11 10:48 ` Greg KH
2024-12-11 10:59 ` Greg KH
2024-12-11 11:05 ` Greg KH
2024-12-11 11:41 ` Miguel Ojeda
2024-12-11 12:43 ` Alice Ryhl
2024-12-11 12:22 ` Danilo Krummrich
2024-12-11 12:34 ` Danilo Krummrich
2024-12-11 13:14 ` Greg KH
2024-12-11 13:31 ` Danilo Krummrich
2024-12-11 13:34 ` Alice Ryhl
2024-12-11 14:29 ` Danilo Krummrich
2024-12-11 14:45 ` Alice Ryhl
2024-12-11 14:52 ` Danilo Krummrich
2024-12-11 14:55 ` Alice Ryhl
2024-12-11 15:03 ` Danilo Krummrich
2024-12-11 15:15 ` Alice Ryhl
2024-12-11 15:36 ` Danilo Krummrich
2024-12-11 15:53 ` Danilo Krummrich
2024-12-11 13:45 ` Greg KH
2024-12-11 14:21 ` Danilo Krummrich
2024-12-11 14:30 ` Greg KH
2024-12-10 22:46 ` [PATCH v5 02/16] rust: implement generic driver registration Danilo Krummrich
2024-12-10 22:46 ` [PATCH v5 03/16] rust: implement `IdArray`, `IdTable` and `RawDeviceId` Danilo Krummrich
2024-12-10 22:46 ` [PATCH v5 04/16] rust: add rcu abstraction Danilo Krummrich
2024-12-11 18:47 ` Boqun Feng
2024-12-10 22:46 ` [PATCH v5 05/16] rust: types: add `Opaque::pin_init` Danilo Krummrich
2024-12-11 9:18 ` Alice Ryhl
2024-12-10 22:46 ` [PATCH v5 06/16] rust: add `Revocable` type Danilo Krummrich
2024-12-11 10:47 ` Benoît du Garreau
2024-12-11 10:57 ` Alice Ryhl
2024-12-10 22:46 ` [PATCH v5 07/16] rust: add `io::{Io, IoRaw}` base types Danilo Krummrich
2024-12-10 22:46 ` [PATCH v5 08/16] rust: add devres abstraction Danilo Krummrich
2024-12-10 22:46 ` [PATCH v5 09/16] rust: pci: add basic PCI device / driver abstractions Danilo Krummrich
2024-12-10 22:46 ` Danilo Krummrich [this message]
2024-12-10 22:46 ` [PATCH v5 11/16] samples: rust: add Rust PCI sample driver Danilo Krummrich
2024-12-16 5:07 ` kernel test robot
2024-12-10 22:46 ` [PATCH v5 12/16] rust: of: add `of::DeviceId` abstraction Danilo Krummrich
2024-12-10 22:46 ` [PATCH v5 13/16] rust: driver: implement `Adapter` Danilo Krummrich
2024-12-12 2:10 ` Fabien Parent
2024-12-10 22:46 ` [PATCH v5 14/16] rust: platform: add basic platform device / driver abstractions Danilo Krummrich
2024-12-11 15:27 ` Rob Herring
2024-12-10 22:46 ` [PATCH v5 15/16] samples: rust: add Rust platform sample driver Danilo Krummrich
2024-12-11 15:29 ` Rob Herring
2024-12-10 22:46 ` [PATCH v5 16/16] MAINTAINERS: add Danilo to DRIVER CORE Danilo Krummrich
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20241210224947.23804-11-dakr@kernel.org \
--to=dakr@kernel.org \
--cc=a.hindborg@samsung.com \
--cc=airlied@gmail.com \
--cc=ajanulgu@redhat.com \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=benno.lossin@proton.me \
--cc=bhelgaas@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=chrisi.schrefl@gmail.com \
--cc=daniel.almeida@collabora.com \
--cc=devicetree@vger.kernel.org \
--cc=dirk.behme@de.bosch.com \
--cc=fabien.parent@linaro.org \
--cc=fujita.tomonori@gmail.com \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=j@jannau.net \
--cc=lina@asahilina.net \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=lyude@redhat.com \
--cc=ojeda@kernel.org \
--cc=pstanner@redhat.com \
--cc=rafael@kernel.org \
--cc=robh@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=saravanak@google.com \
--cc=tmgross@umich.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.