1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
/*********************************************************************************************************************** * Copyright (c) 2020 by the authors * * Author: André Borrmann <pspwizard@gmx.de> * License: Apache License 2.0 / MIT **********************************************************************************************************************/ #![doc(html_root_url = "https://docs.rs/ruspiro-mmu/0.1.1")] #![cfg_attr(not(any(test, doctest)), no_std)] #![feature(llvm_asm)] //#![cfg(target_arch = "aarch64")] //! # RusPiRo MMU API //! //! This crate provide the API to configure and maintain the Raspberry Pi Memory Management Unit. On Raspberry Pi a //! configured and active MMU is a prerequisit to use any atomic operations. //! use ruspiro_arch_aarch64::{register::currentel, register_field, register_field_values}; mod config; mod el1; mod el2; mod macros; mod ttbr0; mod ttbr1; pub use config::TTLB_BLOCKPAGE; /// Initialize the MMU. This configures an initial 1:1 mapping accross the whole available /// memory of the Raspberry Pi. Only the memory region from 0x3F00_0000 to 0x4002_0000 is configured /// as device memory as this is the area the memory mapped peripherals and the core mailboxes are /// located at. /// /// # Safety /// The call to this function is safe when executed as part of the initial setup of the Raspberry Pi kernel and is /// called only once for each core. pub unsafe fn initialize(core: u32, vc_mem_start: u32, vc_mem_size: u32) { // the mmu configuration depends on the exception level we are running in let el = currentel::read(currentel::EL::Field).value(); // disable MMU before changing any settings and re-activating match el { 1 => el1::disable_mmu(), 2 => el2::disable_mmu(), _ => unimplemented!(), } // setup translation table entries let ttlb0_base_addr = ttbr0::setup_translation_tables(core, vc_mem_start, vc_mem_size) as u64; match el { 1 => { let ttlb1_base_addr = ttbr1::setup_translation_tables(core) as u64; el1::enable_mmu(ttlb0_base_addr, ttlb1_base_addr); } 2 => el2::enable_mmu(ttlb0_base_addr), _ => unimplemented!(), } } /// Map a given address to a virtual address with the specified memory attributes. /// TODO: Memory attributes shall be a specific allowed set only - create a new type for this! /// /// # Safety /// This is safe if the MMU has been configured already. Also the given raw pointer need to point to an /// address provided from a call to `alloc::alloc(...)` with at least `size` bytes and is aligned to the actual /// page size boundries. /// # Hint /// If the MMU is not configured to use the TTBR1 virtual address mapping this call has no effect and the returned /// address can not being used. pub unsafe fn map_memory(origin: *mut u8, size: usize, attributes: u64) -> *mut u8 { // the mmu configuration depends on the exception level we are running in let el = currentel::read(currentel::EL::Field).value(); if el == 1 { ttbr1::maintain_pages(origin, size, attributes) } else { origin } } /// Align a given address/size to the next page boundary based on MMU config pub fn page_align(addr: usize) -> usize { (addr + config::PAGE_MASK) & !config::PAGE_MASK } pub fn page_size() -> usize { config::PAGE_SIZE } #[repr(C, align(4096))] struct MmuConfig { /// TLB Level 1 entries will cover a memory range of 1GB each. For a Raspberry Pi we would only need 2 entries on /// this level, however, we would like to have the subsequent tables to start as 4kb aligned address, so reserving /// 512 entries here ttlb_lvl1: [u64; 512], /// TLB Level 2 entries will cover a memory range of 2MB each, so to maintain entries for the first 1GB of the /// Raspberry Pi 512 entries would be enough, however we would need to map the peripheral address space as well and /// they are above the 1GB mark but not greater than 2MB, so 513 entries in total would be enough. Nevertheless any /// memory located after the table shall be page aligned (4kb) we will add entries do keep the overall structure /// size fitting exactly into a multiple of a page and to align the following table to a 4kb boundry ttlb_lvl2: [u64; 1024], /*// TLB Level 3 entries will cover a memory range of 4kB each. So to be able to maintain memory attributes on this /// granule level for every memory block we would need 512*512 entries. That's quite a huge amount of memory that is /// most likely wasted, as there will be only a very small amount ob blocks that might require splitting into pages /// from the tlb configuration point of view. So we would start with 3 blocks beeing able to be maintained on this /// granule level which makes 5*512 entries and gives the overall structure a size of a multiple of a page //ttlb_lvl2: [u64; 2560],*/ } // total size : 6kB