mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 08:15:07 -04:00
rust: io: restrict untyped IO access and register! to Region
Currently the `Io` trait exposes a bunch of untyped IO accesses, but if the
`Io` region itself is typed, then it might be weird to have
let io: Mmio<u32> = /* ... */;
io.read8(1);
while not unsound, it is surely strange. Thus, restrict the untyped methods
and also the register macro to `Region` type only.
Implement it by adding a generic type to `IoLoc` indicating allowed base
types. This also paves the way to add typed register blocks in the future;
for example, we could use this mechanism to block driver A's `register!()`
generated macro from being used on driver B's MMIO. The same mechanism
could be used for relative IO registers. These are future opportunities,
and for now restrict everything to require `IoLoc<Region<SIZE>, _>`.
Suggested-by: Alexandre Courbot <acourbot@nvidia.com>
Link: https://lore.kernel.org/rust-for-linux/DHLB3RO3OSF5.2R7F27U99BKLN@nvidia.com/
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Gary Guo <gary@garyguo.net>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Link: https://patch.msgid.link/20260706-io_projection-v6-3-72cd5d055d54@garyguo.net
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
This commit is contained in:
committed by
Danilo Krummrich
parent
65f6abf9fa
commit
6461c5776b
@@ -244,15 +244,16 @@ pub trait IoCapable<T> {
|
||||
/// (for primitive types like [`u32`]) and typed ones (like those generated by the [`register!`]
|
||||
/// macro).
|
||||
///
|
||||
/// An `IoLoc<T>` carries three pieces of information:
|
||||
/// An `IoLoc<Base, T>` carries the following pieces of information:
|
||||
///
|
||||
/// - The valid `Base` to operate on. For most registers, this should be [`Region`].
|
||||
/// - The offset to access (returned by [`IoLoc::offset`]),
|
||||
/// - The width of the access (determined by [`IoLoc::IoType`]),
|
||||
/// - The type `T` in which the raw data is returned or provided.
|
||||
///
|
||||
/// `T` and `IoLoc::IoType` may differ: for instance, a typed register has `T` = the register type
|
||||
/// with its bitfields, and `IoType` = its backing primitive (e.g. `u32`).
|
||||
pub trait IoLoc<T> {
|
||||
pub trait IoLoc<Base: ?Sized, T> {
|
||||
/// Size ([`u8`], [`u16`], etc) of the I/O performed on the returned [`offset`](IoLoc::offset).
|
||||
type IoType: Into<T> + From<T>;
|
||||
|
||||
@@ -260,12 +261,12 @@ pub trait IoLoc<T> {
|
||||
fn offset(self) -> usize;
|
||||
}
|
||||
|
||||
/// Implements [`IoLoc<$ty>`] for [`usize`], allowing [`usize`] to be used as a parameter of
|
||||
/// [`Io::read`] and [`Io::write`].
|
||||
/// Implements [`IoLoc<Region<SIZE>, $ty>`] for [`usize`], allowing [`usize`] to be used as a
|
||||
/// parameter of [`Io::read`] and [`Io::write`].
|
||||
macro_rules! impl_usize_ioloc {
|
||||
($($ty:ty),*) => {
|
||||
$(
|
||||
impl IoLoc<$ty> for usize {
|
||||
impl<const SIZE: usize> IoLoc<Region<SIZE>, $ty> for usize {
|
||||
type IoType = $ty;
|
||||
|
||||
#[inline(always)]
|
||||
@@ -339,6 +340,7 @@ fn io_addr<U>(&self, offset: usize) -> Result<usize> {
|
||||
#[inline(always)]
|
||||
fn try_read8(&self, offset: usize) -> Result<u8>
|
||||
where
|
||||
usize: IoLoc<Self::Target, u8, IoType = u8>,
|
||||
Self: IoCapable<u8>,
|
||||
{
|
||||
self.try_read(offset)
|
||||
@@ -348,6 +350,7 @@ fn try_read8(&self, offset: usize) -> Result<u8>
|
||||
#[inline(always)]
|
||||
fn try_read16(&self, offset: usize) -> Result<u16>
|
||||
where
|
||||
usize: IoLoc<Self::Target, u16, IoType = u16>,
|
||||
Self: IoCapable<u16>,
|
||||
{
|
||||
self.try_read(offset)
|
||||
@@ -357,6 +360,7 @@ fn try_read16(&self, offset: usize) -> Result<u16>
|
||||
#[inline(always)]
|
||||
fn try_read32(&self, offset: usize) -> Result<u32>
|
||||
where
|
||||
usize: IoLoc<Self::Target, u32, IoType = u32>,
|
||||
Self: IoCapable<u32>,
|
||||
{
|
||||
self.try_read(offset)
|
||||
@@ -366,6 +370,7 @@ fn try_read32(&self, offset: usize) -> Result<u32>
|
||||
#[inline(always)]
|
||||
fn try_read64(&self, offset: usize) -> Result<u64>
|
||||
where
|
||||
usize: IoLoc<Self::Target, u64, IoType = u64>,
|
||||
Self: IoCapable<u64>,
|
||||
{
|
||||
self.try_read(offset)
|
||||
@@ -375,6 +380,7 @@ fn try_read64(&self, offset: usize) -> Result<u64>
|
||||
#[inline(always)]
|
||||
fn try_write8(&self, value: u8, offset: usize) -> Result
|
||||
where
|
||||
usize: IoLoc<Self::Target, u8, IoType = u8>,
|
||||
Self: IoCapable<u8>,
|
||||
{
|
||||
self.try_write(offset, value)
|
||||
@@ -384,6 +390,7 @@ fn try_write8(&self, value: u8, offset: usize) -> Result
|
||||
#[inline(always)]
|
||||
fn try_write16(&self, value: u16, offset: usize) -> Result
|
||||
where
|
||||
usize: IoLoc<Self::Target, u16, IoType = u16>,
|
||||
Self: IoCapable<u16>,
|
||||
{
|
||||
self.try_write(offset, value)
|
||||
@@ -393,6 +400,7 @@ fn try_write16(&self, value: u16, offset: usize) -> Result
|
||||
#[inline(always)]
|
||||
fn try_write32(&self, value: u32, offset: usize) -> Result
|
||||
where
|
||||
usize: IoLoc<Self::Target, u32, IoType = u32>,
|
||||
Self: IoCapable<u32>,
|
||||
{
|
||||
self.try_write(offset, value)
|
||||
@@ -402,6 +410,7 @@ fn try_write32(&self, value: u32, offset: usize) -> Result
|
||||
#[inline(always)]
|
||||
fn try_write64(&self, value: u64, offset: usize) -> Result
|
||||
where
|
||||
usize: IoLoc<Self::Target, u64, IoType = u64>,
|
||||
Self: IoCapable<u64>,
|
||||
{
|
||||
self.try_write(offset, value)
|
||||
@@ -411,6 +420,7 @@ fn try_write64(&self, value: u64, offset: usize) -> Result
|
||||
#[inline(always)]
|
||||
fn read8(&self, offset: usize) -> u8
|
||||
where
|
||||
usize: IoLoc<Self::Target, u8, IoType = u8>,
|
||||
Self: IoCapable<u8>,
|
||||
{
|
||||
self.read(offset)
|
||||
@@ -420,6 +430,7 @@ fn read8(&self, offset: usize) -> u8
|
||||
#[inline(always)]
|
||||
fn read16(&self, offset: usize) -> u16
|
||||
where
|
||||
usize: IoLoc<Self::Target, u16, IoType = u16>,
|
||||
Self: IoCapable<u16>,
|
||||
{
|
||||
self.read(offset)
|
||||
@@ -429,6 +440,7 @@ fn read16(&self, offset: usize) -> u16
|
||||
#[inline(always)]
|
||||
fn read32(&self, offset: usize) -> u32
|
||||
where
|
||||
usize: IoLoc<Self::Target, u32, IoType = u32>,
|
||||
Self: IoCapable<u32>,
|
||||
{
|
||||
self.read(offset)
|
||||
@@ -438,6 +450,7 @@ fn read32(&self, offset: usize) -> u32
|
||||
#[inline(always)]
|
||||
fn read64(&self, offset: usize) -> u64
|
||||
where
|
||||
usize: IoLoc<Self::Target, u64, IoType = u64>,
|
||||
Self: IoCapable<u64>,
|
||||
{
|
||||
self.read(offset)
|
||||
@@ -447,6 +460,7 @@ fn read64(&self, offset: usize) -> u64
|
||||
#[inline(always)]
|
||||
fn write8(&self, value: u8, offset: usize)
|
||||
where
|
||||
usize: IoLoc<Self::Target, u8, IoType = u8>,
|
||||
Self: IoCapable<u8>,
|
||||
{
|
||||
self.write(offset, value)
|
||||
@@ -456,6 +470,7 @@ fn write8(&self, value: u8, offset: usize)
|
||||
#[inline(always)]
|
||||
fn write16(&self, value: u16, offset: usize)
|
||||
where
|
||||
usize: IoLoc<Self::Target, u16, IoType = u16>,
|
||||
Self: IoCapable<u16>,
|
||||
{
|
||||
self.write(offset, value)
|
||||
@@ -465,6 +480,7 @@ fn write16(&self, value: u16, offset: usize)
|
||||
#[inline(always)]
|
||||
fn write32(&self, value: u32, offset: usize)
|
||||
where
|
||||
usize: IoLoc<Self::Target, u32, IoType = u32>,
|
||||
Self: IoCapable<u32>,
|
||||
{
|
||||
self.write(offset, value)
|
||||
@@ -474,6 +490,7 @@ fn write32(&self, value: u32, offset: usize)
|
||||
#[inline(always)]
|
||||
fn write64(&self, value: u64, offset: usize)
|
||||
where
|
||||
usize: IoLoc<Self::Target, u64, IoType = u64>,
|
||||
Self: IoCapable<u64>,
|
||||
{
|
||||
self.write(offset, value)
|
||||
@@ -504,7 +521,7 @@ fn write64(&self, value: u64, offset: usize)
|
||||
#[inline(always)]
|
||||
fn try_read<T, L>(&self, location: L) -> Result<T>
|
||||
where
|
||||
L: IoLoc<T>,
|
||||
L: IoLoc<Self::Target, T>,
|
||||
Self: IoCapable<L::IoType>,
|
||||
{
|
||||
let address = self.io_addr::<L::IoType>(location.offset())?;
|
||||
@@ -538,7 +555,7 @@ fn try_read<T, L>(&self, location: L) -> Result<T>
|
||||
#[inline(always)]
|
||||
fn try_write<T, L>(&self, location: L, value: T) -> Result
|
||||
where
|
||||
L: IoLoc<T>,
|
||||
L: IoLoc<Self::Target, T>,
|
||||
Self: IoCapable<L::IoType>,
|
||||
{
|
||||
let address = self.io_addr::<L::IoType>(location.offset())?;
|
||||
@@ -584,8 +601,8 @@ fn try_write<T, L>(&self, location: L, value: T) -> Result
|
||||
#[inline(always)]
|
||||
fn try_write_reg<T, L, V>(&self, value: V) -> Result
|
||||
where
|
||||
L: IoLoc<T>,
|
||||
V: LocatedRegister<Location = L, Value = T>,
|
||||
L: IoLoc<Self::Target, T>,
|
||||
V: LocatedRegister<Self::Target, Location = L, Value = T>,
|
||||
Self: IoCapable<L::IoType>,
|
||||
{
|
||||
let (location, value) = value.into_io_op();
|
||||
@@ -617,7 +634,7 @@ fn try_write_reg<T, L, V>(&self, value: V) -> Result
|
||||
#[inline(always)]
|
||||
fn try_update<T, L, F>(&self, location: L, f: F) -> Result
|
||||
where
|
||||
L: IoLoc<T>,
|
||||
L: IoLoc<Self::Target, T>,
|
||||
Self: IoCapable<L::IoType>,
|
||||
F: FnOnce(T) -> T,
|
||||
{
|
||||
@@ -656,7 +673,7 @@ fn try_update<T, L, F>(&self, location: L, f: F) -> Result
|
||||
#[inline(always)]
|
||||
fn read<T, L>(&self, location: L) -> T
|
||||
where
|
||||
L: IoLoc<T>,
|
||||
L: IoLoc<Self::Target, T>,
|
||||
Self: IoCapable<L::IoType>,
|
||||
{
|
||||
let address = self.io_addr_assert::<L::IoType>(location.offset());
|
||||
@@ -688,7 +705,7 @@ fn read<T, L>(&self, location: L) -> T
|
||||
#[inline(always)]
|
||||
fn write<T, L>(&self, location: L, value: T)
|
||||
where
|
||||
L: IoLoc<T>,
|
||||
L: IoLoc<Self::Target, T>,
|
||||
Self: IoCapable<L::IoType>,
|
||||
{
|
||||
let address = self.io_addr_assert::<L::IoType>(location.offset());
|
||||
@@ -731,8 +748,8 @@ fn write<T, L>(&self, location: L, value: T)
|
||||
#[inline(always)]
|
||||
fn write_reg<T, L, V>(&self, value: V)
|
||||
where
|
||||
L: IoLoc<T>,
|
||||
V: LocatedRegister<Location = L, Value = T>,
|
||||
L: IoLoc<Self::Target, T>,
|
||||
V: LocatedRegister<Self::Target, Location = L, Value = T>,
|
||||
Self: IoCapable<L::IoType>,
|
||||
{
|
||||
let (location, value) = value.into_io_op();
|
||||
@@ -764,8 +781,8 @@ fn write_reg<T, L, V>(&self, value: V)
|
||||
#[inline(always)]
|
||||
fn update<T, L, F>(&self, location: L, f: F)
|
||||
where
|
||||
L: IoLoc<T>,
|
||||
Self: IoCapable<L::IoType> + Sized,
|
||||
L: IoLoc<Self::Target, T>,
|
||||
Self: IoCapable<L::IoType>,
|
||||
F: FnOnce(T) -> T,
|
||||
{
|
||||
let address = self.io_addr_assert::<L::IoType>(location.offset());
|
||||
|
||||
@@ -113,6 +113,8 @@
|
||||
io::IoLoc, //
|
||||
};
|
||||
|
||||
use super::Region;
|
||||
|
||||
/// Trait implemented by all registers.
|
||||
pub trait Register: Sized {
|
||||
/// Backing primitive type of the register.
|
||||
@@ -129,7 +131,7 @@ pub trait FixedRegister: Register {}
|
||||
|
||||
/// Allows `()` to be used as the `location` parameter of [`Io::write`](super::Io::write) when
|
||||
/// passing a [`FixedRegister`] value.
|
||||
impl<T> IoLoc<T> for ()
|
||||
impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for ()
|
||||
where
|
||||
T: FixedRegister,
|
||||
{
|
||||
@@ -143,7 +145,7 @@ fn offset(self) -> usize {
|
||||
|
||||
/// A [`FixedRegister`] carries its location in its type. Thus `FixedRegister` values can be used
|
||||
/// as an [`IoLoc`].
|
||||
impl<T> IoLoc<T> for T
|
||||
impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for T
|
||||
where
|
||||
T: FixedRegister,
|
||||
{
|
||||
@@ -168,7 +170,7 @@ pub const fn new() -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IoLoc<T> for FixedRegisterLoc<T>
|
||||
impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for FixedRegisterLoc<T>
|
||||
where
|
||||
T: FixedRegister,
|
||||
{
|
||||
@@ -239,7 +241,7 @@ const fn offset(self) -> usize {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B> IoLoc<T> for RelativeRegisterLoc<T, B>
|
||||
impl<const SIZE: usize, T, B> IoLoc<Region<SIZE>, T> for RelativeRegisterLoc<T, B>
|
||||
where
|
||||
T: RelativeRegister,
|
||||
B: RegisterBase<T::BaseFamily> + ?Sized,
|
||||
@@ -283,7 +285,7 @@ pub fn try_new(idx: usize) -> Option<Self> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IoLoc<T> for RegisterArrayLoc<T>
|
||||
impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for RegisterArrayLoc<T>
|
||||
where
|
||||
T: RegisterArray,
|
||||
{
|
||||
@@ -370,7 +372,7 @@ pub fn try_at(self, idx: usize) -> Option<RelativeRegisterArrayLoc<T, B>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B> IoLoc<T> for RelativeRegisterArrayLoc<T, B>
|
||||
impl<const SIZE: usize, T, B> IoLoc<Region<SIZE>, T> for RelativeRegisterArrayLoc<T, B>
|
||||
where
|
||||
T: RelativeRegisterArray,
|
||||
B: RegisterBase<T::BaseFamily> + ?Sized,
|
||||
@@ -387,18 +389,18 @@ fn offset(self) -> usize {
|
||||
/// which to write it.
|
||||
///
|
||||
/// Implementors can be used with [`Io::write_reg`](super::Io::write_reg).
|
||||
pub trait LocatedRegister {
|
||||
pub trait LocatedRegister<Base: ?Sized> {
|
||||
/// Register value to write.
|
||||
type Value: Register;
|
||||
/// Full location information at which to write the value.
|
||||
type Location: IoLoc<Self::Value>;
|
||||
type Location: IoLoc<Base, Self::Value>;
|
||||
|
||||
/// Consumes `self` and returns a `(location, value)` tuple describing a valid I/O write
|
||||
/// operation.
|
||||
fn into_io_op(self) -> (Self::Location, Self::Value);
|
||||
}
|
||||
|
||||
impl<T> LocatedRegister for T
|
||||
impl<const SIZE: usize, T> LocatedRegister<Region<SIZE>> for T
|
||||
where
|
||||
T: FixedRegister,
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user