Files
RedBear-OS/common/src/lib.rs
T
Kamil Koczurek d2c82add9d Move Io/Pio/Mmio from redox_syscall to common
* Fix Mmio repr from transparent to packed.
* Rename Mmio::from to Mmio::new.
* Remove dead imports around affected code.
* Minor formatting.
2024-09-30 13:01:36 +02:00

259 lines
8.4 KiB
Rust

//! This crate provides various abstractions for use by all drivers in the Redox drivers repo.
//!
//! This includes direct memory access via [dma], and Scatter-Gather List support via [sgl]. It also
//! provides various memory management structures for use with drivers, and some logging support.
#![feature(int_roundings)]
#![warn(missing_docs)]
use libredox::call::MmapArgs;
use libredox::flag::{self, O_CLOEXEC, O_RDONLY, O_RDWR, O_WRONLY};
use libredox::{errno::EINVAL, error::*, Fd};
use syscall::PAGE_SIZE;
/// The Direct Memory Access (DMA) API for drivers
pub mod dma;
/// MMIO utilities
pub mod io;
mod logger;
/// The Scatter Gather List (SGL) API for drivers.
pub mod sgl;
pub use logger::setup_logging;
/// Specifies the write behavior for a specific region of memory
///
/// These types indicate to the driver how writes to a specific memory region are handled by the
/// system. This usually refers to the caching behavior that the processor or I/O device responsible
/// for that memory implements.
///
/// aarch64 and x86 have very different cache-coherency rules, so this API as written is likely
/// not sufficient to describe the memory caching behavior in a cross-platform manner. As such,
/// consider this API unstable.
#[derive(Clone, Copy, Debug)]
pub enum MemoryType {
/// A region of memory that implements Write-back caching.
///
/// In write-back caching, the processor will first store data in its local cache, and then
/// flush it to the actual storage location at regular intervals, or as applications access
/// the data.
Writeback,
/// A region of memory that does not implement caching. Writes to these regions are immediate.
Uncacheable,
/// A region of memory that implements write combining.
///
/// Write combining memory regions store all writes in a temporary buffer called a Write
/// Combine Buffer. Multiple writes to the location are stored in a single buffer, and then
/// released to the memory location in an unspecified order. Write-Combine memory does not
/// guarantee that the order at which you write to it is the order at which those writes are
/// committed to memory.
WriteCombining,
/// Memory stored in an intermediate Write Combine Buffer and released later
/// Memory-Mapped I/O. This is an aarch64-specific term.
DeviceMemory,
}
impl Default for MemoryType {
fn default() -> Self {
Self::Writeback
}
}
/// Represents the protection level of an area of memory.
///
/// This structure shouldn't be used directly -- instead, use the [Prot::RO] (Read-Only),
/// [Prot::WO] (Write-Only) and [Prot::RW] (Read-Write) constants to specify the memory's protection
/// level.
#[derive(Clone, Copy, Debug)]
pub struct Prot {
/// The memory is readable
pub read: bool,
/// The memory is writeable
pub write: bool,
}
/// Implements the memory protection level constants
impl Prot {
/// A constant representing Read-Only memory.
pub const RO: Self = Self {
read: true,
write: false,
};
/// A constant representing Write-Only memory
pub const WO: Self = Self {
read: false,
write: true,
};
/// A constant representing Read-Write memory
pub const RW: Self = Self {
read: true,
write: true,
};
}
// TODO: Safe, as the kernel ensures it doesn't conflict with any other memory described in the
// memory map for regular RAM.
/// Maps physical memory to virtual memory
///
/// # Arguments
///
/// * 'base_phys: [usize]' - The base address of the physical memory to map.
/// * 'len: [usize]' - The length of the physical memory to map (Should be a multiple of [PAGE_SIZE]
/// * '_: [Prot]' - The memory protection level of the mapping.
/// * 'type: [MemoryType]' - The caching behavior specification of the memory.
///
/// # Returns
///
/// A '[Result]<*mut ()>' which is:
/// - '[Ok]' containing a raw pointer to the mapped memory.
/// - '[Err]' which contains an error on failure.
///
/// # Errors
///
/// This function will return an error if:
/// - An invalid value is provided to 'read' or 'write'
/// - The system could not open a file descriptor to the memory scheme for the specified [MemoryType].
/// - The system failed to map the physical address to a virtual address. See [libredox::call::mmap]
///
///
/// # Notes
/// - This function is unsafe, and upon using it you will be responsible for freeing the memory with
/// [libredox::call::munmap]. If you want a safe accessor, use [PhysBorrowed] instead.
/// - The MemoryType specified is used to tell the function which memory scheme to access. (i.e
/// /scheme/memory/physical@wb, /scheme/memory/physical@uc, etc).
pub unsafe fn physmap(
base_phys: usize,
len: usize,
Prot { read, write }: Prot,
ty: MemoryType,
) -> Result<*mut ()> {
// TODO: arraystring?
//Return an error rather than potentially crash the kernel.
if base_phys == 0 {
return Err(Error::new(EINVAL));
}
let path = format!(
"/scheme/memory/physical@{}",
match ty {
MemoryType::Writeback => "wb",
MemoryType::Uncacheable => "uc",
MemoryType::WriteCombining => "wc",
MemoryType::DeviceMemory => "dev",
}
);
let mode = match (read, write) {
(true, true) => O_RDWR,
(true, false) => O_RDONLY,
(false, true) => O_WRONLY,
(false, false) => return Err(Error::new(EINVAL)),
};
let mut prot = 0;
if read {
prot |= flag::PROT_READ;
}
if write {
prot |= flag::PROT_WRITE;
}
let fd = Fd::open(&path, O_CLOEXEC | mode, 0)?;
Ok(libredox::call::mmap(MmapArgs {
fd: fd.raw(),
offset: base_phys as u64,
length: len.next_multiple_of(PAGE_SIZE),
flags: flag::MAP_SHARED,
prot,
addr: core::ptr::null_mut(),
})? as *mut ())
}
impl std::fmt::Display for MemoryType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Writeback => "wb",
Self::Uncacheable => "uc",
Self::WriteCombining => "wc",
Self::DeviceMemory => "dev",
}
)
}
}
/// A safe virtual mapping to physical memory that unmaps the memory when the structure goes out
/// of scope.
///
/// This function provides a safe binding to [physmap]. It implements Drop to free the mapped memory
/// when the structure goes out of scope.
pub struct PhysBorrowed {
mem: *mut (),
len: usize,
}
impl PhysBorrowed {
/// Constructs a PhysBorrowed instance.
///
/// # Arguments
/// See [physmap] for a description of the parameters.
///
/// # Returns
/// A '[Result]' which contains the following:
/// - A '[PhysBorrowed]' which represents the newly mapped region.
/// - An 'Err' if a memory mapping error occurs.
///
/// # Errors
/// See [physmap] for a description of the error cases.
pub fn map(base_phys: usize, len: usize, prot: Prot, ty: MemoryType) -> Result<Self> {
let mem = unsafe { physmap(base_phys, len, prot, ty)? };
Ok(Self {
mem,
len: len.next_multiple_of(PAGE_SIZE),
})
}
/// Gets a raw pointer to the borrowed region.
///
/// # Returns
/// - self.mem - A pointer to the mapped region in virtual memory.
///
/// # Notes
/// - The pointer may live beyond the lifetime of [PhysBorrowed], so dereferences to the pointer
/// must be treated as unsafe.
///
pub fn as_ptr(&self) -> *mut () {
self.mem
}
/// Gets the length of the mapped region.
///
/// # Returns
/// - self.len - The length of the mapped region. It should be a multiple of [PAGE_SIZE]
pub fn mapped_len(&self) -> usize {
self.len
}
}
impl Drop for PhysBorrowed {
/// Frees the mapped memory region.
fn drop(&mut self) {
unsafe {
let _ = libredox::call::munmap(self.mem, self.len);
}
}
}
/// Uses the [syscall::iopl] system call to set the I/O privilege level of the current process
/// to 3.
///
/// In Redox, x86 privilege ring 3 represents userspace. Most Redox drivers run in userspace to
/// prevent system instability caused by a faulty driver. Processes with ring 3 IOPL have access to
/// I/O ports.
pub fn acquire_port_io_rights() -> Result<()> {
unsafe {
syscall::iopl(3)?;
}
Ok(())
}