Switch to newly refactored init process, update readme
This commit is contained in:
parent
eee7c6f02b
commit
2aad0977f1
BIN
20240521-hello_harts.png
Normal file
BIN
20240521-hello_harts.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
|
@ -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
|
||||||
|
|
32
src/entry.S
32
src/entry.S
|
@ -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
|
33
src/entry.rs
33
src/entry.rs
|
@ -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 {
|
||||||
|
|
|
@ -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
|
|
116
src/main.rs
116
src/main.rs
|
@ -5,38 +5,118 @@
|
||||||
|
|
||||||
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",);
|
||||||
|
|
||||||
|
// 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
|
// poll console for input and print characters back
|
||||||
loop {
|
loop {
|
||||||
let c = uart::CONSOLE.lock().as_mut().and_then(uart::Device::get);
|
let c = uart::CONSOLE.lock().as_mut().and_then(uart::Device::get);
|
||||||
if let Some(c) = c { print!("{}", c as char); }
|
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]
|
||||||
|
|
Loading…
Reference in a new issue