[PATCH 4/5] lib: sbi: ISA extension emulation.
Inochi Amaoto
inochiama at gmail.com
Mon Oct 27 15:41:50 PDT 2025
On Sun, Oct 26, 2025 at 09:21:58PM +0100, Benedikt Freisen wrote:
> Add trap-based emulation code or compatibility stubs for the following ISA extensions:
> Zba, Zbb, Zbc, Zbs, Zicbom, Zicboz, Zfhmin, Zicond, Zimop, Zcmop, Zcb, Zfa, Zawrs, Zvbb, Supm
>
> Zicbom uses SiFive and XuanTie vendor extensions to flush data caches.
> The Zvbb implementation relies on hardware support for Zbb and RVV 1.0.
>
> Signed-off-by: Benedikt Freisen <b.freisen at gmx.net>
> ---
> CONTRIBUTORS.md | 2 +
> include/sbi/riscv_encoding.h | 177 ++++
> include/sbi/riscv_fp.h | 47 +
> include/sbi/sbi_hart.h | 2 +
> include/sbi/sbi_illegal_insn.h | 1 +
> include/sbi/sbi_insn_emu.h | 24 +
> include/sbi/sbi_insn_emu_fp.h | 19 +
> include/sbi/sbi_insn_emu_v.h | 21 +
> include/sbi/sbi_platform.h | 14 +
> include/sbi/sbi_scratch.h | 12 +-
> include/sbi/sbi_trap.h | 2 +
> lib/sbi/objects.mk | 3 +
> lib/sbi/sbi_emulate_csr.c | 14 +
> lib/sbi/sbi_fwft.c | 14 +-
> lib/sbi/sbi_hart.c | 4 +
> lib/sbi/sbi_illegal_insn.c | 133 ++-
> lib/sbi/sbi_insn_emu.c | 584 ++++++++++++
> lib/sbi/sbi_insn_emu_fp.c | 962 ++++++++++++++++++++
> lib/sbi/sbi_insn_emu_v.c | 1128 ++++++++++++++++++++++++
> lib/sbi/sbi_trap.c | 52 ++
> lib/sbi/sbi_trap_ldst.c | 10 +-
> platform/generic/starfive/jh7110.c | 7 +
> platform/generic/thead/thead-generic.c | 7 +
> 23 files changed, 3195 insertions(+), 44 deletions(-)
> create mode 100644 include/sbi/sbi_insn_emu.h
> create mode 100644 include/sbi/sbi_insn_emu_fp.h
> create mode 100644 include/sbi/sbi_insn_emu_v.h
> create mode 100644 lib/sbi/sbi_insn_emu.c
> create mode 100644 lib/sbi/sbi_insn_emu_fp.c
> create mode 100644 lib/sbi/sbi_insn_emu_v.c
>
> [...]
>
> diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk
> index 8abe1e8..0da775c 100644
> --- a/lib/sbi/objects.mk
> +++ b/lib/sbi/objects.mk
> @@ -81,6 +81,9 @@ libsbi-objs-y += sbi_hfence.o
> libsbi-objs-y += sbi_hsm.o
> libsbi-objs-y += sbi_illegal_atomic.o
> libsbi-objs-y += sbi_illegal_insn.o
> +libsbi-objs-y += sbi_insn_emu.o
> +libsbi-objs-y += sbi_insn_emu_fp.o
> +libsbi-objs-y += sbi_insn_emu_v.o
> libsbi-objs-y += sbi_init.o
> libsbi-objs-y += sbi_ipi.o
> libsbi-objs-y += sbi_irqchip.o
I suggest adding detailed Kconfig entries for these new emulation.
This emulation is quiet heavy and I think it should be possible for
users to decide which extension is needed to be emulated.
> diff --git a/lib/sbi/sbi_emulate_csr.c b/lib/sbi/sbi_emulate_csr.c
> index c2253c8..e0e2373 100644
> --- a/lib/sbi/sbi_emulate_csr.c
> +++ b/lib/sbi/sbi_emulate_csr.c
> @@ -76,6 +76,12 @@ int sbi_emulate_csr_read(int csr_num, struct sbi_trap_regs *regs,
> return SBI_ENOTSUPP;
> *csr_val = csr_read(CSR_MINSTRET);
> break;
> + case CSR_SENVCFG:
> + if (prev_mode == PRV_S && !virt)
> + *csr_val = scratch->sw_senvcfg;
> + else
> + ret = SBI_ENOTSUPP;
> + break;
>
> #if __riscv_xlen == 32
> case CSR_HTIMEDELTAH:
> @@ -162,6 +168,14 @@ int sbi_emulate_csr_write(int csr_num, struct sbi_trap_regs *regs,
> else
> ret = SBI_ENOTSUPP;
> break;
> + case CSR_SENVCFG:
> + if (prev_mode == PRV_S && !virt) {
> + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
> + scratch->sw_senvcfg = csr_val;
> + }
> + else
> + ret = SBI_ENOTSUPP;
> + break;
> #if __riscv_xlen == 32
> case CSR_HTIMEDELTAH:
> if (prev_mode == PRV_S && !virt)
> diff --git a/lib/sbi/sbi_fwft.c b/lib/sbi/sbi_fwft.c
> index a2aefb9..73a5d5b 100644
> --- a/lib/sbi/sbi_fwft.c
> +++ b/lib/sbi/sbi_fwft.c
> @@ -241,11 +241,16 @@ static int fwft_set_pmlen(struct fwft_config *conf, unsigned long value)
> return SBI_EINVAL;
> }
>
> + /* Reset emulated pointer masking */
> + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
> + scratch->sw_pm = 0;
> +
> prev = csr_read_clear(CSR_MENVCFG, ENVCFG_PMM);
> csr_set(CSR_MENVCFG, pmm);
> if ((csr_read(CSR_MENVCFG) & ENVCFG_PMM) != pmm) {
> csr_write(CSR_MENVCFG, prev);
> - return SBI_EINVAL;
> + /* Instead of returning SBI_EINVAL, enable emulation */
> + scratch->sw_pm = value;
> }
>
> return SBI_OK;
> @@ -253,6 +258,13 @@ static int fwft_set_pmlen(struct fwft_config *conf, unsigned long value)
>
> static int fwft_get_pmlen(struct fwft_config *conf, unsigned long *value)
> {
> + /* Check for emulated pointer masking */
> + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
> + if (scratch->sw_pm) {
> + *value = scratch->sw_pm;
> + return SBI_OK;
> + }
> +
> switch (csr_read(CSR_MENVCFG) & ENVCFG_PMM) {
> case ENVCFG_PMM_PMLEN_0:
> *value = 0;
> diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
> index 1b50f67..a7cbf4a 100644
> --- a/lib/sbi/sbi_hart.c
> +++ b/lib/sbi/sbi_hart.c
> @@ -991,6 +991,10 @@ __pmp_skip:
> __check_csr_existence(CSR_CYCLE, SBI_HART_CSR_CYCLE);
> __check_csr_existence(CSR_TIME, SBI_HART_CSR_TIME);
> __check_csr_existence(CSR_INSTRET, SBI_HART_CSR_INSTRET);
> + __check_csr_existence(CSR_MENVCFG, SBI_HART_CSR_MENVCFG);
> + __check_csr_existence(CSR_SENVCFG, SBI_HART_CSR_SENVCFG);
> + /* Initialize value of emulated SENVCFG CSR */
> + scratch->sw_senvcfg = ENVCFG_CBZE | ENVCFG_CBCFE | ENVCFG_CBIE;
>
> #undef __check_csr_existence
>
> diff --git a/lib/sbi/sbi_illegal_insn.c b/lib/sbi/sbi_illegal_insn.c
> index 4b85450..47f050c 100644
> --- a/lib/sbi/sbi_illegal_insn.c
> +++ b/lib/sbi/sbi_illegal_insn.c
> @@ -2,9 +2,13 @@
> * SPDX-License-Identifier: BSD-2-Clause
> *
> * Copyright (c) 2019 Western Digital Corporation or its affiliates.
> + * Copyright (c) 2025 Benedikt Freisen*.
> *
> * Authors:
> * Anup Patel <anup.patel at wdc.com>
> + * Benedikt Freisen <b.freisen at gmx.net>
> + *
> + * *) ISA extension emulation
> */
>
> #include <sbi/riscv_asm.h>
> @@ -15,6 +19,9 @@
> #include <sbi/sbi_error.h>
> #include <sbi/sbi_illegal_atomic.h>
> #include <sbi/sbi_illegal_insn.h>
> +#include <sbi/sbi_insn_emu.h>
> +#include <sbi/sbi_insn_emu_fp.h>
> +#include <sbi/sbi_insn_emu_v.h>
> #include <sbi/sbi_pmu.h>
> #include <sbi/sbi_trap.h>
> #include <sbi/sbi_unpriv.h>
> @@ -25,7 +32,7 @@ int truly_illegal_insn(ulong insn, struct sbi_trap_regs *regs)
> struct sbi_trap_info trap;
>
> trap.cause = CAUSE_ILLEGAL_INSTRUCTION;
> - trap.tval = insn;
> + trap.tval = insn;
> trap.tval2 = 0;
> trap.tinst = 0;
> trap.gva = 0;
> @@ -57,7 +64,8 @@ static int misc_mem_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
> return 0;
> }
>
> - return truly_illegal_insn(insn, regs);
> + /* Delegate Zicbom and Zicboz emulation */
> + return sbi_insn_emu_zicbom_zicboz(insn, regs);
> }
>
> static int system_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
> @@ -70,14 +78,29 @@ static int system_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
> ulong csr_val, new_csr_val;
>
> if (prev_mode == PRV_M) {
> - sbi_printf("%s: Failed to access CSR %#x from M-mode",
> - __func__, csr_num);
> + sbi_printf("%s: Failed to access CSR %#x from M-mode", __func__,
> + csr_num);
> return SBI_EFAIL;
> }
>
> /* Ensure that we got CSR read/write instruction */
> int funct3 = GET_RM(insn);
> if (funct3 == 0 || funct3 == 4) {
> + /* Handle "Zawrs" Wait-on-Reservation-Set */
> + if (insn == INSN_MATCH_WRS_NTO || insn == INSN_MATCH_WRS_STO) {
> + /* do nothing */
> + regs->mepc += 4;
> + return 0;
> + }
> + /* Handle "Zimop" May-Be-Operations */
> + if ((insn & INSN_MASK_MOP_R_N) == INSN_MATCH_MOP_R_N ||
> + (insn & INSN_MASK_MOP_RR_N) == INSN_MATCH_MOP_RR_N) {
> + SET_RD(insn, regs, 0);
> + regs->mepc += 4;
> + return 0;
> + }
> +
> + /* Otherwise treat this as an error */
> sbi_printf("%s: Invalid opcode for CSR read/write instruction",
> __func__);
> return truly_illegal_insn(insn, regs);
> @@ -126,50 +149,77 @@ static int system_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
> }
>
> static const illegal_insn_func illegal_insn_table[32] = {
> - truly_illegal_insn, /* 0 */
> - truly_illegal_insn, /* 1 */
> - truly_illegal_insn, /* 2 */
> - misc_mem_opcode_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 */
> - sbi_illegal_atomic, /* 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 */
> + truly_illegal_insn, /* 0 */
> + sbi_insn_emu_load_fp, /* 1 */
> + truly_illegal_insn, /* 2 */
> + misc_mem_opcode_insn, /* 3 */
> + sbi_insn_emu_op_imm, /* 4 */
> + truly_illegal_insn, /* 5 */
> + sbi_insn_emu_op_imm_32, /* 6 */
> + truly_illegal_insn, /* 7 */
> + truly_illegal_insn, /* 8 */
> + sbi_insn_emu_store_fp, /* 9 */
> + truly_illegal_insn, /* 10 */
> + sbi_illegal_atomic, /* 11 */
> + sbi_insn_emu_op, /* 12 */
> + truly_illegal_insn, /* 13 */
> + sbi_insn_emu_op_32, /* 14 */
> + truly_illegal_insn, /* 15 */
> + truly_illegal_insn, /* 16 */
> + truly_illegal_insn, /* 17 */
> + truly_illegal_insn, /* 18 */
> + truly_illegal_insn, /* 19 */
> + sbi_insn_emu_op_fp, /* 20 */
> + sbi_insn_emu_op_v, /* 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 */
> +};
> +
> +static const illegal_insn_func illegal_insn16_table[24] = {
> + truly_illegal_insn, /* 0 */
> + truly_illegal_insn, /* 1 */
> + truly_illegal_insn, /* 2 */
> + truly_illegal_insn, /* 3 */
> + sbi_insn_emu_c_reserved, /* 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 */
> + sbi_insn_emu_c_mop, /* 11 */
> + sbi_insn_emu_c_misc_alu, /* 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 */
> };
>
Can we convert this to a carray configuration? I think it is time
to build it dynamicly.
> int sbi_illegal_insn_handler(struct sbi_trap_context *tcntx)
> {
> struct sbi_trap_regs *regs = &tcntx->regs;
> - ulong insn = tcntx->trap.tval;
> + ulong insn = tcntx->trap.tval;
> struct sbi_trap_info uptrap;
>
> /*
> - * We only deal with 32-bit (or longer) illegal instructions. If we
> - * see instruction is zero OR instruction is 16-bit then we fetch and
> - * check the instruction encoding using unprivilege access.
> + * We only deal with 32-bit (or longer) illegal instructions directly.
> + * If we see instruction is zero OR instruction is 16-bit then we fetch
> + * and check the instruction encoding using unprivilege access.
> *
> * The program counter (PC) in RISC-V world is always 2-byte aligned
> * so handling only 32-bit (or longer) illegal instructions also help
> @@ -183,7 +233,8 @@ int sbi_illegal_insn_handler(struct sbi_trap_context *tcntx)
> if (uptrap.cause)
> return sbi_trap_redirect(regs, &uptrap);
> if ((insn & 3) != 3)
> - return truly_illegal_insn(insn, regs);
> + return illegal_insn16_table[(insn & 3) << 3 |
> + insn >> 13](insn, regs);
> }
>
> return illegal_insn_table[(insn & 0x7c) >> 2](insn, regs);
>
> [...]
>
> diff --git a/platform/generic/starfive/jh7110.c b/platform/generic/starfive/jh7110.c
> index c132843..037be2e 100644
> --- a/platform/generic/starfive/jh7110.c
> +++ b/platform/generic/starfive/jh7110.c
> @@ -301,6 +301,12 @@ static bool starfive_jh7110_cold_boot_allowed(u32 hartid)
> return generic_cold_boot_allowed(hartid);
> }
>
> +static void starfive_jh7110_flush_data_caches(void)
> +{
> + /* flush L1 data cache via cflush.d.l1 zero */
> + asm volatile(".insn i 0x73, 0, zero, zero, -0x40");
> +}
> +
> static int starfive_jh7110_platform_init(const void *fdt, int nodeoff,
> const struct fdt_match *match)
> {
> @@ -316,6 +322,7 @@ static int starfive_jh7110_platform_init(const void *fdt, int nodeoff,
>
> generic_platform_ops.cold_boot_allowed = starfive_jh7110_cold_boot_allowed;
> generic_platform_ops.final_init = starfive_jh7110_final_init;
> + generic_platform_ops.flush_data_caches = starfive_jh7110_flush_data_caches;
>
> return 0;
> }
> diff --git a/platform/generic/thead/thead-generic.c b/platform/generic/thead/thead-generic.c
> index ddb4f0b..2e6c5a1 100644
> --- a/platform/generic/thead/thead-generic.c
> +++ b/platform/generic/thead/thead-generic.c
> @@ -39,6 +39,12 @@ static int thead_pmu_extensions_init(struct sbi_hart_features *hfeatures)
> return 0;
> }
>
> +static void thead_flush_data_caches(void)
> +{
> + /* flush data cache via th.dcache.call */
> + asm volatile(".insn i 0xb, 0, zero, zero, 1");
> +}
> +
> static int thead_generic_platform_init(const void *fdt, int nodeoff,
> const struct fdt_match *match)
> {
> @@ -48,6 +54,7 @@ static int thead_generic_platform_init(const void *fdt, int nodeoff,
> generic_platform_ops.early_init = thead_tlb_flush_early_init;
> if (quirks->errata & THEAD_QUIRK_ERRATA_THEAD_PMU)
> generic_platform_ops.extensions_init = thead_pmu_extensions_init;
> + generic_platform_ops.flush_data_caches = thead_flush_data_caches;
>
> return 0;
> }
Please split these two diff into standalone patches. And, Please add an
errata quirk for the data_caches. New cores may not suffer this problem.
Regards,
INochi
More information about the opensbi
mailing list