use core::arch::global_asm;
use core::panic::PanicInfo;
+extern crate alloc;
+use alloc::boxed::Box;
+
+use trap::TrapFrame;
+use uart::Uart;
+use uart::SERIAL_BASE;
+
global_asm!(include_str!("trap.S"));
// cfg not test is set here because the analyzer and the test system
// Safety: initialize once (and that's done now)
unsafe { heap::init() };
- // Load the trap frame for interrupt handling
- // This must be done here rather than in the _start function because the _start function is naked and
- // we can't use keywords other than const and sym
unsafe {
+ // Load the trap frame for interrupt handling
+ // This must be done here rather than in the _start function because the _start function is naked and
+ // we can't use keywords other than const and sym
+ let trap_frame = Box::new(TrapFrame::new(Uart::new(SERIAL_BASE)));
asm!(
// Turn on floating point support
"li t0, 1 << 13",
"csrw mscratch, {trap_frame}",
"lla t2, m_trap_vector",
"csrw mtvec, t2",
- trap_frame = in(reg)(&raw mut trap::TRAP_FRAME as usize)
+ trap_frame = in(reg)(Box::into_raw(trap_frame) as usize)
);
// We're now good to take interrupts
use crate::uart::uart_read;
+use crate::uart::Uart;
use core::arch::asm;
use plic::Plic;
#[repr(C)]
-#[derive(Clone, Copy)]
+//#[derive(Clone, Copy)]
pub struct TrapFrame {
pub regs: [usize; 32], // 8 bytes per register; 32 gp regs (0 - 255)
pub fregs: [usize; 32], // 8 bytes per register; 32 fp regs (256 - 511)
// We're also skipping this; it's not needed when we've got only one hart and besides
// that we're in machine mode.
//pub hartid: usize, // 528
+ pub uart: Uart,
}
+
impl TrapFrame {
- pub const fn new() -> Self {
+ pub fn new(u: Uart) -> Self {
TrapFrame {
regs: [0; 32],
fregs: [0; 32],
+ uart: u
}
}
}
-pub static mut TRAP_FRAME: TrapFrame = TrapFrame::new();
-
// unsafe: call only once!
pub unsafe fn init_interrupts() {
asm!("csrsi mstatus, 0b1000", "li t2, 0xaaa", "csrw mie, t2");
cause: usize,
hart: usize,
_status: usize,
- _frame: &mut TrapFrame,
+ frame: &mut TrapFrame,
) -> usize {
// We're going to handle all traps in machine mode. RISC-V lets
// us delegate to supervisor mode, but switching out SATP (virtual memory)
let plic = get_plic();
match plic.claim(0) {
Some(int) => {
- let data = uart_read();
- print!("{}", data as char);
+ let data = frame.uart.read();
+ frame.uart.write(data);
plic.complete(0, int);
}
_ => {
pub fn write(&mut self, data: u8) {
self.serial_port.send(data);
}
+
+ pub fn read(&mut self) -> u8 {
+ return self.serial_port.receive();
+ }
}
// Write trait is used in the macros below; this basicallt
}
}
-static SERIAL_BASE: usize = 0x1000_0000;
+pub 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
*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!