restructure uart to compile correctly

This commit is contained in:
Garen Tyler 2024-02-23 00:06:59 -07:00
parent ca962854c5
commit 4ea64e0b6b
Signed by: garentyler
GPG Key ID: D7A048C454CB7054
13 changed files with 190 additions and 143 deletions

View File

@ -100,6 +100,7 @@ UPROGS=\
$P/_wc\
$P/_zombie\
$P/_shutdown\
$P/_clear\
fs.img: mkfs README.md $(UPROGS)
mkfs/mkfs fs.img README.md $(UPROGS)

View File

@ -43,13 +43,7 @@ pub unsafe fn kvmmake() -> Pagetable {
memset(pagetable.cast(), 0, PAGE_SIZE);
// QEMU test interface used for power management.
kvmmap(
pagetable,
QEMU_POWER,
QEMU_POWER,
PAGE_SIZE,
PTE_R | PTE_W,
);
kvmmap(pagetable, QEMU_POWER, QEMU_POWER, PAGE_SIZE, PTE_R | PTE_W);
// UART registers
for (_, uart) in &crate::hardware::UARTS {
@ -63,22 +57,10 @@ pub unsafe fn kvmmake() -> Pagetable {
}
// VirtIO MMIO disk interface
kvmmap(
pagetable,
VIRTIO0,
VIRTIO0,
PAGE_SIZE,
PTE_R | PTE_W,
);
kvmmap(pagetable, VIRTIO0, VIRTIO0, PAGE_SIZE, PTE_R | PTE_W);
// PLIC
kvmmap(
pagetable,
PLIC,
PLIC,
0x400000,
PTE_R | PTE_W,
);
kvmmap(pagetable, PLIC, PLIC, 0x400000, PTE_R | PTE_W);
let etext_addr = addr_of!(etext) as usize;
@ -159,16 +141,19 @@ pub unsafe fn kvminithart() {
/// - 21..30: 9 bits of level 0 index.
/// - 30..39: 9 bits of level 0 index.
/// - 39..64: Must be zero.
pub unsafe fn walk(mut pagetable: Pagetable, virtual_addr: usize, alloc: bool) -> *mut PagetableEntry {
pub unsafe fn walk(
mut pagetable: Pagetable,
virtual_addr: usize,
alloc: bool,
) -> *mut PagetableEntry {
if virtual_addr > VIRTUAL_MAX {
panic!("walk");
}
let mut level = 2;
while level > 0 {
let pte = addr_of_mut!(
pagetable.as_mut().unwrap()[(virtual_addr >> (12 + (level * 9))) & 0x1ff]
);
let pte =
addr_of_mut!(pagetable.as_mut().unwrap()[(virtual_addr >> (12 + (level * 9))) & 0x1ff]);
if (*pte) & PTE_V as u64 > 0 {
pagetable = (((*pte) >> 10) << 12) as usize as Pagetable;
@ -202,7 +187,7 @@ pub unsafe extern "C" fn walkaddr(pagetable: Pagetable, virtual_addr: usize) ->
return 0;
}
let pte = walk(pagetable, virtual_addr , false);
let pte = walk(pagetable, virtual_addr, false);
if pte.is_null() || *pte & PTE_V as u64 == 0 || *pte & PTE_U as u64 == 0 {
return 0;
}
@ -272,12 +257,7 @@ pub unsafe fn mappages(
///
/// `virtual_addr` amust be page-aligned. The mappings must exist.
/// Optionally free the physical memory.
pub unsafe fn uvmunmap(
pagetable: Pagetable,
virtual_addr: usize,
num_pages: usize,
free: bool,
) {
pub unsafe fn uvmunmap(pagetable: Pagetable, virtual_addr: usize, num_pages: usize, free: bool) {
if virtual_addr % PAGE_SIZE != 0 {
panic!("uvmunmap: not aligned");
}
@ -359,14 +339,7 @@ pub unsafe extern "C" fn uvmalloc(
memset(mem.cast(), 0, PAGE_SIZE);
if mappages(
pagetable,
a,
PAGE_SIZE,
mem as usize,
PTE_R | PTE_U | xperm,
) != 0
{
if mappages(pagetable, a, PAGE_SIZE, mem as usize, PTE_R | PTE_U | xperm) != 0 {
kfree(mem.cast());
uvmdealloc(pagetable, a, old_size);
return 0;
@ -390,14 +363,8 @@ pub unsafe extern "C" fn uvmdealloc(pagetable: Pagetable, old_size: usize, new_s
}
if round_up_page(new_size) < round_up_page(old_size) {
let num_pages =
(round_up_page(old_size) - round_up_page(new_size)) / PAGE_SIZE;
uvmunmap(
pagetable,
round_up_page(new_size),
num_pages,
true,
);
let num_pages = (round_up_page(old_size) - round_up_page(new_size)) / PAGE_SIZE;
uvmunmap(pagetable, round_up_page(new_size), num_pages, true);
}
new_size as u64
@ -424,12 +391,7 @@ pub unsafe fn freewalk(pagetable: Pagetable) {
/// Free user memory pages, then free pagetable pages.
pub unsafe fn uvmfree(pagetable: Pagetable, size: usize) {
uvmunmap(
pagetable,
0,
round_up_page(size) / PAGE_SIZE,
true,
);
uvmunmap(pagetable, 0, round_up_page(size) / PAGE_SIZE, true);
freewalk(pagetable);
}
@ -563,7 +525,12 @@ pub unsafe extern "C" fn copyin(
// depending on usr_dst.
// Returns 0 on success, -1 on error.
#[no_mangle]
pub unsafe extern "C" fn either_copyout(user_dst: i32, dst: usize, src: *mut u8, len: usize) -> i32 {
pub unsafe extern "C" fn either_copyout(
user_dst: i32,
dst: usize,
src: *mut u8,
len: usize,
) -> i32 {
let p = Process::current().unwrap();
if user_dst > 0 {

View File

@ -13,7 +13,7 @@ pub mod printf;
use crate::{
arch::virtual_memory::{either_copyin, either_copyout},
fs::file::{devsw, CONSOLE},
hardware::uart::Uart,
hardware::uart::BufferedUart,
proc::{
process::{procdump, Process},
scheduler::wakeup,
@ -22,7 +22,7 @@ use crate::{
};
use core::ptr::addr_of_mut;
pub static UART0: &Uart = &crate::hardware::UARTS[0].1;
pub static UART0: &BufferedUart = &crate::hardware::UARTS[0].1;
pub const BACKSPACE: u8 = 0x00;
pub const INPUT_BUF_SIZE: usize = 128;
@ -48,8 +48,7 @@ impl Console {
}
impl core::fmt::Write for Console {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
UART0.write_slice(s.as_bytes());
core::fmt::Result::Ok(())
UART0.writer().write_str(s)
}
}
@ -73,11 +72,9 @@ const fn ctrl_x(x: u8) -> u8 {
pub fn consputc(c: u8) {
if c == BACKSPACE {
// If the user typed backspace, overwrite with a space.
UART0.write_byte(0x08);
UART0.write_byte(b' ');
UART0.write_byte(0x08);
UART0.write_slice_buffered(b"\x08 \x08");
} else {
UART0.write_byte(c);
UART0.write_byte_buffered(c);
}
}

View File

@ -30,16 +30,18 @@ pub(crate) use println;
/// Does not use any locks.
macro_rules! uprint {
($($arg:tt)*) => {{
use $crate::hardware::uart::Uart;
// use $crate::hardware::uart::{BufferedUart, Uart, UartWriter};
use core::fmt::Write;
// Do some casts to get a mutable reference.
// Safe because Uart's core::fmt::Write implementation
// only uses the &mut reference immutably.
let uart: *const Uart = &$crate::hardware::UARTS[0].1 as *const Uart;
let uart: &mut Uart = unsafe { &mut *uart.cast_mut() };
// let mut uart: UartWriter = $crate::console::UART0.writer_unbuffered();
let _ = core::write!(uart, $($arg)*);
// let uart: &BufferedUart = &$crate::console::UART0;
// let uart: &Uart = &**uart;
// let mut uart: UartWriter = uart.writer();
//
let _ = core::write!($crate::console::UART0.writer_unbuffered(), $($arg)*);
// let _ = core::write!(uart, $($arg)*);
}};
}
pub(crate) use uprint;

View File

@ -4,6 +4,6 @@ pub mod ramdisk;
pub mod uart;
pub mod virtio_disk;
use uart::Uart;
use uart::BufferedUart;
pub static UARTS: [(usize, Uart); 1] = [(10, Uart::new(0x1000_0000))];
pub static UARTS: [(usize, BufferedUart); 1] = [(10, BufferedUart::new(0x1000_0000))];

View File

@ -2,8 +2,11 @@
#![allow(non_upper_case_globals)]
use crate::{
arch::trap::InterruptBlocker, console::consoleintr, proc::scheduler::wakeup, queue::Queue,
sync::mutex::Mutex,
arch::trap::InterruptBlocker,
console::consoleintr,
proc::scheduler::wakeup,
queue::Queue,
sync::mutex::{Mutex, MutexGuard},
};
use core::ptr::addr_of;
@ -59,14 +62,10 @@ impl Register {
pub struct Uart {
pub base_address: usize,
pub buffer: Mutex<Queue<u8>>,
}
impl Uart {
pub const fn new(base_address: usize) -> Uart {
Uart {
base_address,
buffer: Mutex::new(Queue::new()),
}
Uart { base_address }
}
/// Initialize the UART.
pub unsafe fn initialize(&self) {
@ -86,14 +85,12 @@ impl Uart {
// Enable transmit and receive interrupts.
Register::InterruptEnable.write(self.base_address, IER_TX_ENABLE | IER_RX_ENABLE);
}
/// Handle an interrupt from the hardware.
pub fn interrupt(&self) {
// Read and process incoming data.
while let Some(b) = self.read_byte() {
consoleintr(b);
}
// Send buffered characters.
self.send_buffered_bytes();
}
/// Read one byte from the UART.
pub fn read_byte(&self) -> Option<u8> {
@ -104,40 +101,81 @@ impl Uart {
None
}
}
/// Write a byte to the UART without interrupts.
/// Used for kernel printing and character echoing.
pub fn write_byte(&self, b: u8) {
pub fn writer(&self) -> UartWriter<'_> {
UartWriter(self)
}
pub fn can_write_byte(&self) -> bool {
Register::LineStatus.read(self.base_address) & LSR_TX_IDLE != 0
}
/// Attempt to write one byte to the UART.
/// Returns a bool representing whether the byte was written.
pub fn write_byte(&self, byte: u8) -> bool {
// Block interrupts to prevent TOCTOU manipulation.
let _ = InterruptBlocker::new();
if *crate::PANICKED.lock_spinning() {
loop {
core::hint::spin_loop();
}
if self.can_write_byte() {
Register::TransmitHolding.write(self.base_address, byte);
true
} else {
false
}
// Wait for Transmit Holding Empty to be set in LSR.
while Register::LineStatus.read(self.base_address) & LSR_TX_IDLE == 0 {
}
pub fn write_byte_blocking(&self, byte: u8) {
while !self.write_byte(byte) {
core::hint::spin_loop();
}
Register::TransmitHolding.write(self.base_address, b);
}
pub fn write_slice(&self, bytes: &[u8]) {
pub fn write_slice_blocking(&self, bytes: &[u8]) {
for b in bytes {
self.write_byte(*b);
}
}
}
impl From<BufferedUart> for Uart {
fn from(value: BufferedUart) -> Self {
value.inner
}
}
#[derive(Copy, Clone)]
pub struct UartWriter<'u>(&'u Uart);
impl<'u> core::fmt::Write for UartWriter<'u> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
self.0.write_slice_blocking(s.as_bytes());
core::fmt::Result::Ok(())
}
}
pub struct BufferedUart {
inner: Uart,
buffer: Mutex<Queue<u8>>,
}
impl BufferedUart {
pub const fn new(base_address: usize) -> BufferedUart {
BufferedUart {
inner: Uart::new(base_address),
buffer: Mutex::new(Queue::new()),
}
}
pub fn interrupt(&self) {
let _ = InterruptBlocker::new();
self.inner.interrupt();
// Send buffered characters.
let buf = self.buffer.lock_spinning();
self.send_buffered_bytes(buf);
}
pub fn writer(&self) -> BufferedUartWriter<'_> {
BufferedUartWriter(self)
}
pub fn writer_unbuffered(&self) -> UartWriter<'_> {
self.inner.writer()
}
/// Write a byte to the UART and buffer it.
/// Should not be used in interrupts.
pub fn write_byte_buffered(&self, b: u8) {
pub fn write_byte_buffered(&self, byte: u8) {
let mut buf = self.buffer.lock_spinning();
if *crate::PANICKED.lock_spinning() {
loop {
core::hint::spin_loop();
}
}
// Sleep until there is space in the buffer.
while buf.space_remaining() == 0 {
unsafe {
@ -146,48 +184,69 @@ impl Uart {
}
// Add the byte onto the end of the queue.
buf.push_back(b).expect("space in the uart queue");
buf.push_back(byte).expect("space in the uart queue");
// Drop buf so that send_buffered_bytes() can lock it again.
core::mem::drop(buf);
self.send_buffered_bytes();
self.send_buffered_bytes(buf);
}
/// Write a slice to the UART and buffer it.
/// Should not be used in interrupts.
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
/// 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 mut buf = self.buffer.lock_spinning();
/// Returns how many bytes were sent.
fn send_buffered_bytes(&self, mut buf: MutexGuard<'_, Queue<u8>>) -> usize {
let mut i = 0;
loop {
if Register::LineStatus.read(self.base_address) & LSR_TX_IDLE == 0 {
if !self.inner.can_write_byte() {
// 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;
break;
}
// Pop a byte from the front of the queue.
let Some(b) = buf.pop_front() else {
// Pop a byte from the front of the queue and send it.
match buf.pop_front() {
Some(b) => self.inner.write_byte(b),
// The buffer is empty, we're finished sending bytes.
return;
None => return 0,
};
// Maybe uartputc() is waiting for space in the buffer.
i += 1;
// Check if uartputc() is waiting for space in the buffer.
unsafe {
wakeup(addr_of!(*self).cast_mut().cast());
}
}
Register::TransmitHolding.write(self.base_address, b);
i
}
}
impl core::ops::Deref for BufferedUart {
type Target = Uart;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl From<Uart> for BufferedUart {
fn from(value: Uart) -> Self {
BufferedUart {
inner: value,
buffer: Mutex::new(Queue::new()),
}
}
}
impl core::fmt::Write for Uart {
#[derive(Copy, Clone)]
pub struct BufferedUartWriter<'u>(&'u BufferedUart);
impl<'u> core::fmt::Write for BufferedUartWriter<'u> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
self.write_slice(s.as_bytes());
self.0.write_slice_buffered(s.as_bytes());
core::fmt::Result::Ok(())
}
}

View File

@ -4,6 +4,7 @@
#![allow(clippy::missing_safety_doc)]
#![feature(negative_impls)]
#![feature(panic_info_message)]
#![feature(str_from_raw_parts)]
extern crate alloc;
extern crate core;
@ -21,9 +22,12 @@ mod sync;
mod syscall;
use crate::{proc::cpu::Cpu, sync::mutex::Mutex};
use core::ffi::{c_char, CStr};
use core::{
ffi::{c_char, CStr},
ptr::addr_of,
};
pub(crate) use crate::console::printf::{print, println, uprintln};
pub(crate) use crate::console::printf::{print, println, uprint, uprintln};
pub static mut STARTED: bool = false;
pub static PANICKED: Mutex<bool> = Mutex::new(false);
@ -79,34 +83,33 @@ pub unsafe fn main() -> ! {
arch::trap::inithart();
arch::interrupt::inithart();
}
proc::scheduler::scheduler();
}
#[panic_handler]
fn panic_wrapper(panic_info: &core::panic::PanicInfo) -> ! {
if let Some(location) = panic_info.location() {
print!("kernel panic ({}): ", location.file());
uprint!("kernel panic ({}): ", location.file());
} else {
print!("kernel panic: ");
uprint!("kernel panic: ");
}
if let Some(s) = panic_info.message() {
println!("{}", s);
uprintln!("{}", s);
} else if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
println!("{}", s);
uprintln!("{}", s);
} else if let Some(s) = panic_info.payload().downcast_ref::<&CStr>() {
println!("{:?}", s);
uprintln!("{:?}", s);
} else {
println!("could not recover error message");
uprintln!("could not recover error message");
}
println!("███████╗██╗ ██╗ ██████╗██╗ ██╗██╗██╗");
println!("██╔════╝██║ ██║██╔════╝██║ ██╔╝██║██║");
println!("█████╗ ██║ ██║██║ █████╔╝ ██║██║");
println!("██╔══╝ ██║ ██║██║ ██╔═██╗ ╚═╝╚═╝");
println!("██║ ╚██████╔╝╚██████╗██║ ██╗██╗██╗");
println!("╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝╚═╝╚═╝");
uprintln!("███████╗██╗ ██╗ ██████╗██╗ ██╗██╗██╗");
uprintln!("██╔════╝██║ ██║██╔════╝██║ ██╔╝██║██║");
uprintln!("█████╗ ██║ ██║██║ █████╔╝ ██║██║");
uprintln!("██╔══╝ ██║ ██║██║ ██╔═██╗ ╚═╝╚═╝");
uprintln!("██║ ╚██████╔╝╚██████╗██║ ██╗██╗██╗");
uprintln!("╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝╚═╝╚═╝");
unsafe {
*crate::PANICKED.lock_spinning() = true;
@ -120,8 +123,17 @@ fn panic_wrapper(panic_info: &core::panic::PanicInfo) -> ! {
}
#[no_mangle]
pub unsafe extern "C" fn panic(_: *const c_char) -> ! {
panic!("panic from c");
// let s = CStr::from_ptr(s).to_str().unwrap_or("panic from c");
// panic!("{:?}", CStr::from_ptr(s));
pub unsafe extern "C" fn panic(msg: *const c_char) -> ! {
let mut message = [b' '; 32];
let mut i = 0;
loop {
match *msg.add(i) {
0 => break,
c => message[i] = c as u8,
}
i += 1;
}
let message = core::str::from_raw_parts(addr_of!(message[0]), i);
panic!("panic from c: {}", message);
}

View File

@ -3,7 +3,7 @@ pub mod kalloc;
#[no_mangle]
pub unsafe extern "C" fn memset(dst: *mut u8, data: u8, max_bytes: usize) -> *mut u8 {
for i in 0..max_bytes {
*dst.add(i as usize) = data as u8;
*dst.add(i) = data;
}
dst
}

View File

@ -5,6 +5,7 @@ use super::{
};
use crate::{
arch,
console::printf::println,
sync::spinlock::{Spinlock, SpinlockGuard},
};
use core::{
@ -34,6 +35,8 @@ pub unsafe fn r#yield() {
// - eventually that process transfers control
// via swtch back to the scheduler.
pub unsafe fn scheduler() -> ! {
println!("hart {} starting scheduler", Cpu::current_id());
let cpu = Cpu::current();
cpu.proc = null_mut();

View File

@ -1,4 +1,4 @@
use core::{ffi::c_char, option::Option};
use core::ffi::c_char;
pub(crate) unsafe fn strlen_checked(s: *const c_char, max_chars: usize) -> Option<i32> {
for len in 0..max_chars {

View File

@ -5,7 +5,6 @@ use crate::proc::{
};
use core::{
cell::UnsafeCell,
ops::Drop,
ptr::{addr_of, null_mut},
sync::atomic::{AtomicBool, Ordering},
};

View File

@ -4,8 +4,7 @@ use super::{
};
use core::{
cell::UnsafeCell,
convert::{AsMut, AsRef},
ops::{Deref, DerefMut, Drop},
ops::{Deref, DerefMut},
};
pub struct Mutex<T> {

8
programs/clear.c Normal file
View File

@ -0,0 +1,8 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[]) {
write(1, "\033[2J\033[H", 7);
exit(0);
}