aboutsummaryrefslogtreecommitdiff
path: root/src/arch/riscv/trap_handler.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/riscv/trap_handler.c')
-rw-r--r--src/arch/riscv/trap_handler.c79
1 files changed, 77 insertions, 2 deletions
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;
}