From 9bebfb49bcbd886d80464bc656d174ed9d81cdd6 Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 17 Jun 2019 03:32:10 +0200 Subject: [PATCH] begin MMU implementation --- src/cortex_a9/mmu.rs | 378 ++++++++++++++++++++++++++++++++++++++++++ src/cortex_a9/mod.rs | 1 + src/cortex_a9/regs.rs | 24 ++- src/main.rs | 27 +-- 4 files changed, 408 insertions(+), 22 deletions(-) create mode 100644 src/cortex_a9/mmu.rs diff --git a/src/cortex_a9/mmu.rs b/src/cortex_a9/mmu.rs new file mode 100644 index 0000000..62902e9 --- /dev/null +++ b/src/cortex_a9/mmu.rs @@ -0,0 +1,378 @@ +use core::mem::uninitialized; +use bit_field::BitField; +use super::{regs::*, asm}; +use crate::regs::RegisterW; + +#[derive(Copy, Clone)] +#[repr(u8)] +pub enum AccessDomain { + NoAccess = 0b00, + Client = 0b01, + _Reserved = 0b10, + Manager = 0b11, +} + +const ACCESS_DOMAINS_SIZE: usize = 16; +pub struct AccessDomains([AccessDomain; ACCESS_DOMAINS_SIZE]); + +impl AccessDomains { + pub fn all_manager() -> Self { + AccessDomains([AccessDomain::Manager; ACCESS_DOMAINS_SIZE]) + } +} + +impl Into for AccessDomains { + fn into(self) -> u32 { + let mut result = 0; + for (i, domain) in self.0.iter().enumerate() { + result |= (*domain as u32) << (2 * i); + } + result + } +} + +#[derive(Copy, Clone)] +#[repr(u8)] +pub enum AccessPermissions { + PermissionFault = 0, + PrivilegedOnly, + NoUserWrite, + FullAccess, + _Reserved1, + PrivilegedReadOnly, + ReadOnly, + _Reserved2 +} + +impl AccessPermissions { + fn ap(&self) -> u8 { + (*self as u8) & 0b11 + } + + fn apx(&self) -> bool { + (*self as u8) > (AccessPermissions::FullAccess as u8) + } +} + +pub struct L1Section { + pub global: bool, + pub shareable: bool, + pub access: AccessPermissions, + /// Type EXtension + pub tex: u8, + pub domain: u8, + pub exec: bool, + pub cacheable: bool, + pub bufferable: bool, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct L1Entry(u32); + +impl L1Entry { + #[inline(always)] + pub fn section(phys_base: u32, section: L1Section) -> Self { + /// Must be aligned to 1 MB + assert!(phys_base & 0x000f_ffff == 0); + let mut entry = L1Entry(phys_base); + + entry.0.set_bits(0..=1, 0b10); + entry.0.set_bit(2, section.bufferable); + entry.0.set_bit(3, section.cacheable); + entry.0.set_bit(4, !section.exec); + assert!(section.domain < 16); + entry.0.set_bits(5..=8, section.domain.into()); + entry.0.set_bits(10..=11, section.access.ap().into()); + assert!(section.tex < 8); + entry.0.set_bits(12..=14, section.tex.into()); + entry.0.set_bit(15, section.access.apx()); + entry.0.set_bit(16, section.shareable); + entry.0.set_bit(17, !section.global); + + entry + } +} + +const L1TABLE_SIZE: usize = 4096; + +#[repr(C, align(16384))] +pub struct L1Table { + table: [L1Entry; L1TABLE_SIZE] +} + +impl L1Table { + pub fn flat_layout() -> Self { + let mut result = L1Table { + table: unsafe { uninitialized() } + }; + /* 0x00000000 - 0x00100000 (cacheable) */ + result.direct_mapped_section(0, L1Section { + global: true, + shareable: true, + access: AccessPermissions::FullAccess, + tex: 0b101, + domain: 0b1111, + exec: true, + cacheable: false, + bufferable: true, + }); + /* (DDR cacheable) */ + for ddr in 1..=0x1ff { + result.direct_mapped_section(ddr, L1Section { + global: true, + shareable: true, + access: AccessPermissions::FullAccess, + tex: 0b101, + domain: 0b1111, + exec: true, + cacheable: true, + bufferable: true, + }); + } + /* (unassigned/reserved). */ + for undef in 0x1ff..=0x3ff { + result.direct_mapped_section(undef, L1Section { + global: false, + shareable: false, + access: AccessPermissions::PermissionFault, + tex: 0, + domain: 0, + exec: false, + cacheable: false, + bufferable: false, + }); + } + /* 0x40000000 - 0x7fffffff (FPGA slave0) */ + for fpga_slave in 0x400..=0x7ff { + result.direct_mapped_section(fpga_slave, L1Section { + global: true, + shareable: false, + access: AccessPermissions::FullAccess, + tex: 0, + domain: 0, + exec: false, + cacheable: false, + bufferable: false, + }); + } + /* 0x80000000 - 0xbfffffff (FPGA slave1) */ + for fpga_slave in 0x800..=0xbff { + result.direct_mapped_section(fpga_slave, L1Section { + global: true, + shareable: false, + access: AccessPermissions::FullAccess, + tex: 0, + domain: 0, + exec: false, + cacheable: false, + bufferable: false, + }); + } + /* 0xc0000000 - 0xdfffffff (unassigned/reserved). */ + for undef in 0xc00..=0xdff { + result.direct_mapped_section(undef, L1Section { + global: false, + shareable: false, + access: AccessPermissions::PermissionFault, + tex: 0, + domain: 0, + exec: false, + cacheable: false, + bufferable: false, + }); + } + /* 0xe0000000 - 0xe02fffff (Memory mapped devices) + * UART/USB/IIC/SPI/CAN/GEM/GPIO/QSPI/SD/NAND */ + for mmapped_dev in 0xe00..=0xe02 { + result.direct_mapped_section(mmapped_dev, L1Section { + global: true, + shareable: false, + access: AccessPermissions::FullAccess, + tex: 0, + domain: 0, + exec: true, + cacheable: false, + bufferable: true, + }); + } + /* 0xe0300000 - 0xe0ffffff (unassigned/reserved). */ + for undef in 0xe03..=0xe0f { + result.direct_mapped_section(undef, L1Section { + global: false, + shareable: false, + access: AccessPermissions::PermissionFault, + tex: 0, + domain: 0, + exec: false, + cacheable: false, + bufferable: false, + }); + } + /* 0xe1000000 - 0xe1ffffff (NAND) */ + for nand in 0xe10..=0xe1f { + result.direct_mapped_section(nand, L1Section { + global: true, + shareable: false, + access: AccessPermissions::FullAccess, + tex: 0, + domain: 0, + exec: true, + cacheable: false, + bufferable: true, + }); + } + /* 0xe2000000 - 0xe3ffffff (NOR) */ + for nor in 0xe20..=0xe3f { + result.direct_mapped_section(nor, L1Section { + global: true, + shareable: false, + access: AccessPermissions::FullAccess, + tex: 0, + domain: 0, + exec: true, + cacheable: false, + bufferable: true, + }); + } + /* 0xe4000000 - 0xe5ffffff (SRAM) */ + for nor in 0xe40..=0xe5f { + result.direct_mapped_section(nor, L1Section { + global: true, + shareable: false, + access: AccessPermissions::FullAccess, + tex: 0, + domain: 0, + exec: true, + cacheable: true, + bufferable: true, + }); + } + /* 0xe6000000 - 0xf7ffffff (unassigned/reserved). */ + for undef in 0xe60..=0xf7f { + result.direct_mapped_section(undef, L1Section { + global: false, + shareable: false, + access: AccessPermissions::PermissionFault, + tex: 0, + domain: 0, + exec: false, + cacheable: false, + bufferable: false, + }); + } + /* 0xf8000000 - 0xf8ffffff (AMBA APB Peripherals) */ + for apb in 0xf80..=0xf8f { + result.direct_mapped_section(apb, L1Section { + global: true, + shareable: false, + access: AccessPermissions::FullAccess, + tex: 0, + domain: 0, + exec: true, + cacheable: false, + bufferable: true, + }); + } + /* 0xf9000000 - 0xfbffffff (unassigned/reserved). */ + for undef in 0xf90..=0xfbf { + result.direct_mapped_section(undef, L1Section { + global: false, + shareable: false, + access: AccessPermissions::PermissionFault, + tex: 0, + domain: 0, + exec: false, + cacheable: false, + bufferable: false, + }); + } + /* 0xfc000000 - 0xfdffffff (Linear QSPI - XIP) */ + for qspi in 0xfc0..=0xfdf { + result.direct_mapped_section(qspi, L1Section { + global: true, + shareable: false, + access: AccessPermissions::FullAccess, + tex: 0, + domain: 0, + exec: true, + cacheable: false, + bufferable: true, + }); + } + /* 0xfe000000 - 0xffefffff (unassigned/reserved). */ + for undef in 0xfe0..=0xffe { + result.direct_mapped_section(undef, L1Section { + global: false, + shareable: false, + access: AccessPermissions::PermissionFault, + tex: 0, + domain: 0, + exec: false, + cacheable: false, + bufferable: false, + }); + } + /* 0xfff00000 - 0xffffffff (256K OCM when mapped to high address space) */ + result.direct_mapped_section(0xfff, L1Section { + global: true, + shareable: false, + access: AccessPermissions::FullAccess, + tex: 0b100, + domain: 0, + exec: true, + cacheable: true, + bufferable: true, + }); + + result + } + + #[inline(always)] + fn direct_mapped_section(&mut self, index: usize, section: L1Section) { + assert!(index < L1TABLE_SIZE); + + let base = (index as u32) << 20; + self.table[index] = L1Entry::section(base, section); + } +} + + +pub fn with_mmu !>(l1table: &L1Table, mut f: F) -> ! { + let domains = AccessDomains::all_manager(); + DACR.write(domains.into()); + + let table_base = &l1table.table as *const _ as u32; + assert!(table_base & 0x3fff == 0); + TTBR0.write( + TTBR0::zeroed() + .irgn1(true) + .s(true) + // Outer Cacheable Write-Back, no allocate on write. + .rgn(0b11) + .irgn0(true) + .table_base(table_base >> 14) + ); + + // Enable I-Cache and D-Cache + SCTLR.write( + SCTLR::zeroed() + .m(true) + .a(false) + .c(true) + .i(true) + .unaligned(true) + ); + + // Synchronization barriers + // Allows MMU to start + asm::dsb(); + // Flushes pre-fetch buffer + asm::isb(); + + f(); + + // table must live until here + drop(l1table.table); + unreachable!(); +} diff --git a/src/cortex_a9/mod.rs b/src/cortex_a9/mod.rs index 72f65b0..e4d0b5c 100644 --- a/src/cortex_a9/mod.rs +++ b/src/cortex_a9/mod.rs @@ -1,4 +1,5 @@ pub mod asm; pub mod regs; +pub mod mmu; global_asm!(include_str!("exceptions.s")); diff --git a/src/cortex_a9/regs.rs b/src/cortex_a9/regs.rs index ffe6ac3..8f265b2 100644 --- a/src/cortex_a9/regs.rs +++ b/src/cortex_a9/regs.rs @@ -1,4 +1,4 @@ -use crate::register_bit; +use crate::{register_bit, register_bits}; use crate::regs::{RegisterR, RegisterW, RegisterRW}; macro_rules! def_reg_r { @@ -115,6 +115,28 @@ register_bit!(sctlr, /// Thumb Exception Enable te, 30); +/// Domain Access Control Register +pub struct DACR; +def_reg_r!(DACR, u32, "mrc p15, 0, $0, c3, c0, 0"); +def_reg_w!(DACR, u32, "mcr p15, 0, $0, c3, c0, 0"); + +/// Translation Table Base Register 0 +pub struct TTBR0; +/// Translation Table Base Register 1 +pub struct TTBR1; +def_reg_r!(TTBR0, ttbr::Read, "mrc p15, 0, $0, c2, c0, 0"); +def_reg_w!(TTBR0, ttbr::Write, "mcr p15, 0, $0, c2, c0, 0"); +def_reg_r!(TTBR1, ttbr::Read, "mrc p15, 0, $0, c2, c0, 1"); +def_reg_w!(TTBR1, ttbr::Write, "mcr p15, 0, $0, c2, c0, 1"); +wrap_reg!(ttbr); +register_bits!(ttbr, table_base, u32, 14, 31); +register_bit!(ttbr, irgn0, 6); +register_bits!(ttbr, rgn, u8, 3, 4); +register_bit!(ttbr, + /// Translation table walk to shared memory? + s, 1); +register_bit!(ttbr, irgn1, 0); + /// Invalidate TLBs #[inline(always)] pub fn tlbiall() { diff --git a/src/main.rs b/src/main.rs index 2ae145e..bbdb9aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ #![feature(global_asm)] #![feature(naked_functions)] #![feature(compiler_builtins_lib)] +#![feature(never_type)] use core::fmt::Write; use core::mem::uninitialized; @@ -19,7 +20,7 @@ use uart::Uart; mod eth; use crate::regs::{RegisterR, RegisterW}; -use crate::cortex_a9::{asm, regs::*}; +use crate::cortex_a9::{asm, regs::*, mmu}; extern "C" { static mut __bss_start: u32; @@ -51,8 +52,10 @@ unsafe fn boot_core0() -> ! { l1_cache_init(); zero_bss(&mut __bss_start, &mut __bss_end); - main(); - panic!("return from main"); + mmu::with_mmu(&mmu::L1Table::flat_layout(), || { + main(); + panic!("return from main"); + }); } fn l1_cache_init() { @@ -64,24 +67,6 @@ fn l1_cache_init() { bpiall(); // Invalidate D-Cache dccisw(); - - // (Initialize MMU) - - // Enable I-Cache and D-Cache - SCTLR.write( - SCTLR::zeroed() - .m(false) - .a(false) - .c(true) - .i(true) - .unaligned(true) - ); - - // Synchronization barriers - // Allows MMU to start - asm::dsb(); - // Flushes pre-fetch buffer - asm::isb(); } const UART_RATE: u32 = 115_200;