diff options
Diffstat (limited to 'src/arch')
-rw-r--r-- | src/arch/riscv/include/arch/encoding.h | 6 | ||||
-rw-r--r-- | src/arch/riscv/trap_handler.c | 79 | ||||
-rw-r--r-- | src/arch/riscv/virtual_memory.c | 13 |
3 files changed, 94 insertions, 4 deletions
diff --git a/src/arch/riscv/include/arch/encoding.h b/src/arch/riscv/include/arch/encoding.h index 9f9d8ca5a8..d40446b84f 100644 --- a/src/arch/riscv/include/arch/encoding.h +++ b/src/arch/riscv/include/arch/encoding.h @@ -48,6 +48,11 @@ #define MSTATUS32_SD 0x80000000 #define MSTATUS64_SD 0x8000000000000000 +#define MIE_UTIE 0x00000010 +#define MIE_STIE 0x00000020 +#define MIE_HTIE 0x00000040 +#define MIE_MTIE 0x00000080 + #define SSTATUS_UIE 0x00000001 #define SSTATUS_SIE 0x00000002 #define SSTATUS_UPIE 0x00000010 @@ -126,6 +131,7 @@ #define SIP_SSIP MIP_SSIP #define SIP_STIP MIP_STIP +#define SIE_STIE MIE_STIE #define PRV_U 0 #define PRV_S 1 diff --git a/src/arch/riscv/trap_handler.c b/src/arch/riscv/trap_handler.c index c7a11c6b9c..56812090c5 100644 --- a/src/arch/riscv/trap_handler.c +++ b/src/arch/riscv/trap_handler.c @@ -20,6 +20,10 @@ #include <mcall.h> #include <string.h> #include <vm.h> +#include <commonlib/configstring.h> + +static uint64_t *time; +static uint64_t *timecmp; void handle_supervisor_call(trapframe *tf) { uintptr_t call = tf->gpr[17]; /* a7 */ @@ -70,7 +74,7 @@ void handle_supervisor_call(trapframe *tf) { returnValue = mcall_query_memory(arg0, (memory_block_info*) arg1); break; default: - printk(BIOS_DEBUG, "ERROR! Unrecognized system call\n"); + printk(BIOS_DEBUG, "ERROR! Unrecognized SBI call\n"); returnValue = 0; break; // note: system call we do not know how to handle } @@ -130,8 +134,76 @@ static void print_trap_information(const trapframe *tf) printk(BIOS_DEBUG, "Stored sp: %p\n", (void*) tf->gpr[2]); } -void trap_handler(trapframe *tf) { +static void gettimer(void) +{ + query_result res; + const char *config; + + config = configstring(); + query_rtc(config, (uintptr_t *)&time); + if (!time) + die("Got timer interrupt but found no timer."); + res = query_config_string(config, "core{0{0{timecmp"); + timecmp = (void *)get_uint(res); + if (!timecmp) + die("Got a timer interrupt but found no timecmp."); +} + +static void interrupt_handler(trapframe *tf) +{ + uint64_t cause = tf->cause & ~0x8000000000000000ULL; + uint32_t ssip, ssie; + + switch (cause) { + case IRQ_M_TIMER: + // The only way to reset the timer interrupt is to + // write mtimecmp. But we also have to ensure the + // comparison fails, for a long time, to let + // supervisor interrupt handler compute a new value + // and set it. Finally, it fires if mtimecmp is <= + // mtime, not =, so setting mtimecmp to 0 won't work + // to clear the interrupt and disable a new one. We + // have to set the mtimecmp far into the future. + // Akward! + // + // Further, maybe the platform doesn't have the + // hardware or the payload never uses it. We hold off + // querying some things until we are sure we need + // them. What to do if we can not find them? There are + // no good options. + + // This hart may have disabled timer interrupts. If + // so, just return. Kernels should only enable timer + // interrupts on one hart, and that should be hart 0 + // at present, as we only search for + // "core{0{0{timecmp" above. + ssie = read_csr(sie); + if (!(ssie & SIE_STIE)) + break; + + if (!timecmp) + gettimer(); + *timecmp = (uint64_t) -1; + ssip = read_csr(sip); + ssip |= SIP_STIP; + write_csr(sip, ssip); + break; + default: + printk(BIOS_EMERG, "======================================\n"); + printk(BIOS_EMERG, "Coreboot: Unknown machine interrupt: 0x%llx\n", + cause); + printk(BIOS_EMERG, "======================================\n"); + print_trap_information(tf); + break; + } +} +void trap_handler(trapframe *tf) +{ write_csr(mscratch, tf); + if (tf->cause & 0x8000000000000000ULL) { + interrupt_handler(tf); + return; + } switch(tf->cause) { case CAUSE_MISALIGNED_FETCH: @@ -159,6 +231,9 @@ void trap_handler(trapframe *tf) { handle_supervisor_call(tf); break; default: + printk(BIOS_EMERG, "================================\n"); + printk(BIOS_EMERG, "Coreboot: can not handle a trap:\n"); + printk(BIOS_EMERG, "================================\n"); print_trap_information(tf); break; } diff --git a/src/arch/riscv/virtual_memory.c b/src/arch/riscv/virtual_memory.c index 26a0169335..aceb72eebd 100644 --- a/src/arch/riscv/virtual_memory.c +++ b/src/arch/riscv/virtual_memory.c @@ -292,12 +292,21 @@ void initVirtualMemory(void) { void mstatus_init(void) { uintptr_t ms = 0; + ms = INSERT_FIELD(ms, MSTATUS_FS, 3); ms = INSERT_FIELD(ms, MSTATUS_XS, 3); write_csr(mstatus, ms); - clear_csr(mip, MIP_MSIP); - set_csr(mie, MIP_MSIP); + // clear any pending timer interrupts. + clear_csr(mip, MIP_STIP | MIP_SSIP); + + // enable machine and supervisor timer and + // all other supervisor interrupts. + set_csr(mie, MIP_MTIP | MIP_STIP | MIP_SSIP); + + // Delegate supervisor timer and other interrupts + // to supervisor mode. + set_csr(mideleg, MIP_STIP | MIP_SSIP); set_csr(medeleg, delegate); |