forked from Mirrors/opensbi

We remove mcause, scratch and hartid parameters from various functions for illegal instruction handling because we can always get current HART id and current scratch pointer using just one CSR access. Signed-off-by: Anup Patel <anup.patel@wdc.com> Reviewed-by: Atish Patra <atish.patra@wdc.com>
283 lines
8.6 KiB
C
283 lines
8.6 KiB
C
/*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*
|
|
* Copyright (c) 2019 Western Digital Corporation or its affiliates.
|
|
*
|
|
* Authors:
|
|
* Anup Patel <anup.patel@wdc.com>
|
|
*/
|
|
|
|
#include <sbi/riscv_asm.h>
|
|
#include <sbi/riscv_encoding.h>
|
|
#include <sbi/sbi_console.h>
|
|
#include <sbi/sbi_ecall.h>
|
|
#include <sbi/sbi_error.h>
|
|
#include <sbi/sbi_hart.h>
|
|
#include <sbi/sbi_illegal_insn.h>
|
|
#include <sbi/sbi_ipi.h>
|
|
#include <sbi/sbi_misaligned_ldst.h>
|
|
#include <sbi/sbi_timer.h>
|
|
#include <sbi/sbi_trap.h>
|
|
|
|
static void __noreturn sbi_trap_error(const char *msg, int rc, u32 hartid,
|
|
ulong mcause, ulong mtval, ulong mtval2,
|
|
ulong mtinst, struct sbi_trap_regs *regs)
|
|
{
|
|
sbi_printf("%s: hart%d: %s (error %d)\n", __func__, hartid, msg, rc);
|
|
sbi_printf("%s: hart%d: mcause=0x%" PRILX " mtval=0x%" PRILX "\n",
|
|
__func__, hartid, mcause, mtval);
|
|
if (misa_extension('H')) {
|
|
sbi_printf("%s: hart%d: mtval2=0x%" PRILX
|
|
" mtinst=0x%" PRILX "\n",
|
|
__func__, hartid, mtval2, mtinst);
|
|
}
|
|
sbi_printf("%s: hart%d: mepc=0x%" PRILX " mstatus=0x%" PRILX "\n",
|
|
__func__, hartid, regs->mepc, regs->mstatus);
|
|
sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
|
|
hartid, "ra", regs->ra, "sp", regs->sp);
|
|
sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
|
|
hartid, "gp", regs->gp, "tp", regs->tp);
|
|
sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
|
|
hartid, "s0", regs->s0, "s1", regs->s1);
|
|
sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
|
|
hartid, "a0", regs->a0, "a1", regs->a1);
|
|
sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
|
|
hartid, "a2", regs->a2, "a3", regs->a3);
|
|
sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
|
|
hartid, "a4", regs->a4, "a5", regs->a5);
|
|
sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
|
|
hartid, "a6", regs->a6, "a7", regs->a7);
|
|
sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
|
|
hartid, "s2", regs->s2, "s3", regs->s3);
|
|
sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
|
|
hartid, "s4", regs->s4, "s5", regs->s5);
|
|
sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
|
|
hartid, "s6", regs->s6, "s7", regs->s7);
|
|
sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
|
|
hartid, "s8", regs->s8, "s9", regs->s9);
|
|
sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
|
|
hartid, "s10", regs->s10, "s11", regs->s11);
|
|
sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
|
|
hartid, "t0", regs->t0, "t1", regs->t1);
|
|
sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
|
|
hartid, "t2", regs->t2, "t3", regs->t3);
|
|
sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
|
|
hartid, "t4", regs->t4, "t5", regs->t5);
|
|
sbi_printf("%s: hart%d: %s=0x%" PRILX "\n", __func__, hartid, "t6",
|
|
regs->t6);
|
|
|
|
sbi_hart_hang();
|
|
}
|
|
|
|
/**
|
|
* Redirect trap to lower privledge mode (S-mode or U-mode)
|
|
*
|
|
* @param regs pointer to register state
|
|
* @param trap pointer to trap details
|
|
*
|
|
* @return 0 on success and negative error code on failure
|
|
*/
|
|
int sbi_trap_redirect(struct sbi_trap_regs *regs,
|
|
struct sbi_trap_info *trap)
|
|
{
|
|
ulong hstatus, vsstatus, prev_mode;
|
|
#if __riscv_xlen == 32
|
|
bool prev_virt = (regs->mstatusH & MSTATUSH_MPV) ? TRUE : FALSE;
|
|
#else
|
|
bool prev_virt = (regs->mstatus & MSTATUS_MPV) ? TRUE : FALSE;
|
|
#endif
|
|
/* By default, we redirect to HS-mode */
|
|
bool next_virt = FALSE;
|
|
|
|
/* Sanity check on previous mode */
|
|
prev_mode = (regs->mstatus & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT;
|
|
if (prev_mode != PRV_S && prev_mode != PRV_U)
|
|
return SBI_ENOTSUPP;
|
|
|
|
/* For certain exceptions from VS/VU-mode we redirect to VS-mode */
|
|
if (misa_extension('H') && prev_virt) {
|
|
switch (trap->cause) {
|
|
case CAUSE_FETCH_PAGE_FAULT:
|
|
case CAUSE_LOAD_PAGE_FAULT:
|
|
case CAUSE_STORE_PAGE_FAULT:
|
|
next_virt = TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
}
|
|
|
|
/* Update MSTATUS MPV bits */
|
|
#if __riscv_xlen == 32
|
|
regs->mstatusH &= ~MSTATUSH_MPV;
|
|
regs->mstatusH |= (next_virt) ? MSTATUSH_MPV : 0UL;
|
|
#else
|
|
regs->mstatus &= ~MSTATUS_MPV;
|
|
regs->mstatus |= (next_virt) ? MSTATUS_MPV : 0UL;
|
|
#endif
|
|
|
|
/* Update HSTATUS for VS/VU-mode to HS-mode transition */
|
|
if (misa_extension('H') && prev_virt && !next_virt) {
|
|
/* Update HSTATUS SP2P, SP2V, and SPV bits */
|
|
hstatus = csr_read(CSR_HSTATUS);
|
|
hstatus &= ~HSTATUS_SP2P;
|
|
hstatus |= (regs->mstatus & MSTATUS_SPP) ? HSTATUS_SP2P : 0;
|
|
hstatus &= ~HSTATUS_SP2V;
|
|
hstatus |= (hstatus & HSTATUS_SPV) ? HSTATUS_SP2V : 0;
|
|
hstatus &= ~HSTATUS_SPV;
|
|
hstatus |= (prev_virt) ? HSTATUS_SPV : 0;
|
|
csr_write(CSR_HSTATUS, hstatus);
|
|
csr_write(CSR_HTVAL, trap->tval2);
|
|
csr_write(CSR_HTINST, trap->tinst);
|
|
}
|
|
|
|
/* Update exception related CSRs */
|
|
if (next_virt) {
|
|
/* Update VS-mode exception info */
|
|
csr_write(CSR_VSTVAL, trap->tval);
|
|
csr_write(CSR_VSEPC, trap->epc);
|
|
csr_write(CSR_VSCAUSE, trap->cause);
|
|
|
|
/* Set MEPC to VS-mode exception vector base */
|
|
regs->mepc = csr_read(CSR_VSTVEC);
|
|
|
|
/* Set MPP to VS-mode */
|
|
regs->mstatus &= ~MSTATUS_MPP;
|
|
regs->mstatus |= (PRV_S << MSTATUS_MPP_SHIFT);
|
|
|
|
/* Get VS-mode SSTATUS CSR */
|
|
vsstatus = csr_read(CSR_VSSTATUS);
|
|
|
|
/* Set SPP for VS-mode */
|
|
vsstatus &= ~SSTATUS_SPP;
|
|
if (prev_mode == PRV_S)
|
|
vsstatus |= (1UL << SSTATUS_SPP_SHIFT);
|
|
|
|
/* Set SPIE for VS-mode */
|
|
vsstatus &= ~SSTATUS_SPIE;
|
|
if (vsstatus & SSTATUS_SIE)
|
|
vsstatus |= (1UL << SSTATUS_SPIE_SHIFT);
|
|
|
|
/* Clear SIE for VS-mode */
|
|
vsstatus &= ~SSTATUS_SIE;
|
|
|
|
/* Update VS-mode SSTATUS CSR */
|
|
csr_write(CSR_VSSTATUS, vsstatus);
|
|
} else {
|
|
/* Update S-mode exception info */
|
|
csr_write(CSR_STVAL, trap->tval);
|
|
csr_write(CSR_SEPC, trap->epc);
|
|
csr_write(CSR_SCAUSE, trap->cause);
|
|
|
|
/* Set MEPC to S-mode exception vector base */
|
|
regs->mepc = csr_read(CSR_STVEC);
|
|
|
|
/* Set MPP to S-mode */
|
|
regs->mstatus &= ~MSTATUS_MPP;
|
|
regs->mstatus |= (PRV_S << MSTATUS_MPP_SHIFT);
|
|
|
|
/* Set SPP for S-mode */
|
|
regs->mstatus &= ~MSTATUS_SPP;
|
|
if (prev_mode == PRV_S)
|
|
regs->mstatus |= (1UL << MSTATUS_SPP_SHIFT);
|
|
|
|
/* Set SPIE for S-mode */
|
|
regs->mstatus &= ~MSTATUS_SPIE;
|
|
if (regs->mstatus & MSTATUS_SIE)
|
|
regs->mstatus |= (1UL << MSTATUS_SPIE_SHIFT);
|
|
|
|
/* Clear SIE for S-mode */
|
|
regs->mstatus &= ~MSTATUS_SIE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Handle trap/interrupt
|
|
*
|
|
* This function is called by firmware linked to OpenSBI
|
|
* library for handling trap/interrupt. It expects the
|
|
* following:
|
|
* 1. The 'mscratch' CSR is pointing to sbi_scratch of current HART
|
|
* 2. The 'mcause' CSR is having exception/interrupt cause
|
|
* 3. The 'mtval' CSR is having additional trap information
|
|
* 4. The 'mtval2' CSR is having additional trap information
|
|
* 5. The 'mtinst' CSR is having decoded trap instruction
|
|
* 6. Stack pointer (SP) is setup for current HART
|
|
* 7. Interrupts are disabled in MSTATUS CSR
|
|
*
|
|
* @param regs pointer to register state
|
|
* @param scratch pointer to sbi_scratch of current HART
|
|
*/
|
|
void sbi_trap_handler(struct sbi_trap_regs *regs,
|
|
struct sbi_scratch *scratch)
|
|
{
|
|
int rc = SBI_ENOTSUPP;
|
|
const char *msg = "trap handler failed";
|
|
u32 hartid = current_hartid();
|
|
ulong mcause = csr_read(CSR_MCAUSE);
|
|
ulong mtval = csr_read(CSR_MTVAL), mtval2 = 0, mtinst = 0;
|
|
struct sbi_trap_info trap;
|
|
|
|
if (misa_extension('H')) {
|
|
mtval2 = csr_read(CSR_MTVAL2);
|
|
mtinst = csr_read(CSR_MTINST);
|
|
}
|
|
|
|
if (mcause & (1UL << (__riscv_xlen - 1))) {
|
|
mcause &= ~(1UL << (__riscv_xlen - 1));
|
|
switch (mcause) {
|
|
case IRQ_M_TIMER:
|
|
sbi_timer_process(scratch);
|
|
break;
|
|
case IRQ_M_SOFT:
|
|
sbi_ipi_process(scratch);
|
|
break;
|
|
default:
|
|
msg = "unhandled external interrupt";
|
|
goto trap_error;
|
|
};
|
|
return;
|
|
}
|
|
|
|
switch (mcause) {
|
|
case CAUSE_ILLEGAL_INSTRUCTION:
|
|
rc = sbi_illegal_insn_handler(mtval, regs);
|
|
msg = "illegal instruction handler failed";
|
|
break;
|
|
case CAUSE_MISALIGNED_LOAD:
|
|
rc = sbi_misaligned_load_handler(hartid, mcause, mtval,
|
|
mtval2, mtinst, regs,
|
|
scratch);
|
|
msg = "misaligned load handler failed";
|
|
break;
|
|
case CAUSE_MISALIGNED_STORE:
|
|
rc = sbi_misaligned_store_handler(hartid, mcause, mtval,
|
|
mtval2, mtinst, regs,
|
|
scratch);
|
|
msg = "misaligned store handler failed";
|
|
break;
|
|
case CAUSE_SUPERVISOR_ECALL:
|
|
case CAUSE_HYPERVISOR_ECALL:
|
|
rc = sbi_ecall_handler(hartid, mcause, regs, scratch);
|
|
msg = "ecall handler failed";
|
|
break;
|
|
default:
|
|
/* If the trap came from S or U mode, redirect it there */
|
|
trap.epc = regs->mepc;
|
|
trap.cause = mcause;
|
|
trap.tval = mtval;
|
|
trap.tval2 = mtval2;
|
|
trap.tinst = mtinst;
|
|
rc = sbi_trap_redirect(regs, &trap);
|
|
break;
|
|
};
|
|
|
|
trap_error:
|
|
if (rc) {
|
|
sbi_trap_error(msg, rc, hartid, mcause, mtval,
|
|
mtval2, mtinst, regs);
|
|
}
|
|
}
|