riscv: Add support for timer interrupts
RISCV requires that timer interrupts be handled in machine mode and delegated as necessary. Also you can only reset the timer interrupt by writing to mtimecmp. Further, you must write a number > mtime, not just != mtime. This rather clumsy situation requires that we write some value into the future into mtimecmp lest we never be able to leave machine mode as the interrupt either is not cleared or instantly reoccurs. This current code is tested and works for harvey (Plan 9) timer interrupts. Change-Id: I8538d5fd8d80d9347773c638f5cbf0da18dc1cae Signed-off-by: Ronald G. Minnich <rminnich@gmail.com> Reviewed-on: https://review.coreboot.org/17807 Tested-by: build bot (Jenkins) Reviewed-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net>
This commit is contained in:
parent
a01695bf9a
commit
d9307c2e8a
|
@ -48,6 +48,11 @@
|
||||||
#define MSTATUS32_SD 0x80000000
|
#define MSTATUS32_SD 0x80000000
|
||||||
#define MSTATUS64_SD 0x8000000000000000
|
#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_UIE 0x00000001
|
||||||
#define SSTATUS_SIE 0x00000002
|
#define SSTATUS_SIE 0x00000002
|
||||||
#define SSTATUS_UPIE 0x00000010
|
#define SSTATUS_UPIE 0x00000010
|
||||||
|
@ -126,6 +131,7 @@
|
||||||
|
|
||||||
#define SIP_SSIP MIP_SSIP
|
#define SIP_SSIP MIP_SSIP
|
||||||
#define SIP_STIP MIP_STIP
|
#define SIP_STIP MIP_STIP
|
||||||
|
#define SIE_STIE MIE_STIE
|
||||||
|
|
||||||
#define PRV_U 0
|
#define PRV_U 0
|
||||||
#define PRV_S 1
|
#define PRV_S 1
|
||||||
|
|
|
@ -20,6 +20,10 @@
|
||||||
#include <mcall.h>
|
#include <mcall.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <vm.h>
|
#include <vm.h>
|
||||||
|
#include <commonlib/configstring.h>
|
||||||
|
|
||||||
|
static uint64_t *time;
|
||||||
|
static uint64_t *timecmp;
|
||||||
|
|
||||||
void handle_supervisor_call(trapframe *tf) {
|
void handle_supervisor_call(trapframe *tf) {
|
||||||
uintptr_t call = tf->gpr[17]; /* a7 */
|
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);
|
returnValue = mcall_query_memory(arg0, (memory_block_info*) arg1);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printk(BIOS_DEBUG, "ERROR! Unrecognized system call\n");
|
printk(BIOS_DEBUG, "ERROR! Unrecognized SBI call\n");
|
||||||
returnValue = 0;
|
returnValue = 0;
|
||||||
break; // note: system call we do not know how to handle
|
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]);
|
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);
|
write_csr(mscratch, tf);
|
||||||
|
if (tf->cause & 0x8000000000000000ULL) {
|
||||||
|
interrupt_handler(tf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch(tf->cause) {
|
switch(tf->cause) {
|
||||||
case CAUSE_MISALIGNED_FETCH:
|
case CAUSE_MISALIGNED_FETCH:
|
||||||
|
@ -159,6 +231,9 @@ void trap_handler(trapframe *tf) {
|
||||||
handle_supervisor_call(tf);
|
handle_supervisor_call(tf);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
printk(BIOS_EMERG, "================================\n");
|
||||||
|
printk(BIOS_EMERG, "Coreboot: can not handle a trap:\n");
|
||||||
|
printk(BIOS_EMERG, "================================\n");
|
||||||
print_trap_information(tf);
|
print_trap_information(tf);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,12 +292,21 @@ void initVirtualMemory(void) {
|
||||||
void mstatus_init(void)
|
void mstatus_init(void)
|
||||||
{
|
{
|
||||||
uintptr_t ms = 0;
|
uintptr_t ms = 0;
|
||||||
|
|
||||||
ms = INSERT_FIELD(ms, MSTATUS_FS, 3);
|
ms = INSERT_FIELD(ms, MSTATUS_FS, 3);
|
||||||
ms = INSERT_FIELD(ms, MSTATUS_XS, 3);
|
ms = INSERT_FIELD(ms, MSTATUS_XS, 3);
|
||||||
write_csr(mstatus, ms);
|
write_csr(mstatus, ms);
|
||||||
|
|
||||||
clear_csr(mip, MIP_MSIP);
|
// clear any pending timer interrupts.
|
||||||
set_csr(mie, MIP_MSIP);
|
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);
|
set_csr(medeleg, delegate);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue