forked from Mirrors/opensbi

We remove scratch and hartid parameter from various functions for CSR emulation 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>
157 lines
3.8 KiB
C
157 lines
3.8 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_bitops.h>
|
|
#include <sbi/sbi_emulate_csr.h>
|
|
#include <sbi/sbi_error.h>
|
|
#include <sbi/sbi_illegal_insn.h>
|
|
#include <sbi/sbi_trap.h>
|
|
#include <sbi/sbi_unpriv.h>
|
|
|
|
typedef int (*illegal_insn_func)(ulong insn, u32 hartid, ulong mcause,
|
|
struct sbi_trap_regs *regs,
|
|
struct sbi_scratch *scratch);
|
|
|
|
static int truly_illegal_insn(ulong insn, u32 hartid, ulong mcause,
|
|
struct sbi_trap_regs *regs,
|
|
struct sbi_scratch *scratch)
|
|
{
|
|
struct sbi_trap_info trap;
|
|
|
|
trap.epc = regs->mepc;
|
|
trap.cause = mcause;
|
|
trap.tval = insn;
|
|
trap.tval2 = 0;
|
|
trap.tinst = 0;
|
|
|
|
return sbi_trap_redirect(regs, &trap);
|
|
}
|
|
|
|
static int system_opcode_insn(ulong insn, u32 hartid, ulong mcause,
|
|
struct sbi_trap_regs *regs,
|
|
struct sbi_scratch *scratch)
|
|
{
|
|
int do_write, rs1_num = (insn >> 15) & 0x1f;
|
|
ulong rs1_val = GET_RS1(insn, regs);
|
|
int csr_num = (u32)insn >> 20;
|
|
ulong csr_val, new_csr_val;
|
|
|
|
/*
|
|
* WFI always traps as illegal instruction when executed from
|
|
* VS/VU mode so we just forward it to HS-mode.
|
|
*/
|
|
#if __riscv_xlen == 32
|
|
if ((regs->mstatusH & MSTATUSH_MPV) &&
|
|
#else
|
|
if ((regs->mstatus & MSTATUS_MPV) &&
|
|
#endif
|
|
(insn & INSN_MASK_WFI) == INSN_MATCH_WFI)
|
|
return truly_illegal_insn(insn, hartid, mcause,
|
|
regs, scratch);
|
|
|
|
if (sbi_emulate_csr_read(csr_num, regs, &csr_val))
|
|
return truly_illegal_insn(insn, hartid, mcause,
|
|
regs, scratch);
|
|
|
|
do_write = rs1_num;
|
|
switch (GET_RM(insn)) {
|
|
case 1:
|
|
new_csr_val = rs1_val;
|
|
do_write = 1;
|
|
break;
|
|
case 2:
|
|
new_csr_val = csr_val | rs1_val;
|
|
break;
|
|
case 3:
|
|
new_csr_val = csr_val & ~rs1_val;
|
|
break;
|
|
case 5:
|
|
new_csr_val = rs1_num;
|
|
do_write = 1;
|
|
break;
|
|
case 6:
|
|
new_csr_val = csr_val | rs1_num;
|
|
break;
|
|
case 7:
|
|
new_csr_val = csr_val & ~rs1_num;
|
|
break;
|
|
default:
|
|
return truly_illegal_insn(insn, hartid, mcause, regs, scratch);
|
|
};
|
|
|
|
if (do_write && sbi_emulate_csr_write(csr_num, regs, new_csr_val))
|
|
return truly_illegal_insn(insn, hartid, mcause, regs, scratch);
|
|
|
|
SET_RD(insn, regs, csr_val);
|
|
|
|
regs->mepc += 4;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static illegal_insn_func illegal_insn_table[32] = {
|
|
truly_illegal_insn, /* 0 */
|
|
truly_illegal_insn, /* 1 */
|
|
truly_illegal_insn, /* 2 */
|
|
truly_illegal_insn, /* 3 */
|
|
truly_illegal_insn, /* 4 */
|
|
truly_illegal_insn, /* 5 */
|
|
truly_illegal_insn, /* 6 */
|
|
truly_illegal_insn, /* 7 */
|
|
truly_illegal_insn, /* 8 */
|
|
truly_illegal_insn, /* 9 */
|
|
truly_illegal_insn, /* 10 */
|
|
truly_illegal_insn, /* 11 */
|
|
truly_illegal_insn, /* 12 */
|
|
truly_illegal_insn, /* 13 */
|
|
truly_illegal_insn, /* 14 */
|
|
truly_illegal_insn, /* 15 */
|
|
truly_illegal_insn, /* 16 */
|
|
truly_illegal_insn, /* 17 */
|
|
truly_illegal_insn, /* 18 */
|
|
truly_illegal_insn, /* 19 */
|
|
truly_illegal_insn, /* 20 */
|
|
truly_illegal_insn, /* 21 */
|
|
truly_illegal_insn, /* 22 */
|
|
truly_illegal_insn, /* 23 */
|
|
truly_illegal_insn, /* 24 */
|
|
truly_illegal_insn, /* 25 */
|
|
truly_illegal_insn, /* 26 */
|
|
truly_illegal_insn, /* 27 */
|
|
system_opcode_insn, /* 28 */
|
|
truly_illegal_insn, /* 29 */
|
|
truly_illegal_insn, /* 30 */
|
|
truly_illegal_insn /* 31 */
|
|
};
|
|
|
|
int sbi_illegal_insn_handler(u32 hartid, ulong mcause, ulong insn,
|
|
struct sbi_trap_regs *regs,
|
|
struct sbi_scratch *scratch)
|
|
{
|
|
struct sbi_trap_info uptrap;
|
|
|
|
if (unlikely((insn & 3) != 3)) {
|
|
if (insn == 0) {
|
|
insn = sbi_get_insn(regs->mepc, &uptrap);
|
|
if (uptrap.cause) {
|
|
uptrap.epc = regs->mepc;
|
|
return sbi_trap_redirect(regs, &uptrap);
|
|
}
|
|
}
|
|
if ((insn & 3) != 3)
|
|
return truly_illegal_insn(insn, hartid, mcause, regs,
|
|
scratch);
|
|
}
|
|
|
|
return illegal_insn_table[(insn & 0x7c) >> 2](insn, hartid, mcause,
|
|
regs, scratch);
|
|
}
|