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 = \
$K/entry.o \
$K/console.o \
$K/vm.o \
$K/proc.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 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];

View File

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

View File

@ -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()) };
}
}
}

View File

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

View File

@ -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]

View File

@ -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()) };

View File

@ -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
}

View File

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

View File

@ -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};

View File

@ -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();
}
}

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::{
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 {}

View File

@ -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.