From 96cfda564c03501625bd7b3a5a17bf80be91567d Mon Sep 17 00:00:00 2001 From: Garen Tyler Date: Fri, 20 Oct 2023 16:29:26 -0600 Subject: [PATCH] make a spinmutex (and other things) --- Makefile | 1 - kernel/console.c | 87 -------- kernel/proc.h | 4 +- kernel/rustkernel/src/buf.rs | 2 +- kernel/rustkernel/src/console.rs | 211 ++++++++---------- kernel/rustkernel/src/kalloc.rs | 2 +- kernel/rustkernel/src/lib.rs | 12 +- kernel/rustkernel/src/printf.rs | 4 +- kernel/rustkernel/src/proc.rs | 81 +++++-- kernel/rustkernel/src/sync/mod.rs | 3 + kernel/rustkernel/src/{ => sync}/sleeplock.rs | 2 +- kernel/rustkernel/src/{ => sync}/spinlock.rs | 18 +- kernel/rustkernel/src/sync/spinmutex.rs | 45 ++++ kernel/rustkernel/src/trap.rs | 42 +++- kernel/rustkernel/src/uart.rs | 201 +++++++++++------ 15 files changed, 401 insertions(+), 314 deletions(-) delete mode 100644 kernel/console.c create mode 100644 kernel/rustkernel/src/sync/mod.rs rename kernel/rustkernel/src/{ => sync}/sleeplock.rs (96%) rename kernel/rustkernel/src/{ => sync}/spinlock.rs (84%) create mode 100644 kernel/rustkernel/src/sync/spinmutex.rs diff --git a/Makefile b/Makefile index 3724d41..fa22aa4 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,6 @@ P=programs OBJS = \ $K/entry.o \ - $K/console.o \ $K/vm.o \ $K/proc.o \ $K/swtch.o \ diff --git a/kernel/console.c b/kernel/console.c deleted file mode 100644 index 8e185d5..0000000 --- a/kernel/console.c +++ /dev/null @@ -1,87 +0,0 @@ -// -// Console input and output, to the uart. -// Reads are line at a time. -// Implements special input characters: -// newline -- end of line -// control-h -- backspace -// control-u -- kill line -// control-d -- end of file -// control-p -- print process list -// - -#include - -#include "types.h" -#include "param.h" -#include "spinlock.h" -#include "sleeplock.h" -#include "fs.h" -#include "file.h" -#include "memlayout.h" -#include "riscv.h" -#include "defs.h" -#include "proc.h" - -#define BACKSPACE 0x100 -#define INPUT_BUF_SIZE 128 -#define C(x) ((x)-'@') // Control-x - -struct console { - struct spinlock lock; - - // input - char buf[INPUT_BUF_SIZE]; - uint r; // Read index - uint w; // Write index - uint e; // Edit index -}; - -extern struct console cons; -void consputc(int c); -int consolewrite(int user_src, uint64 src, int n); -int consoleread(int user_dst, uint64 dst, int n); - -void consoleintr(int c) -{ - acquire(&cons.lock); - - switch(c){ - case C('P'): // Print process list. - procdump(); - break; - case C('U'): // Kill line. - while(cons.e != cons.w && - cons.buf[(cons.e-1) % INPUT_BUF_SIZE] != '\n'){ - cons.e--; - consputc(BACKSPACE); - } - break; - case C('H'): // Backspace - case '\x7f': // Delete key - if(cons.e != cons.w){ - cons.e--; - consputc(BACKSPACE); - } - break; - default: - if(c != 0 && cons.e-cons.r < INPUT_BUF_SIZE){ - c = (c == '\r') ? '\n' : c; - - // echo back to the user. - consputc(c); - - // store for consumption by consoleread(). - cons.buf[cons.e++ % INPUT_BUF_SIZE] = c; - - if(c == '\n' || c == C('D') || cons.e-cons.r == INPUT_BUF_SIZE){ - // wake up consoleread() if a whole line (or end-of-file) - // has arrived. - cons.w = cons.e; - wakeup(&cons.r); - } - } - break; - } - - release(&cons.lock); -} diff --git a/kernel/proc.h b/kernel/proc.h index 03fe4e1..46c59df 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -22,8 +22,8 @@ struct context { struct cpu { struct proc *proc; // The process running on this cpu, or null. struct context context; // swtch() here to enter scheduler(). - int noff; // Depth of push_off() nesting. - int intena; // Were interrupts enabled before push_off()? + int interrupt_disable_layers; // Depth of push_off() nesting. + int previous_interrupts_enabled; // Were interrupts enabled before push_off()? }; extern struct cpu cpus[NCPU]; diff --git a/kernel/rustkernel/src/buf.rs b/kernel/rustkernel/src/buf.rs index 05e38e4..ba07a49 100644 --- a/kernel/rustkernel/src/buf.rs +++ b/kernel/rustkernel/src/buf.rs @@ -1,4 +1,4 @@ -use crate::{fs::BSIZE, sleeplock::Sleeplock}; +use crate::{fs::BSIZE, sync::sleeplock::Sleeplock}; #[repr(C)] pub struct Buf { diff --git a/kernel/rustkernel/src/console.rs b/kernel/rustkernel/src/console.rs index 48cba15..1ae61a0 100644 --- a/kernel/rustkernel/src/console.rs +++ b/kernel/rustkernel/src/console.rs @@ -10,66 +10,73 @@ use crate::{ file::{devsw, CONSOLE}, - proc::{killed, myproc, sleep}, - spinlock::{initlock, Spinlock}, + proc::{killed, myproc, sleep_mutex, wakeup, procdump}, + sync::spinmutex::SpinMutex, uart::{uartinit, uartputc, uartputc_sync}, }; use core::{ - ffi::{c_void, CStr}, + ffi::c_void, ptr::addr_of_mut, }; extern "C" { fn either_copyin(dst: *mut c_void, user_src: i32, src: u64, len: u64) -> i32; fn either_copyout(user_dst: i32, dst: u64, src: *mut c_void, len: u64) -> i32; - - pub fn consoleintr(c: i32); - fn wakeup(chan: *mut c_void); - fn procdump(); } -pub const BACKSPACE: i32 = 0x100; -pub const INPUT_BUF_SIZE: u64 = 128; +pub const BACKSPACE: u8 = 0x00; +pub const INPUT_BUF_SIZE: usize = 128; + +pub struct Console { + pub buffer: [u8; INPUT_BUF_SIZE], + pub read_index: usize, + pub write_index: usize, + pub edit_index: usize, +} +impl Console { + pub fn read_byte(&self) -> &u8 { + &self.buffer[self.read_index % self.buffer.len()] + } + pub fn write_byte(&mut self) -> &mut u8 { + let i = self.write_index % self.buffer.len(); + &mut self.buffer[i] + } + pub fn edit_byte(&mut self) -> &mut u8 { + let i = self.edit_index % self.buffer.len(); + &mut self.buffer[i] + } +} #[no_mangle] -pub static mut cons: Console = Console { - lock: unsafe { Spinlock::uninitialized() }, - buffer: [0u8; INPUT_BUF_SIZE as usize], +pub static cons: SpinMutex = SpinMutex::new(Console { + buffer: [0u8; INPUT_BUF_SIZE], read_index: 0, write_index: 0, edit_index: 0, -}; +}); /// ctrl-x -fn ctrl_x(x: char) -> char { - ((x as u8) - b'@') as char +const fn ctrl_x(x: u8) -> u8 { + x - b'@' } /// Send one character to the UART. /// /// Called by printf(), and to echo input /// characters but not from write(). -#[no_mangle] -pub unsafe extern "C" fn consputc(c: i32) { - if c == BACKSPACE { - // If the user typed backspace, overwrite with a space. - uartputc_sync('\x08' as i32); - uartputc_sync(' ' as i32); - uartputc_sync('\x08' as i32); - } else { - uartputc_sync(c); +pub fn consputc(c: u8) { + unsafe { + if c == BACKSPACE { + // If the user typed backspace, overwrite with a space. + uartputc_sync(0x08); + uartputc_sync(b' '); + uartputc_sync(0x08); + } else { + uartputc_sync(c); + } } } -#[repr(C)] -pub struct Console { - pub lock: Spinlock, - pub buffer: [u8; INPUT_BUF_SIZE as usize], - pub read_index: u32, - pub write_index: u32, - pub edit_index: u32, -} - /// User write()s to the console go here. #[no_mangle] pub unsafe extern "C" fn consolewrite(user_src: i32, src: u64, n: i32) -> i32 { @@ -79,7 +86,7 @@ pub unsafe extern "C" fn consolewrite(user_src: i32, src: u64, n: i32) -> i32 { if either_copyin(addr_of_mut!(c).cast(), user_src, src + i as u64, 1) == -1 { return i; } else { - uartputc(c as i32); + uartputc(c as u8); } } 0 @@ -96,31 +103,32 @@ pub unsafe extern "C" fn consoleread(user_dst: i32, mut dst: u64, mut n: i32) -> let mut c; let mut cbuf; - cons.lock.lock(); + // cons.lock.lock(); + let mut console = cons.lock(); while n > 0 { // Wait until interrupt handler has put // some input into cons.buffer. - while cons.read_index == cons.write_index { + while console.read_index == console.write_index { if killed(myproc()) != 0 { - cons.lock.unlock(); + // cons.lock.unlock(); return -1; } - sleep( - addr_of_mut!(cons.read_index).cast(), - addr_of_mut!(cons.lock), + sleep_mutex( + addr_of_mut!(console.read_index).cast(), + &mut console, ); } - - c = cons.buffer[(cons.read_index % INPUT_BUF_SIZE as u32) as usize]; - cons.read_index += 1; + + c = *console.read_byte(); + console.read_index += 1; // ctrl-D or EOF - if c == ctrl_x('D') as u8 { + if c == ctrl_x(b'D') { if n < target { // Save ctrl-D for next time, to make // sure caller gets a 0-byte result. - cons.read_index -= 1; + console.read_index -= 1; } break; } @@ -141,81 +149,12 @@ pub unsafe extern "C" fn consoleread(user_dst: i32, mut dst: u64, mut n: i32) -> } } - cons.lock.unlock(); + // cons.lock.unlock(); target - n } -// /// The console input interrupt handler. -// /// -// /// uartintr() calls this for input character. -// /// Do erase/kill processing, then append to cons.buf. -// /// Wake up consoleread() if a whole line has arrived. -// #[no_mangle] -// pub unsafe extern "C" fn consoleintr(c: i32) { -// cons.lock.lock(); -// -// let ctrl_p = ctrl_x('P') as u8 as i8 as i32; -// let ctrl_u = ctrl_x('P') as u8 as i8 as i32; -// let ctrl_h = ctrl_x('P') as u8 as i8 as i32; -// let ctrl_d = ctrl_x('D') as u8 as i8 as i32; -// let cr = '\r' as u8 as i8 as i32; -// let nl = '\n' as u8 as i8 as i32; -// -// match c { -// // Print process list. -// ctrl_p => procdump(), -// // Kill line -// ctrl_u => { -// while cons.edit_index != cons.write_index -// && cons.buffer[((cons.edit_index - 1) % INPUT_BUF_SIZE as u32) as usize] -// != '\n' as u8 -// { -// cons.edit_index -= 1; -// consputc(BACKSPACE); -// } -// } -// // Backspace -// ctrl_h => { -// if cons.edit_index != cons.write_index { -// cons.edit_index -= 1; -// consputc(BACKSPACE); -// } -// } -// c => { -// if cons.edit_index - cons.read_index < INPUT_BUF_SIZE as u32 { -// let c = if c == cr { nl } else { c }; -// -// // Echo back to the user. -// consputc(c); -// -// // Store for consumption by consoleread(). -// cons.buffer[(cons.edit_index % INPUT_BUF_SIZE as u32) as usize] = c as i8 as u8; -// cons.edit_index += 1; -// -// if c == nl -// || c == ctrl_d -// || cons.edit_index - cons.read_index == INPUT_BUF_SIZE as u32 -// { -// // Wake up consoleread() if a whole line (or EOF) has arrived. -// cons.write_index = cons.edit_index; -// wakeup(addr_of_mut!(cons.read_index).cast()); -// } -// } -// } -// } -// -// cons.lock.unlock(); -// } - pub unsafe fn consoleinit() { - initlock( - addr_of_mut!(cons.lock), - CStr::from_bytes_with_nul(b"cons\0") - .unwrap() - .as_ptr() - .cast_mut(), - ); uartinit(); // Connect read and write syscalls @@ -223,3 +162,45 @@ pub unsafe fn consoleinit() { devsw[CONSOLE].read = consoleread as usize as *const i32; devsw[CONSOLE].write = consolewrite as usize as *const i32; } + +/// The console input interrupt handler. +/// +/// uartintr() calls this for input character. +/// Do erase/kill processing, then append to cons.buf. +/// Wake up consoleread() if a whole line has arrived. +pub fn consoleintr(mut c: u8) { + // cons.lock.lock(); + let mut console = cons.lock(); + + if c == ctrl_x(b'P') { + // Print process list. + unsafe { procdump() }; + } else if c == ctrl_x(b'U') { + // Kill line. + while console.edit_index != console.write_index && console.buffer[(console.edit_index - 1) % INPUT_BUF_SIZE] != b'\n' { + console.edit_index -= 1; + consputc(BACKSPACE); + } + } else if c == ctrl_x(b'H') || c == 0x7f { + // Backspace or delete key. + if console.edit_index != console.write_index { + console.edit_index -= 1; + consputc(BACKSPACE); + } + } else if c != 0 && console.edit_index - console.read_index < INPUT_BUF_SIZE { + c = if c == b'\r' { b'\n' } else { c }; + + // Echo back to the user. + consputc(c); + + // Store for consumption by consoleread(). + *console.edit_byte() = c; + console.edit_index += 1; + + if c == b'\n' || c == ctrl_x(b'D') || console.edit_index - console.read_index == INPUT_BUF_SIZE { + // Wake up consoleread() if a whole line (or EOF) has arrived. + console.write_index = console.edit_index; + unsafe { wakeup(addr_of_mut!(console.read_index).cast()) }; + } + } +} diff --git a/kernel/rustkernel/src/kalloc.rs b/kernel/rustkernel/src/kalloc.rs index 5efd7a4..1ce5892 100644 --- a/kernel/rustkernel/src/kalloc.rs +++ b/kernel/rustkernel/src/kalloc.rs @@ -4,7 +4,7 @@ use crate::{ riscv::{memlayout::PHYSTOP, pg_round_up, PGSIZE}, - spinlock::Spinlock, + sync::spinlock::Spinlock, string::memset, }; use core::{ diff --git a/kernel/rustkernel/src/lib.rs b/kernel/rustkernel/src/lib.rs index cad269f..7d8b0ed 100644 --- a/kernel/rustkernel/src/lib.rs +++ b/kernel/rustkernel/src/lib.rs @@ -2,6 +2,7 @@ #![no_std] #![allow(dead_code)] #![allow(clippy::missing_safety_doc)] +#![feature(negative_impls)] extern crate alloc; extern crate core; @@ -15,8 +16,7 @@ pub(crate) mod param; pub mod printf; pub mod proc; pub(crate) mod riscv; -pub mod sleeplock; -pub mod spinlock; +pub mod sync; pub mod start; pub mod string; pub mod syscall; @@ -35,7 +35,7 @@ extern "C" { pub fn fileinit(); pub fn virtio_disk_init(); pub fn userinit(); - pub fn scheduler(); + // pub fn scheduler(); } use crate::{printf::print, proc::cpuid}; @@ -45,7 +45,7 @@ pub static mut STARTED: bool = false; pub static mut PANICKED: bool = false; #[no_mangle] -pub unsafe extern "C" fn main() { +pub unsafe extern "C" fn main() -> ! { if cpuid() == 0 { console::consoleinit(); printf::printfinit(); @@ -73,7 +73,7 @@ pub unsafe extern "C" fn main() { riscv::plic::plicinithart(); } - scheduler(); + proc::scheduler(); } #[panic_handler] @@ -85,7 +85,7 @@ fn panic_wrapper(panic_info: &core::panic::PanicInfo) -> ! { } unsafe { crate::PANICKED = true }; - + loop { core::hint::spin_loop(); } diff --git a/kernel/rustkernel/src/printf.rs b/kernel/rustkernel/src/printf.rs index b8ba0d2..1d2723c 100644 --- a/kernel/rustkernel/src/printf.rs +++ b/kernel/rustkernel/src/printf.rs @@ -1,4 +1,4 @@ -use crate::spinlock::Spinlock; +use crate::sync::spinlock::Spinlock; use core::ffi::{c_char, CStr}; pub use crate::panic; @@ -25,7 +25,7 @@ macro_rules! print { ).unwrap(); for c in s.as_bytes() { - unsafe { $crate::console::consputc(*c as i8 as i32) }; + $crate::console::consputc(*c); } unsafe { $crate::kalloc::kfree(buf.cast()) }; diff --git a/kernel/rustkernel/src/proc.rs b/kernel/rustkernel/src/proc.rs index d7dcb43..b5b5ca7 100644 --- a/kernel/rustkernel/src/proc.rs +++ b/kernel/rustkernel/src/proc.rs @@ -4,7 +4,8 @@ use crate::{ kalloc::kfree, param::*, riscv::{self, Pagetable, PTE_W}, - spinlock::{pop_off, push_off, Spinlock}, + sync::spinlock::{pop_off, push_off, Spinlock}, + sync::spinmutex::SpinMutexGuard, }; use core::{ ffi::{c_char, c_void}, @@ -29,20 +30,22 @@ extern "C" { pub fn fork() -> i32; pub fn exit(status: i32) -> !; pub fn wait(addr: u64) -> i32; + pub fn procdump(); pub fn proc_pagetable(p: *mut Proc) -> Pagetable; pub fn proc_freepagetable(pagetable: Pagetable, sz: u64); - pub fn wakeup(chan: *mut c_void); + pub fn wakeup(chan: *const c_void); pub fn allocproc() -> *mut Proc; // pub fn freeproc(p: *mut Proc); pub fn uvmalloc(pagetable: Pagetable, oldsz: u64, newsz: u64, xperm: i32) -> u64; pub fn uvmdealloc(pagetable: Pagetable, oldsz: u64, newsz: u64) -> u64; // pub fn sched(); + pub fn scheduler() -> !; pub fn swtch(a: *mut Context, b: *mut Context); } /// Saved registers for kernel context switches. #[repr(C)] -#[derive(Default)] +#[derive(Copy, Clone, Default)] pub struct Context { pub ra: u64, pub sp: u64, @@ -61,26 +64,47 @@ pub struct Context { pub s10: u64, pub s11: u64, } +impl Context { + pub const fn new() -> Context { + Context { + ra: 0u64, + sp: 0u64, + s0: 0u64, + s1: 0u64, + s2: 0u64, + s3: 0u64, + s4: 0u64, + s5: 0u64, + s6: 0u64, + s7: 0u64, + s8: 0u64, + s9: 0u64, + s10: 0u64, + s11: 0u64, + } + } +} /// Per-CPU state. #[repr(C)] +#[derive(Copy, Clone)] pub struct Cpu { - /// The process running on this cpu, or null. pub proc: *mut Proc, - /// swtch() here to enter scheduler() + /// swtch() here to enter scheduler() pub context: Context, /// Depth of push_off() nesting. - pub noff: i32, + pub interrupt_disable_layers: i32, /// Were interrupts enabled before push_off()? - pub intena: i32, + pub previous_interrupts_enabled: i32, } -impl Default for Cpu { - fn default() -> Self { +impl Cpu { + pub const fn new() -> Cpu { Cpu { proc: null_mut(), - context: Context::default(), - noff: 0, - intena: 0, + // proc: None, + context: Context::new(), + interrupt_disable_layers: 0, + previous_interrupts_enabled: 0, } } } @@ -360,9 +384,9 @@ pub unsafe extern "C" fn r#yield() { /// Switch to scheduler. Must hold only p->lock /// and have changed proc->state. Saves and restores -/// intena because intena is a property of this +/// previous_interrupts_enabled because previous_interrupts_enabled is a property of this /// kernel thread, not this CPU. It should -/// be proc->intena and proc->noff, but that would +/// be proc->previous_interrupts_enabled and proc->interrupt_disable_layers, but that would /// break in the few places where a lock is held but /// there's no process. #[no_mangle] @@ -372,7 +396,7 @@ pub unsafe extern "C" fn sched() { if !(*p).lock.held_by_current_cpu() { panic!("sched p->lock"); - } else if (*c).noff != 1 { + } else if (*c).interrupt_disable_layers != 1 { panic!("sched locks"); } else if (*p).state == ProcState::Running { panic!("sched running"); @@ -380,9 +404,9 @@ pub unsafe extern "C" fn sched() { panic!("sched interruptible"); } - let intena = (*c).intena; + let previous_interrupts_enabled = (*c).previous_interrupts_enabled; swtch(addr_of_mut!((*p).context), addr_of_mut!((*c).context)); - (*c).intena = intena; + (*c).previous_interrupts_enabled = previous_interrupts_enabled; } /// Atomically release lock and sleep on chan. @@ -415,6 +439,28 @@ pub unsafe extern "C" fn sleep(chan: *mut c_void, lock: *mut Spinlock) { (*lock).lock(); } +pub unsafe fn sleep_mutex(chan: *mut c_void, mutex: &mut SpinMutexGuard) { + let p = myproc(); + let mutex = mutex.mutex; + + (*p).lock.lock(); + mutex.unlock(); + + // Go to sleep. + (*p).chan = chan; + (*p).state = ProcState::Sleeping; + + sched(); + + // Tidy up. + (*p).chan = null_mut(); + + // Reacquire original lock. + (*p).lock.unlock(); + let guard = mutex.lock(); + core::mem::forget(guard); +} + /// Kill the process with the given pid. /// The victim won't exit until it tries to return /// to user space (see usertrap() in trap.c). @@ -453,3 +499,4 @@ pub unsafe extern "C" fn killed(p: *mut Proc) -> i32 { (*p).lock.unlock(); k } + \ No newline at end of file diff --git a/kernel/rustkernel/src/sync/mod.rs b/kernel/rustkernel/src/sync/mod.rs new file mode 100644 index 0000000..3d543ce --- /dev/null +++ b/kernel/rustkernel/src/sync/mod.rs @@ -0,0 +1,3 @@ +pub mod sleeplock; +pub mod spinlock; +pub mod spinmutex; diff --git a/kernel/rustkernel/src/sleeplock.rs b/kernel/rustkernel/src/sync/sleeplock.rs similarity index 96% rename from kernel/rustkernel/src/sleeplock.rs rename to kernel/rustkernel/src/sync/sleeplock.rs index 443e5a3..c58382e 100644 --- a/kernel/rustkernel/src/sleeplock.rs +++ b/kernel/rustkernel/src/sync/sleeplock.rs @@ -1,6 +1,6 @@ use crate::{ proc::{myproc, sleep, wakeup}, - spinlock::{self, Spinlock}, + sync::spinlock::{self, Spinlock}, }; use core::{ffi::c_char, ptr::addr_of_mut}; diff --git a/kernel/rustkernel/src/spinlock.rs b/kernel/rustkernel/src/sync/spinlock.rs similarity index 84% rename from kernel/rustkernel/src/spinlock.rs rename to kernel/rustkernel/src/sync/spinlock.rs index 4c676bf..63ffea4 100644 --- a/kernel/rustkernel/src/spinlock.rs +++ b/kernel/rustkernel/src/sync/spinlock.rs @@ -23,7 +23,7 @@ impl Spinlock { } } /// Initializes a `Spinlock`. - pub fn new(name: *mut c_char) -> Spinlock { + pub const fn new(name: *mut c_char) -> Spinlock { Spinlock { locked: AtomicBool::new(false), cpu: null_mut(), @@ -57,7 +57,7 @@ impl Spinlock { } self.cpu = null_mut(); - + self.locked.store(false, Ordering::Release); pop_off(); @@ -94,24 +94,26 @@ pub unsafe extern "C" fn push_off() { let cpu = mycpu(); riscv::intr_off(); - if (*cpu).noff == 0 { - (*cpu).intena = old; + if (*cpu).interrupt_disable_layers == 0 { + (*cpu).previous_interrupts_enabled = old; } - (*cpu).noff += 1; + (*cpu).interrupt_disable_layers += 1; } #[no_mangle] pub unsafe extern "C" fn pop_off() { let cpu = mycpu(); if riscv::intr_get() == 1 { + // crate::panic_byte(b'0'); panic!("pop_off - interruptible"); - } else if (*cpu).noff < 1 { + } else if (*cpu).interrupt_disable_layers < 1 { + // crate::panic_byte(b'1'); panic!("pop_off"); } - (*cpu).noff -= 1; + (*cpu).interrupt_disable_layers -= 1; - if (*cpu).noff == 0 && (*cpu).intena == 1 { + if (*cpu).interrupt_disable_layers == 0 && (*cpu).previous_interrupts_enabled == 1 { riscv::intr_on(); } } diff --git a/kernel/rustkernel/src/sync/spinmutex.rs b/kernel/rustkernel/src/sync/spinmutex.rs new file mode 100644 index 0000000..ffd0f15 --- /dev/null +++ b/kernel/rustkernel/src/sync/spinmutex.rs @@ -0,0 +1,45 @@ +use core::{cell::UnsafeCell, ops::{Deref, DerefMut, Drop}, sync::atomic::{AtomicBool, Ordering}}; + +pub struct SpinMutex { + locked: AtomicBool, + pub inner: UnsafeCell, +} +impl SpinMutex { + pub const fn new(value: T) -> SpinMutex { + SpinMutex { + locked: AtomicBool::new(false), + inner: UnsafeCell::new(value), + } + } + pub fn lock(&self) -> SpinMutexGuard<'_, T> { + while self.locked.swap(true, Ordering::Acquire) { + core::hint::spin_loop(); + } + SpinMutexGuard { mutex: &self } + } + pub unsafe fn unlock(&self) { + self.locked.store(false, Ordering::Release); + } +} +unsafe impl Sync for SpinMutex where T: Send {} + +pub struct SpinMutexGuard<'m, T> { + pub mutex: &'m SpinMutex, +} +impl<'m, T> Deref for SpinMutexGuard<'m, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { & *self.mutex.inner.get() } + } +} +impl<'m, T> DerefMut for SpinMutexGuard<'m, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *self.mutex.inner.get() } + } +} +impl<'m, T> Drop for SpinMutexGuard<'m, T> { + fn drop(&mut self) { + unsafe { self.mutex.unlock() } + } +} diff --git a/kernel/rustkernel/src/trap.rs b/kernel/rustkernel/src/trap.rs index b217187..68d3f8e 100644 --- a/kernel/rustkernel/src/trap.rs +++ b/kernel/rustkernel/src/trap.rs @@ -1,8 +1,8 @@ use crate::{ printf::print, - proc::{cpuid, wakeup}, + proc::{cpuid, wakeup, mycpu}, riscv::*, - spinlock::Spinlock, + sync::spinlock::Spinlock, }; use core::{ffi::CStr, ptr::addr_of_mut}; @@ -142,3 +142,41 @@ pub unsafe extern "C" fn devintr() -> i32 { 0 } } + +pub struct InterruptBlocker; +impl InterruptBlocker { + pub fn new() { + unsafe { + let interrupts_before = intr_get(); + let cpu = mycpu(); + + intr_off(); + + if (*cpu).interrupt_disable_layers == 0 { + (*cpu).previous_interrupts_enabled = interrupts_before; + } + (*cpu).interrupt_disable_layers += 1; + // crate::sync::spinlock::push_off(); + } + } +} +impl core::ops::Drop for InterruptBlocker { + fn drop(&mut self) { + unsafe { + let cpu = mycpu(); + + if intr_get() == 1 || (*cpu).interrupt_disable_layers < 1 { + // panic!("pop_off mismatched"); + return; + } + + (*cpu).interrupt_disable_layers -= 1; + + if (*cpu).interrupt_disable_layers == 0 && (*cpu).previous_interrupts_enabled == 1 { + intr_on(); + } + // crate::sync::spinlock::pop_off(); + } + } +} +impl !Send for InterruptBlocker {} diff --git a/kernel/rustkernel/src/uart.rs b/kernel/rustkernel/src/uart.rs index 735dfd8..6a97f58 100644 --- a/kernel/rustkernel/src/uart.rs +++ b/kernel/rustkernel/src/uart.rs @@ -3,85 +3,143 @@ use crate::{ console::consoleintr, - proc::{sleep, wakeup}, + proc::{sleep, sleep_mutex, wakeup}, riscv::memlayout::UART0, - spinlock::{pop_off, push_off, Spinlock}, + sync::spinlock::{pop_off, push_off, Spinlock}, + sync::spinmutex::SpinMutex, + trap::InterruptBlocker, }; use core::{ffi::CStr, ptr::addr_of_mut}; -/// The UART control registers are memory-mapped -/// at address UART0. This function returns the -/// address of one of the registers. -#[inline(always)] -fn get_register_addr>(register: N) -> *mut u8 { - let register: u64 = register.into(); - (UART0 + register) as *mut u8 +enum Register { + ReceiveHolding, + TransmitHolding, + InterruptEnable, + FIFOControl, + InterruptStatus, + LineControl, + LineStatus, +} +impl Register { + pub fn as_ptr(&self) -> *mut u8 { + let addr = UART0 + match self { + Register::ReceiveHolding => 0, + Register::TransmitHolding => 0, + Register::InterruptEnable => 1, + Register::FIFOControl => 2, + Register::InterruptStatus => 2, + Register::LineControl => 2, + Register::LineStatus => 5, + }; + addr as *mut u8 + } + pub fn read(&self) -> u8 { + unsafe { self.as_ptr().read_volatile() } + } + pub fn write(&self, value: u8) { + unsafe { self.as_ptr().write_volatile(value) } + } +} + +pub static uart: SpinMutex = SpinMutex::new(Uart { + buffer: [0u8; UART_TX_BUF_SIZE], + write_index: 0, + read_index: 0, +}); + +pub struct Uart { + pub buffer: [u8; UART_TX_BUF_SIZE], + pub write_index: usize, + pub read_index: usize, +} +impl Uart { + pub fn write_byte_sync(x: u8) { + // let _ = InterruptBlocker::new(); + unsafe { push_off(); } + + if unsafe { crate::PANICKED } { + loop { + core::hint::spin_loop(); + } + } + + // Wait for Transmit Holding Empty to be set in LSR. + while Register::LineStatus.read() & LSR_TX_IDLE == 0 { + core::hint::spin_loop(); + } + + Register::TransmitHolding.write(x); + + unsafe { pop_off(); } + } + /// If the UART is idle, and a character is + /// waiting in the transmit buffer, send it. + pub fn send_queued_bytes(&mut self) { + loop { + self.write_index %= self.buffer.len(); + self.read_index %= self.buffer.len(); + + if self.write_index == self.read_index { + // Transmit buffer is ready. + return; + } + + let c = self.buffer[self.read_index]; + self.read_index += 1; + + // Maybe uartputc() is waiting for space in the buffer. + unsafe { wakeup(addr_of_mut!(self.read_index).cast()) }; + + Register::TransmitHolding.write(c); + } + } } // The UART control registers. // Some have different meanings for read vs write. // See http://byterunner.com/16550.html -/// Receive Holding Register (for input bytes) -const RHR: u8 = 0; -/// Transmit Holding Register (for output bytes) -const THR: u8 = 0; /// Interrupt Enable Register -const IER: u8 = 1; const IER_RX_ENABLE: u8 = 1 << 0; const IER_TX_ENABLE: u8 = 1 << 1; -/// FIFO control register -const FCR: u8 = 2; const FCR_FIFO_ENABLE: u8 = 1 << 0; /// Clear the content of the two FIFOs. const FCR_FIFO_CLEAR: u8 = 3 << 1; -/// Interrupt Status Register -const ISR: u8 = 2; -/// Line Control Register -const LCR: u8 = 2; const LCR_EIGHT_BITS: u8 = 3; /// Special mode to set baud rate const LCR_BAUD_LATCH: u8 = 1 << 7; -/// Line Status Register -const LSR: u8 = 5; /// Input is waiting to be read from RHR const LSR_RX_READY: u8 = 1 << 0; /// THR can accept another character to send const LSR_TX_IDLE: u8 = 1 << 5; -#[inline(always)] -unsafe fn read_register>(register: N) -> u8 { - *get_register_addr(register) -} -#[inline(always)] -unsafe fn write_register>(register: N, value: u8) { - *get_register_addr(register) = value; -} - static mut uart_tx_lock: Spinlock = unsafe { Spinlock::uninitialized() }; -const UART_TX_BUF_SIZE: u64 = 32; -static mut uart_tx_buf: [u8; UART_TX_BUF_SIZE as usize] = [0u8; UART_TX_BUF_SIZE as usize]; +const UART_TX_BUF_SIZE: usize = 32; +static mut uart_tx_buf: [u8; UART_TX_BUF_SIZE] = [0u8; UART_TX_BUF_SIZE]; +// static uart_tx_buf: SpinMutex<[u8; UART_TX_BUF_SIZE]> = SpinMutex::new([0u8; UART_TX_BUF_SIZE]); /// Write next to uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] -static mut uart_tx_w: u64 = 0; +static mut uart_tx_w: usize = 0; /// Read next from uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE] -static mut uart_tx_r: u64 = 0; +static mut uart_tx_r: usize = 0; pub(crate) unsafe fn uartinit() { // Disable interrupts. - write_register(IER, 0x00); + Register::InterruptEnable.write(0x00); // Special mode to set baud rate. - write_register(LCR, LCR_BAUD_LATCH); - // LSB for baud rate of 38.4K. - write_register(0u8, 0x03); - // MSB for baud rate of 38.4K. - write_register(1u8, 0x00); + Register::LineControl.write(LCR_BAUD_LATCH); + unsafe { + // LSB for baud rate of 38.4K. + *(UART0 as *mut u8) = 0x03; + // MSB for baud rate of 38.4K. + *((UART0 + 1) as *mut u8) = 0x00; + } // Leave set-baud mode and set // word length to 8 bits, no parity. - write_register(LCR, LCR_EIGHT_BITS); + Register::LineControl.write(LCR_EIGHT_BITS); // Reset and enable FIFOs. - write_register(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); + Register::FIFOControl.write(FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); // Enable transmit and receive interrupts. - write_register(IER, IER_TX_ENABLE | IER_RX_ENABLE); + Register::InterruptEnable.write(IER_TX_ENABLE | IER_RX_ENABLE); uart_tx_lock = Spinlock::new( CStr::from_bytes_with_nul(b"uart\0") @@ -97,8 +155,10 @@ pub(crate) unsafe fn uartinit() { /// Because it may block, it can't be called /// from interrupts, it's only suitable for use /// by write(). -pub(crate) unsafe fn uartputc(c: i32) { +pub(crate) unsafe fn uartputc(c: u8) { uart_tx_lock.lock(); + // let mut buf = uart_tx_buf.lock(); + // let u = uart.lock(); if crate::PANICKED { loop { @@ -115,7 +175,7 @@ pub(crate) unsafe fn uartputc(c: i32) { ); } - uart_tx_buf[(uart_tx_w % UART_TX_BUF_SIZE) as usize] = c as i8 as u8; + uart_tx_buf[(uart_tx_w % UART_TX_BUF_SIZE) as usize] = c; uart_tx_w += 1; uartstart(); uart_tx_lock.unlock(); @@ -125,21 +185,22 @@ pub(crate) unsafe fn uartputc(c: i32) { /// use interrupts, for use by kernel printf() and /// to echo characters. It spins waiting for the UART's /// output register to be empty. -pub(crate) unsafe fn uartputc_sync(c: i32) { +pub(crate) unsafe fn uartputc_sync(c: u8) { push_off(); + Uart::write_byte_sync(c); - if crate::PANICKED { - loop { - core::hint::spin_loop(); - } - } + // if crate::PANICKED { + // loop { + // core::hint::spin_loop(); + // } + // } - // Wait for Transmit Holding Empty to be set in LSR. - while read_register(LSR) & LSR_TX_IDLE == 0 { - core::hint::spin_loop(); - } + // // Wait for Transmit Holding Empty to be set in LSR. + // while Register::LineStatus.read() & LSR_TX_IDLE == 0 { + // core::hint::spin_loop(); + // } - write_register(THR, c as i8 as u8); + // Register::TransmitHolding.write(c); pop_off(); } @@ -154,47 +215,45 @@ unsafe fn uartstart() { // Transmit buffer is ready. return; } - - if read_register(LSR) & LSR_TX_IDLE == 0 { + if Register::LineStatus.read() & LSR_TX_IDLE == 0 { // The UART transmit holding register is full, // so we cannot give it another byte. // It will interrupt when it's ready for a new byte. return; } + // let buf = uart_tx_buf.lock(); let c = uart_tx_buf[(uart_tx_r % UART_TX_BUF_SIZE) as usize]; uart_tx_r += 1; // Maybe uartputc() is waiting for space in the buffer. wakeup(addr_of_mut!(uart_tx_r).cast()); - write_register(THR, c); + Register::TransmitHolding.write(c); } } -/// Read one input character from the UART. -/// Return -1 if nothing is waiting. -unsafe fn uartgetc() -> i32 { - if read_register(LSR) & 0x01 != 0 { +/// Read one input byte from the UART. +pub(crate) fn uartgetc() -> Option { + if Register::LineStatus.read() & 0x01 != 0 { // Input data is ready. - read_register(RHR) as i32 + Some(Register::ReceiveHolding.read()) } else { - -1 + None } } /// Handle a UART interrupt, raised because input has /// arrived, or the uart is ready for more output, or /// both. Called from devintr(). -#[no_mangle] -pub unsafe extern "C" fn uartintr() { +pub(crate) unsafe fn uartintr() { // Read and process incoming characters. loop { - let c = uartgetc(); - if c == -1 { + if let Some(c) = uartgetc() { + consoleintr(c); + } else { break; } - consoleintr(c); } // Send buffered characters.