make a spinmutex (and other things)

This commit is contained in:
Garen Tyler 2023-10-20 16:29:26 -06:00
parent 29878e42e6
commit 96cfda564c
Signed by: garentyler
GPG Key ID: D7A048C454CB7054
15 changed files with 401 additions and 314 deletions

View File

@ -5,7 +5,6 @@ P=programs
OBJS = \ OBJS = \
$K/entry.o \ $K/entry.o \
$K/console.o \
$K/vm.o \ $K/vm.o \
$K/proc.o \ $K/proc.o \
$K/swtch.o \ $K/swtch.o \

View File

@ -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 <stdarg.h>
#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);
}

View File

@ -22,8 +22,8 @@ struct context {
struct cpu { struct cpu {
struct proc *proc; // The process running on this cpu, or null. struct proc *proc; // The process running on this cpu, or null.
struct context context; // swtch() here to enter scheduler(). struct context context; // swtch() here to enter scheduler().
int noff; // Depth of push_off() nesting. int interrupt_disable_layers; // Depth of push_off() nesting.
int intena; // Were interrupts enabled before push_off()? int previous_interrupts_enabled; // Were interrupts enabled before push_off()?
}; };
extern struct cpu cpus[NCPU]; extern struct cpu cpus[NCPU];

View File

@ -1,4 +1,4 @@
use crate::{fs::BSIZE, sleeplock::Sleeplock}; use crate::{fs::BSIZE, sync::sleeplock::Sleeplock};
#[repr(C)] #[repr(C)]
pub struct Buf { pub struct Buf {

View File

@ -10,66 +10,73 @@
use crate::{ use crate::{
file::{devsw, CONSOLE}, file::{devsw, CONSOLE},
proc::{killed, myproc, sleep}, proc::{killed, myproc, sleep_mutex, wakeup, procdump},
spinlock::{initlock, Spinlock}, sync::spinmutex::SpinMutex,
uart::{uartinit, uartputc, uartputc_sync}, uart::{uartinit, uartputc, uartputc_sync},
}; };
use core::{ use core::{
ffi::{c_void, CStr}, ffi::c_void,
ptr::addr_of_mut, ptr::addr_of_mut,
}; };
extern "C" { extern "C" {
fn either_copyin(dst: *mut c_void, user_src: i32, src: u64, len: u64) -> i32; 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; 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 BACKSPACE: u8 = 0x00;
pub const INPUT_BUF_SIZE: u64 = 128; 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] #[no_mangle]
pub static mut cons: Console = Console { pub static cons: SpinMutex<Console> = SpinMutex::new(Console {
lock: unsafe { Spinlock::uninitialized() }, buffer: [0u8; INPUT_BUF_SIZE],
buffer: [0u8; INPUT_BUF_SIZE as usize],
read_index: 0, read_index: 0,
write_index: 0, write_index: 0,
edit_index: 0, edit_index: 0,
}; });
/// ctrl-x /// ctrl-x
fn ctrl_x(x: char) -> char { const fn ctrl_x(x: u8) -> u8 {
((x as u8) - b'@') as char x - b'@'
} }
/// Send one character to the UART. /// Send one character to the UART.
/// ///
/// Called by printf(), and to echo input /// Called by printf(), and to echo input
/// characters but not from write(). /// characters but not from write().
#[no_mangle] pub fn consputc(c: u8) {
pub unsafe extern "C" fn consputc(c: i32) { unsafe {
if c == BACKSPACE { if c == BACKSPACE {
// If the user typed backspace, overwrite with a space. // If the user typed backspace, overwrite with a space.
uartputc_sync('\x08' as i32); uartputc_sync(0x08);
uartputc_sync(' ' as i32); uartputc_sync(b' ');
uartputc_sync('\x08' as i32); uartputc_sync(0x08);
} else { } else {
uartputc_sync(c); 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. /// User write()s to the console go here.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn consolewrite(user_src: i32, src: u64, n: i32) -> i32 { 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 { if either_copyin(addr_of_mut!(c).cast(), user_src, src + i as u64, 1) == -1 {
return i; return i;
} else { } else {
uartputc(c as i32); uartputc(c as u8);
} }
} }
0 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 c;
let mut cbuf; let mut cbuf;
cons.lock.lock(); // cons.lock.lock();
let mut console = cons.lock();
while n > 0 { while n > 0 {
// Wait until interrupt handler has put // Wait until interrupt handler has put
// some input into cons.buffer. // some input into cons.buffer.
while cons.read_index == cons.write_index { while console.read_index == console.write_index {
if killed(myproc()) != 0 { if killed(myproc()) != 0 {
cons.lock.unlock(); // cons.lock.unlock();
return -1; return -1;
} }
sleep( sleep_mutex(
addr_of_mut!(cons.read_index).cast(), addr_of_mut!(console.read_index).cast(),
addr_of_mut!(cons.lock), &mut console,
); );
} }
c = cons.buffer[(cons.read_index % INPUT_BUF_SIZE as u32) as usize]; c = *console.read_byte();
cons.read_index += 1; console.read_index += 1;
// ctrl-D or EOF // ctrl-D or EOF
if c == ctrl_x('D') as u8 { if c == ctrl_x(b'D') {
if n < target { if n < target {
// Save ctrl-D for next time, to make // Save ctrl-D for next time, to make
// sure caller gets a 0-byte result. // sure caller gets a 0-byte result.
cons.read_index -= 1; console.read_index -= 1;
} }
break; 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 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() { pub unsafe fn consoleinit() {
initlock(
addr_of_mut!(cons.lock),
CStr::from_bytes_with_nul(b"cons\0")
.unwrap()
.as_ptr()
.cast_mut(),
);
uartinit(); uartinit();
// Connect read and write syscalls // Connect read and write syscalls
@ -223,3 +162,45 @@ pub unsafe fn consoleinit() {
devsw[CONSOLE].read = consoleread as usize as *const i32; devsw[CONSOLE].read = consoleread as usize as *const i32;
devsw[CONSOLE].write = consolewrite 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()) };
}
}
}

View File

@ -4,7 +4,7 @@
use crate::{ use crate::{
riscv::{memlayout::PHYSTOP, pg_round_up, PGSIZE}, riscv::{memlayout::PHYSTOP, pg_round_up, PGSIZE},
spinlock::Spinlock, sync::spinlock::Spinlock,
string::memset, string::memset,
}; };
use core::{ use core::{

View File

@ -2,6 +2,7 @@
#![no_std] #![no_std]
#![allow(dead_code)] #![allow(dead_code)]
#![allow(clippy::missing_safety_doc)] #![allow(clippy::missing_safety_doc)]
#![feature(negative_impls)]
extern crate alloc; extern crate alloc;
extern crate core; extern crate core;
@ -15,8 +16,7 @@ pub(crate) mod param;
pub mod printf; pub mod printf;
pub mod proc; pub mod proc;
pub(crate) mod riscv; pub(crate) mod riscv;
pub mod sleeplock; pub mod sync;
pub mod spinlock;
pub mod start; pub mod start;
pub mod string; pub mod string;
pub mod syscall; pub mod syscall;
@ -35,7 +35,7 @@ extern "C" {
pub fn fileinit(); pub fn fileinit();
pub fn virtio_disk_init(); pub fn virtio_disk_init();
pub fn userinit(); pub fn userinit();
pub fn scheduler(); // pub fn scheduler();
} }
use crate::{printf::print, proc::cpuid}; use crate::{printf::print, proc::cpuid};
@ -45,7 +45,7 @@ pub static mut STARTED: bool = false;
pub static mut PANICKED: bool = false; pub static mut PANICKED: bool = false;
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn main() { pub unsafe extern "C" fn main() -> ! {
if cpuid() == 0 { if cpuid() == 0 {
console::consoleinit(); console::consoleinit();
printf::printfinit(); printf::printfinit();
@ -73,7 +73,7 @@ pub unsafe extern "C" fn main() {
riscv::plic::plicinithart(); riscv::plic::plicinithart();
} }
scheduler(); proc::scheduler();
} }
#[panic_handler] #[panic_handler]
@ -85,7 +85,7 @@ fn panic_wrapper(panic_info: &core::panic::PanicInfo) -> ! {
} }
unsafe { crate::PANICKED = true }; unsafe { crate::PANICKED = true };
loop { loop {
core::hint::spin_loop(); core::hint::spin_loop();
} }

View File

@ -1,4 +1,4 @@
use crate::spinlock::Spinlock; use crate::sync::spinlock::Spinlock;
use core::ffi::{c_char, CStr}; use core::ffi::{c_char, CStr};
pub use crate::panic; pub use crate::panic;
@ -25,7 +25,7 @@ macro_rules! print {
).unwrap(); ).unwrap();
for c in s.as_bytes() { 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()) }; unsafe { $crate::kalloc::kfree(buf.cast()) };

View File

@ -4,7 +4,8 @@ use crate::{
kalloc::kfree, kalloc::kfree,
param::*, param::*,
riscv::{self, Pagetable, PTE_W}, riscv::{self, Pagetable, PTE_W},
spinlock::{pop_off, push_off, Spinlock}, sync::spinlock::{pop_off, push_off, Spinlock},
sync::spinmutex::SpinMutexGuard,
}; };
use core::{ use core::{
ffi::{c_char, c_void}, ffi::{c_char, c_void},
@ -29,20 +30,22 @@ extern "C" {
pub fn fork() -> i32; pub fn fork() -> i32;
pub fn exit(status: i32) -> !; pub fn exit(status: i32) -> !;
pub fn wait(addr: u64) -> i32; pub fn wait(addr: u64) -> i32;
pub fn procdump();
pub fn proc_pagetable(p: *mut Proc) -> Pagetable; pub fn proc_pagetable(p: *mut Proc) -> Pagetable;
pub fn proc_freepagetable(pagetable: Pagetable, sz: u64); 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 allocproc() -> *mut Proc;
// pub fn freeproc(p: *mut Proc); // pub fn freeproc(p: *mut Proc);
pub fn uvmalloc(pagetable: Pagetable, oldsz: u64, newsz: u64, xperm: i32) -> u64; pub fn uvmalloc(pagetable: Pagetable, oldsz: u64, newsz: u64, xperm: i32) -> u64;
pub fn uvmdealloc(pagetable: Pagetable, oldsz: u64, newsz: u64) -> u64; pub fn uvmdealloc(pagetable: Pagetable, oldsz: u64, newsz: u64) -> u64;
// pub fn sched(); // pub fn sched();
pub fn scheduler() -> !;
pub fn swtch(a: *mut Context, b: *mut Context); pub fn swtch(a: *mut Context, b: *mut Context);
} }
/// Saved registers for kernel context switches. /// Saved registers for kernel context switches.
#[repr(C)] #[repr(C)]
#[derive(Default)] #[derive(Copy, Clone, Default)]
pub struct Context { pub struct Context {
pub ra: u64, pub ra: u64,
pub sp: u64, pub sp: u64,
@ -61,26 +64,47 @@ pub struct Context {
pub s10: u64, pub s10: u64,
pub s11: 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. /// Per-CPU state.
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)]
pub struct Cpu { pub struct Cpu {
/// The process running on this cpu, or null.
pub proc: *mut Proc, pub proc: *mut Proc,
/// swtch() here to enter scheduler() /// swtch() here to enter scheduler()
pub context: Context, pub context: Context,
/// Depth of push_off() nesting. /// Depth of push_off() nesting.
pub noff: i32, pub interrupt_disable_layers: i32,
/// Were interrupts enabled before push_off()? /// Were interrupts enabled before push_off()?
pub intena: i32, pub previous_interrupts_enabled: i32,
} }
impl Default for Cpu { impl Cpu {
fn default() -> Self { pub const fn new() -> Cpu {
Cpu { Cpu {
proc: null_mut(), proc: null_mut(),
context: Context::default(), // proc: None,
noff: 0, context: Context::new(),
intena: 0, 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 /// Switch to scheduler. Must hold only p->lock
/// and have changed proc->state. Saves and restores /// 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 /// 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 /// break in the few places where a lock is held but
/// there's no process. /// there's no process.
#[no_mangle] #[no_mangle]
@ -372,7 +396,7 @@ pub unsafe extern "C" fn sched() {
if !(*p).lock.held_by_current_cpu() { if !(*p).lock.held_by_current_cpu() {
panic!("sched p->lock"); panic!("sched p->lock");
} else if (*c).noff != 1 { } else if (*c).interrupt_disable_layers != 1 {
panic!("sched locks"); panic!("sched locks");
} else if (*p).state == ProcState::Running { } else if (*p).state == ProcState::Running {
panic!("sched running"); panic!("sched running");
@ -380,9 +404,9 @@ pub unsafe extern "C" fn sched() {
panic!("sched interruptible"); 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)); 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. /// 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(); (*lock).lock();
} }
pub unsafe fn sleep_mutex<T>(chan: *mut c_void, mutex: &mut SpinMutexGuard<T>) {
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. /// Kill the process with the given pid.
/// The victim won't exit until it tries to return /// The victim won't exit until it tries to return
/// to user space (see usertrap() in trap.c). /// 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(); (*p).lock.unlock();
k k
} }

View File

@ -0,0 +1,3 @@
pub mod sleeplock;
pub mod spinlock;
pub mod spinmutex;

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
proc::{myproc, sleep, wakeup}, proc::{myproc, sleep, wakeup},
spinlock::{self, Spinlock}, sync::spinlock::{self, Spinlock},
}; };
use core::{ffi::c_char, ptr::addr_of_mut}; use core::{ffi::c_char, ptr::addr_of_mut};

View File

@ -23,7 +23,7 @@ impl Spinlock {
} }
} }
/// Initializes a `Spinlock`. /// Initializes a `Spinlock`.
pub fn new(name: *mut c_char) -> Spinlock { pub const fn new(name: *mut c_char) -> Spinlock {
Spinlock { Spinlock {
locked: AtomicBool::new(false), locked: AtomicBool::new(false),
cpu: null_mut(), cpu: null_mut(),
@ -57,7 +57,7 @@ impl Spinlock {
} }
self.cpu = null_mut(); self.cpu = null_mut();
self.locked.store(false, Ordering::Release); self.locked.store(false, Ordering::Release);
pop_off(); pop_off();
@ -94,24 +94,26 @@ pub unsafe extern "C" fn push_off() {
let cpu = mycpu(); let cpu = mycpu();
riscv::intr_off(); riscv::intr_off();
if (*cpu).noff == 0 { if (*cpu).interrupt_disable_layers == 0 {
(*cpu).intena = old; (*cpu).previous_interrupts_enabled = old;
} }
(*cpu).noff += 1; (*cpu).interrupt_disable_layers += 1;
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn pop_off() { pub unsafe extern "C" fn pop_off() {
let cpu = mycpu(); let cpu = mycpu();
if riscv::intr_get() == 1 { if riscv::intr_get() == 1 {
// crate::panic_byte(b'0');
panic!("pop_off - interruptible"); panic!("pop_off - interruptible");
} else if (*cpu).noff < 1 { } else if (*cpu).interrupt_disable_layers < 1 {
// crate::panic_byte(b'1');
panic!("pop_off"); 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(); riscv::intr_on();
} }
} }

View File

@ -0,0 +1,45 @@
use core::{cell::UnsafeCell, ops::{Deref, DerefMut, Drop}, sync::atomic::{AtomicBool, Ordering}};
pub struct SpinMutex<T> {
locked: AtomicBool,
pub inner: UnsafeCell<T>,
}
impl<T> SpinMutex<T> {
pub const fn new(value: T) -> SpinMutex<T> {
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<T> Sync for SpinMutex<T> where T: Send {}
pub struct SpinMutexGuard<'m, T> {
pub mutex: &'m SpinMutex<T>,
}
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() }
}
}

View File

@ -1,8 +1,8 @@
use crate::{ use crate::{
printf::print, printf::print,
proc::{cpuid, wakeup}, proc::{cpuid, wakeup, mycpu},
riscv::*, riscv::*,
spinlock::Spinlock, sync::spinlock::Spinlock,
}; };
use core::{ffi::CStr, ptr::addr_of_mut}; use core::{ffi::CStr, ptr::addr_of_mut};
@ -142,3 +142,41 @@ pub unsafe extern "C" fn devintr() -> i32 {
0 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 {}

View File

@ -3,85 +3,143 @@
use crate::{ use crate::{
console::consoleintr, console::consoleintr,
proc::{sleep, wakeup}, proc::{sleep, sleep_mutex, wakeup},
riscv::memlayout::UART0, 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}; use core::{ffi::CStr, ptr::addr_of_mut};
/// The UART control registers are memory-mapped enum Register {
/// at address UART0. This function returns the ReceiveHolding,
/// address of one of the registers. TransmitHolding,
#[inline(always)] InterruptEnable,
fn get_register_addr<N: Into<u64>>(register: N) -> *mut u8 { FIFOControl,
let register: u64 = register.into(); InterruptStatus,
(UART0 + register) as *mut u8 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<Uart> = 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. // The UART control registers.
// Some have different meanings for read vs write. // Some have different meanings for read vs write.
// See http://byterunner.com/16550.html // 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 /// Interrupt Enable Register
const IER: u8 = 1;
const IER_RX_ENABLE: u8 = 1 << 0; const IER_RX_ENABLE: u8 = 1 << 0;
const IER_TX_ENABLE: u8 = 1 << 1; const IER_TX_ENABLE: u8 = 1 << 1;
/// FIFO control register
const FCR: u8 = 2;
const FCR_FIFO_ENABLE: u8 = 1 << 0; const FCR_FIFO_ENABLE: u8 = 1 << 0;
/// Clear the content of the two FIFOs. /// Clear the content of the two FIFOs.
const FCR_FIFO_CLEAR: u8 = 3 << 1; 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; const LCR_EIGHT_BITS: u8 = 3;
/// Special mode to set baud rate /// Special mode to set baud rate
const LCR_BAUD_LATCH: u8 = 1 << 7; const LCR_BAUD_LATCH: u8 = 1 << 7;
/// Line Status Register
const LSR: u8 = 5;
/// Input is waiting to be read from RHR /// Input is waiting to be read from RHR
const LSR_RX_READY: u8 = 1 << 0; const LSR_RX_READY: u8 = 1 << 0;
/// THR can accept another character to send /// THR can accept another character to send
const LSR_TX_IDLE: u8 = 1 << 5; const LSR_TX_IDLE: u8 = 1 << 5;
#[inline(always)]
unsafe fn read_register<N: Into<u64>>(register: N) -> u8 {
*get_register_addr(register)
}
#[inline(always)]
unsafe fn write_register<N: Into<u64>>(register: N, value: u8) {
*get_register_addr(register) = value;
}
static mut uart_tx_lock: Spinlock = unsafe { Spinlock::uninitialized() }; static mut uart_tx_lock: Spinlock = unsafe { Spinlock::uninitialized() };
const UART_TX_BUF_SIZE: u64 = 32; const UART_TX_BUF_SIZE: usize = 32;
static mut uart_tx_buf: [u8; UART_TX_BUF_SIZE as usize] = [0u8; UART_TX_BUF_SIZE as usize]; 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] /// 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] /// 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() { pub(crate) unsafe fn uartinit() {
// Disable interrupts. // Disable interrupts.
write_register(IER, 0x00); Register::InterruptEnable.write(0x00);
// Special mode to set baud rate. // Special mode to set baud rate.
write_register(LCR, LCR_BAUD_LATCH); Register::LineControl.write(LCR_BAUD_LATCH);
// LSB for baud rate of 38.4K. unsafe {
write_register(0u8, 0x03); // LSB for baud rate of 38.4K.
// MSB for baud rate of 38.4K. *(UART0 as *mut u8) = 0x03;
write_register(1u8, 0x00); // MSB for baud rate of 38.4K.
*((UART0 + 1) as *mut u8) = 0x00;
}
// Leave set-baud mode and set // Leave set-baud mode and set
// word length to 8 bits, no parity. // word length to 8 bits, no parity.
write_register(LCR, LCR_EIGHT_BITS); Register::LineControl.write(LCR_EIGHT_BITS);
// Reset and enable FIFOs. // 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. // 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( uart_tx_lock = Spinlock::new(
CStr::from_bytes_with_nul(b"uart\0") 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 /// Because it may block, it can't be called
/// from interrupts, it's only suitable for use /// from interrupts, it's only suitable for use
/// by write(). /// by write().
pub(crate) unsafe fn uartputc(c: i32) { pub(crate) unsafe fn uartputc(c: u8) {
uart_tx_lock.lock(); uart_tx_lock.lock();
// let mut buf = uart_tx_buf.lock();
// let u = uart.lock();
if crate::PANICKED { if crate::PANICKED {
loop { 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; uart_tx_w += 1;
uartstart(); uartstart();
uart_tx_lock.unlock(); uart_tx_lock.unlock();
@ -125,21 +185,22 @@ pub(crate) unsafe fn uartputc(c: i32) {
/// use interrupts, for use by kernel printf() and /// use interrupts, for use by kernel printf() and
/// to echo characters. It spins waiting for the UART's /// to echo characters. It spins waiting for the UART's
/// output register to be empty. /// output register to be empty.
pub(crate) unsafe fn uartputc_sync(c: i32) { pub(crate) unsafe fn uartputc_sync(c: u8) {
push_off(); push_off();
Uart::write_byte_sync(c);
if crate::PANICKED { // if crate::PANICKED {
loop { // loop {
core::hint::spin_loop(); // core::hint::spin_loop();
} // }
} // }
// Wait for Transmit Holding Empty to be set in LSR. // // Wait for Transmit Holding Empty to be set in LSR.
while read_register(LSR) & LSR_TX_IDLE == 0 { // while Register::LineStatus.read() & LSR_TX_IDLE == 0 {
core::hint::spin_loop(); // core::hint::spin_loop();
} // }
write_register(THR, c as i8 as u8); // Register::TransmitHolding.write(c);
pop_off(); pop_off();
} }
@ -154,47 +215,45 @@ unsafe fn uartstart() {
// Transmit buffer is ready. // Transmit buffer is ready.
return; return;
} }
if Register::LineStatus.read() & LSR_TX_IDLE == 0 {
if read_register(LSR) & LSR_TX_IDLE == 0 {
// The UART transmit holding register is full, // The UART transmit holding register is full,
// so we cannot give it another byte. // so we cannot give it another byte.
// It will interrupt when it's ready for a new byte. // It will interrupt when it's ready for a new byte.
return; return;
} }
// let buf = uart_tx_buf.lock();
let c = uart_tx_buf[(uart_tx_r % UART_TX_BUF_SIZE) as usize]; let c = uart_tx_buf[(uart_tx_r % UART_TX_BUF_SIZE) as usize];
uart_tx_r += 1; uart_tx_r += 1;
// Maybe uartputc() is waiting for space in the buffer. // Maybe uartputc() is waiting for space in the buffer.
wakeup(addr_of_mut!(uart_tx_r).cast()); wakeup(addr_of_mut!(uart_tx_r).cast());
write_register(THR, c); Register::TransmitHolding.write(c);
} }
} }
/// Read one input character from the UART. /// Read one input byte from the UART.
/// Return -1 if nothing is waiting. pub(crate) fn uartgetc() -> Option<u8> {
unsafe fn uartgetc() -> i32 { if Register::LineStatus.read() & 0x01 != 0 {
if read_register(LSR) & 0x01 != 0 {
// Input data is ready. // Input data is ready.
read_register(RHR) as i32 Some(Register::ReceiveHolding.read())
} else { } else {
-1 None
} }
} }
/// Handle a UART interrupt, raised because input has /// Handle a UART interrupt, raised because input has
/// arrived, or the uart is ready for more output, or /// arrived, or the uart is ready for more output, or
/// both. Called from devintr(). /// both. Called from devintr().
#[no_mangle] pub(crate) unsafe fn uartintr() {
pub unsafe extern "C" fn uartintr() {
// Read and process incoming characters. // Read and process incoming characters.
loop { loop {
let c = uartgetc(); if let Some(c) = uartgetc() {
if c == -1 { consoleintr(c);
} else {
break; break;
} }
consoleintr(c);
} }
// Send buffered characters. // Send buffered characters.