diff --git a/Makefile b/Makefile index 59a8e6e..de1e684 100644 --- a/Makefile +++ b/Makefile @@ -3,13 +3,11 @@ U=user OBJS = \ $K/entry.o \ - $K/start.o \ $K/console.o \ $K/printf.o \ $K/kalloc.o \ $K/spinlock.o \ $K/string.o \ - $K/main.o \ $K/vm.o \ $K/proc.o \ $K/swtch.o \ diff --git a/kernel/main.c b/kernel/main.c deleted file mode 100644 index cab0e2a..0000000 --- a/kernel/main.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "riscv.h" -#include "defs.h" - -volatile static int started = 0; - -// start() jumps here in supervisor mode on all CPUs. -void main() { - if (cpuid() == 0) { - consoleinit(); - printfinit(); - printf("\n"); - printf("xv6 kernel is booting\n"); - printf("\n"); - kinit(); // physical page allocator - kvminit(); // create kernel page table - kvminithart(); // turn on paging - procinit(); // process table - trapinit(); // trap vectors - trapinithart(); // install kernel trap vector - plicinit(); // set up interrupt controller - plicinithart(); // ask PLIC for device interrupts - binit(); // buffer cache - iinit(); // inode table - fileinit(); // file table - virtio_disk_init(); // emulated hard disk - userinit(); // first user process - __sync_synchronize(); - started = 1; - } else { - while (started == 0) - ; - __sync_synchronize(); - printf("hart %d starting\n", cpuid()); - kvminithart(); // turn on paging - trapinithart(); // install kernel trap vector - plicinithart(); // ask PLIC for device interrupts - } - - scheduler(); -} diff --git a/kernel/spinlock.c b/kernel/spinlock.c index aaf1b09..51252cf 100644 --- a/kernel/spinlock.c +++ b/kernel/spinlock.c @@ -8,12 +8,6 @@ #include "proc.h" #include "defs.h" -void initlock(struct spinlock *lk, char *name) { - lk->name = name; - lk->locked = 0; - lk->cpu = 0; -} - // Acquire the lock. // Loops (spins) until the lock is acquired. void acquire(struct spinlock *lk) { diff --git a/kernel/start.c b/kernel/start.c deleted file mode 100644 index 74d8a4f..0000000 --- a/kernel/start.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "riscv.h" -#include "defs.h" - -void timerinit(); - -// 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(); - -// 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/kmain.rs b/theseus/src/kmain.rs new file mode 100644 index 0000000..583b4c2 --- /dev/null +++ b/theseus/src/kmain.rs @@ -0,0 +1,58 @@ +use crate::riscv; + +extern "C" { + fn cpuid() -> u64; + fn consoleinit(); + fn printfinit(); + // fn printf(); + fn kinit(); + fn kvminit(); + fn kvminithart(); + fn procinit(); + fn trapinit(); + fn trapinithart(); + fn plicinit(); + fn plicinithart(); + fn binit(); + fn iinit(); + fn fileinit(); + fn virtio_disk_init(); + fn userinit(); + fn scheduler(); +} + +pub static mut started: bool = false; + +pub unsafe fn kmain() { + if cpuid() == 0 { + consoleinit(); + printfinit(); + // printf("\n"); + // printf("xv6 kernel is booting\n"); + // printf("\n"); + kinit(); // physical page allocator + kvminit(); // create kernel page table + kvminithart(); // turn on paging + procinit(); // process table + trapinit(); // trap vectors + trapinithart(); // install kernel trap vector + plicinit(); // set up interrupt controller + plicinithart(); // ask PLIC for device interrupts + binit(); // buffer cache + iinit(); // inode table + fileinit(); // file table + virtio_disk_init(); // emulated hard disk + userinit(); // first user process + riscv::barrier(); + started = true; + } else { + while !started {} + riscv::barrier(); + // printf("hart %d starting\n", cpuid()); + kvminithart(); // turn on paging + trapinithart(); // install kernel trap vector + plicinithart(); // ask PLIC for device interrupts + } + + scheduler(); +} diff --git a/theseus/src/lib.rs b/theseus/src/lib.rs index 59c5e02..359c7a4 100644 --- a/theseus/src/lib.rs +++ b/theseus/src/lib.rs @@ -1,6 +1,7 @@ #![no_std] #![crate_type = "staticlib"] +pub mod kmain; pub mod memlayout; pub mod param; pub mod riscv; 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/riscv.rs b/theseus/src/riscv.rs index 7d5d7b8..de6377c 100644 --- a/theseus/src/riscv.rs +++ b/theseus/src/riscv.rs @@ -93,16 +93,21 @@ pub unsafe fn w_sie(x: u64) { } // // Machine-mode Interrupt Enable -// #define MIE_MEIE (1L << 11) // external -// #define MIE_MTIE (1L << 7) // timer -// #define MIE_MSIE (1L << 3) // software -// static inline uint64 r_mie() { -// uint64 x; -// asm volatile("csrr %0, mie" : "=r"(x)); -// return x; -// } +pub const MIE_MEIE: u64 = 1 << 11; // external +pub const MIE_MTIE: u64 = 1 << 7; // timer +pub const MIE_MSIE: u64 = 1 << 3; // software -// static inline void w_mie(uint64 x) { asm volatile("csrw mie, %0" : : "r"(x)); } +#[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 @@ -124,9 +129,6 @@ pub unsafe fn w_sie(x: u64) { // return x; // } -// static inline void w_medeleg(uint64 x) { -// asm volatile("csrw medeleg, %0" : : "r"(x)); -// } #[inline] pub unsafe fn w_medeleg(x: u64) { asm!("csrw medeleg, {x}", x = in(reg) x); @@ -139,9 +141,6 @@ pub unsafe fn w_medeleg(x: u64) { // return x; // } -// static inline void w_mideleg(uint64 x) { -// asm volatile("csrw mideleg, %0" : : "r"(x)); -// } #[inline] pub unsafe fn w_mideleg(x: u64) { asm!("csrw mideleg, {x}", x = in(reg) x); @@ -160,22 +159,17 @@ pub unsafe fn w_mideleg(x: u64) { // } // // Machine-mode interrupt vector -// static inline void w_mtvec(uint64 x) { -// asm volatile("csrw mtvec, %0" : : "r"(x)); -// } +#[inline] +pub unsafe fn w_mtvec(x: u64) { + asm!("csrw mtvec, {x}", x = in(reg) x); +} -// // Physical Memory Protection -// static inline void w_pmpcfg0(uint64 x) { -// asm volatile("csrw pmpcfg0, %0" : : "r"(x)); -// } +// Physical Memory Protection #[inline] pub unsafe fn w_pmpcfg0(x: u64) { asm!("csrw pmpcfg0, {x}", x = in(reg) x); } -// static inline void w_pmpaddr0(uint64 x) { -// asm volatile("csrw pmpaddr0, %0" : : "r"(x)); -// } #[inline] pub unsafe fn w_pmpaddr0(x: u64) { asm!("csrw pmpaddr0, {x}", x = in(reg) x); @@ -186,11 +180,8 @@ pub unsafe fn w_pmpaddr0(x: u64) { // #define MAKE_SATP(pagetable) (SATP_SV39 | (((uint64)pagetable) >> 12)) -// // supervisor address translation and protection; -// // holds the address of the page table. -// static inline void w_satp(uint64 x) { -// asm volatile("csrw satp, %0" : : "r"(x)); -// } +// 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); @@ -202,9 +193,10 @@ pub unsafe fn w_satp(x: u64) { // return x; // } -// static inline void w_mscratch(uint64 x) { -// asm volatile("csrw mscratch, %0" : : "r"(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() { @@ -335,3 +327,9 @@ pub fn PX(level: u64, va: u64) -> u64 { // Sv39, to avoid having to sign-extend virtual addresses // that have the high bit set. pub const MAXVA: u64 = 1 << (9 + 9 + 9 + 12 - 1); + +/// Hardware memory barrier +#[inline] +pub unsafe fn barrier() { + asm!("fence") +} diff --git a/theseus/src/spinlock.rs b/theseus/src/spinlock.rs index faeafd0..cfc6ac7 100644 --- a/theseus/src/spinlock.rs +++ b/theseus/src/spinlock.rs @@ -1,9 +1,18 @@ +use core::ptr::null_mut; + // Mutual exclusion lock. #[repr(C)] pub struct SpinLock { // Is the lock held? pub locked: u32, // C boolean // For debugging: - pub name: Option<*mut ()>, // Name of lock. - pub cpu: Option<*mut ()>, // The cpu holding the lock. + pub name: *const u8, // Name of lock. + pub cpu: *mut (), // The cpu holding the lock. +} + +#[no_mangle] +pub unsafe extern "C" fn initlock(lock: *mut SpinLock, name: *const u8) { + (*lock).name = name; + (*lock).locked = 0; + (*lock).cpu = null_mut(); } diff --git a/theseus/src/start.rs b/theseus/src/start.rs index 5bd0673..4cb49c1 100644 --- a/theseus/src/start.rs +++ b/theseus/src/start.rs @@ -1,20 +1,25 @@ use core::arch::asm; +use core::usize; +use crate::kmain::kmain; +use crate::memlayout::{CLINT_MTIME, CLINT_MTIMECMP}; use crate::param::NCPU; use crate::riscv as rv; extern "C" { - // start.c - fn timerinit(); - - // main.c - fn main(); + // assembly code in kernelvec.S for machine-mode timer interrupt. + fn timervec(); } #[repr(align(16))] -pub struct AlignedStack([u8; 4096 * NCPU]); +pub struct EntryStack([u8; 4096 * NCPU]); + +// entry.S needs one stack per CPU. #[no_mangle] -pub static mut stack0: AlignedStack = AlignedStack([0; 4096 * NCPU]); +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] @@ -27,7 +32,7 @@ pub unsafe extern "C" fn start() { // set M Exception Program Counter to main, for mret. // requires gcc -mcmodel=medany - rv::w_mepc(main as *const () as u64); + rv::w_mepc(kmain as *const () as u64); // disable paging for now. rv::w_satp(0); @@ -52,3 +57,36 @@ pub unsafe extern "C" fn start() { // 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); +} diff --git a/theseus/src/uart.rs b/theseus/src/uart.rs index 1957f08..1fcbf26 100644 --- a/theseus/src/uart.rs +++ b/theseus/src/uart.rs @@ -1,11 +1,13 @@ -use crate::{memlayout::UART0, spinlock::SpinLock}; -use core::ptr::addr_of_mut; +use crate::{ + memlayout::UART0, + spinlock::{initlock, SpinLock}, +}; +use core::ptr::{addr_of_mut, null, null_mut}; extern "C" { // spinlock.c fn push_off(); fn pop_off(); - fn initlock(spinlock: *mut SpinLock, name: *const u8); fn acquire(spinlock: *mut SpinLock); fn release(spinlock: *mut SpinLock); @@ -60,12 +62,12 @@ pub fn WriteReg(reg: u8, v: u8) { // the transmit output buffer. static mut uart_tx_lock: crate::spinlock::SpinLock = crate::spinlock::SpinLock { locked: 0, - cpu: None, - name: None, + cpu: null_mut(), + name: null(), }; // NOTE: string 'uart\0' for c-implmentation of spinlock. Stupid but works. -static UART_LOCK_NAME: [u8; 5] = [117, 97, 114, 116, 0]; +static UART_LOCK_NAME: [u8; 5] = [0x75, 0x61, 0x72, 0x74, 0]; const UART_TX_BUF_SIZE: usize = 32; static mut uart_tx_buf: [u8; UART_TX_BUF_SIZE] = [0; UART_TX_BUF_SIZE];