RustForge

rust-kernel

Rustbelt: a hobby x86_64 kernel written in Rust

rust-kernel / src / arch / aarch64 / board / raspi3.rs
3750 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
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
//! The Raspberry Pi 3 (BCM2837, four Cortex-A53 cores): a **skeleton**.
//! Nothing here is used or tested yet; it writes down where things are so
//! the next stages have a place to start (see "Raspberry Pi roadmap" in
//! the README). QEMU can emulate it too: `qemu-system-aarch64 -M raspi3b`.
//!
//! How it differs from QEMU's virt:
//!
//! ```text
//! 0x0000_0000  RAM (1 GiB minus what the GPU keeps; the firmware loads
//!              kernel8.img at 0x8_0000 and passes the DTB in x0)
//! 0x3f00_0000  peripherals ("0x7e..." in Broadcom's manual = 0x3f... here)
//!   0x3f00_b200  the BCM2835 interrupt controller (not a GIC!)
//!   0x3f00_b880  the mailbox (ask the GPU for a framebuffer)
//!   0x3f20_1000  PL011 UART0 (same driver as virt)
//!   0x3f21_5040  the mini-UART (UART1, a different, simpler chip)
//!   0x3f30_0000  EMMC SD card controller (0x3f20_2000: SDHOST, the other one)
//!   0x3f98_0000  USB (DWC2); networking is a LAN9514 on that USB bus
//! 0x4000_0000  the "ARM local" block: per-core timer interrupts, mailboxes
//! ```
//!
//! So RAM and devices share the first GiB: the 1 GiB blocks paging.rs
//! uses for virt aren't fine enough, and the Pi needs 2 MiB blocks (TODO).

use super::pl011::Pl011;
use super::{Board, IrqController, Kind, Uart};
use crate::arch::aarch64::dtb::Fdt;
use alloc::string::String;
use alloc::vec::Vec;

pub const PERIPHERAL_BASE: usize = 0x3f00_0000;
pub const UART0_BASE: usize = PERIPHERAL_BASE + 0x20_1000;
pub const MINI_UART_BASE: usize = PERIPHERAL_BASE + 0x21_5040;
pub const IRQ_BASE: usize = PERIPHERAL_BASE + 0xb200;
pub const MAILBOX_BASE: usize = PERIPHERAL_BASE + 0xb880;
pub const EMMC_BASE: usize = PERIPHERAL_BASE + 0x30_0000;
pub const SDHOST_BASE: usize = PERIPHERAL_BASE + 0x20_2000;
pub const USB_BASE: usize = PERIPHERAL_BASE + 0x98_0000;
pub const LOCAL_BASE: usize = 0x4000_0000;
/// ARM local block: core 0's timer interrupt control (CNTVIRQ is bit 3).
pub const LOCAL_TIMER_INT_CTRL0: usize = LOCAL_BASE + 0x40;
/// ARM local block: core 0's interrupt source register.
pub const LOCAL_IRQ_SOURCE0: usize = LOCAL_BASE + 0x60;

/// The BCM2835 interrupt controller plus the ARM local block. TODO: the
/// timer goes through the local block (set bit 3 of LOCAL_TIMER_INT_CTRL0,
/// read LOCAL_IRQ_SOURCE0); devices through the BCM2835 pending registers.
pub struct Bcm2835Irq;

impl IrqController for Bcm2835Irq {
    fn init(&self) {
        // TODO(Pi 3): disable everything (IRQ_BASE + 0x1c/0x20/0x24).
    }
    fn enable(&self, _irq: u32) {
        // TODO(Pi 3): local timer -> LOCAL_TIMER_INT_CTRL0; devices -> enable registers.
    }
    fn ack(&self) -> Option<u32> {
        None // TODO(Pi 3): read LOCAL_IRQ_SOURCE0, then the pending registers
    }
    fn eoi(&self, _irq: u32) {
        // Nothing to do on this controller: the device clears its own line.
    }
}

pub struct Raspi3 {
    uart: Pl011,
    irq: Bcm2835Irq,
}

/// Not selected by `board::current()` yet.
pub static RASPI3: Raspi3 = Raspi3 { uart: Pl011::new(UART0_BASE), irq: Bcm2835Irq };

/// TODO(Pi 3): needs 2 MiB granularity (RAM up to 0x3f00_0000, devices after).
static MAP: [(u64, Kind); 1] = [(0, Kind::Normal)];

impl Board for Raspi3 {
    fn name(&self) -> &'static str {
        "Raspberry Pi 3 (skeleton)"
    }
    fn uart(&self) -> &dyn Uart {
        &self.uart
    }
    fn irq(&self) -> &dyn IrqController {
        &self.irq
    }
    fn timer_irq(&self) -> u32 {
        3 // CNTVIRQ's bit in LOCAL_IRQ_SOURCE0
    }
    fn memory_map(&self) -> &'static [(u64, Kind)] {
        &MAP
    }
    fn configure(&self, _dtb: Option<&Fdt>) -> Vec<String> {
        Vec::from([String::from("raspi3: skeleton only (TODO: read the DTB the firmware passes in x0)")])
    }
}