diff --git a/Makefile b/Makefile index f9d8733..9210827 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/kernel/rustkernel/src/arch/riscv/virtual_memory.rs b/kernel/rustkernel/src/arch/riscv/virtual_memory.rs index aa1796d..7c5ed28 100644 --- a/kernel/rustkernel/src/arch/riscv/virtual_memory.rs +++ b/kernel/rustkernel/src/arch/riscv/virtual_memory.rs @@ -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 { diff --git a/kernel/rustkernel/src/console/mod.rs b/kernel/rustkernel/src/console/mod.rs index 0f7e08a..d6bf8b7 100644 --- a/kernel/rustkernel/src/console/mod.rs +++ b/kernel/rustkernel/src/console/mod.rs @@ -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); } } diff --git a/kernel/rustkernel/src/console/printf.rs b/kernel/rustkernel/src/console/printf.rs index 8ba62bf..04aee07 100644 --- a/kernel/rustkernel/src/console/printf.rs +++ b/kernel/rustkernel/src/console/printf.rs @@ -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; diff --git a/kernel/rustkernel/src/hardware/mod.rs b/kernel/rustkernel/src/hardware/mod.rs index a070c77..53ce342 100644 --- a/kernel/rustkernel/src/hardware/mod.rs +++ b/kernel/rustkernel/src/hardware/mod.rs @@ -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))]; diff --git a/kernel/rustkernel/src/hardware/uart.rs b/kernel/rustkernel/src/hardware/uart.rs index b055efd..27c1aac 100644 --- a/kernel/rustkernel/src/hardware/uart.rs +++ b/kernel/rustkernel/src/hardware/uart.rs @@ -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>, } 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 { @@ -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 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>, +} +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>) -> 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 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(()) } } diff --git a/kernel/rustkernel/src/lib.rs b/kernel/rustkernel/src/lib.rs index fd341a8..2227067 100644 --- a/kernel/rustkernel/src/lib.rs +++ b/kernel/rustkernel/src/lib.rs @@ -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 = 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); } diff --git a/kernel/rustkernel/src/mem/mod.rs b/kernel/rustkernel/src/mem/mod.rs index 8fd69ef..54595e7 100644 --- a/kernel/rustkernel/src/mem/mod.rs +++ b/kernel/rustkernel/src/mem/mod.rs @@ -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 } diff --git a/kernel/rustkernel/src/proc/scheduler.rs b/kernel/rustkernel/src/proc/scheduler.rs index 3c9685d..98847c8 100644 --- a/kernel/rustkernel/src/proc/scheduler.rs +++ b/kernel/rustkernel/src/proc/scheduler.rs @@ -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(); diff --git a/kernel/rustkernel/src/string.rs b/kernel/rustkernel/src/string.rs index fd1d467..6e7ada9 100644 --- a/kernel/rustkernel/src/string.rs +++ b/kernel/rustkernel/src/string.rs @@ -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 { for len in 0..max_chars { diff --git a/kernel/rustkernel/src/sync/lock.rs b/kernel/rustkernel/src/sync/lock.rs index 1b024d3..51e5643 100644 --- a/kernel/rustkernel/src/sync/lock.rs +++ b/kernel/rustkernel/src/sync/lock.rs @@ -5,7 +5,6 @@ use crate::proc::{ }; use core::{ cell::UnsafeCell, - ops::Drop, ptr::{addr_of, null_mut}, sync::atomic::{AtomicBool, Ordering}, }; diff --git a/kernel/rustkernel/src/sync/mutex.rs b/kernel/rustkernel/src/sync/mutex.rs index ecd4e42..5012288 100644 --- a/kernel/rustkernel/src/sync/mutex.rs +++ b/kernel/rustkernel/src/sync/mutex.rs @@ -4,8 +4,7 @@ use super::{ }; use core::{ cell::UnsafeCell, - convert::{AsMut, AsRef}, - ops::{Deref, DerefMut, Drop}, + ops::{Deref, DerefMut}, }; pub struct Mutex { diff --git a/programs/clear.c b/programs/clear.c new file mode 100644 index 0000000..499a871 --- /dev/null +++ b/programs/clear.c @@ -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); +}