diff --git a/src/boot/entry.S b/src/boot/entry.S index 9c4a550..094b148 100644 --- a/src/boot/entry.S +++ b/src/boot/entry.S @@ -1,22 +1,20 @@ -.section .text +.section .text.boot .global _start _start: - la sp, stack_top # Set up the stack pointer - la t0, trap_entry # Set up the trap handler - csrw mtvec, t0 # Set the trap vector to our trap handler - li t0, 0x00006000 # Load the mask for FS bits. enable float - csrs mstatus, t0 # Set the FS bits to 11 (Dirty/Initial). enable float - call kmain # Jump to our C code + # 1. Set up the stack pointer using the symbol from our linker script + la sp, stack_top + + # 2. Set up the trap handler (pointing to the one in traps.S) + la t0, trap_entry + csrw mtvec, t0 + + # 3. Enable FPU (Floating Point) + li t0, 0x00006000 + csrs mstatus, t0 + + # 4. Jump to C + call kmain + loop: - wfi # Wait for Interrupt (saves CPU) - j loop # Infinite loop if C returns -.align 4 -trap_entry: - csrr a0, mcause # Argument 1: mcause - csrr a1, mepc # Argument 2: mepc - j handle_trap -.section .bss -.align 16 -stack_low: - .skip 4096 # 4KB of stack space -stack_top: + wfi + j loop \ No newline at end of file diff --git a/src/boot/traps.S b/src/boot/traps.S new file mode 100644 index 0000000..921de6f --- /dev/null +++ b/src/boot/traps.S @@ -0,0 +1,83 @@ +# traps.S +.section .text +.align 4 # mtvec requires 4-byte alignment +.global trap_entry + +trap_entry: + # 1. Create space on the stack for 32 registers (32 * 8 = 256 bytes) + addi sp, sp, -256 + + # 2. Save all General Purpose Registers (GPRs) + # We don't save x0 (zero) because it's always zero + sd ra, 0(sp) + sd gp, 8(sp) + sd tp, 16(sp) + sd t0, 24(sp) + sd t1, 32(sp) + sd t2, 40(sp) + sd s0, 48(sp) + sd s1, 56(sp) + sd a0, 64(sp) + sd a1, 72(sp) + sd a2, 80(sp) + sd a3, 88(sp) + sd a4, 96(sp) + sd a5, 104(sp) + sd a6, 112(sp) + sd a7, 120(sp) + sd s2, 128(sp) + sd s3, 136(sp) + sd s4, 144(sp) + sd s5, 152(sp) + sd s6, 160(sp) + sd s7, 168(sp) + sd s8, 176(sp) + sd s9, 184(sp) + sd s10, 192(sp) + sd s11, 200(sp) + sd t3, 208(sp) + sd t4, 216(sp) + sd t5, 224(sp) + sd t6, 232(sp) + + # 3. Call your C handler + # The CPU already put the cause in 'mcause', so C can read it + call handle_trap + + # 4. Restore all GPRs + ld ra, 0(sp) + ld gp, 8(sp) + ld tp, 16(sp) + ld t0, 24(sp) + ld t1, 32(sp) + ld t2, 40(sp) + ld s0, 48(sp) + ld s1, 56(sp) + ld a0, 64(sp) + ld a1, 72(sp) + ld a2, 80(sp) + ld a3, 88(sp) + ld a4, 96(sp) + ld a5, 104(sp) + ld a6, 112(sp) + ld a7, 120(sp) + ld s2, 128(sp) + ld s3, 136(sp) + ld s4, 144(sp) + ld s5, 152(sp) + ld s6, 160(sp) + ld s7, 168(sp) + ld s8, 176(sp) + ld s9, 184(sp) + ld s10, 192(sp) + ld s11, 200(sp) + ld t3, 208(sp) + ld t4, 216(sp) + ld t5, 224(sp) + ld t6, 232(sp) + + # 5. Shrink the stack back + addi sp, sp, 256 + + # 6. Return from Machine-mode trap + mret \ No newline at end of file diff --git a/src/drivers/uart.c b/src/drivers/uart.c index e1eb4e5..d0059c8 100644 --- a/src/drivers/uart.c +++ b/src/drivers/uart.c @@ -1,14 +1,42 @@ #include #include #include +#include #include #include +void uart_init() +{ + kprint("UART Drive Init..."); + volatile uint8_t *UART_IER = (uint8_t *)(UART_ADDRESS + 1); + *UART_IER = 0x01; // Enable "Received Data Available" interrupt + kputs("OK"); +} + void uart_put(size_t base_addr, uint8_t data) { *(volatile uint8_t *)base_addr = data; } +char uart_getc() +{ + if (*UART_LSR & 0x01) + { + return (char)(*UART_RBR); + } + return '\0'; +} + +void uart_handle_interrupt() +{ + char c = uart_getc(); + if (c != '\0') + { + // Later, this will go into a "Circular Buffer" + kputchar(c); + } +} + int kputchar(int ch) { uart_put(UART_ADDRESS, ch); @@ -107,6 +135,11 @@ void kprintf(const char *format, ...) p++; switch (*p) { + case 'c': + { + char c = (char)va_arg(args, int); + kputchar(c); + } case 's': { char *s = va_arg(args, char *); @@ -127,7 +160,7 @@ void kprintf(const char *format, ...) } case 'x': // Hex case 'p': // Pointer - { + { uint64_t x = va_arg(args, uint64_t); kprint_hex(x); break; diff --git a/src/drivers/uart.h b/src/drivers/uart.h index 08e7af2..62a7cd6 100644 --- a/src/drivers/uart.h +++ b/src/drivers/uart.h @@ -1,8 +1,19 @@ #ifndef UART_H #define UART_H +#define UART_RBR (volatile uint8_t *)(UART_ADDRESS + 0) +#define UART_LSR (volatile uint8_t *)(UART_ADDRESS + 5) #define UART_ADDRESS 0x10000000 +#define UART_INTERRUPT_ENABLE_REGISTER 0x1001100C +#define UART_INTEN_OFFSET 0x0C +#define UART_TX_OFFSET 0x04 +#define UART_INTERRUPT_ENABLE_REGISTER (UART_BASE_ADDR + UART_INTEN_OFFSET) + +void uart_init(); void uart_put(size_t base_addr, uint8_t data); +char uart_getc(); +void uart_handle_interrupt(); + int kputchar(int ch); void kprint_hex(uint64_t val); void kprint_int(int num); diff --git a/src/kernel/interrupts.c b/src/kernel/interrupts.c index 063d13f..c0aefb3 100644 --- a/src/kernel/interrupts.c +++ b/src/kernel/interrupts.c @@ -2,7 +2,24 @@ #include #include #include -#include "memory.h" +#include +#include +#include + +void interrupt_init() +{ + kprint("Initialising Interrupts..."); + uint64_t mstatus_val; + asm volatile("csrr %0, mstatus" : "=r"(mstatus_val)); + mstatus_val |= (1 << MSTATUS_BIT_MIE); + asm volatile("csrw mstatus, %0" ::"r"(mstatus_val)); + + uint64_t mie_val; + asm volatile("csrr %0, mie" : "=r"(mie_val)); + mie_val |= (1 << MIE_BIT_MEIE); //|(1 << MIE_BIT_MTIE); + asm volatile("csrw mie, %0" ::"r"(mie_val)); + kputs("OK"); +} void kpanic(const char *reason) { @@ -31,48 +48,74 @@ void kpanic_force() void handle_trap() { - kprint("\n!!! HARDWARE EXCEPTION DETECTED !!!\n"); - // Read the 'mcause' register to see WHY we trapped unsigned long cause; __asm__ volatile("csrr %0, mcause" : "=r"(cause)); - // fault address (if applicable) - uintptr_t mtval; - asm volatile("csrr %0, mtval" : "=r"(mtval)); + // Check if the top bit is 1 (Interrupt) or 0 (Exception) + // For 64-bit RISC-V, the bit is 63 + int is_interrupt = (cause >> 63) & 1; - switch (cause) + if (is_interrupt) { - case 0: - kprint("Reason: Instruction Address Misaligned\n"); - break; - case 1: - kprint("Reason: Instruction Access Fault\n"); - break; - case 2: - kprint("Reason: Illegal Instruction\n"); - break; - case 3: - kprint("Reason: Breakpoint (ebreak)\n"); - break; - case 4: - kprint("Reason: Load Address Misaligned\n"); - break; - case 5: - kprint("Reason: Load Access Fault\n"); - break; - case 6: - kprint("Reason: Store/AMO Address Misaligned\n"); - break; - case 7: - kprint("Reason: Store/AMO Access Fault\n"); - break; - default: - kprintf("Reason: Unknown Exception Code %d\n", cause); + unsigned long code = cause & 0xfff; + handle_interrupt(code); + return; } - kprintf("Faulting Address (if applicable): %x\n", mtval); + else + { + // fault address (if applicable) + uintptr_t mtval; + asm volatile("csrr %0, mtval" : "=r"(mtval)); - heap_stats(); + switch (cause) + { + case 0: + kpanic("Reason: Instruction Address Misaligned\n"); + break; + case 1: + kpanic("Reason: Instruction Access Fault\n"); + break; + case 2: + kpanic("Reason: Illegal Instruction\n"); + break; + case 3: + kpanic("Reason: Breakpoint (ebreak)\n"); + break; + case 4: + kpanic("Reason: Load Address Misaligned\n"); + break; + case 5: + kpanic("Reason: Load Access Fault\n"); + break; + case 6: + kpanic("Reason: Store/AMO Address Misaligned\n"); + break; + case 7: + kpanic("Reason: Store/AMO Access Fault\n"); + break; + default: + break; + // kpanic("Reason: Unknown Exception Code %d\n", cause); + } + kprintf("Faulting Address (if applicable): %x\n", mtval); + } +} - poweroff(); +void handle_interrupt(unsigned long code) +{ + switch (code) + { + case 7: + break; // timer Interrupt. Ignoring for now. + case 11: + volatile uint32_t *claim_reg = (uint32_t *)PLIC_CLAIM_COMPLETE; + uint32_t irq = *claim_reg; + if (irq == 10) + { + uart_handle_interrupt(); + } + *claim_reg = irq; + break; + } } \ No newline at end of file diff --git a/src/kernel/interrupts.h b/src/kernel/interrupts.h index 007a16e..9ad91ff 100644 --- a/src/kernel/interrupts.h +++ b/src/kernel/interrupts.h @@ -1,8 +1,10 @@ #ifndef PANIC_H #define PANIC_H -void kpanic(const char *reason); +void interrupt_init(); +void kpanic(const char *); void kpanic_force(); +void handle_interrupt(unsigned long code); #define KASSERT(cond, msg) \ if (!(cond)) \ diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 2375ced..32ca547 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -11,9 +12,8 @@ void kmain() kprintf("Hello, from %s!", "SquidgeOS"); kputs("----------------------"); knewline(); + plic_init(); + uart_init(); page_init(); - test_memory_integrity(); - test_memory_alignment(); - test_memory_stress(); - poweroff(); + interrupt_init(); } diff --git a/src/kernel/memory.c b/src/kernel/memory.c index 96ca8bc..b2dd46a 100644 --- a/src/kernel/memory.c +++ b/src/kernel/memory.c @@ -11,7 +11,7 @@ extern uint8_t _heap_start[]; // named in the linker script void page_init() { - kprint("Initialising page allocator.\n"); + kprint("Initialising page allocator..."); uintptr_t start = ((uintptr_t)_heap_start + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); uintptr_t end = 0x88000000; // Default QEMU RAM limit @@ -19,6 +19,7 @@ void page_init() { page_free((void *)addr); } + kputs("OK"); } void page_free(void *addr) diff --git a/src/kernel/plic.c b/src/kernel/plic.c new file mode 100644 index 0000000..5a14838 --- /dev/null +++ b/src/kernel/plic.c @@ -0,0 +1,19 @@ +#include +#include "kernel/plic.h" + +void plic_init() +{ + int hart = 0; + + // 1. Set the priority of the UART interrupt + // We set it to 1. If it's 0 (the default), the interrupt is effectively disabled. + *PLIC_PRIORITY(UART_IRQ) = 1; + + // 2. Enable the UART interrupt for Hart 0 + // This is a bitmask, so we shift 1 by the IRQ number. + *PLIC_ENABLE(hart) = (1 << UART_IRQ); + + // 3. Set the priority threshold for Hart 0 + // We set this to 0 so that ANY interrupt with priority > 0 gets through. + *PLIC_THRESHOLD(hart) = 0; +} \ No newline at end of file diff --git a/src/kernel/plic.h b/src/kernel/plic.h new file mode 100644 index 0000000..9f494c7 --- /dev/null +++ b/src/kernel/plic.h @@ -0,0 +1,23 @@ +#ifndef PLIC_H +#define PLIC_H + +#define PLIC_BASE 0x0c000000 +#define PLIC_CLAIM_COMPLETE 0x0c200004 + +// Priorities: 4 bytes per IRQ (IRQ 0 is reserved/null) +#define PLIC_PRIORITY(irq) ((volatile uint32_t *)(PLIC_BASE + (irq) * 4)) + +// Enables: Each Hart has a 0x80 byte stride for its enable bits +// For Hart 0 Machine Mode: 0x0c002000 +#define PLIC_ENABLE(hart) ((volatile uint32_t *)(PLIC_BASE + 0x2000 + (hart) * 0x80)) + +// Threshold and Claim/Complete: Each Hart has a 0x1000 byte stride +// For Hart 0 Machine Mode: 0x0c200000 and 0x0c200004 +#define PLIC_THRESHOLD(hart) ((volatile uint32_t *)(PLIC_BASE + 0x200000 + (hart) * 0x1000)) +#define PLIC_CLAIM(hart) ((volatile uint32_t *)(PLIC_BASE + 0x200004 + (hart) * 0x1000)) + +#define UART_IRQ 10 + +void plic_init(); + +#endif \ No newline at end of file diff --git a/src/lib/string.c b/src/lib/string.c index 94014ff..491901d 100644 --- a/src/lib/string.c +++ b/src/lib/string.c @@ -11,19 +11,20 @@ void *memset(void *dest, int val, size_t size) return dest; } -void *memcpy(void* dest, const void* src, size_t size) +void *memcpy(void *dest, const void *src, size_t size) { unsigned char *d = dest; const unsigned char *s = src; - while(--size) + while (--size) { *d++ = *s++; } return dest; } -int strcmp(const char * str1, const char * str2) -{ while(*str1 == *str2) +int strcmp(const char *str1, const char *str2) +{ + while (*str1 == *str2) { if (*str1 == '\0') { @@ -35,10 +36,11 @@ int strcmp(const char * str1, const char * str2) return *str1 - *str2; } -size_t strlen(const char* str) +size_t strlen(const char *str) { size_t c = 0; - while(*str++ != '\0'){ + while (*str++ != '\0') + { c++; } return c; diff --git a/src/syscon/syscon.h b/src/syscon/syscon.h index 32dd6a1..dc4abd4 100644 --- a/src/syscon/syscon.h +++ b/src/syscon/syscon.h @@ -8,6 +8,18 @@ #define SYSCON_POWEROFF 0x5555 #define SYSCON_REBOOT 0x7777 +#define write_register(register, value) \ + asm volatile("csrrw zero, %0, %1" ::"i"(register), "r"(value)) + +#define read_register(register, destination) \ + asm volatile("csrrs %0, %1, zero" : "=r"(destination) : "i"(register)) + +#define MSTATUS 0x300 +#define MIE 0x304 +#define MSTATUS_BIT_MIE 3 +#define MIE_BIT_MTIE 7 +#define MIE_BIT_MEIE 11 + void poweroff(void); void reboot(void);