# 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 = "bit_field"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+[[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 = "raw-cpuid"
version = "10.7.0"
name = "riscv"
version = "0.1.0"
dependencies = [
+ "spinning_top",
"uart_16550",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
+[[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",
+]
+
[[package]]
name = "uart_16550"
version = "0.3.0"
target = "riscv64gc-unknown-none-elf"
[dependencies]
+spinning_top = "0.3.0"
uart_16550 = "0.3.0"
# We need to turn off panic unwinding
--- /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 serial_port = MmioSerialPort::new(base);
+ 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 {
+ for byte in s.bytes() {
+ self.serial_port.send(byte);
+ }
+ Ok(())
+ }
+}
+
+static SERIAL_BASE: usize = 0x1000_0000;
+
+// I'm not actually sure Spinlock is necessary here, because I saw that the
+// uart_16550 code already uses atomic_ptr, which sure sounds like it has
+// synchronization features.
+
+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) })
+}
+
+// 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)*));
+}
#![no_main]
#![feature(naked_functions)] // enable functions that are pure inline assembly
+mod console;
+
use core::panic::PanicInfo;
// cfg not test is set here because the analyzer and the test system
#[cfg(not(test))]
#[panic_handler]
fn on_panic(_info: &PanicInfo) -> ! {
- loop {}
+ loop {}
}
/*
#[no_mangle] // C-style linkage needs an unmangled function
#[link_section = ".text.init"]
unsafe extern "C" fn _start() -> ! {
- use core::arch::asm;
- asm!(
- // The linker will try to emit the `la` pseudo-instruction when it believes
- // the referenced variable will be within a 12-bit offset of the _global_pointer,
- // so we must initialize the `gp` register to this at runtime.
- // (see the linker script file for more details)
- ".option push",
- ".option norelax",
- "la gp, _global_pointer",
- ".option pop",
-
- // set the stack pointer
- "la sp, _stack_top",
+ use core::arch::asm;
+ asm!(
+ // The linker will try to emit the `la` pseudo-instruction when it believes
+ // the referenced variable will be within a 12-bit offset of the _global_pointer,
+ // so we must initialize the `gp` register to this at runtime.
+ // (see the linker script file for more details)
+ ".option push",
+ ".option norelax",
+ "la gp, _global_pointer",
+ ".option pop",
+
+ // set the stack pointer
+ "la sp, _stack_top",
- // clear the BSS section
- "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:",
+ // clear the BSS section
+ "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:",
- // "tail-call" to {entry} (call without saving a return address)
- "tail {entry}",
- entry = sym entry, // {entry} refers to the function [entry] below
- options(noreturn) // we must handle "returning" from assembly
- );
+ // "tail-call" to {entry} (call without saving a return address)
+ "tail {entry}",
+ entry = sym entry, // {entry} refers to the function [entry] below
+ options(noreturn) // we must handle "returning" from assembly
+ );
}
#[no_mangle] // Again, being mindful of the C calling convention
extern "C" fn entry() -> ! {
- use uart_16550::MmioSerialPort;
- const SERIAL_BASE: usize = 0x1000_0000;
- let mut serial_port = unsafe { MmioSerialPort::new(SERIAL_BASE) };
- for byte in "KIT -- Hello World!\n".bytes() {
- serial_port.send(byte)
- }
+ use crate::console;
+ console::init_console();
+ println!("KIT-- hello works from println!");
loop {}
}