console/main/trap: enable interrupts, handle UART interrupt
authorKit Rhett Aultman <kit@kitaultman.com>
Mon, 2 Sep 2024 23:56:39 +0000 (19:56 -0400)
committerKit Rhett Aultman <kit@kitaultman.com>
Mon, 2 Sep 2024 23:56:39 +0000 (19:56 -0400)
This commit enables interrupts and configures the RISC-V PLIC to deliver
interrupts for the UART (interrupt 10 for qemu).  To do this:

* mstatus and mie are configured for m-mode interrupts (mstatus also
  likely has s-mode turned on, but we won't use s-mode until we have
  to).
* The MmioSerialPort didn't have its init function called; doing so sets
  the interrupt enable bits on the UART.
* Configures the PLIC to deliver interrupt 10 on priority 1.
* Adds code to the exception handler to claim, handle, and complete the
  interrupt.

A few notes here:
* There's only one interrupt source right now...the UART.  So servicing
  the interrupt is done right in the handler.  There's no need to
  determine any other interrupt sources or worry about timing beyond
  this, for now.
* Because the MmioSerialPort was really mostly set up for the benefit of
  console output, it's not really set up to be used by the interrupt
  handler.  The code's a bit messy to ensure we read the pending byte
  and just echo it back to the terminal.  Future commits should address
  the awkwardness going on here.

Cargo.lock
Cargo.toml
src/console.rs
src/main.rs
src/trap.rs

index d7e35c554e59025ec9e253cbd99f4c042924959e..4ef88eb3c3ab14f67ef25dde227afa4622617d42 100644 (file)
@@ -39,6 +39,12 @@ dependencies = [
  "scopeguard",
 ]
 
+[[package]]
+name = "plic"
+version = "0.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ad606bf31d67b0e10a161b7df7d6a97dda7be22ce4bebcff889476e867c9b7a"
+
 [[package]]
 name = "raw-cpuid"
 version = "10.7.0"
@@ -53,6 +59,7 @@ name = "riscv"
 version = "0.1.0"
 dependencies = [
  "buddy_system_allocator",
+ "plic",
  "spinning_top",
  "uart_16550",
 ]
index ab3c17d3f9a9d2932c0fd345558afaf984d5468b..9495ccb906f2e61c9cc297827e0c14a8c4f33da6 100644 (file)
@@ -10,6 +10,7 @@ target = "riscv64gc-unknown-none-elf"
 buddy_system_allocator = "0.9.1"
 spinning_top = "0.3.0"
 uart_16550 = "0.3.0"
+plic = { version = "0.0.2", features = ["primitive-id"] }
 
 # We need to turn off panic unwinding
 # so we set the panic behavior to abort
index 511e1a571ce164352b696e60e44e5cd1e4475c90..03f0a9fba711d27e99d2d250503b8aef53d75926 100644 (file)
@@ -20,7 +20,8 @@ pub struct Console {
 
 impl Console {
     pub unsafe fn new(base: usize) -> Self {
-        let serial_port = MmioSerialPort::new(base);
+        let mut serial_port = MmioSerialPort::new(base);
+        serial_port.init();
         Console { serial_port }
     }
 
index d7e0076909c5ca6c22968120d7b6c9c661882e9b..3e816947c915cb678a1a0e9eb43004fb30812de9 100644 (file)
@@ -114,6 +114,9 @@ extern "C" fn entry() -> ! {
         "csrw mtvec, t2",
         trap_frame = in(reg)(&raw mut trap::TRAP_FRAME as usize)
           );
+
+        // We're now good to take interrupts
+        trap::init_interrupts();
     }
 
     println!("Kernel init complete");
index ac45c5f5076c3ecec87d3385a78e0ba83575ec26..f1a75d2791df9090a0090a655ebe1427d69379d2 100644 (file)
@@ -1,3 +1,6 @@
+use core::arch::asm;
+use plic::Plic;
+
 #[repr(C)]
 #[derive(Clone, Copy)]
 pub struct TrapFrame {
@@ -26,6 +29,21 @@ impl TrapFrame {
 
 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");
+    let plic = get_plic();
+    // Enable interrupts of any priority on the PLIC
+    plic.set_threshold(0, 0);
+    // PLIC interrupt 10 is the UART on qemu
+    plic.set_priority(10, 1);
+    plic.enable(10, 0);
+}
+
+unsafe fn get_plic() -> &'static Plic {
+    return &*(0xc000000 as *const Plic);
+}
+
 #[no_mangle]
 extern "C" fn m_trap(
     epc: usize,
@@ -66,7 +84,21 @@ extern "C" fn m_trap(
             },
             11 => {
                 // Machine external (interrupt from Platform Interrupt Controller (PLIC))
-                println!("Machine external interrupt CPU#{}", hart);
+                // Since we have only the UART to deal with, this one's pretty easy.
+                unsafe {
+                    let plic = get_plic();
+                    match plic.claim(0) {
+                        Some(int) => {
+                            let uart = 0x1000_0000 as *mut u8;
+                            let data = uart.read_volatile();
+                            print!("{}", data as char);
+                            plic.complete(0, int);
+                        }
+                        _ => {
+                            println!("No interrupt pending?!")
+                        }
+                    }
+                }
             }
             _ => {
                 panic!("Unhandled async trap CPU#{} -> {}\n", hart, cause_num);