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.

225 lines
7.3 KiB
Rust

// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2020 Andre Richter <andre.o.richter@gmail.com>
//! A record of mapped pages.
use super::{
AccessPermissions, Address, AttributeFields, MMIODescriptor, MemAttributes,
PageSliceDescriptor, Physical, Virtual,
};
use crate::{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_pages: PageSliceDescriptor<Physical>,
pub virt_start_addr: Address<Virtual>,
pub attribute_fields: AttributeFields,
}
struct MappingRecord {
inner: [Option<MappingRecordEntry>; 12],
}
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static KERNEL_MAPPING_RECORD: InitStateLock<MappingRecord> =
InitStateLock::new(MappingRecord::new());
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl MappingRecordEntry {
pub fn new(
name: &'static str,
phys_pages: &PageSliceDescriptor<Physical>,
virt_pages: &PageSliceDescriptor<Virtual>,
attr: &AttributeFields,
) -> Self {
Self {
users: [Some(name), None, None, None, None],
phys_pages: *phys_pages,
virt_start_addr: virt_pages.start_addr(),
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 find_next_free(&mut self) -> Result<&mut Option<MappingRecordEntry>, &'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_pages: &PageSliceDescriptor<Physical>,
) -> Option<&mut MappingRecordEntry> {
self.inner
.iter_mut()
.filter(|x| x.is_some())
.map(|x| x.as_mut().unwrap())
.filter(|x| x.attribute_fields.mem_attributes == MemAttributes::Device)
.find(|x| x.phys_pages == *phys_pages)
}
pub fn add(
&mut self,
name: &'static str,
phys_pages: &PageSliceDescriptor<Physical>,
virt_pages: &PageSliceDescriptor<Virtual>,
attr: &AttributeFields,
) -> Result<(), &'static str> {
let x = self.find_next_free()?;
*x = Some(MappingRecordEntry::new(name, phys_pages, virt_pages, attr));
Ok(())
}
pub fn print(&self) {
const KIB_RSHIFT: u32 = 10; // log2(1024).
const MIB_RSHIFT: u32 = 20; // log2(1024 * 1024).
info!(" -----------------------------------------------------------------------------------------------------------------");
info!(
" {:^24} {:^24} {:^7} {:^9} {:^35}",
"Virtual", "Physical", "Size", "Attr", "Entity"
);
info!(" -----------------------------------------------------------------------------------------------------------------");
for i in self
.inner
.iter()
.filter(|x| x.is_some())
.map(|x| x.unwrap())
{
let virt_start = i.virt_start_addr.into_usize();
let virt_end_inclusive = virt_start + i.phys_pages.size() - 1;
let phys_start = i.phys_pages.start_addr().into_usize();
let phys_end_inclusive = i.phys_pages.end_addr_inclusive().into_usize();
let size = i.phys_pages.size();
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 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!(
" {:#011X}..{:#011X} --> {:#011X}..{:#011X} | \
{: >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,
phys_pages: &PageSliceDescriptor<Physical>,
virt_pages: &PageSliceDescriptor<Virtual>,
attr: &AttributeFields,
) -> Result<(), &'static str> {
let mut m = &KERNEL_MAPPING_RECORD;
m.write(|mr| mr.add(name, phys_pages, virt_pages, attr))
}
pub fn kernel_find_and_insert_mmio_duplicate(
phys_mmio_descriptor: &MMIODescriptor<Physical>,
new_user: &'static str,
) -> Option<Address<Virtual>> {
let phys_pages: PageSliceDescriptor<Physical> = phys_mmio_descriptor.clone().into();
let mut m = &KERNEL_MAPPING_RECORD;
m.write(|mr| {
let dup = mr.find_duplicate(&phys_pages)?;
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() {
let mut m = &KERNEL_MAPPING_RECORD;
m.read(|mr| mr.print());
}