Skip to content

Commit e1dfbf5

Browse files
hoshinolinajannau
authored andcommitted
rust: io: mem: Add Mem abstraction
Signed-off-by: Janne Grunau <j@jannau.net>
1 parent 9a4ba04 commit e1dfbf5

1 file changed

Lines changed: 103 additions & 0 deletions

File tree

rust/kernel/io/mem.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! Generic memory-mapped IO.
44
55
use core::ops::Deref;
6+
use core::ptr::NonNull;
67

78
use crate::device::Device;
89
use crate::devres::Devres;
@@ -12,6 +13,7 @@ use crate::io::resource::Resource;
1213
use crate::io::Io;
1314
use crate::io::IoRaw;
1415
use crate::prelude::*;
16+
use crate::types::declare_flags_type;
1517

1618
/// An exclusive memory-mapped IO region.
1719
///
@@ -128,3 +130,104 @@ impl<const SIZE: usize> Deref for IoMem<SIZE> {
128130
unsafe { Io::from_raw(&self.io) }
129131
}
130132
}
133+
134+
declare_flags_type! {
135+
/// Flags to be used when remapping memory.
136+
///
137+
/// They can be combined with the operators `|`, `&`, and `!`.
138+
pub struct MemFlags(crate::ffi::c_ulong) = 0;
139+
}
140+
141+
impl MemFlags {
142+
/// Matches the default mapping for System RAM on the architecture.
143+
///
144+
/// This is usually a read-allocate write-back cache. Moreover, if this flag is specified and
145+
/// the requested remap region is RAM, memremap() will bypass establishing a new mapping and
146+
/// instead return a pointer into the direct map.
147+
pub const WB: MemFlags = MemFlags(bindings::MEMREMAP_WB as _);
148+
149+
/// Establish a mapping whereby writes either bypass the cache or are written through to memory
150+
/// and never exist in a cache-dirty state with respect to program visibility.
151+
///
152+
/// Attempts to map System RAM with this mapping type will fail.
153+
pub const WT: MemFlags = MemFlags(bindings::MEMREMAP_WT as _);
154+
/// Establish a writecombine mapping, whereby writes may be coalesced together (e.g. in the
155+
/// CPU's write buffers), but is otherwise uncached.
156+
///
157+
/// Attempts to map System RAM with this mapping type will fail.
158+
pub const WC: MemFlags = MemFlags(bindings::MEMREMAP_WC as _);
159+
160+
// Note: Skipping MEMREMAP_ENC/DEC since they are under-documented and have zero
161+
// users outside of arch/x86.
162+
}
163+
164+
/// Represents a non-MMIO memory block. This is like [`IoMem`], but for cases where it is known
165+
/// that the resource being mapped does not have I/O side effects.
166+
// Invariants:
167+
// `ptr` is a non-null and valid address of at least `usize` bytes and returned by a `memremap`
168+
// call.
169+
// ```
170+
pub struct Mem {
171+
ptr: NonNull<crate::ffi::c_void>,
172+
size: usize,
173+
}
174+
175+
impl Mem {
176+
/// Tries to create a new instance of a memory block from a Resource.
177+
///
178+
/// The resource described by `res` is mapped into the CPU's address space so that it can be
179+
/// accessed directly. It is also consumed by this function so that it can't be mapped again
180+
/// to a different address.
181+
///
182+
/// If multiple caching flags are specified, the different mapping types will be attempted in
183+
/// the order [`MemFlags::WB`], [`MemFlags::WT`], [`MemFlags::WC`].
184+
///
185+
/// # Flags
186+
///
187+
/// * [`MemFlags::WB`]: Matches the default mapping for System RAM on the architecture.
188+
/// This is usually a read-allocate write-back cache. Moreover, if this flag is specified and
189+
/// the requested remap region is RAM, memremap() will bypass establishing a new mapping and
190+
/// instead return a pointer into the direct map.
191+
///
192+
/// * [`MemFlags::WT`]: Establish a mapping whereby writes either bypass the cache or are written
193+
/// through to memory and never exist in a cache-dirty state with respect to program visibility.
194+
/// Attempts to map System RAM with this mapping type will fail.
195+
/// * [`MemFlags::WC`]: Establish a writecombine mapping, whereby writes may be coalesced together
196+
/// (e.g. in the CPU's write buffers), but is otherwise uncached. Attempts to map System RAM with
197+
/// this mapping type will fail.
198+
///
199+
/// # Safety
200+
///
201+
/// Callers must ensure that either (a) the resulting interface cannot be used to initiate DMA
202+
/// operations, or (b) that DMA operations initiated via the returned interface use DMA handles
203+
/// allocated through the `dma` module.
204+
pub unsafe fn try_new(res: Resource, flags: MemFlags) -> Result<Self> {
205+
let size: usize = res.size().try_into()?;
206+
207+
let addr = unsafe { bindings::memremap(res.start(), size, flags.as_raw()) };
208+
let ptr = NonNull::new(addr).ok_or(ENOMEM)?;
209+
// INVARIANT: `ptr` is non-null and was returned by `memremap`, so it is valid.
210+
Ok(Self { ptr, size })
211+
}
212+
213+
/// Returns the base address of the memory mapping as a raw pointer.
214+
///
215+
/// It is up to the caller to use this pointer safely, depending on the requirements of the
216+
/// hardware backing this memory block.
217+
pub fn ptr(&self) -> *mut u8 {
218+
self.ptr.cast().as_ptr()
219+
}
220+
221+
/// Returns the size of this mapped memory block.
222+
pub fn size(&self) -> usize {
223+
self.size
224+
}
225+
}
226+
227+
impl Drop for Mem {
228+
fn drop(&mut self) {
229+
// SAFETY: By the type invariant, `self.ptr` is a value returned by a previous successful
230+
// call to `memremap`.
231+
unsafe { bindings::memunmap(self.ptr.as_ptr()) };
232+
}
233+
}

0 commit comments

Comments
 (0)