Merge patch series "rust: I/O type generalization and projection"

Gary Guo <gary@garyguo.net> says:

This series presents a major rework of I/O types, as a summary:

- Make I/O regions typed. The existing untyped region still exists
  with a dynamically sized `Region` type.

- Create I/O view types to represent subregion of a full I/O region mapped.
  A projection macro is added to allow safely create such subviews.

- Split I/O traits, make I/O views play a central role, avoid
  duplicate monomorphization and less `unsafe` code.

- Add a `SysMem` backend, and make `Coherent` implement `Io`.

- Add copying methods (memcpy_{from,to}io and friends).

This series generalize `Mmio` type from just an untyped region to typed
representations (so `MmioRaw<T>` is `__iomem *T`). This allows us to remove
the `IoKnownSize` trait; the information is sourced from just the pointer
from the `KnownSize` trait instead.

Building on top of that, `Mmio` and `ConfigSpace` have been converted to
typed views of I/O regions rather than just a big chunk of untyped I/O
memory. These changes made it possible to implement `Io` trait for
`Coherent<T>`.

Shared system memory, `SysMem` is also added to the series, given it
similarity in implementation compared to `Coherent`. In fact, the series
use `SysMem` to implement `Coherent`'s I/O methods.

Built on these generalization, this series add `io_project!()`.
`io_project!()` performs a safe way to project a bigger view to a small
subviews, and some Nova code has been converted in this series to
demonstrate cleanups possible with this addition.

New `io_read!()`, `io_write!()` has been added that supersedes
`dma_read!()`, `dma_write!()` macro. Although, they work for primitives
only (to be exact, types that the backend is `IoCapable` of).
One feature that was lost from the old `dma_read!()` and `dma_write!()`
series was the ability to read/write a large structs. However, the
semantics was unclear to begin with, as there was no guarantee about their
atomicity even for structs that were small enough to fit in u32.

