From 8e2aa21b0a0d434be2f53a9435fec4f63ec192c4 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Thu, 30 Jun 2022 11:41:49 +0530 Subject: [PATCH 01/44] target/riscv: Update [m|h]tinst CSR in riscv_cpu_do_interrupt() We should write transformed instruction encoding of the trapped instruction in [m|h]tinst CSR at time of taking trap as defined by the RISC-V privileged specification v1.12. Reviewed-by: Alistair Francis Signed-off-by: Anup Patel Acked-by: dramforever Message-Id: <20220630061150.905174-2-apatel@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 5 + target/riscv/cpu_helper.c | 252 +++++++++++++++++++++++++++++++++++++- target/riscv/instmap.h | 45 +++++++ 3 files changed, 296 insertions(+), 6 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 5c7acc055ac9..ffb1a1887352 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -285,6 +285,11 @@ struct CPUArchState { /* Signals whether the current exception occurred with two-stage address translation active. */ bool two_stage_lookup; + /* + * Signals whether the current exception occurred while doing two-stage + * address translation for the VS-stage page table walk. + */ + bool two_stage_indirect_lookup; target_ulong scounteren; target_ulong mcounteren; diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 59b3680b1b23..87daf7220f55 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -22,6 +22,7 @@ #include "qemu/main-loop.h" #include "cpu.h" #include "exec/exec-all.h" +#include "instmap.h" #include "tcg/tcg-op.h" #include "trace.h" #include "semihosting/common-semi.h" @@ -1053,7 +1054,8 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, static void raise_mmu_exception(CPURISCVState *env, target_ulong address, MMUAccessType access_type, bool pmp_violation, - bool first_stage, bool two_stage) + bool first_stage, bool two_stage, + bool two_stage_indirect) { CPUState *cs = env_cpu(env); int page_fault_exceptions, vm; @@ -1103,6 +1105,7 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, } env->badaddr = address; env->two_stage_lookup = two_stage; + env->two_stage_indirect_lookup = two_stage_indirect; } hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) @@ -1148,6 +1151,7 @@ void riscv_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, env->badaddr = addr; env->two_stage_lookup = riscv_cpu_virt_enabled(env) || riscv_cpu_two_stage_lookup(mmu_idx); + env->two_stage_indirect_lookup = false; cpu_loop_exit_restore(cs, retaddr); } @@ -1173,6 +1177,7 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, env->badaddr = addr; env->two_stage_lookup = riscv_cpu_virt_enabled(env) || riscv_cpu_two_stage_lookup(mmu_idx); + env->two_stage_indirect_lookup = false; cpu_loop_exit_restore(cs, retaddr); } @@ -1188,6 +1193,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, bool pmp_violation = false; bool first_stage_error = true; bool two_stage_lookup = false; + bool two_stage_indirect_error = false; int ret = TRANSLATE_FAIL; int mode = mmu_idx; /* default TLB page size */ @@ -1225,6 +1231,7 @@ 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; } @@ -1308,12 +1315,218 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, raise_mmu_exception(env, address, access_type, pmp_violation, first_stage_error, riscv_cpu_virt_enabled(env) || - riscv_cpu_two_stage_lookup(mmu_idx)); + riscv_cpu_two_stage_lookup(mmu_idx), + two_stage_indirect_error); cpu_loop_exit_restore(cs, retaddr); } return true; } + +static target_ulong riscv_transformed_insn(CPURISCVState *env, + target_ulong insn, + target_ulong taddr) +{ + target_ulong xinsn = 0; + target_ulong access_rs1 = 0, access_imm = 0, access_size = 0; + + /* + * Only Quadrant 0 and Quadrant 2 of RVC instruction space need to + * be uncompressed. The Quadrant 1 of RVC instruction space need + * not be transformed because these instructions won't generate + * any load/store trap. + */ + + if ((insn & 0x3) != 0x3) { + /* Transform 16bit instruction into 32bit instruction */ + switch (GET_C_OP(insn)) { + case OPC_RISC_C_OP_QUAD0: /* Quadrant 0 */ + switch (GET_C_FUNC(insn)) { + case OPC_RISC_C_FUNC_FLD_LQ: + if (riscv_cpu_xlen(env) != 128) { /* C.FLD (RV32/64) */ + xinsn = OPC_RISC_FLD; + xinsn = SET_RD(xinsn, GET_C_RS2S(insn)); + access_rs1 = GET_C_RS1S(insn); + access_imm = GET_C_LD_IMM(insn); + access_size = 8; + } + break; + case OPC_RISC_C_FUNC_LW: /* C.LW */ + xinsn = OPC_RISC_LW; + xinsn = SET_RD(xinsn, GET_C_RS2S(insn)); + access_rs1 = GET_C_RS1S(insn); + access_imm = GET_C_LW_IMM(insn); + access_size = 4; + break; + case OPC_RISC_C_FUNC_FLW_LD: + if (riscv_cpu_xlen(env) == 32) { /* C.FLW (RV32) */ + xinsn = OPC_RISC_FLW; + xinsn = SET_RD(xinsn, GET_C_RS2S(insn)); + access_rs1 = GET_C_RS1S(insn); + access_imm = GET_C_LW_IMM(insn); + access_size = 4; + } else { /* C.LD (RV64/RV128) */ + xinsn = OPC_RISC_LD; + xinsn = SET_RD(xinsn, GET_C_RS2S(insn)); + access_rs1 = GET_C_RS1S(insn); + access_imm = GET_C_LD_IMM(insn); + access_size = 8; + } + break; + case OPC_RISC_C_FUNC_FSD_SQ: + if (riscv_cpu_xlen(env) != 128) { /* C.FSD (RV32/64) */ + xinsn = OPC_RISC_FSD; + xinsn = SET_RS2(xinsn, GET_C_RS2S(insn)); + access_rs1 = GET_C_RS1S(insn); + access_imm = GET_C_SD_IMM(insn); + access_size = 8; + } + break; + case OPC_RISC_C_FUNC_SW: /* C.SW */ + xinsn = OPC_RISC_SW; + xinsn = SET_RS2(xinsn, GET_C_RS2S(insn)); + access_rs1 = GET_C_RS1S(insn); + access_imm = GET_C_SW_IMM(insn); + access_size = 4; + break; + case OPC_RISC_C_FUNC_FSW_SD: + if (riscv_cpu_xlen(env) == 32) { /* C.FSW (RV32) */ + xinsn = OPC_RISC_FSW; + xinsn = SET_RS2(xinsn, GET_C_RS2S(insn)); + access_rs1 = GET_C_RS1S(insn); + access_imm = GET_C_SW_IMM(insn); + access_size = 4; + } else { /* C.SD (RV64/RV128) */ + xinsn = OPC_RISC_SD; + xinsn = SET_RS2(xinsn, GET_C_RS2S(insn)); + access_rs1 = GET_C_RS1S(insn); + access_imm = GET_C_SD_IMM(insn); + access_size = 8; + } + break; + default: + break; + } + break; + case OPC_RISC_C_OP_QUAD2: /* Quadrant 2 */ + switch (GET_C_FUNC(insn)) { + case OPC_RISC_C_FUNC_FLDSP_LQSP: + if (riscv_cpu_xlen(env) != 128) { /* C.FLDSP (RV32/64) */ + xinsn = OPC_RISC_FLD; + xinsn = SET_RD(xinsn, GET_C_RD(insn)); + access_rs1 = 2; + access_imm = GET_C_LDSP_IMM(insn); + access_size = 8; + } + break; + case OPC_RISC_C_FUNC_LWSP: /* C.LWSP */ + xinsn = OPC_RISC_LW; + xinsn = SET_RD(xinsn, GET_C_RD(insn)); + access_rs1 = 2; + access_imm = GET_C_LWSP_IMM(insn); + access_size = 4; + break; + case OPC_RISC_C_FUNC_FLWSP_LDSP: + if (riscv_cpu_xlen(env) == 32) { /* C.FLWSP (RV32) */ + xinsn = OPC_RISC_FLW; + xinsn = SET_RD(xinsn, GET_C_RD(insn)); + access_rs1 = 2; + access_imm = GET_C_LWSP_IMM(insn); + access_size = 4; + } else { /* C.LDSP (RV64/RV128) */ + xinsn = OPC_RISC_LD; + xinsn = SET_RD(xinsn, GET_C_RD(insn)); + access_rs1 = 2; + access_imm = GET_C_LDSP_IMM(insn); + access_size = 8; + } + break; + case OPC_RISC_C_FUNC_FSDSP_SQSP: + if (riscv_cpu_xlen(env) != 128) { /* C.FSDSP (RV32/64) */ + xinsn = OPC_RISC_FSD; + xinsn = SET_RS2(xinsn, GET_C_RS2(insn)); + access_rs1 = 2; + access_imm = GET_C_SDSP_IMM(insn); + access_size = 8; + } + break; + case OPC_RISC_C_FUNC_SWSP: /* C.SWSP */ + xinsn = OPC_RISC_SW; + xinsn = SET_RS2(xinsn, GET_C_RS2(insn)); + access_rs1 = 2; + access_imm = GET_C_SWSP_IMM(insn); + access_size = 4; + break; + case 7: + if (riscv_cpu_xlen(env) == 32) { /* C.FSWSP (RV32) */ + xinsn = OPC_RISC_FSW; + xinsn = SET_RS2(xinsn, GET_C_RS2(insn)); + access_rs1 = 2; + access_imm = GET_C_SWSP_IMM(insn); + access_size = 4; + } else { /* C.SDSP (RV64/RV128) */ + xinsn = OPC_RISC_SD; + xinsn = SET_RS2(xinsn, GET_C_RS2(insn)); + access_rs1 = 2; + access_imm = GET_C_SDSP_IMM(insn); + access_size = 8; + } + break; + default: + break; + } + break; + default: + break; + } + + /* + * Clear Bit1 of transformed instruction to indicate that + * original insruction was a 16bit instruction + */ + xinsn &= ~((target_ulong)0x2); + } else { + /* Transform 32bit (or wider) instructions */ + switch (MASK_OP_MAJOR(insn)) { + case OPC_RISC_ATOMIC: + xinsn = insn; + access_rs1 = GET_RS1(insn); + access_size = 1 << GET_FUNCT3(insn); + break; + case OPC_RISC_LOAD: + case OPC_RISC_FP_LOAD: + xinsn = SET_I_IMM(insn, 0); + access_rs1 = GET_RS1(insn); + access_imm = GET_IMM(insn); + access_size = 1 << GET_FUNCT3(insn); + break; + case OPC_RISC_STORE: + case OPC_RISC_FP_STORE: + xinsn = SET_S_IMM(insn, 0); + access_rs1 = GET_RS1(insn); + access_imm = GET_STORE_IMM(insn); + access_size = 1 << GET_FUNCT3(insn); + break; + case OPC_RISC_SYSTEM: + if (MASK_OP_SYSTEM(insn) == OPC_RISC_HLVHSV) { + xinsn = insn; + access_rs1 = GET_RS1(insn); + access_size = 1 << ((GET_FUNCT7(insn) >> 1) & 0x3); + access_size = 1 << access_size; + } + break; + default: + break; + } + } + + if (access_size) { + xinsn = SET_RS1(xinsn, (taddr - (env->gpr[access_rs1] + access_imm)) & + (access_size - 1)); + } + + return xinsn; +} #endif /* !CONFIG_USER_ONLY */ /* @@ -1338,6 +1551,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) target_ulong cause = cs->exception_index & RISCV_EXCP_INT_MASK; uint64_t deleg = async ? env->mideleg : env->medeleg; target_ulong tval = 0; + target_ulong tinst = 0; target_ulong htval = 0; target_ulong mtval2 = 0; @@ -1353,20 +1567,43 @@ void riscv_cpu_do_interrupt(CPUState *cs) if (!async) { /* set tval to badaddr for traps with address information */ switch (cause) { - case RISCV_EXCP_INST_GUEST_PAGE_FAULT: case RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT: case RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT: - case RISCV_EXCP_INST_ADDR_MIS: - case RISCV_EXCP_INST_ACCESS_FAULT: case RISCV_EXCP_LOAD_ADDR_MIS: case RISCV_EXCP_STORE_AMO_ADDR_MIS: case RISCV_EXCP_LOAD_ACCESS_FAULT: case RISCV_EXCP_STORE_AMO_ACCESS_FAULT: - case RISCV_EXCP_INST_PAGE_FAULT: case RISCV_EXCP_LOAD_PAGE_FAULT: case RISCV_EXCP_STORE_PAGE_FAULT: write_gva = env->two_stage_lookup; tval = env->badaddr; + if (env->two_stage_indirect_lookup) { + /* + * special pseudoinstruction for G-stage fault taken while + * doing VS-stage page table walk. + */ + tinst = (riscv_cpu_xlen(env) == 32) ? 0x00002000 : 0x00003000; + } else { + /* + * The "Addr. Offset" field in transformed instruction is + * non-zero only for misaligned access. + */ + tinst = riscv_transformed_insn(env, env->bins, tval); + } + break; + case RISCV_EXCP_INST_GUEST_PAGE_FAULT: + case RISCV_EXCP_INST_ADDR_MIS: + case RISCV_EXCP_INST_ACCESS_FAULT: + case RISCV_EXCP_INST_PAGE_FAULT: + write_gva = env->two_stage_lookup; + tval = env->badaddr; + if (env->two_stage_indirect_lookup) { + /* + * special pseudoinstruction for G-stage fault taken while + * doing VS-stage page table walk. + */ + tinst = (riscv_cpu_xlen(env) == 32) ? 0x00002000 : 0x00003000; + } break; case RISCV_EXCP_ILLEGAL_INST: case RISCV_EXCP_VIRT_INSTRUCTION_FAULT: @@ -1446,6 +1683,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->sepc = env->pc; env->stval = tval; env->htval = htval; + env->htinst = tinst; env->pc = (env->stvec >> 2 << 2) + ((async && (env->stvec & 3) == 1) ? cause * 4 : 0); riscv_cpu_set_mode(env, PRV_S); @@ -1476,6 +1714,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->mepc = env->pc; env->mtval = tval; env->mtval2 = mtval2; + env->mtinst = tinst; env->pc = (env->mtvec >> 2 << 2) + ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0); riscv_cpu_set_mode(env, PRV_M); @@ -1488,6 +1727,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) */ env->two_stage_lookup = false; + env->two_stage_indirect_lookup = false; #endif cs->exception_index = RISCV_EXCP_NONE; /* mark handled to qemu */ } diff --git a/target/riscv/instmap.h b/target/riscv/instmap.h index 40b6d2b64dee..f87753057680 100644 --- a/target/riscv/instmap.h +++ b/target/riscv/instmap.h @@ -184,6 +184,8 @@ enum { OPC_RISC_CSRRWI = OPC_RISC_SYSTEM | (0x5 << 12), OPC_RISC_CSRRSI = OPC_RISC_SYSTEM | (0x6 << 12), OPC_RISC_CSRRCI = OPC_RISC_SYSTEM | (0x7 << 12), + + OPC_RISC_HLVHSV = OPC_RISC_SYSTEM | (0x4 << 12), }; #define MASK_OP_FP_LOAD(op) (MASK_OP_MAJOR(op) | (op & (0x7 << 12))) @@ -310,12 +312,20 @@ enum { | (extract32(inst, 12, 8) << 12) \ | (sextract64(inst, 31, 1) << 20)) +#define GET_FUNCT3(inst) extract32(inst, 12, 3) +#define GET_FUNCT7(inst) extract32(inst, 25, 7) #define GET_RM(inst) extract32(inst, 12, 3) #define GET_RS3(inst) extract32(inst, 27, 5) #define GET_RS1(inst) extract32(inst, 15, 5) #define GET_RS2(inst) extract32(inst, 20, 5) #define GET_RD(inst) extract32(inst, 7, 5) #define GET_IMM(inst) sextract64(inst, 20, 12) +#define SET_RS1(inst, val) deposit32(inst, 15, 5, val) +#define SET_RS2(inst, val) deposit32(inst, 20, 5, val) +#define SET_RD(inst, val) deposit32(inst, 7, 5, val) +#define SET_I_IMM(inst, val) deposit32(inst, 20, 12, val) +#define SET_S_IMM(inst, val) \ + deposit32(deposit32(inst, 7, 5, val), 25, 7, (val) >> 5) /* RVC decoding macros */ #define GET_C_IMM(inst) (extract32(inst, 2, 5) \ @@ -346,6 +356,8 @@ enum { | (extract32(inst, 5, 1) << 6)) #define GET_C_LD_IMM(inst) ((extract16(inst, 10, 3) << 3) \ | (extract16(inst, 5, 2) << 6)) +#define GET_C_SW_IMM(inst) GET_C_LW_IMM(inst) +#define GET_C_SD_IMM(inst) GET_C_LD_IMM(inst) #define GET_C_J_IMM(inst) ((extract32(inst, 3, 3) << 1) \ | (extract32(inst, 11, 1) << 4) \ | (extract32(inst, 2, 1) << 5) \ @@ -366,4 +378,37 @@ enum { #define GET_C_RS1S(inst) (8 + extract16(inst, 7, 3)) #define GET_C_RS2S(inst) (8 + extract16(inst, 2, 3)) +#define GET_C_FUNC(inst) extract32(inst, 13, 3) +#define GET_C_OP(inst) extract32(inst, 0, 2) + +enum { + /* RVC Quadrants */ + OPC_RISC_C_OP_QUAD0 = 0x0, + OPC_RISC_C_OP_QUAD1 = 0x1, + OPC_RISC_C_OP_QUAD2 = 0x2 +}; + +enum { + /* RVC Quadrant 0 */ + OPC_RISC_C_FUNC_ADDI4SPN = 0x0, + OPC_RISC_C_FUNC_FLD_LQ = 0x1, + OPC_RISC_C_FUNC_LW = 0x2, + OPC_RISC_C_FUNC_FLW_LD = 0x3, + OPC_RISC_C_FUNC_FSD_SQ = 0x5, + OPC_RISC_C_FUNC_SW = 0x6, + OPC_RISC_C_FUNC_FSW_SD = 0x7 +}; + +enum { + /* RVC Quadrant 2 */ + OPC_RISC_C_FUNC_SLLI_SLLI64 = 0x0, + OPC_RISC_C_FUNC_FLDSP_LQSP = 0x1, + OPC_RISC_C_FUNC_LWSP = 0x2, + OPC_RISC_C_FUNC_FLWSP_LDSP = 0x3, + OPC_RISC_C_FUNC_JR_MV_EBREAK_JALR_ADD = 0x4, + OPC_RISC_C_FUNC_FSDSP_SQSP = 0x5, + OPC_RISC_C_FUNC_SWSP = 0x6, + OPC_RISC_C_FUNC_FSWSP_SDSP = 0x7 +}; + #endif From 9a1f054d5bd9acaa82b66e09309482cba9eced63 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Thu, 30 Jun 2022 11:41:50 +0530 Subject: [PATCH 02/44] target/riscv: Force disable extensions if priv spec version does not match We should disable extensions in riscv_cpu_realize() if minimum required priv spec version is not satisfied. This also ensures that machines with priv spec v1.11 (or lower) cannot enable H, V, and various multi-letter extensions. Fixes: a775398be2e9 ("target/riscv: Add isa extenstion strings to the device tree") Reviewed-by: Alistair Francis Signed-off-by: Anup Patel Signed-off-by: Rahul Pathak Message-Id: <20220630061150.905174-3-apatel@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 150 ++++++++++++++++++++++++++++----------------- 1 file changed, 94 insertions(+), 56 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index ac6f82ebd006..cab74faacab3 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -43,9 +43,82 @@ static const char riscv_single_letter_exts[] = "IEMAFDQCPVH"; struct isa_ext_data { const char *name; - bool enabled; + bool multi_letter; + int min_version; + int ext_enable_offset; }; +#define ISA_EXT_DATA_ENTRY(_name, _m_letter, _min_ver, _prop) \ +{#_name, _m_letter, _min_ver, offsetof(struct RISCVCPUConfig, _prop)} + +/** + * Here are the ordering rules of extension naming defined by RISC-V + * specification : + * 1. All extensions should be separated from other multi-letter extensions + * by an underscore. + * 2. The first letter following the 'Z' conventionally indicates the most + * closely related alphabetical extension category, IMAFDQLCBKJTPVH. + * If multiple 'Z' extensions are named, they should be ordered first + * by category, then alphabetically within a category. + * 3. Standard supervisor-level extensions (starts with 'S') should be + * listed after standard unprivileged extensions. If multiple + * supervisor-level extensions are listed, they should be ordered + * alphabetically. + * 4. Non-standard extensions (starts with 'X') must be listed after all + * standard extensions. They must be separated from other multi-letter + * extensions by an underscore. + */ +static const struct isa_ext_data isa_edata_arr[] = { + ISA_EXT_DATA_ENTRY(h, false, PRIV_VERSION_1_12_0, ext_h), + ISA_EXT_DATA_ENTRY(v, false, PRIV_VERSION_1_12_0, ext_v), + ISA_EXT_DATA_ENTRY(zicsr, true, PRIV_VERSION_1_10_0, ext_icsr), + ISA_EXT_DATA_ENTRY(zifencei, true, PRIV_VERSION_1_10_0, ext_ifencei), + ISA_EXT_DATA_ENTRY(zfh, true, PRIV_VERSION_1_12_0, ext_zfh), + ISA_EXT_DATA_ENTRY(zfhmin, true, PRIV_VERSION_1_12_0, ext_zfhmin), + ISA_EXT_DATA_ENTRY(zfinx, true, PRIV_VERSION_1_12_0, ext_zfinx), + ISA_EXT_DATA_ENTRY(zdinx, true, PRIV_VERSION_1_12_0, ext_zdinx), + ISA_EXT_DATA_ENTRY(zba, true, PRIV_VERSION_1_12_0, ext_zba), + ISA_EXT_DATA_ENTRY(zbb, true, PRIV_VERSION_1_12_0, ext_zbb), + ISA_EXT_DATA_ENTRY(zbc, true, PRIV_VERSION_1_12_0, ext_zbc), + ISA_EXT_DATA_ENTRY(zbkb, true, PRIV_VERSION_1_12_0, ext_zbkb), + ISA_EXT_DATA_ENTRY(zbkc, true, PRIV_VERSION_1_12_0, ext_zbkc), + ISA_EXT_DATA_ENTRY(zbkx, true, PRIV_VERSION_1_12_0, ext_zbkx), + ISA_EXT_DATA_ENTRY(zbs, true, PRIV_VERSION_1_12_0, ext_zbs), + ISA_EXT_DATA_ENTRY(zk, true, PRIV_VERSION_1_12_0, ext_zk), + ISA_EXT_DATA_ENTRY(zkn, true, PRIV_VERSION_1_12_0, ext_zkn), + ISA_EXT_DATA_ENTRY(zknd, true, PRIV_VERSION_1_12_0, ext_zknd), + ISA_EXT_DATA_ENTRY(zkne, true, PRIV_VERSION_1_12_0, ext_zkne), + ISA_EXT_DATA_ENTRY(zknh, true, PRIV_VERSION_1_12_0, ext_zknh), + ISA_EXT_DATA_ENTRY(zkr, true, PRIV_VERSION_1_12_0, ext_zkr), + ISA_EXT_DATA_ENTRY(zks, true, PRIV_VERSION_1_12_0, ext_zks), + ISA_EXT_DATA_ENTRY(zksed, true, PRIV_VERSION_1_12_0, ext_zksed), + ISA_EXT_DATA_ENTRY(zksh, true, PRIV_VERSION_1_12_0, ext_zksh), + ISA_EXT_DATA_ENTRY(zkt, true, PRIV_VERSION_1_12_0, ext_zkt), + ISA_EXT_DATA_ENTRY(zve32f, true, PRIV_VERSION_1_12_0, ext_zve32f), + ISA_EXT_DATA_ENTRY(zve64f, true, PRIV_VERSION_1_12_0, ext_zve64f), + ISA_EXT_DATA_ENTRY(zhinx, true, PRIV_VERSION_1_12_0, ext_zhinx), + ISA_EXT_DATA_ENTRY(zhinxmin, true, PRIV_VERSION_1_12_0, ext_zhinxmin), + ISA_EXT_DATA_ENTRY(svinval, true, PRIV_VERSION_1_12_0, ext_svinval), + ISA_EXT_DATA_ENTRY(svnapot, true, PRIV_VERSION_1_12_0, ext_svnapot), + ISA_EXT_DATA_ENTRY(svpbmt, true, PRIV_VERSION_1_12_0, ext_svpbmt), +}; + +static bool isa_ext_is_enabled(RISCVCPU *cpu, + const struct isa_ext_data *edata) +{ + bool *ext_enabled = (void *)&cpu->cfg + edata->ext_enable_offset; + + return *ext_enabled; +} + +static void isa_ext_update_enabled(RISCVCPU *cpu, + const struct isa_ext_data *edata, bool en) +{ + bool *ext_enabled = (void *)&cpu->cfg + edata->ext_enable_offset; + + *ext_enabled = en; +} + const char * const riscv_int_regnames[] = { "x0/zero", "x1/ra", "x2/sp", "x3/gp", "x4/tp", "x5/t0", "x6/t1", "x7/t2", "x8/s0", "x9/s1", "x10/a0", "x11/a1", "x12/a2", "x13/a3", @@ -530,7 +603,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) CPURISCVState *env = &cpu->env; RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); CPUClass *cc = CPU_CLASS(mcc); - int priv_version = -1; + int i, priv_version = -1; Error *local_err = NULL; cpu_exec_realizefn(cs, &local_err); @@ -558,6 +631,23 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) set_priv_version(env, priv_version); } + /* Force disable extensions if priv spec version does not match */ + for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) { + if (isa_ext_is_enabled(cpu, &isa_edata_arr[i]) && + (env->priv_ver < isa_edata_arr[i].min_version)) { + isa_ext_update_enabled(cpu, &isa_edata_arr[i], false); +#ifndef CONFIG_USER_ONLY + warn_report("disabling %s extension for hart 0x%lx because " + "privilege spec version does not match", + isa_edata_arr[i].name, (unsigned long)env->mhartid); +#else + warn_report("disabling %s extension because " + "privilege spec version does not match", + isa_edata_arr[i].name); +#endif + } + } + if (cpu->cfg.mmu) { riscv_set_feature(env, RISCV_FEATURE_MMU); } @@ -1044,67 +1134,15 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) device_class_set_props(dc, riscv_cpu_properties); } -#define ISA_EDATA_ENTRY(name, prop) {#name, cpu->cfg.prop} - static void riscv_isa_string_ext(RISCVCPU *cpu, char **isa_str, int max_str_len) { char *old = *isa_str; char *new = *isa_str; int i; - /** - * Here are the ordering rules of extension naming defined by RISC-V - * specification : - * 1. All extensions should be separated from other multi-letter extensions - * by an underscore. - * 2. The first letter following the 'Z' conventionally indicates the most - * closely related alphabetical extension category, IMAFDQLCBKJTPVH. - * If multiple 'Z' extensions are named, they should be ordered first - * by category, then alphabetically within a category. - * 3. Standard supervisor-level extensions (starts with 'S') should be - * listed after standard unprivileged extensions. If multiple - * supervisor-level extensions are listed, they should be ordered - * alphabetically. - * 4. Non-standard extensions (starts with 'X') must be listed after all - * standard extensions. They must be separated from other multi-letter - * extensions by an underscore. - */ - struct isa_ext_data isa_edata_arr[] = { - ISA_EDATA_ENTRY(zicsr, ext_icsr), - ISA_EDATA_ENTRY(zifencei, ext_ifencei), - ISA_EDATA_ENTRY(zmmul, ext_zmmul), - ISA_EDATA_ENTRY(zfh, ext_zfh), - ISA_EDATA_ENTRY(zfhmin, ext_zfhmin), - ISA_EDATA_ENTRY(zfinx, ext_zfinx), - ISA_EDATA_ENTRY(zdinx, ext_zdinx), - ISA_EDATA_ENTRY(zba, ext_zba), - ISA_EDATA_ENTRY(zbb, ext_zbb), - ISA_EDATA_ENTRY(zbc, ext_zbc), - ISA_EDATA_ENTRY(zbkb, ext_zbkb), - ISA_EDATA_ENTRY(zbkc, ext_zbkc), - ISA_EDATA_ENTRY(zbkx, ext_zbkx), - ISA_EDATA_ENTRY(zbs, ext_zbs), - ISA_EDATA_ENTRY(zk, ext_zk), - ISA_EDATA_ENTRY(zkn, ext_zkn), - ISA_EDATA_ENTRY(zknd, ext_zknd), - ISA_EDATA_ENTRY(zkne, ext_zkne), - ISA_EDATA_ENTRY(zknh, ext_zknh), - ISA_EDATA_ENTRY(zkr, ext_zkr), - ISA_EDATA_ENTRY(zks, ext_zks), - ISA_EDATA_ENTRY(zksed, ext_zksed), - ISA_EDATA_ENTRY(zksh, ext_zksh), - ISA_EDATA_ENTRY(zkt, ext_zkt), - ISA_EDATA_ENTRY(zve32f, ext_zve32f), - ISA_EDATA_ENTRY(zve64f, ext_zve64f), - ISA_EDATA_ENTRY(zhinx, ext_zhinx), - ISA_EDATA_ENTRY(zhinxmin, ext_zhinxmin), - ISA_EDATA_ENTRY(svinval, ext_svinval), - ISA_EDATA_ENTRY(svnapot, ext_svnapot), - ISA_EDATA_ENTRY(svpbmt, ext_svpbmt), - }; - for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) { - if (isa_edata_arr[i].enabled) { + if (isa_edata_arr[i].multi_letter && + 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 3363277525958e173b4526f9dca225e125b1a5de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9trot?= Date: Sun, 10 Jul 2022 13:04:51 +0200 Subject: [PATCH 03/44] target/riscv: fix shifts shamt value for rv128c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For rv128c shifts, a shamt of 0 is a shamt of 64, while for rv32c/rv64c it stays 0 and is a hint instruction that does not change processor state. For rv128c right shifts, the 6-bit shamt is in addition sign extended to 7 bits. Signed-off-by: Frédéric Pétrot Reviewed-by: Weiwei Li Reviewed-by: Richard Henderson Message-Id: <20220710110451.245567-1-frederic.petrot@univ-grenoble-alpes.fr> Signed-off-by: Alistair Francis --- disas/riscv.c | 27 +++++++++++++++++++++------ target/riscv/insn16.decode | 7 ++++--- target/riscv/translate.c | 20 ++++++++++++++++++-- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 7af6afc8fa8f..489c2ae5e848 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -2402,10 +2402,25 @@ static int32_t operand_sbimm12(rv_inst inst) ((inst << 56) >> 63) << 11; } -static uint32_t operand_cimmsh6(rv_inst inst) +static uint32_t operand_cimmshl6(rv_inst inst, rv_isa isa) { - return ((inst << 51) >> 63) << 5 | + int imm = ((inst << 51) >> 63) << 5 | (inst << 57) >> 59; + if (isa == rv128) { + imm = imm ? imm : 64; + } + return imm; +} + +static uint32_t operand_cimmshr6(rv_inst inst, rv_isa isa) +{ + int imm = ((inst << 51) >> 63) << 5 | + (inst << 57) >> 59; + if (isa == rv128) { + imm = imm | (imm & 32) << 1; + imm = imm ? imm : 64; + } + return imm; } static int32_t operand_cimmi(rv_inst inst) @@ -2529,7 +2544,7 @@ static uint32_t operand_rnum(rv_inst inst) /* decode operands */ -static void decode_inst_operands(rv_decode *dec) +static void decode_inst_operands(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; dec->codec = opcode_data[dec->op].codec; @@ -2652,7 +2667,7 @@ static void decode_inst_operands(rv_decode *dec) case rv_codec_cb_sh6: dec->rd = dec->rs1 = operand_crs1rdq(inst) + 8; dec->rs2 = rv_ireg_zero; - dec->imm = operand_cimmsh6(inst); + dec->imm = operand_cimmshr6(inst, isa); break; case rv_codec_ci: dec->rd = dec->rs1 = operand_crs1rd(inst); @@ -2667,7 +2682,7 @@ static void decode_inst_operands(rv_decode *dec) case rv_codec_ci_sh6: dec->rd = dec->rs1 = operand_crs1rd(inst); dec->rs2 = rv_ireg_zero; - dec->imm = operand_cimmsh6(inst); + dec->imm = operand_cimmshl6(inst, isa); break; case rv_codec_ci_16sp: dec->rd = rv_ireg_sp; @@ -3193,7 +3208,7 @@ disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst) dec.pc = pc; dec.inst = inst; decode_inst_opcode(&dec, isa); - decode_inst_operands(&dec); + decode_inst_operands(&dec, isa); decode_inst_decompress(&dec, isa); decode_inst_lift_pseudo(&dec); format_inst(buf, buflen, 16, &dec); diff --git a/target/riscv/insn16.decode b/target/riscv/insn16.decode index 02c8f61b4814..ccfe59f294d0 100644 --- a/target/riscv/insn16.decode +++ b/target/riscv/insn16.decode @@ -31,7 +31,8 @@ %imm_cb 12:s1 5:2 2:1 10:2 3:2 !function=ex_shift_1 %imm_cj 12:s1 8:1 9:2 6:1 7:1 2:1 11:1 3:3 !function=ex_shift_1 -%shimm_6bit 12:1 2:5 !function=ex_rvc_shifti +%shlimm_6bit 12:1 2:5 !function=ex_rvc_shiftli +%shrimm_6bit 12:1 2:5 !function=ex_rvc_shiftri %uimm_6bit_lq 2:4 12:1 6:1 !function=ex_shift_4 %uimm_6bit_ld 2:3 12:1 5:2 !function=ex_shift_3 %uimm_6bit_lw 2:2 12:1 4:3 !function=ex_shift_2 @@ -82,9 +83,9 @@ @c_addi16sp ... . ..... ..... .. &i imm=%imm_addi16sp rs1=2 rd=2 @c_shift ... . .. ... ..... .. \ - &shift rd=%rs1_3 rs1=%rs1_3 shamt=%shimm_6bit + &shift rd=%rs1_3 rs1=%rs1_3 shamt=%shrimm_6bit @c_shift2 ... . .. ... ..... .. \ - &shift rd=%rd rs1=%rd shamt=%shimm_6bit + &shift rd=%rd rs1=%rd shamt=%shlimm_6bit @c_andi ... . .. ... ..... .. &i imm=%imm_ci rs1=%rs1_3 rd=%rs1_3 diff --git a/target/riscv/translate.c b/target/riscv/translate.c index f8af6daa7090..6eeb72846243 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -705,10 +705,26 @@ static int ex_rvc_register(DisasContext *ctx, int reg) return 8 + reg; } -static int ex_rvc_shifti(DisasContext *ctx, int imm) +static int ex_rvc_shiftli(DisasContext *ctx, int imm) { /* For RV128 a shamt of 0 means a shift by 64. */ - return imm ? imm : 64; + if (get_ol(ctx) == MXL_RV128) { + imm = imm ? imm : 64; + } + return imm; +} + +static int ex_rvc_shiftri(DisasContext *ctx, int imm) +{ + /* + * For RV128 a shamt of 0 means a shift by 64, furthermore, for right + * shifts, the shamt is sign-extended. + */ + if (get_ol(ctx) == MXL_RV128) { + imm = imm | (imm & 32) << 1; + imm = imm ? imm : 64; + } + return imm; } /* Include the auto-generated decoder for 32 bit insn */ From 6d00ffad4e9549086e80a47566ed5f3b4b8bb2dd Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Sun, 10 Jul 2022 18:15:46 +0800 Subject: [PATCH 04/44] target/riscv: move zmmul out of the experimental properties - Zmmul is ratified and is now version 1.0 Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Message-Id: <20220710101546.3907-1-liweiwei@iscas.ac.cn> 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 cab74faacab3..a3e4e2477ff7 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1009,12 +1009,13 @@ static Property riscv_cpu_extensions[] = { DEFINE_PROP_BOOL("zhinx", RISCVCPU, cfg.ext_zhinx, false), DEFINE_PROP_BOOL("zhinxmin", RISCVCPU, cfg.ext_zhinxmin, false), + DEFINE_PROP_BOOL("zmmul", RISCVCPU, cfg.ext_zmmul, false), + /* Vendor-specific custom extensions */ DEFINE_PROP_BOOL("xventanacondops", RISCVCPU, cfg.ext_XVentanaCondOps, false), /* These are experimental so mark with 'x-' */ DEFINE_PROP_BOOL("x-j", RISCVCPU, cfg.ext_j, false), - DEFINE_PROP_BOOL("x-zmmul", RISCVCPU, cfg.ext_zmmul, false), /* ePMP 0.9.3 */ DEFINE_PROP_BOOL("x-epmp", RISCVCPU, cfg.epmp, false), DEFINE_PROP_BOOL("x-aia", RISCVCPU, cfg.aia, false), From e4b4f0b71ccbeb0157489c0904ba4957761528ff Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Mon, 13 Jun 2022 13:58:10 +0200 Subject: [PATCH 05/44] hw/riscv: virt: pass random seed to fdt If the FDT contains /chosen/rng-seed, then the Linux RNG will use it to initialize early. Set this using the usual guest random number generation function. This is confirmed to successfully initialize the RNG on Linux 5.19-rc2. Cc: Alistair Francis Signed-off-by: Jason A. Donenfeld Reviewed-by: Bin Meng Message-Id: <20220613115810.178210-1-Jason@zx2c4.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index bc424dd2f523..f2ce5663a4c7 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qemu/error-report.h" +#include "qemu/guest-random.h" #include "qapi/error.h" #include "hw/boards.h" #include "hw/loader.h" @@ -998,6 +999,7 @@ static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap, MachineState *mc = MACHINE(s); uint32_t phandle = 1, irq_mmio_phandle = 1, msi_pcie_phandle = 1; uint32_t irq_pcie_phandle = 1, irq_virtio_phandle = 1; + uint8_t rng_seed[32]; if (mc->dtb) { mc->fdt = load_device_tree(mc->dtb, &s->fdt_size); @@ -1046,6 +1048,10 @@ static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap, if (cmdline && *cmdline) { qemu_fdt_setprop_string(mc->fdt, "/chosen", "bootargs", cmdline); } + + /* Pass seed to RNG */ + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + qemu_fdt_setprop(mc->fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); } static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem, From 0b572c8131998e7bcd048dbbbe78f95e6101d68d Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Mon, 18 Jul 2022 21:09:50 +0800 Subject: [PATCH 06/44] target/riscv: Add check for supported privilege mode combinations There are 3 suggested privilege mode combinations listed in section 1.2 of the riscv-privileged spec(draft-20220717): 1) M, 2) M, U 3) M, S, U Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Reviewed-by: Andrew Jones Message-Id: <20220718130955.11899-2-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index a3e4e2477ff7..b919ad90564f 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -721,6 +721,12 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) return; } + if (cpu->cfg.ext_s && !cpu->cfg.ext_u) { + error_setg(errp, + "Setting S extension without U extension is illegal"); + return; + } + if (cpu->cfg.ext_f && !cpu->cfg.ext_icsr) { error_setg(errp, "F extension requires Zicsr"); return; From 756b0374dc37af2213e3b652fb3f50a4cc9acb24 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Mon, 18 Jul 2022 21:09:51 +0800 Subject: [PATCH 07/44] target/riscv: H extension depends on I extension Add check for "H depends on an I base integer ISA with 32 x registers" which is stated at the beginning of chapter 8 of the riscv-privileged spec(draft-20220717) Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Reviewed-by: Andrew Jones Message-Id: <20220718130955.11899-3-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index b919ad90564f..fb37ffac6491 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -727,6 +727,12 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) return; } + if (cpu->cfg.ext_h && !cpu->cfg.ext_i) { + error_setg(errp, + "H depends on an I base integer ISA with 32 x registers"); + return; + } + if (cpu->cfg.ext_f && !cpu->cfg.ext_icsr) { error_setg(errp, "F extension requires Zicsr"); return; From 108c4f26ce441fe0fa0ee20c776e2b71706068be Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Mon, 18 Jul 2022 21:09:52 +0800 Subject: [PATCH 08/44] target/riscv: Fix checkpatch warning may triggered in csr_ops table Fix the lines with over 80 characters Fix the lines which are obviously misalgined with other lines in the same group Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Reviewed-by: Andrew Jones Message-Id: <20220718130955.11899-4-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 441 ++++++++++++++++++++++++--------------------- 1 file changed, 234 insertions(+), 207 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 235f2a011e70..7d4b6cecedd6 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -3461,20 +3461,20 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_FRM] = { "frm", fs, read_frm, write_frm }, [CSR_FCSR] = { "fcsr", fs, read_fcsr, write_fcsr }, /* Vector CSRs */ - [CSR_VSTART] = { "vstart", vs, read_vstart, write_vstart, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VXSAT] = { "vxsat", vs, read_vxsat, write_vxsat, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VXRM] = { "vxrm", vs, read_vxrm, write_vxrm, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VCSR] = { "vcsr", vs, read_vcsr, write_vcsr, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VL] = { "vl", vs, read_vl, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VTYPE] = { "vtype", vs, read_vtype, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VLENB] = { "vlenb", vs, read_vlenb, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSTART] = { "vstart", vs, read_vstart, write_vstart, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VXSAT] = { "vxsat", vs, read_vxsat, write_vxsat, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VXRM] = { "vxrm", vs, read_vxrm, write_vxrm, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VCSR] = { "vcsr", vs, read_vcsr, write_vcsr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VL] = { "vl", vs, read_vl, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VTYPE] = { "vtype", vs, read_vtype, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VLENB] = { "vlenb", vs, read_vlenb, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* User Timers and Counters */ [CSR_CYCLE] = { "cycle", ctr, read_hpmcounter }, [CSR_INSTRET] = { "instret", ctr, read_hpmcounter }, @@ -3493,10 +3493,14 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { #if !defined(CONFIG_USER_ONLY) /* Machine Timers and Counters */ - [CSR_MCYCLE] = { "mcycle", any, read_hpmcounter, write_mhpmcounter}, - [CSR_MINSTRET] = { "minstret", any, read_hpmcounter, write_mhpmcounter}, - [CSR_MCYCLEH] = { "mcycleh", any32, read_hpmcounterh, write_mhpmcounterh}, - [CSR_MINSTRETH] = { "minstreth", any32, read_hpmcounterh, write_mhpmcounterh}, + [CSR_MCYCLE] = { "mcycle", any, read_hpmcounter, + write_mhpmcounter }, + [CSR_MINSTRET] = { "minstret", any, read_hpmcounter, + write_mhpmcounter }, + [CSR_MCYCLEH] = { "mcycleh", any32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MINSTRETH] = { "minstreth", any32, read_hpmcounterh, + write_mhpmcounterh }, /* Machine Information Registers */ [CSR_MVENDORID] = { "mvendorid", any, read_mvendorid }, @@ -3505,23 +3509,25 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MHARTID] = { "mhartid", any, read_mhartid }, [CSR_MCONFIGPTR] = { "mconfigptr", any, read_zero, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Machine Trap Setup */ - [CSR_MSTATUS] = { "mstatus", any, read_mstatus, write_mstatus, NULL, - read_mstatus_i128 }, - [CSR_MISA] = { "misa", any, read_misa, write_misa, NULL, - read_misa_i128 }, - [CSR_MIDELEG] = { "mideleg", any, NULL, NULL, rmw_mideleg }, - [CSR_MEDELEG] = { "medeleg", any, read_medeleg, write_medeleg }, - [CSR_MIE] = { "mie", any, NULL, NULL, rmw_mie }, - [CSR_MTVEC] = { "mtvec", any, read_mtvec, write_mtvec }, - [CSR_MCOUNTEREN] = { "mcounteren", any, read_mcounteren, write_mcounteren }, - - [CSR_MSTATUSH] = { "mstatush", any32, read_mstatush, write_mstatush }, + [CSR_MSTATUS] = { "mstatus", any, read_mstatus, write_mstatus, + NULL, read_mstatus_i128 }, + [CSR_MISA] = { "misa", any, read_misa, write_misa, + NULL, read_misa_i128 }, + [CSR_MIDELEG] = { "mideleg", any, NULL, NULL, rmw_mideleg }, + [CSR_MEDELEG] = { "medeleg", any, read_medeleg, write_medeleg }, + [CSR_MIE] = { "mie", any, NULL, NULL, rmw_mie }, + [CSR_MTVEC] = { "mtvec", any, read_mtvec, write_mtvec }, + [CSR_MCOUNTEREN] = { "mcounteren", any, read_mcounteren, + write_mcounteren }, + + [CSR_MSTATUSH] = { "mstatush", any32, read_mstatush, + write_mstatush }, /* Machine Trap Handling */ - [CSR_MSCRATCH] = { "mscratch", any, read_mscratch, write_mscratch, NULL, - read_mscratch_i128, write_mscratch_i128 }, + [CSR_MSCRATCH] = { "mscratch", any, read_mscratch, write_mscratch, + NULL, read_mscratch_i128, write_mscratch_i128 }, [CSR_MEPC] = { "mepc", any, read_mepc, write_mepc }, [CSR_MCAUSE] = { "mcause", any, read_mcause, write_mcause }, [CSR_MTVAL] = { "mtval", any, read_mtval, write_mtval }, @@ -3532,12 +3538,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MIREG] = { "mireg", aia_any, NULL, NULL, rmw_xireg }, /* Machine-Level Interrupts (AIA) */ - [CSR_MTOPEI] = { "mtopei", aia_any, NULL, NULL, rmw_xtopei }, - [CSR_MTOPI] = { "mtopi", aia_any, read_mtopi }, + [CSR_MTOPEI] = { "mtopei", aia_any, NULL, NULL, rmw_xtopei }, + [CSR_MTOPI] = { "mtopi", aia_any, read_mtopi }, /* Virtual Interrupts for Supervisor Level (AIA) */ - [CSR_MVIEN] = { "mvien", aia_any, read_zero, write_ignore }, - [CSR_MVIP] = { "mvip", aia_any, read_zero, write_ignore }, + [CSR_MVIEN] = { "mvien", aia_any, read_zero, write_ignore }, + [CSR_MVIP] = { "mvip", aia_any, read_zero, write_ignore }, /* Machine-Level High-Half CSRs (AIA) */ [CSR_MIDELEGH] = { "midelegh", aia_any32, NULL, NULL, rmw_midelegh }, @@ -3548,33 +3554,34 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* Execution environment configuration */ [CSR_MENVCFG] = { "menvcfg", any, read_menvcfg, write_menvcfg, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MENVCFGH] = { "menvcfgh", any32, read_menvcfgh, write_menvcfgh, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_SENVCFG] = { "senvcfg", smode, read_senvcfg, write_senvcfg, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_HENVCFG] = { "henvcfg", hmode, read_henvcfg, write_henvcfg, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_HENVCFGH] = { "henvcfgh", hmode32, read_henvcfgh, write_henvcfgh, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Supervisor Trap Setup */ - [CSR_SSTATUS] = { "sstatus", smode, read_sstatus, write_sstatus, NULL, - read_sstatus_i128 }, - [CSR_SIE] = { "sie", smode, NULL, NULL, rmw_sie }, - [CSR_STVEC] = { "stvec", smode, read_stvec, write_stvec }, - [CSR_SCOUNTEREN] = { "scounteren", smode, read_scounteren, write_scounteren }, + [CSR_SSTATUS] = { "sstatus", smode, read_sstatus, write_sstatus, + NULL, read_sstatus_i128 }, + [CSR_SIE] = { "sie", smode, NULL, NULL, rmw_sie }, + [CSR_STVEC] = { "stvec", smode, read_stvec, write_stvec }, + [CSR_SCOUNTEREN] = { "scounteren", smode, read_scounteren, + write_scounteren }, /* Supervisor Trap Handling */ - [CSR_SSCRATCH] = { "sscratch", smode, read_sscratch, write_sscratch, NULL, - read_sscratch_i128, write_sscratch_i128 }, + [CSR_SSCRATCH] = { "sscratch", smode, read_sscratch, write_sscratch, + NULL, read_sscratch_i128, write_sscratch_i128 }, [CSR_SEPC] = { "sepc", smode, read_sepc, write_sepc }, [CSR_SCAUSE] = { "scause", smode, read_scause, write_scause }, - [CSR_STVAL] = { "stval", smode, read_stval, write_stval }, + [CSR_STVAL] = { "stval", smode, read_stval, write_stval }, [CSR_SIP] = { "sip", smode, NULL, NULL, rmw_sip }, /* Supervisor Protection and Translation */ - [CSR_SATP] = { "satp", smode, read_satp, write_satp }, + [CSR_SATP] = { "satp", smode, read_satp, write_satp }, /* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */ [CSR_SISELECT] = { "siselect", aia_smode, NULL, NULL, rmw_xiselect }, @@ -3588,87 +3595,100 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_SIEH] = { "sieh", aia_smode32, NULL, NULL, rmw_sieh }, [CSR_SIPH] = { "siph", aia_smode32, NULL, NULL, rmw_siph }, - [CSR_HSTATUS] = { "hstatus", hmode, read_hstatus, write_hstatus, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HEDELEG] = { "hedeleg", hmode, read_hedeleg, write_hedeleg, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HSTATUS] = { "hstatus", hmode, read_hstatus, write_hstatus, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HEDELEG] = { "hedeleg", hmode, read_hedeleg, write_hedeleg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_HIDELEG] = { "hideleg", hmode, NULL, NULL, rmw_hideleg, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HVIP] = { "hvip", hmode, NULL, NULL, rmw_hvip, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HIP] = { "hip", hmode, NULL, NULL, rmw_hip, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HIE] = { "hie", hmode, NULL, NULL, rmw_hie, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HCOUNTEREN] = { "hcounteren", hmode, read_hcounteren, write_hcounteren, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HGEIE] = { "hgeie", hmode, read_hgeie, write_hgeie, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HTVAL] = { "htval", hmode, read_htval, write_htval, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HTINST] = { "htinst", hmode, read_htinst, write_htinst, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HVIP] = { "hvip", hmode, NULL, NULL, rmw_hvip, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HIP] = { "hip", hmode, NULL, NULL, rmw_hip, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HIE] = { "hie", hmode, NULL, NULL, rmw_hie, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HCOUNTEREN] = { "hcounteren", hmode, read_hcounteren, + write_hcounteren, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HGEIE] = { "hgeie", hmode, read_hgeie, write_hgeie, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HTVAL] = { "htval", hmode, read_htval, write_htval, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HTINST] = { "htinst", hmode, read_htinst, write_htinst, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_HGEIP] = { "hgeip", hmode, read_hgeip, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HGATP] = { "hgatp", hmode, read_hgatp, write_hgatp, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HTIMEDELTA] = { "htimedelta", hmode, read_htimedelta, write_htimedelta, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HTIMEDELTAH] = { "htimedeltah", hmode32, read_htimedeltah, write_htimedeltah, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - - [CSR_VSSTATUS] = { "vsstatus", hmode, read_vsstatus, write_vsstatus, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSIP] = { "vsip", hmode, NULL, NULL, rmw_vsip, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSIE] = { "vsie", hmode, NULL, NULL, rmw_vsie , - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSTVEC] = { "vstvec", hmode, read_vstvec, write_vstvec, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSSCRATCH] = { "vsscratch", hmode, read_vsscratch, write_vsscratch, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSEPC] = { "vsepc", hmode, read_vsepc, write_vsepc, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSCAUSE] = { "vscause", hmode, read_vscause, write_vscause, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSTVAL] = { "vstval", hmode, read_vstval, write_vstval, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSATP] = { "vsatp", hmode, read_vsatp, write_vsatp, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - - [CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HGATP] = { "hgatp", hmode, read_hgatp, write_hgatp, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HTIMEDELTA] = { "htimedelta", hmode, read_htimedelta, + write_htimedelta, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HTIMEDELTAH] = { "htimedeltah", hmode32, read_htimedeltah, + write_htimedeltah, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + + [CSR_VSSTATUS] = { "vsstatus", hmode, read_vsstatus, + write_vsstatus, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSIP] = { "vsip", hmode, NULL, NULL, rmw_vsip, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSIE] = { "vsie", hmode, NULL, NULL, rmw_vsie , + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSTVEC] = { "vstvec", hmode, read_vstvec, write_vstvec, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSSCRATCH] = { "vsscratch", hmode, read_vsscratch, + write_vsscratch, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSEPC] = { "vsepc", hmode, read_vsepc, write_vsepc, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSCAUSE] = { "vscause", hmode, read_vscause, write_vscause, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSTVAL] = { "vstval", hmode, read_vstval, write_vstval, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSATP] = { "vsatp", hmode, read_vsatp, write_vsatp, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + + [CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */ [CSR_HVIEN] = { "hvien", aia_hmode, read_zero, write_ignore }, - [CSR_HVICTL] = { "hvictl", aia_hmode, read_hvictl, write_hvictl }, - [CSR_HVIPRIO1] = { "hviprio1", aia_hmode, read_hviprio1, write_hviprio1 }, - [CSR_HVIPRIO2] = { "hviprio2", aia_hmode, read_hviprio2, write_hviprio2 }, + [CSR_HVICTL] = { "hvictl", aia_hmode, read_hvictl, + write_hvictl }, + [CSR_HVIPRIO1] = { "hviprio1", aia_hmode, read_hviprio1, + write_hviprio1 }, + [CSR_HVIPRIO2] = { "hviprio2", aia_hmode, read_hviprio2, + write_hviprio2 }, /* * VS-Level Window to Indirectly Accessed Registers (H-extension with AIA) */ - [CSR_VSISELECT] = { "vsiselect", aia_hmode, NULL, NULL, rmw_xiselect }, - [CSR_VSIREG] = { "vsireg", aia_hmode, NULL, NULL, rmw_xireg }, + [CSR_VSISELECT] = { "vsiselect", aia_hmode, NULL, NULL, + rmw_xiselect }, + [CSR_VSIREG] = { "vsireg", aia_hmode, NULL, NULL, rmw_xireg }, /* VS-Level Interrupts (H-extension with AIA) */ [CSR_VSTOPEI] = { "vstopei", aia_hmode, NULL, NULL, rmw_xtopei }, [CSR_VSTOPI] = { "vstopi", aia_hmode, read_vstopi }, /* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */ - [CSR_HIDELEGH] = { "hidelegh", aia_hmode32, NULL, NULL, rmw_hidelegh }, - [CSR_HVIENH] = { "hvienh", aia_hmode32, read_zero, write_ignore }, + [CSR_HIDELEGH] = { "hidelegh", aia_hmode32, NULL, NULL, + rmw_hidelegh }, + [CSR_HVIENH] = { "hvienh", aia_hmode32, read_zero, + write_ignore }, [CSR_HVIPH] = { "hviph", aia_hmode32, NULL, NULL, rmw_hviph }, - [CSR_HVIPRIO1H] = { "hviprio1h", aia_hmode32, read_hviprio1h, write_hviprio1h }, - [CSR_HVIPRIO2H] = { "hviprio2h", aia_hmode32, read_hviprio2h, write_hviprio2h }, + [CSR_HVIPRIO1H] = { "hviprio1h", aia_hmode32, read_hviprio1h, + write_hviprio1h }, + [CSR_HVIPRIO2H] = { "hviprio2h", aia_hmode32, read_hviprio2h, + write_hviprio2h }, [CSR_VSIEH] = { "vsieh", aia_hmode32, NULL, NULL, rmw_vsieh }, [CSR_VSIPH] = { "vsiph", aia_hmode32, NULL, NULL, rmw_vsiph }, /* Physical Memory Protection */ [CSR_MSECCFG] = { "mseccfg", epmp, read_mseccfg, write_mseccfg, - .min_priv_ver = PRIV_VERSION_1_11_0 }, + .min_priv_ver = PRIV_VERSION_1_11_0 }, [CSR_PMPCFG0] = { "pmpcfg0", pmp, read_pmpcfg, write_pmpcfg }, [CSR_PMPCFG1] = { "pmpcfg1", pmp, read_pmpcfg, write_pmpcfg }, [CSR_PMPCFG2] = { "pmpcfg2", pmp, read_pmpcfg, write_pmpcfg }, @@ -3697,17 +3717,23 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_TDATA3] = { "tdata3", debug, read_tdata, write_tdata }, /* User Pointer Masking */ - [CSR_UMTE] = { "umte", pointer_masking, read_umte, write_umte }, - [CSR_UPMMASK] = { "upmmask", pointer_masking, read_upmmask, write_upmmask }, - [CSR_UPMBASE] = { "upmbase", pointer_masking, read_upmbase, write_upmbase }, + [CSR_UMTE] = { "umte", pointer_masking, read_umte, write_umte }, + [CSR_UPMMASK] = { "upmmask", pointer_masking, read_upmmask, + write_upmmask }, + [CSR_UPMBASE] = { "upmbase", pointer_masking, read_upmbase, + write_upmbase }, /* Machine Pointer Masking */ - [CSR_MMTE] = { "mmte", pointer_masking, read_mmte, write_mmte }, - [CSR_MPMMASK] = { "mpmmask", pointer_masking, read_mpmmask, write_mpmmask }, - [CSR_MPMBASE] = { "mpmbase", pointer_masking, read_mpmbase, write_mpmbase }, + [CSR_MMTE] = { "mmte", pointer_masking, read_mmte, write_mmte }, + [CSR_MPMMASK] = { "mpmmask", pointer_masking, read_mpmmask, + write_mpmmask }, + [CSR_MPMBASE] = { "mpmbase", pointer_masking, read_mpmbase, + write_mpmbase }, /* Supervisor Pointer Masking */ - [CSR_SMTE] = { "smte", pointer_masking, read_smte, write_smte }, - [CSR_SPMMASK] = { "spmmask", pointer_masking, read_spmmask, write_spmmask }, - [CSR_SPMBASE] = { "spmbase", pointer_masking, read_spmbase, write_spmbase }, + [CSR_SMTE] = { "smte", pointer_masking, read_smte, write_smte }, + [CSR_SPMMASK] = { "spmmask", pointer_masking, read_spmmask, + write_spmmask }, + [CSR_SPMBASE] = { "spmbase", pointer_masking, read_spmbase, + write_spmbase }, /* Performance Counters */ [CSR_HPMCOUNTER3] = { "hpmcounter3", ctr, read_hpmcounter }, @@ -3741,125 +3767,126 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_HPMCOUNTER31] = { "hpmcounter31", ctr, read_hpmcounter }, [CSR_MHPMCOUNTER3] = { "mhpmcounter3", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER4] = { "mhpmcounter4", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER5] = { "mhpmcounter5", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER6] = { "mhpmcounter6", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER7] = { "mhpmcounter7", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER8] = { "mhpmcounter8", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER9] = { "mhpmcounter9", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER10] = { "mhpmcounter10", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER11] = { "mhpmcounter11", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER12] = { "mhpmcounter12", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER13] = { "mhpmcounter13", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER14] = { "mhpmcounter14", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER15] = { "mhpmcounter15", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER16] = { "mhpmcounter16", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER17] = { "mhpmcounter17", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER18] = { "mhpmcounter18", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER19] = { "mhpmcounter19", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER20] = { "mhpmcounter20", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER21] = { "mhpmcounter21", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER22] = { "mhpmcounter22", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER23] = { "mhpmcounter23", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER24] = { "mhpmcounter24", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER25] = { "mhpmcounter25", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER26] = { "mhpmcounter26", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER27] = { "mhpmcounter27", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER28] = { "mhpmcounter28", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER29] = { "mhpmcounter29", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER30] = { "mhpmcounter30", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MHPMCOUNTER31] = { "mhpmcounter31", mctr, read_hpmcounter, - write_mhpmcounter }, + write_mhpmcounter }, [CSR_MCOUNTINHIBIT] = { "mcountinhibit", any, read_mcountinhibit, - write_mcountinhibit, .min_priv_ver = PRIV_VERSION_1_11_0 }, + write_mcountinhibit, + .min_priv_ver = PRIV_VERSION_1_11_0 }, [CSR_MHPMEVENT3] = { "mhpmevent3", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT4] = { "mhpmevent4", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT5] = { "mhpmevent5", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT6] = { "mhpmevent6", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT7] = { "mhpmevent7", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT8] = { "mhpmevent8", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT9] = { "mhpmevent9", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT10] = { "mhpmevent10", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT11] = { "mhpmevent11", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT12] = { "mhpmevent12", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT13] = { "mhpmevent13", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT14] = { "mhpmevent14", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT15] = { "mhpmevent15", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT16] = { "mhpmevent16", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT17] = { "mhpmevent17", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT18] = { "mhpmevent18", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT19] = { "mhpmevent19", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT20] = { "mhpmevent20", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT21] = { "mhpmevent21", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT22] = { "mhpmevent22", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT23] = { "mhpmevent23", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT24] = { "mhpmevent24", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT25] = { "mhpmevent25", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT26] = { "mhpmevent26", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT27] = { "mhpmevent27", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT28] = { "mhpmevent28", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT29] = { "mhpmevent29", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT30] = { "mhpmevent30", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_MHPMEVENT31] = { "mhpmevent31", any, read_mhpmevent, - write_mhpmevent }, + write_mhpmevent }, [CSR_HPMCOUNTER3H] = { "hpmcounter3h", ctr32, read_hpmcounterh }, [CSR_HPMCOUNTER4H] = { "hpmcounter4h", ctr32, read_hpmcounterh }, @@ -3892,62 +3919,62 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_HPMCOUNTER31H] = { "hpmcounter31h", ctr32, read_hpmcounterh }, [CSR_MHPMCOUNTER3H] = { "mhpmcounter3h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER4H] = { "mhpmcounter4h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER5H] = { "mhpmcounter5h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER6H] = { "mhpmcounter6h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER7H] = { "mhpmcounter7h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER8H] = { "mhpmcounter8h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER9H] = { "mhpmcounter9h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER10H] = { "mhpmcounter10h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER11H] = { "mhpmcounter11h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER12H] = { "mhpmcounter12h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER13H] = { "mhpmcounter13h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER14H] = { "mhpmcounter14h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER15H] = { "mhpmcounter15h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER16H] = { "mhpmcounter16h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER17H] = { "mhpmcounter17h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER18H] = { "mhpmcounter18h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER19H] = { "mhpmcounter19h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER20H] = { "mhpmcounter20h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER21H] = { "mhpmcounter21h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER22H] = { "mhpmcounter22h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER23H] = { "mhpmcounter23h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER24H] = { "mhpmcounter24h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER25H] = { "mhpmcounter25h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER26H] = { "mhpmcounter26h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER27H] = { "mhpmcounter27h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER28H] = { "mhpmcounter28h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER29H] = { "mhpmcounter29h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER30H] = { "mhpmcounter30h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, [CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", mctr32, read_hpmcounterh, - write_mhpmcounterh }, + write_mhpmcounterh }, #endif /* !CONFIG_USER_ONLY */ }; From c126f83cd64883f7cb4be90a7fbf29e2be3bb9c7 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Mon, 18 Jul 2022 21:09:53 +0800 Subject: [PATCH 09/44] target/riscv: Add check for csrs existed with U extension Add umode/umode32 predicate for mcounteren, menvcfg/menvcfgh Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Reviewed-by: Andrew Jones Message-Id: <20220718130955.11899-5-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 7d4b6cecedd6..5c69dc838c9a 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -339,6 +339,24 @@ static RISCVException hmode32(CPURISCVState *env, int csrno) } +static RISCVException umode(CPURISCVState *env, int csrno) +{ + if (riscv_has_ext(env, RVU)) { + return RISCV_EXCP_NONE; + } + + return RISCV_EXCP_ILLEGAL_INST; +} + +static RISCVException umode32(CPURISCVState *env, int csrno) +{ + if (riscv_cpu_mxl(env) != MXL_RV32) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return umode(env, csrno); +} + /* Checks if PointerMasking registers could be accessed */ static RISCVException pointer_masking(CPURISCVState *env, int csrno) { @@ -3519,7 +3537,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MEDELEG] = { "medeleg", any, read_medeleg, write_medeleg }, [CSR_MIE] = { "mie", any, NULL, NULL, rmw_mie }, [CSR_MTVEC] = { "mtvec", any, read_mtvec, write_mtvec }, - [CSR_MCOUNTEREN] = { "mcounteren", any, read_mcounteren, + [CSR_MCOUNTEREN] = { "mcounteren", umode, read_mcounteren, write_mcounteren }, [CSR_MSTATUSH] = { "mstatush", any32, read_mstatush, @@ -3553,9 +3571,9 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MIPH] = { "miph", aia_any32, NULL, NULL, rmw_miph }, /* Execution environment configuration */ - [CSR_MENVCFG] = { "menvcfg", any, read_menvcfg, write_menvcfg, + [CSR_MENVCFG] = { "menvcfg", umode, read_menvcfg, write_menvcfg, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MENVCFGH] = { "menvcfgh", any32, read_menvcfgh, write_menvcfgh, + [CSR_MENVCFGH] = { "menvcfgh", umode32, read_menvcfgh, write_menvcfgh, .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_SENVCFG] = { "senvcfg", smode, read_senvcfg, write_senvcfg, .min_priv_ver = PRIV_VERSION_1_12_0 }, From 62a09b9b4118ca42e79d9bd179daf7230462705b Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Mon, 18 Jul 2022 21:09:54 +0800 Subject: [PATCH 10/44] target/riscv: Fix checks in hmode/hmode32 Add check for the implicit dependence between H and S Csrs only existed in RV32 will not trigger virtual instruction fault when not in RV32 based on section 8.6.1 of riscv-privileged spec (draft-20220717) Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-Id: <20220718130955.11899-6-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 5 +++++ target/riscv/csr.c | 9 ++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index fb37ffac6491..117d308ae563 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -733,6 +733,11 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) return; } + if (cpu->cfg.ext_h && !cpu->cfg.ext_s) { + error_setg(errp, "H extension implicitly requires S-mode"); + return; + } + if (cpu->cfg.ext_f && !cpu->cfg.ext_icsr) { error_setg(errp, "F extension requires Zicsr"); return; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 5c69dc838c9a..cf15aa67b7a3 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -311,8 +311,7 @@ static int aia_smode32(CPURISCVState *env, int csrno) static RISCVException hmode(CPURISCVState *env, int csrno) { - if (riscv_has_ext(env, RVS) && - riscv_has_ext(env, RVH)) { + if (riscv_has_ext(env, RVH)) { /* Hypervisor extension is supported */ if ((env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) || env->priv == PRV_M) { @@ -328,11 +327,7 @@ static RISCVException hmode(CPURISCVState *env, int csrno) static RISCVException hmode32(CPURISCVState *env, int csrno) { if (riscv_cpu_mxl(env) != MXL_RV32) { - if (!riscv_cpu_virt_enabled(env)) { - return RISCV_EXCP_ILLEGAL_INST; - } else { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } + return RISCV_EXCP_ILLEGAL_INST; } return hmode(env, csrno); From 5de124538b618c864508868eae505e77149649b7 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Mon, 18 Jul 2022 21:09:55 +0800 Subject: [PATCH 11/44] target/riscv: Simplify the check in hmode to reuse the check in riscv_csrrw_check Just add 1 to the effective privledge level when in HS mode, then reuse the check of 'effective_priv < csr_priv' in riscv_csrrw_check to replace the privilege level related check in hmode. Then, hmode will only check whether H extension is supported. When accessing Hypervior CSRs: 1) If accessing from M privilege level, the check of 'effective_priv< csr_priv' passes, returns hmode(...) which will return RISCV_EXCP_ILLEGAL_INST when H extension is not supported and return RISCV_EXCP_NONE otherwise. 2) If accessing from HS privilege level, effective_priv will add 1, the check passes and also returns hmode(...) too. 3) If accessing from VS/VU privilege level, the check fails, and returns RISCV_EXCP_VIRT_INSTRUCTION_FAULT 4) If accessing from U privilege level, the check fails, and returns RISCV_EXCP_ILLEGAL_INST Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Reviewed-by: Andrew Jones Message-Id: <20220718130955.11899-7-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index cf15aa67b7a3..0fb042b2fd0f 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -312,13 +312,7 @@ static int aia_smode32(CPURISCVState *env, int csrno) static RISCVException hmode(CPURISCVState *env, int csrno) { if (riscv_has_ext(env, RVH)) { - /* Hypervisor extension is supported */ - if ((env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) || - env->priv == PRV_M) { - return RISCV_EXCP_NONE; - } else { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } + return RISCV_EXCP_NONE; } return RISCV_EXCP_ILLEGAL_INST; @@ -3279,13 +3273,11 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env, #if !defined(CONFIG_USER_ONLY) int csr_priv, effective_priv = env->priv; - if (riscv_has_ext(env, RVH) && env->priv == PRV_S) { + if (riscv_has_ext(env, RVH) && env->priv == PRV_S && + !riscv_cpu_virt_enabled(env)) { /* - * We are in either HS or VS mode. - * Add 1 to the effective privledge level to allow us to access the - * Hypervisor CSRs. The `hmode` predicate will determine if access - * should be allowed(HS) or if a virtual instruction exception should be - * raised(VS). + * We are in HS mode. Add 1 to the effective privledge level to + * allow us to access the Hypervisor CSRs. */ effective_priv++; } From 780bb81bb3624b304d3226803b61e881777d183d Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Wed, 13 Jul 2022 17:00:31 +0800 Subject: [PATCH 12/44] roms/opensbi: Upgrade from v1.0 to v1.1 Upgrade OpenSBI from v1.0 to v1.1 and the pre-built bios images. The v1.1 release includes the following commits: 5b99603 lib: utils/ipi: Fix size check in aclint_mswi_cold_init() 6dde435 lib: utils/sys: Extend HTIF library to allow custom base address 8257262 platform: sifive_fu740: do not use a global in da9063_reset/shutdown fb688d9 platform: sifive_fu740: fix reset when watchdog is running 5d025eb lib: fix pointer of type 'void *' used in arithmetic 632f593 lib: sbi: Map only the counters enabled in hardware 3b7c204 lib: sbi: Disable interrupt during config matching a26dc60 lib: sbi: Disable interrupt and inhibit counting in M-mode during init 5d53b55 Makefile: fix build with binutils 2.38 6ad8917 lib: fix compilation when strings.h is included ce4c018 lib: utils/serial: Round UART8250 baud rate divisor to nearest integer 01250d0 include: sbi: Add AIA related CSR defines 8f96070 lib: sbi: Detect AIA CSRs at boot-time 65b4c7c lib: sbi: Use AIA CSRs for local interrupts when available 222132f lib: sbi: Add sbi_trap_set_external_irqfn() API 5f56314 lib: utils/irqchip: Allow multiple FDT irqchip drivers 1050940 include: sbi: Introduce nascent_init() platform callback 55e79f8 lib: sbi: Enable mie.MEIE bit for IPIs based on external interrupts. 9f73669 lib: utils/irqchip: Add IMSIC library 811da5c lib: utils/irqchip: Add FDT based driver for IMSIC 7127aaa lib: utils: Disable appropriate IMSIC DT nodes in fdt_fixups() 9979265 lib: utils/irqchip: Add APLIC initialization library 3461219 lib: utils/irqchip: Add FDT based driver for APLIC 8e2ef4f lib: utils: Disable appropriate APLIC DT nodes in fdt_fixups() 3a69cc1 lib: sbi: fix typo in is_region_subset f2ccf2f lib: sbi: verbose sbi_domain_root_add_memregion f3f4604 lib: sbi: Add a simple external interrupt handling framework 4998a71 lib: utils: serial: Initial commit of xlnx-uartlite 2dfbd3c lib: pmp_set/pmp_get moved errors from runtime to compile time b6b7220 firmware: Fix code for accessing hart_count and stack_size d552fc8 lib: Add error messages via conditional compilation for the future 555bdb1 include: Use static asserts for SBI_PLATFORM_xxx_OFFSET defines 1b42d3a include: Use static asserts for SBI_SCRATCH_xxx_OFFSET defines 7924a0b include: Use static asserts for FW_DYNAMIC_INFO_xxx_OFFSET defines 722f80d include: Add defines for [m|h|s]envcfg CSRs 31fecad lib: sbi: Detect menvcfg CSR at boot time 47d6765 lib: sbi: Enable Zicbo[m|z] extensions in the menvcfg CSR 794986f lib: sbi: Enable Svpbmt extension in the menvcfg CSR 499601a lib: sbi: Add Smstateen extension defines d44568a lib: sbi: Detect Smstateen CSRs at boot-time 3383d6a lib: irqchip/imsic: configure mstateen 5c5cbb5 lib: utils/serial: support 'reg-offset' property c1e47d0 include: correct the definition of MSTATUS_VS 9cd95e1 lib: sbi/hart: preserve csr validation value 4035ae9 docs: pmu: Improve the PMU DT bindings d62f6da lib: sbi: Implement Sstc extension 474a9d4 lib: sbi: Fix mstatus_init() for RV32 when Sscofpmf is not available e576b3e include: sbi: Define SBI_PMU_HW_EVENT_MAX to 256 b0c9df5 lib: sbi: Fix mhpmeventh access for rv32 in absence of sscofpmf 1a754bb lib: sbi: Detect and print privileged spec version 5a6be99 lib: sbi: Remove 's' and 'u' from misa_string() output 5b8b377 lib: sbi: Update the name of ISA string printed at boot time d4b563c lib: sbi: Remove MCOUNTEREN and SCOUNTEREN hart features dbc3d8f lib: sbi: Remove MCOUNTINHIBT hart feature 97a17c2 lib: sbi: Remove MENVCFG hart feature a6ab94f lib: sbi: Fix AIA feature detection cad6c91 lib: sbi: Convert hart features into hart extensions be4903a lib: sbi: Detect hart features only once for each hart 994ace3 lib: sbi: Add sbi_hart_update_extension() function 023f0ad lib: sbi_platform: Add callback to populate HART extensions f726f2d Makefile: Allow generated C source to be anywhere in build directory 7fb474b Makefile: Add support for generating C array at compile time 73cf511 lib: utils/reset: Generate FDT reset driver list at compile-time 1e62705 lib: utils/serial: Generate FDT serial driver list at compile-time bfeb305 lib: utils/timer: Generate FDT timer driver list at compile-time 3a69d12 lib: utils/irqchip: Generate FDT irqchip driver list at compile-time 4ee0c57 lib: utils/ipi: Generate FDT ipi driver list at compile-time 998ed43 lib: utils/i2c: Generate FDT i2c adapter driver list at compile-time 4eacd82 lib: utils/gpio: Generate FDT gpio driver list at compile-time a3a3c60 platform: generic: Generate platform override module list at compile-time 9a7a677 platform: generic: Move Sifive platform overrides into own directory 851c14d lib: utils/irqchip: fix typo when checking for CPU node 90a9dd2 lib: utils/fdt: introduce fdt_node_is_enabled() 616da52 lib: utils: check if CPU node is enabled 575bb4e platform: generic: check if CPU node is enabled 1bc67db lib: utils/fdt: rename fdt_parse_max_hart_id f067bb8 lib: sbi: fix system_opcode_insn fab0379 lib: utils/fdt: Require match data to be const 295e5f3 lib: sbi_timer: Drop unnecessary get_platform_ticks wrapper ff65bfe lib: sbi_illegal_insn: Constify illegal_insn_table cb8271c lib: sbi_illegal_insn: Add emulation for fence.tso adc3388 lib: sbi_trap: Redirect exception based on hedeleg ce1d618 platform: generic: add overrides for vendor extensions b20ed9f lib: sbi_hsm: Call a device hook during hart resume 79e42eb lib: sbi_hsm: Assume a consistent resume address 2ea7799 lib: irqchip/plic: Constify plic_data pointers 8c362e7 lib: irqchip/plic: Factor out a context init function 415ecf2 lib: irqchip/plic: Add context save/restore helpers 2b79b69 lib: irqchip/plic: Add priority save/restore helpers 69be3df lib: utils/irqchip: Add FDT wrappers for PLIC save/restore functions 5e56758 lib: utils/irqchip: Add wrapper for T-HEAD PLIC delegation 9dc5ec5 platform: Add HSM implementation for Allwinner D1 551c70c include: sbi: Add mtinst/htinst psuedoinstructions 187127f lib: sbi: Fixup tinst for exceptions in sbi_misaligned_*() a07402a lib: sbi: Fix tval and tinst for sbi_get_insn() c653001 lib: utils: Remove CSRs that set/clear an IMSIC interrupt file bits 7738345 lib: utils/timer: Add a separate compatible for the D1 CLINT d76a196 lib: irqchip/plic: fix typo in plic_warm_irqchip_init 6f1fe98 lib: utils/timer: Remove Allwinner D1 CLINT compatibles c6fdbcf include: sbi: Change spec version to 1.0 3f66465 lib: pmu: allow to use the highest available counter 4489876 include: Bump-up version to 1.1 Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Message-Id: <20220713090613.204046-1-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- .../opensbi-riscv32-generic-fw_dynamic.bin | Bin 108504 -> 117704 bytes .../opensbi-riscv64-generic-fw_dynamic.bin | Bin 105296 -> 115344 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 dba8e8655fbf39c128b19675cdd205805f85e7e1..81bab1adc97bbe9b376f3bfe4da98cdfcf3ade7a 100644 GIT binary patch delta 61109 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%0A=YYPa$97&KyvjYlURJ^rl+N^hZATK1>-F9 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 b>Xn9T_r+LQLdFc&{`Aitd&)Er`1gMRit`iQ diff --git a/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin index f223e56991f56f96521453eaaf89a1f4a9bf2c31..5eb0a743260b47e07e674db3728d6ba4125847c0 100644 GIT binary patch delta 55872 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 Date: Sun, 17 Jul 2022 13:15:43 +0300 Subject: [PATCH 13/44] target/riscv: Fix typo and restore Pointer Masking functionality for RISC-V Fixes: 4302bef9e178 ("target/riscv: Calculate address according to XLEN") Signed-off-by: Alexey Baturo Reviewed-by: Alistair Francis Message-Id: <20220717101543.478533-2-space.monkey.delivers@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 6eeb72846243..76da8db8a773 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -544,7 +544,7 @@ static TCGv get_address(DisasContext *ctx, int rs1, int imm) tcg_gen_addi_tl(addr, src1, imm); if (ctx->pm_mask_enabled) { - tcg_gen_and_tl(addr, addr, pm_mask); + tcg_gen_andc_tl(addr, addr, pm_mask); } else if (get_xl(ctx) == MXL_RV32) { tcg_gen_ext32u_tl(addr, addr); } From 079520033facc70beee5eedee8d7a27a2a9261b4 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 19 Jul 2022 16:26:35 +0800 Subject: [PATCH 14/44] docs: List kvm as a supported accelerator on RISC-V Since commit fbf43c7dbf18 ("target/riscv: enable riscv kvm accel"), KVM accelerator is supported on RISC-V. Let's document it. Signed-off-by: Bin Meng Reviewed-by: Thomas Huth Reviewed-by: Alistair Francis Message-Id: <20220719082635.3741878-1-bin.meng@windriver.com> Signed-off-by: Alistair Francis --- docs/about/build-platforms.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst index 26028756d030..a2fee53248c1 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -46,7 +46,7 @@ Those hosts are officially supported, with various accelerators: * - PPC - kvm, tcg * - RISC-V - - tcg + - kvm, tcg * - s390x - kvm, tcg * - SPARC From 355d5584de1129eec1c4043fdee1335010cfabb6 Mon Sep 17 00:00:00 2001 From: "Yueh-Ting (eop) Chen" Date: Mon, 20 Jun 2022 06:51:02 +0000 Subject: [PATCH 15/44] target/riscv: rvv: Add mask agnostic for vv instructions According to v-spec, mask agnostic behavior can be either kept as undisturbed or set elements' bits to all 1s. To distinguish the difference of mask policies, QEMU should be able to simulate the mask agnostic behavior as "set mask elements' bits to all 1s". There are multiple possibility for agnostic elements according to v-spec. The main intent of this patch-set tries to add option that can distinguish between mask policies. Setting agnostic elements to all 1s allows QEMU to express this. This is the first commit regarding the optional mask agnostic behavior. Follow-up commits will add this optional behavior for all rvv instructions. Signed-off-by: eop Chen Reviewed-by: Frank Chang Reviewed-by: Weiwei Li Reviewed-by: Alistair Francis Message-Id: <165570784143.17634.35095816584573691-1@git.sr.ht> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 2 ++ target/riscv/cpu_helper.c | 2 ++ target/riscv/insn_trans/trans_rvv.c.inc | 3 +++ target/riscv/internals.h | 5 +++-- target/riscv/translate.c | 2 ++ target/riscv/vector_helper.c | 8 ++++++++ 6 files changed, 20 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index ffb1a1887352..561d7fa92c8c 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -439,6 +439,7 @@ struct RISCVCPUConfig { bool ext_zve64f; bool ext_zmmul; bool rvv_ta_all_1s; + bool rvv_ma_all_1s; uint32_t mvendorid; uint64_t marchid; @@ -596,6 +597,7 @@ FIELD(TB_FLAGS, XL, 20, 2) FIELD(TB_FLAGS, PM_MASK_ENABLED, 22, 1) FIELD(TB_FLAGS, PM_BASE_ENABLED, 23, 1) FIELD(TB_FLAGS, VTA, 24, 1) +FIELD(TB_FLAGS, VMA, 25, 1) #ifdef TARGET_RISCV32 #define riscv_cpu_mxl(env) ((void)(env), MXL_RV32) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 87daf7220f55..650574accf0a 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -68,6 +68,8 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, flags = FIELD_DP32(flags, TB_FLAGS, VL_EQ_VLMAX, vl_eq_vlmax); flags = FIELD_DP32(flags, TB_FLAGS, VTA, FIELD_EX64(env->vtype, VTYPE, VTA)); + flags = FIELD_DP32(flags, TB_FLAGS, VMA, + FIELD_EX64(env->vtype, VTYPE, VMA)); } else { flags = FIELD_DP32(flags, TB_FLAGS, VILL, 1); } diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 6c091824b6bc..5ec113f6fd3b 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -1247,6 +1247,7 @@ do_opivv_gvec(DisasContext *s, arg_rmrr *a, GVecGen3Fn *gvec_fn, data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), vreg_ofs(s, a->rs1), vreg_ofs(s, a->rs2), cpu_env, s->cfg_ptr->vlen / 8, @@ -1545,6 +1546,7 @@ static bool do_opivv_widen(DisasContext *s, arg_rmrr *a, data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), vreg_ofs(s, a->rs1), vreg_ofs(s, a->rs2), @@ -1627,6 +1629,7 @@ static bool do_opiwv_widen(DisasContext *s, arg_rmrr *a, data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), vreg_ofs(s, a->rs1), vreg_ofs(s, a->rs2), diff --git a/target/riscv/internals.h b/target/riscv/internals.h index 193ce57a6dc0..5620fbffb6e2 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -26,8 +26,9 @@ FIELD(VDATA, VM, 0, 1) FIELD(VDATA, LMUL, 1, 3) FIELD(VDATA, VTA, 4, 1) FIELD(VDATA, VTA_ALL_1S, 5, 1) -FIELD(VDATA, NF, 6, 4) -FIELD(VDATA, WD, 6, 1) +FIELD(VDATA, VMA, 6, 1) +FIELD(VDATA, NF, 7, 4) +FIELD(VDATA, WD, 7, 1) /* float point classify helpers */ target_ulong fclass_h(uint64_t frs1); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 76da8db8a773..8925a44c6e77 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -95,6 +95,7 @@ typedef struct DisasContext { int8_t lmul; uint8_t sew; uint8_t vta; + uint8_t vma; bool cfg_vta_all_1s; target_ulong vstart; bool vl_eq_vlmax; @@ -1121,6 +1122,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->sew = FIELD_EX32(tb_flags, TB_FLAGS, SEW); ctx->lmul = sextract32(FIELD_EX32(tb_flags, TB_FLAGS, LMUL), 0, 3); ctx->vta = FIELD_EX32(tb_flags, TB_FLAGS, VTA) && cpu->cfg.rvv_ta_all_1s; + ctx->vma = FIELD_EX32(tb_flags, TB_FLAGS, VMA) && cpu->cfg.rvv_ma_all_1s; ctx->cfg_vta_all_1s = cpu->cfg.rvv_ta_all_1s; ctx->vstart = env->vstart; ctx->vl_eq_vlmax = FIELD_EX32(tb_flags, TB_FLAGS, VL_EQ_VLMAX); diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index a96fc49c71e3..de895050e078 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -127,6 +127,11 @@ static inline uint32_t vext_vta(uint32_t desc) return FIELD_EX32(simd_data(desc), VDATA, VTA); } +static inline uint32_t vext_vma(uint32_t desc) +{ + return FIELD_EX32(simd_data(desc), VDATA, VMA); +} + static inline uint32_t vext_vta_all_1s(uint32_t desc) { return FIELD_EX32(simd_data(desc), VDATA, VTA_ALL_1S); @@ -812,10 +817,13 @@ static void do_vext_vv(void *vd, void *v0, void *vs1, void *vs2, uint32_t vl = env->vl; uint32_t total_elems = vext_get_total_elems(env, desc, esz); uint32_t vta = vext_vta(desc); + uint32_t vma = vext_vma(desc); uint32_t i; for (i = env->vstart; i < vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { + /* set masked-off elements to 1s */ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); continue; } fn(vd, vs1, vs2, i); From 265ecd4c62a008f3be351a75e1847cee9c71e380 Mon Sep 17 00:00:00 2001 From: "Yueh-Ting (eop) Chen" Date: Mon, 20 Jun 2022 06:51:02 +0000 Subject: [PATCH 16/44] target/riscv: rvv: Add mask agnostic for vector load / store instructions Signed-off-by: eop Chen Reviewed-by: Frank Chang Reviewed-by: Weiwei Li Acked-by: Alistair Francis Message-Id: <165570784143.17634.35095816584573691-2@git.sr.ht> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 5 ++++ target/riscv/vector_helper.c | 35 +++++++++++++++++-------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 5ec113f6fd3b..0627eda0c0c6 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -712,6 +712,7 @@ static bool ld_us_op(DisasContext *s, arg_r2nfvm *a, uint8_t eew) data = FIELD_DP32(data, VDATA, LMUL, emul); data = FIELD_DP32(data, VDATA, NF, a->nf); data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); return ldst_us_trans(a->rd, a->rs1, data, fn, s, false); } @@ -777,6 +778,7 @@ static bool ld_us_mask_op(DisasContext *s, arg_vlm_v *a, uint8_t eew) data = FIELD_DP32(data, VDATA, NF, 1); /* Mask destination register are always tail-agnostic */ data = FIELD_DP32(data, VDATA, VTA, s->cfg_vta_all_1s); + data = FIELD_DP32(data, VDATA, VMA, s->vma); return ldst_us_trans(a->rd, a->rs1, data, fn, s, false); } @@ -866,6 +868,7 @@ static bool ld_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) data = FIELD_DP32(data, VDATA, LMUL, emul); data = FIELD_DP32(data, VDATA, NF, a->nf); data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s, false); } @@ -996,6 +999,7 @@ static bool ld_index_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) data = FIELD_DP32(data, VDATA, LMUL, emul); data = FIELD_DP32(data, VDATA, NF, a->nf); data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); return ldst_index_trans(a->rd, a->rs1, a->rs2, data, fn, s, false); } @@ -1114,6 +1118,7 @@ static bool ldff_op(DisasContext *s, arg_r2nfvm *a, uint8_t eew) data = FIELD_DP32(data, VDATA, LMUL, emul); data = FIELD_DP32(data, VDATA, NF, a->nf); data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); return ldff_trans(a->rd, a->rs1, data, fn, s); } diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index de895050e078..e3810d2bc314 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -283,14 +283,18 @@ vext_ldst_stride(void *vd, void *v0, target_ulong base, uint32_t esz = 1 << log2_esz; uint32_t total_elems = vext_get_total_elems(env, desc, esz); uint32_t vta = vext_vta(desc); + uint32_t vma = vext_vma(desc); for (i = env->vstart; i < env->vl; i++, env->vstart++) { - if (!vm && !vext_elem_mask(v0, i)) { - continue; - } - k = 0; while (k < nf) { + if (!vm && !vext_elem_mask(v0, i)) { + /* set masked-off elements to 1s */ + vext_set_elems_1s(vd, vma, (i + k * max_elems) * esz, + (i + k * max_elems + 1) * esz); + k++; + continue; + } target_ulong addr = base + stride * i + (k << log2_esz); ldst_elem(env, adjust_addr(env, addr), i + k * max_elems, vd, ra); k++; @@ -482,15 +486,19 @@ vext_ldst_index(void *vd, void *v0, target_ulong base, uint32_t esz = 1 << log2_esz; uint32_t total_elems = vext_get_total_elems(env, desc, esz); uint32_t vta = vext_vta(desc); + uint32_t vma = vext_vma(desc); /* load bytes from guest memory */ for (i = env->vstart; i < env->vl; i++, env->vstart++) { - if (!vm && !vext_elem_mask(v0, i)) { - continue; - } - k = 0; while (k < nf) { + if (!vm && !vext_elem_mask(v0, i)) { + /* set masked-off elements to 1s */ + vext_set_elems_1s(vd, vma, (i + k * max_elems) * esz, + (i + k * max_elems + 1) * esz); + k++; + continue; + } abi_ptr addr = get_index_addr(base, i, vs2) + (k << log2_esz); ldst_elem(env, adjust_addr(env, addr), i + k * max_elems, vd, ra); k++; @@ -579,6 +587,7 @@ vext_ldff(void *vd, void *v0, target_ulong base, uint32_t esz = 1 << log2_esz; uint32_t total_elems = vext_get_total_elems(env, desc, esz); uint32_t vta = vext_vta(desc); + uint32_t vma = vext_vma(desc); target_ulong addr, offset, remain; /* probe every access*/ @@ -624,10 +633,14 @@ vext_ldff(void *vd, void *v0, target_ulong base, } for (i = env->vstart; i < env->vl; i++) { k = 0; - if (!vm && !vext_elem_mask(v0, i)) { - continue; - } while (k < nf) { + if (!vm && !vext_elem_mask(v0, i)) { + /* set masked-off elements to 1s */ + vext_set_elems_1s(vd, vma, (i + k * max_elems) * esz, + (i + k * max_elems + 1) * esz); + k++; + continue; + } target_ulong addr = base + ((i * nf + k) << log2_esz); ldst_elem(env, adjust_addr(env, addr), i + k * max_elems, vd, ra); k++; From bce9a636beabbb2c538cdfca49592b04bfbf7e2c Mon Sep 17 00:00:00 2001 From: "Yueh-Ting (eop) Chen" Date: Mon, 20 Jun 2022 06:50:58 +0000 Subject: [PATCH 17/44] target/riscv: rvv: Add mask agnostic for vx instructions Signed-off-by: eop Chen Reviewed-by: Frank Chang Reviewed-by: Weiwei Li Acked-by: Alistair Francis Message-Id: <165570784143.17634.35095816584573691-3@git.sr.ht> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 2 ++ target/riscv/vector_helper.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 0627eda0c0c6..07d86551a990 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -1301,6 +1301,7 @@ static bool opivx_trans(uint32_t vd, uint32_t rs1, uint32_t vs2, uint32_t vm, data = FIELD_DP32(data, VDATA, LMUL, s->lmul); data = FIELD_DP32(data, VDATA, VTA, s->vta); data = FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s); + data = FIELD_DP32(data, VDATA, VMA, s->vma); desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, s->cfg_ptr->vlen / 8, data)); @@ -1468,6 +1469,7 @@ static bool opivi_trans(uint32_t vd, uint32_t imm, uint32_t vs2, uint32_t vm, data = FIELD_DP32(data, VDATA, LMUL, s->lmul); data = FIELD_DP32(data, VDATA, VTA, s->vta); data = FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s); + data = FIELD_DP32(data, VDATA, VMA, s->vma); desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, s->cfg_ptr->vlen / 8, data)); diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index e3810d2bc314..6be3c4e73988 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -899,10 +899,13 @@ static void do_vext_vx(void *vd, void *v0, target_long s1, void *vs2, uint32_t vl = env->vl; uint32_t total_elems = vext_get_total_elems(env, desc, esz); uint32_t vta = vext_vta(desc); + uint32_t vma = vext_vma(desc); uint32_t i; for (i = env->vstart; i < vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { + /* set masked-off elements to 1s */ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); continue; } fn(vd, s1, vs2, i); From fd93045ebfa6ab07ce7017fb4095736c3f6f315a Mon Sep 17 00:00:00 2001 From: "Yueh-Ting (eop) Chen" Date: Mon, 20 Jun 2022 06:50:58 +0000 Subject: [PATCH 18/44] target/riscv: rvv: Add mask agnostic for vector integer shift instructions Signed-off-by: eop Chen Reviewed-by: Frank Chang Reviewed-by: Weiwei Li Acked-by: Alistair Francis Message-Id: <165570784143.17634.35095816584573691-4@git.sr.ht> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 1 + target/riscv/vector_helper.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 07d86551a990..83b85bb85194 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -1901,6 +1901,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs1), \ vreg_ofs(s, a->rs2), cpu_env, \ diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 6be3c4e73988..d1daa764b75b 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -1298,10 +1298,13 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ uint32_t esz = sizeof(TS1); \ uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ TS1 s1 = *((TS1 *)vs1 + HS1(i)); \ @@ -1339,10 +1342,14 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ uint32_t total_elems = \ vext_get_total_elems(env, desc, esz); \ uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, \ + (i + 1) * esz); \ continue; \ } \ TS2 s2 = *((TS2 *)vs2 + HS2(i)); \ From 6e11d7eaa02c5834e5172d25e8c794ac5731b56e Mon Sep 17 00:00:00 2001 From: "Yueh-Ting (eop) Chen" Date: Mon, 20 Jun 2022 06:51:11 +0000 Subject: [PATCH 19/44] target/riscv: rvv: Add mask agnostic for vector integer comparison instructions Signed-off-by: eop Chen Reviewed-by: Frank Chang Reviewed-by: Weiwei Li Acked-by: Alistair Francis Message-Id: <165570784143.17634.35095816584573691-5@git.sr.ht> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 1 + target/riscv/vector_helper.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 83b85bb85194..e6aa5295a160 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -1718,6 +1718,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ data = FIELD_DP32(data, VDATA, VTA, s->vta); \ data = \ FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s);\ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs1), \ vreg_ofs(s, a->rs2), cpu_env, \ diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index d1daa764b75b..07ce671879bc 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -1404,12 +1404,17 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ uint32_t vl = env->vl; \ uint32_t total_elems = env_archcpu(env)->cfg.vlen; \ uint32_t vta_all_1s = vext_vta_all_1s(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ for (i = env->vstart; i < vl; i++) { \ ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + if (vma) { \ + vext_set_elem_mask(vd, i, 1); \ + } \ continue; \ } \ vext_set_elem_mask(vd, i, DO_OP(s2, s1)); \ @@ -1462,11 +1467,16 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ uint32_t vl = env->vl; \ uint32_t total_elems = env_archcpu(env)->cfg.vlen; \ uint32_t vta_all_1s = vext_vta_all_1s(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + if (vma) { \ + vext_set_elem_mask(vd, i, 1); \ + } \ continue; \ } \ vext_set_elem_mask(vd, i, \ From 72e17a9f86098feb25d2c1f2af043dc5f9f211e3 Mon Sep 17 00:00:00 2001 From: "Yueh-Ting (eop) Chen" Date: Mon, 20 Jun 2022 06:51:11 +0000 Subject: [PATCH 20/44] target/riscv: rvv: Add mask agnostic for vector fix-point arithmetic instructions Signed-off-by: eop Chen Reviewed-by: Frank Chang Reviewed-by: Weiwei Li Acked-by: Alistair Francis Message-Id: <165570784143.17634.35095816584573691-6@git.sr.ht> Signed-off-by: Alistair Francis --- target/riscv/vector_helper.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 07ce671879bc..597fa9c75271 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -2129,10 +2129,12 @@ static inline void vext_vv_rm_1(void *vd, void *v0, void *vs1, void *vs2, CPURISCVState *env, uint32_t vl, uint32_t vm, int vxrm, - opivv2_rm_fn *fn) + opivv2_rm_fn *fn, uint32_t vma, uint32_t esz) { for (uint32_t i = env->vstart; i < vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { + /* set masked-off elements to 1s */ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); continue; } fn(vd, vs1, vs2, i, env, vxrm); @@ -2150,23 +2152,24 @@ vext_vv_rm_2(void *vd, void *v0, void *vs1, void *vs2, uint32_t vl = env->vl; uint32_t total_elems = vext_get_total_elems(env, desc, esz); uint32_t vta = vext_vta(desc); + uint32_t vma = vext_vma(desc); switch (env->vxrm) { case 0: /* rnu */ vext_vv_rm_1(vd, v0, vs1, vs2, - env, vl, vm, 0, fn); + env, vl, vm, 0, fn, vma, esz); break; case 1: /* rne */ vext_vv_rm_1(vd, v0, vs1, vs2, - env, vl, vm, 1, fn); + env, vl, vm, 1, fn, vma, esz); break; case 2: /* rdn */ vext_vv_rm_1(vd, v0, vs1, vs2, - env, vl, vm, 2, fn); + env, vl, vm, 2, fn, vma, esz); break; default: /* rod */ vext_vv_rm_1(vd, v0, vs1, vs2, - env, vl, vm, 3, fn); + env, vl, vm, 3, fn, vma, esz); break; } /* set tail elements to 1s */ @@ -2250,10 +2253,12 @@ static inline void vext_vx_rm_1(void *vd, void *v0, target_long s1, void *vs2, CPURISCVState *env, uint32_t vl, uint32_t vm, int vxrm, - opivx2_rm_fn *fn) + opivx2_rm_fn *fn, uint32_t vma, uint32_t esz) { for (uint32_t i = env->vstart; i < vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { + /* set masked-off elements to 1s */ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); continue; } fn(vd, s1, vs2, i, env, vxrm); @@ -2271,23 +2276,24 @@ vext_vx_rm_2(void *vd, void *v0, target_long s1, void *vs2, uint32_t vl = env->vl; uint32_t total_elems = vext_get_total_elems(env, desc, esz); uint32_t vta = vext_vta(desc); + uint32_t vma = vext_vma(desc); switch (env->vxrm) { case 0: /* rnu */ vext_vx_rm_1(vd, v0, s1, vs2, - env, vl, vm, 0, fn); + env, vl, vm, 0, fn, vma, esz); break; case 1: /* rne */ vext_vx_rm_1(vd, v0, s1, vs2, - env, vl, vm, 1, fn); + env, vl, vm, 1, fn, vma, esz); break; case 2: /* rdn */ vext_vx_rm_1(vd, v0, s1, vs2, - env, vl, vm, 2, fn); + env, vl, vm, 2, fn, vma, esz); break; default: /* rod */ vext_vx_rm_1(vd, v0, s1, vs2, - env, vl, vm, 3, fn); + env, vl, vm, 3, fn, vma, esz); break; } /* set tail elements to 1s */ From 5b448f44c92f7355ae2b02af0699d22674cc5f6e Mon Sep 17 00:00:00 2001 From: "Yueh-Ting (eop) Chen" Date: Mon, 20 Jun 2022 06:51:12 +0000 Subject: [PATCH 21/44] target/riscv: rvv: Add mask agnostic for vector floating-point instructions Signed-off-by: eop Chen Reviewed-by: Frank Chang Reviewed-by: Weiwei Li Acked-by: Alistair Francis Message-Id: <165570784143.17634.35095816584573691-7@git.sr.ht> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 12 ++++++++++++ target/riscv/vector_helper.c | 26 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index e6aa5295a160..8ce3d2860396 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -2361,6 +2361,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ data = FIELD_DP32(data, VDATA, VTA, s->vta); \ data = \ FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s);\ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs1), \ vreg_ofs(s, a->rs2), cpu_env, \ @@ -2446,6 +2447,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ data = FIELD_DP32(data, VDATA, VTA, s->vta); \ data = FIELD_DP32(data, VDATA, VTA_ALL_1S, \ s->cfg_vta_all_1s); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ return opfvf_trans(a->rd, a->rs1, a->rs2, data, \ fns[s->sew - 1], s); \ } \ @@ -2485,6 +2487,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs1), \ vreg_ofs(s, a->rs2), cpu_env, \ @@ -2525,6 +2528,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ return opfvf_trans(a->rd, a->rs1, a->rs2, data, \ fns[s->sew - 1], s); \ } \ @@ -2562,6 +2566,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs1), \ vreg_ofs(s, a->rs2), cpu_env, \ @@ -2602,6 +2607,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ return opfvf_trans(a->rd, a->rs1, a->rs2, data, \ fns[s->sew - 1], s); \ } \ @@ -2686,6 +2692,7 @@ static bool do_opfv(DisasContext *s, arg_rmr *a, data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), vreg_ofs(s, a->rs2), cpu_env, s->cfg_ptr->vlen / 8, @@ -2790,6 +2797,7 @@ static bool trans_vfmv_v_f(DisasContext *s, arg_vfmv_v_f *a) TCGv_i32 desc; uint32_t data = FIELD_DP32(0, VDATA, LMUL, s->lmul); data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); static gen_helper_vmv_vx * const fns[3] = { gen_helper_vmv_v_x_h, gen_helper_vmv_v_x_w, @@ -2891,6 +2899,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs2), cpu_env, \ s->cfg_ptr->vlen / 8, \ @@ -2944,6 +2953,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs2), cpu_env, \ s->cfg_ptr->vlen / 8, \ @@ -3012,6 +3022,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs2), cpu_env, \ s->cfg_ptr->vlen / 8, \ @@ -3067,6 +3078,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs2), cpu_env, \ s->cfg_ptr->vlen / 8, \ diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 597fa9c75271..315742c6b8b9 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -3051,10 +3051,14 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ uint32_t total_elems = \ vext_get_total_elems(env, desc, ESZ); \ uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * ESZ, \ + (i + 1) * ESZ); \ continue; \ } \ do_##NAME(vd, vs1, vs2, i, env); \ @@ -3090,10 +3094,14 @@ void HELPER(NAME)(void *vd, void *v0, uint64_t s1, \ uint32_t total_elems = \ vext_get_total_elems(env, desc, ESZ); \ uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * ESZ, \ + (i + 1) * ESZ); \ continue; \ } \ do_##NAME(vd, s1, vs2, i, env); \ @@ -3665,6 +3673,7 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, \ uint32_t total_elems = \ vext_get_total_elems(env, desc, ESZ); \ uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ if (vl == 0) { \ @@ -3672,6 +3681,9 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, \ } \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * ESZ, \ + (i + 1) * ESZ); \ continue; \ } \ do_##NAME(vd, vs2, i, env); \ @@ -4182,12 +4194,17 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ uint32_t vl = env->vl; \ uint32_t total_elems = env_archcpu(env)->cfg.vlen; \ uint32_t vta_all_1s = vext_vta_all_1s(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ for (i = env->vstart; i < vl; i++) { \ ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + if (vma) { \ + vext_set_elem_mask(vd, i, 1); \ + } \ continue; \ } \ vext_set_elem_mask(vd, i, \ @@ -4215,11 +4232,16 @@ void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ uint32_t vl = env->vl; \ uint32_t total_elems = env_archcpu(env)->cfg.vlen; \ uint32_t vta_all_1s = vext_vta_all_1s(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + if (vma) { \ + vext_set_elem_mask(vd, i, 1); \ + } \ continue; \ } \ vext_set_elem_mask(vd, i, \ @@ -4342,10 +4364,14 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, \ uint32_t total_elems = \ vext_get_total_elems(env, desc, ESZ); \ uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * ESZ, \ + (i + 1) * ESZ); \ continue; \ } \ do_##NAME(vd, vs2, i); \ From 35f2d795f313c43af5851ca2243317c0b0834c6c Mon Sep 17 00:00:00 2001 From: "Yueh-Ting (eop) Chen" Date: Mon, 20 Jun 2022 06:51:11 +0000 Subject: [PATCH 22/44] target/riscv: rvv: Add mask agnostic for vector mask instructions Signed-off-by: eop Chen Reviewed-by: Frank Chang Reviewed-by: Weiwei Li Acked-by: Alistair Francis Message-Id: <165570784143.17634.35095816584573691-8@git.sr.ht> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 3 +++ target/riscv/vector_helper.c | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 8ce3d2860396..c1bd29329efc 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -3275,6 +3275,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ data = \ FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s);\ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), \ vreg_ofs(s, 0), vreg_ofs(s, a->rs2), \ cpu_env, s->cfg_ptr->vlen / 8, \ @@ -3313,6 +3314,7 @@ static bool trans_viota_m(DisasContext *s, arg_viota_m *a) data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); static gen_helper_gvec_3_ptr * const fns[4] = { gen_helper_viota_m_b, gen_helper_viota_m_h, gen_helper_viota_m_w, gen_helper_viota_m_d, @@ -3343,6 +3345,7 @@ static bool trans_vid_v(DisasContext *s, arg_vid_v *a) data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); static gen_helper_gvec_2_ptr * const fns[4] = { gen_helper_vid_v_b, gen_helper_vid_v_h, gen_helper_vid_v_w, gen_helper_vid_v_d, diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 315742c6b8b9..52518648bbfc 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -4879,11 +4879,16 @@ static void vmsetm(void *vd, void *v0, void *vs2, CPURISCVState *env, uint32_t vl = env->vl; uint32_t total_elems = env_archcpu(env)->cfg.vlen; uint32_t vta_all_1s = vext_vta_all_1s(desc); + uint32_t vma = vext_vma(desc); int i; bool first_mask_bit = false; for (i = env->vstart; i < vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { + /* set masked-off elements to 1s */ + if (vma) { + vext_set_elem_mask(vd, i, 1); + } continue; } /* write a zero to all following active elements */ @@ -4944,11 +4949,14 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, CPURISCVState *env, \ uint32_t esz = sizeof(ETYPE); \ uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t sum = 0; \ int i; \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ *((ETYPE *)vd + H(i)) = sum; \ @@ -4975,10 +4983,13 @@ void HELPER(NAME)(void *vd, void *v0, CPURISCVState *env, uint32_t desc) \ uint32_t esz = sizeof(ETYPE); \ uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ int i; \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ *((ETYPE *)vd + H(i)) = i; \ From edabcd0e0aea2ac8d68931f31fcf8d3b99a28f20 Mon Sep 17 00:00:00 2001 From: "Yueh-Ting (eop) Chen" Date: Mon, 20 Jun 2022 06:51:12 +0000 Subject: [PATCH 23/44] target/riscv: rvv: Add mask agnostic for vector permutation instructions Signed-off-by: eop Chen Reviewed-by: Frank Chang Reviewed-by: Weiwei Li Acked-by: Alistair Francis Message-Id: <165570784143.17634.35095816584573691-9@git.sr.ht> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 1 + target/riscv/vector_helper.c | 26 +++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index c1bd29329efc..e58208f363fa 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -3891,6 +3891,7 @@ static bool int_ext_op(DisasContext *s, arg_rmr *a, uint8_t seq) data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), vreg_ofs(s, a->rs2), cpu_env, diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 52518648bbfc..d224861c2ce9 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -5018,11 +5018,14 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ uint32_t esz = sizeof(ETYPE); \ uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ target_ulong offset = s1, i_min, i; \ \ i_min = MAX(env->vstart, offset); \ for (i = i_min; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ *((ETYPE *)vd + H(i)) = *((ETYPE *)vs2 + H(i - offset)); \ @@ -5047,13 +5050,17 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ uint32_t esz = sizeof(ETYPE); \ uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ target_ulong i_max, i; \ \ i_max = MAX(MIN(s1 < vlmax ? vlmax - s1 : 0, vl), env->vstart); \ for (i = env->vstart; i < i_max; ++i) { \ - if (vm || vext_elem_mask(v0, i)) { \ - *((ETYPE *)vd + H(i)) = *((ETYPE *)vs2 + H(i + s1)); \ + if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ + continue; \ } \ + *((ETYPE *)vd + H(i)) = *((ETYPE *)vs2 + H(i + s1)); \ } \ \ for (i = i_max; i < vl; ++i) { \ @@ -5083,10 +5090,13 @@ static void vslide1up_##BITWIDTH(void *vd, void *v0, target_ulong s1, \ uint32_t esz = sizeof(ETYPE); \ uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ if (i == 0) { \ @@ -5128,10 +5138,13 @@ static void vslide1down_##BITWIDTH(void *vd, void *v0, target_ulong s1, \ uint32_t esz = sizeof(ETYPE); \ uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ if (i == vl - 1) { \ @@ -5199,11 +5212,14 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ uint32_t esz = sizeof(TS2); \ uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint64_t index; \ uint32_t i; \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ index = *((TS1 *)vs1 + HS1(i)); \ @@ -5239,11 +5255,14 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ uint32_t esz = sizeof(ETYPE); \ uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint64_t index = s1; \ uint32_t i; \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ if (index >= vlmax) { \ @@ -5318,10 +5337,13 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, \ uint32_t esz = sizeof(ETYPE); \ uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ *((ETYPE *)vd + HD(i)) = *((DTYPE *)vs2 + HS1(i)); \ From 1ad3f9bdc76c83b23d689a111d5a160c528ac8ba Mon Sep 17 00:00:00 2001 From: eopXD Date: Mon, 20 Jun 2022 06:51:11 +0000 Subject: [PATCH 24/44] target/riscv: rvv: Add option 'rvv_ma_all_1s' to enable optional mask agnostic behavior According to v-spec, mask agnostic behavior can be either kept as undisturbed or set elements' bits to all 1s. To distinguish the difference of mask policies, QEMU should be able to simulate the mask agnostic behavior as "set mask elements' bits to all 1s". There are multiple possibility for agnostic elements according to v-spec. The main intent of this patch-set tries to add option that can distinguish between mask policies. Setting agnostic elements to all 1s allows QEMU to express this. This commit adds option 'rvv_ma_all_1s' is added to enable the behavior, it is default as disabled. Signed-off-by: eop Chen Reviewed-by: Frank Chang Reviewed-by: Weiwei Li Reviewed-by: Alistair Francis Message-Id: <165570784143.17634.35095816584573691-10@git.sr.ht> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 117d308ae563..966e5f2dd7e0 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1061,6 +1061,7 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string, false), DEFINE_PROP_BOOL("rvv_ta_all_1s", RISCVCPU, cfg.rvv_ta_all_1s, false), + DEFINE_PROP_BOOL("rvv_ma_all_1s", RISCVCPU, cfg.rvv_ma_all_1s, false), DEFINE_PROP_END_OF_LIST(), }; From 4696f0ab5c436ed53567ce6baec67c921d9b70ae Mon Sep 17 00:00:00 2001 From: Dao Lu Date: Sun, 24 Jul 2022 20:47:28 -0700 Subject: [PATCH 25/44] target/riscv: Add Zihintpause support Added support for RISC-V PAUSE instruction from Zihintpause extension, enabled by default. Tested-by: Heiko Stuebner Reviewed-by: Alistair Francis Signed-off-by: Dao Lu Message-Id: <20220725034728.2620750-2-daolu@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 ++ target/riscv/cpu.h | 1 + target/riscv/insn32.decode | 7 ++++++- target/riscv/insn_trans/trans_rvi.c.inc | 16 ++++++++++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 966e5f2dd7e0..d4635c7df46b 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -73,6 +73,7 @@ static const struct isa_ext_data isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(v, false, PRIV_VERSION_1_12_0, ext_v), ISA_EXT_DATA_ENTRY(zicsr, true, PRIV_VERSION_1_10_0, ext_icsr), ISA_EXT_DATA_ENTRY(zifencei, true, PRIV_VERSION_1_10_0, ext_ifencei), + ISA_EXT_DATA_ENTRY(zihintpause, true, PRIV_VERSION_1_10_0, ext_zihintpause), ISA_EXT_DATA_ENTRY(zfh, true, PRIV_VERSION_1_12_0, ext_zfh), ISA_EXT_DATA_ENTRY(zfhmin, true, PRIV_VERSION_1_12_0, ext_zfhmin), ISA_EXT_DATA_ENTRY(zfinx, true, PRIV_VERSION_1_12_0, ext_zfinx), @@ -987,6 +988,7 @@ static Property riscv_cpu_extensions[] = { DEFINE_PROP_UINT8("pmu-num", RISCVCPU, cfg.pmu_num, 16), DEFINE_PROP_BOOL("Zifencei", RISCVCPU, cfg.ext_ifencei, true), DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true), + DEFINE_PROP_BOOL("Zihintpause", RISCVCPU, cfg.ext_zihintpause, 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.h b/target/riscv/cpu.h index 561d7fa92c8c..4be4b82a837d 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -426,6 +426,7 @@ struct RISCVCPUConfig { bool ext_zkt; bool ext_ifencei; bool ext_icsr; + bool ext_zihintpause; bool ext_svinval; bool ext_svnapot; bool ext_svpbmt; diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 4033565393f0..595fdcdad8be 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -149,7 +149,12 @@ srl 0000000 ..... ..... 101 ..... 0110011 @r sra 0100000 ..... ..... 101 ..... 0110011 @r or 0000000 ..... ..... 110 ..... 0110011 @r and 0000000 ..... ..... 111 ..... 0110011 @r -fence ---- pred:4 succ:4 ----- 000 ----- 0001111 + +{ + pause 0000 0001 0000 00000 000 00000 0001111 + fence ---- pred:4 succ:4 ----- 000 ----- 0001111 +} + fence_i ---- ---- ---- ----- 001 ----- 0001111 csrrw ............ ..... 001 ..... 1110011 @csr csrrs ............ ..... 010 ..... 1110011 @csr diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index ca8e3d1ea1f4..c49dbec0ebb9 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -792,6 +792,22 @@ static bool trans_srad(DisasContext *ctx, arg_srad *a) return gen_shift(ctx, a, EXT_SIGN, tcg_gen_sar_tl, NULL); } +static bool trans_pause(DisasContext *ctx, arg_pause *a) +{ + if (!ctx->cfg_ptr->ext_zihintpause) { + return false; + } + + /* + * PAUSE is a no-op in QEMU, + * end the TB and return to main loop + */ + gen_set_pc_imm(ctx, ctx->pc_succ_insn); + tcg_gen_exit_tb(NULL, 0); + ctx->base.is_jmp = DISAS_NORETURN; + + return true; +} static bool trans_fence(DisasContext *ctx, arg_fence *a) { From 6934f15b225c9324eafa064d3520a698ed09f9df Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 28 Jul 2022 15:19:26 -0300 Subject: [PATCH 26/44] hw/riscv: remove 'fdt' param from riscv_setup_rom_reset_vec() The 'fdt' param is not being used in riscv_setup_rom_reset_vec(). Simplify the API by removing it. While we're at it, remove the redundant 'return' statement at the end of function. Cc: Palmer Dabbelt Cc: Alistair Francis Cc: Bin Meng Cc: Vijai Kumar K Signed-off-by: Daniel Henrique Barboza Reviewed-by: Bin Meng Reviewed-by: Alistair Francis Message-Id: <20220728181926.2123771-1-danielhb413@gmail.com> Signed-off-by: Alistair Francis --- hw/riscv/boot.c | 4 +--- hw/riscv/microchip_pfsoc.c | 2 +- hw/riscv/shakti_c.c | 3 +-- hw/riscv/spike.c | 2 +- hw/riscv/virt.c | 2 +- include/hw/riscv/boot.h | 2 +- 6 files changed, 6 insertions(+), 9 deletions(-) diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 06b4fc5ac305..1ae7596873cb 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -286,7 +286,7 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts hwaddr start_addr, hwaddr rom_base, hwaddr rom_size, uint64_t kernel_entry, - uint64_t fdt_load_addr, void *fdt) + uint64_t fdt_load_addr) { int i; uint32_t start_addr_hi32 = 0x00000000; @@ -326,8 +326,6 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts rom_base, &address_space_memory); riscv_rom_copy_firmware_info(machine, rom_base, rom_size, sizeof(reset_vec), kernel_entry); - - return; } void riscv_setup_direct_kernel(hwaddr kernel_addr, hwaddr fdt_addr) diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index 10a5d0e50189..731315360600 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -583,7 +583,7 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) riscv_setup_rom_reset_vec(machine, &s->soc.u_cpus, firmware_load_addr, memmap[MICROCHIP_PFSOC_ENVM_DATA].base, memmap[MICROCHIP_PFSOC_ENVM_DATA].size, - kernel_entry, fdt_load_addr, machine->fdt); + kernel_entry, fdt_load_addr); } } diff --git a/hw/riscv/shakti_c.c b/hw/riscv/shakti_c.c index 90e2cf609f39..e43cc9445cb1 100644 --- a/hw/riscv/shakti_c.c +++ b/hw/riscv/shakti_c.c @@ -66,8 +66,7 @@ static void shakti_c_machine_state_init(MachineState *mstate) riscv_setup_rom_reset_vec(mstate, &sms->soc.cpus, shakti_c_memmap[SHAKTI_C_RAM].base, shakti_c_memmap[SHAKTI_C_ROM].base, - shakti_c_memmap[SHAKTI_C_ROM].size, 0, 0, - NULL); + shakti_c_memmap[SHAKTI_C_ROM].size, 0, 0); if (mstate->firmware) { riscv_load_firmware(mstate->firmware, shakti_c_memmap[SHAKTI_C_RAM].base, diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index e41b6aa9f099..5ba34543c8b5 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -308,7 +308,7 @@ static void spike_board_init(MachineState *machine) riscv_setup_rom_reset_vec(machine, &s->soc[0], memmap[SPIKE_DRAM].base, memmap[SPIKE_MROM].base, memmap[SPIKE_MROM].size, kernel_entry, - fdt_load_addr, s->fdt); + fdt_load_addr); /* initialize HTIF using symbols found in load_kernel */ htif_mm_init(system_memory, mask_rom, diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index f2ce5663a4c7..c1e8e0fcaf22 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1305,7 +1305,7 @@ static void virt_machine_done(Notifier *notifier, void *data) riscv_setup_rom_reset_vec(machine, &s->soc[0], start_addr, virt_memmap[VIRT_MROM].base, virt_memmap[VIRT_MROM].size, kernel_entry, - fdt_load_addr, machine->fdt); + fdt_load_addr); /* * Only direct boot kernel is currently supported for KVM VM, diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h index d2db29721ae5..a36f7618f5da 100644 --- a/include/hw/riscv/boot.h +++ b/include/hw/riscv/boot.h @@ -51,7 +51,7 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts hwaddr saddr, hwaddr rom_base, hwaddr rom_size, uint64_t kernel_entry, - uint64_t fdt_load_addr, void *fdt); + uint64_t fdt_load_addr); void riscv_rom_copy_firmware_info(MachineState *machine, hwaddr rom_base, hwaddr rom_size, uint32_t reset_vec_size, From eacaf440195675bc528f4aac394da7a74a9d95eb Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 3 Aug 2022 20:36:52 +0800 Subject: [PATCH 27/44] target/riscv: Fix priority of csr related check in riscv_csrrw_check Normally, riscv_csrrw_check is called when executing Zicsr instructions. And we can only do access control for existed CSRs. So the priority of CSR related check, from highest to lowest, should be as follows: 1) check whether Zicsr is supported: raise RISCV_EXCP_ILLEGAL_INST if not 2) check whether csr is existed: raise RISCV_EXCP_ILLEGAL_INST if not 3) do access control: raise RISCV_EXCP_ILLEGAL_INST or RISCV_EXCP_VIRT_ INSTRUCTION_FAULT if not allowed The predicates contain parts of function of both 2) and 3), So they need to be placed in the middle of riscv_csrrw_check Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Message-Id: <20220803123652.3700-1-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 0fb042b2fd0f..d81f466c8068 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -3270,6 +3270,30 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env, /* check privileges and return RISCV_EXCP_ILLEGAL_INST if check fails */ int read_only = get_field(csrno, 0xC00) == 3; int csr_min_priv = csr_ops[csrno].min_priv_ver; + + /* ensure the CSR extension is enabled. */ + if (!cpu->cfg.ext_icsr) { + return RISCV_EXCP_ILLEGAL_INST; + } + + if (env->priv_ver < csr_min_priv) { + return RISCV_EXCP_ILLEGAL_INST; + } + + /* check predicate */ + if (!csr_ops[csrno].predicate) { + return RISCV_EXCP_ILLEGAL_INST; + } + + if (write_mask && read_only) { + return RISCV_EXCP_ILLEGAL_INST; + } + + RISCVException ret = csr_ops[csrno].predicate(env, csrno); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + #if !defined(CONFIG_USER_ONLY) int csr_priv, effective_priv = env->priv; @@ -3290,25 +3314,7 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env, return RISCV_EXCP_ILLEGAL_INST; } #endif - if (write_mask && read_only) { - return RISCV_EXCP_ILLEGAL_INST; - } - - /* ensure the CSR extension is enabled. */ - if (!cpu->cfg.ext_icsr) { - return RISCV_EXCP_ILLEGAL_INST; - } - - /* check predicate */ - if (!csr_ops[csrno].predicate) { - return RISCV_EXCP_ILLEGAL_INST; - } - - if (env->priv_ver < csr_min_priv) { - return RISCV_EXCP_ILLEGAL_INST; - } - - return csr_ops[csrno].predicate(env, csrno); + return RISCV_EXCP_NONE; } static RISCVException riscv_csrrw_do64(CPURISCVState *env, int csrno, From bf8803c64d756128e4537e22fe86e3717a5274f1 Mon Sep 17 00:00:00 2001 From: Wilfred Mallawa Date: Fri, 12 Aug 2022 10:52:30 +1000 Subject: [PATCH 28/44] hw/riscv: opentitan: bump opentitan version The following patch updates opentitan to match the new configuration, as per, lowRISC/opentitan@217a0168ba118503c166a9587819e3811eeb0c0c Note: with this patch we now skip the usage of the opentitan `boot_rom`. The Opentitan boot rom contains hw verification for devies which we are currently not supporting in qemu. As of now, the `boot_rom` has no major significance, however, would be good to support in the future. Tested by running utests from the latest tock [1] (that supports this version of OT). [1] https://github.com/tock/tock/pull/3056 Signed-off-by: Wilfred Mallawa Reviewed-by: Alistair Francis Message-Id: <20220812005229.358850-1-wilfred.mallawa@opensource.wdc.com> Signed-off-by: Alistair Francis --- hw/riscv/opentitan.c | 12 ++++++++---- include/hw/riscv/opentitan.h | 11 ++++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 4495a2c039ff..af13dbe3b184 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -29,9 +29,9 @@ #include "sysemu/sysemu.h" static const MemMapEntry ibex_memmap[] = { - [IBEX_DEV_ROM] = { 0x00008000, 16 * KiB }, - [IBEX_DEV_RAM] = { 0x10000000, 0x10000 }, - [IBEX_DEV_FLASH] = { 0x20000000, 0x80000 }, + [IBEX_DEV_ROM] = { 0x00008000, 0x8000 }, + [IBEX_DEV_RAM] = { 0x10000000, 0x20000 }, + [IBEX_DEV_FLASH] = { 0x20000000, 0x100000 }, [IBEX_DEV_UART] = { 0x40000000, 0x1000 }, [IBEX_DEV_GPIO] = { 0x40040000, 0x1000 }, [IBEX_DEV_SPI_DEVICE] = { 0x40050000, 0x1000 }, @@ -40,6 +40,7 @@ static const MemMapEntry ibex_memmap[] = { [IBEX_DEV_TIMER] = { 0x40100000, 0x1000 }, [IBEX_DEV_SENSOR_CTRL] = { 0x40110000, 0x1000 }, [IBEX_DEV_OTP_CTRL] = { 0x40130000, 0x4000 }, + [IBEX_DEV_LC_CTRL] = { 0x40140000, 0x1000 }, [IBEX_DEV_USBDEV] = { 0x40150000, 0x1000 }, [IBEX_DEV_SPI_HOST0] = { 0x40300000, 0x1000 }, [IBEX_DEV_SPI_HOST1] = { 0x40310000, 0x1000 }, @@ -141,7 +142,8 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) &error_abort); object_property_set_int(OBJECT(&s->cpus), "num-harts", ms->smp.cpus, &error_abort); - object_property_set_int(OBJECT(&s->cpus), "resetvec", 0x8080, &error_abort); + object_property_set_int(OBJECT(&s->cpus), "resetvec", 0x20000490, + &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_fatal); /* Boot ROM */ @@ -253,6 +255,8 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) memmap[IBEX_DEV_SENSOR_CTRL].base, memmap[IBEX_DEV_SENSOR_CTRL].size); create_unimplemented_device("riscv.lowrisc.ibex.otp_ctrl", memmap[IBEX_DEV_OTP_CTRL].base, memmap[IBEX_DEV_OTP_CTRL].size); + create_unimplemented_device("riscv.lowrisc.ibex.lc_ctrl", + memmap[IBEX_DEV_LC_CTRL].base, memmap[IBEX_DEV_LC_CTRL].size); create_unimplemented_device("riscv.lowrisc.ibex.pwrmgr", memmap[IBEX_DEV_PWRMGR].base, memmap[IBEX_DEV_PWRMGR].size); create_unimplemented_device("riscv.lowrisc.ibex.rstmgr", diff --git a/include/hw/riscv/opentitan.h b/include/hw/riscv/opentitan.h index 68892cd8e524..26d960f28874 100644 --- a/include/hw/riscv/opentitan.h +++ b/include/hw/riscv/opentitan.h @@ -74,6 +74,7 @@ enum { IBEX_DEV_TIMER, IBEX_DEV_SENSOR_CTRL, IBEX_DEV_OTP_CTRL, + IBEX_DEV_LC_CTRL, IBEX_DEV_PWRMGR, IBEX_DEV_RSTMGR, IBEX_DEV_CLKMGR, @@ -105,11 +106,11 @@ enum { IBEX_UART0_RX_BREAK_ERR_IRQ = 6, IBEX_UART0_RX_TIMEOUT_IRQ = 7, IBEX_UART0_RX_PARITY_ERR_IRQ = 8, - IBEX_TIMER_TIMEREXPIRED0_0 = 126, - IBEX_SPI_HOST0_ERR_IRQ = 150, - IBEX_SPI_HOST0_SPI_EVENT_IRQ = 151, - IBEX_SPI_HOST1_ERR_IRQ = 152, - IBEX_SPI_HOST1_SPI_EVENT_IRQ = 153, + IBEX_TIMER_TIMEREXPIRED0_0 = 127, + IBEX_SPI_HOST0_ERR_IRQ = 151, + IBEX_SPI_HOST0_SPI_EVENT_IRQ = 152, + IBEX_SPI_HOST1_ERR_IRQ = 153, + IBEX_SPI_HOST1_SPI_EVENT_IRQ = 154, }; #endif From 25da6e311336b431d8cf1eaa8fe8688b7ee710ed Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Sat, 13 Aug 2022 14:51:27 +0100 Subject: [PATCH 29/44] hw/riscv: microchip_pfsoc: fix kernel panics due to missing peripherals Booting using "Direct Kernel Boot" for PolarFire SoC & skipping u-boot entirely is probably not advisable, but it does at least show signs of life. Recent Linux kernel versions make use of peripherals that are missing definitions in QEMU and lead to kernel panics. These issues almost certain rear their head for other methods of booting, but I was unable to figure out a suitable HSS version that is recent enough to support these peripherals & works with QEMU. With these peripherals added, booting a kernel with the following hangs hangs waiting for the system controller's hwrng, but the kernel no longer panics. With the Linux driver for hwrng disabled, it boots to console. qemu-system-riscv64 -M microchip-icicle-kit \ -m 2G -smp 5 \ -kernel $(vmlinux_bin) \ -dtb $(dtb)\ -initrd $(initramfs) \ -display none -serial null \ -serial stdio More peripherals are added than strictly required to fix the panics in the hopes of avoiding a replication of this problem in the future. Some of the peripherals which are in the device tree for recent kernels are implemented in the FPGA fabric. The eMMC/SD mux, which exists as an unimplemented device is replaced by a wider entry. This updated entry covers both the mux & the remainder of the FPGA fabric connected to the MSS using Fabric Interrconnect (FIC) 3. Link: https://github.com/polarfire-soc/icicle-kit-reference-design#fabric-memory-map Link: https://ww1.microchip.com/downloads/aemDocuments/documents/FPGA/ProductDocuments/SupportingCollateral/V1_4_Register_Map.zip Signed-off-by: Conor Dooley Reviewed-by: Alistair Francis Message-Id: <20220813135127.2971754-1-mail@conchuod.ie> Signed-off-by: Alistair Francis --- hw/riscv/microchip_pfsoc.c | 67 +++++++++++++++++++++++++++--- include/hw/riscv/microchip_pfsoc.h | 14 ++++++- 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index 731315360600..a821263d4f51 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -100,8 +100,11 @@ static const MemMapEntry microchip_pfsoc_memmap[] = { [MICROCHIP_PFSOC_L2LIM] = { 0x8000000, 0x2000000 }, [MICROCHIP_PFSOC_PLIC] = { 0xc000000, 0x4000000 }, [MICROCHIP_PFSOC_MMUART0] = { 0x20000000, 0x1000 }, + [MICROCHIP_PFSOC_WDOG0] = { 0x20001000, 0x1000 }, [MICROCHIP_PFSOC_SYSREG] = { 0x20002000, 0x2000 }, + [MICROCHIP_PFSOC_AXISW] = { 0x20004000, 0x1000 }, [MICROCHIP_PFSOC_MPUCFG] = { 0x20005000, 0x1000 }, + [MICROCHIP_PFSOC_FMETER] = { 0x20006000, 0x1000 }, [MICROCHIP_PFSOC_DDR_SGMII_PHY] = { 0x20007000, 0x1000 }, [MICROCHIP_PFSOC_EMMC_SD] = { 0x20008000, 0x1000 }, [MICROCHIP_PFSOC_DDR_CFG] = { 0x20080000, 0x40000 }, @@ -109,19 +112,28 @@ static const MemMapEntry microchip_pfsoc_memmap[] = { [MICROCHIP_PFSOC_MMUART2] = { 0x20102000, 0x1000 }, [MICROCHIP_PFSOC_MMUART3] = { 0x20104000, 0x1000 }, [MICROCHIP_PFSOC_MMUART4] = { 0x20106000, 0x1000 }, + [MICROCHIP_PFSOC_WDOG1] = { 0x20101000, 0x1000 }, + [MICROCHIP_PFSOC_WDOG2] = { 0x20103000, 0x1000 }, + [MICROCHIP_PFSOC_WDOG3] = { 0x20105000, 0x1000 }, + [MICROCHIP_PFSOC_WDOG4] = { 0x20106000, 0x1000 }, [MICROCHIP_PFSOC_SPI0] = { 0x20108000, 0x1000 }, [MICROCHIP_PFSOC_SPI1] = { 0x20109000, 0x1000 }, + [MICROCHIP_PFSOC_I2C0] = { 0x2010a000, 0x1000 }, [MICROCHIP_PFSOC_I2C1] = { 0x2010b000, 0x1000 }, + [MICROCHIP_PFSOC_CAN0] = { 0x2010c000, 0x1000 }, + [MICROCHIP_PFSOC_CAN1] = { 0x2010d000, 0x1000 }, [MICROCHIP_PFSOC_GEM0] = { 0x20110000, 0x2000 }, [MICROCHIP_PFSOC_GEM1] = { 0x20112000, 0x2000 }, [MICROCHIP_PFSOC_GPIO0] = { 0x20120000, 0x1000 }, [MICROCHIP_PFSOC_GPIO1] = { 0x20121000, 0x1000 }, [MICROCHIP_PFSOC_GPIO2] = { 0x20122000, 0x1000 }, + [MICROCHIP_PFSOC_RTC] = { 0x20124000, 0x1000 }, [MICROCHIP_PFSOC_ENVM_CFG] = { 0x20200000, 0x1000 }, [MICROCHIP_PFSOC_ENVM_DATA] = { 0x20220000, 0x20000 }, + [MICROCHIP_PFSOC_USB] = { 0x20201000, 0x1000 }, [MICROCHIP_PFSOC_QSPI_XIP] = { 0x21000000, 0x1000000 }, [MICROCHIP_PFSOC_IOSCB] = { 0x30000000, 0x10000000 }, - [MICROCHIP_PFSOC_EMMC_SD_MUX] = { 0x4f000000, 0x4 }, + [MICROCHIP_PFSOC_FABRIC_FIC3] = { 0x40000000, 0x20000000 }, [MICROCHIP_PFSOC_DRAM_LO] = { 0x80000000, 0x40000000 }, [MICROCHIP_PFSOC_DRAM_LO_ALIAS] = { 0xc0000000, 0x40000000 }, [MICROCHIP_PFSOC_DRAM_HI] = { 0x1000000000, 0x0 }, @@ -292,11 +304,21 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysreg), 0, memmap[MICROCHIP_PFSOC_SYSREG].base); + /* AXISW */ + create_unimplemented_device("microchip.pfsoc.axisw", + memmap[MICROCHIP_PFSOC_AXISW].base, + memmap[MICROCHIP_PFSOC_AXISW].size); + /* MPUCFG */ create_unimplemented_device("microchip.pfsoc.mpucfg", memmap[MICROCHIP_PFSOC_MPUCFG].base, memmap[MICROCHIP_PFSOC_MPUCFG].size); + /* FMETER */ + create_unimplemented_device("microchip.pfsoc.fmeter", + memmap[MICROCHIP_PFSOC_FMETER].base, + memmap[MICROCHIP_PFSOC_FMETER].size); + /* DDR SGMII PHY */ sysbus_realize(SYS_BUS_DEVICE(&s->ddr_sgmii_phy), errp); sysbus_mmio_map(SYS_BUS_DEVICE(&s->ddr_sgmii_phy), 0, @@ -336,6 +358,23 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(DEVICE(s->plic), MICROCHIP_PFSOC_MMUART4_IRQ), serial_hd(4)); + /* Watchdogs */ + create_unimplemented_device("microchip.pfsoc.watchdog0", + memmap[MICROCHIP_PFSOC_WDOG0].base, + memmap[MICROCHIP_PFSOC_WDOG0].size); + create_unimplemented_device("microchip.pfsoc.watchdog1", + memmap[MICROCHIP_PFSOC_WDOG1].base, + memmap[MICROCHIP_PFSOC_WDOG1].size); + create_unimplemented_device("microchip.pfsoc.watchdog2", + memmap[MICROCHIP_PFSOC_WDOG2].base, + memmap[MICROCHIP_PFSOC_WDOG2].size); + create_unimplemented_device("microchip.pfsoc.watchdog3", + memmap[MICROCHIP_PFSOC_WDOG3].base, + memmap[MICROCHIP_PFSOC_WDOG3].size); + create_unimplemented_device("microchip.pfsoc.watchdog4", + memmap[MICROCHIP_PFSOC_WDOG4].base, + memmap[MICROCHIP_PFSOC_WDOG4].size); + /* SPI */ create_unimplemented_device("microchip.pfsoc.spi0", memmap[MICROCHIP_PFSOC_SPI0].base, @@ -344,11 +383,27 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) memmap[MICROCHIP_PFSOC_SPI1].base, memmap[MICROCHIP_PFSOC_SPI1].size); - /* I2C1 */ + /* I2C */ + create_unimplemented_device("microchip.pfsoc.i2c0", + memmap[MICROCHIP_PFSOC_I2C0].base, + memmap[MICROCHIP_PFSOC_I2C0].size); create_unimplemented_device("microchip.pfsoc.i2c1", memmap[MICROCHIP_PFSOC_I2C1].base, memmap[MICROCHIP_PFSOC_I2C1].size); + /* CAN */ + create_unimplemented_device("microchip.pfsoc.can0", + memmap[MICROCHIP_PFSOC_CAN0].base, + memmap[MICROCHIP_PFSOC_CAN0].size); + create_unimplemented_device("microchip.pfsoc.can1", + memmap[MICROCHIP_PFSOC_CAN1].base, + memmap[MICROCHIP_PFSOC_CAN1].size); + + /* USB */ + create_unimplemented_device("microchip.pfsoc.usb", + memmap[MICROCHIP_PFSOC_USB].base, + memmap[MICROCHIP_PFSOC_USB].size); + /* GEMs */ nd = &nd_table[0]; @@ -402,10 +457,10 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->ioscb), 0, memmap[MICROCHIP_PFSOC_IOSCB].base); - /* eMMC/SD mux */ - create_unimplemented_device("microchip.pfsoc.emmc_sd_mux", - memmap[MICROCHIP_PFSOC_EMMC_SD_MUX].base, - memmap[MICROCHIP_PFSOC_EMMC_SD_MUX].size); + /* FPGA Fabric */ + create_unimplemented_device("microchip.pfsoc.fabricfic3", + memmap[MICROCHIP_PFSOC_FABRIC_FIC3].base, + memmap[MICROCHIP_PFSOC_FABRIC_FIC3].size); /* QSPI Flash */ memory_region_init_rom(qspi_xip_mem, OBJECT(dev), diff --git a/include/hw/riscv/microchip_pfsoc.h b/include/hw/riscv/microchip_pfsoc.h index a0673f5f59c1..a757b240e090 100644 --- a/include/hw/riscv/microchip_pfsoc.h +++ b/include/hw/riscv/microchip_pfsoc.h @@ -88,8 +88,11 @@ enum { MICROCHIP_PFSOC_L2LIM, MICROCHIP_PFSOC_PLIC, MICROCHIP_PFSOC_MMUART0, + MICROCHIP_PFSOC_WDOG0, MICROCHIP_PFSOC_SYSREG, + MICROCHIP_PFSOC_AXISW, MICROCHIP_PFSOC_MPUCFG, + MICROCHIP_PFSOC_FMETER, MICROCHIP_PFSOC_DDR_SGMII_PHY, MICROCHIP_PFSOC_EMMC_SD, MICROCHIP_PFSOC_DDR_CFG, @@ -97,19 +100,28 @@ enum { MICROCHIP_PFSOC_MMUART2, MICROCHIP_PFSOC_MMUART3, MICROCHIP_PFSOC_MMUART4, + MICROCHIP_PFSOC_WDOG1, + MICROCHIP_PFSOC_WDOG2, + MICROCHIP_PFSOC_WDOG3, + MICROCHIP_PFSOC_WDOG4, MICROCHIP_PFSOC_SPI0, MICROCHIP_PFSOC_SPI1, + MICROCHIP_PFSOC_I2C0, MICROCHIP_PFSOC_I2C1, + MICROCHIP_PFSOC_CAN0, + MICROCHIP_PFSOC_CAN1, MICROCHIP_PFSOC_GEM0, MICROCHIP_PFSOC_GEM1, MICROCHIP_PFSOC_GPIO0, MICROCHIP_PFSOC_GPIO1, MICROCHIP_PFSOC_GPIO2, + MICROCHIP_PFSOC_RTC, MICROCHIP_PFSOC_ENVM_CFG, MICROCHIP_PFSOC_ENVM_DATA, + MICROCHIP_PFSOC_USB, MICROCHIP_PFSOC_QSPI_XIP, MICROCHIP_PFSOC_IOSCB, - MICROCHIP_PFSOC_EMMC_SD_MUX, + MICROCHIP_PFSOC_FABRIC_FIC3, MICROCHIP_PFSOC_DRAM_LO, MICROCHIP_PFSOC_DRAM_LO_ALIAS, MICROCHIP_PFSOC_DRAM_HI, From 240b363618bdd981333e8ebbb1cb09e702315b0a Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Tue, 16 Aug 2022 16:23:21 -0700 Subject: [PATCH 30/44] target/riscv: Remove additional priv version check for mcountinhibit With .min_priv_version, additiona priv version check is uncessary for mcountinhibit read/write functions. Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Reviewed-by: Alistair Francis Signed-off-by: Atish Patra Message-Id: <20220816232321.558250-7-atishp@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index d81f466c8068..4a7078f7d12f 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -1494,10 +1494,6 @@ static RISCVException write_mtvec(CPURISCVState *env, int csrno, static RISCVException read_mcountinhibit(CPURISCVState *env, int csrno, target_ulong *val) { - if (env->priv_ver < PRIV_VERSION_1_11_0) { - return RISCV_EXCP_ILLEGAL_INST; - } - *val = env->mcountinhibit; return RISCV_EXCP_NONE; } @@ -1508,10 +1504,6 @@ static RISCVException write_mcountinhibit(CPURISCVState *env, int csrno, int cidx; PMUCTRState *counter; - if (env->priv_ver < PRIV_VERSION_1_11_0) { - return RISCV_EXCP_ILLEGAL_INST; - } - env->mcountinhibit = val; /* Check if any other counter is also monitoring cycles/instructions */ From 53c38f7ab1a5b55d63303d36a42c3e74b5fc9225 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Wed, 10 Aug 2022 19:46:09 +0100 Subject: [PATCH 31/44] hw/riscv: virt: fix uart node name "uart" is not a node name that complies with the dt-schema. Change the node name to "serial" to ix warnings seen during dt-validate on a dtbdump of the virt machine such as: /stuff/qemu/qemu.dtb: uart@10000000: $nodename:0: 'uart@10000000' does not match '^serial(@.*)?$' From schema: /stuff/linux/Documentation/devicetree/bindings/serial/8250.yaml Reported-by: Rob Herring Reviewed-by: Alistair Francis Signed-off-by: Conor Dooley Message-id: 20220810184612.157317-2-mail@conchuod.ie Link: https://lore.kernel.org/linux-riscv/20220803170552.GA2250266-robh@kernel.org/ Fixes: 04331d0b56 ("RISC-V VirtIO Machine") Signed-off-by: Conor Dooley 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 c1e8e0fcaf22..9d36133b74e1 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -918,7 +918,7 @@ static void create_fdt_uart(RISCVVirtState *s, const MemMapEntry *memmap, char *name; MachineState *mc = MACHINE(s); - name = g_strdup_printf("/soc/uart@%lx", (long)memmap[VIRT_UART0].base); + name = g_strdup_printf("/soc/serial@%lx", (long)memmap[VIRT_UART0].base); qemu_fdt_add_subnode(mc->fdt, name); qemu_fdt_setprop_string(mc->fdt, name, "compatible", "ns16550a"); qemu_fdt_setprop_cells(mc->fdt, name, "reg", From 95e401d3785a9be9ac4edc7a5a7f9147d917e610 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Wed, 10 Aug 2022 19:46:10 +0100 Subject: [PATCH 32/44] hw/riscv: virt: fix the plic's address cells When optional AIA PLIC support was added the to the virt machine, the address cells property was removed leading the issues with dt-validate on a dump from the virt machine: /stuff/qemu/qemu.dtb: plic@c000000: '#address-cells' is a required property From schema: /stuff/linux/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml Add back the property to suppress the warning. Reported-by: Rob Herring Reviewed-by: Alistair Francis Signed-off-by: Conor Dooley Message-id: 20220810184612.157317-3-mail@conchuod.ie Link: https://lore.kernel.org/linux-riscv/20220803170552.GA2250266-robh@kernel.org/ Fixes: e6faee6585 ("hw/riscv: virt: Add optional AIA APLIC support to virt machine") Signed-off-by: Conor Dooley Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 2 ++ include/hw/riscv/virt.h | 1 + 2 files changed, 3 insertions(+) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 9d36133b74e1..f19758e1df97 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -466,6 +466,8 @@ static void create_fdt_socket_plic(RISCVVirtState *s, qemu_fdt_add_subnode(mc->fdt, plic_name); qemu_fdt_setprop_cell(mc->fdt, plic_name, "#interrupt-cells", FDT_PLIC_INT_CELLS); + qemu_fdt_setprop_cell(mc->fdt, plic_name, + "#address-cells", FDT_PLIC_ADDR_CELLS); qemu_fdt_setprop_string_array(mc->fdt, plic_name, "compatible", (char **)&plic_compat, ARRAY_SIZE(plic_compat)); diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index 984e55c77fb2..be4ab8fe7f71 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -111,6 +111,7 @@ enum { #define FDT_PCI_ADDR_CELLS 3 #define FDT_PCI_INT_CELLS 1 +#define FDT_PLIC_ADDR_CELLS 0 #define FDT_PLIC_INT_CELLS 1 #define FDT_APLIC_INT_CELLS 2 #define FDT_IMSIC_INT_CELLS 0 From ae29379998f101aedf32f9168135eb0545257b3c Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Wed, 10 Aug 2022 19:46:11 +0100 Subject: [PATCH 33/44] hw/riscv: virt: fix syscon subnode paths The reset and poweroff features of the syscon were originally added to top level, which is a valid path for a syscon subnode. Subsequently a reorganisation was carried out while implementing NUMA in which the subnodes were moved into the /soc node. As /soc is a "simple-bus", this path is invalid, and so dt-validate produces the following warnings: /stuff/qemu/qemu.dtb: soc: poweroff: {'value': [[21845]], 'offset': [[0]], 'regmap': [[4]], 'compatible': ['syscon-poweroff']} should not be valid under {'type': 'object'} From schema: /home/conor/.local/lib/python3.9/site-packages/dtschema/schemas/simple-bus.yaml /stuff/qemu/qemu.dtb: soc: reboot: {'value': [[30583]], 'offset': [[0]], 'regmap': [[4]], 'compatible': ['syscon-reboot']} should not be valid under {'type': 'object'} From schema: /home/conor/.local/lib/python3.9/site-packages/dtschema/schemas/simple-bus.yaml Move the syscon subnodes back to the top level and silence the warnings. Reported-by: Rob Herring Signed-off-by: Conor Dooley Reviewed-by: Alistair Francis Message-id: 20220810184612.157317-4-mail@conchuod.ie Link: https://lore.kernel.org/linux-riscv/20220803170552.GA2250266-robh@kernel.org/ Fixes: 18df0b4695 ("hw/riscv: virt: Allow creating multiple NUMA sockets") Signed-off-by: Conor Dooley Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index f19758e1df97..686341a0e2ae 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -897,7 +897,7 @@ static void create_fdt_reset(RISCVVirtState *s, const MemMapEntry *memmap, test_phandle = qemu_fdt_get_phandle(mc->fdt, name); g_free(name); - name = g_strdup_printf("/soc/reboot"); + name = g_strdup_printf("/reboot"); qemu_fdt_add_subnode(mc->fdt, name); qemu_fdt_setprop_string(mc->fdt, name, "compatible", "syscon-reboot"); qemu_fdt_setprop_cell(mc->fdt, name, "regmap", test_phandle); @@ -905,7 +905,7 @@ static void create_fdt_reset(RISCVVirtState *s, const MemMapEntry *memmap, qemu_fdt_setprop_cell(mc->fdt, name, "value", FINISHER_RESET); g_free(name); - name = g_strdup_printf("/soc/poweroff"); + name = g_strdup_printf("/poweroff"); qemu_fdt_add_subnode(mc->fdt, name); qemu_fdt_setprop_string(mc->fdt, name, "compatible", "syscon-poweroff"); qemu_fdt_setprop_cell(mc->fdt, name, "regmap", test_phandle); From d1af78745cfc4e8efdda9b3484b32bbb4507276f Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Wed, 10 Aug 2022 19:46:12 +0100 Subject: [PATCH 34/44] hw/core: fix platform bus node name "platform" is not a valid name for a bus node in dt-schema, so warnings can be see in dt-validate on a dump of the riscv virt dtb: /stuff/qemu/qemu.dtb: platform@4000000: $nodename:0: 'platform@4000000' does not match '^([a-z][a-z0-9\\-]+-bus|bus|soc|axi|ahb|apb)(@[0-9a-f]+)?$' From schema: /home/conor/.local/lib/python3.9/site-packages/dtschema/schemas/simple-bus.yaml "platform-bus" is a valid name, so use that instead. CC: Rob Herring Fixes: 11d306b9df ("hw/arm/sysbus-fdt: helpers for platform bus nodes addition") Reviewed-by: Alistair Francis Signed-off-by: Conor Dooley Message-id: 20220810184612.157317-5-mail@conchuod.ie Signed-off-by: Alistair Francis --- hw/core/sysbus-fdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c index 19d22cbe7301..edb0c49b1960 100644 --- a/hw/core/sysbus-fdt.c +++ b/hw/core/sysbus-fdt.c @@ -539,7 +539,7 @@ void platform_bus_add_all_fdt_nodes(void *fdt, const char *intc, hwaddr addr, assert(fdt); - node = g_strdup_printf("/platform@%"PRIx64, addr); + node = g_strdup_printf("/platform-bus@%"PRIx64, addr); /* Create a /platform node that we can put all devices into */ qemu_fdt_add_subnode(fdt, node); From e0dea2f55f678a1aa1dab3a25c13f52d68b4ec2b Mon Sep 17 00:00:00 2001 From: Rahul Pathak Date: Tue, 16 Aug 2022 10:24:08 +0530 Subject: [PATCH 35/44] target/riscv: Add xicondops in ISA entry XVentanaCondOps is Ventana custom extension. Add its extension entry in the ISA Ext array Signed-off-by: Rahul Pathak Reviewed-by: Alistair Francis Message-id: 20220816045408.1231135-1-rpathak@ventanamicro.com Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index d4635c7df46b..e0d5941230a7 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -102,6 +102,7 @@ static const struct isa_ext_data isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(svinval, true, PRIV_VERSION_1_12_0, ext_svinval), ISA_EXT_DATA_ENTRY(svnapot, true, PRIV_VERSION_1_12_0, ext_svnapot), ISA_EXT_DATA_ENTRY(svpbmt, true, PRIV_VERSION_1_12_0, ext_svpbmt), + ISA_EXT_DATA_ENTRY(xventanacondops, true, PRIV_VERSION_1_12_0, ext_XVentanaCondOps), }; static bool isa_ext_is_enabled(RISCVCPU *cpu, From dc9acc9ce4add37bc5b4437ae9117c318b4f09d4 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Sat, 20 Aug 2022 09:59:58 +0530 Subject: [PATCH 36/44] target/riscv: Use official extension names for AIA CSRs The arch review of AIA spec is completed and we now have official extension names for AIA: Smaia (M-mode AIA CSRs) and Ssaia (S-mode AIA CSRs). Refer, section 1.6 of the latest AIA v0.3.1 stable specification at https://github.com/riscv/riscv-aia/releases/download/0.3.1-draft.32/riscv-interrupts-032.pdf) Based on above, we update QEMU RISC-V to: 1) Have separate config options for Smaia and Ssaia extensions which replace RISCV_FEATURE_AIA in CPU features 2) Not generate AIA INTC compatible string in virt machine Signed-off-by: Anup Patel Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-id: 20220820042958.377018-1-apatel@ventanamicro.com Signed-off-by: Alistair Francis --- hw/intc/riscv_imsic.c | 4 +++- hw/riscv/virt.c | 13 ++----------- target/riscv/cpu.c | 9 ++++----- target/riscv/cpu.h | 4 ++-- target/riscv/cpu_helper.c | 3 ++- target/riscv/csr.c | 24 ++++++++++++++++++------ 6 files changed, 31 insertions(+), 26 deletions(-) diff --git a/hw/intc/riscv_imsic.c b/hw/intc/riscv_imsic.c index 8615e4cc1d9c..4d4d5b50cabf 100644 --- a/hw/intc/riscv_imsic.c +++ b/hw/intc/riscv_imsic.c @@ -344,9 +344,11 @@ static void riscv_imsic_realize(DeviceState *dev, Error **errp) /* Force select AIA feature and setup CSR read-modify-write callback */ if (env) { - riscv_set_feature(env, RISCV_FEATURE_AIA); if (!imsic->mmode) { + rcpu->cfg.ext_ssaia = true; riscv_cpu_set_geilen(env, imsic->num_pages - 1); + } else { + rcpu->cfg.ext_smaia = true; } riscv_cpu_set_aia_ireg_rmw_fn(env, (imsic->mmode) ? PRV_M : PRV_S, riscv_imsic_rmw, imsic); diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 686341a0e2ae..ff8c0df5cd47 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -260,17 +260,8 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket, qemu_fdt_add_subnode(mc->fdt, intc_name); qemu_fdt_setprop_cell(mc->fdt, intc_name, "phandle", intc_phandles[cpu]); - if (riscv_feature(&s->soc[socket].harts[cpu].env, - RISCV_FEATURE_AIA)) { - static const char * const compat[2] = { - "riscv,cpu-intc-aia", "riscv,cpu-intc" - }; - qemu_fdt_setprop_string_array(mc->fdt, intc_name, "compatible", - (char **)&compat, ARRAY_SIZE(compat)); - } else { - qemu_fdt_setprop_string(mc->fdt, intc_name, "compatible", - "riscv,cpu-intc"); - } + qemu_fdt_setprop_string(mc->fdt, intc_name, "compatible", + "riscv,cpu-intc"); qemu_fdt_setprop(mc->fdt, intc_name, "interrupt-controller", NULL, 0); qemu_fdt_setprop_cell(mc->fdt, intc_name, "#interrupt-cells", 1); diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index e0d5941230a7..26d44df44697 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -99,6 +99,8 @@ static const struct isa_ext_data isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zve64f, true, PRIV_VERSION_1_12_0, ext_zve64f), ISA_EXT_DATA_ENTRY(zhinx, true, PRIV_VERSION_1_12_0, ext_zhinx), ISA_EXT_DATA_ENTRY(zhinxmin, true, PRIV_VERSION_1_12_0, ext_zhinxmin), + ISA_EXT_DATA_ENTRY(smaia, true, PRIV_VERSION_1_12_0, ext_smaia), + ISA_EXT_DATA_ENTRY(ssaia, true, PRIV_VERSION_1_12_0, ext_ssaia), ISA_EXT_DATA_ENTRY(svinval, true, PRIV_VERSION_1_12_0, ext_svinval), ISA_EXT_DATA_ENTRY(svnapot, true, PRIV_VERSION_1_12_0, ext_svnapot), ISA_EXT_DATA_ENTRY(svpbmt, true, PRIV_VERSION_1_12_0, ext_svpbmt), @@ -666,10 +668,6 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) } } - if (cpu->cfg.aia) { - riscv_set_feature(env, RISCV_FEATURE_AIA); - } - if (cpu->cfg.debug) { riscv_set_feature(env, RISCV_FEATURE_DEBUG); } @@ -1038,7 +1036,8 @@ static Property riscv_cpu_extensions[] = { DEFINE_PROP_BOOL("x-j", RISCVCPU, cfg.ext_j, false), /* ePMP 0.9.3 */ DEFINE_PROP_BOOL("x-epmp", RISCVCPU, cfg.epmp, false), - DEFINE_PROP_BOOL("x-aia", RISCVCPU, cfg.aia, false), + DEFINE_PROP_BOOL("x-smaia", RISCVCPU, cfg.ext_smaia, false), + DEFINE_PROP_BOOL("x-ssaia", RISCVCPU, cfg.ext_ssaia, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 4be4b82a837d..081cd0554471 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -85,7 +85,6 @@ enum { RISCV_FEATURE_PMP, RISCV_FEATURE_EPMP, RISCV_FEATURE_MISA, - RISCV_FEATURE_AIA, RISCV_FEATURE_DEBUG }; @@ -439,6 +438,8 @@ struct RISCVCPUConfig { bool ext_zve32f; bool ext_zve64f; bool ext_zmmul; + bool ext_smaia; + bool ext_ssaia; bool rvv_ta_all_1s; bool rvv_ma_all_1s; @@ -459,7 +460,6 @@ struct RISCVCPUConfig { bool mmu; bool pmp; bool epmp; - bool aia; bool debug; uint64_t resetvec; diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 650574accf0a..05c0c8d7771b 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -307,6 +307,7 @@ static int riscv_cpu_pending_to_irq(CPURISCVState *env, int extirq, unsigned int extirq_def_prio, uint64_t pending, uint8_t *iprio) { + RISCVCPU *cpu = env_archcpu(env); int irq, best_irq = RISCV_EXCP_NONE; unsigned int prio, best_prio = UINT_MAX; @@ -315,7 +316,7 @@ static int riscv_cpu_pending_to_irq(CPURISCVState *env, } irq = ctz64(pending); - if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + if (!((extirq == IRQ_M_EXT) ? cpu->cfg.ext_smaia : cpu->cfg.ext_ssaia)) { return irq; } diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 4a7078f7d12f..3ddf309055f1 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -257,7 +257,9 @@ static RISCVException any32(CPURISCVState *env, int csrno) static int aia_any(CPURISCVState *env, int csrno) { - if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + RISCVCPU *cpu = env_archcpu(env); + + if (!cpu->cfg.ext_smaia) { return RISCV_EXCP_ILLEGAL_INST; } @@ -266,7 +268,9 @@ static int aia_any(CPURISCVState *env, int csrno) static int aia_any32(CPURISCVState *env, int csrno) { - if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + RISCVCPU *cpu = env_archcpu(env); + + if (!cpu->cfg.ext_smaia) { return RISCV_EXCP_ILLEGAL_INST; } @@ -293,7 +297,9 @@ static int smode32(CPURISCVState *env, int csrno) static int aia_smode(CPURISCVState *env, int csrno) { - if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + RISCVCPU *cpu = env_archcpu(env); + + if (!cpu->cfg.ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } @@ -302,7 +308,9 @@ static int aia_smode(CPURISCVState *env, int csrno) static int aia_smode32(CPURISCVState *env, int csrno) { - if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + RISCVCPU *cpu = env_archcpu(env); + + if (!cpu->cfg.ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } @@ -358,7 +366,9 @@ static RISCVException pointer_masking(CPURISCVState *env, int csrno) static int aia_hmode(CPURISCVState *env, int csrno) { - if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + RISCVCPU *cpu = env_archcpu(env); + + if (!cpu->cfg.ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } @@ -367,7 +377,9 @@ static int aia_hmode(CPURISCVState *env, int csrno) static int aia_hmode32(CPURISCVState *env, int csrno) { - if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + RISCVCPU *cpu = env_archcpu(env); + + if (!cpu->cfg.ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } From 7cbcc538f4b3040db1e39a6547efa501a8a44907 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Wed, 24 Aug 2022 15:13:55 -0700 Subject: [PATCH 37/44] hw/intc: Move mtimer/mtimecmp to aclint Historically, The mtime/mtimecmp has been part of the CPU because they are per hart entities. However, they actually belong to aclint which is a MMIO device. Move them to the ACLINT device. This also emulates the real hardware more closely. Reviewed-by: Anup Patel Reviewed-by: Alistair Francis Reviewed-by: Andrew Jones Signed-off-by: Atish Patra Message-Id: <20220824221357.41070-2-atishp@rivosinc.com> Signed-off-by: Alistair Francis --- hw/intc/riscv_aclint.c | 48 ++++++++++++++++++++++++---------- hw/timer/ibex_timer.c | 18 +++++-------- include/hw/intc/riscv_aclint.h | 2 ++ include/hw/timer/ibex_timer.h | 2 ++ target/riscv/cpu.h | 2 -- target/riscv/machine.c | 5 ++-- 6 files changed, 47 insertions(+), 30 deletions(-) diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c index e7942c4e5a32..eee04643cb19 100644 --- a/hw/intc/riscv_aclint.c +++ b/hw/intc/riscv_aclint.c @@ -32,6 +32,7 @@ #include "hw/intc/riscv_aclint.h" #include "qemu/timer.h" #include "hw/irq.h" +#include "migration/vmstate.h" typedef struct riscv_aclint_mtimer_callback { RISCVAclintMTimerState *s; @@ -65,19 +66,22 @@ static void riscv_aclint_mtimer_write_timecmp(RISCVAclintMTimerState *mtimer, uint64_t rtc_r = cpu_riscv_read_rtc(mtimer); - cpu->env.timecmp = value; - if (cpu->env.timecmp <= rtc_r) { + /* Compute the relative hartid w.r.t the socket */ + hartid = hartid - mtimer->hartid_base; + + mtimer->timecmp[hartid] = value; + if (mtimer->timecmp[hartid] <= rtc_r) { /* * If we're setting an MTIMECMP value in the "past", * immediately raise the timer interrupt */ - qemu_irq_raise(mtimer->timer_irqs[hartid - mtimer->hartid_base]); + qemu_irq_raise(mtimer->timer_irqs[hartid]); return; } /* otherwise, set up the future timer interrupt */ - qemu_irq_lower(mtimer->timer_irqs[hartid - mtimer->hartid_base]); - diff = cpu->env.timecmp - rtc_r; + qemu_irq_lower(mtimer->timer_irqs[hartid]); + diff = mtimer->timecmp[hartid] - rtc_r; /* back to ns (note args switched in muldiv64) */ uint64_t ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq); @@ -102,7 +106,7 @@ static void riscv_aclint_mtimer_write_timecmp(RISCVAclintMTimerState *mtimer, next = MIN(next, INT64_MAX); } - timer_mod(cpu->env.timer, next); + timer_mod(mtimer->timers[hartid], next); } /* @@ -133,11 +137,11 @@ static uint64_t riscv_aclint_mtimer_read(void *opaque, hwaddr addr, "aclint-mtimer: invalid hartid: %zu", hartid); } else if ((addr & 0x7) == 0) { /* timecmp_lo for RV32/RV64 or timecmp for RV64 */ - uint64_t timecmp = env->timecmp; + uint64_t timecmp = mtimer->timecmp[hartid]; return (size == 4) ? (timecmp & 0xFFFFFFFF) : timecmp; } else if ((addr & 0x7) == 4) { /* timecmp_hi */ - uint64_t timecmp = env->timecmp; + uint64_t timecmp = mtimer->timecmp[hartid]; return (timecmp >> 32) & 0xFFFFFFFF; } else { qemu_log_mask(LOG_UNIMP, @@ -177,7 +181,7 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr, } else if ((addr & 0x7) == 0) { if (size == 4) { /* timecmp_lo for RV32/RV64 */ - uint64_t timecmp_hi = env->timecmp >> 32; + uint64_t timecmp_hi = mtimer->timecmp[hartid] >> 32; riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid, timecmp_hi << 32 | (value & 0xFFFFFFFF)); } else { @@ -188,7 +192,7 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr, } else if ((addr & 0x7) == 4) { if (size == 4) { /* timecmp_hi for RV32/RV64 */ - uint64_t timecmp_lo = env->timecmp; + uint64_t timecmp_lo = mtimer->timecmp[hartid]; riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid, value << 32 | (timecmp_lo & 0xFFFFFFFF)); } else { @@ -234,7 +238,7 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr, } riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), mtimer->hartid_base + i, - env->timecmp); + mtimer->timecmp[i]); } return; } @@ -284,6 +288,8 @@ static void riscv_aclint_mtimer_realize(DeviceState *dev, Error **errp) s->timer_irqs = g_new(qemu_irq, s->num_harts); qdev_init_gpio_out(dev, s->timer_irqs, s->num_harts); + s->timers = g_new0(QEMUTimer *, s->num_harts); + s->timecmp = g_new0(uint64_t, s->num_harts); /* Claim timer interrupt bits */ for (i = 0; i < s->num_harts; i++) { RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(s->hartid_base + i)); @@ -310,6 +316,18 @@ static void riscv_aclint_mtimer_reset_enter(Object *obj, ResetType type) riscv_aclint_mtimer_write(mtimer, mtimer->time_base, 0, 8); } +static const VMStateDescription vmstate_riscv_mtimer = { + .name = "riscv_mtimer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_VARRAY_UINT32(timecmp, RISCVAclintMTimerState, + num_harts, 0, + vmstate_info_uint64, uint64_t), + VMSTATE_END_OF_LIST() + } +}; + static void riscv_aclint_mtimer_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -317,6 +335,7 @@ static void riscv_aclint_mtimer_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, riscv_aclint_mtimer_properties); ResettableClass *rc = RESETTABLE_CLASS(klass); rc->phases.enter = riscv_aclint_mtimer_reset_enter; + dc->vmsd = &vmstate_riscv_mtimer; } static const TypeInfo riscv_aclint_mtimer_info = { @@ -336,6 +355,7 @@ DeviceState *riscv_aclint_mtimer_create(hwaddr addr, hwaddr size, { int i; DeviceState *dev = qdev_new(TYPE_RISCV_ACLINT_MTIMER); + RISCVAclintMTimerState *s = RISCV_ACLINT_MTIMER(dev); assert(num_harts <= RISCV_ACLINT_MAX_HARTS); assert(!(addr & 0x7)); @@ -366,11 +386,11 @@ DeviceState *riscv_aclint_mtimer_create(hwaddr addr, hwaddr size, riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc, dev); } - cb->s = RISCV_ACLINT_MTIMER(dev); + cb->s = s; cb->num = i; - env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + s->timers[i] = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_aclint_mtimer_cb, cb); - env->timecmp = 0; + s->timecmp[i] = 0; qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(DEVICE(rvcpu), IRQ_M_TIMER)); diff --git a/hw/timer/ibex_timer.c b/hw/timer/ibex_timer.c index 8c2ca364daab..d8b8e4e1f602 100644 --- a/hw/timer/ibex_timer.c +++ b/hw/timer/ibex_timer.c @@ -60,8 +60,6 @@ static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq) static void ibex_timer_update_irqs(IbexTimerState *s) { - CPUState *cs = qemu_get_cpu(0); - RISCVCPU *cpu = RISCV_CPU(cs); uint64_t value = s->timer_compare_lower0 | ((uint64_t)s->timer_compare_upper0 << 32); uint64_t next, diff; @@ -73,9 +71,9 @@ static void ibex_timer_update_irqs(IbexTimerState *s) } /* Update the CPUs mtimecmp */ - cpu->env.timecmp = value; + s->mtimecmp = value; - if (cpu->env.timecmp <= now) { + if (s->mtimecmp <= now) { /* * If the mtimecmp was in the past raise the interrupt now. */ @@ -91,7 +89,7 @@ static void ibex_timer_update_irqs(IbexTimerState *s) qemu_irq_lower(s->m_timer_irq); qemu_set_irq(s->irq, false); - diff = cpu->env.timecmp - now; + diff = s->mtimecmp - now; next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + muldiv64(diff, NANOSECONDS_PER_SECOND, @@ -99,9 +97,9 @@ static void ibex_timer_update_irqs(IbexTimerState *s) if (next < qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) { /* We overflowed the timer, just set it as large as we can */ - timer_mod(cpu->env.timer, 0x7FFFFFFFFFFFFFFF); + timer_mod(s->mtimer, 0x7FFFFFFFFFFFFFFF); } else { - timer_mod(cpu->env.timer, next); + timer_mod(s->mtimer, next); } } @@ -120,11 +118,9 @@ static void ibex_timer_reset(DeviceState *dev) { IbexTimerState *s = IBEX_TIMER(dev); - CPUState *cpu = qemu_get_cpu(0); - CPURISCVState *env = cpu->env_ptr; - env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + s->mtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &ibex_timer_cb, s); - env->timecmp = 0; + s->mtimecmp = 0; s->timer_ctrl = 0x00000000; s->timer_cfg0 = 0x00010000; diff --git a/include/hw/intc/riscv_aclint.h b/include/hw/intc/riscv_aclint.h index 26d4048687fb..693415eb6def 100644 --- a/include/hw/intc/riscv_aclint.h +++ b/include/hw/intc/riscv_aclint.h @@ -32,6 +32,8 @@ typedef struct RISCVAclintMTimerState { /*< private >*/ SysBusDevice parent_obj; uint64_t time_delta; + uint64_t *timecmp; + QEMUTimer **timers; /*< public >*/ MemoryRegion mmio; diff --git a/include/hw/timer/ibex_timer.h b/include/hw/timer/ibex_timer.h index 1a0a28d5fab5..41f5c82a920b 100644 --- a/include/hw/timer/ibex_timer.h +++ b/include/hw/timer/ibex_timer.h @@ -33,6 +33,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(IbexTimerState, IBEX_TIMER) struct IbexTimerState { /* */ SysBusDevice parent_obj; + uint64_t mtimecmp; + QEMUTimer *mtimer; /* Internal timer for M-mode interrupt */ /* */ MemoryRegion mmio; diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 081cd0554471..53335def2336 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -307,7 +307,6 @@ struct CPUArchState { /* temporary htif regs */ uint64_t mfromhost; uint64_t mtohost; - uint64_t timecmp; /* physical memory protection */ pmp_table_t pmp_state; @@ -362,7 +361,6 @@ struct CPUArchState { float_status fp_status; /* Fields from here on are preserved across CPU reset. */ - QEMUTimer *timer; /* Internal timer */ hwaddr kernel_addr; hwaddr fdt_addr; diff --git a/target/riscv/machine.c b/target/riscv/machine.c index dc182ca81119..b508b042cb73 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -307,8 +307,8 @@ static const VMStateDescription vmstate_pmu_ctr_state = { const VMStateDescription vmstate_riscv_cpu = { .name = "cpu", - .version_id = 3, - .minimum_version_id = 3, + .version_id = 4, + .minimum_version_id = 4, .post_load = riscv_cpu_post_load, .fields = (VMStateField[]) { VMSTATE_UINTTL_ARRAY(env.gpr, RISCVCPU, 32), @@ -359,7 +359,6 @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINTTL(env.mscratch, RISCVCPU), VMSTATE_UINT64(env.mfromhost, RISCVCPU), VMSTATE_UINT64(env.mtohost, RISCVCPU), - VMSTATE_UINT64(env.timecmp, RISCVCPU), VMSTATE_END_OF_LIST() }, From 43888c2f1823212b1064a6a94d65d8acaf954478 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Wed, 24 Aug 2022 15:13:56 -0700 Subject: [PATCH 38/44] target/riscv: Add stimecmp support stimecmp allows the supervisor mode to update stimecmp CSR directly to program the next timer interrupt. This CSR is part of the Sstc extension which was ratified recently. Reviewed-by: Alistair Francis Signed-off-by: Atish Patra Message-Id: <20220824221357.41070-3-atishp@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 9 ++++ target/riscv/cpu.h | 5 ++ target/riscv/cpu_bits.h | 4 ++ target/riscv/csr.c | 86 +++++++++++++++++++++++++++++++++ target/riscv/machine.c | 1 + target/riscv/meson.build | 3 +- target/riscv/time_helper.c | 98 ++++++++++++++++++++++++++++++++++++++ target/riscv/time_helper.h | 30 ++++++++++++ 8 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 target/riscv/time_helper.c create mode 100644 target/riscv/time_helper.h diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 26d44df44697..8ab36e82e190 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -23,6 +23,7 @@ #include "qemu/log.h" #include "cpu.h" #include "internals.h" +#include "time_helper.h" #include "exec/exec-all.h" #include "qapi/error.h" #include "qemu/error-report.h" @@ -101,6 +102,7 @@ static const struct isa_ext_data isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zhinxmin, true, PRIV_VERSION_1_12_0, ext_zhinxmin), ISA_EXT_DATA_ENTRY(smaia, true, PRIV_VERSION_1_12_0, ext_smaia), ISA_EXT_DATA_ENTRY(ssaia, true, PRIV_VERSION_1_12_0, ext_ssaia), + ISA_EXT_DATA_ENTRY(sstc, true, PRIV_VERSION_1_12_0, ext_sstc), ISA_EXT_DATA_ENTRY(svinval, true, PRIV_VERSION_1_12_0, ext_svinval), ISA_EXT_DATA_ENTRY(svnapot, true, PRIV_VERSION_1_12_0, ext_svnapot), ISA_EXT_DATA_ENTRY(svpbmt, true, PRIV_VERSION_1_12_0, ext_svpbmt), @@ -674,6 +676,12 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) set_resetvec(env, cpu->cfg.resetvec); +#ifndef CONFIG_USER_ONLY + if (cpu->cfg.ext_sstc) { + riscv_timer_init(cpu); + } +#endif /* CONFIG_USER_ONLY */ + /* Validate that MISA_MXL is set properly. */ switch (env->misa_mxl_max) { #ifdef TARGET_RISCV64 @@ -994,6 +1002,7 @@ static Property riscv_cpu_extensions[] = { DEFINE_PROP_BOOL("Zve64f", RISCVCPU, cfg.ext_zve64f, false), DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true), DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true), + DEFINE_PROP_BOOL("sstc", RISCVCPU, cfg.ext_sstc, true), DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec), DEFINE_PROP_STRING("vext_spec", RISCVCPU, cfg.vext_spec), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 53335def2336..d2529b757aab 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -308,6 +308,9 @@ struct CPUArchState { uint64_t mfromhost; uint64_t mtohost; + /* Sstc CSRs */ + uint64_t stimecmp; + /* physical memory protection */ pmp_table_t pmp_state; target_ulong mseccfg; @@ -361,6 +364,7 @@ struct CPUArchState { float_status fp_status; /* Fields from here on are preserved across CPU reset. */ + QEMUTimer *stimer; /* Internal timer for S-mode interrupt */ hwaddr kernel_addr; hwaddr fdt_addr; @@ -424,6 +428,7 @@ struct RISCVCPUConfig { bool ext_ifencei; bool ext_icsr; bool ext_zihintpause; + bool ext_sstc; bool ext_svinval; bool ext_svnapot; bool ext_svpbmt; diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 6be5a9e9f046..ac17cf1515c0 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -206,6 +206,10 @@ #define CSR_STVAL 0x143 #define CSR_SIP 0x144 +/* Sstc supervisor CSRs */ +#define CSR_STIMECMP 0x14D +#define CSR_STIMECMPH 0x15D + /* Supervisor Protection and Translation */ #define CSR_SPTBR 0x180 #define CSR_SATP 0x180 diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 3ddf309055f1..04b06a238921 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -22,6 +22,7 @@ #include "qemu/timer.h" #include "cpu.h" #include "pmu.h" +#include "time_helper.h" #include "qemu/main-loop.h" #include "exec/exec-all.h" #include "sysemu/cpu-timers.h" @@ -815,6 +816,81 @@ static RISCVException read_timeh(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException sstc(CPURISCVState *env, int csrno) +{ + CPUState *cs = env_cpu(env); + RISCVCPU *cpu = RISCV_CPU(cs); + + if (!cpu->cfg.ext_sstc || !env->rdtime_fn) { + return RISCV_EXCP_ILLEGAL_INST; + } + + if (env->priv == PRV_M) { + return RISCV_EXCP_NONE; + } + + /* + * No need of separate function for rv32 as menvcfg stores both menvcfg + * menvcfgh for RV32. + */ + if (!(get_field(env->mcounteren, COUNTEREN_TM) && + get_field(env->menvcfg, MENVCFG_STCE))) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return smode(env, csrno); +} + +static RISCVException sstc_32(CPURISCVState *env, int csrno) +{ + if (riscv_cpu_mxl(env) != MXL_RV32) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return sstc(env, csrno); +} + +static RISCVException read_stimecmp(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->stimecmp; + return RISCV_EXCP_NONE; +} + +static RISCVException read_stimecmph(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->stimecmp >> 32; + return RISCV_EXCP_NONE; +} + +static RISCVException write_stimecmp(CPURISCVState *env, int csrno, + target_ulong val) +{ + RISCVCPU *cpu = env_archcpu(env); + + if (riscv_cpu_mxl(env) == MXL_RV32) { + env->stimecmp = deposit64(env->stimecmp, 0, 32, (uint64_t)val); + } else { + env->stimecmp = val; + } + + riscv_timer_write_timecmp(cpu, env->stimer, env->stimecmp, 0, MIP_STIP); + + return RISCV_EXCP_NONE; +} + +static RISCVException write_stimecmph(CPURISCVState *env, int csrno, + target_ulong val) +{ + RISCVCPU *cpu = env_archcpu(env); + + env->stimecmp = deposit64(env->stimecmp, 32, 32, (uint64_t)val); + riscv_timer_write_timecmp(cpu, env->stimer, env->stimecmp, 0, MIP_STIP); + + return RISCV_EXCP_NONE; +} + /* Machine constants */ #define M_MODE_INTERRUPTS ((uint64_t)(MIP_MSIP | MIP_MTIP | MIP_MEIP)) @@ -1723,6 +1799,12 @@ static RISCVException rmw_mip64(CPURISCVState *env, int csrno, new_val |= env->external_seip * MIP_SEIP; } + if (cpu->cfg.ext_sstc && (env->priv == PRV_M) && + get_field(env->menvcfg, MENVCFG_STCE)) { + /* sstc extension forbids STIP & VSTIP to be writeable in mip */ + mask = mask & ~(MIP_STIP | MIP_VSTIP); + } + if (mask) { old_mip = riscv_cpu_update_mip(cpu, mask, (new_val & mask)); } else { @@ -3594,6 +3676,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_SCAUSE] = { "scause", smode, read_scause, write_scause }, [CSR_STVAL] = { "stval", smode, read_stval, write_stval }, [CSR_SIP] = { "sip", smode, NULL, NULL, rmw_sip }, + [CSR_STIMECMP] = { "stimecmp", sstc, read_stimecmp, write_stimecmp, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_STIMECMPH] = { "stimecmph", sstc_32, read_stimecmph, write_stimecmph, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Supervisor Protection and Translation */ [CSR_SATP] = { "satp", smode, read_satp, write_satp }, diff --git a/target/riscv/machine.c b/target/riscv/machine.c index b508b042cb73..622fface484e 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -359,6 +359,7 @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINTTL(env.mscratch, RISCVCPU), VMSTATE_UINT64(env.mfromhost, RISCVCPU), VMSTATE_UINT64(env.mtohost, RISCVCPU), + VMSTATE_UINT64(env.stimecmp, RISCVCPU), VMSTATE_END_OF_LIST() }, diff --git a/target/riscv/meson.build b/target/riscv/meson.build index 6b9435d69a3b..ba25164d7417 100644 --- a/target/riscv/meson.build +++ b/target/riscv/meson.build @@ -29,7 +29,8 @@ riscv_softmmu_ss.add(files( 'debug.c', 'monitor.c', 'machine.c', - 'pmu.c' + 'pmu.c', + 'time_helper.c' )) target_arch += {'riscv': riscv_ss} diff --git a/target/riscv/time_helper.c b/target/riscv/time_helper.c new file mode 100644 index 000000000000..f3fb5eac7b7b --- /dev/null +++ b/target/riscv/time_helper.c @@ -0,0 +1,98 @@ +/* + * RISC-V timer helper implementation. + * + * Copyright (c) 2022 Rivos 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/log.h" +#include "cpu_bits.h" +#include "time_helper.h" +#include "hw/intc/riscv_aclint.h" + +static void riscv_stimer_cb(void *opaque) +{ + RISCVCPU *cpu = opaque; + riscv_cpu_update_mip(cpu, MIP_STIP, BOOL_TO_MASK(1)); +} + +/* + * Called when timecmp is written to update the QEMU timer or immediately + * trigger timer interrupt if mtimecmp <= current timer value. + */ +void riscv_timer_write_timecmp(RISCVCPU *cpu, QEMUTimer *timer, + uint64_t timecmp, uint64_t delta, + uint32_t timer_irq) +{ + uint64_t diff, ns_diff, next; + CPURISCVState *env = &cpu->env; + RISCVAclintMTimerState *mtimer = env->rdtime_fn_arg; + uint32_t timebase_freq = mtimer->timebase_freq; + uint64_t rtc_r = env->rdtime_fn(env->rdtime_fn_arg) + delta; + + if (timecmp <= rtc_r) { + /* + * If we're setting an stimecmp value in the "past", + * immediately raise the timer interrupt + */ + riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(1)); + return; + } + + /* Clear the [V]STIP bit in mip */ + riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(0)); + + /* otherwise, set up the future timer interrupt */ + diff = timecmp - rtc_r; + /* back to ns (note args switched in muldiv64) */ + ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq); + + /* + * check if ns_diff overflowed and check if the addition would potentially + * overflow + */ + if ((NANOSECONDS_PER_SECOND > timebase_freq && ns_diff < diff) || + ns_diff > INT64_MAX) { + next = INT64_MAX; + } else { + /* + * as it is very unlikely qemu_clock_get_ns will return a value + * greater than INT64_MAX, no additional check is needed for an + * unsigned integer overflow. + */ + next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns_diff; + /* + * if ns_diff is INT64_MAX next may still be outside the range + * of a signed integer. + */ + next = MIN(next, INT64_MAX); + } + + timer_mod(timer, next); +} + +void riscv_timer_init(RISCVCPU *cpu) +{ + CPURISCVState *env; + + if (!cpu) { + return; + } + + env = &cpu->env; + env->stimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_stimer_cb, cpu); + env->stimecmp = 0; + +} diff --git a/target/riscv/time_helper.h b/target/riscv/time_helper.h new file mode 100644 index 000000000000..7b3cdcc35020 --- /dev/null +++ b/target/riscv/time_helper.h @@ -0,0 +1,30 @@ +/* + * RISC-V timer header file. + * + * Copyright (c) 2022 Rivos 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 . + */ + +#ifndef RISCV_TIME_HELPER_H +#define RISCV_TIME_HELPER_H + +#include "cpu.h" +#include "qemu/timer.h" + +void riscv_timer_write_timecmp(RISCVCPU *cpu, QEMUTimer *timer, + uint64_t timecmp, uint64_t delta, + uint32_t timer_irq); +void riscv_timer_init(RISCVCPU *cpu); + +#endif From 3ec0fe18a31fabfe999b480e4c21847ac0d51560 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Wed, 24 Aug 2022 15:13:57 -0700 Subject: [PATCH 39/44] target/riscv: Add vstimecmp support vstimecmp CSR allows the guest OS or to program the next guest timer interrupt directly. Thus, hypervisor no longer need to inject the timer interrupt to the guest if vstimecmp is used. This was ratified as a part of the Sstc extension. Reviewed-by: Alistair Francis Signed-off-by: Atish Patra Message-Id: <20220824221357.41070-4-atishp@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 4 ++ target/riscv/cpu_bits.h | 4 ++ target/riscv/cpu_helper.c | 11 +++-- target/riscv/csr.c | 88 ++++++++++++++++++++++++++++++++++++-- target/riscv/machine.c | 1 + target/riscv/time_helper.c | 16 +++++++ 6 files changed, 118 insertions(+), 6 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index d2529b757aab..d895a0af2c6d 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -311,6 +311,8 @@ struct CPUArchState { /* Sstc CSRs */ uint64_t stimecmp; + uint64_t vstimecmp; + /* physical memory protection */ pmp_table_t pmp_state; target_ulong mseccfg; @@ -365,6 +367,8 @@ struct CPUArchState { /* Fields from here on are preserved across CPU reset. */ QEMUTimer *stimer; /* Internal timer for S-mode interrupt */ + QEMUTimer *vstimer; /* Internal timer for VS-mode interrupt */ + bool vstime_irq; hwaddr kernel_addr; hwaddr fdt_addr; diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index ac17cf1515c0..095dab19f512 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -257,6 +257,10 @@ #define CSR_VSIP 0x244 #define CSR_VSATP 0x280 +/* Sstc virtual CSRs */ +#define CSR_VSTIMECMP 0x24D +#define CSR_VSTIMECMPH 0x25D + #define CSR_MTINST 0x34a #define CSR_MTVAL2 0x34b diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 05c0c8d7771b..719c5d5d0209 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -346,8 +346,9 @@ uint64_t riscv_cpu_all_pending(CPURISCVState *env) { uint32_t gein = get_field(env->hstatus, HSTATUS_VGEIN); uint64_t vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0; + uint64_t vstip = (env->vstime_irq) ? MIP_VSTIP : 0; - return (env->mip | vsgein) & env->mie; + return (env->mip | vsgein | vstip) & env->mie; } int riscv_cpu_mirq_pending(CPURISCVState *env) @@ -606,7 +607,7 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value) { CPURISCVState *env = &cpu->env; CPUState *cs = CPU(cpu); - uint64_t gein, vsgein = 0, old = env->mip; + uint64_t gein, vsgein = 0, vstip = 0, old = env->mip; bool locked = false; if (riscv_cpu_virt_enabled(env)) { @@ -614,6 +615,10 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value) vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0; } + /* No need to update mip for VSTIP */ + mask = ((mask == MIP_VSTIP) && env->vstime_irq) ? 0 : mask; + vstip = env->vstime_irq ? MIP_VSTIP : 0; + if (!qemu_mutex_iothread_locked()) { locked = true; qemu_mutex_lock_iothread(); @@ -621,7 +626,7 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value) env->mip = (env->mip & ~mask) | (value & mask); - if (env->mip | vsgein) { + if (env->mip | vsgein | vstip) { cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 04b06a238921..1a35ac48ccbe 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -820,6 +820,7 @@ static RISCVException sstc(CPURISCVState *env, int csrno) { CPUState *cs = env_cpu(env); RISCVCPU *cpu = RISCV_CPU(cs); + bool hmode_check = false; if (!cpu->cfg.ext_sstc || !env->rdtime_fn) { return RISCV_EXCP_ILLEGAL_INST; @@ -838,7 +839,18 @@ static RISCVException sstc(CPURISCVState *env, int csrno) return RISCV_EXCP_ILLEGAL_INST; } - return smode(env, csrno); + if (riscv_cpu_virt_enabled(env)) { + if (!(get_field(env->hcounteren, COUNTEREN_TM) & + get_field(env->henvcfg, HENVCFG_STCE))) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } + } + + if ((csrno == CSR_VSTIMECMP) || (csrno == CSR_VSTIMECMPH)) { + hmode_check = true; + } + + return hmode_check ? hmode(env, csrno) : smode(env, csrno); } static RISCVException sstc_32(CPURISCVState *env, int csrno) @@ -850,17 +862,72 @@ static RISCVException sstc_32(CPURISCVState *env, int csrno) return sstc(env, csrno); } +static RISCVException read_vstimecmp(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->vstimecmp; + + return RISCV_EXCP_NONE; +} + +static RISCVException read_vstimecmph(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->vstimecmp >> 32; + + return RISCV_EXCP_NONE; +} + +static RISCVException write_vstimecmp(CPURISCVState *env, int csrno, + target_ulong val) +{ + RISCVCPU *cpu = env_archcpu(env); + + if (riscv_cpu_mxl(env) == MXL_RV32) { + env->vstimecmp = deposit64(env->vstimecmp, 0, 32, (uint64_t)val); + } else { + env->vstimecmp = val; + } + + riscv_timer_write_timecmp(cpu, env->vstimer, env->vstimecmp, + env->htimedelta, MIP_VSTIP); + + return RISCV_EXCP_NONE; +} + +static RISCVException write_vstimecmph(CPURISCVState *env, int csrno, + target_ulong val) +{ + RISCVCPU *cpu = env_archcpu(env); + + env->vstimecmp = deposit64(env->vstimecmp, 32, 32, (uint64_t)val); + riscv_timer_write_timecmp(cpu, env->vstimer, env->vstimecmp, + env->htimedelta, MIP_VSTIP); + + return RISCV_EXCP_NONE; +} + static RISCVException read_stimecmp(CPURISCVState *env, int csrno, target_ulong *val) { - *val = env->stimecmp; + if (riscv_cpu_virt_enabled(env)) { + *val = env->vstimecmp; + } else { + *val = env->stimecmp; + } + return RISCV_EXCP_NONE; } static RISCVException read_stimecmph(CPURISCVState *env, int csrno, target_ulong *val) { - *val = env->stimecmp >> 32; + if (riscv_cpu_virt_enabled(env)) { + *val = env->vstimecmp >> 32; + } else { + *val = env->stimecmp >> 32; + } + return RISCV_EXCP_NONE; } @@ -869,6 +936,10 @@ static RISCVException write_stimecmp(CPURISCVState *env, int csrno, { RISCVCPU *cpu = env_archcpu(env); + if (riscv_cpu_virt_enabled(env)) { + return write_vstimecmp(env, csrno, val); + } + if (riscv_cpu_mxl(env) == MXL_RV32) { env->stimecmp = deposit64(env->stimecmp, 0, 32, (uint64_t)val); } else { @@ -885,6 +956,10 @@ static RISCVException write_stimecmph(CPURISCVState *env, int csrno, { RISCVCPU *cpu = env_archcpu(env); + if (riscv_cpu_virt_enabled(env)) { + return write_vstimecmph(env, csrno, val); + } + env->stimecmp = deposit64(env->stimecmp, 32, 32, (uint64_t)val); riscv_timer_write_timecmp(cpu, env->stimer, env->stimecmp, 0, MIP_STIP); @@ -1814,6 +1889,7 @@ static RISCVException rmw_mip64(CPURISCVState *env, int csrno, if (csrno != CSR_HVIP) { gin = get_field(env->hstatus, HSTATUS_VGEIN); old_mip |= (env->hgeip & ((target_ulong)1 << gin)) ? MIP_VSEIP : 0; + old_mip |= env->vstime_irq ? MIP_VSTIP : 0; } if (ret_val) { @@ -3680,6 +3756,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_STIMECMPH] = { "stimecmph", sstc_32, read_stimecmph, write_stimecmph, .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSTIMECMP] = { "vstimecmp", sstc, read_vstimecmp, + write_vstimecmp, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSTIMECMPH] = { "vstimecmph", sstc_32, read_vstimecmph, + write_vstimecmph, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Supervisor Protection and Translation */ [CSR_SATP] = { "satp", smode, read_satp, write_satp }, diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 622fface484e..4ba55705d147 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -92,6 +92,7 @@ static const VMStateDescription vmstate_hyper = { VMSTATE_UINTTL(env.hgeie, RISCVCPU), VMSTATE_UINTTL(env.hgeip, RISCVCPU), VMSTATE_UINT64(env.htimedelta, RISCVCPU), + VMSTATE_UINT64(env.vstimecmp, RISCVCPU), VMSTATE_UINTTL(env.hvictl, RISCVCPU), VMSTATE_UINT8_ARRAY(env.hviprio, RISCVCPU, 64), diff --git a/target/riscv/time_helper.c b/target/riscv/time_helper.c index f3fb5eac7b7b..8cce667dfd47 100644 --- a/target/riscv/time_helper.c +++ b/target/riscv/time_helper.c @@ -22,6 +22,14 @@ #include "time_helper.h" #include "hw/intc/riscv_aclint.h" +static void riscv_vstimer_cb(void *opaque) +{ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + env->vstime_irq = 1; + riscv_cpu_update_mip(cpu, MIP_VSTIP, BOOL_TO_MASK(1)); +} + static void riscv_stimer_cb(void *opaque) { RISCVCPU *cpu = opaque; @@ -47,10 +55,16 @@ void riscv_timer_write_timecmp(RISCVCPU *cpu, QEMUTimer *timer, * If we're setting an stimecmp value in the "past", * immediately raise the timer interrupt */ + if (timer_irq == MIP_VSTIP) { + env->vstime_irq = 1; + } riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(1)); return; } + if (timer_irq == MIP_VSTIP) { + env->vstime_irq = 0; + } /* Clear the [V]STIP bit in mip */ riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(0)); @@ -95,4 +109,6 @@ void riscv_timer_init(RISCVCPU *cpu) env->stimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_stimer_cb, cpu); env->stimecmp = 0; + env->vstimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_vstimer_cb, cpu); + env->vstimecmp = 0; } From 14664483457b21235be42fbfb534e5ea881508b8 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Wed, 24 Aug 2022 15:16:57 -0700 Subject: [PATCH 40/44] target/riscv: Add sscofpmf extension support The Sscofpmf ('Ss' for Privileged arch and Supervisor-level extensions, and 'cofpmf' for Count OverFlow and Privilege Mode Filtering) extension allows the perf to handle overflow interrupts and filtering support. This patch provides a framework for programmable counters to leverage the extension. As the extension doesn't have any provision for the overflow bit for fixed counters, the fixed events can also be monitoring using programmable counters. The underlying counters for cycle and instruction counters are always running. Thus, a separate timer device is programmed to handle the overflow. Tested-by: Heiko Stuebner Reviewed-by: Alistair Francis Signed-off-by: Atish Patra Signed-off-by: Atish Patra Message-Id: <20220824221701.41932-2-atishp@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 12 ++ target/riscv/cpu.h | 25 +++ target/riscv/cpu_bits.h | 55 ++++++ target/riscv/csr.c | 166 +++++++++++++++++- target/riscv/machine.c | 1 + target/riscv/pmu.c | 368 +++++++++++++++++++++++++++++++++++++++- target/riscv/pmu.h | 7 + 7 files changed, 623 insertions(+), 11 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 8ab36e82e190..aee14a239af8 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -22,6 +22,7 @@ #include "qemu/ctype.h" #include "qemu/log.h" #include "cpu.h" +#include "pmu.h" #include "internals.h" #include "time_helper.h" #include "exec/exec-all.h" @@ -102,6 +103,7 @@ static const struct isa_ext_data isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zhinxmin, true, PRIV_VERSION_1_12_0, ext_zhinxmin), ISA_EXT_DATA_ENTRY(smaia, true, PRIV_VERSION_1_12_0, ext_smaia), ISA_EXT_DATA_ENTRY(ssaia, true, PRIV_VERSION_1_12_0, ext_ssaia), + ISA_EXT_DATA_ENTRY(sscofpmf, true, PRIV_VERSION_1_12_0, ext_sscofpmf), ISA_EXT_DATA_ENTRY(sstc, true, PRIV_VERSION_1_12_0, ext_sstc), ISA_EXT_DATA_ENTRY(svinval, true, PRIV_VERSION_1_12_0, ext_svinval), ISA_EXT_DATA_ENTRY(svnapot, true, PRIV_VERSION_1_12_0, ext_svnapot), @@ -889,6 +891,15 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) set_misa(env, env->misa_mxl, ext); } +#ifndef CONFIG_USER_ONLY + if (cpu->cfg.pmu_num) { + if (!riscv_pmu_init(cpu, cpu->cfg.pmu_num) && cpu->cfg.ext_sscofpmf) { + cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + riscv_pmu_timer_cb, cpu); + } + } +#endif + riscv_cpu_register_gdb_regs_for_features(cs); qemu_init_vcpu(cs); @@ -993,6 +1004,7 @@ static Property riscv_cpu_extensions[] = { DEFINE_PROP_BOOL("v", RISCVCPU, cfg.ext_v, false), DEFINE_PROP_BOOL("h", RISCVCPU, cfg.ext_h, true), DEFINE_PROP_UINT8("pmu-num", RISCVCPU, cfg.pmu_num, 16), + DEFINE_PROP_BOOL("sscofpmf", RISCVCPU, cfg.ext_sscofpmf, false), DEFINE_PROP_BOOL("Zifencei", RISCVCPU, cfg.ext_ifencei, true), DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true), DEFINE_PROP_BOOL("Zihintpause", RISCVCPU, cfg.ext_zihintpause, true), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index d895a0af2c6d..06751e1e3e18 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -136,6 +136,8 @@ typedef struct PMUCTRState { /* Snapshort value of a counter in RV32 */ target_ulong mhpmcounterh_prev; bool started; + /* Value beyond UINT32_MAX/UINT64_MAX before overflow interrupt trigger */ + target_ulong irq_overflow_left; } PMUCTRState; struct CPUArchState { @@ -301,6 +303,9 @@ struct CPUArchState { /* PMU event selector configured values. First three are unused*/ target_ulong mhpmevent_val[RV_MAX_MHPMEVENTS]; + /* PMU event selector configured values for RV32*/ + target_ulong mhpmeventh_val[RV_MAX_MHPMEVENTS]; + target_ulong sscratch; target_ulong mscratch; @@ -447,6 +452,7 @@ struct RISCVCPUConfig { bool ext_zmmul; bool ext_smaia; bool ext_ssaia; + bool ext_sscofpmf; bool rvv_ta_all_1s; bool rvv_ma_all_1s; @@ -493,6 +499,12 @@ struct ArchCPU { /* Configuration Settings */ RISCVCPUConfig cfg; + + QEMUTimer *pmu_timer; + /* A bitmask of Available programmable counters */ + uint32_t pmu_avail_ctrs; + /* Mapping of events to counters */ + GHashTable *pmu_event_ctr_map; }; static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext) @@ -753,6 +765,19 @@ enum { CSR_TABLE_SIZE = 0x1000 }; +/** + * The event id are encoded based on the encoding specified in the + * SBI specification v0.3 + */ + +enum riscv_pmu_event_idx { + RISCV_PMU_EVENT_HW_CPU_CYCLES = 0x01, + RISCV_PMU_EVENT_HW_INSTRUCTIONS = 0x02, + RISCV_PMU_EVENT_CACHE_DTLB_READ_MISS = 0x10019, + RISCV_PMU_EVENT_CACHE_DTLB_WRITE_MISS = 0x1001B, + RISCV_PMU_EVENT_CACHE_ITLB_PREFETCH_MISS = 0x10021, +}; + /* CSR function table */ extern riscv_csr_operations csr_ops[CSR_TABLE_SIZE]; diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 095dab19f512..7be12cac2ee6 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -390,6 +390,37 @@ #define CSR_MHPMEVENT29 0x33d #define CSR_MHPMEVENT30 0x33e #define CSR_MHPMEVENT31 0x33f + +#define CSR_MHPMEVENT3H 0x723 +#define CSR_MHPMEVENT4H 0x724 +#define CSR_MHPMEVENT5H 0x725 +#define CSR_MHPMEVENT6H 0x726 +#define CSR_MHPMEVENT7H 0x727 +#define CSR_MHPMEVENT8H 0x728 +#define CSR_MHPMEVENT9H 0x729 +#define CSR_MHPMEVENT10H 0x72a +#define CSR_MHPMEVENT11H 0x72b +#define CSR_MHPMEVENT12H 0x72c +#define CSR_MHPMEVENT13H 0x72d +#define CSR_MHPMEVENT14H 0x72e +#define CSR_MHPMEVENT15H 0x72f +#define CSR_MHPMEVENT16H 0x730 +#define CSR_MHPMEVENT17H 0x731 +#define CSR_MHPMEVENT18H 0x732 +#define CSR_MHPMEVENT19H 0x733 +#define CSR_MHPMEVENT20H 0x734 +#define CSR_MHPMEVENT21H 0x735 +#define CSR_MHPMEVENT22H 0x736 +#define CSR_MHPMEVENT23H 0x737 +#define CSR_MHPMEVENT24H 0x738 +#define CSR_MHPMEVENT25H 0x739 +#define CSR_MHPMEVENT26H 0x73a +#define CSR_MHPMEVENT27H 0x73b +#define CSR_MHPMEVENT28H 0x73c +#define CSR_MHPMEVENT29H 0x73d +#define CSR_MHPMEVENT30H 0x73e +#define CSR_MHPMEVENT31H 0x73f + #define CSR_MHPMCOUNTER3H 0xb83 #define CSR_MHPMCOUNTER4H 0xb84 #define CSR_MHPMCOUNTER5H 0xb85 @@ -451,6 +482,7 @@ #define CSR_VSMTE 0x2c0 #define CSR_VSPMMASK 0x2c1 #define CSR_VSPMBASE 0x2c2 +#define CSR_SCOUNTOVF 0xda0 /* Crypto Extension */ #define CSR_SEED 0x015 @@ -628,6 +660,7 @@ typedef enum RISCVException { #define IRQ_VS_EXT 10 #define IRQ_M_EXT 11 #define IRQ_S_GEXT 12 +#define IRQ_PMU_OVF 13 #define IRQ_LOCAL_MAX 16 #define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS - 1) @@ -645,11 +678,13 @@ typedef enum RISCVException { #define MIP_VSEIP (1 << IRQ_VS_EXT) #define MIP_MEIP (1 << IRQ_M_EXT) #define MIP_SGEIP (1 << IRQ_S_GEXT) +#define MIP_LCOFIP (1 << IRQ_PMU_OVF) /* sip masks */ #define SIP_SSIP MIP_SSIP #define SIP_STIP MIP_STIP #define SIP_SEIP MIP_SEIP +#define SIP_LCOFIP MIP_LCOFIP /* MIE masks */ #define MIE_SEIE (1 << IRQ_S_EXT) @@ -803,4 +838,24 @@ typedef enum RISCVException { #define SEED_OPST_WAIT (0b01 << 30) #define SEED_OPST_ES16 (0b10 << 30) #define SEED_OPST_DEAD (0b11 << 30) +/* PMU related bits */ +#define MIE_LCOFIE (1 << IRQ_PMU_OVF) + +#define MHPMEVENT_BIT_OF BIT_ULL(63) +#define MHPMEVENTH_BIT_OF BIT(31) +#define MHPMEVENT_BIT_MINH BIT_ULL(62) +#define MHPMEVENTH_BIT_MINH BIT(30) +#define MHPMEVENT_BIT_SINH BIT_ULL(61) +#define MHPMEVENTH_BIT_SINH BIT(29) +#define MHPMEVENT_BIT_UINH BIT_ULL(60) +#define MHPMEVENTH_BIT_UINH BIT(28) +#define MHPMEVENT_BIT_VSINH BIT_ULL(59) +#define MHPMEVENTH_BIT_VSINH BIT(27) +#define MHPMEVENT_BIT_VUINH BIT_ULL(58) +#define MHPMEVENTH_BIT_VUINH BIT(26) + +#define MHPMEVENT_SSCOF_MASK _ULL(0xFFFF000000000000) +#define MHPMEVENT_IDX_MASK 0xFFFFF +#define MHPMEVENT_SSCOF_RESVD 16 + #endif diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 1a35ac48ccbe..888ddfc4dd4b 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -75,7 +75,7 @@ static RISCVException ctr(CPURISCVState *env, int csrno) CPUState *cs = env_cpu(env); RISCVCPU *cpu = RISCV_CPU(cs); int ctr_index; - int base_csrno = CSR_HPMCOUNTER3; + int base_csrno = CSR_CYCLE; bool rv32 = riscv_cpu_mxl(env) == MXL_RV32 ? true : false; if (rv32 && csrno >= CSR_CYCLEH) { @@ -84,11 +84,18 @@ static RISCVException ctr(CPURISCVState *env, int csrno) } ctr_index = csrno - base_csrno; - if (!cpu->cfg.pmu_num || ctr_index >= (cpu->cfg.pmu_num)) { + if ((csrno >= CSR_CYCLE && csrno <= CSR_INSTRET) || + (csrno >= CSR_CYCLEH && csrno <= CSR_INSTRETH)) { + goto skip_ext_pmu_check; + } + + if ((!cpu->cfg.pmu_num || !(cpu->pmu_avail_ctrs & BIT(ctr_index)))) { /* No counter is enabled in PMU or the counter is out of range */ return RISCV_EXCP_ILLEGAL_INST; } +skip_ext_pmu_check: + if (env->priv == PRV_S) { switch (csrno) { case CSR_CYCLE: @@ -107,7 +114,6 @@ static RISCVException ctr(CPURISCVState *env, int csrno) } break; case CSR_HPMCOUNTER3...CSR_HPMCOUNTER31: - ctr_index = csrno - CSR_CYCLE; if (!get_field(env->mcounteren, 1 << ctr_index)) { return RISCV_EXCP_ILLEGAL_INST; } @@ -131,7 +137,6 @@ static RISCVException ctr(CPURISCVState *env, int csrno) } break; case CSR_HPMCOUNTER3H...CSR_HPMCOUNTER31H: - ctr_index = csrno - CSR_CYCLEH; if (!get_field(env->mcounteren, 1 << ctr_index)) { return RISCV_EXCP_ILLEGAL_INST; } @@ -161,7 +166,6 @@ static RISCVException ctr(CPURISCVState *env, int csrno) } break; case CSR_HPMCOUNTER3...CSR_HPMCOUNTER31: - ctr_index = csrno - CSR_CYCLE; if (!get_field(env->hcounteren, 1 << ctr_index) && get_field(env->mcounteren, 1 << ctr_index)) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; @@ -189,7 +193,6 @@ static RISCVException ctr(CPURISCVState *env, int csrno) } break; case CSR_HPMCOUNTER3H...CSR_HPMCOUNTER31H: - ctr_index = csrno - CSR_CYCLEH; if (!get_field(env->hcounteren, 1 << ctr_index) && get_field(env->mcounteren, 1 << ctr_index)) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; @@ -241,6 +244,18 @@ static RISCVException mctr32(CPURISCVState *env, int csrno) return mctr(env, csrno); } +static RISCVException sscofpmf(CPURISCVState *env, int csrno) +{ + CPUState *cs = env_cpu(env); + RISCVCPU *cpu = RISCV_CPU(cs); + + if (!cpu->cfg.ext_sscofpmf) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return RISCV_EXCP_NONE; +} + static RISCVException any(CPURISCVState *env, int csrno) { return RISCV_EXCP_NONE; @@ -683,9 +698,39 @@ static int read_mhpmevent(CPURISCVState *env, int csrno, target_ulong *val) static int write_mhpmevent(CPURISCVState *env, int csrno, target_ulong val) { int evt_index = csrno - CSR_MCOUNTINHIBIT; + uint64_t mhpmevt_val = val; env->mhpmevent_val[evt_index] = val; + if (riscv_cpu_mxl(env) == MXL_RV32) { + mhpmevt_val = mhpmevt_val | + ((uint64_t)env->mhpmeventh_val[evt_index] << 32); + } + riscv_pmu_update_event_map(env, mhpmevt_val, evt_index); + + return RISCV_EXCP_NONE; +} + +static int read_mhpmeventh(CPURISCVState *env, int csrno, target_ulong *val) +{ + int evt_index = csrno - CSR_MHPMEVENT3H + 3; + + *val = env->mhpmeventh_val[evt_index]; + + return RISCV_EXCP_NONE; +} + +static int write_mhpmeventh(CPURISCVState *env, int csrno, target_ulong val) +{ + int evt_index = csrno - CSR_MHPMEVENT3H + 3; + uint64_t mhpmevth_val = val; + uint64_t mhpmevt_val = env->mhpmevent_val[evt_index]; + + mhpmevt_val = mhpmevt_val | (mhpmevth_val << 32); + env->mhpmeventh_val[evt_index] = val; + + riscv_pmu_update_event_map(env, mhpmevt_val, evt_index); + return RISCV_EXCP_NONE; } @@ -693,12 +738,20 @@ static int write_mhpmcounter(CPURISCVState *env, int csrno, target_ulong val) { int ctr_idx = csrno - CSR_MCYCLE; PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + uint64_t mhpmctr_val = val; counter->mhpmcounter_val = val; if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { counter->mhpmcounter_prev = get_ticks(false); - } else { + if (ctr_idx > 2) { + if (riscv_cpu_mxl(env) == MXL_RV32) { + mhpmctr_val = mhpmctr_val | + ((uint64_t)counter->mhpmcounterh_val << 32); + } + riscv_pmu_setup_timer(env, mhpmctr_val, ctr_idx); + } + } else { /* Other counters can keep incrementing from the given value */ counter->mhpmcounter_prev = val; } @@ -710,11 +763,17 @@ static int write_mhpmcounterh(CPURISCVState *env, int csrno, target_ulong val) { int ctr_idx = csrno - CSR_MCYCLEH; PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + uint64_t mhpmctr_val = counter->mhpmcounter_val; + uint64_t mhpmctrh_val = val; counter->mhpmcounterh_val = val; + mhpmctr_val = mhpmctr_val | (mhpmctrh_val << 32); if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { counter->mhpmcounterh_prev = get_ticks(true); + if (ctr_idx > 2) { + riscv_pmu_setup_timer(env, mhpmctr_val, ctr_idx); + } } else { counter->mhpmcounterh_prev = val; } @@ -790,6 +849,32 @@ static int read_hpmcounterh(CPURISCVState *env, int csrno, target_ulong *val) return riscv_pmu_read_ctr(env, val, true, ctr_index); } +static int read_scountovf(CPURISCVState *env, int csrno, target_ulong *val) +{ + int mhpmevt_start = CSR_MHPMEVENT3 - CSR_MCOUNTINHIBIT; + int i; + *val = 0; + target_ulong *mhpm_evt_val; + uint64_t of_bit_mask; + + if (riscv_cpu_mxl(env) == MXL_RV32) { + mhpm_evt_val = env->mhpmeventh_val; + of_bit_mask = MHPMEVENTH_BIT_OF; + } else { + mhpm_evt_val = env->mhpmevent_val; + of_bit_mask = MHPMEVENT_BIT_OF; + } + + for (i = mhpmevt_start; i < RV_MAX_MHPMEVENTS; i++) { + if ((get_field(env->mcounteren, BIT(i))) && + (mhpm_evt_val[i] & of_bit_mask)) { + *val |= BIT(i); + } + } + + return RISCV_EXCP_NONE; +} + static RISCVException read_time(CPURISCVState *env, int csrno, target_ulong *val) { @@ -969,7 +1054,8 @@ static RISCVException write_stimecmph(CPURISCVState *env, int csrno, /* Machine constants */ #define M_MODE_INTERRUPTS ((uint64_t)(MIP_MSIP | MIP_MTIP | MIP_MEIP)) -#define S_MODE_INTERRUPTS ((uint64_t)(MIP_SSIP | MIP_STIP | MIP_SEIP)) +#define S_MODE_INTERRUPTS ((uint64_t)(MIP_SSIP | MIP_STIP | MIP_SEIP | \ + MIP_LCOFIP)) #define VS_MODE_INTERRUPTS ((uint64_t)(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP)) #define HS_MODE_INTERRUPTS ((uint64_t)(MIP_SGEIP | VS_MODE_INTERRUPTS)) @@ -1010,7 +1096,8 @@ static const target_ulong vs_delegable_excps = DELEGABLE_EXCPS & static const target_ulong sstatus_v1_10_mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS | SSTATUS_SUM | SSTATUS_MXR | SSTATUS_VS; -static const target_ulong sip_writable_mask = SIP_SSIP | MIP_USIP | MIP_UEIP; +static const target_ulong sip_writable_mask = SIP_SSIP | MIP_USIP | MIP_UEIP | + SIP_LCOFIP; static const target_ulong hip_writable_mask = MIP_VSSIP; static const target_ulong hvip_writable_mask = MIP_VSSIP | MIP_VSTIP | MIP_VSEIP; static const target_ulong vsip_writable_mask = MIP_VSSIP; @@ -4071,6 +4158,65 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MHPMEVENT31] = { "mhpmevent31", any, read_mhpmevent, write_mhpmevent }, + [CSR_MHPMEVENT3H] = { "mhpmevent3h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT4H] = { "mhpmevent4h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT5H] = { "mhpmevent5h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT6H] = { "mhpmevent6h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT7H] = { "mhpmevent7h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT8H] = { "mhpmevent8h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT9H] = { "mhpmevent9h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT10H] = { "mhpmevent10h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT11H] = { "mhpmevent11h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT12H] = { "mhpmevent12h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT13H] = { "mhpmevent13h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT14H] = { "mhpmevent14h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT15H] = { "mhpmevent15h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT16H] = { "mhpmevent16h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT17H] = { "mhpmevent17h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT18H] = { "mhpmevent18h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT19H] = { "mhpmevent19h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT20H] = { "mhpmevent20h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT21H] = { "mhpmevent21h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT22H] = { "mhpmevent22h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT23H] = { "mhpmevent23h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT24H] = { "mhpmevent24h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT25H] = { "mhpmevent25h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT26H] = { "mhpmevent26h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT27H] = { "mhpmevent27h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT28H] = { "mhpmevent28h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT29H] = { "mhpmevent29h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT30H] = { "mhpmevent30h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_MHPMEVENT31H] = { "mhpmevent31h", sscofpmf, read_mhpmeventh, + write_mhpmeventh }, + [CSR_HPMCOUNTER3H] = { "hpmcounter3h", ctr32, read_hpmcounterh }, [CSR_HPMCOUNTER4H] = { "hpmcounter4h", ctr32, read_hpmcounterh }, [CSR_HPMCOUNTER5H] = { "hpmcounter5h", ctr32, read_hpmcounterh }, @@ -4159,5 +4305,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { write_mhpmcounterh }, [CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", mctr32, read_hpmcounterh, write_mhpmcounterh }, + [CSR_SCOUNTOVF] = { "scountovf", sscofpmf, read_scountovf }, + #endif /* !CONFIG_USER_ONLY */ }; diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 4ba55705d147..41098f6ad03a 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -356,6 +356,7 @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_STRUCT_ARRAY(env.pmu_ctrs, RISCVCPU, RV_MAX_MHPMCOUNTERS, 0, vmstate_pmu_ctr_state, PMUCTRState), VMSTATE_UINTTL_ARRAY(env.mhpmevent_val, RISCVCPU, RV_MAX_MHPMEVENTS), + VMSTATE_UINTTL_ARRAY(env.mhpmeventh_val, RISCVCPU, RV_MAX_MHPMEVENTS), VMSTATE_UINTTL(env.sscratch, RISCVCPU), VMSTATE_UINTTL(env.mscratch, RISCVCPU), VMSTATE_UINT64(env.mfromhost, RISCVCPU), diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c index 000fe8da45ef..a5f504e53c88 100644 --- a/target/riscv/pmu.c +++ b/target/riscv/pmu.c @@ -19,14 +19,378 @@ #include "qemu/osdep.h" #include "cpu.h" #include "pmu.h" +#include "sysemu/cpu-timers.h" + +#define RISCV_TIMEBASE_FREQ 1000000000 /* 1Ghz */ +#define MAKE_32BIT_MASK(shift, length) \ + (((uint32_t)(~0UL) >> (32 - (length))) << (shift)) + +static bool riscv_pmu_counter_valid(RISCVCPU *cpu, uint32_t ctr_idx) +{ + if (ctr_idx < 3 || ctr_idx >= RV_MAX_MHPMCOUNTERS || + !(cpu->pmu_avail_ctrs & BIT(ctr_idx))) { + return false; + } else { + return true; + } +} + +static bool riscv_pmu_counter_enabled(RISCVCPU *cpu, uint32_t ctr_idx) +{ + CPURISCVState *env = &cpu->env; + + if (riscv_pmu_counter_valid(cpu, ctr_idx) && + !get_field(env->mcountinhibit, BIT(ctr_idx))) { + return true; + } else { + return false; + } +} + +static int riscv_pmu_incr_ctr_rv32(RISCVCPU *cpu, uint32_t ctr_idx) +{ + CPURISCVState *env = &cpu->env; + target_ulong max_val = UINT32_MAX; + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + bool virt_on = riscv_cpu_virt_enabled(env); + + /* Privilege mode filtering */ + if ((env->priv == PRV_M && + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_MINH)) || + (env->priv == PRV_S && virt_on && + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_VSINH)) || + (env->priv == PRV_U && virt_on && + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_VUINH)) || + (env->priv == PRV_S && !virt_on && + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_SINH)) || + (env->priv == PRV_U && !virt_on && + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_UINH))) { + return 0; + } + + /* Handle the overflow scenario */ + if (counter->mhpmcounter_val == max_val) { + if (counter->mhpmcounterh_val == max_val) { + counter->mhpmcounter_val = 0; + counter->mhpmcounterh_val = 0; + /* Generate interrupt only if OF bit is clear */ + if (!(env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_OF)) { + env->mhpmeventh_val[ctr_idx] |= MHPMEVENTH_BIT_OF; + riscv_cpu_update_mip(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); + } + } else { + counter->mhpmcounterh_val++; + } + } else { + counter->mhpmcounter_val++; + } + + return 0; +} + +static int riscv_pmu_incr_ctr_rv64(RISCVCPU *cpu, uint32_t ctr_idx) +{ + CPURISCVState *env = &cpu->env; + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + uint64_t max_val = UINT64_MAX; + bool virt_on = riscv_cpu_virt_enabled(env); + + /* Privilege mode filtering */ + if ((env->priv == PRV_M && + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_MINH)) || + (env->priv == PRV_S && virt_on && + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_VSINH)) || + (env->priv == PRV_U && virt_on && + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_VUINH)) || + (env->priv == PRV_S && !virt_on && + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_SINH)) || + (env->priv == PRV_U && !virt_on && + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_UINH))) { + return 0; + } + + /* Handle the overflow scenario */ + if (counter->mhpmcounter_val == max_val) { + counter->mhpmcounter_val = 0; + /* Generate interrupt only if OF bit is clear */ + if (!(env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_OF)) { + env->mhpmevent_val[ctr_idx] |= MHPMEVENT_BIT_OF; + riscv_cpu_update_mip(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); + } + } else { + counter->mhpmcounter_val++; + } + return 0; +} + +int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx) +{ + uint32_t ctr_idx; + int ret; + CPURISCVState *env = &cpu->env; + gpointer value; + + if (!cpu->cfg.pmu_num) { + return 0; + } + value = g_hash_table_lookup(cpu->pmu_event_ctr_map, + GUINT_TO_POINTER(event_idx)); + if (!value) { + return -1; + } + + ctr_idx = GPOINTER_TO_UINT(value); + if (!riscv_pmu_counter_enabled(cpu, ctr_idx) || + get_field(env->mcountinhibit, BIT(ctr_idx))) { + return -1; + } + + if (riscv_cpu_mxl(env) == MXL_RV32) { + ret = riscv_pmu_incr_ctr_rv32(cpu, ctr_idx); + } else { + ret = riscv_pmu_incr_ctr_rv64(cpu, ctr_idx); + } + + return ret; +} bool riscv_pmu_ctr_monitor_instructions(CPURISCVState *env, uint32_t target_ctr) { - return (target_ctr == 0) ? true : false; + RISCVCPU *cpu; + uint32_t event_idx; + uint32_t ctr_idx; + + /* Fixed instret counter */ + if (target_ctr == 2) { + return true; + } + + cpu = RISCV_CPU(env_cpu(env)); + if (!cpu->pmu_event_ctr_map) { + return false; + } + + event_idx = RISCV_PMU_EVENT_HW_INSTRUCTIONS; + ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, + GUINT_TO_POINTER(event_idx))); + if (!ctr_idx) { + return false; + } + + return target_ctr == ctr_idx ? true : false; } bool riscv_pmu_ctr_monitor_cycles(CPURISCVState *env, uint32_t target_ctr) { - return (target_ctr == 2) ? true : false; + RISCVCPU *cpu; + uint32_t event_idx; + uint32_t ctr_idx; + + /* Fixed mcycle counter */ + if (target_ctr == 0) { + return true; + } + + cpu = RISCV_CPU(env_cpu(env)); + if (!cpu->pmu_event_ctr_map) { + return false; + } + + event_idx = RISCV_PMU_EVENT_HW_CPU_CYCLES; + ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, + GUINT_TO_POINTER(event_idx))); + + /* Counter zero is not used for event_ctr_map */ + if (!ctr_idx) { + return false; + } + + return (target_ctr == ctr_idx) ? true : false; +} + +static gboolean pmu_remove_event_map(gpointer key, gpointer value, + gpointer udata) +{ + return (GPOINTER_TO_UINT(value) == GPOINTER_TO_UINT(udata)) ? true : false; +} + +static int64_t pmu_icount_ticks_to_ns(int64_t value) +{ + int64_t ret = 0; + + if (icount_enabled()) { + ret = icount_to_ns(value); + } else { + ret = (NANOSECONDS_PER_SECOND / RISCV_TIMEBASE_FREQ) * value; + } + + return ret; +} + +int riscv_pmu_update_event_map(CPURISCVState *env, uint64_t value, + uint32_t ctr_idx) +{ + uint32_t event_idx; + RISCVCPU *cpu = RISCV_CPU(env_cpu(env)); + + if (!riscv_pmu_counter_valid(cpu, ctr_idx) || !cpu->pmu_event_ctr_map) { + return -1; + } + + /* + * Expected mhpmevent value is zero for reset case. Remove the current + * mapping. + */ + if (!value) { + g_hash_table_foreach_remove(cpu->pmu_event_ctr_map, + pmu_remove_event_map, + GUINT_TO_POINTER(ctr_idx)); + return 0; + } + + event_idx = value & MHPMEVENT_IDX_MASK; + if (g_hash_table_lookup(cpu->pmu_event_ctr_map, + GUINT_TO_POINTER(event_idx))) { + return 0; + } + + switch (event_idx) { + case RISCV_PMU_EVENT_HW_CPU_CYCLES: + case RISCV_PMU_EVENT_HW_INSTRUCTIONS: + case RISCV_PMU_EVENT_CACHE_DTLB_READ_MISS: + case RISCV_PMU_EVENT_CACHE_DTLB_WRITE_MISS: + case RISCV_PMU_EVENT_CACHE_ITLB_PREFETCH_MISS: + break; + default: + /* We don't support any raw events right now */ + return -1; + } + g_hash_table_insert(cpu->pmu_event_ctr_map, GUINT_TO_POINTER(event_idx), + GUINT_TO_POINTER(ctr_idx)); + + return 0; +} + +static void pmu_timer_trigger_irq(RISCVCPU *cpu, + enum riscv_pmu_event_idx evt_idx) +{ + uint32_t ctr_idx; + CPURISCVState *env = &cpu->env; + PMUCTRState *counter; + target_ulong *mhpmevent_val; + uint64_t of_bit_mask; + int64_t irq_trigger_at; + + if (evt_idx != RISCV_PMU_EVENT_HW_CPU_CYCLES && + evt_idx != RISCV_PMU_EVENT_HW_INSTRUCTIONS) { + return; + } + + ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, + GUINT_TO_POINTER(evt_idx))); + if (!riscv_pmu_counter_enabled(cpu, ctr_idx)) { + return; + } + + if (riscv_cpu_mxl(env) == MXL_RV32) { + mhpmevent_val = &env->mhpmeventh_val[ctr_idx]; + of_bit_mask = MHPMEVENTH_BIT_OF; + } else { + mhpmevent_val = &env->mhpmevent_val[ctr_idx]; + of_bit_mask = MHPMEVENT_BIT_OF; + } + + counter = &env->pmu_ctrs[ctr_idx]; + if (counter->irq_overflow_left > 0) { + irq_trigger_at = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + counter->irq_overflow_left; + timer_mod_anticipate_ns(cpu->pmu_timer, irq_trigger_at); + counter->irq_overflow_left = 0; + return; + } + + if (cpu->pmu_avail_ctrs & BIT(ctr_idx)) { + /* Generate interrupt only if OF bit is clear */ + if (!(*mhpmevent_val & of_bit_mask)) { + *mhpmevent_val |= of_bit_mask; + riscv_cpu_update_mip(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); + } + } +} + +/* Timer callback for instret and cycle counter overflow */ +void riscv_pmu_timer_cb(void *priv) +{ + RISCVCPU *cpu = priv; + + /* Timer event was triggered only for these events */ + pmu_timer_trigger_irq(cpu, RISCV_PMU_EVENT_HW_CPU_CYCLES); + pmu_timer_trigger_irq(cpu, RISCV_PMU_EVENT_HW_INSTRUCTIONS); +} + +int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx) +{ + uint64_t overflow_delta, overflow_at; + int64_t overflow_ns, overflow_left = 0; + RISCVCPU *cpu = RISCV_CPU(env_cpu(env)); + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + + if (!riscv_pmu_counter_valid(cpu, ctr_idx) || !cpu->cfg.ext_sscofpmf) { + return -1; + } + + if (value) { + overflow_delta = UINT64_MAX - value + 1; + } else { + overflow_delta = UINT64_MAX; + } + + /* + * QEMU supports only int64_t timers while RISC-V counters are uint64_t. + * Compute the leftover and save it so that it can be reprogrammed again + * when timer expires. + */ + if (overflow_delta > INT64_MAX) { + overflow_left = overflow_delta - INT64_MAX; + } + + if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || + riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { + overflow_ns = pmu_icount_ticks_to_ns((int64_t)overflow_delta); + overflow_left = pmu_icount_ticks_to_ns(overflow_left) ; + } else { + return -1; + } + overflow_at = (uint64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + overflow_ns; + + if (overflow_at > INT64_MAX) { + overflow_left += overflow_at - INT64_MAX; + counter->irq_overflow_left = overflow_left; + overflow_at = INT64_MAX; + } + timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); + + return 0; +} + + +int riscv_pmu_init(RISCVCPU *cpu, int num_counters) +{ + if (num_counters > (RV_MAX_MHPMCOUNTERS - 3)) { + return -1; + } + + cpu->pmu_event_ctr_map = g_hash_table_new(g_direct_hash, g_direct_equal); + if (!cpu->pmu_event_ctr_map) { + /* PMU support can not be enabled */ + qemu_log_mask(LOG_UNIMP, "PMU events can't be supported\n"); + cpu->cfg.pmu_num = 0; + return -1; + } + + /* Create a bitmask of available programmable counters */ + cpu->pmu_avail_ctrs = MAKE_32BIT_MASK(3, num_counters); + + return 0; } diff --git a/target/riscv/pmu.h b/target/riscv/pmu.h index 58a5bc3a4089..036653627f78 100644 --- a/target/riscv/pmu.h +++ b/target/riscv/pmu.h @@ -26,3 +26,10 @@ bool riscv_pmu_ctr_monitor_instructions(CPURISCVState *env, uint32_t target_ctr); bool riscv_pmu_ctr_monitor_cycles(CPURISCVState *env, uint32_t target_ctr); +void riscv_pmu_timer_cb(void *priv); +int riscv_pmu_init(RISCVCPU *cpu, int num_counters); +int riscv_pmu_update_event_map(CPURISCVState *env, uint64_t value, + uint32_t ctr_idx); +int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx); +int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, + uint32_t ctr_idx); From ade445ef85aa0cf711a311132cb458f11f6a6d12 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Wed, 24 Aug 2022 15:16:58 -0700 Subject: [PATCH 41/44] target/riscv: Simplify counter predicate function All the hpmcounters and the fixed counters (CY, IR, TM) can be represented as a unified counter. Thus, the predicate function doesn't need handle each case separately. Simplify the predicate function so that we just handle things differently between RV32/RV64 and S/HS mode. Reviewed-by: Bin Meng Acked-by: Alistair Francis Signed-off-by: Atish Patra Message-Id: <20220824221701.41932-3-atishp@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 110 ++++----------------------------------------- 1 file changed, 9 insertions(+), 101 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 888ddfc4dd4b..2151e280a868 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -75,6 +75,7 @@ static RISCVException ctr(CPURISCVState *env, int csrno) CPUState *cs = env_cpu(env); RISCVCPU *cpu = RISCV_CPU(cs); int ctr_index; + target_ulong ctr_mask; int base_csrno = CSR_CYCLE; bool rv32 = riscv_cpu_mxl(env) == MXL_RV32 ? true : false; @@ -83,122 +84,29 @@ static RISCVException ctr(CPURISCVState *env, int csrno) base_csrno += 0x80; } ctr_index = csrno - base_csrno; + ctr_mask = BIT(ctr_index); if ((csrno >= CSR_CYCLE && csrno <= CSR_INSTRET) || (csrno >= CSR_CYCLEH && csrno <= CSR_INSTRETH)) { goto skip_ext_pmu_check; } - if ((!cpu->cfg.pmu_num || !(cpu->pmu_avail_ctrs & BIT(ctr_index)))) { + if (!(cpu->pmu_avail_ctrs & ctr_mask)) { /* No counter is enabled in PMU or the counter is out of range */ return RISCV_EXCP_ILLEGAL_INST; } skip_ext_pmu_check: - if (env->priv == PRV_S) { - switch (csrno) { - case CSR_CYCLE: - if (!get_field(env->mcounteren, COUNTEREN_CY)) { - return RISCV_EXCP_ILLEGAL_INST; - } - break; - case CSR_TIME: - if (!get_field(env->mcounteren, COUNTEREN_TM)) { - return RISCV_EXCP_ILLEGAL_INST; - } - break; - case CSR_INSTRET: - if (!get_field(env->mcounteren, COUNTEREN_IR)) { - return RISCV_EXCP_ILLEGAL_INST; - } - break; - case CSR_HPMCOUNTER3...CSR_HPMCOUNTER31: - if (!get_field(env->mcounteren, 1 << ctr_index)) { - return RISCV_EXCP_ILLEGAL_INST; - } - break; - } - if (rv32) { - switch (csrno) { - case CSR_CYCLEH: - if (!get_field(env->mcounteren, COUNTEREN_CY)) { - return RISCV_EXCP_ILLEGAL_INST; - } - break; - case CSR_TIMEH: - if (!get_field(env->mcounteren, COUNTEREN_TM)) { - return RISCV_EXCP_ILLEGAL_INST; - } - break; - case CSR_INSTRETH: - if (!get_field(env->mcounteren, COUNTEREN_IR)) { - return RISCV_EXCP_ILLEGAL_INST; - } - break; - case CSR_HPMCOUNTER3H...CSR_HPMCOUNTER31H: - if (!get_field(env->mcounteren, 1 << ctr_index)) { - return RISCV_EXCP_ILLEGAL_INST; - } - break; - } - } + if (((env->priv == PRV_S) && (!get_field(env->mcounteren, ctr_mask))) || + ((env->priv == PRV_U) && (!get_field(env->scounteren, ctr_mask)))) { + return RISCV_EXCP_ILLEGAL_INST; } if (riscv_cpu_virt_enabled(env)) { - switch (csrno) { - case CSR_CYCLE: - if (!get_field(env->hcounteren, COUNTEREN_CY) && - get_field(env->mcounteren, COUNTEREN_CY)) { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } - break; - case CSR_TIME: - if (!get_field(env->hcounteren, COUNTEREN_TM) && - get_field(env->mcounteren, COUNTEREN_TM)) { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } - break; - case CSR_INSTRET: - if (!get_field(env->hcounteren, COUNTEREN_IR) && - get_field(env->mcounteren, COUNTEREN_IR)) { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } - break; - case CSR_HPMCOUNTER3...CSR_HPMCOUNTER31: - if (!get_field(env->hcounteren, 1 << ctr_index) && - get_field(env->mcounteren, 1 << ctr_index)) { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } - break; - } - if (rv32) { - switch (csrno) { - case CSR_CYCLEH: - if (!get_field(env->hcounteren, COUNTEREN_CY) && - get_field(env->mcounteren, COUNTEREN_CY)) { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } - break; - case CSR_TIMEH: - if (!get_field(env->hcounteren, COUNTEREN_TM) && - get_field(env->mcounteren, COUNTEREN_TM)) { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } - break; - case CSR_INSTRETH: - if (!get_field(env->hcounteren, COUNTEREN_IR) && - get_field(env->mcounteren, COUNTEREN_IR)) { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } - break; - case CSR_HPMCOUNTER3H...CSR_HPMCOUNTER31H: - if (!get_field(env->hcounteren, 1 << ctr_index) && - get_field(env->mcounteren, 1 << ctr_index)) { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } - break; - } + if (!get_field(env->hcounteren, ctr_mask) && + get_field(env->mcounteren, ctr_mask)) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } } #endif From 892320facd73136b48bb58af3bd742686eb05416 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Wed, 24 Aug 2022 15:16:59 -0700 Subject: [PATCH 42/44] target/riscv: Add few cache related PMU events Qemu can monitor the following cache related PMU events through tlb_fill functions. 1. DTLB load/store miss 3. ITLB prefetch miss Increment the PMU counter in tlb_fill function. Reviewed-by: Alistair Francis Tested-by: Heiko Stuebner Signed-off-by: Atish Patra Signed-off-by: Atish Patra Message-Id: <20220824221701.41932-4-atishp@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 719c5d5d0209..67e4c0efd216 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -21,11 +21,13 @@ #include "qemu/log.h" #include "qemu/main-loop.h" #include "cpu.h" +#include "pmu.h" #include "exec/exec-all.h" #include "instmap.h" #include "tcg/tcg-op.h" #include "trace.h" #include "semihosting/common-semi.h" +#include "cpu_bits.h" int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch) { @@ -1189,6 +1191,28 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, cpu_loop_exit_restore(cs, retaddr); } + +static void pmu_tlb_fill_incr_ctr(RISCVCPU *cpu, MMUAccessType access_type) +{ + enum riscv_pmu_event_idx pmu_event_type; + + switch (access_type) { + case MMU_INST_FETCH: + pmu_event_type = RISCV_PMU_EVENT_CACHE_ITLB_PREFETCH_MISS; + break; + case MMU_DATA_LOAD: + pmu_event_type = RISCV_PMU_EVENT_CACHE_DTLB_READ_MISS; + break; + case MMU_DATA_STORE: + pmu_event_type = RISCV_PMU_EVENT_CACHE_DTLB_WRITE_MISS; + break; + default: + return; + } + + riscv_pmu_incr_ctr(cpu, pmu_event_type); +} + bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) @@ -1287,6 +1311,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } } } else { + pmu_tlb_fill_incr_ctr(cpu, access_type); /* Single stage lookup */ ret = get_physical_address(env, &pa, &prot, address, NULL, access_type, mmu_idx, true, false, false); From abd9a20665496aa4f3680fbbd42b5c389ea53d1c Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Wed, 24 Aug 2022 15:17:00 -0700 Subject: [PATCH 43/44] hw/riscv: virt: Add PMU DT node to the device tree Qemu virt machine can support few cache events and cycle/instret counters. It also supports counter overflow for these events. Add a DT node so that OpenSBI/Linux kernel is aware of the virt machine capabilities. There are some dummy nodes added for testing as well. Acked-by: Alistair Francis Signed-off-by: Atish Patra Signed-off-by: Atish Patra Message-Id: <20220824221701.41932-5-atishp@rivosinc.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 16 +++++++++++++ target/riscv/pmu.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++ target/riscv/pmu.h | 1 + 3 files changed, 74 insertions(+) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index ff8c0df5cd47..befa9d2c26ac 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -30,6 +30,7 @@ #include "hw/char/serial.h" #include "target/riscv/cpu.h" #include "hw/core/sysbus-fdt.h" +#include "target/riscv/pmu.h" #include "hw/riscv/riscv_hart.h" #include "hw/riscv/virt.h" #include "hw/riscv/boot.h" @@ -708,6 +709,20 @@ static void create_fdt_socket_aplic(RISCVVirtState *s, aplic_phandles[socket] = aplic_s_phandle; } +static void create_fdt_pmu(RISCVVirtState *s) +{ + char *pmu_name; + MachineState *mc = MACHINE(s); + RISCVCPU hart = s->soc[0].harts[0]; + + pmu_name = g_strdup_printf("/soc/pmu"); + qemu_fdt_add_subnode(mc->fdt, pmu_name); + qemu_fdt_setprop_string(mc->fdt, pmu_name, "compatible", "riscv,pmu"); + riscv_pmu_generate_fdt_node(mc->fdt, hart.cfg.pmu_num, pmu_name); + + g_free(pmu_name); +} + static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, bool is_32_bit, uint32_t *phandle, uint32_t *irq_mmio_phandle, @@ -1036,6 +1051,7 @@ static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap, create_fdt_flash(s, memmap); create_fdt_fw_cfg(s, memmap); + create_fdt_pmu(s); update_bootargs: if (cmdline && *cmdline) { diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c index a5f504e53c88..b8e56d2b7b8e 100644 --- a/target/riscv/pmu.c +++ b/target/riscv/pmu.c @@ -20,11 +20,68 @@ #include "cpu.h" #include "pmu.h" #include "sysemu/cpu-timers.h" +#include "sysemu/device_tree.h" #define RISCV_TIMEBASE_FREQ 1000000000 /* 1Ghz */ #define MAKE_32BIT_MASK(shift, length) \ (((uint32_t)(~0UL) >> (32 - (length))) << (shift)) +/* + * To keep it simple, any event can be mapped to any programmable counters in + * QEMU. The generic cycle & instruction count events can also be monitored + * using programmable counters. In that case, mcycle & minstret must continue + * to provide the correct value as well. Heterogeneous PMU per hart is not + * supported yet. Thus, number of counters are same across all harts. + */ +void riscv_pmu_generate_fdt_node(void *fdt, int num_ctrs, char *pmu_name) +{ + uint32_t fdt_event_ctr_map[20] = {}; + uint32_t cmask; + + /* All the programmable counters can map to any event */ + cmask = MAKE_32BIT_MASK(3, num_ctrs); + + /* + * The event encoding is specified in the SBI specification + * Event idx is a 20bits wide number encoded as follows: + * event_idx[19:16] = type + * event_idx[15:0] = code + * The code field in cache events are encoded as follows: + * event_idx.code[15:3] = cache_id + * event_idx.code[2:1] = op_id + * event_idx.code[0:0] = result_id + */ + + /* SBI_PMU_HW_CPU_CYCLES: 0x01 : type(0x00) */ + fdt_event_ctr_map[0] = cpu_to_be32(0x00000001); + fdt_event_ctr_map[1] = cpu_to_be32(0x00000001); + fdt_event_ctr_map[2] = cpu_to_be32(cmask | 1 << 0); + + /* SBI_PMU_HW_INSTRUCTIONS: 0x02 : type(0x00) */ + fdt_event_ctr_map[3] = cpu_to_be32(0x00000002); + fdt_event_ctr_map[4] = cpu_to_be32(0x00000002); + fdt_event_ctr_map[5] = cpu_to_be32(cmask | 1 << 2); + + /* SBI_PMU_HW_CACHE_DTLB : 0x03 READ : 0x00 MISS : 0x00 type(0x01) */ + fdt_event_ctr_map[6] = cpu_to_be32(0x00010019); + fdt_event_ctr_map[7] = cpu_to_be32(0x00010019); + fdt_event_ctr_map[8] = cpu_to_be32(cmask); + + /* SBI_PMU_HW_CACHE_DTLB : 0x03 WRITE : 0x01 MISS : 0x00 type(0x01) */ + fdt_event_ctr_map[9] = cpu_to_be32(0x0001001B); + fdt_event_ctr_map[10] = cpu_to_be32(0x0001001B); + fdt_event_ctr_map[11] = cpu_to_be32(cmask); + + /* SBI_PMU_HW_CACHE_ITLB : 0x04 READ : 0x00 MISS : 0x00 type(0x01) */ + fdt_event_ctr_map[12] = cpu_to_be32(0x00010021); + fdt_event_ctr_map[13] = cpu_to_be32(0x00010021); + fdt_event_ctr_map[14] = cpu_to_be32(cmask); + + /* This a OpenSBI specific DT property documented in OpenSBI docs */ + qemu_fdt_setprop(fdt, pmu_name, "riscv,event-to-mhpmcounters", + fdt_event_ctr_map, sizeof(fdt_event_ctr_map)); +} + static bool riscv_pmu_counter_valid(RISCVCPU *cpu, uint32_t ctr_idx) { if (ctr_idx < 3 || ctr_idx >= RV_MAX_MHPMCOUNTERS || diff --git a/target/riscv/pmu.h b/target/riscv/pmu.h index 036653627f78..3004ce37b636 100644 --- a/target/riscv/pmu.h +++ b/target/riscv/pmu.h @@ -31,5 +31,6 @@ int riscv_pmu_init(RISCVCPU *cpu, int num_counters); int riscv_pmu_update_event_map(CPURISCVState *env, uint64_t value, uint32_t ctr_idx); int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx); +void riscv_pmu_generate_fdt_node(void *fdt, int num_counters, char *pmu_name); int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx); From f0551560b5c01b1dcbed1ac46ca0bd1155330f5f Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Wed, 24 Aug 2022 15:17:01 -0700 Subject: [PATCH 44/44] target/riscv: Update the privilege field for sscofpmf CSRs The sscofpmf extension was ratified as a part of priv spec v1.12. Mark the csr_ops accordingly. Reviewed-by: Weiwei Li Reviewed-by: Alistair Francis Signed-off-by: Atish Patra Message-Id: <20220824221701.41932-6-atishp@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 90 ++++++++++++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 30 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 2151e280a868..b96db1b62b38 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -4067,63 +4067,92 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { write_mhpmevent }, [CSR_MHPMEVENT3H] = { "mhpmevent3h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT4H] = { "mhpmevent4h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT5H] = { "mhpmevent5h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT6H] = { "mhpmevent6h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT7H] = { "mhpmevent7h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT8H] = { "mhpmevent8h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT9H] = { "mhpmevent9h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT10H] = { "mhpmevent10h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT11H] = { "mhpmevent11h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT12H] = { "mhpmevent12h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT13H] = { "mhpmevent13h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT14H] = { "mhpmevent14h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT15H] = { "mhpmevent15h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT16H] = { "mhpmevent16h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT17H] = { "mhpmevent17h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT18H] = { "mhpmevent18h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT19H] = { "mhpmevent19h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT20H] = { "mhpmevent20h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT21H] = { "mhpmevent21h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT22H] = { "mhpmevent22h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT23H] = { "mhpmevent23h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT24H] = { "mhpmevent24h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT25H] = { "mhpmevent25h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT26H] = { "mhpmevent26h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT27H] = { "mhpmevent27h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT28H] = { "mhpmevent28h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT29H] = { "mhpmevent29h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT30H] = { "mhpmevent30h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MHPMEVENT31H] = { "mhpmevent31h", sscofpmf, read_mhpmeventh, - write_mhpmeventh }, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_HPMCOUNTER3H] = { "hpmcounter3h", ctr32, read_hpmcounterh }, [CSR_HPMCOUNTER4H] = { "hpmcounter4h", ctr32, read_hpmcounterh }, @@ -4213,7 +4242,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { write_mhpmcounterh }, [CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", mctr32, read_hpmcounterh, write_mhpmcounterh }, - [CSR_SCOUNTOVF] = { "scountovf", sscofpmf, read_scountovf }, + [CSR_SCOUNTOVF] = { "scountovf", sscofpmf, read_scountovf, + .min_priv_ver = PRIV_VERSION_1_12_0 }, #endif /* !CONFIG_USER_ONLY */ };