Better abstraction for platform support

This commit is contained in:
Mark Poliakov 2021-09-03 10:53:20 +03:00
parent 2f405ef17b
commit 0e3647283c
34 changed files with 1004 additions and 217 deletions

135
Cargo.lock generated
View File

@ -9,16 +9,50 @@ dependencies = [
"error",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "endian-type-rs"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6419a5c75e40011b9fe0174db3fe24006ab122fbe1b7e9cc5974b338a755c76"
[[package]]
name = "error"
version = "0.1.0"
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "fdt-rs"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99a40cabc11c8258822a593f5c51f2d9f4923e715ca9e2a0630cf77ae15f390b"
dependencies = [
"endian-type-rs",
"fallible-iterator",
"memoffset",
"num-derive",
"num-traits",
"rustc_version",
"static_assertions",
"unsafe_unwrap",
]
[[package]]
name = "kernel"
version = "0.1.0"
@ -26,6 +60,7 @@ dependencies = [
"address",
"cfg-if",
"error",
"fdt-rs",
"spin",
]
@ -38,16 +73,87 @@ dependencies = [
"scopeguard",
]
[[package]]
name = "memoffset"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
dependencies = [
"autocfg",
]
[[package]]
name = "num-derive"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "osdev4"
version = "0.1.0"
[[package]]
name = "proc-macro2"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "spin"
version = "0.9.2"
@ -56,3 +162,32 @@ checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5"
dependencies = [
"lock_api",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "syn"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "unsafe_unwrap"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1230ec65f13e0f9b28d789da20d2d419511893ea9dac2c1f4ef67b8b14e5da80"

View File

@ -55,6 +55,14 @@ impl From<u64> for PhysicalAddress {
}
impl PhysicalAddress {
pub const fn new(value: usize) -> Self {
Self(value)
}
pub const fn add(self, value: usize) -> Self {
Self(self.0 + value)
}
#[inline(always)]
pub fn diff(start: PhysicalAddress, end: PhysicalAddress) -> isize {
if end >= start {

View File

@ -118,8 +118,13 @@ impl<T: AddressSpace> VirtualAddress<T> {
}
#[inline(always)]
pub unsafe fn as_mut<U>(self) -> &'static mut U {
&mut *(self.0 as *mut U)
pub unsafe fn as_mut<U>(self) -> Option<&'static mut U> {
(self.0 as *mut U).as_mut()
}
#[inline(always)]
pub unsafe fn from_ptr<U>(r: *const U) -> Self {
Self::from(r as usize)
}
#[inline(always)]

BIN
bcm2837-rpi-3-b.dtb Normal file

Binary file not shown.

View File

@ -4,6 +4,10 @@ if [ -z "${MACH}" ]; then
MACH=rpi3b
fi
LLVM_DIR=$(llvm-config --bindir)
ARCH=aarch64-unknown-none-${MACH}
set -e
cd kernel && cargo build --target ../etc/aarch64-unknown-none-$MACH.json --features=mach_$MACH
cd kernel && cargo build --target ../etc/aarch64-unknown-none-$MACH.json --features=mach_$MACH,fdt-rs
cd ..
${LLVM_DIR}/llvm-objcopy -O binary target/${ARCH}/debug/kernel target/${ARCH}/debug/kernel.bin

View File

@ -0,0 +1,5 @@
symbol-file target/aarch64-unknown-none-virt/debug/kernel
target remote :1234
layout asm
layout regs
set scheduler-locking on

View File

@ -0,0 +1,23 @@
{
"arch": "aarch64",
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
"executables": true,
"linker": "rust-lld",
"linker-flavor": "ld.lld",
"linker-is-gnu": true,
"llvm-target": "aarch64-unknown-none",
"features": "+a53,+strict-align",
"max-atomic-width": 128,
"os": "none",
"panic-strategy": "abort",
"position-independent-executables": false,
"target-c-int-width": "32",
"target-endian": "little",
"target-pointer-width": "64",
"disable-redzone": true,
"pre-link-args": {
"ld.lld": [
"-Tetc/aarch64-unknown-none-virt.ld"
]
}
}

View File

@ -0,0 +1,29 @@
ENTRY(_entry);
KERNEL_OFFSET = 0xFFFFFF8000000000;
SECTIONS {
. = 0x40080000 + KERNEL_OFFSET;
.text : AT(. - KERNEL_OFFSET) ALIGN(4K) {
KEEP(*(.text.boot))
*(.text*)
}
.rodata : AT(. - KERNEL_OFFSET) ALIGN(4K) {
*(.rodata*)
}
.data : AT(. - KERNEL_OFFSET) ALIGN(4K) {
*(.data*)
}
.bss : AT(. - KERNEL_OFFSET) ALIGN(4K) {
*(COMMON)
*(.bss*)
. = ALIGN(4K);
PROVIDE(__kernel_end = .);
PROVIDE(__kernel_end_phys = . - KERNEL_OFFSET);
}
}

View File

@ -1,5 +1,5 @@
[unstable]
build-std = ["core", "compiler_builtins"]
build-std = ["core", "compiler_builtins", "alloc"]
[build]
target = "../etc/aarch64-unknown-none-rpi3b.json"

View File

@ -10,7 +10,8 @@ address = { path = "../address" }
error = { path = "../error" }
spin = "0.9.2"
cfg-if = "*"
fdt-rs = { version = "*", optional = true, default-features = false }
[features]
mach_rpi3b = []
mach_virt = []
mach_virt = ["fdt-rs"]

View File

@ -73,6 +73,10 @@ pub unsafe fn write_cntp_cval_el0(value: usize) {
llvm_asm!("msr cntp_cval_el0, $0"::"r"(value));
}
pub unsafe fn write_cntp_tval_el0(value: usize) {
llvm_asm!("msr cntp_tval_el0, $0"::"r"(value));
}
#[inline(always)]
pub fn dsb_sy() {
unsafe {

View File

@ -0,0 +1,13 @@
use crate::{
arch::{
cpu,
mach_bcm283x::mbox::CORE_MBOX0,
mmio_read,
},
dev::irq::IrqRegisters,
};
#[no_mangle]
extern "C" fn do_fiq() {
loop {}
}

View File

@ -4,11 +4,11 @@ use crate::{
};
use address::{PhysicalAddress, VirtualAddress};
const MBOX_BASE: usize = 0x3F00B880;
const MBOX_READ: usize = MBOX_BASE + 0x00;
const MBOX_BASE: PhysicalAddress = PhysicalAddress::new(0x3F00B880);
const MBOX_READ: PhysicalAddress = MBOX_BASE.add(0x00);
//const MBOX_POLL: usize = MBOX_BASE + 0x10;
const MBOX_STATUS: usize = MBOX_BASE + 0x18;
const MBOX_WRITE: usize = MBOX_BASE + 0x20;
const MBOX_STATUS: PhysicalAddress = MBOX_BASE.add(0x18);
const MBOX_WRITE: PhysicalAddress = MBOX_BASE.add(0x20);
const MBOX_STATUS_FULL: u32 = 1 << 31;
const MBOX_STATUS_EMPTY: u32 = 1 << 30;
@ -50,9 +50,9 @@ impl IpiDelivery for CoreMailbox {
}
impl CoreMailbox {
const REG_INTC: usize = 0x40000050;
const REG_SET: usize = 0x40000080;
const REG_RDCLR: usize = 0x400000C0;
const REG_INTC: PhysicalAddress = PhysicalAddress::new(0x40000050);
const REG_SET: PhysicalAddress = PhysicalAddress::new(0x40000080);
const REG_RDCLR: PhysicalAddress = PhysicalAddress::new(0x400000C0);
pub fn do_irq(&self) {
let phys_core_id = cpu::get_phys_id();

View File

@ -1,3 +1,64 @@
pub mod irq;
pub mod mbox;
pub mod smp;
pub mod timer;
pub mod mbox;
use crate::{
arch::{cpu, mmio_read, mmio_write},
dev::irq::InterruptController,
mem::phys::{init_from_iter, SimpleMemoryIterator, UsableMemory},
};
use address::PhysicalAddress;
pub struct Intc;
impl Intc {
const REG_TIMER_INTC: PhysicalAddress = PhysicalAddress::new(0x40000040);
const REG_INT_SRC: PhysicalAddress = PhysicalAddress::new(0x40000060);
const INTC_CNTPNSIRQ_IRQ: u32 = 1 << 1;
}
impl InterruptController for Intc {
unsafe fn init(&self) {}
unsafe fn enable_irq(&self, irq: u32) {
match irq {
IRQ_LOCAL_TIMER => {
let phys_core_id = cpu::get_phys_id();
let tmp = mmio_read(Self::REG_TIMER_INTC + 4 * phys_core_id);
mmio_write(Self::REG_TIMER_INTC + 4 * phys_core_id, tmp | Self::INTC_CNTPNSIRQ_IRQ);
}
_ => panic!("Unhandled IRQ number: {}", irq),
}
}
unsafe fn disable_irq(&self, irq: u32) {
todo!();
}
fn is_irq_pending(&self, irq: u32) -> bool {
unsafe { mmio_read(Self::REG_INT_SRC) & (1 << irq) != 0 }
}
unsafe fn clear_irq(&self, irq: u32) {
}
}
pub static INTC: Intc = Intc;
pub const IRQ_LOCAL_TIMER: u32 = 1;
// pub const INT_SRC_TIMER: u32 = 1 << 11;
// pub const INT_SRC_MBOX0: u32 = 1 << 4;
pub fn init_phys_memory() {
let arm_memory = mbox::get_arm_memory().unwrap();
let iter = SimpleMemoryIterator::new(UsableMemory {
start: PhysicalAddress::from(0usize),
end: PhysicalAddress::from(arm_memory),
});
unsafe {
init_from_iter(iter);
}
}

View File

@ -1,87 +1,86 @@
use crate::arch::{
cpu, intrin, mmio_read, mmio_write,
timer::{global_tick, local_tick, GlobalTimer, LocalTimer, LocalTimerMode, GLOBAL_TIMER_FREQ},
};
pub struct CoreTimer;
pub struct SocTimer;
impl CoreTimer {
const REG_PSC: usize = 0x40000008;
const REG_INTC: usize = 0x40000040;
const INTC_CNTPNSIRQ_FIQ: u32 = 1 << 5;
const INTC_CNTPNSIRQ_IRQ: u32 = 1 << 1;
const PRESCALER: u32 = 0x80000000;
const RELOAD_VALUE: usize = 1 << 18;
}
impl LocalTimer for CoreTimer {
unsafe fn enable(mode: LocalTimerMode) {
let phys_core_id = cpu::get_phys_id();
mmio_write(Self::REG_PSC + 4 * phys_core_id, Self::PRESCALER);
let tmp = mmio_read(Self::REG_INTC + 4 * phys_core_id);
let flags = match mode {
LocalTimerMode::Irq => Self::INTC_CNTPNSIRQ_IRQ,
LocalTimerMode::Fiq => Self::INTC_CNTPNSIRQ_FIQ,
};
mmio_write(Self::REG_INTC + 4 * phys_core_id, tmp | flags);
intrin::write_cntp_ctl_el0(1);
}
fn do_irq() {
local_tick();
Self::update(Self::RELOAD_VALUE);
}
#[inline(always)]
fn update(value: usize) {
let current = intrin::read_cntp_cval_el0();
unsafe {
intrin::write_cntp_cval_el0(current + value);
}
}
}
impl SocTimer {
const REG_CTL: usize = 0x40000034;
const REG_IRQ_RT: usize = 0x40000024;
const REG_IRQ_CLR: usize = 0x40000038;
const CTL_TIMER_EN: u32 = 1 << 28;
const CTL_IRQ_EN: u32 = 1 << 29;
const IRQ_RT_IRQ0: u32 = 0;
const IRQ_CLR_VALUE: u32 = (1 << 31) | (1 << 30);
const BASE_FREQ: u32 = 38400000;
pub const fn new() -> Self {
Self {}
}
}
impl GlobalTimer for SocTimer {
unsafe fn enable(&self) {
let reload = Self::BASE_FREQ / (GLOBAL_TIMER_FREQ as u32);
mmio_write(Self::REG_IRQ_RT, Self::IRQ_RT_IRQ0);
mmio_write(
Self::REG_CTL,
reload | Self::CTL_TIMER_EN | Self::CTL_IRQ_EN,
);
}
fn reset(&self) {
unsafe {
mmio_write(Self::REG_IRQ_CLR, Self::IRQ_CLR_VALUE);
}
}
fn do_irq(&self) {
global_tick();
self.reset();
}
}
//pub struct CoreTimer;
//pub struct SocTimer;
//
//impl CoreTimer {
// const REG_PSC: usize = 0x40000008;
// const REG_INTC: usize = 0x40000040;
//
// const INTC_CNTPNSIRQ_FIQ: u32 = 1 << 5;
// const INTC_CNTPNSIRQ_IRQ: u32 = 1 << 1;
//
// const PRESCALER: u32 = 0x80000000;
// const RELOAD_VALUE: usize = 1 << 18;
//}
//
//impl LocalTimer for CoreTimer {
// unsafe fn enable(mode: LocalTimerMode) {
// let phys_core_id = cpu::get_phys_id();
//
// mmio_write(Self::REG_PSC + 4 * phys_core_id, Self::PRESCALER);
// let tmp = mmio_read(Self::REG_INTC + 4 * phys_core_id);
// let flags = match mode {
// LocalTimerMode::Irq => Self::INTC_CNTPNSIRQ_IRQ,
// LocalTimerMode::Fiq => Self::INTC_CNTPNSIRQ_FIQ,
// };
// mmio_write(Self::REG_INTC + 4 * phys_core_id, tmp | flags);
//
// intrin::write_cntp_ctl_el0(1);
// }
//
// fn do_irq() {
// local_tick();
// Self::update(Self::RELOAD_VALUE);
// }
//
// #[inline(always)]
// fn update(value: usize) {
// let current = intrin::read_cntp_cval_el0();
// unsafe {
// intrin::write_cntp_cval_el0(current + value);
// }
// }
//}
//
//impl SocTimer {
// const REG_CTL: usize = 0x40000034;
// const REG_IRQ_RT: usize = 0x40000024;
// const REG_IRQ_CLR: usize = 0x40000038;
//
// const CTL_TIMER_EN: u32 = 1 << 28;
// const CTL_IRQ_EN: u32 = 1 << 29;
// const IRQ_RT_IRQ0: u32 = 0;
// const IRQ_CLR_VALUE: u32 = (1 << 31) | (1 << 30);
//
// const BASE_FREQ: u32 = 38400000;
//
// pub const fn new() -> Self {
// Self {}
// }
//}
//
//impl GlobalTimer for SocTimer {
// unsafe fn enable(&self) {
// let reload = Self::BASE_FREQ / (GLOBAL_TIMER_FREQ as u32);
//
// mmio_write(Self::REG_IRQ_RT, Self::IRQ_RT_IRQ0);
// mmio_write(
// Self::REG_CTL,
// reload | Self::CTL_TIMER_EN | Self::CTL_IRQ_EN,
// );
// }
//
// fn reset(&self) {
// unsafe {
// mmio_write(Self::REG_IRQ_CLR, Self::IRQ_CLR_VALUE);
// }
// }
//
// fn do_irq(&self) {
// global_tick();
// self.reset();
// }
//}

View File

@ -0,0 +1,9 @@
use crate::{
arch::intrin,
dev::irq::{IrqRegisters, GIC},
};
#[no_mangle]
extern "C" fn do_fiq(_regs: &mut IrqRegisters) {
loop {}
}

View File

@ -0,0 +1,4 @@
pub mod timer;
pub mod irq;
pub const IRQ_LOCAL_TIMER: u32 = 30;

View File

View File

@ -10,20 +10,22 @@ pub mod timer;
cfg_if! {
if #[cfg(feature = "mach_rpi3b")] {
pub mod mach_bcm283x;
pub use mach_bcm283x::smp as smp_impl;
pub use mach_bcm283x as machine;
} else if #[cfg(feature = "mach_virt")] {
pub mod mach_virt;
pub use mach_virt as machine;
}
}
pub unsafe fn mmio_write(addr: usize, value: u32) {
pub unsafe fn mmio_write(addr: PhysicalAddress, value: u32) {
core::ptr::write_volatile(
VirtualAddress::<KernelSpace>::from(PhysicalAddress::from(addr)).as_mut_ptr(),
VirtualAddress::<KernelSpace>::from(addr).as_mut_ptr(),
value,
);
}
pub unsafe fn mmio_read(addr: usize) -> u32 {
pub unsafe fn mmio_read(addr: PhysicalAddress) -> u32 {
core::ptr::read_volatile(
VirtualAddress::<KernelSpace>::from(PhysicalAddress::from(addr)).as_mut_ptr(),
VirtualAddress::<KernelSpace>::from(addr).as_mut_ptr(),
)
}

3
kernel/src/arch/psci.rs Normal file
View File

@ -0,0 +1,3 @@
pub fn init() {
}

View File

@ -1,7 +1,7 @@
use crate::{
arch::{
cpu::{self, Cpu, CPU_COUNT},
intrin, smp_impl,
intrin,
},
entry_common,
};
@ -14,10 +14,30 @@ pub trait IpiDelivery {
cfg_if! {
if #[cfg(feature = "mach_rpi3b")] {
use super::mach_bcm283x::mbox;
use super::mach_bcm283x::{mbox, smp as smp_impl};
pub type IpiDeliveryImpl = mbox::CoreMailbox;
use mbox::CORE_MBOX0 as IPI_IMPL;
} else {
// Dummy SMP implementation
mod smp_impl {
use super::{IpiDelivery, IpiMessage};
pub const MAX_CPU: usize = 1;
pub struct IpiDeliveryImpl;
impl IpiDelivery for IpiDeliveryImpl {
fn enable(&self) {}
fn send_ipi(target_id: u32, message: IpiMessage) {}
}
pub(super) fn cpu_ready(_index: usize) {}
pub(super) fn wakeup_ap_cpus() {}
pub static IPI_IMPL: IpiDeliveryImpl = IpiDeliveryImpl;
}
pub use smp_impl::{IpiDeliveryImpl, IPI_IMPL};
}
}

View File

@ -1,53 +1,25 @@
use core::sync::atomic::{AtomicU64, Ordering};
use crate::arch::smp::{self, IpiMessage};
use crate::{
arch::{intrin, machine, smp::{self, IpiMessage}},
dev::irq::{self, InterruptController, InterruptHandler},
};
pub enum LocalTimerMode {
Irq,
Fiq,
}
struct ArmTimer;
pub trait GlobalTimer {
unsafe fn enable(&self);
fn reset(&self);
fn do_irq(&self);
}
pub trait LocalTimer {
unsafe fn enable(mode: LocalTimerMode);
fn do_irq();
fn update(value: usize);
}
cfg_if! {
if #[cfg(feature = "mach_rpi3b")] {
use super::mach_bcm283x;
pub type LocalTimerImpl = mach_bcm283x::timer::CoreTimer;
pub type GlobalTimerImpl = mach_bcm283x::timer::SocTimer;
} else if #[cfg(feature = "mach_virt")] {
use super::mach_virt;
pub type LocalTimerImpl = mach_virt::timer::LocalTimer;
pub type GlobalTimerImpl = mach_virt::timer::GlobalTimer;
}
}
pub fn local_tick() {
}
pub fn global_tick() {
GLOBAL_TICK_COUNT.fetch_add(1, Ordering::Release);
if GLOBAL_TICK_COUNT.load(Ordering::Acquire) % GLOBAL_TIMER_FREQ as u64 == 0 {
debugln!("Tick");
impl InterruptHandler for ArmTimer {
fn do_irq(&self, _irq: u32) {
unsafe {
smp::send_ipi(usize::MAX, IpiMessage::Tick);
intrin::write_cntp_tval_el0(100000);
}
}
}
pub const GLOBAL_TIMER_FREQ: u64 = 1000;
pub unsafe fn enable_local_timer() {
let intc = irq::get_intc();
irq::set_irq_handler(machine::IRQ_LOCAL_TIMER, &LOCAL_TIMER);
intc.enable_irq(machine::IRQ_LOCAL_TIMER);
pub static GLOBAL_TIMER: GlobalTimerImpl = GlobalTimerImpl::new();
pub static GLOBAL_TICK_COUNT: AtomicU64 = AtomicU64::new(0);
intrin::write_cntp_ctl_el0(1);
}
static LOCAL_TIMER: ArmTimer = ArmTimer;

View File

@ -4,8 +4,8 @@
.section .text.boot
.global _entry
_entry:
mrs x0, mpidr_el1
ands x0, x0, #3
mrs x1, mpidr_el1
ands x1, x1, #3
beq _entry_bsp
1:
@ -37,6 +37,10 @@ _ap_wakeup:
.section .text
_entry_bsp:
// Store FDT address
adr x1, fdt_addr
str x0, [x1]
// Setup paging tables
// This is done once for all PEs
adr x0, kernel_l1
@ -45,11 +49,7 @@ _entry_bsp:
orr x1, x1, #PAGE_ISH
str x1, [x0]
mov x1, #1 << 30
orr x1, x1, #PAGE_PRESENT
orr x1, x1, #PAGE_ACCESSED
orr x1, x1, #PAGE_ISH
orr x1, x1, #1 << PAGE_ATTR_SHIFT
orr x1, x1, #1 << 30
str x1, [x0, #8]
// Load BSP stack
@ -152,6 +152,8 @@ upper_half:
msr daifset, #0xF
adr x1, fdt_addr
ldr x0, [x1]
br x11
@ -160,6 +162,9 @@ upper_half:
bsp_stack_bottom:
.skip 32768
bsp_stack_top:
.p2align 3
fdt_addr:
.skip 8
.p2align 12
kernel_l1:
.skip 4096

View File

@ -1,3 +1,5 @@
use crate::arch::mmio_write;
use address::PhysicalAddress;
use core::fmt;
use spin::Mutex;
@ -6,7 +8,13 @@ struct Debug;
impl Debug {
fn putc(&mut self, ch: u8) {
unsafe {
core::ptr::write_volatile(0xFFFFFF803F215040 as *mut u32, ch as u32);
cfg_if! {
if #[cfg(feature = "mach_rpi3b")] {
mmio_write(PhysicalAddress::new(0x3F215040), ch as u32);
} else if #[cfg(feature = "mach_virt")] {
mmio_write(PhysicalAddress::new(0x09000000), ch as u32);
}
}
}
}
}

View File

@ -1,16 +1,9 @@
use crate::arch::{
cpu,
mmio_read,
timer::{GlobalTimer, LocalTimer, LocalTimerImpl, GLOBAL_TIMER},
mach_bcm283x::mbox::CORE_MBOX0
};
const INT_SRC_CNTPNSIRQ: u32 = 1 << 1;
const INT_SRC_TIMER: u32 = 1 << 11;
const INT_SRC_MBOX0: u32 = 1 << 4;
use crate::arch::{intrin, machine};
use alloc::collections::LinkedList;
use address::PhysicalAddress;
#[repr(C)]
struct IrqRegisters {
pub struct IrqRegisters {
x0: usize,
x1: usize,
x2: usize,
@ -37,29 +30,70 @@ struct IrqRegisters {
far: usize,
}
pub trait InterruptController {
unsafe fn init(&self);
unsafe fn enable_irq(&self, irq: u32);
unsafe fn disable_irq(&self, irq: u32);
fn is_irq_pending(&self, irq: u32) -> bool;
unsafe fn clear_irq(&self, irq: u32);
}
pub trait InterruptHandler {
fn do_irq(&self, irq: u32);
}
pub struct InterruptEntry {
irq: u32,
handler: &'static dyn InterruptHandler
}
static mut IRQ_HANDLERS: LinkedList<InterruptEntry> = LinkedList::new();
#[no_mangle]
extern "C" fn do_irq(_regs: &mut IrqRegisters) {
let phys_id = cpu::get_phys_id();
let int_src = unsafe { mmio_read(0x40000060 + phys_id * 4) };
if int_src & INT_SRC_TIMER != 0 {
assert!(phys_id == 0);
GLOBAL_TIMER.do_irq();
return;
let intc = get_intc();
for entry in unsafe { IRQ_HANDLERS.iter() } {
if intc.is_irq_pending(entry.irq) {
entry.handler.do_irq(entry.irq);
unsafe {
intc.clear_irq(entry.irq);
}
}
}
if int_src & INT_SRC_CNTPNSIRQ != 0 {
LocalTimerImpl::do_irq();
return;
}
if int_src & INT_SRC_MBOX0 != 0 {
CORE_MBOX0.do_irq();
return;
}
debugln!("Unhandled IRQ: int_src={:#010b}\n", int_src);
}
#[no_mangle]
extern "C" fn do_fiq() {
// Only used by core timer
LocalTimerImpl::do_irq();
pub fn set_irq_handler(irq: u32, handler: &'static dyn InterruptHandler) {
unsafe {
IRQ_HANDLERS.push_front(InterruptEntry {
irq, handler
});
}
}
cfg_if! {
if #[cfg(feature = "mach_rpi3b")] {
use crate::arch::mach_bcm283x::INTC;
pub fn get_intc() -> &'static impl InterruptController {
&INTC
}
} else {
pub mod gic;
use gic::Gic;
pub static GIC: Gic = Gic::new(
PhysicalAddress::new(0x08000000usize),
PhysicalAddress::new(0x08010000usize),
);
pub fn get_intc() -> &'static impl InterruptController {
&GIC
}
}
}
pub unsafe fn init() {
get_intc().init();
}

77
kernel/src/dev/irq/gic.rs Normal file
View File

@ -0,0 +1,77 @@
use crate::{
arch::{mmio_read, mmio_write},
dev::irq::InterruptController,
};
use address::PhysicalAddress;
pub struct Gic {
gicd_base: PhysicalAddress,
gicc_base: PhysicalAddress,
}
impl InterruptController for Gic {
unsafe fn init(&self) {
mmio_write(self.gicd_base + Self::GICD_CTLR, Self::GICD_CTLR_ENABLE);
mmio_write(self.gicc_base + Self::GICC_CTLR, Self::GICC_CTLR_ENABLE);
mmio_write(self.gicc_base + Self::GICC_PMR, 0xFF);
}
unsafe fn enable_irq(&self, irq: u32) {
self.set_irq_config(irq, 1);
self.unmask_irq(irq);
}
unsafe fn disable_irq(&self, irq: u32) {
todo!();
}
fn is_irq_pending(&self, irq: u32) -> bool {
unsafe {
mmio_read(self.gicd_base + Self::GICD_ICPENDR + ((irq >> 3) & !0x3) as usize)
& (1 << (irq & 0x1F))
!= 0
}
}
unsafe fn clear_irq(&self, irq: u32) {
mmio_write(
self.gicd_base + Self::GICD_ICPENDR + ((irq >> 3) & !0x3) as usize,
1 << (irq & 0x1F),
);
}
}
impl Gic {
const GICD_CTLR: usize = 0;
const GICD_ISENABLER: usize = 0x100;
const GICD_ICFGR: usize = 0xC00;
const GICD_ICPENDR: usize = 0x280;
const GICD_CTLR_ENABLE: u32 = 1;
const GICC_CTLR: usize = 0;
const GICC_PMR: usize = 4;
const GICC_CTLR_ENABLE: u32 = 1;
pub const fn new(gicd_base: PhysicalAddress, gicc_base: PhysicalAddress) -> Self {
Self {
gicd_base,
gicc_base,
}
}
unsafe fn set_irq_config(&self, irq: u32, value: u32) {
mmio_write(
self.gicd_base + Self::GICD_ICFGR + ((irq >> 4) & !0x3) as usize,
value << (1 + (irq & 0xF)),
);
}
unsafe fn unmask_irq(&self, irq: u32) {
mmio_write(
self.gicd_base + Self::GICD_ISENABLER + ((irq >> 3) & !0x3) as usize,
1 << (irq & 0x1F),
);
}
}

110
kernel/src/fdt.rs Normal file
View File

@ -0,0 +1,110 @@
use crate::{
mem::{
self,
phys::{PageUsage, SimpleMemoryIterator, UsableMemory},
},
KernelSpace,
};
use address::{PhysicalAddress, VirtualAddress};
use core::mem::MaybeUninit;
use fdt_rs::{
base::{DevTree, DevTreeProp},
index::{DevTreeIndex, DevTreeIndexNode},
prelude::*,
};
#[allow(dead_code)]
struct FdtManager {
fdt: DevTree<'static>,
index: DevTreeIndex<'static, 'static>,
}
static mut FDT_MANAGER: MaybeUninit<FdtManager> = MaybeUninit::uninit();
fn read_cell_prop(prop: &DevTreeProp, offset: usize, cells: u32) -> Option<usize> {
if cells == 2 {
// Read as two u32s
let high = prop.u32(offset).ok()? as usize;
let low = prop.u32(offset + 1).ok()? as usize;
Some(low | (high << 32))
} else {
let val = prop.u32(offset).ok()?;
Some(val as usize)
}
}
pub fn init(fdt_base_phys: PhysicalAddress) {
let fdt_base = VirtualAddress::<KernelSpace>::from(fdt_base_phys);
let fdt = unsafe { DevTree::from_raw_pointer(fdt_base.as_ptr()) }.unwrap();
let layout = DevTreeIndex::get_layout(&fdt).unwrap();
assert!(layout.align() <= 0x1000);
let page_count = (layout.size() + 0xFFF) / 0x1000;
debugln!("Allocating {} pages for fdt index", page_count);
let pages = mem::phys::alloc_contiguous_pages(PageUsage::Kernel, page_count).unwrap();
let pages_virt = VirtualAddress::<KernelSpace>::from(pages);
let index = DevTreeIndex::new(fdt, unsafe {
pages_virt.as_slice_mut(page_count * 0x1000)
}).unwrap();
unsafe {
FDT_MANAGER.write(FdtManager {
fdt, index
});
}
}
pub fn init_phys_memory(fdt_base_phys: PhysicalAddress) {
let fdt_base = VirtualAddress::<KernelSpace>::from(fdt_base_phys);
let fdt = unsafe { DevTree::from_raw_pointer(fdt_base.as_ptr()) }.unwrap();
let mut address_cells = None;
let mut size_cells = None;
let mut nodes = fdt.nodes();
while let Ok(Some(node)) = nodes.next() {
if node.name().unwrap().is_empty() {
let mut props = node.props();
while let Ok(Some(prop)) = props.next() {
let name = prop.name().unwrap();
match name {
"#address-cells" => address_cells = prop.u32(0).ok(),
"#size-cells" => size_cells = prop.u32(0).ok(),
_ => {}
}
}
}
}
// Find out system address and size cells
let address_cells = address_cells.expect("Failed to find out system's #address-cells");
let size_cells = size_cells.expect("Failed to find out system's #size-cells");
// TODO support multiple memory regions
let mut region_start = None;
let mut region_size = None;
let mut nodes = fdt.nodes();
while let Ok(Some(node)) = nodes.next() {
if node.name().unwrap().starts_with("memory@") {
let mut props = node.props();
while let Ok(Some(prop)) = props.next() {
if prop.name().unwrap() == "reg" {
region_start = read_cell_prop(&prop, 0, address_cells);
region_size = read_cell_prop(&prop, address_cells as usize, size_cells);
}
}
}
}
let region_start = region_start.unwrap();
let region_end = region_start + region_size.unwrap();
let iter = SimpleMemoryIterator::new(UsableMemory {
start: PhysicalAddress::from((region_start + 0xFFF) & !0xFFF),
end: PhysicalAddress::from(region_end & !0xFFF),
});
unsafe {
mem::phys::init_from_iter(iter);
}
}

View File

@ -1,9 +1,19 @@
#![feature(global_asm, llvm_asm, const_panic, maybe_uninit_uninit_array)]
#![feature(
global_asm,
llvm_asm,
const_panic,
maybe_uninit_uninit_array,
alloc_error_handler
)]
#![no_std]
#![no_main]
#[macro_use]
extern crate cfg_if;
#[macro_use]
extern crate alloc;
use alloc::boxed::Box;
#[macro_use]
pub mod debug;
@ -11,22 +21,25 @@ pub mod debug;
pub mod arch;
pub mod boot;
pub mod dev;
#[cfg(feature = "fdt-rs")]
pub mod fdt;
pub mod mem;
pub use mem::KernelSpace;
use address::PhysicalAddress;
use arch::{
cpu, intrin, smp,
timer::{GlobalTimer, LocalTimer, LocalTimerImpl, LocalTimerMode, GLOBAL_TIMER},
cpu::{self, Cpu},
intrin, smp, timer,
};
use mem::phys::{SimpleMemoryIterator, UsableMemory};
use dev::irq;
pub fn entry_common() -> ! {
smp::init_ipi_delivery();
unsafe {
LocalTimerImpl::enable(LocalTimerMode::Irq);
irq::init();
timer::enable_local_timer();
intrin::enable_irq();
}
@ -34,26 +47,23 @@ pub fn entry_common() -> ! {
}
#[no_mangle]
extern "C" fn kernel_bsp_main() -> ! {
extern "C" fn kernel_bsp_main(fdt_base: PhysicalAddress) -> ! {
cpu::init(0);
let arm_memory = 0x3F000000usize;
//let arm_memory = mbox::get_arm_memory().expect("Failed to determine ARM memory");
let iter = SimpleMemoryIterator::new(UsableMemory {
start: PhysicalAddress::from(0usize),
end: PhysicalAddress::from(arm_memory),
});
unsafe {
mem::phys::init_from_iter(iter);
cfg_if! {
if #[cfg(feature = "fdt-rs")] {
// Initialize memory from FDT information
fdt::init_phys_memory(fdt_base);
fdt::init(fdt_base);
} else {
// Platform-specific memory init
mem::phys::init_from_platform();
}
}
mem::heap::init();
debug!("BSP init finished\n");
unsafe {
GLOBAL_TIMER.enable();
}
smp::wakeup_ap_cpus();
//smp::wakeup_ap_cpus();
entry_common();
}

209
kernel/src/mem/heap.rs Normal file
View File

@ -0,0 +1,209 @@
// TODO document this module
#![allow(missing_docs)]
use crate::{
mem::{
self,
phys::{self, PageUsage},
},
KernelSpace,
};
use address::VirtualAddress;
use core::alloc::{GlobalAlloc, Layout};
use core::convert::TryFrom;
use core::mem::{size_of, MaybeUninit};
use core::ptr::null_mut;
use spin::Mutex;
const HEAP_MAGIC: u32 = 0xB0000BA0;
const HEAP_SIZE: usize = 16 << 20; // 16MiB
struct Heap {
start: VirtualAddress<KernelSpace>,
mutex: MaybeUninit<Mutex<()>>,
}
#[derive(Debug)]
struct HeapBlock {
magic: u32,
size: u32,
previous: *mut HeapBlock,
next: *mut HeapBlock,
}
unsafe impl GlobalAlloc for Heap {
#[track_caller]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
// Align the layout by 16 bytes
let count = ((layout.size() + 15) & !15) as u32;
// NOTE: that shouldn't be optimized away
let _lock = self.mutex.assume_init_ref().lock();
// Check if the memory is corrupted
let mut block_it = self.first();
while let Some(ref mut block) = block_it {
if (block.magic & HEAP_MAGIC) != HEAP_MAGIC {
panic!("Heap is out: block {:?}", block);
}
block_it = block.next_mut();
}
let mut block_it = self.first();
while let Some(ref mut block) = block_it {
if !block.is_available() {
block_it = block.next_mut();
continue;
}
if count == block.size {
block.take();
return block.data();
} else if count + size_of::<HeapBlock>() as u32 <= block.size {
block.split(count);
block.take();
return block.data();
}
block_it = block.next_mut();
}
null_mut()
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
// NOTE: that shouldn't be optimized away
let _lock = self.mutex.assume_init_ref().lock();
let address = VirtualAddress::from_ptr(ptr);
// Check heap boundaries
if address < self.start
|| address > self.start + HEAP_SIZE
|| address + layout.size() > self.start + HEAP_SIZE
{
panic!("Deallocating out of heap");
};
// Get the block
let block: &mut HeapBlock = (address - size_of::<HeapBlock>()).as_mut().unwrap();
// Check the magic and shit
if (block.magic & !1) != HEAP_MAGIC {
panic!(
"Heap block is corrupted: magic {:#018x} {:?}",
block.magic, block
);
}
// Free that shit
block.free();
// Check if the adjacent blocks are available
// and merge them in
if let Some(next) = block.next.as_mut() {
if next.is_available() {
block.size += (*block.next).size;
block.next = (*block.next).next;
}
}
if let Some(previous) = block.previous.as_mut() {
if previous.is_available() {
previous.size += block.size;
previous.next = block.next;
}
}
// TODO: cfg debug turn the memory into junk
}
}
impl Heap {
pub unsafe fn place(addr: VirtualAddress<KernelSpace>) -> Self {
let block: &mut HeapBlock = addr.as_mut().unwrap();
block.magic = HEAP_MAGIC;
block.size = (HEAP_SIZE - size_of::<Heap>()) as u32;
block.previous = null_mut();
block.next = null_mut();
Heap {
start: addr,
mutex: MaybeUninit::new(Mutex::new(())),
}
}
fn first(&self) -> Option<&'static mut HeapBlock> {
unsafe { self.start.as_mut::<HeapBlock>() }
}
#[allow(dead_code)]
fn dump(&self) {
// NOTE: that shouldn't be optimized away
let _lock = unsafe { self.mutex.assume_init_ref().lock() };
let mut block_it = self.first();
while let Some(ref mut block) = block_it {
debugln!("{:p}: {:?}", *block, block);
block_it = block.next_mut();
}
}
}
impl HeapBlock {
fn next_mut(&mut self) -> Option<&'static mut HeapBlock> {
unsafe { self.next.as_mut() }
}
fn is_available(&self) -> bool {
self.magic & 1 == 0
}
fn free(&mut self) {
self.magic = HEAP_MAGIC;
}
fn take(&mut self) {
self.magic |= 1;
}
fn split(&mut self, size: u32) -> &'static mut HeapBlock {
assert!(size + size_of::<HeapBlock>() as u32 <= self.size);
unsafe {
let mut new_block = &mut *(self.data().add(size as usize) as *mut HeapBlock);
new_block.previous = self;
new_block.next = self.next;
if let Some(ref mut p) = self.next_mut() {
p.previous = new_block;
};
new_block.magic = HEAP_MAGIC;
new_block.size = self.size - size - size_of::<HeapBlock>() as u32;
self.next = new_block;
self.size = size;
new_block
}
}
fn data(&mut self) -> *mut u8 {
unsafe { (self as *mut _ as *mut u8).add(size_of::<HeapBlock>()) }
}
}
#[global_allocator]
static mut KERNEL_HEAP: Heap = Heap {
start: VirtualAddress::null(),
mutex: MaybeUninit::uninit(),
};
#[alloc_error_handler]
fn alloc_error_handler(layout: Layout) -> ! {
panic!("Failed to allocate {:?}", layout);
}
/// Fuck
pub fn init() {
let base_addr_phys =
phys::alloc_contiguous_pages(PageUsage::Kernel, HEAP_SIZE / mem::PAGE_SIZE).unwrap();
let base_addr = VirtualAddress::try_from(base_addr_phys).unwrap();
unsafe {
KERNEL_HEAP = Heap::place(base_addr);
};
debugln!("Heap: {:?}", base_addr);
}

View File

@ -1,6 +1,7 @@
use address::{AddressSpace, PhysicalAddress, TrivialConvert};
pub mod phys;
pub mod heap;
#[derive(Copy, Clone, PartialEq, PartialOrd)]
pub struct KernelSpace;

View File

@ -19,10 +19,15 @@ pub unsafe trait Manager {
pub struct SimpleManager {
pages: &'static mut [Mutex<PageInfo>],
base_index: usize,
}
impl SimpleManager {
pub(super) unsafe fn initialize(at: PhysicalAddress, count: usize) -> Self {
pub(super) unsafe fn initialize(
base: PhysicalAddress,
at: PhysicalAddress,
count: usize,
) -> Self {
let pages: &'static mut [Mutex<PageInfo>] =
VirtualAddress::<KernelSpace>::from(at).as_slice_mut(count);
@ -37,16 +42,19 @@ impl SimpleManager {
));
}
Self { pages }
Self {
base_index: base.page_index(),
pages,
}
}
pub(super) unsafe fn add_page(&mut self, addr: PhysicalAddress) {
let mut page = self.pages[addr.page_index()].lock();
let mut page = self.pages[addr.page_index() - self.base_index].lock();
assert!(page.refcount == 0 && page.usage == PageUsage::Reserved);
page.usage = PageUsage::Available;
// Fill the page with trash
let slice: &mut [u8; 4096] = VirtualAddress::<KernelSpace>::from(addr).as_mut();
let slice: &mut [u8; 4096] = VirtualAddress::<KernelSpace>::from(addr).as_mut().unwrap();
slice.fill(0);
}
}
@ -59,7 +67,7 @@ unsafe impl Manager for SimpleManager {
if page.usage == PageUsage::Available {
page.usage = pu;
page.refcount = 1;
return Ok(PhysicalAddress::from(index * PAGE_SIZE));
return Ok(PhysicalAddress::from((self.base_index + index) * PAGE_SIZE));
}
}
Err(Errno::OutOfMemory)
@ -83,7 +91,7 @@ unsafe impl Manager for SimpleManager {
page.refcount = 1;
}
return Ok(PhysicalAddress::from(i * PAGE_SIZE));
return Ok(PhysicalAddress::from((self.base_index + i) * PAGE_SIZE));
}
Err(Errno::OutOfMemory)
}

View File

@ -52,7 +52,7 @@ impl Iterator for SimpleMemoryIterator {
}
}
const MAX_PAGES: usize = 1024;
const MAX_PAGES: usize = 16 * 1024;
pub fn alloc_page(pu: PageUsage) -> Result<PhysicalAddress, Errno> {
MANAGER.lock().as_mut().unwrap().alloc_page(pu)
@ -99,6 +99,16 @@ fn find_contiguous<T: Iterator<Item = UsableMemory>>(
}
pub unsafe fn init_from_iter<T: Iterator<Item = UsableMemory> + Clone>(iter: T) {
let mut mem_base = PhysicalAddress::from(usize::MAX);
for reg in iter.clone() {
if reg.start < mem_base {
mem_base = reg.start;
}
}
debugln!("Memory base is {:?}", mem_base);
// Step 1. Count available memory
let mut total_pages = 0usize;
for reg in iter.clone() {
@ -117,7 +127,7 @@ pub unsafe fn init_from_iter<T: Iterator<Item = UsableMemory> + Clone>(iter: T)
reserved::reserve_pages(pages_base, need_pages);
// Step 3. Initialize the memory manager with available pages
let mut manager = ManagerImpl::initialize(pages_base, total_pages);
let mut manager = ManagerImpl::initialize(mem_base, pages_base, total_pages);
let mut usable_pages = 0usize;
'l0: for region in iter {
@ -137,6 +147,11 @@ pub unsafe fn init_from_iter<T: Iterator<Item = UsableMemory> + Clone>(iter: T)
*MANAGER.lock() = Some(manager);
}
pub unsafe fn init_from_platform() {
todo!();
pub fn init_from_platform() {
cfg_if! {
if #[cfg(feature = "mach_rpi3b")] {
use crate::arch::mach_bcm283x;
mach_bcm283x::init_phys_memory();
}
}
}

13
qemu.sh
View File

@ -10,13 +10,26 @@ ARCH=aarch64-unknown-none-${MACH}
KERNEL=target/${ARCH}/debug/kernel
QEMU_OPTS=""
if [ "$QEMU_DINT" = 1 ]; then
QEMU_OPTS="$QEMU_OPTS -d int"
fi
case ${MACH} in
rpi3b)
QEMU_OPTS="$QEMU_OPTS \
-serial null \
-serial stdio \
-dtb bcm2837-rpi-3-b.dtb \
-M raspi3b"
;;
virt)
KERNEL=target/${ARCH}/debug/kernel.bin
QEMU_OPTS="$QEMU_OPTS \
-serial stdio \
-M virt,virtualization=on \
-cpu cortex-a57 \
-m 256"
esac
QEMU_OPTS="$QEMU_OPTS \
-kernel ${KERNEL} \