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
|
.global _start
|
||||||
_start:
|
_start:
|
||||||
la sp, stack_top # Set up the stack pointer
|
# 1. Set up the stack pointer using the symbol from our linker script
|
||||||
la t0, trap_entry # Set up the trap handler
|
la sp, stack_top
|
||||||
csrw mtvec, t0 # Set the trap vector to our trap handler
|
|
||||||
li t0, 0x00006000 # Load the mask for FS bits. enable float
|
# 2. Set up the trap handler (pointing to the one in traps.S)
|
||||||
csrs mstatus, t0 # Set the FS bits to 11 (Dirty/Initial). enable float
|
la t0, trap_entry
|
||||||
call kmain # Jump to our C code
|
csrw mtvec, t0
|
||||||
|
|
||||||
|
# 3. Enable FPU (Floating Point)
|
||||||
|
li t0, 0x00006000
|
||||||
|
csrs mstatus, t0
|
||||||
|
|
||||||
|
# 4. Jump to C
|
||||||
|
call kmain
|
||||||
|
|
||||||
loop:
|
loop:
|
||||||
wfi # Wait for Interrupt (saves CPU)
|
wfi
|
||||||
j loop # Infinite loop if C returns
|
j loop
|
||||||
.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:
|
|
||||||
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 <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <syscon/syscon.h>
|
||||||
#include <drivers/uart.h>
|
#include <drivers/uart.h>
|
||||||
#include <lib/string.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)
|
void uart_put(size_t base_addr, uint8_t data)
|
||||||
{
|
{
|
||||||
*(volatile uint8_t *)base_addr = 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)
|
int kputchar(int ch)
|
||||||
{
|
{
|
||||||
uart_put(UART_ADDRESS, ch);
|
uart_put(UART_ADDRESS, ch);
|
||||||
|
|
@ -107,6 +135,11 @@ void kprintf(const char *format, ...)
|
||||||
p++;
|
p++;
|
||||||
switch (*p)
|
switch (*p)
|
||||||
{
|
{
|
||||||
|
case 'c':
|
||||||
|
{
|
||||||
|
char c = (char)va_arg(args, int);
|
||||||
|
kputchar(c);
|
||||||
|
}
|
||||||
case 's':
|
case 's':
|
||||||
{
|
{
|
||||||
char *s = va_arg(args, char *);
|
char *s = va_arg(args, char *);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,19 @@
|
||||||
#ifndef UART_H
|
#ifndef UART_H
|
||||||
#define 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_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);
|
void uart_put(size_t base_addr, uint8_t data);
|
||||||
|
char uart_getc();
|
||||||
|
void uart_handle_interrupt();
|
||||||
|
|
||||||
int kputchar(int ch);
|
int kputchar(int ch);
|
||||||
void kprint_hex(uint64_t val);
|
void kprint_hex(uint64_t val);
|
||||||
void kprint_int(int num);
|
void kprint_int(int num);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,24 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <drivers/uart.h>
|
#include <drivers/uart.h>
|
||||||
#include <syscon/syscon.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)
|
void kpanic(const char *reason)
|
||||||
{
|
{
|
||||||
|
|
@ -31,12 +48,22 @@ void kpanic_force()
|
||||||
|
|
||||||
void handle_trap()
|
void handle_trap()
|
||||||
{
|
{
|
||||||
kprint("\n!!! HARDWARE EXCEPTION DETECTED !!!\n");
|
|
||||||
|
|
||||||
// Read the 'mcause' register to see WHY we trapped
|
// Read the 'mcause' register to see WHY we trapped
|
||||||
unsigned long cause;
|
unsigned long cause;
|
||||||
__asm__ volatile("csrr %0, mcause" : "=r"(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)
|
// fault address (if applicable)
|
||||||
uintptr_t mtval;
|
uintptr_t mtval;
|
||||||
asm volatile("csrr %0, mtval" : "=r"(mtval));
|
asm volatile("csrr %0, mtval" : "=r"(mtval));
|
||||||
|
|
@ -44,35 +71,51 @@ void handle_trap()
|
||||||
switch (cause)
|
switch (cause)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
kprint("Reason: Instruction Address Misaligned\n");
|
kpanic("Reason: Instruction Address Misaligned\n");
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
kprint("Reason: Instruction Access Fault\n");
|
kpanic("Reason: Instruction Access Fault\n");
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
kprint("Reason: Illegal Instruction\n");
|
kpanic("Reason: Illegal Instruction\n");
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
kprint("Reason: Breakpoint (ebreak)\n");
|
kpanic("Reason: Breakpoint (ebreak)\n");
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
kprint("Reason: Load Address Misaligned\n");
|
kpanic("Reason: Load Address Misaligned\n");
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
kprint("Reason: Load Access Fault\n");
|
kpanic("Reason: Load Access Fault\n");
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
kprint("Reason: Store/AMO Address Misaligned\n");
|
kpanic("Reason: Store/AMO Address Misaligned\n");
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
kprint("Reason: Store/AMO Access Fault\n");
|
kpanic("Reason: Store/AMO Access Fault\n");
|
||||||
break;
|
break;
|
||||||
default:
|
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);
|
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
|
#ifndef PANIC_H
|
||||||
#define PANIC_H
|
#define PANIC_H
|
||||||
|
|
||||||
void kpanic(const char *reason);
|
void interrupt_init();
|
||||||
|
void kpanic(const char *);
|
||||||
void kpanic_force();
|
void kpanic_force();
|
||||||
|
void handle_interrupt(unsigned long code);
|
||||||
|
|
||||||
#define KASSERT(cond, msg) \
|
#define KASSERT(cond, msg) \
|
||||||
if (!(cond)) \
|
if (!(cond)) \
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <drivers/uart.h>
|
#include <drivers/uart.h>
|
||||||
#include <syscon/syscon.h>
|
#include <syscon/syscon.h>
|
||||||
|
#include <kernel/plic.h>
|
||||||
#include <kernel/interrupts.h>
|
#include <kernel/interrupts.h>
|
||||||
#include <kernel/memory.h>
|
#include <kernel/memory.h>
|
||||||
|
|
||||||
|
|
@ -11,9 +12,8 @@ void kmain()
|
||||||
kprintf("Hello, from %s!", "SquidgeOS");
|
kprintf("Hello, from %s!", "SquidgeOS");
|
||||||
kputs("----------------------");
|
kputs("----------------------");
|
||||||
knewline();
|
knewline();
|
||||||
|
plic_init();
|
||||||
|
uart_init();
|
||||||
page_init();
|
page_init();
|
||||||
test_memory_integrity();
|
interrupt_init();
|
||||||
test_memory_alignment();
|
|
||||||
test_memory_stress();
|
|
||||||
poweroff();
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ extern uint8_t _heap_start[]; // named in the linker script
|
||||||
|
|
||||||
void page_init()
|
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 start = ((uintptr_t)_heap_start + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
|
||||||
uintptr_t end = 0x88000000; // Default QEMU RAM limit
|
uintptr_t end = 0x88000000; // Default QEMU RAM limit
|
||||||
|
|
||||||
|
|
@ -19,6 +19,7 @@ void page_init()
|
||||||
{
|
{
|
||||||
page_free((void *)addr);
|
page_free((void *)addr);
|
||||||
}
|
}
|
||||||
|
kputs("OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
void page_free(void *addr)
|
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)
|
int strcmp(const char *str1, const char *str2)
|
||||||
{ while(*str1 == *str2)
|
{
|
||||||
|
while (*str1 == *str2)
|
||||||
{
|
{
|
||||||
if (*str1 == '\0')
|
if (*str1 == '\0')
|
||||||
{
|
{
|
||||||
|
|
@ -38,7 +39,8 @@ int strcmp(const char * str1, const char * str2)
|
||||||
size_t strlen(const char *str)
|
size_t strlen(const char *str)
|
||||||
{
|
{
|
||||||
size_t c = 0;
|
size_t c = 0;
|
||||||
while(*str++ != '\0'){
|
while (*str++ != '\0')
|
||||||
|
{
|
||||||
c++;
|
c++;
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,18 @@
|
||||||
#define SYSCON_POWEROFF 0x5555
|
#define SYSCON_POWEROFF 0x5555
|
||||||
#define SYSCON_REBOOT 0x7777
|
#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 poweroff(void);
|
||||||
void reboot(void);
|
void reboot(void);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue