Skip to content

Commit debd390

Browse files
committed
rust: io_mem: Add Mem abstraction
To be refactored into io once that's upstream.
1 parent 4cb2396 commit debd390

1 file changed

Lines changed: 104 additions & 1 deletion

File tree

rust/kernel/io_mem.rs

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
77
#![allow(dead_code)]
88

9+
use crate::types::declare_flags_type;
910
use crate::{addr::*, bindings, error::code::*, error::Result};
11+
1012
use core::convert::TryInto;
1113
use core::ptr::NonNull;
1214

@@ -54,7 +56,7 @@ impl Resource {
5456
}
5557
}
5658

57-
/// Represents a memory block of at least `SIZE` bytes.
59+
/// Represents an MMIO memory block of at least `SIZE` bytes.
5860
///
5961
/// # Invariants
6062
///
@@ -327,3 +329,104 @@ impl<const SIZE: usize> Drop for IoMem<SIZE> {
327329
unsafe { bindings::iounmap(self.ptr as _) };
328330
}
329331
}
332+
333+
declare_flags_type! {
334+
/// Flags to be used when remapping memory.
335+
///
336+
/// They can be combined with the operators `|`, `&`, and `!`.
337+
pub struct MemFlags(core::ffi::c_ulong) = 0;
338+
}
339+
340+
impl MemFlags {
341+
/// Matches the default mapping for System RAM on the architecture.
342+
///
343+
/// This is usually a read-allocate write-back cache. Moreover, if this flag is specified and
344+
/// the requested remap region is RAM, memremap() will bypass establishing a new mapping and
345+
/// instead return a pointer into the direct map.
346+
pub const WB: MemFlags = MemFlags(bindings::MEMREMAP_WB as _);
347+
348+
/// Establish a mapping whereby writes either bypass the cache or are written through to memory
349+
/// and never exist in a cache-dirty state with respect to program visibility.
350+
///
351+
/// Attempts to map System RAM with this mapping type will fail.
352+
pub const WT: MemFlags = MemFlags(bindings::MEMREMAP_WT as _);
353+
/// Establish a writecombine mapping, whereby writes may be coalesced together (e.g. in the
354+
/// CPU's write buffers), but is otherwise uncached.
355+
///
356+
/// Attempts to map System RAM with this mapping type will fail.
357+
pub const WC: MemFlags = MemFlags(bindings::MEMREMAP_WC as _);
358+
359+
// Note: Skipping MEMREMAP_ENC/DEC since they are under-documented and have zero
360+
// users outside of arch/x86.
361+
}
362+
363+
/// Represents a non-MMIO memory block. This is like [`IoMem`], but for cases where it is known
364+
/// that the resource being mapped does not have I/O side effects.
365+
// Invariants:
366+
// `ptr` is a non-null and valid address of at least `usize` bytes and returned by a `memremap`
367+
// call.
368+
// ```
369+
pub struct Mem {
370+
ptr: NonNull<core::ffi::c_void>,
371+
size: usize,
372+
}
373+
374+
impl Mem {
375+
/// Tries to create a new instance of a memory block from a Resource.
376+
///
377+
/// The resource described by `res` is mapped into the CPU's address space so that it can be
378+
/// accessed directly. It is also consumed by this function so that it can't be mapped again
379+
/// to a different address.
380+
///
381+
/// If multiple caching flags are specified, the different mapping types will be attempted in
382+
/// the order [`MemFlags::WB`], [`MemFlags::WT`], [`MemFlags::WC`].
383+
///
384+
/// # Flags
385+
///
386+
/// * [`MemFlags::WB`]: Matches the default mapping for System RAM on the architecture.
387+
/// This is usually a read-allocate write-back cache. Moreover, if this flag is specified and
388+
/// the requested remap region is RAM, memremap() will bypass establishing a new mapping and
389+
/// instead return a pointer into the direct map.
390+
///
391+
/// * [`MemFlags::WT`]: Establish a mapping whereby writes either bypass the cache or are written
392+
/// through to memory and never exist in a cache-dirty state with respect to program visibility.
393+
/// Attempts to map System RAM with this mapping type will fail.
394+
/// * [`MemFlags::WC`]: Establish a writecombine mapping, whereby writes may be coalesced together
395+
/// (e.g. in the CPU's write buffers), but is otherwise uncached. Attempts to map System RAM with
396+
/// this mapping type will fail.
397+
///
398+
/// # Safety
399+
///
400+
/// Callers must ensure that either (a) the resulting interface cannot be used to initiate DMA
401+
/// operations, or (b) that DMA operations initiated via the returned interface use DMA handles
402+
/// allocated through the `dma` module.
403+
pub unsafe fn try_new(res: Resource, flags: MemFlags) -> Result<Self> {
404+
let size: usize = res.size.try_into()?;
405+
406+
let addr = unsafe { bindings::memremap(res.start(), size, flags.as_raw()) };
407+
let ptr = NonNull::new(addr).ok_or(ENOMEM)?;
408+
// INVARIANT: `ptr` is non-null and was returned by `ioremap`, so it is valid.
409+
Ok(Self { ptr, size })
410+
}
411+
412+
/// Returns the base address of the memory mapping as a raw pointer.
413+
///
414+
/// It is up to the caller to use this pointer safely, depending on the requirements of the
415+
/// hardware backing this memory block.
416+
pub fn ptr(&self) -> *mut u8 {
417+
self.ptr.cast().as_ptr()
418+
}
419+
420+
/// Returns the size of this mapped memory block.
421+
pub fn size(&self) -> usize {
422+
self.size
423+
}
424+
}
425+
426+
impl Drop for Mem {
427+
fn drop(&mut self) {
428+
// SAFETY: By the type invariant, `self.ptr` is a value returned by a previous successful
429+
// call to `memremap`.
430+
unsafe { bindings::memunmap(self.ptr.as_ptr()) };
431+
}
432+
}

0 commit comments

Comments
 (0)