From: Danilo Krummrich <dakr@redhat.com>
To: gregkh@linuxfoundation.org, rafael@kernel.org,
bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com,
wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net,
bjorn3_gh@protonmail.com, benno.lossin@proton.me,
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
Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-pci@vger.kernel.org, Danilo Krummrich <dakr@redhat.com>
Subject: [RFC PATCH 10/11] rust: add basic abstractions for iomem operations
Date: Mon, 20 May 2024 19:25:47 +0200 [thread overview]
Message-ID: <20240520172554.182094-11-dakr@redhat.com> (raw)
In-Reply-To: <20240520172554.182094-1-dakr@redhat.com>
From: Philipp Stanner <pstanner@redhat.com>
Access to the kernel's IO-functions (e.g., readb()) is needed by almost
all drivers. Currently, there are no abstractions for those functions.
Since iomem is so widely used, it's necessary to provide a generic
interface that all subsystems providing IO memory can use. The existing
C implementations of such subsystems typically provide their own
wrappers around functions like ioremap() which take care of respecting
resource boundaries etc. It is, therefore, desirable to use these
wrappers because using ioremap() and iounmap() directly would
effectively result in parts of those subsystems being reimplemented in
Rust.
As most if not all device drivers should be fully satisfied regarding
their iomem demands by the existing subsystem interfaces, Rust
abstractions as congruent as possible with the existing infrastructure
shall use the existing subsystem (e.g., PCI) interfaces for creating
IO mappings, while simultaneously wrapping those mappings in Rust
containers whose Drop() traits ensure that the resources are released
again.
The process for mapping iomem would consequently look as follows:
1. The subsystem abstraction (e.g., PCI) requests and ioremaps the
memory through the corresponding C functions.
2. The subsystem uses resources obtained in step #1 to create a Rust
IoMem data structure that implements the IO functionality such as
readb() for the iomem.
3. The subsystem code wrapps IoMem into additional containers that
ensure, e.g., thread safety, prevent UAF etc. Additionally, the
subsystem ensures that access to IoMem is revoked latest when the
driver's remove() callback is invoked.
Hereby, the subsystem data structure obtains ownership over the iomem.
Release of the iomem and, possibly, other subsystem associated data is
then handled through the Drop() trait of the subsystem data structure.
IO memory can become invalid during runtime (for example because the
driver's remove() callback was invoked, revoking access to the driver's
resources). However, in parallel executing routines might still be
active. Consequently, the subsytem should also guard the iomem in some
way.
One way to do this is the Devres implementation, which provides a
container that is capable of revoking access to its payload when
the driver's remove() callback is invoked.
The figure illustrates what usage of IoMem through subsystems might look
like:
Devres
*------------------------------*
| |
| subsystem data structure |
| *----------------------* |
| | IoMem | |
| | *------------------* | |
| | | io_addr = 0x42, | | |
| | | io_len = 9001, | | |
| | | | | |
| | | readb(), | | |
| | | writeb(), | | |
| | | ... | | |
| | *------------------* | |
| | deref(), | |
| | drop(), | |
| | ... | |
| *----------------------* |
| deref(), |
| drop(), |
*------------------------------*
For additional convenience, subsystem abstractions can implement the
Deref() trait for their data structures so that access he iomem can be
fully transparent.
In summary, IoMem would just serve as a container providing the IO
functions, and the subsystem, which knows about the memory layout,
request mechanisms etc, shall create and guard IoMem and ensure that its
resources are released latest at remove() (through Devres) or earlier
through its Drop() implementation.
Add 'IoMem', a struct holding an IO-Pointer and a length parameter,
which both shall be initialized validly by the subsystem.
Add Rust abstractions for basic IO memory operations on IoMem.
Signed-off-by: Philipp Stanner <pstanner@redhat.com>
Signed-off-by: Danilo Krummrich <dakr@redhat.com>
---
rust/helpers.c | 106 +++++++++++++++++++++++++++++++++
rust/kernel/iomem.rs | 135 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 241 insertions(+)
create mode 100644 rust/kernel/iomem.rs
diff --git a/rust/helpers.c b/rust/helpers.c
index c3d80301185c..dc2405772b1a 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -34,6 +34,7 @@
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/pci.h>
+#include <linux/io.h>
__noreturn void rust_helper_BUG(void)
{
@@ -179,6 +180,111 @@ int rust_helper_devm_add_action(struct device *dev, void (*action)(void *), void
return devm_add_action(dev, action, data);
}
+/* io.h */
+u8 rust_helper_readb(const volatile void __iomem *addr)
+{
+ return readb(addr);
+}
+EXPORT_SYMBOL_GPL(rust_helper_readb);
+
+u16 rust_helper_readw(const volatile void __iomem *addr)
+{
+ return readw(addr);
+}
+EXPORT_SYMBOL_GPL(rust_helper_readw);
+
+u32 rust_helper_readl(const volatile void __iomem *addr)
+{
+ return readl(addr);
+}
+EXPORT_SYMBOL_GPL(rust_helper_readl);
+
+#ifdef CONFIG_64BIT
+u64 rust_helper_readq(const volatile void __iomem *addr)
+{
+ return readq(addr);
+}
+EXPORT_SYMBOL_GPL(rust_helper_readq);
+#endif
+
+void rust_helper_writeb(u8 value, volatile void __iomem *addr)
+{
+ writeb(value, addr);
+}
+EXPORT_SYMBOL_GPL(rust_helper_writeb);
+
+void rust_helper_writew(u16 value, volatile void __iomem *addr)
+{
+ writew(value, addr);
+}
+EXPORT_SYMBOL_GPL(rust_helper_writew);
+
+void rust_helper_writel(u32 value, volatile void __iomem *addr)
+{
+ writel(value, addr);
+}
+EXPORT_SYMBOL_GPL(rust_helper_writel);
+
+#ifdef CONFIG_64BIT
+void rust_helper_writeq(u64 value, volatile void __iomem *addr)
+{
+ writeq(value, addr);
+}
+EXPORT_SYMBOL_GPL(rust_helper_writeq);
+#endif
+
+u8 rust_helper_readb_relaxed(const volatile void __iomem *addr)
+{
+ return readb_relaxed(addr);
+}
+EXPORT_SYMBOL_GPL(rust_helper_readb_relaxed);
+
+u16 rust_helper_readw_relaxed(const volatile void __iomem *addr)
+{
+ return readw_relaxed(addr);
+}
+EXPORT_SYMBOL_GPL(rust_helper_readw_relaxed);
+
+u32 rust_helper_readl_relaxed(const volatile void __iomem *addr)
+{
+ return readl_relaxed(addr);
+}
+EXPORT_SYMBOL_GPL(rust_helper_readl_relaxed);
+
+#ifdef CONFIG_64BIT
+u64 rust_helper_readq_relaxed(const volatile void __iomem *addr)
+{
+ return readq_relaxed(addr);
+}
+EXPORT_SYMBOL_GPL(rust_helper_readq_relaxed);
+#endif
+
+void rust_helper_writeb_relaxed(u8 value, volatile void __iomem *addr)
+{
+ writeb_relaxed(value, addr);
+}
+EXPORT_SYMBOL_GPL(rust_helper_writeb_relaxed);
+
+void rust_helper_writew_relaxed(u16 value, volatile void __iomem *addr)
+{
+ writew_relaxed(value, addr);
+}
+EXPORT_SYMBOL_GPL(rust_helper_writew_relaxed);
+
+void rust_helper_writel_relaxed(u32 value, volatile void __iomem *addr)
+{
+ writel_relaxed(value, addr);
+}
+EXPORT_SYMBOL_GPL(rust_helper_writel_relaxed);
+
+#ifdef CONFIG_64BIT
+void rust_helper_writeq_relaxed(u64 value, volatile void __iomem *addr)
+{
+ writeq_relaxed(value, addr);
+}
+EXPORT_SYMBOL_GPL(rust_helper_writeq_relaxed);
+#endif
+
void rust_helper_pci_set_drvdata(struct pci_dev *pdev, void *data)
{
pci_set_drvdata(pdev, data);
diff --git a/rust/kernel/iomem.rs b/rust/kernel/iomem.rs
new file mode 100644
index 000000000000..efb6cd0829b4
--- /dev/null
+++ b/rust/kernel/iomem.rs
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use crate::bindings;
+use crate::error::{code::EINVAL, Result};
+
+/// IO-mapped memory, starting at the base pointer @ioptr and spanning @malxen bytes.
+///
+/// The creator (usually a subsystem such as PCI) is responsible for creating the
+/// mapping, performing an additional region request etc.
+pub struct IoMem {
+ pub ioptr: usize,
+ maxlen: usize,
+}
+
+impl IoMem {
+ pub(crate) fn new(ioptr: usize, maxlen: usize) -> Result<Self> {
+ if ioptr == 0 || maxlen == 0 {
+ return Err(EINVAL);
+ }
+
+ Ok(Self { ioptr, maxlen })
+ }
+
+ fn get_io_addr(&self, offset: usize, len: usize) -> Result<usize> {
+ if offset + len > self.maxlen {
+ return Err(EINVAL);
+ }
+
+ Ok(self.ioptr + offset)
+ }
+
+ pub fn readb(&self, offset: usize) -> Result<u8> {
+ let ioptr: usize = self.get_io_addr(offset, 1)?;
+
+ Ok(unsafe { bindings::readb(ioptr as _) })
+ }
+
+ pub fn readw(&self, offset: usize) -> Result<u16> {
+ let ioptr: usize = self.get_io_addr(offset, 2)?;
+
+ Ok(unsafe { bindings::readw(ioptr as _) })
+ }
+
+ pub fn readl(&self, offset: usize) -> Result<u32> {
+ let ioptr: usize = self.get_io_addr(offset, 4)?;
+
+ Ok(unsafe { bindings::readl(ioptr as _) })
+ }
+
+ pub fn readq(&self, offset: usize) -> Result<u64> {
+ let ioptr: usize = self.get_io_addr(offset, 8)?;
+
+ Ok(unsafe { bindings::readq(ioptr as _) })
+ }
+
+ pub fn readb_relaxed(&self, offset: usize) -> Result<u8> {
+ let ioptr: usize = self.get_io_addr(offset, 1)?;
+
+ Ok(unsafe { bindings::readb_relaxed(ioptr as _) })
+ }
+
+ pub fn readw_relaxed(&self, offset: usize) -> Result<u16> {
+ let ioptr: usize = self.get_io_addr(offset, 2)?;
+
+ Ok(unsafe { bindings::readw_relaxed(ioptr as _) })
+ }
+
+ pub fn readl_relaxed(&self, offset: usize) -> Result<u32> {
+ let ioptr: usize = self.get_io_addr(offset, 4)?;
+
+ Ok(unsafe { bindings::readl_relaxed(ioptr as _) })
+ }
+
+ pub fn readq_relaxed(&self, offset: usize) -> Result<u64> {
+ let ioptr: usize = self.get_io_addr(offset, 8)?;
+
+ Ok(unsafe { bindings::readq_relaxed(ioptr as _) })
+ }
+
+ pub fn writeb(&self, byte: u8, offset: usize) -> Result {
+ let ioptr: usize = self.get_io_addr(offset, 1)?;
+
+ unsafe { bindings::writeb(byte, ioptr as _) }
+ Ok(())
+ }
+
+ pub fn writew(&self, word: u16, offset: usize) -> Result {
+ let ioptr: usize = self.get_io_addr(offset, 2)?;
+
+ unsafe { bindings::writew(word, ioptr as _) }
+ Ok(())
+ }
+
+ pub fn writel(&self, lword: u32, offset: usize) -> Result {
+ let ioptr: usize = self.get_io_addr(offset, 4)?;
+
+ unsafe { bindings::writel(lword, ioptr as _) }
+ Ok(())
+ }
+
+ pub fn writeq(&self, qword: u64, offset: usize) -> Result {
+ let ioptr: usize = self.get_io_addr(offset, 8)?;
+
+ unsafe { bindings::writeq(qword, ioptr as _) }
+ Ok(())
+ }
+
+ pub fn writeb_relaxed(&self, byte: u8, offset: usize) -> Result {
+ let ioptr: usize = self.get_io_addr(offset, 1)?;
+
+ unsafe { bindings::writeb_relaxed(byte, ioptr as _) }
+ Ok(())
+ }
+
+ pub fn writew_relaxed(&self, word: u16, offset: usize) -> Result {
+ let ioptr: usize = self.get_io_addr(offset, 2)?;
+
+ unsafe { bindings::writew_relaxed(word, ioptr as _) }
+ Ok(())
+ }
+
+ pub fn writel_relaxed(&self, lword: u32, offset: usize) -> Result {
+ let ioptr: usize = self.get_io_addr(offset, 4)?;
+
+ unsafe { bindings::writel_relaxed(lword, ioptr as _) }
+ Ok(())
+ }
+
+ pub fn writeq_relaxed(&self, qword: u64, offset: usize) -> Result {
+ let ioptr: usize = self.get_io_addr(offset, 8)?;
+
+ unsafe { bindings::writeq_relaxed(qword, ioptr as _) }
+ Ok(())
+ }
+}
--
2.45.1
next prev parent reply other threads:[~2024-05-20 17:27 UTC|newest]
Thread overview: 51+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-05-20 17:25 [RFC PATCH 00/11] [RFC] Device / Driver and PCI Rust abstractions Danilo Krummrich
2024-05-20 17:25 ` [RFC PATCH 01/11] rust: add abstraction for struct device Danilo Krummrich
2024-05-20 18:00 ` Greg KH
2024-05-20 18:24 ` Miguel Ojeda
2024-05-20 20:22 ` Danilo Krummrich
2024-05-21 9:24 ` Greg KH
2024-05-21 20:42 ` Danilo Krummrich
2024-06-04 14:17 ` Greg KH
2024-06-04 16:23 ` Danilo Krummrich
2024-05-20 17:25 ` [RFC PATCH 02/11] rust: add driver abstraction Danilo Krummrich
2024-05-20 18:14 ` Greg KH
2024-05-20 22:30 ` Danilo Krummrich
2024-05-21 9:35 ` Greg KH
2024-05-21 9:59 ` Greg KH
2024-05-21 22:21 ` Danilo Krummrich
2024-06-04 14:27 ` Greg KH
2024-06-04 15:41 ` Danilo Krummrich
2024-06-04 16:00 ` Greg KH
2024-06-04 20:06 ` Danilo Krummrich
2024-05-21 5:42 ` Dave Airlie
2024-05-21 8:04 ` Greg KH
2024-05-21 22:42 ` Danilo Krummrich
2024-05-29 11:10 ` Dirk Behme
2024-05-30 5:58 ` Dirk Behme
2024-05-20 17:25 ` [RFC PATCH 03/11] rust: add rcu abstraction Danilo Krummrich
2024-05-20 17:25 ` [RFC PATCH 04/11] rust: add revocable mutex Danilo Krummrich
2024-05-20 17:25 ` [RFC PATCH 05/11] rust: add revocable objects Danilo Krummrich
2024-05-31 8:35 ` Dirk Behme
2024-05-20 17:25 ` [RFC PATCH 06/11] rust: add device::Data Danilo Krummrich
2024-05-20 17:25 ` [RFC PATCH 07/11] rust: add `dev_*` print macros Danilo Krummrich
2024-05-20 17:25 ` [RFC PATCH 08/11] rust: add devres abstraction Danilo Krummrich
2024-05-29 12:00 ` Dirk Behme
2024-06-03 7:20 ` Dirk Behme
2024-05-20 17:25 ` [RFC PATCH 09/11] rust: add basic PCI driver abstractions Danilo Krummrich
2024-05-20 17:25 ` Danilo Krummrich [this message]
2024-05-20 22:32 ` [RFC PATCH 10/11] rust: add basic abstractions for iomem operations Miguel Ojeda
2024-05-21 2:07 ` Dave Airlie
2024-05-21 3:01 ` Wedson Almeida Filho
2024-05-21 8:03 ` Philipp Stanner
2024-05-25 19:24 ` Wedson Almeida Filho
2024-05-21 2:59 ` Danilo Krummrich
2024-05-21 7:36 ` Philipp Stanner
2024-05-21 9:18 ` Miguel Ojeda
2024-05-21 18:36 ` Danilo Krummrich
2024-05-20 17:25 ` [RFC PATCH 11/11] rust: PCI: add BAR request and ioremap Danilo Krummrich
2024-05-20 23:27 ` Miguel Ojeda
2024-05-21 11:22 ` Danilo Krummrich
2024-05-20 18:14 ` [RFC PATCH 00/11] [RFC] Device / Driver and PCI Rust abstractions Greg KH
2024-05-20 18:16 ` Greg KH
2024-05-20 19:50 ` Danilo Krummrich
2024-05-21 9:21 ` 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=20240520172554.182094-11-dakr@redhat.com \
--to=dakr@redhat.com \
--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=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=rust-for-linux@vger.kernel.org \
--cc=wedsonaf@gmail.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®