* [PATCH v2] rust: io: Add a BigEndianMmio wrapper
@ 2026-08-06 6:22 Link Mauve
2026-08-06 11:44 ` Gary Guo
0 siblings, 1 reply; 3+ messages in thread
From: Link Mauve @ 2026-08-06 6:22 UTC (permalink / raw)
To: rust-for-linux
Cc: Link Mauve, Ethan Plant, Michael Garofalo, Ash Logan,
Jonathan Neuschäfer, Sasha Finkelstein, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, linux-kernel, driver-core
This allows the user to read from and write to big-endian MMIO devices,
such as those found on PowerPC systems.
The implementation is pretty much a copy-paste of the RelaxedMmio type,
with the business logic changed.
This has been tested with various WIP drivers on the Nintendo Wii, and I
thought it would be nicer to upstream it before those are completely
ready, to get some review.
Signed-off-by: Link Mauve <linkmauve@linkmauve.fr>
---
rust/helpers/io.c | 34 ++++++++++++++++++
rust/kernel/io.rs | 88 +++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 120 insertions(+), 2 deletions(-)
Changes since v1:
- Link to v1:
https://lore.kernel.org/rust-for-linux/178598049138.1212746.2443151507074972963.b4-review@b4/T/
- Fix copy/paste mistake in backend docstring (thank you Ethan Plant!)
- Fix safety comment in macro documentation.
diff --git a/rust/helpers/io.c b/rust/helpers/io.c
index 7ed9a4f77f1b..3bbb86a00d67 100644
--- a/rust/helpers/io.c
+++ b/rust/helpers/io.c
@@ -120,6 +120,40 @@ __rust_helper void rust_helper_writeq_relaxed(u64 value, void __iomem *addr)
}
#endif
+__rust_helper u16 rust_helper_ioread16be(const void __iomem *addr)
+{
+ return ioread16be(addr);
+}
+
+__rust_helper u32 rust_helper_ioread32be(const void __iomem *addr)
+{
+ return ioread32be(addr);
+}
+
+#ifdef CONFIG_64BIT
+__rust_helper u64 rust_helper_ioread64be(const void __iomem *addr)
+{
+ return ioread64be(addr);
+}
+#endif
+
+__rust_helper void rust_helper_iowrite16be(u16 value, void __iomem *addr)
+{
+ iowrite16be(value, addr);
+}
+
+__rust_helper void rust_helper_iowrite32be(u32 value, void __iomem *addr)
+{
+ iowrite32be(value, addr);
+}
+
+#ifdef CONFIG_64BIT
+__rust_helper void rust_helper_iowrite64be(u64 value, void __iomem *addr)
+{
+ iowrite64be(value, addr);
+}
+#endif
+
__rust_helper resource_size_t rust_helper_resource_size(struct resource *res)
{
return resource_size(res);
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index a38c20ba3d23..ab8b7864298a 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -1194,14 +1194,14 @@ impl IoCapable<$ty> for $backend {
#[inline]
fn io_read(view: <$backend as IoBackend>::View<'_, $ty>) -> $ty {
// SAFETY: `$backend::as_ptr(view)` is a valid pointer for MMIO operations for both
- // `MmioBackend` and `RelaxedMmioBackend`.
+ // `MmioBackend`, `RelaxedMmioBackend` and `BigEndianMmioBackend`.
unsafe { bindings::$read_fn($backend::as_ptr(view).cast_const().cast()) }
}
#[inline]
fn io_write(view: <$backend as IoBackend>::View<'_, $ty>, value: $ty) {
// SAFETY: `$backend::as_ptr(view)` is a valid pointer for MMIO operations for both
- // `MmioBackend` and `RelaxedMmioBackend`.
+ // `MmioBackend`, `RelaxedMmioBackend` and `BigEndianMmioBackend`.
unsafe { bindings::$write_fn(value, $backend::as_ptr(view).cast()) }
}
}
@@ -1751,3 +1751,87 @@ macro_rules! io_write {
}
#[doc(inline)]
pub use crate::io_write;
+
+/// [`Mmio`] wrapper using big-endian accessors.
+///
+/// This type provides an implementation of [`Io`] that uses big-endian I/O MMIO operands instead of
+/// the regular little-endian ones.
+///
+/// See [`Mmio::big_endian`] for a usage example.
+#[repr(transparent)]
+pub struct BigEndianMmio<'a, T: ?Sized>(Mmio<'a, T>);
+
+impl<T: ?Sized> Copy for BigEndianMmio<'_, T> {}
+impl<T: ?Sized> Clone for BigEndianMmio<'_, T> {
+ #[inline]
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+
+/// I/O Backend for memory-mapped I/O using big-endian accessors.
+pub struct BigEndianMmioBackend;
+
+impl IoBackend for BigEndianMmioBackend {
+ type View<'a, T: ?Sized + KnownSize> = BigEndianMmio<'a, T>;
+
+ #[inline]
+ fn as_ptr<'a, T: ?Sized + KnownSize>(view: Self::View<'a, T>) -> *mut T {
+ MmioBackend::as_ptr(view.0)
+ }
+
+ #[inline]
+ unsafe fn project_view<'a, T: ?Sized + KnownSize, U: ?Sized + KnownSize>(
+ view: Self::View<'a, T>,
+ ptr: *mut U,
+ ) -> Self::View<'a, U> {
+ // SAFETY: Per safety requirement.
+ BigEndianMmio(unsafe { MmioBackend::project_view(view.0, ptr) })
+ }
+}
+
+impl<'a, T: ?Sized + KnownSize> IoBase<'a> for BigEndianMmio<'a, T> {
+ type Backend = BigEndianMmioBackend;
+ type Target = T;
+
+ #[inline]
+ fn as_view(self) -> BigEndianMmio<'a, T> {
+ self
+ }
+}
+
+impl<'a, T: ?Sized> Mmio<'a, T> {
+ /// Returns a [`BigEndianMmio`] reference that performs big-endian I/O operations.
+ ///
+ /// Big-endian makes no change to 8-bit accesses, but will invert the bytes of 16-, 32- and
+ /// 64-bit accesses, to ensure numbers will be read in their correct order.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// Io,
+ /// Mmio,
+ /// Region,
+ /// BigEndianMmio,
+ /// };
+ ///
+ /// fn do_io(io: Mmio<'_, Region<0x100>>) {
+ /// // The access is performed using `ioread32be` instead of `readl`.
+ /// let v = io.big_endian().read32(0x10);
+ /// }
+ ///
+ /// ```
+ #[inline]
+ pub fn big_endian(self) -> BigEndianMmio<'a, T> {
+ BigEndianMmio(self)
+ }
+}
+
+// MMIO regions support 8, 16, and 32-bit accesses.
+impl_mmio_io_capable!(BigEndianMmioBackend, u8, readb, writeb);
+impl_mmio_io_capable!(BigEndianMmioBackend, u16, ioread16be, iowrite16be);
+impl_mmio_io_capable!(BigEndianMmioBackend, u32, ioread32be, iowrite32be);
+// MMIO regions on 64-bit systems also support 64-bit accesses.
+#[cfg(CONFIG_64BIT)]
+impl_mmio_io_capable!(BigEndianMmioBackend, u64, ioread64be, iowrite64be);
base-commit: 1701fda2f58e345c050f4309971bdc07cd6146ba
--
2.55.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH v2] rust: io: Add a BigEndianMmio wrapper
2026-08-06 6:22 [PATCH v2] rust: io: Add a BigEndianMmio wrapper Link Mauve
@ 2026-08-06 11:44 ` Gary Guo
2026-08-06 12:13 ` Link Mauve
0 siblings, 1 reply; 3+ messages in thread
From: Gary Guo @ 2026-08-06 11:44 UTC (permalink / raw)
To: Link Mauve, rust-for-linux
Cc: Ethan Plant, Michael Garofalo, Ash Logan,
Jonathan Neuschäfer, Sasha Finkelstein, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, linux-kernel, driver-core
On Thu Aug 6, 2026 at 7:22 AM BST, Link Mauve wrote:
> This allows the user to read from and write to big-endian MMIO devices,
> such as those found on PowerPC systems.
>
> The implementation is pretty much a copy-paste of the RelaxedMmio type,
> with the business logic changed.
>
> This has been tested with various WIP drivers on the Nintendo Wii, and I
> thought it would be nicer to upstream it before those are completely
> ready, to get some review.
An alternative approach would be to have a generic wrapper `BigEndian<IO>` where
it performs byte-swapping in its `IoCapable` implementation. This is more
composable as it works for all I/O.
Of course, this means that it won't be using the specialized BE I/O impl, but
then the only architectures that have specialized ioread16be are PPC32 and
SPARC, so maybe it's a reasonable trade-off to make?
Best,
Gary
>
> Signed-off-by: Link Mauve <linkmauve@linkmauve.fr>
> ---
> rust/helpers/io.c | 34 ++++++++++++++++++
> rust/kernel/io.rs | 88 +++++++++++++++++++++++++++++++++++++++++++++--
> 2 files changed, 120 insertions(+), 2 deletions(-)
>
> Changes since v1:
> - Link to v1:
> https://lore.kernel.org/rust-for-linux/178598049138.1212746.2443151507074972963.b4-review@b4/T/
> - Fix copy/paste mistake in backend docstring (thank you Ethan Plant!)
> - Fix safety comment in macro documentation.
>
> diff --git a/rust/helpers/io.c b/rust/helpers/io.c
> index 7ed9a4f77f1b..3bbb86a00d67 100644
> --- a/rust/helpers/io.c
> +++ b/rust/helpers/io.c
> @@ -120,6 +120,40 @@ __rust_helper void rust_helper_writeq_relaxed(u64 value, void __iomem *addr)
> }
> #endif
>
> +__rust_helper u16 rust_helper_ioread16be(const void __iomem *addr)
> +{
> + return ioread16be(addr);
> +}
> +
> +__rust_helper u32 rust_helper_ioread32be(const void __iomem *addr)
> +{
> + return ioread32be(addr);
> +}
> +
> +#ifdef CONFIG_64BIT
> +__rust_helper u64 rust_helper_ioread64be(const void __iomem *addr)
> +{
> + return ioread64be(addr);
> +}
> +#endif
> +
> +__rust_helper void rust_helper_iowrite16be(u16 value, void __iomem *addr)
> +{
> + iowrite16be(value, addr);
> +}
> +
> +__rust_helper void rust_helper_iowrite32be(u32 value, void __iomem *addr)
> +{
> + iowrite32be(value, addr);
> +}
> +
> +#ifdef CONFIG_64BIT
> +__rust_helper void rust_helper_iowrite64be(u64 value, void __iomem *addr)
> +{
> + iowrite64be(value, addr);
> +}
> +#endif
> +
> __rust_helper resource_size_t rust_helper_resource_size(struct resource *res)
> {
> return resource_size(res);
> diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
> index a38c20ba3d23..ab8b7864298a 100644
> --- a/rust/kernel/io.rs
> +++ b/rust/kernel/io.rs
> @@ -1194,14 +1194,14 @@ impl IoCapable<$ty> for $backend {
> #[inline]
> fn io_read(view: <$backend as IoBackend>::View<'_, $ty>) -> $ty {
> // SAFETY: `$backend::as_ptr(view)` is a valid pointer for MMIO operations for both
> - // `MmioBackend` and `RelaxedMmioBackend`.
> + // `MmioBackend`, `RelaxedMmioBackend` and `BigEndianMmioBackend`.
> unsafe { bindings::$read_fn($backend::as_ptr(view).cast_const().cast()) }
> }
>
> #[inline]
> fn io_write(view: <$backend as IoBackend>::View<'_, $ty>, value: $ty) {
> // SAFETY: `$backend::as_ptr(view)` is a valid pointer for MMIO operations for both
> - // `MmioBackend` and `RelaxedMmioBackend`.
> + // `MmioBackend`, `RelaxedMmioBackend` and `BigEndianMmioBackend`.
> unsafe { bindings::$write_fn(value, $backend::as_ptr(view).cast()) }
> }
> }
> @@ -1751,3 +1751,87 @@ macro_rules! io_write {
> }
> #[doc(inline)]
> pub use crate::io_write;
> +
> +/// [`Mmio`] wrapper using big-endian accessors.
> +///
> +/// This type provides an implementation of [`Io`] that uses big-endian I/O MMIO operands instead of
> +/// the regular little-endian ones.
> +///
> +/// See [`Mmio::big_endian`] for a usage example.
> +#[repr(transparent)]
> +pub struct BigEndianMmio<'a, T: ?Sized>(Mmio<'a, T>);
> +
> +impl<T: ?Sized> Copy for BigEndianMmio<'_, T> {}
> +impl<T: ?Sized> Clone for BigEndianMmio<'_, T> {
> + #[inline]
> + fn clone(&self) -> Self {
> + *self
> + }
> +}
> +
> +/// I/O Backend for memory-mapped I/O using big-endian accessors.
> +pub struct BigEndianMmioBackend;
> +
> +impl IoBackend for BigEndianMmioBackend {
> + type View<'a, T: ?Sized + KnownSize> = BigEndianMmio<'a, T>;
> +
> + #[inline]
> + fn as_ptr<'a, T: ?Sized + KnownSize>(view: Self::View<'a, T>) -> *mut T {
> + MmioBackend::as_ptr(view.0)
> + }
> +
> + #[inline]
> + unsafe fn project_view<'a, T: ?Sized + KnownSize, U: ?Sized + KnownSize>(
> + view: Self::View<'a, T>,
> + ptr: *mut U,
> + ) -> Self::View<'a, U> {
> + // SAFETY: Per safety requirement.
> + BigEndianMmio(unsafe { MmioBackend::project_view(view.0, ptr) })
> + }
> +}
> +
> +impl<'a, T: ?Sized + KnownSize> IoBase<'a> for BigEndianMmio<'a, T> {
> + type Backend = BigEndianMmioBackend;
> + type Target = T;
> +
> + #[inline]
> + fn as_view(self) -> BigEndianMmio<'a, T> {
> + self
> + }
> +}
> +
> +impl<'a, T: ?Sized> Mmio<'a, T> {
> + /// Returns a [`BigEndianMmio`] reference that performs big-endian I/O operations.
> + ///
> + /// Big-endian makes no change to 8-bit accesses, but will invert the bytes of 16-, 32- and
> + /// 64-bit accesses, to ensure numbers will be read in their correct order.
> + ///
> + /// # Examples
> + ///
> + /// ```no_run
> + /// use kernel::io::{
> + /// Io,
> + /// Mmio,
> + /// Region,
> + /// BigEndianMmio,
> + /// };
> + ///
> + /// fn do_io(io: Mmio<'_, Region<0x100>>) {
> + /// // The access is performed using `ioread32be` instead of `readl`.
> + /// let v = io.big_endian().read32(0x10);
> + /// }
> + ///
> + /// ```
> + #[inline]
> + pub fn big_endian(self) -> BigEndianMmio<'a, T> {
> + BigEndianMmio(self)
> + }
> +}
> +
> +// MMIO regions support 8, 16, and 32-bit accesses.
> +impl_mmio_io_capable!(BigEndianMmioBackend, u8, readb, writeb);
> +impl_mmio_io_capable!(BigEndianMmioBackend, u16, ioread16be, iowrite16be);
> +impl_mmio_io_capable!(BigEndianMmioBackend, u32, ioread32be, iowrite32be);
> +// MMIO regions on 64-bit systems also support 64-bit accesses.
> +#[cfg(CONFIG_64BIT)]
> +impl_mmio_io_capable!(BigEndianMmioBackend, u64, ioread64be, iowrite64be);
>
> base-commit: 1701fda2f58e345c050f4309971bdc07cd6146ba
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH v2] rust: io: Add a BigEndianMmio wrapper
2026-08-06 11:44 ` Gary Guo
@ 2026-08-06 12:13 ` Link Mauve
0 siblings, 0 replies; 3+ messages in thread
From: Link Mauve @ 2026-08-06 12:13 UTC (permalink / raw)
To: Gary Guo
Cc: Link Mauve, rust-for-linux, Ethan Plant, Michael Garofalo,
Ash Logan, Jonathan Neuschäfer, Sasha Finkelstein,
Miguel Ojeda, Boqun Feng, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, linux-kernel, driver-core
On Thu, Aug 06, 2026 at 12:44:31PM +0100, Gary Guo wrote:
> On Thu Aug 6, 2026 at 7:22 AM BST, Link Mauve wrote:
> > This allows the user to read from and write to big-endian MMIO devices,
> > such as those found on PowerPC systems.
> >
> > The implementation is pretty much a copy-paste of the RelaxedMmio type,
> > with the business logic changed.
> >
> > This has been tested with various WIP drivers on the Nintendo Wii, and I
> > thought it would be nicer to upstream it before those are completely
> > ready, to get some review.
>
> An alternative approach would be to have a generic wrapper `BigEndian<IO>` where
> it performs byte-swapping in its `IoCapable` implementation. This is more
> composable as it works for all I/O.
>
> Of course, this means that it won't be using the specialized BE I/O impl, but
> then the only architectures that have specialized ioread16be are PPC32 and
> SPARC, so maybe it's a reasonable trade-off to make?
In the end this should all be inlined anyway, I’ll compare the generated
assembly but I expect no change here.
>
> Best,
> Gary
[…]
--
Link Mauve
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-08-06 12:13 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-06 6:22 [PATCH v2] rust: io: Add a BigEndianMmio wrapper Link Mauve
2026-08-06 11:44 ` Gary Guo
2026-08-06 12:13 ` Link Mauve
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox