use crate::registers::{set_mair_el1, set_tcr_el1, set_ttbr0_el1, set_ttbr1_el1};
use crate::PAGE_SIZE;
use ruvix_hal::mmu::{MmuError, PagePermissions};
use ruvix_hal::Mmu as MmuTrait;
const MAIR_DEVICE_nGnRnE: u8 = 0;
const MAIR_NORMAL_WB: u8 = 1;
const MAIR_NORMAL_WT: u8 = 2;
mod pte_flags {
pub const VALID: u64 = 1 << 0;
pub const PAGE: u64 = 1 << 1;
pub const USER: u64 = 1 << 6;
pub const RO: u64 = 1 << 7;
pub const SHAREABLE: u64 = 2 << 8;
pub const AF: u64 = 1 << 10;
pub const XN: u64 = 1 << 54;
pub const PXN: u64 = 1 << 53;
}
pub struct Mmu {
kernel_l0: [u64; 512],
user_l0: [u64; 512],
}
impl Mmu {
pub const fn new() -> Self {
Self {
kernel_l0: [0; 512],
user_l0: [0; 512],
}
}
pub unsafe fn init(&mut self) {
unsafe {
self.configure_mair();
self.configure_tcr();
self.setup_page_tables();
self.install_page_tables();
}
}
unsafe fn configure_mair(&self) {
let mair: u64 = (0x00 << (MAIR_DEVICE_nGnRnE * 8)) | (0xFF << (MAIR_NORMAL_WB * 8)) | (0xBB << (MAIR_NORMAL_WT * 8));
unsafe {
set_mair_el1(mair);
}
}
unsafe fn configure_tcr(&self) {
let tcr: u64 = (16 << 0) | (16 << 16) | (0 << 14) | (2 << 30) | (1 << 8) | (1 << 24) | (1 << 10) | (1 << 26) | (3 << 12) | (3 << 28) | (2 << 32);
unsafe {
set_tcr_el1(tcr);
}
}
unsafe fn setup_page_tables(&mut self) {
}
unsafe fn install_page_tables(&self) {
let ttbr0 = self.user_l0.as_ptr() as u64;
let ttbr1 = self.kernel_l0.as_ptr() as u64;
unsafe {
set_ttbr0_el1(ttbr0);
set_ttbr1_el1(ttbr1);
}
}
fn permissions_to_flags(perms: PagePermissions) -> u64 {
let mut flags = pte_flags::VALID | pte_flags::AF;
if !perms.contains(PagePermissions::WRITE) {
flags |= pte_flags::RO;
}
if !perms.contains(PagePermissions::EXECUTE) {
flags |= pte_flags::XN;
}
if perms.contains(PagePermissions::USER) {
flags |= pte_flags::USER;
} else {
flags |= pte_flags::PXN; }
if perms.contains(PagePermissions::DEVICE) {
flags |= (MAIR_DEVICE_nGnRnE as u64) << 2; } else {
flags |= (MAIR_NORMAL_WB as u64) << 2; }
flags
}
}
impl MmuTrait for Mmu {
fn map_page(
&mut self,
virt: u64,
phys: u64,
perms: PagePermissions,
) -> Result<(), MmuError> {
if virt & (PAGE_SIZE as u64 - 1) != 0 {
return Err(MmuError::NotPageAligned);
}
if phys & (PAGE_SIZE as u64 - 1) != 0 {
return Err(MmuError::NotPageAligned);
}
let _flags = Self::permissions_to_flags(perms);
Ok(())
}
fn unmap_page(&mut self, virt: u64) -> Result<(), MmuError> {
if virt & (PAGE_SIZE as u64 - 1) != 0 {
return Err(MmuError::NotPageAligned);
}
Ok(())
}
fn set_permissions(&mut self, _virt: u64, _perms: PagePermissions) -> Result<(), MmuError> {
if _virt & (PAGE_SIZE as u64 - 1) != 0 {
return Err(MmuError::NotPageAligned);
}
Ok(())
}
fn translate(&self, virt: u64) -> Result<u64, MmuError> {
Err(MmuError::NotMapped)
}
fn get_page_entry(&self, virt: u64) -> Result<ruvix_hal::mmu::PageEntry, MmuError> {
if virt & (PAGE_SIZE as u64 - 1) != 0 {
return Err(MmuError::NotPageAligned);
}
Err(MmuError::NotMapped)
}
fn flush_tlb(&mut self) {
unsafe {
core::arch::asm!(
"tlbi vmalle1is", "dsb ish", "isb", options(nostack, nomem, preserves_flags)
);
}
}
}
impl Default for Mmu {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mair_values() {
assert_eq!(MAIR_DEVICE_nGnRnE, 0);
assert_eq!(MAIR_NORMAL_WB, 1);
assert_eq!(MAIR_NORMAL_WT, 2);
}
#[test]
fn test_permissions_to_flags() {
let read_only = PagePermissions::READ;
let flags = Mmu::permissions_to_flags(read_only);
assert!(flags & pte_flags::RO != 0);
assert!(flags & pte_flags::XN != 0);
let read_write_exec = PagePermissions::READ | PagePermissions::WRITE | PagePermissions::EXECUTE;
let flags = Mmu::permissions_to_flags(read_write_exec);
assert!(flags & pte_flags::RO == 0);
assert!(flags & pte_flags::XN == 0);
}
}