Suggested-by: Danilo Krummrich <dakr@kernel.org>
Link: https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/Generic.20I.2FO.20backends/near/571198078
Link: https://patch.msgid.link/20260706-io_projection-v6-0-72cd5d055d54@garyguo.net
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
This commit is contained in:
Danilo Krummrich
2026-07-13 23:50:39 +02:00
16 changed files with 1639 additions and 688 deletions
+25 -28
View File
@@ -9,14 +9,16 @@ use kernel::{
dma::{
Coherent,
CoherentBox,
CoherentView,
DmaAddress, //
},
io::{
io_project,
io_write,
Io, //
},
pci,
prelude::*,
transmute::{
AsBytes,
FromBytes, //
}, //
prelude::*, //
};
pub(crate) mod cmdq;
@@ -48,21 +50,21 @@ const LOG_BUFFER_SIZE: usize = RM_LOG_BUFFER_NUM_PAGES * GSP_PAGE_SIZE;
/// Array of page table entries, as understood by the GSP bootloader.
#[repr(C)]
#[derive(FromBytes, IntoBytes)]
struct PteArray<const NUM_ENTRIES: usize>([u64; NUM_ENTRIES]);
/// SAFETY: arrays of `u64` implement `FromBytes` and we are but a wrapper around one.
unsafe impl<const NUM_ENTRIES: usize> FromBytes for PteArray<NUM_ENTRIES> {}
/// SAFETY: arrays of `u64` implement `AsBytes` and we are but a wrapper around one.
unsafe impl<const NUM_ENTRIES: usize> AsBytes for PteArray<NUM_ENTRIES> {}
impl<const NUM_PAGES: usize> PteArray<NUM_PAGES> {
/// Returns the page table entry for `index`, for a mapping starting at `start`.
// TODO: Replace with `IoView` projection once available.
fn entry(start: DmaAddress, index: usize) -> Result<u64> {
start
.checked_add(num::usize_as_u64(index) << GSP_PAGE_SHIFT)
.ok_or(EOVERFLOW)
/// Initialize a new page table array mapping `NUM_PAGES` GSP pages starting at address `start`.
fn init(view: CoherentView<'_, Self>, start: DmaAddress) -> Result<()> {
for i in 0..NUM_PAGES {
io_write!(view, .0[build: i],
start
.checked_add(num::usize_as_u64(i) << GSP_PAGE_SHIFT)
.ok_or(EOVERFLOW)?
);
}
Ok(())
}
}
@@ -89,17 +91,12 @@ impl LogBuffer {
let start_addr = obj.0.dma_handle();
// SAFETY: `obj` has just been created and we are its sole user.
let pte_region = unsafe {
&mut obj.0.as_mut()[size_of::<u64>()..][..RM_LOG_BUFFER_NUM_PAGES * size_of::<u64>()]
};
// Write values one by one to avoid an on-stack instance of `PteArray`.
for (i, chunk) in pte_region.chunks_exact_mut(size_of::<u64>()).enumerate() {
let pte_value = PteArray::<0>::entry(start_addr, i)?;
chunk.copy_from_slice(&pte_value.to_ne_bytes());
}
let pte_view = io_project!(
obj.0,
[build: size_of::<u64>()..][build: ..RM_LOG_BUFFER_NUM_PAGES * size_of::<u64>()]
)
.try_cast::<PteArray<RM_LOG_BUFFER_NUM_PAGES>>()?;
PteArray::init(pte_view, start_addr)?;
Ok(obj)
}
+37 -29
View File
@@ -2,16 +2,23 @@
mod continuation;
use core::mem;
use core::{
mem,
sync::atomic::{
fence,
Ordering, //
},
};
use kernel::{
device,
dma::{
Coherent,
CoherentBox,
DmaAddress, //
},
dma_write,
io::{
io_project,
poll::read_poll_timeout,
Io, //
},
@@ -171,20 +178,18 @@ static_assert!(align_of::<MsgqData>() == GSP_PAGE_SIZE);
#[repr(C)]
// There is no struct defined for this in the open-gpu-kernel-source headers.
// Instead it is defined by code in `GspMsgQueuesInit()`.
// TODO: Revert to private once `IoView` projections replace the `gsp_mem` module.
pub(super) struct Msgq {
struct Msgq {
/// Header for sending messages, including the write pointer.
pub(super) tx: MsgqTxHeader,
tx: MsgqTxHeader,
/// Header for receiving messages, including the read pointer.
pub(super) rx: MsgqRxHeader,
rx: MsgqRxHeader,
/// The message queue proper.
msgq: MsgqData,
}
/// Structure shared between the driver and the GSP and containing the command and message queues.
#[repr(C)]
// TODO: Revert to private once `IoView` projections replace the `gsp_mem` module.
pub(super) struct GspMem {
struct GspMem {
/// Self-mapping page table entries.
ptes: PteArray<{ Self::PTE_ARRAY_SIZE }>,
/// CPU queue: the driver writes commands here, and the GSP reads them. It also contains the
@@ -192,13 +197,13 @@ pub(super) struct GspMem {
/// index into the GSP queue.
///
/// This member is read-only for the GSP.
pub(super) cpuq: Msgq,
cpuq: Msgq,
/// GSP queue: the GSP writes messages here, and the driver reads them. It also contains the
/// write and read pointers that the GSP updates. This means that the read pointer here is an
/// index into the CPU queue.
///
/// This member is read-only for the driver.
pub(super) gspq: Msgq,
gspq: Msgq,
}
impl GspMem {
@@ -232,20 +237,12 @@ impl DmaGspMem {
const MSGQ_SIZE: u32 = num::usize_into_u32::<{ size_of::<Msgq>() }>();
const RX_HDR_OFF: u32 = num::usize_into_u32::<{ mem::offset_of!(Msgq, rx) }>();
let gsp_mem = Coherent::<GspMem>::zeroed(dev, GFP_KERNEL)?;
let mut gsp_mem = CoherentBox::<GspMem>::zeroed(dev, GFP_KERNEL)?;
gsp_mem.cpuq.tx = MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES);
gsp_mem.cpuq.rx = MsgqRxHeader::new();
let start = gsp_mem.dma_handle();
// Write values one by one to avoid an on-stack instance of `PteArray`.
for i in 0..GspMem::PTE_ARRAY_SIZE {
dma_write!(gsp_mem, .ptes.0[build: i], PteArray::<0>::entry(start, i)?);
}
dma_write!(
gsp_mem,
.cpuq.tx,
MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES)
);
dma_write!(gsp_mem, .cpuq.rx, MsgqRxHeader::new());
let gsp_mem: Coherent<_> = gsp_mem.into();
PteArray::init(io_project!(gsp_mem, .ptes), gsp_mem.dma_handle())?;
Ok(Self(gsp_mem))
}
@@ -406,7 +403,7 @@ impl DmaGspMem {
//
// - The returned value is within `0..MSGQ_NUM_PAGES`.
fn gsp_write_ptr(&self) -> u32 {
super::fw::gsp_mem::gsp_write_ptr(&self.0)
MsgqTxHeader::write_ptr(io_project!(self.0, .gspq.tx)) % MSGQ_NUM_PAGES
}
// Returns the index of the memory page the GSP will read the next command from.
@@ -415,7 +412,7 @@ impl DmaGspMem {
//
// - The returned value is within `0..MSGQ_NUM_PAGES`.
fn gsp_read_ptr(&self) -> u32 {
super::fw::gsp_mem::gsp_read_ptr(&self.0)
MsgqRxHeader::read_ptr(io_project!(self.0, .gspq.rx)) % MSGQ_NUM_PAGES
}
// Returns the index of the memory page the CPU can read the next message from.
@@ -424,12 +421,18 @@ impl DmaGspMem {
//
// - The returned value is within `0..MSGQ_NUM_PAGES`.
fn cpu_read_ptr(&self) -> u32 {
super::fw::gsp_mem::cpu_read_ptr(&self.0)
MsgqRxHeader::read_ptr(io_project!(self.0, .cpuq.rx)) % MSGQ_NUM_PAGES
}
// Informs the GSP that it can send `elem_count` new pages into the message queue.
fn advance_cpu_read_ptr(&mut self, elem_count: u32) {
super::fw::gsp_mem::advance_cpu_read_ptr(&self.0, elem_count)
let rx = io_project!(self.0, .cpuq.rx);
let rptr = MsgqRxHeader::read_ptr(rx).wrapping_add(elem_count) % MSGQ_NUM_PAGES;
// Ensure read pointer is properly ordered.
fence(Ordering::SeqCst);
MsgqRxHeader::set_read_ptr(rx, rptr)
}
// Returns the index of the memory page the CPU can write the next command to.
@@ -438,12 +441,17 @@ impl DmaGspMem {
//
// - The returned value is within `0..MSGQ_NUM_PAGES`.
fn cpu_write_ptr(&self) -> u32 {
super::fw::gsp_mem::cpu_write_ptr(&self.0)
MsgqTxHeader::write_ptr(io_project!(self.0, .cpuq.tx)) % MSGQ_NUM_PAGES
}
// Informs the GSP that it can process `elem_count` new pages from the command queue.
fn advance_cpu_write_ptr(&mut self, elem_count: u32) {
super::fw::gsp_mem::advance_cpu_write_ptr(&self.0, elem_count)
let tx = io_project!(self.0, .cpuq.tx);
let wptr = MsgqTxHeader::write_ptr(tx).wrapping_add(elem_count) % MSGQ_NUM_PAGES;
MsgqTxHeader::set_write_ptr(tx, wptr);
// Ensure all command data is visible before triggering the GSP read.
fence(Ordering::SeqCst);
}
}
+28 -54
View File
@@ -10,7 +10,14 @@ use r570_144 as bindings;
use core::ops::Range;
use kernel::{
dma::Coherent,
dma::{
Coherent,
CoherentView, //
},
io::{
io_read,
io_write, //
},
prelude::*,
ptr::{
Alignable,
@@ -44,59 +51,6 @@ use crate::{
},
};
// TODO: Replace with `IoView` projections once available.
pub(super) mod gsp_mem {
use core::sync::atomic::{
fence,
Ordering, //
};
use kernel::{
dma::Coherent,
dma_read,
dma_write, //
};
use crate::gsp::cmdq::{
GspMem,
MSGQ_NUM_PAGES, //
};
pub(in crate::gsp) fn gsp_write_ptr(qs: &Coherent<GspMem>) -> u32 {
dma_read!(qs, .gspq.tx.0.writePtr) % MSGQ_NUM_PAGES
}
pub(in crate::gsp) fn gsp_read_ptr(qs: &Coherent<GspMem>) -> u32 {
dma_read!(qs, .gspq.rx.0.readPtr) % MSGQ_NUM_PAGES
}
pub(in crate::gsp) fn cpu_read_ptr(qs: &Coherent<GspMem>) -> u32 {
dma_read!(qs, .cpuq.rx.0.readPtr) % MSGQ_NUM_PAGES
}
pub(in crate::gsp) fn advance_cpu_read_ptr(qs: &Coherent<GspMem>, count: u32) {
let rptr = cpu_read_ptr(qs).wrapping_add(count) % MSGQ_NUM_PAGES;
// Ensure read pointer is properly ordered.
fence(Ordering::SeqCst);
dma_write!(qs, .cpuq.rx.0.readPtr, rptr);
}
pub(in crate::gsp) fn cpu_write_ptr(qs: &Coherent<GspMem>) -> u32 {
dma_read!(qs, .cpuq.tx.0.writePtr) % MSGQ_NUM_PAGES
}
pub(in crate::gsp) fn advance_cpu_write_ptr(qs: &Coherent<GspMem>, count: u32) {
let wptr = cpu_write_ptr(qs).wrapping_add(count) % MSGQ_NUM_PAGES;
dma_write!(qs, .cpuq.tx.0.writePtr, wptr);
// Ensure all command data is visible before triggering the GSP read.
fence(Ordering::SeqCst);
}
}
/// Maximum size of a single GSP message queue element in bytes.
pub(crate) const GSP_MSG_QUEUE_ELEMENT_SIZE_MAX: usize =
num::u32_as_usize(bindings::GSP_MSG_QUEUE_ELEMENT_SIZE_MAX);
@@ -720,6 +674,16 @@ impl MsgqTxHeader {
entryOff: num::usize_into_u32::<GSP_PAGE_SIZE>(),
})
}
/// Returns the value of the write pointer for this queue.
pub(crate) fn write_ptr(this: CoherentView<'_, Self>) -> u32 {
io_read!(this, .0.writePtr)
}
/// Sets the value of the write pointer for this queue.
pub(crate) fn set_write_ptr(this: CoherentView<'_, Self>, val: u32) {
io_write!(this, .0.writePtr, val)
}
}
// SAFETY: Padding is explicit and does not contain uninitialized data.
@@ -735,6 +699,16 @@ impl MsgqRxHeader {
pub(crate) fn new() -> Self {
Self(Default::default())
}
/// Returns the value of the read pointer for this queue.
pub(crate) fn read_ptr(this: CoherentView<'_, Self>) -> u32 {
io_read!(this, .0.readPtr)
}
/// Sets the value of the read pointer for this queue.
pub(crate) fn set_read_ptr(this: CoherentView<'_, Self>, val: u32) {
io_write!(this, .0.readPtr, val)
}
}
// SAFETY: Padding is explicit and does not contain uninitialized data.
+2 -5
View File
@@ -20,7 +20,6 @@
//! this method is not used in this driver.
//!
use core::ops::Deref;
use kernel::{
clk::Clk,
device::{Bound, Core, Device},
@@ -212,8 +211,7 @@ impl pwm::PwmOps for Th1520PwmDriverData {
) -> Result<Self::WfHw> {
let data = chip.drvdata();
let hwpwm = pwm.hwpwm();
let iomem_accessor = data.iomem.access(parent_dev)?;
let iomap = iomem_accessor.deref();
let iomap = data.iomem.access(parent_dev)?;
let ctrl = iomap.try_read32(th1520_pwm_ctrl(hwpwm))?;
let period_cycles = iomap.try_read32(th1520_pwm_per(hwpwm))?;
@@ -247,8 +245,7 @@ impl pwm::PwmOps for Th1520PwmDriverData {
) -> Result {
let data = chip.drvdata();
let hwpwm = pwm.hwpwm();
let iomem_accessor = data.iomem.access(parent_dev)?;
let iomap = iomem_accessor.deref();
let iomap = data.iomem.access(parent_dev)?;
let duty_cycles = iomap.try_read32(th1520_pwm_fp(hwpwm))?;
let was_enabled = duty_cycles != 0;
+13
View File
@@ -19,6 +19,19 @@ __rust_helper void rust_helper_iounmap(void __iomem *addr)
iounmap(addr);
}
__rust_helper void rust_helper_memcpy_fromio(void *dst,
const volatile void __iomem *src,
size_t count)
{
memcpy_fromio(dst, src, count);
}
__rust_helper void rust_helper_memcpy_toio(volatile void __iomem *dst,
const void *src, size_t count)
{
memcpy_toio(dst, src, count);
}
__rust_helper u8 rust_helper_readb(const void __iomem *addr)
{
return readb(addr);
+12 -12
View File
@@ -70,17 +70,19 @@ struct Inner<T> {
/// devres::Devres,
/// io::{
/// Io,
/// IoKnownSize,
/// IoBase,
/// Mmio,
/// MmioRaw,
/// PhysAddr, //
/// MmioBackend,
/// PhysAddr,
/// Region, //
/// },
/// prelude::*,
/// };
/// use core::ops::Deref;
///
/// // See also [`pci::Bar`] for a real example.
/// struct IoMem<const SIZE: usize>(MmioRaw<SIZE>);
/// struct IoMem<const SIZE: usize>(MmioRaw<Region<SIZE>>);
///
/// impl<const SIZE: usize> IoMem<SIZE> {
/// /// # Safety
@@ -95,7 +97,7 @@ struct Inner<T> {
/// return Err(ENOMEM);
/// }
///
/// Ok(IoMem(MmioRaw::new(addr as usize, SIZE)?))
/// Ok(IoMem(MmioRaw::new_region(addr as usize, SIZE)?))
/// }
/// }
///
@@ -106,12 +108,13 @@ struct Inner<T> {
/// }
/// }
///
/// impl<const SIZE: usize> Deref for IoMem<SIZE> {
/// type Target = Mmio<SIZE>;
/// impl<'a, const SIZE: usize> IoBase<'a> for &'a IoMem<SIZE> {
/// type Backend = MmioBackend;
/// type Target = Region<SIZE>;
///
/// fn deref(&self) -> &Self::Target {
/// fn as_view(self) -> Mmio<'a, Region<SIZE>> {
/// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`.
/// unsafe { Mmio::from_raw(&self.0) }
/// unsafe { Mmio::from_raw(self.0) }
/// }
/// }
/// # fn no_run(dev: &Device<Bound>) -> Result<(), Error> {
@@ -299,10 +302,7 @@ impl<T: Send + 'static> Devres<T> {
/// use kernel::{
/// device::Core,
/// devres::Devres,
/// io::{
/// Io,
/// IoKnownSize, //
/// },
/// io::Io,
/// pci, //
/// };
///
+156 -125
View File
@@ -14,14 +14,22 @@ use crate::{
},
error::to_result,
fs::file,
io::{
IoBackend,
IoBase,
IoCapable,
IoCopyable,
SysMem,
SysMemBackend, //
},
prelude::*,
ptr::KnownSize,
sync::aref::ARef,
transmute::{
AsBytes,
FromBytes, //
}, //
uaccess::UserSliceWriter,
},
uaccess::UserSliceWriter, //
};
use core::{
ops::{
@@ -654,52 +662,6 @@ impl<T: KnownSize + ?Sized> Coherent<T> {
// SAFETY: per safety requirement.
unsafe { &mut *self.as_mut_ptr() }
}
/// Reads the value of `field` and ensures that its type is [`FromBytes`].
///
/// # Safety
///
/// This must be called from the [`dma_read`] macro which ensures that the `field` pointer is
/// validated beforehand.
///
/// Public but hidden since it should only be used from [`dma_read`] macro.
#[doc(hidden)]
pub unsafe fn field_read<F: FromBytes>(&self, field: *const F) -> F {
// SAFETY:
// - By the safety requirements field is valid.
// - Using read_volatile() here is not sound as per the usual rules, the usage here is
// a special exception with the following notes in place. When dealing with a potential
// race from a hardware or code outside kernel (e.g. user-space program), we need that
// read on a valid memory is not UB. Currently read_volatile() is used for this, and the
// rationale behind is that it should generate the same code as READ_ONCE() which the
// kernel already relies on to avoid UB on data races. Note that the usage of
// read_volatile() is limited to this particular case, it cannot be used to prevent
// the UB caused by racing between two kernel functions nor do they provide atomicity.
unsafe { field.read_volatile() }
}
/// Writes a value to `field` and ensures that its type is [`AsBytes`].
///
/// # Safety
///
/// This must be called from the [`dma_write`] macro which ensures that the `field` pointer is
/// validated beforehand.
///
/// Public but hidden since it should only be used from [`dma_write`] macro.
#[doc(hidden)]
pub unsafe fn field_write<F: AsBytes>(&self, field: *mut F, val: F) {
// SAFETY:
// - By the safety requirements field is valid.
// - Using write_volatile() here is not sound as per the usual rules, the usage here is
// a special exception with the following notes in place. When dealing with a potential
// race from a hardware or code outside kernel (e.g. user-space program), we need that
// write on a valid memory is not UB. Currently write_volatile() is used for this, and the
// rationale behind is that it should generate the same code as WRITE_ONCE() which the
// kernel already relies on to avoid UB on data races. Note that the usage of
// write_volatile() is limited to this particular case, it cannot be used to prevent
// the UB caused by racing between two kernel functions nor do they provide atomicity.
unsafe { field.write_volatile(val) }
}
}
impl<T: AsBytes + FromBytes> Coherent<T> {
@@ -1133,84 +1095,153 @@ unsafe impl Send for CoherentHandle {}
// plain `Copy` values.
unsafe impl Sync for CoherentHandle {}
/// Reads a field of an item from an allocated region of structs.
/// View type for `Coherent`.
///
/// The syntax is of the form `kernel::dma_read!(dma, proj)` where `dma` is an expression evaluating
/// to a [`Coherent`] and `proj` is a [projection specification](kernel::ptr::project!).
///
/// # Examples
///
/// ```
/// use kernel::device::Device;
/// use kernel::dma::{attrs::*, Coherent};
///
/// struct MyStruct { field: u32, }
///
/// // SAFETY: All bit patterns are acceptable values for `MyStruct`.
/// unsafe impl kernel::transmute::FromBytes for MyStruct{};
/// // SAFETY: Instances of `MyStruct` have no uninitialized portions.
/// unsafe impl kernel::transmute::AsBytes for MyStruct{};
///
/// # fn test(alloc: &kernel::dma::Coherent<[MyStruct]>) -> Result {
/// let whole = kernel::dma_read!(alloc, [try: 2]);
/// let field = kernel::dma_read!(alloc, [panic: 1].field);
/// # Ok::<(), Error>(()) }
/// ```
#[macro_export]
macro_rules! dma_read {
($dma:expr, $($proj:tt)*) => {{
let dma = &$dma;
let ptr = $crate::ptr::project!(
$crate::dma::Coherent::as_ptr(dma), $($proj)*
);
// SAFETY: The pointer created by the projection is within the DMA region.
unsafe { $crate::dma::Coherent::field_read(dma, ptr) }
}};
/// This is same as [`SysMem`] but with additional information that allows handing out a DMA handle.
pub struct CoherentView<'a, T: ?Sized> {
cpu_addr: SysMem<'a, T>,
dma_handle: DmaAddress,
}
/// Writes to a field of an item from an allocated region of structs.
///
/// The syntax is of the form `kernel::dma_write!(dma, proj, val)` where `dma` is an expression
/// evaluating to a [`Coherent`], `proj` is a
/// [projection specification](kernel::ptr::project!), and `val` is the value to be written to the
/// projected location.
///
/// # Examples
///
/// ```
/// use kernel::device::Device;
/// use kernel::dma::{attrs::*, Coherent};
///
/// struct MyStruct { member: u32, }
///
/// // SAFETY: All bit patterns are acceptable values for `MyStruct`.
/// unsafe impl kernel::transmute::FromBytes for MyStruct{};
/// // SAFETY: Instances of `MyStruct` have no uninitialized portions.
/// unsafe impl kernel::transmute::AsBytes for MyStruct{};
///
/// # fn test(alloc: &kernel::dma::Coherent<[MyStruct]>) -> Result {
/// kernel::dma_write!(alloc, [try: 2].member, 0xf);
/// kernel::dma_write!(alloc, [panic: 1], MyStruct { member: 0xf });
/// # Ok::<(), Error>(()) }
/// ```
#[macro_export]
macro_rules! dma_write {
(@parse [$dma:expr] [$($proj:tt)*] [, $val:expr]) => {{
let dma = &$dma;
let ptr = $crate::ptr::project!(
mut $crate::dma::Coherent::as_mut_ptr(dma), $($proj)*
);
let val = $val;
// SAFETY: The pointer created by the projection is within the DMA region.
unsafe { $crate::dma::Coherent::field_write(dma, ptr, val) }
}};
(@parse [$dma:expr] [$($proj:tt)*] [.$field:tt $($rest:tt)*]) => {
$crate::dma_write!(@parse [$dma] [$($proj)* .$field] [$($rest)*])
};
(@parse [$dma:expr] [$($proj:tt)*] [[$flavor:ident: $index:expr] $($rest:tt)*]) => {
$crate::dma_write!(@parse [$dma] [$($proj)* [$flavor: $index]] [$($rest)*])
};
($dma:expr, $($rest:tt)*) => {
$crate::dma_write!(@parse [$dma] [] [$($rest)*])
};
impl<T: ?Sized> Copy for CoherentView<'_, T> {}
impl<T: ?Sized> Clone for CoherentView<'_, T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<'a, T: ?Sized> CoherentView<'a, T> {
/// Erase the DMA handle information and obtain a [`SysMem`] view of the same memory region.
#[inline]
pub fn as_sys_mem(self) -> SysMem<'a, T> {
self.cpu_addr
}
/// Returns a DMA handle which may be given to the device as the DMA address base of the region.
#[inline]
pub fn dma_handle(self) -> DmaAddress {
self.dma_handle
}
/// Returns a reference to the data in the region.
///
/// # Safety
///
/// * Callers must ensure that the device does not read/write to/from memory while the returned
/// reference is live.
/// * Callers must ensure that this call does not race with a write (including call to `as_mut`)
/// to the same region while the returned reference is live.
#[inline]
pub unsafe fn as_ref(self) -> &'a T {
// SAFETY: pointer is aligned and valid per type invariant. Aliasing rule is satisfied per
// safety requirement.
unsafe { &*self.cpu_addr.as_ptr() }
}
/// Returns a mutable reference to the data in the region.
///
/// # Safety
///
/// * Callers must ensure that the device does not read/write to/from memory while the returned
/// reference is live.
/// * Callers must ensure that this call does not race with a read (including call to `as_ref`)
/// or write (including call to `as_mut`) to the same region while the returned reference is
/// live.
#[inline]
pub unsafe fn as_mut(self) -> &'a mut T {
// SAFETY: pointer is aligned and valid per type invariant. Aliasing rule is satisfied per
// safety requirement.
unsafe { &mut *self.cpu_addr.as_ptr() }
}
}
/// `IoBackend` implementation for `Coherent`.
pub struct CoherentIoBackend;
impl IoBackend for CoherentIoBackend {
type View<'a, T: ?Sized + KnownSize> = CoherentView<'a, T>;
#[inline]
fn as_ptr<'a, T: ?Sized + KnownSize>(view: Self::View<'a, T>) -> *mut T {
SysMemBackend::as_ptr(view.cpu_addr)
}
#[inline]
unsafe fn project_view<'a, T: ?Sized + KnownSize, U: ?Sized + KnownSize>(
view: Self::View<'a, T>,
ptr: *mut U,
) -> Self::View<'a, U> {
let offset = ptr.addr() - view.cpu_addr.as_ptr().addr();
// CAST: The offset DMA address can never overflow.
let dma_handle = view.dma_handle + offset as DmaAddress;
CoherentView {
dma_handle,
// SAFETY: Per safety requirement.
cpu_addr: unsafe { SysMemBackend::project_view(view.cpu_addr, ptr) },
}
}
}
impl<T> IoCapable<T> for CoherentIoBackend
where
SysMemBackend: IoCapable<T>,
{
#[inline]
fn io_read<'a>(view: Self::View<'a, T>) -> T {
SysMemBackend::io_read(view.cpu_addr)
}
#[inline]
fn io_write<'a>(view: Self::View<'a, T>, value: T) {
SysMemBackend::io_write(view.cpu_addr, value)
}
}
impl IoCopyable for CoherentIoBackend {
#[inline]
unsafe fn copy_from_io(view: Self::View<'_, [u8]>, buffer: *mut u8) {
// SAFETY: Per safety requirement.
unsafe { SysMemBackend::copy_from_io(view.cpu_addr, buffer) }
}
#[inline]
unsafe fn copy_to_io(view: Self::View<'_, [u8]>, buffer: *const u8) {
// SAFETY: Per safety requirement.
unsafe { SysMemBackend::copy_to_io(view.cpu_addr, buffer) }
}
#[inline]
fn copy_read<T: zerocopy::FromBytes>(view: Self::View<'_, T>) -> T {
SysMemBackend::copy_read(view.cpu_addr)
}
#[inline]
fn copy_write<T: zerocopy::IntoBytes>(view: Self::View<'_, T>, value: T) {
SysMemBackend::copy_write(view.cpu_addr, value)
}
}
impl<'a, T: ?Sized + KnownSize> IoBase<'a> for CoherentView<'a, T> {
type Backend = CoherentIoBackend;
type Target = T;
#[inline]
fn as_view(self) -> CoherentView<'a, Self::Target> {
self
}
}
impl<'a, T: ?Sized + KnownSize> IoBase<'a> for &'a Coherent<T> {
type Backend = CoherentIoBackend;
type Target = T;
#[inline]
fn as_view(self) -> CoherentView<'a, Self::Target> {
CoherentView {
// SAFETY: `cpu_addr` is valid and aligned kernel accessible memory.
cpu_addr: unsafe { SysMem::new(self.cpu_addr.as_ptr()) },
dma_handle: self.dma_handle,
}
}
}
+1204 -322
View File
File diff suppressed because it is too large Load Diff
+16 -13
View File
@@ -2,8 +2,6 @@
//! Generic memory-mapped IO.
use core::ops::Deref;
use crate::{
device::{
Bound,
@@ -16,7 +14,9 @@ use crate::{
Region,
Resource, //
},
IoBase,
Mmio,
MmioBackend,
MmioRaw, //
},
prelude::*,
@@ -225,11 +225,13 @@ impl<'a, const SIZE: usize> ExclusiveIoMem<'a, SIZE> {
}
}
impl<const SIZE: usize> Deref for ExclusiveIoMem<'_, SIZE> {
type Target = Mmio<SIZE>;
impl<'a, const SIZE: usize> IoBase<'a> for &'a ExclusiveIoMem<'_, SIZE> {
type Backend = MmioBackend;
type Target = super::Region<SIZE>;
fn deref(&self) -> &Self::Target {
&self.iomem
#[inline]
fn as_view(self) -> Mmio<'a, Self::Target> {
self.iomem.as_view()
}
}
@@ -244,7 +246,7 @@ impl<const SIZE: usize> Deref for ExclusiveIoMem<'_, SIZE> {
/// start of the I/O memory mapped region.
pub struct IoMem<'a, const SIZE: usize = 0> {
dev: &'a Device<Bound>,
io: MmioRaw<SIZE>,
io: MmioRaw<super::Region<SIZE>>,
}
impl<const SIZE: usize> ForLt for IoMem<'static, SIZE> {
@@ -292,8 +294,7 @@ impl<'a, const SIZE: usize> IoMem<'a, SIZE> {
return Err(ENOMEM);
}
let io = MmioRaw::new(addr as usize, size)?;
let io = MmioRaw::new_region(addr as usize, size)?;
Ok(IoMem { dev, io })
}
@@ -316,11 +317,13 @@ impl<const SIZE: usize> Drop for IoMem<'_, SIZE> {
}
}
impl<const SIZE: usize> Deref for IoMem<'_, SIZE> {
type Target = Mmio<SIZE>;
impl<'a, const SIZE: usize> IoBase<'a> for &'a IoMem<'_, SIZE> {
type Backend = MmioBackend;
type Target = super::Region<SIZE>;
fn deref(&self) -> &Self::Target {
#[inline]
fn as_view(self) -> Mmio<'a, Self::Target> {
// SAFETY: Safe as by the invariant of `IoMem`.
unsafe { Mmio::from_raw(&self.io) }
unsafe { Mmio::from_raw(self.io) }
}
}
+4 -2
View File
@@ -48,13 +48,14 @@ use crate::{
/// use kernel::io::{
/// Io,
/// Mmio,
/// Region,
/// poll::read_poll_timeout, //
/// };
/// use kernel::time::Delta;
///
/// const HW_READY: u16 = 0x01;
///
/// fn wait_for_hardware<const SIZE: usize>(io: &Mmio<SIZE>) -> Result {
/// fn wait_for_hardware<const SIZE: usize>(io: Mmio<'_, Region<SIZE>>) -> Result {
/// read_poll_timeout(
/// // The `op` closure reads the value of a specific status register.
/// || io.try_read16(0x1000),
@@ -135,13 +136,14 @@ where
/// use kernel::io::{
/// Io,
/// Mmio,
/// Region,
/// poll::read_poll_timeout_atomic, //
/// };
/// use kernel::time::Delta;
///
/// const HW_READY: u16 = 0x01;
///
/// fn wait_for_hardware<const SIZE: usize>(io: &Mmio<SIZE>) -> Result {
/// fn wait_for_hardware<const SIZE: usize>(io: Mmio<'_, Region<SIZE>>) -> Result {
/// read_poll_timeout_atomic(
/// // The `op` closure reads the value of a specific status register.
/// || io.try_read16(0x1000),
+23 -21
View File
@@ -58,7 +58,7 @@
//! },
//! num::Bounded,
//! };
//! # use kernel::io::Mmio;
//! # use kernel::io::{Mmio, Region};
//! # register! {
//! # pub BOOT_0(u32) @ 0x00000100 {
//! # 15:8 vendor_id;
@@ -66,7 +66,7 @@
//! # 3:0 minor_revision;
//! # }
//! # }
//! # fn test(io: &Mmio<0x1000>) {
//! # fn test(io: Mmio<'_, Region<0x1000>>) {
//! # fn obtain_vendor_id() -> u8 { 0xff }
//!
//! // Read from the register's defined offset (0x100).
@@ -113,6 +113,8 @@ use crate::{
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 @@ where
/// 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 @@ impl<T: FixedRegister> FixedRegisterLoc<T> {
}
}
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 @@ where
}
}
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 @@ impl<T: RegisterArray> RegisterArrayLoc<T> {
}
}
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 @@ where
}
}
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 @@ where
/// 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,
{
@@ -444,7 +446,7 @@ where
/// Io,
/// },
/// };
/// # use kernel::io::Mmio;
/// # use kernel::io::{Mmio, Region};
///
/// register! {
/// FIXED_REG(u32) @ 0x100 {
@@ -453,7 +455,7 @@ where
/// }
/// }
///
/// # fn test(io: &Mmio<0x1000>) {
/// # fn test(io: Mmio<'_, Region<0x1000>>) {
/// let val = io.read(FIXED_REG);
///
/// // Write from an already-existing value.
@@ -557,7 +559,7 @@ where
/// Io,
/// },
/// };
/// # use kernel::io::Mmio;
/// # use kernel::io::{Mmio, Region};
///
/// // Type used to identify the base.
/// pub struct CpuCtlBase;
@@ -582,7 +584,7 @@ where
/// }
/// }
///
/// # fn test(io: Mmio<0x1000>) {
/// # fn test(io: Mmio<'_, Region<0x1000>>) {
/// // Read the status of `Cpu0`.
/// let cpu0_started = io.read(CPU_CTL::of::<Cpu0>());
///
@@ -599,7 +601,7 @@ where
/// }
/// }
///
/// # fn test2(io: Mmio<0x1000>) {
/// # fn test2(io: Mmio<'_, Region<0x1000>>) {
/// // Start the aliased `CPU0`, leaving its other fields untouched.
/// io.update(CPU_CTL_ALIAS::of::<Cpu0>(), |r| r.with_alias_start(true));
/// # }
@@ -636,7 +638,7 @@ where
/// Io,
/// },
/// };
/// # use kernel::io::Mmio;
/// # use kernel::io::{Mmio, Region};
/// # fn get_scratch_idx() -> usize {
/// # 0x15
/// # }
@@ -649,7 +651,7 @@ where
/// }
/// }
///
/// # fn test(io: &Mmio<0x1000>)
/// # fn test(io: Mmio<'_, Region<0x1000>>)
/// # -> Result<(), Error>{
/// // Read scratch register 0, i.e. I/O address `0x80`.
/// let scratch_0 = io.read(SCRATCH::at(0)).value();
@@ -722,7 +724,7 @@ where
/// Io,
/// },
/// };
/// # use kernel::io::Mmio;
/// # use kernel::io::{Mmio, Region};
/// # fn get_scratch_idx() -> usize {
/// # 0x15
/// # }
@@ -750,7 +752,7 @@ where
/// }
/// }
///
/// # fn test(io: &Mmio<0x1000>) -> Result<(), Error> {
/// # fn test(io: Mmio<'_, Region<0x1000>>) -> Result<(), Error> {
/// // Read scratch register 0 of CPU0.
/// let scratch = io.read(CPU_SCRATCH::of::<Cpu0>().at(0));
///
@@ -792,7 +794,7 @@ where
/// }
/// }
///
/// # fn test2(io: &Mmio<0x1000>) -> Result<(), Error> {
/// # fn test2(io: Mmio<'_, Region<0x1000>>) -> Result<(), Error> {
/// let cpu0_status = io.read(CPU_FIRMWARE_STATUS::of::<Cpu0>()).status();
/// # Ok(())
/// # }
+3
View File
@@ -16,6 +16,9 @@
// Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on
// the unstable features in use.
//
// Stable since Rust 1.87.0.
#![feature(unsigned_is_multiple_of)]
//
// Stable since Rust 1.89.0.
#![feature(generic_arg_infer)]
//
-1
View File
@@ -43,7 +43,6 @@ pub use self::id::{
pub use self::io::{
Bar,
ConfigSpace,
ConfigSpaceKind,
ConfigSpaceSize,
DevresBar,
Extended,
+95 -73
View File
@@ -8,22 +8,21 @@ use crate::{
device,
devres::DevresLt,
io::{
Io,
IoBackend,
IoBase,
IoCapable,
IoKnownSize,
Mmio,
MmioRaw, //
MmioBackend,
MmioRaw,
Region, //
},
prelude::*,
ptr::KnownSize,
types::{
CovariantForLt,
ForLt, //
}, //
};
use core::{
marker::PhantomData,
ops::Deref, //
};
/// Represents the size of a PCI configuration space.
///
@@ -50,68 +49,95 @@ impl ConfigSpaceSize {
}
}
/// Marker type for normal (256-byte) PCI configuration space.
pub struct Normal;
/// Alias for normal (256-byte) PCI configuration space.
pub type Normal = Region<256>;
/// Marker type for extended (4096-byte) PCIe configuration space.
pub struct Extended;
/// Alias for extended (4096-byte) PCIe configuration space.
pub type Extended = Region<4096>;
/// Trait for PCI configuration space size markers.
///
/// This trait is implemented by [`Normal`] and [`Extended`] to provide
/// compile-time knowledge of the configuration space size.
pub trait ConfigSpaceKind {
/// The size of this configuration space in bytes.
const SIZE: usize;
}
impl ConfigSpaceKind for Normal {
const SIZE: usize = 256;
}
impl ConfigSpaceKind for Extended {
const SIZE: usize = 4096;
}
/// The PCI configuration space of a device.
/// A view of PCI configuration space of a device.
///
/// Provides typed read and write accessors for configuration registers
/// using the standard `pci_read_config_*` and `pci_write_config_*` helpers.
///
/// The generic parameter `S` indicates the maximum size of the configuration space.
/// Use [`Normal`] for 256-byte legacy configuration space or [`Extended`] for
/// 4096-byte PCIe extended configuration space (default).
pub struct ConfigSpace<'a, S: ConfigSpaceKind = Extended> {
/// The generic parameter `T` is the type of the view. The full configuration space is also a
/// special type of view; in such cases, `T` can be [`Normal`] for 256-byte legacy configuration
/// space or [`Extended`] for 4096-byte PCIe extended configuration space (default).
///
/// # Invariants
///
/// `ptr` is aligned and range `ptr..ptr + KnownSize::size(ptr)` is within
/// `0..pdev.cfg_size().into_raw()`.
pub struct ConfigSpace<'a, T: ?Sized = Extended> {
pub(crate) pdev: &'a Device<device::Bound>,
_marker: PhantomData<S>,
ptr: *mut T,
}
impl<T: ?Sized> Copy for ConfigSpace<'_, T> {}
impl<T: ?Sized> Clone for ConfigSpace<'_, T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
// SAFETY: `ConfigSpace<'_, T>` is conceptually `&T` but in I/O memory.
unsafe impl<T: ?Sized + Sync> Send for ConfigSpace<'_, T> {}
// SAFETY: `ConfigSpace<'_, T>` is conceptually `&T` but in I/O memory.
unsafe impl<T: ?Sized + Sync> Sync for ConfigSpace<'_, T> {}
/// I/O Backend for PCI configuration space.
pub struct ConfigSpaceBackend;
impl IoBackend for ConfigSpaceBackend {
type View<'a, T: ?Sized + KnownSize> = ConfigSpace<'a, T>;
#[inline]
fn as_ptr<'a, T: ?Sized + KnownSize>(view: ConfigSpace<'a, T>) -> *mut T {
view.ptr
}
#[inline]
unsafe fn project_view<'a, T: ?Sized + KnownSize, U: ?Sized + KnownSize>(
view: Self::View<'a, T>,
ptr: *mut U,
) -> Self::View<'a, U> {
// INVARIANT: Per safety requirement.
ConfigSpace {
pdev: view.pdev,
ptr,
}
}
}
/// Implements [`IoCapable`] on [`ConfigSpace`] for `$ty` using `$read_fn` and `$write_fn`.
macro_rules! impl_config_space_io_capable {
($ty:ty, $read_fn:ident, $write_fn:ident) => {
impl<'a, S: ConfigSpaceKind> IoCapable<$ty> for ConfigSpace<'a, S> {
unsafe fn io_read(&self, address: usize) -> $ty {
impl IoCapable<$ty> for ConfigSpaceBackend {
fn io_read(view: ConfigSpace<'_, $ty>) -> $ty {
// CAST: The offset is cast to `i32` because the C functions expect a 32-bit
// signed offset parameter. PCI configuration space size is at most 4096 bytes,
// so the value always fits within `i32` without truncation or sign change.
let addr = view.ptr.addr() as i32;
let mut val: $ty = 0;
// Return value from C function is ignored in infallible accessors.
let _ret =
// SAFETY: By the type invariant `self.pdev` is a valid address.
// CAST: The offset is cast to `i32` because the C functions expect a 32-bit
// signed offset parameter. PCI configuration space size is at most 4096 bytes,
// so the value always fits within `i32` without truncation or sign change.
unsafe { bindings::$read_fn(self.pdev.as_raw(), address as i32, &mut val) };
// SAFETY: By the type invariant `pdev` is a valid address.
let _ = unsafe { bindings::$read_fn(view.pdev.as_raw(), addr, &mut val) };
val
}
unsafe fn io_write(&self, value: $ty, address: usize) {
fn io_write(view: ConfigSpace<'_, $ty>, value: $ty) {
// CAST: The offset is cast to `i32` because the C functions expect a 32-bit
// signed offset parameter. PCI configuration space size is at most 4096 bytes,
// so the value always fits within `i32` without truncation or sign change.
let addr = view.ptr.addr() as i32;
// Return value from C function is ignored in infallible accessors.
let _ret =
// SAFETY: By the type invariant `self.pdev` is a valid address.
// CAST: The offset is cast to `i32` because the C functions expect a 32-bit
// signed offset parameter. PCI configuration space size is at most 4096 bytes,
// so the value always fits within `i32` without truncation or sign change.
unsafe { bindings::$write_fn(self.pdev.as_raw(), address as i32, value) };
// SAFETY: By the type invariant `pdev` is a valid address.
let _ = unsafe { bindings::$write_fn(view.pdev.as_raw(), addr, value) };
}
}
};
@@ -122,24 +148,16 @@ impl_config_space_io_capable!(u8, pci_read_config_byte, pci_write_config_byte);
impl_config_space_io_capable!(u16, pci_read_config_word, pci_write_config_word);
impl_config_space_io_capable!(u32, pci_read_config_dword, pci_write_config_dword);
impl<'a, S: ConfigSpaceKind> Io for ConfigSpace<'a, S> {
/// Returns the base address of the I/O region. It is always 0 for configuration space.
#[inline]
fn addr(&self) -> usize {
0
}
impl<'a, T: ?Sized + KnownSize> IoBase<'a> for ConfigSpace<'a, T> {
type Backend = ConfigSpaceBackend;
type Target = T;
/// Returns the maximum size of the configuration space.
#[inline]
fn maxsize(&self) -> usize {
self.pdev.cfg_size().into_raw()
fn as_view(self) -> ConfigSpace<'a, T> {
self
}
}
impl<'a, S: ConfigSpaceKind> IoKnownSize for ConfigSpace<'a, S> {
const MIN_SIZE: usize = S::SIZE;
}
/// A PCI BAR to perform I/O-Operations on.
///
/// I/O backend assumes that the device is little-endian and will automatically
@@ -151,7 +169,7 @@ impl<'a, S: ConfigSpaceKind> IoKnownSize for ConfigSpace<'a, S> {
/// memory mapped PCI BAR and its size.
pub struct Bar<'a, const SIZE: usize = 0> {
pdev: &'a Device<device::Bound>,
io: MmioRaw<SIZE>,
io: MmioRaw<crate::io::Region<SIZE>>,
num: i32,
}
@@ -204,7 +222,7 @@ impl<'a, const SIZE: usize> Bar<'a, SIZE> {
return Err(ENOMEM);
}
let io = match MmioRaw::new(ioptr, len as usize) {
let io = match MmioRaw::new_region(ioptr, len as usize) {
Ok(io) => io,
Err(err) => {
// SAFETY:
@@ -264,12 +282,14 @@ impl<const SIZE: usize> Drop for Bar<'_, SIZE> {
}
}
impl<const SIZE: usize> Deref for Bar<'_, SIZE> {
type Target = Mmio<SIZE>;
impl<'a, const SIZE: usize> IoBase<'a> for &'a Bar<'_, SIZE> {
type Backend = MmioBackend;
type Target = crate::io::Region<SIZE>;
fn deref(&self) -> &Self::Target {
#[inline]
fn as_view(self) -> Mmio<'a, Self::Target> {
// SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped.
unsafe { Mmio::from_raw(&self.io) }
unsafe { Mmio::from_raw(self.io) }
}
}
@@ -304,23 +324,25 @@ impl Device<device::Bound> {
}
}
/// Return an initialized normal (256-byte) config space object.
/// Return a view of the normal (256-byte) config space.
pub fn config_space<'a>(&'a self) -> ConfigSpace<'a, Normal> {
// INVARIANT: null is aligned and the range is within config space.
ConfigSpace {
pdev: self,
_marker: PhantomData,
ptr: Normal::ptr_from_raw_parts_mut(core::ptr::null_mut(), self.cfg_size().into_raw()),
}
}
/// Return an initialized extended (4096-byte) config space object.
/// Return a view of the extended (4096-byte) config space.
pub fn config_space_extended<'a>(&'a self) -> Result<ConfigSpace<'a, Extended>> {
if self.cfg_size() != ConfigSpaceSize::Extended {
return Err(EINVAL);
}
// INVARIANT: null is aligned and we just checked the `cfg_size`.
Ok(ConfigSpace {
pdev: self,
_marker: PhantomData,
ptr: Extended::ptr_from_raw_parts_mut(core::ptr::null_mut(), 4096),
})
}
}
+12
View File
@@ -235,11 +235,20 @@ impl_alignable_uint!(u8, u16, u32, u64, usize);
///
/// This is a generalization of [`size_of`] that works for dynamically sized types.
pub trait KnownSize {
/// Minimum size of this type known at compile-time.
const MIN_SIZE: usize;
/// Minimum alignment of this type known at compile-time.
const MIN_ALIGN: Alignment;
/// Get the size of an object of this type in bytes, with the metadata of the given pointer.
fn size(p: *const Self) -> usize;
}
impl<T> KnownSize for T {
const MIN_SIZE: usize = size_of::<T>();
const MIN_ALIGN: Alignment = Alignment::of::<T>();
#[inline(always)]
fn size(_: *const Self) -> usize {
size_of::<T>()
@@ -247,6 +256,9 @@ impl<T> KnownSize for T {
}
impl<T> KnownSize for [T] {
const MIN_SIZE: usize = 0;
const MIN_ALIGN: Alignment = Alignment::of::<T>();
#[inline(always)]
fn size(p: *const Self) -> usize {
p.len() * size_of::<T>()
+9 -3
View File
@@ -12,6 +12,11 @@ use kernel::{
Device,
DmaMask, //
},
io::{
io_project,
io_read,
Io, //
},
page, pci,
prelude::*,
scatterlist::{Owned, SGTable},
@@ -34,6 +39,7 @@ const TEST_VALUES: [(u32, u32); 5] = [
(0xcd, 0xef),
];
#[derive(FromBytes, IntoBytes)]
struct MyStruct {
h: u32,
b: u32,
@@ -77,7 +83,7 @@ impl pci::Driver for DmaSampleDriver {
Coherent::zeroed_slice(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?;
for (i, value) in TEST_VALUES.into_iter().enumerate() {
kernel::dma_write!(ca, [try: i], MyStruct::new(value.0, value.1));
io_project!(ca, [panic: i]).copy_write(MyStruct::new(value.0, value.1));
}
let size = 4 * page::PAGE_SIZE;
@@ -97,8 +103,8 @@ impl pci::Driver for DmaSampleDriver {
impl DmaSampleDriver {
fn check_dma(&self) {
for (i, value) in TEST_VALUES.into_iter().enumerate() {
let val0 = kernel::dma_read!(self.ca, [panic: i].h);
let val1 = kernel::dma_read!(self.ca, [panic: i].b);
let val0 = io_read!(self.ca, [panic: i].h);
let val1 = io_read!(self.ca, [panic: i].b);
assert_eq!(val0, value.0);
assert_eq!(val1, value.1);