Skip to content

Commit af024d4

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

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::c_str;
89
use crate::device::Bound;
@@ -14,6 +15,7 @@ use crate::io::resource::Resource;
1415
use crate::io::Io;
1516
use crate::io::IoRaw;
1617
use crate::prelude::*;
18+
use crate::types::declare_flags_type;
1719

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

0 commit comments

Comments
 (0)