| /* SPDX-License-Identifier: GPL-2.0-only */ |
| /* |
| * Early initialization code for riscv |
| */ |
| |
| #include <arch/encoding.h> |
| #include <arch/exception.h> |
| #include <console/console.h> |
| #include <vm.h> |
| #include <mcall.h> |
| #include <sbi.h> |
| #include <types.h> |
| |
| static const char *const exception_names[] = { |
| "Instruction address misaligned", |
| "Instruction access fault", |
| "Illegal instruction", |
| "Breakpoint", |
| "Load address misaligned", |
| "Load access fault", |
| "Store address misaligned", |
| "Store access fault", |
| "Environment call from U-mode", |
| "Environment call from S-mode", |
| "Reserved (10)", |
| "Environment call from M-mode", |
| "Instruction page fault", |
| "Load page fault", |
| "Reserved (14)", |
| "Store page fault", |
| }; |
| |
| static const char *mstatus_to_previous_mode(uintptr_t ms) |
| { |
| switch (ms & MSTATUS_MPP) { |
| case 0x00000000: |
| return "user"; |
| case 0x00000800: |
| return "supervisor"; |
| case 0x00001000: |
| return "hypervisor"; |
| case 0x00001800: |
| return "machine"; |
| } |
| |
| return "unknown"; |
| } |
| |
| static void print_trap_information(const struct trapframe *tf) |
| { |
| const char *previous_mode; |
| bool mprv = !!(tf->status & MSTATUS_MPRV); |
| int hart_id = read_csr(mhartid); |
| |
| /* Leave some space around the trap message */ |
| printk(BIOS_DEBUG, "\n"); |
| |
| if (tf->cause < ARRAY_SIZE(exception_names)) |
| printk(BIOS_DEBUG, "Exception: %s\n", exception_names[tf->cause]); |
| else |
| printk(BIOS_DEBUG, "Trap: Unknown cause %p\n", (void *)tf->cause); |
| |
| previous_mode = mstatus_to_previous_mode(read_csr(mstatus)); |
| printk(BIOS_DEBUG, "Hart ID: %d\n", hart_id); |
| printk(BIOS_DEBUG, "Previous mode: %s%s\n", previous_mode, mprv ? " (MPRV)" : ""); |
| printk(BIOS_DEBUG, "Bad instruction pc: %p\n", (void *)tf->epc); |
| printk(BIOS_DEBUG, "Bad address: %p\n", (void *)tf->badvaddr); |
| printk(BIOS_DEBUG, "Stored ra: %p\n", (void *)tf->gpr[1]); |
| printk(BIOS_DEBUG, "Stored sp: %p\n", (void *)tf->gpr[2]); |
| } |
| |
| static void interrupt_handler(struct trapframe *tf) |
| { |
| uint64_t cause = tf->cause & ~0x8000000000000000ULL; |
| |
| switch (cause) { |
| case IRQ_M_TIMER: |
| /* |
| * Set interrupt pending for supervisor mode and disable timer |
| * interrupt in machine mode. |
| * To receive another timer interrupt just set timecmp and |
| * enable machine mode timer interrupt again. |
| */ |
| |
| clear_csr(mie, MIP_MTIP); |
| set_csr(mip, MIP_STIP); |
| |
| break; |
| case IRQ_M_SOFT: |
| if (HLS()->ipi_pending & IPI_SOFT) { |
| set_csr(mip, MIP_SSIP); |
| } else if (HLS()->ipi_pending & IPI_FENCE_I) { |
| asm volatile("fence.i"); |
| } else if (HLS()->ipi_pending & IPI_SFENCE_VMA) { |
| asm volatile("sfence.vma"); |
| } else if (HLS()->ipi_pending & IPI_SFENCE_VMA_ASID) { |
| asm volatile("sfence.vma"); |
| } else if (HLS()->ipi_pending & IPI_SHUTDOWN) { |
| while (HLS()->ipi_pending & IPI_SHUTDOWN) |
| asm volatile("wfi"); |
| } |
| 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)(struct trapframe *tf) = default_trap_handler; |
| |
| void default_trap_handler(struct trapframe *tf) |
| { |
| if (tf->cause & 0x8000000000000000ULL) { |
| interrupt_handler(tf); |
| return; |
| } |
| |
| switch (tf->cause) { |
| case CAUSE_MISALIGNED_FETCH: |
| case CAUSE_FETCH_ACCESS: |
| case CAUSE_ILLEGAL_INSTRUCTION: |
| case CAUSE_BREAKPOINT: |
| case CAUSE_LOAD_ACCESS: |
| case CAUSE_STORE_ACCESS: |
| case CAUSE_USER_ECALL: |
| case CAUSE_HYPERVISOR_ECALL: |
| case CAUSE_MACHINE_ECALL: |
| print_trap_information(tf); |
| break; |
| case CAUSE_SUPERVISOR_ECALL: |
| handle_sbi(tf); |
| return; |
| case CAUSE_MISALIGNED_LOAD: |
| case CAUSE_MISALIGNED_STORE: |
| print_trap_information(tf); |
| handle_misaligned(tf); |
| return; |
| default: |
| printk(BIOS_EMERG, "================================\n"); |
| printk(BIOS_EMERG, "coreboot: can not handle a trap:\n"); |
| printk(BIOS_EMERG, "================================\n"); |
| print_trap_information(tf); |
| break; |
| } |
| |
| die("Can't recover from trap. Halting.\n"); |
| } |
| |
| /* This function used to redirect trap to s-mode. */ |
| void redirect_trap(void) |
| { |
| write_csr(stval, read_csr(mtval)); |
| write_csr(sepc, read_csr(mepc)); |
| write_csr(scause, read_csr(mcause)); |
| write_csr(mepc, read_csr(stvec)); |
| |
| uintptr_t status = read_csr(mstatus); |
| uintptr_t mpp = EXTRACT_FIELD(status, MSTATUS_MPP); |
| status = INSERT_FIELD(status, MSTATUS_MPP, 1); |
| status = INSERT_FIELD(status, MSTATUS_SPP, mpp & 1); |
| write_csr(mstatus, status); |
| } |