make a spinmutex (and other things)
This commit is contained in:
parent
29878e42e6
commit
96cfda564c
1
Makefile
1
Makefile
@ -5,7 +5,6 @@ P=programs
|
||||
|
||||
OBJS = \
|
||||
$K/entry.o \
|
||||
$K/console.o \
|
||||
$K/vm.o \
|
||||
$K/proc.o \
|
||||
$K/swtch.o \
|
||||
|
@ -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);
|
||||
}
|
@ -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];
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{fs::BSIZE, sleeplock::Sleeplock};
|
||||
use crate::{fs::BSIZE, sync::sleeplock::Sleeplock};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Buf {
|
||||
|
@ -10,64 +10,71 @@
|
||||
|
||||
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<Console> = 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) {
|
||||
pub fn consputc(c: u8) {
|
||||
unsafe {
|
||||
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);
|
||||
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.
|
||||
@ -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()) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
use crate::{
|
||||
riscv::{memlayout::PHYSTOP, pg_round_up, PGSIZE},
|
||||
spinlock::Spinlock,
|
||||
sync::spinlock::Spinlock,
|
||||
string::memset,
|
||||
};
|
||||
use core::{
|
||||
|
@ -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]
|
||||
|
@ -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()) };
|
||||
|
@ -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()
|
||||
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<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.
|
||||
/// 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
|
||||
}
|
||||
|
3
kernel/rustkernel/src/sync/mod.rs
Normal file
3
kernel/rustkernel/src/sync/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod sleeplock;
|
||||
pub mod spinlock;
|
||||
pub mod spinmutex;
|
@ -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};
|
||||
|
@ -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(),
|
||||
@ -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();
|
||||
}
|
||||
}
|
45
kernel/rustkernel/src/sync/spinmutex.rs
Normal file
45
kernel/rustkernel/src/sync/spinmutex.rs
Normal 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() }
|
||||
}
|
||||
}
|
@ -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 {}
|
||||
|
@ -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<N: Into<u64>>(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<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.
|
||||
// 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<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() };
|
||||
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);
|
||||
Register::LineControl.write(LCR_BAUD_LATCH);
|
||||
unsafe {
|
||||
// LSB for baud rate of 38.4K.
|
||||
write_register(0u8, 0x03);
|
||||
*(UART0 as *mut u8) = 0x03;
|
||||
// MSB for baud rate of 38.4K.
|
||||
write_register(1u8, 0x00);
|
||||
*((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<u8> {
|
||||
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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user