diff --git a/Makefile b/Makefile index 59a8e6e..9715e5b 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,6 @@ U=user OBJS = \ $K/entry.o \ - $K/start.o \ $K/console.o \ $K/printf.o \ $K/kalloc.o \ diff --git a/kernel/entry.S b/kernel/entry.S index 5ab365e..574dfd0 100644 --- a/kernel/entry.S +++ b/kernel/entry.S @@ -5,8 +5,8 @@ .section .text .global _entry _entry: - # set up a stack for C. - # stack0 is declared in start.c, + # set up a stack for Rust + # stack0 is declared in start.rs, # with a 4096-byte stack per CPU. # sp = stack0 + (hartid * 4096) la sp, stack0 @@ -15,7 +15,7 @@ _entry: addi a1, a1, 1 mul a0, a0, a1 add sp, sp, a0 - # jump to start() in start.c + # jump to start() in start.rs call start spin: j spin diff --git a/kernel/start.c b/kernel/start.c deleted file mode 100644 index f83b619..0000000 --- a/kernel/start.c +++ /dev/null @@ -1,85 +0,0 @@ -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "riscv.h" -#include "defs.h" - -void main(); -void timerinit(); - -// entry.S needs one stack per CPU. -__attribute__((aligned(16))) char stack0[4096 * NCPU]; - -// a scratch area per CPU for machine-mode timer interrupts. -uint64 timer_scratch[NCPU][5]; - -// assembly code in kernelvec.S for machine-mode timer interrupt. -extern void timervec(); - -// entry.S jumps here in machine mode on stack0. -void start() { - // set M Previous Privilege mode to Supervisor, for mret. - unsigned long x = r_mstatus(); - x &= ~MSTATUS_MPP_MASK; - x |= MSTATUS_MPP_S; - w_mstatus(x); - - // set M Exception Program Counter to main, for mret. - // requires gcc -mcmodel=medany - w_mepc((uint64)main); - - // disable paging for now. - w_satp(0); - - // delegate all interrupts and exceptions to supervisor mode. - w_medeleg(0xffff); - w_mideleg(0xffff); - w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE); - - // configure Physical Memory Protection to give supervisor mode - // access to all of physical memory. - w_pmpaddr0(0x3fffffffffffffull); - w_pmpcfg0(0xf); - - // ask for clock interrupts. - timerinit(); - - // keep each CPU's hartid in its tp register, for cpuid(). - int id = r_mhartid(); - w_tp(id); - - // switch to supervisor mode and jump to main(). - asm volatile("mret"); -} - -// arrange to receive timer interrupts. -// they will arrive in machine mode at -// at timervec in kernelvec.S, -// which turns them into software interrupts for -// devintr() in trap.c. -void timerinit() { - // each CPU has a separate source of timer interrupts. - int id = r_mhartid(); - - // ask the CLINT for a timer interrupt. - int interval = 1000000; // cycles; about 1/10th second in qemu. - *(uint64 *)CLINT_MTIMECMP(id) = *(uint64 *)CLINT_MTIME + interval; - - // prepare information in scratch[] for timervec. - // scratch[0..2] : space for timervec to save registers. - // scratch[3] : address of CLINT MTIMECMP register. - // scratch[4] : desired interval (in cycles) between timer interrupts. - uint64 *scratch = &timer_scratch[id][0]; - scratch[3] = CLINT_MTIMECMP(id); - scratch[4] = interval; - w_mscratch((uint64)scratch); - - // set the machine-mode trap handler. - w_mtvec((uint64)timervec); - - // enable machine-mode interrupts. - w_mstatus(r_mstatus() | MSTATUS_MIE); - - // enable machine-mode timer interrupts. - w_mie(r_mie() | MIE_MTIE); -} diff --git a/theseus/src/lib.rs b/theseus/src/lib.rs index 4f91dd2..59c5e02 100644 --- a/theseus/src/lib.rs +++ b/theseus/src/lib.rs @@ -2,8 +2,10 @@ #![crate_type = "staticlib"] pub mod memlayout; +pub mod param; pub mod riscv; pub mod spinlock; +pub mod start; pub mod uart; extern "C" { diff --git a/theseus/src/memlayout.rs b/theseus/src/memlayout.rs index 114ca5d..4e34cdf 100644 --- a/theseus/src/memlayout.rs +++ b/theseus/src/memlayout.rs @@ -28,13 +28,12 @@ pub const VIRTIO0: i32 = 0x10001000; pub const VIRTIO0_IRQ: i32 = 1; // core local interruptor (CLINT), which contains the timer. -pub const CLINT: i64 = 0x2000000; -// const CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 8 * (hartid)); +pub const CLINT: u64 = 0x2000000; #[inline] -pub fn CLINT_MTIMECMP(hartid: i32) -> i64 { - CLINT + 0x4000 + 8 * hartid as i64 +pub fn CLINT_MTIMECMP(hartid: u64) -> *mut u64 { + (CLINT + 0x4000 + 8 * hartid) as *mut u64 } -pub const CLINT_MTIME: i64 = CLINT + 0xBFF8; // cycles since boot. +pub const CLINT_MTIME: *mut u64 = (CLINT + 0xBFF8) as *mut u64; // cycles since boot. // qemu puts platform-level interrupt controller (PLIC) here. pub const PLIC: i64 = 0x0c000000; diff --git a/theseus/src/param.rs b/theseus/src/param.rs new file mode 100644 index 0000000..17b1369 --- /dev/null +++ b/theseus/src/param.rs @@ -0,0 +1,13 @@ +pub const NPROC: usize = 64; // maximum number of processes +pub const NCPU: usize = 8; // maximum number of CPUs +pub const NOFILE: usize = 16; // open files per process +pub const NFILE: usize = 100; // open files per system +pub const NINODE: usize = 50; // maximum number of active i-nodes +pub const NDEV: usize = 10; // maximum major device number +pub const ROOTDEV: usize = 1; // device number of file system root disk +pub const MAXARG: usize = 32; // max exec arguments +pub const MAXOPBLOCKS: usize = 10; // max # of blocks any FS op writes +pub const LOGSIZE: usize = MAXOPBLOCKS * 3; // max data blocks in on-disk log +pub const NBUF: usize = MAXOPBLOCKS * 3; // size of disk block cache +pub const FSSIZE: usize = 2000; // size of file system in blocks +pub const MAXPATH: usize = 128; // maximum file path name diff --git a/theseus/src/riscv.rs b/theseus/src/riscv.rs index 4a16823..94994d2 100644 --- a/theseus/src/riscv.rs +++ b/theseus/src/riscv.rs @@ -1,3 +1,282 @@ +use core::arch::asm; + +// which hart (core) is this? +#[inline] +pub unsafe fn r_mhartid() -> u64 { + let x: u64; + asm!("csrr {x}, mhartid", x = out(reg) x); + x +} + +// Machine Status Register, mstatus +pub const MSTATUS_MPP_MASK: u64 = 3 << 11; // previous mode. +pub const MSTATUS_MPP_M: u64 = 3 << 11; +pub const MSTATUS_MPP_S: u64 = 1 << 11; +pub const MSTATUS_MPP_U: u64 = 0 << 11; +pub const MSTATUS_MIE: u64 = 1 << 3; // machine-mode interrupt enable. + +#[inline] +pub unsafe fn r_mstatus() -> u64 { + let x: u64; + asm!("csrr {x}, mstatus", x = out(reg) x); + x +} + +#[inline] +pub unsafe fn w_mstatus(x: u64) { + asm!("csrw mstatus, {x}", x = in(reg) x); +} + +// // machine exception program counter, holds the +// // instruction address to which a return from +// // exception will go. +#[inline] +pub unsafe fn w_mepc(x: u64) { + asm!("csrw mepc, {x}", x = in(reg) x); +} + +// // Supervisor Status Register, sstatus + +// #define SSTATUS_SPP (1L << 8) // Previous mode, 1=Supervisor, 0=User +// #define SSTATUS_SPIE (1L << 5) // Supervisor Previous Interrupt Enable +// #define SSTATUS_UPIE (1L << 4) // User Previous Interrupt Enable +// #define SSTATUS_SIE (1L << 1) // Supervisor Interrupt Enable +// #define SSTATUS_UIE (1L << 0) // User Interrupt Enable + +#[inline] +pub unsafe fn r_sstatus() -> u64 { + let x: u64; + asm!("csrr {x}, sstatus", x = out(reg) x); + x +} + +#[inline] +pub unsafe fn w_sstatus(x: u64) { + asm!("csrw sstatus, {x}", x = in(reg) x); +} + +// // Supervisor Interrupt Pending +#[inline] +pub unsafe fn r_sip() -> u64 { + let x: u64; + asm!("csrr {x}, sip", x = out(reg) x); + x +} + +// static inline void w_sip(uint64 x) { asm volatile("csrw sip, %0" : : "r"(x)); } +#[inline] +pub unsafe fn w_sip(x: u64) { + asm!("csrw sip, {x}", x = in(reg) x); +} + +// Supervisor Interrupt Enable +pub const SIE_SEIE: u64 = 1 << 9; // external +pub const SIE_STIE: u64 = 1 << 5; // timer +pub const SIE_SSIE: u64 = 1 << 1; // software + +// static inline uint64 r_sie() { +// uint64 x; +// asm volatile("csrr %0, sie" : "=r"(x)); +// return x; +// } +#[inline] +pub unsafe fn r_sie() -> u64 { + let x: u64; + asm!("csrr {x}, sie", x = out(reg) x); + x +} + +// static inline void w_sie(uint64 x) { asm volatile("csrw sie, %0" : : "r"(x)); } +#[inline] +pub unsafe fn w_sie(x: u64) { + asm!("csrw sie, {x}", x = in(reg) x); +} + +// // Machine-mode Interrupt Enable +pub const MIE_MEIE: u64 = 1 << 11; // external +pub const MIE_MTIE: u64 = 1 << 7; // timer +pub const MIE_MSIE: u64 = 1 << 3; // software + +#[inline] +pub unsafe fn r_mie() -> u64 { + let x: u64; + asm!("csrr {x}, mie", x = out(reg) x); + x +} + +#[inline] +pub unsafe fn w_mie(x: u64) { + asm!("csrw mie, {x}", x = in(reg) x); +} + +// // supervisor exception program counter, holds the +// // instruction address to which a return from +// // exception will go. +// static inline void w_sepc(uint64 x) { +// asm volatile("csrw sepc, %0" : : "r"(x)); +// } + +// static inline uint64 r_sepc() { +// uint64 x; +// asm volatile("csrr %0, sepc" : "=r"(x)); +// return x; +// } + +// // Machine Exception Delegation +// static inline uint64 r_medeleg() { +// uint64 x; +// asm volatile("csrr %0, medeleg" : "=r"(x)); +// return x; +// } + +#[inline] +pub unsafe fn w_medeleg(x: u64) { + asm!("csrw medeleg, {x}", x = in(reg) x); +} + +// // Machine Interrupt Delegation +// static inline uint64 r_mideleg() { +// uint64 x; +// asm volatile("csrr %0, mideleg" : "=r"(x)); +// return x; +// } + +#[inline] +pub unsafe fn w_mideleg(x: u64) { + asm!("csrw mideleg, {x}", x = in(reg) x); +} + +// // Supervisor Trap-Vector Base Address +// // low two bits are mode. +// static inline void w_stvec(uint64 x) { +// asm volatile("csrw stvec, %0" : : "r"(x)); +// } + +// static inline uint64 r_stvec() { +// uint64 x; +// asm volatile("csrr %0, stvec" : "=r"(x)); +// return x; +// } + +// // Machine-mode interrupt vector +#[inline] +pub unsafe fn w_mtvec(x: u64) { + asm!("csrw mtvec, {x}", x = in(reg) x); +} + +// Physical Memory Protection +#[inline] +pub unsafe fn w_pmpcfg0(x: u64) { + asm!("csrw pmpcfg0, {x}", x = in(reg) x); +} + +#[inline] +pub unsafe fn w_pmpaddr0(x: u64) { + asm!("csrw pmpaddr0, {x}", x = in(reg) x); +} + +// // use riscv's sv39 page table scheme. +// #define SATP_SV39 (8L << 60) + +// #define MAKE_SATP(pagetable) (SATP_SV39 | (((uint64)pagetable) >> 12)) + +// supervisor address translation and protection; +// holds the address of the page table. +#[inline] +pub unsafe fn w_satp(x: u64) { + asm!("csrw satp, {x}", x = in(reg) x); +} + +// static inline uint64 r_satp() { +// uint64 x; +// asm volatile("csrr %0, satp" : "=r"(x)); +// return x; +// } + +#[inline] +pub unsafe fn w_mscratch(x: u64) { + asm!("csrw mscratch, {x}", x = in(reg) x); +} + +// // Supervisor Trap Cause +// static inline uint64 r_scause() { +// uint64 x; +// asm volatile("csrr %0, scause" : "=r"(x)); +// return x; +// } + +// // Supervisor Trap Value +// static inline uint64 r_stval() { +// uint64 x; +// asm volatile("csrr %0, stval" : "=r"(x)); +// return x; +// } + +// // Machine-mode Counter-Enable +// static inline void w_mcounteren(uint64 x) { +// asm volatile("csrw mcounteren, %0" : : "r"(x)); +// } + +// static inline uint64 r_mcounteren() { +// uint64 x; +// asm volatile("csrr %0, mcounteren" : "=r"(x)); +// return x; +// } + +// // machine-mode cycle counter +// static inline uint64 r_time() { +// uint64 x; +// asm volatile("csrr %0, time" : "=r"(x)); +// return x; +// } + +// // enable device interrupts +// static inline void intr_on() { w_sstatus(r_sstatus() | SSTATUS_SIE); } + +// // disable device interrupts +// static inline void intr_off() { w_sstatus(r_sstatus() & ~SSTATUS_SIE); } + +// // are device interrupts enabled? +// static inline int intr_get() { +// uint64 x = r_sstatus(); +// return (x & SSTATUS_SIE) != 0; +// } + +// static inline uint64 r_sp() { +// uint64 x; +// asm volatile("mv %0, sp" : "=r"(x)); +// return x; +// } + +// // read and write tp, the thread pointer, which xv6 uses to hold +// // this core's hartid (core number), the index into cpus[]. +// static inline uint64 r_tp() { +// uint64 x; +// asm volatile("mv %0, tp" : "=r"(x)); +// return x; +// } + +// static inline void w_tp(uint64 x) { asm volatile("mv tp, %0" : : "r"(x)); } +#[inline] +pub unsafe fn w_tp(x: u64) { + asm!("mv tp, {x}", x = in(reg) x); +} + +// static inline uint64 r_ra() { +// uint64 x; +// asm volatile("mv %0, ra" : "=r"(x)); +// return x; +// } + +// // flush the TLB. +// static inline void sfence_vma() { +// // the zero, zero means flush all TLB entries. +// asm volatile("sfence.vma zero, zero"); +// } + +// typedef uint64 pte_t; +// typedef uint64 *pagetable_t; // 512 PTEs + pub const PGSIZE: u64 = 4096; // bytes per page pub const PGSHIFT: i32 = 12; // bits of offset within a page diff --git a/theseus/src/start.rs b/theseus/src/start.rs new file mode 100644 index 0000000..9f095d6 --- /dev/null +++ b/theseus/src/start.rs @@ -0,0 +1,94 @@ +use core::arch::asm; +use core::usize; + +use crate::memlayout::{CLINT_MTIME, CLINT_MTIMECMP}; +use crate::param::NCPU; +use crate::riscv as rv; + +extern "C" { + // assembly code in kernelvec.S for machine-mode timer interrupt. + fn timervec(); + + // main.c + fn main(); +} + +#[repr(align(16))] +pub struct EntryStack([u8; 4096 * NCPU]); + +// entry.S needs one stack per CPU. +#[no_mangle] +pub static mut stack0: EntryStack = EntryStack([0; 4096 * NCPU]); + +// a scratch area per CPU for machine-mode timer interrupts. +static mut timer_scratch: [[u64; 5]; NCPU] = [[0; 5]; NCPU]; + +// entry.S jumps here in machine mode on stack0. +#[no_mangle] +pub unsafe extern "C" fn start() { + // set M Previous Privilege mode to Supervisor, for mret. + let mut x = rv::r_mstatus(); + x &= !rv::MSTATUS_MPP_MASK; + x |= rv::MSTATUS_MPP_S; + rv::w_mstatus(x); + + // set M Exception Program Counter to main, for mret. + // requires gcc -mcmodel=medany + rv::w_mepc(main as *const () as u64); + + // disable paging for now. + rv::w_satp(0); + + // delegate all interrupts and exceptions to supervisor mode. + rv::w_medeleg(0xffff); + rv::w_mideleg(0xffff); + rv::w_sie(rv::r_sie() | rv::SIE_SEIE | rv::SIE_STIE | rv::SIE_SSIE); + + // configure Physical Memory Protection to give supervisor mode + // access to all of physical memory. + rv::w_pmpaddr0(0x3fffffffffffff); + rv::w_pmpcfg0(0xf); + + // ask for clock interrupts. + timerinit(); + + // keep each CPU's hartid in its tp register, for cpuid(). + let id = rv::r_mhartid(); + rv::w_tp(id); + + // switch to supervisor mode and jump to main(). + asm!("mret"); +} + +// arrange to receive timer interrupts. +// they will arrive in machine mode at +// at timervec in kernelvec.S, +// which turns them into software interrupts for +// devintr() in trap.c. +unsafe fn timerinit() { + // each CPU has a separate source of timer interrupts. + let id = rv::r_mhartid(); + + // ask the CLINT for a timer interrupt. + let interval: u64 = 1000000; // cycles; about 1/10th second in qemu. + *CLINT_MTIMECMP(id) = *CLINT_MTIME + interval; + + // prepare information in scratch[] for timervec. + // scratch[0..2] : space for timervec to save registers. + // scratch[3] : address of CLINT MTIMECMP register. + // scratch[4] : desired interval (in cycles) between timer interrupts. + timer_scratch[id as usize][3] = CLINT_MTIMECMP(id) as u64; + timer_scratch[id as usize][4] = interval; + + // w_mscratch((uint64)scratch); + rv::w_mscratch(timer_scratch[id as usize].as_ptr() as u64); + + // set the machine-mode trap handler. + rv::w_mtvec(timervec as *const () as u64); + + // enable machine-mode interrupts. + rv::w_mstatus(rv::r_mstatus() | rv::MSTATUS_MIE); + + // enable machine-mode timer interrupts. + rv::w_mie(rv::r_mie() | rv::MIE_MTIE); +}