Switch to newly refactored init process, update readme

This commit is contained in:
gil 2024-05-21 15:17:15 -05:00
parent eee7c6f02b
commit 2aad0977f1
6 changed files with 147 additions and 87 deletions

BIN
20240521-hello_harts.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -15,6 +15,10 @@ A kernel for RISC-V written in Rust. Currently focused on running on QEMU generi
![A flowchart showing the kernel initialization process. On the left column are hart 0-specific steps, on the right are steps for other harts. The first stage happens in the _entry code. 1. Setting global, stack, and thread pointers. 2. Initializing the BSS (main hart only). The second stage shows the start code, where both the main hart and the other harts prepare CSRs for S-mode interrupts. This stage is also where PMP and timers would be set up. The final stage is for the main function, where the main hart (hart 0) initializes the console, paging system, processing system, and interrupts before the first user process. While hart 0 is initializing, the other harts wait for it to finish. Once hart 0 is done, the other harts will enable paging and interrupts, and all harts will enter the scheduler.](20240520-kernel_init_diagram.png)
* **2024-05-21:** I've removed the original `entry.s` and replaced it with what was formerly `entry_alt.s`. The new start() and main() functions are based on the implementations in the xv6 kernel. Next steps are setting up the page tables and process tables, so that everything works better.
![A picture of the current console output as of May 21, 2024. Each hart prints a message which says "Hello from hart" followed by the hart id.](20240521-hello_harts.png)
## Research and implement
- [ ] Basics

View file

@ -1,32 +1,30 @@
# entry.S
# 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
# Set global pointer (gp)
# Important to have relaxation off for this instruction
.option push
.option norelax
la gp, _global_pointer
la gp, _global_pointer
.option pop
li t5, 0xffff
csrw medeleg, t5
csrw mideleg, t5
# 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
li t0, (0b11 << 11) | (1 << 7) | (1 << 3)
csrw mstatus, 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
@ -35,9 +33,5 @@ _entry:
addi t0, t0, 1
bne t0, t1, 1b
2:
j 4f
3:
wfi
j 3b
4:
# Tail call Rust start function
tail start

View file

@ -1,7 +1,16 @@
// src/entry.rs
#![no_std]
#![no_main]
use crate::uart;
mod heap;
mod uart;
static INIT_LOCK: spin::Once<()> = spin::Once::new();
core::arch::global_asm!(include_str!("entry.s"));
/// After some initialization in asm/entry.S, the kernel will jump here and
/// each hart will have its own setup sequence.
#[no_mangle]
@ -45,7 +54,7 @@ extern "C" fn main() {
INIT_LOCK.call_once(|| {
// Disable machine interrupts while initializing
interrupt::machine::disable();
// TODO Initialize console
console_init();
// TODO Write boot message
// TODO Set up paging
@ -64,6 +73,12 @@ extern "C" fn main() {
riscv::asm::fence(); // Emit a fence just in case
});
// poll console for input and print characters back
loop {
let c = crate::uart::CONSOLE.lock().as_mut().and_then(crate::uart::Device::get);
if let Some(c) = c { crate::print!("{}", c as char); }
}
} else {
INIT_LOCK.wait();
riscv::asm::fence(); // Emit a fence just in case
@ -71,14 +86,18 @@ extern "C" fn main() {
}
}
#[no_mangle]
extern "C" fn kinit() {
use crate::uart;
uart::Device::new(0x1000_0000);
fn console_init() {
// Initialize heap
unsafe { crate::heap::init(); };
// Set up UART and print to console
crate::uart::init_console(0x1000_0000);
crate::println!("Hello from hart {}!", hartid());
}
#[no_mangle]
extern "C" fn kinit_hart() {}
fn kinit() {}
fn kinit_hart() {}
#[inline]
fn hartid() -> usize {

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

@ -5,40 +5,120 @@
use core::panic::PanicInfo;
mod entry;
mod heap;
mod trap;
mod uart;
core::arch::global_asm!(include_str!("entry.S"));
static INIT_LOCK: spin::Once<()> = spin::Once::new();
core::arch::global_asm!(include_str!("entry.s"));
/// After some initialization in asm/entry.s, the kernel will jump here and
/// each hart will have its own setup sequence.
#[no_mangle]
extern "C" fn start() -> ! {
// UNSAFE: Called exactly once, right here.
unsafe { heap::init() };
unsafe extern "C" fn start() {
use core::arch::asm;
use riscv::register::{mepc, mstatus, satp, sie, sstatus};
// Now we're free to use dynamic allocation!
// {
// extern crate alloc;
// use alloc::boxed::Box;
// let _my_heap_pointer = Box::new(10);
// // _my_heap_pointer lives on the heap!
// }
// 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);
// println!("This should not print because the console is not initialised.");
uart::init_console(0x1000_0000);
println!("Hello, world!");
// Disable paging
satp::write(0);
// print current hartid
println!("hartid: {}", riscv::register::mhartid::read());
// 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",);
// poll console for input and print characters back
loop {
let c = uart::CONSOLE.lock().as_mut().and_then(uart::Device::get);
if let Some(c) = c { print!("{}", c as char); }
// Enable S-mode external, timer, and software interrupts
sie::set_sext();
sie::set_stimer();
sie::set_ssoft();
// TODO configure PMP
// TODO timer init
// Trying to figure out why I can't use mret here. Might need
// asm!(
// "la t1, 1f",
// "csrw mepc, t1",
// "la ra, 1f",
// "mret",
// "1:",
// );
main();
}
fn main() {
use riscv::interrupt;
use riscv::register::{mstatus, sstatus};
if hartid() == 0 {
INIT_LOCK.call_once(|| {
// Disable machine interrupts while initializing
interrupt::machine::disable();
console_init();
// 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(); // Emit a fence just in case
});
// poll console for input and print characters back
loop {
let c = uart::CONSOLE.lock().as_mut().and_then(uart::Device::get);
if let Some(c) = c { print!("{}", c as char); }
}
} else {
INIT_LOCK.wait();
riscv::asm::fence(); // Emit a fence just in case
kinit_hart();
println!("Hello from hart {}!", hartid());
}
}
fn console_init() {
// Initialize heap
unsafe { heap::init(); };
// Set up UART and print to console
uart::init_console(0x1000_0000);
println!("Hello from hart {}!", hartid());
}
fn kinit() {}
fn kinit_hart() {}
#[inline]
fn hartid() -> usize {
use core::arch::asm;
let id: usize;
unsafe {
asm!(
"mv {id}, tp",
id = out(reg) id,
);
}
id
}
#[no_mangle]
extern "C" fn eh_personality() {}