+++ /dev/null
-use spinning_top::Spinlock;
-use uart_16550::MmioSerialPort;
-
-/*
- * The goal here is to produce a console logging singleton
- * that can then be accessed through print! and println!
- * macros. Singletons are kinda frowned on in Rust, but
- * this can still be done. It requires some kind of sync
- * mutex (I use spinning_top because the stdlib Mutex is
- * not available), hiding initialization state (this uses
- * an Option because the static initializaer code needs
- * to be some kind of constant expression), and an AP
- * that mutates the state but doesn't leak it (here, it's
- * output-only macros).
- */
-
-pub struct Console {
- serial_port: MmioSerialPort,
-}
-
-impl Console {
- pub unsafe fn new(base: usize) -> Self {
- let mut serial_port = MmioSerialPort::new(base);
- serial_port.init();
- Console { serial_port }
- }
-
- pub fn write(&mut self, data: u8) {
- self.serial_port.send(data);
- }
-}
-
-// Write trait is used in the macros below; this basicallt
-// is a simple function for writing a string slice out to
-// the console.
-
-impl core::fmt::Write for Console {
- fn write_str(&mut self, s: &str) -> core::fmt::Result {
- return self.serial_port.write_str(s);
- }
-}
-
-static SERIAL_BASE: usize = 0x1000_0000;
-
-// Spinlock provides us a means to have a global console that is initialized
-// at runtime. Note below that locking outside of this module seems to have
-// odd behaviors, especially failure to ever acquire the lock.
-// The public specifier is here to enable the use of macros.
-pub static CONSOLE: Spinlock<Option<Console>> = Spinlock::new(None);
-
-// Since we can't use something like lazy_static!, we're left with having
-// to explicitly call the init first. This should be done only once
-// at early initialization time
-pub fn init_console() {
- let mut console = CONSOLE.lock();
- *console = Some(unsafe { Console::new(SERIAL_BASE) })
-}
-
-// Read a byte from the underlying UART, clearing any asserted interrupt in the process.
-// It's not entirely clear why, but locking CONSOLE outside of this module, or at least doing
-// so while in interrupt context, seems to never return, but providing a public function
-// to do the work gets around this problem.
-pub fn console_read() -> u8 {
- return CONSOLE
- .lock()
- .as_mut()
- .unwrap_or_else(|| panic!("Failed to acquire console for reading!"))
- .serial_port
- .receive();
-}
-
-// These, like a lot of the code in here, came courtesy of Meyer Zinn's
-// baremetal Rust tutorials: https://meyerzinn.tech/posts/2023/03/08/p1-printing-and-allocating/
-// I don't know how to use macros that well yet!
-
-#[macro_export]
-macro_rules! print {
- ($($arg:tt)*) => ({
- use core::fmt::Write;
- $crate::console::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)*));
-}
#![feature(panic_info_message)]
#[macro_use]
-mod console;
+mod uart;
mod heap;
mod trap;
#[no_mangle] // Again, being mindful of the C calling convention
extern "C" fn entry() -> ! {
// Init serial console
- use crate::console;
+ use crate::uart;
use core::arch::asm;
- console::init_console();
- println!("Console init complete");
+ uart::init_uart();
+ println!("UART init complete");
// Init heap
use crate::heap;
-use crate::console::console_read;
+use crate::uart::uart_read;
use core::arch::asm;
use plic::Plic;
let plic = get_plic();
match plic.claim(0) {
Some(int) => {
- let data = console_read();
+ let data = uart_read();
print!("{}", data as char);
plic.complete(0, int);
}
--- /dev/null
+use spinning_top::Spinlock;
+use uart_16550::MmioSerialPort;
+
+/*
+ * The goal here is to produce a console logging singleton
+ * that can then be accessed through print! and println!
+ * macros. Singletons are kinda frowned on in Rust, but
+ * this can still be done. It requires some kind of sync
+ * mutex (I use spinning_top because the stdlib Mutex is
+ * not available), hiding initialization state (this uses
+ * an Option because the static initializaer code needs
+ * to be some kind of constant expression), and an AP
+ * that mutates the state but doesn't leak it (here, it's
+ * output-only macros).
+ */
+
+pub struct Uart {
+ serial_port: MmioSerialPort,
+}
+
+impl Uart {
+ pub unsafe fn new(base: usize) -> Self {
+ let mut serial_port = MmioSerialPort::new(base);
+ serial_port.init();
+ Uart { serial_port }
+ }
+
+ pub fn write(&mut self, data: u8) {
+ self.serial_port.send(data);
+ }
+}
+
+// Write trait is used in the macros below; this basicallt
+// is a simple function for writing a string slice out to
+// the console.
+
+impl core::fmt::Write for Uart {
+ fn write_str(&mut self, s: &str) -> core::fmt::Result {
+ return self.serial_port.write_str(s);
+ }
+}
+
+static SERIAL_BASE: usize = 0x1000_0000;
+
+// Spinlock provides us a means to have a global console that is initialized
+// at runtime. Note below that locking outside of this module seems to have
+// odd behaviors, especially failure to ever acquire the lock.
+// The public specifier is here to enable the use of macros.
+pub static UART: Spinlock<Option<Uart>> = Spinlock::new(None);
+
+// Since we can't use something like lazy_static!, we're left with having
+// to explicitly call the init first. This should be done only once
+// at early initialization time
+pub fn init_uart() {
+ let mut uart = UART.lock();
+ *uart = Some(unsafe { Uart::new(SERIAL_BASE) })
+}
+
+// Read a byte from the underlying UART, clearing any asserted interrupt in the process.
+// It's not entirely clear why, but locking CONSOLE outside of this module, or at least doing
+// so while in interrupt context, seems to never return, but providing a public function
+// to do the work gets around this problem.
+pub fn uart_read() -> u8 {
+ return UART
+ .lock()
+ .as_mut()
+ .unwrap_or_else(|| panic!("Failed to acquire console for reading!"))
+ .serial_port
+ .receive();
+}
+
+// These, like a lot of the code in here, came courtesy of Meyer Zinn's
+// baremetal Rust tutorials: https://meyerzinn.tech/posts/2023/03/08/p1-printing-and-allocating/
+// I don't know how to use macros that well yet!
+
+#[macro_export]
+macro_rules! print {
+ ($($arg:tt)*) => ({
+ use core::fmt::Write;
+ $crate::uart::UART.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)*));
+}