rust-kernel
Rustbelt: a hobby x86_64 kernel written in Rust
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 107 108 | //! Turning on the MMU.
//!
//! Until now every address was a physical address. With the MMU on, the
//! CPU looks each address up in **page tables**. An arm64 CPU has two
//! table pointers: `TTBR0_EL1` for the low half of the address space
//! (addresses starting with 0x0000...) and `TTBR1_EL1` for the high half
//! (0xffff...). Linux keeps the kernel in the high half and programs in the
//! low half; we set up both:
//!
//! - **identity map** (TTBR0): virtual = physical, so the code keeps
//! running at the address it was linked at when the MMU switches on;
//! - **higher half** (TTBR1): the same memory again at
//! `HIGH_BASE + physical`. The drivers switch to it right away
//! (mmio.rs); the kernel's code is still linked low (moving it up is a
//! later stage).
//!
//! Settings (TCR_EL1): 4 KiB pages ("granule"), 39-bit addresses on both
//! sides (T0SZ = T1SZ = 25), so a walk starts at a level-1 table whose
//! 512 entries cover 1 GiB each. The bring-up only needs 1 GiB **block**
//! entries, no smaller tables: one per (first address, kind) in the board's
//! memory map. Memory types come from MAIR_EL1: index 0 = Device-nGnRnE
//! (registers: no caching, no reordering), index 1 = Normal write-back RAM.
use ;
use asm;
use addr_of_mut;
/// Where the higher half starts with T1SZ = 25: the top 2^39 bytes.
pub const HIGH_BASE: u64 = 0xffff_ff80_0000_0000;
;
static mut IDENTITY: Table = Table;
static mut HIGH: Table = Table;
const VALID_BLOCK: u64 = 0b01; // level-1 block descriptor
const ATTR_DEVICE: u64 = 0 << 2; // MAIR index 0
const ATTR_NORMAL: u64 = 1 << 2; // MAIR index 1
const INNER_SHAREABLE: u64 = 3 << 8;
const ACCESS_FLAG: u64 = 1 << 10; // "accessed" (else the first access faults)
const PXN: u64 = 1 << 53; // the kernel may not execute it
const UXN: u64 = 1 << 54; // user space may not execute it
/// The higher-half address of physical address `phys`.
/// Fill both tables from the board's memory map and switch the MMU on
/// (with the data and instruction caches).
///
/// # Safety
/// Once, early, on CPU 0, with the MMU off.
pub unsafe
/// Is the MMU on?
|