Compare commits

..

No commits in common. "5dfd3f780fd6c5be33b50343500a9ab37db511ee" and "109afc7840b563b877b70d5540c3a8ef2cbb8224" have entirely different histories.

11 changed files with 179 additions and 165 deletions

View file

@ -24,5 +24,4 @@ runner = """ qemu-system-riscv64
-s
-nographic
-serial mon:stdio
-bios none
-kernel """
-bios """

22
Cargo.lock generated
View file

@ -26,7 +26,7 @@ version = "0.1.0"
dependencies = [
"linked_list_allocator",
"riscv",
"spin",
"spinning_top 0.3.0",
]
[[package]]
@ -35,7 +35,7 @@ version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286"
dependencies = [
"spinning_top",
"spinning_top 0.2.5",
]
[[package]]
@ -64,15 +64,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
dependencies = [
"lock_api",
]
[[package]]
name = "spinning_top"
version = "0.2.5"
@ -81,3 +72,12 @@ checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0"
dependencies = [
"lock_api",
]
[[package]]
name = "spinning_top"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300"
dependencies = [
"lock_api",
]

View file

@ -6,4 +6,4 @@ edition = "2021"
[dependencies]
linked_list_allocator = "0.10.5"
riscv = "0.11.1"
spin = "0.9.8"
spinning_top = "0.3.0"

View file

@ -3,17 +3,15 @@ created: 2024-05-13T14:28:49-05:00
modified: 2024-05-19T17:41:01-05:00
---
# kernel
A kernel for RISC-V written in Rust. Currently focused on running on QEMU generic riscv64, advice and assistance welcome & encouraged.
## Progress
# Progress
* **2024-05-19:** As of right now, the basic "stuff" for initializing the kernel is there. The diagram below shows what I'm thinking as far as what the init process should be. This is what I'm currently working on implementing, and I will update this chart (and prettify it) more
![Flowchart (tentatively) showing the plan for the kernel's initialization process. On the left is hart 0, the main hardware thread, and on the right is hart 1...n, all other hardware threads. All harts will set the global, stack, and thread pointers, disable paging, and set the mstatus and mepc control status registers, before doing their own independent init processes. Hart 0 will disable interrupts and enter the primary initialization function, kinit, before starting the first user process. Other harts will enable interrupts, do their own init, kinit_hart, then wait for a software inter-processor interrupt.](20240519-kernel_init_diagram.png)
## Research and implement
# Research and implement
- [ ] Basics
- [ ] Processes
@ -32,6 +30,6 @@ A kernel for RISC-V written in Rust. Currently focused on running on QEMU generi
- [ ] Device drivers
- [ ] User applications
## Resources
# Resources
See [RESOURCES.md](RESOURCES.md)

View file

@ -2,7 +2,7 @@
fn main() {
// Tell ld to use linker script.
println!("cargo::rustc-link-arg=-Tlds/kernel.lds");
println!("cargo::rustc-link-arg=-Tsrc/script.lds");
// Don't do any magic linker stuff.
println!("cargo::rustc-link-arg=--omagic");
}

View file

@ -1,43 +0,0 @@
# entry.S
.option norvc
.section .data
.section .text.init
.global _entry
_entry:
# Any hardware threads (hart) that are not bootstrapping
# need to wait for an IPI
csrr t0, mhartid
bnez t0, 3f
# SATP should be zero, but let's make sure
csrw satp, zero
.option push
.option norelax
la gp, _global_pointer
.option pop
li t5, 0xffff
csrw medeleg, t5
csrw mideleg, t5
la sp, _stack_end
li t0, (0b11 << 11) | (1 << 7) | (1 << 3)
csrw mstatus, t0
la t0, _bss_start
la t1, _bss_end
bgeu t0, t1, 2f
1:
sb zero, 0(t0)
addi t0, t0, 1
bne t0, t1, 1b
2:
j 4f
3:
wfi
j 3b
4:
tail start

View file

@ -1,86 +1,102 @@
// src/entry.rs
static READY: spin::Once<()> = spin::Once::new();
/// After some initialization in asm/entry.S, the kernel will jump here and
/// each hart will have its own setup sequence.
/// This is the first function called when the system boots. In this function,
/// we set up important control status registers (CSR) such as `mstatus` and
/// `satp`. The global, stack, and thread pointers are also configured here.
#[no_mangle]
unsafe extern "C" fn _start() {
#[link_section = ".text.init"]
unsafe extern "C" fn entry() {
use core::arch::asm;
use riscv::register::{mepc, mstatus, satp, sie, sstatus};
use riscv::register::{mhartid, mstatus, satp, sstatus};
// Set previous privilege for all harts to M-mode, set previous interrupt
// enable, and set floating-point unit to initial state
mstatus::set_mpp(mstatus::MPP::Supervisor);
mstatus::set_mpie();
mstatus::set_fs(sstatus::FS::Initial);
// Global pointer `gp`
//
// Push current option stack to temporarily disable relaxation, load
// _global_pointer symbol (provided by linker), then pop option stack.
//
// Important to keep relaxation off for this instruction, so that
// the instruction is emitted as:
// 1:
// auipc gp, %pcrel_hi(_global_pointer)
// addi gp, gp, %pcrel_lo(1b)"
// instead of:
// mv gp, gp
asm!(
".option push",
".option norelax",
"la gp, _global_pointer",
".option pop",
);
// Store ref to main in M-mode exception program counter
mepc::write(main as usize);
// Stack pointer `sp`
//
// Set up the stack pointer for each hart, since each hart will have its own stack.
asm!("la sp, _stack_end");
// Thread pointer `tp`
//
// Set up the thread pointer, which will hold the hart id for each hart.
write_tp(&mhartid::read());
// Disable paging
satp::write(0);
// Delegate all traps to S-mode, using inline assembly since we have not
// provided a wrapper for it yet
asm!("li t0, 0xffff", "csrw medeleg, t0", "csrw mideleg, t0",);
mstatus::set_mpp(mstatus::MPP::Machine);
mstatus::set_fs(sstatus::FS::Initial);
// Enable S-mode external, timer, and software interrupts
sie::set_sext();
sie::set_stimer();
sie::set_ssoft();
asm!(
"csrw mie, x0",
// TODO configure PMP
// TODO timer init
"la t1, {kinit}",
"csrw mepc, t1",
// Use mret to jump to main
asm!("mret");
"la ra, 2f",
"mret",
"2:",
kinit = sym kinit,
);
}
extern "C" fn main() {
use riscv::interrupt;
use riscv::register::{mstatus, sstatus};
if hartid() == 0 {
READY.call_once(|| {
// Disable machine interrupts while initializing
interrupt::machine::disable();
// TODO Initialize console
// TODO Write boot message
// TODO Set up paging
// TODO Set up processes
// TODO Set up trap vectors
// TODO Set up PLIC
kinit();
unsafe {
mstatus::set_mpp(mstatus::MPP::User);
mstatus::set_mpie();
mstatus::set_spie();
mstatus::set_fs(sstatus::FS::Initial);
}
riscv::asm::fence();
});
} else {
READY.wait();
kinit_hart();
}
#[no_mangle]
extern "C" fn start() {
// TODO start (start for hart 0)
}
#[no_mangle]
extern "C" fn hstart() {
// TODO starth (start for other harts)
}
// TODO ...then kinit
#[no_mangle]
extern "C" fn kinit() {
use crate::uart;
use core::arch::asm;
uart::Device::new(0x1000_0000);
if read_tp() == 0 {
// Clear bss and set stack pointer
// Only hart 0 does this
unsafe {
asm!(
"la t0, _bss_start",
"la t1, _bss_end",
"bgeu t0, t1, 2f",
"1:",
"sw x0, (t0)", // Store zero as word at t0
"addi t0, t0, 4",
"bne t0, t1, 1b",
"2:",
);
}
}
}
#[no_mangle]
extern "C" fn kinit_hart() {}
#[inline]
fn hartid() -> usize {
fn read_tp() -> usize {
use core::arch::asm;
let id: usize;
unsafe {
@ -91,3 +107,14 @@ fn hartid() -> usize {
}
id
}
#[inline]
fn write_tp(id: &usize) {
use core::arch::asm;
unsafe {
asm!(
"csrw mhartid, {id}",
id = in(reg) id,
);
}
}

View file

@ -1,37 +0,0 @@
# entry.S
.option norvc
.section .data
.section .text.init
.global _entry_alt
_entry_alt:
# Set global pointer (gp)
# Important to have relaxation off for this instruction
.option push
.option norelax
la gp, _global_pointer
.option pop
# Set thread pointer (tp) to hart id
csrr tp, mhartid
# Set stack pointer (sp) for all harts
la sp, _stack_end
li t0, 0x10000 # Give each hart plenty of space for their stacks
mul t0, t0, tp
sub sp, sp, t0
# Jump to start if not hart 0
bnez tp, 2f
# Prepare BSS section if hart 0
la t0, _bss_start
la t1, _bss_end
bgeu t0, t1, 2f
1:
sb zero, 0(t0)
addi t0, t0, 1
bne t0, t1, 1b
2:
# Tail call Rust start function
tail start

View file

@ -10,9 +10,79 @@ mod heap;
mod trap;
mod uart;
core::arch::global_asm!(include_str!("entry.S"));
#[naked]
#[no_mangle]
#[link_section = ".text.init"]
unsafe extern "C" fn _enter() -> ! {
// FIXME see if possible to replace this somehow...
use core::arch::asm;
asm!(
// load hartid into `tp``
// if hartid =/= 0, then jump to busy loop
"csrr tp, mhartid",
"bnez tp, 3f",
// before we use the `la` pseudo-instruction for the first time,
// we need to set `gp` (look up linker relaxation)
".option push", // pushes the current option stack
".option norelax", // disable relaxation so we can properly set `gp`
// this instruction compiles to:
// 1:
// auipc gp, %pcrel_hi(_global_pointer)
// addi gp, gp, %pcrel_lo(1b)
// if relaxation were on, this would simply be `mv gp, gp` or `addi gp, gp, 0`
"la gp, _global_pointer",
".option pop", // pops the current option stack
// delegate all traps to S-mode trap handler
"li t5, 0xffff",
"csrw medeleg, t5", // delegate all machine exceptions
"csrw mideleg, t5", // delegate all machine interrupts
// set the stack pointer
"la sp, _stack_end",
// Make sure machine mode is set, and enable coarse interrupts
"li t0, (0b11 << 11) | (1 << 7) | (1 << 3)",
"csrw mstatus, t0",
// Set mtvec to the location of our trap handler function
"la t1, {trap_vector}",
"csrw mtvec, t1",
// Set MSIE, MTIE, and MEIE on machine interrupt enable CSR:
// (1 << 3) = MSIE to enable machine-/M-mode software interrupts
// | (1 << 7) = MTIE to enable M-mode timer interrupts (disabled for now)
// | (1 << 11) = MEIE to enable M-mode external interrupts
"li t2, (1 << 3) | (1 << 11)",
"csrw mie, t2",
// clear the BSS
"la t0, _bss_start",
"la t1, _bss_end",
"bgeu t0, t1, 2f",
"1:",
"sb zero, 0(t0)",
"addi t0, t0, 1",
"bne t0, t1, 1b",
"2:",
"j 4f",
// BSS is clear!
// busy loop if hartid =/= 0
"3:",
"wfi", // wait for interrupt (or nop)
"j 3b",
"4:",
// "tail-call" to {start} (call without saving a return address)
"tail {start}",
start = sym start, // {start} refers to the function [start] below
trap_vector = sym trap::trap_handler, // {trap_vector} refers to function [trap::trap_handler]
options(noreturn) // we must handle "returning" from assembly
);
}
extern "C" fn start() -> ! {
// UNSAFE: Called exactly once, right here.
unsafe { heap::init() };
@ -55,4 +125,4 @@ extern "C" fn abort() -> ! {
loop { riscv::asm::wfi(); }
}
// TODO unit testing
// TODO unit testing

View file

@ -1,7 +1,7 @@
/* lds/kernel.lds */
/* src/script.lds */
OUTPUT_ARCH("riscv")
ENTRY(_entry)
ENTRY(_enter)
MEMORY {
ram (wxa) : ORIGIN = 0x80000000, LENGTH = 128M

View file

@ -1,9 +1,9 @@
// src/uart.rs
//! This module provides access to the UART console.
use spin::Mutex; // Replaces spinning_top crate
use spinning_top::Spinlock;
pub static CONSOLE: Mutex<Option<Device>> = Mutex::new(None);
pub static CONSOLE: Spinlock<Option<Device>> = Spinlock::new(None);
/// Represents an initialised UART device.
pub struct Device {
@ -65,7 +65,7 @@ impl core::fmt::Write for Device {
/// # Safety
/// `base` must point to the base address of a UART device.
pub fn init_console(base: usize) {
let mut console = CONSOLE.lock();
let mut console: spinning_top::lock_api::MutexGuard<spinning_top::RawSpinlock, Option<Device>> = CONSOLE.lock();
*console = Some(Device::new(base));
}