Compare commits
No commits in common. "5dfd3f780fd6c5be33b50343500a9ab37db511ee" and "109afc7840b563b877b70d5540c3a8ef2cbb8224" have entirely different histories.
5dfd3f780f
...
109afc7840
|
@ -24,5 +24,4 @@ runner = """ qemu-system-riscv64
|
|||
-s
|
||||
-nographic
|
||||
-serial mon:stdio
|
||||
-bios none
|
||||
-kernel """
|
||||
-bios """
|
22
Cargo.lock
generated
22
Cargo.lock
generated
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
2
build.rs
2
build.rs
|
@ -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");
|
||||
}
|
||||
|
|
43
src/entry.S
43
src/entry.S
|
@ -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
|
141
src/entry.rs
141
src/entry.rs
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
76
src/main.rs
76
src/main.rs
|
@ -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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* lds/kernel.lds */
|
||||
/* src/script.lds */
|
||||
|
||||
OUTPUT_ARCH("riscv")
|
||||
ENTRY(_entry)
|
||||
ENTRY(_enter)
|
||||
|
||||
MEMORY {
|
||||
ram (wxa) : ORIGIN = 0x80000000, LENGTH = 128M
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue