RustForge

rust-kernel

Rustbelt: a hobby x86_64 kernel written in Rust

rust-kernel / src / arch / aarch64 / mod.rs
2719 bytes ยท raw
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
//! Stage 22: the arm64 (aarch64) side of the `arch` interface (see
//! src/arch/mod.rs). This is a *bring-up*: enough to boot on QEMU's `virt`
//! board with a Cortex-A53, print, take exceptions, turn on the MMU, and
//! get a timer interrupt through the GIC. No processes, no scheduler yet.
//!
//! | File | Part |
//! |------|------|
//! | `boot.rs` | entry: `_start`, EL2 -> EL1, stack, .bss, then `kmain` |
//! | `console.rs` | text out through the board's UART, `print!`/`println!` |
//! | `exceptions.rs` | the vector table, ESR decoding, IRQ dispatch |
//! | `gic.rs` | the GICv2 interrupt controller (virt) |
//! | `timer.rs` | the generic timer (CNTV), the tick count |
//! | `paging.rs` | MMU: identity map + the same memory again in the higher half |
//! | `dtb.rs` | a small device tree (DTB) reader |
//! | `mmio.rs` | device register access (through the higher half once the MMU is on) |
//! | `board/` | what differs between boards: `virt` (works), `raspi3` (a skeleton) |
//! | `main.rs` | the kernel binary `rustbelt-aarch64` (`kmain`) |
//! | `link.ld` | where the kernel goes in memory (0x4020_0000 on virt) |

pub mod board;
pub mod boot;
pub mod console;
pub mod dtb;
pub mod exceptions;
pub mod gic;
pub mod mmio;
pub mod paging;
pub mod timer;

/// The `arch` interface's `interrupts` (vector table + IRQ mask); the
/// controller itself is the board's (`board::current().irq()`).
pub use exceptions as interrupts;

use core::arch::asm;

/// The banner, the first line the arm64 kernel prints.
pub const BANNER: &str = concat!("Rustbelt ", env!("CARGO_PKG_VERSION"), " (stage 22: arm64 bring-up)");

/// The exception level we're running at now (1 = kernel, 2 = hypervisor).
pub fn current_el() -> u64 {
    let el: u64;
    unsafe { asm!("mrs {}, CurrentEL", out(reg) el) };
    (el >> 2) & 3
}

/// End the QEMU run with `code` as QEMU's exit status, through Arm
/// **semihosting** (QEMU must be started with `-semihosting`): `HLT #0xF000`
/// with operation 0x18 (`SYS_EXIT`) in w0 and, in x1, the address of two
/// words: the reason `ADP_Stopped_ApplicationExit` (0x20026) and the code.
///
/// (PSCI `SYSTEM_OFF` would also stop QEMU, but it can't carry a status.)
/// Without `-semihosting` the HLT is an undefined instruction; on a real
/// board there's no semihosting, so this is a QEMU-only way out.
pub fn exit(code: u32) -> ! {
    let block: [u64; 2] = [0x20026, code as u64];
    unsafe {
        asm!("hlt #0xf000", in("w0") 0x18u32, in("x1") block.as_ptr(), options(nostack));
    }
    halt()
}

/// Stop this CPU for good: mask interrupts and wait forever.
pub fn halt() -> ! {
    loop {
        unsafe { asm!("msr daifset, #0xf", "wfe", options(nomem, nostack)) };
    }
}