* [PATCH v2 0/6] target/riscv: Add support for Control Transfer Records Ext.
@ 2024-06-19 15:27 Rajnesh Kanwal
2024-06-19 15:27 ` [PATCH v2 1/6] target/riscv: Remove obsolete sfence.vm instruction Rajnesh Kanwal
` (7 more replies)
0 siblings, 8 replies; 21+ messages in thread
From: Rajnesh Kanwal @ 2024-06-19 15:27 UTC (permalink / raw)
To: qemu-riscv, qemu-devel
Cc: alistair.francis, bin.meng, liweiwei, dbarboza, zhiwei_liu,
atishp, apatel, rkanwal, beeman, tech-control-transfer-records,
jason.chien
This series enables Control Transfer Records extension support on riscv
platform. This extension is similar to Arch LBR in x86 and BRBE in ARM.
The Extension has been stable and the latest release can be found here [0]
CTR extension depends on couple of other extensions:
1. S[m|s]csrind : The indirect CSR extension [1] which defines additional
([M|S|VS]IREG2-[M|S|VS]IREG6) register to address size limitation of
RISC-V CSR address space. CTR access ctrsource, ctrtartget and ctrdata
CSRs using sscsrind extension.
2. Smstateen: The mstateen bit[54] controls the access to the CTR ext to
S-mode.
3. Sscofpmf: Counter overflow and privilege mode filtering. [2]
The series is based on Smcdeleg/Ssccfg counter delegation extension [3]
patches. CTR itself doesn't depend on counter delegation support. This
rebase is basically to include the Smcsrind patches.
Due to the dependency of these extensions, the following extensions must be
enabled to use the control transfer records feature.
"smstateen=true,sscofpmf=true,smcsrind=true,sscsrind=true,smctr=true,ssctr=true"
Here is the link to a quick guide [5] to setup and run a basic perf demo on
Linux to use CTR Ext.
The Qemu patches can be found here:
https://github.com/rajnesh-kanwal/qemu/tree/ctr_upstream_v2
The opensbi patch can be found here:
https://github.com/rajnesh-kanwal/opensbi/tree/ctr_upstream_v2
The Linux kernel patches can be found here:
https://github.com/rajnesh-kanwal/linux/tree/ctr_upstream_v2
[0]: https://github.com/riscv/riscv-control-transfer-records/release
[1]: https://github.com/riscv/riscv-indirect-csr-access
[2]: https://github.com/riscvarchive/riscv-count-overflow/tree/main
[3]: https://github.com/riscv/riscv-smcdeleg-ssccfg
[4]: https://lore.kernel.org/all/20240217000134.3634191-1-atishp@rivosinc.com/
[5]: https://github.com/rajnesh-kanwal/linux/wiki/Running-CTR-basic-demo-on-QEMU-RISC%E2%80%90V-Virt-machine
Changelog:
v2: Lots of improvements based on Jason Chien's feedback including:
- Added CTR recording for cm.jalt, cm.jt, cm.popret, cm.popretz.
- Fixed and added more CTR extension enable checks.
- Fixed CTR CSR predicate functions.
- Fixed external trap xTE bit checks.
- One fix in freeze function for VS-mode.
- Lots of minor code improvements.
- Added checks in sctrclr instruction helper.
v1:
- https://github.com/rajnesh-kanwal/qemu/tree/ctr_upstream
Rajnesh Kanwal (6):
target/riscv: Remove obsolete sfence.vm instruction
target/riscv: Add Control Transfer Records CSR definitions.
target/riscv: Add support for Control Transfer Records extension CSRs.
target/riscv: Add support to record CTR entries.
target/riscv: Add CTR sctrclr instruction.
target/riscv: Add support to access ctrsource, ctrtarget, ctrdata
regs.
target/riscv/cpu.c | 4 +
target/riscv/cpu.h | 14 +
target/riscv/cpu_bits.h | 154 ++++++++++
target/riscv/cpu_cfg.h | 2 +
target/riscv/cpu_helper.c | 265 +++++++++++++++++
target/riscv/csr.c | 276 +++++++++++++++++-
target/riscv/helper.h | 9 +-
target/riscv/insn32.decode | 2 +-
.../riscv/insn_trans/trans_privileged.c.inc | 21 +-
target/riscv/insn_trans/trans_rvi.c.inc | 31 ++
target/riscv/insn_trans/trans_rvzce.c.inc | 20 ++
target/riscv/op_helper.c | 159 +++++++++-
target/riscv/tcg/tcg-cpu.c | 6 +
target/riscv/translate.c | 10 +
14 files changed, 960 insertions(+), 13 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 1/6] target/riscv: Remove obsolete sfence.vm instruction
2024-06-19 15:27 [PATCH v2 0/6] target/riscv: Add support for Control Transfer Records Ext Rajnesh Kanwal
@ 2024-06-19 15:27 ` Rajnesh Kanwal
2024-06-25 7:58 ` Jason Chien
2024-06-19 15:27 ` [PATCH v2 2/6] target/riscv: Add Control Transfer Records CSR definitions Rajnesh Kanwal
` (6 subsequent siblings)
7 siblings, 1 reply; 21+ messages in thread
From: Rajnesh Kanwal @ 2024-06-19 15:27 UTC (permalink / raw)
To: qemu-riscv, qemu-devel
Cc: alistair.francis, bin.meng, liweiwei, dbarboza, zhiwei_liu,
atishp, apatel, rkanwal, beeman, tech-control-transfer-records,
jason.chien
Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
---
target/riscv/insn32.decode | 1 -
target/riscv/insn_trans/trans_privileged.c.inc | 5 -----
2 files changed, 6 deletions(-)
diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
index f22df04cfd..9cb1a1b4ec 100644
--- a/target/riscv/insn32.decode
+++ b/target/riscv/insn32.decode
@@ -112,7 +112,6 @@ sret 0001000 00010 00000 000 00000 1110011
mret 0011000 00010 00000 000 00000 1110011
wfi 0001000 00101 00000 000 00000 1110011
sfence_vma 0001001 ..... ..... 000 00000 1110011 @sfence_vma
-sfence_vm 0001000 00100 ..... 000 00000 1110011 @sfence_vm
# *** RV32I Base Instruction Set ***
lui .................... ..... 0110111 @u
diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
index bc5263a4e0..4eccdddeaa 100644
--- a/target/riscv/insn_trans/trans_privileged.c.inc
+++ b/target/riscv/insn_trans/trans_privileged.c.inc
@@ -127,8 +127,3 @@ static bool trans_sfence_vma(DisasContext *ctx, arg_sfence_vma *a)
#endif
return false;
}
-
-static bool trans_sfence_vm(DisasContext *ctx, arg_sfence_vm *a)
-{
- return false;
-}
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 2/6] target/riscv: Add Control Transfer Records CSR definitions.
2024-06-19 15:27 [PATCH v2 0/6] target/riscv: Add support for Control Transfer Records Ext Rajnesh Kanwal
2024-06-19 15:27 ` [PATCH v2 1/6] target/riscv: Remove obsolete sfence.vm instruction Rajnesh Kanwal
@ 2024-06-19 15:27 ` Rajnesh Kanwal
2024-06-26 0:07 ` Alistair Francis
2024-06-26 6:00 ` Jason Chien
2024-06-19 15:27 ` [PATCH v2 3/6] target/riscv: Add support for Control Transfer Records extension CSRs Rajnesh Kanwal
` (5 subsequent siblings)
7 siblings, 2 replies; 21+ messages in thread
From: Rajnesh Kanwal @ 2024-06-19 15:27 UTC (permalink / raw)
To: qemu-riscv, qemu-devel
Cc: alistair.francis, bin.meng, liweiwei, dbarboza, zhiwei_liu,
atishp, apatel, rkanwal, beeman, tech-control-transfer-records,
jason.chien
The Control Transfer Records (CTR) extension provides a method to
record a limited branch history in register-accessible internal chip
storage.
This extension is similar to Arch LBR in x86 and BRBE in ARM.
The Extension has been stable and the latest release can be found here
https://github.com/riscv/riscv-control-transfer-records/release
Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
---
target/riscv/cpu_bits.h | 154 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 154 insertions(+)
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 86e15381c8..71ddccaf1a 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -242,6 +242,17 @@
#define CSR_SIEH 0x114
#define CSR_SIPH 0x154
+/* Machine-Level Control transfer records CSRs */
+#define CSR_MCTRCTL 0x34e
+
+/* Supervisor-Level Control transfer records CSRs */
+#define CSR_SCTRCTL 0x14e
+#define CSR_SCTRSTATUS 0x14f
+#define CSR_SCTRDEPTH 0x15f
+
+/* VS-Level Control transfer records CSRs */
+#define CSR_VSCTRCTL 0x24e
+
/* Hpervisor CSRs */
#define CSR_HSTATUS 0x600
#define CSR_HEDELEG 0x602
@@ -339,6 +350,7 @@
#define SMSTATEEN0_CS (1ULL << 0)
#define SMSTATEEN0_FCSR (1ULL << 1)
#define SMSTATEEN0_JVT (1ULL << 2)
+#define SMSTATEEN0_CTR (1ULL << 54)
#define SMSTATEEN0_HSCONTXT (1ULL << 57)
#define SMSTATEEN0_IMSIC (1ULL << 58)
#define SMSTATEEN0_AIA (1ULL << 59)
@@ -854,6 +866,148 @@ typedef enum RISCVException {
#define UMTE_U_PM_INSN U_PM_INSN
#define UMTE_MASK (UMTE_U_PM_ENABLE | MMTE_U_PM_CURRENT | UMTE_U_PM_INSN)
+/* mctrctl CSR bits. */
+#define MCTRCTL_U_ENABLE BIT(0)
+#define MCTRCTL_S_ENABLE BIT(1)
+#define MCTRCTL_M_ENABLE BIT(2)
+#define MCTRCTL_RASEMU BIT(7)
+#define MCTRCTL_STE BIT(8)
+#define MCTRCTL_MTE BIT(9)
+#define MCTRCTL_BPFRZ BIT(11)
+#define MCTRCTL_LCOFIFRZ BIT(12)
+#define MCTRCTL_EXCINH BIT(33)
+#define MCTRCTL_INTRINH BIT(34)
+#define MCTRCTL_TRETINH BIT(35)
+#define MCTRCTL_NTBREN BIT(36)
+#define MCTRCTL_TKBRINH BIT(37)
+#define MCTRCTL_INDCALL_INH BIT(40)
+#define MCTRCTL_DIRCALL_INH BIT(41)
+#define MCTRCTL_INDJUMP_INH BIT(42)
+#define MCTRCTL_DIRJUMP_INH BIT(43)
+#define MCTRCTL_CORSWAP_INH BIT(44)
+#define MCTRCTL_RET_INH BIT(45)
+#define MCTRCTL_INDOJUMP_INH BIT(46)
+#define MCTRCTL_DIROJUMP_INH BIT(47)
+
+#define MCTRCTL_INH_START 32U
+
+#define MCTRCTL_MASK (MCTRCTL_M_ENABLE | MCTRCTL_S_ENABLE | \
+ MCTRCTL_U_ENABLE | MCTRCTL_RASEMU | \
+ MCTRCTL_MTE | MCTRCTL_STE | \
+ MCTRCTL_BPFRZ | MCTRCTL_LCOFIFRZ | \
+ MCTRCTL_EXCINH | MCTRCTL_INTRINH | \
+ MCTRCTL_TRETINH | MCTRCTL_NTBREN | \
+ MCTRCTL_TKBRINH | MCTRCTL_INDCALL_INH | \
+ MCTRCTL_DIRCALL_INH | MCTRCTL_INDJUMP_INH | \
+ MCTRCTL_DIRJUMP_INH | MCTRCTL_CORSWAP_INH | \
+ MCTRCTL_RET_INH | MCTRCTL_INDOJUMP_INH | \
+ MCTRCTL_DIROJUMP_INH)
+
+/* sctrctl CSR bits. */
+#define SCTRCTL_U_ENABLE MCTRCTL_U_ENABLE
+#define SCTRCTL_S_ENABLE MCTRCTL_S_ENABLE
+#define SCTRCTL_RASEMU MCTRCTL_RASEMU
+#define SCTRCTL_STE MCTRCTL_STE
+#define SCTRCTL_BPFRZ MCTRCTL_BPFRZ
+#define SCTRCTL_LCOFIFRZ MCTRCTL_LCOFIFRZ
+#define SCTRCTL_EXCINH MCTRCTL_EXCINH
+#define SCTRCTL_INTRINH MCTRCTL_INTRINH
+#define SCTRCTL_TRETINH MCTRCTL_TRETINH
+#define SCTRCTL_NTBREN MCTRCTL_NTBREN
+#define SCTRCTL_TKBRINH MCTRCTL_TKBRINH
+#define SCTRCTL_INDCALL_INH MCTRCTL_INDCALL_INH
+#define SCTRCTL_DIRCALL_INH MCTRCTL_DIRCALL_INH
+#define SCTRCTL_INDJUMP_INH MCTRCTL_INDJUMP_INH
+#define SCTRCTL_DIRJUMP_INH MCTRCTL_DIRJUMP_INH
+#define SCTRCTL_CORSWAP_INH MCTRCTL_CORSWAP_INH
+#define SCTRCTL_RET_INH MCTRCTL_RET_INH
+#define SCTRCTL_INDOJUMP_INH MCTRCTL_INDOJUMP_INH
+#define SCTRCTL_DIROJUMP_INH MCTRCTL_DIROJUMP_INH
+
+#define SCTRCTL_MASK (SCTRCTL_S_ENABLE | SCTRCTL_U_ENABLE | \
+ SCTRCTL_RASEMU | SCTRCTL_STE | \
+ SCTRCTL_BPFRZ | SCTRCTL_LCOFIFRZ | \
+ SCTRCTL_EXCINH | SCTRCTL_INTRINH | \
+ SCTRCTL_TRETINH | SCTRCTL_NTBREN | \
+ SCTRCTL_TKBRINH | SCTRCTL_INDCALL_INH | \
+ SCTRCTL_DIRCALL_INH | SCTRCTL_INDJUMP_INH | \
+ SCTRCTL_DIRJUMP_INH | SCTRCTL_CORSWAP_INH | \
+ SCTRCTL_RET_INH | SCTRCTL_INDOJUMP_INH | \
+ SCTRCTL_DIROJUMP_INH)
+
+/* sctrstatus CSR bits. */
+#define SCTRSTATUS_WRPTR_MASK 0xFF
+#define SCTRSTATUS_FROZEN BIT(31)
+#define SCTRSTATUS_MASK (SCTRSTATUS_WRPTR_MASK | SCTRSTATUS_FROZEN)
+
+/* sctrdepth CSR bits. */
+#define SCTRDEPTH_MASK 0x7
+#define SCTRDEPTH_MIN 0U /* 16 Entries. */
+#define SCTRDEPTH_MAX 4U /* 256 Entries. */
+
+/* vsctrctl CSR bits. */
+#define VSCTRCTL_VU_ENABLE MCTRCTL_U_ENABLE
+#define VSCTRCTL_VS_ENABLE MCTRCTL_S_ENABLE
+#define VSCTRCTL_RASEMU MCTRCTL_RASEMU
+#define VSCTRCTL_VSTE MCTRCTL_STE
+#define VSCTRCTL_BPFRZ MCTRCTL_BPFRZ
+#define VSCTRCTL_LCOFIFRZ MCTRCTL_LCOFIFRZ
+#define VSCTRCTL_EXCINH MCTRCTL_EXCINH
+#define VSCTRCTL_INTRINH MCTRCTL_INTRINH
+#define VSCTRCTL_TRETINH MCTRCTL_TRETINH
+#define VSCTRCTL_NTBREN MCTRCTL_NTBREN
+#define VSCTRCTL_TKBRINH MCTRCTL_TKBRINH
+#define VSCTRCTL_INDCALL_INH MCTRCTL_INDCALL_INH
+#define VSCTRCTL_DIRCALL_INH MCTRCTL_DIRCALL_INH
+#define VSCTRCTL_INDJUMP_INH MCTRCTL_INDJUMP_INH
+#define VSCTRCTL_DIRJUMP_INH MCTRCTL_DIRJUMP_INH
+#define VSCTRCTL_CORSWAP_INH MCTRCTL_CORSWAP_INH
+#define VSCTRCTL_RET_INH MCTRCTL_RET_INH
+#define VSCTRCTL_INDOJUMP_INH MCTRCTL_INDOJUMP_INH
+#define VSCTRCTL_DIROJUMP_INH MCTRCTL_DIROJUMP_INH
+
+#define VSCTRCTL_MASK (VSCTRCTL_VS_ENABLE | VSCTRCTL_VU_ENABLE | \
+ VSCTRCTL_RASEMU | VSCTRCTL_VSTE | \
+ VSCTRCTL_BPFRZ | VSCTRCTL_LCOFIFRZ | \
+ VSCTRCTL_EXCINH | VSCTRCTL_INTRINH | \
+ VSCTRCTL_TRETINH | VSCTRCTL_NTBREN | \
+ VSCTRCTL_TKBRINH | VSCTRCTL_INDCALL_INH | \
+ VSCTRCTL_DIRCALL_INH | VSCTRCTL_INDJUMP_INH | \
+ VSCTRCTL_DIRJUMP_INH | VSCTRCTL_CORSWAP_INH | \
+ VSCTRCTL_RET_INH | VSCTRCTL_INDOJUMP_INH | \
+ VSCTRCTL_DIROJUMP_INH)
+
+#define CTR_ENTRIES_FIRST 0x200
+#define CTR_ENTRIES_LAST 0x2ff
+
+#define CTRSOURCE_VALID BIT(0)
+#define CTRTARGET_MISP BIT(0)
+
+#define CTRDATA_TYPE_MASK 0xF
+#define CTRDATA_CCV BIT(15)
+#define CTRDATA_CCM_MASK 0xFFF0000
+#define CTRDATA_CCE_MASK 0xF0000000
+
+#define CTRDATA_MASK (CTRDATA_TYPE_MASK | CTRDATA_CCV | \
+ CTRDATA_CCM_MASK | CTRDATA_CCE_MASK)
+
+#define CTRDATA_TYPE_NONE 0
+#define CTRDATA_TYPE_EXCEPTION 1
+#define CTRDATA_TYPE_INTERRUPT 2
+#define CTRDATA_TYPE_EXCEP_INT_RET 3
+#define CTRDATA_TYPE_NONTAKEN_BRANCH 4
+#define CTRDATA_TYPE_TAKEN_BRANCH 5
+#define CTRDATA_TYPE_RESERVED_0 6
+#define CTRDATA_TYPE_RESERVED_1 7
+#define CTRDATA_TYPE_INDIRECT_CALL 8
+#define CTRDATA_TYPE_DIRECT_CALL 9
+#define CTRDATA_TYPE_INDIRECT_JUMP 10
+#define CTRDATA_TYPE_DIRECT_JUMP 11
+#define CTRDATA_TYPE_CO_ROUTINE_SWAP 12
+#define CTRDATA_TYPE_RETURN 13
+#define CTRDATA_TYPE_OTHER_INDIRECT_JUMP 14
+#define CTRDATA_TYPE_OTHER_DIRECT_JUMP 15
+
/* MISELECT, SISELECT, and VSISELECT bits (AIA) */
#define ISELECT_IPRIO0 0x30
#define ISELECT_IPRIO15 0x3f
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 3/6] target/riscv: Add support for Control Transfer Records extension CSRs.
2024-06-19 15:27 [PATCH v2 0/6] target/riscv: Add support for Control Transfer Records Ext Rajnesh Kanwal
2024-06-19 15:27 ` [PATCH v2 1/6] target/riscv: Remove obsolete sfence.vm instruction Rajnesh Kanwal
2024-06-19 15:27 ` [PATCH v2 2/6] target/riscv: Add Control Transfer Records CSR definitions Rajnesh Kanwal
@ 2024-06-19 15:27 ` Rajnesh Kanwal
2024-06-25 8:28 ` Jason Chien
2024-08-27 9:28 ` Frank Chang
2024-06-19 15:27 ` [PATCH v2 4/6] target/riscv: Add support to record CTR entries Rajnesh Kanwal
` (4 subsequent siblings)
7 siblings, 2 replies; 21+ messages in thread
From: Rajnesh Kanwal @ 2024-06-19 15:27 UTC (permalink / raw)
To: qemu-riscv, qemu-devel
Cc: alistair.francis, bin.meng, liweiwei, dbarboza, zhiwei_liu,
atishp, apatel, rkanwal, beeman, tech-control-transfer-records,
jason.chien
This commit adds support for [m|s|vs]ctrcontrol, sctrstatus and
sctrdepth CSRs handling.
Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
---
target/riscv/cpu.h | 5 ++
target/riscv/cpu_cfg.h | 2 +
target/riscv/csr.c | 128 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 135 insertions(+)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index a185e2d494..3d4d5172b8 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -263,6 +263,11 @@ struct CPUArchState {
target_ulong mcause;
target_ulong mtval; /* since: priv-1.10.0 */
+ uint64_t mctrctl;
+ uint32_t sctrdepth;
+ uint32_t sctrstatus;
+ uint64_t vsctrctl;
+
/* Machine and Supervisor interrupt priorities */
uint8_t miprio[64];
uint8_t siprio[64];
diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h
index d9354dc80a..d329a65811 100644
--- a/target/riscv/cpu_cfg.h
+++ b/target/riscv/cpu_cfg.h
@@ -123,6 +123,8 @@ struct RISCVCPUConfig {
bool ext_zvfhmin;
bool ext_smaia;
bool ext_ssaia;
+ bool ext_smctr;
+ bool ext_ssctr;
bool ext_sscofpmf;
bool ext_smepmp;
bool rvv_ta_all_1s;
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 2f92e4b717..0b5bf4d050 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -621,6 +621,48 @@ static RISCVException pointer_masking(CPURISCVState *env, int csrno)
return RISCV_EXCP_ILLEGAL_INST;
}
+/*
+ * M-mode:
+ * Without ext_smctr raise illegal inst excep.
+ * Otherwise everything is accessible to m-mode.
+ *
+ * S-mode:
+ * Without ext_ssctr or mstateen.ctr raise illegal inst excep.
+ * Otherwise everything other than mctrctl is accessible.
+ *
+ * VS-mode:
+ * Without ext_ssctr or mstateen.ctr raise illegal inst excep.
+ * Without hstateen.ctr raise virtual illegal inst excep.
+ * Otherwise allow sctrctl (vsctrctl), sctrstatus, 0x200-0x2ff entry range.
+ * Always raise illegal instruction exception for sctrdepth.
+ */
+static RISCVException ctr_mmode(CPURISCVState *env, int csrno)
+{
+ /* Check if smctr-ext is present */
+ if (riscv_cpu_cfg(env)->ext_smctr) {
+ return RISCV_EXCP_NONE;
+ }
+
+ return RISCV_EXCP_ILLEGAL_INST;
+}
+
+static RISCVException ctr_smode(CPURISCVState *env, int csrno)
+{
+ const RISCVCPUConfig *cfg = riscv_cpu_cfg(env);
+
+ if (!cfg->ext_smctr && !cfg->ext_ssctr) {
+ return RISCV_EXCP_ILLEGAL_INST;
+ }
+
+ RISCVException ret = smstateen_acc_ok(env, 0, SMSTATEEN0_CTR);
+ if (ret == RISCV_EXCP_NONE && csrno == CSR_SCTRDEPTH &&
+ env->virt_enabled) {
+ return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
+ }
+
+ return ret;
+}
+
static RISCVException aia_hmode(CPURISCVState *env, int csrno)
{
int ret;
@@ -3835,6 +3877,86 @@ static RISCVException write_satp(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
+static RISCVException rmw_sctrdepth(CPURISCVState *env, int csrno,
+ target_ulong *ret_val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ uint64_t mask = wr_mask & SCTRDEPTH_MASK;
+
+ if (ret_val) {
+ *ret_val = env->sctrdepth;
+ }
+
+ env->sctrdepth = (env->sctrdepth & ~mask) | (new_val & mask);
+
+ /* Correct depth. */
+ if (wr_mask & SCTRDEPTH_MASK) {
+ uint64_t depth = get_field(env->sctrdepth, SCTRDEPTH_MASK);
+
+ if (depth > SCTRDEPTH_MAX) {
+ depth = SCTRDEPTH_MAX;
+ env->sctrdepth = set_field(env->sctrdepth, SCTRDEPTH_MASK, depth);
+ }
+
+ /* Update sctrstatus.WRPTR with a legal value */
+ depth = 16 << depth;
+ env->sctrstatus =
+ env->sctrstatus & (~SCTRSTATUS_WRPTR_MASK | (depth - 1));
+ }
+
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException rmw_sctrstatus(CPURISCVState *env, int csrno,
+ target_ulong *ret_val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ uint32_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
+ uint32_t mask = wr_mask & SCTRSTATUS_MASK;
+
+ if (ret_val) {
+ *ret_val = env->sctrstatus;
+ }
+
+ env->sctrstatus = (env->sctrstatus & ~mask) | (new_val & mask);
+
+ /* Update sctrstatus.WRPTR with a legal value */
+ env->sctrstatus = env->sctrstatus & (~SCTRSTATUS_WRPTR_MASK | (depth - 1));
+
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException rmw_xctrctl(CPURISCVState *env, int csrno,
+ target_ulong *ret_val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ uint64_t csr_mask, mask = wr_mask;
+ uint64_t *ctl_ptr = &env->mctrctl;
+
+ if (csrno == CSR_MCTRCTL) {
+ csr_mask = MCTRCTL_MASK;
+ } else if (csrno == CSR_SCTRCTL && !env->virt_enabled) {
+ csr_mask = SCTRCTL_MASK;
+ } else {
+ /*
+ * This is for csrno == CSR_SCTRCTL and env->virt_enabled == true
+ * or csrno == CSR_VSCTRCTL.
+ */
+ csr_mask = VSCTRCTL_MASK;
+ ctl_ptr = &env->vsctrctl;
+ }
+
+ mask &= csr_mask;
+
+ if (ret_val) {
+ *ret_val = *ctl_ptr & csr_mask;
+ }
+
+ *ctl_ptr = (*ctl_ptr & ~mask) | (new_val & mask);
+
+ return RISCV_EXCP_NONE;
+}
+
static RISCVException read_vstopi(CPURISCVState *env, int csrno,
target_ulong *val)
{
@@ -5771,6 +5893,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_SPMBASE] = { "spmbase", pointer_masking, read_spmbase,
write_spmbase },
+ [CSR_MCTRCTL] = { "mctrctl", ctr_mmode, NULL, NULL, rmw_xctrctl },
+ [CSR_SCTRCTL] = { "sctrctl", ctr_smode, NULL, NULL, rmw_xctrctl },
+ [CSR_VSCTRCTL] = { "vsctrctl", ctr_smode, NULL, NULL, rmw_xctrctl },
+ [CSR_SCTRDEPTH] = { "sctrdepth", ctr_smode, NULL, NULL, rmw_sctrdepth },
+ [CSR_SCTRSTATUS] = { "sctrstatus", ctr_smode, NULL, NULL, rmw_sctrstatus },
+
/* Performance Counters */
[CSR_HPMCOUNTER3] = { "hpmcounter3", ctr, read_hpmcounter },
[CSR_HPMCOUNTER4] = { "hpmcounter4", ctr, read_hpmcounter },
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 4/6] target/riscv: Add support to record CTR entries.
2024-06-19 15:27 [PATCH v2 0/6] target/riscv: Add support for Control Transfer Records Ext Rajnesh Kanwal
` (2 preceding siblings ...)
2024-06-19 15:27 ` [PATCH v2 3/6] target/riscv: Add support for Control Transfer Records extension CSRs Rajnesh Kanwal
@ 2024-06-19 15:27 ` Rajnesh Kanwal
2024-06-26 3:49 ` Jason Chien
2024-06-19 15:27 ` [PATCH v2 5/6] target/riscv: Add CTR sctrclr instruction Rajnesh Kanwal
` (3 subsequent siblings)
7 siblings, 1 reply; 21+ messages in thread
From: Rajnesh Kanwal @ 2024-06-19 15:27 UTC (permalink / raw)
To: qemu-riscv, qemu-devel
Cc: alistair.francis, bin.meng, liweiwei, dbarboza, zhiwei_liu,
atishp, apatel, rkanwal, beeman, tech-control-transfer-records,
jason.chien
This commit adds logic to records CTR entries of different types
and adds required hooks in TCG and interrupt/Exception logic to
record events.
This commit also adds support to invoke freeze CTR logic for breakpoint
exceptions and counter overflow interrupts.
Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
---
target/riscv/cpu.h | 8 +
target/riscv/cpu_helper.c | 258 ++++++++++++++++++
target/riscv/helper.h | 8 +-
.../riscv/insn_trans/trans_privileged.c.inc | 6 +-
target/riscv/insn_trans/trans_rvi.c.inc | 31 +++
target/riscv/insn_trans/trans_rvzce.c.inc | 20 ++
target/riscv/op_helper.c | 126 ++++++++-
target/riscv/translate.c | 10 +
8 files changed, 461 insertions(+), 6 deletions(-)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 3d4d5172b8..e32f5ab146 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -268,6 +268,10 @@ struct CPUArchState {
uint32_t sctrstatus;
uint64_t vsctrctl;
+ uint64_t ctr_src[16 << SCTRDEPTH_MAX];
+ uint64_t ctr_dst[16 << SCTRDEPTH_MAX];
+ uint64_t ctr_data[16 << SCTRDEPTH_MAX];
+
/* Machine and Supervisor interrupt priorities */
uint8_t miprio[64];
uint8_t siprio[64];
@@ -565,6 +569,10 @@ RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit);
#endif
void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en);
+void riscv_ctr_freeze(CPURISCVState *env, uint64_t freeze_mask, bool virt);
+void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst,
+ uint64_t type, target_ulong prev_priv, bool prev_virt);
+
void riscv_translate_init(void);
G_NORETURN void riscv_raise_exception(CPURISCVState *env,
uint32_t exception, uintptr_t pc);
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index a441a03ef4..1537602e1b 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -691,6 +691,246 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv,
}
}
+void riscv_ctr_freeze(CPURISCVState *env, uint64_t freeze_mask, bool virt)
+{
+ uint64_t ctl = virt ? env->mctrctl : env->vsctrctl;
+
+ assert((freeze_mask & (~(MCTRCTL_BPFRZ | MCTRCTL_LCOFIFRZ))) == 0);
+
+ if (ctl & freeze_mask) {
+ env->sctrstatus |= SCTRSTATUS_FROZEN;
+ }
+}
+
+static uint64_t riscv_ctr_priv_to_mask(target_ulong priv, bool virt)
+{
+ switch (priv) {
+ case PRV_M:
+ return MCTRCTL_M_ENABLE;
+ case PRV_S:
+ if (virt) {
+ return VSCTRCTL_VS_ENABLE;
+ }
+ return MCTRCTL_S_ENABLE;
+ case PRV_U:
+ if (virt) {
+ return VSCTRCTL_VU_ENABLE;
+ }
+ return MCTRCTL_U_ENABLE;
+ }
+
+ g_assert_not_reached();
+}
+
+static uint64_t riscv_ctr_get_control(CPURISCVState *env, target_long priv,
+ bool virt)
+{
+ switch (priv) {
+ case PRV_M:
+ return env->mctrctl;
+ case PRV_S:
+ case PRV_U:
+ if (virt) {
+ return env->vsctrctl;
+ }
+ return env->mctrctl;
+ }
+
+ g_assert_not_reached();
+}
+
+/*
+ * This function assumes that src privilege and target privilege are not same
+ * and src privilege is less than target privilege. This includes the virtual
+ * state as well.
+ */
+static bool riscv_ctr_check_xte(CPURISCVState *env, target_long src_prv,
+ bool src_virt)
+{
+ target_long tgt_prv = env->priv;
+ bool res = true;
+
+ /*
+ * VS and U mode are same in terms of xTE bits required to record an
+ * external trap. See 6.1.2. External Traps, table 8 External Trap Enable
+ * Requirements. This changes VS to U to simplify the logic a bit.
+ */
+ if (src_virt && src_prv == PRV_S) {
+ src_prv = PRV_U;
+ } else if (env->virt_enabled && tgt_prv == PRV_S) {
+ tgt_prv = PRV_U;
+ }
+
+ /* VU mode is an outlier here. */
+ if (src_virt && src_prv == PRV_U) {
+ res &= !!(env->vsctrctl & VSCTRCTL_VSTE);
+ }
+
+ switch (src_prv) {
+ case PRV_U:
+ if (tgt_prv == PRV_U) {
+ break;
+ }
+ res &= !!(env->mctrctl & SCTRCTL_STE);
+ /* fall-through */
+ case PRV_S:
+ if (tgt_prv == PRV_S) {
+ break;
+ }
+ res &= !!(env->mctrctl & MCTRCTL_MTE);
+ /* fall-through */
+ case PRV_M:
+ break;
+ }
+
+ return res;
+}
+
+/*
+ * Special cases for traps and trap returns:
+ *
+ * 1- Traps, and trap returns, between enabled modes are recorded as normal.
+ * 2- Traps from an inhibited mode to an enabled mode, and trap returns from an
+ * enabled mode back to an inhibited mode, are partially recorded. In such
+ * cases, the PC from the inhibited mode (source PC for traps, and target PC
+ * for trap returns) is 0.
+ *
+ * 3- Trap returns from an inhibited mode to an enabled mode are not recorded.
+ * Traps from an enabled mode to an inhibited mode, known as external traps,
+ * receive special handling.
+ * By default external traps are not recorded, but a handshake mechanism exists
+ * to allow partial recording. Software running in the target mode of the trap
+ * can opt-in to allowing CTR to record traps into that mode even when the mode
+ * is inhibited. The MTE, STE, and VSTE bits allow M-mode, S-mode, and VS-mode,
+ * respectively, to opt-in. When an External Trap occurs, and xTE=1, such that
+ * x is the target privilege mode of the trap, will CTR record the trap. In such
+ * cases, the target PC is 0.
+ */
+/*
+ * CTR arrays are implemented as circular buffers and new entry is stored at
+ * sctrstatus.WRPTR, but they are presented to software as moving circular
+ * buffers. Which means, software get's the illusion that whenever a new entry
+ * is added the whole buffer is moved by one place and the new entry is added at
+ * the start keeping new entry at idx 0 and older ones follow.
+ *
+ * Depth = 16.
+ *
+ * buffer [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F]
+ * WRPTR W
+ * entry 7 6 5 4 3 2 1 0 F E D C B A 9 8
+ *
+ * When a new entry is added:
+ * buffer [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F]
+ * WRPTR W
+ * entry 8 7 6 5 4 3 2 1 0 F E D C B A 9
+ *
+ * entry here denotes the logical entry number that software can access
+ * using ctrsource, ctrtarget and ctrdata registers. So xiselect 0x200
+ * will return entry 0 i-e buffer[8] and 0x201 will return entry 1 i-e
+ * buffer[7]. Here is how we convert entry to buffer idx.
+ *
+ * entry = isel - CTR_ENTRIES_FIRST;
+ * idx = (sctrstatus.WRPTR - entry - 1) & (depth - 1);
+ */
+void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst,
+ uint64_t type, target_ulong src_priv, bool src_virt)
+{
+ bool tgt_virt = env->virt_enabled;
+ uint64_t src_mask = riscv_ctr_priv_to_mask(src_priv, src_virt);
+ uint64_t tgt_mask = riscv_ctr_priv_to_mask(env->priv, tgt_virt);
+ uint64_t src_ctrl = riscv_ctr_get_control(env, src_priv, src_virt);
+ uint64_t tgt_ctrl = riscv_ctr_get_control(env, env->priv, tgt_virt);
+ uint64_t depth, head;
+ bool ext_trap = false;
+
+ /*
+ * Return immediately if both target and src recording is disabled or if
+ * CTR is in frozen state.
+ */
+ if ((!(src_ctrl & src_mask) && !(tgt_ctrl & tgt_mask)) ||
+ env->sctrstatus & SCTRSTATUS_FROZEN) {
+ return;
+ }
+
+ /*
+ * With RAS Emul enabled, only allow Indirect, direct calls, Function
+ * returns and Co-routine swap types.
+ */
+ if (env->mctrctl & MCTRCTL_RASEMU &&
+ type != CTRDATA_TYPE_INDIRECT_CALL &&
+ type != CTRDATA_TYPE_DIRECT_CALL &&
+ type != CTRDATA_TYPE_RETURN &&
+ type != CTRDATA_TYPE_CO_ROUTINE_SWAP) {
+ return;
+ }
+
+ if (type == CTRDATA_TYPE_EXCEPTION || type == CTRDATA_TYPE_INTERRUPT) {
+ /* Case 2 for traps. */
+ if (!(src_ctrl & src_mask)) {
+ src = 0;
+ } else if (!(tgt_ctrl & tgt_mask)) {
+ /* Check if target priv-mode has allowed external trap recording. */
+ if (!riscv_ctr_check_xte(env, src_priv, src_virt)) {
+ return;
+ }
+
+ ext_trap = true;
+ dst = 0;
+ }
+ } else if (type == CTRDATA_TYPE_EXCEP_INT_RET) {
+ /*
+ * Case 3 for trap returns. Trap returns from inhibited mode are not
+ * recorded.
+ */
+ if (!(src_ctrl & src_mask)) {
+ return;
+ }
+
+ /* Case 2 for trap returns. */
+ if (!(tgt_ctrl & tgt_mask)) {
+ dst = 0;
+ }
+ }
+
+ /* Ignore filters in case of RASEMU mode or External trap. */
+ if (!(tgt_ctrl & MCTRCTL_RASEMU) && !ext_trap) {
+ /*
+ * Check if the specific type is inhibited. Not taken branch filter is
+ * an enable bit and needs to be checked separatly.
+ */
+ bool check = tgt_ctrl & BIT_ULL(type + MCTRCTL_INH_START);
+ if ((type == CTRDATA_TYPE_NONTAKEN_BRANCH && !check) ||
+ (type != CTRDATA_TYPE_NONTAKEN_BRANCH && check)) {
+ return;
+ }
+ }
+
+ head = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK);
+
+ depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
+ if (tgt_ctrl & MCTRCTL_RASEMU && type == CTRDATA_TYPE_RETURN) {
+ head = (head - 1) & (depth - 1);
+
+ env->ctr_src[head] &= ~CTRSOURCE_VALID;
+ env->sctrstatus =
+ set_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK, head);
+ return;
+ }
+
+ /* In case of Co-routine SWAP we overwrite latest entry. */
+ if (tgt_ctrl & MCTRCTL_RASEMU && type == CTRDATA_TYPE_CO_ROUTINE_SWAP) {
+ head = (head - 1) & (depth - 1);
+ }
+
+ env->ctr_src[head] = src | CTRSOURCE_VALID;
+ env->ctr_dst[head] = dst & ~CTRTARGET_MISP;
+ env->ctr_data[head] = set_field(0, CTRDATA_TYPE_MASK, type);
+
+ head = (head + 1) & (depth - 1);
+
+ env->sctrstatus = set_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK, head);
+}
+
void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en)
{
g_assert(newpriv <= PRV_M && newpriv != PRV_RESERVED);
@@ -1669,10 +1909,13 @@ void riscv_cpu_do_interrupt(CPUState *cs)
!(env->mip & (1 << cause));
bool vs_injected = env->hvip & (1 << cause) & env->hvien &&
!(env->mip & (1 << cause));
+ const bool prev_virt = env->virt_enabled;
+ const target_ulong prev_priv = env->priv;
target_ulong tval = 0;
target_ulong tinst = 0;
target_ulong htval = 0;
target_ulong mtval2 = 0;
+ target_ulong src;
if (!async) {
/* set tval to badaddr for traps with address information */
@@ -1807,6 +2050,8 @@ void riscv_cpu_do_interrupt(CPUState *cs)
env->pc = (env->stvec >> 2 << 2) +
((async && (env->stvec & 3) == 1) ? cause * 4 : 0);
riscv_cpu_set_mode(env, PRV_S, virt);
+
+ src = env->sepc;
} else {
/* handle the trap in M-mode */
if (riscv_has_ext(env, RVH)) {
@@ -1838,6 +2083,19 @@ void riscv_cpu_do_interrupt(CPUState *cs)
env->pc = (env->mtvec >> 2 << 2) +
((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
riscv_cpu_set_mode(env, PRV_M, virt);
+ src = env->mepc;
+ }
+
+ if (riscv_cpu_cfg(env)->ext_smctr || riscv_cpu_cfg(env)->ext_ssctr) {
+ if (async && cause == IRQ_PMU_OVF) {
+ riscv_ctr_freeze(env, MCTRCTL_LCOFIFRZ, virt);
+ } else if (!async && cause == RISCV_EXCP_BREAKPOINT) {
+ riscv_ctr_freeze(env, MCTRCTL_BPFRZ, virt);
+ }
+
+ riscv_ctr_add_entry(env, src, env->pc,
+ async ? CTRDATA_TYPE_INTERRUPT : CTRDATA_TYPE_EXCEPTION,
+ prev_priv, prev_virt);
}
/*
diff --git a/target/riscv/helper.h b/target/riscv/helper.h
index 451261ce5a..b8fb7c8734 100644
--- a/target/riscv/helper.h
+++ b/target/riscv/helper.h
@@ -129,12 +129,16 @@ DEF_HELPER_2(csrr_i128, tl, env, int)
DEF_HELPER_4(csrw_i128, void, env, int, tl, tl)
DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl)
#ifndef CONFIG_USER_ONLY
-DEF_HELPER_1(sret, tl, env)
-DEF_HELPER_1(mret, tl, env)
+DEF_HELPER_2(sret, tl, env, tl)
+DEF_HELPER_2(mret, tl, env, tl)
DEF_HELPER_1(wfi, void, env)
DEF_HELPER_1(wrs_nto, void, env)
DEF_HELPER_1(tlb_flush, void, env)
DEF_HELPER_1(tlb_flush_all, void, env)
+DEF_HELPER_4(ctr_branch, void, env, tl, tl, tl)
+DEF_HELPER_4(ctr_jal, void, env, tl, tl, tl)
+DEF_HELPER_5(ctr_jalr, void, env, tl, tl, tl, tl)
+DEF_HELPER_3(ctr_popret, void, env, tl, tl)
/* Native Debug */
DEF_HELPER_1(itrigger_match, void, env)
#endif
diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
index 4eccdddeaa..339d659151 100644
--- a/target/riscv/insn_trans/trans_privileged.c.inc
+++ b/target/riscv/insn_trans/trans_privileged.c.inc
@@ -78,9 +78,10 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a)
{
#ifndef CONFIG_USER_ONLY
if (has_ext(ctx, RVS)) {
+ TCGv src = tcg_constant_tl(ctx->base.pc_next);
decode_save_opc(ctx);
translator_io_start(&ctx->base);
- gen_helper_sret(cpu_pc, tcg_env);
+ gen_helper_sret(cpu_pc, tcg_env, src);
exit_tb(ctx); /* no chaining */
ctx->base.is_jmp = DISAS_NORETURN;
} else {
@@ -95,9 +96,10 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a)
static bool trans_mret(DisasContext *ctx, arg_mret *a)
{
#ifndef CONFIG_USER_ONLY
+ TCGv src = tcg_constant_tl(ctx->base.pc_next);
decode_save_opc(ctx);
translator_io_start(&ctx->base);
- gen_helper_mret(cpu_pc, tcg_env);
+ gen_helper_mret(cpu_pc, tcg_env, src);
exit_tb(ctx); /* no chaining */
ctx->base.is_jmp = DISAS_NORETURN;
return true;
diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc
index ad40d3e87f..26633569a8 100644
--- a/target/riscv/insn_trans/trans_rvi.c.inc
+++ b/target/riscv/insn_trans/trans_rvi.c.inc
@@ -75,6 +75,14 @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a)
gen_set_gpr(ctx, a->rd, succ_pc);
tcg_gen_mov_tl(cpu_pc, target_pc);
+#ifndef CONFIG_USER_ONLY
+ if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
+ TCGv src = tcg_constant_tl(ctx->base.pc_next);
+ TCGv rs1 = tcg_constant_tl(a->rs1);
+ TCGv rd = tcg_constant_tl(a->rd);
+ gen_helper_ctr_jalr(tcg_env, src, cpu_pc, rd, rs1);
+ }
+#endif
lookup_and_goto_ptr(ctx);
if (misaligned) {
@@ -164,6 +172,11 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond)
TCGv src1 = get_gpr(ctx, a->rs1, EXT_SIGN);
TCGv src2 = get_gpr(ctx, a->rs2, EXT_SIGN);
target_ulong orig_pc_save = ctx->pc_save;
+#ifndef CONFIG_USER_ONLY
+ TCGv src = tcg_constant_tl(ctx->base.pc_next);
+ TCGv taken;
+ TCGv dest;
+#endif
if (get_xl(ctx) == MXL_RV128) {
TCGv src1h = get_gprh(ctx, a->rs1);
@@ -176,6 +189,16 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond)
} else {
tcg_gen_brcond_tl(cond, src1, src2, l);
}
+
+#ifndef CONFIG_USER_ONLY
+ if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
+ dest = tcg_constant_tl(ctx->base.pc_next + ctx->cur_insn_len);
+ taken = tcg_constant_tl(0);
+
+ gen_helper_ctr_branch(tcg_env, src, dest, taken);
+ }
+#endif
+
gen_goto_tb(ctx, 1, ctx->cur_insn_len);
ctx->pc_save = orig_pc_save;
@@ -188,6 +211,14 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond)
gen_pc_plus_diff(target_pc, ctx, a->imm);
gen_exception_inst_addr_mis(ctx, target_pc);
} else {
+#ifndef CONFIG_USER_ONLY
+ if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
+ dest = tcg_constant_tl(ctx->base.pc_next + a->imm);
+ taken = tcg_constant_tl(1);
+
+ gen_helper_ctr_branch(tcg_env, src, dest, taken);
+ }
+#endif
gen_goto_tb(ctx, 0, a->imm);
}
ctx->pc_save = -1;
diff --git a/target/riscv/insn_trans/trans_rvzce.c.inc b/target/riscv/insn_trans/trans_rvzce.c.inc
index cd234ad960..377d3fff70 100644
--- a/target/riscv/insn_trans/trans_rvzce.c.inc
+++ b/target/riscv/insn_trans/trans_rvzce.c.inc
@@ -204,6 +204,12 @@ static bool gen_pop(DisasContext *ctx, arg_cmpp *a, bool ret, bool ret_val)
if (ret) {
TCGv ret_addr = get_gpr(ctx, xRA, EXT_SIGN);
tcg_gen_mov_tl(cpu_pc, ret_addr);
+#ifndef CONFIG_USER_ONLY
+ if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
+ TCGv src = tcg_constant_tl(ctx->base.pc_next);
+ gen_helper_ctr_popret(tcg_env, src, cpu_pc);
+ }
+#endif
tcg_gen_lookup_and_goto_ptr();
ctx->base.is_jmp = DISAS_NORETURN;
}
@@ -309,6 +315,20 @@ static bool trans_cm_jalt(DisasContext *ctx, arg_cm_jalt *a)
gen_set_gpr(ctx, xRA, succ_pc);
}
+#ifndef CONFIG_USER_ONLY
+ if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
+ /*
+ * We are reusing helper_ctr_jal() here. If rd is x1 or x5,
+ * this will record a direct call (cm.jalt) and if it's x0
+ * then this will record a direct jump (cm.jt).
+ */
+ TCGv rd = tcg_constant_tl(a->index >= 32 ? 1 : 0);
+ TCGv src = tcg_constant_tl(ctx->base.pc_next);
+ gen_helper_ctr_jal(tcg_env, src, addr, rd);
+ }
+#endif
+
+
tcg_gen_mov_tl(cpu_pc, addr);
tcg_gen_lookup_and_goto_ptr();
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index 25a5263573..5a1e92c45e 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -259,10 +259,12 @@ void helper_cbo_inval(CPURISCVState *env, target_ulong address)
#ifndef CONFIG_USER_ONLY
-target_ulong helper_sret(CPURISCVState *env)
+target_ulong helper_sret(CPURISCVState *env, target_ulong curr_pc)
{
uint64_t mstatus;
target_ulong prev_priv, prev_virt = env->virt_enabled;
+ const target_ulong src_priv = env->priv;
+ const bool src_virt = env->virt_enabled;
if (!(env->priv >= PRV_S)) {
riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
@@ -309,10 +311,15 @@ target_ulong helper_sret(CPURISCVState *env)
riscv_cpu_set_mode(env, prev_priv, prev_virt);
+ if (riscv_cpu_cfg(env)->ext_smctr || riscv_cpu_cfg(env)->ext_ssctr) {
+ riscv_ctr_add_entry(env, curr_pc, retpc, CTRDATA_TYPE_EXCEP_INT_RET,
+ src_priv, src_virt);
+ }
+
return retpc;
}
-target_ulong helper_mret(CPURISCVState *env)
+target_ulong helper_mret(CPURISCVState *env, target_ulong curr_pc)
{
if (!(env->priv >= PRV_M)) {
riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
@@ -350,9 +357,124 @@ target_ulong helper_mret(CPURISCVState *env)
riscv_cpu_set_mode(env, prev_priv, prev_virt);
+ if (riscv_cpu_cfg(env)->ext_smctr || riscv_cpu_cfg(env)->ext_ssctr) {
+ riscv_ctr_add_entry(env, curr_pc, retpc, CTRDATA_TYPE_EXCEP_INT_RET,
+ PRV_M, false);
+ }
+
return retpc;
}
+/*
+ * Indirect calls
+ * - jalr x1, rs where rs != x5;
+ * - jalr x5, rs where rs != x1;
+ * - c.jalr rs1 where rs1 != x5;
+ *
+ * Indirect jumps
+ * - jalr x0, rs where rs != x1 and rs != x5;
+ * - c.jr rs1 where rs1 != x1 and rs1 != x5.
+ *
+ * Returns
+ * - jalr rd, rs where (rs == x1 or rs == x5) and rd != x1 and rd != x5;
+ * - c.jr rs1 where rs1 == x1 or rs1 == x5.
+ *
+ * Co-routine swap
+ * - jalr x1, x5;
+ * - jalr x5, x1;
+ * - c.jalr x5.
+ *
+ * Other indirect jumps
+ * - jalr rd, rs where rs != x1, rs != x5, rd != x0, rd != x1 and rd != x5.
+ */
+void helper_ctr_jalr(CPURISCVState *env, target_ulong src, target_ulong dest,
+ target_ulong rd, target_ulong rs1)
+{
+ target_ulong curr_priv = env->priv;
+ bool curr_virt = env->virt_enabled;
+
+ if ((rd == 1 && rs1 != 5) || (rd == 5 && rs1 != 1)) {
+ riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_INDIRECT_CALL,
+ curr_priv, curr_virt);
+ } else if (rd == 0 && rs1 != 1 && rs1 != 5) {
+ riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_INDIRECT_JUMP,
+ curr_priv, curr_virt);
+ } else if ((rs1 == 1 || rs1 == 5) && (rd != 1 && rd != 5)) {
+ riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_RETURN,
+ curr_priv, curr_virt);
+ } else if ((rs1 == 1 && rd == 5) || (rs1 == 5 && rd == 1)) {
+ riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_CO_ROUTINE_SWAP,
+ curr_priv, curr_virt);
+ } else {
+ riscv_ctr_add_entry(env, src, dest,
+ CTRDATA_TYPE_OTHER_INDIRECT_JUMP, curr_priv,
+ curr_virt);
+ }
+}
+
+/*
+ * Direct calls
+ * - jal x1;
+ * - jal x5;
+ * - c.jal.
+ * - cm.jalt.
+ *
+ * Direct jumps
+ * - jal x0;
+ * - c.j;
+ * - cm.jt.
+ *
+ * Other direct jumps
+ * - jal rd where rd != x1 and rd != x5 and rd != x0;
+ */
+void helper_ctr_jal(CPURISCVState *env, target_ulong src, target_ulong dest,
+ target_ulong rd)
+{
+ target_ulong priv = env->priv;
+ bool virt = env->virt_enabled;
+
+ /*
+ * If rd is x1 or x5 link registers, treat this as direct call otherwise
+ * its a direct jump.
+ */
+ if (rd == 1 || rd == 5) {
+ riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_DIRECT_CALL, priv,
+ virt);
+ } else if (rd == 0) {
+ riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_DIRECT_JUMP, priv,
+ virt);
+ } else {
+ riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_OTHER_DIRECT_JUMP,
+ priv, virt);
+ }
+}
+
+/*
+ * Returns
+ * - cm.popret
+ * - cm.popretz
+ */
+void helper_ctr_popret(CPURISCVState *env, target_ulong src, target_ulong dest)
+{
+ riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_RETURN,
+ env->priv, env->virt_enabled);
+}
+
+void helper_ctr_branch(CPURISCVState *env, target_ulong src, target_ulong dest,
+ target_ulong branch_taken)
+{
+ target_ulong curr_priv = env->priv;
+ bool curr_virt = env->virt_enabled;
+
+ if (branch_taken) {
+ riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_TAKEN_BRANCH,
+ curr_priv, curr_virt);
+ } else {
+ riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_NONTAKEN_BRANCH,
+ curr_priv, curr_virt);
+ }
+}
+
void helper_wfi(CPURISCVState *env)
{
CPUState *cs = env_cpu(env);
diff --git a/target/riscv/translate.c b/target/riscv/translate.c
index 15e7123a68..07391297e8 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -572,6 +572,16 @@ static void gen_jal(DisasContext *ctx, int rd, target_ulong imm)
}
}
+#ifndef CONFIG_USER_ONLY
+ if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
+ TCGv dest = tcg_constant_tl(ctx->base.pc_next + imm);
+ TCGv src = tcg_constant_tl(ctx->base.pc_next);
+ TCGv tcg_rd = tcg_constant_tl((target_ulong)rd);
+
+ gen_helper_ctr_jal(tcg_env, src, dest, tcg_rd);
+ }
+#endif
+
gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len);
gen_set_gpr(ctx, rd, succ_pc);
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 5/6] target/riscv: Add CTR sctrclr instruction.
2024-06-19 15:27 [PATCH v2 0/6] target/riscv: Add support for Control Transfer Records Ext Rajnesh Kanwal
` (3 preceding siblings ...)
2024-06-19 15:27 ` [PATCH v2 4/6] target/riscv: Add support to record CTR entries Rajnesh Kanwal
@ 2024-06-19 15:27 ` Rajnesh Kanwal
2024-06-25 9:05 ` Jason Chien
2024-06-19 15:27 ` [PATCH v2 6/6] target/riscv: Add support to access ctrsource, ctrtarget, ctrdata regs Rajnesh Kanwal
` (2 subsequent siblings)
7 siblings, 1 reply; 21+ messages in thread
From: Rajnesh Kanwal @ 2024-06-19 15:27 UTC (permalink / raw)
To: qemu-riscv, qemu-devel
Cc: alistair.francis, bin.meng, liweiwei, dbarboza, zhiwei_liu,
atishp, apatel, rkanwal, beeman, tech-control-transfer-records,
jason.chien
CTR extension adds a new instruction sctrclr to quickly
clear the recorded entries buffer.
Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
---
target/riscv/cpu.h | 1 +
target/riscv/cpu_helper.c | 7 ++++
target/riscv/helper.h | 1 +
target/riscv/insn32.decode | 1 +
.../riscv/insn_trans/trans_privileged.c.inc | 10 ++++++
target/riscv/op_helper.c | 33 +++++++++++++++++++
6 files changed, 53 insertions(+)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index e32f5ab146..fdc18a782a 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -572,6 +572,7 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en);
void riscv_ctr_freeze(CPURISCVState *env, uint64_t freeze_mask, bool virt);
void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst,
uint64_t type, target_ulong prev_priv, bool prev_virt);
+void riscv_ctr_clear(CPURISCVState *env);
void riscv_translate_init(void);
G_NORETURN void riscv_raise_exception(CPURISCVState *env,
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 1537602e1b..d98628cfe3 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -702,6 +702,13 @@ void riscv_ctr_freeze(CPURISCVState *env, uint64_t freeze_mask, bool virt)
}
}
+void riscv_ctr_clear(CPURISCVState *env)
+{
+ memset(env->ctr_src, 0x0, sizeof(env->ctr_src));
+ memset(env->ctr_dst, 0x0, sizeof(env->ctr_dst));
+ memset(env->ctr_data, 0x0, sizeof(env->ctr_data));
+}
+
static uint64_t riscv_ctr_priv_to_mask(target_ulong priv, bool virt)
{
switch (priv) {
diff --git a/target/riscv/helper.h b/target/riscv/helper.h
index b8fb7c8734..a3b2d87527 100644
--- a/target/riscv/helper.h
+++ b/target/riscv/helper.h
@@ -131,6 +131,7 @@ DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl)
#ifndef CONFIG_USER_ONLY
DEF_HELPER_2(sret, tl, env, tl)
DEF_HELPER_2(mret, tl, env, tl)
+DEF_HELPER_1(ctr_clear, void, env)
DEF_HELPER_1(wfi, void, env)
DEF_HELPER_1(wrs_nto, void, env)
DEF_HELPER_1(tlb_flush, void, env)
diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
index 9cb1a1b4ec..d3d38c7c68 100644
--- a/target/riscv/insn32.decode
+++ b/target/riscv/insn32.decode
@@ -107,6 +107,7 @@
# *** Privileged Instructions ***
ecall 000000000000 00000 000 00000 1110011
ebreak 000000000001 00000 000 00000 1110011
+sctrclr 000100000100 00000 000 00000 1110011
uret 0000000 00010 00000 000 00000 1110011
sret 0001000 00010 00000 000 00000 1110011
mret 0011000 00010 00000 000 00000 1110011
diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
index 339d659151..dd9da8651f 100644
--- a/target/riscv/insn_trans/trans_privileged.c.inc
+++ b/target/riscv/insn_trans/trans_privileged.c.inc
@@ -69,6 +69,16 @@ static bool trans_ebreak(DisasContext *ctx, arg_ebreak *a)
return true;
}
+static bool trans_sctrclr(DisasContext *ctx, arg_sctrclr *a)
+{
+#ifndef CONFIG_USER_ONLY
+ gen_helper_ctr_clear(tcg_env);
+ return true;
+#else
+ return false;
+#endif
+}
+
static bool trans_uret(DisasContext *ctx, arg_uret *a)
{
return false;
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index 5a1e92c45e..15a770360e 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -475,6 +475,39 @@ void helper_ctr_branch(CPURISCVState *env, target_ulong src, target_ulong dest,
}
}
+void helper_ctr_clear(CPURISCVState *env)
+{
+ if (!riscv_cpu_cfg(env)->ext_ssctr && !riscv_cpu_cfg(env)->ext_smctr) {
+ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+ }
+
+ /*
+ * It's safe to call smstateen_acc_ok() for umode access regardless of the
+ * state of bit 54 (CTR bit in case of m/hstateen) of sstateen. If the bit
+ * is zero, smstateen_acc_ok() will return the correct exception code and
+ * if it's one, smstateen_acc_ok() will return RISCV_EXCP_NONE. In that
+ * scenario the U-mode check below will handle that case.
+ */
+ RISCVException ret = smstateen_acc_ok(env, 0, SMSTATEEN0_CTR);
+ if (ret != RISCV_EXCP_NONE) {
+ riscv_raise_exception(env, ret, GETPC());
+ }
+
+ if (env->priv == PRV_U) {
+ /*
+ * One corner case is when sctrclr is executed from VU-mode and
+ * mstateen.CTR = 0, in which case we are supposed to raise
+ * RISCV_EXCP_ILLEGAL_INST. This case is already handled in
+ * smstateen_acc_ok().
+ */
+ uint32_t excep = env->virt_enabled ? RISCV_EXCP_VIRT_INSTRUCTION_FAULT :
+ RISCV_EXCP_ILLEGAL_INST;
+ riscv_raise_exception(env, excep, GETPC());
+ }
+
+ riscv_ctr_clear(env);
+}
+
void helper_wfi(CPURISCVState *env)
{
CPUState *cs = env_cpu(env);
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 6/6] target/riscv: Add support to access ctrsource, ctrtarget, ctrdata regs.
2024-06-19 15:27 [PATCH v2 0/6] target/riscv: Add support for Control Transfer Records Ext Rajnesh Kanwal
` (4 preceding siblings ...)
2024-06-19 15:27 ` [PATCH v2 5/6] target/riscv: Add CTR sctrclr instruction Rajnesh Kanwal
@ 2024-06-19 15:27 ` Rajnesh Kanwal
2024-06-25 9:48 ` Jason Chien
2024-06-26 0:01 ` [PATCH v2 0/6] target/riscv: Add support for Control Transfer Records Ext Alistair Francis
2024-06-26 6:18 ` Jason Chien
7 siblings, 1 reply; 21+ messages in thread
From: Rajnesh Kanwal @ 2024-06-19 15:27 UTC (permalink / raw)
To: qemu-riscv, qemu-devel
Cc: alistair.francis, bin.meng, liweiwei, dbarboza, zhiwei_liu,
atishp, apatel, rkanwal, beeman, tech-control-transfer-records,
jason.chien
CTR entries are accessed using ctrsource, ctrtarget and ctrdata
registers using smcsrind/sscsrind extension. This commits extends
the csrind extension to support CTR registers.
ctrsource is accessible through xireg CSR, ctrtarget is accessible
through xireg1 and ctrdata is accessible through xireg2 CSR.
CTR supports maximum depth of 256 entries which are accessed using
xiselect range 0x200 to 0x2ff.
This commits also adds properties to enable CTR extension. CTR can be
enabled using smctr=true and ssctr=true now.
Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
---
target/riscv/cpu.c | 4 +
target/riscv/csr.c | 148 ++++++++++++++++++++++++++++++++++++-
target/riscv/tcg/tcg-cpu.c | 6 ++
3 files changed, 157 insertions(+), 1 deletion(-)
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 30bdfc22ae..a77b1d5caf 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -193,6 +193,8 @@ const RISCVIsaExtData isa_edata_arr[] = {
ISA_EXT_DATA_ENTRY(sstvala, PRIV_VERSION_1_12_0, has_priv_1_12),
ISA_EXT_DATA_ENTRY(sstvecd, PRIV_VERSION_1_12_0, has_priv_1_12),
ISA_EXT_DATA_ENTRY(svade, PRIV_VERSION_1_11_0, ext_svade),
+ ISA_EXT_DATA_ENTRY(smctr, PRIV_VERSION_1_12_0, ext_smctr),
+ ISA_EXT_DATA_ENTRY(ssctr, PRIV_VERSION_1_12_0, ext_ssctr),
ISA_EXT_DATA_ENTRY(svadu, PRIV_VERSION_1_12_0, ext_svadu),
ISA_EXT_DATA_ENTRY(svinval, PRIV_VERSION_1_12_0, ext_svinval),
ISA_EXT_DATA_ENTRY(svnapot, PRIV_VERSION_1_12_0, ext_svnapot),
@@ -1473,6 +1475,8 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = {
MULTI_EXT_CFG_BOOL("sscsrind", ext_sscsrind, false),
MULTI_EXT_CFG_BOOL("smcdeleg", ext_smcdeleg, false),
MULTI_EXT_CFG_BOOL("ssccfg", ext_ssccfg, false),
+ MULTI_EXT_CFG_BOOL("smctr", ext_smctr, false),
+ MULTI_EXT_CFG_BOOL("ssctr", ext_ssctr, false),
MULTI_EXT_CFG_BOOL("zifencei", ext_zifencei, true),
MULTI_EXT_CFG_BOOL("zicsr", ext_zicsr, true),
MULTI_EXT_CFG_BOOL("zihintntl", ext_zihintntl, true),
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 0b5bf4d050..3ed9f95a4f 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -2278,6 +2278,13 @@ static bool xiselect_cd_range(target_ulong isel)
return (ISELECT_CD_FIRST <= isel && isel <= ISELECT_CD_LAST);
}
+static bool xiselect_ctr_range(int csrno, target_ulong isel)
+{
+ /* MIREG-MIREG6 for the range 0x200-0x2ff are not used by CTR. */
+ return CTR_ENTRIES_FIRST <= isel && isel <= CTR_ENTRIES_LAST &&
+ csrno < CSR_MIREG;
+}
+
static int rmw_iprio(target_ulong xlen,
target_ulong iselect, uint8_t *iprio,
target_ulong *val, target_ulong new_val,
@@ -2323,6 +2330,124 @@ static int rmw_iprio(target_ulong xlen,
return 0;
}
+static int rmw_ctrsource(CPURISCVState *env, int isel, target_ulong *val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ /*
+ * CTR arrays are treated as circular buffers and TOS always points to next
+ * empty slot, keeping TOS - 1 always pointing to latest entry. Given entry
+ * 0 is always the latest one, traversal is a bit different here. See the
+ * below example.
+ *
+ * Depth = 16.
+ *
+ * idx [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F]
+ * TOS H
+ * entry 6 5 4 3 2 1 0 F E D C B A 9 8 7
+ */
+ const uint64_t entry = isel - CTR_ENTRIES_FIRST;
+ const uint64_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
+ uint64_t idx;
+
+ /* Entry greater than depth-1 is read-only zero */
+ if (entry >= depth) {
+ if (val) {
+ *val = 0;
+ }
+ return 0;
+ }
+
+ idx = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK);
+ idx = (idx - entry - 1) & (depth - 1);
+
+ if (val) {
+ *val = env->ctr_src[idx];
+ }
+
+ env->ctr_src[idx] = (env->ctr_src[idx] & ~wr_mask) | (new_val & wr_mask);
+
+ return 0;
+}
+
+static int rmw_ctrtarget(CPURISCVState *env, int isel, target_ulong *val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ /*
+ * CTR arrays are treated as circular buffers and TOS always points to next
+ * empty slot, keeping TOS - 1 always pointing to latest entry. Given entry
+ * 0 is always the latest one, traversal is a bit different here. See the
+ * below example.
+ *
+ * Depth = 16.
+ *
+ * idx [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F]
+ * head H
+ * entry 6 5 4 3 2 1 0 F E D C B A 9 8 7
+ */
+ const uint64_t entry = isel - CTR_ENTRIES_FIRST;
+ const uint64_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
+ uint64_t idx;
+
+ /* Entry greater than depth-1 is read-only zero */
+ if (entry >= depth) {
+ if (val) {
+ *val = 0;
+ }
+ return 0;
+ }
+
+ idx = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK);
+ idx = (idx - entry - 1) & (depth - 1);
+
+ if (val) {
+ *val = env->ctr_dst[idx];
+ }
+
+ env->ctr_dst[idx] = (env->ctr_dst[idx] & ~wr_mask) | (new_val & wr_mask);
+
+ return 0;
+}
+
+static int rmw_ctrdata(CPURISCVState *env, int isel, target_ulong *val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ /*
+ * CTR arrays are treated as circular buffers and TOS always points to next
+ * empty slot, keeping TOS - 1 always pointing to latest entry. Given entry
+ * 0 is always the latest one, traversal is a bit different here. See the
+ * below example.
+ *
+ * Depth = 16.
+ *
+ * idx [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F]
+ * head H
+ * entry 6 5 4 3 2 1 0 F E D C B A 9 8 7
+ */
+ const uint64_t entry = isel - CTR_ENTRIES_FIRST;
+ const uint64_t mask = wr_mask & CTRDATA_MASK;
+ const uint64_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
+ uint64_t idx;
+
+ /* Entry greater than depth-1 is read-only zero */
+ if (entry >= depth) {
+ if (val) {
+ *val = 0;
+ }
+ return 0;
+ }
+
+ idx = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK);
+ idx = (idx - entry - 1) & (depth - 1);
+
+ if (val) {
+ *val = env->ctr_data[idx];
+ }
+
+ env->ctr_data[idx] = (env->ctr_data[idx] & ~mask) | (new_val & mask);
+
+ return 0;
+}
+
static RISCVException rmw_xireg_aia(CPURISCVState *env, int csrno,
target_ulong isel, target_ulong *val,
target_ulong new_val, target_ulong wr_mask)
@@ -2473,6 +2598,25 @@ done:
return ret;
}
+static int rmw_xireg_ctr(CPURISCVState *env, int csrno,
+ target_ulong isel, target_ulong *val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ if (!riscv_cpu_cfg(env)->ext_smctr && !riscv_cpu_cfg(env)->ext_ssctr) {
+ return -EINVAL;
+ }
+
+ if (csrno == CSR_SIREG || csrno == CSR_VSIREG) {
+ return rmw_ctrsource(env, isel, val, new_val, wr_mask);
+ } else if (csrno == CSR_SIREG2 || csrno == CSR_VSIREG2) {
+ return rmw_ctrtarget(env, isel, val, new_val, wr_mask);
+ } else if (csrno == CSR_SIREG3 || csrno == CSR_VSIREG3) {
+ return rmw_ctrdata(env, isel, val, new_val, wr_mask);
+ }
+
+ return 0;
+}
+
/*
* rmw_xireg_sxcsrind: Perform indirect access to xireg and xireg2-xireg6
*
@@ -2484,11 +2628,13 @@ static int rmw_xireg_sxcsrind(CPURISCVState *env, int csrno,
target_ulong isel, target_ulong *val,
target_ulong new_val, target_ulong wr_mask)
{
- int ret = -EINVAL;
bool virt = csrno == CSR_VSIREG ? true : false;
+ int ret = -EINVAL;
if (xiselect_cd_range(isel)) {
ret = rmw_xireg_cd(env, csrno, isel, val, new_val, wr_mask);
+ } else if (xiselect_ctr_range(csrno, isel)) {
+ ret = rmw_xireg_ctr(env, csrno, isel, val, new_val, wr_mask);
} else {
/*
* As per the specification, access to unimplented region is undefined
diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c
index 683f604d9f..df75bb190b 100644
--- a/target/riscv/tcg/tcg-cpu.c
+++ b/target/riscv/tcg/tcg-cpu.c
@@ -726,6 +726,12 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
cpu->pmu_avail_ctrs = 0;
}
+ if ((cpu->cfg.ext_smctr || cpu->cfg.ext_ssctr) &&
+ (!riscv_has_ext(env, RVS) || !cpu->cfg.ext_sscsrind)) {
+ error_setg(errp, "Smctr and Ssctr require S-mode and Sscsrind");
+ return;
+ }
+
/*
* Disable isa extensions based on priv spec after we
* validated and set everything we need.
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v2 1/6] target/riscv: Remove obsolete sfence.vm instruction
2024-06-19 15:27 ` [PATCH v2 1/6] target/riscv: Remove obsolete sfence.vm instruction Rajnesh Kanwal
@ 2024-06-25 7:58 ` Jason Chien
0 siblings, 0 replies; 21+ messages in thread
From: Jason Chien @ 2024-06-25 7:58 UTC (permalink / raw)
To: Rajnesh Kanwal, qemu-riscv, qemu-devel
Cc: alistair.francis, bin.meng, liweiwei, dbarboza, zhiwei_liu,
atishp, apatel, beeman, tech-control-transfer-records
Reviewed-by: Jason Chien <jason.chien@sifive.com>
On 2024/6/19 下午 11:27, Rajnesh Kanwal wrote:
> Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
> Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
> ---
> target/riscv/insn32.decode | 1 -
> target/riscv/insn_trans/trans_privileged.c.inc | 5 -----
> 2 files changed, 6 deletions(-)
>
> diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
> index f22df04cfd..9cb1a1b4ec 100644
> --- a/target/riscv/insn32.decode
> +++ b/target/riscv/insn32.decode
> @@ -112,7 +112,6 @@ sret 0001000 00010 00000 000 00000 1110011
> mret 0011000 00010 00000 000 00000 1110011
> wfi 0001000 00101 00000 000 00000 1110011
> sfence_vma 0001001 ..... ..... 000 00000 1110011 @sfence_vma
> -sfence_vm 0001000 00100 ..... 000 00000 1110011 @sfence_vm
>
> # *** RV32I Base Instruction Set ***
> lui .................... ..... 0110111 @u
> diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
> index bc5263a4e0..4eccdddeaa 100644
> --- a/target/riscv/insn_trans/trans_privileged.c.inc
> +++ b/target/riscv/insn_trans/trans_privileged.c.inc
> @@ -127,8 +127,3 @@ static bool trans_sfence_vma(DisasContext *ctx, arg_sfence_vma *a)
> #endif
> return false;
> }
> -
> -static bool trans_sfence_vm(DisasContext *ctx, arg_sfence_vm *a)
> -{
> - return false;
> -}
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 3/6] target/riscv: Add support for Control Transfer Records extension CSRs.
2024-06-19 15:27 ` [PATCH v2 3/6] target/riscv: Add support for Control Transfer Records extension CSRs Rajnesh Kanwal
@ 2024-06-25 8:28 ` Jason Chien
2024-08-27 9:28 ` Frank Chang
1 sibling, 0 replies; 21+ messages in thread
From: Jason Chien @ 2024-06-25 8:28 UTC (permalink / raw)
To: Rajnesh Kanwal, qemu-riscv, qemu-devel
Cc: alistair.francis, bin.meng, liweiwei, dbarboza, zhiwei_liu,
atishp, apatel, beeman, tech-control-transfer-records
Hi Rajnesh,
On 2024/6/19 下午 11:27, Rajnesh Kanwal wrote:
> This commit adds support for [m|s|vs]ctrcontrol, sctrstatus and
> sctrdepth CSRs handling.
>
> Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
> ---
> target/riscv/cpu.h | 5 ++
> target/riscv/cpu_cfg.h | 2 +
> target/riscv/csr.c | 128 +++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 135 insertions(+)
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index a185e2d494..3d4d5172b8 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -263,6 +263,11 @@ struct CPUArchState {
> target_ulong mcause;
> target_ulong mtval; /* since: priv-1.10.0 */
>
> + uint64_t mctrctl;
> + uint32_t sctrdepth;
> + uint32_t sctrstatus;
> + uint64_t vsctrctl;
> +
> /* Machine and Supervisor interrupt priorities */
> uint8_t miprio[64];
> uint8_t siprio[64];
> diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h
> index d9354dc80a..d329a65811 100644
> --- a/target/riscv/cpu_cfg.h
> +++ b/target/riscv/cpu_cfg.h
> @@ -123,6 +123,8 @@ struct RISCVCPUConfig {
> bool ext_zvfhmin;
> bool ext_smaia;
> bool ext_ssaia;
> + bool ext_smctr;
> + bool ext_ssctr;
> bool ext_sscofpmf;
> bool ext_smepmp;
> bool rvv_ta_all_1s;
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index 2f92e4b717..0b5bf4d050 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -621,6 +621,48 @@ static RISCVException pointer_masking(CPURISCVState *env, int csrno)
> return RISCV_EXCP_ILLEGAL_INST;
> }
>
> +/*
> + * M-mode:
> + * Without ext_smctr raise illegal inst excep.
> + * Otherwise everything is accessible to m-mode.
> + *
> + * S-mode:
> + * Without ext_ssctr or mstateen.ctr raise illegal inst excep.
> + * Otherwise everything other than mctrctl is accessible.
> + *
> + * VS-mode:
> + * Without ext_ssctr or mstateen.ctr raise illegal inst excep.
> + * Without hstateen.ctr raise virtual illegal inst excep.
> + * Otherwise allow sctrctl (vsctrctl), sctrstatus, 0x200-0x2ff entry range.
> + * Always raise illegal instruction exception for sctrdepth.
> + */
> +static RISCVException ctr_mmode(CPURISCVState *env, int csrno)
> +{
> + /* Check if smctr-ext is present */
> + if (riscv_cpu_cfg(env)->ext_smctr) {
> + return RISCV_EXCP_NONE;
> + }
> +
> + return RISCV_EXCP_ILLEGAL_INST;
> +}
> +
> +static RISCVException ctr_smode(CPURISCVState *env, int csrno)
> +{
> + const RISCVCPUConfig *cfg = riscv_cpu_cfg(env);
> +
> + if (!cfg->ext_smctr && !cfg->ext_ssctr) {
> + return RISCV_EXCP_ILLEGAL_INST;
> + }
> +
> + RISCVException ret = smstateen_acc_ok(env, 0, SMSTATEEN0_CTR);
> + if (ret == RISCV_EXCP_NONE && csrno == CSR_SCTRDEPTH &&
> + env->virt_enabled) {
> + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
> + }
> +
> + return ret;
> +}
> +
> static RISCVException aia_hmode(CPURISCVState *env, int csrno)
> {
> int ret;
> @@ -3835,6 +3877,86 @@ static RISCVException write_satp(CPURISCVState *env, int csrno,
> return RISCV_EXCP_NONE;
> }
>
> +static RISCVException rmw_sctrdepth(CPURISCVState *env, int csrno,
> + target_ulong *ret_val,
> + target_ulong new_val, target_ulong wr_mask)
> +{
> + uint64_t mask = wr_mask & SCTRDEPTH_MASK;
> +
> + if (ret_val) {
> + *ret_val = env->sctrdepth;
> + }
> +
> + env->sctrdepth = (env->sctrdepth & ~mask) | (new_val & mask);
> +
> + /* Correct depth. */
> + if (wr_mask & SCTRDEPTH_MASK) {
We can use the variable "mask" here.
if (mask) {}
> + uint64_t depth = get_field(env->sctrdepth, SCTRDEPTH_MASK);
> +
> + if (depth > SCTRDEPTH_MAX) {
> + depth = SCTRDEPTH_MAX;
> + env->sctrdepth = set_field(env->sctrdepth, SCTRDEPTH_MASK, depth);
> + }
> +
> + /* Update sctrstatus.WRPTR with a legal value */
> + depth = 16 << depth;
> + env->sctrstatus =
> + env->sctrstatus & (~SCTRSTATUS_WRPTR_MASK | (depth - 1));
> + }
> +
> + return RISCV_EXCP_NONE;
> +}
> +
> +static RISCVException rmw_sctrstatus(CPURISCVState *env, int csrno,
> + target_ulong *ret_val,
> + target_ulong new_val, target_ulong wr_mask)
> +{
> + uint32_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
> + uint32_t mask = wr_mask & SCTRSTATUS_MASK;
> +
> + if (ret_val) {
> + *ret_val = env->sctrstatus;
> + }
> +
> + env->sctrstatus = (env->sctrstatus & ~mask) | (new_val & mask);
> +
> + /* Update sctrstatus.WRPTR with a legal value */
> + env->sctrstatus = env->sctrstatus & (~SCTRSTATUS_WRPTR_MASK | (depth - 1));
> +
> + return RISCV_EXCP_NONE;
> +}
> +
> +static RISCVException rmw_xctrctl(CPURISCVState *env, int csrno,
> + target_ulong *ret_val,
> + target_ulong new_val, target_ulong wr_mask)
> +{
> + uint64_t csr_mask, mask = wr_mask;
> + uint64_t *ctl_ptr = &env->mctrctl;
> +
> + if (csrno == CSR_MCTRCTL) {
> + csr_mask = MCTRCTL_MASK;
> + } else if (csrno == CSR_SCTRCTL && !env->virt_enabled) {
> + csr_mask = SCTRCTL_MASK;
> + } else {
> + /*
> + * This is for csrno == CSR_SCTRCTL and env->virt_enabled == true
> + * or csrno == CSR_VSCTRCTL.
> + */
> + csr_mask = VSCTRCTL_MASK;
> + ctl_ptr = &env->vsctrctl;
> + }
> +
> + mask &= csr_mask;
> +
> + if (ret_val) {
> + *ret_val = *ctl_ptr & csr_mask;
> + }
> +
> + *ctl_ptr = (*ctl_ptr & ~mask) | (new_val & mask);
> +
> + return RISCV_EXCP_NONE;
> +}
> +
> static RISCVException read_vstopi(CPURISCVState *env, int csrno,
> target_ulong *val)
> {
> @@ -5771,6 +5893,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
> [CSR_SPMBASE] = { "spmbase", pointer_masking, read_spmbase,
> write_spmbase },
>
> + [CSR_MCTRCTL] = { "mctrctl", ctr_mmode, NULL, NULL, rmw_xctrctl },
> + [CSR_SCTRCTL] = { "sctrctl", ctr_smode, NULL, NULL, rmw_xctrctl },
> + [CSR_VSCTRCTL] = { "vsctrctl", ctr_smode, NULL, NULL, rmw_xctrctl },
> + [CSR_SCTRDEPTH] = { "sctrdepth", ctr_smode, NULL, NULL, rmw_sctrdepth },
> + [CSR_SCTRSTATUS] = { "sctrstatus", ctr_smode, NULL, NULL, rmw_sctrstatus },
Would you mind aligning the right curly bracket like other CSR entries?
> +
> /* Performance Counters */
> [CSR_HPMCOUNTER3] = { "hpmcounter3", ctr, read_hpmcounter },
> [CSR_HPMCOUNTER4] = { "hpmcounter4", ctr, read_hpmcounter },
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 5/6] target/riscv: Add CTR sctrclr instruction.
2024-06-19 15:27 ` [PATCH v2 5/6] target/riscv: Add CTR sctrclr instruction Rajnesh Kanwal
@ 2024-06-25 9:05 ` Jason Chien
0 siblings, 0 replies; 21+ messages in thread
From: Jason Chien @ 2024-06-25 9:05 UTC (permalink / raw)
To: Rajnesh Kanwal, qemu-riscv, qemu-devel
Cc: alistair.francis, bin.meng, liweiwei, dbarboza, zhiwei_liu,
atishp, apatel, beeman, tech-control-transfer-records
Hi Rajnesh,
On 2024/6/19 下午 11:27, Rajnesh Kanwal wrote:
> CTR extension adds a new instruction sctrclr to quickly
> clear the recorded entries buffer.
>
> Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
> ---
> target/riscv/cpu.h | 1 +
> target/riscv/cpu_helper.c | 7 ++++
> target/riscv/helper.h | 1 +
> target/riscv/insn32.decode | 1 +
> .../riscv/insn_trans/trans_privileged.c.inc | 10 ++++++
> target/riscv/op_helper.c | 33 +++++++++++++++++++
> 6 files changed, 53 insertions(+)
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index e32f5ab146..fdc18a782a 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -572,6 +572,7 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en);
> void riscv_ctr_freeze(CPURISCVState *env, uint64_t freeze_mask, bool virt);
> void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst,
> uint64_t type, target_ulong prev_priv, bool prev_virt);
> +void riscv_ctr_clear(CPURISCVState *env);
>
> void riscv_translate_init(void);
> G_NORETURN void riscv_raise_exception(CPURISCVState *env,
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index 1537602e1b..d98628cfe3 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -702,6 +702,13 @@ void riscv_ctr_freeze(CPURISCVState *env, uint64_t freeze_mask, bool virt)
> }
> }
>
> +void riscv_ctr_clear(CPURISCVState *env)
> +{
> + memset(env->ctr_src, 0x0, sizeof(env->ctr_src));
> + memset(env->ctr_dst, 0x0, sizeof(env->ctr_dst));
> + memset(env->ctr_data, 0x0, sizeof(env->ctr_data));
> +}
> +
> static uint64_t riscv_ctr_priv_to_mask(target_ulong priv, bool virt)
> {
> switch (priv) {
> diff --git a/target/riscv/helper.h b/target/riscv/helper.h
> index b8fb7c8734..a3b2d87527 100644
> --- a/target/riscv/helper.h
> +++ b/target/riscv/helper.h
> @@ -131,6 +131,7 @@ DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl)
> #ifndef CONFIG_USER_ONLY
> DEF_HELPER_2(sret, tl, env, tl)
> DEF_HELPER_2(mret, tl, env, tl)
> +DEF_HELPER_1(ctr_clear, void, env)
> DEF_HELPER_1(wfi, void, env)
> DEF_HELPER_1(wrs_nto, void, env)
> DEF_HELPER_1(tlb_flush, void, env)
> diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
> index 9cb1a1b4ec..d3d38c7c68 100644
> --- a/target/riscv/insn32.decode
> +++ b/target/riscv/insn32.decode
> @@ -107,6 +107,7 @@
> # *** Privileged Instructions ***
> ecall 000000000000 00000 000 00000 1110011
> ebreak 000000000001 00000 000 00000 1110011
> +sctrclr 000100000100 00000 000 00000 1110011
> uret 0000000 00010 00000 000 00000 1110011
> sret 0001000 00010 00000 000 00000 1110011
> mret 0011000 00010 00000 000 00000 1110011
> diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
> index 339d659151..dd9da8651f 100644
> --- a/target/riscv/insn_trans/trans_privileged.c.inc
> +++ b/target/riscv/insn_trans/trans_privileged.c.inc
> @@ -69,6 +69,16 @@ static bool trans_ebreak(DisasContext *ctx, arg_ebreak *a)
> return true;
> }
>
> +static bool trans_sctrclr(DisasContext *ctx, arg_sctrclr *a)
> +{
> +#ifndef CONFIG_USER_ONLY
> + gen_helper_ctr_clear(tcg_env);
This will always generate a helper function call, which can be avoided
by checking the existence of Smctr and Ssctr here instead of checking
them in the helper function.
> + return true;
> +#else
> + return false;
> +#endif
> +}
> +
> static bool trans_uret(DisasContext *ctx, arg_uret *a)
> {
> return false;
> diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
> index 5a1e92c45e..15a770360e 100644
> --- a/target/riscv/op_helper.c
> +++ b/target/riscv/op_helper.c
> @@ -475,6 +475,39 @@ void helper_ctr_branch(CPURISCVState *env, target_ulong src, target_ulong dest,
> }
> }
>
> +void helper_ctr_clear(CPURISCVState *env)
> +{
> + if (!riscv_cpu_cfg(env)->ext_ssctr && !riscv_cpu_cfg(env)->ext_smctr) {
> + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> + }
> +
> + /*
> + * It's safe to call smstateen_acc_ok() for umode access regardless of the
> + * state of bit 54 (CTR bit in case of m/hstateen) of sstateen. If the bit
> + * is zero, smstateen_acc_ok() will return the correct exception code and
> + * if it's one, smstateen_acc_ok() will return RISCV_EXCP_NONE. In that
> + * scenario the U-mode check below will handle that case.
> + */
> + RISCVException ret = smstateen_acc_ok(env, 0, SMSTATEEN0_CTR);
> + if (ret != RISCV_EXCP_NONE) {
> + riscv_raise_exception(env, ret, GETPC());
> + }
> +
> + if (env->priv == PRV_U) {
> + /*
> + * One corner case is when sctrclr is executed from VU-mode and
> + * mstateen.CTR = 0, in which case we are supposed to raise
> + * RISCV_EXCP_ILLEGAL_INST. This case is already handled in
> + * smstateen_acc_ok().
> + */
> + uint32_t excep = env->virt_enabled ? RISCV_EXCP_VIRT_INSTRUCTION_FAULT :
> + RISCV_EXCP_ILLEGAL_INST;
> + riscv_raise_exception(env, excep, GETPC());
> + }
> +
> + riscv_ctr_clear(env);
> +}
> +
> void helper_wfi(CPURISCVState *env)
> {
> CPUState *cs = env_cpu(env);
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 6/6] target/riscv: Add support to access ctrsource, ctrtarget, ctrdata regs.
2024-06-19 15:27 ` [PATCH v2 6/6] target/riscv: Add support to access ctrsource, ctrtarget, ctrdata regs Rajnesh Kanwal
@ 2024-06-25 9:48 ` Jason Chien
0 siblings, 0 replies; 21+ messages in thread
From: Jason Chien @ 2024-06-25 9:48 UTC (permalink / raw)
To: Rajnesh Kanwal, qemu-riscv, qemu-devel
Cc: alistair.francis, bin.meng, liweiwei, dbarboza, zhiwei_liu,
atishp, apatel, beeman, tech-control-transfer-records
Hi Rajnesh,
On 2024/6/19 下午 11:27, Rajnesh Kanwal wrote:
> CTR entries are accessed using ctrsource, ctrtarget and ctrdata
> registers using smcsrind/sscsrind extension. This commits extends
> the csrind extension to support CTR registers.
>
> ctrsource is accessible through xireg CSR, ctrtarget is accessible
> through xireg1 and ctrdata is accessible through xireg2 CSR.
>
> CTR supports maximum depth of 256 entries which are accessed using
> xiselect range 0x200 to 0x2ff.
>
> This commits also adds properties to enable CTR extension. CTR can be
> enabled using smctr=true and ssctr=true now.
>
> Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
> ---
> target/riscv/cpu.c | 4 +
> target/riscv/csr.c | 148 ++++++++++++++++++++++++++++++++++++-
> target/riscv/tcg/tcg-cpu.c | 6 ++
> 3 files changed, 157 insertions(+), 1 deletion(-)
>
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index 30bdfc22ae..a77b1d5caf 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -193,6 +193,8 @@ const RISCVIsaExtData isa_edata_arr[] = {
> ISA_EXT_DATA_ENTRY(sstvala, PRIV_VERSION_1_12_0, has_priv_1_12),
> ISA_EXT_DATA_ENTRY(sstvecd, PRIV_VERSION_1_12_0, has_priv_1_12),
> ISA_EXT_DATA_ENTRY(svade, PRIV_VERSION_1_11_0, ext_svade),
> + ISA_EXT_DATA_ENTRY(smctr, PRIV_VERSION_1_12_0, ext_smctr),
> + ISA_EXT_DATA_ENTRY(ssctr, PRIV_VERSION_1_12_0, ext_ssctr),
> ISA_EXT_DATA_ENTRY(svadu, PRIV_VERSION_1_12_0, ext_svadu),
> ISA_EXT_DATA_ENTRY(svinval, PRIV_VERSION_1_12_0, ext_svinval),
> ISA_EXT_DATA_ENTRY(svnapot, PRIV_VERSION_1_12_0, ext_svnapot),
> @@ -1473,6 +1475,8 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = {
> MULTI_EXT_CFG_BOOL("sscsrind", ext_sscsrind, false),
> MULTI_EXT_CFG_BOOL("smcdeleg", ext_smcdeleg, false),
> MULTI_EXT_CFG_BOOL("ssccfg", ext_ssccfg, false),
> + MULTI_EXT_CFG_BOOL("smctr", ext_smctr, false),
> + MULTI_EXT_CFG_BOOL("ssctr", ext_ssctr, false),
> MULTI_EXT_CFG_BOOL("zifencei", ext_zifencei, true),
> MULTI_EXT_CFG_BOOL("zicsr", ext_zicsr, true),
> MULTI_EXT_CFG_BOOL("zihintntl", ext_zihintntl, true),
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index 0b5bf4d050..3ed9f95a4f 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -2278,6 +2278,13 @@ static bool xiselect_cd_range(target_ulong isel)
> return (ISELECT_CD_FIRST <= isel && isel <= ISELECT_CD_LAST);
> }
>
> +static bool xiselect_ctr_range(int csrno, target_ulong isel)
> +{
> + /* MIREG-MIREG6 for the range 0x200-0x2ff are not used by CTR. */
> + return CTR_ENTRIES_FIRST <= isel && isel <= CTR_ENTRIES_LAST &&
> + csrno < CSR_MIREG;
> +}
> +
> static int rmw_iprio(target_ulong xlen,
> target_ulong iselect, uint8_t *iprio,
> target_ulong *val, target_ulong new_val,
> @@ -2323,6 +2330,124 @@ static int rmw_iprio(target_ulong xlen,
> return 0;
> }
>
> +static int rmw_ctrsource(CPURISCVState *env, int isel, target_ulong *val,
> + target_ulong new_val, target_ulong wr_mask)
> +{
> + /*
> + * CTR arrays are treated as circular buffers and TOS always points to next
> + * empty slot, keeping TOS - 1 always pointing to latest entry. Given entry
> + * 0 is always the latest one, traversal is a bit different here. See the
> + * below example.
> + *
> + * Depth = 16.
> + *
> + * idx [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F]
> + * TOS H
> + * entry 6 5 4 3 2 1 0 F E D C B A 9 8 7
> + */
> + const uint64_t entry = isel - CTR_ENTRIES_FIRST;
> + const uint64_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
> + uint64_t idx;
> +
> + /* Entry greater than depth-1 is read-only zero */
> + if (entry >= depth) {
> + if (val) {
> + *val = 0;
> + }
> + return 0;
> + }
> +
> + idx = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK);
> + idx = (idx - entry - 1) & (depth - 1);
> +
> + if (val) {
> + *val = env->ctr_src[idx];
> + }
> +
> + env->ctr_src[idx] = (env->ctr_src[idx] & ~wr_mask) | (new_val & wr_mask);
> +
> + return 0;
> +}
> +
> +static int rmw_ctrtarget(CPURISCVState *env, int isel, target_ulong *val,
> + target_ulong new_val, target_ulong wr_mask)
> +{
> + /*
> + * CTR arrays are treated as circular buffers and TOS always points to next
> + * empty slot, keeping TOS - 1 always pointing to latest entry. Given entry
> + * 0 is always the latest one, traversal is a bit different here. See the
> + * below example.
> + *
> + * Depth = 16.
> + *
> + * idx [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F]
> + * head H
> + * entry 6 5 4 3 2 1 0 F E D C B A 9 8 7
> + */
> + const uint64_t entry = isel - CTR_ENTRIES_FIRST;
> + const uint64_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
> + uint64_t idx;
> +
> + /* Entry greater than depth-1 is read-only zero */
> + if (entry >= depth) {
> + if (val) {
> + *val = 0;
> + }
> + return 0;
> + }
> +
> + idx = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK);
> + idx = (idx - entry - 1) & (depth - 1);
> +
> + if (val) {
> + *val = env->ctr_dst[idx];
> + }
> +
> + env->ctr_dst[idx] = (env->ctr_dst[idx] & ~wr_mask) | (new_val & wr_mask);
> +
> + return 0;
> +}
> +
> +static int rmw_ctrdata(CPURISCVState *env, int isel, target_ulong *val,
> + target_ulong new_val, target_ulong wr_mask)
> +{
> + /*
> + * CTR arrays are treated as circular buffers and TOS always points to next
> + * empty slot, keeping TOS - 1 always pointing to latest entry. Given entry
> + * 0 is always the latest one, traversal is a bit different here. See the
> + * below example.
> + *
> + * Depth = 16.
> + *
> + * idx [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F]
> + * head H
> + * entry 6 5 4 3 2 1 0 F E D C B A 9 8 7
> + */
> + const uint64_t entry = isel - CTR_ENTRIES_FIRST;
> + const uint64_t mask = wr_mask & CTRDATA_MASK;
> + const uint64_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
> + uint64_t idx;
> +
> + /* Entry greater than depth-1 is read-only zero */
> + if (entry >= depth) {
> + if (val) {
> + *val = 0;
> + }
> + return 0;
> + }
> +
> + idx = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK);
> + idx = (idx - entry - 1) & (depth - 1);
> +
> + if (val) {
> + *val = env->ctr_data[idx];
> + }
> +
> + env->ctr_data[idx] = (env->ctr_data[idx] & ~mask) | (new_val & mask);
> +
> + return 0;
> +}
> +
> static RISCVException rmw_xireg_aia(CPURISCVState *env, int csrno,
> target_ulong isel, target_ulong *val,
> target_ulong new_val, target_ulong wr_mask)
> @@ -2473,6 +2598,25 @@ done:
> return ret;
> }
>
> +static int rmw_xireg_ctr(CPURISCVState *env, int csrno,
> + target_ulong isel, target_ulong *val,
> + target_ulong new_val, target_ulong wr_mask)
> +{
> + if (!riscv_cpu_cfg(env)->ext_smctr && !riscv_cpu_cfg(env)->ext_ssctr) {
> + return -EINVAL;
> + }
> +
> + if (csrno == CSR_SIREG || csrno == CSR_VSIREG) {
> + return rmw_ctrsource(env, isel, val, new_val, wr_mask);
> + } else if (csrno == CSR_SIREG2 || csrno == CSR_VSIREG2) {
> + return rmw_ctrtarget(env, isel, val, new_val, wr_mask);
> + } else if (csrno == CSR_SIREG3 || csrno == CSR_VSIREG3) {
> + return rmw_ctrdata(env, isel, val, new_val, wr_mask);
> + }
[s|vs]ireg4/5/6 are read-only 0. We should make *val=0 for such cases.
The spec states:
When siselect holds a value in this range, sireg provides access to
ctrsource, sireg2 provides access to ctrtarget, and sireg3 provides
access to ctrdata. sireg4, sireg5, and sireg6 are read-only 0.
When vsiselect holds a value in 0x200..0x2FF, the vsireg* registers
provide access to the same CTR entry register state as the analogous
sireg* registers.
> +
> + return 0;
> +}
> +
> /*
> * rmw_xireg_sxcsrind: Perform indirect access to xireg and xireg2-xireg6
> *
> @@ -2484,11 +2628,13 @@ static int rmw_xireg_sxcsrind(CPURISCVState *env, int csrno,
> target_ulong isel, target_ulong *val,
> target_ulong new_val, target_ulong wr_mask)
> {
> - int ret = -EINVAL;
> bool virt = csrno == CSR_VSIREG ? true : false;
> + int ret = -EINVAL;
>
> if (xiselect_cd_range(isel)) {
> ret = rmw_xireg_cd(env, csrno, isel, val, new_val, wr_mask);
> + } else if (xiselect_ctr_range(csrno, isel)) {
> + ret = rmw_xireg_ctr(env, csrno, isel, val, new_val, wr_mask);
> } else {
> /*
> * As per the specification, access to unimplented region is undefined
> diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c
> index 683f604d9f..df75bb190b 100644
> --- a/target/riscv/tcg/tcg-cpu.c
> +++ b/target/riscv/tcg/tcg-cpu.c
> @@ -726,6 +726,12 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
> cpu->pmu_avail_ctrs = 0;
> }
>
> + if ((cpu->cfg.ext_smctr || cpu->cfg.ext_ssctr) &&
> + (!riscv_has_ext(env, RVS) || !cpu->cfg.ext_sscsrind)) {
I see other extensions check dependency with cpu_cfg_ext_is_user_set()
and an error is reported if the dependent extension is disabled by the
user. Is it better to put it this way?
> + error_setg(errp, "Smctr and Ssctr require S-mode and Sscsrind");
> + return;
> + }
> +
> /*
> * Disable isa extensions based on priv spec after we
> * validated and set everything we need.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 0/6] target/riscv: Add support for Control Transfer Records Ext.
2024-06-19 15:27 [PATCH v2 0/6] target/riscv: Add support for Control Transfer Records Ext Rajnesh Kanwal
` (5 preceding siblings ...)
2024-06-19 15:27 ` [PATCH v2 6/6] target/riscv: Add support to access ctrsource, ctrtarget, ctrdata regs Rajnesh Kanwal
@ 2024-06-26 0:01 ` Alistair Francis
2024-06-26 6:18 ` Jason Chien
7 siblings, 0 replies; 21+ messages in thread
From: Alistair Francis @ 2024-06-26 0:01 UTC (permalink / raw)
To: Rajnesh Kanwal
Cc: qemu-riscv, qemu-devel, alistair.francis, bin.meng, liweiwei,
dbarboza, zhiwei_liu, atishp, apatel, beeman,
tech-control-transfer-records, jason.chien
On Thu, Jun 20, 2024 at 1:28 AM Rajnesh Kanwal <rkanwal@rivosinc.com> wrote:
>
> This series enables Control Transfer Records extension support on riscv
> platform. This extension is similar to Arch LBR in x86 and BRBE in ARM.
> The Extension has been stable and the latest release can be found here [0]
Can you be explicit about the exact version. RISC-V specs have a
tendency to change and the latest release today might not be the
latest tomorrow.
v1.0.0_rc2 at https://github.com/riscv/riscv-control-transfer-records/releases/tag/v1.0.0_rc2
for example would be really helpful
>
> CTR extension depends on couple of other extensions:
>
> 1. S[m|s]csrind : The indirect CSR extension [1] which defines additional
> ([M|S|VS]IREG2-[M|S|VS]IREG6) register to address size limitation of
> RISC-V CSR address space. CTR access ctrsource, ctrtartget and ctrdata
> CSRs using sscsrind extension.
>
> 2. Smstateen: The mstateen bit[54] controls the access to the CTR ext to
> S-mode.
>
> 3. Sscofpmf: Counter overflow and privilege mode filtering. [2]
>
> The series is based on Smcdeleg/Ssccfg counter delegation extension [3]
> patches. CTR itself doesn't depend on counter delegation support. This
> rebase is basically to include the Smcsrind patches.
Same comment here about specific versions
>
> Due to the dependency of these extensions, the following extensions must be
> enabled to use the control transfer records feature.
>
> "smstateen=true,sscofpmf=true,smcsrind=true,sscsrind=true,smctr=true,ssctr=true"
We shouldn't have this dependency if the actual spec doesn't have it.
Even if the spec does have the dependency we should the implication
rules to enable the required extensions if a user asks for CTR
Alistair
>
> Here is the link to a quick guide [5] to setup and run a basic perf demo on
> Linux to use CTR Ext.
>
> The Qemu patches can be found here:
> https://github.com/rajnesh-kanwal/qemu/tree/ctr_upstream_v2
>
> The opensbi patch can be found here:
> https://github.com/rajnesh-kanwal/opensbi/tree/ctr_upstream_v2
>
> The Linux kernel patches can be found here:
> https://github.com/rajnesh-kanwal/linux/tree/ctr_upstream_v2
>
> [0]: https://github.com/riscv/riscv-control-transfer-records/release
> [1]: https://github.com/riscv/riscv-indirect-csr-access
> [2]: https://github.com/riscvarchive/riscv-count-overflow/tree/main
> [3]: https://github.com/riscv/riscv-smcdeleg-ssccfg
> [4]: https://lore.kernel.org/all/20240217000134.3634191-1-atishp@rivosinc.com/
> [5]: https://github.com/rajnesh-kanwal/linux/wiki/Running-CTR-basic-demo-on-QEMU-RISC%E2%80%90V-Virt-machine
>
> Changelog:
>
> v2: Lots of improvements based on Jason Chien's feedback including:
> - Added CTR recording for cm.jalt, cm.jt, cm.popret, cm.popretz.
> - Fixed and added more CTR extension enable checks.
> - Fixed CTR CSR predicate functions.
> - Fixed external trap xTE bit checks.
> - One fix in freeze function for VS-mode.
> - Lots of minor code improvements.
> - Added checks in sctrclr instruction helper.
>
> v1:
> - https://github.com/rajnesh-kanwal/qemu/tree/ctr_upstream
>
>
> Rajnesh Kanwal (6):
> target/riscv: Remove obsolete sfence.vm instruction
> target/riscv: Add Control Transfer Records CSR definitions.
> target/riscv: Add support for Control Transfer Records extension CSRs.
> target/riscv: Add support to record CTR entries.
> target/riscv: Add CTR sctrclr instruction.
> target/riscv: Add support to access ctrsource, ctrtarget, ctrdata
> regs.
>
> target/riscv/cpu.c | 4 +
> target/riscv/cpu.h | 14 +
> target/riscv/cpu_bits.h | 154 ++++++++++
> target/riscv/cpu_cfg.h | 2 +
> target/riscv/cpu_helper.c | 265 +++++++++++++++++
> target/riscv/csr.c | 276 +++++++++++++++++-
> target/riscv/helper.h | 9 +-
> target/riscv/insn32.decode | 2 +-
> .../riscv/insn_trans/trans_privileged.c.inc | 21 +-
> target/riscv/insn_trans/trans_rvi.c.inc | 31 ++
> target/riscv/insn_trans/trans_rvzce.c.inc | 20 ++
> target/riscv/op_helper.c | 159 +++++++++-
> target/riscv/tcg/tcg-cpu.c | 6 +
> target/riscv/translate.c | 10 +
> 14 files changed, 960 insertions(+), 13 deletions(-)
>
> --
> 2.34.1
>
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 2/6] target/riscv: Add Control Transfer Records CSR definitions.
2024-06-19 15:27 ` [PATCH v2 2/6] target/riscv: Add Control Transfer Records CSR definitions Rajnesh Kanwal
@ 2024-06-26 0:07 ` Alistair Francis
2024-06-26 6:00 ` Jason Chien
1 sibling, 0 replies; 21+ messages in thread
From: Alistair Francis @ 2024-06-26 0:07 UTC (permalink / raw)
To: Rajnesh Kanwal
Cc: qemu-riscv, qemu-devel, alistair.francis, bin.meng, liweiwei,
dbarboza, zhiwei_liu, atishp, apatel, beeman,
tech-control-transfer-records, jason.chien
On Thu, Jun 20, 2024 at 1:28 AM Rajnesh Kanwal <rkanwal@rivosinc.com> wrote:
>
> The Control Transfer Records (CTR) extension provides a method to
> record a limited branch history in register-accessible internal chip
> storage.
>
> This extension is similar to Arch LBR in x86 and BRBE in ARM.
> The Extension has been stable and the latest release can be found here
> https://github.com/riscv/riscv-control-transfer-records/release
Specific version please. Also the spec is not yet ratified, so it
isn't actually stable in the general sense.
>
> Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
Otherwise
Acked-by: Alistair Francis <alistair.francis@wdc.com>
Alistair
> ---
> target/riscv/cpu_bits.h | 154 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 154 insertions(+)
>
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index 86e15381c8..71ddccaf1a 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -242,6 +242,17 @@
> #define CSR_SIEH 0x114
> #define CSR_SIPH 0x154
>
> +/* Machine-Level Control transfer records CSRs */
> +#define CSR_MCTRCTL 0x34e
> +
> +/* Supervisor-Level Control transfer records CSRs */
> +#define CSR_SCTRCTL 0x14e
> +#define CSR_SCTRSTATUS 0x14f
> +#define CSR_SCTRDEPTH 0x15f
> +
> +/* VS-Level Control transfer records CSRs */
> +#define CSR_VSCTRCTL 0x24e
> +
> /* Hpervisor CSRs */
> #define CSR_HSTATUS 0x600
> #define CSR_HEDELEG 0x602
> @@ -339,6 +350,7 @@
> #define SMSTATEEN0_CS (1ULL << 0)
> #define SMSTATEEN0_FCSR (1ULL << 1)
> #define SMSTATEEN0_JVT (1ULL << 2)
> +#define SMSTATEEN0_CTR (1ULL << 54)
> #define SMSTATEEN0_HSCONTXT (1ULL << 57)
> #define SMSTATEEN0_IMSIC (1ULL << 58)
> #define SMSTATEEN0_AIA (1ULL << 59)
> @@ -854,6 +866,148 @@ typedef enum RISCVException {
> #define UMTE_U_PM_INSN U_PM_INSN
> #define UMTE_MASK (UMTE_U_PM_ENABLE | MMTE_U_PM_CURRENT | UMTE_U_PM_INSN)
>
> +/* mctrctl CSR bits. */
> +#define MCTRCTL_U_ENABLE BIT(0)
> +#define MCTRCTL_S_ENABLE BIT(1)
> +#define MCTRCTL_M_ENABLE BIT(2)
> +#define MCTRCTL_RASEMU BIT(7)
> +#define MCTRCTL_STE BIT(8)
> +#define MCTRCTL_MTE BIT(9)
> +#define MCTRCTL_BPFRZ BIT(11)
> +#define MCTRCTL_LCOFIFRZ BIT(12)
> +#define MCTRCTL_EXCINH BIT(33)
> +#define MCTRCTL_INTRINH BIT(34)
> +#define MCTRCTL_TRETINH BIT(35)
> +#define MCTRCTL_NTBREN BIT(36)
> +#define MCTRCTL_TKBRINH BIT(37)
> +#define MCTRCTL_INDCALL_INH BIT(40)
> +#define MCTRCTL_DIRCALL_INH BIT(41)
> +#define MCTRCTL_INDJUMP_INH BIT(42)
> +#define MCTRCTL_DIRJUMP_INH BIT(43)
> +#define MCTRCTL_CORSWAP_INH BIT(44)
> +#define MCTRCTL_RET_INH BIT(45)
> +#define MCTRCTL_INDOJUMP_INH BIT(46)
> +#define MCTRCTL_DIROJUMP_INH BIT(47)
> +
> +#define MCTRCTL_INH_START 32U
> +
> +#define MCTRCTL_MASK (MCTRCTL_M_ENABLE | MCTRCTL_S_ENABLE | \
> + MCTRCTL_U_ENABLE | MCTRCTL_RASEMU | \
> + MCTRCTL_MTE | MCTRCTL_STE | \
> + MCTRCTL_BPFRZ | MCTRCTL_LCOFIFRZ | \
> + MCTRCTL_EXCINH | MCTRCTL_INTRINH | \
> + MCTRCTL_TRETINH | MCTRCTL_NTBREN | \
> + MCTRCTL_TKBRINH | MCTRCTL_INDCALL_INH | \
> + MCTRCTL_DIRCALL_INH | MCTRCTL_INDJUMP_INH | \
> + MCTRCTL_DIRJUMP_INH | MCTRCTL_CORSWAP_INH | \
> + MCTRCTL_RET_INH | MCTRCTL_INDOJUMP_INH | \
> + MCTRCTL_DIROJUMP_INH)
> +
> +/* sctrctl CSR bits. */
> +#define SCTRCTL_U_ENABLE MCTRCTL_U_ENABLE
> +#define SCTRCTL_S_ENABLE MCTRCTL_S_ENABLE
> +#define SCTRCTL_RASEMU MCTRCTL_RASEMU
> +#define SCTRCTL_STE MCTRCTL_STE
> +#define SCTRCTL_BPFRZ MCTRCTL_BPFRZ
> +#define SCTRCTL_LCOFIFRZ MCTRCTL_LCOFIFRZ
> +#define SCTRCTL_EXCINH MCTRCTL_EXCINH
> +#define SCTRCTL_INTRINH MCTRCTL_INTRINH
> +#define SCTRCTL_TRETINH MCTRCTL_TRETINH
> +#define SCTRCTL_NTBREN MCTRCTL_NTBREN
> +#define SCTRCTL_TKBRINH MCTRCTL_TKBRINH
> +#define SCTRCTL_INDCALL_INH MCTRCTL_INDCALL_INH
> +#define SCTRCTL_DIRCALL_INH MCTRCTL_DIRCALL_INH
> +#define SCTRCTL_INDJUMP_INH MCTRCTL_INDJUMP_INH
> +#define SCTRCTL_DIRJUMP_INH MCTRCTL_DIRJUMP_INH
> +#define SCTRCTL_CORSWAP_INH MCTRCTL_CORSWAP_INH
> +#define SCTRCTL_RET_INH MCTRCTL_RET_INH
> +#define SCTRCTL_INDOJUMP_INH MCTRCTL_INDOJUMP_INH
> +#define SCTRCTL_DIROJUMP_INH MCTRCTL_DIROJUMP_INH
> +
> +#define SCTRCTL_MASK (SCTRCTL_S_ENABLE | SCTRCTL_U_ENABLE | \
> + SCTRCTL_RASEMU | SCTRCTL_STE | \
> + SCTRCTL_BPFRZ | SCTRCTL_LCOFIFRZ | \
> + SCTRCTL_EXCINH | SCTRCTL_INTRINH | \
> + SCTRCTL_TRETINH | SCTRCTL_NTBREN | \
> + SCTRCTL_TKBRINH | SCTRCTL_INDCALL_INH | \
> + SCTRCTL_DIRCALL_INH | SCTRCTL_INDJUMP_INH | \
> + SCTRCTL_DIRJUMP_INH | SCTRCTL_CORSWAP_INH | \
> + SCTRCTL_RET_INH | SCTRCTL_INDOJUMP_INH | \
> + SCTRCTL_DIROJUMP_INH)
> +
> +/* sctrstatus CSR bits. */
> +#define SCTRSTATUS_WRPTR_MASK 0xFF
> +#define SCTRSTATUS_FROZEN BIT(31)
> +#define SCTRSTATUS_MASK (SCTRSTATUS_WRPTR_MASK | SCTRSTATUS_FROZEN)
> +
> +/* sctrdepth CSR bits. */
> +#define SCTRDEPTH_MASK 0x7
> +#define SCTRDEPTH_MIN 0U /* 16 Entries. */
> +#define SCTRDEPTH_MAX 4U /* 256 Entries. */
> +
> +/* vsctrctl CSR bits. */
> +#define VSCTRCTL_VU_ENABLE MCTRCTL_U_ENABLE
> +#define VSCTRCTL_VS_ENABLE MCTRCTL_S_ENABLE
> +#define VSCTRCTL_RASEMU MCTRCTL_RASEMU
> +#define VSCTRCTL_VSTE MCTRCTL_STE
> +#define VSCTRCTL_BPFRZ MCTRCTL_BPFRZ
> +#define VSCTRCTL_LCOFIFRZ MCTRCTL_LCOFIFRZ
> +#define VSCTRCTL_EXCINH MCTRCTL_EXCINH
> +#define VSCTRCTL_INTRINH MCTRCTL_INTRINH
> +#define VSCTRCTL_TRETINH MCTRCTL_TRETINH
> +#define VSCTRCTL_NTBREN MCTRCTL_NTBREN
> +#define VSCTRCTL_TKBRINH MCTRCTL_TKBRINH
> +#define VSCTRCTL_INDCALL_INH MCTRCTL_INDCALL_INH
> +#define VSCTRCTL_DIRCALL_INH MCTRCTL_DIRCALL_INH
> +#define VSCTRCTL_INDJUMP_INH MCTRCTL_INDJUMP_INH
> +#define VSCTRCTL_DIRJUMP_INH MCTRCTL_DIRJUMP_INH
> +#define VSCTRCTL_CORSWAP_INH MCTRCTL_CORSWAP_INH
> +#define VSCTRCTL_RET_INH MCTRCTL_RET_INH
> +#define VSCTRCTL_INDOJUMP_INH MCTRCTL_INDOJUMP_INH
> +#define VSCTRCTL_DIROJUMP_INH MCTRCTL_DIROJUMP_INH
> +
> +#define VSCTRCTL_MASK (VSCTRCTL_VS_ENABLE | VSCTRCTL_VU_ENABLE | \
> + VSCTRCTL_RASEMU | VSCTRCTL_VSTE | \
> + VSCTRCTL_BPFRZ | VSCTRCTL_LCOFIFRZ | \
> + VSCTRCTL_EXCINH | VSCTRCTL_INTRINH | \
> + VSCTRCTL_TRETINH | VSCTRCTL_NTBREN | \
> + VSCTRCTL_TKBRINH | VSCTRCTL_INDCALL_INH | \
> + VSCTRCTL_DIRCALL_INH | VSCTRCTL_INDJUMP_INH | \
> + VSCTRCTL_DIRJUMP_INH | VSCTRCTL_CORSWAP_INH | \
> + VSCTRCTL_RET_INH | VSCTRCTL_INDOJUMP_INH | \
> + VSCTRCTL_DIROJUMP_INH)
> +
> +#define CTR_ENTRIES_FIRST 0x200
> +#define CTR_ENTRIES_LAST 0x2ff
> +
> +#define CTRSOURCE_VALID BIT(0)
> +#define CTRTARGET_MISP BIT(0)
> +
> +#define CTRDATA_TYPE_MASK 0xF
> +#define CTRDATA_CCV BIT(15)
> +#define CTRDATA_CCM_MASK 0xFFF0000
> +#define CTRDATA_CCE_MASK 0xF0000000
> +
> +#define CTRDATA_MASK (CTRDATA_TYPE_MASK | CTRDATA_CCV | \
> + CTRDATA_CCM_MASK | CTRDATA_CCE_MASK)
> +
> +#define CTRDATA_TYPE_NONE 0
> +#define CTRDATA_TYPE_EXCEPTION 1
> +#define CTRDATA_TYPE_INTERRUPT 2
> +#define CTRDATA_TYPE_EXCEP_INT_RET 3
> +#define CTRDATA_TYPE_NONTAKEN_BRANCH 4
> +#define CTRDATA_TYPE_TAKEN_BRANCH 5
> +#define CTRDATA_TYPE_RESERVED_0 6
> +#define CTRDATA_TYPE_RESERVED_1 7
> +#define CTRDATA_TYPE_INDIRECT_CALL 8
> +#define CTRDATA_TYPE_DIRECT_CALL 9
> +#define CTRDATA_TYPE_INDIRECT_JUMP 10
> +#define CTRDATA_TYPE_DIRECT_JUMP 11
> +#define CTRDATA_TYPE_CO_ROUTINE_SWAP 12
> +#define CTRDATA_TYPE_RETURN 13
> +#define CTRDATA_TYPE_OTHER_INDIRECT_JUMP 14
> +#define CTRDATA_TYPE_OTHER_DIRECT_JUMP 15
> +
> /* MISELECT, SISELECT, and VSISELECT bits (AIA) */
> #define ISELECT_IPRIO0 0x30
> #define ISELECT_IPRIO15 0x3f
> --
> 2.34.1
>
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 4/6] target/riscv: Add support to record CTR entries.
2024-06-19 15:27 ` [PATCH v2 4/6] target/riscv: Add support to record CTR entries Rajnesh Kanwal
@ 2024-06-26 3:49 ` Jason Chien
2024-10-15 18:50 ` Rajnesh Kanwal
0 siblings, 1 reply; 21+ messages in thread
From: Jason Chien @ 2024-06-26 3:49 UTC (permalink / raw)
To: Rajnesh Kanwal, qemu-riscv, qemu-devel
Cc: alistair.francis, bin.meng, liweiwei, dbarboza, zhiwei_liu,
atishp, apatel, beeman, tech-control-transfer-records
Hi Rajnesh,
On 2024/6/19 下午 11:27, Rajnesh Kanwal wrote:
> This commit adds logic to records CTR entries of different types
> and adds required hooks in TCG and interrupt/Exception logic to
> record events.
>
> This commit also adds support to invoke freeze CTR logic for breakpoint
> exceptions and counter overflow interrupts.
>
> Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
> ---
> target/riscv/cpu.h | 8 +
> target/riscv/cpu_helper.c | 258 ++++++++++++++++++
> target/riscv/helper.h | 8 +-
> .../riscv/insn_trans/trans_privileged.c.inc | 6 +-
> target/riscv/insn_trans/trans_rvi.c.inc | 31 +++
> target/riscv/insn_trans/trans_rvzce.c.inc | 20 ++
> target/riscv/op_helper.c | 126 ++++++++-
> target/riscv/translate.c | 10 +
> 8 files changed, 461 insertions(+), 6 deletions(-)
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 3d4d5172b8..e32f5ab146 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -268,6 +268,10 @@ struct CPUArchState {
> uint32_t sctrstatus;
> uint64_t vsctrctl;
>
> + uint64_t ctr_src[16 << SCTRDEPTH_MAX];
> + uint64_t ctr_dst[16 << SCTRDEPTH_MAX];
> + uint64_t ctr_data[16 << SCTRDEPTH_MAX];
> +
> /* Machine and Supervisor interrupt priorities */
> uint8_t miprio[64];
> uint8_t siprio[64];
> @@ -565,6 +569,10 @@ RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit);
> #endif
> void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en);
>
> +void riscv_ctr_freeze(CPURISCVState *env, uint64_t freeze_mask, bool virt);
It looks like riscv_ctr_freeze() is only used in
target/riscv/cpu_helper.c. We can make it a static function.
> +void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst,
> + uint64_t type, target_ulong prev_priv, bool prev_virt);
> +
> void riscv_translate_init(void);
> G_NORETURN void riscv_raise_exception(CPURISCVState *env,
> uint32_t exception, uintptr_t pc);
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index a441a03ef4..1537602e1b 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -691,6 +691,246 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv,
> }
> }
>
> +void riscv_ctr_freeze(CPURISCVState *env, uint64_t freeze_mask, bool virt)
> +{
> + uint64_t ctl = virt ? env->mctrctl : env->vsctrctl;
> +
> + assert((freeze_mask & (~(MCTRCTL_BPFRZ | MCTRCTL_LCOFIFRZ))) == 0);
> +
> + if (ctl & freeze_mask) {
> + env->sctrstatus |= SCTRSTATUS_FROZEN;
> + }
> +}
> +
> +static uint64_t riscv_ctr_priv_to_mask(target_ulong priv, bool virt)
> +{
> + switch (priv) {
> + case PRV_M:
> + return MCTRCTL_M_ENABLE;
> + case PRV_S:
> + if (virt) {
> + return VSCTRCTL_VS_ENABLE;
> + }
> + return MCTRCTL_S_ENABLE;
> + case PRV_U:
> + if (virt) {
> + return VSCTRCTL_VU_ENABLE;
> + }
> + return MCTRCTL_U_ENABLE;
> + }
> +
> + g_assert_not_reached();
> +}
> +
> +static uint64_t riscv_ctr_get_control(CPURISCVState *env, target_long priv,
> + bool virt)
> +{
> + switch (priv) {
> + case PRV_M:
> + return env->mctrctl;
> + case PRV_S:
> + case PRV_U:
> + if (virt) {
> + return env->vsctrctl;
> + }
> + return env->mctrctl;
> + }
> +
> + g_assert_not_reached();
> +}
> +
> +/*
> + * This function assumes that src privilege and target privilege are not same
> + * and src privilege is less than target privilege. This includes the virtual
> + * state as well.
> + */
> +static bool riscv_ctr_check_xte(CPURISCVState *env, target_long src_prv,
> + bool src_virt)
This function is problematic. Suppose an external trap traps from U mode
to M mode. We need to check both sctrctl.STE and mctrctl.MTE, but this
function only checks sctrctl.STE.
> +{
> + target_long tgt_prv = env->priv;
> + bool res = true;
> +
> + /*
> + * VS and U mode are same in terms of xTE bits required to record an
> + * external trap. See 6.1.2. External Traps, table 8 External Trap Enable
> + * Requirements. This changes VS to U to simplify the logic a bit.
> + */
> + if (src_virt && src_prv == PRV_S) {
> + src_prv = PRV_U;
> + } else if (env->virt_enabled && tgt_prv == PRV_S) {
> + tgt_prv = PRV_U;
> + }
> +
> + /* VU mode is an outlier here. */
> + if (src_virt && src_prv == PRV_U) {
> + res &= !!(env->vsctrctl & VSCTRCTL_VSTE);
> + }
> +
> + switch (src_prv) {
> + case PRV_U:
> + if (tgt_prv == PRV_U) {
> + break;
> + }
> + res &= !!(env->mctrctl & SCTRCTL_STE);
> + /* fall-through */
> + case PRV_S:
> + if (tgt_prv == PRV_S) {
> + break;
> + }
> + res &= !!(env->mctrctl & MCTRCTL_MTE);
> + /* fall-through */
> + case PRV_M:
> + break;
> + }
> +
> + return res;
> +}
> +
> +/*
> + * Special cases for traps and trap returns:
> + *
> + * 1- Traps, and trap returns, between enabled modes are recorded as normal.
> + * 2- Traps from an inhibited mode to an enabled mode, and trap returns from an
> + * enabled mode back to an inhibited mode, are partially recorded. In such
> + * cases, the PC from the inhibited mode (source PC for traps, and target PC
> + * for trap returns) is 0.
> + *
> + * 3- Trap returns from an inhibited mode to an enabled mode are not recorded.
> + * Traps from an enabled mode to an inhibited mode, known as external traps,
> + * receive special handling.
> + * By default external traps are not recorded, but a handshake mechanism exists
> + * to allow partial recording. Software running in the target mode of the trap
> + * can opt-in to allowing CTR to record traps into that mode even when the mode
> + * is inhibited. The MTE, STE, and VSTE bits allow M-mode, S-mode, and VS-mode,
> + * respectively, to opt-in. When an External Trap occurs, and xTE=1, such that
> + * x is the target privilege mode of the trap, will CTR record the trap. In such
> + * cases, the target PC is 0.
> + */
> +/*
> + * CTR arrays are implemented as circular buffers and new entry is stored at
> + * sctrstatus.WRPTR, but they are presented to software as moving circular
> + * buffers. Which means, software get's the illusion that whenever a new entry
> + * is added the whole buffer is moved by one place and the new entry is added at
> + * the start keeping new entry at idx 0 and older ones follow.
> + *
> + * Depth = 16.
> + *
> + * buffer [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F]
> + * WRPTR W
> + * entry 7 6 5 4 3 2 1 0 F E D C B A 9 8
> + *
> + * When a new entry is added:
> + * buffer [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F]
> + * WRPTR W
> + * entry 8 7 6 5 4 3 2 1 0 F E D C B A 9
> + *
> + * entry here denotes the logical entry number that software can access
> + * using ctrsource, ctrtarget and ctrdata registers. So xiselect 0x200
> + * will return entry 0 i-e buffer[8] and 0x201 will return entry 1 i-e
> + * buffer[7]. Here is how we convert entry to buffer idx.
> + *
> + * entry = isel - CTR_ENTRIES_FIRST;
> + * idx = (sctrstatus.WRPTR - entry - 1) & (depth - 1);
> + */
> +void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst,
> + uint64_t type, target_ulong src_priv, bool src_virt)
> +{
> + bool tgt_virt = env->virt_enabled;
> + uint64_t src_mask = riscv_ctr_priv_to_mask(src_priv, src_virt);
> + uint64_t tgt_mask = riscv_ctr_priv_to_mask(env->priv, tgt_virt);
> + uint64_t src_ctrl = riscv_ctr_get_control(env, src_priv, src_virt);
> + uint64_t tgt_ctrl = riscv_ctr_get_control(env, env->priv, tgt_virt);
> + uint64_t depth, head;
> + bool ext_trap = false;
> +
> + /*
> + * Return immediately if both target and src recording is disabled or if
> + * CTR is in frozen state.
> + */
> + if ((!(src_ctrl & src_mask) && !(tgt_ctrl & tgt_mask)) ||
> + env->sctrstatus & SCTRSTATUS_FROZEN) {
> + return;
> + }
> +
> + /*
> + * With RAS Emul enabled, only allow Indirect, direct calls, Function
> + * returns and Co-routine swap types.
> + */
> + if (env->mctrctl & MCTRCTL_RASEMU &&
I think we should check vsctrctl.RASEMU for VS and VU mode.
You can consider defining a variable here as it will be used multiple
times below.
bool rasemu = MCTRCTL_RASEMU & ((env->virt_enabled) ? env->vsctrctl :
env->mctrctl);
> + type != CTRDATA_TYPE_INDIRECT_CALL &&
> + type != CTRDATA_TYPE_DIRECT_CALL &&
> + type != CTRDATA_TYPE_RETURN &&
> + type != CTRDATA_TYPE_CO_ROUTINE_SWAP) {
> + return;
> + }
> +
> + if (type == CTRDATA_TYPE_EXCEPTION || type == CTRDATA_TYPE_INTERRUPT) {
> + /* Case 2 for traps. */
> + if (!(src_ctrl & src_mask)) {
> + src = 0;
> + } else if (!(tgt_ctrl & tgt_mask)) {
> + /* Check if target priv-mode has allowed external trap recording. */
> + if (!riscv_ctr_check_xte(env, src_priv, src_virt)) {
> + return;
> + }
> +
> + ext_trap = true;
> + dst = 0;
> + }
> + } else if (type == CTRDATA_TYPE_EXCEP_INT_RET) {
> + /*
> + * Case 3 for trap returns. Trap returns from inhibited mode are not
> + * recorded.
> + */
> + if (!(src_ctrl & src_mask)) {
> + return;
> + }
> +
> + /* Case 2 for trap returns. */
> + if (!(tgt_ctrl & tgt_mask)) {
> + dst = 0;
> + }
> + }
> +
> + /* Ignore filters in case of RASEMU mode or External trap. */
> + if (!(tgt_ctrl & MCTRCTL_RASEMU) && !ext_trap) {
> + /*
> + * Check if the specific type is inhibited. Not taken branch filter is
> + * an enable bit and needs to be checked separatly.
> + */
> + bool check = tgt_ctrl & BIT_ULL(type + MCTRCTL_INH_START);
> + if ((type == CTRDATA_TYPE_NONTAKEN_BRANCH && !check) ||
> + (type != CTRDATA_TYPE_NONTAKEN_BRANCH && check)) {
> + return;
> + }
> + }
> +
> + head = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK);
> +
> + depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
> + if (tgt_ctrl & MCTRCTL_RASEMU && type == CTRDATA_TYPE_RETURN) {
> + head = (head - 1) & (depth - 1);
> +
> + env->ctr_src[head] &= ~CTRSOURCE_VALID;
> + env->sctrstatus =
> + set_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK, head);
> + return;
> + }
> +
> + /* In case of Co-routine SWAP we overwrite latest entry. */
> + if (tgt_ctrl & MCTRCTL_RASEMU && type == CTRDATA_TYPE_CO_ROUTINE_SWAP) {
> + head = (head - 1) & (depth - 1);
> + }
The code can be reduced here.
if (rasemu) {
head = (head - 1) & (depth - 1);
if (CTRDATA_TYPE_CO_ROUTINE_SWAP) { ... }
else if (CTRDATA_TYPE_CO_ROUTINE_SWAP) { ... }
}
> +
> + env->ctr_src[head] = src | CTRSOURCE_VALID;
> + env->ctr_dst[head] = dst & ~CTRTARGET_MISP;
> + env->ctr_data[head] = set_field(0, CTRDATA_TYPE_MASK, type);
> +
> + head = (head + 1) & (depth - 1);
> +
> + env->sctrstatus = set_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK, head);
> +}
> +
> void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en)
> {
> g_assert(newpriv <= PRV_M && newpriv != PRV_RESERVED);
> @@ -1669,10 +1909,13 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> !(env->mip & (1 << cause));
> bool vs_injected = env->hvip & (1 << cause) & env->hvien &&
> !(env->mip & (1 << cause));
> + const bool prev_virt = env->virt_enabled;
> + const target_ulong prev_priv = env->priv;
> target_ulong tval = 0;
> target_ulong tinst = 0;
> target_ulong htval = 0;
> target_ulong mtval2 = 0;
> + target_ulong src;
>
> if (!async) {
> /* set tval to badaddr for traps with address information */
> @@ -1807,6 +2050,8 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> env->pc = (env->stvec >> 2 << 2) +
> ((async && (env->stvec & 3) == 1) ? cause * 4 : 0);
> riscv_cpu_set_mode(env, PRV_S, virt);
> +
> + src = env->sepc;
> } else {
> /* handle the trap in M-mode */
> if (riscv_has_ext(env, RVH)) {
> @@ -1838,6 +2083,19 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> env->pc = (env->mtvec >> 2 << 2) +
> ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
> riscv_cpu_set_mode(env, PRV_M, virt);
> + src = env->mepc;
> + }
> +
> + if (riscv_cpu_cfg(env)->ext_smctr || riscv_cpu_cfg(env)->ext_ssctr) {
> + if (async && cause == IRQ_PMU_OVF) {
> + riscv_ctr_freeze(env, MCTRCTL_LCOFIFRZ, virt);
> + } else if (!async && cause == RISCV_EXCP_BREAKPOINT) {
> + riscv_ctr_freeze(env, MCTRCTL_BPFRZ, virt);
> + }
> +
> + riscv_ctr_add_entry(env, src, env->pc,
> + async ? CTRDATA_TYPE_INTERRUPT : CTRDATA_TYPE_EXCEPTION,
> + prev_priv, prev_virt);
> }
>
> /*
> diff --git a/target/riscv/helper.h b/target/riscv/helper.h
> index 451261ce5a..b8fb7c8734 100644
> --- a/target/riscv/helper.h
> +++ b/target/riscv/helper.h
> @@ -129,12 +129,16 @@ DEF_HELPER_2(csrr_i128, tl, env, int)
> DEF_HELPER_4(csrw_i128, void, env, int, tl, tl)
> DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl)
> #ifndef CONFIG_USER_ONLY
> -DEF_HELPER_1(sret, tl, env)
> -DEF_HELPER_1(mret, tl, env)
> +DEF_HELPER_2(sret, tl, env, tl)
> +DEF_HELPER_2(mret, tl, env, tl)
> DEF_HELPER_1(wfi, void, env)
> DEF_HELPER_1(wrs_nto, void, env)
> DEF_HELPER_1(tlb_flush, void, env)
> DEF_HELPER_1(tlb_flush_all, void, env)
> +DEF_HELPER_4(ctr_branch, void, env, tl, tl, tl)
> +DEF_HELPER_4(ctr_jal, void, env, tl, tl, tl)
> +DEF_HELPER_5(ctr_jalr, void, env, tl, tl, tl, tl)
> +DEF_HELPER_3(ctr_popret, void, env, tl, tl)
> /* Native Debug */
> DEF_HELPER_1(itrigger_match, void, env)
> #endif
> diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
> index 4eccdddeaa..339d659151 100644
> --- a/target/riscv/insn_trans/trans_privileged.c.inc
> +++ b/target/riscv/insn_trans/trans_privileged.c.inc
> @@ -78,9 +78,10 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a)
> {
> #ifndef CONFIG_USER_ONLY
> if (has_ext(ctx, RVS)) {
> + TCGv src = tcg_constant_tl(ctx->base.pc_next);
> decode_save_opc(ctx);
> translator_io_start(&ctx->base);
> - gen_helper_sret(cpu_pc, tcg_env);
> + gen_helper_sret(cpu_pc, tcg_env, src);
> exit_tb(ctx); /* no chaining */
> ctx->base.is_jmp = DISAS_NORETURN;
> } else {
> @@ -95,9 +96,10 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a)
> static bool trans_mret(DisasContext *ctx, arg_mret *a)
> {
> #ifndef CONFIG_USER_ONLY
> + TCGv src = tcg_constant_tl(ctx->base.pc_next);
> decode_save_opc(ctx);
> translator_io_start(&ctx->base);
> - gen_helper_mret(cpu_pc, tcg_env);
> + gen_helper_mret(cpu_pc, tcg_env, src);
> exit_tb(ctx); /* no chaining */
> ctx->base.is_jmp = DISAS_NORETURN;
> return true;
> diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc
> index ad40d3e87f..26633569a8 100644
> --- a/target/riscv/insn_trans/trans_rvi.c.inc
> +++ b/target/riscv/insn_trans/trans_rvi.c.inc
> @@ -75,6 +75,14 @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a)
> gen_set_gpr(ctx, a->rd, succ_pc);
>
> tcg_gen_mov_tl(cpu_pc, target_pc);
> +#ifndef CONFIG_USER_ONLY
> + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
> + TCGv src = tcg_constant_tl(ctx->base.pc_next);
> + TCGv rs1 = tcg_constant_tl(a->rs1);
> + TCGv rd = tcg_constant_tl(a->rd);
> + gen_helper_ctr_jalr(tcg_env, src, cpu_pc, rd, rs1);
> + }
> +#endif
> lookup_and_goto_ptr(ctx);
>
> if (misaligned) {
> @@ -164,6 +172,11 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond)
> TCGv src1 = get_gpr(ctx, a->rs1, EXT_SIGN);
> TCGv src2 = get_gpr(ctx, a->rs2, EXT_SIGN);
> target_ulong orig_pc_save = ctx->pc_save;
> +#ifndef CONFIG_USER_ONLY
> + TCGv src = tcg_constant_tl(ctx->base.pc_next);
> + TCGv taken;
> + TCGv dest;
> +#endif
>
> if (get_xl(ctx) == MXL_RV128) {
> TCGv src1h = get_gprh(ctx, a->rs1);
> @@ -176,6 +189,16 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond)
> } else {
> tcg_gen_brcond_tl(cond, src1, src2, l);
> }
> +
> +#ifndef CONFIG_USER_ONLY
> + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
> + dest = tcg_constant_tl(ctx->base.pc_next + ctx->cur_insn_len);
> + taken = tcg_constant_tl(0);
> +
> + gen_helper_ctr_branch(tcg_env, src, dest, taken);
> + }
> +#endif
> +
> gen_goto_tb(ctx, 1, ctx->cur_insn_len);
> ctx->pc_save = orig_pc_save;
>
> @@ -188,6 +211,14 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond)
> gen_pc_plus_diff(target_pc, ctx, a->imm);
> gen_exception_inst_addr_mis(ctx, target_pc);
> } else {
> +#ifndef CONFIG_USER_ONLY
> + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
> + dest = tcg_constant_tl(ctx->base.pc_next + a->imm);
> + taken = tcg_constant_tl(1);
> +
> + gen_helper_ctr_branch(tcg_env, src, dest, taken);
> + }
> +#endif
> gen_goto_tb(ctx, 0, a->imm);
> }
> ctx->pc_save = -1;
> diff --git a/target/riscv/insn_trans/trans_rvzce.c.inc b/target/riscv/insn_trans/trans_rvzce.c.inc
> index cd234ad960..377d3fff70 100644
> --- a/target/riscv/insn_trans/trans_rvzce.c.inc
> +++ b/target/riscv/insn_trans/trans_rvzce.c.inc
> @@ -204,6 +204,12 @@ static bool gen_pop(DisasContext *ctx, arg_cmpp *a, bool ret, bool ret_val)
> if (ret) {
> TCGv ret_addr = get_gpr(ctx, xRA, EXT_SIGN);
> tcg_gen_mov_tl(cpu_pc, ret_addr);
> +#ifndef CONFIG_USER_ONLY
> + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
> + TCGv src = tcg_constant_tl(ctx->base.pc_next);
> + gen_helper_ctr_popret(tcg_env, src, cpu_pc);
> + }
> +#endif
> tcg_gen_lookup_and_goto_ptr();
> ctx->base.is_jmp = DISAS_NORETURN;
> }
> @@ -309,6 +315,20 @@ static bool trans_cm_jalt(DisasContext *ctx, arg_cm_jalt *a)
> gen_set_gpr(ctx, xRA, succ_pc);
> }
>
> +#ifndef CONFIG_USER_ONLY
> + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
> + /*
> + * We are reusing helper_ctr_jal() here. If rd is x1 or x5,
> + * this will record a direct call (cm.jalt) and if it's x0
> + * then this will record a direct jump (cm.jt).
> + */
> + TCGv rd = tcg_constant_tl(a->index >= 32 ? 1 : 0);
> + TCGv src = tcg_constant_tl(ctx->base.pc_next);
> + gen_helper_ctr_jal(tcg_env, src, addr, rd);
> + }
> +#endif
> +
> +
> tcg_gen_mov_tl(cpu_pc, addr);
>
> tcg_gen_lookup_and_goto_ptr();
> diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
> index 25a5263573..5a1e92c45e 100644
> --- a/target/riscv/op_helper.c
> +++ b/target/riscv/op_helper.c
> @@ -259,10 +259,12 @@ void helper_cbo_inval(CPURISCVState *env, target_ulong address)
>
> #ifndef CONFIG_USER_ONLY
>
> -target_ulong helper_sret(CPURISCVState *env)
> +target_ulong helper_sret(CPURISCVState *env, target_ulong curr_pc)
> {
> uint64_t mstatus;
> target_ulong prev_priv, prev_virt = env->virt_enabled;
> + const target_ulong src_priv = env->priv;
> + const bool src_virt = env->virt_enabled;
>
> if (!(env->priv >= PRV_S)) {
> riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> @@ -309,10 +311,15 @@ target_ulong helper_sret(CPURISCVState *env)
>
> riscv_cpu_set_mode(env, prev_priv, prev_virt);
>
> + if (riscv_cpu_cfg(env)->ext_smctr || riscv_cpu_cfg(env)->ext_ssctr) {
> + riscv_ctr_add_entry(env, curr_pc, retpc, CTRDATA_TYPE_EXCEP_INT_RET,
> + src_priv, src_virt);
> + }
> +
> return retpc;
> }
>
> -target_ulong helper_mret(CPURISCVState *env)
> +target_ulong helper_mret(CPURISCVState *env, target_ulong curr_pc)
> {
> if (!(env->priv >= PRV_M)) {
> riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> @@ -350,9 +357,124 @@ target_ulong helper_mret(CPURISCVState *env)
>
> riscv_cpu_set_mode(env, prev_priv, prev_virt);
>
> + if (riscv_cpu_cfg(env)->ext_smctr || riscv_cpu_cfg(env)->ext_ssctr) {
> + riscv_ctr_add_entry(env, curr_pc, retpc, CTRDATA_TYPE_EXCEP_INT_RET,
> + PRV_M, false);
> + }
> +
> return retpc;
> }
>
> +/*
> + * Indirect calls
> + * - jalr x1, rs where rs != x5;
> + * - jalr x5, rs where rs != x1;
> + * - c.jalr rs1 where rs1 != x5;
> + *
> + * Indirect jumps
> + * - jalr x0, rs where rs != x1 and rs != x5;
> + * - c.jr rs1 where rs1 != x1 and rs1 != x5.
> + *
> + * Returns
> + * - jalr rd, rs where (rs == x1 or rs == x5) and rd != x1 and rd != x5;
> + * - c.jr rs1 where rs1 == x1 or rs1 == x5.
> + *
> + * Co-routine swap
> + * - jalr x1, x5;
> + * - jalr x5, x1;
> + * - c.jalr x5.
> + *
> + * Other indirect jumps
> + * - jalr rd, rs where rs != x1, rs != x5, rd != x0, rd != x1 and rd != x5.
> + */
> +void helper_ctr_jalr(CPURISCVState *env, target_ulong src, target_ulong dest,
> + target_ulong rd, target_ulong rs1)
> +{
> + target_ulong curr_priv = env->priv;
> + bool curr_virt = env->virt_enabled;
> +
> + if ((rd == 1 && rs1 != 5) || (rd == 5 && rs1 != 1)) {
> + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_INDIRECT_CALL,
> + curr_priv, curr_virt);
> + } else if (rd == 0 && rs1 != 1 && rs1 != 5) {
> + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_INDIRECT_JUMP,
> + curr_priv, curr_virt);
> + } else if ((rs1 == 1 || rs1 == 5) && (rd != 1 && rd != 5)) {
> + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_RETURN,
> + curr_priv, curr_virt);
> + } else if ((rs1 == 1 && rd == 5) || (rs1 == 5 && rd == 1)) {
> + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_CO_ROUTINE_SWAP,
> + curr_priv, curr_virt);
> + } else {
> + riscv_ctr_add_entry(env, src, dest,
> + CTRDATA_TYPE_OTHER_INDIRECT_JUMP, curr_priv,
> + curr_virt);
> + }
> +}
> +
> +/*
> + * Direct calls
> + * - jal x1;
> + * - jal x5;
> + * - c.jal.
> + * - cm.jalt.
> + *
> + * Direct jumps
> + * - jal x0;
> + * - c.j;
> + * - cm.jt.
> + *
> + * Other direct jumps
> + * - jal rd where rd != x1 and rd != x5 and rd != x0;
> + */
> +void helper_ctr_jal(CPURISCVState *env, target_ulong src, target_ulong dest,
> + target_ulong rd)
> +{
> + target_ulong priv = env->priv;
> + bool virt = env->virt_enabled;
> +
> + /*
> + * If rd is x1 or x5 link registers, treat this as direct call otherwise
> + * its a direct jump.
> + */
> + if (rd == 1 || rd == 5) {
> + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_DIRECT_CALL, priv,
> + virt);
> + } else if (rd == 0) {
> + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_DIRECT_JUMP, priv,
> + virt);
> + } else {
> + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_OTHER_DIRECT_JUMP,
> + priv, virt);
> + }
> +}
> +
> +/*
> + * Returns
> + * - cm.popret
> + * - cm.popretz
> + */
> +void helper_ctr_popret(CPURISCVState *env, target_ulong src, target_ulong dest)
> +{
> + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_RETURN,
> + env->priv, env->virt_enabled);
> +}
> +
> +void helper_ctr_branch(CPURISCVState *env, target_ulong src, target_ulong dest,
> + target_ulong branch_taken)
> +{
> + target_ulong curr_priv = env->priv;
> + bool curr_virt = env->virt_enabled;
> +
> + if (branch_taken) {
> + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_TAKEN_BRANCH,
> + curr_priv, curr_virt);
> + } else {
> + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_NONTAKEN_BRANCH,
> + curr_priv, curr_virt);
> + }
> +}
> +
> void helper_wfi(CPURISCVState *env)
> {
> CPUState *cs = env_cpu(env);
> diff --git a/target/riscv/translate.c b/target/riscv/translate.c
> index 15e7123a68..07391297e8 100644
> --- a/target/riscv/translate.c
> +++ b/target/riscv/translate.c
> @@ -572,6 +572,16 @@ static void gen_jal(DisasContext *ctx, int rd, target_ulong imm)
> }
> }
>
> +#ifndef CONFIG_USER_ONLY
> + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
> + TCGv dest = tcg_constant_tl(ctx->base.pc_next + imm);
> + TCGv src = tcg_constant_tl(ctx->base.pc_next);
> + TCGv tcg_rd = tcg_constant_tl((target_ulong)rd);
> +
> + gen_helper_ctr_jal(tcg_env, src, dest, tcg_rd);
> + }
> +#endif
> +
> gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len);
> gen_set_gpr(ctx, rd, succ_pc);
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 2/6] target/riscv: Add Control Transfer Records CSR definitions.
2024-06-19 15:27 ` [PATCH v2 2/6] target/riscv: Add Control Transfer Records CSR definitions Rajnesh Kanwal
2024-06-26 0:07 ` Alistair Francis
@ 2024-06-26 6:00 ` Jason Chien
1 sibling, 0 replies; 21+ messages in thread
From: Jason Chien @ 2024-06-26 6:00 UTC (permalink / raw)
To: Rajnesh Kanwal, qemu-riscv, qemu-devel
Cc: alistair.francis, bin.meng, liweiwei, dbarboza, zhiwei_liu,
atishp, apatel, beeman, tech-control-transfer-records
Hi Rajnesh,
On 2024/6/19 下午 11:27, Rajnesh Kanwal wrote:
> The Control Transfer Records (CTR) extension provides a method to
> record a limited branch history in register-accessible internal chip
> storage.
>
> This extension is similar to Arch LBR in x86 and BRBE in ARM.
> The Extension has been stable and the latest release can be found here
> https://github.com/riscv/riscv-control-transfer-records/release
>
> Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
> ---
> target/riscv/cpu_bits.h | 154 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 154 insertions(+)
>
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index 86e15381c8..71ddccaf1a 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -242,6 +242,17 @@
> #define CSR_SIEH 0x114
> #define CSR_SIPH 0x154
>
> +/* Machine-Level Control transfer records CSRs */
> +#define CSR_MCTRCTL 0x34e
> +
> +/* Supervisor-Level Control transfer records CSRs */
> +#define CSR_SCTRCTL 0x14e
> +#define CSR_SCTRSTATUS 0x14f
> +#define CSR_SCTRDEPTH 0x15f
> +
> +/* VS-Level Control transfer records CSRs */
> +#define CSR_VSCTRCTL 0x24e
> +
> /* Hpervisor CSRs */
> #define CSR_HSTATUS 0x600
> #define CSR_HEDELEG 0x602
> @@ -339,6 +350,7 @@
> #define SMSTATEEN0_CS (1ULL << 0)
> #define SMSTATEEN0_FCSR (1ULL << 1)
> #define SMSTATEEN0_JVT (1ULL << 2)
> +#define SMSTATEEN0_CTR (1ULL << 54)
> #define SMSTATEEN0_HSCONTXT (1ULL << 57)
> #define SMSTATEEN0_IMSIC (1ULL << 58)
> #define SMSTATEEN0_AIA (1ULL << 59)
> @@ -854,6 +866,148 @@ typedef enum RISCVException {
> #define UMTE_U_PM_INSN U_PM_INSN
> #define UMTE_MASK (UMTE_U_PM_ENABLE | MMTE_U_PM_CURRENT | UMTE_U_PM_INSN)
>
> +/* mctrctl CSR bits. */
> +#define MCTRCTL_U_ENABLE BIT(0)
> +#define MCTRCTL_S_ENABLE BIT(1)
> +#define MCTRCTL_M_ENABLE BIT(2)
> +#define MCTRCTL_RASEMU BIT(7)
> +#define MCTRCTL_STE BIT(8)
> +#define MCTRCTL_MTE BIT(9)
> +#define MCTRCTL_BPFRZ BIT(11)
> +#define MCTRCTL_LCOFIFRZ BIT(12)
> +#define MCTRCTL_EXCINH BIT(33)
> +#define MCTRCTL_INTRINH BIT(34)
> +#define MCTRCTL_TRETINH BIT(35)
> +#define MCTRCTL_NTBREN BIT(36)
> +#define MCTRCTL_TKBRINH BIT(37)
> +#define MCTRCTL_INDCALL_INH BIT(40)
> +#define MCTRCTL_DIRCALL_INH BIT(41)
> +#define MCTRCTL_INDJUMP_INH BIT(42)
> +#define MCTRCTL_DIRJUMP_INH BIT(43)
> +#define MCTRCTL_CORSWAP_INH BIT(44)
> +#define MCTRCTL_RET_INH BIT(45)
> +#define MCTRCTL_INDOJUMP_INH BIT(46)
> +#define MCTRCTL_DIROJUMP_INH BIT(47)
> +
> +#define MCTRCTL_INH_START 32U
> +
> +#define MCTRCTL_MASK (MCTRCTL_M_ENABLE | MCTRCTL_S_ENABLE | \
> + MCTRCTL_U_ENABLE | MCTRCTL_RASEMU | \
> + MCTRCTL_MTE | MCTRCTL_STE | \
> + MCTRCTL_BPFRZ | MCTRCTL_LCOFIFRZ | \
> + MCTRCTL_EXCINH | MCTRCTL_INTRINH | \
> + MCTRCTL_TRETINH | MCTRCTL_NTBREN | \
> + MCTRCTL_TKBRINH | MCTRCTL_INDCALL_INH | \
> + MCTRCTL_DIRCALL_INH | MCTRCTL_INDJUMP_INH | \
> + MCTRCTL_DIRJUMP_INH | MCTRCTL_CORSWAP_INH | \
> + MCTRCTL_RET_INH | MCTRCTL_INDOJUMP_INH | \
> + MCTRCTL_DIROJUMP_INH)
> +
> +/* sctrctl CSR bits. */
> +#define SCTRCTL_U_ENABLE MCTRCTL_U_ENABLE
> +#define SCTRCTL_S_ENABLE MCTRCTL_S_ENABLE
> +#define SCTRCTL_RASEMU MCTRCTL_RASEMU
> +#define SCTRCTL_STE MCTRCTL_STE
> +#define SCTRCTL_BPFRZ MCTRCTL_BPFRZ
> +#define SCTRCTL_LCOFIFRZ MCTRCTL_LCOFIFRZ
> +#define SCTRCTL_EXCINH MCTRCTL_EXCINH
> +#define SCTRCTL_INTRINH MCTRCTL_INTRINH
> +#define SCTRCTL_TRETINH MCTRCTL_TRETINH
> +#define SCTRCTL_NTBREN MCTRCTL_NTBREN
> +#define SCTRCTL_TKBRINH MCTRCTL_TKBRINH
> +#define SCTRCTL_INDCALL_INH MCTRCTL_INDCALL_INH
> +#define SCTRCTL_DIRCALL_INH MCTRCTL_DIRCALL_INH
> +#define SCTRCTL_INDJUMP_INH MCTRCTL_INDJUMP_INH
> +#define SCTRCTL_DIRJUMP_INH MCTRCTL_DIRJUMP_INH
> +#define SCTRCTL_CORSWAP_INH MCTRCTL_CORSWAP_INH
> +#define SCTRCTL_RET_INH MCTRCTL_RET_INH
> +#define SCTRCTL_INDOJUMP_INH MCTRCTL_INDOJUMP_INH
> +#define SCTRCTL_DIROJUMP_INH MCTRCTL_DIROJUMP_INH
> +
> +#define SCTRCTL_MASK (SCTRCTL_S_ENABLE | SCTRCTL_U_ENABLE | \
> + SCTRCTL_RASEMU | SCTRCTL_STE | \
> + SCTRCTL_BPFRZ | SCTRCTL_LCOFIFRZ | \
> + SCTRCTL_EXCINH | SCTRCTL_INTRINH | \
> + SCTRCTL_TRETINH | SCTRCTL_NTBREN | \
> + SCTRCTL_TKBRINH | SCTRCTL_INDCALL_INH | \
> + SCTRCTL_DIRCALL_INH | SCTRCTL_INDJUMP_INH | \
> + SCTRCTL_DIRJUMP_INH | SCTRCTL_CORSWAP_INH | \
> + SCTRCTL_RET_INH | SCTRCTL_INDOJUMP_INH | \
> + SCTRCTL_DIROJUMP_INH)
> +
> +/* sctrstatus CSR bits. */
> +#define SCTRSTATUS_WRPTR_MASK 0xFF
> +#define SCTRSTATUS_FROZEN BIT(31)
> +#define SCTRSTATUS_MASK (SCTRSTATUS_WRPTR_MASK | SCTRSTATUS_FROZEN)
> +
> +/* sctrdepth CSR bits. */
> +#define SCTRDEPTH_MASK 0x7
> +#define SCTRDEPTH_MIN 0U /* 16 Entries. */
> +#define SCTRDEPTH_MAX 4U /* 256 Entries. */
> +
> +/* vsctrctl CSR bits. */
> +#define VSCTRCTL_VU_ENABLE MCTRCTL_U_ENABLE
> +#define VSCTRCTL_VS_ENABLE MCTRCTL_S_ENABLE
> +#define VSCTRCTL_RASEMU MCTRCTL_RASEMU
> +#define VSCTRCTL_VSTE MCTRCTL_STE
> +#define VSCTRCTL_BPFRZ MCTRCTL_BPFRZ
> +#define VSCTRCTL_LCOFIFRZ MCTRCTL_LCOFIFRZ
> +#define VSCTRCTL_EXCINH MCTRCTL_EXCINH
> +#define VSCTRCTL_INTRINH MCTRCTL_INTRINH
> +#define VSCTRCTL_TRETINH MCTRCTL_TRETINH
> +#define VSCTRCTL_NTBREN MCTRCTL_NTBREN
> +#define VSCTRCTL_TKBRINH MCTRCTL_TKBRINH
> +#define VSCTRCTL_INDCALL_INH MCTRCTL_INDCALL_INH
> +#define VSCTRCTL_DIRCALL_INH MCTRCTL_DIRCALL_INH
> +#define VSCTRCTL_INDJUMP_INH MCTRCTL_INDJUMP_INH
> +#define VSCTRCTL_DIRJUMP_INH MCTRCTL_DIRJUMP_INH
> +#define VSCTRCTL_CORSWAP_INH MCTRCTL_CORSWAP_INH
> +#define VSCTRCTL_RET_INH MCTRCTL_RET_INH
> +#define VSCTRCTL_INDOJUMP_INH MCTRCTL_INDOJUMP_INH
> +#define VSCTRCTL_DIROJUMP_INH MCTRCTL_DIROJUMP_INH
> +
> +#define VSCTRCTL_MASK (VSCTRCTL_VS_ENABLE | VSCTRCTL_VU_ENABLE | \
> + VSCTRCTL_RASEMU | VSCTRCTL_VSTE | \
> + VSCTRCTL_BPFRZ | VSCTRCTL_LCOFIFRZ | \
> + VSCTRCTL_EXCINH | VSCTRCTL_INTRINH | \
> + VSCTRCTL_TRETINH | VSCTRCTL_NTBREN | \
> + VSCTRCTL_TKBRINH | VSCTRCTL_INDCALL_INH | \
> + VSCTRCTL_DIRCALL_INH | VSCTRCTL_INDJUMP_INH | \
> + VSCTRCTL_DIRJUMP_INH | VSCTRCTL_CORSWAP_INH | \
> + VSCTRCTL_RET_INH | VSCTRCTL_INDOJUMP_INH | \
> + VSCTRCTL_DIROJUMP_INH)
Do you think it's a good idea to define these macros like below?
+/* CTR control register commom fields */
+#define XCTRCTL_U BIT_ULL(0)
+#define XCTRCTL_S BIT_ULL(1)
+#define XCTRCTL_RASEMU BIT_ULL(7)
+#define XCTRCTL_STE BIT_ULL(8)
+#define XCTRCTL_BPFRZ BIT_ULL(11)
+#define XCTRCTL_LCOFIFRZ BIT_ULL(12)
+#define XCTRCTL_EXCINH BIT_ULL(33)
+#define XCTRCTL_INTRINH BIT_ULL(34)
+#define XCTRCTL_TRETINH BIT_ULL(35)
+#define XCTRCTL_NTBREN BIT_ULL(36)
+#define XCTRCTL_TKBRINH BIT_ULL(37)
+#define XCTRCTL_INDCALLINH BIT_ULL(40)
+#define XCTRCTL_DIRCALLINH BIT_ULL(41)
+#define XCTRCTL_INDJMPINH BIT_ULL(42)
+#define XCTRCTL_DIRJMPINH BIT_ULL(43)
+#define XCTRCTL_CORSWAPINH BIT_ULL(44)
+#define XCTRCTL_RETINH BIT_ULL(45)
+#define XCTRCTL_INDLJMPINH BIT_ULL(46)
+#define XCTRCTL_DIRLJMPINH BIT_ULL(47)
+
+#define XCTRCTL_MASK (XCTRCTL_U | XCTRCTL_S | XCTRCTL_RASEMU |
XCTRCTL_STE | \
+ XCTRCTL_BPFRZ |
XCTRCTL_LCOFIFRZ | XCTRCTL_EXCINH | \
+ XCTRCTL_INTRINH |
XCTRCTL_TRETINH | XCTRCTL_NTBREN | \
+ XCTRCTL_TKBRINH |
XCTRCTL_INDCALLINH | \
+ XCTRCTL_DIRCALLINH |
XCTRCTL_INDJMPINH | \
+ XCTRCTL_DIRJMPINH |
XCTRCTL_CORSWAPINH | \
+ XCTRCTL_RETINH |
XCTRCTL_INDLJMPINH | XCTRCTL_DIRLJMPINH)
+
+/* CTR mctrctl bits */
+#define MCTRCTL_M BIT_ULL(2)
+#define MCTRCTL_MTE BIT_ULL(9)
+
+#define MCTRCTL_MASK (XCTRCTL_MASK | MCTRCTL_M | MCTRCTL_MTE)
+#define SCTRCTL_MASK XCTRCTL_MASK
+#define VSCTRCTL_MASK XCTRCTL_MASK
> +
> +#define CTR_ENTRIES_FIRST 0x200
> +#define CTR_ENTRIES_LAST 0x2ff
> +
> +#define CTRSOURCE_VALID BIT(0)
> +#define CTRTARGET_MISP BIT(0)
> +
> +#define CTRDATA_TYPE_MASK 0xF
> +#define CTRDATA_CCV BIT(15)
> +#define CTRDATA_CCM_MASK 0xFFF0000
> +#define CTRDATA_CCE_MASK 0xF0000000
> +
> +#define CTRDATA_MASK (CTRDATA_TYPE_MASK | CTRDATA_CCV | \
> + CTRDATA_CCM_MASK | CTRDATA_CCE_MASK)
> +
> +#define CTRDATA_TYPE_NONE 0
> +#define CTRDATA_TYPE_EXCEPTION 1
> +#define CTRDATA_TYPE_INTERRUPT 2
> +#define CTRDATA_TYPE_EXCEP_INT_RET 3
> +#define CTRDATA_TYPE_NONTAKEN_BRANCH 4
> +#define CTRDATA_TYPE_TAKEN_BRANCH 5
> +#define CTRDATA_TYPE_RESERVED_0 6
> +#define CTRDATA_TYPE_RESERVED_1 7
> +#define CTRDATA_TYPE_INDIRECT_CALL 8
> +#define CTRDATA_TYPE_DIRECT_CALL 9
> +#define CTRDATA_TYPE_INDIRECT_JUMP 10
> +#define CTRDATA_TYPE_DIRECT_JUMP 11
> +#define CTRDATA_TYPE_CO_ROUTINE_SWAP 12
> +#define CTRDATA_TYPE_RETURN 13
> +#define CTRDATA_TYPE_OTHER_INDIRECT_JUMP 14
> +#define CTRDATA_TYPE_OTHER_DIRECT_JUMP 15
I prefer using typedef enum {} for the instruction types, since there is
type checking for function parameters.
> +
> /* MISELECT, SISELECT, and VSISELECT bits (AIA) */
> #define ISELECT_IPRIO0 0x30
> #define ISELECT_IPRIO15 0x3f
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 0/6] target/riscv: Add support for Control Transfer Records Ext.
2024-06-19 15:27 [PATCH v2 0/6] target/riscv: Add support for Control Transfer Records Ext Rajnesh Kanwal
` (6 preceding siblings ...)
2024-06-26 0:01 ` [PATCH v2 0/6] target/riscv: Add support for Control Transfer Records Ext Alistair Francis
@ 2024-06-26 6:18 ` Jason Chien
7 siblings, 0 replies; 21+ messages in thread
From: Jason Chien @ 2024-06-26 6:18 UTC (permalink / raw)
To: Rajnesh Kanwal, qemu-riscv, qemu-devel
Cc: alistair.francis, bin.meng, liweiwei, dbarboza, zhiwei_liu,
atishp, apatel, beeman, tech-control-transfer-records
Hi Rajnesh,
On 2024/6/19 下午 11:27, Rajnesh Kanwal wrote:
> This series enables Control Transfer Records extension support on riscv
> platform. This extension is similar to Arch LBR in x86 and BRBE in ARM.
> The Extension has been stable and the latest release can be found here [0]
>
> CTR extension depends on couple of other extensions:
>
> 1. S[m|s]csrind : The indirect CSR extension [1] which defines additional
> ([M|S|VS]IREG2-[M|S|VS]IREG6) register to address size limitation of
> RISC-V CSR address space. CTR access ctrsource, ctrtartget and ctrdata
> CSRs using sscsrind extension.
There is no dependency on Smcsrind.
>
> 2. Smstateen: The mstateen bit[54] controls the access to the CTR ext to
> S-mode.
This bit is missing in write_mstateen0(), write_mstateen0h(),
write_hstateen0() and write_hstateen0h().
>
> 3. Sscofpmf: Counter overflow and privilege mode filtering. [2]
>
> The series is based on Smcdeleg/Ssccfg counter delegation extension [3]
> patches. CTR itself doesn't depend on counter delegation support. This
> rebase is basically to include the Smcsrind patches.
>
> Due to the dependency of these extensions, the following extensions must be
> enabled to use the control transfer records feature.
>
> "smstateen=true,sscofpmf=true,smcsrind=true,sscsrind=true,smctr=true,ssctr=true"
>
> Here is the link to a quick guide [5] to setup and run a basic perf demo on
> Linux to use CTR Ext.
>
> The Qemu patches can be found here:
> https://github.com/rajnesh-kanwal/qemu/tree/ctr_upstream_v2
>
> The opensbi patch can be found here:
> https://github.com/rajnesh-kanwal/opensbi/tree/ctr_upstream_v2
>
> The Linux kernel patches can be found here:
> https://github.com/rajnesh-kanwal/linux/tree/ctr_upstream_v2
>
> [0]: https://github.com/riscv/riscv-control-transfer-records/release
> [1]: https://github.com/riscv/riscv-indirect-csr-access
> [2]: https://github.com/riscvarchive/riscv-count-overflow/tree/main
> [3]: https://github.com/riscv/riscv-smcdeleg-ssccfg
> [4]: https://lore.kernel.org/all/20240217000134.3634191-1-atishp@rivosinc.com/
> [5]: https://github.com/rajnesh-kanwal/linux/wiki/Running-CTR-basic-demo-on-QEMU-RISC%E2%80%90V-Virt-machine
>
> Changelog:
>
> v2: Lots of improvements based on Jason Chien's feedback including:
Thank you for mentioning my name!
> - Added CTR recording for cm.jalt, cm.jt, cm.popret, cm.popretz.
> - Fixed and added more CTR extension enable checks.
> - Fixed CTR CSR predicate functions.
> - Fixed external trap xTE bit checks.
> - One fix in freeze function for VS-mode.
> - Lots of minor code improvements.
> - Added checks in sctrclr instruction helper.
>
> v1:
> - https://github.com/rajnesh-kanwal/qemu/tree/ctr_upstream
>
>
> Rajnesh Kanwal (6):
> target/riscv: Remove obsolete sfence.vm instruction
> target/riscv: Add Control Transfer Records CSR definitions.
> target/riscv: Add support for Control Transfer Records extension CSRs.
> target/riscv: Add support to record CTR entries.
> target/riscv: Add CTR sctrclr instruction.
> target/riscv: Add support to access ctrsource, ctrtarget, ctrdata
> regs.
>
> target/riscv/cpu.c | 4 +
> target/riscv/cpu.h | 14 +
> target/riscv/cpu_bits.h | 154 ++++++++++
> target/riscv/cpu_cfg.h | 2 +
> target/riscv/cpu_helper.c | 265 +++++++++++++++++
> target/riscv/csr.c | 276 +++++++++++++++++-
> target/riscv/helper.h | 9 +-
> target/riscv/insn32.decode | 2 +-
> .../riscv/insn_trans/trans_privileged.c.inc | 21 +-
> target/riscv/insn_trans/trans_rvi.c.inc | 31 ++
> target/riscv/insn_trans/trans_rvzce.c.inc | 20 ++
> target/riscv/op_helper.c | 159 +++++++++-
> target/riscv/tcg/tcg-cpu.c | 6 +
> target/riscv/translate.c | 10 +
> 14 files changed, 960 insertions(+), 13 deletions(-)
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 3/6] target/riscv: Add support for Control Transfer Records extension CSRs.
2024-06-19 15:27 ` [PATCH v2 3/6] target/riscv: Add support for Control Transfer Records extension CSRs Rajnesh Kanwal
2024-06-25 8:28 ` Jason Chien
@ 2024-08-27 9:28 ` Frank Chang
2024-10-17 11:18 ` Rajnesh Kanwal
1 sibling, 1 reply; 21+ messages in thread
From: Frank Chang @ 2024-08-27 9:28 UTC (permalink / raw)
To: Rajnesh Kanwal
Cc: qemu-riscv, qemu-devel, alistair.francis, bin.meng, liweiwei,
dbarboza, zhiwei_liu, atishp, apatel, beeman,
tech-control-transfer-records, jason.chien
Rajnesh Kanwal <rkanwal@rivosinc.com> 於 2024年6月19日 週三 下午11:27寫道:
>
> This commit adds support for [m|s|vs]ctrcontrol, sctrstatus and
> sctrdepth CSRs handling.
>
> Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
> ---
> target/riscv/cpu.h | 5 ++
> target/riscv/cpu_cfg.h | 2 +
> target/riscv/csr.c | 128 +++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 135 insertions(+)
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index a185e2d494..3d4d5172b8 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -263,6 +263,11 @@ struct CPUArchState {
> target_ulong mcause;
> target_ulong mtval; /* since: priv-1.10.0 */
>
> + uint64_t mctrctl;
> + uint32_t sctrdepth;
> + uint32_t sctrstatus;
> + uint64_t vsctrctl;
> +
> /* Machine and Supervisor interrupt priorities */
> uint8_t miprio[64];
> uint8_t siprio[64];
> diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h
> index d9354dc80a..d329a65811 100644
> --- a/target/riscv/cpu_cfg.h
> +++ b/target/riscv/cpu_cfg.h
> @@ -123,6 +123,8 @@ struct RISCVCPUConfig {
> bool ext_zvfhmin;
> bool ext_smaia;
> bool ext_ssaia;
> + bool ext_smctr;
> + bool ext_ssctr;
Base on: https://github.com/riscv/riscv-control-transfer-records/pull/29
Smctr and Ssctr depend on both S-mode and Sscsrind.
We should add the implied rules for Smctr and Ssctr.
Regards,
Frank Chang
> bool ext_sscofpmf;
> bool ext_smepmp;
> bool rvv_ta_all_1s;
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index 2f92e4b717..0b5bf4d050 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -621,6 +621,48 @@ static RISCVException pointer_masking(CPURISCVState *env, int csrno)
> return RISCV_EXCP_ILLEGAL_INST;
> }
>
> +/*
> + * M-mode:
> + * Without ext_smctr raise illegal inst excep.
> + * Otherwise everything is accessible to m-mode.
> + *
> + * S-mode:
> + * Without ext_ssctr or mstateen.ctr raise illegal inst excep.
> + * Otherwise everything other than mctrctl is accessible.
> + *
> + * VS-mode:
> + * Without ext_ssctr or mstateen.ctr raise illegal inst excep.
> + * Without hstateen.ctr raise virtual illegal inst excep.
> + * Otherwise allow sctrctl (vsctrctl), sctrstatus, 0x200-0x2ff entry range.
> + * Always raise illegal instruction exception for sctrdepth.
> + */
> +static RISCVException ctr_mmode(CPURISCVState *env, int csrno)
> +{
> + /* Check if smctr-ext is present */
> + if (riscv_cpu_cfg(env)->ext_smctr) {
> + return RISCV_EXCP_NONE;
> + }
> +
> + return RISCV_EXCP_ILLEGAL_INST;
> +}
> +
> +static RISCVException ctr_smode(CPURISCVState *env, int csrno)
> +{
> + const RISCVCPUConfig *cfg = riscv_cpu_cfg(env);
> +
> + if (!cfg->ext_smctr && !cfg->ext_ssctr) {
> + return RISCV_EXCP_ILLEGAL_INST;
> + }
> +
> + RISCVException ret = smstateen_acc_ok(env, 0, SMSTATEEN0_CTR);
> + if (ret == RISCV_EXCP_NONE && csrno == CSR_SCTRDEPTH &&
> + env->virt_enabled) {
> + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
> + }
> +
> + return ret;
> +}
> +
> static RISCVException aia_hmode(CPURISCVState *env, int csrno)
> {
> int ret;
> @@ -3835,6 +3877,86 @@ static RISCVException write_satp(CPURISCVState *env, int csrno,
> return RISCV_EXCP_NONE;
> }
>
> +static RISCVException rmw_sctrdepth(CPURISCVState *env, int csrno,
> + target_ulong *ret_val,
> + target_ulong new_val, target_ulong wr_mask)
> +{
> + uint64_t mask = wr_mask & SCTRDEPTH_MASK;
> +
> + if (ret_val) {
> + *ret_val = env->sctrdepth;
> + }
> +
> + env->sctrdepth = (env->sctrdepth & ~mask) | (new_val & mask);
> +
> + /* Correct depth. */
> + if (wr_mask & SCTRDEPTH_MASK) {
> + uint64_t depth = get_field(env->sctrdepth, SCTRDEPTH_MASK);
> +
> + if (depth > SCTRDEPTH_MAX) {
> + depth = SCTRDEPTH_MAX;
> + env->sctrdepth = set_field(env->sctrdepth, SCTRDEPTH_MASK, depth);
> + }
> +
> + /* Update sctrstatus.WRPTR with a legal value */
> + depth = 16 << depth;
> + env->sctrstatus =
> + env->sctrstatus & (~SCTRSTATUS_WRPTR_MASK | (depth - 1));
> + }
> +
> + return RISCV_EXCP_NONE;
> +}
> +
> +static RISCVException rmw_sctrstatus(CPURISCVState *env, int csrno,
> + target_ulong *ret_val,
> + target_ulong new_val, target_ulong wr_mask)
> +{
> + uint32_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
> + uint32_t mask = wr_mask & SCTRSTATUS_MASK;
> +
> + if (ret_val) {
> + *ret_val = env->sctrstatus;
> + }
> +
> + env->sctrstatus = (env->sctrstatus & ~mask) | (new_val & mask);
> +
> + /* Update sctrstatus.WRPTR with a legal value */
> + env->sctrstatus = env->sctrstatus & (~SCTRSTATUS_WRPTR_MASK | (depth - 1));
> +
> + return RISCV_EXCP_NONE;
> +}
> +
> +static RISCVException rmw_xctrctl(CPURISCVState *env, int csrno,
> + target_ulong *ret_val,
> + target_ulong new_val, target_ulong wr_mask)
> +{
> + uint64_t csr_mask, mask = wr_mask;
> + uint64_t *ctl_ptr = &env->mctrctl;
> +
> + if (csrno == CSR_MCTRCTL) {
> + csr_mask = MCTRCTL_MASK;
> + } else if (csrno == CSR_SCTRCTL && !env->virt_enabled) {
> + csr_mask = SCTRCTL_MASK;
> + } else {
> + /*
> + * This is for csrno == CSR_SCTRCTL and env->virt_enabled == true
> + * or csrno == CSR_VSCTRCTL.
> + */
> + csr_mask = VSCTRCTL_MASK;
> + ctl_ptr = &env->vsctrctl;
> + }
> +
> + mask &= csr_mask;
> +
> + if (ret_val) {
> + *ret_val = *ctl_ptr & csr_mask;
> + }
> +
> + *ctl_ptr = (*ctl_ptr & ~mask) | (new_val & mask);
> +
> + return RISCV_EXCP_NONE;
> +}
> +
> static RISCVException read_vstopi(CPURISCVState *env, int csrno,
> target_ulong *val)
> {
> @@ -5771,6 +5893,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
> [CSR_SPMBASE] = { "spmbase", pointer_masking, read_spmbase,
> write_spmbase },
>
> + [CSR_MCTRCTL] = { "mctrctl", ctr_mmode, NULL, NULL, rmw_xctrctl },
> + [CSR_SCTRCTL] = { "sctrctl", ctr_smode, NULL, NULL, rmw_xctrctl },
> + [CSR_VSCTRCTL] = { "vsctrctl", ctr_smode, NULL, NULL, rmw_xctrctl },
> + [CSR_SCTRDEPTH] = { "sctrdepth", ctr_smode, NULL, NULL, rmw_sctrdepth },
> + [CSR_SCTRSTATUS] = { "sctrstatus", ctr_smode, NULL, NULL, rmw_sctrstatus },
> +
> /* Performance Counters */
> [CSR_HPMCOUNTER3] = { "hpmcounter3", ctr, read_hpmcounter },
> [CSR_HPMCOUNTER4] = { "hpmcounter4", ctr, read_hpmcounter },
> --
> 2.34.1
>
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 4/6] target/riscv: Add support to record CTR entries.
2024-06-26 3:49 ` Jason Chien
@ 2024-10-15 18:50 ` Rajnesh Kanwal
0 siblings, 0 replies; 21+ messages in thread
From: Rajnesh Kanwal @ 2024-10-15 18:50 UTC (permalink / raw)
To: Jason Chien
Cc: qemu-riscv, qemu-devel, alistair.francis, bin.meng, liweiwei,
dbarboza, zhiwei_liu, atishp, apatel, beeman,
tech-control-transfer-records
Hi Jason,
On Wed, Jun 26, 2024 at 4:49 AM Jason Chien <jason.chien@sifive.com> wrote:
>
> Hi Rajnesh,
>
> On 2024/6/19 下午 11:27, Rajnesh Kanwal wrote:
> > This commit adds logic to records CTR entries of different types
> > and adds required hooks in TCG and interrupt/Exception logic to
> > record events.
> >
> > This commit also adds support to invoke freeze CTR logic for breakpoint
> > exceptions and counter overflow interrupts.
> >
> > Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
> > ---
> > target/riscv/cpu.h | 8 +
> > target/riscv/cpu_helper.c | 258 ++++++++++++++++++
> > target/riscv/helper.h | 8 +-
> > .../riscv/insn_trans/trans_privileged.c.inc | 6 +-
> > target/riscv/insn_trans/trans_rvi.c.inc | 31 +++
> > target/riscv/insn_trans/trans_rvzce.c.inc | 20 ++
> > target/riscv/op_helper.c | 126 ++++++++-
> > target/riscv/translate.c | 10 +
> > 8 files changed, 461 insertions(+), 6 deletions(-)
> >
> > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> > index 3d4d5172b8..e32f5ab146 100644
> > --- a/target/riscv/cpu.h
> > +++ b/target/riscv/cpu.h
> > @@ -268,6 +268,10 @@ struct CPUArchState {
> > uint32_t sctrstatus;
> > uint64_t vsctrctl;
> >
> > + uint64_t ctr_src[16 << SCTRDEPTH_MAX];
> > + uint64_t ctr_dst[16 << SCTRDEPTH_MAX];
> > + uint64_t ctr_data[16 << SCTRDEPTH_MAX];
> > +
> > /* Machine and Supervisor interrupt priorities */
> > uint8_t miprio[64];
> > uint8_t siprio[64];
> > @@ -565,6 +569,10 @@ RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit);
> > #endif
> > void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en);
> >
> > +void riscv_ctr_freeze(CPURISCVState *env, uint64_t freeze_mask, bool virt);
> It looks like riscv_ctr_freeze() is only used in
> target/riscv/cpu_helper.c. We can make it a static function.
Thanks. I will fix this in v3.
> > +void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst,
> > + uint64_t type, target_ulong prev_priv, bool prev_virt);
> > +
> > void riscv_translate_init(void);
> > G_NORETURN void riscv_raise_exception(CPURISCVState *env,
> > uint32_t exception, uintptr_t pc);
> > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> > index a441a03ef4..1537602e1b 100644
> > --- a/target/riscv/cpu_helper.c
> > +++ b/target/riscv/cpu_helper.c
> > @@ -691,6 +691,246 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv,
> > }
> > }
> >
> > +void riscv_ctr_freeze(CPURISCVState *env, uint64_t freeze_mask, bool virt)
> > +{
> > + uint64_t ctl = virt ? env->mctrctl : env->vsctrctl;
> > +
> > + assert((freeze_mask & (~(MCTRCTL_BPFRZ | MCTRCTL_LCOFIFRZ))) == 0);
> > +
> > + if (ctl & freeze_mask) {
> > + env->sctrstatus |= SCTRSTATUS_FROZEN;
> > + }
> > +}
> > +
> > +static uint64_t riscv_ctr_priv_to_mask(target_ulong priv, bool virt)
> > +{
> > + switch (priv) {
> > + case PRV_M:
> > + return MCTRCTL_M_ENABLE;
> > + case PRV_S:
> > + if (virt) {
> > + return VSCTRCTL_VS_ENABLE;
> > + }
> > + return MCTRCTL_S_ENABLE;
> > + case PRV_U:
> > + if (virt) {
> > + return VSCTRCTL_VU_ENABLE;
> > + }
> > + return MCTRCTL_U_ENABLE;
> > + }
> > +
> > + g_assert_not_reached();
> > +}
> > +
> > +static uint64_t riscv_ctr_get_control(CPURISCVState *env, target_long priv,
> > + bool virt)
> > +{
> > + switch (priv) {
> > + case PRV_M:
> > + return env->mctrctl;
> > + case PRV_S:
> > + case PRV_U:
> > + if (virt) {
> > + return env->vsctrctl;
> > + }
> > + return env->mctrctl;
> > + }
> > +
> > + g_assert_not_reached();
> > +}
> > +
> > +/*
> > + * This function assumes that src privilege and target privilege are not same
> > + * and src privilege is less than target privilege. This includes the virtual
> > + * state as well.
> > + */
> > +static bool riscv_ctr_check_xte(CPURISCVState *env, target_long src_prv,
> > + bool src_virt)
> This function is problematic. Suppose an external trap traps from U mode
> to M mode. We need to check both sctrctl.STE and mctrctl.MTE, but this
> function only checks sctrctl.STE.
I think it does check both MTE and STE. Switch case doesn't break and
falls through to check for MTE bit. Correct me if I am missing something.
> > +{
> > + target_long tgt_prv = env->priv;
> > + bool res = true;
> > +
> > + /*
> > + * VS and U mode are same in terms of xTE bits required to record an
> > + * external trap. See 6.1.2. External Traps, table 8 External Trap Enable
> > + * Requirements. This changes VS to U to simplify the logic a bit.
> > + */
> > + if (src_virt && src_prv == PRV_S) {
> > + src_prv = PRV_U;
> > + } else if (env->virt_enabled && tgt_prv == PRV_S) {
> > + tgt_prv = PRV_U;
> > + }
> > +
> > + /* VU mode is an outlier here. */
> > + if (src_virt && src_prv == PRV_U) {
> > + res &= !!(env->vsctrctl & VSCTRCTL_VSTE);
> > + }
> > +
> > + switch (src_prv) {
> > + case PRV_U:
> > + if (tgt_prv == PRV_U) {
> > + break;
> > + }
> > + res &= !!(env->mctrctl & SCTRCTL_STE);
> > + /* fall-through */
> > + case PRV_S:
> > + if (tgt_prv == PRV_S) {
> > + break;
> > + }
> > + res &= !!(env->mctrctl & MCTRCTL_MTE);
> > + /* fall-through */
> > + case PRV_M:
> > + break;
> > + }
> > +
> > + return res;
> > +}
> > +
> > +/*
> > + * Special cases for traps and trap returns:
> > + *
> > + * 1- Traps, and trap returns, between enabled modes are recorded as normal.
> > + * 2- Traps from an inhibited mode to an enabled mode, and trap returns from an
> > + * enabled mode back to an inhibited mode, are partially recorded. In such
> > + * cases, the PC from the inhibited mode (source PC for traps, and target PC
> > + * for trap returns) is 0.
> > + *
> > + * 3- Trap returns from an inhibited mode to an enabled mode are not recorded.
> > + * Traps from an enabled mode to an inhibited mode, known as external traps,
> > + * receive special handling.
> > + * By default external traps are not recorded, but a handshake mechanism exists
> > + * to allow partial recording. Software running in the target mode of the trap
> > + * can opt-in to allowing CTR to record traps into that mode even when the mode
> > + * is inhibited. The MTE, STE, and VSTE bits allow M-mode, S-mode, and VS-mode,
> > + * respectively, to opt-in. When an External Trap occurs, and xTE=1, such that
> > + * x is the target privilege mode of the trap, will CTR record the trap. In such
> > + * cases, the target PC is 0.
> > + */
> > +/*
> > + * CTR arrays are implemented as circular buffers and new entry is stored at
> > + * sctrstatus.WRPTR, but they are presented to software as moving circular
> > + * buffers. Which means, software get's the illusion that whenever a new entry
> > + * is added the whole buffer is moved by one place and the new entry is added at
> > + * the start keeping new entry at idx 0 and older ones follow.
> > + *
> > + * Depth = 16.
> > + *
> > + * buffer [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F]
> > + * WRPTR W
> > + * entry 7 6 5 4 3 2 1 0 F E D C B A 9 8
> > + *
> > + * When a new entry is added:
> > + * buffer [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F]
> > + * WRPTR W
> > + * entry 8 7 6 5 4 3 2 1 0 F E D C B A 9
> > + *
> > + * entry here denotes the logical entry number that software can access
> > + * using ctrsource, ctrtarget and ctrdata registers. So xiselect 0x200
> > + * will return entry 0 i-e buffer[8] and 0x201 will return entry 1 i-e
> > + * buffer[7]. Here is how we convert entry to buffer idx.
> > + *
> > + * entry = isel - CTR_ENTRIES_FIRST;
> > + * idx = (sctrstatus.WRPTR - entry - 1) & (depth - 1);
> > + */
> > +void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst,
> > + uint64_t type, target_ulong src_priv, bool src_virt)
> > +{
> > + bool tgt_virt = env->virt_enabled;
> > + uint64_t src_mask = riscv_ctr_priv_to_mask(src_priv, src_virt);
> > + uint64_t tgt_mask = riscv_ctr_priv_to_mask(env->priv, tgt_virt);
> > + uint64_t src_ctrl = riscv_ctr_get_control(env, src_priv, src_virt);
> > + uint64_t tgt_ctrl = riscv_ctr_get_control(env, env->priv, tgt_virt);
> > + uint64_t depth, head;
> > + bool ext_trap = false;
> > +
> > + /*
> > + * Return immediately if both target and src recording is disabled or if
> > + * CTR is in frozen state.
> > + */
> > + if ((!(src_ctrl & src_mask) && !(tgt_ctrl & tgt_mask)) ||
> > + env->sctrstatus & SCTRSTATUS_FROZEN) {
> > + return;
> > + }
> > +
> > + /*
> > + * With RAS Emul enabled, only allow Indirect, direct calls, Function
> > + * returns and Co-routine swap types.
> > + */
> > + if (env->mctrctl & MCTRCTL_RASEMU &&
> I think we should check vsctrctl.RASEMU for VS and VU mode.
> You can consider defining a variable here as it will be used multiple
> times below.
> bool rasemu = MCTRCTL_RASEMU & ((env->virt_enabled) ? env->vsctrctl :
> env->mctrctl);
Sorry my bad. I understand what you are saying but I think just replacing
env->mctrctl with tgt_ctl should fix this. We have a similar RASEMU check in
this same function.
> > + type != CTRDATA_TYPE_INDIRECT_CALL &&
> > + type != CTRDATA_TYPE_DIRECT_CALL &&
> > + type != CTRDATA_TYPE_RETURN &&
> > + type != CTRDATA_TYPE_CO_ROUTINE_SWAP) {
> > + return;
> > + }
> > +
> > + if (type == CTRDATA_TYPE_EXCEPTION || type == CTRDATA_TYPE_INTERRUPT) {
> > + /* Case 2 for traps. */
> > + if (!(src_ctrl & src_mask)) {
> > + src = 0;
> > + } else if (!(tgt_ctrl & tgt_mask)) {
> > + /* Check if target priv-mode has allowed external trap recording. */
> > + if (!riscv_ctr_check_xte(env, src_priv, src_virt)) {
> > + return;
> > + }
> > +
> > + ext_trap = true;
> > + dst = 0;
> > + }
> > + } else if (type == CTRDATA_TYPE_EXCEP_INT_RET) {
> > + /*
> > + * Case 3 for trap returns. Trap returns from inhibited mode are not
> > + * recorded.
> > + */
> > + if (!(src_ctrl & src_mask)) {
> > + return;
> > + }
> > +
> > + /* Case 2 for trap returns. */
> > + if (!(tgt_ctrl & tgt_mask)) {
> > + dst = 0;
> > + }
> > + }
> > +
> > + /* Ignore filters in case of RASEMU mode or External trap. */
> > + if (!(tgt_ctrl & MCTRCTL_RASEMU) && !ext_trap) {
> > + /*
> > + * Check if the specific type is inhibited. Not taken branch filter is
> > + * an enable bit and needs to be checked separatly.
> > + */
> > + bool check = tgt_ctrl & BIT_ULL(type + MCTRCTL_INH_START);
> > + if ((type == CTRDATA_TYPE_NONTAKEN_BRANCH && !check) ||
> > + (type != CTRDATA_TYPE_NONTAKEN_BRANCH && check)) {
> > + return;
> > + }
> > + }
> > +
> > + head = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK);
> > +
> > + depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
> > + if (tgt_ctrl & MCTRCTL_RASEMU && type == CTRDATA_TYPE_RETURN) {
> > + head = (head - 1) & (depth - 1);
> > +
> > + env->ctr_src[head] &= ~CTRSOURCE_VALID;
> > + env->sctrstatus =
> > + set_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK, head);
> > + return;
> > + }
> > +
> > + /* In case of Co-routine SWAP we overwrite latest entry. */
> > + if (tgt_ctrl & MCTRCTL_RASEMU && type == CTRDATA_TYPE_CO_ROUTINE_SWAP) {
> > + head = (head - 1) & (depth - 1);
> > + }
>
> The code can be reduced here.
>
> if (rasemu) {
> head = (head - 1) & (depth - 1);
> if (CTRDATA_TYPE_CO_ROUTINE_SWAP) { ... }
> else if (CTRDATA_TYPE_CO_ROUTINE_SWAP) { ... }
> }
>
> > +
> > + env->ctr_src[head] = src | CTRSOURCE_VALID;
> > + env->ctr_dst[head] = dst & ~CTRTARGET_MISP;
> > + env->ctr_data[head] = set_field(0, CTRDATA_TYPE_MASK, type);
> > +
> > + head = (head + 1) & (depth - 1);
> > +
> > + env->sctrstatus = set_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK, head);
> > +}
> > +
> > void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en)
> > {
> > g_assert(newpriv <= PRV_M && newpriv != PRV_RESERVED);
> > @@ -1669,10 +1909,13 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> > !(env->mip & (1 << cause));
> > bool vs_injected = env->hvip & (1 << cause) & env->hvien &&
> > !(env->mip & (1 << cause));
> > + const bool prev_virt = env->virt_enabled;
> > + const target_ulong prev_priv = env->priv;
> > target_ulong tval = 0;
> > target_ulong tinst = 0;
> > target_ulong htval = 0;
> > target_ulong mtval2 = 0;
> > + target_ulong src;
> >
> > if (!async) {
> > /* set tval to badaddr for traps with address information */
> > @@ -1807,6 +2050,8 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> > env->pc = (env->stvec >> 2 << 2) +
> > ((async && (env->stvec & 3) == 1) ? cause * 4 : 0);
> > riscv_cpu_set_mode(env, PRV_S, virt);
> > +
> > + src = env->sepc;
> > } else {
> > /* handle the trap in M-mode */
> > if (riscv_has_ext(env, RVH)) {
> > @@ -1838,6 +2083,19 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> > env->pc = (env->mtvec >> 2 << 2) +
> > ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
> > riscv_cpu_set_mode(env, PRV_M, virt);
> > + src = env->mepc;
> > + }
> > +
> > + if (riscv_cpu_cfg(env)->ext_smctr || riscv_cpu_cfg(env)->ext_ssctr) {
> > + if (async && cause == IRQ_PMU_OVF) {
> > + riscv_ctr_freeze(env, MCTRCTL_LCOFIFRZ, virt);
> > + } else if (!async && cause == RISCV_EXCP_BREAKPOINT) {
> > + riscv_ctr_freeze(env, MCTRCTL_BPFRZ, virt);
> > + }
> > +
> > + riscv_ctr_add_entry(env, src, env->pc,
> > + async ? CTRDATA_TYPE_INTERRUPT : CTRDATA_TYPE_EXCEPTION,
> > + prev_priv, prev_virt);
> > }
> >
> > /*
> > diff --git a/target/riscv/helper.h b/target/riscv/helper.h
> > index 451261ce5a..b8fb7c8734 100644
> > --- a/target/riscv/helper.h
> > +++ b/target/riscv/helper.h
> > @@ -129,12 +129,16 @@ DEF_HELPER_2(csrr_i128, tl, env, int)
> > DEF_HELPER_4(csrw_i128, void, env, int, tl, tl)
> > DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl)
> > #ifndef CONFIG_USER_ONLY
> > -DEF_HELPER_1(sret, tl, env)
> > -DEF_HELPER_1(mret, tl, env)
> > +DEF_HELPER_2(sret, tl, env, tl)
> > +DEF_HELPER_2(mret, tl, env, tl)
> > DEF_HELPER_1(wfi, void, env)
> > DEF_HELPER_1(wrs_nto, void, env)
> > DEF_HELPER_1(tlb_flush, void, env)
> > DEF_HELPER_1(tlb_flush_all, void, env)
> > +DEF_HELPER_4(ctr_branch, void, env, tl, tl, tl)
> > +DEF_HELPER_4(ctr_jal, void, env, tl, tl, tl)
> > +DEF_HELPER_5(ctr_jalr, void, env, tl, tl, tl, tl)
> > +DEF_HELPER_3(ctr_popret, void, env, tl, tl)
> > /* Native Debug */
> > DEF_HELPER_1(itrigger_match, void, env)
> > #endif
> > diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
> > index 4eccdddeaa..339d659151 100644
> > --- a/target/riscv/insn_trans/trans_privileged.c.inc
> > +++ b/target/riscv/insn_trans/trans_privileged.c.inc
> > @@ -78,9 +78,10 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a)
> > {
> > #ifndef CONFIG_USER_ONLY
> > if (has_ext(ctx, RVS)) {
> > + TCGv src = tcg_constant_tl(ctx->base.pc_next);
> > decode_save_opc(ctx);
> > translator_io_start(&ctx->base);
> > - gen_helper_sret(cpu_pc, tcg_env);
> > + gen_helper_sret(cpu_pc, tcg_env, src);
> > exit_tb(ctx); /* no chaining */
> > ctx->base.is_jmp = DISAS_NORETURN;
> > } else {
> > @@ -95,9 +96,10 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a)
> > static bool trans_mret(DisasContext *ctx, arg_mret *a)
> > {
> > #ifndef CONFIG_USER_ONLY
> > + TCGv src = tcg_constant_tl(ctx->base.pc_next);
> > decode_save_opc(ctx);
> > translator_io_start(&ctx->base);
> > - gen_helper_mret(cpu_pc, tcg_env);
> > + gen_helper_mret(cpu_pc, tcg_env, src);
> > exit_tb(ctx); /* no chaining */
> > ctx->base.is_jmp = DISAS_NORETURN;
> > return true;
> > diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc
> > index ad40d3e87f..26633569a8 100644
> > --- a/target/riscv/insn_trans/trans_rvi.c.inc
> > +++ b/target/riscv/insn_trans/trans_rvi.c.inc
> > @@ -75,6 +75,14 @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a)
> > gen_set_gpr(ctx, a->rd, succ_pc);
> >
> > tcg_gen_mov_tl(cpu_pc, target_pc);
> > +#ifndef CONFIG_USER_ONLY
> > + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
> > + TCGv src = tcg_constant_tl(ctx->base.pc_next);
> > + TCGv rs1 = tcg_constant_tl(a->rs1);
> > + TCGv rd = tcg_constant_tl(a->rd);
> > + gen_helper_ctr_jalr(tcg_env, src, cpu_pc, rd, rs1);
> > + }
> > +#endif
> > lookup_and_goto_ptr(ctx);
> >
> > if (misaligned) {
> > @@ -164,6 +172,11 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond)
> > TCGv src1 = get_gpr(ctx, a->rs1, EXT_SIGN);
> > TCGv src2 = get_gpr(ctx, a->rs2, EXT_SIGN);
> > target_ulong orig_pc_save = ctx->pc_save;
> > +#ifndef CONFIG_USER_ONLY
> > + TCGv src = tcg_constant_tl(ctx->base.pc_next);
> > + TCGv taken;
> > + TCGv dest;
> > +#endif
> >
> > if (get_xl(ctx) == MXL_RV128) {
> > TCGv src1h = get_gprh(ctx, a->rs1);
> > @@ -176,6 +189,16 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond)
> > } else {
> > tcg_gen_brcond_tl(cond, src1, src2, l);
> > }
> > +
> > +#ifndef CONFIG_USER_ONLY
> > + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
> > + dest = tcg_constant_tl(ctx->base.pc_next + ctx->cur_insn_len);
> > + taken = tcg_constant_tl(0);
> > +
> > + gen_helper_ctr_branch(tcg_env, src, dest, taken);
> > + }
> > +#endif
> > +
> > gen_goto_tb(ctx, 1, ctx->cur_insn_len);
> > ctx->pc_save = orig_pc_save;
> >
> > @@ -188,6 +211,14 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond)
> > gen_pc_plus_diff(target_pc, ctx, a->imm);
> > gen_exception_inst_addr_mis(ctx, target_pc);
> > } else {
> > +#ifndef CONFIG_USER_ONLY
> > + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
> > + dest = tcg_constant_tl(ctx->base.pc_next + a->imm);
> > + taken = tcg_constant_tl(1);
> > +
> > + gen_helper_ctr_branch(tcg_env, src, dest, taken);
> > + }
> > +#endif
> > gen_goto_tb(ctx, 0, a->imm);
> > }
> > ctx->pc_save = -1;
> > diff --git a/target/riscv/insn_trans/trans_rvzce.c.inc b/target/riscv/insn_trans/trans_rvzce.c.inc
> > index cd234ad960..377d3fff70 100644
> > --- a/target/riscv/insn_trans/trans_rvzce.c.inc
> > +++ b/target/riscv/insn_trans/trans_rvzce.c.inc
> > @@ -204,6 +204,12 @@ static bool gen_pop(DisasContext *ctx, arg_cmpp *a, bool ret, bool ret_val)
> > if (ret) {
> > TCGv ret_addr = get_gpr(ctx, xRA, EXT_SIGN);
> > tcg_gen_mov_tl(cpu_pc, ret_addr);
> > +#ifndef CONFIG_USER_ONLY
> > + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
> > + TCGv src = tcg_constant_tl(ctx->base.pc_next);
> > + gen_helper_ctr_popret(tcg_env, src, cpu_pc);
> > + }
> > +#endif
> > tcg_gen_lookup_and_goto_ptr();
> > ctx->base.is_jmp = DISAS_NORETURN;
> > }
> > @@ -309,6 +315,20 @@ static bool trans_cm_jalt(DisasContext *ctx, arg_cm_jalt *a)
> > gen_set_gpr(ctx, xRA, succ_pc);
> > }
> >
> > +#ifndef CONFIG_USER_ONLY
> > + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
> > + /*
> > + * We are reusing helper_ctr_jal() here. If rd is x1 or x5,
> > + * this will record a direct call (cm.jalt) and if it's x0
> > + * then this will record a direct jump (cm.jt).
> > + */
> > + TCGv rd = tcg_constant_tl(a->index >= 32 ? 1 : 0);
> > + TCGv src = tcg_constant_tl(ctx->base.pc_next);
> > + gen_helper_ctr_jal(tcg_env, src, addr, rd);
> > + }
> > +#endif
> > +
> > +
> > tcg_gen_mov_tl(cpu_pc, addr);
> >
> > tcg_gen_lookup_and_goto_ptr();
> > diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
> > index 25a5263573..5a1e92c45e 100644
> > --- a/target/riscv/op_helper.c
> > +++ b/target/riscv/op_helper.c
> > @@ -259,10 +259,12 @@ void helper_cbo_inval(CPURISCVState *env, target_ulong address)
> >
> > #ifndef CONFIG_USER_ONLY
> >
> > -target_ulong helper_sret(CPURISCVState *env)
> > +target_ulong helper_sret(CPURISCVState *env, target_ulong curr_pc)
> > {
> > uint64_t mstatus;
> > target_ulong prev_priv, prev_virt = env->virt_enabled;
> > + const target_ulong src_priv = env->priv;
> > + const bool src_virt = env->virt_enabled;
> >
> > if (!(env->priv >= PRV_S)) {
> > riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> > @@ -309,10 +311,15 @@ target_ulong helper_sret(CPURISCVState *env)
> >
> > riscv_cpu_set_mode(env, prev_priv, prev_virt);
> >
> > + if (riscv_cpu_cfg(env)->ext_smctr || riscv_cpu_cfg(env)->ext_ssctr) {
> > + riscv_ctr_add_entry(env, curr_pc, retpc, CTRDATA_TYPE_EXCEP_INT_RET,
> > + src_priv, src_virt);
> > + }
> > +
> > return retpc;
> > }
> >
> > -target_ulong helper_mret(CPURISCVState *env)
> > +target_ulong helper_mret(CPURISCVState *env, target_ulong curr_pc)
> > {
> > if (!(env->priv >= PRV_M)) {
> > riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> > @@ -350,9 +357,124 @@ target_ulong helper_mret(CPURISCVState *env)
> >
> > riscv_cpu_set_mode(env, prev_priv, prev_virt);
> >
> > + if (riscv_cpu_cfg(env)->ext_smctr || riscv_cpu_cfg(env)->ext_ssctr) {
> > + riscv_ctr_add_entry(env, curr_pc, retpc, CTRDATA_TYPE_EXCEP_INT_RET,
> > + PRV_M, false);
> > + }
> > +
> > return retpc;
> > }
> >
> > +/*
> > + * Indirect calls
> > + * - jalr x1, rs where rs != x5;
> > + * - jalr x5, rs where rs != x1;
> > + * - c.jalr rs1 where rs1 != x5;
> > + *
> > + * Indirect jumps
> > + * - jalr x0, rs where rs != x1 and rs != x5;
> > + * - c.jr rs1 where rs1 != x1 and rs1 != x5.
> > + *
> > + * Returns
> > + * - jalr rd, rs where (rs == x1 or rs == x5) and rd != x1 and rd != x5;
> > + * - c.jr rs1 where rs1 == x1 or rs1 == x5.
> > + *
> > + * Co-routine swap
> > + * - jalr x1, x5;
> > + * - jalr x5, x1;
> > + * - c.jalr x5.
> > + *
> > + * Other indirect jumps
> > + * - jalr rd, rs where rs != x1, rs != x5, rd != x0, rd != x1 and rd != x5.
> > + */
> > +void helper_ctr_jalr(CPURISCVState *env, target_ulong src, target_ulong dest,
> > + target_ulong rd, target_ulong rs1)
> > +{
> > + target_ulong curr_priv = env->priv;
> > + bool curr_virt = env->virt_enabled;
> > +
> > + if ((rd == 1 && rs1 != 5) || (rd == 5 && rs1 != 1)) {
> > + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_INDIRECT_CALL,
> > + curr_priv, curr_virt);
> > + } else if (rd == 0 && rs1 != 1 && rs1 != 5) {
> > + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_INDIRECT_JUMP,
> > + curr_priv, curr_virt);
> > + } else if ((rs1 == 1 || rs1 == 5) && (rd != 1 && rd != 5)) {
> > + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_RETURN,
> > + curr_priv, curr_virt);
> > + } else if ((rs1 == 1 && rd == 5) || (rs1 == 5 && rd == 1)) {
> > + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_CO_ROUTINE_SWAP,
> > + curr_priv, curr_virt);
> > + } else {
> > + riscv_ctr_add_entry(env, src, dest,
> > + CTRDATA_TYPE_OTHER_INDIRECT_JUMP, curr_priv,
> > + curr_virt);
> > + }
> > +}
> > +
> > +/*
> > + * Direct calls
> > + * - jal x1;
> > + * - jal x5;
> > + * - c.jal.
> > + * - cm.jalt.
> > + *
> > + * Direct jumps
> > + * - jal x0;
> > + * - c.j;
> > + * - cm.jt.
> > + *
> > + * Other direct jumps
> > + * - jal rd where rd != x1 and rd != x5 and rd != x0;
> > + */
> > +void helper_ctr_jal(CPURISCVState *env, target_ulong src, target_ulong dest,
> > + target_ulong rd)
> > +{
> > + target_ulong priv = env->priv;
> > + bool virt = env->virt_enabled;
> > +
> > + /*
> > + * If rd is x1 or x5 link registers, treat this as direct call otherwise
> > + * its a direct jump.
> > + */
> > + if (rd == 1 || rd == 5) {
> > + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_DIRECT_CALL, priv,
> > + virt);
> > + } else if (rd == 0) {
> > + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_DIRECT_JUMP, priv,
> > + virt);
> > + } else {
> > + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_OTHER_DIRECT_JUMP,
> > + priv, virt);
> > + }
> > +}
> > +
> > +/*
> > + * Returns
> > + * - cm.popret
> > + * - cm.popretz
> > + */
> > +void helper_ctr_popret(CPURISCVState *env, target_ulong src, target_ulong dest)
> > +{
> > + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_RETURN,
> > + env->priv, env->virt_enabled);
> > +}
> > +
> > +void helper_ctr_branch(CPURISCVState *env, target_ulong src, target_ulong dest,
> > + target_ulong branch_taken)
> > +{
> > + target_ulong curr_priv = env->priv;
> > + bool curr_virt = env->virt_enabled;
> > +
> > + if (branch_taken) {
> > + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_TAKEN_BRANCH,
> > + curr_priv, curr_virt);
> > + } else {
> > + riscv_ctr_add_entry(env, src, dest, CTRDATA_TYPE_NONTAKEN_BRANCH,
> > + curr_priv, curr_virt);
> > + }
> > +}
> > +
> > void helper_wfi(CPURISCVState *env)
> > {
> > CPUState *cs = env_cpu(env);
> > diff --git a/target/riscv/translate.c b/target/riscv/translate.c
> > index 15e7123a68..07391297e8 100644
> > --- a/target/riscv/translate.c
> > +++ b/target/riscv/translate.c
> > @@ -572,6 +572,16 @@ static void gen_jal(DisasContext *ctx, int rd, target_ulong imm)
> > }
> > }
> >
> > +#ifndef CONFIG_USER_ONLY
> > + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) {
> > + TCGv dest = tcg_constant_tl(ctx->base.pc_next + imm);
> > + TCGv src = tcg_constant_tl(ctx->base.pc_next);
> > + TCGv tcg_rd = tcg_constant_tl((target_ulong)rd);
> > +
> > + gen_helper_ctr_jal(tcg_env, src, dest, tcg_rd);
> > + }
> > +#endif
> > +
> > gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len);
> > gen_set_gpr(ctx, rd, succ_pc);
> >
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 3/6] target/riscv: Add support for Control Transfer Records extension CSRs.
2024-08-27 9:28 ` Frank Chang
@ 2024-10-17 11:18 ` Rajnesh Kanwal
2024-10-17 15:04 ` Frank Chang
0 siblings, 1 reply; 21+ messages in thread
From: Rajnesh Kanwal @ 2024-10-17 11:18 UTC (permalink / raw)
To: Frank Chang
Cc: qemu-riscv, qemu-devel, alistair.francis, bin.meng, liweiwei,
dbarboza, zhiwei_liu, atishp, apatel, beeman,
tech-control-transfer-records, jason.chien
On Tue, Aug 27, 2024 at 10:28 AM Frank Chang <frank.chang@sifive.com> wrote:
>
> Rajnesh Kanwal <rkanwal@rivosinc.com> 於 2024年6月19日 週三 下午11:27寫道:
> >
> > This commit adds support for [m|s|vs]ctrcontrol, sctrstatus and
> > sctrdepth CSRs handling.
> >
> > Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
> > ---
> > target/riscv/cpu.h | 5 ++
> > target/riscv/cpu_cfg.h | 2 +
> > target/riscv/csr.c | 128 +++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 135 insertions(+)
> >
> > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> > index a185e2d494..3d4d5172b8 100644
> > --- a/target/riscv/cpu.h
> > +++ b/target/riscv/cpu.h
> > @@ -263,6 +263,11 @@ struct CPUArchState {
> > target_ulong mcause;
> > target_ulong mtval; /* since: priv-1.10.0 */
> >
> > + uint64_t mctrctl;
> > + uint32_t sctrdepth;
> > + uint32_t sctrstatus;
> > + uint64_t vsctrctl;
> > +
> > /* Machine and Supervisor interrupt priorities */
> > uint8_t miprio[64];
> > uint8_t siprio[64];
> > diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h
> > index d9354dc80a..d329a65811 100644
> > --- a/target/riscv/cpu_cfg.h
> > +++ b/target/riscv/cpu_cfg.h
> > @@ -123,6 +123,8 @@ struct RISCVCPUConfig {
> > bool ext_zvfhmin;
> > bool ext_smaia;
> > bool ext_ssaia;
> > + bool ext_smctr;
> > + bool ext_ssctr;
>
> Base on: https://github.com/riscv/riscv-control-transfer-records/pull/29
> Smctr and Ssctr depend on both S-mode and Sscsrind.
> We should add the implied rules for Smctr and Ssctr.
>
> Regards,
> Frank Chang
Hi Frank,
Are you referring to the checks in riscv_cpu_validate_set_extensions()?
These checks are already present in the last patch.
https://lore.kernel.org/qemu-riscv/20240619152708.135991-7-rkanwal@rivosinc.com/
>
>
> > bool ext_sscofpmf;
> > bool ext_smepmp;
> > bool rvv_ta_all_1s;
> > diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> > index 2f92e4b717..0b5bf4d050 100644
> > --- a/target/riscv/csr.c
> > +++ b/target/riscv/csr.c
> > @@ -621,6 +621,48 @@ static RISCVException pointer_masking(CPURISCVState *env, int csrno)
> > return RISCV_EXCP_ILLEGAL_INST;
> > }
> >
> > +/*
> > + * M-mode:
> > + * Without ext_smctr raise illegal inst excep.
> > + * Otherwise everything is accessible to m-mode.
> > + *
> > + * S-mode:
> > + * Without ext_ssctr or mstateen.ctr raise illegal inst excep.
> > + * Otherwise everything other than mctrctl is accessible.
> > + *
> > + * VS-mode:
> > + * Without ext_ssctr or mstateen.ctr raise illegal inst excep.
> > + * Without hstateen.ctr raise virtual illegal inst excep.
> > + * Otherwise allow sctrctl (vsctrctl), sctrstatus, 0x200-0x2ff entry range.
> > + * Always raise illegal instruction exception for sctrdepth.
> > + */
> > +static RISCVException ctr_mmode(CPURISCVState *env, int csrno)
> > +{
> > + /* Check if smctr-ext is present */
> > + if (riscv_cpu_cfg(env)->ext_smctr) {
> > + return RISCV_EXCP_NONE;
> > + }
> > +
> > + return RISCV_EXCP_ILLEGAL_INST;
> > +}
> > +
> > +static RISCVException ctr_smode(CPURISCVState *env, int csrno)
> > +{
> > + const RISCVCPUConfig *cfg = riscv_cpu_cfg(env);
> > +
> > + if (!cfg->ext_smctr && !cfg->ext_ssctr) {
> > + return RISCV_EXCP_ILLEGAL_INST;
> > + }
> > +
> > + RISCVException ret = smstateen_acc_ok(env, 0, SMSTATEEN0_CTR);
> > + if (ret == RISCV_EXCP_NONE && csrno == CSR_SCTRDEPTH &&
> > + env->virt_enabled) {
> > + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
> > + }
> > +
> > + return ret;
> > +}
> > +
> > static RISCVException aia_hmode(CPURISCVState *env, int csrno)
> > {
> > int ret;
> > @@ -3835,6 +3877,86 @@ static RISCVException write_satp(CPURISCVState *env, int csrno,
> > return RISCV_EXCP_NONE;
> > }
> >
> > +static RISCVException rmw_sctrdepth(CPURISCVState *env, int csrno,
> > + target_ulong *ret_val,
> > + target_ulong new_val, target_ulong wr_mask)
> > +{
> > + uint64_t mask = wr_mask & SCTRDEPTH_MASK;
> > +
> > + if (ret_val) {
> > + *ret_val = env->sctrdepth;
> > + }
> > +
> > + env->sctrdepth = (env->sctrdepth & ~mask) | (new_val & mask);
> > +
> > + /* Correct depth. */
> > + if (wr_mask & SCTRDEPTH_MASK) {
> > + uint64_t depth = get_field(env->sctrdepth, SCTRDEPTH_MASK);
> > +
> > + if (depth > SCTRDEPTH_MAX) {
> > + depth = SCTRDEPTH_MAX;
> > + env->sctrdepth = set_field(env->sctrdepth, SCTRDEPTH_MASK, depth);
> > + }
> > +
> > + /* Update sctrstatus.WRPTR with a legal value */
> > + depth = 16 << depth;
> > + env->sctrstatus =
> > + env->sctrstatus & (~SCTRSTATUS_WRPTR_MASK | (depth - 1));
> > + }
> > +
> > + return RISCV_EXCP_NONE;
> > +}
> > +
> > +static RISCVException rmw_sctrstatus(CPURISCVState *env, int csrno,
> > + target_ulong *ret_val,
> > + target_ulong new_val, target_ulong wr_mask)
> > +{
> > + uint32_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
> > + uint32_t mask = wr_mask & SCTRSTATUS_MASK;
> > +
> > + if (ret_val) {
> > + *ret_val = env->sctrstatus;
> > + }
> > +
> > + env->sctrstatus = (env->sctrstatus & ~mask) | (new_val & mask);
> > +
> > + /* Update sctrstatus.WRPTR with a legal value */
> > + env->sctrstatus = env->sctrstatus & (~SCTRSTATUS_WRPTR_MASK | (depth - 1));
> > +
> > + return RISCV_EXCP_NONE;
> > +}
> > +
> > +static RISCVException rmw_xctrctl(CPURISCVState *env, int csrno,
> > + target_ulong *ret_val,
> > + target_ulong new_val, target_ulong wr_mask)
> > +{
> > + uint64_t csr_mask, mask = wr_mask;
> > + uint64_t *ctl_ptr = &env->mctrctl;
> > +
> > + if (csrno == CSR_MCTRCTL) {
> > + csr_mask = MCTRCTL_MASK;
> > + } else if (csrno == CSR_SCTRCTL && !env->virt_enabled) {
> > + csr_mask = SCTRCTL_MASK;
> > + } else {
> > + /*
> > + * This is for csrno == CSR_SCTRCTL and env->virt_enabled == true
> > + * or csrno == CSR_VSCTRCTL.
> > + */
> > + csr_mask = VSCTRCTL_MASK;
> > + ctl_ptr = &env->vsctrctl;
> > + }
> > +
> > + mask &= csr_mask;
> > +
> > + if (ret_val) {
> > + *ret_val = *ctl_ptr & csr_mask;
> > + }
> > +
> > + *ctl_ptr = (*ctl_ptr & ~mask) | (new_val & mask);
> > +
> > + return RISCV_EXCP_NONE;
> > +}
> > +
> > static RISCVException read_vstopi(CPURISCVState *env, int csrno,
> > target_ulong *val)
> > {
> > @@ -5771,6 +5893,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
> > [CSR_SPMBASE] = { "spmbase", pointer_masking, read_spmbase,
> > write_spmbase },
> >
> > + [CSR_MCTRCTL] = { "mctrctl", ctr_mmode, NULL, NULL, rmw_xctrctl },
> > + [CSR_SCTRCTL] = { "sctrctl", ctr_smode, NULL, NULL, rmw_xctrctl },
> > + [CSR_VSCTRCTL] = { "vsctrctl", ctr_smode, NULL, NULL, rmw_xctrctl },
> > + [CSR_SCTRDEPTH] = { "sctrdepth", ctr_smode, NULL, NULL, rmw_sctrdepth },
> > + [CSR_SCTRSTATUS] = { "sctrstatus", ctr_smode, NULL, NULL, rmw_sctrstatus },
> > +
> > /* Performance Counters */
> > [CSR_HPMCOUNTER3] = { "hpmcounter3", ctr, read_hpmcounter },
> > [CSR_HPMCOUNTER4] = { "hpmcounter4", ctr, read_hpmcounter },
> > --
> > 2.34.1
> >
> >
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 3/6] target/riscv: Add support for Control Transfer Records extension CSRs.
2024-10-17 11:18 ` Rajnesh Kanwal
@ 2024-10-17 15:04 ` Frank Chang
2024-10-18 10:24 ` Rajnesh Kanwal
0 siblings, 1 reply; 21+ messages in thread
From: Frank Chang @ 2024-10-17 15:04 UTC (permalink / raw)
To: Rajnesh Kanwal
Cc: qemu-riscv, qemu-devel, alistair.francis, bin.meng, liweiwei,
dbarboza, zhiwei_liu, atishp, apatel, beeman,
tech-control-transfer-records, jason.chien
[-- Attachment #1: Type: text/plain, Size: 9248 bytes --]
On Thu, Oct 17, 2024 at 7:18 PM Rajnesh Kanwal <rkanwal@rivosinc.com> wrote:
> On Tue, Aug 27, 2024 at 10:28 AM Frank Chang <frank.chang@sifive.com>
> wrote:
> >
> > Rajnesh Kanwal <rkanwal@rivosinc.com> 於 2024年6月19日 週三 下午11:27寫道:
> > >
> > > This commit adds support for [m|s|vs]ctrcontrol, sctrstatus and
> > > sctrdepth CSRs handling.
> > >
> > > Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
> > > ---
> > > target/riscv/cpu.h | 5 ++
> > > target/riscv/cpu_cfg.h | 2 +
> > > target/riscv/csr.c | 128 +++++++++++++++++++++++++++++++++++++++++
> > > 3 files changed, 135 insertions(+)
> > >
> > > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> > > index a185e2d494..3d4d5172b8 100644
> > > --- a/target/riscv/cpu.h
> > > +++ b/target/riscv/cpu.h
> > > @@ -263,6 +263,11 @@ struct CPUArchState {
> > > target_ulong mcause;
> > > target_ulong mtval; /* since: priv-1.10.0 */
> > >
> > > + uint64_t mctrctl;
> > > + uint32_t sctrdepth;
> > > + uint32_t sctrstatus;
> > > + uint64_t vsctrctl;
> > > +
> > > /* Machine and Supervisor interrupt priorities */
> > > uint8_t miprio[64];
> > > uint8_t siprio[64];
> > > diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h
> > > index d9354dc80a..d329a65811 100644
> > > --- a/target/riscv/cpu_cfg.h
> > > +++ b/target/riscv/cpu_cfg.h
> > > @@ -123,6 +123,8 @@ struct RISCVCPUConfig {
> > > bool ext_zvfhmin;
> > > bool ext_smaia;
> > > bool ext_ssaia;
> > > + bool ext_smctr;
> > > + bool ext_ssctr;
> >
> > Base on: https://github.com/riscv/riscv-control-transfer-records/pull/29
> > Smctr and Ssctr depend on both S-mode and Sscsrind.
> > We should add the implied rules for Smctr and Ssctr.
> >
> > Regards,
> > Frank Chang
>
> Hi Frank,
>
> Are you referring to the checks in riscv_cpu_validate_set_extensions()?
> These checks are already present in the last patch.
>
>
> https://lore.kernel.org/qemu-riscv/20240619152708.135991-7-rkanwal@rivosinc.com/
>
>
Hi Rajnesh,
No, I was referring to the implied rules defined in:
https://github.com/qemu/qemu/blob/master/target/riscv/cpu.c#L2630
The implied rules will enable the implied extensions when the "parent"
extension is enabled.
Unless user turns them off explicitly from the command line,
which is an error and will be caught by the check rules you mentioned above.
The spec defines that:
"Smctr and Ssctr depend on both the implementation of S-mode and the
Sscsrind extension"
which means that we should add RVS and Sscsrind extensions as the implied
rules for Smctr and Ssctr respectively.
The use of the word "depends" in the spec is quite ambiguous.
But I once had a chance to ask Andrew Waterman about it,
and he replied that we should treat “depends on” or “requires” as a synonym
for “implies”.
Regards,
Frank Chang
>
> >
> > > bool ext_sscofpmf;
> > > bool ext_smepmp;
> > > bool rvv_ta_all_1s;
> > > diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> > > index 2f92e4b717..0b5bf4d050 100644
> > > --- a/target/riscv/csr.c
> > > +++ b/target/riscv/csr.c
> > > @@ -621,6 +621,48 @@ static RISCVException
> pointer_masking(CPURISCVState *env, int csrno)
> > > return RISCV_EXCP_ILLEGAL_INST;
> > > }
> > >
> > > +/*
> > > + * M-mode:
> > > + * Without ext_smctr raise illegal inst excep.
> > > + * Otherwise everything is accessible to m-mode.
> > > + *
> > > + * S-mode:
> > > + * Without ext_ssctr or mstateen.ctr raise illegal inst excep.
> > > + * Otherwise everything other than mctrctl is accessible.
> > > + *
> > > + * VS-mode:
> > > + * Without ext_ssctr or mstateen.ctr raise illegal inst excep.
> > > + * Without hstateen.ctr raise virtual illegal inst excep.
> > > + * Otherwise allow sctrctl (vsctrctl), sctrstatus, 0x200-0x2ff entry
> range.
> > > + * Always raise illegal instruction exception for sctrdepth.
> > > + */
> > > +static RISCVException ctr_mmode(CPURISCVState *env, int csrno)
> > > +{
> > > + /* Check if smctr-ext is present */
> > > + if (riscv_cpu_cfg(env)->ext_smctr) {
> > > + return RISCV_EXCP_NONE;
> > > + }
> > > +
> > > + return RISCV_EXCP_ILLEGAL_INST;
> > > +}
> > > +
> > > +static RISCVException ctr_smode(CPURISCVState *env, int csrno)
> > > +{
> > > + const RISCVCPUConfig *cfg = riscv_cpu_cfg(env);
> > > +
> > > + if (!cfg->ext_smctr && !cfg->ext_ssctr) {
> > > + return RISCV_EXCP_ILLEGAL_INST;
> > > + }
> > > +
> > > + RISCVException ret = smstateen_acc_ok(env, 0, SMSTATEEN0_CTR);
> > > + if (ret == RISCV_EXCP_NONE && csrno == CSR_SCTRDEPTH &&
> > > + env->virt_enabled) {
> > > + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
> > > + }
> > > +
> > > + return ret;
> > > +}
> > > +
> > > static RISCVException aia_hmode(CPURISCVState *env, int csrno)
> > > {
> > > int ret;
> > > @@ -3835,6 +3877,86 @@ static RISCVException write_satp(CPURISCVState
> *env, int csrno,
> > > return RISCV_EXCP_NONE;
> > > }
> > >
> > > +static RISCVException rmw_sctrdepth(CPURISCVState *env, int csrno,
> > > + target_ulong *ret_val,
> > > + target_ulong new_val,
> target_ulong wr_mask)
> > > +{
> > > + uint64_t mask = wr_mask & SCTRDEPTH_MASK;
> > > +
> > > + if (ret_val) {
> > > + *ret_val = env->sctrdepth;
> > > + }
> > > +
> > > + env->sctrdepth = (env->sctrdepth & ~mask) | (new_val & mask);
> > > +
> > > + /* Correct depth. */
> > > + if (wr_mask & SCTRDEPTH_MASK) {
> > > + uint64_t depth = get_field(env->sctrdepth, SCTRDEPTH_MASK);
> > > +
> > > + if (depth > SCTRDEPTH_MAX) {
> > > + depth = SCTRDEPTH_MAX;
> > > + env->sctrdepth = set_field(env->sctrdepth,
> SCTRDEPTH_MASK, depth);
> > > + }
> > > +
> > > + /* Update sctrstatus.WRPTR with a legal value */
> > > + depth = 16 << depth;
> > > + env->sctrstatus =
> > > + env->sctrstatus & (~SCTRSTATUS_WRPTR_MASK | (depth - 1));
> > > + }
> > > +
> > > + return RISCV_EXCP_NONE;
> > > +}
> > > +
> > > +static RISCVException rmw_sctrstatus(CPURISCVState *env, int csrno,
> > > + target_ulong *ret_val,
> > > + target_ulong new_val,
> target_ulong wr_mask)
> > > +{
> > > + uint32_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
> > > + uint32_t mask = wr_mask & SCTRSTATUS_MASK;
> > > +
> > > + if (ret_val) {
> > > + *ret_val = env->sctrstatus;
> > > + }
> > > +
> > > + env->sctrstatus = (env->sctrstatus & ~mask) | (new_val & mask);
> > > +
> > > + /* Update sctrstatus.WRPTR with a legal value */
> > > + env->sctrstatus = env->sctrstatus & (~SCTRSTATUS_WRPTR_MASK |
> (depth - 1));
> > > +
> > > + return RISCV_EXCP_NONE;
> > > +}
> > > +
> > > +static RISCVException rmw_xctrctl(CPURISCVState *env, int csrno,
> > > + target_ulong *ret_val,
> > > + target_ulong new_val,
> target_ulong wr_mask)
> > > +{
> > > + uint64_t csr_mask, mask = wr_mask;
> > > + uint64_t *ctl_ptr = &env->mctrctl;
> > > +
> > > + if (csrno == CSR_MCTRCTL) {
> > > + csr_mask = MCTRCTL_MASK;
> > > + } else if (csrno == CSR_SCTRCTL && !env->virt_enabled) {
> > > + csr_mask = SCTRCTL_MASK;
> > > + } else {
> > > + /*
> > > + * This is for csrno == CSR_SCTRCTL and env->virt_enabled ==
> true
> > > + * or csrno == CSR_VSCTRCTL.
> > > + */
> > > + csr_mask = VSCTRCTL_MASK;
> > > + ctl_ptr = &env->vsctrctl;
> > > + }
> > > +
> > > + mask &= csr_mask;
> > > +
> > > + if (ret_val) {
> > > + *ret_val = *ctl_ptr & csr_mask;
> > > + }
> > > +
> > > + *ctl_ptr = (*ctl_ptr & ~mask) | (new_val & mask);
> > > +
> > > + return RISCV_EXCP_NONE;
> > > +}
> > > +
> > > static RISCVException read_vstopi(CPURISCVState *env, int csrno,
> > > target_ulong *val)
> > > {
> > > @@ -5771,6 +5893,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
> > > [CSR_SPMBASE] = { "spmbase", pointer_masking, read_spmbase,
> > > write_spmbase
> },
> > >
> > > + [CSR_MCTRCTL] = { "mctrctl", ctr_mmode, NULL, NULL,
> rmw_xctrctl },
> > > + [CSR_SCTRCTL] = { "sctrctl", ctr_smode, NULL, NULL,
> rmw_xctrctl },
> > > + [CSR_VSCTRCTL] = { "vsctrctl", ctr_smode, NULL, NULL,
> rmw_xctrctl },
> > > + [CSR_SCTRDEPTH] = { "sctrdepth", ctr_smode, NULL, NULL,
> rmw_sctrdepth },
> > > + [CSR_SCTRSTATUS] = { "sctrstatus", ctr_smode, NULL, NULL,
> rmw_sctrstatus },
> > > +
> > > /* Performance Counters */
> > > [CSR_HPMCOUNTER3] = { "hpmcounter3", ctr,
> read_hpmcounter },
> > > [CSR_HPMCOUNTER4] = { "hpmcounter4", ctr,
> read_hpmcounter },
> > > --
> > > 2.34.1
> > >
> > >
>
[-- Attachment #2: Type: text/html, Size: 12769 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 3/6] target/riscv: Add support for Control Transfer Records extension CSRs.
2024-10-17 15:04 ` Frank Chang
@ 2024-10-18 10:24 ` Rajnesh Kanwal
0 siblings, 0 replies; 21+ messages in thread
From: Rajnesh Kanwal @ 2024-10-18 10:24 UTC (permalink / raw)
To: Frank Chang
Cc: qemu-riscv, qemu-devel, alistair.francis, bin.meng, liweiwei,
dbarboza, zhiwei_liu, atishp, apatel, beeman,
tech-control-transfer-records, jason.chien
On Thu, Oct 17, 2024 at 4:05 PM Frank Chang <frank.chang@sifive.com> wrote:
>
> On Thu, Oct 17, 2024 at 7:18 PM Rajnesh Kanwal <rkanwal@rivosinc.com> wrote:
>>
>> On Tue, Aug 27, 2024 at 10:28 AM Frank Chang <frank.chang@sifive.com> wrote:
>> >
>> > Rajnesh Kanwal <rkanwal@rivosinc.com> 於 2024年6月19日 週三 下午11:27寫道:
>> > >
>> > > This commit adds support for [m|s|vs]ctrcontrol, sctrstatus and
>> > > sctrdepth CSRs handling.
>> > >
>> > > Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
>> > > ---
>> > > target/riscv/cpu.h | 5 ++
>> > > target/riscv/cpu_cfg.h | 2 +
>> > > target/riscv/csr.c | 128 +++++++++++++++++++++++++++++++++++++++++
>> > > 3 files changed, 135 insertions(+)
>> > >
>> > > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
>> > > index a185e2d494..3d4d5172b8 100644
>> > > --- a/target/riscv/cpu.h
>> > > +++ b/target/riscv/cpu.h
>> > > @@ -263,6 +263,11 @@ struct CPUArchState {
>> > > target_ulong mcause;
>> > > target_ulong mtval; /* since: priv-1.10.0 */
>> > >
>> > > + uint64_t mctrctl;
>> > > + uint32_t sctrdepth;
>> > > + uint32_t sctrstatus;
>> > > + uint64_t vsctrctl;
>> > > +
>> > > /* Machine and Supervisor interrupt priorities */
>> > > uint8_t miprio[64];
>> > > uint8_t siprio[64];
>> > > diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h
>> > > index d9354dc80a..d329a65811 100644
>> > > --- a/target/riscv/cpu_cfg.h
>> > > +++ b/target/riscv/cpu_cfg.h
>> > > @@ -123,6 +123,8 @@ struct RISCVCPUConfig {
>> > > bool ext_zvfhmin;
>> > > bool ext_smaia;
>> > > bool ext_ssaia;
>> > > + bool ext_smctr;
>> > > + bool ext_ssctr;
>> >
>> > Base on: https://github.com/riscv/riscv-control-transfer-records/pull/29
>> > Smctr and Ssctr depend on both S-mode and Sscsrind.
>> > We should add the implied rules for Smctr and Ssctr.
>> >
>> > Regards,
>> > Frank Chang
>>
>> Hi Frank,
>>
>> Are you referring to the checks in riscv_cpu_validate_set_extensions()?
>> These checks are already present in the last patch.
>>
>> https://lore.kernel.org/qemu-riscv/20240619152708.135991-7-rkanwal@rivosinc.com/
>>
>
> Hi Rajnesh,
>
> No, I was referring to the implied rules defined in:
> https://github.com/qemu/qemu/blob/master/target/riscv/cpu.c#L2630
>
> The implied rules will enable the implied extensions when the "parent" extension is enabled.
> Unless user turns them off explicitly from the command line,
> which is an error and will be caught by the check rules you mentioned above.
>
> The spec defines that:
> "Smctr and Ssctr depend on both the implementation of S-mode and the Sscsrind extension"
> which means that we should add RVS and Sscsrind extensions as the implied rules for Smctr and Ssctr respectively.
>
> The use of the word "depends" in the spec is quite ambiguous.
> But I once had a chance to ask Andrew Waterman about it,
> and he replied that we should treat “depends on” or “requires” as a synonym for “implies”.
>
>
> Regards,
> Frank Chang
>
Thanks for the explanation Frank. I will fix this in the next version.
>> >
>> >
>> > > bool ext_sscofpmf;
>> > > bool ext_smepmp;
>> > > bool rvv_ta_all_1s;
>> > > diff --git a/target/riscv/csr.c b/target/riscv/csr.c
>> > > index 2f92e4b717..0b5bf4d050 100644
>> > > --- a/target/riscv/csr.c
>> > > +++ b/target/riscv/csr.c
>> > > @@ -621,6 +621,48 @@ static RISCVException pointer_masking(CPURISCVState *env, int csrno)
>> > > return RISCV_EXCP_ILLEGAL_INST;
>> > > }
>> > >
>> > > +/*
>> > > + * M-mode:
>> > > + * Without ext_smctr raise illegal inst excep.
>> > > + * Otherwise everything is accessible to m-mode.
>> > > + *
>> > > + * S-mode:
>> > > + * Without ext_ssctr or mstateen.ctr raise illegal inst excep.
>> > > + * Otherwise everything other than mctrctl is accessible.
>> > > + *
>> > > + * VS-mode:
>> > > + * Without ext_ssctr or mstateen.ctr raise illegal inst excep.
>> > > + * Without hstateen.ctr raise virtual illegal inst excep.
>> > > + * Otherwise allow sctrctl (vsctrctl), sctrstatus, 0x200-0x2ff entry range.
>> > > + * Always raise illegal instruction exception for sctrdepth.
>> > > + */
>> > > +static RISCVException ctr_mmode(CPURISCVState *env, int csrno)
>> > > +{
>> > > + /* Check if smctr-ext is present */
>> > > + if (riscv_cpu_cfg(env)->ext_smctr) {
>> > > + return RISCV_EXCP_NONE;
>> > > + }
>> > > +
>> > > + return RISCV_EXCP_ILLEGAL_INST;
>> > > +}
>> > > +
>> > > +static RISCVException ctr_smode(CPURISCVState *env, int csrno)
>> > > +{
>> > > + const RISCVCPUConfig *cfg = riscv_cpu_cfg(env);
>> > > +
>> > > + if (!cfg->ext_smctr && !cfg->ext_ssctr) {
>> > > + return RISCV_EXCP_ILLEGAL_INST;
>> > > + }
>> > > +
>> > > + RISCVException ret = smstateen_acc_ok(env, 0, SMSTATEEN0_CTR);
>> > > + if (ret == RISCV_EXCP_NONE && csrno == CSR_SCTRDEPTH &&
>> > > + env->virt_enabled) {
>> > > + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
>> > > + }
>> > > +
>> > > + return ret;
>> > > +}
>> > > +
>> > > static RISCVException aia_hmode(CPURISCVState *env, int csrno)
>> > > {
>> > > int ret;
>> > > @@ -3835,6 +3877,86 @@ static RISCVException write_satp(CPURISCVState *env, int csrno,
>> > > return RISCV_EXCP_NONE;
>> > > }
>> > >
>> > > +static RISCVException rmw_sctrdepth(CPURISCVState *env, int csrno,
>> > > + target_ulong *ret_val,
>> > > + target_ulong new_val, target_ulong wr_mask)
>> > > +{
>> > > + uint64_t mask = wr_mask & SCTRDEPTH_MASK;
>> > > +
>> > > + if (ret_val) {
>> > > + *ret_val = env->sctrdepth;
>> > > + }
>> > > +
>> > > + env->sctrdepth = (env->sctrdepth & ~mask) | (new_val & mask);
>> > > +
>> > > + /* Correct depth. */
>> > > + if (wr_mask & SCTRDEPTH_MASK) {
>> > > + uint64_t depth = get_field(env->sctrdepth, SCTRDEPTH_MASK);
>> > > +
>> > > + if (depth > SCTRDEPTH_MAX) {
>> > > + depth = SCTRDEPTH_MAX;
>> > > + env->sctrdepth = set_field(env->sctrdepth, SCTRDEPTH_MASK, depth);
>> > > + }
>> > > +
>> > > + /* Update sctrstatus.WRPTR with a legal value */
>> > > + depth = 16 << depth;
>> > > + env->sctrstatus =
>> > > + env->sctrstatus & (~SCTRSTATUS_WRPTR_MASK | (depth - 1));
>> > > + }
>> > > +
>> > > + return RISCV_EXCP_NONE;
>> > > +}
>> > > +
>> > > +static RISCVException rmw_sctrstatus(CPURISCVState *env, int csrno,
>> > > + target_ulong *ret_val,
>> > > + target_ulong new_val, target_ulong wr_mask)
>> > > +{
>> > > + uint32_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
>> > > + uint32_t mask = wr_mask & SCTRSTATUS_MASK;
>> > > +
>> > > + if (ret_val) {
>> > > + *ret_val = env->sctrstatus;
>> > > + }
>> > > +
>> > > + env->sctrstatus = (env->sctrstatus & ~mask) | (new_val & mask);
>> > > +
>> > > + /* Update sctrstatus.WRPTR with a legal value */
>> > > + env->sctrstatus = env->sctrstatus & (~SCTRSTATUS_WRPTR_MASK | (depth - 1));
>> > > +
>> > > + return RISCV_EXCP_NONE;
>> > > +}
>> > > +
>> > > +static RISCVException rmw_xctrctl(CPURISCVState *env, int csrno,
>> > > + target_ulong *ret_val,
>> > > + target_ulong new_val, target_ulong wr_mask)
>> > > +{
>> > > + uint64_t csr_mask, mask = wr_mask;
>> > > + uint64_t *ctl_ptr = &env->mctrctl;
>> > > +
>> > > + if (csrno == CSR_MCTRCTL) {
>> > > + csr_mask = MCTRCTL_MASK;
>> > > + } else if (csrno == CSR_SCTRCTL && !env->virt_enabled) {
>> > > + csr_mask = SCTRCTL_MASK;
>> > > + } else {
>> > > + /*
>> > > + * This is for csrno == CSR_SCTRCTL and env->virt_enabled == true
>> > > + * or csrno == CSR_VSCTRCTL.
>> > > + */
>> > > + csr_mask = VSCTRCTL_MASK;
>> > > + ctl_ptr = &env->vsctrctl;
>> > > + }
>> > > +
>> > > + mask &= csr_mask;
>> > > +
>> > > + if (ret_val) {
>> > > + *ret_val = *ctl_ptr & csr_mask;
>> > > + }
>> > > +
>> > > + *ctl_ptr = (*ctl_ptr & ~mask) | (new_val & mask);
>> > > +
>> > > + return RISCV_EXCP_NONE;
>> > > +}
>> > > +
>> > > static RISCVException read_vstopi(CPURISCVState *env, int csrno,
>> > > target_ulong *val)
>> > > {
>> > > @@ -5771,6 +5893,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>> > > [CSR_SPMBASE] = { "spmbase", pointer_masking, read_spmbase,
>> > > write_spmbase },
>> > >
>> > > + [CSR_MCTRCTL] = { "mctrctl", ctr_mmode, NULL, NULL, rmw_xctrctl },
>> > > + [CSR_SCTRCTL] = { "sctrctl", ctr_smode, NULL, NULL, rmw_xctrctl },
>> > > + [CSR_VSCTRCTL] = { "vsctrctl", ctr_smode, NULL, NULL, rmw_xctrctl },
>> > > + [CSR_SCTRDEPTH] = { "sctrdepth", ctr_smode, NULL, NULL, rmw_sctrdepth },
>> > > + [CSR_SCTRSTATUS] = { "sctrstatus", ctr_smode, NULL, NULL, rmw_sctrstatus },
>> > > +
>> > > /* Performance Counters */
>> > > [CSR_HPMCOUNTER3] = { "hpmcounter3", ctr, read_hpmcounter },
>> > > [CSR_HPMCOUNTER4] = { "hpmcounter4", ctr, read_hpmcounter },
>> > > --
>> > > 2.34.1
>> > >
>> > >
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2024-10-18 10:25 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-06-19 15:27 [PATCH v2 0/6] target/riscv: Add support for Control Transfer Records Ext Rajnesh Kanwal
2024-06-19 15:27 ` [PATCH v2 1/6] target/riscv: Remove obsolete sfence.vm instruction Rajnesh Kanwal
2024-06-25 7:58 ` Jason Chien
2024-06-19 15:27 ` [PATCH v2 2/6] target/riscv: Add Control Transfer Records CSR definitions Rajnesh Kanwal
2024-06-26 0:07 ` Alistair Francis
2024-06-26 6:00 ` Jason Chien
2024-06-19 15:27 ` [PATCH v2 3/6] target/riscv: Add support for Control Transfer Records extension CSRs Rajnesh Kanwal
2024-06-25 8:28 ` Jason Chien
2024-08-27 9:28 ` Frank Chang
2024-10-17 11:18 ` Rajnesh Kanwal
2024-10-17 15:04 ` Frank Chang
2024-10-18 10:24 ` Rajnesh Kanwal
2024-06-19 15:27 ` [PATCH v2 4/6] target/riscv: Add support to record CTR entries Rajnesh Kanwal
2024-06-26 3:49 ` Jason Chien
2024-10-15 18:50 ` Rajnesh Kanwal
2024-06-19 15:27 ` [PATCH v2 5/6] target/riscv: Add CTR sctrclr instruction Rajnesh Kanwal
2024-06-25 9:05 ` Jason Chien
2024-06-19 15:27 ` [PATCH v2 6/6] target/riscv: Add support to access ctrsource, ctrtarget, ctrdata regs Rajnesh Kanwal
2024-06-25 9:48 ` Jason Chien
2024-06-26 0:01 ` [PATCH v2 0/6] target/riscv: Add support for Control Transfer Records Ext Alistair Francis
2024-06-26 6:18 ` Jason Chien
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).