diff options
author | Atish Patra <atish.patra@wdc.com> | 2019-06-19 00:54:01 +0300 |
---|---|---|
committer | Anup Patel <anup.patel@wdc.com> | 2019-06-19 07:18:51 +0300 |
commit | 749b0b093242a4c27f7c4f66121afd7852b2de48 (patch) | |
tree | 90c46fe6e750ddf08dd57347ddd498571792353a /lib/sbi/sbi_illegal_insn.c | |
parent | a5b37bd7d275fc65d8fd0b19bd3a08edfe4e6096 (diff) | |
download | opensbi-749b0b093242a4c27f7c4f66121afd7852b2de48.tar.xz |
lib: Move sbi core library to lib/sbi
Signed-off-by: Atish Patra <atish.patra@wdc.com>
Acked-by: Anup Patel <anup.patel@wdc.com>
Diffstat (limited to 'lib/sbi/sbi_illegal_insn.c')
-rw-r--r-- | lib/sbi/sbi_illegal_insn.c | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/lib/sbi/sbi_illegal_insn.c b/lib/sbi/sbi_illegal_insn.c new file mode 100644 index 0000000..5541838 --- /dev/null +++ b/lib/sbi/sbi_illegal_insn.c @@ -0,0 +1,131 @@ +/* + * 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/riscv_unpriv.h> +#include <sbi/sbi_bits.h> +#include <sbi/sbi_emulate_csr.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_illegal_insn.h> +#include <sbi/sbi_trap.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) +{ + return sbi_trap_redirect(regs, scratch, regs->mepc, mcause, insn); +} + +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; + + if (sbi_emulate_csr_read(csr_num, hartid, regs->mstatus, scratch, + &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, hartid, regs->mstatus, + scratch, 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, + struct sbi_trap_regs *regs, + struct sbi_scratch *scratch) +{ + ulong insn = csr_read(mbadaddr); + + if (unlikely((insn & 3) != 3)) { + if (insn == 0) + insn = get_insn(regs->mepc, NULL); + 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); +} |