From 41a450393ebf3a24085874c7e2738d71cc5fad33 Mon Sep 17 00:00:00 2001 From: gil Date: Mon, 13 May 2024 22:59:41 -0500 Subject: [PATCH] Add rx ability to `uart` --- Cargo.lock | 34 +++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 28 +++++++++++++++++++++------ src/uart.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0acf8e8..479f982 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,40 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "riscv-uefi" version = "0.1.0" +dependencies = [ + "spinning_top", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "spinning_top" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" +dependencies = [ + "lock_api", +] diff --git a/Cargo.toml b/Cargo.toml index cac17e8..108623b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] +spinning_top = "0.3.0" diff --git a/src/main.rs b/src/main.rs index e43a6f0..24f9197 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,19 @@ unsafe extern "C" fn _start() -> ! { // set the stack pointer "la sp, _init_stack_top", + "la sp, _init_stack_top", + + // clear the BSS + "la t0, _bss_start", + "la t1, _bss_end", + "bgeu t0, t1, 2f", + "1:", + "sb zero, 0(t0)", + "addi t0, t0, 1", + "bne t0, t1, 1b", + "2:", + // BSS is clear! + // "tail-call" to {entry} (call without saving a return address) "tail {entry}", entry = sym entry, // {entry} refers to the function [entry] below @@ -30,13 +43,16 @@ unsafe extern "C" fn _start() -> ! { } extern "C" fn entry() -> ! { - // UNSAFE: correct address for QEMU virt device - let mut console = unsafe { uart::Device::new(0x1000_0000) }; - for byte in "Hello, world!".bytes() { - console.put(byte); - } + println!("This should not print because the console is not initialised."); + unsafe { uart::init_console(0x1000_0000) }; + println!("Hello, world!"); - loop {} + loop { + let c = uart::CONSOLE.lock().as_mut().and_then(uart::Device::get); + if let Some(c) = c { + print!("{}", c as char); + } + } } #[panic_handler] diff --git a/src/uart.rs b/src/uart.rs index da536c3..f54e272 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -1,5 +1,9 @@ //! This module provides access to the UART console. +use spinning_top::Spinlock; + +pub static CONSOLE: Spinlock> = Spinlock::new(None); + /// Represents an initialised UART device. pub struct Device { base: usize, @@ -29,4 +33,55 @@ impl Device { core::ptr::write_volatile(ptr, character); } } + + /// Gets a character from the UART input. + pub fn get(&mut self) -> Option { + const READY: u8 = 0b1; + + let ptr = self.base as *mut u8; + // SAFETY: Fine as long as base is correct. + let lsr = unsafe { ptr.offset(5) }; + unsafe { + if core::ptr::read_volatile(lsr) & READY == READY { + Some(core::ptr::read_volatile(ptr)) + } else { + None + } + } + } +} + +impl core::fmt::Write for Device { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + for c in s.bytes() { + self.put(c); + } + Ok(()) // there are never errors writing to UART :) + } +} + +/// Initialise the UART debugging console. +/// # Safety +/// `base` must point to the base address of a UART device. +pub unsafe fn init_console(base: usize) { + let mut console = CONSOLE.lock(); + *console = Some(unsafe { Device::new(base) }); +} + +/// Prints a formatted string to the [CONSOLE]. +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ({ + use core::fmt::Write; + $crate::uart::CONSOLE.lock().as_mut().map(|writer| { + writer.write_fmt(format_args!($($arg)*)).unwrap() + }); + }); +} + +/// println prints a formatted string to the [CONSOLE] with a trailing newline character. +#[macro_export] +macro_rules! println { + ($fmt:expr) => ($crate::print!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => ($crate::print!(concat!($fmt, "\n"), $($arg)*)); }