// SPDX-License-Identifier: MIT OR Apache-2.0 // // Copyright (c) 2020-2022 Andre Richter //! A record of mapped pages. use super::{ AccessPermissions, Address, AttributeFields, MMIODescriptor, MemAttributes, MemoryRegion, Physical, Virtual, }; use crate::{bsp, common, info, synchronization, synchronization::InitStateLock, warn}; //-------------------------------------------------------------------------------------------------- // Private Definitions //-------------------------------------------------------------------------------------------------- /// Type describing a virtual memory mapping. #[allow(missing_docs)] #[derive(Copy, Clone)] struct MappingRecordEntry { pub users: [Option<&'static str>; 5], pub phys_start_addr: Address, pub virt_start_addr: Address, pub num_pages: usize, pub attribute_fields: AttributeFields, } struct MappingRecord { inner: [Option; 12], } //-------------------------------------------------------------------------------------------------- // Global instances //-------------------------------------------------------------------------------------------------- static KERNEL_MAPPING_RECORD: InitStateLock = InitStateLock::new(MappingRecord::new()); //-------------------------------------------------------------------------------------------------- // Private Code //-------------------------------------------------------------------------------------------------- impl MappingRecordEntry { pub fn new( name: &'static str, virt_region: &MemoryRegion, phys_region: &MemoryRegion, attr: &AttributeFields, ) -> Self { Self { users: [Some(name), None, None, None, None], phys_start_addr: phys_region.start_addr(), virt_start_addr: virt_region.start_addr(), num_pages: phys_region.num_pages(), attribute_fields: *attr, } } fn find_next_free_user(&mut self) -> Result<&mut Option<&'static str>, &'static str> { if let Some(x) = self.users.iter_mut().find(|x| x.is_none()) { return Ok(x); }; Err("Storage for user info exhausted") } pub fn add_user(&mut self, user: &'static str) -> Result<(), &'static str> { let x = self.find_next_free_user()?; *x = Some(user); Ok(()) } } impl MappingRecord { pub const fn new() -> Self { Self { inner: [None; 12] } } fn size(&self) -> usize { self.inner.iter().filter(|x| x.is_some()).count() } fn sort(&mut self) { let upper_bound_exclusive = self.size(); let entries = &mut self.inner[0..upper_bound_exclusive]; if !entries.is_sorted_by_key(|item| item.unwrap().virt_start_addr) { entries.sort_unstable_by_key(|item| item.unwrap().virt_start_addr) } } fn find_next_free(&mut self) -> Result<&mut Option, &'static str> { if let Some(x) = self.inner.iter_mut().find(|x| x.is_none()) { return Ok(x); } Err("Storage for mapping info exhausted") } fn find_duplicate( &mut self, phys_region: &MemoryRegion, ) -> Option<&mut MappingRecordEntry> { self.inner .iter_mut() .filter_map(|x| x.as_mut()) .filter(|x| x.attribute_fields.mem_attributes == MemAttributes::Device) .find(|x| { if x.phys_start_addr != phys_region.start_addr() { return false; } if x.num_pages != phys_region.num_pages() { return false; } true }) } pub fn add( &mut self, name: &'static str, virt_region: &MemoryRegion, phys_region: &MemoryRegion, attr: &AttributeFields, ) -> Result<(), &'static str> { let x = self.find_next_free()?; *x = Some(MappingRecordEntry::new( name, virt_region, phys_region, attr, )); self.sort(); Ok(()) } pub fn print(&self) { info!(" -------------------------------------------------------------------------------------------------------------------------------------------"); info!( " {:^44} {:^30} {:^7} {:^9} {:^35}", "Virtual", "Physical", "Size", "Attr", "Entity" ); info!(" -------------------------------------------------------------------------------------------------------------------------------------------"); for i in self.inner.iter().flatten() { let size = i.num_pages * bsp::memory::mmu::KernelGranule::SIZE; let virt_start = i.virt_start_addr; let virt_end_inclusive = virt_start + (size - 1); let phys_start = i.phys_start_addr; let phys_end_inclusive = phys_start + (size - 1); let (size, unit) = common::size_human_readable_ceil(size); let attr = match i.attribute_fields.mem_attributes { MemAttributes::CacheableDRAM => "C", MemAttributes::Device => "Dev", }; let acc_p = match i.attribute_fields.acc_perms { AccessPermissions::ReadOnly => "RO", AccessPermissions::ReadWrite => "RW", }; let xn = if i.attribute_fields.execute_never { "XN" } else { "X" }; info!( " {}..{} --> {}..{} | {:>3} {} | {:<3} {} {:<2} | {}", virt_start, virt_end_inclusive, phys_start, phys_end_inclusive, size, unit, attr, acc_p, xn, i.users[0].unwrap() ); for k in i.users[1..].iter() { if let Some(additional_user) = *k { info!( " | {}", additional_user ); } } } info!(" -------------------------------------------------------------------------------------------------------------------------------------------"); } } //-------------------------------------------------------------------------------------------------- // Public Code //-------------------------------------------------------------------------------------------------- use synchronization::interface::ReadWriteEx; /// Add an entry to the mapping info record. pub fn kernel_add( name: &'static str, virt_region: &MemoryRegion, phys_region: &MemoryRegion, attr: &AttributeFields, ) -> Result<(), &'static str> { KERNEL_MAPPING_RECORD.write(|mr| mr.add(name, virt_region, phys_region, attr)) } pub fn kernel_find_and_insert_mmio_duplicate( mmio_descriptor: &MMIODescriptor, new_user: &'static str, ) -> Option> { let phys_region: MemoryRegion = (*mmio_descriptor).into(); KERNEL_MAPPING_RECORD.write(|mr| { let dup = mr.find_duplicate(&phys_region)?; if let Err(x) = dup.add_user(new_user) { warn!("{}", x); } Some(dup.virt_start_addr) }) } /// Human-readable print of all recorded kernel mappings. pub fn kernel_print() { KERNEL_MAPPING_RECORD.read(|mr| mr.print()); }