// SPDX-License-Identifier: MIT // // Copyright (c) 2018-2019 Andre Richter //! Memory Management. use core::{fmt, ops::RangeInclusive}; #[derive(Copy, Clone)] pub enum Translation { Identity, Offset(usize), } #[derive(Copy, Clone)] pub enum MemAttributes { CacheableDRAM, Device, } #[derive(Copy, Clone)] pub enum AccessPermissions { ReadOnly, ReadWrite, } #[derive(Copy, Clone)] pub struct AttributeFields { pub mem_attributes: MemAttributes, pub acc_perms: AccessPermissions, pub execute_never: bool, } impl Default for AttributeFields { fn default() -> AttributeFields { AttributeFields { mem_attributes: MemAttributes::CacheableDRAM, acc_perms: AccessPermissions::ReadWrite, execute_never: true, } } } /// An architecture agnostic descriptor for a memory range. pub struct RangeDescriptor { pub name: &'static str, pub virtual_range: fn() -> RangeInclusive, pub translation: Translation, pub attribute_fields: AttributeFields, } /// Human-readable output of a RangeDescriptor. impl fmt::Display for RangeDescriptor { 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 ) } } /// Type for expressing the kernel's virtual memory layout. pub struct KernelVirtualLayout { max_virt_addr_inclusive: usize, inner: [RangeDescriptor; NUM_SPECIAL_RANGES], } impl KernelVirtualLayout<{ NUM_SPECIAL_RANGES }> { pub const fn new(max: usize, layout: [RangeDescriptor; NUM_SPECIAL_RANGES]) -> Self { Self { max_virt_addr_inclusive: max, inner: layout, } } /// For a virtual address, find and return the output address and corresponding attributes. /// /// If the address is not found in `inner`, return an identity mapped default with normal /// cacheable DRAM attributes. pub fn get_virt_addr_properties( &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) { let output_addr = match i.translation { 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::println; for i in self.inner.iter() { println!("{}", i); } } }