Skip to content

Commit 76c179d

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

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::{
89
c_str,
@@ -21,6 +22,7 @@ use crate::{
2122
IoRaw, //
2223
},
2324
prelude::*,
25+
types::declare_flags_type, //
2426
};
2527

2628
/// An IO request for a specific device and resource.
@@ -285,3 +287,104 @@ impl<const SIZE: usize> Deref for IoMem<SIZE> {
285287
unsafe { Io::from_raw(&self.io) }
286288
}
287289
}
290+
291+
declare_flags_type! {
292+
/// Flags to be used when remapping memory.
293+
///
294+
/// They can be combined with the operators `|`, `&`, and `!`.
295+
pub struct MemFlags(crate::ffi::c_ulong) = 0;
296+
}
297+
298+
impl MemFlags {
299+
/// Matches the default mapping for System RAM on the architecture.
300+
///
301+
/// This is usually a read-allocate write-back cache. Moreover, if this flag is specified and
302+
/// the requested remap region is RAM, memremap() will bypass establishing a new mapping and
303+
/// instead return a pointer into the direct map.
304+
pub const WB: MemFlags = MemFlags(bindings::MEMREMAP_WB as _);
305+
306+
/// Establish a mapping whereby writes either bypass the cache or are written through to memory
307+
/// and never exist in a cache-dirty state with respect to program visibility.
308+
///
309+
/// Attempts to map System RAM with this mapping type will fail.
310+
pub const WT: MemFlags = MemFlags(bindings::MEMREMAP_WT as _);
311+
/// Establish a writecombine mapping, whereby writes may be coalesced together (e.g. in the
312+
/// CPU's write buffers), but is otherwise uncached.
313+
///
314+
/// Attempts to map System RAM with this mapping type will fail.
315+
pub const WC: MemFlags = MemFlags(bindings::MEMREMAP_WC as _);
316+
317+
// Note: Skipping MEMREMAP_ENC/DEC since they are under-documented and have zero
318+
// users outside of arch/x86.
319+
}
320+
321+
/// Represents a non-MMIO memory block. This is like [`IoMem`], but for cases where it is known
322+
/// that the resource being mapped does not have I/O side effects.
323+
// Invariants:
324+
// `ptr` is a non-null and valid address of at least `usize` bytes and returned by a `memremap`
325+
// call.
326+
// ```
327+
pub struct Mem {
328+
ptr: NonNull<crate::ffi::c_void>,
329+
size: usize,
330+
}
331+
332+
impl Mem {
333+
/// Tries to create a new instance of a memory block from a Resource.
334+
///
335+
/// The resource described by `res` is mapped into the CPU's address space so that it can be
336+
/// accessed directly. It is also consumed by this function so that it can't be mapped again
337+
/// to a different address.
338+
///
339+
/// If multiple caching flags are specified, the different mapping types will be attempted in
340+
/// the order [`MemFlags::WB`], [`MemFlags::WT`], [`MemFlags::WC`].
341+
///
342+
/// # Flags
343+
///
344+
/// * [`MemFlags::WB`]: Matches the default mapping for System RAM on the architecture.
345+
/// This is usually a read-allocate write-back cache. Moreover, if this flag is specified and
346+
/// the requested remap region is RAM, memremap() will bypass establishing a new mapping and
347+
/// instead return a pointer into the direct map.
348+
///
349+
/// * [`MemFlags::WT`]: Establish a mapping whereby writes either bypass the cache or are written
350+
/// through to memory and never exist in a cache-dirty state with respect to program visibility.
351+
/// Attempts to map System RAM with this mapping type will fail.
352+
/// * [`MemFlags::WC`]: Establish a writecombine mapping, whereby writes may be coalesced together
353+
/// (e.g. in the CPU's write buffers), but is otherwise uncached. Attempts to map System RAM with
354+
/// this mapping type will fail.
355+
///
356+
/// # Safety
357+
///
358+
/// Callers must ensure that either (a) the resulting interface cannot be used to initiate DMA
359+
/// operations, or (b) that DMA operations initiated via the returned interface use DMA handles
360+
/// allocated through the `dma` module.
361+
pub unsafe fn try_new(res: Resource, flags: MemFlags) -> Result<Self> {
362+
let size: usize = res.size().try_into()?;
363+
364+
let addr = unsafe { bindings::memremap(res.start(), size, flags.as_raw()) };
365+
let ptr = NonNull::new(addr).ok_or(ENOMEM)?;
366+
// INVARIANT: `ptr` is non-null and was returned by `memremap`, so it is valid.
367+
Ok(Self { ptr, size })
368+
}
369+
370+
/// Returns the base address of the memory mapping as a raw pointer.
371+
///
372+
/// It is up to the caller to use this pointer safely, depending on the requirements of the
373+
/// hardware backing this memory block.
374+
pub fn ptr(&self) -> *mut u8 {
375+
self.ptr.cast().as_ptr()
376+
}
377+
378+
/// Returns the size of this mapped memory block.
379+
pub fn size(&self) -> usize {
380+
self.size
381+
}
382+
}
383+
384+
impl Drop for Mem {
385+
fn drop(&mut self) {
386+
// SAFETY: By the type invariant, `self.ptr` is a value returned by a previous successful
387+
// call to `memremap`.
388+
unsafe { bindings::memunmap(self.ptr.as_ptr()) };
389+
}
390+
}

0 commit comments

Comments
 (0)