Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

rust: io: separate generic I/O helpers from MMIO implementation

The previous Io<SIZE> type combined both the generic I/O access helpers
and MMIO implementation details in a single struct. This coupling prevented
reusing the I/O helpers for other backends, such as PCI configuration
space.

Establish a clean separation between the I/O interface and concrete
backends by separating generic I/O helpers from MMIO implementation.

Introduce a new trait hierarchy to handle different access capabilities:

- IoCapable<T>: A marker trait indicating that a backend supports I/O
operations of a certain type (u8, u16, u32, or u64).

- Io trait: Defines fallible (try_read8, try_write8, etc.) and infallibile
(read8, write8, etc.) I/O methods with runtime bounds checking and
compile-time bounds checking.

- IoKnownSize trait: The marker trait for types support infallible I/O
methods.

Move the MMIO-specific logic into a dedicated Mmio<SIZE> type that
implements the Io traits. Rename IoRaw to MmioRaw and update consumers to
use the new types.

Cc: Alexandre Courbot <acourbot@nvidia.com>
Cc: Alice Ryhl <aliceryhl@google.com>
Cc: Bjorn Helgaas <helgaas@kernel.org>
Cc: Gary Guo <gary@garyguo.net>
Cc: Danilo Krummrich <dakr@kernel.org>
Cc: John Hubbard <jhubbard@nvidia.com>
Signed-off-by: Zhi Wang <zhiw@nvidia.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Gary Guo <gary@garyguo.net>
Link: https://patch.msgid.link/20260121202212.4438-3-zhiw@nvidia.com
[ Add #[expect(unused)] to define_{read,write}!(). - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>

authored by

Zhi Wang and committed by
Danilo Krummrich
121d87b2 7043698a

+439 -135
+1
drivers/gpu/drm/tyr/regs.rs
··· 11 11 use kernel::device::Bound; 12 12 use kernel::device::Device; 13 13 use kernel::devres::Devres; 14 + use kernel::io::Io; 14 15 use kernel::prelude::*; 15 16 16 17 use crate::driver::IoMem;
+4 -1
drivers/gpu/nova-core/gsp/sequencer.rs
··· 12 12 13 13 use kernel::{ 14 14 device, 15 - io::poll::read_poll_timeout, 15 + io::{ 16 + poll::read_poll_timeout, 17 + Io, // 18 + }, 16 19 prelude::*, 17 20 time::{ 18 21 delay::fsleep,
+54 -36
drivers/gpu/nova-core/regs/macros.rs
··· 369 369 370 370 /// Read the register from its address in `io`. 371 371 #[inline(always)] 372 - pub(crate) fn read<const SIZE: usize, T>(io: &T) -> Self where 373 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 372 + pub(crate) fn read<T, I>(io: &T) -> Self where 373 + T: ::core::ops::Deref<Target = I>, 374 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 374 375 { 375 376 Self(io.read32($offset)) 376 377 } 377 378 378 379 /// Write the value contained in `self` to the register address in `io`. 379 380 #[inline(always)] 380 - pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where 381 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 381 + pub(crate) fn write<T, I>(self, io: &T) where 382 + T: ::core::ops::Deref<Target = I>, 383 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 382 384 { 383 385 io.write32(self.0, $offset) 384 386 } ··· 388 386 /// Read the register from its address in `io` and run `f` on its value to obtain a new 389 387 /// value to write back. 390 388 #[inline(always)] 391 - pub(crate) fn update<const SIZE: usize, T, F>( 389 + pub(crate) fn update<T, I, F>( 392 390 io: &T, 393 391 f: F, 394 392 ) where 395 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 393 + T: ::core::ops::Deref<Target = I>, 394 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 396 395 F: ::core::ops::FnOnce(Self) -> Self, 397 396 { 398 397 let reg = f(Self::read(io)); ··· 411 408 /// Read the register from `io`, using the base address provided by `base` and adding 412 409 /// the register's offset to it. 413 410 #[inline(always)] 414 - pub(crate) fn read<const SIZE: usize, T, B>( 411 + pub(crate) fn read<T, I, B>( 415 412 io: &T, 416 413 #[allow(unused_variables)] 417 414 base: &B, 418 415 ) -> Self where 419 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 416 + T: ::core::ops::Deref<Target = I>, 417 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 420 418 B: crate::regs::macros::RegisterBase<$base>, 421 419 { 422 420 const OFFSET: usize = $name::OFFSET; ··· 432 428 /// Write the value contained in `self` to `io`, using the base address provided by 433 429 /// `base` and adding the register's offset to it. 434 430 #[inline(always)] 435 - pub(crate) fn write<const SIZE: usize, T, B>( 431 + pub(crate) fn write<T, I, B>( 436 432 self, 437 433 io: &T, 438 434 #[allow(unused_variables)] 439 435 base: &B, 440 436 ) where 441 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 437 + T: ::core::ops::Deref<Target = I>, 438 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 442 439 B: crate::regs::macros::RegisterBase<$base>, 443 440 { 444 441 const OFFSET: usize = $name::OFFSET; ··· 454 449 /// the register's offset to it, then run `f` on its value to obtain a new value to 455 450 /// write back. 456 451 #[inline(always)] 457 - pub(crate) fn update<const SIZE: usize, T, B, F>( 452 + pub(crate) fn update<T, I, B, F>( 458 453 io: &T, 459 454 base: &B, 460 455 f: F, 461 456 ) where 462 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 457 + T: ::core::ops::Deref<Target = I>, 458 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 463 459 B: crate::regs::macros::RegisterBase<$base>, 464 460 F: ::core::ops::FnOnce(Self) -> Self, 465 461 { ··· 480 474 481 475 /// Read the array register at index `idx` from its address in `io`. 482 476 #[inline(always)] 483 - pub(crate) fn read<const SIZE: usize, T>( 477 + pub(crate) fn read<T, I>( 484 478 io: &T, 485 479 idx: usize, 486 480 ) -> Self where 487 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 481 + T: ::core::ops::Deref<Target = I>, 482 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 488 483 { 489 484 build_assert!(idx < Self::SIZE); 490 485 ··· 497 490 498 491 /// Write the value contained in `self` to the array register with index `idx` in `io`. 499 492 #[inline(always)] 500 - pub(crate) fn write<const SIZE: usize, T>( 493 + pub(crate) fn write<T, I>( 501 494 self, 502 495 io: &T, 503 496 idx: usize 504 497 ) where 505 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 498 + T: ::core::ops::Deref<Target = I>, 499 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 506 500 { 507 501 build_assert!(idx < Self::SIZE); 508 502 ··· 515 507 /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a 516 508 /// new value to write back. 517 509 #[inline(always)] 518 - pub(crate) fn update<const SIZE: usize, T, F>( 510 + pub(crate) fn update<T, I, F>( 519 511 io: &T, 520 512 idx: usize, 521 513 f: F, 522 514 ) where 523 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 515 + T: ::core::ops::Deref<Target = I>, 516 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 524 517 F: ::core::ops::FnOnce(Self) -> Self, 525 518 { 526 519 let reg = f(Self::read(io, idx)); ··· 533 524 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 534 525 /// access was out-of-bounds. 535 526 #[inline(always)] 536 - pub(crate) fn try_read<const SIZE: usize, T>( 527 + pub(crate) fn try_read<T, I>( 537 528 io: &T, 538 529 idx: usize, 539 530 ) -> ::kernel::error::Result<Self> where 540 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 531 + T: ::core::ops::Deref<Target = I>, 532 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 541 533 { 542 534 if idx < Self::SIZE { 543 535 Ok(Self::read(io, idx)) ··· 552 542 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 553 543 /// access was out-of-bounds. 554 544 #[inline(always)] 555 - pub(crate) fn try_write<const SIZE: usize, T>( 545 + pub(crate) fn try_write<T, I>( 556 546 self, 557 547 io: &T, 558 548 idx: usize, 559 549 ) -> ::kernel::error::Result where 560 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 550 + T: ::core::ops::Deref<Target = I>, 551 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 561 552 { 562 553 if idx < Self::SIZE { 563 554 Ok(self.write(io, idx)) ··· 573 562 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 574 563 /// access was out-of-bounds. 575 564 #[inline(always)] 576 - pub(crate) fn try_update<const SIZE: usize, T, F>( 565 + pub(crate) fn try_update<T, I, F>( 577 566 io: &T, 578 567 idx: usize, 579 568 f: F, 580 569 ) -> ::kernel::error::Result where 581 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 570 + T: ::core::ops::Deref<Target = I>, 571 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 582 572 F: ::core::ops::FnOnce(Self) -> Self, 583 573 { 584 574 if idx < Self::SIZE { ··· 605 593 /// Read the array register at index `idx` from `io`, using the base address provided 606 594 /// by `base` and adding the register's offset to it. 607 595 #[inline(always)] 608 - pub(crate) fn read<const SIZE: usize, T, B>( 596 + pub(crate) fn read<T, I, B>( 609 597 io: &T, 610 598 #[allow(unused_variables)] 611 599 base: &B, 612 600 idx: usize, 613 601 ) -> Self where 614 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 602 + T: ::core::ops::Deref<Target = I>, 603 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 615 604 B: crate::regs::macros::RegisterBase<$base>, 616 605 { 617 606 build_assert!(idx < Self::SIZE); ··· 627 614 /// Write the value contained in `self` to `io`, using the base address provided by 628 615 /// `base` and adding the offset of array register `idx` to it. 629 616 #[inline(always)] 630 - pub(crate) fn write<const SIZE: usize, T, B>( 617 + pub(crate) fn write<T, I, B>( 631 618 self, 632 619 io: &T, 633 620 #[allow(unused_variables)] 634 621 base: &B, 635 622 idx: usize 636 623 ) where 637 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 624 + T: ::core::ops::Deref<Target = I>, 625 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 638 626 B: crate::regs::macros::RegisterBase<$base>, 639 627 { 640 628 build_assert!(idx < Self::SIZE); ··· 650 636 /// by `base` and adding the register's offset to it, then run `f` on its value to 651 637 /// obtain a new value to write back. 652 638 #[inline(always)] 653 - pub(crate) fn update<const SIZE: usize, T, B, F>( 639 + pub(crate) fn update<T, I, B, F>( 654 640 io: &T, 655 641 base: &B, 656 642 idx: usize, 657 643 f: F, 658 644 ) where 659 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 645 + T: ::core::ops::Deref<Target = I>, 646 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 660 647 B: crate::regs::macros::RegisterBase<$base>, 661 648 F: ::core::ops::FnOnce(Self) -> Self, 662 649 { ··· 671 656 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 672 657 /// access was out-of-bounds. 673 658 #[inline(always)] 674 - pub(crate) fn try_read<const SIZE: usize, T, B>( 659 + pub(crate) fn try_read<T, I, B>( 675 660 io: &T, 676 661 base: &B, 677 662 idx: usize, 678 663 ) -> ::kernel::error::Result<Self> where 679 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 664 + T: ::core::ops::Deref<Target = I>, 665 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 680 666 B: crate::regs::macros::RegisterBase<$base>, 681 667 { 682 668 if idx < Self::SIZE { ··· 693 677 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 694 678 /// access was out-of-bounds. 695 679 #[inline(always)] 696 - pub(crate) fn try_write<const SIZE: usize, T, B>( 680 + pub(crate) fn try_write<T, I, B>( 697 681 self, 698 682 io: &T, 699 683 base: &B, 700 684 idx: usize, 701 685 ) -> ::kernel::error::Result where 702 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 686 + T: ::core::ops::Deref<Target = I>, 687 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 703 688 B: crate::regs::macros::RegisterBase<$base>, 704 689 { 705 690 if idx < Self::SIZE { ··· 717 700 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 718 701 /// access was out-of-bounds. 719 702 #[inline(always)] 720 - pub(crate) fn try_update<const SIZE: usize, T, B, F>( 703 + pub(crate) fn try_update<T, I, B, F>( 721 704 io: &T, 722 705 base: &B, 723 706 idx: usize, 724 707 f: F, 725 708 ) -> ::kernel::error::Result where 726 - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 709 + T: ::core::ops::Deref<Target = I>, 710 + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 727 711 B: crate::regs::macros::RegisterBase<$base>, 728 712 F: ::core::ops::FnOnce(Self) -> Self, 729 713 {
+1
drivers/gpu/nova-core/vbios.rs
··· 6 6 7 7 use kernel::{ 8 8 device, 9 + io::Io, 9 10 prelude::*, 10 11 ptr::{ 11 12 Alignable,
+4 -1
drivers/pwm/pwm_th1520.rs
··· 26 26 clk::Clk, 27 27 device::{Bound, Core, Device}, 28 28 devres, 29 - io::mem::IoMem, 29 + io::{ 30 + mem::IoMem, 31 + Io, // 32 + }, 30 33 of, platform, 31 34 prelude::*, 32 35 pwm, time,
+13 -6
rust/kernel/devres.rs
··· 74 74 /// devres::Devres, 75 75 /// io::{ 76 76 /// Io, 77 - /// IoRaw, 78 - /// PhysAddr, 77 + /// IoKnownSize, 78 + /// Mmio, 79 + /// MmioRaw, 80 + /// PhysAddr, // 79 81 /// }, 82 + /// prelude::*, 80 83 /// }; 81 84 /// use core::ops::Deref; 82 85 /// 83 86 /// // See also [`pci::Bar`] for a real example. 84 - /// struct IoMem<const SIZE: usize>(IoRaw<SIZE>); 87 + /// struct IoMem<const SIZE: usize>(MmioRaw<SIZE>); 85 88 /// 86 89 /// impl<const SIZE: usize> IoMem<SIZE> { 87 90 /// /// # Safety ··· 99 96 /// return Err(ENOMEM); 100 97 /// } 101 98 /// 102 - /// Ok(IoMem(IoRaw::new(addr as usize, SIZE)?)) 99 + /// Ok(IoMem(MmioRaw::new(addr as usize, SIZE)?)) 103 100 /// } 104 101 /// } 105 102 /// ··· 111 108 /// } 112 109 /// 113 110 /// impl<const SIZE: usize> Deref for IoMem<SIZE> { 114 - /// type Target = Io<SIZE>; 111 + /// type Target = Mmio<SIZE>; 115 112 /// 116 113 /// fn deref(&self) -> &Self::Target { 117 114 /// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`. 118 - /// unsafe { Io::from_raw(&self.0) } 115 + /// unsafe { Mmio::from_raw(&self.0) } 119 116 /// } 120 117 /// } 121 118 /// # fn no_run(dev: &Device<Bound>) -> Result<(), Error> { ··· 261 258 /// use kernel::{ 262 259 /// device::Core, 263 260 /// devres::Devres, 261 + /// io::{ 262 + /// Io, 263 + /// IoKnownSize, // 264 + /// }, 264 265 /// pci, // 265 266 /// }; 266 267 ///
+335 -73
rust/kernel/io.rs
··· 32 32 /// By itself, the existence of an instance of this structure does not provide any guarantees that 33 33 /// the represented MMIO region does exist or is properly mapped. 34 34 /// 35 - /// Instead, the bus specific MMIO implementation must convert this raw representation into an `Io` 36 - /// instance providing the actual memory accessors. Only by the conversion into an `Io` structure 37 - /// any guarantees are given. 38 - pub struct IoRaw<const SIZE: usize = 0> { 35 + /// Instead, the bus specific MMIO implementation must convert this raw representation into an 36 + /// `Mmio` instance providing the actual memory accessors. Only by the conversion into an `Mmio` 37 + /// structure any guarantees are given. 38 + pub struct MmioRaw<const SIZE: usize = 0> { 39 39 addr: usize, 40 40 maxsize: usize, 41 41 } 42 42 43 - impl<const SIZE: usize> IoRaw<SIZE> { 44 - /// Returns a new `IoRaw` instance on success, an error otherwise. 43 + impl<const SIZE: usize> MmioRaw<SIZE> { 44 + /// Returns a new `MmioRaw` instance on success, an error otherwise. 45 45 pub fn new(addr: usize, maxsize: usize) -> Result<Self> { 46 46 if maxsize < SIZE { 47 47 return Err(EINVAL); ··· 81 81 /// ffi::c_void, 82 82 /// io::{ 83 83 /// Io, 84 - /// IoRaw, 84 + /// IoKnownSize, 85 + /// Mmio, 86 + /// MmioRaw, 85 87 /// PhysAddr, 86 88 /// }, 87 89 /// }; 88 90 /// use core::ops::Deref; 89 91 /// 90 92 /// // See also `pci::Bar` for a real example. 91 - /// struct IoMem<const SIZE: usize>(IoRaw<SIZE>); 93 + /// struct IoMem<const SIZE: usize>(MmioRaw<SIZE>); 92 94 /// 93 95 /// impl<const SIZE: usize> IoMem<SIZE> { 94 96 /// /// # Safety ··· 105 103 /// return Err(ENOMEM); 106 104 /// } 107 105 /// 108 - /// Ok(IoMem(IoRaw::new(addr as usize, SIZE)?)) 106 + /// Ok(IoMem(MmioRaw::new(addr as usize, SIZE)?)) 109 107 /// } 110 108 /// } 111 109 /// ··· 117 115 /// } 118 116 /// 119 117 /// impl<const SIZE: usize> Deref for IoMem<SIZE> { 120 - /// type Target = Io<SIZE>; 118 + /// type Target = Mmio<SIZE>; 121 119 /// 122 120 /// fn deref(&self) -> &Self::Target { 123 121 /// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`. 124 - /// unsafe { Io::from_raw(&self.0) } 122 + /// unsafe { Mmio::from_raw(&self.0) } 125 123 /// } 126 124 /// } 127 125 /// ··· 135 133 /// # } 136 134 /// ``` 137 135 #[repr(transparent)] 138 - pub struct Io<const SIZE: usize = 0>(IoRaw<SIZE>); 136 + pub struct Mmio<const SIZE: usize = 0>(MmioRaw<SIZE>); 139 137 140 138 macro_rules! define_read { 141 - ($(#[$attr:meta])* $name:ident, $try_name:ident, $c_fn:ident -> $type_name:ty) => { 139 + (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $c_fn:ident -> $type_name:ty) => { 142 140 /// Read IO data from a given offset known at compile time. 143 141 /// 144 142 /// Bound checks are performed on compile time, hence if the offset is not known at compile 145 143 /// time, the build will fail. 146 144 $(#[$attr])* 147 145 #[inline] 148 - pub fn $name(&self, offset: usize) -> $type_name { 146 + $vis fn $name(&self, offset: usize) -> $type_name { 149 147 let addr = self.io_addr_assert::<$type_name>(offset); 150 148 151 149 // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. 152 150 unsafe { bindings::$c_fn(addr as *const c_void) } 153 151 } 152 + }; 154 153 154 + (fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $c_fn:ident -> $type_name:ty) => { 155 155 /// Read IO data from a given offset. 156 156 /// 157 157 /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is 158 158 /// out of bounds. 159 159 $(#[$attr])* 160 - pub fn $try_name(&self, offset: usize) -> Result<$type_name> { 160 + $vis fn $try_name(&self, offset: usize) -> Result<$type_name> { 161 161 let addr = self.io_addr::<$type_name>(offset)?; 162 162 163 163 // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. ··· 167 163 } 168 164 }; 169 165 } 166 + #[expect(unused)] 167 + pub(crate) use define_read; 170 168 171 169 macro_rules! define_write { 172 - ($(#[$attr:meta])* $name:ident, $try_name:ident, $c_fn:ident <- $type_name:ty) => { 170 + (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $c_fn:ident <- $type_name:ty) => { 173 171 /// Write IO data from a given offset known at compile time. 174 172 /// 175 173 /// Bound checks are performed on compile time, hence if the offset is not known at compile 176 174 /// time, the build will fail. 177 175 $(#[$attr])* 178 176 #[inline] 179 - pub fn $name(&self, value: $type_name, offset: usize) { 177 + $vis fn $name(&self, value: $type_name, offset: usize) { 180 178 let addr = self.io_addr_assert::<$type_name>(offset); 181 179 182 180 // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. 183 181 unsafe { bindings::$c_fn(value, addr as *mut c_void) } 184 182 } 183 + }; 185 184 185 + (fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $c_fn:ident <- $type_name:ty) => { 186 186 /// Write IO data from a given offset. 187 187 /// 188 188 /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is 189 189 /// out of bounds. 190 190 $(#[$attr])* 191 - pub fn $try_name(&self, value: $type_name, offset: usize) -> Result { 191 + $vis fn $try_name(&self, value: $type_name, offset: usize) -> Result { 192 192 let addr = self.io_addr::<$type_name>(offset)?; 193 193 194 194 // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. 195 - unsafe { bindings::$c_fn(value, addr as *mut c_void) } 195 + unsafe { bindings::$c_fn(value, addr as *mut c_void) }; 196 196 Ok(()) 197 197 } 198 198 }; 199 199 } 200 + #[expect(unused)] 201 + pub(crate) use define_write; 200 202 201 - impl<const SIZE: usize> Io<SIZE> { 202 - /// Converts an `IoRaw` into an `Io` instance, providing the accessors to the MMIO mapping. 203 - /// 204 - /// # Safety 205 - /// 206 - /// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size 207 - /// `maxsize`. 208 - pub unsafe fn from_raw(raw: &IoRaw<SIZE>) -> &Self { 209 - // SAFETY: `Io` is a transparent wrapper around `IoRaw`. 210 - unsafe { &*core::ptr::from_ref(raw).cast() } 203 + /// Checks whether an access of type `U` at the given `offset` 204 + /// is valid within this region. 205 + #[inline] 206 + const fn offset_valid<U>(offset: usize, size: usize) -> bool { 207 + let type_size = core::mem::size_of::<U>(); 208 + if let Some(end) = offset.checked_add(type_size) { 209 + end <= size && offset % type_size == 0 210 + } else { 211 + false 211 212 } 213 + } 214 + 215 + /// Marker trait indicating that an I/O backend supports operations of a certain type. 216 + /// 217 + /// Different I/O backends can implement this trait to expose only the operations they support. 218 + /// 219 + /// For example, a PCI configuration space may implement `IoCapable<u8>`, `IoCapable<u16>`, 220 + /// and `IoCapable<u32>`, but not `IoCapable<u64>`, while an MMIO region on a 64-bit 221 + /// system might implement all four. 222 + pub trait IoCapable<T> {} 223 + 224 + /// Types implementing this trait (e.g. MMIO BARs or PCI config regions) 225 + /// can perform I/O operations on regions of memory. 226 + /// 227 + /// This is an abstract representation to be implemented by arbitrary I/O 228 + /// backends (e.g. MMIO, PCI config space, etc.). 229 + /// 230 + /// The [`Io`] trait provides: 231 + /// - Base address and size information 232 + /// - Helper methods for offset validation and address calculation 233 + /// - Fallible (runtime checked) accessors for different data widths 234 + /// 235 + /// Which I/O methods are available depends on which [`IoCapable<T>`] traits 236 + /// are implemented for the type. 237 + /// 238 + /// # Examples 239 + /// 240 + /// For MMIO regions, all widths (u8, u16, u32, and u64 on 64-bit systems) are typically 241 + /// supported. For PCI configuration space, u8, u16, and u32 are supported but u64 is not. 242 + pub trait Io { 243 + /// Minimum usable size of this region. 244 + const MIN_SIZE: usize; 212 245 213 246 /// Returns the base address of this mapping. 214 - #[inline] 215 - pub fn addr(&self) -> usize { 216 - self.0.addr() 217 - } 247 + fn addr(&self) -> usize; 218 248 219 249 /// Returns the maximum size of this mapping. 220 - #[inline] 221 - pub fn maxsize(&self) -> usize { 222 - self.0.maxsize() 223 - } 250 + fn maxsize(&self) -> usize; 224 251 225 - #[inline] 226 - const fn offset_valid<U>(offset: usize, size: usize) -> bool { 227 - let type_size = core::mem::size_of::<U>(); 228 - if let Some(end) = offset.checked_add(type_size) { 229 - end <= size && offset % type_size == 0 230 - } else { 231 - false 232 - } 233 - } 234 - 252 + /// Returns the absolute I/O address for a given `offset`, 253 + /// performing runtime bound checks. 235 254 #[inline] 236 255 fn io_addr<U>(&self, offset: usize) -> Result<usize> { 237 - if !Self::offset_valid::<U>(offset, self.maxsize()) { 256 + if !offset_valid::<U>(offset, self.maxsize()) { 238 257 return Err(EINVAL); 239 258 } 240 259 ··· 266 239 self.addr().checked_add(offset).ok_or(EINVAL) 267 240 } 268 241 242 + /// Returns the absolute I/O address for a given `offset`, 243 + /// performing compile-time bound checks. 269 244 #[inline] 270 245 fn io_addr_assert<U>(&self, offset: usize) -> usize { 271 - build_assert!(Self::offset_valid::<U>(offset, SIZE)); 246 + build_assert!(offset_valid::<U>(offset, Self::MIN_SIZE)); 272 247 273 248 self.addr() + offset 274 249 } 275 250 276 - define_read!(read8, try_read8, readb -> u8); 277 - define_read!(read16, try_read16, readw -> u16); 278 - define_read!(read32, try_read32, readl -> u32); 251 + /// Fallible 8-bit read with runtime bounds check. 252 + #[inline(always)] 253 + fn try_read8(&self, _offset: usize) -> Result<u8> 254 + where 255 + Self: IoCapable<u8>, 256 + { 257 + build_error!("Backend does not support fallible 8-bit read") 258 + } 259 + 260 + /// Fallible 16-bit read with runtime bounds check. 261 + #[inline(always)] 262 + fn try_read16(&self, _offset: usize) -> Result<u16> 263 + where 264 + Self: IoCapable<u16>, 265 + { 266 + build_error!("Backend does not support fallible 16-bit read") 267 + } 268 + 269 + /// Fallible 32-bit read with runtime bounds check. 270 + #[inline(always)] 271 + fn try_read32(&self, _offset: usize) -> Result<u32> 272 + where 273 + Self: IoCapable<u32>, 274 + { 275 + build_error!("Backend does not support fallible 32-bit read") 276 + } 277 + 278 + /// Fallible 64-bit read with runtime bounds check. 279 + #[inline(always)] 280 + fn try_read64(&self, _offset: usize) -> Result<u64> 281 + where 282 + Self: IoCapable<u64>, 283 + { 284 + build_error!("Backend does not support fallible 64-bit read") 285 + } 286 + 287 + /// Fallible 8-bit write with runtime bounds check. 288 + #[inline(always)] 289 + fn try_write8(&self, _value: u8, _offset: usize) -> Result 290 + where 291 + Self: IoCapable<u8>, 292 + { 293 + build_error!("Backend does not support fallible 8-bit write") 294 + } 295 + 296 + /// Fallible 16-bit write with runtime bounds check. 297 + #[inline(always)] 298 + fn try_write16(&self, _value: u16, _offset: usize) -> Result 299 + where 300 + Self: IoCapable<u16>, 301 + { 302 + build_error!("Backend does not support fallible 16-bit write") 303 + } 304 + 305 + /// Fallible 32-bit write with runtime bounds check. 306 + #[inline(always)] 307 + fn try_write32(&self, _value: u32, _offset: usize) -> Result 308 + where 309 + Self: IoCapable<u32>, 310 + { 311 + build_error!("Backend does not support fallible 32-bit write") 312 + } 313 + 314 + /// Fallible 64-bit write with runtime bounds check. 315 + #[inline(always)] 316 + fn try_write64(&self, _value: u64, _offset: usize) -> Result 317 + where 318 + Self: IoCapable<u64>, 319 + { 320 + build_error!("Backend does not support fallible 64-bit write") 321 + } 322 + 323 + /// Infallible 8-bit read with compile-time bounds check. 324 + #[inline(always)] 325 + fn read8(&self, _offset: usize) -> u8 326 + where 327 + Self: IoKnownSize + IoCapable<u8>, 328 + { 329 + build_error!("Backend does not support infallible 8-bit read") 330 + } 331 + 332 + /// Infallible 16-bit read with compile-time bounds check. 333 + #[inline(always)] 334 + fn read16(&self, _offset: usize) -> u16 335 + where 336 + Self: IoKnownSize + IoCapable<u16>, 337 + { 338 + build_error!("Backend does not support infallible 16-bit read") 339 + } 340 + 341 + /// Infallible 32-bit read with compile-time bounds check. 342 + #[inline(always)] 343 + fn read32(&self, _offset: usize) -> u32 344 + where 345 + Self: IoKnownSize + IoCapable<u32>, 346 + { 347 + build_error!("Backend does not support infallible 32-bit read") 348 + } 349 + 350 + /// Infallible 64-bit read with compile-time bounds check. 351 + #[inline(always)] 352 + fn read64(&self, _offset: usize) -> u64 353 + where 354 + Self: IoKnownSize + IoCapable<u64>, 355 + { 356 + build_error!("Backend does not support infallible 64-bit read") 357 + } 358 + 359 + /// Infallible 8-bit write with compile-time bounds check. 360 + #[inline(always)] 361 + fn write8(&self, _value: u8, _offset: usize) 362 + where 363 + Self: IoKnownSize + IoCapable<u8>, 364 + { 365 + build_error!("Backend does not support infallible 8-bit write") 366 + } 367 + 368 + /// Infallible 16-bit write with compile-time bounds check. 369 + #[inline(always)] 370 + fn write16(&self, _value: u16, _offset: usize) 371 + where 372 + Self: IoKnownSize + IoCapable<u16>, 373 + { 374 + build_error!("Backend does not support infallible 16-bit write") 375 + } 376 + 377 + /// Infallible 32-bit write with compile-time bounds check. 378 + #[inline(always)] 379 + fn write32(&self, _value: u32, _offset: usize) 380 + where 381 + Self: IoKnownSize + IoCapable<u32>, 382 + { 383 + build_error!("Backend does not support infallible 32-bit write") 384 + } 385 + 386 + /// Infallible 64-bit write with compile-time bounds check. 387 + #[inline(always)] 388 + fn write64(&self, _value: u64, _offset: usize) 389 + where 390 + Self: IoKnownSize + IoCapable<u64>, 391 + { 392 + build_error!("Backend does not support infallible 64-bit write") 393 + } 394 + } 395 + 396 + /// Marker trait for types with a known size at compile time. 397 + /// 398 + /// This trait is implemented by I/O backends that have a compile-time known size, 399 + /// enabling the use of infallible I/O accessors with compile-time bounds checking. 400 + /// 401 + /// Types implementing this trait can use the infallible methods in [`Io`] trait 402 + /// (e.g., `read8`, `write32`), which require `Self: IoKnownSize` bound. 403 + pub trait IoKnownSize: Io {} 404 + 405 + // MMIO regions support 8, 16, and 32-bit accesses. 406 + impl<const SIZE: usize> IoCapable<u8> for Mmio<SIZE> {} 407 + impl<const SIZE: usize> IoCapable<u16> for Mmio<SIZE> {} 408 + impl<const SIZE: usize> IoCapable<u32> for Mmio<SIZE> {} 409 + 410 + // MMIO regions on 64-bit systems also support 64-bit accesses. 411 + #[cfg(CONFIG_64BIT)] 412 + impl<const SIZE: usize> IoCapable<u64> for Mmio<SIZE> {} 413 + 414 + impl<const SIZE: usize> Io for Mmio<SIZE> { 415 + const MIN_SIZE: usize = SIZE; 416 + 417 + /// Returns the base address of this mapping. 418 + #[inline] 419 + fn addr(&self) -> usize { 420 + self.0.addr() 421 + } 422 + 423 + /// Returns the maximum size of this mapping. 424 + #[inline] 425 + fn maxsize(&self) -> usize { 426 + self.0.maxsize() 427 + } 428 + 429 + define_read!(fallible, try_read8, readb -> u8); 430 + define_read!(fallible, try_read16, readw -> u16); 431 + define_read!(fallible, try_read32, readl -> u32); 279 432 define_read!( 433 + fallible, 280 434 #[cfg(CONFIG_64BIT)] 281 - read64, 282 435 try_read64, 283 436 readq -> u64 284 437 ); 285 438 286 - define_read!(read8_relaxed, try_read8_relaxed, readb_relaxed -> u8); 287 - define_read!(read16_relaxed, try_read16_relaxed, readw_relaxed -> u16); 288 - define_read!(read32_relaxed, try_read32_relaxed, readl_relaxed -> u32); 289 - define_read!( 290 - #[cfg(CONFIG_64BIT)] 291 - read64_relaxed, 292 - try_read64_relaxed, 293 - readq_relaxed -> u64 294 - ); 295 - 296 - define_write!(write8, try_write8, writeb <- u8); 297 - define_write!(write16, try_write16, writew <- u16); 298 - define_write!(write32, try_write32, writel <- u32); 439 + define_write!(fallible, try_write8, writeb <- u8); 440 + define_write!(fallible, try_write16, writew <- u16); 441 + define_write!(fallible, try_write32, writel <- u32); 299 442 define_write!( 443 + fallible, 300 444 #[cfg(CONFIG_64BIT)] 301 - write64, 302 445 try_write64, 303 446 writeq <- u64 304 447 ); 305 448 306 - define_write!(write8_relaxed, try_write8_relaxed, writeb_relaxed <- u8); 307 - define_write!(write16_relaxed, try_write16_relaxed, writew_relaxed <- u16); 308 - define_write!(write32_relaxed, try_write32_relaxed, writel_relaxed <- u32); 309 - define_write!( 449 + define_read!(infallible, read8, readb -> u8); 450 + define_read!(infallible, read16, readw -> u16); 451 + define_read!(infallible, read32, readl -> u32); 452 + define_read!( 453 + infallible, 310 454 #[cfg(CONFIG_64BIT)] 311 - write64_relaxed, 312 - try_write64_relaxed, 455 + read64, 456 + readq -> u64 457 + ); 458 + 459 + define_write!(infallible, write8, writeb <- u8); 460 + define_write!(infallible, write16, writew <- u16); 461 + define_write!(infallible, write32, writel <- u32); 462 + define_write!( 463 + infallible, 464 + #[cfg(CONFIG_64BIT)] 465 + write64, 466 + writeq <- u64 467 + ); 468 + } 469 + 470 + impl<const SIZE: usize> IoKnownSize for Mmio<SIZE> {} 471 + 472 + impl<const SIZE: usize> Mmio<SIZE> { 473 + /// Converts an `MmioRaw` into an `Mmio` instance, providing the accessors to the MMIO mapping. 474 + /// 475 + /// # Safety 476 + /// 477 + /// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size 478 + /// `maxsize`. 479 + pub unsafe fn from_raw(raw: &MmioRaw<SIZE>) -> &Self { 480 + // SAFETY: `Mmio` is a transparent wrapper around `MmioRaw`. 481 + unsafe { &*core::ptr::from_ref(raw).cast() } 482 + } 483 + 484 + define_read!(infallible, pub read8_relaxed, readb_relaxed -> u8); 485 + define_read!(infallible, pub read16_relaxed, readw_relaxed -> u16); 486 + define_read!(infallible, pub read32_relaxed, readl_relaxed -> u32); 487 + define_read!( 488 + infallible, 489 + #[cfg(CONFIG_64BIT)] 490 + pub read64_relaxed, 491 + readq_relaxed -> u64 492 + ); 493 + 494 + define_read!(fallible, pub try_read8_relaxed, readb_relaxed -> u8); 495 + define_read!(fallible, pub try_read16_relaxed, readw_relaxed -> u16); 496 + define_read!(fallible, pub try_read32_relaxed, readl_relaxed -> u32); 497 + define_read!( 498 + fallible, 499 + #[cfg(CONFIG_64BIT)] 500 + pub try_read64_relaxed, 501 + readq_relaxed -> u64 502 + ); 503 + 504 + define_write!(infallible, pub write8_relaxed, writeb_relaxed <- u8); 505 + define_write!(infallible, pub write16_relaxed, writew_relaxed <- u16); 506 + define_write!(infallible, pub write32_relaxed, writel_relaxed <- u32); 507 + define_write!( 508 + infallible, 509 + #[cfg(CONFIG_64BIT)] 510 + pub write64_relaxed, 511 + writeq_relaxed <- u64 512 + ); 513 + 514 + define_write!(fallible, pub try_write8_relaxed, writeb_relaxed <- u8); 515 + define_write!(fallible, pub try_write16_relaxed, writew_relaxed <- u16); 516 + define_write!(fallible, pub try_write32_relaxed, writel_relaxed <- u32); 517 + define_write!( 518 + fallible, 519 + #[cfg(CONFIG_64BIT)] 520 + pub try_write64_relaxed, 313 521 writeq_relaxed <- u64 314 522 ); 315 523 }
+8 -8
rust/kernel/io/mem.rs
··· 16 16 Region, 17 17 Resource, // 18 18 }, 19 - Io, 20 - IoRaw, // 19 + Mmio, 20 + MmioRaw, // 21 21 }, 22 22 prelude::*, 23 23 }; ··· 212 212 } 213 213 214 214 impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> { 215 - type Target = Io<SIZE>; 215 + type Target = Mmio<SIZE>; 216 216 217 217 fn deref(&self) -> &Self::Target { 218 218 &self.iomem ··· 226 226 /// 227 227 /// # Invariants 228 228 /// 229 - /// [`IoMem`] always holds an [`IoRaw`] instance that holds a valid pointer to the 229 + /// [`IoMem`] always holds an [`MmioRaw`] instance that holds a valid pointer to the 230 230 /// start of the I/O memory mapped region. 231 231 pub struct IoMem<const SIZE: usize = 0> { 232 - io: IoRaw<SIZE>, 232 + io: MmioRaw<SIZE>, 233 233 } 234 234 235 235 impl<const SIZE: usize> IoMem<SIZE> { ··· 264 264 return Err(ENOMEM); 265 265 } 266 266 267 - let io = IoRaw::new(addr as usize, size)?; 267 + let io = MmioRaw::new(addr as usize, size)?; 268 268 let io = IoMem { io }; 269 269 270 270 Ok(io) ··· 287 287 } 288 288 289 289 impl<const SIZE: usize> Deref for IoMem<SIZE> { 290 - type Target = Io<SIZE>; 290 + type Target = Mmio<SIZE>; 291 291 292 292 fn deref(&self) -> &Self::Target { 293 293 // SAFETY: Safe as by the invariant of `IoMem`. 294 - unsafe { Io::from_raw(&self.io) } 294 + unsafe { Mmio::from_raw(&self.io) } 295 295 } 296 296 }
+12 -4
rust/kernel/io/poll.rs
··· 45 45 /// # Examples 46 46 /// 47 47 /// ```no_run 48 - /// use kernel::io::{Io, poll::read_poll_timeout}; 48 + /// use kernel::io::{ 49 + /// Io, 50 + /// Mmio, 51 + /// poll::read_poll_timeout, // 52 + /// }; 49 53 /// use kernel::time::Delta; 50 54 /// 51 55 /// const HW_READY: u16 = 0x01; 52 56 /// 53 - /// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result { 57 + /// fn wait_for_hardware<const SIZE: usize>(io: &Mmio<SIZE>) -> Result { 54 58 /// read_poll_timeout( 55 59 /// // The `op` closure reads the value of a specific status register. 56 60 /// || io.try_read16(0x1000), ··· 132 128 /// # Examples 133 129 /// 134 130 /// ```no_run 135 - /// use kernel::io::{poll::read_poll_timeout_atomic, Io}; 131 + /// use kernel::io::{ 132 + /// Io, 133 + /// Mmio, 134 + /// poll::read_poll_timeout_atomic, // 135 + /// }; 136 136 /// use kernel::time::Delta; 137 137 /// 138 138 /// const HW_READY: u16 = 0x01; 139 139 /// 140 - /// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result { 140 + /// fn wait_for_hardware<const SIZE: usize>(io: &Mmio<SIZE>) -> Result { 141 141 /// read_poll_timeout_atomic( 142 142 /// // The `op` closure reads the value of a specific status register. 143 143 /// || io.try_read16(0x1000),
+6 -6
rust/kernel/pci/io.rs
··· 8 8 device, 9 9 devres::Devres, 10 10 io::{ 11 - Io, 12 - IoRaw, // 11 + Mmio, 12 + MmioRaw, // 13 13 }, 14 14 prelude::*, 15 15 sync::aref::ARef, // ··· 27 27 /// memory mapped PCI BAR and its size. 28 28 pub struct Bar<const SIZE: usize = 0> { 29 29 pdev: ARef<Device>, 30 - io: IoRaw<SIZE>, 30 + io: MmioRaw<SIZE>, 31 31 num: i32, 32 32 } 33 33 ··· 63 63 return Err(ENOMEM); 64 64 } 65 65 66 - let io = match IoRaw::new(ioptr, len as usize) { 66 + let io = match MmioRaw::new(ioptr, len as usize) { 67 67 Ok(io) => io, 68 68 Err(err) => { 69 69 // SAFETY: ··· 117 117 } 118 118 119 119 impl<const SIZE: usize> Deref for Bar<SIZE> { 120 - type Target = Io<SIZE>; 120 + type Target = Mmio<SIZE>; 121 121 122 122 fn deref(&self) -> &Self::Target { 123 123 // SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped. 124 - unsafe { Io::from_raw(&self.io) } 124 + unsafe { Mmio::from_raw(&self.io) } 125 125 } 126 126 } 127 127
+1
samples/rust/rust_driver_pci.rs
··· 7 7 use kernel::{ 8 8 device::Core, 9 9 devres::Devres, 10 + io::Io, 10 11 pci, 11 12 prelude::*, 12 13 sync::aref::ARef, //