summaryrefslogtreecommitdiff
path: root/src/arch/riscv
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/riscv')
-rw-r--r--src/arch/riscv/include/arch/encoding.h6
-rw-r--r--src/arch/riscv/trap_handler.c79
-rw-r--r--src/arch/riscv/virtual_memory.c13
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);