You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
269 lines
8.0 KiB
Rust
269 lines
8.0 KiB
Rust
5 years ago
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||
|
//
|
||
3 years ago
|
// Copyright (c) 2020-2022 Andre Richter <andre.o.richter@gmail.com>
|
||
5 years ago
|
|
||
|
//! Memory Management Unit.
|
||
|
//!
|
||
|
//! In order to decouple `BSP` and `arch` parts of the MMU code (to keep them pluggable), this file
|
||
4 years ago
|
//! provides types for composing an architecture-agnostic description of the kernel's virtual memory
|
||
|
//! layout.
|
||
5 years ago
|
//!
|
||
|
//! The `BSP` provides such a description through the `bsp::memory::mmu::virt_mem_layout()`
|
||
|
//! function.
|
||
|
//!
|
||
|
//! The `MMU` driver of the `arch` code uses `bsp::memory::mmu::virt_mem_layout()` to compile and
|
||
4 years ago
|
//! install respective translation tables.
|
||
5 years ago
|
|
||
|
#[cfg(target_arch = "aarch64")]
|
||
|
#[path = "../_arch/aarch64/memory/mmu.rs"]
|
||
|
mod arch_mmu;
|
||
4 years ago
|
|
||
|
mod translation_table;
|
||
5 years ago
|
|
||
|
use core::{fmt, ops::RangeInclusive};
|
||
|
|
||
4 years ago
|
//--------------------------------------------------------------------------------------------------
|
||
|
// Architectural Public Reexports
|
||
|
//--------------------------------------------------------------------------------------------------
|
||
|
pub use arch_mmu::mmu;
|
||
|
|
||
5 years ago
|
//--------------------------------------------------------------------------------------------------
|
||
|
// Public Definitions
|
||
|
//--------------------------------------------------------------------------------------------------
|
||
|
|
||
4 years ago
|
/// MMU enable errors variants.
|
||
|
#[allow(missing_docs)]
|
||
|
#[derive(Debug)]
|
||
|
pub enum MMUEnableError {
|
||
|
AlreadyEnabled,
|
||
|
Other(&'static str),
|
||
|
}
|
||
|
|
||
5 years ago
|
/// Memory Management interfaces.
|
||
|
pub mod interface {
|
||
4 years ago
|
use super::*;
|
||
5 years ago
|
|
||
|
/// MMU functions.
|
||
|
pub trait MMU {
|
||
4 years ago
|
/// Called by the kernel during early init. Supposed to take the translation tables from the
|
||
5 years ago
|
/// `BSP`-supplied `virt_mem_layout()` and install/activate them for the respective MMU.
|
||
|
///
|
||
|
/// # Safety
|
||
|
///
|
||
|
/// - Changes the HW's global state.
|
||
4 years ago
|
unsafe fn enable_mmu_and_caching(&self) -> Result<(), MMUEnableError>;
|
||
|
|
||
|
/// Returns true if the MMU is enabled, false otherwise.
|
||
|
fn is_enabled(&self) -> bool;
|
||
5 years ago
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
/// Describes the characteristics of a translation granule.
|
||
|
pub struct TranslationGranule<const GRANULE_SIZE: usize>;
|
||
|
|
||
4 years ago
|
/// Describes properties of an address space.
|
||
|
pub struct AddressSpace<const AS_SIZE: usize>;
|
||
4 years ago
|
|
||
5 years ago
|
/// Architecture agnostic translation types.
|
||
|
#[allow(missing_docs)]
|
||
|
#[derive(Copy, Clone)]
|
||
|
pub enum Translation {
|
||
|
Identity,
|
||
|
Offset(usize),
|
||
|
}
|
||
|
|
||
|
/// Architecture agnostic memory attributes.
|
||
|
#[allow(missing_docs)]
|
||
|
#[derive(Copy, Clone)]
|
||
|
pub enum MemAttributes {
|
||
|
CacheableDRAM,
|
||
|
Device,
|
||
|
}
|
||
|
|
||
|
/// Architecture agnostic access permissions.
|
||
|
#[allow(missing_docs)]
|
||
|
#[derive(Copy, Clone)]
|
||
|
pub enum AccessPermissions {
|
||
|
ReadOnly,
|
||
|
ReadWrite,
|
||
|
}
|
||
|
|
||
|
/// Collection of memory attributes.
|
||
|
#[allow(missing_docs)]
|
||
|
#[derive(Copy, Clone)]
|
||
|
pub struct AttributeFields {
|
||
|
pub mem_attributes: MemAttributes,
|
||
|
pub acc_perms: AccessPermissions,
|
||
|
pub execute_never: bool,
|
||
|
}
|
||
|
|
||
|
/// Architecture agnostic descriptor for a memory range.
|
||
|
#[allow(missing_docs)]
|
||
4 years ago
|
pub struct TranslationDescriptor {
|
||
5 years ago
|
pub name: &'static str,
|
||
|
pub virtual_range: fn() -> RangeInclusive<usize>,
|
||
4 years ago
|
pub physical_range_translation: Translation,
|
||
5 years ago
|
pub attribute_fields: AttributeFields,
|
||
|
}
|
||
|
|
||
|
/// Type for expressing the kernel's virtual memory layout.
|
||
|
pub struct KernelVirtualLayout<const NUM_SPECIAL_RANGES: usize> {
|
||
|
/// The last (inclusive) address of the address space.
|
||
|
max_virt_addr_inclusive: usize,
|
||
|
|
||
|
/// Array of descriptors for non-standard (normal cacheable DRAM) memory regions.
|
||
4 years ago
|
inner: [TranslationDescriptor; NUM_SPECIAL_RANGES],
|
||
5 years ago
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------
|
||
|
// Public Code
|
||
|
//--------------------------------------------------------------------------------------------------
|
||
|
|
||
4 years ago
|
impl fmt::Display for MMUEnableError {
|
||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
|
match self {
|
||
|
MMUEnableError::AlreadyEnabled => write!(f, "MMU is already enabled"),
|
||
|
MMUEnableError::Other(x) => write!(f, "{}", x),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
impl<const GRANULE_SIZE: usize> TranslationGranule<GRANULE_SIZE> {
|
||
|
/// The granule's size.
|
||
|
pub const SIZE: usize = Self::size_checked();
|
||
|
|
||
|
/// The granule's shift, aka log2(size).
|
||
|
pub const SHIFT: usize = Self::SIZE.trailing_zeros() as usize;
|
||
|
|
||
|
const fn size_checked() -> usize {
|
||
|
assert!(GRANULE_SIZE.is_power_of_two());
|
||
|
|
||
|
GRANULE_SIZE
|
||
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
impl<const AS_SIZE: usize> AddressSpace<AS_SIZE> {
|
||
4 years ago
|
/// The address space size.
|
||
|
pub const SIZE: usize = Self::size_checked();
|
||
|
|
||
|
/// The address space shift, aka log2(size).
|
||
4 years ago
|
pub const SIZE_SHIFT: usize = Self::SIZE.trailing_zeros() as usize;
|
||
4 years ago
|
|
||
|
const fn size_checked() -> usize {
|
||
|
assert!(AS_SIZE.is_power_of_two());
|
||
|
|
||
4 years ago
|
// Check for architectural restrictions as well.
|
||
|
Self::arch_address_space_size_sanity_checks();
|
||
4 years ago
|
|
||
|
AS_SIZE
|
||
|
}
|
||
|
}
|
||
|
|
||
5 years ago
|
impl Default for AttributeFields {
|
||
|
fn default() -> AttributeFields {
|
||
|
AttributeFields {
|
||
|
mem_attributes: MemAttributes::CacheableDRAM,
|
||
|
acc_perms: AccessPermissions::ReadWrite,
|
||
|
execute_never: true,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
/// Human-readable output of a TranslationDescriptor.
|
||
|
impl fmt::Display for TranslationDescriptor {
|
||
5 years ago
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||
|
// Call the function to which self.range points, and dereference the result, which causes
|
||
|
// Rust to copy the value.
|
||
|
let start = *(self.virtual_range)().start();
|
||
|
let end = *(self.virtual_range)().end();
|
||
|
let size = end - start + 1;
|
||
|
|
||
|
// log2(1024).
|
||
|
const KIB_RSHIFT: u32 = 10;
|
||
|
|
||
|
// log2(1024 * 1024).
|
||
|
const MIB_RSHIFT: u32 = 20;
|
||
|
|
||
|
let (size, unit) = if (size >> MIB_RSHIFT) > 0 {
|
||
|
(size >> MIB_RSHIFT, "MiB")
|
||
|
} else if (size >> KIB_RSHIFT) > 0 {
|
||
|
(size >> KIB_RSHIFT, "KiB")
|
||
|
} else {
|
||
|
(size, "Byte")
|
||
|
};
|
||
|
|
||
|
let attr = match self.attribute_fields.mem_attributes {
|
||
|
MemAttributes::CacheableDRAM => "C",
|
||
|
MemAttributes::Device => "Dev",
|
||
|
};
|
||
|
|
||
|
let acc_p = match self.attribute_fields.acc_perms {
|
||
|
AccessPermissions::ReadOnly => "RO",
|
||
|
AccessPermissions::ReadWrite => "RW",
|
||
|
};
|
||
|
|
||
|
let xn = if self.attribute_fields.execute_never {
|
||
|
"PXN"
|
||
|
} else {
|
||
|
"PX"
|
||
|
};
|
||
|
|
||
|
write!(
|
||
|
f,
|
||
|
" {:#010x} - {:#010x} | {: >3} {} | {: <3} {} {: <3} | {}",
|
||
|
start, end, size, unit, attr, acc_p, xn, self.name
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<const NUM_SPECIAL_RANGES: usize> KernelVirtualLayout<{ NUM_SPECIAL_RANGES }> {
|
||
|
/// Create a new instance.
|
||
4 years ago
|
pub const fn new(max: usize, layout: [TranslationDescriptor; NUM_SPECIAL_RANGES]) -> Self {
|
||
5 years ago
|
Self {
|
||
|
max_virt_addr_inclusive: max,
|
||
|
inner: layout,
|
||
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
/// For a virtual address, find and return the physical output address and corresponding
|
||
|
/// attributes.
|
||
5 years ago
|
///
|
||
|
/// If the address is not found in `inner`, return an identity mapped default with normal
|
||
|
/// cacheable DRAM attributes.
|
||
4 years ago
|
pub fn virt_addr_properties(
|
||
5 years ago
|
&self,
|
||
|
virt_addr: usize,
|
||
|
) -> Result<(usize, AttributeFields), &'static str> {
|
||
|
if virt_addr > self.max_virt_addr_inclusive {
|
||
|
return Err("Address out of range");
|
||
|
}
|
||
|
|
||
|
for i in self.inner.iter() {
|
||
|
if (i.virtual_range)().contains(&virt_addr) {
|
||
4 years ago
|
let output_addr = match i.physical_range_translation {
|
||
5 years ago
|
Translation::Identity => virt_addr,
|
||
|
Translation::Offset(a) => a + (virt_addr - (i.virtual_range)().start()),
|
||
|
};
|
||
|
|
||
|
return Ok((output_addr, i.attribute_fields));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Ok((virt_addr, AttributeFields::default()))
|
||
|
}
|
||
|
|
||
|
/// Print the memory layout.
|
||
|
pub fn print_layout(&self) {
|
||
|
use crate::info;
|
||
|
|
||
|
for i in self.inner.iter() {
|
||
|
info!("{}", i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[cfg(test)]
|
||
4 years ago
|
pub fn inner(&self) -> &[TranslationDescriptor; NUM_SPECIAL_RANGES] {
|
||
5 years ago
|
&self.inner
|
||
|
}
|
||
|
}
|