From 94692c3a34d9e2e149bd9db188425e9da6e578c6 Mon Sep 17 00:00:00 2001 From: LIU Zhiwei Date: Mon, 12 Jun 2023 13:10:27 +0200 Subject: [PATCH 01/54] target/riscv: Use xl instead of mxl for disassemble MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disassemble function(plugin_disas, target_disas, monitor_disas) will always call set_disas_info before disassembling instructions. plugin_disas and target_disas will always be called under a TB, which has the same XLEN. We can't ensure that monitor_disas will always be called under a TB, but current XLEN will still be a better choice, thus we can ensure at least the disassemble of the nearest one TB is right. Signed-off-by: LIU Zhiwei Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Signed-off-by: Christoph Müllner Message-Id: <20230612111034.3955227-2-christoph.muellner@vrull.eu> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 4035fe0e62..3faf703131 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -858,9 +858,10 @@ static void riscv_cpu_reset_hold(Object *obj) static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info) { RISCVCPU *cpu = RISCV_CPU(s); + CPURISCVState *env = &cpu->env; info->target_info = &cpu->cfg; - switch (riscv_cpu_mxl(&cpu->env)) { + switch (env->xl) { case MXL_RV32: info->print_insn = print_insn_riscv32; break; From 2e8c1e0215449eb0a88bbb3fae2f6277777611c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20M=C3=BCllner?= Date: Mon, 12 Jun 2023 13:10:28 +0200 Subject: [PATCH 02/54] target/riscv: Factor out extension tests to cpu_cfg.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch moves the extension test functions that are used to gate vendor extension decoders, into cpu_cfg.h. This allows to reuse them in the disassembler. This patch does not introduce new functionality. However, the patch includes a small change: The parameter for the extension test functions has been changed from 'DisasContext*' to 'const RISCVCPUConfig*' to keep the code in cpu_cfg.h self-contained. Signed-off-by: Christoph Müllner Reviewed-by: Daniel Henrique Barboza Reviewed-by: LIU Zhiwei Reviewed-by: Weiwei Li Message-Id: <20230612111034.3955227-3-christoph.muellner@vrull.eu> Signed-off-by: Alistair Francis --- target/riscv/cpu_cfg.h | 26 ++++++++++++++++++++++++++ target/riscv/translate.c | 27 ++------------------------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index c4a627d335..0b4fe4b540 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -133,4 +133,30 @@ struct RISCVCPUConfig { }; typedef struct RISCVCPUConfig RISCVCPUConfig; + +/* Helper functions to test for extensions. */ + +static inline bool always_true_p(const RISCVCPUConfig *cfg __attribute__((__unused__))) +{ + return true; +} + +static inline bool has_xthead_p(const RISCVCPUConfig *cfg) +{ + return cfg->ext_xtheadba || cfg->ext_xtheadbb || + cfg->ext_xtheadbs || cfg->ext_xtheadcmo || + cfg->ext_xtheadcondmov || + cfg->ext_xtheadfmemidx || cfg->ext_xtheadfmv || + cfg->ext_xtheadmac || cfg->ext_xtheadmemidx || + cfg->ext_xtheadmempair || cfg->ext_xtheadsync; +} + +#define MATERIALISE_EXT_PREDICATE(ext) \ + static inline bool has_ ## ext ## _p(const RISCVCPUConfig *cfg) \ + { \ + return cfg->ext_ ## ext ; \ + } + +MATERIALISE_EXT_PREDICATE(XVentanaCondOps) + #endif diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 8a33da811e..0a5ab89c43 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -121,29 +121,6 @@ static inline bool has_ext(DisasContext *ctx, uint32_t ext) return ctx->misa_ext & ext; } -static bool always_true_p(DisasContext *ctx __attribute__((__unused__))) -{ - return true; -} - -static bool has_xthead_p(DisasContext *ctx __attribute__((__unused__))) -{ - return ctx->cfg_ptr->ext_xtheadba || ctx->cfg_ptr->ext_xtheadbb || - ctx->cfg_ptr->ext_xtheadbs || ctx->cfg_ptr->ext_xtheadcmo || - ctx->cfg_ptr->ext_xtheadcondmov || - ctx->cfg_ptr->ext_xtheadfmemidx || ctx->cfg_ptr->ext_xtheadfmv || - ctx->cfg_ptr->ext_xtheadmac || ctx->cfg_ptr->ext_xtheadmemidx || - ctx->cfg_ptr->ext_xtheadmempair || ctx->cfg_ptr->ext_xtheadsync; -} - -#define MATERIALISE_EXT_PREDICATE(ext) \ - static bool has_ ## ext ## _p(DisasContext *ctx) \ - { \ - return ctx->cfg_ptr->ext_ ## ext ; \ - } - -MATERIALISE_EXT_PREDICATE(XVentanaCondOps); - #ifdef TARGET_RISCV32 #define get_xl(ctx) MXL_RV32 #elif defined(CONFIG_USER_ONLY) @@ -1134,7 +1111,7 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) * that are tested in-order until a decoder matches onto the opcode. */ static const struct { - bool (*guard_func)(DisasContext *); + bool (*guard_func)(const RISCVCPUConfig *); bool (*decode_func)(DisasContext *, uint32_t); } decoders[] = { { always_true_p, decode_insn32 }, @@ -1163,7 +1140,7 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) ctx->opcode = opcode32; for (size_t i = 0; i < ARRAY_SIZE(decoders); ++i) { - if (decoders[i].guard_func(ctx) && + if (decoders[i].guard_func(ctx->cfg_ptr) && decoders[i].decode_func(ctx, opcode32)) { return; } From 5d326db2f9d0f4c90a3298f12bcb803e7bf302dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20M=C3=BCllner?= Date: Mon, 12 Jun 2023 13:10:29 +0200 Subject: [PATCH 03/54] disas/riscv: Move types/constants to new header file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to enable vendor disassembler support, we need to move types and constants into a header file so that other compilation units can use them as well. This patch does not introduce any functional changes. Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Signed-off-by: Christoph Müllner Message-Id: <20230612111034.3955227-4-christoph.muellner@vrull.eu> Signed-off-by: Alistair Francis --- disas/riscv.c | 270 +---------------------------------------------- disas/riscv.h | 282 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 283 insertions(+), 269 deletions(-) create mode 100644 disas/riscv.h diff --git a/disas/riscv.c b/disas/riscv.c index 5005364aba..4a55348267 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -20,157 +20,7 @@ #include "qemu/osdep.h" #include "disas/dis-asm.h" #include "target/riscv/cpu_cfg.h" - -/* types */ - -typedef uint64_t rv_inst; -typedef uint16_t rv_opcode; - -/* enums */ - -typedef enum { - rv32, - rv64, - rv128 -} rv_isa; - -typedef enum { - rv_rm_rne = 0, - rv_rm_rtz = 1, - rv_rm_rdn = 2, - rv_rm_rup = 3, - rv_rm_rmm = 4, - rv_rm_dyn = 7, -} rv_rm; - -typedef enum { - rv_fence_i = 8, - rv_fence_o = 4, - rv_fence_r = 2, - rv_fence_w = 1, -} rv_fence; - -typedef enum { - rv_ireg_zero, - rv_ireg_ra, - rv_ireg_sp, - rv_ireg_gp, - rv_ireg_tp, - rv_ireg_t0, - rv_ireg_t1, - rv_ireg_t2, - rv_ireg_s0, - rv_ireg_s1, - rv_ireg_a0, - rv_ireg_a1, - rv_ireg_a2, - rv_ireg_a3, - rv_ireg_a4, - rv_ireg_a5, - rv_ireg_a6, - rv_ireg_a7, - rv_ireg_s2, - rv_ireg_s3, - rv_ireg_s4, - rv_ireg_s5, - rv_ireg_s6, - rv_ireg_s7, - rv_ireg_s8, - rv_ireg_s9, - rv_ireg_s10, - rv_ireg_s11, - rv_ireg_t3, - rv_ireg_t4, - rv_ireg_t5, - rv_ireg_t6, -} rv_ireg; - -typedef enum { - rvc_end, - rvc_rd_eq_ra, - rvc_rd_eq_x0, - rvc_rs1_eq_x0, - rvc_rs2_eq_x0, - rvc_rs2_eq_rs1, - rvc_rs1_eq_ra, - rvc_imm_eq_zero, - rvc_imm_eq_n1, - rvc_imm_eq_p1, - rvc_csr_eq_0x001, - rvc_csr_eq_0x002, - rvc_csr_eq_0x003, - rvc_csr_eq_0xc00, - rvc_csr_eq_0xc01, - rvc_csr_eq_0xc02, - rvc_csr_eq_0xc80, - rvc_csr_eq_0xc81, - rvc_csr_eq_0xc82, -} rvc_constraint; - -typedef enum { - rv_codec_illegal, - rv_codec_none, - rv_codec_u, - rv_codec_uj, - rv_codec_i, - rv_codec_i_sh5, - rv_codec_i_sh6, - rv_codec_i_sh7, - rv_codec_i_csr, - rv_codec_s, - rv_codec_sb, - rv_codec_r, - rv_codec_r_m, - rv_codec_r4_m, - rv_codec_r_a, - rv_codec_r_l, - rv_codec_r_f, - rv_codec_cb, - rv_codec_cb_imm, - rv_codec_cb_sh5, - rv_codec_cb_sh6, - rv_codec_ci, - rv_codec_ci_sh5, - rv_codec_ci_sh6, - rv_codec_ci_16sp, - rv_codec_ci_lwsp, - rv_codec_ci_ldsp, - rv_codec_ci_lqsp, - rv_codec_ci_li, - rv_codec_ci_lui, - rv_codec_ci_none, - rv_codec_ciw_4spn, - rv_codec_cj, - rv_codec_cj_jal, - rv_codec_cl_lw, - rv_codec_cl_ld, - rv_codec_cl_lq, - rv_codec_cr, - rv_codec_cr_mv, - rv_codec_cr_jalr, - rv_codec_cr_jr, - rv_codec_cs, - rv_codec_cs_sw, - rv_codec_cs_sd, - rv_codec_cs_sq, - rv_codec_css_swsp, - rv_codec_css_sdsp, - rv_codec_css_sqsp, - rv_codec_k_bs, - rv_codec_k_rnum, - rv_codec_v_r, - rv_codec_v_ldst, - rv_codec_v_i, - rv_codec_vsetvli, - rv_codec_vsetivli, - rv_codec_zcb_ext, - rv_codec_zcb_mul, - rv_codec_zcb_lb, - rv_codec_zcb_lh, - rv_codec_zcmp_cm_pushpop, - rv_codec_zcmp_cm_mv, - rv_codec_zcmt_jt, -} rv_codec; +#include "disas/riscv.h" typedef enum { rv_op_illegal = 0, @@ -966,51 +816,6 @@ typedef enum { rv_op_czero_nez = 790, } rv_op; -/* structures */ - -typedef struct { - RISCVCPUConfig *cfg; - uint64_t pc; - uint64_t inst; - int32_t imm; - uint16_t op; - uint8_t codec; - uint8_t rd; - uint8_t rs1; - uint8_t rs2; - uint8_t rs3; - uint8_t rm; - uint8_t pred; - uint8_t succ; - uint8_t aq; - uint8_t rl; - uint8_t bs; - uint8_t rnum; - uint8_t vm; - uint32_t vzimm; - uint8_t rlist; -} rv_decode; - -typedef struct { - const int op; - const rvc_constraint *constraints; -} rv_comp_data; - -enum { - rvcd_imm_nz = 0x1 -}; - -typedef struct { - const char * const name; - const rv_codec codec; - const char * const format; - const rv_comp_data *pseudo; - const short decomp_rv32; - const short decomp_rv64; - const short decomp_rv128; - const short decomp_data; -} rv_opcode_data; - /* register names */ static const char rv_ireg_name_sym[32][5] = { @@ -1034,79 +839,6 @@ static const char rv_vreg_name_sym[32][4] = { "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31" }; -/* instruction formats */ - -#define rv_fmt_none "O\t" -#define rv_fmt_rs1 "O\t1" -#define rv_fmt_offset "O\to" -#define rv_fmt_pred_succ "O\tp,s" -#define rv_fmt_rs1_rs2 "O\t1,2" -#define rv_fmt_rd_imm "O\t0,i" -#define rv_fmt_rd_offset "O\t0,o" -#define rv_fmt_rd_rs1_rs2 "O\t0,1,2" -#define rv_fmt_frd_rs1 "O\t3,1" -#define rv_fmt_frd_frs1 "O\t3,4" -#define rv_fmt_rd_frs1 "O\t0,4" -#define rv_fmt_rd_frs1_frs2 "O\t0,4,5" -#define rv_fmt_frd_frs1_frs2 "O\t3,4,5" -#define rv_fmt_rm_frd_frs1 "O\tr,3,4" -#define rv_fmt_rm_frd_rs1 "O\tr,3,1" -#define rv_fmt_rm_rd_frs1 "O\tr,0,4" -#define rv_fmt_rm_frd_frs1_frs2 "O\tr,3,4,5" -#define rv_fmt_rm_frd_frs1_frs2_frs3 "O\tr,3,4,5,6" -#define rv_fmt_rd_rs1_imm "O\t0,1,i" -#define rv_fmt_rd_rs1_offset "O\t0,1,i" -#define rv_fmt_rd_offset_rs1 "O\t0,i(1)" -#define rv_fmt_frd_offset_rs1 "O\t3,i(1)" -#define rv_fmt_rd_csr_rs1 "O\t0,c,1" -#define rv_fmt_rd_csr_zimm "O\t0,c,7" -#define rv_fmt_rs2_offset_rs1 "O\t2,i(1)" -#define rv_fmt_frs2_offset_rs1 "O\t5,i(1)" -#define rv_fmt_rs1_rs2_offset "O\t1,2,o" -#define rv_fmt_rs2_rs1_offset "O\t2,1,o" -#define rv_fmt_aqrl_rd_rs2_rs1 "OAR\t0,2,(1)" -#define rv_fmt_aqrl_rd_rs1 "OAR\t0,(1)" -#define rv_fmt_rd "O\t0" -#define rv_fmt_rd_zimm "O\t0,7" -#define rv_fmt_rd_rs1 "O\t0,1" -#define rv_fmt_rd_rs2 "O\t0,2" -#define rv_fmt_rs1_offset "O\t1,o" -#define rv_fmt_rs2_offset "O\t2,o" -#define rv_fmt_rs1_rs2_bs "O\t1,2,b" -#define rv_fmt_rd_rs1_rnum "O\t0,1,n" -#define rv_fmt_ldst_vd_rs1_vm "O\tD,(1)m" -#define rv_fmt_ldst_vd_rs1_rs2_vm "O\tD,(1),2m" -#define rv_fmt_ldst_vd_rs1_vs2_vm "O\tD,(1),Fm" -#define rv_fmt_vd_vs2_vs1 "O\tD,F,E" -#define rv_fmt_vd_vs2_vs1_vl "O\tD,F,El" -#define rv_fmt_vd_vs2_vs1_vm "O\tD,F,Em" -#define rv_fmt_vd_vs2_rs1_vl "O\tD,F,1l" -#define rv_fmt_vd_vs2_fs1_vl "O\tD,F,4l" -#define rv_fmt_vd_vs2_rs1_vm "O\tD,F,1m" -#define rv_fmt_vd_vs2_fs1_vm "O\tD,F,4m" -#define rv_fmt_vd_vs2_imm_vl "O\tD,F,il" -#define rv_fmt_vd_vs2_imm_vm "O\tD,F,im" -#define rv_fmt_vd_vs2_uimm_vm "O\tD,F,um" -#define rv_fmt_vd_vs1_vs2_vm "O\tD,E,Fm" -#define rv_fmt_vd_rs1_vs2_vm "O\tD,1,Fm" -#define rv_fmt_vd_fs1_vs2_vm "O\tD,4,Fm" -#define rv_fmt_vd_vs1 "O\tD,E" -#define rv_fmt_vd_rs1 "O\tD,1" -#define rv_fmt_vd_fs1 "O\tD,4" -#define rv_fmt_vd_imm "O\tD,i" -#define rv_fmt_vd_vs2 "O\tD,F" -#define rv_fmt_vd_vs2_vm "O\tD,Fm" -#define rv_fmt_rd_vs2_vm "O\t0,Fm" -#define rv_fmt_rd_vs2 "O\t0,F" -#define rv_fmt_fd_vs2 "O\t3,F" -#define rv_fmt_vd_vm "O\tDm" -#define rv_fmt_vsetvli "O\t0,1,v" -#define rv_fmt_vsetivli "O\t0,u,v" -#define rv_fmt_rs1_rs2_zce_ldst "O\t2,i(1)" -#define rv_fmt_push_rlist "O\tx,-i" -#define rv_fmt_pop_rlist "O\tx,i" -#define rv_fmt_zcmt_index "O\ti" - /* pseudo-instruction constraints */ static const rvc_constraint rvcc_jal[] = { rvc_rd_eq_ra, rvc_end }; diff --git a/disas/riscv.h b/disas/riscv.h new file mode 100644 index 0000000000..9288255915 --- /dev/null +++ b/disas/riscv.h @@ -0,0 +1,282 @@ +/* + * QEMU disassembler -- RISC-V specific header. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DISAS_RISCV_H +#define DISAS_RISCV_H + +#include "qemu/osdep.h" +#include "target/riscv/cpu_cfg.h" + +/* types */ + +typedef uint64_t rv_inst; +typedef uint16_t rv_opcode; + +/* enums */ + +typedef enum { + rv32, + rv64, + rv128 +} rv_isa; + +typedef enum { + rv_rm_rne = 0, + rv_rm_rtz = 1, + rv_rm_rdn = 2, + rv_rm_rup = 3, + rv_rm_rmm = 4, + rv_rm_dyn = 7, +} rv_rm; + +typedef enum { + rv_fence_i = 8, + rv_fence_o = 4, + rv_fence_r = 2, + rv_fence_w = 1, +} rv_fence; + +typedef enum { + rv_ireg_zero, + rv_ireg_ra, + rv_ireg_sp, + rv_ireg_gp, + rv_ireg_tp, + rv_ireg_t0, + rv_ireg_t1, + rv_ireg_t2, + rv_ireg_s0, + rv_ireg_s1, + rv_ireg_a0, + rv_ireg_a1, + rv_ireg_a2, + rv_ireg_a3, + rv_ireg_a4, + rv_ireg_a5, + rv_ireg_a6, + rv_ireg_a7, + rv_ireg_s2, + rv_ireg_s3, + rv_ireg_s4, + rv_ireg_s5, + rv_ireg_s6, + rv_ireg_s7, + rv_ireg_s8, + rv_ireg_s9, + rv_ireg_s10, + rv_ireg_s11, + rv_ireg_t3, + rv_ireg_t4, + rv_ireg_t5, + rv_ireg_t6, +} rv_ireg; + +typedef enum { + rvc_end, + rvc_rd_eq_ra, + rvc_rd_eq_x0, + rvc_rs1_eq_x0, + rvc_rs2_eq_x0, + rvc_rs2_eq_rs1, + rvc_rs1_eq_ra, + rvc_imm_eq_zero, + rvc_imm_eq_n1, + rvc_imm_eq_p1, + rvc_csr_eq_0x001, + rvc_csr_eq_0x002, + rvc_csr_eq_0x003, + rvc_csr_eq_0xc00, + rvc_csr_eq_0xc01, + rvc_csr_eq_0xc02, + rvc_csr_eq_0xc80, + rvc_csr_eq_0xc81, + rvc_csr_eq_0xc82, +} rvc_constraint; + +typedef enum { + rv_codec_illegal, + rv_codec_none, + rv_codec_u, + rv_codec_uj, + rv_codec_i, + rv_codec_i_sh5, + rv_codec_i_sh6, + rv_codec_i_sh7, + rv_codec_i_csr, + rv_codec_s, + rv_codec_sb, + rv_codec_r, + rv_codec_r_m, + rv_codec_r4_m, + rv_codec_r_a, + rv_codec_r_l, + rv_codec_r_f, + rv_codec_cb, + rv_codec_cb_imm, + rv_codec_cb_sh5, + rv_codec_cb_sh6, + rv_codec_ci, + rv_codec_ci_sh5, + rv_codec_ci_sh6, + rv_codec_ci_16sp, + rv_codec_ci_lwsp, + rv_codec_ci_ldsp, + rv_codec_ci_lqsp, + rv_codec_ci_li, + rv_codec_ci_lui, + rv_codec_ci_none, + rv_codec_ciw_4spn, + rv_codec_cj, + rv_codec_cj_jal, + rv_codec_cl_lw, + rv_codec_cl_ld, + rv_codec_cl_lq, + rv_codec_cr, + rv_codec_cr_mv, + rv_codec_cr_jalr, + rv_codec_cr_jr, + rv_codec_cs, + rv_codec_cs_sw, + rv_codec_cs_sd, + rv_codec_cs_sq, + rv_codec_css_swsp, + rv_codec_css_sdsp, + rv_codec_css_sqsp, + rv_codec_k_bs, + rv_codec_k_rnum, + rv_codec_v_r, + rv_codec_v_ldst, + rv_codec_v_i, + rv_codec_vsetvli, + rv_codec_vsetivli, + rv_codec_zcb_ext, + rv_codec_zcb_mul, + rv_codec_zcb_lb, + rv_codec_zcb_lh, + rv_codec_zcmp_cm_pushpop, + rv_codec_zcmp_cm_mv, + rv_codec_zcmt_jt, +} rv_codec; + +/* structures */ + +typedef struct { + RISCVCPUConfig *cfg; + uint64_t pc; + uint64_t inst; + int32_t imm; + uint16_t op; + uint8_t codec; + uint8_t rd; + uint8_t rs1; + uint8_t rs2; + uint8_t rs3; + uint8_t rm; + uint8_t pred; + uint8_t succ; + uint8_t aq; + uint8_t rl; + uint8_t bs; + uint8_t rnum; + uint8_t vm; + uint32_t vzimm; + uint8_t rlist; +} rv_decode; + +typedef struct { + const int op; + const rvc_constraint *constraints; +} rv_comp_data; + +enum { + rvcd_imm_nz = 0x1 +}; + +typedef struct { + const char * const name; + const rv_codec codec; + const char * const format; + const rv_comp_data *pseudo; + const short decomp_rv32; + const short decomp_rv64; + const short decomp_rv128; + const short decomp_data; +} rv_opcode_data; + +/* instruction formats */ + +#define rv_fmt_none "O\t" +#define rv_fmt_rs1 "O\t1" +#define rv_fmt_offset "O\to" +#define rv_fmt_pred_succ "O\tp,s" +#define rv_fmt_rs1_rs2 "O\t1,2" +#define rv_fmt_rd_imm "O\t0,i" +#define rv_fmt_rd_offset "O\t0,o" +#define rv_fmt_rd_rs1_rs2 "O\t0,1,2" +#define rv_fmt_frd_rs1 "O\t3,1" +#define rv_fmt_frd_frs1 "O\t3,4" +#define rv_fmt_rd_frs1 "O\t0,4" +#define rv_fmt_rd_frs1_frs2 "O\t0,4,5" +#define rv_fmt_frd_frs1_frs2 "O\t3,4,5" +#define rv_fmt_rm_frd_frs1 "O\tr,3,4" +#define rv_fmt_rm_frd_rs1 "O\tr,3,1" +#define rv_fmt_rm_rd_frs1 "O\tr,0,4" +#define rv_fmt_rm_frd_frs1_frs2 "O\tr,3,4,5" +#define rv_fmt_rm_frd_frs1_frs2_frs3 "O\tr,3,4,5,6" +#define rv_fmt_rd_rs1_imm "O\t0,1,i" +#define rv_fmt_rd_rs1_offset "O\t0,1,i" +#define rv_fmt_rd_offset_rs1 "O\t0,i(1)" +#define rv_fmt_frd_offset_rs1 "O\t3,i(1)" +#define rv_fmt_rd_csr_rs1 "O\t0,c,1" +#define rv_fmt_rd_csr_zimm "O\t0,c,7" +#define rv_fmt_rs2_offset_rs1 "O\t2,i(1)" +#define rv_fmt_frs2_offset_rs1 "O\t5,i(1)" +#define rv_fmt_rs1_rs2_offset "O\t1,2,o" +#define rv_fmt_rs2_rs1_offset "O\t2,1,o" +#define rv_fmt_aqrl_rd_rs2_rs1 "OAR\t0,2,(1)" +#define rv_fmt_aqrl_rd_rs1 "OAR\t0,(1)" +#define rv_fmt_rd "O\t0" +#define rv_fmt_rd_zimm "O\t0,7" +#define rv_fmt_rd_rs1 "O\t0,1" +#define rv_fmt_rd_rs2 "O\t0,2" +#define rv_fmt_rs1_offset "O\t1,o" +#define rv_fmt_rs2_offset "O\t2,o" +#define rv_fmt_rs1_rs2_bs "O\t1,2,b" +#define rv_fmt_rd_rs1_rnum "O\t0,1,n" +#define rv_fmt_ldst_vd_rs1_vm "O\tD,(1)m" +#define rv_fmt_ldst_vd_rs1_rs2_vm "O\tD,(1),2m" +#define rv_fmt_ldst_vd_rs1_vs2_vm "O\tD,(1),Fm" +#define rv_fmt_vd_vs2_vs1 "O\tD,F,E" +#define rv_fmt_vd_vs2_vs1_vl "O\tD,F,El" +#define rv_fmt_vd_vs2_vs1_vm "O\tD,F,Em" +#define rv_fmt_vd_vs2_rs1_vl "O\tD,F,1l" +#define rv_fmt_vd_vs2_fs1_vl "O\tD,F,4l" +#define rv_fmt_vd_vs2_rs1_vm "O\tD,F,1m" +#define rv_fmt_vd_vs2_fs1_vm "O\tD,F,4m" +#define rv_fmt_vd_vs2_imm_vl "O\tD,F,il" +#define rv_fmt_vd_vs2_imm_vm "O\tD,F,im" +#define rv_fmt_vd_vs2_uimm_vm "O\tD,F,um" +#define rv_fmt_vd_vs1_vs2_vm "O\tD,E,Fm" +#define rv_fmt_vd_rs1_vs2_vm "O\tD,1,Fm" +#define rv_fmt_vd_fs1_vs2_vm "O\tD,4,Fm" +#define rv_fmt_vd_vs1 "O\tD,E" +#define rv_fmt_vd_rs1 "O\tD,1" +#define rv_fmt_vd_fs1 "O\tD,4" +#define rv_fmt_vd_imm "O\tD,i" +#define rv_fmt_vd_vs2 "O\tD,F" +#define rv_fmt_vd_vs2_vm "O\tD,Fm" +#define rv_fmt_rd_vs2_vm "O\t0,Fm" +#define rv_fmt_rd_vs2 "O\t0,F" +#define rv_fmt_fd_vs2 "O\t3,F" +#define rv_fmt_vd_vm "O\tDm" +#define rv_fmt_vsetvli "O\t0,1,v" +#define rv_fmt_vsetivli "O\t0,u,v" +#define rv_fmt_rs1_rs2_zce_ldst "O\t2,i(1)" +#define rv_fmt_push_rlist "O\tx,-i" +#define rv_fmt_pop_rlist "O\tx,i" +#define rv_fmt_zcmt_index "O\ti" + +#endif /* DISAS_RISCV_H */ From 01b1361f84d55a86be486323836a29488b52e3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20M=C3=BCllner?= Date: Mon, 12 Jun 2023 13:10:30 +0200 Subject: [PATCH 04/54] disas/riscv: Make rv_op_illegal a shared enum value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The enum value 'rv_op_illegal' does not represent an instruction, but is a catch-all value in case we have no match in the decoder. Let's make the value a shared one, so that other compile units can reuse it. Reviewed-by: Alistair Francis Reviewed-by: LIU Zhiwei Signed-off-by: Christoph Müllner Message-Id: <20230612111034.3955227-5-christoph.muellner@vrull.eu> Signed-off-by: Alistair Francis --- disas/riscv.c | 2 +- disas/riscv.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/disas/riscv.c b/disas/riscv.c index 4a55348267..b6ced2a26a 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -23,7 +23,7 @@ #include "disas/riscv.h" typedef enum { - rv_op_illegal = 0, + /* 0 is reserved for rv_op_illegal. */ rv_op_lui = 1, rv_op_auipc = 2, rv_op_jal = 3, diff --git a/disas/riscv.h b/disas/riscv.h index 9288255915..debbe69239 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -191,6 +191,10 @@ typedef struct { const rvc_constraint *constraints; } rv_comp_data; +enum { + rv_op_illegal = 0 +}; + enum { rvcd_imm_nz = 0x1 }; From fd7c64f6bd863df28081576a6f5c7a44dd65721f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20M=C3=BCllner?= Date: Mon, 12 Jun 2023 13:10:31 +0200 Subject: [PATCH 05/54] disas/riscv: Encapsulate opcode_data into decode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a reference to a struct rv_opcode_data object into struct rv_decode. This further allows to remove all references to the global variable opcode_data (which is renamed to rvi_opcode_data). This patch does not introduce any functional change, but prepares the code for more struct rv_opcode_data objects in the future. This patch is based on previous work from Liu Zhiwei: https://lists.nongnu.org/archive/html/qemu-devel/2022-08/msg03662.html Co-developed-by: LIU Zhiwei Reviewed-by: Alistair Francis Signed-off-by: Christoph Müllner Message-Id: <20230612111034.3955227-6-christoph.muellner@vrull.eu> Signed-off-by: Alistair Francis --- disas/riscv.c | 9 ++++++++- disas/riscv.h | 33 +++++++++++++++++---------------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index b6ced2a26a..b6789ab92a 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1068,7 +1068,7 @@ static const rv_comp_data rvcp_fsgnjx_q[] = { /* instruction metadata */ -const rv_opcode_data opcode_data[] = { +const rv_opcode_data rvi_opcode_data[] = { { "illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, { "lui", rv_codec_u, rv_fmt_rd_imm, NULL, 0, 0, 0 }, { "auipc", rv_codec_u, rv_fmt_rd_offset, NULL, 0, 0, 0 }, @@ -3889,6 +3889,7 @@ static uint32_t operand_tbl_index(rv_inst inst) static void decode_inst_operands(rv_decode *dec, rv_isa isa) { + const rv_opcode_data *opcode_data = dec->opcode_data; rv_inst inst = dec->inst; dec->codec = opcode_data[dec->op].codec; switch (dec->codec) { @@ -4371,6 +4372,7 @@ static void append(char *s1, const char *s2, size_t n) static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) { + const rv_opcode_data *opcode_data = dec->opcode_data; char tmp[64]; const char *fmt; @@ -4612,6 +4614,7 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) static void decode_inst_lift_pseudo(rv_decode *dec) { + const rv_opcode_data *opcode_data = dec->opcode_data; const rv_comp_data *comp_data = opcode_data[dec->op].pseudo; if (!comp_data) { return; @@ -4630,6 +4633,7 @@ static void decode_inst_lift_pseudo(rv_decode *dec) static void decode_inst_decompress_rv32(rv_decode *dec) { + const rv_opcode_data *opcode_data = dec->opcode_data; int decomp_op = opcode_data[dec->op].decomp_rv32; if (decomp_op != rv_op_illegal) { if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) @@ -4644,6 +4648,7 @@ static void decode_inst_decompress_rv32(rv_decode *dec) static void decode_inst_decompress_rv64(rv_decode *dec) { + const rv_opcode_data *opcode_data = dec->opcode_data; int decomp_op = opcode_data[dec->op].decomp_rv64; if (decomp_op != rv_op_illegal) { if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) @@ -4658,6 +4663,7 @@ static void decode_inst_decompress_rv64(rv_decode *dec) static void decode_inst_decompress_rv128(rv_decode *dec) { + const rv_opcode_data *opcode_data = dec->opcode_data; int decomp_op = opcode_data[dec->op].decomp_rv128; if (decomp_op != rv_op_illegal) { if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) @@ -4694,6 +4700,7 @@ disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst, rv_decode dec = { 0 }; dec.pc = pc; dec.inst = inst; + dec.opcode_data = rvi_opcode_data; dec.cfg = cfg; decode_inst_opcode(&dec, isa); decode_inst_operands(&dec, isa); diff --git a/disas/riscv.h b/disas/riscv.h index debbe69239..460196510c 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -163,10 +163,27 @@ typedef enum { /* structures */ +typedef struct { + const int op; + const rvc_constraint *constraints; +} rv_comp_data; + +typedef struct { + const char * const name; + const rv_codec codec; + const char * const format; + const rv_comp_data *pseudo; + const short decomp_rv32; + const short decomp_rv64; + const short decomp_rv128; + const short decomp_data; +} rv_opcode_data; + typedef struct { RISCVCPUConfig *cfg; uint64_t pc; uint64_t inst; + const rv_opcode_data *opcode_data; int32_t imm; uint16_t op; uint8_t codec; @@ -186,11 +203,6 @@ typedef struct { uint8_t rlist; } rv_decode; -typedef struct { - const int op; - const rvc_constraint *constraints; -} rv_comp_data; - enum { rv_op_illegal = 0 }; @@ -199,17 +211,6 @@ enum { rvcd_imm_nz = 0x1 }; -typedef struct { - const char * const name; - const rv_codec codec; - const char * const format; - const rv_comp_data *pseudo; - const short decomp_rv32; - const short decomp_rv64; - const short decomp_rv128; - const short decomp_data; -} rv_opcode_data; - /* instruction formats */ #define rv_fmt_none "O\t" From c859a2424dbbae8f5ea64c0f8445981402cd8552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20M=C3=BCllner?= Date: Mon, 12 Jun 2023 13:10:32 +0200 Subject: [PATCH 06/54] disas/riscv: Provide infrastructure for vendor extensions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A previous patch provides a pointer to the RISCVCPUConfig data. Let's use this to add the necessary code for vendor extensions. This patch does not change the current behaviour, but clearly defines how vendor extension support can be added to the disassembler. Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Signed-off-by: Christoph Müllner Message-Id: <20230612111034.3955227-7-christoph.muellner@vrull.eu> Signed-off-by: Alistair Francis --- disas/riscv.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index b6789ab92a..dc3bfb0123 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -4700,9 +4700,33 @@ disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst, rv_decode dec = { 0 }; dec.pc = pc; dec.inst = inst; - dec.opcode_data = rvi_opcode_data; dec.cfg = cfg; - decode_inst_opcode(&dec, isa); + + static const struct { + bool (*guard_func)(const RISCVCPUConfig *); + const rv_opcode_data *opcode_data; + void (*decode_func)(rv_decode *, rv_isa); + } decoders[] = { + { always_true_p, rvi_opcode_data, decode_inst_opcode }, + }; + + for (size_t i = 0; i < ARRAY_SIZE(decoders); i++) { + bool (*guard_func)(const RISCVCPUConfig *) = decoders[i].guard_func; + const rv_opcode_data *opcode_data = decoders[i].opcode_data; + void (*decode_func)(rv_decode *, rv_isa) = decoders[i].decode_func; + + if (guard_func(cfg)) { + dec.opcode_data = opcode_data; + decode_func(&dec, isa); + if (dec.op != rv_op_illegal) + break; + } + } + + if (dec.op == rv_op_illegal) { + dec.opcode_data = rvi_opcode_data; + } + decode_inst_operands(&dec, isa); decode_inst_decompress(&dec, isa); decode_inst_lift_pseudo(&dec); From f6f72338d80ec6f15a6b18643797bc10901aadf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20M=C3=BCllner?= Date: Mon, 12 Jun 2023 13:10:33 +0200 Subject: [PATCH 07/54] disas/riscv: Add support for XVentanaCondOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds XVentanaCondOps support to the RISC-V disassembler. Co-developed-by: LIU Zhiwei Acked-by: Alistair Francis Signed-off-by: Christoph Müllner Reviewed-by: Daniel Henrique Barboza Message-Id: <20230612111034.3955227-8-christoph.muellner@vrull.eu> Signed-off-by: Alistair Francis --- disas/meson.build | 5 ++++- disas/riscv-xventana.c | 41 +++++++++++++++++++++++++++++++++++++++++ disas/riscv-xventana.h | 18 ++++++++++++++++++ disas/riscv.c | 4 ++++ 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 disas/riscv-xventana.c create mode 100644 disas/riscv-xventana.h diff --git a/disas/meson.build b/disas/meson.build index 3a480eb9f8..f2b21e120f 100644 --- a/disas/meson.build +++ b/disas/meson.build @@ -6,7 +6,10 @@ common_ss.add(when: 'CONFIG_M68K_DIS', if_true: files('m68k.c')) common_ss.add(when: 'CONFIG_MICROBLAZE_DIS', if_true: files('microblaze.c')) common_ss.add(when: 'CONFIG_MIPS_DIS', if_true: files('mips.c', 'nanomips.c')) common_ss.add(when: 'CONFIG_NIOS2_DIS', if_true: files('nios2.c')) -common_ss.add(when: 'CONFIG_RISCV_DIS', if_true: files('riscv.c')) +common_ss.add(when: 'CONFIG_RISCV_DIS', if_true: files( + 'riscv.c', + 'riscv-xventana.c' +)) common_ss.add(when: 'CONFIG_SH4_DIS', if_true: files('sh4.c')) common_ss.add(when: 'CONFIG_SPARC_DIS', if_true: files('sparc.c')) common_ss.add(when: 'CONFIG_XTENSA_DIS', if_true: files('xtensa.c')) diff --git a/disas/riscv-xventana.c b/disas/riscv-xventana.c new file mode 100644 index 0000000000..a0224d1fb3 --- /dev/null +++ b/disas/riscv-xventana.c @@ -0,0 +1,41 @@ +/* + * QEMU RISC-V Disassembler for xventana. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "disas/riscv.h" +#include "disas/riscv-xventana.h" + +typedef enum { + /* 0 is reserved for rv_op_illegal. */ + ventana_op_vt_maskc = 1, + ventana_op_vt_maskcn = 2, +} rv_ventana_op; + +const rv_opcode_data ventana_opcode_data[] = { + { "vt.illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, + { "vt.maskc", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "vt.maskcn", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, +}; + +void decode_xventanacondops(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 30: + switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { + case 6: op = ventana_op_vt_maskc; break; + case 7: op = ventana_op_vt_maskcn; break; + } + break; + } + break; + } + + dec->op = op; +} diff --git a/disas/riscv-xventana.h b/disas/riscv-xventana.h new file mode 100644 index 0000000000..72be9ffa16 --- /dev/null +++ b/disas/riscv-xventana.h @@ -0,0 +1,18 @@ +/* + * QEMU disassembler -- RISC-V specific header (xventana*). + * + * Copyright (c) 2023 VRULL GmbH + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DISAS_RISCV_XVENTANA_H +#define DISAS_RISCV_XVENTANA_H + +#include "disas/riscv.h" + +extern const rv_opcode_data ventana_opcode_data[]; + +void decode_xventanacondops(rv_decode*, rv_isa); + +#endif /* DISAS_RISCV_XVENTANA_H */ diff --git a/disas/riscv.c b/disas/riscv.c index dc3bfb0123..c7bfd4ed32 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -22,6 +22,9 @@ #include "target/riscv/cpu_cfg.h" #include "disas/riscv.h" +/* Vendor extensions */ +#include "disas/riscv-xventana.h" + typedef enum { /* 0 is reserved for rv_op_illegal. */ rv_op_lui = 1, @@ -4708,6 +4711,7 @@ disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst, void (*decode_func)(rv_decode *, rv_isa); } decoders[] = { { always_true_p, rvi_opcode_data, decode_inst_opcode }, + { has_XVentanaCondOps_p, ventana_opcode_data, decode_xventanacondops }, }; for (size_t i = 0; i < ARRAY_SIZE(decoders); i++) { From 318df7238b9f842af96aad01ec183012c8fecab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20M=C3=BCllner?= Date: Mon, 12 Jun 2023 13:10:34 +0200 Subject: [PATCH 08/54] disas/riscv: Add support for XThead* instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support for emulating XThead* instruction has been added recently. This patch adds support for these instructions to the RISC-V disassembler. Co-developed-by: LIU Zhiwei Acked-by: Alistair Francis Signed-off-by: Christoph Müllner Message-Id: <20230612111034.3955227-9-christoph.muellner@vrull.eu> Signed-off-by: Alistair Francis --- disas/meson.build | 1 + disas/riscv-xthead.c | 707 +++++++++++++++++++++++++++++++++++++++++ disas/riscv-xthead.h | 28 ++ disas/riscv.c | 69 ++++ disas/riscv.h | 12 + target/riscv/cpu_cfg.h | 11 + 6 files changed, 828 insertions(+) create mode 100644 disas/riscv-xthead.c create mode 100644 disas/riscv-xthead.h diff --git a/disas/meson.build b/disas/meson.build index f2b21e120f..815523ab85 100644 --- a/disas/meson.build +++ b/disas/meson.build @@ -8,6 +8,7 @@ common_ss.add(when: 'CONFIG_MIPS_DIS', if_true: files('mips.c', 'nanomips.c')) common_ss.add(when: 'CONFIG_NIOS2_DIS', if_true: files('nios2.c')) common_ss.add(when: 'CONFIG_RISCV_DIS', if_true: files( 'riscv.c', + 'riscv-xthead.c', 'riscv-xventana.c' )) common_ss.add(when: 'CONFIG_SH4_DIS', if_true: files('sh4.c')) diff --git a/disas/riscv-xthead.c b/disas/riscv-xthead.c new file mode 100644 index 0000000000..99da679d16 --- /dev/null +++ b/disas/riscv-xthead.c @@ -0,0 +1,707 @@ +/* + * QEMU RISC-V Disassembler for xthead. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "disas/riscv.h" +#include "disas/riscv-xthead.h" + +typedef enum { + /* 0 is reserved for rv_op_illegal. */ + /* XTheadBa */ + rv_op_th_addsl = 1, + /* XTheadBb */ + rv_op_th_srri, + rv_op_th_srriw, + rv_op_th_ext, + rv_op_th_extu, + rv_op_th_ff0, + rv_op_th_ff1, + rv_op_th_rev, + rv_op_th_revw, + rv_op_th_tstnbz, + /* XTheadBs */ + rv_op_th_tst, + /* XTheadCmo */ + rv_op_th_dcache_call, + rv_op_th_dcache_ciall, + rv_op_th_dcache_iall, + rv_op_th_dcache_cpa, + rv_op_th_dcache_cipa, + rv_op_th_dcache_ipa, + rv_op_th_dcache_cva, + rv_op_th_dcache_civa, + rv_op_th_dcache_iva, + rv_op_th_dcache_csw, + rv_op_th_dcache_cisw, + rv_op_th_dcache_isw, + rv_op_th_dcache_cpal1, + rv_op_th_dcache_cval1, + rv_op_th_icache_iall, + rv_op_th_icache_ialls, + rv_op_th_icache_ipa, + rv_op_th_icache_iva, + rv_op_th_l2cache_call, + rv_op_th_l2cache_ciall, + rv_op_th_l2cache_iall, + /* XTheadCondMov */ + rv_op_th_mveqz, + rv_op_th_mvnez, + /* XTheadFMemIdx */ + rv_op_th_flrd, + rv_op_th_flrw, + rv_op_th_flurd, + rv_op_th_flurw, + rv_op_th_fsrd, + rv_op_th_fsrw, + rv_op_th_fsurd, + rv_op_th_fsurw, + /* XTheadFmv */ + rv_op_th_fmv_hw_x, + rv_op_th_fmv_x_hw, + /* XTheadMac */ + rv_op_th_mula, + rv_op_th_mulah, + rv_op_th_mulaw, + rv_op_th_muls, + rv_op_th_mulsw, + rv_op_th_mulsh, + /* XTheadMemIdx */ + rv_op_th_lbia, + rv_op_th_lbib, + rv_op_th_lbuia, + rv_op_th_lbuib, + rv_op_th_lhia, + rv_op_th_lhib, + rv_op_th_lhuia, + rv_op_th_lhuib, + rv_op_th_lwia, + rv_op_th_lwib, + rv_op_th_lwuia, + rv_op_th_lwuib, + rv_op_th_ldia, + rv_op_th_ldib, + rv_op_th_sbia, + rv_op_th_sbib, + rv_op_th_shia, + rv_op_th_shib, + rv_op_th_swia, + rv_op_th_swib, + rv_op_th_sdia, + rv_op_th_sdib, + rv_op_th_lrb, + rv_op_th_lrbu, + rv_op_th_lrh, + rv_op_th_lrhu, + rv_op_th_lrw, + rv_op_th_lrwu, + rv_op_th_lrd, + rv_op_th_srb, + rv_op_th_srh, + rv_op_th_srw, + rv_op_th_srd, + rv_op_th_lurb, + rv_op_th_lurbu, + rv_op_th_lurh, + rv_op_th_lurhu, + rv_op_th_lurw, + rv_op_th_lurwu, + rv_op_th_lurd, + rv_op_th_surb, + rv_op_th_surh, + rv_op_th_surw, + rv_op_th_surd, + /* XTheadMemPair */ + rv_op_th_ldd, + rv_op_th_lwd, + rv_op_th_lwud, + rv_op_th_sdd, + rv_op_th_swd, + /* XTheadSync */ + rv_op_th_sfence_vmas, + rv_op_th_sync, + rv_op_th_sync_i, + rv_op_th_sync_is, + rv_op_th_sync_s, +} rv_xthead_op; + +const rv_opcode_data xthead_opcode_data[] = { + { "th.illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, + /* XTheadBa */ + { "th.addsl", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + /* XTheadBb */ + { "th.srri", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "th.srriw", rv_codec_r2_imm5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "th.ext", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 }, + { "th.extu", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 }, + { "th.ff0", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "th.ff1", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "th.rev", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "th.revw", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "th.tstnbz", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + /* XTheadBs */ + { "th.tst", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + /* XTheadCmo */ + { "th.dcache.call", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.dcache.ciall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.dcache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.dcache.cpa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.cipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.ipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.cva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.civa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.iva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.csw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.cisw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.isw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.cpal1", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.cval1", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.icache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.icache.ialls", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.icache.ipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.icache.iva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.l2cache.call", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.l2cache.ciall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.l2cache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + /* XTheadCondMov */ + { "th.mveqz", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "th.mvnez", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + /* XTheadFMemIdx */ + { "th.flrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.flrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.flurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.flurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.fsrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.fsrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.fsurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.fsurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, + /* XTheadFmv */ + { "th.fmv.hw.x", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, + { "th.fmv.x.hw", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, + /* XTheadMac */ + { "th.mula", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "th.mulaw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "th.mulah", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "th.muls", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "th.mulsw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "th.mulsh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + /* XTheadMemIdx */ + { "th.lbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 }, + { "th.lbuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lbuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lhia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lhib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lhuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lhuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lwia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lwib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lwuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lwuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.ldia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.ldib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.sbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.sbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.shia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.shib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.swia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.swib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.sdia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.sdib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lrb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lrbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lrh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lrhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lrw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lrwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lrd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.srb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.srh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.srw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.srd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lurb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lurbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lurh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lurhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lurw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lurwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lurd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.surb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.surh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.surw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.surd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + /* XTheadMemPair */ + { "th.ldd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, + { "th.lwd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, + { "th.lwud", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, + { "th.sdd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, + { "th.swd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, + /* XTheadSync */ + { "th.sfence.vmas", rv_codec_r, rv_fmt_rs1_rs2, NULL, 0, 0, 0 }, + { "th.sync", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.sync.i", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.sync.is", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.sync.s", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, +}; + +void decode_xtheadba(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 1: + switch ((inst >> 25) & 0b1111111) { + case 0b0000000: + case 0b0000001: + case 0b0000010: + case 0b0000011: op = rv_op_th_addsl; break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadbb(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 1: + switch ((inst >> 25) & 0b1111111) { + case 0b0001010: op = rv_op_th_srriw; break; + case 0b1000000: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_tstnbz; + } + break; + case 0b1000001: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_rev; + } + break; + case 0b1000010: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_ff0; + } + break; + case 0b1000011: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_ff1; + } + break; + case 0b1000100: + case 0b1001000: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_revw; + } + break; + case 0b0000100: + case 0b0000101: op = rv_op_th_srri; break; + } + break; + case 2: op = rv_op_th_ext; break; + case 3: op = rv_op_th_extu; break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadbs(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 1: + switch ((inst >> 26) & 0b111111) { + case 0b100010: op = rv_op_th_tst; break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadcmo(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 0: + switch ((inst >> 20 & 0b111111111111)) { + case 0b000000000001: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_dcache_call; + } + break; + case 0b000000000011: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_dcache_ciall; + } + break; + case 0b000000000010: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_dcache_iall; + } + break; + case 0b000000101001: op = rv_op_th_dcache_cpa; break; + case 0b000000101011: op = rv_op_th_dcache_cipa; break; + case 0b000000101010: op = rv_op_th_dcache_ipa; break; + case 0b000000100101: op = rv_op_th_dcache_cva; break; + case 0b000000100111: op = rv_op_th_dcache_civa; break; + case 0b000000100110: op = rv_op_th_dcache_iva; break; + case 0b000000100001: op = rv_op_th_dcache_csw; break; + case 0b000000100011: op = rv_op_th_dcache_cisw; break; + case 0b000000100010: op = rv_op_th_dcache_isw; break; + case 0b000000101000: op = rv_op_th_dcache_cpal1; break; + case 0b000000100100: op = rv_op_th_dcache_cval1; break; + case 0b000000010000: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_icache_iall; + } + break; + case 0b000000010001: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_icache_ialls; + } + break; + case 0b000000111000: op = rv_op_th_icache_ipa; break; + case 0b000000110000: op = rv_op_th_icache_iva; break; + case 0b000000010101: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_l2cache_call; + } + break; + case 0b000000010111: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_l2cache_ciall; + } + break; + case 0b000000010110: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_l2cache_iall; + } + break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadcondmov(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 1: + switch ((inst >> 25) & 0b1111111) { + case 0b0100000: op = rv_op_th_mveqz; break; + case 0b0100001: op = rv_op_th_mvnez; break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadfmemidx(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 6: + switch ((inst >> 27) & 0b11111) { + case 8: op = rv_op_th_flrw; break; + case 10: op = rv_op_th_flurw; break; + case 12: op = rv_op_th_flrd; break; + case 14: op = rv_op_th_flurd; break; + } + break; + case 7: + switch ((inst >> 27) & 0b11111) { + case 8: op = rv_op_th_fsrw; break; + case 10: op = rv_op_th_fsurw; break; + case 12: op = rv_op_th_fsrd; break; + case 14: op = rv_op_th_fsurd; break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadfmv(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 1: + switch ((inst >> 25) & 0b1111111) { + case 0b1010000: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_fmv_hw_x; + } + break; + case 0b1100000: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_fmv_x_hw; + } + break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadmac(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 1: + switch ((inst >> 25) & 0b1111111) { + case 0b0010000: op = rv_op_th_mula; break; + case 0b0010001: op = rv_op_th_muls; break; + case 0b0010010: op = rv_op_th_mulaw; break; + case 0b0010011: op = rv_op_th_mulsw; break; + case 0b0010100: op = rv_op_th_mulah; break; + case 0b0010101: op = rv_op_th_mulsh; break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadmemidx(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 4: + switch ((inst >> 27) & 0b11111) { + case 0: op = rv_op_th_lrb; break; + case 1: op = rv_op_th_lbib; break; + case 2: op = rv_op_th_lurb; break; + case 3: op = rv_op_th_lbia; break; + case 4: op = rv_op_th_lrh; break; + case 5: op = rv_op_th_lhib; break; + case 6: op = rv_op_th_lurh; break; + case 7: op = rv_op_th_lhia; break; + case 8: op = rv_op_th_lrw; break; + case 9: op = rv_op_th_lwib; break; + case 10: op = rv_op_th_lurw; break; + case 11: op = rv_op_th_lwia; break; + case 12: op = rv_op_th_lrd; break; + case 13: op = rv_op_th_ldib; break; + case 14: op = rv_op_th_lurd; break; + case 15: op = rv_op_th_ldia; break; + case 16: op = rv_op_th_lrbu; break; + case 17: op = rv_op_th_lbuib; break; + case 18: op = rv_op_th_lurbu; break; + case 19: op = rv_op_th_lbuia; break; + case 20: op = rv_op_th_lrhu; break; + case 21: op = rv_op_th_lhuib; break; + case 22: op = rv_op_th_lurhu; break; + case 23: op = rv_op_th_lhuia; break; + case 24: op = rv_op_th_lrwu; break; + case 25: op = rv_op_th_lwuib; break; + case 26: op = rv_op_th_lurwu; break; + case 27: op = rv_op_th_lwuia; break; + } + break; + case 5: + switch ((inst >> 27) & 0b11111) { + case 0: op = rv_op_th_srb; break; + case 1: op = rv_op_th_sbib; break; + case 2: op = rv_op_th_surb; break; + case 3: op = rv_op_th_sbia; break; + case 4: op = rv_op_th_srh; break; + case 5: op = rv_op_th_shib; break; + case 6: op = rv_op_th_surh; break; + case 7: op = rv_op_th_shia; break; + case 8: op = rv_op_th_srw; break; + case 9: op = rv_op_th_swib; break; + case 10: op = rv_op_th_surw; break; + case 11: op = rv_op_th_swia; break; + case 12: op = rv_op_th_srd; break; + case 13: op = rv_op_th_sdib; break; + case 14: op = rv_op_th_surd; break; + case 15: op = rv_op_th_sdia; break; + } + break; + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadmempair(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 4: + switch ((inst >> 27) & 0b11111) { + case 28: op = rv_op_th_lwd; break; + case 30: op = rv_op_th_lwud; break; + case 31: op = rv_op_th_ldd; break; + } + break; + case 5: + switch ((inst >> 27) & 0b11111) { + case 28: op = rv_op_th_swd; break; + case 31: op = rv_op_th_sdd; break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadsync(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 0: + switch ((inst >> 25) & 0b1111111) { + case 0b0000010: op = rv_op_th_sfence_vmas; break; + case 0b0000000: + switch ((inst >> 20) & 0b11111) { + case 0b11000: op = rv_op_th_sync; break; + case 0b11010: op = rv_op_th_sync_i; break; + case 0b11011: op = rv_op_th_sync_is; break; + case 0b11001: op = rv_op_th_sync_s; break; + } + break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} diff --git a/disas/riscv-xthead.h b/disas/riscv-xthead.h new file mode 100644 index 0000000000..fcd42746e7 --- /dev/null +++ b/disas/riscv-xthead.h @@ -0,0 +1,28 @@ +/* + * QEMU disassembler -- RISC-V specific header (xthead*). + * + * Copyright (c) 2023 VRULL GmbH + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DISAS_RISCV_XTHEAD_H +#define DISAS_RISCV_XTHEAD_H + +#include "disas/riscv.h" + +extern const rv_opcode_data xthead_opcode_data[]; + +void decode_xtheadba(rv_decode *, rv_isa); +void decode_xtheadbb(rv_decode *, rv_isa); +void decode_xtheadbs(rv_decode *, rv_isa); +void decode_xtheadcmo(rv_decode *, rv_isa); +void decode_xtheadcondmov(rv_decode *, rv_isa); +void decode_xtheadfmemidx(rv_decode *, rv_isa); +void decode_xtheadfmv(rv_decode *, rv_isa); +void decode_xtheadmac(rv_decode *, rv_isa); +void decode_xtheadmemidx(rv_decode *, rv_isa); +void decode_xtheadmempair(rv_decode *, rv_isa); +void decode_xtheadsync(rv_decode *, rv_isa); + +#endif /* DISAS_RISCV_XTHEAD_H */ diff --git a/disas/riscv.c b/disas/riscv.c index c7bfd4ed32..94e568a7e9 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -18,11 +18,13 @@ */ #include "qemu/osdep.h" +#include "qemu/bitops.h" #include "disas/dis-asm.h" #include "target/riscv/cpu_cfg.h" #include "disas/riscv.h" /* Vendor extensions */ +#include "disas/riscv-xthead.h" #include "disas/riscv-xventana.h" typedef enum { @@ -3869,6 +3871,26 @@ static uint32_t operand_zcmp_rlist(rv_inst inst) return ((inst << 56) >> 60); } +static uint32_t operand_imm6(rv_inst inst) +{ + return (inst << 38) >> 60; +} + +static uint32_t operand_imm2(rv_inst inst) +{ + return (inst << 37) >> 62; +} + +static uint32_t operand_immh(rv_inst inst) +{ + return (inst << 32) >> 58; +} + +static uint32_t operand_imml(rv_inst inst) +{ + return (inst << 38) >> 58; +} + static uint32_t calculate_stack_adj(rv_isa isa, uint32_t rlist, uint32_t spimm) { int xlen_bytes_log2 = isa == rv64 ? 3 : 2; @@ -4233,6 +4255,38 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa) case rv_codec_zcmt_jt: dec->imm = operand_tbl_index(inst); break; + case rv_codec_r2_imm5: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->imm = operand_rs2(inst); + break; + case rv_codec_r2: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + break; + case rv_codec_r2_imm6: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->imm = operand_imm6(inst); + break; + case rv_codec_r_imm2: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->rs2 = operand_rs2(inst); + dec->imm = operand_imm2(inst); + break; + case rv_codec_r2_immhl: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->imm = operand_immh(inst); + dec->imm1 = operand_imml(inst); + break; + case rv_codec_r2_imm2_imm5: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->imm = sextract32(operand_rs2(inst), 0, 5); + dec->imm1 = operand_imm2(inst); + break; }; } @@ -4446,6 +4500,10 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) snprintf(tmp, sizeof(tmp), "%u", ((uint32_t)dec->imm & 0b11111)); append(buf, tmp, buflen); break; + case 'j': + snprintf(tmp, sizeof(tmp), "%d", dec->imm1); + append(buf, tmp, buflen); + break; case 'o': snprintf(tmp, sizeof(tmp), "%d", dec->imm); append(buf, tmp, buflen); @@ -4711,6 +4769,17 @@ disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst, void (*decode_func)(rv_decode *, rv_isa); } decoders[] = { { always_true_p, rvi_opcode_data, decode_inst_opcode }, + { has_xtheadba_p, xthead_opcode_data, decode_xtheadba }, + { has_xtheadbb_p, xthead_opcode_data, decode_xtheadbb }, + { has_xtheadbs_p, xthead_opcode_data, decode_xtheadbs }, + { has_xtheadcmo_p, xthead_opcode_data, decode_xtheadcmo }, + { has_xtheadcondmov_p, xthead_opcode_data, decode_xtheadcondmov }, + { has_xtheadfmemidx_p, xthead_opcode_data, decode_xtheadfmemidx }, + { has_xtheadfmv_p, xthead_opcode_data, decode_xtheadfmv }, + { has_xtheadmac_p, xthead_opcode_data, decode_xtheadmac }, + { has_xtheadmemidx_p, xthead_opcode_data, decode_xtheadmemidx }, + { has_xtheadmempair_p, xthead_opcode_data, decode_xtheadmempair }, + { has_xtheadsync_p, xthead_opcode_data, decode_xtheadsync }, { has_XVentanaCondOps_p, ventana_opcode_data, decode_xventanacondops }, }; diff --git a/disas/riscv.h b/disas/riscv.h index 460196510c..052a0c4281 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -159,6 +159,12 @@ typedef enum { rv_codec_zcmp_cm_pushpop, rv_codec_zcmp_cm_mv, rv_codec_zcmt_jt, + rv_codec_r2_imm5, + rv_codec_r2, + rv_codec_r2_imm6, + rv_codec_r_imm2, + rv_codec_r2_immhl, + rv_codec_r2_imm2_imm5, } rv_codec; /* structures */ @@ -185,6 +191,7 @@ typedef struct { uint64_t inst; const rv_opcode_data *opcode_data; int32_t imm; + int32_t imm1; uint16_t op; uint8_t codec; uint8_t rd; @@ -283,5 +290,10 @@ enum { #define rv_fmt_push_rlist "O\tx,-i" #define rv_fmt_pop_rlist "O\tx,i" #define rv_fmt_zcmt_index "O\ti" +#define rv_fmt_rd_rs1_rs2_imm "O\t0,1,2,i" +#define rv_fmt_frd_rs1_rs2_imm "O\t3,1,2,i" +#define rv_fmt_rd_rs1_immh_imml "O\t0,1,i,j" +#define rv_fmt_rd_rs1_immh_imml_addr "O\t0,(1),i,j" +#define rv_fmt_rd2_imm "O\t0,2,(1),i" #endif /* DISAS_RISCV_H */ diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 0b4fe4b540..6b7e736bc2 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -157,6 +157,17 @@ static inline bool has_xthead_p(const RISCVCPUConfig *cfg) return cfg->ext_ ## ext ; \ } +MATERIALISE_EXT_PREDICATE(xtheadba) +MATERIALISE_EXT_PREDICATE(xtheadbb) +MATERIALISE_EXT_PREDICATE(xtheadbs) +MATERIALISE_EXT_PREDICATE(xtheadcmo) +MATERIALISE_EXT_PREDICATE(xtheadcondmov) +MATERIALISE_EXT_PREDICATE(xtheadfmemidx) +MATERIALISE_EXT_PREDICATE(xtheadfmv) +MATERIALISE_EXT_PREDICATE(xtheadmac) +MATERIALISE_EXT_PREDICATE(xtheadmemidx) +MATERIALISE_EXT_PREDICATE(xtheadmempair) +MATERIALISE_EXT_PREDICATE(xtheadsync) MATERIALISE_EXT_PREDICATE(XVentanaCondOps) #endif From 869d76f2073f6ed28e26bb264d4c2c8841f06496 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Sat, 3 Jun 2023 21:42:34 +0800 Subject: [PATCH 09/54] target/riscv: Make MPV only work when MPP != PRV_M Upon MRET or explicit memory access with MPRV=1, MPV should be ignored when MPP=PRV_M. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-Id: <20230603134236.15719-2-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 3 ++- target/riscv/op_helper.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index a944f25694..8e3c73da52 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -47,7 +47,8 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch) if (mode == PRV_M && get_field(status, MSTATUS_MPRV)) { mode = get_field(env->mstatus, MSTATUS_MPP); - virt = get_field(env->mstatus, MSTATUS_MPV); + virt = get_field(env->mstatus, MSTATUS_MPV) && + (mode != PRV_M); if (virt) { status = env->vsstatus; } diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index f563dc3981..9cdb9cdd06 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -335,7 +335,8 @@ target_ulong helper_mret(CPURISCVState *env) riscv_raise_exception(env, RISCV_EXCP_INST_ACCESS_FAULT, GETPC()); } - target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV); + target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV) && + (prev_priv != PRV_M); mstatus = set_field(mstatus, MSTATUS_MIE, get_field(mstatus, MSTATUS_MPIE)); mstatus = set_field(mstatus, MSTATUS_MPIE, 1); From 03dd405dd5da6e0df3023bf9394ad0490b8369ab Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Sat, 3 Jun 2023 21:42:35 +0800 Subject: [PATCH 10/54] target/riscv: Support MSTATUS.MPV/GVA only when RVH is enabled MPV and GVA bits are added by hypervisor extension to mstatus and mstatush (if MXLEN=32). Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-Id: <20230603134236.15719-3-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 58499b5afc..6ac11d1f11 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -1311,11 +1311,9 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, } if (xl != MXL_RV32 || env->debugger) { - /* - * RV32: MPV and GVA are not in mstatus. The current plan is to - * add them to mstatush. For now, we just don't support it. - */ - mask |= MSTATUS_MPV | MSTATUS_GVA; + if (riscv_has_ext(env, RVH)) { + mask |= MSTATUS_MPV | MSTATUS_GVA; + } if ((val & MSTATUS64_UXL) != 0) { mask |= MSTATUS64_UXL; } @@ -1351,7 +1349,7 @@ static RISCVException write_mstatush(CPURISCVState *env, int csrno, target_ulong val) { uint64_t valh = (uint64_t)val << 32; - uint64_t mask = MSTATUS_MPV | MSTATUS_GVA; + uint64_t mask = riscv_has_ext(env, RVH) ? MSTATUS_MPV | MSTATUS_GVA : 0; env->mstatus = (env->mstatus & ~mask) | (valh & mask); From 98fbdaaed50781e18d9a612590d1d47106de0aca Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Sat, 3 Jun 2023 21:42:36 +0800 Subject: [PATCH 11/54] target/riscv: Remove redundant assignment to SXL SXL is initialized as env->misa_mxl which is also the mxl value. So we can just remain it unchanged to keep it read-only. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-Id: <20230603134236.15719-4-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 6ac11d1f11..25345f3153 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -1321,10 +1321,6 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, mstatus = (mstatus & ~mask) | (val & mask); - if (xl > MXL_RV32) { - /* SXL field is for now read only */ - mstatus = set_field(mstatus, MSTATUS64_SXL, xl); - } env->mstatus = mstatus; /* From 029f5feed6e5b7173788b7414af3e8aa32746344 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 20 Jun 2023 12:24:43 -0300 Subject: [PATCH 12/54] target/riscv/cpu.c: fix veyron-v1 CPU properties Commit 7f0bdfb5bfc2 ("target/riscv/cpu.c: remove cfg setup from riscv_cpu_init()") removed code that was enabling mmu, pmp, ext_ifencei and ext_icsr from riscv_cpu_init(), the init() function of TYPE_RISCV_CPU, parent type of all RISC-V CPUss. This was done to force CPUs to explictly enable all extensions and features it requires, without any 'magic values' that were inherited by the parent type. This commit failed to make appropriate changes in the 'veyron-v1' CPU, added earlier by commit e1d084a8524a. The result is that the veyron-v1 CPU has ext_ifencei, ext_icsr and pmp set to 'false', which is not the case. The reason why it took this long to notice (thanks LIU Zhiwei for reporting it) is because Linux doesn't mind 'ifencei' and 'icsr' being absent in the 'riscv,isa' DT, implying that they're both present if the 'i' extension is enabled. OpenSBI also doesn't error out or warns about the lack of 'pmp', it'll just not protect memory pages. Fix it by setting them to 'true' in rv64_veyron_v1_cpu_init() like 7f0bdfb5bfc2 already did with other CPUs. Reported-by: LIU Zhiwei Fixes: 7f0bdfb5bfc2 ("target/riscv/cpu.c: remove cfg setup from riscv_cpu_init()") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: LIU Zhiwei Message-Id: <20230620152443.137079-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 3faf703131..b9b3879281 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -444,6 +444,9 @@ static void rv64_veyron_v1_cpu_init(Object *obj) /* Enable ISA extensions */ cpu->cfg.mmu = true; + cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_icsr = true; + cpu->cfg.pmp = true; cpu->cfg.ext_icbom = true; cpu->cfg.cbom_blocksize = 64; cpu->cfg.cboz_blocksize = 64; From 3a610f5430f61bd67f0ac78ef40c8d3bd2b4f8ee Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 14 Jun 2023 11:25:46 +0800 Subject: [PATCH 13/54] target/riscv: Add additional xlen for address when MPRV=1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As specified in privilege spec:"When MPRV=1, load and store memory addresses are treated as though the current XLEN were set to MPP’s XLEN". So the xlen for address may be different from current xlen. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Daniel Henrique Barboza Message-Id: <20230614032547.35895-2-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 49 +++++++++++++++++++++++++++++++++------ target/riscv/cpu_helper.c | 1 + target/riscv/translate.c | 13 ++++++++++- 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 7adb8706ac..3081603464 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -500,6 +500,7 @@ FIELD(TB_FLAGS, ITRIGGER, 22, 1) /* Virtual mode enabled */ FIELD(TB_FLAGS, VIRT_ENABLED, 23, 1) FIELD(TB_FLAGS, PRIV, 24, 2) +FIELD(TB_FLAGS, AXL, 26, 2) #ifdef TARGET_RISCV32 #define riscv_cpu_mxl(env) ((void)(env), MXL_RV32) @@ -516,13 +517,20 @@ static inline const RISCVCPUConfig *riscv_cpu_cfg(CPURISCVState *env) return &env_archcpu(env)->cfg; } -#if defined(TARGET_RISCV32) -#define cpu_recompute_xl(env) ((void)(env), MXL_RV32) -#else -static inline RISCVMXL cpu_recompute_xl(CPURISCVState *env) +#if !defined(CONFIG_USER_ONLY) +static inline int cpu_address_mode(CPURISCVState *env) +{ + int mode = env->priv; + + if (mode == PRV_M && get_field(env->mstatus, MSTATUS_MPRV)) { + mode = get_field(env->mstatus, MSTATUS_MPP); + } + return mode; +} + +static inline RISCVMXL cpu_get_xl(CPURISCVState *env, target_ulong mode) { RISCVMXL xl = env->misa_mxl; -#if !defined(CONFIG_USER_ONLY) /* * When emulating a 32-bit-only cpu, use RV32. * When emulating a 64-bit cpu, and MXL has been reduced to RV32, @@ -530,7 +538,7 @@ static inline RISCVMXL cpu_recompute_xl(CPURISCVState *env) * back to RV64 for lower privs. */ if (xl != MXL_RV32) { - switch (env->priv) { + switch (mode) { case PRV_M: break; case PRV_U: @@ -541,11 +549,38 @@ static inline RISCVMXL cpu_recompute_xl(CPURISCVState *env) break; } } -#endif return xl; } #endif +#if defined(TARGET_RISCV32) +#define cpu_recompute_xl(env) ((void)(env), MXL_RV32) +#else +static inline RISCVMXL cpu_recompute_xl(CPURISCVState *env) +{ +#if !defined(CONFIG_USER_ONLY) + return cpu_get_xl(env, env->priv); +#else + return env->misa_mxl; +#endif +} +#endif + +#if defined(TARGET_RISCV32) +#define cpu_address_xl(env) ((void)(env), MXL_RV32) +#else +static inline RISCVMXL cpu_address_xl(CPURISCVState *env) +{ +#ifdef CONFIG_USER_ONLY + return env->xl; +#else + int mode = cpu_address_mode(env); + + return cpu_get_xl(env, mode); +#endif +} +#endif + static inline int riscv_cpu_xlen(CPURISCVState *env) { return 16 << env->xl; diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 8e3c73da52..2e771ddfc9 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -135,6 +135,7 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, flags = FIELD_DP32(flags, TB_FLAGS, FS, fs); flags = FIELD_DP32(flags, TB_FLAGS, VS, vs); flags = FIELD_DP32(flags, TB_FLAGS, XL, env->xl); + flags = FIELD_DP32(flags, TB_FLAGS, AXL, cpu_address_xl(env)); if (env->cur_pmmask != 0) { flags = FIELD_DP32(flags, TB_FLAGS, PM_MASK_ENABLED, 1); } diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 0a5ab89c43..98d54c5617 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -64,6 +64,7 @@ typedef struct DisasContext { target_ulong priv_ver; RISCVMXL misa_mxl_max; RISCVMXL xl; + RISCVMXL address_xl; uint32_t misa_ext; uint32_t opcode; RISCVExtStatus mstatus_fs; @@ -129,6 +130,14 @@ static inline bool has_ext(DisasContext *ctx, uint32_t ext) #define get_xl(ctx) ((ctx)->xl) #endif +#ifdef TARGET_RISCV32 +#define get_address_xl(ctx) MXL_RV32 +#elif defined(CONFIG_USER_ONLY) +#define get_address_xl(ctx) MXL_RV64 +#else +#define get_address_xl(ctx) ((ctx)->address_xl) +#endif + /* The word size for this machine mode. */ static inline int __attribute__((unused)) get_xlen(DisasContext *ctx) { @@ -575,12 +584,13 @@ static TCGv get_address(DisasContext *ctx, int rs1, int imm) tcg_gen_addi_tl(addr, src1, imm); if (ctx->pm_mask_enabled) { tcg_gen_andc_tl(addr, addr, pm_mask); - } else if (get_xl(ctx) == MXL_RV32) { + } else if (get_address_xl(ctx) == MXL_RV32) { tcg_gen_ext32u_tl(addr, addr); } if (ctx->pm_base_enabled) { tcg_gen_or_tl(addr, addr, pm_base); } + return addr; } @@ -1177,6 +1187,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->vl_eq_vlmax = FIELD_EX32(tb_flags, TB_FLAGS, VL_EQ_VLMAX); ctx->misa_mxl_max = env->misa_mxl_max; ctx->xl = FIELD_EX32(tb_flags, TB_FLAGS, XL); + ctx->address_xl = FIELD_EX32(tb_flags, TB_FLAGS, AXL); ctx->cs = cs; ctx->pm_mask_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_MASK_ENABLED); ctx->pm_base_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_BASE_ENABLED); From ef1ba32afff6b3d9be377804904b784da6594219 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 14 Jun 2023 11:25:47 +0800 Subject: [PATCH 14/54] target/riscv: update cur_pmbase/pmmask based on mode affected by MPRV Pointer mask is also affected by MPRV which means cur_pmbase/pmmask should also take MPRV into consideration. As pointer mask for instruction is not supported currently, so we can directly update cur_pmbase/pmmask based on address related mode and xlen affected by MPRV now. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Daniel Henrique Barboza Message-Id: <20230614032547.35895-3-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 7 +++++-- target/riscv/csr.c | 27 ++++++++++++++++++++------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 2e771ddfc9..bb9d923818 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -149,13 +149,16 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, void riscv_cpu_update_mask(CPURISCVState *env) { target_ulong mask = 0, base = 0; + RISCVMXL xl = env->xl; /* * TODO: Current RVJ spec does not specify * how the extension interacts with XLEN. */ #ifndef CONFIG_USER_ONLY + int mode = cpu_address_mode(env); + xl = cpu_get_xl(env, mode); if (riscv_has_ext(env, RVJ)) { - switch (env->priv) { + switch (mode) { case PRV_M: if (env->mmte & M_PM_ENABLE) { mask = env->mpmmask; @@ -179,7 +182,7 @@ void riscv_cpu_update_mask(CPURISCVState *env) } } #endif - if (env->xl == MXL_RV32) { + if (xl == MXL_RV32) { env->cur_pmmask = mask & UINT32_MAX; env->cur_pmbase = base & UINT32_MAX; } else { diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 25345f3153..ea7585329e 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -1329,8 +1329,9 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, */ if (env->debugger) { env->xl = cpu_recompute_xl(env); - riscv_cpu_update_mask(env); } + + riscv_cpu_update_mask(env); return RISCV_EXCP_NONE; } @@ -3633,7 +3634,7 @@ static RISCVException write_mpmmask(CPURISCVState *env, int csrno, uint64_t mstatus; env->mpmmask = val; - if ((env->priv == PRV_M) && (env->mmte & M_PM_ENABLE)) { + if ((cpu_address_mode(env) == PRV_M) && (env->mmte & M_PM_ENABLE)) { env->cur_pmmask = val; } env->mmte |= EXT_STATUS_DIRTY; @@ -3661,8 +3662,11 @@ static RISCVException write_spmmask(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } env->spmmask = val; - if ((env->priv == PRV_S) && (env->mmte & S_PM_ENABLE)) { + if ((cpu_address_mode(env) == PRV_S) && (env->mmte & S_PM_ENABLE)) { env->cur_pmmask = val; + if (cpu_get_xl(env, PRV_S) == MXL_RV32) { + env->cur_pmmask &= UINT32_MAX; + } } env->mmte |= EXT_STATUS_DIRTY; @@ -3689,8 +3693,11 @@ static RISCVException write_upmmask(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } env->upmmask = val; - if ((env->priv == PRV_U) && (env->mmte & U_PM_ENABLE)) { + if ((cpu_address_mode(env) == PRV_U) && (env->mmte & U_PM_ENABLE)) { env->cur_pmmask = val; + if (cpu_get_xl(env, PRV_U) == MXL_RV32) { + env->cur_pmmask &= UINT32_MAX; + } } env->mmte |= EXT_STATUS_DIRTY; @@ -3713,7 +3720,7 @@ static RISCVException write_mpmbase(CPURISCVState *env, int csrno, uint64_t mstatus; env->mpmbase = val; - if ((env->priv == PRV_M) && (env->mmte & M_PM_ENABLE)) { + if ((cpu_address_mode(env) == PRV_M) && (env->mmte & M_PM_ENABLE)) { env->cur_pmbase = val; } env->mmte |= EXT_STATUS_DIRTY; @@ -3741,8 +3748,11 @@ static RISCVException write_spmbase(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } env->spmbase = val; - if ((env->priv == PRV_S) && (env->mmte & S_PM_ENABLE)) { + if ((cpu_address_mode(env) == PRV_S) && (env->mmte & S_PM_ENABLE)) { env->cur_pmbase = val; + if (cpu_get_xl(env, PRV_S) == MXL_RV32) { + env->cur_pmbase &= UINT32_MAX; + } } env->mmte |= EXT_STATUS_DIRTY; @@ -3769,8 +3779,11 @@ static RISCVException write_upmbase(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } env->upmbase = val; - if ((env->priv == PRV_U) && (env->mmte & U_PM_ENABLE)) { + if ((cpu_address_mode(env) == PRV_U) && (env->mmte & U_PM_ENABLE)) { env->cur_pmbase = val; + if (cpu_get_xl(env, PRV_U) == MXL_RV32) { + env->cur_pmbase &= UINT32_MAX; + } } env->mmte |= EXT_STATUS_DIRTY; From aa903cf31391dd505b399627158f1292a6d19896 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 30 Jun 2023 23:36:04 +0800 Subject: [PATCH 15/54] roms/opensbi: Upgrade from v1.2 to v1.3 Upgrade OpenSBI from v1.2 to v1.3 and the pre-built bios images. The v1.3 release includes the following commits: 440fa81 treewide: Replace TRUE/FALSE with true/false 6509127 Makefile: Remove -N ldflag to prevent linker RWX warning 65638f8 lib: utils/sys: Allow custom HTIF base address for RV32 f14595a lib: sbi: Allow platform to influence cold boot HART selection 6957ae0 platform: generic: Allow platform_override to select cold boot HART cb7e7c3 platform: generic: Allow platform_override to perform firmware init 8020df8 generic/starfive: Add Starfive JH7110 platform implementation 6997552 lib: sbi_hsm: Rename 'priv' argument to 'arg1' 9e397e3 docs: domain_support: Use capital letter for privilege modes 9e0ba09 include: sbi: Fine grain the permissions for M and SU modes aace1e1 lib: sbi: Use finer permission semantics for address validation 22dbdb3 lib: sbi: Add permissions for the firmware start till end 1ac14f1 lib: sbi: Use finer permission sematics to decide on PMP bits 44f736c lib: sbi: Modify the boot time region flag prints 20646e0 lib: utils: Use SU-{R/W/X} flags for region permissions during parsing 3e2f573 lib: utils: Disallow non-root domains from adding M-mode regions 59a08cd lib: utils: Add M-mode {R/W} flags to the MMIO regions 001106d docs: Update domain's region permissions and requirements da5594b platform: generic: allwinner: Fix PLIC array bounds ce2a834 docs: generic.md: fix typo of andes-ae350 8ecbe6d lib: sbi_hsm: handle failure when hart_stop returns SBI_ENOTSUPP b1818ee include: types: add always inline compiler attribute 9c4eb35 lib: utils: atcsmu: Add Andes System Management Unit support 787296a platform: andes/ae350: Implement hart hotplug using HSM extension 7aaeeab lib: reset/fdt_reset_atcwdt200: Use defined macros and function in atcsmu.h a990309 lib: utils: Fix reserved memory node for firmware memory fefa548 firmware: Split RO/RX and RW sections 2f40a99 firmware: Move dynsym and reladyn sections to RX section c10e3fe firmware: Add RW section offset in scratch b666760 lib: sbi: Print the RW section offset 230278d lib: sbi: Add separate entries for firmware RX and RW regions dea0922 platform: renesas/rzfive: Configure Local memory regions as part of root domain 33bf917 lib: utils: Add fdt_add_cpu_idle_states() helper function c45992c platform: generic: allwinner: Advertise nonretentive suspend c8ea836 firmware: Fix fw_rw_offset computation in fw_base.S 8050081 firmware: Not to clear all the MIP 84d15f4 lib: sbi_hsm: Use csr_set to restore the MIP 199189b lib: utils: Mark only the largest region as reserved in FDT 66b0e23 lib: sbi: Ensure domidx_to_domain_table is null-terminated 642f3de Makefile: Add missing .dep files for fw_*.elf.ld 09b34d8 include: Add support for byteorder/endianness conversion 680bea0 lib: utils/fdt: Use byteorder conversion functions in libfdt_env.h b224ddb include: types: Add typedefs for endianness aa5dafc include: sbi: Fix BSWAPx() macros for big-endian host e3bf1af include: Add defines for SBI debug console extension 0ee3a86 lib: sbi: Add sbi_nputs() function 4e0572f lib: sbi: Add sbi_ngets() function eab48c3 lib: sbi: Add sbi_domain_check_addr_range() function 5a41a38 lib: sbi: Implement SBI debug console extension c43903c lib: sbi: Add console_puts() callback in the console device 29285ae lib: utils/serial: Implement console_puts() for semihosting 65c2190 lib: sbi: Speed-up sbi_printf() and friends using nputs() 321293c lib: utils/fdt: Fix fdt_pmu.c header dependency aafcc90 platform: generic/allwinner: Fix sun20i-d1.c header dependency 745aaec platform: generic/andes: Fix ae350.c header dependency 99d09b6 include: fdt/fdt_helper: Change fdt_get_address() to return root.next_arg1 6861ee9 lib: utils: fdt_fixup: Fix compile error 4f2be40 docs: fix typo in fw.md 30ea806 lib: sbi_hart: Enable hcontext and scontext 81adc62 lib: sbi: Align SBI vendor extension id with mvendorid CSR 31b82e0 include: sbi: Remove extid parameter from vendor_ext_provider() callback c100951 platform: generic: renesas: rzfive: Add support to configure the PMA 2491242 platform: generic: renesas: rzfive: Configure the PMA region 67b2a40 lib: sbi: sbi_ecall: Check the range of SBI error 5a75f53 lib: sbi/sbi_domain: cosmetic style fixes bc06ff6 lib: utils/fdt/fdt_domain: Simplify region access permission check 17b3776 docs: domain_support: Update the DT example 1364d5a lib: sbi_hsm: Factor out invalid state detection 40f16a8 lib: sbi_hsm: Don't try to restore state on failed change c88e039 lib: sbi_hsm: Ensure errors are consistent with spec b1ae6ef lib: sbi_hsm: Move misplaced comment 07673fc lib: sbi_hsm: Remove unnecessary include 8a40306 lib: sbi_hsm: Export some functions 73623a0 lib: sbi: Add system suspend skeleton c9917b6 lib: sbi: Add system_suspend_allowed domain property 7c964e2 lib: sbi: Implement system suspend 37558dc docs: Correct opensbi-domain property name 5ccebf0 platform: generic: Add system suspend test 908be1b gpio/starfive: add gpio driver and support gpio reset 4b28afc make: Add a command line option for debugging OpenSBI e9d08bd lib: utils/i2c: Add minimal StarFive jh7110 I2C driver 568ea49 platform: starfive: add PMIC power ops in JH7110 visionfive2 board 506144f lib: serial: Cadence: Enable compatibility for cdns,uart-r1p8 1fe8dc9 lib: sbi_pmu: add callback for counter width 51951d9 lib: sbi_pmu: Implement sbi_pmu_counter_fw_read_hi 60c358e lib: sbi_pmu: Reserve space for implementation specific firmware events 548e4b4 lib: sbi_pmu: Rename fw_counter_value b51ddff lib: sbi_pmu: Update sbi_pmu dev ops 641d2e9 lib: sbi_pmu: Use dedicated event code for platform firmware events 57d3aa3 lib: sbi_pmu: Introduce fw_counter_write_value API c631a7d lib: sbi_pmu: Add hartid parameter PMU device ops d56049e lib: sbi: Refactor the calls to sbi_hart_switch_mode() e8e9ed3 lib: sbi: Set the state of a hart to START_PENDING after the hart is ready c6a092c lib: sbi: Clear IPIs before init_warm_startup in non-boot harts ed88a63 lib: sbi_scratch: Optimize the alignment code for alloc size 73ab11d lib: sbi: Fix how to check whether the domain contains fw_region f64dfcd lib: sbi: Introduce sbi_entry_count() function 30b9e7e lib: sbi_hsm: Fix sbi_hsm_hart_start() for platform with hart hotplug 8e90259 lib: sbi_hart: clear mip csr during hart init 45ba2b2 include: Add defines for SBI CPPC extension 33caae8 lib: sbi: Implement SBI CPPC extension 91767d0 lib: sbi: Print the CPPC device name edc9914 lib: sbi_pmu: Align the event type offset as per SBI specification ee016a7 docs: Correct FW_JUMP_FDT_ADDR calculation example 2868f26 lib: utils: fdt_fixup: avoid buffer overrun 66fa925 lib: sbi: Optimize sbi_tlb 24dde46 lib: sbi: Optimize sbi_ipi 80078ab sbi: tlb: Simplify to tlb_process_count/tlb_process function bf40e07 lib: sbi: Optimize sbi_tlb queue waiting eeab500 platform: generic: andes/renesas: Add SBI EXT to check for enabling IOCP errata f692289 firmware: Optimize loading relocation type e41dbb5 firmware: Change to use positive offset to access relocation entries bdb3c42 lib: sbi: Do not clear active_events for cycle/instret when stopping 674e019 lib: sbi: Fix counter index calculation for SBI_PMU_CFG_FLAG_SKIP_MATCH f5dfd99 lib: sbi: Don't check SBI error range for legacy console getchar 7919530 lib: sbi: Add debug print when sbi_pmu_init fails 4e33530 lib: sbi: Remove unnecessary semicolon 6bc02de lib: sbi: Simplify sbi_ipi_process remove goto dc1c7db lib: sbi: Simplify BITS_PER_LONG definition f58c140 lib: sbi: Introduce register_extensions extension callback e307ba7 lib: sbi: Narrow vendor extension range 042f0c3 lib: sbi: pmu: Remove unnecessary probe function 8b952d4 lib: sbi: Only register available extensions 767b5fc lib: sbi: Optimize probe of srst/susp c3e31cb lib: sbi: Remove 0/1 probe implementations 33f1722 lib: sbi: Document sbi_ecall_extension members d4c46e0 Makefile: Dereference symlinks on install 8b99a7f lib: sbi: Fix return of sbi_console_init 264d0be lib: utils: Improve fdt_serial_init 9a0bdd0 lib: utils: Improve fdt_ipi 122f226 lib: utils: Improve fdt_timer df75e09 lib: utils/ipi: buffer overrun aclint_mswi_cold_init bdde2ec lib: sbi: Align system suspend errors with spec aad7a37 include: sbi_scratch: Add helper macros to access data type 5cf9a54 platform: Allow platforms to specify heap size 40d36a6 lib: sbi: Introduce simple heap allocator 2a04f70 lib: sbi: Print scratch size and usage at boot time bbff53f lib: sbi_pmu: Use heap for per-HART PMU state ef4542d lib: sbi: Use heap for root domain creation 66daafe lib: sbi: Use scratch space to save per-HART domain pointer fa5ad2e lib: utils/gpio: Use heap in SiFive and StartFive GPIO drivers 903e88c lib: utils/i2c: Use heap in DesignWare and SiFive I2C drivers 5a8cfcd lib: utils/ipi: Use heap in ACLINT MSWI driver 3013716 lib: utils/irqchip: Use heap in PLIC, APLIC and IMSIC drivers 7e5636a lib: utils/timer: Use heap in ACLINT MTIMER driver 3c1c972 lib: utils/fdt: Use heap in FDT domain parsing acbd8fc lib: utils/ipi: Use scratch space to save per-HART MSWI pointer f0516be lib: utils/timer: Use scratch space to save per-HART MTIMER pointer b3594ac lib: utils/irqchip: Use scratch space to save per-HART PLIC pointer 1df52fa lib: utils/irqchip: Don't check hartid in imsic_update_hartid_table() 355796c lib: utils/irqchip: Use scratch space to save per-HART IMSIC pointer 524feec docs: Add OpenSBI logo and use it in the top-level README.md 932be2c README.md: Improve project copyright information 8153b26 platform/lib: Set no-map attribute on all PMP regions d64942f firmware: Fix find hart index 27c957a lib: reset: Move fdt_reset_init into generic_early_init 8bd666a lib: sbi: check A2 register in ecall_dbcn_handler. 2552799 include: Bump-up version to 1.3 Signed-off-by: Bin Meng Message-Id: <20230630160717.843044-1-bmeng@tinylab.org> Tested-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Signed-off-by: Alistair Francis --- .../opensbi-riscv32-generic-fw_dynamic.bin | Bin 123072 -> 135344 bytes .../opensbi-riscv64-generic-fw_dynamic.bin | Bin 121800 -> 138304 bytes roms/opensbi | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin index 6a8425885c7dd0263391637b665c83d04684c68a..7b6c67e0ae299abb79dc0975ce8336e0185d4071 100644 GIT binary patch literal 135344 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 delta 61918 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 P@6=TjkA@5i7w-Q9a-9_0 diff --git a/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin index 80bdbf2170037541848cf96a6ff71bacea91baf7..1b831b412ca9ff7aac9b036db64b66caea1b2942 100644 GIT binary patch literal 138304 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
  • OwmRNyA=4$8*VpT#b4(g3*GKO_9SR)i7R3L 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 delta 60084 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 DkE0-T diff --git a/roms/opensbi b/roms/opensbi index 6b5188ca14..2552799a1d 160000 --- a/roms/opensbi +++ b/roms/opensbi @@ -1 +1 @@ -Subproject commit 6b5188ca14e59ce7bf71afe4e7d3d557c3d31bf8 +Subproject commit 2552799a1df30a3dcd2321a8b75d61d06f5fb9fc From 11b937b65256537cff74fe731a96465f8d0c2d88 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 30 Jun 2023 23:39:23 +0800 Subject: [PATCH 16/54] tests/avocado: riscv: Enable 32-bit Spike OpenSBI boot testing The 32-bit Spike boot issue has been fixed in the OpenSBI v1.3. Let's enable the 32-bit Spike OpenSBI boot testing. Signed-off-by: Bin Meng Message-Id: <20230630160717.843044-2-bmeng@tinylab.org> Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Signed-off-by: Alistair Francis --- tests/avocado/riscv_opensbi.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/avocado/riscv_opensbi.py b/tests/avocado/riscv_opensbi.py index e02f0d404a..bfff9cc3c3 100644 --- a/tests/avocado/riscv_opensbi.py +++ b/tests/avocado/riscv_opensbi.py @@ -6,7 +6,6 @@ # later. See the COPYING file in the top-level directory. from avocado_qemu import QemuSystemTest -from avocado import skip from avocado_qemu import wait_for_console_pattern class RiscvOpenSBI(QemuSystemTest): @@ -21,7 +20,6 @@ class RiscvOpenSBI(QemuSystemTest): wait_for_console_pattern(self, 'Platform Name') wait_for_console_pattern(self, 'Boot HART MEDELEG') - @skip("requires OpenSBI fix to work") def test_riscv32_spike(self): """ :avocado: tags=arch:riscv32 From bf01a04f5fbb4c22ac861a76fdc4c7a74247e44b Mon Sep 17 00:00:00 2001 From: Tommy Wu Date: Tue, 27 Jun 2023 07:12:14 -0700 Subject: [PATCH 17/54] hw/misc: sifive_e_aon: Support the watchdog timer of HiFive 1 rev b. The watchdog timer is in the always-on domain device of HiFive 1 rev b, so this patch added the AON device to the sifive_e machine. This patch only implemented the functionality of the watchdog timer. Signed-off-by: Tommy Wu Reviewed-by: Frank Chang Acked-by: Alistair Francis Message-Id: <20230627141216.3962299-2-tommy.wu@sifive.com> Signed-off-by: Alistair Francis --- hw/misc/Kconfig | 3 + hw/misc/meson.build | 1 + hw/misc/sifive_e_aon.c | 319 +++++++++++++++++++++++++++++++++ include/hw/misc/sifive_e_aon.h | 60 +++++++ 4 files changed, 383 insertions(+) create mode 100644 hw/misc/sifive_e_aon.c create mode 100644 include/hw/misc/sifive_e_aon.h diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index e4c2149175..6996d265e4 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -158,6 +158,9 @@ config SIFIVE_TEST config SIFIVE_E_PRCI bool +config SIFIVE_E_AON + bool + config SIFIVE_U_OTP bool diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 05877f61cc..892f8b91c5 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -30,6 +30,7 @@ system_ss.add(when: 'CONFIG_MCHP_PFSOC_IOSCB', if_true: files('mchp_pfsoc_ioscb. system_ss.add(when: 'CONFIG_MCHP_PFSOC_SYSREG', if_true: files('mchp_pfsoc_sysreg.c')) system_ss.add(when: 'CONFIG_SIFIVE_TEST', if_true: files('sifive_test.c')) system_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c')) +system_ss.add(when: 'CONFIG_SIFIVE_E_AON', if_true: files('sifive_e_aon.c')) system_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c')) system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c')) diff --git a/hw/misc/sifive_e_aon.c b/hw/misc/sifive_e_aon.c new file mode 100644 index 0000000000..4656457d0b --- /dev/null +++ b/hw/misc/sifive_e_aon.c @@ -0,0 +1,319 @@ +/* + * SiFive HiFive1 AON (Always On Domain) for QEMU. + * + * Copyright (c) 2022 SiFive, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "qemu/log.h" +#include "hw/irq.h" +#include "hw/registerfields.h" +#include "hw/misc/sifive_e_aon.h" +#include "qapi/visitor.h" +#include "qapi/error.h" +#include "sysemu/watchdog.h" +#include "hw/qdev-properties.h" + +REG32(AON_WDT_WDOGCFG, 0x0) + FIELD(AON_WDT_WDOGCFG, SCALE, 0, 4) + FIELD(AON_WDT_WDOGCFG, RSVD0, 4, 4) + FIELD(AON_WDT_WDOGCFG, RSTEN, 8, 1) + FIELD(AON_WDT_WDOGCFG, ZEROCMP, 9, 1) + FIELD(AON_WDT_WDOGCFG, RSVD1, 10, 2) + FIELD(AON_WDT_WDOGCFG, EN_ALWAYS, 12, 1) + FIELD(AON_WDT_WDOGCFG, EN_CORE_AWAKE, 13, 1) + FIELD(AON_WDT_WDOGCFG, RSVD2, 14, 14) + FIELD(AON_WDT_WDOGCFG, IP0, 28, 1) + FIELD(AON_WDT_WDOGCFG, RSVD3, 29, 3) +REG32(AON_WDT_WDOGCOUNT, 0x8) + FIELD(AON_WDT_WDOGCOUNT, VALUE, 0, 31) +REG32(AON_WDT_WDOGS, 0x10) +REG32(AON_WDT_WDOGFEED, 0x18) +REG32(AON_WDT_WDOGKEY, 0x1c) +REG32(AON_WDT_WDOGCMP0, 0x20) + +static void sifive_e_aon_wdt_update_wdogcount(SiFiveEAONState *r) +{ + int64_t now; + if (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS) == 0 && + FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE) == 0) { + return; + } + + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + r->wdogcount += muldiv64(now - r->wdog_restart_time, + r->wdogclk_freq, NANOSECONDS_PER_SECOND); + + /* Clean the most significant bit. */ + r->wdogcount &= R_AON_WDT_WDOGCOUNT_VALUE_MASK; + r->wdog_restart_time = now; +} + +static void sifive_e_aon_wdt_update_state(SiFiveEAONState *r) +{ + uint16_t wdogs; + bool cmp_signal = false; + sifive_e_aon_wdt_update_wdogcount(r); + wdogs = (uint16_t)(r->wdogcount >> + FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, SCALE)); + + if (wdogs >= r->wdogcmp0) { + cmp_signal = true; + if (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, ZEROCMP) == 1) { + r->wdogcount = 0; + wdogs = 0; + } + } + + if (cmp_signal) { + if (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, RSTEN) == 1) { + watchdog_perform_action(); + } + r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, IP0, 1); + } + + qemu_set_irq(r->wdog_irq, FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, IP0)); + + if (wdogs < r->wdogcmp0 && + (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS) == 1 || + FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE) == 1)) { + int64_t next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + next += muldiv64((r->wdogcmp0 - wdogs) << + FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, SCALE), + NANOSECONDS_PER_SECOND, r->wdogclk_freq); + timer_mod(r->wdog_timer, next); + } else { + timer_mod(r->wdog_timer, INT64_MAX); + } +} + +/* + * Callback used when the timer set using timer_mod expires. + */ +static void sifive_e_aon_wdt_expired_cb(void *opaque) +{ + SiFiveEAONState *r = SIFIVE_E_AON(opaque); + sifive_e_aon_wdt_update_state(r); +} + +static uint64_t +sifive_e_aon_wdt_read(void *opaque, hwaddr addr, unsigned int size) +{ + SiFiveEAONState *r = SIFIVE_E_AON(opaque); + + switch (addr) { + case A_AON_WDT_WDOGCFG: + return r->wdogcfg; + case A_AON_WDT_WDOGCOUNT: + sifive_e_aon_wdt_update_wdogcount(r); + return r->wdogcount; + case A_AON_WDT_WDOGS: + sifive_e_aon_wdt_update_wdogcount(r); + return r->wdogcount >> + FIELD_EX32(r->wdogcfg, + AON_WDT_WDOGCFG, + SCALE); + case A_AON_WDT_WDOGFEED: + return 0; + case A_AON_WDT_WDOGKEY: + return r->wdogunlock; + case A_AON_WDT_WDOGCMP0: + return r->wdogcmp0; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n", + __func__, (int)addr); + } + + return 0; +} + +static void +sifive_e_aon_wdt_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + SiFiveEAONState *r = SIFIVE_E_AON(opaque); + uint32_t value = val64; + + switch (addr) { + case A_AON_WDT_WDOGCFG: { + uint8_t new_en_always; + uint8_t new_en_core_awake; + uint8_t old_en_always; + uint8_t old_en_core_awake; + if (r->wdogunlock == 0) { + return; + } + + new_en_always = FIELD_EX32(value, AON_WDT_WDOGCFG, EN_ALWAYS); + new_en_core_awake = FIELD_EX32(value, AON_WDT_WDOGCFG, EN_CORE_AWAKE); + old_en_always = FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS); + old_en_core_awake = FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, + EN_CORE_AWAKE); + + if ((old_en_always || + old_en_core_awake) == 1 && + (new_en_always || + new_en_core_awake) == 0) { + sifive_e_aon_wdt_update_wdogcount(r); + } else if ((old_en_always || + old_en_core_awake) == 0 && + (new_en_always || + new_en_core_awake) == 1) { + r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + } + r->wdogcfg = value; + r->wdogunlock = 0; + break; + } + case A_AON_WDT_WDOGCOUNT: + if (r->wdogunlock == 0) { + return; + } + r->wdogcount = value & R_AON_WDT_WDOGCOUNT_VALUE_MASK; + r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + r->wdogunlock = 0; + break; + case A_AON_WDT_WDOGS: + return; + case A_AON_WDT_WDOGFEED: + if (r->wdogunlock == 0) { + return; + } + if (value == SIFIVE_E_AON_WDOGFEED) { + r->wdogcount = 0; + r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + } + r->wdogunlock = 0; + break; + case A_AON_WDT_WDOGKEY: + if (value == SIFIVE_E_AON_WDOGKEY) { + r->wdogunlock = 1; + } + break; + case A_AON_WDT_WDOGCMP0: + if (r->wdogunlock == 0) { + return; + } + r->wdogcmp0 = (uint16_t) value; + r->wdogunlock = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x v=0x%x\n", + __func__, (int)addr, (int)value); + } + sifive_e_aon_wdt_update_state(r); +} + +static uint64_t +sifive_e_aon_read(void *opaque, hwaddr addr, unsigned int size) +{ + if (addr < SIFIVE_E_AON_RTC) { + return sifive_e_aon_wdt_read(opaque, addr, size); + } else if (addr < SIFIVE_E_AON_MAX) { + qemu_log_mask(LOG_UNIMP, "%s: Unimplemented read: addr=0x%x\n", + __func__, (int)addr); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n", + __func__, (int)addr); + } + return 0; +} + +static void +sifive_e_aon_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + if (addr < SIFIVE_E_AON_RTC) { + sifive_e_aon_wdt_write(opaque, addr, val64, size); + } else if (addr < SIFIVE_E_AON_MAX) { + qemu_log_mask(LOG_UNIMP, "%s: Unimplemented write: addr=0x%x\n", + __func__, (int)addr); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x\n", + __func__, (int)addr); + } +} + +static const MemoryRegionOps sifive_e_aon_ops = { + .read = sifive_e_aon_read, + .write = sifive_e_aon_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4 + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static void sifive_e_aon_reset(DeviceState *dev) +{ + SiFiveEAONState *r = SIFIVE_E_AON(dev); + + r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, RSTEN, 0); + r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS, 0); + r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE, 0); + r->wdogcmp0 = 0xbeef; + + sifive_e_aon_wdt_update_state(r); +} + +static void sifive_e_aon_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + SiFiveEAONState *r = SIFIVE_E_AON(obj); + + memory_region_init_io(&r->mmio, OBJECT(r), &sifive_e_aon_ops, r, + TYPE_SIFIVE_E_AON, SIFIVE_E_AON_MAX); + sysbus_init_mmio(sbd, &r->mmio); + + /* watchdog timer */ + r->wdog_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + sifive_e_aon_wdt_expired_cb, r); + r->wdogclk_freq = SIFIVE_E_LFCLK_DEFAULT_FREQ; + sysbus_init_irq(sbd, &r->wdog_irq); +} + +static Property sifive_e_aon_properties[] = { + DEFINE_PROP_UINT64("wdogclk-frequency", SiFiveEAONState, wdogclk_freq, + SIFIVE_E_LFCLK_DEFAULT_FREQ), + DEFINE_PROP_END_OF_LIST(), +}; + +static void sifive_e_aon_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->reset = sifive_e_aon_reset; + device_class_set_props(dc, sifive_e_aon_properties); +} + +static const TypeInfo sifive_e_aon_info = { + .name = TYPE_SIFIVE_E_AON, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SiFiveEAONState), + .instance_init = sifive_e_aon_init, + .class_init = sifive_e_aon_class_init, +}; + +static void sifive_e_aon_register_types(void) +{ + type_register_static(&sifive_e_aon_info); +} + +type_init(sifive_e_aon_register_types) diff --git a/include/hw/misc/sifive_e_aon.h b/include/hw/misc/sifive_e_aon.h new file mode 100644 index 0000000000..2ae1c4139c --- /dev/null +++ b/include/hw/misc/sifive_e_aon.h @@ -0,0 +1,60 @@ +/* + * SiFive HiFive1 AON (Always On Domain) interface. + * + * Copyright (c) 2022 SiFive, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_SIFIVE_AON_H +#define HW_SIFIVE_AON_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_SIFIVE_E_AON "riscv.sifive.e.aon" +OBJECT_DECLARE_SIMPLE_TYPE(SiFiveEAONState, SIFIVE_E_AON) + +#define SIFIVE_E_AON_WDOGKEY (0x51F15E) +#define SIFIVE_E_AON_WDOGFEED (0xD09F00D) +#define SIFIVE_E_LFCLK_DEFAULT_FREQ (32768) + +enum { + SIFIVE_E_AON_WDT = 0x0, + SIFIVE_E_AON_RTC = 0x40, + SIFIVE_E_AON_LFROSC = 0x70, + SIFIVE_E_AON_BACKUP = 0x80, + SIFIVE_E_AON_PMU = 0x100, + SIFIVE_E_AON_MAX = 0x150 +}; + +struct SiFiveEAONState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion mmio; + + /*< watchdog timer >*/ + QEMUTimer *wdog_timer; + qemu_irq wdog_irq; + uint64_t wdog_restart_time; + uint64_t wdogclk_freq; + + uint32_t wdogcfg; + uint16_t wdogcmp0; + uint32_t wdogcount; + uint8_t wdogunlock; +}; + +#endif From 82193640c49ded26835fe90e60cdfd4385d4c0e2 Mon Sep 17 00:00:00 2001 From: Tommy Wu Date: Tue, 27 Jun 2023 07:12:15 -0700 Subject: [PATCH 18/54] hw/riscv: sifive_e: Support the watchdog timer of HiFive 1 rev b. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create the AON device when we realize the sifive_e machine. This patch only implemented the functionality of the watchdog timer, not all the functionality of the AON device. Signed-off-by: Tommy Wu Reviewed-by: Frank Chang Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230627141216.3962299-3-tommy.wu@sifive.com> Signed-off-by: Alistair Francis --- hw/riscv/Kconfig | 1 + hw/riscv/sifive_e.c | 17 +++++++++++++++-- include/hw/riscv/sifive_e.h | 9 ++++++--- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 6528ebfa3a..b6a5eb4452 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -60,6 +60,7 @@ config SIFIVE_E select SIFIVE_PLIC select SIFIVE_UART select SIFIVE_E_PRCI + select SIFIVE_E_AON select UNIMP config SIFIVE_U diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 04939b60c3..0d37adc542 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -45,6 +45,7 @@ #include "hw/intc/riscv_aclint.h" #include "hw/intc/sifive_plic.h" #include "hw/misc/sifive_e_prci.h" +#include "hw/misc/sifive_e_aon.h" #include "chardev/char.h" #include "sysemu/sysemu.h" @@ -185,6 +186,8 @@ static void sifive_e_soc_init(Object *obj) object_property_set_int(OBJECT(&s->cpus), "resetvec", 0x1004, &error_abort); object_initialize_child(obj, "riscv.sifive.e.gpio0", &s->gpio, TYPE_SIFIVE_GPIO); + object_initialize_child(obj, "riscv.sifive.e.aon", &s->aon, + TYPE_SIFIVE_E_AON); } static void sifive_e_soc_realize(DeviceState *dev, Error **errp) @@ -223,10 +226,17 @@ static void sifive_e_soc_realize(DeviceState *dev, Error **errp) RISCV_ACLINT_DEFAULT_MTIMER_SIZE, 0, ms->smp.cpus, RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME, RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, false); - create_unimplemented_device("riscv.sifive.e.aon", - memmap[SIFIVE_E_DEV_AON].base, memmap[SIFIVE_E_DEV_AON].size); sifive_e_prci_create(memmap[SIFIVE_E_DEV_PRCI].base); + /* AON */ + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->aon), errp)) { + return; + } + + /* Map AON registers */ + sysbus_mmio_map(SYS_BUS_DEVICE(&s->aon), 0, memmap[SIFIVE_E_DEV_AON].base); + /* GPIO */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { @@ -245,6 +255,9 @@ static void sifive_e_soc_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_E_GPIO0_IRQ0 + i)); } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->aon), 0, + qdev_get_gpio_in(DEVICE(s->plic), + SIFIVE_E_AON_WDT_IRQ)); sifive_uart_create(sys_mem, memmap[SIFIVE_E_DEV_UART0].base, serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_E_UART0_IRQ)); diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h index b824a79e2d..31180a680e 100644 --- a/include/hw/riscv/sifive_e.h +++ b/include/hw/riscv/sifive_e.h @@ -22,6 +22,7 @@ #include "hw/riscv/riscv_hart.h" #include "hw/riscv/sifive_cpu.h" #include "hw/gpio/sifive_gpio.h" +#include "hw/misc/sifive_e_aon.h" #include "hw/boards.h" #define TYPE_RISCV_E_SOC "riscv.sifive.e.soc" @@ -35,6 +36,7 @@ typedef struct SiFiveESoCState { /*< public >*/ RISCVHartArrayState cpus; DeviceState *plic; + SiFiveEAONState aon; SIFIVEGPIOState gpio; MemoryRegion xip_mem; MemoryRegion mask_rom; @@ -76,9 +78,10 @@ enum { }; enum { - SIFIVE_E_UART0_IRQ = 3, - SIFIVE_E_UART1_IRQ = 4, - SIFIVE_E_GPIO0_IRQ0 = 8 + SIFIVE_E_AON_WDT_IRQ = 1, + SIFIVE_E_UART0_IRQ = 3, + SIFIVE_E_UART1_IRQ = 4, + SIFIVE_E_GPIO0_IRQ0 = 8 }; #define SIFIVE_E_PLIC_HART_CONFIG "M" From 2f849e9d7aa4ee8782c524b4521ba36451cfd414 Mon Sep 17 00:00:00 2001 From: Tommy Wu Date: Tue, 27 Jun 2023 07:12:16 -0700 Subject: [PATCH 19/54] tests/qtest: sifive-e-aon-watchdog-test.c: Add QTest of watchdog of sifive_e Add some simple tests of the watchdog timer in the always-on domain device of HiFive 1 rev b. Signed-off-by: Tommy Wu Reviewed-by: Frank Chang Acked-by: Thomas Huth Acked-by: Alistair Francis Message-Id: <20230627141216.3962299-4-tommy.wu@sifive.com> Signed-off-by: Alistair Francis --- tests/qtest/meson.build | 3 + tests/qtest/sifive-e-aon-watchdog-test.c | 450 +++++++++++++++++++++++ 2 files changed, 453 insertions(+) create mode 100644 tests/qtest/sifive-e-aon-watchdog-test.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 74630f6672..b071d400b3 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -234,6 +234,9 @@ qtests_s390x = \ 'cpu-plug-test', 'migration-test'] +qtests_riscv32 = \ + (config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : []) + qos_test_ss = ss.source_set() qos_test_ss.add( 'ac97-test.c', diff --git a/tests/qtest/sifive-e-aon-watchdog-test.c b/tests/qtest/sifive-e-aon-watchdog-test.c new file mode 100644 index 0000000000..1f313d16ad --- /dev/null +++ b/tests/qtest/sifive-e-aon-watchdog-test.c @@ -0,0 +1,450 @@ +/* + * QTest testcase for the watchdog timer of HiFive 1 rev b. + * + * Copyright (c) 2023 SiFive, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "qemu/bitops.h" +#include "libqtest.h" +#include "hw/registerfields.h" +#include "hw/misc/sifive_e_aon.h" + +FIELD(AON_WDT_WDOGCFG, SCALE, 0, 4) +FIELD(AON_WDT_WDOGCFG, RSVD0, 4, 4) +FIELD(AON_WDT_WDOGCFG, RSTEN, 8, 1) +FIELD(AON_WDT_WDOGCFG, ZEROCMP, 9, 1) +FIELD(AON_WDT_WDOGCFG, RSVD1, 10, 2) +FIELD(AON_WDT_WDOGCFG, EN_ALWAYS, 12, 1) +FIELD(AON_WDT_WDOGCFG, EN_CORE_AWAKE, 13, 1) +FIELD(AON_WDT_WDOGCFG, RSVD2, 14, 14) +FIELD(AON_WDT_WDOGCFG, IP0, 28, 1) +FIELD(AON_WDT_WDOGCFG, RSVD3, 29, 3) + +#define WDOG_BASE (0x10000000) +#define WDOGCFG (0x0) +#define WDOGCOUNT (0x8) +#define WDOGS (0x10) +#define WDOGFEED (0x18) +#define WDOGKEY (0x1c) +#define WDOGCMP0 (0x20) + +#define SIFIVE_E_AON_WDOGKEY (0x51F15E) +#define SIFIVE_E_AON_WDOGFEED (0xD09F00D) +#define SIFIVE_E_LFCLK_DEFAULT_FREQ (32768) + +static void test_init(QTestState *qts) +{ + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, 0); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCMP0, 0xBEEF); +} + +static void test_wdogcount(void) +{ + uint64_t tmp; + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + tmp = qtest_readl(qts, WDOG_BASE + WDOGCOUNT); + qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0xBEEF); + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == tmp); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0xBEEF); + g_assert(0xBEEF == qtest_readl(qts, WDOG_BASE + WDOGCOUNT)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0xAAAAAAAA); + g_assert(0x2AAAAAAA == qtest_readl(qts, WDOG_BASE + WDOGCOUNT)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGFEED, 0xAAAAAAAA); + g_assert(0x2AAAAAAA == qtest_readl(qts, WDOG_BASE + WDOGCOUNT)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGFEED, SIFIVE_E_AON_WDOGFEED); + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGCOUNT)); + + qtest_quit(qts); +} + +static void test_wdogcfg(void) +{ + uint32_t tmp_cfg; + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + tmp_cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + qtest_writel(qts, WDOG_BASE + WDOGCFG, 0xFFFFFFFF); + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCFG) == tmp_cfg); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, 0xFFFFFFFF); + g_assert(0xFFFFFFFF == qtest_readl(qts, WDOG_BASE + WDOGCFG)); + + tmp_cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(15 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, 0); + tmp_cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, IP0)); + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGCFG)); + + qtest_quit(qts); +} + +static void test_wdogcmp0(void) +{ + uint32_t tmp; + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + tmp = qtest_readl(qts, WDOG_BASE + WDOGCMP0); + qtest_writel(qts, WDOG_BASE + WDOGCMP0, 0xBEEF); + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCMP0) == tmp); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCMP0, 0xBEEF); + g_assert(0xBEEF == qtest_readl(qts, WDOG_BASE + WDOGCMP0)); + + qtest_quit(qts); +} + +static void test_wdogkey(void) +{ + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGKEY)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, 0xFFFF); + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGKEY)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + g_assert(1 == qtest_readl(qts, WDOG_BASE + WDOGKEY)); + + qtest_writel(qts, WDOG_BASE + WDOGFEED, 0xAAAAAAAA); + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGKEY)); + + qtest_quit(qts); +} + +static void test_wdogfeed(void) +{ + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGFEED)); + + qtest_writel(qts, WDOG_BASE + WDOGFEED, 0xFFFF); + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGFEED)); + + qtest_quit(qts); +} + +static void test_scaled_wdogs(void) +{ + uint32_t cfg; + uint32_t fake_count = 0x12345678; + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCOUNT, fake_count); + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == fake_count); + g_assert((uint16_t)qtest_readl(qts, WDOG_BASE + WDOGS) == + (uint16_t)fake_count); + + for (int i = 0; i < 16; i++) { + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, i); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + g_assert((uint16_t)qtest_readl(qts, WDOG_BASE + WDOGS) == + (uint16_t)(fake_count >> + FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE))); + } + + qtest_quit(qts); +} + +static void test_watchdog(void) +{ + uint32_t cfg; + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCMP0, SIFIVE_E_LFCLK_DEFAULT_FREQ); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, 0); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + + qtest_clock_step(qts, NANOSECONDS_PER_SECOND); + + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == + SIFIVE_E_LFCLK_DEFAULT_FREQ); + g_assert(qtest_readl(qts, WDOG_BASE + WDOGS) == + SIFIVE_E_LFCLK_DEFAULT_FREQ); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_quit(qts); +} + +static void test_scaled_watchdog(void) +{ + uint32_t cfg; + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCMP0, 10); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, 15); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + + qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 10); + + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == + SIFIVE_E_LFCLK_DEFAULT_FREQ * 10); + + g_assert(10 == qtest_readl(qts, WDOG_BASE + WDOGS)); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(15 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_quit(qts); +} + +static void test_periodic_int(void) +{ + uint32_t cfg; + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCMP0, SIFIVE_E_LFCLK_DEFAULT_FREQ); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, 0); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, ZEROCMP, 1); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + + qtest_clock_step(qts, NANOSECONDS_PER_SECOND); + + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGCOUNT)); + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGS)); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_clock_step(qts, NANOSECONDS_PER_SECOND); + + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGCOUNT)); + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGS)); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_quit(qts); +} + +static void test_enable_disable(void) +{ + uint32_t cfg; + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCMP0, 10); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, 15); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + + qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 2); + + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == + SIFIVE_E_LFCLK_DEFAULT_FREQ * 2); + g_assert(2 == qtest_readl(qts, WDOG_BASE + WDOGS)); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(15 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 0); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + + qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 8); + + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == + SIFIVE_E_LFCLK_DEFAULT_FREQ * 2); + g_assert(2 == qtest_readl(qts, WDOG_BASE + WDOGS)); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(15 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + + qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 8); + + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == + SIFIVE_E_LFCLK_DEFAULT_FREQ * 10); + g_assert(10 == qtest_readl(qts, WDOG_BASE + WDOGS)); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(15 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_quit(qts); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + qtest_add_func("/sifive-e-aon-watchdog-test/wdogcount", + test_wdogcount); + qtest_add_func("/sifive-e-aon-watchdog-test/wdogcfg", + test_wdogcfg); + qtest_add_func("/sifive-e-aon-watchdog-test/wdogcmp0", + test_wdogcmp0); + qtest_add_func("/sifive-e-aon-watchdog-test/wdogkey", + test_wdogkey); + qtest_add_func("/sifive-e-aon-watchdog-test/wdogfeed", + test_wdogfeed); + qtest_add_func("/sifive-e-aon-watchdog-test/scaled_wdogs", + test_scaled_wdogs); + qtest_add_func("/sifive-e-aon-watchdog-test/watchdog", + test_watchdog); + qtest_add_func("/sifive-e-aon-watchdog-test/scaled_watchdog", + test_scaled_watchdog); + qtest_add_func("/sifive-e-aon-watchdog-test/periodic_int", + test_periodic_int); + qtest_add_func("/sifive-e-aon-watchdog-test/enable_disable", + test_enable_disable); + return g_test_run(); +} From 278c1bcef568f41e298792f9f437efd333305812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 27 Jun 2023 16:32:34 +0200 Subject: [PATCH 20/54] target/riscv: Only unify 'riscv32/64' -> 'riscv' for host cpu in meson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to keep the ability to distinct between 32/64-bit host. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20230627143235.29947-2-philmd@linaro.org> Signed-off-by: Alistair Francis --- meson.build | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/meson.build b/meson.build index 58413d44a5..657e01069b 100644 --- a/meson.build +++ b/meson.build @@ -55,16 +55,11 @@ qapi_trace_events = [] bsd_oses = ['gnu/kfreebsd', 'freebsd', 'netbsd', 'openbsd', 'dragonfly', 'darwin'] supported_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux'] -supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv', 'x86', 'x86_64', +supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv32', 'riscv64', 'x86', 'x86_64', 'arm', 'aarch64', 'loongarch64', 'mips', 'mips64', 'sparc64'] cpu = host_machine.cpu_family() -# Unify riscv* to a single family. -if cpu in ['riscv32', 'riscv64'] - cpu = 'riscv' -endif - target_dirs = config_host['TARGET_DIRS'].split() have_linux_user = false have_bsd_user = false @@ -99,6 +94,8 @@ elif cpu == 'x86' host_arch = 'i386' elif cpu == 'mips64' host_arch = 'mips' +elif cpu in ['riscv32', 'riscv64'] + host_arch = 'riscv' else host_arch = cpu endif @@ -113,7 +110,7 @@ elif cpu in ['ppc', 'ppc64'] kvm_targets = ['ppc-softmmu', 'ppc64-softmmu'] elif cpu in ['mips', 'mips64'] kvm_targets = ['mips-softmmu', 'mipsel-softmmu', 'mips64-softmmu', 'mips64el-softmmu'] -elif cpu in ['riscv'] +elif cpu in ['riscv32', 'riscv64'] kvm_targets = ['riscv32-softmmu', 'riscv64-softmmu'] else kvm_targets = [] From 4de81093f894c42bce975e52fc7b470a76046301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 27 Jun 2023 16:32:35 +0200 Subject: [PATCH 21/54] target/riscv: Only build KVM guest with same wordsize as host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per Anup Patel in [*]: > Currently, we only support running rv64 guest on rv64 host > and rv32 guest on rv32 host. > > In the future, we might support running rv32 guest on rv64 > host but as of now we don't see a strong push for it. Therefore, when only using the KVM accelerator it is pointless to build qemu-system-riscv32 on a rv64 host (or qemu-system-riscv64 on a rv32 host). Restrict meson to only build the correct binary, avoiding to waste ressources building unusable code. [*] https://lore.kernel.org/qemu-devel/CAAhSdy2JeRHeeoEc1XKQhPO3aDz4YKeyQsPT4S8yKJcYTA+AiQ@mail.gmail.com/ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20230627143235.29947-3-philmd@linaro.org> Signed-off-by: Alistair Francis --- meson.build | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 657e01069b..1ed47c779d 100644 --- a/meson.build +++ b/meson.build @@ -110,8 +110,10 @@ elif cpu in ['ppc', 'ppc64'] kvm_targets = ['ppc-softmmu', 'ppc64-softmmu'] elif cpu in ['mips', 'mips64'] kvm_targets = ['mips-softmmu', 'mipsel-softmmu', 'mips64-softmmu', 'mips64el-softmmu'] -elif cpu in ['riscv32', 'riscv64'] - kvm_targets = ['riscv32-softmmu', 'riscv64-softmmu'] +elif cpu in ['riscv32'] + kvm_targets = ['riscv32-softmmu'] +elif cpu in ['riscv64'] + kvm_targets = ['riscv64-softmmu'] else kvm_targets = [] endif From b227f6a8a7db28f48b4f1120d521eacdc25e66ef Mon Sep 17 00:00:00 2001 From: Ivan Klokov Date: Thu, 29 Jun 2023 11:37:30 +0300 Subject: [PATCH 22/54] target/riscv: Add RVV registers to log Print RvV extension register to log if VPU option is enabled. Signed-off-by: Ivan Klokov Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-Id: <20230629083730.386604-1-ivan.klokov@syntacore.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 57 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index b9b3879281..0c9faf4633 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -55,6 +55,17 @@ struct isa_ext_data { #define ISA_EXT_DATA_ENTRY(_name, _min_ver, _prop) \ {#_name, _min_ver, offsetof(struct RISCVCPUConfig, _prop)} +/* + * From vector_helper.c + * Note that vector data is stored in host-endian 64-bit chunks, + * so addressing bytes needs a host-endian fixup. + */ +#if HOST_BIG_ENDIAN +#define BYTE(x) ((x) ^ 7) +#else +#define BYTE(x) (x) +#endif + /* * Here are the ordering rules of extension naming defined by RISC-V * specification : @@ -183,6 +194,14 @@ const char * const riscv_fpr_regnames[] = { "f30/ft10", "f31/ft11" }; +const char * const riscv_rvv_regnames[] = { + "v0", "v1", "v2", "v3", "v4", "v5", "v6", + "v7", "v8", "v9", "v10", "v11", "v12", "v13", + "v14", "v15", "v16", "v17", "v18", "v19", "v20", + "v21", "v22", "v23", "v24", "v25", "v26", "v27", + "v28", "v29", "v30", "v31" +}; + static const char * const riscv_excp_names[] = { "misaligned_fetch", "fault_fetch", @@ -611,7 +630,8 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - int i; + int i, j; + uint8_t *p; #if !defined(CONFIG_USER_ONLY) if (riscv_has_ext(env, RVH)) { @@ -695,6 +715,41 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) } } } + if (riscv_has_ext(env, RVV) && (flags & CPU_DUMP_VPU)) { + static const int dump_rvv_csrs[] = { + CSR_VSTART, + CSR_VXSAT, + CSR_VXRM, + CSR_VCSR, + CSR_VL, + CSR_VTYPE, + CSR_VLENB, + }; + for (int i = 0; i < ARRAY_SIZE(dump_rvv_csrs); ++i) { + int csrno = dump_rvv_csrs[i]; + target_ulong val = 0; + RISCVException res = riscv_csrrw_debug(env, csrno, &val, 0, 0); + + /* + * Rely on the smode, hmode, etc, predicates within csr.c + * to do the filtering of the registers that are present. + */ + if (res == RISCV_EXCP_NONE) { + qemu_fprintf(f, " %-8s " TARGET_FMT_lx "\n", + csr_ops[csrno].name, val); + } + } + uint16_t vlenb = cpu->cfg.vlen >> 3; + + for (i = 0; i < 32; i++) { + qemu_fprintf(f, " %-8s ", riscv_rvv_regnames[i]); + p = (uint8_t *)env->vreg; + for (j = vlenb - 1 ; j >= 0; j--) { + qemu_fprintf(f, "%02x", *(p + i * vlenb + BYTE(j))); + } + qemu_fprintf(f, "\n"); + } + } } static void riscv_cpu_set_pc(CPUState *cs, vaddr value) From c0716c81b2e436eb69975f1890dd2ae46bce5369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 29 Jun 2023 14:11:03 +0200 Subject: [PATCH 23/54] hw/riscv/virt: Restrict ACLINT to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Advanced Core Local Interruptor (ACLINT) device can only be used with TCG. Check for TCG enabled instead of KVM being not. Only add the property when TCG is used. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-Id: <20230629121103.87733-3-philmd@linaro.org> Signed-off-by: Alistair Francis --- docs/system/riscv/virt.rst | 1 + hw/riscv/virt.c | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/system/riscv/virt.rst b/docs/system/riscv/virt.rst index b33f45e5b3..f9a2eac544 100644 --- a/docs/system/riscv/virt.rst +++ b/docs/system/riscv/virt.rst @@ -93,6 +93,7 @@ The following machine-specific options are supported: When this option is "on", ACLINT devices will be emulated instead of SiFive CLINT. When not specified, this option is assumed to be "off". + This option is restricted to the TCG accelerator. - aia=[none|aplic|aplic-imsic] diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 68ccd0bde1..cdb88a1529 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -44,6 +44,7 @@ #include "chardev/char.h" #include "sysemu/device_tree.h" #include "sysemu/sysemu.h" +#include "sysemu/tcg.h" #include "sysemu/kvm.h" #include "sysemu/tpm.h" #include "hw/pci/pci.h" @@ -776,7 +777,7 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, g_free(clust_name); - if (!kvm_enabled()) { + if (tcg_enabled()) { if (s->have_aclint) { create_fdt_socket_aclint(s, memmap, socket, &intc_phandles[phandle_pos]); @@ -1370,7 +1371,7 @@ static void virt_machine_init(MachineState *machine) hart_count, &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_fatal); - if (!kvm_enabled()) { + if (tcg_enabled()) { if (s->have_aclint) { if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { /* Per-socket ACLINT MTIMER */ @@ -1682,12 +1683,13 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); #endif - object_class_property_add_bool(oc, "aclint", virt_get_aclint, - virt_set_aclint); - object_class_property_set_description(oc, "aclint", - "Set on/off to enable/disable " - "emulating ACLINT devices"); - + if (tcg_enabled()) { + object_class_property_add_bool(oc, "aclint", virt_get_aclint, + virt_set_aclint); + object_class_property_set_description(oc, "aclint", + "Set on/off to enable/disable " + "emulating ACLINT devices"); + } object_class_property_add_str(oc, "aia", virt_get_aia, virt_set_aia); object_class_property_set_description(oc, "aia", From 9e1c7d982d7feb5b6547276ecc4c10468c0f5092 Mon Sep 17 00:00:00 2001 From: Robbin Ehn Date: Mon, 19 Jun 2023 10:24:03 +0200 Subject: [PATCH 24/54] linux-user/riscv: Add syscall riscv_hwprobe This patch adds the new syscall for the "RISC-V Hardware Probing Interface" (https://docs.kernel.org/riscv/hwprobe.html). Reviewed-by: Palmer Dabbelt Signed-off-by: Robbin Ehn Message-Id: <06a4543df2aa6101ca9a48f21a3198064b4f1f87.camel@rivosinc.com> Signed-off-by: Alistair Francis --- linux-user/riscv/syscall32_nr.h | 1 + linux-user/riscv/syscall64_nr.h | 1 + linux-user/syscall.c | 146 ++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+) diff --git a/linux-user/riscv/syscall32_nr.h b/linux-user/riscv/syscall32_nr.h index 1327d7dffa..412e58e5b2 100644 --- a/linux-user/riscv/syscall32_nr.h +++ b/linux-user/riscv/syscall32_nr.h @@ -228,6 +228,7 @@ #define TARGET_NR_accept4 242 #define TARGET_NR_arch_specific_syscall 244 #define TARGET_NR_riscv_flush_icache (TARGET_NR_arch_specific_syscall + 15) +#define TARGET_NR_riscv_hwprobe (TARGET_NR_arch_specific_syscall + 14) #define TARGET_NR_prlimit64 261 #define TARGET_NR_fanotify_init 262 #define TARGET_NR_fanotify_mark 263 diff --git a/linux-user/riscv/syscall64_nr.h b/linux-user/riscv/syscall64_nr.h index 6659751933..29e1eb2075 100644 --- a/linux-user/riscv/syscall64_nr.h +++ b/linux-user/riscv/syscall64_nr.h @@ -251,6 +251,7 @@ #define TARGET_NR_recvmmsg 243 #define TARGET_NR_arch_specific_syscall 244 #define TARGET_NR_riscv_flush_icache (TARGET_NR_arch_specific_syscall + 15) +#define TARGET_NR_riscv_hwprobe (TARGET_NR_arch_specific_syscall + 14) #define TARGET_NR_wait4 260 #define TARGET_NR_prlimit64 261 #define TARGET_NR_fanotify_init 262 diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 9b9e3bd5e3..420bab7c68 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -8983,6 +8983,147 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count) } #endif /* TARGET_NR_getdents64 */ +#if defined(TARGET_NR_riscv_hwprobe) + +#define RISCV_HWPROBE_KEY_MVENDORID 0 +#define RISCV_HWPROBE_KEY_MARCHID 1 +#define RISCV_HWPROBE_KEY_MIMPID 2 + +#define RISCV_HWPROBE_KEY_BASE_BEHAVIOR 3 +#define RISCV_HWPROBE_BASE_BEHAVIOR_IMA (1 << 0) + +#define RISCV_HWPROBE_KEY_IMA_EXT_0 4 +#define RISCV_HWPROBE_IMA_FD (1 << 0) +#define RISCV_HWPROBE_IMA_C (1 << 1) + +#define RISCV_HWPROBE_KEY_CPUPERF_0 5 +#define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0) +#define RISCV_HWPROBE_MISALIGNED_EMULATED (1 << 0) +#define RISCV_HWPROBE_MISALIGNED_SLOW (2 << 0) +#define RISCV_HWPROBE_MISALIGNED_FAST (3 << 0) +#define RISCV_HWPROBE_MISALIGNED_UNSUPPORTED (4 << 0) +#define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0) + +struct riscv_hwprobe { + abi_llong key; + abi_ullong value; +}; + +static void risc_hwprobe_fill_pairs(CPURISCVState *env, + struct riscv_hwprobe *pair, + size_t pair_count) +{ + const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); + + for (; pair_count > 0; pair_count--, pair++) { + abi_llong key; + abi_ullong value; + __put_user(0, &pair->value); + __get_user(key, &pair->key); + switch (key) { + case RISCV_HWPROBE_KEY_MVENDORID: + __put_user(cfg->mvendorid, &pair->value); + break; + case RISCV_HWPROBE_KEY_MARCHID: + __put_user(cfg->marchid, &pair->value); + break; + case RISCV_HWPROBE_KEY_MIMPID: + __put_user(cfg->mimpid, &pair->value); + break; + case RISCV_HWPROBE_KEY_BASE_BEHAVIOR: + value = riscv_has_ext(env, RVI) && + riscv_has_ext(env, RVM) && + riscv_has_ext(env, RVA) ? + RISCV_HWPROBE_BASE_BEHAVIOR_IMA : 0; + __put_user(value, &pair->value); + break; + case RISCV_HWPROBE_KEY_IMA_EXT_0: + value = riscv_has_ext(env, RVF) && + riscv_has_ext(env, RVD) ? + RISCV_HWPROBE_IMA_FD : 0; + value |= riscv_has_ext(env, RVC) ? + RISCV_HWPROBE_IMA_C : pair->value; + __put_user(value, &pair->value); + break; + case RISCV_HWPROBE_KEY_CPUPERF_0: + __put_user(RISCV_HWPROBE_MISALIGNED_FAST, &pair->value); + break; + default: + __put_user(-1, &pair->key); + break; + } + } +} + +static int cpu_set_valid(abi_long arg3, abi_long arg4) +{ + int ret, i, tmp; + size_t host_mask_size, target_mask_size; + unsigned long *host_mask; + + /* + * cpu_set_t represent CPU masks as bit masks of type unsigned long *. + * arg3 contains the cpu count. + */ + tmp = (8 * sizeof(abi_ulong)); + target_mask_size = ((arg3 + tmp - 1) / tmp) * sizeof(abi_ulong); + host_mask_size = (target_mask_size + (sizeof(*host_mask) - 1)) & + ~(sizeof(*host_mask) - 1); + + host_mask = alloca(host_mask_size); + + ret = target_to_host_cpu_mask(host_mask, host_mask_size, + arg4, target_mask_size); + if (ret != 0) { + return ret; + } + + for (i = 0 ; i < host_mask_size / sizeof(*host_mask); i++) { + if (host_mask[i] != 0) { + return 0; + } + } + return -TARGET_EINVAL; +} + +static abi_long do_riscv_hwprobe(CPUArchState *cpu_env, abi_long arg1, + abi_long arg2, abi_long arg3, + abi_long arg4, abi_long arg5) +{ + int ret; + struct riscv_hwprobe *host_pairs; + + /* flags must be 0 */ + if (arg5 != 0) { + return -TARGET_EINVAL; + } + + /* check cpu_set */ + if (arg3 != 0) { + ret = cpu_set_valid(arg3, arg4); + if (ret != 0) { + return ret; + } + } else if (arg4 != 0) { + return -TARGET_EINVAL; + } + + /* no pairs */ + if (arg2 == 0) { + return 0; + } + + host_pairs = lock_user(VERIFY_WRITE, arg1, + sizeof(*host_pairs) * (size_t)arg2, 0); + if (host_pairs == NULL) { + return -TARGET_EFAULT; + } + risc_hwprobe_fill_pairs(cpu_env, host_pairs, arg2); + unlock_user(host_pairs, arg1, sizeof(*host_pairs) * (size_t)arg2); + return 0; +} +#endif /* TARGET_NR_riscv_hwprobe */ + #if defined(TARGET_NR_pivot_root) && defined(__NR_pivot_root) _syscall2(int, pivot_root, const char *, new_root, const char *, put_old) #endif @@ -13665,6 +13806,11 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return ret; #endif +#if defined(TARGET_NR_riscv_hwprobe) + case TARGET_NR_riscv_hwprobe: + return do_riscv_hwprobe(cpu_env, arg1, arg2, arg3, arg4, arg5); +#endif + default: qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num); return -TARGET_ENOSYS; From 4556fdaa5cb7157b9adb3ee136f44d5952b94736 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Thu, 15 Jun 2023 14:32:57 +0800 Subject: [PATCH 25/54] target/riscv: Add properties for BF16 extensions Add ext_zfbfmin/zvfbfmin/zvfbfwma properties. Add require check for BF16 extensions. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Daniel Henrique Barboza Message-Id: <20230615063302.102409-2-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 20 ++++++++++++++++++++ target/riscv/cpu_cfg.h | 3 +++ 2 files changed, 23 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 0c9faf4633..0272b1d071 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1118,6 +1118,11 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } + if (cpu->cfg.ext_zfbfmin && !riscv_has_ext(env, RVF)) { + error_setg(errp, "Zfbfmin extension depends on F extension"); + return; + } + if (riscv_has_ext(env, RVD) && !riscv_has_ext(env, RVF)) { error_setg(errp, "D extension requires F extension"); return; @@ -1168,6 +1173,21 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } + if (cpu->cfg.ext_zvfbfmin && !cpu->cfg.ext_zfbfmin) { + error_setg(errp, "Zvfbfmin extension depends on Zfbfmin extension"); + return; + } + + if (cpu->cfg.ext_zvfbfmin && !cpu->cfg.ext_zve32f) { + error_setg(errp, "Zvfbfmin extension depends on Zve32f extension"); + return; + } + + if (cpu->cfg.ext_zvfbfwma && !cpu->cfg.ext_zvfbfmin) { + error_setg(errp, "Zvfbfwma extension depends on Zvfbfmin extension"); + return; + } + /* Set the ISA extensions, checks should have happened above */ if (cpu->cfg.ext_zhinx) { cpu->cfg.ext_zhinxmin = true; diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 6b7e736bc2..e9ee39d7de 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -75,6 +75,7 @@ struct RISCVCPUConfig { bool ext_svpbmt; bool ext_zdinx; bool ext_zawrs; + bool ext_zfbfmin; bool ext_zfh; bool ext_zfhmin; bool ext_zfinx; @@ -84,6 +85,8 @@ struct RISCVCPUConfig { bool ext_zve64f; bool ext_zve64d; bool ext_zmmul; + bool ext_zvfbfmin; + bool ext_zvfbfwma; bool ext_zvfh; bool ext_zvfhmin; bool ext_smaia; From 5d1270caac2ef7b8c887d4cb5a2444ba6d237516 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Thu, 15 Jun 2023 14:32:58 +0800 Subject: [PATCH 26/54] target/riscv: Add support for Zfbfmin extension Add trans_* and helper function for Zfbfmin instructions. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Richard Henderson Message-Id: <20230615063302.102409-3-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/fpu_helper.c | 12 +++++ target/riscv/helper.h | 4 ++ target/riscv/insn32.decode | 4 ++ target/riscv/insn_trans/trans_rvbf16.c.inc | 53 ++++++++++++++++++++++ target/riscv/insn_trans/trans_rvzfh.c.inc | 12 ++--- target/riscv/translate.c | 1 + 6 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 target/riscv/insn_trans/trans_rvbf16.c.inc diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index 5dd14d8390..eb5ee5c4c9 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -593,3 +593,15 @@ uint64_t helper_fcvt_d_h(CPURISCVState *env, uint64_t rs1) float16 frs1 = check_nanbox_h(env, rs1); return float16_to_float64(frs1, true, &env->fp_status); } + +uint64_t helper_fcvt_bf16_s(CPURISCVState *env, uint64_t rs1) +{ + float32 frs1 = check_nanbox_s(env, rs1); + return nanbox_h(env, float32_to_bfloat16(frs1, &env->fp_status)); +} + +uint64_t helper_fcvt_s_bf16(CPURISCVState *env, uint64_t rs1) +{ + float16 frs1 = check_nanbox_h(env, rs1); + return nanbox_s(env, bfloat16_to_float32(frs1, &env->fp_status)); +} diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 98e97810fd..ef8487f1ee 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -1153,3 +1153,7 @@ DEF_HELPER_FLAGS_3(sm4ks, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl) /* Zce helper */ DEF_HELPER_FLAGS_2(cm_jalt, TCG_CALL_NO_WG, tl, env, i32) + +/* BF16 functions */ +DEF_HELPER_FLAGS_2(fcvt_bf16_s, TCG_CALL_NO_RWG, i64, env, i64) +DEF_HELPER_FLAGS_2(fcvt_s_bf16, TCG_CALL_NO_RWG, i64, env, i64) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 73d5d1b045..45fdcad185 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -908,3 +908,7 @@ sm4ks .. 11010 ..... ..... 000 ..... 0110011 @k_aes # *** RV32 Zicond Standard Extension *** czero_eqz 0000111 ..... ..... 101 ..... 0110011 @r czero_nez 0000111 ..... ..... 111 ..... 0110011 @r + +# *** Zfbfmin Standard Extension *** +fcvt_bf16_s 0100010 01000 ..... ... ..... 1010011 @r2_rm +fcvt_s_bf16 0100000 00110 ..... ... ..... 1010011 @r2_rm diff --git a/target/riscv/insn_trans/trans_rvbf16.c.inc b/target/riscv/insn_trans/trans_rvbf16.c.inc new file mode 100644 index 0000000000..8cafde505f --- /dev/null +++ b/target/riscv/insn_trans/trans_rvbf16.c.inc @@ -0,0 +1,53 @@ +/* + * RISC-V translation routines for the BF16 Standard Extensions. + * + * Copyright (c) 2020-2023 PLCT Lab + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#define REQUIRE_ZFBFMIN(ctx) do { \ + if (!ctx->cfg_ptr->ext_zfbfmin) { \ + return false; \ + } \ +} while (0) + +static bool trans_fcvt_bf16_s(DisasContext *ctx, arg_fcvt_bf16_s *a) +{ + REQUIRE_FPU; + REQUIRE_ZFBFMIN(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_bf16_s(dest, cpu_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fcvt_s_bf16(DisasContext *ctx, arg_fcvt_s_bf16 *a) +{ + REQUIRE_FPU; + REQUIRE_ZFBFMIN(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_s_bf16(dest, cpu_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); + mark_fs_dirty(ctx); + return true; +} diff --git a/target/riscv/insn_trans/trans_rvzfh.c.inc b/target/riscv/insn_trans/trans_rvzfh.c.inc index 74dde37ff7..8b1e2519bb 100644 --- a/target/riscv/insn_trans/trans_rvzfh.c.inc +++ b/target/riscv/insn_trans/trans_rvzfh.c.inc @@ -28,8 +28,8 @@ } \ } while (0) -#define REQUIRE_ZFHMIN(ctx) do { \ - if (!ctx->cfg_ptr->ext_zfhmin) { \ +#define REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx) do { \ + if (!ctx->cfg_ptr->ext_zfhmin && !ctx->cfg_ptr->ext_zfbfmin) { \ return false; \ } \ } while (0) @@ -46,7 +46,7 @@ static bool trans_flh(DisasContext *ctx, arg_flh *a) TCGv t0; REQUIRE_FPU; - REQUIRE_ZFHMIN(ctx); + REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx); decode_save_opc(ctx); t0 = get_gpr(ctx, a->rs1, EXT_NONE); @@ -69,7 +69,7 @@ static bool trans_fsh(DisasContext *ctx, arg_fsh *a) TCGv t0; REQUIRE_FPU; - REQUIRE_ZFHMIN(ctx); + REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx); decode_save_opc(ctx); t0 = get_gpr(ctx, a->rs1, EXT_NONE); @@ -574,7 +574,7 @@ static bool trans_fcvt_h_wu(DisasContext *ctx, arg_fcvt_h_wu *a) static bool trans_fmv_x_h(DisasContext *ctx, arg_fmv_x_h *a) { REQUIRE_FPU; - REQUIRE_ZFHMIN(ctx); + REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx); TCGv dest = dest_gpr(ctx, a->rd); @@ -594,7 +594,7 @@ static bool trans_fmv_x_h(DisasContext *ctx, arg_fmv_x_h *a) static bool trans_fmv_h_x(DisasContext *ctx, arg_fmv_h_x *a) { REQUIRE_FPU; - REQUIRE_ZFHMIN(ctx); + REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx); TCGv t0 = get_gpr(ctx, a->rs1, EXT_ZERO); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 98d54c5617..621dd99241 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -1095,6 +1095,7 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) #include "insn_trans/trans_rvk.c.inc" #include "insn_trans/trans_privileged.c.inc" #include "insn_trans/trans_svinval.c.inc" +#include "insn_trans/trans_rvbf16.c.inc" #include "decode-xthead.c.inc" #include "insn_trans/trans_xthead.c.inc" #include "insn_trans/trans_xventanacondops.c.inc" From 87b27bfca432c69a2abfd7a9ebc1fa27876b31f5 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Thu, 15 Jun 2023 14:32:59 +0800 Subject: [PATCH 27/54] target/riscv: Add support for Zvfbfmin extension Add trans_* and helper function for Zvfbfmin instructions. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Richard Henderson Message-Id: <20230615063302.102409-4-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/helper.h | 3 + target/riscv/insn32.decode | 4 ++ target/riscv/insn_trans/trans_rvbf16.c.inc | 64 ++++++++++++++++++++++ target/riscv/vector_helper.c | 6 ++ 4 files changed, 77 insertions(+) diff --git a/target/riscv/helper.h b/target/riscv/helper.h index ef8487f1ee..fc48853e07 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -1157,3 +1157,6 @@ DEF_HELPER_FLAGS_2(cm_jalt, TCG_CALL_NO_WG, tl, env, i32) /* BF16 functions */ DEF_HELPER_FLAGS_2(fcvt_bf16_s, TCG_CALL_NO_RWG, i64, env, i64) DEF_HELPER_FLAGS_2(fcvt_s_bf16, TCG_CALL_NO_RWG, i64, env, i64) + +DEF_HELPER_5(vfncvtbf16_f_f_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfwcvtbf16_f_f_v, void, ptr, ptr, ptr, env, i32) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 45fdcad185..10d001f14d 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -912,3 +912,7 @@ czero_nez 0000111 ..... ..... 111 ..... 0110011 @r # *** Zfbfmin Standard Extension *** fcvt_bf16_s 0100010 01000 ..... ... ..... 1010011 @r2_rm fcvt_s_bf16 0100000 00110 ..... ... ..... 1010011 @r2_rm + +# *** Zvfbfmin Standard Extension *** +vfncvtbf16_f_f_w 010010 . ..... 11101 001 ..... 1010111 @r2_vm +vfwcvtbf16_f_f_v 010010 . ..... 01101 001 ..... 1010111 @r2_vm diff --git a/target/riscv/insn_trans/trans_rvbf16.c.inc b/target/riscv/insn_trans/trans_rvbf16.c.inc index 8cafde505f..f794a3f745 100644 --- a/target/riscv/insn_trans/trans_rvbf16.c.inc +++ b/target/riscv/insn_trans/trans_rvbf16.c.inc @@ -22,6 +22,12 @@ } \ } while (0) +#define REQUIRE_ZVFBFMIN(ctx) do { \ + if (!ctx->cfg_ptr->ext_zvfbfmin) { \ + return false; \ + } \ +} while (0) + static bool trans_fcvt_bf16_s(DisasContext *ctx, arg_fcvt_bf16_s *a) { REQUIRE_FPU; @@ -51,3 +57,61 @@ static bool trans_fcvt_s_bf16(DisasContext *ctx, arg_fcvt_s_bf16 *a) mark_fs_dirty(ctx); return true; } + +static bool trans_vfncvtbf16_f_f_w(DisasContext *ctx, arg_vfncvtbf16_f_f_w *a) +{ + REQUIRE_FPU; + REQUIRE_ZVFBFMIN(ctx); + + if (opfv_narrow_check(ctx, a) && (ctx->sew == MO_16)) { + uint32_t data = 0; + TCGLabel *over = gen_new_label(); + + gen_set_rm_chkfrm(ctx, RISCV_FRM_DYN); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); + + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, ctx->lmul); + data = FIELD_DP32(data, VDATA, VTA, ctx->vta); + data = FIELD_DP32(data, VDATA, VMA, ctx->vma); + tcg_gen_gvec_3_ptr(vreg_ofs(ctx, a->rd), vreg_ofs(ctx, 0), + vreg_ofs(ctx, a->rs2), cpu_env, + ctx->cfg_ptr->vlen / 8, + ctx->cfg_ptr->vlen / 8, data, + gen_helper_vfncvtbf16_f_f_w); + mark_vs_dirty(ctx); + gen_set_label(over); + return true; + } + return false; +} + +static bool trans_vfwcvtbf16_f_f_v(DisasContext *ctx, arg_vfwcvtbf16_f_f_v *a) +{ + REQUIRE_FPU; + REQUIRE_ZVFBFMIN(ctx); + + if (opfv_widen_check(ctx, a) && (ctx->sew == MO_16)) { + uint32_t data = 0; + TCGLabel *over = gen_new_label(); + + gen_set_rm_chkfrm(ctx, RISCV_FRM_DYN); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); + + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, ctx->lmul); + data = FIELD_DP32(data, VDATA, VTA, ctx->vta); + data = FIELD_DP32(data, VDATA, VMA, ctx->vma); + tcg_gen_gvec_3_ptr(vreg_ofs(ctx, a->rd), vreg_ofs(ctx, 0), + vreg_ofs(ctx, a->rs2), cpu_env, + ctx->cfg_ptr->vlen / 8, + ctx->cfg_ptr->vlen / 8, data, + gen_helper_vfwcvtbf16_f_f_v); + mark_vs_dirty(ctx); + gen_set_label(over); + return true; + } + return false; +} diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 1e06e7447c..4d2bd42155 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -4535,6 +4535,9 @@ RVVCALL(OPFVV1, vfwcvt_f_f_v_w, WOP_UU_W, H8, H4, float32_to_float64) GEN_VEXT_V_ENV(vfwcvt_f_f_v_h, 4) GEN_VEXT_V_ENV(vfwcvt_f_f_v_w, 8) +RVVCALL(OPFVV1, vfwcvtbf16_f_f_v, WOP_UU_H, H4, H2, bfloat16_to_float32) +GEN_VEXT_V_ENV(vfwcvtbf16_f_f_v, 4) + /* Narrowing Floating-Point/Integer Type-Convert Instructions */ /* (TD, T2, TX2) */ #define NOP_UU_B uint8_t, uint16_t, uint32_t @@ -4581,6 +4584,9 @@ RVVCALL(OPFVV1, vfncvt_f_f_w_w, NOP_UU_W, H4, H8, float64_to_float32) GEN_VEXT_V_ENV(vfncvt_f_f_w_h, 2) GEN_VEXT_V_ENV(vfncvt_f_f_w_w, 4) +RVVCALL(OPFVV1, vfncvtbf16_f_f_w, NOP_UU_H, H2, H4, float32_to_bfloat16) +GEN_VEXT_V_ENV(vfncvtbf16_f_f_w, 2) + /* * Vector Reduction Operations */ From adf772b0f788b80bea427c945a48343610d4af49 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Thu, 15 Jun 2023 14:33:00 +0800 Subject: [PATCH 28/54] target/riscv: Add support for Zvfbfwma extension Add trans_* and helper function for Zvfbfwma instructions. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Richard Henderson Message-Id: <20230615063302.102409-5-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/helper.h | 3 ++ target/riscv/insn32.decode | 4 ++ target/riscv/insn_trans/trans_rvbf16.c.inc | 58 ++++++++++++++++++++++ target/riscv/vector_helper.c | 11 ++++ 4 files changed, 76 insertions(+) diff --git a/target/riscv/helper.h b/target/riscv/helper.h index fc48853e07..3170b8daa6 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -1160,3 +1160,6 @@ DEF_HELPER_FLAGS_2(fcvt_s_bf16, TCG_CALL_NO_RWG, i64, env, i64) DEF_HELPER_5(vfncvtbf16_f_f_w, void, ptr, ptr, ptr, env, i32) DEF_HELPER_5(vfwcvtbf16_f_f_v, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_6(vfwmaccbf16_vv, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwmaccbf16_vf, void, ptr, ptr, i64, ptr, env, i32) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 10d001f14d..8c5d293f07 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -916,3 +916,7 @@ fcvt_s_bf16 0100000 00110 ..... ... ..... 1010011 @r2_rm # *** Zvfbfmin Standard Extension *** vfncvtbf16_f_f_w 010010 . ..... 11101 001 ..... 1010111 @r2_vm vfwcvtbf16_f_f_v 010010 . ..... 01101 001 ..... 1010111 @r2_vm + +# *** Zvfbfwma Standard Extension *** +vfwmaccbf16_vv 111011 . ..... ..... 001 ..... 1010111 @r_vm +vfwmaccbf16_vf 111011 . ..... ..... 101 ..... 1010111 @r_vm diff --git a/target/riscv/insn_trans/trans_rvbf16.c.inc b/target/riscv/insn_trans/trans_rvbf16.c.inc index f794a3f745..911bc29908 100644 --- a/target/riscv/insn_trans/trans_rvbf16.c.inc +++ b/target/riscv/insn_trans/trans_rvbf16.c.inc @@ -28,6 +28,12 @@ } \ } while (0) +#define REQUIRE_ZVFBFWMA(ctx) do { \ + if (!ctx->cfg_ptr->ext_zvfbfwma) { \ + return false; \ + } \ +} while (0) + static bool trans_fcvt_bf16_s(DisasContext *ctx, arg_fcvt_bf16_s *a) { REQUIRE_FPU; @@ -115,3 +121,55 @@ static bool trans_vfwcvtbf16_f_f_v(DisasContext *ctx, arg_vfwcvtbf16_f_f_v *a) } return false; } + +static bool trans_vfwmaccbf16_vv(DisasContext *ctx, arg_vfwmaccbf16_vv *a) +{ + REQUIRE_FPU; + REQUIRE_ZVFBFWMA(ctx); + + if (require_rvv(ctx) && vext_check_isa_ill(ctx) && (ctx->sew == MO_16) && + vext_check_dss(ctx, a->rd, a->rs1, a->rs2, a->vm)) { + uint32_t data = 0; + TCGLabel *over = gen_new_label(); + + gen_set_rm_chkfrm(ctx, RISCV_FRM_DYN); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); + + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, ctx->lmul); + data = FIELD_DP32(data, VDATA, VTA, ctx->vta); + data = FIELD_DP32(data, VDATA, VMA, ctx->vma); + tcg_gen_gvec_4_ptr(vreg_ofs(ctx, a->rd), vreg_ofs(ctx, 0), + vreg_ofs(ctx, a->rs1), + vreg_ofs(ctx, a->rs2), cpu_env, + ctx->cfg_ptr->vlen / 8, + ctx->cfg_ptr->vlen / 8, data, + gen_helper_vfwmaccbf16_vv); + mark_vs_dirty(ctx); + gen_set_label(over); + return true; + } + return false; +} + +static bool trans_vfwmaccbf16_vf(DisasContext *ctx, arg_vfwmaccbf16_vf *a) +{ + REQUIRE_FPU; + REQUIRE_ZVFBFWMA(ctx); + + if (require_rvv(ctx) && (ctx->sew == MO_16) && vext_check_isa_ill(ctx) && + vext_check_ds(ctx, a->rd, a->rs2, a->vm)) { + uint32_t data = 0; + + gen_set_rm(ctx, RISCV_FRM_DYN); + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, ctx->lmul); + data = FIELD_DP32(data, VDATA, VTA, ctx->vta); + data = FIELD_DP32(data, VDATA, VMA, ctx->vma); + return opfvf_trans(a->rd, a->rs1, a->rs2, data, + gen_helper_vfwmaccbf16_vf, ctx); + } + + return false; +} diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 4d2bd42155..71bb9b4457 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -3554,6 +3554,17 @@ RVVCALL(OPFVF3, vfwmacc_vf_w, WOP_UUU_W, H8, H4, fwmacc32) GEN_VEXT_VF(vfwmacc_vf_h, 4) GEN_VEXT_VF(vfwmacc_vf_w, 8) +static uint32_t fwmaccbf16(uint16_t a, uint16_t b, uint32_t d, float_status *s) +{ + return float32_muladd(bfloat16_to_float32(a, s), + bfloat16_to_float32(b, s), d, 0, s); +} + +RVVCALL(OPFVV3, vfwmaccbf16_vv, WOP_UUU_H, H4, H2, H2, fwmaccbf16) +GEN_VEXT_VV_ENV(vfwmaccbf16_vv, 4) +RVVCALL(OPFVF3, vfwmaccbf16_vf, WOP_UUU_H, H4, H2, fwmacc16) +GEN_VEXT_VF(vfwmaccbf16_vf, 4) + static uint32_t fwnmacc16(uint16_t a, uint16_t b, uint32_t d, float_status *s) { return float32_muladd(float16_to_float32(a, true, s), From 889caa44011d32a584c8e3a31bf91a7f9b73f2a3 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Thu, 15 Jun 2023 14:33:01 +0800 Subject: [PATCH 29/54] target/riscv: Expose properties for BF16 extensions Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Daniel Henrique Barboza Message-Id: <20230615063302.102409-6-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 0272b1d071..fd647534cf 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -94,6 +94,7 @@ static const struct isa_ext_data isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zifencei, PRIV_VERSION_1_10_0, ext_ifencei), ISA_EXT_DATA_ENTRY(zihintpause, PRIV_VERSION_1_10_0, ext_zihintpause), ISA_EXT_DATA_ENTRY(zawrs, PRIV_VERSION_1_12_0, ext_zawrs), + ISA_EXT_DATA_ENTRY(zfbfmin, PRIV_VERSION_1_12_0, ext_zfbfmin), ISA_EXT_DATA_ENTRY(zfh, PRIV_VERSION_1_11_0, ext_zfh), ISA_EXT_DATA_ENTRY(zfhmin, PRIV_VERSION_1_11_0, ext_zfhmin), ISA_EXT_DATA_ENTRY(zfinx, PRIV_VERSION_1_12_0, ext_zfinx), @@ -125,6 +126,8 @@ static const struct isa_ext_data isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zve32f, PRIV_VERSION_1_10_0, ext_zve32f), ISA_EXT_DATA_ENTRY(zve64f, PRIV_VERSION_1_10_0, ext_zve64f), ISA_EXT_DATA_ENTRY(zve64d, PRIV_VERSION_1_10_0, ext_zve64d), + ISA_EXT_DATA_ENTRY(zvfbfmin, PRIV_VERSION_1_12_0, ext_zvfbfmin), + ISA_EXT_DATA_ENTRY(zvfbfwma, PRIV_VERSION_1_12_0, ext_zvfbfwma), ISA_EXT_DATA_ENTRY(zvfh, PRIV_VERSION_1_12_0, ext_zvfh), ISA_EXT_DATA_ENTRY(zvfhmin, PRIV_VERSION_1_12_0, ext_zvfhmin), ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx), @@ -1762,6 +1765,10 @@ static Property riscv_cpu_extensions[] = { DEFINE_PROP_BOOL("x-zvfh", RISCVCPU, cfg.ext_zvfh, false), DEFINE_PROP_BOOL("x-zvfhmin", RISCVCPU, cfg.ext_zvfhmin, false), + DEFINE_PROP_BOOL("x-zfbfmin", RISCVCPU, cfg.ext_zfbfmin, false), + DEFINE_PROP_BOOL("x-zvfbfmin", RISCVCPU, cfg.ext_zvfbfmin, false), + DEFINE_PROP_BOOL("x-zvfbfwma", RISCVCPU, cfg.ext_zvfbfwma, false), + DEFINE_PROP_END_OF_LIST(), }; From 60ef34a48cf1990c55f0ff86086f40d7dfbb4181 Mon Sep 17 00:00:00 2001 From: Jason Chien Date: Tue, 27 Jun 2023 07:48:52 +0000 Subject: [PATCH 30/54] target/riscv: Set the correct exception for implict G-stage translation fail The privileged spec states: For a memory access made to support VS-stage address translation (such as to read/write a VS-level page table), permissions are checked as though for a load or store, not for the original access type. However, any exception is always reported for the original access type (instruction, load, or store/AMO). The current implementation converts the access type to LOAD if implicit G-stage translation fails which results in only reporting "Load guest-page fault". This commit removes the convertion of access type, so the reported exception conforms to the spec. Signed-off-by: Jason Chien Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-Id: <20230627074915.7686-1-jason.chien@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 1 - 1 file changed, 1 deletion(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index bb9d923818..9f611d89bb 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1282,7 +1282,6 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (ret == TRANSLATE_G_STAGE_FAIL) { first_stage_error = false; two_stage_indirect_error = true; - access_type = MMU_DATA_LOAD; } qemu_log_mask(CPU_LOG_MMU, From 32b2d75bf7fab6fa82dd01b2413ca14753b90973 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Mon, 3 Jul 2023 15:17:59 +0800 Subject: [PATCH 31/54] target/riscv: Add disas support for BF16 extensions Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Acked-by: Alistair Francis Message-Id: <20230703071759.86775-2-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- disas/riscv.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/disas/riscv.c b/disas/riscv.c index 94e568a7e9..9f0195be30 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -819,6 +819,16 @@ typedef enum { rv_op_cm_jalt = 788, rv_op_czero_eqz = 789, rv_op_czero_nez = 790, + rv_op_fcvt_bf16_s = 791, + rv_op_fcvt_s_bf16 = 792, + rv_op_vfncvtbf16_f_f_w = 793, + rv_op_vfwcvtbf16_f_f_v = 794, + rv_op_vfwmaccbf16_vv = 795, + rv_op_vfwmaccbf16_vf = 796, + rv_op_flh = 797, + rv_op_fsh = 798, + rv_op_fmv_h_x = 799, + rv_op_fmv_x_h = 800, } rv_op; /* register names */ @@ -1905,6 +1915,16 @@ const rv_opcode_data rvi_opcode_data[] = { { "cm.jalt", rv_codec_zcmt_jt, rv_fmt_zcmt_index, NULL, 0 }, { "czero.eqz", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "czero.nez", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "fcvt.bf16.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fcvt.s.bf16", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "vfncvtbf16.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwcvtbf16.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwmaccbf16.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwmaccbf16.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "flh", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 }, + { "fsh", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 }, + { "fmv.h.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, + { "fmv.x.h", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, }; /* CSR names */ @@ -2380,6 +2400,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 3: op = rv_op_vloxei8_v; break; } break; + case 1: op = rv_op_flh; break; case 2: op = rv_op_flw; break; case 3: op = rv_op_fld; break; case 4: op = rv_op_flq; break; @@ -2583,6 +2604,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 3: op = rv_op_vsoxei8_v; break; } break; + case 1: op = rv_op_fsh; break; case 2: op = rv_op_fsw; break; case 3: op = rv_op_fsd; break; case 4: op = rv_op_fsq; break; @@ -2860,6 +2882,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) switch ((inst >> 20) & 0b11111) { case 1: op = rv_op_fcvt_s_d; break; case 3: op = rv_op_fcvt_s_q; break; + case 6: op = rv_op_fcvt_s_bf16; break; } break; case 33: @@ -2868,6 +2891,11 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 3: op = rv_op_fcvt_d_q; break; } break; + case 34: + switch (((inst >> 20) & 0b11111)) { + case 8: op = rv_op_fcvt_bf16_s; break; + } + break; case 35: switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_q_s; break; @@ -2972,6 +3000,12 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 1: op = rv_op_fclass_d; break; } break; + case 114: + switch (((inst >> 17) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { + case 0: op = rv_op_fmv_x_h; break; + } + break; case 115: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { @@ -2991,6 +3025,12 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 0: op = rv_op_fmv_d_x; break; } break; + case 122: + switch (((inst >> 17) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { + case 0: op = rv_op_fmv_h_x; break; + } + break; case 123: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { @@ -3087,6 +3127,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 10: op = rv_op_vfwcvt_f_xu_v; break; case 11: op = rv_op_vfwcvt_f_x_v; break; case 12: op = rv_op_vfwcvt_f_f_v; break; + case 13: op = rv_op_vfwcvtbf16_f_f_v; break; case 14: op = rv_op_vfwcvt_rtz_xu_f_v; break; case 15: op = rv_op_vfwcvt_rtz_x_f_v; break; case 16: op = rv_op_vfncvt_xu_f_w; break; @@ -3097,6 +3138,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 21: op = rv_op_vfncvt_rod_f_f_w; break; case 22: op = rv_op_vfncvt_rtz_xu_f_w; break; case 23: op = rv_op_vfncvt_rtz_x_f_w; break; + case 29: op = rv_op_vfncvtbf16_f_f_w; break; } break; case 19: @@ -3128,6 +3170,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 52: op = rv_op_vfwadd_wv; break; case 54: op = rv_op_vfwsub_wv; break; case 56: op = rv_op_vfwmul_vv; break; + case 59: op = rv_op_vfwmaccbf16_vv; break; case 60: op = rv_op_vfwmacc_vv; break; case 61: op = rv_op_vfwnmacc_vv; break; case 62: op = rv_op_vfwmsac_vv; break; @@ -3366,6 +3409,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 52: op = rv_op_vfwadd_wf; break; case 54: op = rv_op_vfwsub_wf; break; case 56: op = rv_op_vfwmul_vf; break; + case 59: op = rv_op_vfwmaccbf16_vf; break; case 60: op = rv_op_vfwmacc_vf; break; case 61: op = rv_op_vfwnmacc_vf; break; case 62: op = rv_op_vfwmsac_vf; break; From c5cc248b478bc21e50845ba6b2414c4a688e195f Mon Sep 17 00:00:00 2001 From: "yang.zhang" Date: Fri, 7 Jul 2023 11:23:06 +0800 Subject: [PATCH 32/54] target/riscv KVM_RISCV_SET_TIMER macro is not configured correctly Should set/get riscv all reg timer,i.e, time/compare/frequency/state. Signed-off-by: Yang Zhang Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1688 Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-Id: <20230707032306.4606-1-gaoshanliukou@163.com> Signed-off-by: Alistair Francis --- target/riscv/kvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c index 0f932a5b96..c3976a588d 100644 --- a/target/riscv/kvm.c +++ b/target/riscv/kvm.c @@ -99,7 +99,7 @@ static uint64_t kvm_riscv_reg_id(CPURISCVState *env, uint64_t type, #define KVM_RISCV_SET_TIMER(cs, env, name, reg) \ do { \ - int ret = kvm_set_one_reg(cs, RISCV_TIMER_REG(env, time), ®); \ + int ret = kvm_set_one_reg(cs, RISCV_TIMER_REG(env, name), ®); \ if (ret) { \ abort(); \ } \ From 49554856f0b6f622ab6afdcc275d4ab2ecb3625c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 5 Jul 2023 20:59:37 -0700 Subject: [PATCH 33/54] riscv: Generate devicetree only after machine initialization is complete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the devicetree is created before machine initialization is complete, it misses dynamic devices. Specifically, the tpm device is not added to the devicetree file and is therefore not instantiated in Linux. Load/create devicetree in virt_machine_done() to solve the problem. Cc: Daniel Henrique Barboza Cc: Alistair Francis Cc: Daniel Henrique Barboza Fixes: 325b7c4e75 hw/riscv: Enable TPM backends Signed-off-by: Guenter Roeck Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-Id: <20230706035937.1870483-1-linux@roeck-us.net> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index cdb88a1529..96b01703a9 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1249,6 +1249,17 @@ static void virt_machine_done(Notifier *notifier, void *data) uint64_t kernel_entry = 0; BlockBackend *pflash_blk0; + /* load/create device tree */ + if (machine->dtb) { + machine->fdt = load_device_tree(machine->dtb, &s->fdt_size); + if (!machine->fdt) { + error_report("load_device_tree() failed"); + exit(1); + } + } else { + create_fdt(s, memmap); + } + /* * Only direct boot kernel is currently supported for KVM VM, * so the "-bios" parameter is not supported when KVM is enabled. @@ -1509,17 +1520,6 @@ static void virt_machine_init(MachineState *machine) } virt_flash_map(s, system_memory); - /* load/create device tree */ - if (machine->dtb) { - machine->fdt = load_device_tree(machine->dtb, &s->fdt_size); - if (!machine->fdt) { - error_report("load_device_tree() failed"); - exit(1); - } - } else { - create_fdt(s, memmap); - } - s->machine_done.notify = virt_machine_done; qemu_add_machine_init_done_notifier(&s->machine_done); } From 1ad53688b9e15b179dbbfe44ee8b86d7e46715a8 Mon Sep 17 00:00:00 2001 From: Lakshmi Bai Raja Subramanian Date: Tue, 20 Jun 2023 19:20:06 +0530 Subject: [PATCH 34/54] hw/riscv: virt: Convert fdt_load_addr to uint64_t fdt_load_addr was previously declared as uint32_t which doe not match with the return type of riscv_compute_fdt_addr(). This patch modifies the fdt_load_addr type from a uint32_t to a uint64_t to match the riscv_compute_fdt_addr() return type. This fixes calculating the fdt address when DRAM is mapped to higher 64-bit address. Reviewed-by: Daniel Henrique Barboza Signed-off-by: Lakshmi Bai Raja Subramanian [ Change by AF: - Cleanup commit title and message ] Reviewed-by: Alistair Francis Message-Id: <168872495192.6334.3845988291412774261-1@git.sr.ht> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 96b01703a9..2b380eff73 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1245,7 +1245,7 @@ static void virt_machine_done(Notifier *notifier, void *data) target_ulong start_addr = memmap[VIRT_DRAM].base; target_ulong firmware_end_addr, kernel_start_addr; const char *firmware_name = riscv_default_firmware_name(&s->soc[0]); - uint32_t fdt_load_addr; + uint64_t fdt_load_addr; uint64_t kernel_entry = 0; BlockBackend *pflash_blk0; From eddabb6b888fa915ed679637983d8f39adfb725c Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:19 -0300 Subject: [PATCH 35/54] target/riscv: skip features setup for KVM CPUs As it is today it's not possible to use '-cpu host' if the RISC-V host has RVH enabled. This is the resulting error: $ ./qemu/build/qemu-system-riscv64 \ -machine virt,accel=kvm -m 2G -smp 1 \ -nographic -snapshot -kernel ./guest_imgs/Image \ -initrd ./guest_imgs/rootfs_kvm_riscv64.img \ -append "earlycon=sbi root=/dev/ram rw" \ -cpu host qemu-system-riscv64: H extension requires priv spec 1.12.0 This happens because we're checking for priv spec for all CPUs, and since we're not setting env->priv_ver for the 'host' CPU, it's being default to zero (i.e. PRIV_SPEC_1_10_0). In reality env->priv_ver does not make sense when running with the KVM 'host' CPU. It's used to gate certain CSRs/extensions during translation to make them unavailable if the hart declares an older spec version. It doesn't have any other use. E.g. OpenSBI version 1.2 retrieves the spec checking if the CSR_MCOUNTEREN, CSR_MCOUNTINHIBIT and CSR_MENVCFG CSRs are available [1]. 'priv_ver' is just one example. We're doing a lot of feature validation and setup during riscv_cpu_realize() that it doesn't apply to KVM CPUs. Validating the feature set for those CPUs is a KVM problem that should be handled in KVM specific code. The new riscv_cpu_realize_tcg() helper contains all validation logic that are applicable to TCG CPUs only. riscv_cpu_realize() verifies if we're running TCG and, if it's the case, proceed with the usual TCG realize() logic. [1] lib/sbi/sbi_hart.c, hart_detect_features() Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-Id: <20230706101738.460804-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index fd647534cf..6232e6513b 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -34,6 +34,7 @@ #include "migration/vmstate.h" #include "fpu/softfloat-helpers.h" #include "sysemu/kvm.h" +#include "sysemu/tcg.h" #include "kvm_riscv.h" #include "tcg/tcg.h" @@ -1386,20 +1387,12 @@ static void riscv_cpu_validate_misa_priv(CPURISCVState *env, Error **errp) } } -static void riscv_cpu_realize(DeviceState *dev, Error **errp) +static void riscv_cpu_realize_tcg(DeviceState *dev, Error **errp) { - CPUState *cs = CPU(dev); RISCVCPU *cpu = RISCV_CPU(dev); CPURISCVState *env = &cpu->env; - RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - return; - } - riscv_cpu_validate_misa_mxl(cpu, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -1434,7 +1427,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) } #ifndef CONFIG_USER_ONLY - cs->tcg_cflags |= CF_PCREL; + CPU(dev)->tcg_cflags |= CF_PCREL; if (cpu->cfg.ext_sstc) { riscv_timer_init(cpu); @@ -1447,6 +1440,28 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) } } #endif +} + +static void riscv_cpu_realize(DeviceState *dev, Error **errp) +{ + CPUState *cs = CPU(dev); + RISCVCPU *cpu = RISCV_CPU(dev); + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + + if (tcg_enabled()) { + riscv_cpu_realize_tcg(dev, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + } riscv_cpu_finalize_features(cpu, &local_err); if (local_err != NULL) { From 43d1de32f8ef4502005e6b48e6602408fa711390 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:20 -0300 Subject: [PATCH 36/54] hw/riscv/virt.c: skip 'mmu-type' FDT if satp mode not set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The absence of a satp mode in riscv_host_cpu_init() is causing the following error: $ ./qemu/build/qemu-system-riscv64 -machine virt,accel=kvm \ -m 2G -smp 1 -nographic -snapshot \ -kernel ./guest_imgs/Image \ -initrd ./guest_imgs/rootfs_kvm_riscv64.img \ -append "earlycon=sbi root=/dev/ram rw" \ -cpu host ** ERROR:../target/riscv/cpu.c:320:satp_mode_str: code should not be reached Bail out! ERROR:../target/riscv/cpu.c:320:satp_mode_str: code should not be reached Aborted The error is triggered from create_fdt_socket_cpus() in hw/riscv/virt.c. It's trying to get satp_mode_str for a NULL cpu->cfg.satp_mode.map. For this KVM cpu we would need to inherit the satp supported modes from the RISC-V host. At this moment this is not possible because the KVM driver does not support it. And even when it does we can't just let this broken for every other older kernel. Since mmu-type is not a required node, according to [1], skip the 'mmu-type' FDT node if there's no satp_mode set. We'll revisit this logic when we can get satp information from KVM. [1] https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/riscv/cpus.yaml Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230706101738.460804-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 2b380eff73..d90286dc46 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -244,13 +244,13 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket, s->soc[socket].hartid_base + cpu); qemu_fdt_add_subnode(ms->fdt, cpu_name); - satp_mode_max = satp_mode_max_from_map( - s->soc[socket].harts[cpu].cfg.satp_mode.map); - sv_name = g_strdup_printf("riscv,%s", - satp_mode_str(satp_mode_max, is_32_bit)); - qemu_fdt_setprop_string(ms->fdt, cpu_name, "mmu-type", sv_name); - g_free(sv_name); - + if (cpu_ptr->cfg.satp_mode.supported != 0) { + satp_mode_max = satp_mode_max_from_map(cpu_ptr->cfg.satp_mode.map); + sv_name = g_strdup_printf("riscv,%s", + satp_mode_str(satp_mode_max, is_32_bit)); + qemu_fdt_setprop_string(ms->fdt, cpu_name, "mmu-type", sv_name); + g_free(sv_name); + } name = riscv_isa_string(cpu_ptr); qemu_fdt_setprop_string(ms->fdt, cpu_name, "riscv,isa", name); From 1e341500459e0900a07b0c3a86de4526ebe4496d Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:21 -0300 Subject: [PATCH 37/54] target/riscv/cpu.c: restrict 'mvendorid' value We're going to change the handling of mvendorid/marchid/mimpid by the KVM driver. Since these are always present in all CPUs let's put the same validation for everyone. It doesn't make sense to allow 'mvendorid' to be different than it is already set in named (vendor) CPUs. Generic (dynamic) CPUs can have any 'mvendorid' they want. Change 'mvendorid' to be a class property created via 'object_class_property_add', instead of using the DEFINE_PROP_UINT32() macro. This allow us to define a custom setter for it that will verify, for named CPUs, if mvendorid is different than it is already set by the CPU. This is the error thrown for the 'veyron-v1' CPU if 'mvendorid' is set to an invalid value: $ qemu-system-riscv64 -M virt -nographic -cpu veyron-v1,mvendorid=2 qemu-system-riscv64: can't apply global veyron-v1-riscv-cpu.mvendorid=2: Unable to change veyron-v1-riscv-cpu mvendorid (0x61f) Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-Id: <20230706101738.460804-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 6232e6513b..a778241d9f 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1812,7 +1812,6 @@ static void riscv_cpu_add_user_properties(Object *obj) static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true), - DEFINE_PROP_UINT32("mvendorid", RISCVCPU, cfg.mvendorid, 0), DEFINE_PROP_UINT64("marchid", RISCVCPU, cfg.marchid, RISCV_CPU_MARCHID), DEFINE_PROP_UINT64("mimpid", RISCVCPU, cfg.mimpid, RISCV_CPU_MIMPID), @@ -1899,6 +1898,40 @@ static const struct TCGCPUOps riscv_tcg_ops = { #endif /* !CONFIG_USER_ONLY */ }; +static bool riscv_cpu_is_dynamic(Object *cpu_obj) +{ + return object_dynamic_cast(cpu_obj, TYPE_RISCV_DYNAMIC_CPU) != NULL; +} + +static void cpu_set_mvendorid(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + bool dynamic_cpu = riscv_cpu_is_dynamic(obj); + RISCVCPU *cpu = RISCV_CPU(obj); + uint32_t prev_val = cpu->cfg.mvendorid; + uint32_t value; + + if (!visit_type_uint32(v, name, &value, errp)) { + return; + } + + if (!dynamic_cpu && prev_val != value) { + error_setg(errp, "Unable to change %s mvendorid (0x%x)", + object_get_typename(obj), prev_val); + return; + } + + cpu->cfg.mvendorid = value; +} + +static void cpu_get_mvendorid(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + bool value = RISCV_CPU(obj)->cfg.mvendorid; + + visit_type_bool(v, name, &value, errp); +} + static void riscv_cpu_class_init(ObjectClass *c, void *data) { RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); @@ -1930,6 +1963,9 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) cc->gdb_get_dynamic_xml = riscv_gdb_get_dynamic_xml; cc->tcg_ops = &riscv_tcg_ops; + object_class_property_add(c, "mvendorid", "uint32", cpu_get_mvendorid, + cpu_set_mvendorid, NULL, NULL); + device_class_set_props(dc, riscv_cpu_properties); } From a1863ad368d32b53153bf417a83f01c85bb9d82c Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:22 -0300 Subject: [PATCH 38/54] target/riscv/cpu.c: restrict 'mimpid' value Following the same logic used with 'mvendorid' let's also restrict 'mimpid' for named CPUs. Generic CPUs keep setting the value freely. Note that we're getting rid of the default RISCV_CPU_MARCHID value. The reason is that this is not a good default since it's dynamic, changing with with every QEMU version, regardless of whether the actual implementation of the CPU changed from one QEMU version to the other. Named CPU should set it to a meaningful value instead and generic CPUs can set whatever they want. This is the error thrown for an invalid 'mimpid' value for the veyron-v1 CPU: $ ./qemu-system-riscv64 -M virt -nographic -cpu veyron-v1,mimpid=2 qemu-system-riscv64: can't apply global veyron-v1-riscv-cpu.mimpid=2: Unable to change veyron-v1-riscv-cpu mimpid (0x111) Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-Id: <20230706101738.460804-5-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index a778241d9f..477f8f8f97 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -43,7 +43,6 @@ #define RISCV_CPU_MARCHID ((QEMU_VERSION_MAJOR << 16) | \ (QEMU_VERSION_MINOR << 8) | \ (QEMU_VERSION_MICRO)) -#define RISCV_CPU_MIMPID RISCV_CPU_MARCHID static const char riscv_single_letter_exts[] = "IEMAFDQCPVH"; @@ -1813,7 +1812,6 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true), DEFINE_PROP_UINT64("marchid", RISCVCPU, cfg.marchid, RISCV_CPU_MARCHID), - DEFINE_PROP_UINT64("mimpid", RISCVCPU, cfg.mimpid, RISCV_CPU_MIMPID), #ifndef CONFIG_USER_ONLY DEFINE_PROP_UINT64("resetvec", RISCVCPU, env.resetvec, DEFAULT_RSTVEC), @@ -1932,6 +1930,35 @@ static void cpu_get_mvendorid(Object *obj, Visitor *v, const char *name, visit_type_bool(v, name, &value, errp); } +static void cpu_set_mimpid(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + bool dynamic_cpu = riscv_cpu_is_dynamic(obj); + RISCVCPU *cpu = RISCV_CPU(obj); + uint64_t prev_val = cpu->cfg.mimpid; + uint64_t value; + + if (!visit_type_uint64(v, name, &value, errp)) { + return; + } + + if (!dynamic_cpu && prev_val != value) { + error_setg(errp, "Unable to change %s mimpid (0x%" PRIu64 ")", + object_get_typename(obj), prev_val); + return; + } + + cpu->cfg.mimpid = value; +} + +static void cpu_get_mimpid(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + bool value = RISCV_CPU(obj)->cfg.mimpid; + + visit_type_bool(v, name, &value, errp); +} + static void riscv_cpu_class_init(ObjectClass *c, void *data) { RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); @@ -1966,6 +1993,9 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) object_class_property_add(c, "mvendorid", "uint32", cpu_get_mvendorid, cpu_set_mvendorid, NULL, NULL); + object_class_property_add(c, "mimpid", "uint64", cpu_get_mimpid, + cpu_set_mimpid, NULL, NULL); + device_class_set_props(dc, riscv_cpu_properties); } From d6a427e2c0b249bc8d3e0b4d7c5150f829d0907a Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:23 -0300 Subject: [PATCH 39/54] target/riscv/cpu.c: restrict 'marchid' value 'marchid' shouldn't be set to a different value as previously set for named CPUs. For all other CPUs it shouldn't be freely set either - the spec requires that 'marchid' can't have the MSB (most significant bit) set and every other bit set to zero, i.e. 0x80000000 is an invalid 'marchid' value for 32 bit CPUs. As with 'mimpid', setting a default value based on the current QEMU version is not a good idea because it implies that the CPU implementation changes from one QEMU version to the other. Named CPUs should set 'marchid' to a meaningful value instead, and generic CPUs can set to any valid value. For the 'veyron-v1' CPU this is the error thrown if 'marchid' is set to a different val: $ ./build/qemu-system-riscv64 -M virt -nographic -cpu veyron-v1,marchid=0x80000000 qemu-system-riscv64: can't apply global veyron-v1-riscv-cpu.marchid=0x80000000: Unable to change veyron-v1-riscv-cpu marchid (0x8000000000010000) And, for generics CPUs, this is the error when trying to set to an invalid val: $ ./build/qemu-system-riscv64 -M virt -nographic -cpu rv64,marchid=0x8000000000000000 qemu-system-riscv64: can't apply global rv64-riscv-cpu.marchid=0x8000000000000000: Unable to set marchid with MSB (64) bit set and the remaining bits zero Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-Id: <20230706101738.460804-6-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 60 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 477f8f8f97..9080d021fa 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -39,11 +39,6 @@ #include "tcg/tcg.h" /* RISC-V CPU definitions */ - -#define RISCV_CPU_MARCHID ((QEMU_VERSION_MAJOR << 16) | \ - (QEMU_VERSION_MINOR << 8) | \ - (QEMU_VERSION_MICRO)) - static const char riscv_single_letter_exts[] = "IEMAFDQCPVH"; struct isa_ext_data { @@ -1811,8 +1806,6 @@ static void riscv_cpu_add_user_properties(Object *obj) static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true), - DEFINE_PROP_UINT64("marchid", RISCVCPU, cfg.marchid, RISCV_CPU_MARCHID), - #ifndef CONFIG_USER_ONLY DEFINE_PROP_UINT64("resetvec", RISCVCPU, env.resetvec, DEFAULT_RSTVEC), #endif @@ -1959,6 +1952,56 @@ static void cpu_get_mimpid(Object *obj, Visitor *v, const char *name, visit_type_bool(v, name, &value, errp); } +static void cpu_set_marchid(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + bool dynamic_cpu = riscv_cpu_is_dynamic(obj); + RISCVCPU *cpu = RISCV_CPU(obj); + uint64_t prev_val = cpu->cfg.marchid; + uint64_t value, invalid_val; + uint32_t mxlen = 0; + + if (!visit_type_uint64(v, name, &value, errp)) { + return; + } + + if (!dynamic_cpu && prev_val != value) { + error_setg(errp, "Unable to change %s marchid (0x%" PRIu64 ")", + object_get_typename(obj), prev_val); + return; + } + + switch (riscv_cpu_mxl(&cpu->env)) { + case MXL_RV32: + mxlen = 32; + break; + case MXL_RV64: + case MXL_RV128: + mxlen = 64; + break; + default: + g_assert_not_reached(); + } + + invalid_val = 1LL << (mxlen - 1); + + if (value == invalid_val) { + error_setg(errp, "Unable to set marchid with MSB (%u) bit set " + "and the remaining bits zero", mxlen); + return; + } + + cpu->cfg.marchid = value; +} + +static void cpu_get_marchid(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + bool value = RISCV_CPU(obj)->cfg.marchid; + + visit_type_bool(v, name, &value, errp); +} + static void riscv_cpu_class_init(ObjectClass *c, void *data) { RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); @@ -1996,6 +2039,9 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) object_class_property_add(c, "mimpid", "uint64", cpu_get_mimpid, cpu_set_mimpid, NULL, NULL); + object_class_property_add(c, "marchid", "uint64", cpu_get_marchid, + cpu_set_marchid, NULL, NULL); + device_class_set_props(dc, riscv_cpu_properties); } From 492265ae8be70537391c08390cb7c64580c902d9 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:24 -0300 Subject: [PATCH 40/54] target/riscv: use KVM scratch CPUs to init KVM properties Certain validations, such as the validations done for the machine IDs (mvendorid/marchid/mimpid), are done before starting the CPU. Non-dynamic (named) CPUs tries to match user input with a preset default. As it is today we can't prefetch a KVM default for these cases because we're only able to read/write KVM regs after the vcpu is spinning. Our target/arm friends use a concept called "scratch CPU", which consists of creating a vcpu for doing queries and validations and so on, which is discarded shortly after use [1]. This is a suitable solution for what we need so let's implement it in target/riscv as well. kvm_riscv_init_machine_ids() will be used to do any pre-launch setup for KVM CPUs, via riscv_cpu_add_user_properties(). The function will create a KVM scratch CPU, fetch KVM regs that work as default values for user properties, and then discard the scratch CPU afterwards. We're starting by initializing 'mvendorid'. This concept will be used to init other KVM specific properties in the next patches as well. [1] target/arm/kvm.c, kvm_arm_create_scratch_host_vcpu() Suggested-by: Andrew Jones Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Acked-by: Alistair Francis Message-Id: <20230706101738.460804-7-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 6 +++ target/riscv/kvm.c | 85 ++++++++++++++++++++++++++++++++++++++++ target/riscv/kvm_riscv.h | 1 + 3 files changed, 92 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 9080d021fa..0e1265bb17 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1792,6 +1792,12 @@ static void riscv_cpu_add_user_properties(Object *obj) Property *prop; DeviceState *dev = DEVICE(obj); +#ifndef CONFIG_USER_ONLY + if (kvm_enabled()) { + kvm_riscv_init_user_properties(obj); + } +#endif + riscv_cpu_add_misa_properties(obj); for (prop = riscv_cpu_extensions; prop && prop->name; prop++) { diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c index c3976a588d..7402bfbfc5 100644 --- a/target/riscv/kvm.c +++ b/target/riscv/kvm.c @@ -309,6 +309,91 @@ static void kvm_riscv_put_regs_timer(CPUState *cs) env->kvm_timer_dirty = false; } +typedef struct KVMScratchCPU { + int kvmfd; + int vmfd; + int cpufd; +} KVMScratchCPU; + +/* + * Heavily inspired by kvm_arm_create_scratch_host_vcpu() + * from target/arm/kvm.c. + */ +static bool kvm_riscv_create_scratch_vcpu(KVMScratchCPU *scratch) +{ + int kvmfd = -1, vmfd = -1, cpufd = -1; + + kvmfd = qemu_open_old("/dev/kvm", O_RDWR); + if (kvmfd < 0) { + goto err; + } + do { + vmfd = ioctl(kvmfd, KVM_CREATE_VM, 0); + } while (vmfd == -1 && errno == EINTR); + if (vmfd < 0) { + goto err; + } + cpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0); + if (cpufd < 0) { + goto err; + } + + scratch->kvmfd = kvmfd; + scratch->vmfd = vmfd; + scratch->cpufd = cpufd; + + return true; + + err: + if (cpufd >= 0) { + close(cpufd); + } + if (vmfd >= 0) { + close(vmfd); + } + if (kvmfd >= 0) { + close(kvmfd); + } + + return false; +} + +static void kvm_riscv_destroy_scratch_vcpu(KVMScratchCPU *scratch) +{ + close(scratch->cpufd); + close(scratch->vmfd); + close(scratch->kvmfd); +} + +static void kvm_riscv_init_machine_ids(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) +{ + CPURISCVState *env = &cpu->env; + struct kvm_one_reg reg; + int ret; + + reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG, + KVM_REG_RISCV_CONFIG_REG(mvendorid)); + reg.addr = (uint64_t)&cpu->cfg.mvendorid; + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_report("Unable to retrieve mvendorid from host, error %d", ret); + } +} + +void kvm_riscv_init_user_properties(Object *cpu_obj) +{ + RISCVCPU *cpu = RISCV_CPU(cpu_obj); + KVMScratchCPU kvmcpu; + + if (!kvm_riscv_create_scratch_vcpu(&kvmcpu)) { + return; + } + + kvm_riscv_init_machine_ids(cpu, &kvmcpu); + + kvm_riscv_destroy_scratch_vcpu(&kvmcpu); +} + const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO }; diff --git a/target/riscv/kvm_riscv.h b/target/riscv/kvm_riscv.h index ed281bdce0..e3ba935808 100644 --- a/target/riscv/kvm_riscv.h +++ b/target/riscv/kvm_riscv.h @@ -19,6 +19,7 @@ #ifndef QEMU_KVM_RISCV_H #define QEMU_KVM_RISCV_H +void kvm_riscv_init_user_properties(Object *cpu_obj); void kvm_riscv_reset_vcpu(RISCVCPU *cpu); void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level); From d758f8849851dd4e97a575e52c11ec748ab1bbec Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:25 -0300 Subject: [PATCH 41/54] target/riscv: read marchid/mimpid in kvm_riscv_init_machine_ids() Allow 'marchid' and 'mimpid' to also be initialized in kvm_riscv_init_machine_ids(). After this change, the handling of mvendorid/marchid/mimpid for the 'host' CPU type will be equal to what we already have for TCG named CPUs, i.e. the user is not able to set these values to a different val than the one that is already preset. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Acked-by: Alistair Francis Message-Id: <20230706101738.460804-8-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c index 7402bfbfc5..d8c1423b54 100644 --- a/target/riscv/kvm.c +++ b/target/riscv/kvm.c @@ -378,6 +378,22 @@ static void kvm_riscv_init_machine_ids(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) if (ret != 0) { error_report("Unable to retrieve mvendorid from host, error %d", ret); } + + reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG, + KVM_REG_RISCV_CONFIG_REG(marchid)); + reg.addr = (uint64_t)&cpu->cfg.marchid; + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_report("Unable to retrieve marchid from host, error %d", ret); + } + + reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG, + KVM_REG_RISCV_CONFIG_REG(mimpid)); + reg.addr = (uint64_t)&cpu->cfg.mimpid; + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_report("Unable to retrieve mimpid from host, error %d", ret); + } } void kvm_riscv_init_user_properties(Object *cpu_obj) From 1fb5a622f7ef0cfd65b39decd768983e1d0ba1c2 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:26 -0300 Subject: [PATCH 42/54] target/riscv: handle mvendorid/marchid/mimpid for KVM CPUs After changing user validation for mvendorid/marchid/mimpid to guarantee that the value is validated on user input time, coupled with the work in fetching KVM default values for them by using a scratch CPU, we're certain that the values in cpu->cfg.(mvendorid|marchid|mimpid) are already good to be written back to KVM. There's no need to write the values back for 'host' type CPUs since the values can't be changed, so let's do that just for generic CPUs. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Acked-by: Alistair Francis Message-Id: <20230706101738.460804-9-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c index d8c1423b54..f264286d51 100644 --- a/target/riscv/kvm.c +++ b/target/riscv/kvm.c @@ -495,6 +495,33 @@ void kvm_arch_init_irq_routing(KVMState *s) { } +static int kvm_vcpu_set_machine_ids(RISCVCPU *cpu, CPUState *cs) +{ + CPURISCVState *env = &cpu->env; + uint64_t id; + int ret; + + id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG, + KVM_REG_RISCV_CONFIG_REG(mvendorid)); + ret = kvm_set_one_reg(cs, id, &cpu->cfg.mvendorid); + if (ret != 0) { + return ret; + } + + id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG, + KVM_REG_RISCV_CONFIG_REG(marchid)); + ret = kvm_set_one_reg(cs, id, &cpu->cfg.marchid); + if (ret != 0) { + return ret; + } + + id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG, + KVM_REG_RISCV_CONFIG_REG(mimpid)); + ret = kvm_set_one_reg(cs, id, &cpu->cfg.mimpid); + + return ret; +} + int kvm_arch_init_vcpu(CPUState *cs) { int ret = 0; @@ -513,6 +540,10 @@ int kvm_arch_init_vcpu(CPUState *cs) } env->misa_ext = isa; + if (!object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_CPU_HOST)) { + ret = kvm_vcpu_set_machine_ids(cpu, cs); + } + return ret; } From e28b9c497428f9efccc7b7f5fc69e7c6524b41a0 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:28 -0300 Subject: [PATCH 43/54] target/riscv/kvm.c: init 'misa_ext_mask' with scratch CPU At this moment we're retrieving env->misa_ext during kvm_arch_init_cpu(), leaving env->misa_ext_mask behind. We want to set env->misa_ext_mask, and we want to set it as early as possible. The reason is that we're going to use it in the validation process of the KVM MISA properties we're going to add next. Setting it during arch_init_cpu() is too late for user validation. Move the code to a new helper that is going to be called during init() time, via kvm_riscv_init_user_properties(), like we're already doing for the machine ID properties. Set both misa_ext and misa_ext_mask to the same value retrieved by the 'isa' config reg. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Acked-by: Alistair Francis Message-Id: <20230706101738.460804-11-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c index f264286d51..0d19267010 100644 --- a/target/riscv/kvm.c +++ b/target/riscv/kvm.c @@ -396,6 +396,28 @@ static void kvm_riscv_init_machine_ids(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) } } +static void kvm_riscv_init_misa_ext_mask(RISCVCPU *cpu, + KVMScratchCPU *kvmcpu) +{ + CPURISCVState *env = &cpu->env; + struct kvm_one_reg reg; + int ret; + + reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG, + KVM_REG_RISCV_CONFIG_REG(isa)); + reg.addr = (uint64_t)&env->misa_ext_mask; + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + + if (ret) { + error_report("Unable to fetch ISA register from KVM, " + "error %d", ret); + kvm_riscv_destroy_scratch_vcpu(kvmcpu); + exit(EXIT_FAILURE); + } + + env->misa_ext = env->misa_ext_mask; +} + void kvm_riscv_init_user_properties(Object *cpu_obj) { RISCVCPU *cpu = RISCV_CPU(cpu_obj); @@ -406,6 +428,7 @@ void kvm_riscv_init_user_properties(Object *cpu_obj) } kvm_riscv_init_machine_ids(cpu, &kvmcpu); + kvm_riscv_init_misa_ext_mask(cpu, &kvmcpu); kvm_riscv_destroy_scratch_vcpu(&kvmcpu); } @@ -525,21 +548,10 @@ static int kvm_vcpu_set_machine_ids(RISCVCPU *cpu, CPUState *cs) int kvm_arch_init_vcpu(CPUState *cs) { int ret = 0; - target_ulong isa; RISCVCPU *cpu = RISCV_CPU(cs); - CPURISCVState *env = &cpu->env; - uint64_t id; qemu_add_vm_change_state_handler(kvm_riscv_vm_state_change, cs); - id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG, - KVM_REG_RISCV_CONFIG_REG(isa)); - ret = kvm_get_one_reg(cs, id, &isa); - if (ret) { - return ret; - } - env->misa_ext = isa; - if (!object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_CPU_HOST)) { ret = kvm_vcpu_set_machine_ids(cpu, cs); } From ed7e6182063ead28274751bd59f11f74a4cf3063 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:29 -0300 Subject: [PATCH 44/54] target/riscv/cpu: add misa_ext_info_arr[] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Next patch will add KVM specific user properties for both MISA and multi-letter extensions. For MISA extensions we want to make use of what is already available in misa_ext_cfgs[] to avoid code repetition. misa_ext_info_arr[] array will hold name and description for each MISA extension that misa_ext_cfgs[] is declaring. We'll then use this new array in KVM code to avoid duplicating strings. Two getters were added to allow KVM to retrieve the 'name' and 'description' for each MISA property. There's nothing holding us back from doing the same with multi-letter extensions. For now doing just with MISA extensions is enough. It is worth documenting that even using the __bultin_ctz() directive to populate the misa_ext_info_arr[] we are forced to assign 'name' and 'description' during runtime in riscv_cpu_add_misa_properties(). The reason is that some Gitlab runners ('clang-user' and 'tsan-build') will throw errors like this if we fetch 'name' and 'description' from the array in the MISA_CFG() macro: ../target/riscv/cpu.c:1624:5: error: initializer element is not a compile-time constant MISA_CFG(RVA, true), ^~~~~~~~~~~~~~~~~~~ ../target/riscv/cpu.c:1619:53: note: expanded from macro 'MISA_CFG' {.name = misa_ext_info_arr[MISA_INFO_IDX(_bit)].name, \ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~ gcc and others compilers/builders were fine with that change. We can't ignore failures in the Gitlab pipeline though, so code was changed to make every runner happy. As a side effect, misa_ext_cfg[] is no longer a 'const' array because it must be set during runtime. Suggested-by: Andrew Jones Signed-off-by: Daniel Henrique Barboza Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-Id: <20230706101738.460804-12-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 110 +++++++++++++++++++++++++++++++++------------ target/riscv/cpu.h | 7 ++- 2 files changed, 88 insertions(+), 29 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 0e1265bb17..35ba220c8f 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1636,33 +1636,83 @@ static void cpu_get_misa_ext_cfg(Object *obj, Visitor *v, const char *name, visit_type_bool(v, name, &value, errp); } -static const RISCVCPUMisaExtConfig misa_ext_cfgs[] = { - {.name = "a", .description = "Atomic instructions", - .misa_bit = RVA, .enabled = true}, - {.name = "c", .description = "Compressed instructions", - .misa_bit = RVC, .enabled = true}, - {.name = "d", .description = "Double-precision float point", - .misa_bit = RVD, .enabled = true}, - {.name = "f", .description = "Single-precision float point", - .misa_bit = RVF, .enabled = true}, - {.name = "i", .description = "Base integer instruction set", - .misa_bit = RVI, .enabled = true}, - {.name = "e", .description = "Base integer instruction set (embedded)", - .misa_bit = RVE, .enabled = false}, - {.name = "m", .description = "Integer multiplication and division", - .misa_bit = RVM, .enabled = true}, - {.name = "s", .description = "Supervisor-level instructions", - .misa_bit = RVS, .enabled = true}, - {.name = "u", .description = "User-level instructions", - .misa_bit = RVU, .enabled = true}, - {.name = "h", .description = "Hypervisor", - .misa_bit = RVH, .enabled = true}, - {.name = "x-j", .description = "Dynamic translated languages", - .misa_bit = RVJ, .enabled = false}, - {.name = "v", .description = "Vector operations", - .misa_bit = RVV, .enabled = false}, - {.name = "g", .description = "General purpose (IMAFD_Zicsr_Zifencei)", - .misa_bit = RVG, .enabled = false}, +typedef struct misa_ext_info { + const char *name; + const char *description; +} MISAExtInfo; + +#define MISA_INFO_IDX(_bit) \ + __builtin_ctz(_bit) + +#define MISA_EXT_INFO(_bit, _propname, _descr) \ + [MISA_INFO_IDX(_bit)] = {.name = _propname, .description = _descr} + +static const MISAExtInfo misa_ext_info_arr[] = { + MISA_EXT_INFO(RVA, "a", "Atomic instructions"), + MISA_EXT_INFO(RVC, "c", "Compressed instructions"), + MISA_EXT_INFO(RVD, "d", "Double-precision float point"), + MISA_EXT_INFO(RVF, "f", "Single-precision float point"), + MISA_EXT_INFO(RVI, "i", "Base integer instruction set"), + MISA_EXT_INFO(RVE, "e", "Base integer instruction set (embedded)"), + MISA_EXT_INFO(RVM, "m", "Integer multiplication and division"), + MISA_EXT_INFO(RVS, "s", "Supervisor-level instructions"), + MISA_EXT_INFO(RVU, "u", "User-level instructions"), + MISA_EXT_INFO(RVH, "h", "Hypervisor"), + MISA_EXT_INFO(RVJ, "x-j", "Dynamic translated languages"), + MISA_EXT_INFO(RVV, "v", "Vector operations"), + MISA_EXT_INFO(RVG, "g", "General purpose (IMAFD_Zicsr_Zifencei)"), +}; + +static int riscv_validate_misa_info_idx(uint32_t bit) +{ + int idx; + + /* + * Our lowest valid input (RVA) is 1 and + * __builtin_ctz() is UB with zero. + */ + g_assert(bit != 0); + idx = MISA_INFO_IDX(bit); + + g_assert(idx < ARRAY_SIZE(misa_ext_info_arr)); + return idx; +} + +const char *riscv_get_misa_ext_name(uint32_t bit) +{ + int idx = riscv_validate_misa_info_idx(bit); + const char *val = misa_ext_info_arr[idx].name; + + g_assert(val != NULL); + return val; +} + +const char *riscv_get_misa_ext_description(uint32_t bit) +{ + int idx = riscv_validate_misa_info_idx(bit); + const char *val = misa_ext_info_arr[idx].description; + + g_assert(val != NULL); + return val; +} + +#define MISA_CFG(_bit, _enabled) \ + {.misa_bit = _bit, .enabled = _enabled} + +static RISCVCPUMisaExtConfig misa_ext_cfgs[] = { + MISA_CFG(RVA, true), + MISA_CFG(RVC, true), + MISA_CFG(RVD, true), + MISA_CFG(RVF, true), + MISA_CFG(RVI, true), + MISA_CFG(RVE, false), + MISA_CFG(RVM, true), + MISA_CFG(RVS, true), + MISA_CFG(RVU, true), + MISA_CFG(RVH, true), + MISA_CFG(RVJ, false), + MISA_CFG(RVV, false), + MISA_CFG(RVG, false), }; static void riscv_cpu_add_misa_properties(Object *cpu_obj) @@ -1670,7 +1720,11 @@ static void riscv_cpu_add_misa_properties(Object *cpu_obj) int i; for (i = 0; i < ARRAY_SIZE(misa_ext_cfgs); i++) { - const RISCVCPUMisaExtConfig *misa_cfg = &misa_ext_cfgs[i]; + RISCVCPUMisaExtConfig *misa_cfg = &misa_ext_cfgs[i]; + int bit = misa_cfg->misa_bit; + + misa_cfg->name = riscv_get_misa_ext_name(bit); + misa_cfg->description = riscv_get_misa_ext_description(bit); object_property_add(cpu_obj, misa_cfg->name, "bool", cpu_get_misa_ext_cfg, diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 3081603464..6ea22e0eea 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -41,7 +41,10 @@ #define RV(x) ((target_ulong)1 << (x - 'A')) -/* Consider updating misa_ext_cfgs[] when adding new MISA bits here */ +/* + * Consider updating misa_ext_info_arr[] and misa_ext_cfgs[] + * when adding new MISA bits here. + */ #define RVI RV('I') #define RVE RV('E') /* E and I are mutually exclusive */ #define RVM RV('M') @@ -56,6 +59,8 @@ #define RVJ RV('J') #define RVG RV('G') +const char *riscv_get_misa_ext_name(uint32_t bit); +const char *riscv_get_misa_ext_description(uint32_t bit); /* Privileged specification version */ enum { From 92becce5e172357d9be047306d92b986242f0f0d Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:30 -0300 Subject: [PATCH 45/54] target/riscv: add KVM specific MISA properties Using all TCG user properties in KVM is tricky. First because KVM supports only a small subset of what TCG provides, so most of the cpu->cfg flags do nothing for KVM. Second, and more important, we don't have a way of telling if any given value is an user input or not. For TCG this has a small impact since we just validating everything and error out if needed. But for KVM it would be good to know if a given value was set by the user or if it's a value already provided by KVM. Otherwise we don't know how to handle failed kvm_set_one_regs() when writing the configurations back. These characteristics make it overly complicated to use the same user facing flags for both KVM and TCG. A simpler approach is to create KVM specific properties that have specialized logic, forking KVM and TCG use cases for those cases only. Fully separating KVM/TCG properties is unneeded at this point - in fact we want the user experience to be as equal as possible, regardless of the acceleration chosen. We'll start this fork with the MISA properties, adding the MISA bits that the KVM driver currently supports. A new KVMCPUConfig type is introduced. It'll hold general information about an extension. For MISA extensions we're going to use the newly created getters of misa_ext_infos[] to populate their name and description. 'offset' holds the MISA bit (RVA, RVC, ...). We're calling it 'offset' instead of 'misa_bit' because this same KVMCPUConfig struct will be used to multi-letter extensions later on. This new type also holds a 'user_set' flag. This flag will be set when the user set an option that's different than what is already configured in the host, requiring KVM intervention to write the regs back during kvm_arch_init_vcpu(). Similar mechanics will be implemented for multi-letter extensions as well. There is no need to duplicate more code than necessary, so we're going to use the existing kvm_riscv_init_user_properties() to add the KVM specific properties. Any code that is adding a TCG user prop is then changed slightly to verify first if there's a KVM prop with the same name already added. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-Id: <20230706101738.460804-13-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 5 +++ target/riscv/kvm.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 35ba220c8f..5c8832a030 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1726,6 +1726,11 @@ static void riscv_cpu_add_misa_properties(Object *cpu_obj) misa_cfg->name = riscv_get_misa_ext_name(bit); misa_cfg->description = riscv_get_misa_ext_description(bit); + /* Check if KVM already created the property */ + if (object_property_find(cpu_obj, misa_cfg->name)) { + continue; + } + object_property_add(cpu_obj, misa_cfg->name, "bool", cpu_get_misa_ext_cfg, cpu_set_misa_ext_cfg, diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c index 0d19267010..8cd08f4396 100644 --- a/target/riscv/kvm.c +++ b/target/riscv/kvm.c @@ -22,8 +22,10 @@ #include #include "qemu/timer.h" +#include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" +#include "qapi/visitor.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" #include "sysemu/kvm_int.h" @@ -105,6 +107,81 @@ static uint64_t kvm_riscv_reg_id(CPURISCVState *env, uint64_t type, } \ } while (0) +typedef struct KVMCPUConfig { + const char *name; + const char *description; + target_ulong offset; + int kvm_reg_id; + bool user_set; +} KVMCPUConfig; + +#define KVM_MISA_CFG(_bit, _reg_id) \ + {.offset = _bit, .kvm_reg_id = _reg_id} + +/* KVM ISA extensions */ +static KVMCPUConfig kvm_misa_ext_cfgs[] = { + KVM_MISA_CFG(RVA, KVM_RISCV_ISA_EXT_A), + KVM_MISA_CFG(RVC, KVM_RISCV_ISA_EXT_C), + KVM_MISA_CFG(RVD, KVM_RISCV_ISA_EXT_D), + KVM_MISA_CFG(RVF, KVM_RISCV_ISA_EXT_F), + KVM_MISA_CFG(RVH, KVM_RISCV_ISA_EXT_H), + KVM_MISA_CFG(RVI, KVM_RISCV_ISA_EXT_I), + KVM_MISA_CFG(RVM, KVM_RISCV_ISA_EXT_M), +}; + +static void kvm_cpu_set_misa_ext_cfg(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) +{ + KVMCPUConfig *misa_ext_cfg = opaque; + target_ulong misa_bit = misa_ext_cfg->offset; + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; + bool value, host_bit; + + if (!visit_type_bool(v, name, &value, errp)) { + return; + } + + host_bit = env->misa_ext_mask & misa_bit; + + if (value == host_bit) { + return; + } + + if (!value) { + misa_ext_cfg->user_set = true; + return; + } + + /* + * Forbid users to enable extensions that aren't + * available in the hart. + */ + error_setg(errp, "Enabling MISA bit '%s' is not allowed: it's not " + "enabled in the host", misa_ext_cfg->name); +} + +static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(kvm_misa_ext_cfgs); i++) { + KVMCPUConfig *misa_cfg = &kvm_misa_ext_cfgs[i]; + int bit = misa_cfg->offset; + + misa_cfg->name = riscv_get_misa_ext_name(bit); + misa_cfg->description = riscv_get_misa_ext_description(bit); + + object_property_add(cpu_obj, misa_cfg->name, "bool", + NULL, + kvm_cpu_set_misa_ext_cfg, + NULL, misa_cfg); + object_property_set_description(cpu_obj, misa_cfg->name, + misa_cfg->description); + } +} + static int kvm_riscv_get_regs_core(CPUState *cs) { int ret = 0; @@ -427,6 +504,7 @@ void kvm_riscv_init_user_properties(Object *cpu_obj) return; } + kvm_riscv_add_cpu_user_properties(cpu_obj); kvm_riscv_init_machine_ids(cpu, &kvmcpu); kvm_riscv_init_misa_ext_mask(cpu, &kvmcpu); From 7313fffb40ea9775b38ff2ce73c4d6b0f43edc2f Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:31 -0300 Subject: [PATCH 46/54] target/riscv/kvm.c: update KVM MISA bits Our design philosophy with KVM properties can be resumed in two main decisions based on KVM interface availability and what the user wants to do: - if the user disables an extension that the host KVM module doesn't know about (i.e. it doesn't implement the kvm_get_one_reg() interface), keep booting the CPU. This will avoid users having to deal with issues with older KVM versions while disabling features they don't care; - for any other case we're going to error out immediately. If the user wants to enable a feature that KVM doesn't know about this a problem that is worth aborting - the user must know that the feature wasn't enabled in the hart. Likewise, if KVM knows about the extension, the user wants to enable/disable it, and we fail to do it so, that's also a problem we can't shrug it off. In the case of MISA bits we won't even try enabling bits that aren't already available in the host. The ioctl() is so likely to fail that it's not worth trying. This check is already done in the previous patch, in kvm_cpu_set_misa_ext_cfg(), thus we don't need to worry about it now. In kvm_riscv_update_cpu_misa_ext() we'll go through every potential user option and do as follows: - if the user didn't set the property or set to the same value of the host, do nothing; - Disable the given extension in KVM. Error out if anything goes wrong. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Acked-by: Alistair Francis Message-Id: <20230706101738.460804-14-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c index 8cd08f4396..f64019e87c 100644 --- a/target/riscv/kvm.c +++ b/target/riscv/kvm.c @@ -162,6 +162,41 @@ static void kvm_cpu_set_misa_ext_cfg(Object *obj, Visitor *v, "enabled in the host", misa_ext_cfg->name); } +static void kvm_riscv_update_cpu_misa_ext(RISCVCPU *cpu, CPUState *cs) +{ + CPURISCVState *env = &cpu->env; + uint64_t id, reg; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(kvm_misa_ext_cfgs); i++) { + KVMCPUConfig *misa_cfg = &kvm_misa_ext_cfgs[i]; + target_ulong misa_bit = misa_cfg->offset; + + if (!misa_cfg->user_set) { + continue; + } + + /* If we're here we're going to disable the MISA bit */ + reg = 0; + id = kvm_riscv_reg_id(env, KVM_REG_RISCV_ISA_EXT, + misa_cfg->kvm_reg_id); + ret = kvm_set_one_reg(cs, id, ®); + if (ret != 0) { + /* + * We're not checking for -EINVAL because if the bit is about + * to be disabled, it means that it was already enabled by + * KVM. We determined that by fetching the 'isa' register + * during init() time. Any error at this point is worth + * aborting. + */ + error_report("Unable to set KVM reg %s, error %d", + misa_cfg->name, ret); + exit(EXIT_FAILURE); + } + env->misa_ext &= ~misa_bit; + } +} + static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj) { int i; @@ -632,8 +667,13 @@ int kvm_arch_init_vcpu(CPUState *cs) if (!object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_CPU_HOST)) { ret = kvm_vcpu_set_machine_ids(cpu, cs); + if (ret != 0) { + return ret; + } } + kvm_riscv_update_cpu_misa_ext(cpu, cs); + return ret; } From f7a69fa6e6e21580b6e56dac87ad2ab0bf228065 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:32 -0300 Subject: [PATCH 47/54] target/riscv/kvm.c: add multi-letter extension KVM properties Let's add KVM user properties for the multi-letter extensions that KVM currently supports: zicbom, zicboz, zihintpause, zbb, ssaia, sstc, svinval and svpbmt. As with MISA extensions, we're using the KVMCPUConfig type to hold information about the state of each extension. However, multi-letter extensions have more cases to cover than MISA extensions, so we're adding an extra 'supported' flag as well. This flag will reflect if a given extension is supported by KVM, i.e. KVM knows how to handle it. This is determined during KVM extension discovery in kvm_riscv_init_multiext_cfg(), where we test for EINVAL errors. Any other error will cause an abort. The use of the 'user_set' is similar to what we already do with MISA extensions: the flag set only if the user is changing the extension state. The 'supported' flag will be used later on to make an exception for users that are disabling multi-letter extensions that are unknown to KVM. Signed-off-by: Daniel Henrique Barboza Acked-by: Alistair Francis Reviewed-by: Andrew Jones Message-Id: <20230706101738.460804-15-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 8 +++ target/riscv/kvm.c | 119 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 5c8832a030..31e591a938 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1860,6 +1860,14 @@ static void riscv_cpu_add_user_properties(Object *obj) riscv_cpu_add_misa_properties(obj); for (prop = riscv_cpu_extensions; prop && prop->name; prop++) { +#ifndef CONFIG_USER_ONLY + if (kvm_enabled()) { + /* Check if KVM created the property already */ + if (object_property_find(obj, prop->name)) { + continue; + } + } +#endif qdev_property_add_static(dev, prop); } diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c index f64019e87c..bd86d8748e 100644 --- a/target/riscv/kvm.c +++ b/target/riscv/kvm.c @@ -113,6 +113,7 @@ typedef struct KVMCPUConfig { target_ulong offset; int kvm_reg_id; bool user_set; + bool supported; } KVMCPUConfig; #define KVM_MISA_CFG(_bit, _reg_id) \ @@ -197,6 +198,81 @@ static void kvm_riscv_update_cpu_misa_ext(RISCVCPU *cpu, CPUState *cs) } } +#define CPUCFG(_prop) offsetof(struct RISCVCPUConfig, _prop) + +#define KVM_EXT_CFG(_name, _prop, _reg_id) \ + {.name = _name, .offset = CPUCFG(_prop), \ + .kvm_reg_id = _reg_id} + +static KVMCPUConfig kvm_multi_ext_cfgs[] = { + KVM_EXT_CFG("zicbom", ext_icbom, KVM_RISCV_ISA_EXT_ZICBOM), + KVM_EXT_CFG("zicboz", ext_icboz, KVM_RISCV_ISA_EXT_ZICBOZ), + KVM_EXT_CFG("zihintpause", ext_zihintpause, KVM_RISCV_ISA_EXT_ZIHINTPAUSE), + KVM_EXT_CFG("zbb", ext_zbb, KVM_RISCV_ISA_EXT_ZBB), + KVM_EXT_CFG("ssaia", ext_ssaia, KVM_RISCV_ISA_EXT_SSAIA), + KVM_EXT_CFG("sstc", ext_sstc, KVM_RISCV_ISA_EXT_SSTC), + KVM_EXT_CFG("svinval", ext_svinval, KVM_RISCV_ISA_EXT_SVINVAL), + KVM_EXT_CFG("svpbmt", ext_svpbmt, KVM_RISCV_ISA_EXT_SVPBMT), +}; + +static void kvm_cpu_cfg_set(RISCVCPU *cpu, KVMCPUConfig *multi_ext, + uint32_t val) +{ + int cpu_cfg_offset = multi_ext->offset; + bool *ext_enabled = (void *)&cpu->cfg + cpu_cfg_offset; + + *ext_enabled = val; +} + +static uint32_t kvm_cpu_cfg_get(RISCVCPU *cpu, + KVMCPUConfig *multi_ext) +{ + int cpu_cfg_offset = multi_ext->offset; + bool *ext_enabled = (void *)&cpu->cfg + cpu_cfg_offset; + + return *ext_enabled; +} + +static void kvm_cpu_set_multi_ext_cfg(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) +{ + KVMCPUConfig *multi_ext_cfg = opaque; + RISCVCPU *cpu = RISCV_CPU(obj); + bool value, host_val; + + if (!visit_type_bool(v, name, &value, errp)) { + return; + } + + host_val = kvm_cpu_cfg_get(cpu, multi_ext_cfg); + + /* + * Ignore if the user is setting the same value + * as the host. + */ + if (value == host_val) { + return; + } + + if (!multi_ext_cfg->supported) { + /* + * Error out if the user is trying to enable an + * extension that KVM doesn't support. Ignore + * option otherwise. + */ + if (value) { + error_setg(errp, "KVM does not support disabling extension %s", + multi_ext_cfg->name); + } + + return; + } + + multi_ext_cfg->user_set = true; + kvm_cpu_cfg_set(cpu, multi_ext_cfg, value); +} + static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj) { int i; @@ -215,6 +291,15 @@ static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj) object_property_set_description(cpu_obj, misa_cfg->name, misa_cfg->description); } + + for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) { + KVMCPUConfig *multi_cfg = &kvm_multi_ext_cfgs[i]; + + object_property_add(cpu_obj, multi_cfg->name, "bool", + NULL, + kvm_cpu_set_multi_ext_cfg, + NULL, multi_cfg); + } } static int kvm_riscv_get_regs_core(CPUState *cs) @@ -530,6 +615,39 @@ static void kvm_riscv_init_misa_ext_mask(RISCVCPU *cpu, env->misa_ext = env->misa_ext_mask; } +static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) +{ + CPURISCVState *env = &cpu->env; + uint64_t val; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) { + KVMCPUConfig *multi_ext_cfg = &kvm_multi_ext_cfgs[i]; + struct kvm_one_reg reg; + + reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_ISA_EXT, + multi_ext_cfg->kvm_reg_id); + reg.addr = (uint64_t)&val; + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + if (errno == EINVAL) { + /* Silently default to 'false' if KVM does not support it. */ + multi_ext_cfg->supported = false; + val = false; + } else { + error_report("Unable to read ISA_EXT KVM register %s, " + "error %d", multi_ext_cfg->name, ret); + kvm_riscv_destroy_scratch_vcpu(kvmcpu); + exit(EXIT_FAILURE); + } + } else { + multi_ext_cfg->supported = true; + } + + kvm_cpu_cfg_set(cpu, multi_ext_cfg, val); + } +} + void kvm_riscv_init_user_properties(Object *cpu_obj) { RISCVCPU *cpu = RISCV_CPU(cpu_obj); @@ -542,6 +660,7 @@ void kvm_riscv_init_user_properties(Object *cpu_obj) kvm_riscv_add_cpu_user_properties(cpu_obj); kvm_riscv_init_machine_ids(cpu, &kvmcpu); kvm_riscv_init_misa_ext_mask(cpu, &kvmcpu); + kvm_riscv_init_multiext_cfg(cpu, &kvmcpu); kvm_riscv_destroy_scratch_vcpu(&kvmcpu); } From b71f9dcada2edacfa9baaf872de717c903fb333b Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:33 -0300 Subject: [PATCH 48/54] target/riscv/cpu.c: add satp_mode properties earlier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit riscv_cpu_add_user_properties() ended up with an excess of "#ifndef CONFIG_USER_ONLY" blocks after changes that added KVM properties handling. KVM specific properties are required to be created earlier than their TCG counterparts, but the remaining props can be created at any order. Move riscv_add_satp_mode_properties() to the start of the function, inside the !CONFIG_USER_ONLY block already present there, to remove the last ifndef block. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-Id: <20230706101738.460804-16-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 31e591a938..deb3c0f035 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1852,6 +1852,8 @@ static void riscv_cpu_add_user_properties(Object *obj) DeviceState *dev = DEVICE(obj); #ifndef CONFIG_USER_ONLY + riscv_add_satp_mode_properties(obj); + if (kvm_enabled()) { kvm_riscv_init_user_properties(obj); } @@ -1870,10 +1872,6 @@ static void riscv_cpu_add_user_properties(Object *obj) #endif qdev_property_add_static(dev, prop); } - -#ifndef CONFIG_USER_ONLY - riscv_add_satp_mode_properties(obj); -#endif } static Property riscv_cpu_properties[] = { From 63c7eedc28f25141a44e34534c6b5317caaf38cc Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:34 -0300 Subject: [PATCH 49/54] target/riscv/cpu.c: remove priv_ver check from riscv_isa_string_ext() riscv_isa_string_ext() is being used by riscv_isa_string(), which is then used by boards to retrieve the 'riscv,isa' string to be written in the FDT. All this happens after riscv_cpu_realize(), meaning that we're already past riscv_cpu_validate_set_extensions() and, more important, riscv_cpu_disable_priv_spec_isa_exts(). This means that all extensions that needed to be disabled due to priv_spec mismatch are already disabled. Checking this again during riscv_isa_string_ext() is unneeded. Remove it. As a bonus, riscv_isa_string_ext() can now be used with the 'host' KVM-only CPU type since it doesn't have a env->priv_ver assigned and it would fail this check for no good reason. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-Id: <20230706101738.460804-17-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index deb3c0f035..2acf77949f 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -2124,8 +2124,7 @@ static void riscv_isa_string_ext(RISCVCPU *cpu, char **isa_str, int i; for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) { - if (cpu->env.priv_ver >= isa_edata_arr[i].min_version && - isa_ext_is_enabled(cpu, &isa_edata_arr[i])) { + if (isa_ext_is_enabled(cpu, &isa_edata_arr[i])) { new = g_strconcat(old, "_", isa_edata_arr[i].name, NULL); g_free(old); old = new; From 56f0e992ca01391f81a42b29883288fa1e4380c1 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:35 -0300 Subject: [PATCH 50/54] target/riscv/cpu.c: create KVM mock properties KVM-specific properties are being created inside target/riscv/kvm.c. But at this moment we're gathering all the remaining properties from TCG and adding them as is when running KVM. This creates a situation where non-KVM properties are setting flags to 'true' due to its default settings (e.g. Zawrs). Users can also freely enable them via command line. This doesn't impact runtime per se because KVM doesn't care about these flags, but code such as riscv_isa_string_ext() take those flags into account. The result is that, for a KVM guest, setting non-KVM properties will make them appear in the riscv,isa DT. We want to keep the same API for both TCG and KVM and at the same time, when running KVM, forbid non-KVM extensions to be enabled internally. We accomplish both by changing riscv_cpu_add_user_properties() to add a mock boolean property for every non-KVM extension in riscv_cpu_extensions[]. Then, when running KVM, users are still free to set extensions at will, but we'll error out if a non-KVM extension is enabled. Setting such extension to 'false' will be ignored. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-Id: <20230706101738.460804-18-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 2acf77949f..b2883ca533 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1840,6 +1840,26 @@ static Property riscv_cpu_extensions[] = { DEFINE_PROP_END_OF_LIST(), }; + +#ifndef CONFIG_USER_ONLY +static void cpu_set_cfg_unavailable(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) +{ + const char *propname = opaque; + bool value; + + if (!visit_type_bool(v, name, &value, errp)) { + return; + } + + if (value) { + error_setg(errp, "extension %s is not available with KVM", + propname); + } +} +#endif + /* * Add CPU properties with user-facing flags. * @@ -1868,6 +1888,22 @@ static void riscv_cpu_add_user_properties(Object *obj) if (object_property_find(obj, prop->name)) { continue; } + + /* + * Set the default to disabled for every extension + * unknown to KVM and error out if the user attempts + * to enable any of them. + * + * We're giving a pass for non-bool properties since they're + * not related to the availability of extensions and can be + * safely ignored as is. + */ + if (prop->info == &qdev_prop_bool) { + object_property_add(obj, prop->name, "bool", + NULL, cpu_set_cfg_unavailable, + NULL, (void *)prop->name); + continue; + } } #endif qdev_property_add_static(dev, prop); From df817297d7a5397a3383207b0e0174b464cf42ab Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:36 -0300 Subject: [PATCH 51/54] target/riscv: update multi-letter extension KVM properties We're now ready to update the multi-letter extensions status for KVM. kvm_riscv_update_cpu_cfg_isa_ext() is called called during vcpu creation time to verify which user options changes host defaults (via the 'user_set' flag) and tries to write them back to KVM. Failure to commit a change to KVM is only ignored in case KVM doesn't know about the extension (-EINVAL error code) and the user wanted to disable the given extension. Otherwise we're going to abort the boot process. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-Id: <20230706101738.460804-19-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c index bd86d8748e..4d7476cf15 100644 --- a/target/riscv/kvm.c +++ b/target/riscv/kvm.c @@ -273,6 +273,32 @@ static void kvm_cpu_set_multi_ext_cfg(Object *obj, Visitor *v, kvm_cpu_cfg_set(cpu, multi_ext_cfg, value); } +static void kvm_riscv_update_cpu_cfg_isa_ext(RISCVCPU *cpu, CPUState *cs) +{ + CPURISCVState *env = &cpu->env; + uint64_t id, reg; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) { + KVMCPUConfig *multi_ext_cfg = &kvm_multi_ext_cfgs[i]; + + if (!multi_ext_cfg->user_set) { + continue; + } + + id = kvm_riscv_reg_id(env, KVM_REG_RISCV_ISA_EXT, + multi_ext_cfg->kvm_reg_id); + reg = kvm_cpu_cfg_get(cpu, multi_ext_cfg); + ret = kvm_set_one_reg(cs, id, ®); + if (ret != 0) { + error_report("Unable to %s extension %s in KVM, error %d", + reg ? "enable" : "disable", + multi_ext_cfg->name, ret); + exit(EXIT_FAILURE); + } + } +} + static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj) { int i; @@ -792,6 +818,7 @@ int kvm_arch_init_vcpu(CPUState *cs) } kvm_riscv_update_cpu_misa_ext(cpu, cs); + kvm_riscv_update_cpu_cfg_isa_ext(cpu, cs); return ret; } From a1be1d9a77a8170822258663e5fb0580f5252536 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:37 -0300 Subject: [PATCH 52/54] target/riscv/kvm.c: add kvmconfig_get_cfg_addr() helper There are 2 places in which we need to get a pointer to a certain property of the cpu->cfg struct based on property offset. Next patch will add a couple more. Create a helper to avoid repeating this code over and over. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-Id: <20230706101738.460804-20-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c index 4d7476cf15..fd22ef4162 100644 --- a/target/riscv/kvm.c +++ b/target/riscv/kvm.c @@ -215,11 +215,15 @@ static KVMCPUConfig kvm_multi_ext_cfgs[] = { KVM_EXT_CFG("svpbmt", ext_svpbmt, KVM_RISCV_ISA_EXT_SVPBMT), }; +static void *kvmconfig_get_cfg_addr(RISCVCPU *cpu, KVMCPUConfig *kvmcfg) +{ + return (void *)&cpu->cfg + kvmcfg->offset; +} + static void kvm_cpu_cfg_set(RISCVCPU *cpu, KVMCPUConfig *multi_ext, uint32_t val) { - int cpu_cfg_offset = multi_ext->offset; - bool *ext_enabled = (void *)&cpu->cfg + cpu_cfg_offset; + bool *ext_enabled = kvmconfig_get_cfg_addr(cpu, multi_ext); *ext_enabled = val; } @@ -227,8 +231,7 @@ static void kvm_cpu_cfg_set(RISCVCPU *cpu, KVMCPUConfig *multi_ext, static uint32_t kvm_cpu_cfg_get(RISCVCPU *cpu, KVMCPUConfig *multi_ext) { - int cpu_cfg_offset = multi_ext->offset; - bool *ext_enabled = (void *)&cpu->cfg + cpu_cfg_offset; + bool *ext_enabled = kvmconfig_get_cfg_addr(cpu, multi_ext); return *ext_enabled; } From b9f822215ee58b57863bc082c322bb88529bd958 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 6 Jul 2023 07:17:38 -0300 Subject: [PATCH 53/54] target/riscv/kvm.c: read/write (cbom|cboz)_blocksize in KVM If we don't set a proper cbom_blocksize|cboz_blocksize in the FDT the Linux Kernel will fail to detect the availability of the CBOM/CBOZ extensions, regardless of the contents of the 'riscv,isa' DT prop. The FDT is being written using the cpu->cfg.cbom|z_blocksize attributes, so let's expose them as user properties like it is already done with TCG. This will also require us to determine proper blocksize values during init() time since the FDT is already created during realize(). We'll take a ride in kvm_riscv_init_multiext_cfg() to do it. Note that we don't need to fetch both cbom and cboz blocksizes every time: check for their parent extensions (icbom and icboz) and only read the blocksizes if needed. In contrast with cbom|z_blocksize properties from TCG, the user is not able to set any value that is different from the 'host' value when running KVM. KVM can be particularly harsh dealing with it: a ENOTSUPP can be thrown for the mere attempt of executing kvm_set_one_reg() for these 2 regs. Hopefully we don't need to call kvm_set_one_reg() for these regs. We'll check if the user input matches the host value in kvm_cpu_set_cbomz_blksize(), the set() accessor for both blocksize properties. We'll fail fast since it's already known to not be supported. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Acked-by: Alistair Francis Message-Id: <20230706101738.460804-21-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c index fd22ef4162..9d8a8982f9 100644 --- a/target/riscv/kvm.c +++ b/target/riscv/kvm.c @@ -276,6 +276,42 @@ static void kvm_cpu_set_multi_ext_cfg(Object *obj, Visitor *v, kvm_cpu_cfg_set(cpu, multi_ext_cfg, value); } +static KVMCPUConfig kvm_cbom_blocksize = { + .name = "cbom_blocksize", + .offset = CPUCFG(cbom_blocksize), + .kvm_reg_id = KVM_REG_RISCV_CONFIG_REG(zicbom_block_size) +}; + +static KVMCPUConfig kvm_cboz_blocksize = { + .name = "cboz_blocksize", + .offset = CPUCFG(cboz_blocksize), + .kvm_reg_id = KVM_REG_RISCV_CONFIG_REG(zicboz_block_size) +}; + +static void kvm_cpu_set_cbomz_blksize(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) +{ + KVMCPUConfig *cbomz_cfg = opaque; + RISCVCPU *cpu = RISCV_CPU(obj); + uint16_t value, *host_val; + + if (!visit_type_uint16(v, name, &value, errp)) { + return; + } + + host_val = kvmconfig_get_cfg_addr(cpu, cbomz_cfg); + + if (value != *host_val) { + error_report("Unable to set %s to a different value than " + "the host (%u)", + cbomz_cfg->name, *host_val); + exit(EXIT_FAILURE); + } + + cbomz_cfg->user_set = true; +} + static void kvm_riscv_update_cpu_cfg_isa_ext(RISCVCPU *cpu, CPUState *cs) { CPURISCVState *env = &cpu->env; @@ -329,6 +365,14 @@ static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj) kvm_cpu_set_multi_ext_cfg, NULL, multi_cfg); } + + object_property_add(cpu_obj, "cbom_blocksize", "uint16", + NULL, kvm_cpu_set_cbomz_blksize, + NULL, &kvm_cbom_blocksize); + + object_property_add(cpu_obj, "cboz_blocksize", "uint16", + NULL, kvm_cpu_set_cbomz_blksize, + NULL, &kvm_cboz_blocksize); } static int kvm_riscv_get_regs_core(CPUState *cs) @@ -644,6 +688,24 @@ static void kvm_riscv_init_misa_ext_mask(RISCVCPU *cpu, env->misa_ext = env->misa_ext_mask; } +static void kvm_riscv_read_cbomz_blksize(RISCVCPU *cpu, KVMScratchCPU *kvmcpu, + KVMCPUConfig *cbomz_cfg) +{ + CPURISCVState *env = &cpu->env; + struct kvm_one_reg reg; + int ret; + + reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG, + cbomz_cfg->kvm_reg_id); + reg.addr = (uint64_t)kvmconfig_get_cfg_addr(cpu, cbomz_cfg); + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_report("Unable to read KVM reg %s, error %d", + cbomz_cfg->name, ret); + exit(EXIT_FAILURE); + } +} + static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) { CPURISCVState *env = &cpu->env; @@ -675,6 +737,14 @@ static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) kvm_cpu_cfg_set(cpu, multi_ext_cfg, val); } + + if (cpu->cfg.ext_icbom) { + kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cbom_blocksize); + } + + if (cpu->cfg.ext_icboz) { + kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cboz_blocksize); + } } void kvm_riscv_init_user_properties(Object *cpu_obj) From a47842d16653b4f73b5d56ff0c252dd8a329481b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20M=C3=BCllner?= Date: Mon, 10 Jul 2023 09:12:43 +0200 Subject: [PATCH 54/54] riscv: Add support for the Zfa extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduces the RISC-V Zfa extension, which introduces additional floating-point instructions: * fli (load-immediate) with pre-defined immediates * fminm/fmaxm (like fmin/fmax but with different NaN behaviour) * fround/froundmx (round to integer) * fcvtmod.w.d (Modular Convert-to-Integer) * fmv* to access high bits of float register bigger than XLEN * Quiet comparison instructions (fleq/fltq) Zfa defines its instructions in combination with the following extensions: * single-precision floating-point (F) * double-precision floating-point (D) * quad-precision floating-point (Q) * half-precision floating-point (Zfh) Since QEMU does not support the RISC-V quad-precision floating-point ISA extension (Q), this patch does not include the instructions that depend on this extension. All other instructions are included in this patch. The Zfa specification can be found here: https://github.com/riscv/riscv-isa-manual/blob/master/src/zfa.tex The Zfa specifciation is frozen and is in public review since May 3, 2023: https://groups.google.com/a/groups.riscv.org/g/isa-dev/c/SED4ntBkabg The patch also includes a TCG test for the fcvtmod.w.d instruction. The test cases test for correct results and flag behaviour. Note, that the Zfa specification requires fcvtmod's flag behaviour to be identical to a fcvt with the same operands (which is also tested). Reviewed-by: Richard Henderson Signed-off-by: Christoph Müllner Message-Id: <20230710071243.282464-1-christoph.muellner@vrull.eu> Signed-off-by: Alistair Francis --- disas/riscv.c | 139 ++++++ disas/riscv.h | 3 + target/riscv/cpu.c | 8 + target/riscv/cpu_cfg.h | 1 + target/riscv/fpu_helper.c | 154 +++++++ target/riscv/helper.h | 19 + target/riscv/insn32.decode | 26 ++ target/riscv/insn_trans/trans_rvzfa.c.inc | 521 ++++++++++++++++++++++ target/riscv/translate.c | 1 + tests/tcg/riscv64/Makefile.target | 6 + tests/tcg/riscv64/test-fcvtmod.c | 345 ++++++++++++++ 11 files changed, 1223 insertions(+) create mode 100644 target/riscv/insn_trans/trans_rvzfa.c.inc create mode 100644 tests/tcg/riscv64/test-fcvtmod.c diff --git a/disas/riscv.c b/disas/riscv.c index 9f0195be30..cd7b6e86a7 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -829,6 +829,39 @@ typedef enum { rv_op_fsh = 798, rv_op_fmv_h_x = 799, rv_op_fmv_x_h = 800, + rv_op_fli_s = 801, + rv_op_fli_d = 802, + rv_op_fli_q = 803, + rv_op_fli_h = 804, + rv_op_fminm_s = 805, + rv_op_fmaxm_s = 806, + rv_op_fminm_d = 807, + rv_op_fmaxm_d = 808, + rv_op_fminm_q = 809, + rv_op_fmaxm_q = 810, + rv_op_fminm_h = 811, + rv_op_fmaxm_h = 812, + rv_op_fround_s = 813, + rv_op_froundnx_s = 814, + rv_op_fround_d = 815, + rv_op_froundnx_d = 816, + rv_op_fround_q = 817, + rv_op_froundnx_q = 818, + rv_op_fround_h = 819, + rv_op_froundnx_h = 820, + rv_op_fcvtmod_w_d = 821, + rv_op_fmvh_x_d = 822, + rv_op_fmvp_d_x = 823, + rv_op_fmvh_x_q = 824, + rv_op_fmvp_q_x = 825, + rv_op_fleq_s = 826, + rv_op_fltq_s = 827, + rv_op_fleq_d = 828, + rv_op_fltq_d = 829, + rv_op_fleq_q = 830, + rv_op_fltq_q = 831, + rv_op_fleq_h = 832, + rv_op_fltq_h = 833, } rv_op; /* register names */ @@ -854,6 +887,23 @@ static const char rv_vreg_name_sym[32][4] = { "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31" }; +/* The FLI.[HSDQ] numeric constants (0.0 for symbolic constants). + * The constants use the hex floating-point literal representation + * that is printed when using the printf %a format specifier, + * which matches the output that is generated by the disassembler. + */ +static const char rv_fli_name_const[32][9] = +{ + "0x1p+0", "min", "0x1p-16", "0x1p-15", + "0x1p-8", "0x1p-7", "0x1p-4", "0x1p-3", + "0x1p-2", "0x1.4p-2", "0x1.8p-2", "0x1.cp-2", + "0x1p-1", "0x1.4p-1", "0x1.8p-1", "0x1.cp-1", + "0x1p+0", "0x1.4p+0", "0x1.8p+0", "0x1.cp+0", + "0x1p+1", "0x1.4p+1", "0x1.8p+1", "0x1p+2", + "0x1p+3", "0x1p+4", "0x1p+7", "0x1p+8", + "0x1p+15", "0x1p+16", "inf", "nan" +}; + /* pseudo-instruction constraints */ static const rvc_constraint rvcc_jal[] = { rvc_rd_eq_ra, rvc_end }; @@ -1925,6 +1975,39 @@ const rv_opcode_data rvi_opcode_data[] = { { "fsh", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 }, { "fmv.h.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, { "fmv.x.h", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, + { "fli.s", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 }, + { "fli.d", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 }, + { "fli.q", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 }, + { "fli.h", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 }, + { "fminm.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fmaxm.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fminm.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fmaxm.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fminm.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fmaxm.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fminm.h", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fmaxm.h", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fround.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "froundnx.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fround.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "froundnx.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fround.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "froundnx.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fround.h", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "froundnx.h", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fcvtmod.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, + { "fmvh.x.d", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, + { "fmvp.d.x", rv_codec_r, rv_fmt_frd_rs1_rs2, NULL, 0, 0, 0 }, + { "fmvh.x.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, + { "fmvp.q.x", rv_codec_r, rv_fmt_frd_rs1_rs2, NULL, 0, 0, 0 }, + { "fleq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "fltq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "fleq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "fltq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "fleq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "fltq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "fleq.h", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "fltq.h", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, }; /* CSR names */ @@ -2864,24 +2947,38 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fmin_s; break; case 1: op = rv_op_fmax_s; break; + case 2: op = rv_op_fminm_s; break; + case 3: op = rv_op_fmaxm_s; break; } break; case 21: switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fmin_d; break; case 1: op = rv_op_fmax_d; break; + case 2: op = rv_op_fminm_d; break; + case 3: op = rv_op_fmaxm_d; break; + } + break; + case 22: + switch (((inst >> 12) & 0b111)) { + case 2: op = rv_op_fminm_h; break; + case 3: op = rv_op_fmaxm_h; break; } break; case 23: switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fmin_q; break; case 1: op = rv_op_fmax_q; break; + case 2: op = rv_op_fminm_q; break; + case 3: op = rv_op_fmaxm_q; break; } break; case 32: switch ((inst >> 20) & 0b11111) { case 1: op = rv_op_fcvt_s_d; break; case 3: op = rv_op_fcvt_s_q; break; + case 4: op = rv_op_fround_s; break; + case 5: op = rv_op_froundnx_s; break; case 6: op = rv_op_fcvt_s_bf16; break; } break; @@ -2889,10 +2986,14 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_d_s; break; case 3: op = rv_op_fcvt_d_q; break; + case 4: op = rv_op_fround_d; break; + case 5: op = rv_op_froundnx_d; break; } break; case 34: switch (((inst >> 20) & 0b11111)) { + case 4: op = rv_op_fround_h; break; + case 5: op = rv_op_froundnx_h; break; case 8: op = rv_op_fcvt_bf16_s; break; } break; @@ -2900,6 +3001,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_q_s; break; case 1: op = rv_op_fcvt_q_d; break; + case 4: op = rv_op_fround_q; break; + case 5: op = rv_op_froundnx_q; break; } break; case 44: @@ -2922,6 +3025,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 0: op = rv_op_fle_s; break; case 1: op = rv_op_flt_s; break; case 2: op = rv_op_feq_s; break; + case 4: op = rv_op_fleq_s; break; + case 5: op = rv_op_fltq_s; break; } break; case 81: @@ -2929,6 +3034,14 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 0: op = rv_op_fle_d; break; case 1: op = rv_op_flt_d; break; case 2: op = rv_op_feq_d; break; + case 4: op = rv_op_fleq_d; break; + case 5: op = rv_op_fltq_d; break; + } + break; + case 82: + switch (((inst >> 12) & 0b111)) { + case 4: op = rv_op_fleq_h; break; + case 5: op = rv_op_fltq_h; break; } break; case 83: @@ -2936,6 +3049,18 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 0: op = rv_op_fle_q; break; case 1: op = rv_op_flt_q; break; case 2: op = rv_op_feq_q; break; + case 4: op = rv_op_fleq_q; break; + case 5: op = rv_op_fltq_q; break; + } + break; + case 89: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_fmvp_d_x; break; + } + break; + case 91: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_fmvp_q_x; break; } break; case 96: @@ -2952,6 +3077,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 1: op = rv_op_fcvt_wu_d; break; case 2: op = rv_op_fcvt_l_d; break; case 3: op = rv_op_fcvt_lu_d; break; + case 8: op = rv_op_fcvtmod_w_d; break; } break; case 99: @@ -2998,6 +3124,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_x_d; break; case 1: op = rv_op_fclass_d; break; + case 8: op = rv_op_fmvh_x_d; break; } break; case 114: @@ -3011,30 +3138,35 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_x_q; break; case 1: op = rv_op_fclass_q; break; + case 8: op = rv_op_fmvh_x_q; break; } break; case 120: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_s_x; break; + case 8: op = rv_op_fli_s; break; } break; case 121: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_d_x; break; + case 8: op = rv_op_fli_d; break; } break; case 122: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_h_x; break; + case 8: op = rv_op_fli_h; break; } break; case 123: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_q_x; break; + case 8: op = rv_op_fli_q; break; } break; } @@ -4298,6 +4430,10 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa) break; case rv_codec_zcmt_jt: dec->imm = operand_tbl_index(inst); + break; + case rv_codec_fli: + dec->rd = operand_rd(inst); + dec->imm = operand_rs1(inst); break; case rv_codec_r2_imm5: dec->rd = operand_rd(inst); @@ -4708,6 +4844,9 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) append(buf, tmp, buflen); break; } + case 'h': + append(buf, rv_fli_name_const[dec->imm], buflen); + break; default: break; } diff --git a/disas/riscv.h b/disas/riscv.h index 052a0c4281..9cf901fc1e 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -165,6 +165,7 @@ typedef enum { rv_codec_r_imm2, rv_codec_r2_immhl, rv_codec_r2_imm2_imm5, + rv_codec_fli, } rv_codec; /* structures */ @@ -229,6 +230,7 @@ enum { #define rv_fmt_rd_offset "O\t0,o" #define rv_fmt_rd_rs1_rs2 "O\t0,1,2" #define rv_fmt_frd_rs1 "O\t3,1" +#define rv_fmt_frd_rs1_rs2 "O\t3,1,2" #define rv_fmt_frd_frs1 "O\t3,4" #define rv_fmt_rd_frs1 "O\t0,4" #define rv_fmt_rd_frs1_frs2 "O\t0,4,5" @@ -295,5 +297,6 @@ enum { #define rv_fmt_rd_rs1_immh_imml "O\t0,1,i,j" #define rv_fmt_rd_rs1_immh_imml_addr "O\t0,(1),i,j" #define rv_fmt_rd2_imm "O\t0,2,(1),i" +#define rv_fmt_fli "O\t3,h" #endif /* DISAS_RISCV_H */ diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index b2883ca533..9339c0241d 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -89,6 +89,7 @@ static const struct isa_ext_data isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zifencei, PRIV_VERSION_1_10_0, ext_ifencei), ISA_EXT_DATA_ENTRY(zihintpause, PRIV_VERSION_1_10_0, ext_zihintpause), ISA_EXT_DATA_ENTRY(zawrs, PRIV_VERSION_1_12_0, ext_zawrs), + ISA_EXT_DATA_ENTRY(zfa, PRIV_VERSION_1_12_0, ext_zfa), ISA_EXT_DATA_ENTRY(zfbfmin, PRIV_VERSION_1_12_0, ext_zfbfmin), ISA_EXT_DATA_ENTRY(zfh, PRIV_VERSION_1_11_0, ext_zfh), ISA_EXT_DATA_ENTRY(zfhmin, PRIV_VERSION_1_11_0, ext_zfhmin), @@ -429,6 +430,7 @@ static void rv64_thead_c906_cpu_init(Object *obj) set_misa(env, MXL_RV64, RVG | RVC | RVS | RVU); env->priv_ver = PRIV_VERSION_1_11_0; + cpu->cfg.ext_zfa = true; cpu->cfg.ext_zfh = true; cpu->cfg.mmu = true; cpu->cfg.ext_xtheadba = true; @@ -1107,6 +1109,11 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } + if (cpu->cfg.ext_zfa && !riscv_has_ext(env, RVF)) { + error_setg(errp, "Zfa extension requires F extension"); + return; + } + if (cpu->cfg.ext_zfh) { cpu->cfg.ext_zfhmin = true; } @@ -1750,6 +1757,7 @@ static Property riscv_cpu_extensions[] = { DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true), DEFINE_PROP_BOOL("Zihintpause", RISCVCPU, cfg.ext_zihintpause, true), DEFINE_PROP_BOOL("Zawrs", RISCVCPU, cfg.ext_zawrs, true), + DEFINE_PROP_BOOL("Zfa", RISCVCPU, cfg.ext_zfa, true), DEFINE_PROP_BOOL("Zfh", RISCVCPU, cfg.ext_zfh, false), DEFINE_PROP_BOOL("Zfhmin", RISCVCPU, cfg.ext_zfhmin, false), DEFINE_PROP_BOOL("Zve32f", RISCVCPU, cfg.ext_zve32f, false), diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index e9ee39d7de..2bd9510ba3 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -75,6 +75,7 @@ struct RISCVCPUConfig { bool ext_svpbmt; bool ext_zdinx; bool ext_zawrs; + bool ext_zfa; bool ext_zfbfmin; bool ext_zfh; bool ext_zfhmin; diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index eb5ee5c4c9..871a70a316 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -252,6 +252,14 @@ uint64_t helper_fmin_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) float32_minimum_number(frs1, frs2, &env->fp_status)); } +uint64_t helper_fminm_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) +{ + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); + float32 ret = float32_min(frs1, frs2, &env->fp_status); + return nanbox_s(env, ret); +} + uint64_t helper_fmax_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float32 frs1 = check_nanbox_s(env, rs1); @@ -261,6 +269,14 @@ uint64_t helper_fmax_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) float32_maximum_number(frs1, frs2, &env->fp_status)); } +uint64_t helper_fmaxm_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) +{ + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); + float32 ret = float32_max(frs1, frs2, &env->fp_status); + return nanbox_s(env, ret); +} + uint64_t helper_fsqrt_s(CPURISCVState *env, uint64_t rs1) { float32 frs1 = check_nanbox_s(env, rs1); @@ -274,6 +290,13 @@ target_ulong helper_fle_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) return float32_le(frs1, frs2, &env->fp_status); } +target_ulong helper_fleq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) +{ + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); + return float32_le_quiet(frs1, frs2, &env->fp_status); +} + target_ulong helper_flt_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float32 frs1 = check_nanbox_s(env, rs1); @@ -281,6 +304,13 @@ target_ulong helper_flt_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) return float32_lt(frs1, frs2, &env->fp_status); } +target_ulong helper_fltq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) +{ + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); + return float32_lt_quiet(frs1, frs2, &env->fp_status); +} + target_ulong helper_feq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float32 frs1 = check_nanbox_s(env, rs1); @@ -338,6 +368,30 @@ target_ulong helper_fclass_s(CPURISCVState *env, uint64_t rs1) return fclass_s(frs1); } +uint64_t helper_fround_s(CPURISCVState *env, uint64_t rs1) +{ + float_status *fs = &env->fp_status; + uint16_t nx_old = get_float_exception_flags(fs) & float_flag_inexact; + float32 frs1 = check_nanbox_s(env, rs1); + + frs1 = float32_round_to_int(frs1, fs); + + /* Restore the original NX flag. */ + uint16_t flags = get_float_exception_flags(fs); + flags &= ~float_flag_inexact; + flags |= nx_old; + set_float_exception_flags(flags, fs); + + return nanbox_s(env, frs1); +} + +uint64_t helper_froundnx_s(CPURISCVState *env, uint64_t rs1) +{ + float32 frs1 = check_nanbox_s(env, rs1); + frs1 = float32_round_to_int(frs1, &env->fp_status); + return nanbox_s(env, frs1); +} + uint64_t helper_fadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) { return float64_add(frs1, frs2, &env->fp_status); @@ -365,6 +419,11 @@ uint64_t helper_fmin_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) float64_minimum_number(frs1, frs2, &env->fp_status); } +uint64_t helper_fminm_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float64_min(frs1, frs2, &env->fp_status); +} + uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) { return env->priv_ver < PRIV_VERSION_1_11_0 ? @@ -372,6 +431,11 @@ uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) float64_maximum_number(frs1, frs2, &env->fp_status); } +uint64_t helper_fmaxm_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float64_max(frs1, frs2, &env->fp_status); +} + uint64_t helper_fcvt_s_d(CPURISCVState *env, uint64_t rs1) { return nanbox_s(env, float64_to_float32(rs1, &env->fp_status)); @@ -393,11 +457,21 @@ target_ulong helper_fle_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) return float64_le(frs1, frs2, &env->fp_status); } +target_ulong helper_fleq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float64_le_quiet(frs1, frs2, &env->fp_status); +} + target_ulong helper_flt_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) { return float64_lt(frs1, frs2, &env->fp_status); } +target_ulong helper_fltq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float64_lt_quiet(frs1, frs2, &env->fp_status); +} + target_ulong helper_feq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) { return float64_eq_quiet(frs1, frs2, &env->fp_status); @@ -408,6 +482,11 @@ target_ulong helper_fcvt_w_d(CPURISCVState *env, uint64_t frs1) return float64_to_int32(frs1, &env->fp_status); } +uint64_t helper_fcvtmod_w_d(CPURISCVState *env, uint64_t value) +{ + return float64_to_int32_modulo(value, float_round_to_zero, &env->fp_status); +} + target_ulong helper_fcvt_wu_d(CPURISCVState *env, uint64_t frs1) { return (int32_t)float64_to_uint32(frs1, &env->fp_status); @@ -448,6 +527,27 @@ target_ulong helper_fclass_d(uint64_t frs1) return fclass_d(frs1); } +uint64_t helper_fround_d(CPURISCVState *env, uint64_t frs1) +{ + float_status *fs = &env->fp_status; + uint16_t nx_old = get_float_exception_flags(fs) & float_flag_inexact; + + frs1 = float64_round_to_int(frs1, fs); + + /* Restore the original NX flag. */ + uint16_t flags = get_float_exception_flags(fs); + flags &= ~float_flag_inexact; + flags |= nx_old; + set_float_exception_flags(flags, fs); + + return frs1; +} + +uint64_t helper_froundnx_d(CPURISCVState *env, uint64_t frs1) +{ + return float64_round_to_int(frs1, &env->fp_status); +} + uint64_t helper_fadd_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float16 frs1 = check_nanbox_h(env, rs1); @@ -485,6 +585,14 @@ uint64_t helper_fmin_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) float16_minimum_number(frs1, frs2, &env->fp_status)); } +uint64_t helper_fminm_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) +{ + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); + float16 ret = float16_min(frs1, frs2, &env->fp_status); + return nanbox_h(env, ret); +} + uint64_t helper_fmax_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float16 frs1 = check_nanbox_h(env, rs1); @@ -494,6 +602,14 @@ uint64_t helper_fmax_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) float16_maximum_number(frs1, frs2, &env->fp_status)); } +uint64_t helper_fmaxm_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) +{ + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); + float16 ret = float16_max(frs1, frs2, &env->fp_status); + return nanbox_h(env, ret); +} + uint64_t helper_fsqrt_h(CPURISCVState *env, uint64_t rs1) { float16 frs1 = check_nanbox_h(env, rs1); @@ -507,6 +623,13 @@ target_ulong helper_fle_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) return float16_le(frs1, frs2, &env->fp_status); } +target_ulong helper_fleq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) +{ + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); + return float16_le_quiet(frs1, frs2, &env->fp_status); +} + target_ulong helper_flt_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float16 frs1 = check_nanbox_h(env, rs1); @@ -514,6 +637,13 @@ target_ulong helper_flt_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) return float16_lt(frs1, frs2, &env->fp_status); } +target_ulong helper_fltq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) +{ + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); + return float16_lt_quiet(frs1, frs2, &env->fp_status); +} + target_ulong helper_feq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float16 frs1 = check_nanbox_h(env, rs1); @@ -527,6 +657,30 @@ target_ulong helper_fclass_h(CPURISCVState *env, uint64_t rs1) return fclass_h(frs1); } +uint64_t helper_fround_h(CPURISCVState *env, uint64_t rs1) +{ + float_status *fs = &env->fp_status; + uint16_t nx_old = get_float_exception_flags(fs) & float_flag_inexact; + float16 frs1 = check_nanbox_h(env, rs1); + + frs1 = float16_round_to_int(frs1, fs); + + /* Restore the original NX flag. */ + uint16_t flags = get_float_exception_flags(fs); + flags &= ~float_flag_inexact; + flags |= nx_old; + set_float_exception_flags(flags, fs); + + return nanbox_h(env, frs1); +} + +uint64_t helper_froundnx_h(CPURISCVState *env, uint64_t rs1) +{ + float16 frs1 = check_nanbox_s(env, rs1); + frs1 = float16_round_to_int(frs1, &env->fp_status); + return nanbox_h(env, frs1); +} + target_ulong helper_fcvt_w_h(CPURISCVState *env, uint64_t rs1) { float16 frs1 = check_nanbox_h(env, rs1); diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 3170b8daa6..c95adaf08a 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -25,10 +25,14 @@ DEF_HELPER_FLAGS_3(fsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmul_s, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fdiv_s, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmin_s, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fminm_s, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmax_s, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmaxm_s, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_2(fsqrt_s, TCG_CALL_NO_RWG, i64, env, i64) DEF_HELPER_FLAGS_3(fle_s, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(fleq_s, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_3(flt_s, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(fltq_s, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_3(feq_s, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_2(fcvt_w_s, TCG_CALL_NO_RWG, tl, env, i64) DEF_HELPER_FLAGS_2(fcvt_wu_s, TCG_CALL_NO_RWG, tl, env, i64) @@ -39,6 +43,8 @@ DEF_HELPER_FLAGS_2(fcvt_s_wu, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_s_l, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_s_lu, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fclass_s, TCG_CALL_NO_RWG_SE, tl, env, i64) +DEF_HELPER_FLAGS_2(fround_s, TCG_CALL_NO_RWG_SE, i64, env, i64) +DEF_HELPER_FLAGS_2(froundnx_s, TCG_CALL_NO_RWG_SE, i64, env, i64) /* Floating Point - Double Precision */ DEF_HELPER_FLAGS_3(fadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64) @@ -46,14 +52,19 @@ DEF_HELPER_FLAGS_3(fsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmul_d, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fdiv_d, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmin_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fminm_d, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmax_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmaxm_d, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_2(fcvt_s_d, TCG_CALL_NO_RWG, i64, env, i64) DEF_HELPER_FLAGS_2(fcvt_d_s, TCG_CALL_NO_RWG, i64, env, i64) DEF_HELPER_FLAGS_2(fsqrt_d, TCG_CALL_NO_RWG, i64, env, i64) DEF_HELPER_FLAGS_3(fle_d, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(fleq_d, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_3(flt_d, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(fltq_d, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_3(feq_d, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_2(fcvt_w_d, TCG_CALL_NO_RWG, tl, env, i64) +DEF_HELPER_FLAGS_2(fcvtmod_w_d, TCG_CALL_NO_RWG, i64, env, i64) DEF_HELPER_FLAGS_2(fcvt_wu_d, TCG_CALL_NO_RWG, tl, env, i64) DEF_HELPER_FLAGS_2(fcvt_l_d, TCG_CALL_NO_RWG, tl, env, i64) DEF_HELPER_FLAGS_2(fcvt_lu_d, TCG_CALL_NO_RWG, tl, env, i64) @@ -62,6 +73,8 @@ DEF_HELPER_FLAGS_2(fcvt_d_wu, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_d_l, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_d_lu, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_1(fclass_d, TCG_CALL_NO_RWG_SE, tl, i64) +DEF_HELPER_FLAGS_2(fround_d, TCG_CALL_NO_RWG_SE, i64, env, i64) +DEF_HELPER_FLAGS_2(froundnx_d, TCG_CALL_NO_RWG_SE, i64, env, i64) /* Bitmanip */ DEF_HELPER_FLAGS_2(clmul, TCG_CALL_NO_RWG_SE, tl, tl, tl) @@ -78,10 +91,14 @@ DEF_HELPER_FLAGS_3(fsub_h, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmul_h, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fdiv_h, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmin_h, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fminm_h, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmax_h, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmaxm_h, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_2(fsqrt_h, TCG_CALL_NO_RWG, i64, env, i64) DEF_HELPER_FLAGS_3(fle_h, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(fleq_h, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_3(flt_h, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(fltq_h, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_3(feq_h, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_2(fcvt_s_h, TCG_CALL_NO_RWG, i64, env, i64) DEF_HELPER_FLAGS_2(fcvt_h_s, TCG_CALL_NO_RWG, i64, env, i64) @@ -96,6 +113,8 @@ DEF_HELPER_FLAGS_2(fcvt_h_wu, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_h_l, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_h_lu, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fclass_h, TCG_CALL_NO_RWG_SE, tl, env, i64) +DEF_HELPER_FLAGS_2(fround_h, TCG_CALL_NO_RWG_SE, i64, env, i64) +DEF_HELPER_FLAGS_2(froundnx_h, TCG_CALL_NO_RWG_SE, i64, env, i64) /* Cache-block operations */ DEF_HELPER_2(cbo_clean_flush, void, env, tl) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 8c5d293f07..e341fa9213 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -821,6 +821,32 @@ binvi 01101. ........... 001 ..... 0010011 @sh bset 0010100 .......... 001 ..... 0110011 @r bseti 00101. ........... 001 ..... 0010011 @sh +# *** Zfa Standard Extension *** +fli_s 1111000 00001 ..... 000 ..... 1010011 @r2 +fli_d 1111001 00001 ..... 000 ..... 1010011 @r2 +fli_h 1111010 00001 ..... 000 ..... 1010011 @r2 +fminm_s 0010100 ..... ..... 010 ..... 1010011 @r +fmaxm_s 0010100 ..... ..... 011 ..... 1010011 @r +fminm_d 0010101 ..... ..... 010 ..... 1010011 @r +fmaxm_d 0010101 ..... ..... 011 ..... 1010011 @r +fminm_h 0010110 ..... ..... 010 ..... 1010011 @r +fmaxm_h 0010110 ..... ..... 011 ..... 1010011 @r +fround_s 0100000 00100 ..... ... ..... 1010011 @r2_rm +froundnx_s 0100000 00101 ..... ... ..... 1010011 @r2_rm +fround_d 0100001 00100 ..... ... ..... 1010011 @r2_rm +froundnx_d 0100001 00101 ..... ... ..... 1010011 @r2_rm +fround_h 0100010 00100 ..... ... ..... 1010011 @r2_rm +froundnx_h 0100010 00101 ..... ... ..... 1010011 @r2_rm +fcvtmod_w_d 1100001 01000 ..... 001 ..... 1010011 @r2 +fmvh_x_d 1110001 00001 ..... 000 ..... 1010011 @r2 +fmvp_d_x 1011001 ..... ..... 000 ..... 1010011 @r +fleq_s 1010000 ..... ..... 100 ..... 1010011 @r +fltq_s 1010000 ..... ..... 101 ..... 1010011 @r +fleq_d 1010001 ..... ..... 100 ..... 1010011 @r +fltq_d 1010001 ..... ..... 101 ..... 1010011 @r +fleq_h 1010010 ..... ..... 100 ..... 1010011 @r +fltq_h 1010010 ..... ..... 101 ..... 1010011 @r + # *** RV32 Zfh Extension *** flh ............ ..... 001 ..... 0000111 @i fsh ....... ..... ..... 001 ..... 0100111 @s diff --git a/target/riscv/insn_trans/trans_rvzfa.c.inc b/target/riscv/insn_trans/trans_rvzfa.c.inc new file mode 100644 index 0000000000..2c715af3e5 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzfa.c.inc @@ -0,0 +1,521 @@ +/* + * RISC-V translation routines for the Zfa Standard Extension. + * + * Copyright (c) 2023 Christoph Müllner, christoph.muellner@vrull.eu + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#define REQUIRE_ZFA(ctx) do { \ + if (!ctx->cfg_ptr->ext_zfa) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_ZFH(ctx) do { \ + if (!ctx->cfg_ptr->ext_zfh) { \ + return false; \ + } \ +} while (0) + +static bool trans_fli_s(DisasContext *ctx, arg_fli_s *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVF); + + /* Values below are NaN-boxed to avoid a gen_nanbox_s(). */ + static const uint64_t fli_s_table[] = { + 0xffffffffbf800000, /* -1.0 */ + 0xffffffff00800000, /* minimum positive normal */ + 0xffffffff37800000, /* 1.0 * 2^-16 */ + 0xffffffff38000000, /* 1.0 * 2^-15 */ + 0xffffffff3b800000, /* 1.0 * 2^-8 */ + 0xffffffff3c000000, /* 1.0 * 2^-7 */ + 0xffffffff3d800000, /* 1.0 * 2^-4 */ + 0xffffffff3e000000, /* 1.0 * 2^-3 */ + 0xffffffff3e800000, /* 0.25 */ + 0xffffffff3ea00000, /* 0.3125 */ + 0xffffffff3ec00000, /* 0.375 */ + 0xffffffff3ee00000, /* 0.4375 */ + 0xffffffff3f000000, /* 0.5 */ + 0xffffffff3f200000, /* 0.625 */ + 0xffffffff3f400000, /* 0.75 */ + 0xffffffff3f600000, /* 0.875 */ + 0xffffffff3f800000, /* 1.0 */ + 0xffffffff3fa00000, /* 1.25 */ + 0xffffffff3fc00000, /* 1.5 */ + 0xffffffff3fe00000, /* 1.75 */ + 0xffffffff40000000, /* 2.0 */ + 0xffffffff40200000, /* 2.5 */ + 0xffffffff40400000, /* 3 */ + 0xffffffff40800000, /* 4 */ + 0xffffffff41000000, /* 8 */ + 0xffffffff41800000, /* 16 */ + 0xffffffff43000000, /* 2^7 */ + 0xffffffff43800000, /* 2^8 */ + 0xffffffff47000000, /* 2^15 */ + 0xffffffff47800000, /* 2^16 */ + 0xffffffff7f800000, /* +inf */ + 0xffffffff7fc00000, /* Canonical NaN */ + }; + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + tcg_gen_movi_i64(dest, fli_s_table[a->rs1]); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fli_d(DisasContext *ctx, arg_fli_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + + static const uint64_t fli_d_table[] = { + 0xbff0000000000000, /* -1.0 */ + 0x0010000000000000, /* minimum positive normal */ + 0x3ef0000000000000, /* 1.0 * 2^-16 */ + 0x3f00000000000000, /* 1.0 * 2^-15 */ + 0x3f70000000000000, /* 1.0 * 2^-8 */ + 0x3f80000000000000, /* 1.0 * 2^-7 */ + 0x3fb0000000000000, /* 1.0 * 2^-4 */ + 0x3fc0000000000000, /* 1.0 * 2^-3 */ + 0x3fd0000000000000, /* 0.25 */ + 0x3fd4000000000000, /* 0.3125 */ + 0x3fd8000000000000, /* 0.375 */ + 0x3fdc000000000000, /* 0.4375 */ + 0x3fe0000000000000, /* 0.5 */ + 0x3fe4000000000000, /* 0.625 */ + 0x3fe8000000000000, /* 0.75 */ + 0x3fec000000000000, /* 0.875 */ + 0x3ff0000000000000, /* 1.0 */ + 0x3ff4000000000000, /* 1.25 */ + 0x3ff8000000000000, /* 1.5 */ + 0x3ffc000000000000, /* 1.75 */ + 0x4000000000000000, /* 2.0 */ + 0x4004000000000000, /* 2.5 */ + 0x4008000000000000, /* 3 */ + 0x4010000000000000, /* 4 */ + 0x4020000000000000, /* 8 */ + 0x4030000000000000, /* 16 */ + 0x4060000000000000, /* 2^7 */ + 0x4070000000000000, /* 2^8 */ + 0x40e0000000000000, /* 2^15 */ + 0x40f0000000000000, /* 2^16 */ + 0x7ff0000000000000, /* +inf */ + 0x7ff8000000000000, /* Canonical NaN */ + }; + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + tcg_gen_movi_i64(dest, fli_d_table[a->rs1]); + gen_set_fpr_d(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fli_h(DisasContext *ctx, arg_fli_h *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_ZFH(ctx); + + /* Values below are NaN-boxed to avoid a gen_nanbox_h(). */ + static const uint64_t fli_h_table[] = { + 0xffffffffffffbc00, /* -1.0 */ + 0xffffffffffff0400, /* minimum positive normal */ + 0xffffffffffff0100, /* 1.0 * 2^-16 */ + 0xffffffffffff0200, /* 1.0 * 2^-15 */ + 0xffffffffffff1c00, /* 1.0 * 2^-8 */ + 0xffffffffffff2000, /* 1.0 * 2^-7 */ + 0xffffffffffff2c00, /* 1.0 * 2^-4 */ + 0xffffffffffff3000, /* 1.0 * 2^-3 */ + 0xffffffffffff3400, /* 0.25 */ + 0xffffffffffff3500, /* 0.3125 */ + 0xffffffffffff3600, /* 0.375 */ + 0xffffffffffff3700, /* 0.4375 */ + 0xffffffffffff3800, /* 0.5 */ + 0xffffffffffff3900, /* 0.625 */ + 0xffffffffffff3a00, /* 0.75 */ + 0xffffffffffff3b00, /* 0.875 */ + 0xffffffffffff3c00, /* 1.0 */ + 0xffffffffffff3d00, /* 1.25 */ + 0xffffffffffff3e00, /* 1.5 */ + 0xffffffffffff3f00, /* 1.75 */ + 0xffffffffffff4000, /* 2.0 */ + 0xffffffffffff4100, /* 2.5 */ + 0xffffffffffff4200, /* 3 */ + 0xffffffffffff4400, /* 4 */ + 0xffffffffffff4800, /* 8 */ + 0xffffffffffff4c00, /* 16 */ + 0xffffffffffff5800, /* 2^7 */ + 0xffffffffffff5c00, /* 2^8 */ + 0xffffffffffff7800, /* 2^15 */ + 0xffffffffffff7c00, /* 2^16 */ + 0xffffffffffff7c00, /* +inf */ + 0xffffffffffff7e00, /* Canonical NaN */ + }; + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + tcg_gen_movi_i64(dest, fli_h_table[a->rs1]); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fminm_s(DisasContext *ctx, arg_fminm_s *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVF); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fminm_s(dest, cpu_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fmaxm_s(DisasContext *ctx, arg_fmaxm_s *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVF); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fmaxm_s(dest, cpu_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fminm_d(DisasContext *ctx, arg_fminm_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); + + gen_helper_fminm_d(dest, cpu_env, src1, src2); + gen_set_fpr_d(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fmaxm_d(DisasContext *ctx, arg_fmaxm_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); + + gen_helper_fmaxm_d(dest, cpu_env, src1, src2); + gen_set_fpr_d(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fminm_h(DisasContext *ctx, arg_fminm_h *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fminm_h(dest, cpu_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fmaxm_h(DisasContext *ctx, arg_fmaxm_h *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fmaxm_h(dest, cpu_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fround_s(DisasContext *ctx, arg_fround_s *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVF); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fround_s(dest, cpu_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_froundnx_s(DisasContext *ctx, arg_froundnx_s *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVF); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_froundnx_s(dest, cpu_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fround_d(DisasContext *ctx, arg_fround_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fround_d(dest, cpu_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_froundnx_d(DisasContext *ctx, arg_froundnx_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_froundnx_d(dest, cpu_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fround_h(DisasContext *ctx, arg_fround_h *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fround_h(dest, cpu_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_froundnx_h(DisasContext *ctx, arg_froundnx_h *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_froundnx_h(dest, cpu_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +bool trans_fcvtmod_w_d(DisasContext *ctx, arg_fcvtmod_w_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + + TCGv dst = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 t1 = tcg_temp_new_i64(); + + /* Rounding mode is RTZ. */ + gen_set_rm(ctx, RISCV_FRM_RTZ); + gen_helper_fcvtmod_w_d(t1, cpu_env, src1); + tcg_gen_trunc_i64_tl(dst, t1); + gen_set_gpr(ctx, a->rd, dst); + + return true; +} + +bool trans_fmvh_x_d(DisasContext *ctx, arg_fmvh_x_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + REQUIRE_32BIT(ctx); + + TCGv dst = dest_gpr(ctx, a->rd); + TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_sari_i64(t1, cpu_fpr[a->rs1], 32); + tcg_gen_trunc_i64_tl(dst, t1); + gen_set_gpr(ctx, a->rd, dst); + return true; +} + +bool trans_fmvp_d_x(DisasContext *ctx, arg_fmvp_d_x *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + REQUIRE_32BIT(ctx); + + TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE); + TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); + tcg_gen_concat_tl_i64(cpu_fpr[a->rd], src1, src2); + + mark_fs_dirty(ctx); + return true; +} + +bool trans_fleq_s(DisasContext *ctx, arg_fleq_s *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVF); + + TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fleq_s(dest, cpu_env, src1, src2); + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +bool trans_fltq_s(DisasContext *ctx, arg_fltq_s *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVF); + + TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fltq_s(dest, cpu_env, src1, src2); + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +bool trans_fleq_d(DisasContext *ctx, arg_fleq_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + + TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fltq_s(dest, cpu_env, src1, src2); + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +bool trans_fltq_d(DisasContext *ctx, arg_fltq_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + + TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fltq_s(dest, cpu_env, src1, src2); + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +bool trans_fleq_h(DisasContext *ctx, arg_fleq_h *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_ZFH(ctx); + + TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fleq_h(dest, cpu_env, src1, src2); + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +bool trans_fltq_h(DisasContext *ctx, arg_fltq_h *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_ZFH(ctx); + + TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fltq_h(dest, cpu_env, src1, src2); + gen_set_gpr(ctx, a->rd, dest); + return true; +} diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 621dd99241..697df1be9e 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -1091,6 +1091,7 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) #include "insn_trans/trans_rvzicond.c.inc" #include "insn_trans/trans_rvzawrs.c.inc" #include "insn_trans/trans_rvzicbo.c.inc" +#include "insn_trans/trans_rvzfa.c.inc" #include "insn_trans/trans_rvzfh.c.inc" #include "insn_trans/trans_rvk.c.inc" #include "insn_trans/trans_privileged.c.inc" diff --git a/tests/tcg/riscv64/Makefile.target b/tests/tcg/riscv64/Makefile.target index 42993549cb..a7e390c384 100644 --- a/tests/tcg/riscv64/Makefile.target +++ b/tests/tcg/riscv64/Makefile.target @@ -12,3 +12,9 @@ run-test-noc: QEMU_OPTS += -cpu rv64,c=false TESTS += test-aes run-test-aes: QEMU_OPTS += -cpu rv64,zk=on + +# Test for fcvtmod +TESTS += test-fcvtmod +test-fcvtmod: CFLAGS += -march=rv64imafdc +test-fcvtmod: LDFLAGS += -static +run-test-fcvtmod: QEMU_OPTS += -cpu rv64,d=true,Zfa=true diff --git a/tests/tcg/riscv64/test-fcvtmod.c b/tests/tcg/riscv64/test-fcvtmod.c new file mode 100644 index 0000000000..f050579974 --- /dev/null +++ b/tests/tcg/riscv64/test-fcvtmod.c @@ -0,0 +1,345 @@ +#include +#include +#include + +#define FFLAG_NX_SHIFT 0 /* inexact */ +#define FFLAG_UF_SHIFT 1 /* underflow */ +#define FFLAG_OF_SHIFT 2 /* overflow */ +#define FFLAG_DZ_SHIFT 3 /* divide by zero */ +#define FFLAG_NV_SHIFT 4 /* invalid operation */ + +#define FFLAG_NV (1UL << FFLAG_NV_SHIFT) +#define FFLAG_DZ (1UL << FFLAG_DZ_SHIFT) +#define FFLAG_OF (1UL << FFLAG_OF_SHIFT) +#define FFLAG_UF (1UL << FFLAG_UF_SHIFT) +#define FFLAG_NX (1UL << FFLAG_NX_SHIFT) + +typedef struct fp64_fcvt_fcvtmod_testcase { + const char* name; + union { + uint64_t inp_lu; + double inp_lf; + }; + uint64_t exp_fcvt; + uint8_t exp_fcvt_fflags; + uint64_t exp_fcvtmod; + uint8_t exp_fcvtmod_fflags; +} fp64_fcvt_fcvtmod_testcase_t; + +void print_fflags(uint8_t fflags) +{ + int set = 0; + + if (fflags == 0) { + printf("-"); + return; + } + + if (fflags & FFLAG_NV) { + printf("%sFFLAG_NV", set ? " | " : ""); + set = 1; + } + if (fflags & FFLAG_DZ) { + printf("%sFFLAG_DZ", set ? " | " : ""); + set = 1; + } + if (fflags & FFLAG_OF) { + printf("%sFFLAG_OF", set ? " | " : ""); + set = 1; + } + if (fflags & FFLAG_UF) { + printf("%sFFLAG_UF", set ? " | " : ""); + set = 1; + } + if (fflags & FFLAG_NX) { + printf("%sFFLAG_NX", set ? " | " : ""); + set = 1; + } +} + +/* Clear all FP flags. */ +static inline void clear_fflags() +{ + __asm__ __volatile__("fsflags zero"); +} + +/* Read all FP flags. */ +static inline uint8_t get_fflags() +{ + uint64_t v; + __asm__ __volatile__("frflags %0" : "=r"(v)); + return (uint8_t)v; +} + +/* Move input value (without conversations) into an FP register. */ +static inline double do_fmv_d_x(uint64_t inp) +{ + double fpr; + __asm__ __volatile__("fmv.d.x %0, %1" : "=f"(fpr) : "r"(inp)); + return fpr; +} + +static inline uint64_t do_fcvt_w_d(uint64_t inp, uint8_t *fflags) +{ + uint64_t ret; + double fpr = do_fmv_d_x(inp); + + clear_fflags(); + + __asm__ __volatile__("fcvt.w.d %0, %1, rtz" : "=r"(ret) : "f"(fpr)); + + *fflags = get_fflags(); + + return ret; +} + +static inline uint64_t do_fcvtmod_w_d(uint64_t inp, uint8_t *fflags) +{ + uint64_t ret; + double fpr = do_fmv_d_x(inp); + + clear_fflags(); + + /* fcvtmod.w.d rd, rs1, rtz = 1100001 01000 rs1 001 rd 1010011 */ + asm(".insn r 0x53, 0x1, 0x61, %0, %1, f8" : "=r"(ret) : "f"(fpr)); + + *fflags = get_fflags(); + + return ret; +} + +static const fp64_fcvt_fcvtmod_testcase_t tests[] = { + /* Zero (exp=0, frac=0) */ + { .name = "+0.0", + .inp_lf = 0x0p0, + .exp_fcvt = 0x0000000000000000, + .exp_fcvt_fflags = 0, + .exp_fcvtmod = 0x0000000000000000, + .exp_fcvtmod_fflags = 0 }, + { .name = "-0.0", + .inp_lf = -0x0p0, + .exp_fcvt = 0x0000000000000000, + .exp_fcvt_fflags = 0, + .exp_fcvtmod = 0x0000000000000000, + .exp_fcvtmod_fflags = 0 }, + + /* Subnormal: exp=0 frac!=0 */ + { .name = "Subnormal frac=1", + .inp_lu = 0x0000000000000001, + .exp_fcvt = 0x0000000000000000, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + { .name = "Subnormal frac=0xf..f", + .inp_lu = 0x0000ffffffffffff, + .exp_fcvt = 0x0000000000000000, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + { .name = "Neg subnormal frac=1", + .inp_lu = 0x0000000000000001, + .exp_fcvt = 0x0000000000000000, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + { .name = "Neg subnormal frac=0xf..f", + .inp_lu = 0x8000ffffffffffff, + .exp_fcvt = 0x0000000000000000, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + + /* Infinity: exp=0x7ff, frac=0 */ + { .name = "+INF", + .inp_lu = 0x7ff0000000000000, + .exp_fcvt = 0x000000007fffffff, /* int32 max */ + .exp_fcvt_fflags = FFLAG_NV, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NV }, + { .name = "-INF", + .inp_lu = 0xfff0000000000000, + .exp_fcvt = 0xffffffff80000000, /* int32 min */ + .exp_fcvt_fflags = FFLAG_NV, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NV }, + + /* NaN: exp=7ff, frac!=0 */ + { .name = "canonical NaN", + .inp_lu = 0x7ff8000000000000, + .exp_fcvt = 0x000000007fffffff, /* int32 max */ + .exp_fcvt_fflags = FFLAG_NV, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NV }, + { .name = "non-canonical NaN", + .inp_lu = 0x7ff8000000100000, + .exp_fcvt = 0x000000007fffffff, /* int32 min */ + .exp_fcvt_fflags = FFLAG_NV, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NV }, + + /* Normal numbers: exp!=0, exp!=7ff */ + { .name = "+smallest normal value", + .inp_lu = 0x0010000000000000, + .exp_fcvt = 0, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + { .name = "-smallest normal value", + .inp_lu = 0x8010000000000000, + .exp_fcvt = 0, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + + { .name = "+0.5", + .inp_lf = 0x1p-1, + .exp_fcvt = 0, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + { .name = "-0.5", + .inp_lf = -0x1p-1, + .exp_fcvt = 0, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + + { .name = "+value just below 1.0", + .inp_lu = 0x3fefffffffffffff, + .exp_fcvt = 0, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + { .name = "-value just above -1.0", + .inp_lu = 0xbfefffffffffffff, + .exp_fcvt = 0, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + + { .name = "+1.0", + .inp_lf = 0x1p0, + .exp_fcvt = 0x0000000000000001, + .exp_fcvt_fflags = 0, + .exp_fcvtmod = 0x0000000000000001, + .exp_fcvtmod_fflags = 0 }, + { .name = "-1.0", + .inp_lf = -0x1p0, + .exp_fcvt = 0xffffffffffffffff, + .exp_fcvt_fflags = 0, + .exp_fcvtmod = 0xffffffffffffffff, + .exp_fcvtmod_fflags = 0 }, + + { .name = "+1.5", + .inp_lu = 0x3ff8000000000000, + .exp_fcvt = 1, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 1, + .exp_fcvtmod_fflags = FFLAG_NX }, + { .name = "-1.5", + .inp_lu = 0xbff8000000000000, + .exp_fcvt = 0xffffffffffffffff, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0xffffffffffffffff, + .exp_fcvtmod_fflags = FFLAG_NX }, + + { .name = "+max int32 (2147483647)", + .inp_lu = 0x41dfffffffc00000, + .exp_fcvt = 0x000000007fffffff, + .exp_fcvt_fflags = 0, + .exp_fcvtmod = 0x000000007fffffff, + .exp_fcvtmod_fflags = 0 }, + { .name = "+max int32 +1 (2147483648)", + .inp_lf = 0x1p31, + .exp_fcvt = 0x000000007fffffff, + .exp_fcvt_fflags = FFLAG_NV, + .exp_fcvtmod = (uint64_t)-2147483648l, /* int32 min */ + .exp_fcvtmod_fflags = FFLAG_NV }, + { .name = "+max int32 +2 (2147483649)", + .inp_lu = 0x41e0000000200000, + .exp_fcvt = 0x000000007fffffff, + .exp_fcvt_fflags = FFLAG_NV, + .exp_fcvtmod = (uint64_t)-2147483647l, /* int32 min +1 */ + .exp_fcvtmod_fflags = FFLAG_NV }, + + { .name = "-max int32 (-2147483648)", + .inp_lf = -0x1p31, + .exp_fcvt = 0xffffffff80000000, + .exp_fcvt_fflags = 0, + .exp_fcvtmod = 0xffffffff80000000, + .exp_fcvtmod_fflags = 0 }, + { .name = "-max int32 -1 (-2147483649)", + .inp_lf = -0x1.00000002p+31, + .exp_fcvt = 0xffffffff80000000, + .exp_fcvt_fflags = FFLAG_NV, + .exp_fcvtmod = 2147483647, /* int32 max */ + .exp_fcvtmod_fflags = FFLAG_NV }, + { .name = "-max int32 -2 (-2147483650)", + .inp_lf = -0x1.00000004p+31, + .exp_fcvt = 0xffffffff80000000, + .exp_fcvt_fflags = FFLAG_NV, + .exp_fcvtmod = 2147483646, /* int32 max -1 */ + .exp_fcvtmod_fflags = FFLAG_NV }, +}; + +int run_fcvtmod_tests() +{ + uint64_t act_fcvt; + uint8_t act_fcvt_fflags; + uint64_t act_fcvtmod; + uint8_t act_fcvtmod_fflags; + + for (size_t i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) { + const fp64_fcvt_fcvtmod_testcase_t *t = &tests[i]; + + act_fcvt = do_fcvt_w_d(t->inp_lu, &act_fcvt_fflags); + int fcvt_correct = act_fcvt == t->exp_fcvt && + act_fcvt_fflags == t->exp_fcvt_fflags; + act_fcvtmod = do_fcvtmod_w_d(t->inp_lu, &act_fcvtmod_fflags); + int fcvtmod_correct = act_fcvtmod == t->exp_fcvtmod && + act_fcvtmod_fflags == t->exp_fcvtmod_fflags; + + if (fcvt_correct && fcvtmod_correct) { + continue; + } + + printf("Test %zu (%s) failed!\n", i, t->name); + + double fpr = do_fmv_d_x(t->inp_lu); + printf("inp_lu: 0x%016lx == %lf\n", t->inp_lu, fpr); + printf("inp_lf: %lf\n", t->inp_lf); + + uint32_t sign = (t->inp_lu >> 63); + uint32_t exp = (uint32_t)(t->inp_lu >> 52) & 0x7ff; + uint64_t frac = t->inp_lu & 0xfffffffffffffull; /* significand */ + int true_exp = exp - 1023; + int shift = true_exp - 52; + uint64_t true_frac = frac | 1ull << 52; + + printf("sign=%d, exp=0x%03x, frac=0x%012lx\n", sign, exp, frac); + printf("true_exp=%d, shift=%d, true_frac=0x%016lx\n", true_exp, shift, true_frac); + + if (!fcvt_correct) { + printf("act_fcvt: 0x%016lx == %li\n", act_fcvt, act_fcvt); + printf("exp_fcvt: 0x%016lx == %li\n", t->exp_fcvt, t->exp_fcvt); + printf("act_fcvt_fflags: "); print_fflags(act_fcvt_fflags); printf("\n"); + printf("exp_fcvt_fflags: "); print_fflags(t->exp_fcvt_fflags); printf("\n"); + } + + if (!fcvtmod_correct) { + printf("act_fcvtmod: 0x%016lx == %li\n", act_fcvtmod, act_fcvtmod); + printf("exp_fcvtmod: 0x%016lx == %li\n", t->exp_fcvtmod, t->exp_fcvtmod); + printf("act_fcvtmod_fflags: "); print_fflags(act_fcvtmod_fflags); printf("\n"); + printf("exp_fcvtmod_fflags: "); print_fflags(t->exp_fcvtmod_fflags); printf("\n"); + } + + return 1; + } + + return 0; +} + +int main() +{ + return run_fcvtmod_tests(); +}