Keyboard Work - setting up PLIC, UART, Interrupts
This commit is contained in:
parent
4c0a4958a2
commit
50757427be
12 changed files with 295 additions and 68 deletions
|
|
@ -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
|
||||
83
src/boot/traps.S
Normal file
83
src/boot/traps.S
Normal file
|
|
@ -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
|
||||
|
|
@ -1,14 +1,42 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <syscon/syscon.h>
|
||||
#include <drivers/uart.h>
|
||||
#include <lib/string.h>
|
||||
|
||||
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 *);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,24 @@
|
|||
#include <stdint.h>
|
||||
#include <drivers/uart.h>
|
||||
#include <syscon/syscon.h>
|
||||
#include "memory.h"
|
||||
#include <kernel/plic.h>
|
||||
#include <kernel/interrupts.h>
|
||||
#include <kernel/memory.h>
|
||||
|
||||
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,12 +48,22 @@ 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));
|
||||
|
||||
// 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;
|
||||
|
||||
if (is_interrupt)
|
||||
{
|
||||
unsigned long code = cause & 0xfff;
|
||||
handle_interrupt(code);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// fault address (if applicable)
|
||||
uintptr_t mtval;
|
||||
asm volatile("csrr %0, mtval" : "=r"(mtval));
|
||||
|
|
@ -44,35 +71,51 @@ void handle_trap()
|
|||
switch (cause)
|
||||
{
|
||||
case 0:
|
||||
kprint("Reason: Instruction Address Misaligned\n");
|
||||
kpanic("Reason: Instruction Address Misaligned\n");
|
||||
break;
|
||||
case 1:
|
||||
kprint("Reason: Instruction Access Fault\n");
|
||||
kpanic("Reason: Instruction Access Fault\n");
|
||||
break;
|
||||
case 2:
|
||||
kprint("Reason: Illegal Instruction\n");
|
||||
kpanic("Reason: Illegal Instruction\n");
|
||||
break;
|
||||
case 3:
|
||||
kprint("Reason: Breakpoint (ebreak)\n");
|
||||
kpanic("Reason: Breakpoint (ebreak)\n");
|
||||
break;
|
||||
case 4:
|
||||
kprint("Reason: Load Address Misaligned\n");
|
||||
kpanic("Reason: Load Address Misaligned\n");
|
||||
break;
|
||||
case 5:
|
||||
kprint("Reason: Load Access Fault\n");
|
||||
kpanic("Reason: Load Access Fault\n");
|
||||
break;
|
||||
case 6:
|
||||
kprint("Reason: Store/AMO Address Misaligned\n");
|
||||
kpanic("Reason: Store/AMO Address Misaligned\n");
|
||||
break;
|
||||
case 7:
|
||||
kprint("Reason: Store/AMO Access Fault\n");
|
||||
kpanic("Reason: Store/AMO Access Fault\n");
|
||||
break;
|
||||
default:
|
||||
kprintf("Reason: Unknown Exception Code %d\n", cause);
|
||||
break;
|
||||
// kpanic("Reason: Unknown Exception Code %d\n", cause);
|
||||
}
|
||||
kprintf("Faulting Address (if applicable): %x\n", mtval);
|
||||
|
||||
heap_stats();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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)) \
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include <stddef.h>
|
||||
#include <drivers/uart.h>
|
||||
#include <syscon/syscon.h>
|
||||
#include <kernel/plic.h>
|
||||
#include <kernel/interrupts.h>
|
||||
#include <kernel/memory.h>
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
19
src/kernel/plic.c
Normal file
19
src/kernel/plic.c
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#include <stdint.h>
|
||||
#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;
|
||||
}
|
||||
23
src/kernel/plic.h
Normal file
23
src/kernel/plic.h
Normal file
|
|
@ -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
|
||||
|
|
@ -23,7 +23,8 @@ void *memcpy(void* dest, const void* src, size_t size)
|
|||
}
|
||||
|
||||
int strcmp(const char *str1, const char *str2)
|
||||
{ while(*str1 == *str2)
|
||||
{
|
||||
while (*str1 == *str2)
|
||||
{
|
||||
if (*str1 == '\0')
|
||||
{
|
||||
|
|
@ -38,7 +39,8 @@ int strcmp(const char * str1, const char * str2)
|
|||
size_t strlen(const char *str)
|
||||
{
|
||||
size_t c = 0;
|
||||
while(*str++ != '\0'){
|
||||
while (*str++ != '\0')
|
||||
{
|
||||
c++;
|
||||
}
|
||||
return c;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue