| /* |
| * 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, |
| 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%lx mtval=0x%lx\n", |
| __func__, hartid, mcause, mtval); |
| sbi_printf("%s: hart%d: mepc=0x%lx mstatus=0x%lx\n", |
| __func__, hartid, regs->mepc, regs->mstatus); |
| sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", |
| __func__, hartid, "ra", regs->ra, "sp", regs->sp); |
| sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", |
| __func__, hartid, "gp", regs->gp, "tp", regs->tp); |
| sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", |
| __func__, hartid, "s0", regs->s0, "s1", regs->s1); |
| sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", |
| __func__, hartid, "a0", regs->a0, "a1", regs->a1); |
| sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", |
| __func__, hartid, "a2", regs->a2, "a3", regs->a3); |
| sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", |
| __func__, hartid, "a4", regs->a4, "a5", regs->a5); |
| sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", |
| __func__, hartid, "a6", regs->a6, "a7", regs->a7); |
| sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", |
| __func__, hartid, "s2", regs->s2, "s3", regs->s3); |
| sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", |
| __func__, hartid, "s4", regs->s4, "s5", regs->s5); |
| sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", |
| __func__, hartid, "s6", regs->s6, "s7", regs->s7); |
| sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", |
| __func__, hartid, "s8", regs->s8, "s9", regs->s9); |
| sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", |
| __func__, hartid, "s10", regs->s10, "s11", regs->s11); |
| sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", |
| __func__, hartid, "t0", regs->t0, "t1", regs->t1); |
| sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", |
| __func__, hartid, "t2", regs->t2, "t3", regs->t3); |
| sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n", |
| __func__, hartid, "t4", regs->t4, "t5", regs->t5); |
| sbi_printf("%s: hart%d: %s=0x%lx\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 scratch pointer to sbi_scratch of current HART |
| * @param epc error PC for lower privledge mode |
| * @param cause exception cause for lower privledge mode |
| * @param tval trap value for lower privledge mode |
| * |
| * @return 0 on success and negative error code on failure |
| */ |
| int sbi_trap_redirect(struct sbi_trap_regs *regs, |
| struct sbi_scratch *scratch, |
| ulong epc, ulong cause, ulong tval) |
| { |
| ulong new_mstatus, prev_mode; |
| |
| /* 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; |
| |
| /* Update S-mode exception info */ |
| csr_write(stval, tval); |
| csr_write(sepc, epc); |
| csr_write(scause, cause); |
| |
| /* Set MEPC to S-mode exception vector base */ |
| regs->mepc = csr_read(stvec); |
| |
| /* Initial value of new MSTATUS */ |
| new_mstatus = regs->mstatus; |
| |
| /* Clear MPP, SPP, SPIE, and SIE */ |
| new_mstatus &= ~(MSTATUS_MPP | |
| MSTATUS_SPP | MSTATUS_SPIE | MSTATUS_SIE); |
| |
| /* Set SPP */ |
| if (prev_mode == PRV_S) |
| new_mstatus |= (1UL << MSTATUS_SPP_SHIFT); |
| |
| /* Set SPIE */ |
| if (regs->mstatus & MSTATUS_SIE) |
| new_mstatus |= (1UL << MSTATUS_SPIE_SHIFT); |
| |
| /* Set MPP */ |
| new_mstatus |= (PRV_S << MSTATUS_MPP_SHIFT); |
| |
| /* Set new value in MSTATUS */ |
| regs->mstatus = new_mstatus; |
| |
| 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. Stack pointer (SP) is setup for current HART |
| * 5. 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 = sbi_current_hartid(); |
| ulong mcause = csr_read(mcause); |
| |
| 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(hartid, mcause, regs, scratch); |
| msg = "illegal instruction handler failed"; |
| break; |
| case CAUSE_MISALIGNED_LOAD: |
| rc = sbi_misaligned_load_handler(hartid, mcause, regs, scratch); |
| msg = "misaligned load handler failed"; |
| break; |
| case CAUSE_MISALIGNED_STORE: |
| rc = sbi_misaligned_store_handler(hartid, mcause, 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: |
| break; |
| }; |
| |
| trap_error: |
| if (rc) { |
| sbi_trap_error(msg, rc, hartid, mcause, csr_read(mtval), regs); |
| } |
| } |