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.

138 lines
5.3 KiB
Rust

// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2021-2022 Andre Richter <andre.o.richter@gmail.com>
//! Translation table.
#[cfg(target_arch = "aarch64")]
#[path = "../../_arch/aarch64/memory/mmu/translation_table.rs"]
mod arch_translation_table;
use super::{AttributeFields, MemoryRegion};
use crate::memory::{Address, Physical, Virtual};
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
#[cfg(target_arch = "aarch64")]
pub use arch_translation_table::FixedSizeTranslationTable;
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Translation table interfaces.
pub mod interface {
use crate::memory::mmu::PageAddress;
use super::*;
/// Translation table operations.
pub trait TranslationTable {
/// Anything that needs to run before any of the other provided functions can be used.
///
/// # Safety
///
/// - Implementor must ensure that this function can run only once or is harmless if invoked
/// multiple times.
fn init(&mut self) -> Result<(), &'static str>;
/// Map the given virtual memory region to the given physical memory region.
///
/// # Safety
///
/// - Using wrong attributes can cause multiple issues of different nature in the system.
/// - It is not required that the architectural implementation prevents aliasing. That is,
/// mapping to the same physical memory using multiple virtual addresses, which would
/// break Rust's ownership assumptions. This should be protected against in the kernel's
/// generic MMU code.
unsafe fn map_at(
&mut self,
virt_region: &MemoryRegion<Virtual>,
phys_region: &MemoryRegion<Physical>,
attr: &AttributeFields,
) -> Result<(), &'static str>;
/// Try to translate a virtual page address to a physical page address.
///
/// Will only succeed if there exists a valid mapping for the input page.
fn try_virt_page_addr_to_phys_page_addr(
&self,
virt_page_addr: PageAddress<Virtual>,
) -> Result<PageAddress<Physical>, &'static str>;
/// Try to get the attributes of a page.
///
/// Will only succeed if there exists a valid mapping for the input page.
fn try_page_attributes(
&self,
virt_page_addr: PageAddress<Virtual>,
) -> Result<AttributeFields, &'static str>;
/// Try to translate a virtual address to a physical address.
///
/// Will only succeed if there exists a valid mapping for the input address.
fn try_virt_addr_to_phys_addr(
&self,
virt_addr: Address<Virtual>,
) -> Result<Address<Physical>, &'static str>;
}
}
//--------------------------------------------------------------------------------------------------
// Testing
//--------------------------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
use super::*;
use crate::memory::mmu::{AccessPermissions, MemAttributes, PageAddress};
use arch_translation_table::MinSizeTranslationTable;
use interface::TranslationTable;
use test_macros::kernel_test;
/// Sanity checks for the TranslationTable implementation.
#[kernel_test]
fn translationtable_implementation_sanity() {
// This will occupy a lot of space on the stack.
let mut tables = MinSizeTranslationTable::new_for_runtime();
assert_eq!(tables.init(), Ok(()));
let virt_end_exclusive_page_addr: PageAddress<Virtual> = PageAddress::MAX;
let virt_start_page_addr: PageAddress<Virtual> =
virt_end_exclusive_page_addr.checked_offset(-5).unwrap();
let phys_start_page_addr: PageAddress<Physical> = PageAddress::from(0);
let phys_end_exclusive_page_addr: PageAddress<Physical> =
phys_start_page_addr.checked_offset(5).unwrap();
let virt_region = MemoryRegion::new(virt_start_page_addr, virt_end_exclusive_page_addr);
let phys_region = MemoryRegion::new(phys_start_page_addr, phys_end_exclusive_page_addr);
let attr = AttributeFields {
mem_attributes: MemAttributes::CacheableDRAM,
acc_perms: AccessPermissions::ReadWrite,
execute_never: true,
};
unsafe { assert_eq!(tables.map_at(&virt_region, &phys_region, &attr), Ok(())) };
assert_eq!(
tables.try_virt_page_addr_to_phys_page_addr(virt_start_page_addr),
Ok(phys_start_page_addr)
);
assert_eq!(
tables.try_page_attributes(virt_start_page_addr.checked_offset(-1).unwrap()),
Err("Page marked invalid")
);
assert_eq!(tables.try_page_attributes(virt_start_page_addr), Ok(attr));
let virt_addr = virt_start_page_addr.into_inner() + 0x100;
let phys_addr = phys_start_page_addr.into_inner() + 0x100;
assert_eq!(tables.try_virt_addr_to_phys_addr(virt_addr), Ok(phys_addr));
}
}