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) ![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 ## Research and implement
- [ ] Basics - [ ] Basics

View file

@ -1,32 +1,30 @@
# entry.S # entry.s
.option norvc .option norvc
.section .data .section .data
.section .text.init .section .text.init
.global _entry .global _entry
_entry: _entry:
# Any hardware threads (hart) that are not bootstrapping # Set global pointer (gp)
# need to wait for an IPI # Important to have relaxation off for this instruction
csrr t0, mhartid
bnez t0, 3f
# SATP should be zero, but let's make sure
csrw satp, zero
.option push .option push
.option norelax .option norelax
la gp, _global_pointer la gp, _global_pointer
.option pop .option pop
li t5, 0xffff # Set thread pointer (tp) to hart id
csrw medeleg, t5 csrr tp, mhartid
csrw mideleg, t5
# Set stack pointer (sp) for all harts
la sp, _stack_end 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) # Jump to start if not hart 0
csrw mstatus, t0 bnez tp, 2f
# Prepare BSS section if hart 0
la t0, _bss_start la t0, _bss_start
la t1, _bss_end la t1, _bss_end
bgeu t0, t1, 2f bgeu t0, t1, 2f
@ -35,9 +33,5 @@ _entry:
addi t0, t0, 1 addi t0, t0, 1
bne t0, t1, 1b bne t0, t1, 1b
2: 2:
j 4f # Tail call Rust start function
3:
wfi
j 3b
4:
tail start tail start

View file

@ -1,7 +1,16 @@
// src/entry.rs // src/entry.rs
#![no_std]
#![no_main]
use crate::uart;
mod heap;
mod uart;
static INIT_LOCK: spin::Once<()> = spin::Once::new(); 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 /// After some initialization in asm/entry.S, the kernel will jump here and
/// each hart will have its own setup sequence. /// each hart will have its own setup sequence.
#[no_mangle] #[no_mangle]
@ -45,7 +54,7 @@ extern "C" fn main() {
INIT_LOCK.call_once(|| { INIT_LOCK.call_once(|| {
// Disable machine interrupts while initializing // Disable machine interrupts while initializing
interrupt::machine::disable(); interrupt::machine::disable();
// TODO Initialize console console_init();
// TODO Write boot message // TODO Write boot message
// TODO Set up paging // TODO Set up paging
@ -64,6 +73,12 @@ extern "C" fn main() {
riscv::asm::fence(); // Emit a fence just in case 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 { } else {
INIT_LOCK.wait(); INIT_LOCK.wait();
riscv::asm::fence(); // Emit a fence just in case riscv::asm::fence(); // Emit a fence just in case
@ -71,14 +86,18 @@ extern "C" fn main() {
} }
} }
#[no_mangle] fn console_init() {
extern "C" fn kinit() { // Initialize heap
use crate::uart; unsafe { crate::heap::init(); };
uart::Device::new(0x1000_0000);
// Set up UART and print to console
crate::uart::init_console(0x1000_0000);
crate::println!("Hello from hart {}!", hartid());
} }
#[no_mangle] fn kinit() {}
extern "C" fn kinit_hart() {}
fn kinit_hart() {}
#[inline] #[inline]
fn hartid() -> usize { 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; use core::panic::PanicInfo;
mod entry;
mod heap; mod heap;
mod trap;
mod uart; 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] #[no_mangle]
extern "C" fn start() -> ! { unsafe extern "C" fn start() {
// UNSAFE: Called exactly once, right here. use core::arch::asm;
unsafe { heap::init() }; use riscv::register::{mepc, mstatus, satp, sie, sstatus};
// Now we're free to use dynamic allocation! // Set previous privilege for all harts to M-mode, set previous interrupt
// { // enable, and set floating-point unit to initial state
// extern crate alloc; mstatus::set_mpp(mstatus::MPP::Supervisor);
// use alloc::boxed::Box; mstatus::set_mpie();
// let _my_heap_pointer = Box::new(10); mstatus::set_fs(sstatus::FS::Initial);
// // _my_heap_pointer lives on the heap!
// }
// println!("This should not print because the console is not initialised."); // Disable paging
uart::init_console(0x1000_0000); satp::write(0);
println!("Hello, world!");
// print current hartid // Delegate all traps to S-mode, using inline assembly since we have not
println!("hartid: {}", riscv::register::mhartid::read()); // provided a wrapper for it yet
asm!("li t0, 0xffff", "csrw medeleg, t0", "csrw mideleg, t0",);
// poll console for input and print characters back // Enable S-mode external, timer, and software interrupts
loop { sie::set_sext();
let c = uart::CONSOLE.lock().as_mut().and_then(uart::Device::get); sie::set_stimer();
if let Some(c) = c { print!("{}", c as char); } 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] #[no_mangle]
extern "C" fn eh_personality() {} extern "C" fn eh_personality() {}