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
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 v3 12/16] rust: pci: implement I/O mappable `pci::Bar`
Date: Tue, 22 Oct 2024 23:31:49 +0200 [thread overview]
Message-ID: <20241022213221.2383-13-dakr@kernel.org> (raw)
In-Reply-To: <20241022213221.2383-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 | 145 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 145 insertions(+)
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index ccc9a5f322e4..58f7d9c0045b 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -5,10 +5,13 @@
//! 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,
str::CStr,
types::{ARef, ForeignOwnable},
ThisModule,
@@ -239,9 +242,116 @@ 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 `Io` 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: Io<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);
+ }
+
+ // SAFETY: `ioptr` is guaranteed to be the start of a valid I/O mapped memory region of size
+ // `len`.
+ let io = match unsafe { Io::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.base_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 {
+ &self.io
+ }
+}
+
impl Device {
/// Create a PCI Device instance from an existing `device::Device`.
///
@@ -275,6 +385,41 @@ 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)
+ }
+
+ /// Returns a new `ARef` of the base `device::Device`.
+ pub fn as_dev(&self) -> ARef<device::Device> {
+ self.0.clone()
+ }
}
impl AsRef<device::Device> for Device {
--
2.46.2
next prev parent reply other threads:[~2024-10-22 21:33 UTC|newest]
Thread overview: 88+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
2024-10-22 21:31 ` [PATCH v3 01/16] rust: init: introduce `Opaque::try_ffi_init` Danilo Krummrich
2024-10-29 12:42 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 02/16] rust: introduce `InPlaceModule` Danilo Krummrich
2024-10-29 12:45 ` Alice Ryhl
2024-11-04 0:15 ` Greg KH
2024-11-04 17:36 ` Miguel Ojeda
2024-10-22 21:31 ` [PATCH v3 03/16] rust: pass module name to `Module::init` Danilo Krummrich
2024-10-29 12:55 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 04/16] rust: implement generic driver registration Danilo Krummrich
2024-10-22 21:31 ` [PATCH v3 05/16] rust: implement `IdArray`, `IdTable` and `RawDeviceId` Danilo Krummrich
2024-11-25 13:42 ` Miguel Ojeda
2024-10-22 21:31 ` [PATCH v3 06/16] rust: add rcu abstraction Danilo Krummrich
2024-10-29 13:59 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 07/16] rust: add `Revocable` type Danilo Krummrich
2024-10-29 13:26 ` Alice Ryhl
2024-12-03 9:21 ` Danilo Krummrich
2024-12-03 9:24 ` Alice Ryhl
2024-12-03 9:35 ` Danilo Krummrich
2024-12-03 10:58 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 08/16] rust: add `dev_*` print macros Danilo Krummrich
2024-11-04 0:24 ` Greg KH
2024-10-22 21:31 ` [PATCH v3 09/16] rust: add `io::Io` base type Danilo Krummrich
2024-10-28 15:43 ` Alice Ryhl
2024-10-29 9:20 ` Danilo Krummrich
2024-10-29 10:18 ` Alice Ryhl
2024-11-06 23:44 ` Daniel Almeida
2024-11-06 23:31 ` Daniel Almeida
2024-10-22 21:31 ` [PATCH v3 10/16] rust: add devres abstraction Danilo Krummrich
2024-10-31 14:03 ` Alice Ryhl
2024-11-27 12:21 ` Alice Ryhl
2024-11-27 13:19 ` Danilo Krummrich
2024-11-27 13:20 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 11/16] rust: pci: add basic PCI device / driver abstractions Danilo Krummrich
2024-10-27 22:42 ` Boqun Feng
2024-10-28 10:21 ` Danilo Krummrich
2024-11-26 14:06 ` Danilo Krummrich
2024-10-29 21:16 ` Christian Schrefl
2024-10-22 21:31 ` Danilo Krummrich [this message]
2024-10-22 21:31 ` [PATCH v3 13/16] samples: rust: add Rust PCI sample driver Danilo Krummrich
2024-10-23 15:57 ` Rob Herring
2024-10-28 13:22 ` Danilo Krummrich
2024-10-22 21:31 ` [PATCH v3 14/16] rust: of: add `of::DeviceId` abstraction Danilo Krummrich
2024-10-22 23:03 ` Rob Herring
2024-10-23 6:33 ` Danilo Krummrich
2024-10-27 4:38 ` Fabien Parent
2024-10-29 13:37 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions Danilo Krummrich
2024-10-22 23:47 ` Rob Herring
2024-10-23 6:44 ` Danilo Krummrich
2024-10-23 14:23 ` Rob Herring
2024-10-28 10:15 ` Danilo Krummrich
2024-10-30 12:23 ` Rob Herring
2024-11-26 12:39 ` Danilo Krummrich
2024-11-26 14:44 ` Rob Herring
2024-11-26 15:17 ` Danilo Krummrich
2024-11-26 19:15 ` Rob Herring
2024-11-26 20:01 ` Danilo Krummrich
2024-10-24 9:11 ` Dirk Behme
2024-10-28 10:19 ` Danilo Krummrich
2024-10-29 7:20 ` Dirk Behme
2024-10-29 8:50 ` Danilo Krummrich
2024-10-29 9:19 ` Dirk Behme
2024-10-29 9:50 ` Danilo Krummrich
2024-10-29 9:55 ` Danilo Krummrich
2024-10-29 10:08 ` Dirk Behme
2024-10-30 13:18 ` Rob Herring
2024-10-27 4:32 ` Fabien Parent
2024-10-28 13:44 ` Dirk Behme
2024-10-29 13:16 ` Alice Ryhl
2024-10-30 15:50 ` Alice Ryhl
2024-10-30 18:07 ` Danilo Krummrich
2024-10-31 8:23 ` Alice Ryhl
2024-11-26 14:13 ` Danilo Krummrich
2024-12-04 19:25 ` Daniel Almeida
2024-12-04 19:30 ` Danilo Krummrich
2024-10-22 21:31 ` [PATCH v3 16/16] samples: rust: add Rust platform sample driver Danilo Krummrich
2024-10-23 0:04 ` Rob Herring
2024-10-23 6:59 ` Danilo Krummrich
2024-10-23 15:37 ` Rob Herring
2024-10-28 9:32 ` Danilo Krummrich
2024-10-25 10:32 ` Dirk Behme
2024-10-25 16:08 ` Rob Herring
2024-10-23 5:13 ` [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Greg KH
2024-10-23 7:28 ` Danilo Krummrich
2024-10-25 5:15 ` Dirk Behme
2024-11-16 14:32 ` Janne Grunau
2024-11-16 14:50 ` Greg KH
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=20241022213221.2383-13-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=daniel.almeida@collabora.com \
--cc=devicetree@vger.kernel.org \
--cc=fujita.tomonori@gmail.com \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--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.