Make UART good

This commit is contained in:
Garen Tyler 2023-10-29 15:25:17 -06:00
parent 585da48917
commit a240d29135
Signed by: garentyler
GPG Key ID: D7A048C454CB7054
5 changed files with 179 additions and 199 deletions

View File

@ -17,7 +17,7 @@ use crate::{
sync::spinmutex::SpinMutex,
};
use core::{ffi::c_void, ptr::addr_of_mut};
use uart::{uartinit, uartputc, Uart};
use uart::UART0;
extern "C" {
fn either_copyin(dst: *mut c_void, user_src: i32, src: u64, len: u64) -> i32;
@ -48,9 +48,7 @@ impl Console {
}
impl core::fmt::Write for Console {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
for b in s.as_bytes() {
Uart::write_byte_sync(*b);
}
UART0.write_slice(s.as_bytes());
core::fmt::Result::Ok(())
}
}
@ -75,11 +73,11 @@ const fn ctrl_x(x: u8) -> u8 {
pub fn consputc(c: u8) {
if c == BACKSPACE {
// If the user typed backspace, overwrite with a space.
Uart::write_byte_sync(0x08);
Uart::write_byte_sync(b' ');
Uart::write_byte_sync(0x08);
UART0.write_byte(0x08);
UART0.write_byte(b' ');
UART0.write_byte(0x08);
} else {
Uart::write_byte_sync(c);
UART0.write_byte(c);
}
}
@ -92,7 +90,7 @@ pub 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 u8);
UART0.write_byte_buffered(c as u8);
}
}
0
@ -160,7 +158,7 @@ pub fn consoleread(user_dst: i32, mut dst: u64, mut n: i32) -> i32 {
}
pub unsafe fn consoleinit() {
uartinit();
UART0.initialize();
// Connect read and write syscalls
// to consoleread and consolewrite.

View File

@ -3,99 +3,11 @@
use crate::{
console::consoleintr,
proc::{sleep_lock, wakeup},
riscv::memlayout::UART0,
proc::wakeup,
sync::spinlock::Spinlock,
sync::spinmutex::SpinMutex,
trap::InterruptBlocker,
};
use core::ptr::addr_of_mut;
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 {
/// Alternate version of Uart::write_byte() that doesn't
/// use interrupts, for use by kernel printf() and
/// to echo characters. It spins waiting for the UART's
/// output register to be empty.
pub fn write_byte_sync(x: u8) {
let _ = InterruptBlocker::new();
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);
}
/// 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);
}
}
}
use core::ptr::addr_of;
// The UART control registers.
// Some have different meanings for read vs write.
@ -115,116 +27,186 @@ const LSR_RX_READY: u8 = 1 << 0;
/// THR can accept another character to send
const LSR_TX_IDLE: u8 = 1 << 5;
static mut uart_tx_lock: Spinlock = Spinlock::new();
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: usize = 0;
/// Read next from uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE]
static mut uart_tx_r: usize = 0;
pub(crate) unsafe fn uartinit() {
// Disable interrupts.
Register::InterruptEnable.write(0x00);
// Special mode to set baud rate.
Register::LineControl.write(LCR_BAUD_LATCH);
unsafe {
// LSB for baud rate of 38.4K.
*(UART0 as *mut u8) = 0x03;
// MSB for baud rate of 38.4K.
*((UART0 + 1) as *mut u8) = 0x00;
pub static UART0: Uart = Uart::new(crate::riscv::memlayout::UART0);
enum Register {
ReceiveHolding,
TransmitHolding,
InterruptEnable,
FIFOControl,
InterruptStatus,
LineControl,
LineStatus,
}
impl Register {
pub fn as_offset(&self) -> usize {
match self {
Register::ReceiveHolding => 0,
Register::TransmitHolding => 0,
Register::InterruptEnable => 1,
Register::FIFOControl => 2,
Register::InterruptStatus => 2,
Register::LineControl => 2,
Register::LineStatus => 5,
}
}
pub fn as_ptr(&self, base_address: usize) -> *mut u8 {
(base_address + self.as_offset()) as *mut u8
}
pub fn read(&self, base_address: usize) -> u8 {
unsafe { self.as_ptr(base_address).read_volatile() }
}
pub fn write(&self, base_address: usize, value: u8) {
unsafe { self.as_ptr(base_address).write_volatile(value) }
}
// Leave set-baud mode and set
// word length to 8 bits, no parity.
Register::LineControl.write(LCR_EIGHT_BITS);
// Reset and enable FIFOs.
Register::FIFOControl.write(FCR_FIFO_ENABLE | FCR_FIFO_CLEAR);
// Enable transmit and receive interrupts.
Register::InterruptEnable.write(IER_TX_ENABLE | IER_RX_ENABLE);
uart_tx_lock = Spinlock::new();
}
/// Add a character to the output buffer and tell the
/// UART to start sending if it isn't already.
/// Blocks if the output buffer is full.
/// 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: u8) {
let _guard = uart_tx_lock.lock();
// let mut buf = uart_tx_buf.lock_unguarded();
// let u = uart.lock_unguarded();
pub struct Uart {
pub lock: Spinlock,
pub base_address: usize,
pub buffer: [u8; UART_TX_BUF_SIZE],
pub queue_start: usize,
pub queue_end: usize,
}
impl Uart {
pub const fn new(base_address: usize) -> Uart {
Uart {
lock: Spinlock::new(),
base_address,
buffer: [0u8; UART_TX_BUF_SIZE],
queue_start: 0,
queue_end: 0,
}
}
/// Initialize the UART.
pub unsafe fn initialize(&self) {
// Disable interrupts.
Register::InterruptEnable.write(self.base_address, 0x00);
// Special mode to set baud rate.
Register::LineControl.write(self.base_address, LCR_BAUD_LATCH);
// LSB for baud rate of 38.4K.
*(self.base_address as *mut u8) = 0x03;
// MSB for baud rate of 38.4K.
*((self.base_address + 1) as *mut u8) = 0x00;
// Leave set-baud mode and set
// word length to 8 bits, no parity.
Register::LineControl.write(self.base_address, LCR_EIGHT_BITS);
// Reset and enable FIFOs.
Register::FIFOControl.write(self.base_address, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR);
// Enable transmit and receive interrupts.
Register::InterruptEnable.write(self.base_address, IER_TX_ENABLE | IER_RX_ENABLE);
}
pub fn interrupt(&self) {
// Read and process incoming data.
while let Some(b) = self.read_byte() {
consoleintr(b);
}
if crate::PANICKED {
// Send buffered characters.
let _guard = self.lock.lock();
self.send_buffered_bytes();
}
/// Read one byte from the UART.
pub fn read_byte(&self) -> Option<u8> {
if Register::LineStatus.read(self.base_address) & 0x01 != 0 {
// Input data is ready.
Some(Register::ReceiveHolding.read(self.base_address))
} else {
None
}
}
/// Write a byte to the UART without interrupts.
/// Used for kernel printing and character echoing.
pub fn write_byte(&self, b: u8) {
let _ = InterruptBlocker::new();
if unsafe { crate::PANICKED } {
loop {
core::hint::spin_loop();
}
}
while uart_tx_w == uart_tx_r + UART_TX_BUF_SIZE {
// Buffer is full.
// Wait for uartstart() to open up space in the buffer.
sleep_lock(
addr_of_mut!(uart_tx_r).cast(),
addr_of_mut!(uart_tx_lock).cast(),
);
// Wait for Transmit Holding Empty to be set in LSR.
while Register::LineStatus.read(self.base_address) & LSR_TX_IDLE == 0 {
core::hint::spin_loop();
}
uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] = c;
uart_tx_w += 1;
uartstart();
}
Register::TransmitHolding.write(self.base_address, b);
}
pub fn write_slice(&self, bytes: &[u8]) {
for b in bytes {
self.write_byte(*b);
}
}
/// Write a byte to the UART and buffer it.
/// Should not be used in interrupts.
pub fn write_byte_buffered(&self, b: u8) {
let guard = self.lock.lock();
/// If the UART is idle, and a character is waiting
/// in the transmit buffer, send it.
/// Caller must hold uart_tx_lock.
/// Called from both the top and bottom halves.
unsafe fn uartstart() {
if unsafe { crate::PANICKED } {
loop {
if uart_tx_w == uart_tx_r {
// Transmit buffer is ready.
core::hint::spin_loop();
}
}
while self.queue_start == self.queue_end + 1 {
unsafe {
guard.sleep(addr_of!(*self).cast_mut().cast());
}
}
// Unsafely cast self as mutable.
// self.lock is held so it should be fine.
let this: &mut Uart = unsafe { &mut *addr_of!(*self).cast_mut() };
// Add the byte onto the end of the queue.
this.buffer[this.queue_end] = b;
this.queue_end += 1;
this.queue_end %= this.buffer.len();
this.send_buffered_bytes();
}
pub fn write_slice_buffered(&self, bytes: &[u8]) {
for b in bytes {
self.write_byte_buffered(*b);
}
}
/// If the UART is idle, and a character is
/// waiting in the transmit buffer, send it.
/// self.lock should be held.
fn send_buffered_bytes(&self) {
let this: &mut Uart = unsafe { &mut *addr_of!(*self).cast_mut() };
loop {
// Ensure the indices are correct.
this.queue_start %= this.buffer.len();
this.queue_end %= this.buffer.len();
if this.queue_start == this.queue_end {
// The buffer is empty, we're finished sending bytes.
return;
}
if Register::LineStatus.read() & LSR_TX_IDLE == 0 {
if Register::LineStatus.read(this.base_address) & 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_unguarded();
let c = uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE];
uart_tx_r += 1;
// Pop a byte from the front of the queue.
let b = this.buffer[this.queue_start];
this.queue_start += 1;
this.queue_start %= this.buffer.len();
// Maybe uartputc() is waiting for space in the buffer.
wakeup(addr_of_mut!(uart_tx_r).cast());
unsafe {
wakeup(addr_of!(*self).cast_mut().cast());
}
Register::TransmitHolding.write(c);
Register::TransmitHolding.write(this.base_address, b);
}
}
}
/// Read one input byte from the UART.
pub(crate) fn uartgetc() -> Option<u8> {
if Register::LineStatus.read() & 0x01 != 0 {
// Input data is ready.
Some(Register::ReceiveHolding.read())
} else {
None
}
}
/// Handle a UART interrupt, raised because input has
/// arrived, or the uart is ready for more output, or
/// both. Called from devintr().
pub(crate) unsafe fn uartintr() {
// Read and process incoming characters.
while let Some(c) = uartgetc() {
consoleintr(c);
}
// Send buffered characters.
let _guard = uart_tx_lock.lock();
uartstart();
}

View File

@ -30,7 +30,7 @@ pub unsafe fn kvmmake() -> Pagetable {
kvmmap(pagetable, QEMU_POWER, QEMU_POWER, PGSIZE, PTE_R | PTE_W);
// UART registers
kvmmap(pagetable, UART0, UART0, PGSIZE, PTE_R | PTE_W);
kvmmap(pagetable, UART0 as u64, UART0 as u64, PGSIZE, PTE_R | PTE_W);
// VirtIO MMIO disk interface
kvmmap(pagetable, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);

View File

@ -22,7 +22,7 @@ use super::{MAXVA, PGSIZE};
pub const QEMU_POWER: u64 = 0x100000u64;
// QEMU puts UART registers here in physical memory.
pub const UART0: u64 = 0x10000000;
pub const UART0: usize = 0x10000000;
pub const UART0_IRQ: i32 = 10;
// Virtio MMIO interface

View File

@ -56,7 +56,7 @@ pub unsafe extern "C" fn devintr() -> i32 {
let irq = plic::plic_claim();
if irq == UART0_IRQ {
crate::console::uart::uartintr();
crate::console::uart::UART0.interrupt();
} else if irq == VIRTIO0_IRQ {
virtio_disk_intr();
} else if irq > 0 {