|
6 | 6 |
|
7 | 7 | #![allow(dead_code)] |
8 | 8 |
|
| 9 | +use crate::types::declare_flags_type; |
9 | 10 | use crate::{addr::*, bindings, error::code::*, error::Result}; |
| 11 | + |
10 | 12 | use core::convert::TryInto; |
11 | 13 | use core::ptr::NonNull; |
12 | 14 |
|
@@ -54,7 +56,7 @@ impl Resource { |
54 | 56 | } |
55 | 57 | } |
56 | 58 |
|
57 | | -/// Represents a memory block of at least `SIZE` bytes. |
| 59 | +/// Represents an MMIO memory block of at least `SIZE` bytes. |
58 | 60 | /// |
59 | 61 | /// # Invariants |
60 | 62 | /// |
@@ -327,3 +329,104 @@ impl<const SIZE: usize> Drop for IoMem<SIZE> { |
327 | 329 | unsafe { bindings::iounmap(self.ptr as _) }; |
328 | 330 | } |
329 | 331 | } |
| 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